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:
Shaun Walker
2020-04-30 13:58:04 -04:00
parent 099fddf2b6
commit 34538dd945
44 changed files with 1051 additions and 912 deletions

View File

@ -5,8 +5,6 @@ using Oqtane.Models;
using Oqtane.Shared;
using Oqtane.Infrastructure;
// ReSharper disable StringIndexOfIsCultureSpecific.1
namespace Oqtane.Controllers
{
[Route("{site}/api/[controller]")]
@ -14,9 +12,9 @@ namespace Oqtane.Controllers
{
private readonly IConfigurationRoot _config;
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;
_installationManager = installationManager;
@ -27,33 +25,17 @@ namespace Oqtane.Controllers
[HttpPost]
public Installation Post([FromBody] InstallConfig config)
{
//TODO Security ????
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;
config.Alias = config.Alias ?? HttpContext.Request.Host.Value;
var result = DatabaseManager.InstallDatabase(config);
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 = _databaseManager.Install(config);
}
else
{
installation.Message = "Installation Not Authorized";
}
installation.Message = "Application Is Already Installed";
return installation;
}
@ -61,12 +43,8 @@ namespace Oqtane.Controllers
[HttpGet("installed")]
public Installation IsInstalled()
{
var installation = new Installation {Success = false, Message = ""};
installation.Success = _databaseManager.IsInstalled;
installation.Message = _databaseManager.Message;
return installation;
bool isInstalled = _databaseManager.IsInstalled();
return new Installation {Success = isInstalled, Message = string.Empty};
}
[HttpGet("upgrade")]

View File

@ -22,16 +22,18 @@ namespace Oqtane.Controllers
{
private readonly IModuleDefinitionRepository _moduleDefinitions;
private readonly IModuleRepository _modules;
private readonly ITenantRepository _tenants;
private readonly IUserPermissions _userPermissions;
private readonly IInstallationManager _installationManager;
private readonly IWebHostEnvironment _environment;
private readonly IServiceProvider _serviceProvider;
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;
_modules = modules;
_tenants = tenants;
_userPermissions = userPermissions;
_installationManager = installationManager;
_environment = environment;
@ -104,8 +106,18 @@ namespace Oqtane.Controllers
Type moduletype = Type.GetType(moduledefinition.ServerManagerType);
if (moduletype != null && moduletype.GetInterface("IInstallable") != null)
{
var moduleobject = ActivatorUtilities.CreateInstance(_serviceProvider, moduletype);
((IInstallable)moduleobject).Uninstall();
foreach (Tenant tenant in _tenants.GetTenants())
{
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;
_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();
}
}

View File

@ -1,13 +1,13 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Reflection;
using DbUp;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
@ -15,193 +15,172 @@ using Oqtane.Extensions;
using Oqtane.Models;
using Oqtane.Repository;
using Oqtane.Shared;
using Oqtane.Enums;
using File = System.IO.File;
namespace Oqtane.Infrastructure
{
public class DatabaseManager
public class DatabaseManager : IDatabaseManager
{
private readonly IConfigurationRoot _config;
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;
_serviceScopeFactory = serviceScopeFactory;
_cache = cache;
}
public string Message { get; set; }
public void StartupMigration()
public bool IsInstalled()
{
var defaultConnectionString = _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 defaultConnectionString = NormalizeConnectionString(_config.GetConnectionString(SettingKeys.ConnectionStringKey));
var result = !string.IsNullOrEmpty(defaultConnectionString);
if (result)
{
using (var scope = _serviceScopeFactory.CreateScope())
{
var dbContext = scope.ServiceProvider.GetRequiredService<MasterDBContext>();
result = dbContext.Database.CanConnect();
var db = scope.ServiceProvider.GetRequiredService<MasterDBContext>();
result = db.Database.CanConnect();
if (result)
{
try
{
result = db.Tenant.Any();
}
catch
{
result = false;
}
}
}
if (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"));
}
return result;
}
result = !dbUpgradeConfig.Build().IsUpgradeRequired();
if (!result) Message = "Master Installation Scripts Have Not Been Executed";
public Installation Install()
{
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
{
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;
}
public static string NormalizeConnectionString(string connectionString, string dataDirectory)
private Installation CreateDatabase(InstallConfig install)
{
connectionString = connectionString
.Replace("|DataDirectory|", dataDirectory);
return connectionString;
}
var result = new Installation { Success = false, Message = string.Empty };
public static Installation InstallDatabase([NotNull] InstallConfig installConfig)
{
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))
if (install.IsNewTenant)
{
result = MasterMigration(connectionString, alias, result, installConfig.IsMaster);
if (installConfig.IsMaster && result.Success)
try
{
WriteVersionInfo(connectionString);
TenantMigration(connectionString, dataDirectory);
UpdateConnectionStringSetting(connectionString);
//create data directory if does not exist
var dataDirectory = AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString();
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;
}
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))
catch (Exception ex)
{
dbc.Database.EnsureCreated();
//check for vanilla db
firstInstall = !TableExists(dbc, "SchemaVersions");
result.Message = ex.Message;
}
}
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
{
result.Success = true;
@ -210,74 +189,326 @@ namespace Oqtane.Infrastructure
return result;
}
private static void ModuleMigration(Assembly assembly, string connectionString)
private Installation MigrateMaster(InstallConfig install)
{
Console.WriteLine($"Migrating assembly {assembly.FullName}");
var dbUpgradeConfig = DeployChanges.To.SqlDatabase(connectionString)
.WithScriptsEmbeddedInAssembly(assembly, s => !s.ToLower().Contains("uninstall.sql")); // scripts must be included as Embedded Resources
var dbUpgrade = dbUpgradeConfig.Build();
if (dbUpgrade.IsUpgradeRequired())
var result = new Installation { Success = false, Message = string.Empty };
if (install.TenantName == Constants.MasterTenant)
{
var result = dbUpgrade.PerformUpgrade();
if (!result.Successful)
var upgradeConfig = DeployChanges
.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();
if (version == null || version.Version != Constants.Version)
using (var db = new InstallationContext(NormalizeConnectionString(_config.GetConnectionString(SettingKeys.ConnectionStringKey))))
{
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.SaveChanges();
}
}
}
private static void TenantMigration(string connectionString, string dataDirectory)
private string NormalizeConnectionString(string connectionString)
{
Console.WriteLine("Tenant migration");
var assemblies = AppDomain.CurrentDomain.GetAssemblies()
.Where(item => item.FullName != null && item.FullName.ToLower().Contains(".module.")).ToArray();
var dataDirectory = AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString();
connectionString = connectionString.Replace("|DataDirectory|", dataDirectory);
return connectionString;
}
// get tenants
using (var db = new InstallationContext(connectionString))
private string DenormalizeConnectionString(string 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())
{
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);
}
AddOrUpdateAppSetting($"ConnectionStrings:{SettingKeys.ConnectionStringKey}", connectionString);
_config.Reload();
}
}
public static void UpdateConnectionStringSetting(string connectionString)
{
AddOrUpdateAppSetting($"ConnectionStrings:{SettingKeys.ConnectionStringKey}", connectionString);
}
public static void AddOrUpdateAppSetting<T>(string sectionPathKey, T value)
public void AddOrUpdateAppSetting<T>(string sectionPathKey, T value)
{
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
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)
{
var value = _config.GetSection(SettingKeys.InstallationSection).GetValue(key, defaultValue);
@ -368,96 +554,5 @@ namespace Oqtane.Infrastructure
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;
}
}
}
}

View 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);
}
}

View File

@ -1,8 +1,10 @@
namespace Oqtane.Infrastructure
using Oqtane.Models;
namespace Oqtane.Infrastructure
{
public interface IInstallable
{
bool Install(string version);
bool Uninstall();
bool Install(Tenant tenant, string version);
bool Uninstall(Tenant tenant);
}
}

View File

@ -69,10 +69,14 @@ namespace Oqtane.Infrastructure
{
log.UserId = user.UserId;
}
HttpRequest request = _accessor.HttpContext.Request;
if (request != null)
log.Url = "";
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());

View File

@ -18,14 +18,14 @@ namespace Oqtane.Modules.HtmlText.Manager
_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)

View File

@ -17,11 +17,6 @@
<RootNamespace>Oqtane</RootNamespace>
</PropertyGroup>
<ItemGroup>
<None Remove="Modules\HtmlText\Scripts\HtmlText.1.0.0.sql" />
<None Remove="Modules\HtmlText\Scripts\HtmlText.Uninstall.sql" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Modules\HtmlText\Scripts\HtmlText.1.0.0.sql" />
<EmbeddedResource Include="Modules\HtmlText\Scripts\HtmlText.Uninstall.sql" />

View File

@ -12,11 +12,10 @@ namespace Oqtane.Server
public static void Main(string[] args)
{
var host = BuildWebHost(args);
// execute any database migrations for the framework or extensions
using (var serviceScope = host.Services.GetRequiredService<IServiceScopeFactory>().CreateScope())
{
var databaseManager = serviceScope.ServiceProvider.GetService<DatabaseManager>();
databaseManager.StartupMigration();
var databaseManager = serviceScope.ServiceProvider.GetService<IDatabaseManager>();
databaseManager.Install();
}
host.Run();
}

View File

@ -17,7 +17,11 @@ namespace Oqtane.Repository
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> 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<ModuleDefinition> ModuleDefinition { get; set; }
public virtual DbSet<Job> Job { get; set; }
public virtual DbSet<ApplicationVersion> ApplicationVersion { get; set; }
}
}

View File

@ -5,6 +5,7 @@ namespace Oqtane.Repository
{
public interface IModuleDefinitionRepository
{
IEnumerable<ModuleDefinition> GetModuleDefinitions();
IEnumerable<ModuleDefinition> GetModuleDefinitions(int sideId);
ModuleDefinition GetModuleDefinition(int moduleDefinitionId, int sideId);
void UpdateModuleDefinition(ModuleDefinition moduleDefinition);

View File

@ -6,8 +6,8 @@ namespace Oqtane.Repository
{
public interface ISqlRepository
{
bool ExecuteEmbeddedScript(Assembly assembly, string script);
void ExecuteScript(Tenant tenant, string script);
bool ExecuteScript(Tenant tenant, Assembly assembly, string filename);
int ExecuteNonQuery(Tenant tenant, string query);
SqlDataReader ExecuteReader(Tenant tenant, string query);
}

View File

@ -25,6 +25,11 @@ namespace Oqtane.Repository
_permissions = permissions;
}
public IEnumerable<ModuleDefinition> GetModuleDefinitions()
{
return LoadModuleDefinitions(-1); // used only during startup
}
public IEnumerable<ModuleDefinition> GetModuleDefinitions(int siteId)
{
return LoadModuleDefinitions(siteId);
@ -72,8 +77,12 @@ namespace Oqtane.Repository
}
List<ModuleDefinition> moduleDefinitions = _moduleDefinitions;
// get module definition permissions for site
List<Permission> permissions = _permissions.GetPermissions(siteId, EntityNames.ModuleDefinition).ToList();
List<Permission> permissions = new List<Permission>();
if (siteId != -1)
{
// get module definition permissions for site
permissions = _permissions.GetPermissions(siteId, EntityNames.ModuleDefinition).ToList();
}
// get module definitions in database
List<ModuleDefinition> moduledefs = _db.ModuleDefinition.ToList();
@ -88,7 +97,10 @@ namespace Oqtane.Repository
moduledef = new ModuleDefinition { ModuleDefinitionName = moduledefinition.ModuleDefinitionName };
_db.ModuleDefinition.Add(moduledef);
_db.SaveChanges();
_permissions.UpdatePermissions(siteId, EntityNames.ModuleDefinition, moduledef.ModuleDefinitionId, moduledefinition.Permissions);
if (siteId != -1)
{
_permissions.UpdatePermissions(siteId, EntityNames.ModuleDefinition, moduledef.ModuleDefinitionId, moduledefinition.Permissions);
}
}
else
{
@ -109,13 +121,16 @@ namespace Oqtane.Repository
{
moduledefinition.Version = moduledef.Version;
}
if (permissions.Count == 0)
if (siteId != -1)
{
_permissions.UpdatePermissions(siteId, EntityNames.ModuleDefinition, moduledef.ModuleDefinitionId, moduledefinition.Permissions);
}
else
{
moduledefinition.Permissions = permissions.Where(item => item.EntityId == moduledef.ModuleDefinitionId).EncodePermissions();
if (permissions.Count == 0)
{
_permissions.UpdatePermissions(siteId, EntityNames.ModuleDefinition, moduledef.ModuleDefinitionId, moduledefinition.Permissions);
}
else
{
moduledefinition.Permissions = permissions.Where(item => item.EntityId == moduledef.ModuleDefinitionId).EncodePermissions();
}
}
// remove module definition from list as it is already synced
moduledefs.Remove(moduledef);
@ -131,7 +146,10 @@ namespace Oqtane.Repository
// any remaining module definitions are orphans
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.SaveChanges();
}

View File

@ -87,8 +87,15 @@ namespace Oqtane.Repository
Type moduletype = Type.GetType(moduledefinition.ServerManagerType);
if (moduletype != null && moduletype.GetInterface("IPortable") != null)
{
var moduleobject = ActivatorUtilities.CreateInstance(_serviceProvider, moduletype);
modulecontent.Content = ((IPortable) moduleobject).ExportModule(module);
try
{
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);
if (moduletype != null && moduletype.GetInterface("IPortable") != null)
{
var moduleobject = ActivatorUtilities.CreateInstance(_serviceProvider, moduletype);
((IPortable) moduleobject).ImportModule(module, modulecontent.Content, modulecontent.Version);
success = true;
try
{
var moduleobject = ActivatorUtilities.CreateInstance(_serviceProvider, moduletype);
((IPortable)moduleobject).ImportModule(module, modulecontent.Content, modulecontent.Version);
success = true;
}
catch
{
// error in IPortable implementation
}
}
}
}

View File

@ -784,14 +784,14 @@ namespace Oqtane.Repository
Type moduletype = Type.GetType(moduledefinition.ServerManagerType);
if (moduletype != null && moduletype.GetInterface("IPortable") != null)
{
var moduleobject = ActivatorUtilities.CreateInstance(_serviceProvider, moduletype);
try
{
var moduleobject = ActivatorUtilities.CreateInstance(_serviceProvider, moduletype);
((IPortable)moduleobject).ImportModule(module, pagetemplatemodule.Content, moduledefinition.Version);
}
catch
{
// error in module import
// error in IPortable implementation
}
}
}

View File

@ -10,18 +10,21 @@ namespace Oqtane.Repository
{
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
bool success = true;
string uninstallScript = "";
string script = "";
if (assembly != null)
{
@ -33,39 +36,27 @@ namespace Oqtane.Repository
{
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, uninstallScript);
}
catch
{
success = false;
}
ExecuteScript(tenant, script);
}
catch
{
success = false;
}
}
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)
{
SqlConnection conn = new SqlConnection(FormatConnectionString(tenant.DBConnectionString));

View File

@ -17,47 +17,48 @@ namespace Oqtane.Repository
int aliasId = -1;
string aliasName = "";
// get alias identifier based on request context
if (accessor.HttpContext != null)
if (siteState != null && siteState.Alias != null)
{
// 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);
}
}
}
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);
// background processes can pass in an alias using the SiteState service
_alias = siteState.Alias;
}
else
{
_alias = aliases.FirstOrDefault(item => item.Name == aliasName
//if here is only one alias and other methods fail, take it (case of startup install)
|| aliases.Count() == 1);
{
// get alias identifier based on request context
if (accessor.HttpContext != null)
{
// 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)

View File

@ -7,8 +7,6 @@ CREATE TABLE [dbo].[Tenant](
[TenantId] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](100) NOT NULL,
[DBConnectionString] [nvarchar](1024) NOT NULL,
[DBSchema] [nvarchar](50) NOT NULL,
[IsInitialized] [bit] NOT NULL,
[CreatedBy] [nvarchar](256) NOT NULL,
[CreatedOn] [datetime] NOT NULL,
[ModifiedBy] [nvarchar](256) NOT NULL,
@ -119,32 +117,4 @@ REFERENCES [dbo].[Job] ([JobId])
ON DELETE CASCADE
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

View File

@ -1,2 +1,5 @@
alter table Tenant drop column DBSchema
go
/*
schema updates
*/

View File

@ -55,7 +55,14 @@ namespace Oqtane.Security
public User GetUser()
{
return GetUser(_accessor.HttpContext.User);
if (_accessor.HttpContext != null)
{
return GetUser(_accessor.HttpContext.User);
}
else
{
return null;
}
}
}
}

View File

@ -155,7 +155,7 @@ namespace Oqtane
services.AddSingleton(Configuration);
services.AddSingleton<IInstallationManager, InstallationManager>();
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 )
InstallationManager.UnpackPackages("Modules,Themes", _webRoot);

View File

@ -16,7 +16,6 @@
.main .top-row {
background-color: #e6e6e6;
border-bottom: 1px solid #d6d5d5;
z-index: 9999;
}
.sidebar {
@ -42,6 +41,10 @@
margin-bottom: 0;
}
.app-controlpanel {
z-index: 9999;
}
.app-menu .nav-item {
font-size: 0.9rem;
padding-bottom: 0.5rem;
@ -97,19 +100,21 @@
position: fixed;
left: 275px;
top: 0;
z-index: 1;
z-index: 3
}
.sidebar {
width: 250px;
height: 100vh;
position: sticky;
top: 0;
z-index: 1
}
.main .top-row {
position: sticky;
top: 0;
z-index: 2
}
.main > div {
@ -154,10 +159,23 @@
}
@media (max-width: 767px) {
.breadcrumbs {
position: fixed;
top: 150px;
width: 100%;
left: 0;
z-index: 1;
}
.sidebar {
margin-top: 3.5rem;
position: fixed;
width: 100%;
z-index: 2;
}
.main .top-row {
z-index: 2;
}
.main > .top-row.px-4 {
@ -172,13 +190,6 @@
margin-left: auto;
}
.breadcrumbs {
position: fixed;
top: 150px;
width: 100%;
left: 0;
}
.main > .container {
margin-top: 200px;
}

View File

@ -48,7 +48,7 @@ window.interop = {
}
}
},
includeLink: function (id, rel, url, type) {
includeLink: function (id, rel, url, type, integrity, crossorigin) {
var link;
if (id !== "") {
link = document.getElementById(id);
@ -66,6 +66,12 @@ window.interop = {
if (type !== "") {
link.type = type;
}
if (integrity !== "") {
link.integrity = integrity;
}
if (crossorigin !== "") {
link.crossorigin = crossorigin;
}
document.head.appendChild(link);
}
else {
@ -78,9 +84,15 @@ window.interop = {
if (type !== "" && link.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;
if (id !== "") {
script = document.getElementById(id);
@ -92,6 +104,12 @@ window.interop = {
}
if (src !== "") {
script.src = src;
if (integrity !== "") {
script.integrity = integrity;
}
if (crossorigin !== "") {
script.crossorigin = crossorigin;
}
}
else {
script.innerHTML = content;
@ -108,6 +126,12 @@ window.interop = {
if (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 {
if (script.innerHTML !== content) {