Merge branch 'oqtane:dev' into dev

This commit is contained in:
Charles Nurse
2021-05-11 13:29:23 -07:00
97 changed files with 1088 additions and 1023 deletions

View File

@ -1,11 +1,10 @@
@namespace Oqtane.Installer @namespace Oqtane.Installer
@using Oqtane.Interfaces @using Oqtane.Interfaces
@using Oqtane.Installer.Controls
@inject NavigationManager NavigationManager @inject NavigationManager NavigationManager
@inject IInstallationService InstallationService @inject IInstallationService InstallationService
@inject ISiteService SiteService @inject ISiteService SiteService
@inject IUserService UserService @inject IUserService UserService
@inject IDatabaseService DatabaseService
@inject IJSRuntime JSRuntime @inject IJSRuntime JSRuntime
@inject IStringLocalizer<Installer> Localizer @inject IStringLocalizer<Installer> Localizer
@ -22,25 +21,28 @@
<h2>@Localizer["Database Configuration"]</h2><br /> <h2>@Localizer["Database Configuration"]</h2><br />
<table class="form-group" cellpadding="4" cellspacing="4" style="margin: auto;"> <table class="form-group" cellpadding="4" cellspacing="4" style="margin: auto;">
<tbody> <tbody>
<tr> <tr>
<td> <td>
<label class="control-label" style="font-weight: bold">@Localizer["Database Type:"]</label> <label class="control-label" style="font-weight: bold">@Localizer["Database Type:"]</label>
</td> </td>
<td> <td>
<select class="custom-select" value="@_databaseName" @onchange="(e => DatabaseChanged(e))"> <select class="custom-select" value="@_databaseName" @onchange="(e => DatabaseChanged(e))">
@foreach (var database in _databases) @if (_databases != null)
{ {
<option value="@database.Name">@Localizer[@database.FriendlyName]</option> foreach (var database in _databases)
} {
</select> <option value="@database.Name">@Localizer[@database.FriendlyName]</option>
</td> }
</tr> }
@{ </select>
if (_databaseConfigType != null) </td>
{ </tr>
@DatabaseConfigComponent; @{
if (_databaseConfigType != null)
{
@DatabaseConfigComponent;
}
} }
}
</tbody> </tbody>
</table> </table>
</div> </div>
@ -95,7 +97,7 @@
</div> </div>
@code { @code {
private IList<Database> _databases; private List<Database> _databases;
private string _databaseName = "LocalDB"; private string _databaseName = "LocalDB";
private Type _databaseConfigType; private Type _databaseConfigType;
private object _databaseConfig; private object _databaseConfig;
@ -108,44 +110,9 @@
private string _message = string.Empty; private string _message = string.Empty;
private string _loadingDisplay = "display: none;"; private string _loadingDisplay = "display: none;";
protected override void OnInitialized() protected override async Task OnInitializedAsync()
{ {
base.OnInitialized(); _databases = await DatabaseService.GetDatabasesAsync();
_databases = new List<Database>
{
new()
{
Name = "LocalDB",
FriendlyName = "Local Database",
Type = "Oqtane.Installer.Controls.LocalDBConfig, Oqtane.Client"
},
new()
{
Name = "SqlServer",
FriendlyName = "SQL Server",
Type = "Oqtane.Installer.Controls.SqlServerConfig, Oqtane.Client"
},
new()
{
Name = "Sqlite",
FriendlyName = "Sqlite",
Type = "Oqtane.Installer.Controls.SqliteConfig, Oqtane.Client"
},
new()
{
Name = "MySQL",
FriendlyName = "MySQL",
Type = "Oqtane.Installer.Controls.MySQLConfig, Oqtane.Client"
},
new()
{
Name = "PostgreSQL",
FriendlyName = "PostgreSQL",
Type = "Oqtane.Installer.Controls.PostgreSQLConfig, Oqtane.Client"
}
};
LoadDatabaseConfigComponent(); LoadDatabaseConfigComponent();
} }
@ -157,7 +124,7 @@
LoadDatabaseConfigComponent(); LoadDatabaseConfigComponent();
} }
catch (Exception exception) catch
{ {
_message = Localizer["Error loading Database Configuration Control"]; _message = Localizer["Error loading Database Configuration Control"];
} }

View File

@ -5,6 +5,7 @@
@inject NavigationManager NavigationManager @inject NavigationManager NavigationManager
@inject ILocalizationService LocalizationService @inject ILocalizationService LocalizationService
@inject ILanguageService LanguageService @inject ILanguageService LanguageService
@inject IPackageService PackageService
@inject IStringLocalizer<Add> Localizer @inject IStringLocalizer<Add> Localizer
@if (_supportedCultures == null) @if (_supportedCultures == null)
@ -13,57 +14,114 @@
} }
else else
{ {
@if (_supportedCultures?.Count() > 1) <TabStrip>
{ <TabPanel Name="Manage" ResourceKey="Manage">
<table class="table table-borderless"> @if (_availableCultures.Count() == 0)
<tr> {
<td> <ModuleMessage Type="MessageType.Info" Message="@_message"></ModuleMessage>
<Label For="name" HelpText="Name Of The Langauage" ResourceKey="Name">Name:</Label> }
</td> else
<td> {
<select id="_code" class="form-control" @bind="@_code"> <table class="table table-borderless">
@foreach (var culture in _supportedCultures) <tr>
{ <td>
<option value="@culture.Name">@culture.DisplayName</option> <Label For="name" HelpText="Name Of The Language" ResourceKey="Name">Name:</Label>
} </td>
</select> <td>
</td> <select id="_code" class="form-control" @bind="@_code">
</tr> @foreach (var culture in _availableCultures)
<tr> {
<td> <option value="@culture.Name">@culture.DisplayName</option>
<Label For="default" HelpText="Indicates Whether Or Not This Language Is The Default For The Site" ResourceKey="IsDefault">Default?</Label> }
</td> </select>
<td> </td>
<select id="default" class="form-control" @bind="@_isDefault"> </tr>
<option value="True">@Localizer["Yes"]</option> <tr>
<option value="False">@Localizer["No"]</option> <td>
</select> <Label For="default" HelpText="Indicates Whether Or Not This Language Is The Default For The Site" ResourceKey="IsDefault">Default?</Label>
</td> </td>
</tr> <td>
</table> <select id="default" class="form-control" @bind="@_isDefault">
<button type="button" class="btn btn-success" @onclick="SaveLanguage">@Localizer["Save"]</button> <option value="True">@Localizer["Yes"]</option>
} <option value="False">@Localizer["No"]</option>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@Localizer["Cancel"]</NavLink> </select>
</td>
</tr>
</table>
<button type="button" class="btn btn-success" @onclick="SaveLanguage">@Localizer["Save"]</button>
}
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@Localizer["Cancel"]</NavLink>
</TabPanel>
<TabPanel Name="Download" ResourceKey="Download" Security="SecurityAccessLevel.Host">
@if (_packages != null && _packages.Count > 0)
{
<ModuleMessage Type="MessageType.Info" Message="Download one or more language packages from the list below. Once you are ready click Install to complete the installation."></ModuleMessage>
<Pager Items="@_packages">
<Header>
<th>@Localizer["Name"]</th>
<th>@Localizer["Version"]</th>
<th style="width: 1px"></th>
</Header>
<Row>
<td>@context.Name</td>
<td>@context.Version</td>
<td>
<button type="button" class="btn btn-primary" @onclick=@(async () => await DownloadLanguage(context.PackageId, context.Version))>@Localizer["Download"]</button>
</td>
</Row>
</Pager>
<button type="button" class="btn btn-success" @onclick="InstallLanguages">@Localizer["Install"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@Localizer["Cancel"]</NavLink>
}
else
{
<ModuleMessage Type="MessageType.Info" Message="No Language Packages Are Available To Download"></ModuleMessage>
}
</TabPanel>
<TabPanel Name="Upload" ResourceKey="Upload" Security="SecurityAccessLevel.Host">
<table class="table table-borderless">
<tr>
<td>
<Label HelpText="Upload one or more language packages. Once they are uploaded click Install to complete the installation." ResourceKey="Module">Language: </Label>
</td>
<td>
<FileManager Filter="nupkg" ShowFiles="false" Folder="Packages" UploadMultiple="true" />
</td>
</tr>
</table>
<button type="button" class="btn btn-success" @onclick="InstallLanguages">@Localizer["Install"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@Localizer["Cancel"]</NavLink>
</TabPanel>
</TabStrip>
} }
@code { @code {
private string _code = string.Empty; private string _code = string.Empty;
private string _isDefault = "False"; private string _isDefault = "False";
private string _message;
private IEnumerable<Culture> _supportedCultures;
private IEnumerable<Culture> _availableCultures;
private List<Package> _packages;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
private IEnumerable<Culture> _supportedCultures;
protected override async Task OnParametersSetAsync() protected override async Task OnParametersSetAsync()
{ {
var languages = await LanguageService.GetLanguagesAsync(PageState.Site.SiteId);
var languagesCodes = languages.Select(l => l.Code).ToList();
_supportedCultures = await LocalizationService.GetCulturesAsync(); _supportedCultures = await LocalizationService.GetCulturesAsync();
if (_supportedCultures.Count() <= 1) _availableCultures = _supportedCultures
.Where(c => !c.Name.Equals(Constants.DefaultCulture) && !languagesCodes.Contains(c.Name));
_packages = await PackageService.GetPackagesAsync("language");
if (_supportedCultures.Count() == 1)
{ {
AddModuleMessage(Localizer["The Only Supported Culture That Has Been Defined Is English"], MessageType.Warning); _message = Localizer["The Only Installed Language Is English"];
} }
else else if (_availableCultures.Count() == 0)
{ {
_supportedCultures = _supportedCultures.Where(c => !c.Name.Equals(Constants.DefaultCulture)); _message = Localizer["All The Installed Languages Have Been Added."];
} }
} }
@ -97,6 +155,35 @@ else
} }
} }
private async Task InstallLanguages()
{
try
{
await PackageService.InstallPackagesAsync();
AddModuleMessage(Localizer["Language Packages Installed Successfully. You Must <a href=\"{0}\">Restart</a> Your Application To Apply These Changes.", NavigateUrl("admin/system")], MessageType.Success);
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Installing Language Package");
}
}
private async Task DownloadLanguage(string packageid, string version)
{
try
{
await PackageService.DownloadPackageAsync(packageid, version, "Packages");
await logger.LogInformation("Language Paclage {Name} {Version} Downloaded Successfully", packageid, version);
AddModuleMessage(Localizer["Language Package Downloaded Successfully. Click Install To Complete Installation."], MessageType.Success);
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Downloading Language Package {Name} {Version}", packageid, version);
AddModuleMessage(Localizer["Error Downloading Language Package"], MessageType.Error);
}
}
private async Task SetCultureAsync(string culture) private async Task SetCultureAsync(string culture)
{ {
if (culture != CultureInfo.CurrentUICulture.Name) if (culture != CultureInfo.CurrentUICulture.Name)

View File

@ -1,6 +1,8 @@
@namespace Oqtane.Modules.Admin.Languages @namespace Oqtane.Modules.Admin.Languages
@inherits ModuleBase @inherits ModuleBase
@inject ILanguageService LanguageService @inject ILanguageService LanguageService
@inject ILocalizationService LocalizationService
@inject IPackageService PackageService
@inject IStringLocalizer<Index> Localizer @inject IStringLocalizer<Index> Localizer
@if (_languages == null) @if (_languages == null)
@ -17,24 +19,43 @@ else
<th>@Localizer["Name"]</th> <th>@Localizer["Name"]</th>
<th>@Localizer["Code"]</th> <th>@Localizer["Code"]</th>
<th>@Localizer["Default?"]</th> <th>@Localizer["Default?"]</th>
<th style="width: 1px;">&nbsp;</th>
</Header> </Header>
<Row> <Row>
<td><ActionDialog Header="Delete Langauge" Message="@Localizer["Are You Sure You Wish To Delete The {0} Language?", context.Name]" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteLanguage(context))" Disabled="@(context.IsDefault)" ResourceKey="DeleteLanguage" /></td> <td><ActionDialog Header="Delete Langauge" Message="@Localizer["Are You Sure You Wish To Delete The {0} Language From This Site?", context.Name]" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteLanguage(context))" Disabled="@((context.IsDefault && _languages.Count > 2) || context.Code == Constants.DefaultCulture)" ResourceKey="DeleteLanguage" /></td>
<td>@context.Name</td> <td>@context.Name</td>
<td>@context.Code</td> <td>@context.Code</td>
<td><TriStateCheckBox Value="@(context.IsDefault)" Disabled="true"></TriStateCheckBox></td> <td><TriStateCheckBox Value="@(context.IsDefault)" Disabled="true"></TriStateCheckBox></td>
<td>
@if (UpgradeAvailable(context.Code))
{
<button type="button" class="btn btn-success" @onclick=@(async () => await DownloadLanguage(context.Code))>@Localizer["Upgrade"]</button>
}
</td>
</Row> </Row>
</Pager> </Pager>
} }
@code { @code {
private List<Language> _languages; private List<Language> _languages;
private List<Package> _packages;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
protected override async Task OnParametersSetAsync() protected override async Task OnParametersSetAsync()
{ {
_languages = await LanguageService.GetLanguagesAsync(PageState.Site.SiteId); _languages = await LanguageService.GetLanguagesAsync(PageState.Site.SiteId);
var cultures = await LocalizationService.GetCulturesAsync();
var culture = cultures.First(c => c.Name.Equals(Constants.DefaultCulture));
// Adds English as default language
_languages.Insert(0, new Language { Name = culture.DisplayName, Code = culture.Name, IsDefault = !_languages.Any(l => l.IsDefault) });
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
{
_packages = await PackageService.GetPackagesAsync("language");
}
} }
private async Task DeleteLanguage(Language language) private async Task DeleteLanguage(Language language)
@ -53,4 +74,38 @@ else
AddModuleMessage(Localizer["Error Deleting Language"], MessageType.Error); AddModuleMessage(Localizer["Error Deleting Language"], MessageType.Error);
} }
} }
private bool UpgradeAvailable(string code)
{
var upgradeavailable = false;
if (_packages != null)
{
var package = _packages.Where(item => item.PackageId == (Constants.PackageId + ".Client." + code)).FirstOrDefault();
if (package != null)
{
upgradeavailable = (Version.Parse(package.Version).CompareTo(Version.Parse(Constants.Version)) > 0);
}
}
return upgradeavailable;
}
private async Task DownloadLanguage(string code)
{
try
{
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
{
await PackageService.DownloadPackageAsync(Constants.PackageId + ".Client." + code, Constants.Version, "Packages");
await logger.LogInformation("Language Package Downloaded {Code} {Version}", code, Constants.Version);
await PackageService.InstallPackagesAsync();
AddModuleMessage(Localizer["Language Package Installed Successfully. You Must <a href=\"{0}\">Restart</a> Your Application To Apply These Changes.", NavigateUrl("admin/system")], MessageType.Success);
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Downloading Language Package {Code} {Version} {Error}", code, Constants.Version, ex.Message);
AddModuleMessage(Localizer["Error Downloading Language Package"], MessageType.Error);
}
}
} }

View File

@ -115,7 +115,9 @@
// complete the login on the server so that the cookies are set correctly on SignalR // complete the login on the server so that the cookies are set correctly on SignalR
string antiforgerytoken = await interop.GetElementByName("__RequestVerificationToken"); string antiforgerytoken = await interop.GetElementByName("__RequestVerificationToken");
var fields = new { __RequestVerificationToken = antiforgerytoken, username = _username, password = _password, remember = _remember, returnurl = _returnUrl }; var fields = new { __RequestVerificationToken = antiforgerytoken, username = _username, password = _password, remember = _remember, returnurl = _returnUrl };
await interop.SubmitForm($"/{PageState.Alias.AliasId}/pages/login/", fields); string url = "/pages/login/";
if (!string.IsNullOrEmpty(PageState.Alias.Path)) url = "/" + PageState.Alias.Path + url;
await interop.SubmitForm(url, fields);
} }
else else
{ {

View File

@ -167,46 +167,53 @@
private async Task SaveModule() private async Task SaveModule()
{ {
var pagemodule = await PageModuleService.GetPageModuleAsync(ModuleState.PageModuleId); if (!string.IsNullOrEmpty(_title))
pagemodule.PageId = int.Parse(_pageId);
pagemodule.Title = _title;
pagemodule.ContainerType = (_containerType != "-") ? _containerType : string.Empty;
if (!string.IsNullOrEmpty(pagemodule.ContainerType) && pagemodule.ContainerType == PageState.Page.DefaultContainerType)
{ {
pagemodule.ContainerType = string.Empty; var pagemodule = await PageModuleService.GetPageModuleAsync(ModuleState.PageModuleId);
} pagemodule.PageId = int.Parse(_pageId);
if (!string.IsNullOrEmpty(pagemodule.ContainerType) && pagemodule.ContainerType == PageState.Site.DefaultContainerType) pagemodule.Title = _title;
{ pagemodule.ContainerType = (_containerType != "-") ? _containerType : string.Empty;
pagemodule.ContainerType = string.Empty; if (!string.IsNullOrEmpty(pagemodule.ContainerType) && pagemodule.ContainerType == PageState.Page.DefaultContainerType)
}
await PageModuleService.UpdatePageModuleAsync(pagemodule);
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
var module = ModuleState;
module.AllPages = bool.Parse(_allPages);
module.Permissions = _permissionGrid.GetPermissions();
await ModuleService.UpdateModuleAsync(module);
if (_moduleSettingsType != null)
{
if (_moduleSettings is ISettingsControl moduleSettingsControl)
{ {
// module settings updated using explicit interface pagemodule.ContainerType = string.Empty;
await moduleSettingsControl.UpdateSettings();
} }
else if (!string.IsNullOrEmpty(pagemodule.ContainerType) && pagemodule.ContainerType == PageState.Site.DefaultContainerType)
{ {
// legacy support - module settings updated by convention ( ie. by calling a public method named "UpdateSettings" in settings component ) pagemodule.ContainerType = string.Empty;
_moduleSettings?.GetType().GetMethod("UpdateSettings")?.Invoke(_moduleSettings, null);
} }
} await PageModuleService.UpdatePageModuleAsync(pagemodule);
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
if (_containerSettingsType != null && _containerSettings is ISettingsControl containerSettingsControl) var module = ModuleState;
module.AllPages = bool.Parse(_allPages);
module.Permissions = _permissionGrid.GetPermissions();
await ModuleService.UpdateModuleAsync(module);
if (_moduleSettingsType != null)
{
if (_moduleSettings is ISettingsControl moduleSettingsControl)
{
// module settings updated using explicit interface
await moduleSettingsControl.UpdateSettings();
}
else
{
// legacy support - module settings updated by convention ( ie. by calling a public method named "UpdateSettings" in settings component )
_moduleSettings?.GetType().GetMethod("UpdateSettings")?.Invoke(_moduleSettings, null);
}
}
if (_containerSettingsType != null && _containerSettings is ISettingsControl containerSettingsControl)
{
await containerSettingsControl.UpdateSettings();
}
NavigationManager.NavigateTo(NavigateUrl());
}
else
{ {
await containerSettingsControl.UpdateSettings(); AddModuleMessage(Localizer["You Must Provide A Title For The Module"], MessageType.Warning);
} }
NavigationManager.NavigateTo(NavigateUrl());
} }
} }

View File

@ -30,7 +30,7 @@
</tr> </tr>
<tr> <tr>
<td> <td>
<Label For="alias" HelpText="Enter the alias for the server" ResourceKey="Aliases">Aliases: </Label> <Label For="alias" HelpText="Enter the aliases for the site. An alias can be a domain name (www.site.com) or a virtual folder (ie. www.site.com/folder). If a site has multiple aliases they can be separated by commas." ResourceKey="Aliases">Aliases: </Label>
</td> </td>
<td> <td>
<textarea id="alias" class="form-control" @bind="@_urls" rows="3"></textarea> <textarea id="alias" class="form-control" @bind="@_urls" rows="3"></textarea>
@ -60,67 +60,67 @@
</tr> </tr>
</table> </table>
<Section Name="Appearance" Heading="Appearance" ResourceKey="Appearance"> <Section Name="Appearance" Heading="Appearance" ResourceKey="Appearance">
<table class="table table-borderless"> <table class="table table-borderless">
<tr> <tr>
<td> <td>
<Label For="logo" HelpText="Specify a logo for the site" ResourceKey="Logo">Logo: </Label> <Label For="logo" HelpText="Specify a logo for the site" ResourceKey="Logo">Logo: </Label>
</td> </td>
<td> <td>
<FileManager FileId="@_logofileid" Filter="@Constants.ImageFiles" @ref="_logofilemanager" /> <FileManager FileId="@_logofileid" Filter="@Constants.ImageFiles" @ref="_logofilemanager" />
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<Label For="favicon" HelpText="Specify a Favicon" ResourceKey="FavoriteIcon">Favicon: </Label> <Label For="favicon" HelpText="Specify a Favicon" ResourceKey="FavoriteIcon">Favicon: </Label>
</td> </td>
<td> <td>
<FileManager FileId="@_faviconfileid" Filter="ico" @ref="_faviconfilemanager" /> <FileManager FileId="@_faviconfileid" Filter="ico" @ref="_faviconfilemanager" />
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<Label For="defaultTheme" HelpText="Select the sites default theme" ResourceKey="DefaultTheme">Default Theme: </Label> <Label For="defaultTheme" HelpText="Select the sites default theme" ResourceKey="DefaultTheme">Default Theme: </Label>
</td> </td>
<td> <td>
<select id="defaultTheme" class="form-control" value="@_themetype" @onchange="(e => ThemeChanged(e))"> <select id="defaultTheme" class="form-control" value="@_themetype" @onchange="(e => ThemeChanged(e))">
<option value="-">&lt;@Localizer["Select Theme"]&gt;</option> <option value="-">&lt;@Localizer["Select Theme"]&gt;</option>
@foreach (var theme in _themes) @foreach (var theme in _themes)
{ {
<option value="@theme.TypeName">@theme.Name</option> <option value="@theme.TypeName">@theme.Name</option>
} }
</select> </select>
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<Label For="defaultContainer" HelpText="Select the default container for the site" ResourceKey="DefaultContainer">Default Container: </Label> <Label For="defaultContainer" HelpText="Select the default container for the site" ResourceKey="DefaultContainer">Default Container: </Label>
</td> </td>
<td> <td>
<select id="defaultContainer" class="form-control" @bind="@_containertype"> <select id="defaultContainer" class="form-control" @bind="@_containertype">
<option value="-">&lt;@Localizer["Select Container"]&gt;</option> <option value="-">&lt;@Localizer["Select Container"]&gt;</option>
@foreach (var container in _containers) @foreach (var container in _containers)
{ {
<option value="@container.TypeName">@container.Name</option> <option value="@container.TypeName">@container.Name</option>
} }
</select> </select>
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<Label For="defaultAdminContainer" HelpText="Select the default admin container for the site" ResourceKey="DefaultAdminContainer">Default Admin Container: </Label> <Label For="defaultAdminContainer" HelpText="Select the default admin container for the site" ResourceKey="DefaultAdminContainer">Default Admin Container: </Label>
</td> </td>
<td> <td>
<select id="defaultAdminContainer" class="form-control" @bind="@_admincontainertype"> <select id="defaultAdminContainer" class="form-control" @bind="@_admincontainertype">
<option value="-">&lt;@Localizer["Select Container"]&gt;</option> <option value="-">&lt;@Localizer["Select Container"]&gt;</option>
<option value="">&lt;@Localizer["Default Admin Container"]&gt;</option> <option value="">&lt;@Localizer["Default Admin Container"]&gt;</option>
@foreach (var container in _containers) @foreach (var container in _containers)
{ {
<option value="@container.TypeName">@container.Name</option> <option value="@container.TypeName">@container.Name</option>
} }
</select> </select>
</td> </td>
</tr> </tr>
</table> </table>
</Section> </Section>
<Section Name="SMTP" Heading="SMTP Settings" ResourceKey="SMTPSettings"> <Section Name="SMTP" Heading="SMTP Settings" ResourceKey="SMTPSettings">
<table class="table table-borderless"> <table class="table table-borderless">
@ -218,6 +218,7 @@
<br /> <br />
<button type="button" class="btn btn-success" @onclick="SaveSite">@Localizer["Save"]</button> <button type="button" class="btn btn-success" @onclick="SaveSite">@Localizer["Save"]</button>
<ActionDialog Header="Delete Site" Message="@Localizer["Are You Sure You Wish To Delete This Site?"]" Action="Delete" Security="SecurityAccessLevel.Host" Class="btn btn-danger" OnClick="@(async () => await DeleteSite())" ResourceKey="DeleteSite" />
<br /> <br />
<br /> <br />
<AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon" DeletedBy="@_deletedby" DeletedOn="@_deletedon"></AuditInfo> <AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon" DeletedBy="@_deletedby" DeletedOn="@_deletedon"></AuditInfo>
@ -276,7 +277,7 @@
_tenant = _tenantList.Find(item => item.TenantId == site.TenantId).Name; _tenant = _tenantList.Find(item => item.TenantId == site.TenantId).Name;
foreach (Alias alias in _aliasList.Where(item => item.SiteId == site.SiteId && item.TenantId == site.TenantId).ToList()) foreach (Alias alias in _aliasList.Where(item => item.SiteId == site.SiteId && item.TenantId == site.TenantId).ToList())
{ {
_urls += alias.Name + "\n"; _urls += alias.Name + ",";
} }
if (site.LogoFileId != null) if (site.LogoFileId != null)
{ {
@ -424,7 +425,6 @@
site = await SiteService.UpdateSiteAsync(site); site = await SiteService.UpdateSiteAsync(site);
_urls = _urls.Replace("\n", ",");
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()) foreach (Alias alias in _aliasList.Where(item => item.SiteId == site.SiteId && item.TenantId == site.TenantId).ToList())
{ {
@ -476,6 +476,36 @@
} }
} }
private async Task DeleteSite()
{
try
{
var sites = await SiteService.GetSitesAsync();
if (sites.Count > 1)
{
await SiteService.DeleteSiteAsync(PageState.Site.SiteId);
await logger.LogInformation("Site Deleted {SiteId}", PageState.Site.SiteId);
var aliases = await AliasService.GetAliasesAsync();
foreach (Alias a in aliases.Where(item => item.SiteId == PageState.Site.SiteId))
{
await AliasService.DeleteAliasAsync(a.AliasId);
}
NavigationManager.NavigateTo(NavigateUrl("admin/sites"));
}
else
{
AddModuleMessage(Localizer["You Are Not Authorized To Delete The Site"], MessageType.Warning);
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Deleting Site {SiteId} {Error}", PageState.Site.SiteId, ex.Message);
AddModuleMessage(Localizer["Error Deleting Site"], MessageType.Error);
}
}
private async Task SendEmail() private async Task SendEmail()
{ {
if (_smtphost != "" && _smtpport != "" && _smtpsender != "") if (_smtphost != "" && _smtpport != "" && _smtpsender != "")

View File

@ -9,6 +9,7 @@
@inject ISiteTemplateService SiteTemplateService @inject ISiteTemplateService SiteTemplateService
@inject IUserService UserService @inject IUserService UserService
@inject IInstallationService InstallationService @inject IInstallationService InstallationService
@inject IDatabaseService DatabaseService
@inject IStringLocalizer<Add> Localizer @inject IStringLocalizer<Add> Localizer
@inject IEnumerable<IOqtaneDatabase> Databases @inject IEnumerable<IOqtaneDatabase> Databases
@ -18,151 +19,151 @@
} }
else else
{ {
<table class="table table-borderless"> <table class="table table-borderless">
<tr>
<td>
<Label For="name" HelpText="Enter the name of the site" ResourceKey="Name">Site Name: </Label>
</td>
<td>
<input id="name" class="form-control" @bind="@_name" />
</td>
</tr>
<tr>
<td>
<Label For="alias" HelpText="Enter the alias for the server" ResourceKey="Aliases">Aliases: </Label>
</td>
<td>
<textarea id="alias" class="form-control" @bind="@_urls" rows="3"></textarea>
</td>
</tr>
<tr>
<td>
<Label For="defaultTheme" HelpText="Select the default theme for the website" ResourceKey="DefaultTheme">Default Theme: </Label>
</td>
<td>
<select id="defaultTheme" class="form-control" @onchange="(e => ThemeChanged(e))">
<option value="-">&lt;@Localizer["Select Theme"]&gt;</option>
@foreach (var theme in _themes)
{
<option value="@theme.TypeName">@theme.Name</option>
}
</select>
</td>
</tr>
<tr>
<td>
<Label For="defaultContainer" HelpText="Select the default container for the site" ResourceKey="DefaultContainer">Default Container: </Label>
</td>
<td>
<select id="defaultContainer" class="form-control" @bind="@_containertype">
<option value="-">&lt;@Localizer["Select Container"]&gt;</option>
@foreach (var container in _containers)
{
<option value="@container.TypeName">@container.Name</option>
}
</select>
</td>
</tr>
<tr>
<td>
<Label For="adminContainer" HelpText="Select the admin container for the site" ResourceKey="AdminContainer">Admin Container: </Label>
</td>
<td>
<select id="adminContainer" class="form-control" @bind="@_admincontainertype">
<option value="-">&lt;@Localizer["Select Container"]&gt;</option>
<option value="">&lt;@Localizer["Default Admin Container"]&gt;</option>
@foreach (var container in _containers)
{
<option value="@container.TypeName">@container.Name</option>
}
</select>
</td>
</tr>
<tr>
<td>
<Label For="siteTemplate" HelpText="Select the site template" ResourceKey="SiteTemplate">Site Template: </Label>
</td>
<td>
<select id="siteTemplate" class="form-control" @bind="@_sitetemplatetype">
<option value="-">&lt;@Localizer["Select Site Template"]&gt;</option>
@foreach (SiteTemplate siteTemplate in _siteTemplates)
{
<option value="@siteTemplate.TypeName">@siteTemplate.Name</option>
}
</select>
</td>
</tr>
<tr>
<td>
<Label For="tenant" HelpText="Select the tenant for the site" ResourceKey="Tenant">Tenant: </Label>
</td>
<td>
<select id="tenant" class="form-control" @onchange="(e => TenantChanged(e))">
<option value="-">&lt;@Localizer["Select Tenant"]&gt;</option>
<option value="+">&lt;@Localizer["Create New Tenant"]&gt;</option>
@foreach (Tenant tenant in _tenants)
{
<option value="@tenant.TenantId">@tenant.Name</option>
}
</select>
</td>
</tr>
@if (_tenantid == "+")
{
<tr> <tr>
<td colspan="2"> <td>
<hr class="app-rule" /> <Label For="name" HelpText="Enter the name of the site" ResourceKey="Name">Site Name: </Label>
</td>
<td>
<input id="name" class="form-control" @bind="@_name" />
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<Label For="name" HelpText="Enter the name for the tenant" ResourceKey="TenantName">Tenant Name: </Label> <Label For="alias" HelpText="Enter the aliases for the site. An alias can be a domain name (www.site.com) or a virtual folder (ie. www.site.com/folder). If a site has multiple aliases they can be separated by commas." ResourceKey="Aliases">Aliases: </Label>
</td> </td>
<td> <td>
<input id="name" class="form-control" @bind="@_tenantName" /> <textarea id="alias" class="form-control" @bind="@_urls" rows="3"></textarea>
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<Label For="databaseType" HelpText="Select the database type for the tenant" ResourceKey="DatabaseType">Database Type: </Label> <Label For="defaultTheme" HelpText="Select the default theme for the website" ResourceKey="DefaultTheme">Default Theme: </Label>
</td> </td>
<td> <td>
<select id="databaseType" class="custom-select" value="@_databaseName" @onchange="(e => DatabaseChanged(e))"> <select id="defaultTheme" class="form-control" @onchange="(e => ThemeChanged(e))">
@foreach (var database in _databases) <option value="-">&lt;@Localizer["Select Theme"]&gt;</option>
@foreach (var theme in _themes)
{ {
<option value="@database.Name">@Localizer[@database.FriendlyName]</option> <option value="@theme.TypeName">@theme.Name</option>
} }
</select> </select>
</td> </td>
</tr> </tr>
if (_databaseConfigType != null) <tr>
<td>
<Label For="defaultContainer" HelpText="Select the default container for the site" ResourceKey="DefaultContainer">Default Container: </Label>
</td>
<td>
<select id="defaultContainer" class="form-control" @bind="@_containertype">
<option value="-">&lt;@Localizer["Select Container"]&gt;</option>
@foreach (var container in _containers)
{
<option value="@container.TypeName">@container.Name</option>
}
</select>
</td>
</tr>
<tr>
<td>
<Label For="adminContainer" HelpText="Select the admin container for the site" ResourceKey="AdminContainer">Admin Container: </Label>
</td>
<td>
<select id="adminContainer" class="form-control" @bind="@_admincontainertype">
<option value="-">&lt;@Localizer["Select Container"]&gt;</option>
<option value="">&lt;@Localizer["Default Admin Container"]&gt;</option>
@foreach (var container in _containers)
{
<option value="@container.TypeName">@container.Name</option>
}
</select>
</td>
</tr>
<tr>
<td>
<Label For="siteTemplate" HelpText="Select the site template" ResourceKey="SiteTemplate">Site Template: </Label>
</td>
<td>
<select id="siteTemplate" class="form-control" @bind="@_sitetemplatetype">
<option value="-">&lt;@Localizer["Select Site Template"]&gt;</option>
@foreach (SiteTemplate siteTemplate in _siteTemplates)
{
<option value="@siteTemplate.TypeName">@siteTemplate.Name</option>
}
</select>
</td>
</tr>
<tr>
<td>
<Label For="tenant" HelpText="Select the tenant for the site" ResourceKey="Tenant">Tenant: </Label>
</td>
<td>
<select id="tenant" class="form-control" @onchange="(e => TenantChanged(e))">
<option value="-">&lt;@Localizer["Select Tenant"]&gt;</option>
<option value="+">&lt;@Localizer["Create New Tenant"]&gt;</option>
@foreach (Tenant tenant in _tenants)
{
<option value="@tenant.TenantId">@tenant.Name</option>
}
</select>
</td>
</tr>
@if (_tenantid == "+")
{ {
@DatabaseConfigComponent; <tr>
<td colspan="2">
<hr class="app-rule" />
</td>
</tr>
<tr>
<td>
<Label For="name" HelpText="Enter the name for the tenant" ResourceKey="TenantName">Tenant Name: </Label>
</td>
<td>
<input id="name" class="form-control" @bind="@_tenantName" />
</td>
</tr>
<tr>
<td>
<Label For="databaseType" HelpText="Select the database type for the tenant" ResourceKey="DatabaseType">Database Type: </Label>
</td>
<td>
<select id="databaseType" class="custom-select" value="@_databaseName" @onchange="(e => DatabaseChanged(e))">
@foreach (var database in _databases)
{
<option value="@database.Name">@Localizer[@database.FriendlyName]</option>
}
</select>
</td>
</tr>
if (_databaseConfigType != null)
{
@DatabaseConfigComponent;
}
<tr>
<td>
<Label For="hostUsername" HelpText="Enter the username of the host for this site" ResourceKey="HostUsername">Host Username:</Label>
</td>
<td>
<input id="hostUsername" class="form-control" @bind="@_hostUserName" readonly />
</td>
</tr>
<tr>
<td>
<Label For="hostPassword" HelpText="Enter the password for the host of this site" ResourceKey="HostPassword">Host Password:</Label>
</td>
<td>
<input id="hostPassword" type="password" class="form-control" @bind="@_hostpassword" />
</td>
</tr>
} }
<tr> </table>
<td>
<Label For="hostUsername" HelpText="Enter the username of the host for this site" ResourceKey="HostUsername">Host Username:</Label>
</td>
<td>
<input id="hostUsername" class="form-control" @bind="@_hostUserName" readonly />
</td>
</tr>
<tr>
<td>
<Label For="hostPassword" HelpText="Enter the password for the host of this site" ResourceKey="HostPassword">Host Password:</Label>
</td>
<td>
<input id="hostPassword" type="password" class="form-control" @bind="@_hostpassword" />
</td>
</tr>
}
</table>
<button type="button" class="btn btn-success" @onclick="SaveSite">@Localizer["Save"]</button> <button type="button" class="btn btn-success" @onclick="SaveSite">@Localizer["Save"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@Localizer["Cancel"]</NavLink> <NavLink class="btn btn-secondary" href="@NavigateUrl()">@Localizer["Cancel"]</NavLink>
} }
@code { @code {
private IList<Database> _databases; private List<Database> _databases;
private string _databaseName = "LocalDB"; private string _databaseName = "LocalDB";
private Type _databaseConfigType; private Type _databaseConfigType;
private object _databaseConfig; private object _databaseConfig;
@ -177,7 +178,7 @@ else
private string _tenantid = "-"; private string _tenantid = "-";
private string _tenantName = string.Empty; private string _tenantName = string.Empty;
private string _hostUserName = UserNames.Host; private string _hostUserName = UserNames.Host;
private string _hostpassword = string.Empty; private string _hostpassword = string.Empty;
@ -197,44 +198,10 @@ else
_themeList = await ThemeService.GetThemesAsync(); _themeList = await ThemeService.GetThemesAsync();
_themes = ThemeService.GetThemeControls(_themeList); _themes = ThemeService.GetThemeControls(_themeList);
_siteTemplates = await SiteTemplateService.GetSiteTemplatesAsync(); _siteTemplates = await SiteTemplateService.GetSiteTemplatesAsync();
_databases = await DatabaseService.GetDatabasesAsync();
_databases = new List<Database>
{
new()
{
Name = "LocalDB",
FriendlyName = "Local Database",
Type = "Oqtane.Installer.Controls.LocalDBConfig, Oqtane.Client"
},
new()
{
Name = "SqlServer",
FriendlyName = "SQL Server",
Type = "Oqtane.Installer.Controls.SqlServerConfig, Oqtane.Client"
},
new()
{
Name = "Sqlite",
FriendlyName = "Sqlite",
Type = "Oqtane.Installer.Controls.SqliteConfig, Oqtane.Client"
},
new()
{
Name = "MySQL",
FriendlyName = "MySQL",
Type = "Oqtane.Installer.Controls.MySQLConfig, Oqtane.Client"
},
new()
{
Name = "PostgreSQL",
FriendlyName = "PostgreSQL",
Type = "Oqtane.Installer.Controls.PostGreSQLConfig, Oqtane.Client"
}
};
LoadDatabaseConfigComponent(); LoadDatabaseConfigComponent();
} }
private void DatabaseChanged(ChangeEventArgs eventArgs) private void DatabaseChanged(ChangeEventArgs eventArgs)
{ {
try try
@ -243,7 +210,7 @@ else
LoadDatabaseConfigComponent(); LoadDatabaseConfigComponent();
} }
catch (Exception exception) catch
{ {
AddModuleMessage(Localizer["Error loading Database Configuration Control"], MessageType.Error); AddModuleMessage(Localizer["Error loading Database Configuration Control"], MessageType.Error);
} }
@ -365,15 +332,15 @@ else
if (tenant != null) if (tenant != null)
{ {
config.TenantName = tenant.Name; config.TenantName = tenant.Name;
config.ConnectionString= tenant.DBConnectionString; config.ConnectionString = tenant.DBConnectionString;
config.IsNewTenant = false; config.IsNewTenant = false;
} }
} }
if (!string.IsNullOrEmpty(config.TenantName)) if (!string.IsNullOrEmpty(config.TenantName))
{ {
config.SiteName = _name; config.SiteName = _name;
config.Aliases = _urls.Replace("\n", ","); config.Aliases = _urls;
config.DefaultTheme = _themetype; config.DefaultTheme = _themetype;
config.DefaultContainer = _containertype; config.DefaultContainer = _containertype;
config.DefaultAdminContainer = _admincontainertype; config.DefaultAdminContainer = _admincontainertype;

View File

@ -1,283 +0,0 @@
@namespace Oqtane.Modules.Admin.Sites
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject ISiteService SiteService
@inject ITenantService TenantService
@inject IAliasService AliasService
@inject IThemeService ThemeService
@inject IStringLocalizer<Edit> Localizer
@if (_initialized)
{
<table class="table table-borderless">
<tr>
<td>
<Label For="name" HelpText="Enter the name of the site" ResourceKey="Name">Name: </Label>
</td>
<td>
<input id="name" class="form-control" @bind="@_name" />
</td>
</tr>
<tr>
<td>
<Label For="alias" HelpText="Enter the alias for the server" ResourceKey="Aliases">Aliases: </Label>
</td>
<td>
<textarea id="alias" class="form-control" @bind="@_urls" rows="3" />
</td>
</tr>
<tr>
<td>
<Label For="defaultTheme" HelpText="Select the default theme for the website" ResourceKey="DefaultTheme">Default Theme: </Label>
</td>
<td>
<select id="defaultTheme" class="form-control" value="@_themetype" @onchange="(e => ThemeChanged(e))">
<option value="-">&lt;@Localizer["Select Theme"]&gt;</option>
@foreach (var theme in _themes)
{
<option value="@theme.TypeName">@theme.Name</option>
}
</select>
</td>
</tr>
<tr>
<td>
<Label For="defaultContainer" HelpText="Select the default container for the site" ResourceKey="DefaultContainer">Default Container: </Label>
</td>
<td>
<select id="defaultIdea" class="form-control" @bind="@_containertype">
<option value="-">&lt;@Localizer["Select Container"]&gt;</option>
@foreach (var container in _containers)
{
<option value="@container.TypeName">@container.Name</option>
}
</select>
</td>
</tr>
<tr>
<td>
<Label For="defaultAdminContainer" HelpText="Select the default admin container for the site" ResourceKey="DefaultAdminContainer">Default Admin Container: </Label>
</td>
<td>
<select id="defaultAdminContainer" class="form-control" @bind="@_admincontainertype">
<option value="-">&lt;@Localizer["Select Container"]&gt;</option>
<option value="">&lt;@Localizer["Default Admin Container"]&gt;</option>
@foreach (var container in _containers)
{
<option value="@container.TypeName">@container.Name</option>
}
</select>
</td>
</tr>
<tr>
<td>
<Label For="isDeleted" HelpText="Has this site been deleted?" ResourceKey="IsDeleted">Is Deleted? </Label>
</td>
<td>
<select id="isDeleted" class="form-control" @bind="@_isdeleted">
<option value="True">@Localizer["Yes"]</option>
<option value="False">@Localizer["No"]</option>
</select>
</td>
</tr>
<tr>
<td>
<Label For="tenant" HelpText="The tenant for the site" ResourceKey="Tenant">Tenant: </Label>
</td>
<td>
<input id="tenant" class="form-control" @bind="@_tenant" readonly />
</td>
</tr>
<tr>
<td>
<Label For="connectionstring" HelpText="The database connection string" ResourceKey="ConnectionString">Connection String: </Label>
</td>
<td>
<textarea id="connectionstring" class="form-control" @bind="@_connectionstring" rows="3" readonly></textarea>
</td>
</tr>
</table>
<br />
<button type="button" class="btn btn-success" @onclick="SaveSite">@Localizer["Save"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@Localizer["Cancel"]</NavLink>
<br />
<br />
<AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon" DeletedBy="@_deletedby" DeletedOn="@_deletedon"></AuditInfo>
}
@code {
private bool _initialized = false;
private List<Theme> _themeList;
private List<ThemeControl> _themes = new List<ThemeControl>();
private List<ThemeControl> _containers = new List<ThemeControl>();
private Alias _alias;
private string _name = string.Empty;
private List<Alias> _aliasList;
private string _urls = string.Empty;
private string _themetype;
private string _containertype = "-";
private string _admincontainertype = "-";
private string _createdby;
private DateTime _createdon;
private string _modifiedby;
private DateTime _modifiedon;
private string _deletedby;
private DateTime? _deletedon;
private string _isdeleted;
private string _tenant = string.Empty;
private string _connectionstring = string.Empty;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
protected override async Task OnInitializedAsync()
{
try
{
_themeList = await ThemeService.GetThemesAsync();
_aliasList = await AliasService.GetAliasesAsync();
_alias = _aliasList.Find(item => item.AliasId == Int32.Parse(PageState.QueryString["id"]));
SiteService.SetAlias(_alias);
var site = await SiteService.GetSiteAsync(_alias.SiteId);
if (site != null)
{
_name = site.Name;
foreach (Alias alias in _aliasList.Where(item => item.SiteId == site.SiteId && item.TenantId == site.TenantId).ToList())
{
_urls += alias.Name + "\n";
}
_themes = ThemeService.GetThemeControls(_themeList);
_themetype = site.DefaultThemeType;
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
_containertype = site.DefaultContainerType;
_admincontainertype = site.AdminContainerType;
_createdby = site.CreatedBy;
_createdon = site.CreatedOn;
_modifiedby = site.ModifiedBy;
_modifiedon = site.ModifiedOn;
_deletedby = site.DeletedBy;
_deletedon = site.DeletedOn;
_isdeleted = site.IsDeleted.ToString();
List<Tenant> tenants = await TenantService.GetTenantsAsync();
Tenant tenant = tenants.Find(item => item.TenantId == site.TenantId);
if (tenant != null)
{
_tenant = tenant.Name;
_connectionstring = tenant.DBConnectionString;
}
_initialized = true;
}
}
catch (Exception ex)
{
await Log(_alias, LogLevel.Error, string.Empty, ex, "Error Loading Site {SiteId} {Error}", _alias.SiteId, ex.Message);
AddModuleMessage(ex.Message, MessageType.Error);
}
}
private async void ThemeChanged(ChangeEventArgs e)
{
try
{
_themetype = (string)e.Value;
if (_themetype != "-")
{
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
}
else
{
_containers = new List<ThemeControl>();
}
_containertype = "-";
_admincontainertype = "";
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Pane Layouts For Theme {ThemeType} {Error}", _themetype, ex.Message);
AddModuleMessage(Localizer["Error Loading Pane Layouts For Theme"], MessageType.Error);
}
}
private async Task SaveSite()
{
try
{
if (_name != string.Empty && _urls != string.Empty && _themetype != "-" && _containertype != "-")
{
var unique = true;
foreach (string name in _urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
if (_aliasList.Exists(item => item.Name == name && item.SiteId != _alias.SiteId && item.TenantId != _alias.TenantId))
{
unique = false;
}
}
if (unique)
{
SiteService.SetAlias(_alias);
var site = await SiteService.GetSiteAsync(_alias.SiteId);
if (site != null)
{
site.Name = _name;
site.LogoFileId = null;
site.DefaultThemeType = _themetype;
site.DefaultContainerType = _containertype;
site.AdminContainerType = _admincontainertype;
site.IsDeleted = (_isdeleted == null || Boolean.Parse(_isdeleted));
site = await SiteService.UpdateSiteAsync(site);
_urls = _urls.Replace("\n", ",");
var names = _urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
foreach (Alias alias in _aliasList.Where(item => item.SiteId == site.SiteId && item.TenantId == site.TenantId).ToList())
{
if (!names.Contains(alias.Name))
{
await AliasService.DeleteAliasAsync(alias.AliasId);
}
}
foreach (string name in names)
{
if (!_aliasList.Exists(item => item.Name == name))
{
Alias alias = new Alias
{
Name = name,
TenantId = site.TenantId,
SiteId = site.SiteId
};
await AliasService.AddAliasAsync(alias);
}
}
await Log(_alias, LogLevel.Information, PermissionNames.Edit, null, "Site Saved {Site}", site);
NavigationManager.NavigateTo(NavigateUrl());
}
}
else
{
AddModuleMessage(Localizer["An Alias Specified Has Already Been Used For Another Site"], MessageType.Warning);
}
}
else
{
AddModuleMessage(Localizer["You Must Provide A Site Name, Alias, And Default Theme/Container"], MessageType.Warning);
}
}
catch (Exception ex)
{
await Log(_alias, LogLevel.Error, string.Empty, ex, "Error Saving Site {SiteId} {Error}", _alias.SiteId, ex.Message);
AddModuleMessage(Localizer["Error Saving Site"], MessageType.Error);
}
}
}

View File

@ -15,13 +15,11 @@ else
<Pager Items="@_sites"> <Pager Items="@_sites">
<Header> <Header>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th> <th style="width: 1px;">&nbsp;</th>
<th>@Localizer["Name"]</th> <th>@Localizer["Name"]</th>
</Header> </Header>
<Row> <Row>
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.AliasId.ToString())" ResourceKey="EditSite" /></td> <td><NavLink class="btn btn-primary" href="@(_scheme + context.Name +"/admin/site")">@Localizer["Edit"]</NavLink></td>
<td><ActionDialog Header="Delete Site" Message="@Localizer["Are You Sure You Wish To Delete The {0} Site?", context.Name]" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteSite(context))" ResourceKey="DeleteSite" /></td>
<td><a href="@(_scheme + context.Name +"?reload")">@context.Name</a></td> <td><a href="@(_scheme + context.Name +"?reload")">@context.Name</a></td>
</Row> </Row>
</Pager> </Pager>
@ -48,34 +46,4 @@ else
} }
} }
} }
private async Task DeleteSite(Alias alias)
{
try
{
if (alias.SiteId != PageState.Site.SiteId || alias.TenantId != PageState.Site.TenantId)
{
SiteService.SetAlias(alias);
await SiteService.DeleteSiteAsync(alias.SiteId);
await Log(alias, LogLevel.Information, "", null, "Site Deleted {SiteId}", alias.SiteId);
var aliases = await AliasService.GetAliasesAsync();
foreach (Alias a in aliases.Where(item => item.SiteId == alias.SiteId && item.TenantId == alias.TenantId))
{
await AliasService.DeleteAliasAsync(a.AliasId);
}
NavigationManager.NavigateTo(NavigateUrl());
}
else
{
AddModuleMessage(Localizer["You Can Not Delete The Current Site"], MessageType.Warning);
}
}
catch (Exception ex)
{
await Log(alias, LogLevel.Error, "", ex, "Error Deleting Site {SiteId} {Error}", alias.SiteId, ex.Message);
AddModuleMessage(Localizer["Error Deleting Site"], MessageType.Error);
}
}
} }

View File

@ -51,7 +51,7 @@
if (htmltext != null) if (htmltext != null)
{ {
_content = htmltext.Content; _content = htmltext.Content;
_content = _content.Replace(Constants.ContentUrl, "/" + PageState.Alias.AliasId.ToString() + Constants.ContentUrl); _content = _content.Replace(Constants.ContentUrl, "/" + PageState.Alias.Path + Constants.ContentUrl);
_createdby = htmltext.CreatedBy; _createdby = htmltext.CreatedBy;
_createdon = htmltext.CreatedOn; _createdon = htmltext.CreatedOn;
_modifiedby = htmltext.ModifiedBy; _modifiedby = htmltext.ModifiedBy;
@ -72,7 +72,7 @@
private async Task SaveContent() private async Task SaveContent()
{ {
string content = await RichTextEditorHtml.GetHtml(); string content = await RichTextEditorHtml.GetHtml();
content = content.Replace("/" + PageState.Alias.AliasId.ToString() + Constants.ContentUrl, Constants.ContentUrl); content = content.Replace("/" + PageState.Alias.Path + Constants.ContentUrl, Constants.ContentUrl);
try try
{ {

View File

@ -26,7 +26,7 @@
if (htmltext != null) if (htmltext != null)
{ {
content = htmltext.Content; content = htmltext.Content;
content = content.Replace(Constants.ContentUrl, "/" + PageState.Alias.AliasId.ToString() + Constants.ContentUrl); content = content.Replace(Constants.ContentUrl, "/" + PageState.Alias.Path + Constants.ContentUrl);
} }
} }
catch (Exception ex) catch (Exception ex)

View File

@ -17,7 +17,7 @@ namespace Oqtane.Modules.HtmlText.Services
_siteState = siteState; _siteState = siteState;
} }
private string ApiUrl => CreateApiUrl(_siteState.Alias, "HtmlText"); private string ApiUrl => CreateApiUrl("HtmlText", _siteState.Alias);
public async Task<Models.HtmlText> GetHtmlTextAsync(int moduleId) public async Task<Models.HtmlText> GetHtmlTextAsync(int moduleId)
{ {

View File

@ -1,4 +1,4 @@
using Oqtane.Models; using Oqtane.Models;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Net.Http; using System.Net.Http;
using System.Linq; using System.Linq;
@ -19,7 +19,7 @@ namespace Oqtane.Services
_siteState = siteState; _siteState = siteState;
} }
private string Apiurl => CreateApiUrl(_siteState.Alias, "Alias"); private string Apiurl => CreateApiUrl("Alias", _siteState.Alias);
public async Task<List<Alias>> GetAliasesAsync() public async Task<List<Alias>> GetAliasesAsync()
{ {
@ -32,10 +32,9 @@ namespace Oqtane.Services
return await GetJsonAsync<Alias>($"{Apiurl}/{aliasId}"); return await GetJsonAsync<Alias>($"{Apiurl}/{aliasId}");
} }
public async Task<Alias> GetAliasAsync(string name, DateTime lastSyncDate) public async Task<Alias> GetAliasAsync(string path, DateTime lastSyncDate)
{ {
name = (string.IsNullOrEmpty(name)) ? "~" : name; return await GetJsonAsync<Alias>($"{Apiurl}/name/?path={WebUtility.UrlEncode(path)}&sync={lastSyncDate.ToString("yyyyMMddHHmmssfff")}");
return await GetJsonAsync<Alias>($"{Apiurl}/name/{WebUtility.UrlEncode(name)}?sync={lastSyncDate.ToString("yyyyMMddHHmmssfff")}");
} }
public async Task<Alias> AddAliasAsync(Alias alias) public async Task<Alias> AddAliasAsync(Alias alias)

View File

@ -0,0 +1,28 @@
using Oqtane.Models;
using System.Threading.Tasks;
using System.Net.Http;
using System.Linq;
using System.Collections.Generic;
using Oqtane.Shared;
namespace Oqtane.Services
{
public class DatabaseService : ServiceBase, IDatabaseService
{
private readonly SiteState _siteState;
public DatabaseService(HttpClient http, SiteState siteState) : base(http)
{
_siteState = siteState;
}
private string Apiurl => CreateApiUrl("Database", _siteState.Alias);
public async Task<List<Database>> GetDatabasesAsync()
{
List<Database> databases = await GetJsonAsync<List<Database>>(Apiurl);
return databases.OrderBy(item => item.FriendlyName).ToList();
}
}
}

View File

@ -21,7 +21,7 @@ namespace Oqtane.Services
_jsRuntime = jsRuntime; _jsRuntime = jsRuntime;
} }
private string Apiurl => CreateApiUrl(_siteState.Alias, "File"); private string Apiurl => CreateApiUrl("File", _siteState.Alias);
public async Task<List<File>> GetFilesAsync(int folderId) public async Task<List<File>> GetFilesAsync(int folderId)
{ {

View File

@ -19,7 +19,7 @@ namespace Oqtane.Services
_siteState = siteState; _siteState = siteState;
} }
private string ApiUrl => CreateApiUrl(_siteState.Alias, "Folder"); private string ApiUrl => CreateApiUrl("Folder", _siteState.Alias);
public async Task<List<Folder>> GetFoldersAsync(int siteId) public async Task<List<Folder>> GetFoldersAsync(int siteId)
{ {

View File

@ -7,9 +7,14 @@ namespace Oqtane.Services
{ {
public class InstallationService : ServiceBase, IInstallationService public class InstallationService : ServiceBase, IInstallationService
{ {
public InstallationService(HttpClient http):base(http) { } private readonly SiteState _siteState;
private string ApiUrl => CreateApiUrl("Installation"); public InstallationService(HttpClient http, SiteState siteState) : base(http)
{
_siteState = siteState;
}
private string ApiUrl => CreateApiUrl("Installation", _siteState.Alias);
public async Task<Installation> IsInstalled() public async Task<Installation> IsInstalled()
{ {

View File

@ -0,0 +1,11 @@
using Oqtane.Models;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Oqtane.Services
{
public interface IDatabaseService
{
Task<List<Database>> GetDatabasesAsync();
}
}

View File

@ -1,4 +1,4 @@
using Oqtane.Models; using Oqtane.Models;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -8,5 +8,6 @@ namespace Oqtane.Services
{ {
Task<List<Package>> GetPackagesAsync(string tag); Task<List<Package>> GetPackagesAsync(string tag);
Task DownloadPackageAsync(string packageId, string version, string folder); Task DownloadPackageAsync(string packageId, string version, string folder);
Task InstallPackagesAsync();
} }
} }

View File

@ -1,4 +1,5 @@
using Oqtane.Models; using Oqtane.Models;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -6,8 +7,6 @@ namespace Oqtane.Services
{ {
public interface ISiteService public interface ISiteService
{ {
void SetAlias(Alias alias);
Task<List<Site>> GetSitesAsync(); Task<List<Site>> GetSitesAsync();
Task<Site> GetSiteAsync(int siteId); Task<Site> GetSiteAsync(int siteId);
@ -17,5 +16,8 @@ namespace Oqtane.Services
Task<Site> UpdateSiteAsync(Site site); Task<Site> UpdateSiteAsync(Site site);
Task DeleteSiteAsync(int siteId); Task DeleteSiteAsync(int siteId);
[Obsolete("This method is deprecated.", false)]
void SetAlias(Alias alias);
} }
} }

View File

@ -1,4 +1,4 @@
using Oqtane.Models; using Oqtane.Models;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Net.Http; using System.Net.Http;
using System.Linq; using System.Linq;
@ -16,7 +16,7 @@ namespace Oqtane.Services
_siteState = siteState; _siteState = siteState;
} }
private string Apiurl => CreateApiUrl(_siteState.Alias, "JobLog"); private string Apiurl => CreateApiUrl("JobLog", _siteState.Alias);
public async Task<List<JobLog>> GetJobLogsAsync() public async Task<List<JobLog>> GetJobLogsAsync()
{ {

View File

@ -1,4 +1,4 @@
using Oqtane.Models; using Oqtane.Models;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Net.Http; using System.Net.Http;
using System.Linq; using System.Linq;
@ -16,7 +16,7 @@ namespace Oqtane.Services
_siteState = siteState; _siteState = siteState;
} }
private string Apiurl => CreateApiUrl(_siteState.Alias, "Job"); private string Apiurl => CreateApiUrl("Job", _siteState.Alias);
public async Task<List<Job>> GetJobsAsync() public async Task<List<Job>> GetJobsAsync()
{ {

View File

@ -17,7 +17,7 @@ namespace Oqtane.Services
_siteState = siteState; _siteState = siteState;
} }
private string Apiurl => CreateApiUrl(_siteState.Alias, "Language"); private string Apiurl => CreateApiUrl("Language", _siteState.Alias);
public async Task<List<Language>> GetLanguagesAsync(int siteId) public async Task<List<Language>> GetLanguagesAsync(int siteId)
{ {

View File

@ -15,7 +15,7 @@ namespace Oqtane.Services
_siteState = siteState; _siteState = siteState;
} }
private string Apiurl => CreateApiUrl(_siteState.Alias, "Localization"); private string Apiurl => CreateApiUrl("Localization", _siteState.Alias);
public async Task<IEnumerable<Culture>> GetCulturesAsync() => await GetJsonAsync<IEnumerable<Culture>>(Apiurl); public async Task<IEnumerable<Culture>> GetCulturesAsync() => await GetJsonAsync<IEnumerable<Culture>>(Apiurl);
} }

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net.Http; using System.Net.Http;
using System.Text.Json; using System.Text.Json;
@ -23,7 +23,7 @@ namespace Oqtane.Services
_navigationManager = navigationManager; _navigationManager = navigationManager;
} }
private string Apiurl => CreateApiUrl(_siteState.Alias, "Log"); private string Apiurl => CreateApiUrl("Log", _siteState.Alias);
public async Task<List<Log>> GetLogsAsync(int siteId, string level, string function, int rows) public async Task<List<Log>> GetLogsAsync(int siteId, string level, string function, int rows)
{ {
@ -49,7 +49,6 @@ namespace Oqtane.Services
} }
else else
{ {
base.Alias = alias;
log.SiteId = alias.SiteId; log.SiteId = alias.SiteId;
} }
log.PageId = pageId; log.PageId = pageId;

View File

@ -21,7 +21,7 @@ namespace Oqtane.Services
_siteState = siteState; _siteState = siteState;
} }
private string Apiurl => CreateApiUrl(_siteState.Alias, "ModuleDefinition"); private string Apiurl => CreateApiUrl("ModuleDefinition", _siteState.Alias);
public async Task<List<ModuleDefinition>> GetModuleDefinitionsAsync(int siteId) public async Task<List<ModuleDefinition>> GetModuleDefinitionsAsync(int siteId)
{ {

View File

@ -1,4 +1,4 @@
using Oqtane.Models; using Oqtane.Models;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
@ -17,7 +17,7 @@ namespace Oqtane.Services
_siteState = siteState; _siteState = siteState;
} }
private string Apiurl => CreateApiUrl(_siteState.Alias, "Module"); private string Apiurl => CreateApiUrl("Module", _siteState.Alias);
public async Task<List<Module>> GetModulesAsync(int siteId) public async Task<List<Module>> GetModulesAsync(int siteId)
{ {

View File

@ -1,4 +1,4 @@
using Oqtane.Models; using Oqtane.Models;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Net.Http; using System.Net.Http;
using Oqtane.Shared; using Oqtane.Shared;
@ -16,7 +16,7 @@ namespace Oqtane.Services
_siteState = siteState; _siteState = siteState;
} }
private string Apiurl => CreateApiUrl(_siteState.Alias, "Notification"); private string Apiurl => CreateApiUrl("Notification", _siteState.Alias);
public async Task<List<Notification>> GetNotificationsAsync(int siteId, string direction, int userId) public async Task<List<Notification>> GetNotificationsAsync(int siteId, string direction, int userId)
{ {

View File

@ -1,16 +1,21 @@
using Oqtane.Models; using Oqtane.Models;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Linq; using System.Linq;
using Oqtane.Shared;
namespace Oqtane.Services namespace Oqtane.Services
{ {
public class PackageService : ServiceBase, IPackageService public class PackageService : ServiceBase, IPackageService
{ {
public PackageService(HttpClient http) : base(http) { } private readonly SiteState _siteState;
private string Apiurl => CreateApiUrl("Package"); public PackageService(HttpClient http, SiteState siteState) : base(http)
{
_siteState = siteState;
}
private string Apiurl => CreateApiUrl("Package", _siteState.Alias);
public async Task<List<Package>> GetPackagesAsync(string tag) public async Task<List<Package>> GetPackagesAsync(string tag)
{ {
@ -22,5 +27,10 @@ namespace Oqtane.Services
{ {
await PostAsync($"{Apiurl}?packageid={packageId}&version={version}&folder={folder}"); await PostAsync($"{Apiurl}?packageid={packageId}&version={version}&folder={folder}");
} }
public async Task InstallPackagesAsync()
{
await GetJsonAsync<List<string>>($"{Apiurl}/install");
}
} }
} }

View File

@ -1,4 +1,4 @@
using Oqtane.Models; using Oqtane.Models;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using Oqtane.Shared; using Oqtane.Shared;
@ -15,7 +15,7 @@ namespace Oqtane.Services
_siteState = siteState; _siteState = siteState;
} }
private string Apiurl => CreateApiUrl(_siteState.Alias, "PageModule"); private string Apiurl => CreateApiUrl("PageModule", _siteState.Alias);
public async Task<PageModule> GetPageModuleAsync(int pageModuleId) public async Task<PageModule> GetPageModuleAsync(int pageModuleId)
{ {

View File

@ -1,4 +1,4 @@
using Oqtane.Models; using Oqtane.Models;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
@ -20,7 +20,7 @@ namespace Oqtane.Services
_siteState = siteState; _siteState = siteState;
} }
private string Apiurl => CreateApiUrl(_siteState.Alias, "Page"); private string Apiurl => CreateApiUrl("Page", _siteState.Alias);
public async Task<List<Page>> GetPagesAsync(int siteId) public async Task<List<Page>> GetPagesAsync(int siteId)
{ {

View File

@ -1,4 +1,4 @@
using Oqtane.Models; using Oqtane.Models;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Net.Http; using System.Net.Http;
using System.Linq; using System.Linq;
@ -17,7 +17,7 @@ namespace Oqtane.Services
_siteState = siteState; _siteState = siteState;
} }
private string Apiurl => CreateApiUrl(_siteState.Alias, "Profile"); private string Apiurl => CreateApiUrl("Profile", _siteState.Alias);
public async Task<List<Profile>> GetProfilesAsync(int siteId) public async Task<List<Profile>> GetProfilesAsync(int siteId)
{ {

View File

@ -18,7 +18,7 @@ namespace Oqtane.Services
_siteState = siteState; _siteState = siteState;
} }
private string Apiurl => CreateApiUrl(_siteState.Alias, "Role"); private string Apiurl => CreateApiUrl("Role", _siteState.Alias);
public async Task<List<Role>> GetRolesAsync(int siteId) public async Task<List<Role>> GetRolesAsync(int siteId)
{ {

View File

@ -5,6 +5,7 @@ using System.Net.Http.Json;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Oqtane.Models; using Oqtane.Models;
using Oqtane.Shared;
namespace Oqtane.Services namespace Oqtane.Services
{ {
@ -17,6 +18,55 @@ namespace Oqtane.Services
_http = client; _http = client;
} }
public string CreateApiUrl(string serviceName, Alias alias)
{
return CreateApiUrl(serviceName, alias, ControllerRoutes.ApiRoute);
}
public string CreateApiUrl(string serviceName, Alias alias, string routeTemplate)
{
string apiurl = "/";
if (routeTemplate == ControllerRoutes.ApiRoute)
{
if (alias != null && !string.IsNullOrEmpty(alias.Path))
{
// include the alias path for multi-tenant context
apiurl += alias.Path + "/";
}
}
else
{
// legacy support for ControllerRoutes.Default
if (alias != null)
{
// include the alias for multi-tenant context
apiurl += $"{alias.AliasId}/";
}
else
{
// tenant agnostic
apiurl += "~/";
}
}
apiurl += $"api/{serviceName}";
return apiurl;
}
// add entityid parameter to url for custom authorization policy
public string CreateAuthorizationPolicyUrl(string url, int entityId)
{
string qs = "entityid=" + entityId.ToString();
if (url.Contains("?"))
{
return url + "&" + qs;
}
else
{
return url + "?" + qs;
}
}
protected async Task GetAsync(string uri) protected async Task GetAsync(string uri)
{ {
var response = await _http.GetAsync(uri); var response = await _http.GetAsync(uri);
@ -135,61 +185,26 @@ namespace Oqtane.Services
//TODO Missing content JSON validation //TODO Missing content JSON validation
} }
// create an API Url which is tenant agnostic ( for use during installation )
public string CreateApiUrl(string serviceName)
{
return CreateApiUrl(null, serviceName);
}
// create an API Url which is tenant aware ( for use with repositories )
public string CreateApiUrl(Alias alias, string serviceName)
{
string apiurl = "/";
if (Alias != null)
{
alias = Alias; // override the default alias ( for cross-tenant service calls )
}
if (alias != null)
{
// include the alias for multi-tenant context
apiurl += $"{alias.AliasId}/";
}
else
{
// tenant agnostic
apiurl += "~/";
}
apiurl += $"api/{serviceName}";
return apiurl;
}
// can be used to override the default alias
public Alias Alias { get; set; }
// add entityid parameter to url for custom authorization policy
public string CreateAuthorizationPolicyUrl(string url, int entityId)
{
string qs = "entityid=" + entityId.ToString();
if (url.Contains("?"))
{
return url + "&" + qs;
}
else
{
return url + "?" + qs;
}
}
[Obsolete("This method is obsolete. Use CreateApiUrl(Alias alias, string serviceName) instead.", false)] [Obsolete("This method is obsolete. Use CreateApiUrl(Alias alias, string serviceName) instead.", false)]
public string CreateApiUrl(Alias alias, string absoluteUri, string serviceName) public string CreateApiUrl(Alias alias, string absoluteUri, string serviceName)
{ {
// only retained for short term backward compatibility // only retained for short term backward compatibility
return CreateApiUrl(alias, serviceName); return CreateApiUrl(alias, serviceName);
} }
[Obsolete("This method is obsolete. Use CreateApiUrl(string serviceName, Alias alias) instead.", false)]
public string CreateApiUrl(string serviceName)
{
return CreateApiUrl(serviceName, null, ControllerRoutes.Default);
}
[Obsolete("This method is deprecated.", false)]
public Alias Alias { get; set; }
[Obsolete("This method is obsolete. Use CreateApiUrl(string serviceName, Alias alias) instead.", false)]
public string CreateApiUrl(Alias alias, string serviceName)
{
return CreateApiUrl(serviceName, alias, ControllerRoutes.Default);
}
} }
} }

View File

@ -1,4 +1,4 @@
using System; using System;
using Oqtane.Models; using Oqtane.Models;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Net.Http; using System.Net.Http;
@ -18,7 +18,7 @@ namespace Oqtane.Services
_siteState = siteState; _siteState = siteState;
} }
private string Apiurl => CreateApiUrl(_siteState.Alias, "Setting"); private string Apiurl => CreateApiUrl("Setting", _siteState.Alias);
public async Task<Dictionary<string, string>> GetTenantSettingsAsync() public async Task<Dictionary<string, string>> GetTenantSettingsAsync()
{ {
return await GetSettingsAsync(EntityNames.Tenant, -1); return await GetSettingsAsync(EntityNames.Tenant, -1);

View File

@ -1,9 +1,10 @@
using Oqtane.Models; using Oqtane.Models;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Net.Http; using System.Net.Http;
using System.Linq; using System.Linq;
using System.Collections.Generic; using System.Collections.Generic;
using Oqtane.Shared; using Oqtane.Shared;
using System;
namespace Oqtane.Services namespace Oqtane.Services
{ {
@ -18,12 +19,7 @@ namespace Oqtane.Services
_siteState = siteState; _siteState = siteState;
} }
private string Apiurl => CreateApiUrl(_siteState.Alias, "Site"); private string Apiurl => CreateApiUrl("Site", _siteState.Alias);
public void SetAlias(Alias alias)
{
base.Alias = alias;
}
public async Task<List<Site>> GetSitesAsync() public async Task<List<Site>> GetSitesAsync()
{ {
@ -50,5 +46,11 @@ namespace Oqtane.Services
{ {
await DeleteAsync($"{Apiurl}/{siteId}"); await DeleteAsync($"{Apiurl}/{siteId}");
} }
[Obsolete("This method is deprecated.", false)]
public void SetAlias(Alias alias)
{
base.Alias = alias;
}
} }
} }

View File

@ -1,4 +1,5 @@
using Oqtane.Models; using Oqtane.Models;
using Oqtane.Shared;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
@ -8,9 +9,13 @@ namespace Oqtane.Services
{ {
public class SiteTemplateService : ServiceBase, ISiteTemplateService public class SiteTemplateService : ServiceBase, ISiteTemplateService
{ {
public SiteTemplateService(HttpClient http) : base(http) { } private readonly SiteState _siteState;
private string Apiurl => CreateApiUrl("SiteTemplate"); public SiteTemplateService(HttpClient http, SiteState siteState) : base(http)
{
_siteState = siteState;
}
private string Apiurl => CreateApiUrl("SiteTemplate", _siteState.Alias);
public async Task<List<SiteTemplate>> GetSiteTemplatesAsync() public async Task<List<SiteTemplate>> GetSiteTemplatesAsync()
{ {

View File

@ -1,4 +1,4 @@
using Oqtane.Models; using Oqtane.Models;
using Oqtane.Shared; using Oqtane.Shared;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -14,7 +14,7 @@ namespace Oqtane.Services
_siteState = siteState; _siteState = siteState;
} }
private string Apiurl => CreateApiUrl(_siteState.Alias, "Sql"); private string Apiurl => CreateApiUrl("Sql", _siteState.Alias);
public async Task<SqlQuery> ExecuteQueryAsync(SqlQuery sqlquery) public async Task<SqlQuery> ExecuteQueryAsync(SqlQuery sqlquery)
{ {

View File

@ -1,14 +1,20 @@
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Collections.Generic; using System.Collections.Generic;
using Oqtane.Shared;
namespace Oqtane.Services namespace Oqtane.Services
{ {
public class SystemService : ServiceBase, ISystemService public class SystemService : ServiceBase, ISystemService
{ {
public SystemService(HttpClient http) : base(http) { } private readonly SiteState _siteState;
private string Apiurl => CreateApiUrl("System"); public SystemService(HttpClient http, SiteState siteState) : base(http)
{
_siteState = siteState;
}
private string Apiurl => CreateApiUrl("System", _siteState.Alias);
public async Task<Dictionary<string, string>> GetSystemInfoAsync() public async Task<Dictionary<string, string>> GetSystemInfoAsync()
{ {

View File

@ -1,4 +1,4 @@
using Oqtane.Models; using Oqtane.Models;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Collections.Generic; using System.Collections.Generic;
@ -16,7 +16,7 @@ namespace Oqtane.Services
_siteState = siteState; _siteState = siteState;
} }
private string Apiurl => CreateApiUrl(_siteState.Alias, "Tenant"); private string Apiurl => CreateApiUrl("Tenant", _siteState.Alias);
public async Task<List<Tenant>> GetTenantsAsync() public async Task<List<Tenant>> GetTenantsAsync()
{ {

View File

@ -16,7 +16,7 @@ namespace Oqtane.Services
_siteState = siteState; _siteState = siteState;
} }
private string ApiUrl => CreateApiUrl(_siteState.Alias, "Theme"); private string ApiUrl => CreateApiUrl("Theme", _siteState.Alias);
public async Task<List<Theme>> GetThemesAsync() public async Task<List<Theme>> GetThemesAsync()
{ {

View File

@ -1,4 +1,4 @@
using Oqtane.Models; using Oqtane.Models;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -16,7 +16,7 @@ namespace Oqtane.Services
_siteState = siteState; _siteState = siteState;
} }
private string Apiurl => CreateApiUrl(_siteState.Alias, "UserRole"); private string Apiurl => CreateApiUrl("UserRole", _siteState.Alias);
public async Task<List<UserRole>> GetUserRolesAsync(int siteId) public async Task<List<UserRole>> GetUserRolesAsync(int siteId)
{ {

View File

@ -14,7 +14,7 @@ namespace Oqtane.Services
_siteState = siteState; _siteState = siteState;
} }
private string Apiurl => CreateApiUrl(_siteState.Alias, "User"); private string Apiurl => CreateApiUrl("User", _siteState.Alias);
public async Task<User> GetUserAsync(int userId, int siteId) public async Task<User> GetUserAsync(int userId, int siteId)
{ {

View File

@ -39,7 +39,9 @@ namespace Oqtane.Themes.Controls
var interop = new Interop(jsRuntime); var interop = new Interop(jsRuntime);
string antiforgerytoken = await interop.GetElementByName("__RequestVerificationToken"); string antiforgerytoken = await interop.GetElementByName("__RequestVerificationToken");
var fields = new { __RequestVerificationToken = antiforgerytoken, returnurl = !authorizedtoviewpage ? PageState.Alias.Path : PageState.Alias.Path + "/" + PageState.Page.Path }; var fields = new { __RequestVerificationToken = antiforgerytoken, returnurl = !authorizedtoviewpage ? PageState.Alias.Path : PageState.Alias.Path + "/" + PageState.Page.Path };
await interop.SubmitForm($"/{PageState.Alias.AliasId}/pages/logout/", fields); string url = "/pages/logout/";
if (!string.IsNullOrEmpty(PageState.Alias.Path)) url = "/" + PageState.Alias.Path + url;
await interop.SubmitForm(url, fields);
} }
else else
{ {

View File

@ -14,7 +14,7 @@ using Microsoft.AspNetCore.Http;
namespace Oqtane.Controllers namespace Oqtane.Controllers
{ {
[Route(ControllerRoutes.Default)] [Route(ControllerRoutes.ApiRoute)]
public class AliasController : Controller public class AliasController : Controller
{ {
private readonly IAliasRepository _aliases; private readonly IAliasRepository _aliases;
@ -46,17 +46,16 @@ namespace Oqtane.Controllers
return _aliases.GetAlias(id); return _aliases.GetAlias(id);
} }
// GET api/<controller>/name/xxx?sync=yyyyMMddHHmmssfff // GET api/<controller>/name/?path=xxx&sync=yyyyMMddHHmmssfff
[HttpGet("name/{**name}")] [HttpGet("name")]
public Alias Get(string name, string sync) public Alias Get(string path, string sync)
{ {
Alias alias = null; Alias alias = null;
if (_accessor.HttpContext != null) if (_accessor.HttpContext != null)
{ {
name = (name == "~") ? "" : name; path = _accessor.HttpContext.Request.Host.Value + "/" + WebUtility.UrlDecode(path);
name = _accessor.HttpContext.Request.Host.Value + "/" + WebUtility.UrlDecode(name); alias = _aliases.GetAlias(path);
alias = _aliases.GetAlias(name);
} }
// get sync events // get sync events

View File

@ -0,0 +1,52 @@
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using Oqtane.Shared;
namespace Oqtane.Controllers
{
[Route(ControllerRoutes.ApiRoute)]
public class DatabaseController : Controller
{
public DatabaseController() { }
// GET: api/<controller>
[HttpGet]
public IEnumerable<Models.Database> Get()
{
var databases = new List<Models.Database>
{
new()
{
Name = "LocalDB",
FriendlyName = "Local Database",
Type = "Oqtane.Installer.Controls.LocalDBConfig, Oqtane.Client"
},
new()
{
Name = "SqlServer",
FriendlyName = "SQL Server",
Type = "Oqtane.Installer.Controls.SqlServerConfig, Oqtane.Client"
},
new()
{
Name = "Sqlite",
FriendlyName = "Sqlite",
Type = "Oqtane.Installer.Controls.SqliteConfig, Oqtane.Client"
},
new()
{
Name = "MySQL",
FriendlyName = "MySQL",
Type = "Oqtane.Installer.Controls.MySQLConfig, Oqtane.Client"
},
new()
{
Name = "PostgreSQL",
FriendlyName = "PostgreSQL",
Type = "Oqtane.Installer.Controls.PostGreSQLConfig, Oqtane.Client"
}
};
return databases;
}
}
}

View File

@ -16,30 +16,27 @@ using System.Net;
using Oqtane.Enums; using Oqtane.Enums;
using Oqtane.Infrastructure; using Oqtane.Infrastructure;
using Oqtane.Repository; using Oqtane.Repository;
using Microsoft.AspNetCore.Routing.Constraints;
using Oqtane.Extensions; using Oqtane.Extensions;
// ReSharper disable StringIndexOfIsCultureSpecific.1 // ReSharper disable StringIndexOfIsCultureSpecific.1
namespace Oqtane.Controllers namespace Oqtane.Controllers
{ {
[Route(ControllerRoutes.Default)] [Route(ControllerRoutes.ApiRoute)]
public class FileController : Controller public class FileController : Controller
{ {
private readonly IWebHostEnvironment _environment; private readonly IWebHostEnvironment _environment;
private readonly IFileRepository _files; private readonly IFileRepository _files;
private readonly IFolderRepository _folders; private readonly IFolderRepository _folders;
private readonly IUserPermissions _userPermissions; private readonly IUserPermissions _userPermissions;
private readonly ITenantResolver _tenants;
private readonly ILogManager _logger; private readonly ILogManager _logger;
public FileController(IWebHostEnvironment environment, IFileRepository files, IFolderRepository folders, IUserPermissions userPermissions, ITenantResolver tenants, ILogManager logger) public FileController(IWebHostEnvironment environment, IFileRepository files, IFolderRepository folders, IUserPermissions userPermissions, ILogManager logger)
{ {
_environment = environment; _environment = environment;
_files = files; _files = files;
_folders = folders; _folders = folders;
_userPermissions = userPermissions; _userPermissions = userPermissions;
_tenants = tenants;
_logger = logger; _logger = logger;
} }

View File

@ -15,21 +15,21 @@ using Microsoft.AspNetCore.Hosting;
namespace Oqtane.Controllers namespace Oqtane.Controllers
{ {
[Route(ControllerRoutes.Default)] [Route(ControllerRoutes.ApiRoute)]
public class FolderController : Controller public class FolderController : Controller
{ {
private readonly IWebHostEnvironment _environment; private readonly IWebHostEnvironment _environment;
private readonly IFolderRepository _folders; private readonly IFolderRepository _folders;
private readonly IUserPermissions _userPermissions; private readonly IUserPermissions _userPermissions;
private readonly ITenantResolver _tenants; private readonly ITenantManager _tenantManager;
private readonly ILogManager _logger; private readonly ILogManager _logger;
public FolderController(IWebHostEnvironment environment, IFolderRepository folders, IUserPermissions userPermissions, ITenantResolver tenants, ILogManager logger) public FolderController(IWebHostEnvironment environment, IFolderRepository folders, IUserPermissions userPermissions, ITenantManager tenantManager, ILogManager logger)
{ {
_environment = environment; _environment = environment;
_folders = folders; _folders = folders;
_userPermissions = userPermissions; _userPermissions = userPermissions;
_tenants = tenants; _tenantManager = tenantManager;
_logger = logger; _logger = logger;
} }
@ -235,7 +235,7 @@ namespace Oqtane.Controllers
private string GetFolderPath(Folder folder) private string GetFolderPath(Folder folder)
{ {
return Utilities.PathCombine(_environment.ContentRootPath, "Content", "Tenants", _tenants.GetTenant().TenantId.ToString(), "Sites", folder.SiteId.ToString(), folder.Path); return Utilities.PathCombine(_environment.ContentRootPath, "Content", "Tenants", _tenantManager.GetTenant().TenantId.ToString(), "Sites", folder.SiteId.ToString(), folder.Path);
} }
} }
} }

View File

@ -12,11 +12,10 @@ using Oqtane.Modules;
using Oqtane.Shared; using Oqtane.Shared;
using Oqtane.Themes; using Oqtane.Themes;
using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Caching.Memory;
using System.Collections.Generic;
namespace Oqtane.Controllers namespace Oqtane.Controllers
{ {
[Route(ControllerRoutes.Default)] [Route(ControllerRoutes.ApiRoute)]
public class InstallationController : Controller public class InstallationController : Controller
{ {
private readonly IConfigurationRoot _config; private readonly IConfigurationRoot _config;

View File

@ -1,4 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Oqtane.Models; using Oqtane.Models;
@ -12,7 +12,7 @@ using Oqtane.Repository;
namespace Oqtane.Controllers namespace Oqtane.Controllers
{ {
[Route(ControllerRoutes.Default)] [Route(ControllerRoutes.ApiRoute)]
public class JobController : Controller public class JobController : Controller
{ {
private readonly IJobRepository _jobs; private readonly IJobRepository _jobs;

View File

@ -1,4 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Oqtane.Enums; using Oqtane.Enums;
@ -9,7 +9,7 @@ using Oqtane.Repository;
namespace Oqtane.Controllers namespace Oqtane.Controllers
{ {
[Route(ControllerRoutes.Default)] [Route(ControllerRoutes.ApiRoute)]
public class JobLogController : Controller public class JobLogController : Controller
{ {
private readonly IJobLogRepository _jobLogs; private readonly IJobLogRepository _jobLogs;

View File

@ -9,7 +9,7 @@ using Oqtane.Shared;
namespace Oqtane.Controllers namespace Oqtane.Controllers
{ {
[Route(ControllerRoutes.Default)] [Route(ControllerRoutes.ApiRoute)]
public class LanguageController : Controller public class LanguageController : Controller
{ {
private readonly ILanguageRepository _languages; private readonly ILanguageRepository _languages;

View File

@ -9,7 +9,7 @@ using Oqtane.Shared;
namespace Oqtane.Controllers namespace Oqtane.Controllers
{ {
[Route(ControllerRoutes.Default)] [Route(ControllerRoutes.ApiRoute)]
public class LocalizationController : Controller public class LocalizationController : Controller
{ {
private readonly ILocalizationManager _localizationManager; private readonly ILocalizationManager _localizationManager;

View File

@ -1,4 +1,4 @@
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Oqtane.Models; using Oqtane.Models;
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
@ -9,7 +9,7 @@ using Oqtane.Shared;
namespace Oqtane.Controllers namespace Oqtane.Controllers
{ {
[Route(ControllerRoutes.Default)] [Route(ControllerRoutes.ApiRoute)]
public class LogController : Controller public class LogController : Controller
{ {
private readonly ILogManager _logger; private readonly ILogManager _logger;

View File

@ -11,7 +11,7 @@ using Oqtane.Security;
namespace Oqtane.Controllers namespace Oqtane.Controllers
{ {
[Route(ControllerRoutes.Default)] [Route(ControllerRoutes.ApiRoute)]
public class ModuleController : Controller public class ModuleController : Controller
{ {
private readonly IModuleRepository _modules; private readonly IModuleRepository _modules;
@ -20,11 +20,11 @@ namespace Oqtane.Controllers
private readonly IModuleDefinitionRepository _moduleDefinitions; private readonly IModuleDefinitionRepository _moduleDefinitions;
private readonly ISettingRepository _settings; private readonly ISettingRepository _settings;
private readonly IUserPermissions _userPermissions; private readonly IUserPermissions _userPermissions;
private readonly ITenantResolver _tenants;
private readonly ISyncManager _syncManager; private readonly ISyncManager _syncManager;
private readonly ILogManager _logger; private readonly ILogManager _logger;
private readonly Alias _alias;
public ModuleController(IModuleRepository modules, IPageModuleRepository pageModules, IPageRepository pages, IModuleDefinitionRepository moduleDefinitions, ISettingRepository settings, IUserPermissions userPermissions, ITenantResolver tenants, ISyncManager syncManager, ILogManager logger) public ModuleController(IModuleRepository modules, IPageModuleRepository pageModules, IPageRepository pages, IModuleDefinitionRepository moduleDefinitions, ISettingRepository settings, IUserPermissions userPermissions, ITenantManager tenantManager, ISyncManager syncManager, ILogManager logger)
{ {
_modules = modules; _modules = modules;
_pageModules = pageModules; _pageModules = pageModules;
@ -32,9 +32,9 @@ namespace Oqtane.Controllers
_moduleDefinitions = moduleDefinitions; _moduleDefinitions = moduleDefinitions;
_settings = settings; _settings = settings;
_userPermissions = userPermissions; _userPermissions = userPermissions;
_tenants = tenants;
_syncManager = syncManager; _syncManager = syncManager;
_logger = logger; _logger = logger;
_alias = tenantManager.GetAlias();
} }
// GET: api/<controller>?siteid=x // GET: api/<controller>?siteid=x
@ -108,7 +108,7 @@ namespace Oqtane.Controllers
if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Page, module.PageId, PermissionNames.Edit)) if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Page, module.PageId, PermissionNames.Edit))
{ {
module = _modules.AddModule(module); module = _modules.AddModule(module);
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Site, _tenants.GetAlias().SiteId); _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Module Added {Module}", module); _logger.Log(LogLevel.Information, this, LogFunction.Create, "Module Added {Module}", module);
} }
else else
@ -142,7 +142,7 @@ namespace Oqtane.Controllers
} }
} }
} }
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Site, _tenants.GetAlias().SiteId); _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId);
} }
else else
{ {
@ -161,7 +161,7 @@ namespace Oqtane.Controllers
if (_userPermissions.IsAuthorized(User, EntityNames.Module, id, PermissionNames.Edit)) if (_userPermissions.IsAuthorized(User, EntityNames.Module, id, PermissionNames.Edit))
{ {
_modules.DeleteModule(id); _modules.DeleteModule(id);
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Site, _tenants.GetAlias().SiteId); _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Deleted {ModuleId}", id); _logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Deleted {ModuleId}", id);
} }
else else

View File

@ -17,7 +17,7 @@ using System.Text.Json;
namespace Oqtane.Controllers namespace Oqtane.Controllers
{ {
[Route(ControllerRoutes.Default)] [Route(ControllerRoutes.ApiRoute)]
public class ModuleDefinitionController : Controller public class ModuleDefinitionController : Controller
{ {
private readonly IModuleDefinitionRepository _moduleDefinitions; private readonly IModuleDefinitionRepository _moduleDefinitions;

View File

@ -10,7 +10,7 @@ using Oqtane.Security;
namespace Oqtane.Controllers namespace Oqtane.Controllers
{ {
[Route(ControllerRoutes.Default)] [Route(ControllerRoutes.ApiRoute)]
public class NotificationController : Controller public class NotificationController : Controller
{ {
private readonly INotificationRepository _notifications; private readonly INotificationRepository _notifications;

View File

@ -1,28 +1,33 @@
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Oqtane.Models; using Oqtane.Models;
using Newtonsoft.Json; using Newtonsoft.Json;
using System; using System;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Threading;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Oqtane.Shared; using Oqtane.Shared;
using Oqtane.Infrastructure;
using Oqtane.Enums;
// ReSharper disable PartialTypeWithSinglePart // ReSharper disable PartialTypeWithSinglePart
namespace Oqtane.Controllers namespace Oqtane.Controllers
{ {
[Route(ControllerRoutes.Default)] [Route(ControllerRoutes.ApiRoute)]
public class PackageController : Controller public class PackageController : Controller
{ {
private readonly IInstallationManager _installationManager;
private readonly IWebHostEnvironment _environment; private readonly IWebHostEnvironment _environment;
private readonly ILogManager _logger;
public PackageController(IWebHostEnvironment environment) public PackageController(IInstallationManager installationManager, IWebHostEnvironment environment, ILogManager logger)
{ {
_installationManager = installationManager;
_environment = environment; _environment = environment;
_logger = logger;
} }
// GET: api/<controller>?tag=x // GET: api/<controller>?tag=x
@ -86,6 +91,14 @@ namespace Oqtane.Controllers
} }
} }
} }
[HttpGet("install")]
[Authorize(Roles = RoleNames.Host)]
public void InstallPackages()
{
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Packages Installed");
_installationManager.InstallPackages("Packages");
}
} }
public partial class SearchResult public partial class SearchResult

View File

@ -13,7 +13,7 @@ using Oqtane.Repository;
namespace Oqtane.Controllers namespace Oqtane.Controllers
{ {
[Route(ControllerRoutes.Default)] [Route(ControllerRoutes.ApiRoute)]
public class PageController : Controller public class PageController : Controller
{ {
private readonly IPageRepository _pages; private readonly IPageRepository _pages;
@ -21,20 +21,20 @@ namespace Oqtane.Controllers
private readonly IPageModuleRepository _pageModules; private readonly IPageModuleRepository _pageModules;
private readonly ISettingRepository _settings; private readonly ISettingRepository _settings;
private readonly IUserPermissions _userPermissions; private readonly IUserPermissions _userPermissions;
private readonly ITenantResolver _tenants;
private readonly ISyncManager _syncManager; private readonly ISyncManager _syncManager;
private readonly ILogManager _logger; private readonly ILogManager _logger;
private readonly Alias _alias;
public PageController(IPageRepository pages, IModuleRepository modules, IPageModuleRepository pageModules, ISettingRepository settings, IUserPermissions userPermissions, ITenantResolver tenants, ISyncManager syncManager, ILogManager logger) public PageController(IPageRepository pages, IModuleRepository modules, IPageModuleRepository pageModules, ISettingRepository settings, IUserPermissions userPermissions, ITenantManager tenantManager, ISyncManager syncManager, ILogManager logger)
{ {
_pages = pages; _pages = pages;
_modules = modules; _modules = modules;
_pageModules = pageModules; _pageModules = pageModules;
_settings = settings; _settings = settings;
_userPermissions = userPermissions; _userPermissions = userPermissions;
_tenants = tenants;
_syncManager = syncManager; _syncManager = syncManager;
_logger = logger; _logger = logger;
_alias = tenantManager.GetAlias();
} }
// GET: api/<controller>?siteid=x // GET: api/<controller>?siteid=x
@ -132,7 +132,7 @@ namespace Oqtane.Controllers
if (_userPermissions.IsAuthorized(User,PermissionNames.Edit, permissions)) if (_userPermissions.IsAuthorized(User,PermissionNames.Edit, permissions))
{ {
page = _pages.AddPage(page); page = _pages.AddPage(page);
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Site, page.SiteId); _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, page.SiteId);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Page Added {Page}", page); _logger.Log(LogLevel.Information, this, LogFunction.Create, "Page Added {Page}", page);
if (!page.Path.StartsWith("admin/")) if (!page.Path.StartsWith("admin/"))
@ -183,7 +183,7 @@ namespace Oqtane.Controllers
page.IsPersonalizable = false; page.IsPersonalizable = false;
page.UserId = int.Parse(userid); page.UserId = int.Parse(userid);
page = _pages.AddPage(page); page = _pages.AddPage(page);
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Site, page.SiteId); _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, page.SiteId);
// copy modules // copy modules
List<PageModule> pagemodules = _pageModules.GetPageModules(page.SiteId).ToList(); List<PageModule> pagemodules = _pageModules.GetPageModules(page.SiteId).ToList();
@ -228,7 +228,7 @@ namespace Oqtane.Controllers
if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Page, page.PageId, PermissionNames.Edit)) if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Page, page.PageId, PermissionNames.Edit))
{ {
page = _pages.UpdatePage(page); page = _pages.UpdatePage(page);
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Site, page.SiteId); _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, page.SiteId);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Page Updated {Page}", page); _logger.Log(LogLevel.Information, this, LogFunction.Update, "Page Updated {Page}", page);
} }
else else
@ -258,7 +258,7 @@ namespace Oqtane.Controllers
} }
order += 2; order += 2;
} }
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Site, siteid); _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, siteid);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Page Order Updated {SiteId} {PageId} {ParentId}", siteid, pageid, parentid); _logger.Log(LogLevel.Information, this, LogFunction.Update, "Page Order Updated {SiteId} {PageId} {ParentId}", siteid, pageid, parentid);
} }
else else
@ -277,7 +277,7 @@ namespace Oqtane.Controllers
if (_userPermissions.IsAuthorized(User, EntityNames.Page, page.PageId, PermissionNames.Edit)) if (_userPermissions.IsAuthorized(User, EntityNames.Page, page.PageId, PermissionNames.Edit))
{ {
_pages.DeletePage(page.PageId); _pages.DeletePage(page.PageId);
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Site, page.SiteId); _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, page.SiteId);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Page Deleted {PageId}", page.PageId); _logger.Log(LogLevel.Information, this, LogFunction.Delete, "Page Deleted {PageId}", page.PageId);
} }
else else

View File

@ -11,22 +11,22 @@ using Oqtane.Security;
namespace Oqtane.Controllers namespace Oqtane.Controllers
{ {
[Route(ControllerRoutes.Default)] [Route(ControllerRoutes.ApiRoute)]
public class PageModuleController : Controller public class PageModuleController : Controller
{ {
private readonly IPageModuleRepository _pageModules; private readonly IPageModuleRepository _pageModules;
private readonly IUserPermissions _userPermissions; private readonly IUserPermissions _userPermissions;
private readonly ITenantResolver _tenants;
private readonly ISyncManager _syncManager; private readonly ISyncManager _syncManager;
private readonly ILogManager _logger; private readonly ILogManager _logger;
private readonly Alias _alias;
public PageModuleController(IPageModuleRepository pageModules, IUserPermissions userPermissions, ITenantResolver tenants, ISyncManager syncManager, ILogManager logger) public PageModuleController(IPageModuleRepository pageModules, IUserPermissions userPermissions, ITenantManager tenantManager, ISyncManager syncManager, ILogManager logger)
{ {
_pageModules = pageModules; _pageModules = pageModules;
_userPermissions = userPermissions; _userPermissions = userPermissions;
_tenants = tenants;
_syncManager = syncManager; _syncManager = syncManager;
_logger = logger; _logger = logger;
_alias = tenantManager.GetAlias();
} }
// GET api/<controller>/5 // GET api/<controller>/5
@ -71,7 +71,7 @@ namespace Oqtane.Controllers
if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Page, pageModule.PageId, PermissionNames.Edit)) if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Page, pageModule.PageId, PermissionNames.Edit))
{ {
pageModule = _pageModules.AddPageModule(pageModule); pageModule = _pageModules.AddPageModule(pageModule);
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Site, _tenants.GetAlias().SiteId); _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Page Module Added {PageModule}", pageModule); _logger.Log(LogLevel.Information, this, LogFunction.Create, "Page Module Added {PageModule}", pageModule);
} }
else else
@ -91,7 +91,7 @@ namespace Oqtane.Controllers
if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Module, pageModule.ModuleId, PermissionNames.Edit)) if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Module, pageModule.ModuleId, PermissionNames.Edit))
{ {
pageModule = _pageModules.UpdatePageModule(pageModule); pageModule = _pageModules.UpdatePageModule(pageModule);
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Site, _tenants.GetAlias().SiteId); _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Page Module Updated {PageModule}", pageModule); _logger.Log(LogLevel.Information, this, LogFunction.Update, "Page Module Updated {PageModule}", pageModule);
} }
else else
@ -121,7 +121,7 @@ namespace Oqtane.Controllers
} }
order += 2; order += 2;
} }
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Site, _tenants.GetAlias().SiteId); _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Page Module Order Updated {PageId} {Pane}", pageid, pane); _logger.Log(LogLevel.Information, this, LogFunction.Update, "Page Module Order Updated {PageId} {Pane}", pageid, pane);
} }
else else
@ -140,7 +140,7 @@ namespace Oqtane.Controllers
if (_userPermissions.IsAuthorized(User, EntityNames.Page, pagemodule.PageId, PermissionNames.Edit)) if (_userPermissions.IsAuthorized(User, EntityNames.Page, pagemodule.PageId, PermissionNames.Edit))
{ {
_pageModules.DeletePageModule(id); _pageModules.DeletePageModule(id);
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Site, _tenants.GetAlias().SiteId); _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Page Module Deleted {PageModuleId}", id); _logger.Log(LogLevel.Information, this, LogFunction.Delete, "Page Module Deleted {PageModuleId}", id);
} }
else else

View File

@ -1,4 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Oqtane.Enums; using Oqtane.Enums;
@ -9,7 +9,7 @@ using Oqtane.Repository;
namespace Oqtane.Controllers namespace Oqtane.Controllers
{ {
[Route(ControllerRoutes.Default)] [Route(ControllerRoutes.ApiRoute)]
public class ProfileController : Controller public class ProfileController : Controller
{ {
private readonly IProfileRepository _profiles; private readonly IProfileRepository _profiles;

View File

@ -9,7 +9,7 @@ using Oqtane.Repository;
namespace Oqtane.Controllers namespace Oqtane.Controllers
{ {
[Route(ControllerRoutes.Default)] [Route(ControllerRoutes.ApiRoute)]
public class RoleController : Controller public class RoleController : Controller
{ {
private readonly IRoleRepository _roles; private readonly IRoleRepository _roles;

View File

@ -10,24 +10,24 @@ using Oqtane.Repository;
namespace Oqtane.Controllers namespace Oqtane.Controllers
{ {
[Route(ControllerRoutes.Default)] [Route(ControllerRoutes.ApiRoute)]
public class SettingController : Controller public class SettingController : Controller
{ {
private readonly ISettingRepository _settings; private readonly ISettingRepository _settings;
private readonly IPageModuleRepository _pageModules; private readonly IPageModuleRepository _pageModules;
private readonly IUserPermissions _userPermissions; private readonly IUserPermissions _userPermissions;
private readonly ITenantResolver _tenants;
private readonly ISyncManager _syncManager; private readonly ISyncManager _syncManager;
private readonly ILogManager _logger; private readonly ILogManager _logger;
private readonly Alias _alias;
public SettingController(ISettingRepository settings, IPageModuleRepository pageModules, IUserPermissions userPermissions, ITenantResolver tenants, ISyncManager syncManager, ILogManager logger) public SettingController(ISettingRepository settings, IPageModuleRepository pageModules, IUserPermissions userPermissions, ITenantManager tenantManager, ISyncManager syncManager, ILogManager logger)
{ {
_settings = settings; _settings = settings;
_pageModules = pageModules; _pageModules = pageModules;
_userPermissions = userPermissions; _userPermissions = userPermissions;
_tenants = tenants;
_syncManager = syncManager; _syncManager = syncManager;
_logger = logger; _logger = logger;
_alias = tenantManager.GetAlias();
} }
// GET: api/<controller> // GET: api/<controller>
@ -73,7 +73,7 @@ namespace Oqtane.Controllers
setting = _settings.AddSetting(setting); setting = _settings.AddSetting(setting);
if (setting.EntityName == EntityNames.Module) if (setting.EntityName == EntityNames.Module)
{ {
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Site, _tenants.GetAlias().SiteId); _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId);
} }
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Setting Added {Setting}", setting); _logger.Log(LogLevel.Information, this, LogFunction.Create, "Setting Added {Setting}", setting);
} }
@ -95,7 +95,7 @@ namespace Oqtane.Controllers
setting = _settings.UpdateSetting(setting); setting = _settings.UpdateSetting(setting);
if (setting.EntityName == EntityNames.Module) if (setting.EntityName == EntityNames.Module)
{ {
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Site, _tenants.GetAlias().SiteId); _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId);
} }
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Setting Updated {Setting}", setting); _logger.Log(LogLevel.Information, this, LogFunction.Update, "Setting Updated {Setting}", setting);
} }
@ -118,7 +118,7 @@ namespace Oqtane.Controllers
_settings.DeleteSetting(id); _settings.DeleteSetting(id);
if (setting.EntityName == EntityNames.Module) if (setting.EntityName == EntityNames.Module)
{ {
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Site, _tenants.GetAlias().SiteId); _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId);
} }
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Setting Deleted {Setting}", setting); _logger.Log(LogLevel.Information, this, LogFunction.Delete, "Setting Deleted {Setting}", setting);
} }

View File

@ -1,4 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Oqtane.Models; using Oqtane.Models;
@ -10,20 +10,20 @@ using Oqtane.Repository;
namespace Oqtane.Controllers namespace Oqtane.Controllers
{ {
[Route(ControllerRoutes.Default)] [Route(ControllerRoutes.ApiRoute)]
public class SiteController : Controller public class SiteController : Controller
{ {
private readonly ISiteRepository _sites; private readonly ISiteRepository _sites;
private readonly ITenantResolver _tenants;
private readonly ISyncManager _syncManager; private readonly ISyncManager _syncManager;
private readonly ILogManager _logger; private readonly ILogManager _logger;
private readonly Alias _alias;
public SiteController(ISiteRepository sites, ITenantResolver tenants, ISyncManager syncManager, ILogManager logger) public SiteController(ISiteRepository sites, ITenantManager tenantManager, ISyncManager syncManager, ILogManager logger)
{ {
_sites = sites; _sites = sites;
_tenants = tenants;
_syncManager = syncManager; _syncManager = syncManager;
_logger = logger; _logger = logger;
_alias = tenantManager.GetAlias();
} }
// GET: api/<controller> // GET: api/<controller>
@ -52,8 +52,7 @@ namespace Oqtane.Controllers
{ {
// provision initial site during installation // provision initial site during installation
authorized = true; authorized = true;
Tenant tenant = _tenants.GetTenant(); site.TenantId = _alias.TenantId;
site.TenantId = tenant.TenantId;
} }
else else
{ {
@ -76,7 +75,7 @@ namespace Oqtane.Controllers
if (ModelState.IsValid) if (ModelState.IsValid)
{ {
site = _sites.UpdateSite(site); site = _sites.UpdateSite(site);
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Site, site.SiteId); _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, site.SiteId);
_logger.Log(site.SiteId, LogLevel.Information, this, LogFunction.Update, "Site Updated {Site}", site); _logger.Log(site.SiteId, LogLevel.Information, this, LogFunction.Update, "Site Updated {Site}", site);
} }
return site; return site;

View File

@ -1,4 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Oqtane.Models; using Oqtane.Models;
@ -7,7 +7,7 @@ using Oqtane.Shared;
namespace Oqtane.Controllers namespace Oqtane.Controllers
{ {
[Route(ControllerRoutes.Default)] [Route(ControllerRoutes.ApiRoute)]
public class SiteTemplateController : Controller public class SiteTemplateController : Controller
{ {
private readonly ISiteTemplateRepository _siteTemplates; private readonly ISiteTemplateRepository _siteTemplates;

View File

@ -1,4 +1,4 @@
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Oqtane.Models; using Oqtane.Models;
using System.Collections.Generic; using System.Collections.Generic;
@ -11,7 +11,7 @@ using Microsoft.Data.SqlClient;
namespace Oqtane.Controllers namespace Oqtane.Controllers
{ {
[Route(ControllerRoutes.Default)] [Route(ControllerRoutes.ApiRoute)]
public class SqlController : Controller public class SqlController : Controller
{ {
private readonly ITenantRepository _tenants; private readonly ITenantRepository _tenants;

View File

@ -1,4 +1,4 @@
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using System.Collections.Generic; using System.Collections.Generic;
using Oqtane.Shared; using Oqtane.Shared;
@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Hosting;
namespace Oqtane.Controllers namespace Oqtane.Controllers
{ {
[Route(ControllerRoutes.Default)] [Route(ControllerRoutes.ApiRoute)]
public class SystemController : Controller public class SystemController : Controller
{ {
private readonly IWebHostEnvironment _environment; private readonly IWebHostEnvironment _environment;

View File

@ -1,4 +1,4 @@
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Oqtane.Models; using Oqtane.Models;
using System.Collections.Generic; using System.Collections.Generic;
@ -9,7 +9,7 @@ using Oqtane.Repository;
namespace Oqtane.Controllers namespace Oqtane.Controllers
{ {
[Route(ControllerRoutes.Default)] [Route(ControllerRoutes.ApiRoute)]
public class TenantController : Controller public class TenantController : Controller
{ {
private readonly ITenantRepository _tenants; private readonly ITenantRepository _tenants;

View File

@ -16,7 +16,7 @@ using System.Text.Json;
namespace Oqtane.Controllers namespace Oqtane.Controllers
{ {
[Route(ControllerRoutes.Default)] [Route(ControllerRoutes.ApiRoute)]
public class ThemeController : Controller public class ThemeController : Controller
{ {
private readonly IThemeRepository _themes; private readonly IThemeRepository _themes;

View File

@ -18,7 +18,7 @@ using Oqtane.Extensions;
namespace Oqtane.Controllers namespace Oqtane.Controllers
{ {
[Route(ControllerRoutes.Default)] [Route(ControllerRoutes.ApiRoute)]
public class UserController : Controller public class UserController : Controller
{ {
private readonly IUserRepository _users; private readonly IUserRepository _users;
@ -26,26 +26,26 @@ namespace Oqtane.Controllers
private readonly IUserRoleRepository _userRoles; private readonly IUserRoleRepository _userRoles;
private readonly UserManager<IdentityUser> _identityUserManager; private readonly UserManager<IdentityUser> _identityUserManager;
private readonly SignInManager<IdentityUser> _identitySignInManager; private readonly SignInManager<IdentityUser> _identitySignInManager;
private readonly ITenantResolver _tenants;
private readonly INotificationRepository _notifications; private readonly INotificationRepository _notifications;
private readonly IFolderRepository _folders; private readonly IFolderRepository _folders;
private readonly ISyncManager _syncManager; private readonly ISyncManager _syncManager;
private readonly ISiteRepository _sites; private readonly ISiteRepository _sites;
private readonly ILogManager _logger; private readonly ILogManager _logger;
private readonly Alias _alias;
public UserController(IUserRepository users, IRoleRepository roles, IUserRoleRepository userRoles, UserManager<IdentityUser> identityUserManager, SignInManager<IdentityUser> identitySignInManager, ITenantResolver tenants, INotificationRepository notifications, IFolderRepository folders, ISyncManager syncManager, ISiteRepository sites, ILogManager logger) public UserController(IUserRepository users, IRoleRepository roles, IUserRoleRepository userRoles, UserManager<IdentityUser> identityUserManager, SignInManager<IdentityUser> identitySignInManager, ITenantManager tenantManager, INotificationRepository notifications, IFolderRepository folders, ISyncManager syncManager, ISiteRepository sites, ILogManager logger)
{ {
_users = users; _users = users;
_roles = roles; _roles = roles;
_userRoles = userRoles; _userRoles = userRoles;
_identityUserManager = identityUserManager; _identityUserManager = identityUserManager;
_identitySignInManager = identitySignInManager; _identitySignInManager = identitySignInManager;
_tenants = tenants;
_folders = folders; _folders = folders;
_notifications = notifications; _notifications = notifications;
_syncManager = syncManager; _syncManager = syncManager;
_sites = sites; _sites = sites;
_logger = logger; _logger = logger;
_alias = tenantManager.GetAlias();
} }
// GET api/<controller>/5?siteid=x // GET api/<controller>/5?siteid=x
@ -146,7 +146,7 @@ namespace Oqtane.Controllers
if (!verified) if (!verified)
{ {
string token = await _identityUserManager.GenerateEmailConfirmationTokenAsync(identityuser); string token = await _identityUserManager.GenerateEmailConfirmationTokenAsync(identityuser);
string url = HttpContext.Request.Scheme + "://" + _tenants.GetAlias().Name + "/login?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token); string url = HttpContext.Request.Scheme + "://" + _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 Complete The Registration Of Your User Account Please Click The Link Displayed Below:\n\n" + url + "\n\nThank You!";
var notification = new Notification(user.SiteId, null, newUser, "User Account Verification", body, null); var notification = new Notification(user.SiteId, null, newUser, "User Account Verification", body, null);
_notifications.AddNotification(notification); _notifications.AddNotification(notification);
@ -243,7 +243,7 @@ namespace Oqtane.Controllers
} }
} }
user = _users.UpdateUser(user); user = _users.UpdateUser(user);
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.User, user.UserId); _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.User, user.UserId);
user.Password = ""; // remove sensitive information user.Password = ""; // remove sensitive information
_logger.Log(LogLevel.Information, this, LogFunction.Update, "User Updated {User}", user); _logger.Log(LogLevel.Information, this, LogFunction.Update, "User Updated {User}", user);
} }
@ -401,7 +401,7 @@ namespace Oqtane.Controllers
if (identityuser != null) if (identityuser != null)
{ {
string token = await _identityUserManager.GeneratePasswordResetTokenAsync(identityuser); string token = await _identityUserManager.GeneratePasswordResetTokenAsync(identityuser);
string url = HttpContext.Request.Scheme + "://" + _tenants.GetAlias().Name + "/reset?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token); string url = HttpContext.Request.Scheme + "://" + _alias.Name + "/reset?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token);
string body = "Dear " + user.DisplayName + ",\n\nPlease Click The Link Displayed Below To Reset Your Password:\n\n" + url + "\n\nThank You!"; string body = "Dear " + user.DisplayName + ",\n\nPlease Click The Link Displayed Below To Reset Your Password:\n\n" + url + "\n\nThank You!";
var notification = new Notification(user.SiteId, null, user, "User Password Reset", body, null); var notification = new Notification(user.SiteId, null, user, "User Password Reset", body, null);
_notifications.AddNotification(notification); _notifications.AddNotification(notification);

View File

@ -10,22 +10,22 @@ using System.Linq;
namespace Oqtane.Controllers namespace Oqtane.Controllers
{ {
[Route(ControllerRoutes.Default)] [Route(ControllerRoutes.ApiRoute)]
public class UserRoleController : Controller public class UserRoleController : Controller
{ {
private readonly IUserRoleRepository _userRoles; private readonly IUserRoleRepository _userRoles;
private readonly IRoleRepository _roles; private readonly IRoleRepository _roles;
private readonly ITenantResolver _tenants;
private readonly ISyncManager _syncManager; private readonly ISyncManager _syncManager;
private readonly ILogManager _logger; private readonly ILogManager _logger;
private readonly Alias _alias;
public UserRoleController(IUserRoleRepository userRoles, IRoleRepository roles, ITenantResolver tenants, ISyncManager syncManager, ILogManager logger) public UserRoleController(IUserRoleRepository userRoles, IRoleRepository roles, ITenantManager tenantManager, ISyncManager syncManager, ILogManager logger)
{ {
_userRoles = userRoles; _userRoles = userRoles;
_roles = roles; _roles = roles;
_syncManager = syncManager; _syncManager = syncManager;
_tenants = tenants;
_logger = logger; _logger = logger;
_alias = tenantManager.GetAlias();
} }
// GET: api/<controller>?siteid=x // GET: api/<controller>?siteid=x
@ -62,7 +62,7 @@ namespace Oqtane.Controllers
userRole = _userRoles.AddUserRole(userRole); userRole = _userRoles.AddUserRole(userRole);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "User Role Added {UserRole}", userRole); _logger.Log(LogLevel.Information, this, LogFunction.Create, "User Role Added {UserRole}", userRole);
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.User, userRole.UserId); _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.User, userRole.UserId);
} }
return userRole; return userRole;
} }
@ -76,7 +76,7 @@ namespace Oqtane.Controllers
if (ModelState.IsValid && (User.IsInRole(RoleNames.Host) || role.Name != RoleNames.Host)) if (ModelState.IsValid && (User.IsInRole(RoleNames.Host) || role.Name != RoleNames.Host))
{ {
userRole = _userRoles.UpdateUserRole(userRole); userRole = _userRoles.UpdateUserRole(userRole);
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.User, userRole.UserId); _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.User, userRole.UserId);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "User Role Updated {UserRole}", userRole); _logger.Log(LogLevel.Information, this, LogFunction.Update, "User Role Updated {UserRole}", userRole);
} }
return userRole; return userRole;
@ -96,15 +96,15 @@ namespace Oqtane.Controllers
if (userRole.Role.Name == RoleNames.Host) if (userRole.Role.Name == RoleNames.Host)
{ {
// add site specific user roles to preserve user access // add site specific user roles to preserve user access
var role = _roles.GetRoles(_tenants.GetAlias().SiteId).FirstOrDefault(item => item.Name == RoleNames.Registered); var role = _roles.GetRoles(_alias.SiteId).FirstOrDefault(item => item.Name == RoleNames.Registered);
userRole = _userRoles.AddUserRole(new UserRole { UserId = userRole.UserId, RoleId = role.RoleId, EffectiveDate = null, ExpiryDate = null }); userRole = _userRoles.AddUserRole(new UserRole { UserId = userRole.UserId, RoleId = role.RoleId, EffectiveDate = null, ExpiryDate = null });
_logger.Log(LogLevel.Information, this, LogFunction.Create, "User Role Added {UserRole}", userRole); _logger.Log(LogLevel.Information, this, LogFunction.Create, "User Role Added {UserRole}", userRole);
role = _roles.GetRoles(_tenants.GetAlias().SiteId).FirstOrDefault(item => item.Name == RoleNames.Admin); role = _roles.GetRoles(_alias.SiteId).FirstOrDefault(item => item.Name == RoleNames.Admin);
userRole = _userRoles.AddUserRole(new UserRole { UserId = userRole.UserId, RoleId = role.RoleId, EffectiveDate = null, ExpiryDate = null }); userRole = _userRoles.AddUserRole(new UserRole { UserId = userRole.UserId, RoleId = role.RoleId, EffectiveDate = null, ExpiryDate = null });
_logger.Log(LogLevel.Information, this, LogFunction.Create, "User Role Added {UserRole}", userRole); _logger.Log(LogLevel.Information, this, LogFunction.Create, "User Role Added {UserRole}", userRole);
} }
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.User, userRole.UserId); _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.User, userRole.UserId);
} }
} }
} }

View File

@ -38,5 +38,8 @@ namespace Oqtane.Extensions
return app; return app;
} }
public static IApplicationBuilder UseTenantResolution(this IApplicationBuilder builder)
=> builder.UseMiddleware<TenantMiddleware>();
} }
} }

View File

@ -464,12 +464,12 @@ namespace Oqtane.Infrastructure
{ {
using (var scope = _serviceScopeFactory.CreateScope()) using (var scope = _serviceScopeFactory.CreateScope())
{ {
// use the SiteState to 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 firstAlias = install.Aliases.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)[0];
var alias = aliases.GetAliases().FirstOrDefault(item => item.Name == firstAlias); var alias = aliases.GetAliases().FirstOrDefault(item => item.Name == firstAlias);
var siteState = scope.ServiceProvider.GetRequiredService<SiteState>(); var tenantManager = scope.ServiceProvider.GetRequiredService<ITenantManager>();
siteState.Alias = alias; tenantManager.SetAlias(alias);
var sites = scope.ServiceProvider.GetRequiredService<ISiteRepository>(); var sites = scope.ServiceProvider.GetRequiredService<ISiteRepository>();
var site = sites.GetSites().FirstOrDefault(item => item.Name == install.SiteName); var site = sites.GetSites().FirstOrDefault(item => item.Name == install.SiteName);

View File

@ -0,0 +1,12 @@
using Oqtane.Models;
namespace Oqtane.Infrastructure
{
public interface ITenantManager
{
Alias GetAlias();
Tenant GetTenant();
void SetAlias(Alias alias);
void SetTenant(int tenantId);
}
}

View File

@ -92,11 +92,12 @@ namespace Oqtane.Infrastructure
try try
{ {
var notes = ""; var notes = "";
var tenants = scope.ServiceProvider.GetRequiredService<ITenantRepository>(); var tenantRepository = scope.ServiceProvider.GetRequiredService<ITenantRepository>();
var siteState = scope.ServiceProvider.GetRequiredService<SiteState>(); var tenantManager = scope.ServiceProvider.GetRequiredService<ITenantManager>();
foreach (var tenant in tenants.GetTenants()) foreach (var tenant in tenantRepository.GetTenants())
{ {
siteState.Alias = new Alias { TenantId = tenant.TenantId }; // set tenant and execute job
tenantManager.SetTenant(tenant.TenantId);
notes += ExecuteJob(scope.ServiceProvider); notes += ExecuteJob(scope.ServiceProvider);
} }
log.Notes = notes; log.Notes = notes;

View File

@ -14,18 +14,18 @@ namespace Oqtane.Infrastructure
public class LogManager : ILogManager public class LogManager : ILogManager
{ {
private readonly ILogRepository _logs; private readonly ILogRepository _logs;
private readonly ITenantResolver _tenantResolver;
private readonly IConfigurationRoot _config; private readonly IConfigurationRoot _config;
private readonly IUserPermissions _userPermissions; private readonly IUserPermissions _userPermissions;
private readonly IHttpContextAccessor _accessor; private readonly IHttpContextAccessor _accessor;
private readonly Alias _alias;
public LogManager(ILogRepository logs, ITenantResolver tenantResolver, IConfigurationRoot config, IUserPermissions userPermissions, IHttpContextAccessor accessor) public LogManager(ILogRepository logs, ITenantManager tenantManager, IConfigurationRoot config, IUserPermissions userPermissions, IHttpContextAccessor accessor)
{ {
_logs = logs; _logs = logs;
_tenantResolver = tenantResolver;
_config = config; _config = config;
_userPermissions = userPermissions; _userPermissions = userPermissions;
_accessor = accessor; _accessor = accessor;
_alias = tenantManager.GetAlias();
} }
public void Log(LogLevel level, object @class, LogFunction function, string message, params object[] args) public void Log(LogLevel level, object @class, LogFunction function, string message, params object[] args)
@ -49,10 +49,9 @@ namespace Oqtane.Infrastructure
if (siteId == -1) if (siteId == -1)
{ {
log.SiteId = null; log.SiteId = null;
Alias alias = _tenantResolver.GetAlias(); if (_alias != null)
if (alias != null)
{ {
log.SiteId = alias.SiteId; log.SiteId = _alias.SiteId;
} }
} }
else else

View File

@ -0,0 +1,41 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
namespace Oqtane.Infrastructure
{
internal class TenantMiddleware
{
private readonly RequestDelegate next;
public TenantMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context)
{
// check if framework is installed
var config = context.RequestServices.GetService(typeof(IConfiguration)) as IConfiguration;
if (!string.IsNullOrEmpty(config.GetConnectionString("DefaultConnection")))
{
// get alias
var tenantManager = context.RequestServices.GetService(typeof(ITenantManager)) as ITenantManager;
var alias = tenantManager.GetAlias();
// rewrite path by removing alias path prefix from api and pages requests
if (alias != null && !string.IsNullOrEmpty(alias.Path))
{
string path = context.Request.Path.ToString();
if (path.StartsWith("/" + alias.Path) && (path.Contains("/api/") || path.Contains("/pages/")))
{
context.Request.Path = path.Replace("/" + alias.Path, "");
}
}
}
// continue processing
if (next != null) await next(context);
}
}
}

View File

@ -0,0 +1,87 @@
using System;
using System.Linq;
using Microsoft.AspNetCore.Http;
using Oqtane.Models;
using Oqtane.Repository;
using Oqtane.Shared;
namespace Oqtane.Infrastructure
{
public class TenantManager : ITenantManager
{
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IAliasRepository _aliasRepository;
private readonly ITenantRepository _tenantRepository;
private readonly SiteState _siteState;
public TenantManager(IHttpContextAccessor httpContextAccessor, IAliasRepository aliasRepository, ITenantRepository tenantRepository, SiteState siteState)
{
_httpContextAccessor = httpContextAccessor;
_aliasRepository = aliasRepository;
_tenantRepository = tenantRepository;
_siteState = siteState;
}
public Alias GetAlias()
{
Alias alias = null;
if (_siteState != null && _siteState.Alias != null)
{
alias = _siteState.Alias;
}
else
{
// if there is http context
if (_httpContextAccessor.HttpContext != null)
{
// legacy support for client api requests which would include the alias as a path prefix ( ie. {alias}/api/[controller] )
int aliasId;
string[] segments = _httpContextAccessor.HttpContext.Request.Path.Value.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
if (segments.Length > 1 && (segments[1] == "api" || segments[1] == "pages") && int.TryParse(segments[0], out aliasId))
{
alias = _aliasRepository.GetAliases().ToList().FirstOrDefault(item => item.AliasId == aliasId);
}
// resolve alias based on host name and path
if (alias == null)
{
string name = _httpContextAccessor.HttpContext.Request.Host.Value + _httpContextAccessor.HttpContext.Request.Path;
alias = _aliasRepository.GetAlias(name);
}
// if there is a match save it
if (alias != null)
{
_siteState.Alias = alias;
}
}
}
return alias;
}
public Tenant GetTenant()
{
var alias = GetAlias();
if (alias != null)
{
// return tenant details
return _tenantRepository.GetTenants().ToList().FirstOrDefault(item => item.TenantId == alias.TenantId);
}
return null;
}
public void SetAlias(Alias alias)
{
// background processes can set the alias using the SiteState service
_siteState.Alias = alias;
}
public void SetTenant(int tenantId)
{
// background processes can set the alias using the SiteState service
_siteState.Alias = new Alias { TenantId = tenantId };
}
}
}

View File

@ -27,9 +27,9 @@ namespace Oqtane.Infrastructure
// core framework upgrade logic - executed for every tenant // core framework upgrade logic - executed for every tenant
using (var scope = _serviceScopeFactory.CreateScope()) using (var scope = _serviceScopeFactory.CreateScope())
{ {
// set SiteState based on tenant // set tenant
var siteState = scope.ServiceProvider.GetRequiredService<SiteState>(); var tenantManager = scope.ServiceProvider.GetRequiredService<ITenantManager>();
siteState.Alias = new Alias { TenantId = tenant.TenantId }; tenantManager.SetTenant(tenant.TenantId);
switch (version) switch (version)
{ {

View File

@ -1,6 +1,5 @@
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Oqtane.Modules.HtmlText.Models;
using Oqtane.Modules.HtmlText.Repository; using Oqtane.Modules.HtmlText.Repository;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Oqtane.Shared; using Oqtane.Shared;
@ -12,7 +11,7 @@ using Oqtane.Controllers;
namespace Oqtane.Modules.HtmlText.Controllers namespace Oqtane.Modules.HtmlText.Controllers
{ {
[Route(ControllerRoutes.Default)] [Route(ControllerRoutes.ApiRoute)]
public class HtmlTextController : ModuleControllerBase public class HtmlTextController : ModuleControllerBase
{ {
private readonly IHtmlTextRepository _htmlText; private readonly IHtmlTextRepository _htmlText;

View File

@ -1,8 +1,6 @@
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Oqtane.Modules.HtmlText.Models; using Oqtane.Infrastructure;
using Oqtane.Repository; using Oqtane.Repository;
using Oqtane.Interfaces;
using Oqtane.Repository.Databases.Interfaces; using Oqtane.Repository.Databases.Interfaces;
// ReSharper disable MemberCanBePrivate.Global // ReSharper disable MemberCanBePrivate.Global
@ -12,7 +10,7 @@ namespace Oqtane.Modules.HtmlText.Repository
{ {
public class HtmlTextContext : DBContextBase, IService, IMultiDatabase public class HtmlTextContext : DBContextBase, IService, IMultiDatabase
{ {
public HtmlTextContext(IDbConfig dbConfig, ITenantResolver tenantResolver) : base(dbConfig, tenantResolver) public HtmlTextContext(IDbConfig dbConfig, ITenantManager tenantManager) : base(dbConfig, tenantManager)
{ {
} }

View File

@ -1,3 +1,3 @@
@page "/{alias}/pages/login" @page "/pages/login"
@namespace Oqtane.Pages @namespace Oqtane.Pages
@model Oqtane.Pages.LoginModel @model Oqtane.Pages.LoginModel

View File

@ -1,3 +1,3 @@
@page "/{alias}/pages/logout" @page "/pages/logout"
@namespace Oqtane.Pages @namespace Oqtane.Pages
@model Oqtane.Pages.LogoutModel @model Oqtane.Pages.LogoutModel

View File

@ -22,7 +22,7 @@
<body> <body>
@(Html.AntiForgeryToken()) @(Html.AntiForgeryToken())
<app> <app>
<component type="typeof(Oqtane.App)" render-mode="Server" /> <component type="typeof(Oqtane.App)" render-mode="ServerPrerendered" />
</app> </app>
<div id="blazor-error-ui"> <div id="blazor-error-ui">

View File

@ -8,7 +8,6 @@ using System;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using Microsoft.AspNetCore.Http.Extensions;
using Oqtane.Repository; using Oqtane.Repository;
using Microsoft.AspNetCore.Localization; using Microsoft.AspNetCore.Localization;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
@ -18,21 +17,18 @@ namespace Oqtane.Pages
public class HostModel : PageModel public class HostModel : PageModel
{ {
private IConfiguration _configuration; private IConfiguration _configuration;
private readonly SiteState _state; private readonly ITenantManager _tenantManager;
private readonly IAliasRepository _aliases;
private readonly ILocalizationManager _localizationManager; private readonly ILocalizationManager _localizationManager;
private readonly ILanguageRepository _languages; private readonly ILanguageRepository _languages;
public HostModel( public HostModel(
IConfiguration configuration, IConfiguration configuration,
SiteState state, ITenantManager tenantManager,
IAliasRepository aliases,
ILocalizationManager localizationManager, ILocalizationManager localizationManager,
ILanguageRepository languages) ILanguageRepository languages)
{ {
_configuration = configuration; _configuration = configuration;
_state = state; _tenantManager = tenantManager;
_aliases = aliases;
_localizationManager = localizationManager; _localizationManager = localizationManager;
_languages = languages; _languages = languages;
} }
@ -54,19 +50,14 @@ namespace Oqtane.Pages
// if culture not specified and framework is installed // if culture not specified and framework is installed
if (HttpContext.Request.Cookies[CookieRequestCultureProvider.DefaultCookieName] == null && !string.IsNullOrEmpty(_configuration.GetConnectionString("DefaultConnection"))) if (HttpContext.Request.Cookies[CookieRequestCultureProvider.DefaultCookieName] == null && !string.IsNullOrEmpty(_configuration.GetConnectionString("DefaultConnection")))
{ {
var uri = new Uri(Request.GetDisplayUrl()); var alias = _tenantManager.GetAlias();
var hostname = uri.Authority + "/" + uri.LocalPath.Substring(1);
var alias = _aliases.GetAlias(hostname);
if (alias != null) if (alias != null)
{ {
_state.Alias = alias;
// set default language for site if the culture is not supported // set default language for site if the culture is not supported
var languages = _languages.GetLanguages(alias.SiteId); var languages = _languages.GetLanguages(alias.SiteId);
if (languages.Any() && languages.All(l => l.Code != CultureInfo.CurrentUICulture.Name)) if (languages.Any() && languages.All(l => l.Code != CultureInfo.CurrentUICulture.Name))
{ {
var defaultLanguage = languages.Where(l => l.IsDefault).SingleOrDefault() ?? languages.First(); var defaultLanguage = languages.Where(l => l.IsDefault).SingleOrDefault() ?? languages.First();
SetLocalizationCookie(defaultLanguage.Code); SetLocalizationCookie(defaultLanguage.Code);
} }
else else
@ -74,10 +65,6 @@ namespace Oqtane.Pages
SetLocalizationCookie(_localizationManager.GetDefaultCulture()); SetLocalizationCookie(_localizationManager.GetDefaultCulture());
} }
} }
else
{
Message = $"No Matching Alias For Host Name {hostname}";
}
} }
} }

View File

@ -55,17 +55,28 @@ namespace Oqtane.Repository
List<Alias> aliases = GetAliases().ToList(); List<Alias> aliases = GetAliases().ToList();
var segments = name.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries); var segments = name.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
// iterate segments in reverse order // iterate segments to find keywords
for (int i = segments.Length; i > 0; i--) int start = segments.Length;
for (int i = 0; i < segments.Length; i++)
{ {
name = string.Join("/", segments, 0, i); if (segments[i] == "api" || segments[i] == "pages" || segments[i] == "*")
alias = aliases.Find(item => item.Name.Equals(name, StringComparison.OrdinalIgnoreCase)); {
start = i;
break;
}
}
// iterate segments in reverse order to find alias match
for (int i = start; i > 0; i--)
{
alias = aliases.Find(item => item.Name.Equals(string.Join("/", segments, 0, i), StringComparison.OrdinalIgnoreCase));
if (alias != null) if (alias != null)
{ {
break; // found a matching alias break; // found a matching alias
} }
} }
// return fallback alias
// return fallback alias if none found
return alias ?? aliases.Find(item => item.Name.Equals("*")); return alias ?? aliases.Find(item => item.Name.Equals("*"));
} }

View File

@ -8,9 +8,10 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Oqtane.Extensions; using Oqtane.Extensions;
using Oqtane.Infrastructure;
using Oqtane.Interfaces; using Oqtane.Interfaces;
using Oqtane.Migrations.Framework; using Oqtane.Migrations.Framework;
using Oqtane.Repository.Databases.Interfaces; using Oqtane.Models;
using Oqtane.Shared; using Oqtane.Shared;
// ReSharper disable BuiltInTypeReferenceStyleForMemberAccess // ReSharper disable BuiltInTypeReferenceStyleForMemberAccess
@ -20,26 +21,27 @@ namespace Oqtane.Repository
public class DBContextBase : IdentityUserContext<IdentityUser> public class DBContextBase : IdentityUserContext<IdentityUser>
{ {
private readonly ITenantResolver _tenantResolver; private readonly ITenantResolver _tenantResolver;
private readonly ITenantManager _tenantManager;
private readonly IHttpContextAccessor _accessor; private readonly IHttpContextAccessor _accessor;
private readonly IConfiguration _configuration; private readonly IConfiguration _configuration;
private string _connectionString; private string _connectionString;
private string _databaseType; private string _databaseType;
public DBContextBase(ITenantResolver tenantResolver, IHttpContextAccessor httpContextAccessor) public DBContextBase(ITenantManager tenantManager, IHttpContextAccessor httpContextAccessor)
{ {
_connectionString = String.Empty; _connectionString = String.Empty;
_tenantResolver = tenantResolver; _tenantManager = tenantManager;
_accessor = httpContextAccessor; _accessor = httpContextAccessor;
} }
public DBContextBase(IDbConfig dbConfig, ITenantResolver tenantResolver) public DBContextBase(IDbConfig dbConfig, ITenantManager tenantManager)
{ {
_accessor = dbConfig.Accessor; _accessor = dbConfig.Accessor;
_configuration = dbConfig.Configuration; _configuration = dbConfig.Configuration;
_connectionString = dbConfig.ConnectionString; _connectionString = dbConfig.ConnectionString;
_databaseType = dbConfig.DatabaseType; _databaseType = dbConfig.DatabaseType;
Databases = dbConfig.Databases; Databases = dbConfig.Databases;
_tenantResolver = tenantResolver; _tenantManager = tenantManager;
} }
public IEnumerable<IOqtaneDatabase> Databases { get; } public IEnumerable<IOqtaneDatabase> Databases { get; }
@ -48,9 +50,18 @@ namespace Oqtane.Repository
{ {
optionsBuilder.ReplaceService<IMigrationsAssembly, MultiDatabaseMigrationsAssembly>(); optionsBuilder.ReplaceService<IMigrationsAssembly, MultiDatabaseMigrationsAssembly>();
if (string.IsNullOrEmpty(_connectionString) && _tenantResolver != null) if (string.IsNullOrEmpty(_connectionString))
{ {
var tenant = _tenantResolver.GetTenant();
Tenant tenant;
if (_tenantResolver != null)
{
tenant = _tenantResolver.GetTenant();
}
else
{
tenant = _tenantManager.GetTenant();
}
if (tenant != null) if (tenant != null)
{ {
@ -103,5 +114,25 @@ namespace Oqtane.Repository
return base.SaveChanges(); return base.SaveChanges();
} }
[Obsolete("This constructor is obsolete. Use DBContextBase(ITenantManager tenantManager, IHttpContextAccessor httpContextAccessor) instead.", false)]
public DBContextBase(ITenantResolver tenantResolver, IHttpContextAccessor httpContextAccessor)
{
_connectionString = String.Empty;
_tenantResolver = tenantResolver;
_accessor = httpContextAccessor;
}
[Obsolete("This constructor is obsolete. Use DBContextBase(IDbConfig dbConfig, ITenantManager tenantManager) instead.", false)]
public DBContextBase(IDbConfig dbConfig, ITenantResolver tenantResolver)
{
_accessor = dbConfig.Accessor;
_configuration = dbConfig.Configuration;
_connectionString = dbConfig.ConnectionString;
_databaseType = dbConfig.DatabaseType;
Databases = dbConfig.Databases;
_tenantResolver = tenantResolver;
}
} }
} }

View File

@ -1,6 +1,5 @@
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Oqtane.Interfaces; using Oqtane.Infrastructure;
using Oqtane.Models; using Oqtane.Models;
using Oqtane.Repository.Databases.Interfaces; using Oqtane.Repository.Databases.Interfaces;
@ -12,7 +11,7 @@ namespace Oqtane.Repository
{ {
public class TenantDBContext : DBContextBase, IMultiDatabase public class TenantDBContext : DBContextBase, IMultiDatabase
{ {
public TenantDBContext(IDbConfig dbConfig, ITenantResolver tenantResolver) : base(dbConfig, tenantResolver) { } public TenantDBContext(IDbConfig dbConfig, ITenantManager tenantManager) : base(dbConfig, tenantManager) { }
public virtual DbSet<Site> Site { get; set; } public virtual DbSet<Site> Site { get; set; }
public virtual DbSet<Page> Page { get; set; } public virtual DbSet<Page> Page { get; set; }

View File

@ -1,7 +1,8 @@
using Oqtane.Models; using Oqtane.Models;
namespace Oqtane.Repository namespace Oqtane.Repository
{ {
// class deprecated and replaced by ITenantManager
public interface ITenantResolver public interface ITenantResolver
{ {
Alias GetAlias(); Alias GetAlias();

View File

@ -1,78 +1,26 @@
using System; using Oqtane.Infrastructure;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Http;
using Oqtane.Models; using Oqtane.Models;
using Oqtane.Shared;
namespace Oqtane.Repository namespace Oqtane.Repository
{ {
// class deprecated and replaced by ITenantManager
public class TenantResolver : ITenantResolver public class TenantResolver : ITenantResolver
{ {
private readonly IHttpContextAccessor _accessor; private readonly ITenantManager _tenantManager;
private readonly IAliasRepository _aliasRepository;
private readonly ITenantRepository _tenantRepository;
private readonly SiteState _siteState;
private Alias _alias; public TenantResolver(ITenantManager tenantManager)
private Tenant _tenant;
public TenantResolver(IHttpContextAccessor accessor, IAliasRepository aliasRepository, ITenantRepository tenantRepository, SiteState siteState)
{ {
_accessor = accessor; _tenantManager = tenantManager;
_aliasRepository = aliasRepository;
_tenantRepository = tenantRepository;
_siteState = siteState;
} }
public Alias GetAlias() public Alias GetAlias()
{ {
if (_alias == null) ResolveTenant(); return _tenantManager.GetAlias();
return _alias;
} }
public Tenant GetTenant() public Tenant GetTenant()
{ {
if (_tenant == null) ResolveTenant(); return _tenantManager.GetTenant();
return _tenant;
}
private void ResolveTenant()
{
if (_siteState != null && _siteState.Alias != null)
{
// background processes can pass in an alias using the SiteState service
_alias = _siteState.Alias;
}
else
{
int aliasId = -1;
// get aliasid identifier based on request
if (_accessor.HttpContext != null)
{
string[] segments = _accessor.HttpContext.Request.Path.Value.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
if (segments.Length > 1 && (segments[1] == "api" || segments[1] == "pages") && segments[0] != "~")
{
aliasId = int.Parse(segments[0]);
}
}
// get the alias
IEnumerable<Alias> aliases = _aliasRepository.GetAliases().ToList(); // cached
if (aliasId != -1)
{
_alias = aliases.FirstOrDefault(item => item.AliasId == aliasId);
}
}
if (_alias != null)
{
// get the tenant
IEnumerable<Tenant> tenants = _tenantRepository.GetTenants(); // cached
_tenant = tenants.FirstOrDefault(item => item.TenantId == _alias.TenantId);
}
} }
} }
} }

View File

@ -1,5 +1,4 @@
using System; using System;
using System.Collections;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
@ -10,7 +9,6 @@ using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
@ -126,6 +124,7 @@ namespace Oqtane
services.AddScoped<ISystemService, SystemService>(); services.AddScoped<ISystemService, SystemService>();
services.AddScoped<ILocalizationService, LocalizationService>(); services.AddScoped<ILocalizationService, LocalizationService>();
services.AddScoped<ILanguageService, LanguageService>(); services.AddScoped<ILanguageService, LanguageService>();
services.AddScoped<IDatabaseService, DatabaseService>();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
@ -175,13 +174,13 @@ namespace Oqtane
services.AddSingleton<IDatabaseManager, DatabaseManager>(); services.AddSingleton<IDatabaseManager, DatabaseManager>();
// install any modules or themes ( this needs to occur BEFORE the assemblies are loaded into the app domain ) // install any modules or themes ( this needs to occur BEFORE the assemblies are loaded into the app domain )
InstallationManager.InstallPackages("Modules,Themes", _env.WebRootPath, _env.ContentRootPath); InstallationManager.InstallPackages("Modules,Themes,Packages", _env.WebRootPath, _env.ContentRootPath);
// register transient scoped core services // register transient scoped core services
services.AddTransient<ITenantManager, TenantManager>();
services.AddTransient<IModuleDefinitionRepository, ModuleDefinitionRepository>(); services.AddTransient<IModuleDefinitionRepository, ModuleDefinitionRepository>();
services.AddTransient<IThemeRepository, ThemeRepository>(); services.AddTransient<IThemeRepository, ThemeRepository>();
services.AddTransient<IUserPermissions, UserPermissions>(); services.AddTransient<IUserPermissions, UserPermissions>();
services.AddTransient<ITenantResolver, TenantResolver>();
services.AddTransient<IAliasRepository, AliasRepository>(); services.AddTransient<IAliasRepository, AliasRepository>();
services.AddTransient<ITenantRepository, TenantRepository>(); services.AddTransient<ITenantRepository, TenantRepository>();
services.AddTransient<ISiteRepository, SiteRepository>(); services.AddTransient<ISiteRepository, SiteRepository>();
@ -206,6 +205,8 @@ namespace Oqtane
services.AddTransient<ISqlRepository, SqlRepository>(); services.AddTransient<ISqlRepository, SqlRepository>();
services.AddTransient<IUpgradeManager, UpgradeManager>(); services.AddTransient<IUpgradeManager, UpgradeManager>();
services.AddTransient<ILanguageRepository, LanguageRepository>(); services.AddTransient<ILanguageRepository, LanguageRepository>();
// obsolete - replaced by ITenantManager
services.AddTransient<ITenantResolver, TenantResolver>();
// load the external assemblies into the app domain, install services // load the external assemblies into the app domain, install services
services.AddOqtane(_runtime, _supportedCultures); services.AddOqtane(_runtime, _supportedCultures);
@ -240,7 +241,8 @@ namespace Oqtane
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts(); app.UseHsts();
} }
// to allow install middleware it should be moved up
// execute any IServerStartup logic
app.ConfigureOqtaneAssemblies(env); app.ConfigureOqtaneAssemblies(env);
// Allow oqtane localization middleware // Allow oqtane localization middleware
@ -248,6 +250,7 @@ namespace Oqtane
app.UseHttpsRedirection(); app.UseHttpsRedirection();
app.UseStaticFiles(); app.UseStaticFiles();
app.UseTenantResolution(); // must be declared directly after static files
app.UseBlazorFrameworkFiles(); app.UseBlazorFrameworkFiles();
app.UseRouting(); app.UseRouting();
app.UseAuthentication(); app.UseAuthentication();
@ -255,7 +258,7 @@ namespace Oqtane
if (_useSwagger) if (_useSwagger)
{ {
app.UseSwagger(); app.UseSwagger();
app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "Oqtane V1"); }); app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "Oqtane " + Constants.Version); });
} }
app.UseEndpoints(endpoints => app.UseEndpoints(endpoints =>

View File

@ -1 +0,0 @@
/* Module Custom Styles */

View File

@ -1 +0,0 @@
/* Module Script */

View File

@ -1 +0,0 @@
["\\bin\\Debug\\net5.0\\Oqtane.Blogs.Client.Oqtane.dll","\\bin\\Debug\\net5.0\\Oqtane.Blogs.Client.Oqtane.pdb","\\bin\\Debug\\net5.0\\Oqtane.Blogs.Server.Oqtane.dll","\\bin\\Debug\\net5.0\\Oqtane.Blogs.Server.Oqtane.pdb","\\bin\\Debug\\net5.0\\Oqtane.Blogs.Shared.Oqtane.dll","\\bin\\Debug\\net5.0\\Oqtane.Blogs.Shared.Oqtane.pdb","\\wwwroot\\Modules\\Oqtane.Blogs\\Module.css","\\wwwroot\\Modules\\Oqtane.Blogs\\Module.js"]

View File

@ -1,9 +1,6 @@
using System; namespace Oqtane.Shared {
using System.Collections.Generic;
using System.Text;
namespace Oqtane.Shared {
public class ControllerRoutes { public class ControllerRoutes {
public const string Default = "{alias}/api/[controller]"; public const string Default = "{alias}/api/[controller]";
public const string ApiRoute = "api/[controller]";
} }
} }

View File

@ -1,4 +1,4 @@
using Oqtane.Models; using Oqtane.Models;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
@ -102,7 +102,7 @@ namespace Oqtane.Shared
public static string ContentUrl(Alias alias, int fileId, bool asAttachment) public static string ContentUrl(Alias alias, int fileId, bool asAttachment)
{ {
var aliasUrl = (alias == null) ? "/~" : "/" + alias.AliasId; var aliasUrl = (alias != null && !string.IsNullOrEmpty(alias.Path)) ? "/" + alias.Path : "";
var method = asAttachment ? "/attach":""; var method = asAttachment ? "/attach":"";
return $"{aliasUrl}{Constants.ContentUrl}{fileId}{method}"; return $"{aliasUrl}{Constants.ContentUrl}{fileId}{method}";

View File

@ -49,9 +49,16 @@ There is a separate [Documentation repository](https://github.com/oqtane/oqtane.
# Roadmap # Roadmap
This project is a work in progress and the schedule for implementing enhancements is dependent upon the availability of community members who are willing/able to assist. This project is a work in progress and the schedule for implementing enhancements is dependent upon the availability of community members who are willing/able to assist.
V.3.0.0 ( Nov 2021 )
- [ ] Migration to .NET 6
V.2.2.0 ( Aug 2021 )
- [ ] Alternate Authentication Providers ( ie. Azure B2C, Social logins, etc... )
- [ ] Configurable password complexity for local authentication
V.2.1.0 ( May 2021 ) V.2.1.0 ( May 2021 )
- [ ] Cross Platform Database Support ( ie. SQLite ) - see [#964](https://github.com/oqtane/oqtane.framework/discussions/964) - [ ] Cross Platform Database Support ( ie. SQLite, MySQL, PostreSQL ) - see [#964](https://github.com/oqtane/oqtane.framework/discussions/964)
- [ ] EF Core Migrations for Database Installation/Upgrade - see [#964](https://github.com/oqtane/oqtane.framework/discussions/964) - [ ] EF Core Migrations - see [#964](https://github.com/oqtane/oqtane.framework/discussions/964)
V.2.0.2 ( Apr 19, 2021 ) V.2.0.2 ( Apr 19, 2021 )
- [x] Assorted fixes and user experience improvements - [x] Assorted fixes and user experience improvements