Merge pull request #422 from sbwalker/master
install/upgrade refactoring to consolidate all use cases and implement IInstallable interface for modules, moved tenant creation to site management UI, fixed z-order issues in Blazor theme, enhanced JS Interop methods to support integrity and crossorigin
This commit is contained in:
commit
ac6ee7f4b0
|
@ -62,7 +62,8 @@ else
|
||||||
- Repository\I[Module]Repository.cs - interface for defining repository methods<br />
|
- Repository\I[Module]Repository.cs - interface for defining repository methods<br />
|
||||||
- Repository\[Module]Respository.cs - implements repository interface methods for data access using EF Core<br />
|
- Repository\[Module]Respository.cs - implements repository interface methods for data access using EF Core<br />
|
||||||
- Repository\[Module]Context.cs - provides a DB Context for data access<br />
|
- Repository\[Module]Context.cs - provides a DB Context for data access<br />
|
||||||
- Scripts\01.00.00.sql - database schema definition<br /><br />
|
- Scripts\[Module].1.0.0.sql - database schema definition script<br /><br />
|
||||||
|
- Scripts\[Module].Uninstall.sql - database uninstall script<br /><br />
|
||||||
[RootPath]Shared\<br />
|
[RootPath]Shared\<br />
|
||||||
- [Owner].[Module]s.Module.Shared.csproj - shared project<br />
|
- [Owner].[Module]s.Module.Shared.csproj - shared project<br />
|
||||||
- Models\[Module].cs - model definition<br /><br />
|
- Models\[Module].cs - model definition<br /><br />
|
||||||
|
|
|
@ -11,7 +11,8 @@ namespace [Owner].[Module]s.Modules
|
||||||
Description = "[Module]",
|
Description = "[Module]",
|
||||||
Version = "1.0.0",
|
Version = "1.0.0",
|
||||||
Dependencies = "[Owner].[Module]s.Module.Shared",
|
Dependencies = "[Owner].[Module]s.Module.Shared",
|
||||||
ServerManagerType = "[ServerManagerType]"
|
ServerManagerType = "[ServerManagerType]",
|
||||||
|
ReleaseVersions = "1.0.0"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,18 +3,32 @@ using System.Linq;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Oqtane.Modules;
|
using Oqtane.Modules;
|
||||||
using Oqtane.Models;
|
using Oqtane.Models;
|
||||||
|
using Oqtane.Infrastructure;
|
||||||
|
using Oqtane.Repository;
|
||||||
using [Owner].[Module]s.Models;
|
using [Owner].[Module]s.Models;
|
||||||
using [Owner].[Module]s.Repository;
|
using [Owner].[Module]s.Repository;
|
||||||
|
|
||||||
namespace [Owner].[Module]s.Manager
|
namespace [Owner].[Module]s.Manager
|
||||||
{
|
{
|
||||||
public class [Module]Manager : IPortable
|
public class [Module]Manager : IInstallable, IPortable
|
||||||
{
|
{
|
||||||
private I[Module]Repository _[Module]s;
|
private I[Module]Repository _[Module]s;
|
||||||
|
private ISqlRepository _sql;
|
||||||
|
|
||||||
public [Module]Manager(I[Module]Repository [Module]s)
|
public [Module]Manager(I[Module]Repository [Module]s, ISqlRepository sql)
|
||||||
{
|
{
|
||||||
_[Module]s = [Module]s;
|
_[Module]s = [Module]s;
|
||||||
|
_sql = sql;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Install(Tenant tenant, string version)
|
||||||
|
{
|
||||||
|
return _sql.ExecuteScript(tenant, GetType().Assembly, "[Owner].[Module]." + version + ".sql");
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Uninstall(Tenant tenant)
|
||||||
|
{
|
||||||
|
return _sql.ExecuteScript(tenant, GetType().Assembly, "[Owner].[Module].Uninstall.sql");
|
||||||
}
|
}
|
||||||
|
|
||||||
public string ExportModule(Module module)
|
public string ExportModule(Module module)
|
||||||
|
|
|
@ -13,13 +13,13 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Remove="Scripts\01.00.00.sql" />
|
<None Remove="Scripts\[Module].1.0.0.sql" />
|
||||||
<None Remove="Scripts\Uninstall.sql" />
|
<None Remove="Scripts\[Module].Uninstall.sql" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Include="Scripts\01.00.00.sql" />
|
<EmbeddedResource Include="Scripts\[Module].1.0.0.sql" />
|
||||||
<EmbeddedResource Include="Scripts\Uninstall.sql" />
|
<EmbeddedResource Include="Scripts\[Module].Uninstall.sql" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -54,7 +54,8 @@ else
|
||||||
- Repository\I[Module]Repository.cs - interface for defining repository methods<br />
|
- Repository\I[Module]Repository.cs - interface for defining repository methods<br />
|
||||||
- Repository\[Module]Respository.cs - implements repository interface methods for data access using EF Core<br />
|
- Repository\[Module]Respository.cs - implements repository interface methods for data access using EF Core<br />
|
||||||
- Repository\[Module]Context.cs - provides a DB Context for data access<br />
|
- Repository\[Module]Context.cs - provides a DB Context for data access<br />
|
||||||
- Scripts\01.00.00.sql - database schema definition<br /><br />
|
- Scripts\[Module].1.0.0.sql - database schema definition script<br /><br />
|
||||||
|
- Scripts\[Module].Uninstall.sql - database uninstall script<br /><br />
|
||||||
[RootPath]Oqtane.Shared\Modules\[Module]\<br />
|
[RootPath]Oqtane.Shared\Modules\[Module]\<br />
|
||||||
- Models\[Module].cs - model definition<br /><br />
|
- Models\[Module].cs - model definition<br /><br />
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,8 @@ namespace [Owner].[Module]s.Modules
|
||||||
Description = "[Module]",
|
Description = "[Module]",
|
||||||
Version = "1.0.0",
|
Version = "1.0.0",
|
||||||
Dependencies = "[Owner].[Module]s.Module.Shared",
|
Dependencies = "[Owner].[Module]s.Module.Shared",
|
||||||
ServerManagerType = "[ServerManagerType]"
|
ServerManagerType = "[ServerManagerType]",
|
||||||
|
ReleaseVersions = "1.0.0"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,18 +3,32 @@ using System.Linq;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Oqtane.Modules;
|
using Oqtane.Modules;
|
||||||
using Oqtane.Models;
|
using Oqtane.Models;
|
||||||
|
using Oqtane.Infrastructure;
|
||||||
|
using Oqtane.Repository;
|
||||||
using [Owner].[Module]s.Models;
|
using [Owner].[Module]s.Models;
|
||||||
using [Owner].[Module]s.Repository;
|
using [Owner].[Module]s.Repository;
|
||||||
|
|
||||||
namespace [Owner].[Module]s.Manager
|
namespace [Owner].[Module]s.Manager
|
||||||
{
|
{
|
||||||
public class [Module]Manager : IPortable
|
public class [Module]Manager : IInstallable, IPortable
|
||||||
{
|
{
|
||||||
private I[Module]Repository _[Module]s;
|
private I[Module]Repository _[Module]s;
|
||||||
|
private ISqlRepository _sql;
|
||||||
|
|
||||||
public [Module]Manager(I[Module]Repository [Module]s)
|
public [Module]Manager(I[Module]Repository [Module]s, ISqlRepository sql)
|
||||||
{
|
{
|
||||||
_[Module]s = [Module]s;
|
_[Module]s = [Module]s;
|
||||||
|
_sql = sql;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Install(Tenant tenant, string version)
|
||||||
|
{
|
||||||
|
return _sql.ExecuteScript(tenant, GetType().Assembly, "[Owner].[Module]." + version + ".sql");
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Uninstall(Tenant tenant)
|
||||||
|
{
|
||||||
|
return _sql.ExecuteScript(tenant, GetType().Assembly, "[Owner].[Module].Uninstall.sql");
|
||||||
}
|
}
|
||||||
|
|
||||||
public string ExportModule(Module module)
|
public string ExportModule(Module module)
|
||||||
|
|
|
@ -5,8 +5,9 @@
|
||||||
@inject IAliasService AliasService
|
@inject IAliasService AliasService
|
||||||
@inject ISiteService SiteService
|
@inject ISiteService SiteService
|
||||||
@inject IThemeService ThemeService
|
@inject IThemeService ThemeService
|
||||||
@inject ISiteTemplateService SiteTemplateService
|
@inject ISiteTemplateService SiteTemplateService
|
||||||
@inject IUserService UserService
|
@inject IUserService UserService
|
||||||
|
@inject IInstallationService InstallationService
|
||||||
|
|
||||||
@if (_tenants == null)
|
@if (_tenants == null)
|
||||||
{
|
{
|
||||||
|
@ -17,21 +18,7 @@ else
|
||||||
<table class="table table-borderless">
|
<table class="table table-borderless">
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<Label For="tenant" HelpText="Select the tenant for the site">Tenant: </Label>
|
<Label For="name" HelpText="Enter the name of the site">Site Name: </Label>
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<select id="tenant" class="form-control" @onchange="(e => TenantChanged(e))">
|
|
||||||
<option value="-1"><Select Tenant></option>
|
|
||||||
@foreach (Tenant tenant in _tenants)
|
|
||||||
{
|
|
||||||
<option value="@tenant.TenantId">@tenant.Name</option>
|
|
||||||
}
|
|
||||||
</select>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<Label For="name" HelpText="Enter the name of the site">Name: </Label>
|
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<input id="name" class="form-control" @bind="@_name" />
|
<input id="name" class="form-control" @bind="@_name" />
|
||||||
|
@ -101,14 +88,99 @@ else
|
||||||
</select>
|
</select>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@if (!_isinitialized)
|
<tr>
|
||||||
|
<td>
|
||||||
|
<Label For="tenant" HelpText="Select the tenant for the site">Tenant: </Label>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<select id="tenant" class="form-control" @onchange="(e => TenantChanged(e))">
|
||||||
|
<option value="-"><Select Tenant></option>
|
||||||
|
<option value="+"><Create New Tenant></option>
|
||||||
|
@foreach (Tenant tenant in _tenants)
|
||||||
|
{
|
||||||
|
<option value="@tenant.TenantId">@tenant.Name</option>
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
@if (_tenantid == "+")
|
||||||
{
|
{
|
||||||
|
<tr>
|
||||||
|
<td colspan="2">
|
||||||
|
<hr class="app-rule" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<Label For="name" HelpText="Enter the name for the tenant">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">Database Type: </Label>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<select id="databaseType" class="custom-select" @bind="@_databasetype">
|
||||||
|
<option value="LocalDB">Local Database</option>
|
||||||
|
<option value="SQLServer">SQL Server</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<Label For="server" HelpText="Enter the server for the tenant">Server: </Label>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input id="server" type="text" class="form-control" @bind="@_server" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<Label For="database" HelpText="Enter the database for the tenant">Database: </Label>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input id="database" type="text" class="form-control" @bind="@_database" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<Label For="integratedSecurity" HelpText="Select if you want integrated security or not">Integrated Security: </Label>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<select id="integratedSecurity" class="custom-select" @onchange="SetIntegratedSecurity">
|
||||||
|
<option value="true" selected>True</option>
|
||||||
|
<option value="false">False</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
@if (!_integratedsecurity)
|
||||||
|
{
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<Label For="username" HelpText="Enter the username for the integrated security">Database Username: </Label>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input id="username" type="text" class="form-control" @bind="@_username" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<Label For="password" HelpText="Enter the password for the integrated security">Database Password: </Label>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input id="password" type="password" class="form-control" @bind="@_password" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<Label For="hostUsername" HelpText="Enter the username of the host for this site">Host Username:</Label>
|
<Label For="hostUsername" HelpText="Enter the username of the host for this site">Host Username:</Label>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<input id="hostUsername" class="form-control" @bind="@_username" readonly />
|
<input id="hostUsername" class="form-control" @bind="@_hostusername" readonly />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -116,7 +188,7 @@ else
|
||||||
<Label For="hostPassword" HelpText="Enter the password for the host of this site">Host Password:</Label>
|
<Label For="hostPassword" HelpText="Enter the password for the host of this site">Host Password:</Label>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<input id="hostPassword" type="password" class="form-control" @bind="@_password" />
|
<input id="hostPassword" type="password" class="form-control" @bind="@_hostpassword" />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
}
|
}
|
||||||
|
@ -132,16 +204,24 @@ else
|
||||||
private List<SiteTemplate> _siteTemplates;
|
private List<SiteTemplate> _siteTemplates;
|
||||||
private List<Theme> _themeList;
|
private List<Theme> _themeList;
|
||||||
private List<Tenant> _tenants;
|
private List<Tenant> _tenants;
|
||||||
private string _tenantid = "-1";
|
private string _tenantid = "-";
|
||||||
|
|
||||||
|
private string _tenantname = string.Empty;
|
||||||
|
private string _databasetype = "LocalDB";
|
||||||
|
private string _server = "(LocalDb)\\MSSQLLocalDB";
|
||||||
|
private string _database = "Oqtane-" + DateTime.UtcNow.ToString("yyyyMMddHHmm");
|
||||||
|
private string _username = string.Empty;
|
||||||
|
private string _password = string.Empty;
|
||||||
|
private bool _integratedsecurity = true;
|
||||||
|
private string _hostusername = Constants.HostUser;
|
||||||
|
private string _hostpassword = string.Empty;
|
||||||
|
|
||||||
private string _name = string.Empty;
|
private string _name = string.Empty;
|
||||||
private string _urls = string.Empty;
|
private string _urls = string.Empty;
|
||||||
private string _themetype = string.Empty;
|
private string _themetype = string.Empty;
|
||||||
private string _layouttype = string.Empty;
|
private string _layouttype = string.Empty;
|
||||||
private string _containertype = string.Empty;
|
private string _containertype = string.Empty;
|
||||||
private string _sitetemplatetype = string.Empty;
|
private string _sitetemplatetype = string.Empty;
|
||||||
private bool _isinitialized = true;
|
|
||||||
private string _username = string.Empty;
|
|
||||||
private string _password = string.Empty;
|
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||||
|
|
||||||
|
@ -153,29 +233,29 @@ else
|
||||||
_themes = ThemeService.GetThemeTypes(_themeList);
|
_themes = ThemeService.GetThemeTypes(_themeList);
|
||||||
_containers = ThemeService.GetContainerTypes(_themeList);
|
_containers = ThemeService.GetContainerTypes(_themeList);
|
||||||
_siteTemplates = await SiteTemplateService.GetSiteTemplatesAsync();
|
_siteTemplates = await SiteTemplateService.GetSiteTemplatesAsync();
|
||||||
_username = Constants.HostUser;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void TenantChanged(ChangeEventArgs e)
|
private void TenantChanged(ChangeEventArgs e)
|
||||||
{
|
{
|
||||||
try
|
_tenantid = (string)e.Value;
|
||||||
|
if (string.IsNullOrEmpty(_tenantname))
|
||||||
{
|
{
|
||||||
_tenantid = (string)e.Value;
|
_tenantname = _name;
|
||||||
if (_tenantid != "-1")
|
|
||||||
{
|
|
||||||
var tenant = _tenants.FirstOrDefault(item => item.TenantId == int.Parse(_tenantid));
|
|
||||||
if (tenant != null)
|
|
||||||
{
|
|
||||||
_isinitialized = tenant.IsInitialized;
|
|
||||||
StateHasChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetIntegratedSecurity(ChangeEventArgs e)
|
||||||
|
{
|
||||||
|
if (Convert.ToBoolean((string)e.Value))
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Loading Tenant {TenantId} {Error}", _tenantid, ex.Message);
|
_integratedsecurity = true;
|
||||||
AddModuleMessage("Error Loading Tenant", MessageType.Error);
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_integratedsecurity = false;
|
||||||
|
}
|
||||||
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void ThemeChanged(ChangeEventArgs e)
|
private async void ThemeChanged(ChangeEventArgs e)
|
||||||
|
@ -191,7 +271,7 @@ else
|
||||||
{
|
{
|
||||||
_panelayouts = new Dictionary<string, string>();
|
_panelayouts = new Dictionary<string, string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
@ -203,105 +283,116 @@ else
|
||||||
|
|
||||||
private async Task SaveSite()
|
private async Task SaveSite()
|
||||||
{
|
{
|
||||||
if (_tenantid != "-1" && _name != string.Empty && _urls != string.Empty && !string.IsNullOrEmpty(_themetype) && (_panelayouts.Count == 0 || !string.IsNullOrEmpty(_layouttype)) && !string.IsNullOrEmpty(_containertype) && !string.IsNullOrEmpty(_sitetemplatetype))
|
if (_tenantid != "-" && _name != string.Empty && _urls != string.Empty && !string.IsNullOrEmpty(_themetype) && (_panelayouts.Count == 0 || !string.IsNullOrEmpty(_layouttype)) && !string.IsNullOrEmpty(_containertype) && !string.IsNullOrEmpty(_sitetemplatetype))
|
||||||
{
|
{
|
||||||
var unique = true;
|
var duplicates = new List<string>();
|
||||||
var aliases = await AliasService.GetAliasesAsync();
|
var aliases = await AliasService.GetAliasesAsync();
|
||||||
foreach (string name in _urls.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
|
foreach (string name in _urls.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
|
||||||
{
|
{
|
||||||
if (aliases.Exists(item => item.Name == name))
|
if (aliases.Exists(item => item.Name == name))
|
||||||
{
|
{
|
||||||
unique = false;
|
duplicates.Add(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unique)
|
if (duplicates.Count == 0)
|
||||||
{
|
{
|
||||||
var isvalid = true;
|
InstallConfig config = new InstallConfig();
|
||||||
|
|
||||||
if (!_isinitialized)
|
if (_tenantid == "+")
|
||||||
{
|
{
|
||||||
var user = new User();
|
if (!string.IsNullOrEmpty(_tenantname) && _tenants.FirstOrDefault(item => item.Name == _tenantname) == null)
|
||||||
user.SiteId = PageState.Site.SiteId;
|
|
||||||
user.Username = _username;
|
|
||||||
user.Password = _password;
|
|
||||||
user = await UserService.LoginUserAsync(user, false, false);
|
|
||||||
isvalid = user.IsAuthenticated;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isvalid)
|
|
||||||
{
|
|
||||||
ShowProgressIndicator();
|
|
||||||
|
|
||||||
aliases = new List<Alias>();
|
|
||||||
_urls = _urls.Replace("\n", ",");
|
|
||||||
|
|
||||||
foreach (string name in _urls.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
|
|
||||||
{
|
|
||||||
var alias = new Alias();
|
|
||||||
alias.Name = name;
|
|
||||||
alias.TenantId = int.Parse(_tenantid);
|
|
||||||
alias.SiteId = -1;
|
|
||||||
alias = await AliasService.AddAliasAsync(alias);
|
|
||||||
aliases.Add(alias);
|
|
||||||
}
|
|
||||||
|
|
||||||
var site = new Site();
|
|
||||||
site.TenantId = int.Parse(_tenantid);
|
|
||||||
site.Name = _name;
|
|
||||||
site.LogoFileId = null;
|
|
||||||
site.FaviconFileId = null;
|
|
||||||
site.DefaultThemeType = _themetype;
|
|
||||||
site.DefaultLayoutType = (_layouttype == null ? string.Empty : _layouttype);
|
|
||||||
site.DefaultContainerType = _containertype;
|
|
||||||
site.PwaIsEnabled = false;
|
|
||||||
site.PwaAppIconFileId = null;
|
|
||||||
site.PwaSplashIconFileId = null;
|
|
||||||
site.AllowRegistration = false;
|
|
||||||
site.SiteTemplateType = _sitetemplatetype;
|
|
||||||
site = await SiteService.AddSiteAsync(site, aliases[0]);
|
|
||||||
|
|
||||||
foreach (Alias alias in aliases)
|
|
||||||
{
|
|
||||||
alias.SiteId = site.SiteId;
|
|
||||||
await AliasService.UpdateAliasAsync(alias);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_isinitialized)
|
|
||||||
{
|
{
|
||||||
|
// validate host credentials
|
||||||
var user = new User();
|
var user = new User();
|
||||||
user.SiteId = site.SiteId;
|
user.SiteId = PageState.Site.SiteId;
|
||||||
user.Username = _username;
|
user.Username = Constants.HostUser;
|
||||||
user.Password = _password;
|
user.Password = _hostpassword;
|
||||||
user.Email = PageState.User.Email;
|
user = await UserService.LoginUserAsync(user, false, false);
|
||||||
user.DisplayName = PageState.User.DisplayName;
|
if (user.IsAuthenticated)
|
||||||
user = await UserService.AddUserAsync(user, aliases[0]);
|
|
||||||
|
|
||||||
if (user != null)
|
|
||||||
{
|
{
|
||||||
var tenant = _tenants.FirstOrDefault(item => item.TenantId == int.Parse(_tenantid));
|
if (!string.IsNullOrEmpty(_server) && !string.IsNullOrEmpty(_database))
|
||||||
if (tenant != null)
|
|
||||||
{
|
{
|
||||||
tenant.IsInitialized = true;
|
var connectionString = string.Empty;
|
||||||
await TenantService.UpdateTenantAsync(tenant);
|
if (_databasetype == "LocalDB")
|
||||||
|
{
|
||||||
|
connectionString = "Data Source=" + _server + ";AttachDbFilename=|DataDirectory|\\" + _database + ".mdf;Initial Catalog=" + _database + ";Integrated Security=SSPI;";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
connectionString = "Data Source=" + _server + ";Initial Catalog=" + _database + ";";
|
||||||
|
|
||||||
|
if (_integratedsecurity)
|
||||||
|
{
|
||||||
|
connectionString += "Integrated Security=SSPI;";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
connectionString += "User ID=" + _username + ";Password=" + _password;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
config.ConnectionString = connectionString;
|
||||||
|
config.HostPassword = _hostpassword;
|
||||||
|
config.HostEmail = user.Email;
|
||||||
|
config.HostName = user.DisplayName;
|
||||||
|
config.TenantName = _tenantname;
|
||||||
|
config.IsNewTenant = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AddModuleMessage("You Must Specify A Server And Database", MessageType.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AddModuleMessage("Invalid Host Password", MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AddModuleMessage("Tenant Name Is Missing Or Already Exists", MessageType.Error);
|
||||||
}
|
}
|
||||||
|
|
||||||
await Log(aliases[0], LogLevel.Information, string.Empty, null, "Site Created {Site}", site);
|
|
||||||
|
|
||||||
var uri = new Uri(NavigationManager.Uri);
|
|
||||||
NavigationManager.NavigateTo(uri.Scheme + "://" + aliases[0].Name, true);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await logger.LogError("Invalid Password Entered For Host {Username}", _username);
|
var tenant = _tenants.FirstOrDefault(item => item.TenantId == int.Parse(_tenantid));
|
||||||
AddModuleMessage("Invalid Host Password", MessageType.Error);
|
if (tenant != null)
|
||||||
|
{
|
||||||
|
config.TenantName = tenant.Name;
|
||||||
|
config.ConnectionString= tenant.DBConnectionString;
|
||||||
|
config.IsNewTenant = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(config.TenantName))
|
||||||
|
{
|
||||||
|
config.SiteName = _name;
|
||||||
|
config.Aliases = _urls.Replace("\n", ",");
|
||||||
|
config.DefaultTheme = _themetype;
|
||||||
|
config.DefaultLayout = _layouttype;
|
||||||
|
config.DefaultContainer = _containertype;
|
||||||
|
config.SiteTemplate = _sitetemplatetype;
|
||||||
|
|
||||||
|
ShowProgressIndicator();
|
||||||
|
|
||||||
|
var installation = await InstallationService.Install(config);
|
||||||
|
if (installation.Success)
|
||||||
|
{
|
||||||
|
var aliasname = config.Aliases.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)[0];
|
||||||
|
var uri = new Uri(NavigationManager.Uri);
|
||||||
|
NavigationManager.NavigateTo(uri.Scheme + "://" + aliasname, true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await logger.LogError("Error Creating Site {Error}", installation.Message);
|
||||||
|
AddModuleMessage(installation.Message, MessageType.Error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
AddModuleMessage("An Alias Specified Has Already Been Used For Another Site", MessageType.Warning);
|
AddModuleMessage(string.Join(", ", duplicates.ToArray()) + " Already Used For Another Site", MessageType.Warning);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -1,156 +0,0 @@
|
||||||
@namespace Oqtane.Modules.Admin.Tenants
|
|
||||||
@inherits ModuleBase
|
|
||||||
@inject NavigationManager NavigationManager
|
|
||||||
@inject ITenantService TenantService
|
|
||||||
@inject IInstallationService InstallationService
|
|
||||||
|
|
||||||
<table class="table table-borderless">
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<Label For="name" HelpText="Enter the name for the tenant">Name: </Label>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<input id="name" class="form-control" @bind="@name" />
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<Label For="databaseType" HelpText="Select the database type for the tenant">Database Type: </Label>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<select id="databaseType" class="custom-select" @bind="@type">
|
|
||||||
<option value="LocalDB">Local Database</option>
|
|
||||||
<option value="SQLServer">SQL Server</option>
|
|
||||||
</select>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<Label For="server" HelpText="Enter the server for the tenant">Server: </Label>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<input id="server" type="text" class="form-control" @bind="@server" />
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<Label For="database" HelpText="Enter the database for the tenant">Database: </Label>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<input id="database" type="text" class="form-control" @bind="@database" />
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<Label For="integratedSecurity" HelpText="Select if you want integrated security or not">Integrated Security: </Label>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<select id="integratedSecurity" class="custom-select" @onchange="SetIntegratedSecurity">
|
|
||||||
<option value="true" selected>True</option>
|
|
||||||
<option value="false">False</option>
|
|
||||||
</select>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr style="@integratedsecurity">
|
|
||||||
<td>
|
|
||||||
<Label For="username" HelpText="Enter the username for the integrated security">Username: </Label>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<input id="username" type="text" class="form-control" @bind="@username" />
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr style="@integratedsecurity">
|
|
||||||
<td>
|
|
||||||
<Label For="password" HelpText="Enter the password for the integrated security">Password: </Label>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<input id="password" type="password" class="form-control" @bind="@password" />
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
<button type="button" class="btn btn-success" @onclick="SaveTenant">Save</button>
|
|
||||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
|
|
||||||
|
|
||||||
@code {
|
|
||||||
private string name = string.Empty;
|
|
||||||
private string type = "LocalDB";
|
|
||||||
private string server = "(LocalDb)\\MSSQLLocalDB";
|
|
||||||
private string database = "Oqtane-" + DateTime.UtcNow.ToString("yyyyMMddHHmm");
|
|
||||||
private string username = string.Empty;
|
|
||||||
private string password = string.Empty;
|
|
||||||
private string schema = string.Empty;
|
|
||||||
private string integratedsecurity = "display: none;";
|
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
|
||||||
|
|
||||||
private void SetIntegratedSecurity(ChangeEventArgs e)
|
|
||||||
{
|
|
||||||
if (Convert.ToBoolean((string)e.Value))
|
|
||||||
{
|
|
||||||
integratedsecurity = "display: none;";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
integratedsecurity = string.Empty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task SaveTenant()
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrEmpty(name))
|
|
||||||
{
|
|
||||||
ShowProgressIndicator();
|
|
||||||
|
|
||||||
var connectionString = string.Empty;
|
|
||||||
if (type == "LocalDB")
|
|
||||||
{
|
|
||||||
connectionString = "Data Source=" + server + ";AttachDbFilename=|DataDirectory|\\" + database + ".mdf;Initial Catalog=" + database + ";Integrated Security=SSPI;";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
connectionString = "Data Source=" + server + ";Initial Catalog=" + database + ";";
|
|
||||||
|
|
||||||
if (integratedsecurity == "display: none;")
|
|
||||||
{
|
|
||||||
connectionString += "Integrated Security=SSPI;";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
connectionString += "User ID=" + username + ";Password=" + password;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var config = new InstallConfig
|
|
||||||
{
|
|
||||||
IsMaster = false,
|
|
||||||
ConnectionString = connectionString,
|
|
||||||
};
|
|
||||||
|
|
||||||
var installation = await InstallationService.Install(config);
|
|
||||||
if (installation.Success)
|
|
||||||
{
|
|
||||||
//TODO : Move to Database Manager
|
|
||||||
var tenant = new Tenant
|
|
||||||
{
|
|
||||||
Name = name,
|
|
||||||
DBConnectionString = connectionString,
|
|
||||||
IsInitialized = false
|
|
||||||
};
|
|
||||||
await TenantService.AddTenantAsync(tenant);
|
|
||||||
await logger.LogInformation("Tenant Created {Tenant}", tenant);
|
|
||||||
|
|
||||||
NavigationManager.NavigateTo(NavigateUrl());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await logger.LogError("Error Creating Tenant {Error}", installation.Message);
|
|
||||||
AddModuleMessage(installation.Message, MessageType.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
AddModuleMessage("You Must Provide A Name For The Tenant", MessageType.Warning);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -6,7 +6,7 @@
|
||||||
<table class="table table-borderless">
|
<table class="table table-borderless">
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<Label For="name" HelpText="Enter the nameof the tenant">Name: </Label>
|
<Label For="name" HelpText="The name of the tenant">Name: </Label>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
@if (name == Constants.MasterTenant)
|
@if (name == Constants.MasterTenant)
|
||||||
|
@ -21,10 +21,10 @@
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<Label For="connectionString" HelpText="Enter the connection string for the tenant">Connection String: </Label>
|
<Label For="connectionstring" HelpText="The database connection string">Connection String: </Label>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<input id="integratedSecurity" class="form-control" @bind="@connectionstring" />
|
<textarea id="connectionstring" class="form-control" @bind="@connectionstring" rows="3" readonly></textarea>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
@namespace Oqtane.Modules.Admin.Tenants
|
@namespace Oqtane.Modules.Admin.Tenants
|
||||||
@inherits ModuleBase
|
@inherits ModuleBase
|
||||||
@inject ITenantService TenantService
|
@inject ITenantService TenantService
|
||||||
|
@inject IAliasService AliasService
|
||||||
|
|
||||||
@if (tenants == null)
|
@if (tenants == null)
|
||||||
{
|
{
|
||||||
|
@ -8,8 +9,6 @@
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<ActionLink Action="Add" Text="Add Tenant" />
|
|
||||||
|
|
||||||
<Pager Items="@tenants">
|
<Pager Items="@tenants">
|
||||||
<Header>
|
<Header>
|
||||||
<th> </th>
|
<th> </th>
|
||||||
|
@ -39,9 +38,25 @@ else
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await TenantService.DeleteTenantAsync(Tenant.TenantId);
|
string message = string.Empty;
|
||||||
await logger.LogInformation("Tenant Deleted {Tenant}", Tenant);
|
var aliases = await AliasService.GetAliasesAsync();
|
||||||
StateHasChanged();
|
foreach (var alias in aliases)
|
||||||
|
{
|
||||||
|
if (alias.TenantId == Tenant.TenantId)
|
||||||
|
{
|
||||||
|
message += ", " + alias.Name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (string.IsNullOrEmpty(message))
|
||||||
|
{
|
||||||
|
await TenantService.DeleteTenantAsync(Tenant.TenantId);
|
||||||
|
await logger.LogInformation("Tenant Deleted {Tenant}", Tenant);
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AddModuleMessage("Tenant Cannot Be Deleted Until The Following Sites Are Deleted: " + message.Substring(2), MessageType.Warning);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
|
@ -7,116 +7,116 @@
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="mx-auto text-center">
|
<div class="mx-auto text-center">
|
||||||
<img src="oqtane.png"/>
|
<img src="oqtane.png" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr class="app-rule"/>
|
<hr class="app-rule" />
|
||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col text-center">
|
<div class="col text-center">
|
||||||
<h2>Database Configuration</h2><br/>
|
<h2>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">Database Type: </label>
|
<label class="control-label" style="font-weight: bold">Database Type: </label>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<select class="custom-select" @bind="@_databaseType">
|
<select class="custom-select" @bind="@_databaseType">
|
||||||
<option value="LocalDB">Local Database</option>
|
<option value="LocalDB">Local Database</option>
|
||||||
<option value="SQLServer">SQL Server</option>
|
<option value="SQLServer">SQL Server</option>
|
||||||
</select>
|
</select>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<label class="control-label" style="font-weight: bold">Server: </label>
|
<label class="control-label" style="font-weight: bold">Server: </label>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<input type="text" class="form-control" @bind="@_serverName"/>
|
<input type="text" class="form-control" @bind="@_serverName" />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<label class="control-label" style="font-weight: bold">Database: </label>
|
<label class="control-label" style="font-weight: bold">Database: </label>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<input type="text" class="form-control" @bind="@_databaseName"/>
|
<input type="text" class="form-control" @bind="@_databaseName" />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<label class="control-label" style="font-weight: bold">Integrated Security: </label>
|
<label class="control-label" style="font-weight: bold">Integrated Security: </label>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<select class="custom-select" @onchange="SetIntegratedSecurity">
|
<select class="custom-select" @onchange="SetIntegratedSecurity">
|
||||||
<option value="true" selected>True</option>
|
<option value="true" selected>True</option>
|
||||||
<option value="false">False</option>
|
<option value="false">False</option>
|
||||||
</select>
|
</select>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr style="@_integratedSecurityDisplay">
|
<tr style="@_integratedSecurityDisplay">
|
||||||
<td>
|
<td>
|
||||||
<label class="control-label" style="font-weight: bold">Username: </label>
|
<label class="control-label" style="font-weight: bold">Username: </label>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<input type="text" class="form-control" @bind="@_username"/>
|
<input type="text" class="form-control" @bind="@_username" />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr style="@_integratedSecurityDisplay">
|
<tr style="@_integratedSecurityDisplay">
|
||||||
<td>
|
<td>
|
||||||
<label class="control-label" style="font-weight: bold">Password: </label>
|
<label class="control-label" style="font-weight: bold">Password: </label>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<input type="password" class="form-control" @bind="@_password"/>
|
<input type="password" class="form-control" @bind="@_password" />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div class="col text-center">
|
<div class="col text-center">
|
||||||
<h2>Application Administrator</h2><br/>
|
<h2>Application Administrator</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">Username: </label>
|
<label class="control-label" style="font-weight: bold">Username: </label>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<input type="text" class="form-control" @bind="@_hostUsername" readonly/>
|
<input type="text" class="form-control" @bind="@_hostUsername" readonly />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<label class="control-label" style="font-weight: bold">Password: </label>
|
<label class="control-label" style="font-weight: bold">Password: </label>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<input type="password" class="form-control" @bind="@_hostPassword"/>
|
<input type="password" class="form-control" @bind="@_hostPassword" />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<label class="control-label" style="font-weight: bold">Confirm: </label>
|
<label class="control-label" style="font-weight: bold">Confirm: </label>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<input type="password" class="form-control" @bind="@_confirmPassword"/>
|
<input type="password" class="form-control" @bind="@_confirmPassword" />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<label class="control-label" style="font-weight: bold">Email: </label>
|
<label class="control-label" style="font-weight: bold">Email: </label>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<input type="text" class="form-control" @bind="@_hostEmail"/>
|
<input type="text" class="form-control" @bind="@_hostEmail" />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr class="app-rule"/>
|
<hr class="app-rule" />
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="mx-auto text-center">
|
<div class="mx-auto text-center">
|
||||||
<button type="button" class="btn btn-success" @onclick="Install">Install Now</button><br/><br/>
|
<button type="button" class="btn btn-success" @onclick="Install">Install Now</button><br /><br />
|
||||||
@((MarkupString) _message)
|
@((MarkupString) _message)
|
||||||
</div>
|
</div>
|
||||||
<div class="app-progress-indicator" style="@_loadingDisplay"></div>
|
<div class="app-progress-indicator" style="@_loadingDisplay"></div>
|
||||||
|
@ -146,7 +146,7 @@
|
||||||
|
|
||||||
private async Task Install()
|
private async Task Install()
|
||||||
{
|
{
|
||||||
if (_hostUsername != "" && _hostPassword.Length >= 6 && _hostPassword == _confirmPassword && _hostEmail != "")
|
if (_serverName != "" && _databaseName != "" && _hostUsername != "" && _hostPassword.Length >= 6 && _hostPassword == _confirmPassword && _hostEmail != "")
|
||||||
{
|
{
|
||||||
_loadingDisplay = "";
|
_loadingDisplay = "";
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
|
@ -169,19 +169,24 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Uri uri = new Uri(NavigationManager.Uri);
|
||||||
|
|
||||||
var config = new InstallConfig
|
var config = new InstallConfig
|
||||||
{
|
{
|
||||||
ConnectionString = connectionstring,
|
ConnectionString = connectionstring,
|
||||||
HostUser = _hostUsername,
|
Aliases = uri.Authority,
|
||||||
HostEmail = _hostEmail,
|
HostEmail = _hostEmail,
|
||||||
Password = _hostPassword,
|
HostPassword = _hostPassword,
|
||||||
IsMaster = true,
|
HostName = Constants.HostUser,
|
||||||
|
TenantName = Constants.MasterTenant,
|
||||||
|
IsNewTenant = true,
|
||||||
|
SiteName = Constants.DefaultSite
|
||||||
};
|
};
|
||||||
|
|
||||||
var installation = await InstallationService.Install(config);
|
var installation = await InstallationService.Install(config);
|
||||||
if (installation.Success)
|
if (installation.Success)
|
||||||
{
|
{
|
||||||
NavigationManager.NavigateTo("", true);
|
NavigationManager.NavigateTo(uri.Scheme + "://" + uri.Authority, true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -73,13 +73,13 @@ namespace Oqtane.UI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task IncludeLink(string id, string rel, string url, string type)
|
public Task IncludeLink(string id, string rel, string url, string type, string integrity, string crossorigin)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_jsRuntime.InvokeAsync<string>(
|
_jsRuntime.InvokeAsync<string>(
|
||||||
"interop.includeLink",
|
"interop.includeLink",
|
||||||
id, rel, url, type);
|
id, rel, url, type, integrity, crossorigin);
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
|
@ -88,13 +88,13 @@ namespace Oqtane.UI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task IncludeScript(string id, string src, string content, string location)
|
public Task IncludeScript(string id, string src, string content, string location, string integrity, string crossorigin)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_jsRuntime.InvokeAsync<string>(
|
_jsRuntime.InvokeAsync<string>(
|
||||||
"interop.includeScript",
|
"interop.includeScript",
|
||||||
id, src, content, location);
|
id, src, content, location, integrity, crossorigin);
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
}
|
}
|
||||||
if (PageState.Site.FaviconFileId != null)
|
if (PageState.Site.FaviconFileId != null)
|
||||||
{
|
{
|
||||||
await interop.IncludeLink("fav-icon", "shortcut icon", Utilities.ContentUrl(PageState.Alias.Path, PageState.Site.FaviconFileId.Value), "image/x-icon");
|
await interop.IncludeLink("fav-icon", "shortcut icon", Utilities.ContentUrl(PageState.Alias.Path, PageState.Site.FaviconFileId.Value), "image/x-icon", "", "");
|
||||||
}
|
}
|
||||||
if (PageState.Site.PwaIsEnabled)
|
if (PageState.Site.PwaIsEnabled)
|
||||||
{
|
{
|
||||||
|
@ -74,7 +74,7 @@
|
||||||
"document.getElementById('pwa-manifest').setAttribute('href', url); " +
|
"document.getElementById('pwa-manifest').setAttribute('href', url); " +
|
||||||
"} " +
|
"} " +
|
||||||
", 1000);";
|
", 1000);";
|
||||||
await interop.IncludeScript("pwa-manifestscript", "", manifest, "body");
|
await interop.IncludeScript("pwa-manifestscript", "", manifest, "body", "", "");
|
||||||
|
|
||||||
// service worker must be in root of site
|
// service worker must be in root of site
|
||||||
string serviceworker = "if ('serviceWorker' in navigator) { " +
|
string serviceworker = "if ('serviceWorker' in navigator) { " +
|
||||||
|
@ -84,6 +84,6 @@
|
||||||
"console.log('ServiceWorker Registration Failed ', err); " +
|
"console.log('ServiceWorker Registration Failed ', err); " +
|
||||||
"}); " +
|
"}); " +
|
||||||
"}";
|
"}";
|
||||||
await interop.IncludeScript("pwa-serviceworker", "", serviceworker, "body");
|
await interop.IncludeScript("pwa-serviceworker", "", serviceworker, "body", "", "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,6 @@ using Oqtane.Models;
|
||||||
using Oqtane.Shared;
|
using Oqtane.Shared;
|
||||||
using Oqtane.Infrastructure;
|
using Oqtane.Infrastructure;
|
||||||
|
|
||||||
// ReSharper disable StringIndexOfIsCultureSpecific.1
|
|
||||||
|
|
||||||
namespace Oqtane.Controllers
|
namespace Oqtane.Controllers
|
||||||
{
|
{
|
||||||
[Route("{site}/api/[controller]")]
|
[Route("{site}/api/[controller]")]
|
||||||
|
@ -14,9 +12,9 @@ namespace Oqtane.Controllers
|
||||||
{
|
{
|
||||||
private readonly IConfigurationRoot _config;
|
private readonly IConfigurationRoot _config;
|
||||||
private readonly IInstallationManager _installationManager;
|
private readonly IInstallationManager _installationManager;
|
||||||
private readonly DatabaseManager _databaseManager;
|
private readonly IDatabaseManager _databaseManager;
|
||||||
|
|
||||||
public InstallationController(IConfigurationRoot config, IInstallationManager installationManager, DatabaseManager databaseManager)
|
public InstallationController(IConfigurationRoot config, IInstallationManager installationManager, IDatabaseManager databaseManager)
|
||||||
{
|
{
|
||||||
_config = config;
|
_config = config;
|
||||||
_installationManager = installationManager;
|
_installationManager = installationManager;
|
||||||
|
@ -27,33 +25,17 @@ namespace Oqtane.Controllers
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public Installation Post([FromBody] InstallConfig config)
|
public Installation Post([FromBody] InstallConfig config)
|
||||||
{
|
{
|
||||||
//TODO Security ????
|
|
||||||
var installation = new Installation {Success = false, Message = ""};
|
var installation = new Installation {Success = false, Message = ""};
|
||||||
|
|
||||||
if (ModelState.IsValid && (!_databaseManager.IsInstalled || !config.IsMaster))
|
if (ModelState.IsValid && (User.IsInRole(Constants.HostRole) || string.IsNullOrEmpty(_config.GetConnectionString(SettingKeys.ConnectionStringKey))))
|
||||||
{
|
{
|
||||||
bool master = config.IsMaster;
|
installation = _databaseManager.Install(config);
|
||||||
|
}
|
||||||
config.Alias = config.Alias ?? HttpContext.Request.Host.Value;
|
else
|
||||||
var result = DatabaseManager.InstallDatabase(config);
|
{
|
||||||
|
installation.Message = "Installation Not Authorized";
|
||||||
if (result.Success)
|
|
||||||
{
|
|
||||||
if (master)
|
|
||||||
{
|
|
||||||
_config.Reload();
|
|
||||||
}
|
|
||||||
|
|
||||||
_databaseManager.BuildDefaultSite(config.Password, config.HostEmail);
|
|
||||||
installation.Success = true;
|
|
||||||
return installation;
|
|
||||||
}
|
|
||||||
|
|
||||||
installation.Message = result.Message;
|
|
||||||
return installation;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
installation.Message = "Application Is Already Installed";
|
|
||||||
return installation;
|
return installation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,12 +43,8 @@ namespace Oqtane.Controllers
|
||||||
[HttpGet("installed")]
|
[HttpGet("installed")]
|
||||||
public Installation IsInstalled()
|
public Installation IsInstalled()
|
||||||
{
|
{
|
||||||
var installation = new Installation {Success = false, Message = ""};
|
bool isInstalled = _databaseManager.IsInstalled();
|
||||||
|
return new Installation {Success = isInstalled, Message = string.Empty};
|
||||||
installation.Success = _databaseManager.IsInstalled;
|
|
||||||
installation.Message = _databaseManager.Message;
|
|
||||||
|
|
||||||
return installation;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("upgrade")]
|
[HttpGet("upgrade")]
|
||||||
|
|
|
@ -22,16 +22,18 @@ namespace Oqtane.Controllers
|
||||||
{
|
{
|
||||||
private readonly IModuleDefinitionRepository _moduleDefinitions;
|
private readonly IModuleDefinitionRepository _moduleDefinitions;
|
||||||
private readonly IModuleRepository _modules;
|
private readonly IModuleRepository _modules;
|
||||||
|
private readonly ITenantRepository _tenants;
|
||||||
private readonly IUserPermissions _userPermissions;
|
private readonly IUserPermissions _userPermissions;
|
||||||
private readonly IInstallationManager _installationManager;
|
private readonly IInstallationManager _installationManager;
|
||||||
private readonly IWebHostEnvironment _environment;
|
private readonly IWebHostEnvironment _environment;
|
||||||
private readonly IServiceProvider _serviceProvider;
|
private readonly IServiceProvider _serviceProvider;
|
||||||
private readonly ILogManager _logger;
|
private readonly ILogManager _logger;
|
||||||
|
|
||||||
public ModuleDefinitionController(IModuleDefinitionRepository moduleDefinitions, IModuleRepository modules, IUserPermissions userPermissions, IInstallationManager installationManager, IWebHostEnvironment environment, IServiceProvider serviceProvider, ILogManager logger)
|
public ModuleDefinitionController(IModuleDefinitionRepository moduleDefinitions, IModuleRepository modules,ITenantRepository tenants, IUserPermissions userPermissions, IInstallationManager installationManager, IWebHostEnvironment environment, IServiceProvider serviceProvider, ILogManager logger)
|
||||||
{
|
{
|
||||||
_moduleDefinitions = moduleDefinitions;
|
_moduleDefinitions = moduleDefinitions;
|
||||||
_modules = modules;
|
_modules = modules;
|
||||||
|
_tenants = tenants;
|
||||||
_userPermissions = userPermissions;
|
_userPermissions = userPermissions;
|
||||||
_installationManager = installationManager;
|
_installationManager = installationManager;
|
||||||
_environment = environment;
|
_environment = environment;
|
||||||
|
@ -104,8 +106,18 @@ namespace Oqtane.Controllers
|
||||||
Type moduletype = Type.GetType(moduledefinition.ServerManagerType);
|
Type moduletype = Type.GetType(moduledefinition.ServerManagerType);
|
||||||
if (moduletype != null && moduletype.GetInterface("IInstallable") != null)
|
if (moduletype != null && moduletype.GetInterface("IInstallable") != null)
|
||||||
{
|
{
|
||||||
var moduleobject = ActivatorUtilities.CreateInstance(_serviceProvider, moduletype);
|
foreach (Tenant tenant in _tenants.GetTenants())
|
||||||
((IInstallable)moduleobject).Uninstall();
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var moduleobject = ActivatorUtilities.CreateInstance(_serviceProvider, moduletype);
|
||||||
|
((IInstallable)moduleobject).Uninstall(tenant);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// an error occurred executing the uninstall
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,6 +202,11 @@ namespace Oqtane.Controllers
|
||||||
module.ModuleDefinitionName = moduleDefinition.ModuleDefinitionName;
|
module.ModuleDefinitionName = moduleDefinition.ModuleDefinitionName;
|
||||||
_modules.UpdateModule(module);
|
_modules.UpdateModule(module);
|
||||||
|
|
||||||
|
if (moduleDefinition.Template == "internal")
|
||||||
|
{
|
||||||
|
// need logic to add embedded scripts to Oqtane.Server.csproj - also you need to remove them on uninstall
|
||||||
|
}
|
||||||
|
|
||||||
_installationManager.RestartApplication();
|
_installationManager.RestartApplication();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Data;
|
using System.Data;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using DbUp;
|
using DbUp;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
@ -15,193 +15,172 @@ using Oqtane.Extensions;
|
||||||
using Oqtane.Models;
|
using Oqtane.Models;
|
||||||
using Oqtane.Repository;
|
using Oqtane.Repository;
|
||||||
using Oqtane.Shared;
|
using Oqtane.Shared;
|
||||||
|
using Oqtane.Enums;
|
||||||
using File = System.IO.File;
|
using File = System.IO.File;
|
||||||
|
|
||||||
namespace Oqtane.Infrastructure
|
namespace Oqtane.Infrastructure
|
||||||
{
|
{
|
||||||
public class DatabaseManager
|
public class DatabaseManager : IDatabaseManager
|
||||||
{
|
{
|
||||||
private readonly IConfigurationRoot _config;
|
private readonly IConfigurationRoot _config;
|
||||||
private readonly IServiceScopeFactory _serviceScopeFactory;
|
private readonly IServiceScopeFactory _serviceScopeFactory;
|
||||||
private bool _isInstalled;
|
private readonly IMemoryCache _cache;
|
||||||
|
|
||||||
|
|
||||||
public DatabaseManager(IConfigurationRoot config, IServiceScopeFactory serviceScopeFactory)
|
public DatabaseManager(IConfigurationRoot config, IServiceScopeFactory serviceScopeFactory, IMemoryCache cache)
|
||||||
{
|
{
|
||||||
_config = config;
|
_config = config;
|
||||||
_serviceScopeFactory = serviceScopeFactory;
|
_serviceScopeFactory = serviceScopeFactory;
|
||||||
|
_cache = cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Message { get; set; }
|
public bool IsInstalled()
|
||||||
|
|
||||||
public void StartupMigration()
|
|
||||||
{
|
{
|
||||||
var defaultConnectionString = _config.GetConnectionString(SettingKeys.ConnectionStringKey);
|
var defaultConnectionString = NormalizeConnectionString(_config.GetConnectionString(SettingKeys.ConnectionStringKey));
|
||||||
var defaultAlias = GetInstallationConfig(SettingKeys.DefaultAliasKey, string.Empty);
|
|
||||||
var dataDirectory = AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString();
|
|
||||||
|
|
||||||
//create data directory if does not exists
|
|
||||||
if (!Directory.Exists(dataDirectory)) Directory.CreateDirectory(dataDirectory);
|
|
||||||
|
|
||||||
// if no values specified, fallback to IDE installer
|
|
||||||
if (string.IsNullOrEmpty(defaultConnectionString))
|
|
||||||
{
|
|
||||||
IsInstalled = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var freshInstall = !IsMasterInstalled(defaultConnectionString);
|
|
||||||
var password = GetInstallationConfig(SettingKeys.HostPasswordKey, String.Empty);
|
|
||||||
var email = GetInstallationConfig(SettingKeys.HostEmailKey, String.Empty);
|
|
||||||
if (freshInstall && (string.IsNullOrEmpty(password) || string.IsNullOrEmpty(email) || string.IsNullOrEmpty(defaultAlias)))
|
|
||||||
{
|
|
||||||
IsInstalled = false;
|
|
||||||
Message = "Incomplete startup install configuration";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = MasterMigration(defaultConnectionString, defaultAlias, null, true);
|
|
||||||
IsInstalled = result.Success;
|
|
||||||
|
|
||||||
if (result.Success)
|
|
||||||
{
|
|
||||||
WriteVersionInfo(defaultConnectionString);
|
|
||||||
TenantMigration(defaultConnectionString, dataDirectory);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_isInstalled && !IsDefaultSiteInstalled(defaultConnectionString))
|
|
||||||
{
|
|
||||||
BuildDefaultSite(password,email);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsInstalled
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (!_isInstalled) _isInstalled = CheckInstallState();
|
|
||||||
|
|
||||||
return _isInstalled;
|
|
||||||
}
|
|
||||||
set => _isInstalled = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool CheckInstallState()
|
|
||||||
{
|
|
||||||
var defaultConnectionString = _config.GetConnectionString(SettingKeys.ConnectionStringKey);
|
|
||||||
var result = !string.IsNullOrEmpty(defaultConnectionString);
|
var result = !string.IsNullOrEmpty(defaultConnectionString);
|
||||||
if (result)
|
if (result)
|
||||||
{
|
{
|
||||||
using (var scope = _serviceScopeFactory.CreateScope())
|
using (var scope = _serviceScopeFactory.CreateScope())
|
||||||
{
|
{
|
||||||
var dbContext = scope.ServiceProvider.GetRequiredService<MasterDBContext>();
|
var db = scope.ServiceProvider.GetRequiredService<MasterDBContext>();
|
||||||
result = dbContext.Database.CanConnect();
|
result = db.Database.CanConnect();
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
result = db.Tenant.Any();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (result)
|
}
|
||||||
{
|
return result;
|
||||||
//I think this is obsolete now and not accurate, maybe check presence of some table, Version ???
|
}
|
||||||
var dbUpgradeConfig = DeployChanges
|
|
||||||
.To
|
|
||||||
.SqlDatabase(defaultConnectionString)
|
|
||||||
.WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly(), s => s.Contains("Master"));
|
|
||||||
|
|
||||||
result = !dbUpgradeConfig.Build().IsUpgradeRequired();
|
public Installation Install()
|
||||||
if (!result) Message = "Master Installation Scripts Have Not Been Executed";
|
{
|
||||||
|
return Install(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Installation Install(InstallConfig install)
|
||||||
|
{
|
||||||
|
var result = new Installation { Success = false, Message = string.Empty };
|
||||||
|
|
||||||
|
// get configuration
|
||||||
|
if (install == null)
|
||||||
|
{
|
||||||
|
// startup or silent installation
|
||||||
|
install = new InstallConfig { ConnectionString = _config.GetConnectionString(SettingKeys.ConnectionStringKey), TenantName = Constants.MasterTenant, IsNewTenant = false };
|
||||||
|
|
||||||
|
if (!IsInstalled())
|
||||||
|
{
|
||||||
|
install.Aliases = GetInstallationConfig(SettingKeys.DefaultAliasKey, string.Empty);
|
||||||
|
install.HostPassword = GetInstallationConfig(SettingKeys.HostPasswordKey, string.Empty);
|
||||||
|
install.HostEmail = GetInstallationConfig(SettingKeys.HostEmailKey, string.Empty);
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(install.ConnectionString) && !string.IsNullOrEmpty(install.Aliases) && !string.IsNullOrEmpty(install.HostPassword) && !string.IsNullOrEmpty(install.HostEmail))
|
||||||
|
{
|
||||||
|
// silent install
|
||||||
|
install.HostName = Constants.HostUser;
|
||||||
|
install.SiteTemplate = GetInstallationConfig(SettingKeys.SiteTemplateKey, Constants.DefaultSiteTemplate);
|
||||||
|
install.DefaultTheme = GetInstallationConfig(SettingKeys.DefaultThemeKey, Constants.DefaultTheme);
|
||||||
|
install.DefaultLayout = GetInstallationConfig(SettingKeys.DefaultLayoutKey, Constants.DefaultLayout);
|
||||||
|
install.DefaultContainer = GetInstallationConfig(SettingKeys.DefaultContainerKey, Constants.DefaultContainer);
|
||||||
|
install.SiteName = Constants.DefaultSite;
|
||||||
|
install.IsNewTenant = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// silent installation is missing required information
|
||||||
|
install.ConnectionString = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// install wizard or add new site
|
||||||
|
if (!string.IsNullOrEmpty(install.Aliases))
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(install.SiteTemplate))
|
||||||
|
{
|
||||||
|
install.SiteTemplate = GetInstallationConfig(SettingKeys.SiteTemplateKey, Constants.DefaultSiteTemplate);
|
||||||
|
}
|
||||||
|
if (string.IsNullOrEmpty(install.DefaultTheme))
|
||||||
|
{
|
||||||
|
install.DefaultTheme = GetInstallationConfig(SettingKeys.DefaultThemeKey, Constants.DefaultTheme);
|
||||||
|
if (string.IsNullOrEmpty(install.DefaultLayout))
|
||||||
|
{
|
||||||
|
install.DefaultLayout = GetInstallationConfig(SettingKeys.DefaultLayoutKey, Constants.DefaultLayout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (string.IsNullOrEmpty(install.DefaultContainer))
|
||||||
|
{
|
||||||
|
install.DefaultContainer = GetInstallationConfig(SettingKeys.DefaultContainerKey, Constants.DefaultContainer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Message = "Database is not available";
|
result.Message = "Invalid Installation Configuration";
|
||||||
|
install.ConnectionString = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
// proceed with installation/migration
|
||||||
|
if (!string.IsNullOrEmpty(install.ConnectionString))
|
||||||
{
|
{
|
||||||
Message = "Connection string is empty";
|
result = CreateDatabase(install);
|
||||||
|
if (result.Success)
|
||||||
|
{
|
||||||
|
result = MigrateMaster(install);
|
||||||
|
if (result.Success)
|
||||||
|
{
|
||||||
|
result = CreateTenant(install);
|
||||||
|
if (result.Success)
|
||||||
|
{
|
||||||
|
result = MigrateTenants(install);
|
||||||
|
if (result.Success)
|
||||||
|
{
|
||||||
|
result = MigrateModules(install);
|
||||||
|
if (result.Success)
|
||||||
|
{
|
||||||
|
result = CreateSite(install);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Installation CreateDatabase(InstallConfig install)
|
||||||
public static string NormalizeConnectionString(string connectionString, string dataDirectory)
|
|
||||||
{
|
{
|
||||||
connectionString = connectionString
|
var result = new Installation { Success = false, Message = string.Empty };
|
||||||
.Replace("|DataDirectory|", dataDirectory);
|
|
||||||
return connectionString;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Installation InstallDatabase([NotNull] InstallConfig installConfig)
|
if (install.IsNewTenant)
|
||||||
{
|
|
||||||
var dataDirectory = AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString();
|
|
||||||
var result = new Installation {Success = false, Message = ""};
|
|
||||||
|
|
||||||
var alias = installConfig.Alias;
|
|
||||||
var connectionString = NormalizeConnectionString(installConfig.ConnectionString, dataDirectory);
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(connectionString) && !string.IsNullOrEmpty(alias))
|
|
||||||
{
|
{
|
||||||
result = MasterMigration(connectionString, alias, result, installConfig.IsMaster);
|
try
|
||||||
if (installConfig.IsMaster && result.Success)
|
|
||||||
{
|
{
|
||||||
WriteVersionInfo(connectionString);
|
//create data directory if does not exist
|
||||||
TenantMigration(connectionString, dataDirectory);
|
var dataDirectory = AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString();
|
||||||
UpdateConnectionStringSetting(connectionString);
|
if (!Directory.Exists(dataDirectory)) Directory.CreateDirectory(dataDirectory);
|
||||||
|
|
||||||
|
using (var dbc = new DbContext(new DbContextOptionsBuilder().UseSqlServer(NormalizeConnectionString(install.ConnectionString)).Options))
|
||||||
|
{
|
||||||
|
// create empty database if it does not exist
|
||||||
|
dbc.Database.EnsureCreated();
|
||||||
|
result.Success = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return result;
|
catch (Exception ex)
|
||||||
}
|
|
||||||
|
|
||||||
result = new Installation
|
|
||||||
{
|
|
||||||
Success = false,
|
|
||||||
Message = "Connection string is empty",
|
|
||||||
};
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Installation MasterMigration(string connectionString, string alias, Installation result, bool master)
|
|
||||||
{
|
|
||||||
if (result == null) result = new Installation {Success = false, Message = string.Empty};
|
|
||||||
|
|
||||||
bool firstInstall;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// create empty database if does not exists
|
|
||||||
// dbup database creation does not work correctly on localdb databases
|
|
||||||
using (var dbc = new DbContext(new DbContextOptionsBuilder().UseSqlServer(connectionString).Options))
|
|
||||||
{
|
{
|
||||||
dbc.Database.EnsureCreated();
|
result.Message = ex.Message;
|
||||||
//check for vanilla db
|
|
||||||
firstInstall = !TableExists(dbc, "SchemaVersions");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
result.Message = e.Message;
|
|
||||||
Console.WriteLine(e);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
// when alias is not specified on first install, fallback to ide
|
|
||||||
if (firstInstall && string.IsNullOrEmpty(alias)) return result;
|
|
||||||
|
|
||||||
var dbUpgradeConfig = DeployChanges
|
|
||||||
.To
|
|
||||||
.SqlDatabase(connectionString)
|
|
||||||
.WithVariable("ConnectionString", connectionString)
|
|
||||||
.WithVariable("Alias", alias)
|
|
||||||
.WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly(), s => s.Contains("Master."));
|
|
||||||
|
|
||||||
var dbUpgrade = dbUpgradeConfig.Build();
|
|
||||||
if (!dbUpgrade.IsUpgradeRequired())
|
|
||||||
{
|
|
||||||
result.Success = true;
|
|
||||||
result.Message = string.Empty;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
var upgradeResult = dbUpgrade.PerformUpgrade();
|
|
||||||
if (!upgradeResult.Successful)
|
|
||||||
{
|
|
||||||
Console.WriteLine(upgradeResult.Error.Message);
|
|
||||||
result.Message = upgradeResult.Error.Message;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
result.Success = true;
|
result.Success = true;
|
||||||
|
@ -210,74 +189,326 @@ namespace Oqtane.Infrastructure
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ModuleMigration(Assembly assembly, string connectionString)
|
private Installation MigrateMaster(InstallConfig install)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Migrating assembly {assembly.FullName}");
|
var result = new Installation { Success = false, Message = string.Empty };
|
||||||
var dbUpgradeConfig = DeployChanges.To.SqlDatabase(connectionString)
|
|
||||||
.WithScriptsEmbeddedInAssembly(assembly, s => !s.ToLower().Contains("uninstall.sql")); // scripts must be included as Embedded Resources
|
if (install.TenantName == Constants.MasterTenant)
|
||||||
var dbUpgrade = dbUpgradeConfig.Build();
|
|
||||||
if (dbUpgrade.IsUpgradeRequired())
|
|
||||||
{
|
{
|
||||||
var result = dbUpgrade.PerformUpgrade();
|
var upgradeConfig = DeployChanges
|
||||||
if (!result.Successful)
|
.To
|
||||||
|
.SqlDatabase(NormalizeConnectionString(install.ConnectionString))
|
||||||
|
.WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly(), s => s.Contains("Master."));
|
||||||
|
|
||||||
|
var upgrade = upgradeConfig.Build();
|
||||||
|
if (upgrade.IsUpgradeRequired())
|
||||||
{
|
{
|
||||||
// TODO: log result.Error.Message - problem is logger is not available here
|
var upgradeResult = upgrade.PerformUpgrade();
|
||||||
|
result.Success = upgradeResult.Successful;
|
||||||
|
if (!result.Success)
|
||||||
|
{
|
||||||
|
result.Message = upgradeResult.Error.Message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result.Success = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.Success)
|
||||||
|
{
|
||||||
|
CreateApplicationVersion(install.ConnectionString);
|
||||||
|
UpdateConnectionString(install.ConnectionString);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result.Success = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void WriteVersionInfo(string connectionString)
|
private Installation CreateTenant(InstallConfig install)
|
||||||
{
|
{
|
||||||
using (var db = new InstallationContext(connectionString))
|
var result = new Installation { Success = false, Message = string.Empty };
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(install.TenantName) && !string.IsNullOrEmpty(install.Aliases))
|
||||||
{
|
{
|
||||||
var version = db.ApplicationVersion.ToList().LastOrDefault();
|
using (var db = new InstallationContext(NormalizeConnectionString(_config.GetConnectionString(SettingKeys.ConnectionStringKey))))
|
||||||
if (version == null || version.Version != Constants.Version)
|
|
||||||
{
|
{
|
||||||
version = new ApplicationVersion {Version = Constants.Version, CreatedOn = DateTime.UtcNow};
|
Tenant tenant;
|
||||||
|
if (install.IsNewTenant)
|
||||||
|
{
|
||||||
|
tenant = new Tenant { Name = install.TenantName, DBConnectionString = DenormalizeConnectionString(install.ConnectionString), CreatedBy = "", CreatedOn = DateTime.UtcNow, ModifiedBy = "", ModifiedOn = DateTime.UtcNow };
|
||||||
|
db.Tenant.Add(tenant);
|
||||||
|
db.SaveChanges();
|
||||||
|
_cache.Remove("tenants");
|
||||||
|
|
||||||
|
if (install.TenantName == Constants.MasterTenant)
|
||||||
|
{
|
||||||
|
var job = new Job { Name = "Notification Job", JobType = "Oqtane.Infrastructure.NotificationJob, Oqtane.Server", Frequency = "m", Interval = 1, StartDate = null, EndDate = null, IsEnabled = false, IsStarted = false, IsExecuting = false, NextExecution = null, RetentionHistory = 10, CreatedBy = "", CreatedOn = DateTime.UtcNow, ModifiedBy = "", ModifiedOn = DateTime.UtcNow };
|
||||||
|
db.Job.Add(job);
|
||||||
|
db.SaveChanges();
|
||||||
|
_cache.Remove("jobs");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tenant = db.Tenant.FirstOrDefault(item => item.Name == install.TenantName);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (string aliasname in install.Aliases.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
|
||||||
|
{
|
||||||
|
var alias = new Alias { Name = aliasname, TenantId = tenant.TenantId, SiteId = -1, CreatedBy = "", CreatedOn = DateTime.UtcNow, ModifiedBy = "", ModifiedOn = DateTime.UtcNow };
|
||||||
|
db.Alias.Add(alias);
|
||||||
|
db.SaveChanges();
|
||||||
|
}
|
||||||
|
_cache.Remove("aliases");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Success = true;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Installation MigrateTenants(InstallConfig install)
|
||||||
|
{
|
||||||
|
var result = new Installation { Success = false, Message = string.Empty };
|
||||||
|
|
||||||
|
using (var db = new InstallationContext(NormalizeConnectionString(_config.GetConnectionString(SettingKeys.ConnectionStringKey))))
|
||||||
|
{
|
||||||
|
foreach (var tenant in db.Tenant.ToList())
|
||||||
|
{
|
||||||
|
var upgradeConfig = DeployChanges.To.SqlDatabase(NormalizeConnectionString(tenant.DBConnectionString))
|
||||||
|
.WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly(), s => s.Contains("Tenant"));
|
||||||
|
|
||||||
|
var upgrade = upgradeConfig.Build();
|
||||||
|
if (upgrade.IsUpgradeRequired())
|
||||||
|
{
|
||||||
|
var upgradeResult = upgrade.PerformUpgrade();
|
||||||
|
result.Success = upgradeResult.Successful;
|
||||||
|
if (!result.Success)
|
||||||
|
{
|
||||||
|
result.Message = upgradeResult.Error.Message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(result.Message))
|
||||||
|
{
|
||||||
|
result.Success = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Installation MigrateModules(InstallConfig install)
|
||||||
|
{
|
||||||
|
var result = new Installation { Success = false, Message = string.Empty };
|
||||||
|
|
||||||
|
using (var scope = _serviceScopeFactory.CreateScope())
|
||||||
|
{
|
||||||
|
var moduledefinitions = scope.ServiceProvider.GetRequiredService<IModuleDefinitionRepository>();
|
||||||
|
foreach (var moduledefinition in moduledefinitions.GetModuleDefinitions())
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(moduledefinition.ServerManagerType) && !string.IsNullOrEmpty(moduledefinition.ReleaseVersions))
|
||||||
|
{
|
||||||
|
string[] versions = moduledefinition.ReleaseVersions.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
using (var db = new InstallationContext(NormalizeConnectionString(_config.GetConnectionString(SettingKeys.ConnectionStringKey))))
|
||||||
|
{
|
||||||
|
foreach (var tenant in db.Tenant.ToList())
|
||||||
|
{
|
||||||
|
int index = Array.FindIndex(versions, item => item == moduledefinition.Version);
|
||||||
|
if (tenant.Name == install.TenantName && install.TenantName != Constants.MasterTenant)
|
||||||
|
{
|
||||||
|
index = -1;
|
||||||
|
}
|
||||||
|
if (index != (versions.Length - 1))
|
||||||
|
{
|
||||||
|
if (index == -1) index = 0;
|
||||||
|
for (int i = index; i < versions.Length; i++)
|
||||||
|
{
|
||||||
|
Type moduletype = Type.GetType(moduledefinition.ServerManagerType);
|
||||||
|
if (moduletype != null && moduletype.GetInterface("IInstallable") != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var moduleobject = ActivatorUtilities.CreateInstance(scope.ServiceProvider, moduletype);
|
||||||
|
((IInstallable)moduleobject).Install(tenant, versions[i]);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
result.Message = "An Error Occurred Installing " + moduledefinition.Name + " - " + ex.Message.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (moduledefinition.Version != versions[versions.Length - 1])
|
||||||
|
{
|
||||||
|
moduledefinition.Version = versions[versions.Length - 1];
|
||||||
|
db.Entry(moduledefinition).State = EntityState.Modified;
|
||||||
|
db.SaveChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(result.Message))
|
||||||
|
{
|
||||||
|
result.Success = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Installation CreateSite(InstallConfig install)
|
||||||
|
{
|
||||||
|
var result = new Installation { Success = false, Message = string.Empty };
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(install.TenantName) && !string.IsNullOrEmpty(install.Aliases) && !string.IsNullOrEmpty(install.SiteName))
|
||||||
|
{
|
||||||
|
using (var scope = _serviceScopeFactory.CreateScope())
|
||||||
|
{
|
||||||
|
// use the SiteState to set the Alias explicitly so the tenant can be resolved
|
||||||
|
var aliases = scope.ServiceProvider.GetRequiredService<IAliasRepository>();
|
||||||
|
string firstalias = install.Aliases.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)[0];
|
||||||
|
var alias = aliases.GetAliases().FirstOrDefault(item => item.Name == firstalias);
|
||||||
|
var siteState = scope.ServiceProvider.GetRequiredService<SiteState>();
|
||||||
|
siteState.Alias = alias;
|
||||||
|
|
||||||
|
var sites = scope.ServiceProvider.GetRequiredService<ISiteRepository>();
|
||||||
|
var site = sites.GetSites().FirstOrDefault(item => item.Name == install.SiteName);
|
||||||
|
if (site == null)
|
||||||
|
{
|
||||||
|
var tenants = scope.ServiceProvider.GetRequiredService<ITenantRepository>();
|
||||||
|
var users = scope.ServiceProvider.GetRequiredService<IUserRepository>();
|
||||||
|
var roles = scope.ServiceProvider.GetRequiredService<IRoleRepository>();
|
||||||
|
var userroles = scope.ServiceProvider.GetRequiredService<IUserRoleRepository>();
|
||||||
|
var folders = scope.ServiceProvider.GetRequiredService<IFolderRepository>();
|
||||||
|
var log = scope.ServiceProvider.GetRequiredService<ILogManager>();
|
||||||
|
var identityUserManager = scope.ServiceProvider.GetRequiredService<UserManager<IdentityUser>>();
|
||||||
|
|
||||||
|
var tenant = tenants.GetTenants().FirstOrDefault(item => item.Name == install.TenantName);
|
||||||
|
|
||||||
|
site = new Site
|
||||||
|
{
|
||||||
|
TenantId = tenant.TenantId,
|
||||||
|
Name = install.SiteName,
|
||||||
|
LogoFileId = null,
|
||||||
|
DefaultThemeType = install.DefaultTheme,
|
||||||
|
DefaultLayoutType = install.DefaultLayout,
|
||||||
|
DefaultContainerType = install.DefaultContainer,
|
||||||
|
SiteTemplateType = install.SiteTemplate
|
||||||
|
};
|
||||||
|
site = sites.AddSite(site);
|
||||||
|
|
||||||
|
IdentityUser identityUser = identityUserManager.FindByNameAsync(Constants.HostUser).GetAwaiter().GetResult();
|
||||||
|
if (identityUser == null)
|
||||||
|
{
|
||||||
|
identityUser = new IdentityUser { UserName = Constants.HostUser, Email = install.HostEmail, EmailConfirmed = true };
|
||||||
|
var create = identityUserManager.CreateAsync(identityUser, install.HostPassword).GetAwaiter().GetResult();
|
||||||
|
if (create.Succeeded)
|
||||||
|
{
|
||||||
|
var user = new User
|
||||||
|
{
|
||||||
|
SiteId = site.SiteId,
|
||||||
|
Username = Constants.HostUser,
|
||||||
|
Password = install.HostPassword,
|
||||||
|
Email = install.HostEmail,
|
||||||
|
DisplayName = install.HostName,
|
||||||
|
LastIPAddress = "",
|
||||||
|
LastLoginOn = null
|
||||||
|
};
|
||||||
|
|
||||||
|
user = users.AddUser(user);
|
||||||
|
var hostRoleId = roles.GetRoles(user.SiteId, true).FirstOrDefault(item => item.Name == Constants.HostRole)?.RoleId ?? 0;
|
||||||
|
var userRole = new UserRole { UserId = user.UserId, RoleId = hostRoleId, EffectiveDate = null, ExpiryDate = null };
|
||||||
|
userroles.AddUserRole(userRole);
|
||||||
|
|
||||||
|
// add user folder
|
||||||
|
var folder = folders.GetFolder(user.SiteId, Utilities.PathCombine("Users", "\\"));
|
||||||
|
if (folder != null)
|
||||||
|
{
|
||||||
|
folders.AddFolder(new Folder
|
||||||
|
{
|
||||||
|
SiteId = folder.SiteId,
|
||||||
|
ParentId = folder.FolderId,
|
||||||
|
Name = "My Folder",
|
||||||
|
Path = Utilities.PathCombine(folder.Path, user.UserId.ToString(), "\\"),
|
||||||
|
Order = 1,
|
||||||
|
IsSystem = true,
|
||||||
|
Permissions = new List<Permission>
|
||||||
|
{
|
||||||
|
new Permission(PermissionNames.Browse, user.UserId, true),
|
||||||
|
new Permission(PermissionNames.View, Constants.AllUsersRole, true),
|
||||||
|
new Permission(PermissionNames.Edit, user.UserId, true),
|
||||||
|
}.EncodePermissions(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (string aliasname in install.Aliases.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
|
||||||
|
{
|
||||||
|
alias = aliases.GetAliases().FirstOrDefault(item => item.Name == aliasname);
|
||||||
|
alias.SiteId = site.SiteId;
|
||||||
|
aliases.UpdateAlias(alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Log(site.SiteId, LogLevel.Trace, this, LogFunction.Create, "Site Created {Site}", site);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Success = true;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CreateApplicationVersion(string connectionString)
|
||||||
|
{
|
||||||
|
using (var db = new InstallationContext(NormalizeConnectionString(connectionString)))
|
||||||
|
{
|
||||||
|
var version = db.ApplicationVersion.FirstOrDefault(item => item.Version == Constants.Version);
|
||||||
|
if (version == null)
|
||||||
|
{
|
||||||
|
version = new ApplicationVersion { Version = Constants.Version, CreatedOn = DateTime.UtcNow };
|
||||||
db.ApplicationVersion.Add(version);
|
db.ApplicationVersion.Add(version);
|
||||||
db.SaveChanges();
|
db.SaveChanges();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void TenantMigration(string connectionString, string dataDirectory)
|
private string NormalizeConnectionString(string connectionString)
|
||||||
{
|
{
|
||||||
Console.WriteLine("Tenant migration");
|
var dataDirectory = AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString();
|
||||||
var assemblies = AppDomain.CurrentDomain.GetAssemblies()
|
connectionString = connectionString.Replace("|DataDirectory|", dataDirectory);
|
||||||
.Where(item => item.FullName != null && item.FullName.ToLower().Contains(".module.")).ToArray();
|
return connectionString;
|
||||||
|
}
|
||||||
|
|
||||||
// get tenants
|
private string DenormalizeConnectionString(string connectionString)
|
||||||
using (var db = new InstallationContext(connectionString))
|
{
|
||||||
|
var dataDirectory = AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString();
|
||||||
|
connectionString = connectionString.Replace(dataDirectory, "|DataDirectory|");
|
||||||
|
return connectionString;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateConnectionString(string connectionString)
|
||||||
|
{
|
||||||
|
connectionString = DenormalizeConnectionString(connectionString);
|
||||||
|
if (_config.GetConnectionString(SettingKeys.ConnectionStringKey) != connectionString)
|
||||||
{
|
{
|
||||||
foreach (var tenant in db.Tenant.ToList())
|
AddOrUpdateAppSetting($"ConnectionStrings:{SettingKeys.ConnectionStringKey}", connectionString);
|
||||||
{
|
_config.Reload();
|
||||||
Console.WriteLine($"Migrating tenant {tenant.Name}");
|
|
||||||
connectionString = NormalizeConnectionString(tenant.DBConnectionString, dataDirectory);
|
|
||||||
// upgrade framework
|
|
||||||
var dbUpgradeConfig = DeployChanges.To.SqlDatabase(connectionString)
|
|
||||||
.WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly(), s => s.Contains("Tenant"));
|
|
||||||
var dbUpgrade = dbUpgradeConfig.Build();
|
|
||||||
if (dbUpgrade.IsUpgradeRequired())
|
|
||||||
{
|
|
||||||
var result = dbUpgrade.PerformUpgrade();
|
|
||||||
if (!result.Successful)
|
|
||||||
{
|
|
||||||
// TODO: log result.Error.Message - problem is logger is not available here
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// iterate through Oqtane module assemblies and execute any database scripts
|
|
||||||
foreach (var assembly in assemblies) ModuleMigration(assembly, connectionString);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void UpdateConnectionStringSetting(string connectionString)
|
public void AddOrUpdateAppSetting<T>(string sectionPathKey, T value)
|
||||||
{
|
|
||||||
AddOrUpdateAppSetting($"ConnectionStrings:{SettingKeys.ConnectionStringKey}", connectionString);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void AddOrUpdateAppSetting<T>(string sectionPathKey, T value)
|
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -296,7 +527,7 @@ namespace Oqtane.Infrastructure
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void SetValueRecursively<T>(string sectionPathKey, dynamic jsonObj, T value)
|
private void SetValueRecursively<T>(string sectionPathKey, dynamic jsonObj, T value)
|
||||||
{
|
{
|
||||||
// split the string at the first ':' character
|
// split the string at the first ':' character
|
||||||
var remainingSections = sectionPathKey.Split(":", 2);
|
var remainingSections = sectionPathKey.Split(":", 2);
|
||||||
|
@ -315,51 +546,6 @@ namespace Oqtane.Infrastructure
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void BuildDefaultSite(string password, string email)
|
|
||||||
{
|
|
||||||
using (var scope = _serviceScopeFactory.CreateScope())
|
|
||||||
{
|
|
||||||
//Gather required services
|
|
||||||
var siteRepository = scope.ServiceProvider.GetRequiredService<ISiteRepository>();
|
|
||||||
|
|
||||||
// Build default site only if no site present
|
|
||||||
if (siteRepository.GetSites().Any()) return;
|
|
||||||
|
|
||||||
var users = scope.ServiceProvider.GetRequiredService<IUserRepository>();
|
|
||||||
var roles = scope.ServiceProvider.GetRequiredService<IRoleRepository>();
|
|
||||||
var userRoles = scope.ServiceProvider.GetRequiredService<IUserRoleRepository>();
|
|
||||||
var folders = scope.ServiceProvider.GetRequiredService<IFolderRepository>();
|
|
||||||
var identityUserManager = scope.ServiceProvider.GetRequiredService<UserManager<IdentityUser>>();
|
|
||||||
var tenants = scope.ServiceProvider.GetRequiredService<ITenantRepository>();
|
|
||||||
|
|
||||||
var tenant = tenants.GetTenants().First();
|
|
||||||
|
|
||||||
var site = new Site
|
|
||||||
{
|
|
||||||
TenantId = tenant.TenantId,
|
|
||||||
Name = "Default Site",
|
|
||||||
LogoFileId = null,
|
|
||||||
DefaultThemeType = GetInstallationConfig(SettingKeys.DefaultThemeKey, Constants.DefaultTheme),
|
|
||||||
DefaultLayoutType = GetInstallationConfig(SettingKeys.DefaultLayoutKey, Constants.DefaultLayout),
|
|
||||||
DefaultContainerType = GetInstallationConfig(SettingKeys.DefaultContainerKey, Constants.DefaultContainer),
|
|
||||||
SiteTemplateType = GetInstallationConfig(SettingKeys.SiteTemplateKey, Constants.DefaultSiteTemplate),
|
|
||||||
};
|
|
||||||
site = siteRepository.AddSite(site);
|
|
||||||
|
|
||||||
var user = new User
|
|
||||||
{
|
|
||||||
SiteId = site.SiteId,
|
|
||||||
Username = Constants.HostUser,
|
|
||||||
Password = password,
|
|
||||||
Email = email,
|
|
||||||
DisplayName = Constants.HostUser
|
|
||||||
};
|
|
||||||
CreateHostUser(folders, userRoles, roles, users, identityUserManager, user);
|
|
||||||
tenant.IsInitialized = true;
|
|
||||||
tenants.UpdateTenant(tenant);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GetInstallationConfig(string key, string defaultValue)
|
private string GetInstallationConfig(string key, string defaultValue)
|
||||||
{
|
{
|
||||||
var value = _config.GetSection(SettingKeys.InstallationSection).GetValue(key, defaultValue);
|
var value = _config.GetSection(SettingKeys.InstallationSection).GetValue(key, defaultValue);
|
||||||
|
@ -368,96 +554,5 @@ namespace Oqtane.Infrastructure
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void CreateHostUser(IFolderRepository folderRepository, IUserRoleRepository userRoleRepository, IRoleRepository roleRepository, IUserRepository userRepository, UserManager<IdentityUser> identityUserManager, User user)
|
|
||||||
{
|
|
||||||
var identityUser = new IdentityUser {UserName = user.Username, Email = user.Email, EmailConfirmed = true};
|
|
||||||
var result = identityUserManager.CreateAsync(identityUser, user.Password).GetAwaiter().GetResult();
|
|
||||||
|
|
||||||
if (result.Succeeded)
|
|
||||||
{
|
|
||||||
user.LastLoginOn = null;
|
|
||||||
user.LastIPAddress = "";
|
|
||||||
var newUser = userRepository.AddUser(user);
|
|
||||||
|
|
||||||
// assign to host role if this is the host user ( initial installation )
|
|
||||||
if (user.Username == Constants.HostUser)
|
|
||||||
{
|
|
||||||
var hostRoleId = roleRepository.GetRoles(user.SiteId, true).FirstOrDefault(item => item.Name == Constants.HostRole)?.RoleId ?? 0;
|
|
||||||
var userRole = new UserRole {UserId = newUser.UserId, RoleId = hostRoleId, EffectiveDate = null, ExpiryDate = null};
|
|
||||||
userRoleRepository.AddUserRole(userRole);
|
|
||||||
}
|
|
||||||
|
|
||||||
// add folder for user
|
|
||||||
var folder = folderRepository.GetFolder(user.SiteId, Utilities.PathCombine("Users","\\"));
|
|
||||||
if (folder != null)
|
|
||||||
folderRepository.AddFolder(new Folder
|
|
||||||
{
|
|
||||||
SiteId = folder.SiteId,
|
|
||||||
ParentId = folder.FolderId,
|
|
||||||
Name = "My Folder",
|
|
||||||
Path = Utilities.PathCombine(folder.Path, newUser.UserId.ToString(),"\\"),
|
|
||||||
Order = 1,
|
|
||||||
IsSystem = true,
|
|
||||||
Permissions = new List<Permission>
|
|
||||||
{
|
|
||||||
new Permission(PermissionNames.Browse, newUser.UserId, true),
|
|
||||||
new Permission(PermissionNames.View, Constants.AllUsersRole, true),
|
|
||||||
new Permission(PermissionNames.Edit, newUser.UserId, true),
|
|
||||||
}.EncodePermissions(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsDefaultSiteInstalled(string connectionString)
|
|
||||||
{
|
|
||||||
using (var db = new InstallationContext(connectionString))
|
|
||||||
{
|
|
||||||
return db.Tenant.Any(t => t.IsInitialized);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsMasterInstalled(string connectionString)
|
|
||||||
{
|
|
||||||
using (var db = new InstallationContext(connectionString))
|
|
||||||
{
|
|
||||||
|
|
||||||
//check if DbUp was initialized
|
|
||||||
return TableExists(db, "SchemaVersions");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool TableExists(DbContext context, string tableName)
|
|
||||||
{
|
|
||||||
return TableExists(context, "dbo", tableName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool TableExists(DbContext context, string schema, string tableName)
|
|
||||||
{
|
|
||||||
if (!context.Database.CanConnect()) return false;
|
|
||||||
var connection = context.Database.GetDbConnection();
|
|
||||||
|
|
||||||
if (connection.State.Equals(ConnectionState.Closed))
|
|
||||||
connection.Open();
|
|
||||||
|
|
||||||
using (var command = connection.CreateCommand())
|
|
||||||
{
|
|
||||||
command.CommandText = @"
|
|
||||||
SELECT 1 FROM INFORMATION_SCHEMA.TABLES
|
|
||||||
WHERE TABLE_SCHEMA = @Schema
|
|
||||||
AND TABLE_NAME = @TableName";
|
|
||||||
|
|
||||||
var schemaParam = command.CreateParameter();
|
|
||||||
schemaParam.ParameterName = "@Schema";
|
|
||||||
schemaParam.Value = schema;
|
|
||||||
command.Parameters.Add(schemaParam);
|
|
||||||
|
|
||||||
var tableNameParam = command.CreateParameter();
|
|
||||||
tableNameParam.ParameterName = "@TableName";
|
|
||||||
tableNameParam.Value = tableName;
|
|
||||||
command.Parameters.Add(tableNameParam);
|
|
||||||
|
|
||||||
return command.ExecuteScalar() != null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
12
Oqtane.Server/Infrastructure/Interfaces/IDatabaseManager.cs
Normal file
12
Oqtane.Server/Infrastructure/Interfaces/IDatabaseManager.cs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
using Oqtane.Models;
|
||||||
|
using Oqtane.Shared;
|
||||||
|
|
||||||
|
namespace Oqtane.Infrastructure
|
||||||
|
{
|
||||||
|
public interface IDatabaseManager
|
||||||
|
{
|
||||||
|
bool IsInstalled();
|
||||||
|
Installation Install();
|
||||||
|
Installation Install(InstallConfig install);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,10 @@
|
||||||
namespace Oqtane.Infrastructure
|
using Oqtane.Models;
|
||||||
|
|
||||||
|
namespace Oqtane.Infrastructure
|
||||||
{
|
{
|
||||||
public interface IInstallable
|
public interface IInstallable
|
||||||
{
|
{
|
||||||
bool Install(string version);
|
bool Install(Tenant tenant, string version);
|
||||||
bool Uninstall();
|
bool Uninstall(Tenant tenant);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,10 +69,14 @@ namespace Oqtane.Infrastructure
|
||||||
{
|
{
|
||||||
log.UserId = user.UserId;
|
log.UserId = user.UserId;
|
||||||
}
|
}
|
||||||
HttpRequest request = _accessor.HttpContext.Request;
|
log.Url = "";
|
||||||
if (request != null)
|
if (_accessor.HttpContext != null)
|
||||||
{
|
{
|
||||||
log.Url = $"{request.Scheme}://{request.Host}{request.Path}{request.QueryString}";
|
HttpRequest request = _accessor.HttpContext.Request;
|
||||||
|
if (request != null)
|
||||||
|
{
|
||||||
|
log.Url = $"{request.Scheme}://{request.Host}{request.Path}{request.QueryString}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Type type = Type.GetType(@class.ToString());
|
Type type = Type.GetType(@class.ToString());
|
||||||
|
|
|
@ -18,14 +18,14 @@ namespace Oqtane.Modules.HtmlText.Manager
|
||||||
_sql = sql;
|
_sql = sql;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Install(string version)
|
public bool Install(Tenant tenant, string version)
|
||||||
{
|
{
|
||||||
return _sql.ExecuteEmbeddedScript(GetType().Assembly, "HtmlText." + version + ".sql");
|
return _sql.ExecuteScript(tenant, GetType().Assembly, "HtmlText." + version + ".sql");
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Uninstall()
|
public bool Uninstall(Tenant tenant)
|
||||||
{
|
{
|
||||||
return _sql.ExecuteEmbeddedScript(GetType().Assembly, "HtmlText.Uninstall.sql");
|
return _sql.ExecuteScript(tenant, GetType().Assembly, "HtmlText.Uninstall.sql");
|
||||||
}
|
}
|
||||||
|
|
||||||
public string ExportModule(Module module)
|
public string ExportModule(Module module)
|
||||||
|
|
|
@ -17,11 +17,6 @@
|
||||||
<RootNamespace>Oqtane</RootNamespace>
|
<RootNamespace>Oqtane</RootNamespace>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<None Remove="Modules\HtmlText\Scripts\HtmlText.1.0.0.sql" />
|
|
||||||
<None Remove="Modules\HtmlText\Scripts\HtmlText.Uninstall.sql" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Include="Modules\HtmlText\Scripts\HtmlText.1.0.0.sql" />
|
<EmbeddedResource Include="Modules\HtmlText\Scripts\HtmlText.1.0.0.sql" />
|
||||||
<EmbeddedResource Include="Modules\HtmlText\Scripts\HtmlText.Uninstall.sql" />
|
<EmbeddedResource Include="Modules\HtmlText\Scripts\HtmlText.Uninstall.sql" />
|
||||||
|
|
|
@ -12,11 +12,10 @@ namespace Oqtane.Server
|
||||||
public static void Main(string[] args)
|
public static void Main(string[] args)
|
||||||
{
|
{
|
||||||
var host = BuildWebHost(args);
|
var host = BuildWebHost(args);
|
||||||
// execute any database migrations for the framework or extensions
|
|
||||||
using (var serviceScope = host.Services.GetRequiredService<IServiceScopeFactory>().CreateScope())
|
using (var serviceScope = host.Services.GetRequiredService<IServiceScopeFactory>().CreateScope())
|
||||||
{
|
{
|
||||||
var databaseManager = serviceScope.ServiceProvider.GetService<DatabaseManager>();
|
var databaseManager = serviceScope.ServiceProvider.GetService<IDatabaseManager>();
|
||||||
databaseManager.StartupMigration();
|
databaseManager.Install();
|
||||||
}
|
}
|
||||||
host.Run();
|
host.Run();
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,11 @@ namespace Oqtane.Repository
|
||||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||||
=> optionsBuilder.UseSqlServer(_connectionString);
|
=> optionsBuilder.UseSqlServer(_connectionString);
|
||||||
|
|
||||||
public virtual DbSet<ApplicationVersion> ApplicationVersion { get; set; }
|
public virtual DbSet<Alias> Alias { get; set; }
|
||||||
public virtual DbSet<Tenant> Tenant { get; set; }
|
public virtual DbSet<Tenant> Tenant { get; set; }
|
||||||
|
public virtual DbSet<ModuleDefinition> ModuleDefinition { get; set; }
|
||||||
|
public virtual DbSet<Job> Job { get; set; }
|
||||||
|
|
||||||
|
public virtual DbSet<ApplicationVersion> ApplicationVersion { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ namespace Oqtane.Repository
|
||||||
{
|
{
|
||||||
public interface IModuleDefinitionRepository
|
public interface IModuleDefinitionRepository
|
||||||
{
|
{
|
||||||
|
IEnumerable<ModuleDefinition> GetModuleDefinitions();
|
||||||
IEnumerable<ModuleDefinition> GetModuleDefinitions(int sideId);
|
IEnumerable<ModuleDefinition> GetModuleDefinitions(int sideId);
|
||||||
ModuleDefinition GetModuleDefinition(int moduleDefinitionId, int sideId);
|
ModuleDefinition GetModuleDefinition(int moduleDefinitionId, int sideId);
|
||||||
void UpdateModuleDefinition(ModuleDefinition moduleDefinition);
|
void UpdateModuleDefinition(ModuleDefinition moduleDefinition);
|
||||||
|
|
|
@ -6,8 +6,8 @@ namespace Oqtane.Repository
|
||||||
{
|
{
|
||||||
public interface ISqlRepository
|
public interface ISqlRepository
|
||||||
{
|
{
|
||||||
bool ExecuteEmbeddedScript(Assembly assembly, string script);
|
|
||||||
void ExecuteScript(Tenant tenant, string script);
|
void ExecuteScript(Tenant tenant, string script);
|
||||||
|
bool ExecuteScript(Tenant tenant, Assembly assembly, string filename);
|
||||||
int ExecuteNonQuery(Tenant tenant, string query);
|
int ExecuteNonQuery(Tenant tenant, string query);
|
||||||
SqlDataReader ExecuteReader(Tenant tenant, string query);
|
SqlDataReader ExecuteReader(Tenant tenant, string query);
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,11 @@ namespace Oqtane.Repository
|
||||||
_permissions = permissions;
|
_permissions = permissions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IEnumerable<ModuleDefinition> GetModuleDefinitions()
|
||||||
|
{
|
||||||
|
return LoadModuleDefinitions(-1); // used only during startup
|
||||||
|
}
|
||||||
|
|
||||||
public IEnumerable<ModuleDefinition> GetModuleDefinitions(int siteId)
|
public IEnumerable<ModuleDefinition> GetModuleDefinitions(int siteId)
|
||||||
{
|
{
|
||||||
return LoadModuleDefinitions(siteId);
|
return LoadModuleDefinitions(siteId);
|
||||||
|
@ -72,8 +77,12 @@ namespace Oqtane.Repository
|
||||||
}
|
}
|
||||||
List<ModuleDefinition> moduleDefinitions = _moduleDefinitions;
|
List<ModuleDefinition> moduleDefinitions = _moduleDefinitions;
|
||||||
|
|
||||||
// get module definition permissions for site
|
List<Permission> permissions = new List<Permission>();
|
||||||
List<Permission> permissions = _permissions.GetPermissions(siteId, EntityNames.ModuleDefinition).ToList();
|
if (siteId != -1)
|
||||||
|
{
|
||||||
|
// get module definition permissions for site
|
||||||
|
permissions = _permissions.GetPermissions(siteId, EntityNames.ModuleDefinition).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
// get module definitions in database
|
// get module definitions in database
|
||||||
List<ModuleDefinition> moduledefs = _db.ModuleDefinition.ToList();
|
List<ModuleDefinition> moduledefs = _db.ModuleDefinition.ToList();
|
||||||
|
@ -88,7 +97,10 @@ namespace Oqtane.Repository
|
||||||
moduledef = new ModuleDefinition { ModuleDefinitionName = moduledefinition.ModuleDefinitionName };
|
moduledef = new ModuleDefinition { ModuleDefinitionName = moduledefinition.ModuleDefinitionName };
|
||||||
_db.ModuleDefinition.Add(moduledef);
|
_db.ModuleDefinition.Add(moduledef);
|
||||||
_db.SaveChanges();
|
_db.SaveChanges();
|
||||||
_permissions.UpdatePermissions(siteId, EntityNames.ModuleDefinition, moduledef.ModuleDefinitionId, moduledefinition.Permissions);
|
if (siteId != -1)
|
||||||
|
{
|
||||||
|
_permissions.UpdatePermissions(siteId, EntityNames.ModuleDefinition, moduledef.ModuleDefinitionId, moduledefinition.Permissions);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -109,13 +121,16 @@ namespace Oqtane.Repository
|
||||||
{
|
{
|
||||||
moduledefinition.Version = moduledef.Version;
|
moduledefinition.Version = moduledef.Version;
|
||||||
}
|
}
|
||||||
if (permissions.Count == 0)
|
if (siteId != -1)
|
||||||
{
|
{
|
||||||
_permissions.UpdatePermissions(siteId, EntityNames.ModuleDefinition, moduledef.ModuleDefinitionId, moduledefinition.Permissions);
|
if (permissions.Count == 0)
|
||||||
}
|
{
|
||||||
else
|
_permissions.UpdatePermissions(siteId, EntityNames.ModuleDefinition, moduledef.ModuleDefinitionId, moduledefinition.Permissions);
|
||||||
{
|
}
|
||||||
moduledefinition.Permissions = permissions.Where(item => item.EntityId == moduledef.ModuleDefinitionId).EncodePermissions();
|
else
|
||||||
|
{
|
||||||
|
moduledefinition.Permissions = permissions.Where(item => item.EntityId == moduledef.ModuleDefinitionId).EncodePermissions();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// remove module definition from list as it is already synced
|
// remove module definition from list as it is already synced
|
||||||
moduledefs.Remove(moduledef);
|
moduledefs.Remove(moduledef);
|
||||||
|
@ -131,7 +146,10 @@ namespace Oqtane.Repository
|
||||||
// any remaining module definitions are orphans
|
// any remaining module definitions are orphans
|
||||||
foreach (ModuleDefinition moduledefinition in moduledefs)
|
foreach (ModuleDefinition moduledefinition in moduledefs)
|
||||||
{
|
{
|
||||||
_permissions.DeletePermissions(siteId, EntityNames.ModuleDefinition, moduledefinition.ModuleDefinitionId);
|
if (siteId != -1)
|
||||||
|
{
|
||||||
|
_permissions.DeletePermissions(siteId, EntityNames.ModuleDefinition, moduledefinition.ModuleDefinitionId);
|
||||||
|
}
|
||||||
_db.ModuleDefinition.Remove(moduledefinition); // delete
|
_db.ModuleDefinition.Remove(moduledefinition); // delete
|
||||||
_db.SaveChanges();
|
_db.SaveChanges();
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,8 +87,15 @@ namespace Oqtane.Repository
|
||||||
Type moduletype = Type.GetType(moduledefinition.ServerManagerType);
|
Type moduletype = Type.GetType(moduledefinition.ServerManagerType);
|
||||||
if (moduletype != null && moduletype.GetInterface("IPortable") != null)
|
if (moduletype != null && moduletype.GetInterface("IPortable") != null)
|
||||||
{
|
{
|
||||||
var moduleobject = ActivatorUtilities.CreateInstance(_serviceProvider, moduletype);
|
try
|
||||||
modulecontent.Content = ((IPortable) moduleobject).ExportModule(module);
|
{
|
||||||
|
var moduleobject = ActivatorUtilities.CreateInstance(_serviceProvider, moduletype);
|
||||||
|
modulecontent.Content = ((IPortable)moduleobject).ExportModule(module);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// error in IPortable implementation
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,9 +131,16 @@ namespace Oqtane.Repository
|
||||||
Type moduletype = Type.GetType(moduledefinition.ServerManagerType);
|
Type moduletype = Type.GetType(moduledefinition.ServerManagerType);
|
||||||
if (moduletype != null && moduletype.GetInterface("IPortable") != null)
|
if (moduletype != null && moduletype.GetInterface("IPortable") != null)
|
||||||
{
|
{
|
||||||
var moduleobject = ActivatorUtilities.CreateInstance(_serviceProvider, moduletype);
|
try
|
||||||
((IPortable) moduleobject).ImportModule(module, modulecontent.Content, modulecontent.Version);
|
{
|
||||||
success = true;
|
var moduleobject = ActivatorUtilities.CreateInstance(_serviceProvider, moduletype);
|
||||||
|
((IPortable)moduleobject).ImportModule(module, modulecontent.Content, modulecontent.Version);
|
||||||
|
success = true;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// error in IPortable implementation
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -784,14 +784,14 @@ namespace Oqtane.Repository
|
||||||
Type moduletype = Type.GetType(moduledefinition.ServerManagerType);
|
Type moduletype = Type.GetType(moduledefinition.ServerManagerType);
|
||||||
if (moduletype != null && moduletype.GetInterface("IPortable") != null)
|
if (moduletype != null && moduletype.GetInterface("IPortable") != null)
|
||||||
{
|
{
|
||||||
var moduleobject = ActivatorUtilities.CreateInstance(_serviceProvider, moduletype);
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
var moduleobject = ActivatorUtilities.CreateInstance(_serviceProvider, moduletype);
|
||||||
((IPortable)moduleobject).ImportModule(module, pagetemplatemodule.Content, moduledefinition.Version);
|
((IPortable)moduleobject).ImportModule(module, pagetemplatemodule.Content, moduledefinition.Version);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
// error in module import
|
// error in IPortable implementation
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,18 +10,21 @@ namespace Oqtane.Repository
|
||||||
{
|
{
|
||||||
public class SqlRepository : ISqlRepository
|
public class SqlRepository : ISqlRepository
|
||||||
{
|
{
|
||||||
private readonly ITenantRepository _tenants;
|
|
||||||
|
|
||||||
public SqlRepository(ITenantRepository tenants)
|
public void ExecuteScript(Tenant tenant, string script)
|
||||||
{
|
{
|
||||||
_tenants = tenants;
|
// execute script in curent tenant
|
||||||
|
foreach (string query in script.Split("GO", StringSplitOptions.RemoveEmptyEntries))
|
||||||
|
{
|
||||||
|
ExecuteNonQuery(tenant, query);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ExecuteEmbeddedScript(Assembly assembly, string filename)
|
public bool ExecuteScript(Tenant tenant, Assembly assembly, string filename)
|
||||||
{
|
{
|
||||||
// script must be included as an Embedded Resource within an assembly
|
// script must be included as an Embedded Resource within an assembly
|
||||||
bool success = true;
|
bool success = true;
|
||||||
string uninstallScript = "";
|
string script = "";
|
||||||
|
|
||||||
if (assembly != null)
|
if (assembly != null)
|
||||||
{
|
{
|
||||||
|
@ -33,39 +36,27 @@ namespace Oqtane.Repository
|
||||||
{
|
{
|
||||||
using (var reader = new StreamReader(resourceStream))
|
using (var reader = new StreamReader(resourceStream))
|
||||||
{
|
{
|
||||||
uninstallScript = reader.ReadToEnd();
|
script = reader.ReadToEnd();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(uninstallScript))
|
if (!string.IsNullOrEmpty(script))
|
||||||
{
|
{
|
||||||
foreach (Tenant tenant in _tenants.GetTenants())
|
try
|
||||||
{
|
{
|
||||||
try
|
ExecuteScript(tenant, script);
|
||||||
{
|
}
|
||||||
ExecuteScript(tenant, uninstallScript);
|
catch
|
||||||
}
|
{
|
||||||
catch
|
success = false;
|
||||||
{
|
|
||||||
success = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ExecuteScript(Tenant tenant, string script)
|
|
||||||
{
|
|
||||||
// execute script in curent tenant
|
|
||||||
foreach (string query in script.Split("GO", StringSplitOptions.RemoveEmptyEntries))
|
|
||||||
{
|
|
||||||
ExecuteNonQuery(tenant, query);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int ExecuteNonQuery(Tenant tenant, string query)
|
public int ExecuteNonQuery(Tenant tenant, string query)
|
||||||
{
|
{
|
||||||
SqlConnection conn = new SqlConnection(FormatConnectionString(tenant.DBConnectionString));
|
SqlConnection conn = new SqlConnection(FormatConnectionString(tenant.DBConnectionString));
|
||||||
|
|
|
@ -17,47 +17,48 @@ namespace Oqtane.Repository
|
||||||
int aliasId = -1;
|
int aliasId = -1;
|
||||||
string aliasName = "";
|
string aliasName = "";
|
||||||
|
|
||||||
// get alias identifier based on request context
|
if (siteState != null && siteState.Alias != null)
|
||||||
if (accessor.HttpContext != null)
|
|
||||||
{
|
{
|
||||||
// check if an alias is passed as a querystring parameter ( for cross tenant access )
|
// background processes can pass in an alias using the SiteState service
|
||||||
if (accessor.HttpContext.Request.Query.ContainsKey("aliasid"))
|
_alias = siteState.Alias;
|
||||||
{
|
|
||||||
aliasId = int.Parse(accessor.HttpContext.Request.Query["aliasid"]);
|
|
||||||
}
|
|
||||||
else // get the alias from the request url
|
|
||||||
{
|
|
||||||
aliasName = accessor.HttpContext.Request.Host.Value;
|
|
||||||
string path = accessor.HttpContext.Request.Path.Value;
|
|
||||||
string[] segments = path.Split(new[] {'/'}, StringSplitOptions.RemoveEmptyEntries);
|
|
||||||
if (segments.Length > 1 && segments[1] == "api" && segments[0] != "~")
|
|
||||||
{
|
|
||||||
aliasName += "/" + segments[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (aliasName.EndsWith("/"))
|
|
||||||
{
|
|
||||||
aliasName = aliasName.Substring(0, aliasName.Length - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else // background processes can pass in an alias using the SiteState service
|
|
||||||
{
|
|
||||||
aliasId = siteState?.Alias?.AliasId ?? -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the alias and tenant
|
|
||||||
IEnumerable<Alias> aliases = aliasRepository.GetAliases().ToList(); // cached
|
|
||||||
if (aliasId != -1)
|
|
||||||
{
|
|
||||||
_alias = aliases.FirstOrDefault(item => item.AliasId == aliasId);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// get alias identifier based on request context
|
||||||
_alias = aliases.FirstOrDefault(item => item.Name == aliasName
|
if (accessor.HttpContext != null)
|
||||||
//if here is only one alias and other methods fail, take it (case of startup install)
|
{
|
||||||
|| aliases.Count() == 1);
|
// check if an alias is passed as a querystring parameter ( for cross tenant access )
|
||||||
|
if (accessor.HttpContext.Request.Query.ContainsKey("aliasid"))
|
||||||
|
{
|
||||||
|
aliasId = int.Parse(accessor.HttpContext.Request.Query["aliasid"]);
|
||||||
|
}
|
||||||
|
else // get the alias from the request url
|
||||||
|
{
|
||||||
|
aliasName = accessor.HttpContext.Request.Host.Value;
|
||||||
|
string path = accessor.HttpContext.Request.Path.Value;
|
||||||
|
string[] segments = path.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
if (segments.Length > 1 && segments[1] == "api" && segments[0] != "~")
|
||||||
|
{
|
||||||
|
aliasName += "/" + segments[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aliasName.EndsWith("/"))
|
||||||
|
{
|
||||||
|
aliasName = aliasName.Substring(0, aliasName.Length - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the alias
|
||||||
|
IEnumerable<Alias> aliases = aliasRepository.GetAliases().ToList(); // cached
|
||||||
|
if (aliasId != -1)
|
||||||
|
{
|
||||||
|
_alias = aliases.FirstOrDefault(item => item.AliasId == aliasId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_alias = aliases.FirstOrDefault(item => item.Name == aliasName || aliases.Count() == 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_alias != null)
|
if (_alias != null)
|
||||||
|
|
|
@ -7,8 +7,6 @@ CREATE TABLE [dbo].[Tenant](
|
||||||
[TenantId] [int] IDENTITY(1,1) NOT NULL,
|
[TenantId] [int] IDENTITY(1,1) NOT NULL,
|
||||||
[Name] [nvarchar](100) NOT NULL,
|
[Name] [nvarchar](100) NOT NULL,
|
||||||
[DBConnectionString] [nvarchar](1024) NOT NULL,
|
[DBConnectionString] [nvarchar](1024) NOT NULL,
|
||||||
[DBSchema] [nvarchar](50) NOT NULL,
|
|
||||||
[IsInitialized] [bit] NOT NULL,
|
|
||||||
[CreatedBy] [nvarchar](256) NOT NULL,
|
[CreatedBy] [nvarchar](256) NOT NULL,
|
||||||
[CreatedOn] [datetime] NOT NULL,
|
[CreatedOn] [datetime] NOT NULL,
|
||||||
[ModifiedBy] [nvarchar](256) NOT NULL,
|
[ModifiedBy] [nvarchar](256) NOT NULL,
|
||||||
|
@ -119,32 +117,4 @@ REFERENCES [dbo].[Job] ([JobId])
|
||||||
ON DELETE CASCADE
|
ON DELETE CASCADE
|
||||||
GO
|
GO
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
Create seed data
|
|
||||||
|
|
||||||
*/
|
|
||||||
SET IDENTITY_INSERT [dbo].[Tenant] ON
|
|
||||||
GO
|
|
||||||
INSERT [dbo].[Tenant] ([TenantId], [Name], [DBConnectionString], [DBSchema], [IsInitialized], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn])
|
|
||||||
VALUES (1, N'Master', N'$ConnectionString$', N'', 0, '', getdate(), '', getdate())
|
|
||||||
GO
|
|
||||||
SET IDENTITY_INSERT [dbo].[Tenant] OFF
|
|
||||||
GO
|
|
||||||
|
|
||||||
SET IDENTITY_INSERT [dbo].[Alias] ON
|
|
||||||
GO
|
|
||||||
INSERT [dbo].[Alias] ([AliasId], [Name], [TenantId], [SiteId], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn])
|
|
||||||
VALUES (1, N'$Alias$', 1, 1, '', getdate(), '', getdate())
|
|
||||||
GO
|
|
||||||
SET IDENTITY_INSERT [dbo].[Alias] OFF
|
|
||||||
GO
|
|
||||||
|
|
||||||
SET IDENTITY_INSERT [dbo].[Job] ON
|
|
||||||
GO
|
|
||||||
INSERT [dbo].[Job] ([JobId], [Name], [JobType], [Frequency], [Interval], [StartDate], [EndDate], [IsEnabled], [IsStarted], [IsExecuting], [NextExecution], [RetentionHistory], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn])
|
|
||||||
VALUES (1, N'Notification Job', N'Oqtane.Infrastructure.NotificationJob, Oqtane.Server', N'm', 1, null, null, 0, 0, 0, null, 10, '', getdate(), '', getdate())
|
|
||||||
GO
|
|
||||||
SET IDENTITY_INSERT [dbo].[Job] OFF
|
|
||||||
GO
|
|
||||||
|
|
||||||
|
|
|
@ -1,2 +1,5 @@
|
||||||
alter table Tenant drop column DBSchema
|
/*
|
||||||
go
|
|
||||||
|
schema updates
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
|
@ -55,7 +55,14 @@ namespace Oqtane.Security
|
||||||
|
|
||||||
public User GetUser()
|
public User GetUser()
|
||||||
{
|
{
|
||||||
return GetUser(_accessor.HttpContext.User);
|
if (_accessor.HttpContext != null)
|
||||||
|
{
|
||||||
|
return GetUser(_accessor.HttpContext.User);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -155,7 +155,7 @@ namespace Oqtane
|
||||||
services.AddSingleton(Configuration);
|
services.AddSingleton(Configuration);
|
||||||
services.AddSingleton<IInstallationManager, InstallationManager>();
|
services.AddSingleton<IInstallationManager, InstallationManager>();
|
||||||
services.AddSingleton<ISyncManager, SyncManager>();
|
services.AddSingleton<ISyncManager, SyncManager>();
|
||||||
services.AddSingleton<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.UnpackPackages("Modules,Themes", _webRoot);
|
InstallationManager.UnpackPackages("Modules,Themes", _webRoot);
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
.main .top-row {
|
.main .top-row {
|
||||||
background-color: #e6e6e6;
|
background-color: #e6e6e6;
|
||||||
border-bottom: 1px solid #d6d5d5;
|
border-bottom: 1px solid #d6d5d5;
|
||||||
z-index: 9999;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar {
|
.sidebar {
|
||||||
|
@ -42,6 +41,10 @@
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.app-controlpanel {
|
||||||
|
z-index: 9999;
|
||||||
|
}
|
||||||
|
|
||||||
.app-menu .nav-item {
|
.app-menu .nav-item {
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
padding-bottom: 0.5rem;
|
padding-bottom: 0.5rem;
|
||||||
|
@ -97,19 +100,21 @@
|
||||||
position: fixed;
|
position: fixed;
|
||||||
left: 275px;
|
left: 275px;
|
||||||
top: 0;
|
top: 0;
|
||||||
z-index: 1;
|
z-index: 3
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar {
|
.sidebar {
|
||||||
width: 250px;
|
width: 250px;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
z-index: 1
|
||||||
}
|
}
|
||||||
|
|
||||||
.main .top-row {
|
.main .top-row {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
z-index: 2
|
||||||
}
|
}
|
||||||
|
|
||||||
.main > div {
|
.main > div {
|
||||||
|
@ -154,10 +159,23 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 767px) {
|
@media (max-width: 767px) {
|
||||||
|
.breadcrumbs {
|
||||||
|
position: fixed;
|
||||||
|
top: 150px;
|
||||||
|
width: 100%;
|
||||||
|
left: 0;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.sidebar {
|
.sidebar {
|
||||||
margin-top: 3.5rem;
|
margin-top: 3.5rem;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main .top-row {
|
||||||
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main > .top-row.px-4 {
|
.main > .top-row.px-4 {
|
||||||
|
@ -172,13 +190,6 @@
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.breadcrumbs {
|
|
||||||
position: fixed;
|
|
||||||
top: 150px;
|
|
||||||
width: 100%;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main > .container {
|
.main > .container {
|
||||||
margin-top: 200px;
|
margin-top: 200px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,7 @@ window.interop = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
includeLink: function (id, rel, url, type) {
|
includeLink: function (id, rel, url, type, integrity, crossorigin) {
|
||||||
var link;
|
var link;
|
||||||
if (id !== "") {
|
if (id !== "") {
|
||||||
link = document.getElementById(id);
|
link = document.getElementById(id);
|
||||||
|
@ -66,6 +66,12 @@ window.interop = {
|
||||||
if (type !== "") {
|
if (type !== "") {
|
||||||
link.type = type;
|
link.type = type;
|
||||||
}
|
}
|
||||||
|
if (integrity !== "") {
|
||||||
|
link.integrity = integrity;
|
||||||
|
}
|
||||||
|
if (crossorigin !== "") {
|
||||||
|
link.crossorigin = crossorigin;
|
||||||
|
}
|
||||||
document.head.appendChild(link);
|
document.head.appendChild(link);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -78,9 +84,15 @@ window.interop = {
|
||||||
if (type !== "" && link.type !== type) {
|
if (type !== "" && link.type !== type) {
|
||||||
link.setAttribute('type', type);
|
link.setAttribute('type', type);
|
||||||
}
|
}
|
||||||
|
if (integrity !== "" && link.integrity !== integrity) {
|
||||||
|
link.setAttribute('integrity', integrity);
|
||||||
|
}
|
||||||
|
if (crossorigin !== "" && link.crossorigin !== crossorigin) {
|
||||||
|
link.setAttribute('crossorigin', crossorigin);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
includeScript: function (id, src, content, location) {
|
includeScript: function (id, src, content, location, integrity, crossorigin) {
|
||||||
var script;
|
var script;
|
||||||
if (id !== "") {
|
if (id !== "") {
|
||||||
script = document.getElementById(id);
|
script = document.getElementById(id);
|
||||||
|
@ -92,6 +104,12 @@ window.interop = {
|
||||||
}
|
}
|
||||||
if (src !== "") {
|
if (src !== "") {
|
||||||
script.src = src;
|
script.src = src;
|
||||||
|
if (integrity !== "") {
|
||||||
|
script.integrity = integrity;
|
||||||
|
}
|
||||||
|
if (crossorigin !== "") {
|
||||||
|
script.crossorigin = crossorigin;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
script.innerHTML = content;
|
script.innerHTML = content;
|
||||||
|
@ -108,6 +126,12 @@ window.interop = {
|
||||||
if (script.src !== src) {
|
if (script.src !== src) {
|
||||||
script.src = src;
|
script.src = src;
|
||||||
}
|
}
|
||||||
|
if (integrity !== "" && script.integrity !== integrity) {
|
||||||
|
script.setAttribute('integrity', integrity);
|
||||||
|
}
|
||||||
|
if (crossorigin !== "" && script.crossorigin !== crossorigin) {
|
||||||
|
script.setAttribute('crossorigin', crossorigin);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (script.innerHTML !== content) {
|
if (script.innerHTML !== content) {
|
||||||
|
|
|
@ -7,7 +7,6 @@ namespace Oqtane.Models
|
||||||
public int TenantId { get; set; }
|
public int TenantId { get; set; }
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public string DBConnectionString { get; set; }
|
public string DBConnectionString { get; set; }
|
||||||
public bool IsInitialized { get; set; }
|
|
||||||
public string CreatedBy { get; set; }
|
public string CreatedBy { get; set; }
|
||||||
public DateTime CreatedOn { get; set; }
|
public DateTime CreatedOn { get; set; }
|
||||||
public string ModifiedBy { get; set; }
|
public string ModifiedBy { get; set; }
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
public const string HostUser = "host";
|
public const string HostUser = "host";
|
||||||
|
|
||||||
public const string MasterTenant = "Master";
|
public const string MasterTenant = "Master";
|
||||||
|
public const string DefaultSite = "Default Site";
|
||||||
|
|
||||||
public const string AllUsersRole = "All Users";
|
public const string AllUsersRole = "All Users";
|
||||||
public const string HostRole = "Host Users";
|
public const string HostRole = "Host Users";
|
||||||
|
|
|
@ -2,11 +2,17 @@
|
||||||
{
|
{
|
||||||
public class InstallConfig
|
public class InstallConfig
|
||||||
{
|
{
|
||||||
public string Alias { get; set; }
|
|
||||||
public string ConnectionString { get; set; }
|
public string ConnectionString { get; set; }
|
||||||
public string HostUser { get; set; }
|
public string Aliases { get; set; }
|
||||||
public string Password { get; set; }
|
public string TenantName { get; set; }
|
||||||
|
public bool IsNewTenant { get; set; }
|
||||||
|
public string SiteName { get; set; }
|
||||||
|
public string HostPassword { get; set; }
|
||||||
public string HostEmail { get; set; }
|
public string HostEmail { get; set; }
|
||||||
public bool IsMaster { get; set; }
|
public string HostName { get; set; }
|
||||||
|
public string SiteTemplate { get; set; }
|
||||||
|
public string DefaultTheme { get; set; }
|
||||||
|
public string DefaultLayout { get; set; }
|
||||||
|
public string DefaultContainer { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user