diff --git a/Oqtane.Server/Controllers/SqlController.cs b/Oqtane.Server/Controllers/SqlController.cs index 7e7bedcc..d2a13078 100644 --- a/Oqtane.Server/Controllers/SqlController.cs +++ b/Oqtane.Server/Controllers/SqlController.cs @@ -6,11 +6,8 @@ using Oqtane.Shared; using Oqtane.Infrastructure; using Oqtane.Repository; using Oqtane.Enums; -using System.Data.SqlClient; -using System.Data; -using System.Dynamic; -using Newtonsoft.Json; using System; +using Microsoft.Data.SqlClient; namespace Oqtane.Controllers { diff --git a/Oqtane.Server/Data/Oqtane.db b/Oqtane.Server/Data/Oqtane.db new file mode 100644 index 00000000..d02cc5a4 Binary files /dev/null and b/Oqtane.Server/Data/Oqtane.db differ diff --git a/Oqtane.Server/Infrastructure/DatabaseManager.cs b/Oqtane.Server/Infrastructure/DatabaseManager.cs index 308b5cf0..8a002481 100644 --- a/Oqtane.Server/Infrastructure/DatabaseManager.cs +++ b/Oqtane.Server/Infrastructure/DatabaseManager.cs @@ -1,10 +1,7 @@ using System; using System.Collections.Generic; -using System.Data; using System.IO; using System.Linq; -using System.Reflection; -using DbUp; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Memory; @@ -18,6 +15,11 @@ using Oqtane.Shared; using Oqtane.Enums; using File = System.IO.File; +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable ConvertToUsingDeclaration +// ReSharper disable BuiltInTypeReferenceStyleForMemberAccess +// ReSharper disable UseIndexFromEndExpression + namespace Oqtane.Infrastructure { public class DatabaseManager : IDatabaseManager @@ -166,7 +168,7 @@ namespace Oqtane.Infrastructure { //create data directory if does not exist var dataDirectory = AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString(); - if (!Directory.Exists(dataDirectory)) Directory.CreateDirectory(dataDirectory); + if (!Directory.Exists(dataDirectory)) Directory.CreateDirectory(dataDirectory ?? String.Empty); var connectionString = NormalizeConnectionString(install.ConnectionString); using (var dbc = new DbContext(new DbContextOptionsBuilder().UseOqtaneDatabase(connectionString).Options)) @@ -195,26 +197,20 @@ namespace Oqtane.Infrastructure if (install.TenantName == TenantNames.Master) { - MigrateScriptNamingConvention("Master", install.ConnectionString); - - var upgradeConfig = DeployChanges - .To - .SqlDatabase(NormalizeConnectionString(install.ConnectionString)) - .WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly(), s => s.Contains("Master.") && s.EndsWith(".sql",StringComparison.OrdinalIgnoreCase)); - - var upgrade = upgradeConfig.Build(); - if (upgrade.IsUpgradeRequired()) + try { - var upgradeResult = upgrade.PerformUpgrade(); - result.Success = upgradeResult.Successful; - if (!result.Success) + var dbConfig = new DbConfig(null, null) {ConnectionString = install.ConnectionString}; + + using (var masterDbContext = new MasterDBContext(new DbContextOptions(), dbConfig)) { - result.Message = upgradeResult.Error.Message; + // Push latest model into database + masterDbContext.Database.Migrate(); + result.Success = true; } } - else + catch (Exception ex) { - result.Success = true; + result.Message = ex.Message; } if (result.Success) @@ -251,10 +247,14 @@ namespace Oqtane.Infrastructure tenant = db.Tenant.FirstOrDefault(item => item.Name == install.TenantName); } - foreach (string aliasname in install.Aliases.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) + foreach (var aliasName in install.Aliases.Split(new[] { ',' }, 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); + if (tenant != null) + { + 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"); @@ -270,7 +270,7 @@ namespace Oqtane.Infrastructure { var result = new Installation { Success = false, Message = string.Empty }; - string[] versions = Constants.ReleaseVersions.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + var versions = Constants.ReleaseVersions.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); using (var scope = _serviceScopeFactory.CreateScope()) { @@ -280,29 +280,28 @@ namespace Oqtane.Infrastructure { foreach (var tenant in db.Tenant.ToList()) { - MigrateScriptNamingConvention("Tenant", tenant.DBConnectionString); - - var upgradeConfig = DeployChanges.To.SqlDatabase(NormalizeConnectionString(tenant.DBConnectionString)) - .WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly(), s => s.Contains("Tenant.") && s.EndsWith(".sql", StringComparison.OrdinalIgnoreCase)); - - var upgrade = upgradeConfig.Build(); - if (upgrade.IsUpgradeRequired()) + try { - var upgradeResult = upgrade.PerformUpgrade(); - result.Success = upgradeResult.Successful; - if (!result.Success) + var dbConfig = new DbConfig(null, null) {ConnectionString = install.ConnectionString}; + using (var tenantDbContext = new TenantDBContext(dbConfig, null)) { - result.Message = upgradeResult.Error.Message; + // Push latest model into database + tenantDbContext.Database.Migrate(); + result.Success = true; } } + catch (Exception ex) + { + result.Message = ex.Message; + } // execute any version specific upgrade logic - string version = tenant.Version; - int index = Array.FindIndex(versions, item => item == version); + var version = tenant.Version; + var index = Array.FindIndex(versions, item => item == version); if (index != (versions.Length - 1)) { if (index == -1) index = 0; - for (int i = index; i < versions.Length; i++) + for (var i = index; i < versions.Length; i++) { upgrades.Upgrade(tenant, versions[i]); } @@ -328,53 +327,61 @@ namespace Oqtane.Infrastructure using (var scope = _serviceScopeFactory.CreateScope()) { - var moduledefinitions = scope.ServiceProvider.GetRequiredService(); + var moduleDefinitions = scope.ServiceProvider.GetRequiredService(); var sql = scope.ServiceProvider.GetRequiredService(); - foreach (var moduledefinition in moduledefinitions.GetModuleDefinitions()) + foreach (var moduleDefinition in moduleDefinitions.GetModuleDefinitions()) { - if (!string.IsNullOrEmpty(moduledefinition.ReleaseVersions) && !string.IsNullOrEmpty(moduledefinition.ServerManagerType)) + if (!string.IsNullOrEmpty(moduleDefinition.ReleaseVersions) && !string.IsNullOrEmpty(moduleDefinition.ServerManagerType)) { - Type moduletype = Type.GetType(moduledefinition.ServerManagerType); - if (moduletype != null) + var moduleType = Type.GetType(moduleDefinition.ServerManagerType); + if (moduleType != null) { - string[] versions = moduledefinition.ReleaseVersions.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + var versions = moduleDefinition.ReleaseVersions.Split(new[] { ',' }, 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 != TenantNames.Master) + if (moduleType.GetInterface("IMigratable") != null) { - index = -1; + var moduleObject = ActivatorUtilities.CreateInstance(scope.ServiceProvider, moduleType) as IMigratable; + moduleObject.Migrate(tenant, MigrationType.Up); } - if (index != (versions.Length - 1)) + else { - if (index == -1) index = 0; - for (int i = index; i < versions.Length; i++) + var index = Array.FindIndex(versions, item => item == moduleDefinition.Version); + if (tenant.Name == install.TenantName && install.TenantName != TenantNames.Master) { - try + index = -1; + } + if (index != (versions.Length - 1)) + { + if (index == -1) index = 0; + for (var i = index; i < versions.Length; i++) { - if (moduletype.GetInterface("IInstallable") != null) + try { - var moduleobject = ActivatorUtilities.CreateInstance(scope.ServiceProvider, moduletype); - ((IInstallable)moduleobject).Install(tenant, versions[i]); + if (moduleType.GetInterface("IInstallable") != null) + { + var moduleObject = ActivatorUtilities.CreateInstance(scope.ServiceProvider, moduleType); + ((IInstallable)moduleObject).Install(tenant, versions[i]); + } + else + { + sql.ExecuteScript(tenant, moduleType.Assembly, Utilities.GetTypeName(moduleDefinition.ModuleDefinitionName) + "." + versions[i] + ".sql"); + } } - else + catch (Exception ex) { - sql.ExecuteScript(tenant, moduletype.Assembly, Utilities.GetTypeName(moduledefinition.ModuleDefinitionName) + "." + versions[i] + ".sql"); + result.Message = "An Error Occurred Installing " + moduleDefinition.Name + " Version " + versions[i] + " - " + ex.Message; } } - catch (Exception ex) - { - result.Message = "An Error Occurred Installing " + moduledefinition.Name + " Version " + versions[i] + " - " + ex.Message.ToString(); - } } } } - if (string.IsNullOrEmpty(result.Message) && moduledefinition.Version != versions[versions.Length - 1]) + if (string.IsNullOrEmpty(result.Message) && moduleDefinition.Version != versions[versions.Length - 1]) { - moduledefinition.Version = versions[versions.Length - 1]; - db.Entry(moduledefinition).State = EntityState.Modified; + moduleDefinition.Version = versions[versions.Length - 1]; + db.Entry(moduleDefinition).State = EntityState.Modified; db.SaveChanges(); } } @@ -401,8 +408,8 @@ namespace Oqtane.Infrastructure { // use the SiteState to set the Alias explicitly so the tenant can be resolved var aliases = scope.ServiceProvider.GetRequiredService(); - string firstalias = install.Aliases.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)[0]; - var alias = aliases.GetAliases().FirstOrDefault(item => item.Name == firstalias); + var firstAlias = install.Aliases.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)[0]; + var alias = aliases.GetAliases().FirstOrDefault(item => item.Name == firstAlias); var siteState = scope.ServiceProvider.GetRequiredService(); siteState.Alias = alias; @@ -413,82 +420,88 @@ namespace Oqtane.Infrastructure var tenants = scope.ServiceProvider.GetRequiredService(); var users = scope.ServiceProvider.GetRequiredService(); var roles = scope.ServiceProvider.GetRequiredService(); - var userroles = scope.ServiceProvider.GetRequiredService(); + var userRoles = scope.ServiceProvider.GetRequiredService(); var folders = scope.ServiceProvider.GetRequiredService(); var log = scope.ServiceProvider.GetRequiredService(); var identityUserManager = scope.ServiceProvider.GetRequiredService>(); var tenant = tenants.GetTenants().FirstOrDefault(item => item.Name == install.TenantName); - site = new Site + if (tenant != null) { - 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(UserNames.Host).GetAwaiter().GetResult(); - if (identityUser == null) - { - identityUser = new IdentityUser { UserName = UserNames.Host, Email = install.HostEmail, EmailConfirmed = true }; - var create = identityUserManager.CreateAsync(identityUser, install.HostPassword).GetAwaiter().GetResult(); - if (create.Succeeded) + site = new Site { - var user = new User - { - SiteId = site.SiteId, - Username = UserNames.Host, - Password = install.HostPassword, - Email = install.HostEmail, - DisplayName = install.HostName, - LastIPAddress = "", - LastLoginOn = null - }; + TenantId = tenant.TenantId, + Name = install.SiteName, + LogoFileId = null, + DefaultThemeType = install.DefaultTheme, + DefaultLayoutType = install.DefaultLayout, + DefaultContainerType = install.DefaultContainer, + SiteTemplateType = install.SiteTemplate + }; + site = sites.AddSite(site); - user = users.AddUser(user); - var hostRoleId = roles.GetRoles(user.SiteId, true).FirstOrDefault(item => item.Name == RoleNames.Host)?.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", Path.DirectorySeparatorChar.ToString())); - if (folder != null) + var identityUser = identityUserManager.FindByNameAsync(UserNames.Host).GetAwaiter().GetResult(); + if (identityUser == null) + { + identityUser = new IdentityUser {UserName = UserNames.Host, Email = install.HostEmail, EmailConfirmed = true}; + var create = identityUserManager.CreateAsync(identityUser, install.HostPassword).GetAwaiter().GetResult(); + if (create.Succeeded) { - folders.AddFolder(new Folder + var user = new User { - SiteId = folder.SiteId, - ParentId = folder.FolderId, - Name = "My Folder", - Path = Utilities.PathCombine(folder.Path, user.UserId.ToString(), Path.DirectorySeparatorChar.ToString()), - Order = 1, - IsSystem = true, - Permissions = new List + SiteId = site.SiteId, + Username = UserNames.Host, + 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 == RoleNames.Host)?.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", Path.DirectorySeparatorChar.ToString())); + if (folder != null) + { + folders.AddFolder(new Folder { - new Permission(PermissionNames.Browse, user.UserId, true), - new Permission(PermissionNames.View, RoleNames.Everyone, true), - new Permission(PermissionNames.Edit, user.UserId, true), - }.EncodePermissions(), - }); + SiteId = folder.SiteId, + ParentId = folder.FolderId, + Name = "My Folder", + Path = Utilities.PathCombine(folder.Path, user.UserId.ToString(), Path.DirectorySeparatorChar.ToString()), + Order = 1, + IsSystem = true, + Permissions = new List + { + new Permission(PermissionNames.Browse, user.UserId, true), + new Permission(PermissionNames.View, RoleNames.Everyone, true), + new Permission(PermissionNames.Edit, user.UserId, true), + }.EncodePermissions(), + }); + } } } + + foreach (var aliasName in install.Aliases.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries)) + { + alias = aliases.GetAliases().FirstOrDefault(item => item.Name == aliasName); + if (alias != null) + { + alias.SiteId = site.SiteId; + aliases.UpdateAlias(alias); + } + } + + tenant.Version = Constants.Version; + tenants.UpdateTenant(tenant); } - 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); - } - - tenant.Version = Constants.Version; - tenants.UpdateTenant(tenant); - - log.Log(site.SiteId, LogLevel.Trace, this, LogFunction.Create, "Site Created {Site}", site); + if (site != null) log.Log(site.SiteId, LogLevel.Trace, this, LogFunction.Create, "Site Created {Site}", site); } } } @@ -508,7 +521,7 @@ namespace Oqtane.Infrastructure private string DenormalizeConnectionString(string connectionString) { var dataDirectory = AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString(); - connectionString = connectionString.Replace(dataDirectory, "|DataDirectory|"); + connectionString = connectionString.Replace(dataDirectory ?? String.Empty, "|DataDirectory|"); return connectionString; } @@ -567,18 +580,5 @@ namespace Oqtane.Infrastructure if (string.IsNullOrEmpty(value)) value = defaultValue; return value; } - - private void MigrateScriptNamingConvention(string scriptType, string connectionString) - { - // migrate to new naming convention for scripts - var migrateConfig = DeployChanges.To.SqlDatabase(NormalizeConnectionString(connectionString)) - .WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly(), s => s == scriptType + ".00.00.00.00.sql"); - var migrate = migrateConfig.Build(); - if (migrate.IsUpgradeRequired()) - { - migrate.PerformUpgrade(); - } - } - } } diff --git a/Oqtane.Server/Infrastructure/Interfaces/IInstallable.cs b/Oqtane.Server/Infrastructure/Interfaces/IInstallable.cs index baef7184..48ed9bc8 100644 --- a/Oqtane.Server/Infrastructure/Interfaces/IInstallable.cs +++ b/Oqtane.Server/Infrastructure/Interfaces/IInstallable.cs @@ -1,10 +1,13 @@ -using Oqtane.Models; +using Microsoft.EntityFrameworkCore; +using Oqtane.Enums; +using Oqtane.Models; namespace Oqtane.Infrastructure { public interface IInstallable { bool Install(Tenant tenant, string version); + bool Uninstall(Tenant tenant); } } diff --git a/Oqtane.Server/Infrastructure/Interfaces/IMigratable.cs b/Oqtane.Server/Infrastructure/Interfaces/IMigratable.cs new file mode 100644 index 00000000..009268fc --- /dev/null +++ b/Oqtane.Server/Infrastructure/Interfaces/IMigratable.cs @@ -0,0 +1,10 @@ +using Oqtane.Enums; +using Oqtane.Models; + +namespace Oqtane.Infrastructure +{ + public interface IMigratable + { + bool Migrate(Tenant tenant, MigrationType migrationType); + } +} diff --git a/Oqtane.Server/Modules/HtmlText/Manager/HtmlTextManager.cs b/Oqtane.Server/Modules/HtmlText/Manager/HtmlTextManager.cs index 7e068076..d8afa1b2 100644 --- a/Oqtane.Server/Modules/HtmlText/Manager/HtmlTextManager.cs +++ b/Oqtane.Server/Modules/HtmlText/Manager/HtmlTextManager.cs @@ -1,40 +1,64 @@ -using Oqtane.Infrastructure; +using System; +using Oqtane.Infrastructure; using Oqtane.Models; using Oqtane.Repository; using Oqtane.Modules.HtmlText.Models; using Oqtane.Modules.HtmlText.Repository; using System.Net; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Oqtane.Enums; +// ReSharper disable ConvertToUsingDeclaration namespace Oqtane.Modules.HtmlText.Manager { - public class HtmlTextManager : IInstallable, IPortable + public class HtmlTextManager : IMigratable, IPortable { - private IHtmlTextRepository _htmlTexts; - private ISqlRepository _sql; + private readonly IHtmlTextRepository _htmlText; + private readonly ISqlRepository _sql; - public HtmlTextManager(IHtmlTextRepository htmltexts, ISqlRepository sql) + public HtmlTextManager(IHtmlTextRepository htmlText, ISqlRepository sql) { - _htmlTexts = htmltexts; + _htmlText = htmlText; _sql = sql; } - public bool Install(Tenant tenant, string version) + public bool Migrate(Tenant tenant, MigrationType migrationType) { - return _sql.ExecuteScript(tenant, GetType().Assembly, "HtmlText." + version + ".sql"); - } + var result = true; - public bool Uninstall(Tenant tenant) - { - return _sql.ExecuteScript(tenant, GetType().Assembly, "HtmlText.Uninstall.sql"); + var dbConfig = new DbConfig(null, null) {ConnectionString = tenant.DBConnectionString}; + using (var db = new HtmlTextContext(dbConfig, null)) + { + try + { + var migrator = db.GetService(); + if (migrationType == MigrationType.Down) + { + migrator.Migrate(Migration.InitialDatabase); + } + else + { + migrator.Migrate(); + } + } + catch (Exception e) + { + Console.WriteLine(e); + result = false; + } + + } + return result; } public string ExportModule(Module module) { string content = ""; - HtmlTextInfo htmltext = _htmlTexts.GetHtmlText(module.ModuleId); - if (htmltext != null) + var htmlText = _htmlText.GetHtmlText(module.ModuleId); + if (htmlText != null) { - content = WebUtility.HtmlEncode(htmltext.Content); + content = WebUtility.HtmlEncode(htmlText.Content); } return content; } @@ -42,18 +66,18 @@ namespace Oqtane.Modules.HtmlText.Manager public void ImportModule(Module module, string content, string version) { content = WebUtility.HtmlDecode(content); - HtmlTextInfo htmltext = _htmlTexts.GetHtmlText(module.ModuleId); - if (htmltext != null) + var htmlText = _htmlText.GetHtmlText(module.ModuleId); + if (htmlText != null) { - htmltext.Content = content; - _htmlTexts.UpdateHtmlText(htmltext); + htmlText.Content = content; + _htmlText.UpdateHtmlText(htmlText); } else { - htmltext = new HtmlTextInfo(); - htmltext.ModuleId = module.ModuleId; - htmltext.Content = content; - _htmlTexts.AddHtmlText(htmltext); + htmlText = new HtmlTextInfo(); + htmlText.ModuleId = module.ModuleId; + htmlText.Content = content; + _htmlText.AddHtmlText(htmlText); } } } diff --git a/Oqtane.Server/Modules/HtmlText/Migrations/01000000_InitializeModule.cs b/Oqtane.Server/Modules/HtmlText/Migrations/01000000_InitializeModule.cs new file mode 100644 index 00000000..2214df2c --- /dev/null +++ b/Oqtane.Server/Modules/HtmlText/Migrations/01000000_InitializeModule.cs @@ -0,0 +1,26 @@ +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Oqtane.Modules.HtmlText.Migrations.EntityBuilders; +using Oqtane.Modules.HtmlText.Repository; + +namespace Oqtane.Modules.HtmlText.Migrations +{ + [DbContext(typeof(HtmlTextContext))] + [Migration("HtmlText.01.00.00.00")] + public class InitializeModule : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + //Create HtmlText table + var entityBuilder = new HtmlTextEntityBuilder(migrationBuilder); + entityBuilder.Create(); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + //Drop HtmlText table + var entityBuilder = new HtmlTextEntityBuilder(migrationBuilder); + entityBuilder.Drop(); + } + } +} diff --git a/Oqtane.Server/Modules/HtmlText/Migrations/EntityBuilders/HtmlTextEntityBuilder.cs b/Oqtane.Server/Modules/HtmlText/Migrations/EntityBuilders/HtmlTextEntityBuilder.cs new file mode 100644 index 00000000..972c4c7d --- /dev/null +++ b/Oqtane.Server/Modules/HtmlText/Migrations/EntityBuilders/HtmlTextEntityBuilder.cs @@ -0,0 +1,43 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations.Operations; +using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders; +using Oqtane.Migrations; +using Oqtane.Migrations.EntityBuilders; +using Oqtane.Migrations.Extensions; + +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedAutoPropertyAccessor.Global + +namespace Oqtane.Modules.HtmlText.Migrations.EntityBuilders +{ + public class HtmlTextEntityBuilder : AuditableBaseEntityBuilder + { + private const string _entityTableName = "HtmlText"; + private readonly PrimaryKey _primaryKey = new("PK_HtmlText", x => x.HtmlTextId); + private readonly ForeignKey _moduleForeignKey = new("FK_HtmlText_Module", x => x.ModuleId, "Module", "ModuleId", ReferentialAction.Cascade); + + public HtmlTextEntityBuilder(MigrationBuilder migrationBuilder) : base(migrationBuilder) + { + EntityTableName = _entityTableName; + PrimaryKey = _primaryKey; + ForeignKeys.Add(_moduleForeignKey); + } + + protected override HtmlTextEntityBuilder BuildTable(ColumnsBuilder table) + { + HtmlTextId = table.AddAutoIncrementColumn("HtmlTextId"); + ModuleId = table.AddIntegerColumn("ModuleId"); + Content = table.AddMaxStringColumn("Content"); + + AddAuditableColumns(table); + + return this; + } + + public OperationBuilder HtmlTextId { get; set; } + + public OperationBuilder ModuleId { get; set; } + + public OperationBuilder Content { get; set; } + } +} diff --git a/Oqtane.Server/Modules/HtmlText/Repository/HtmlTextContext.cs b/Oqtane.Server/Modules/HtmlText/Repository/HtmlTextContext.cs index e9ac5d12..f4aaf48c 100644 --- a/Oqtane.Server/Modules/HtmlText/Repository/HtmlTextContext.cs +++ b/Oqtane.Server/Modules/HtmlText/Repository/HtmlTextContext.cs @@ -10,7 +10,7 @@ namespace Oqtane.Modules.HtmlText.Repository { public virtual DbSet HtmlText { get; set; } - public HtmlTextContext(ITenantResolver tenantResolver, IHttpContextAccessor accessor, IConfiguration configuration) : base(tenantResolver, accessor, configuration) + public HtmlTextContext(IDbConfig dbConfig, ITenantResolver tenantResolver) : base(dbConfig, tenantResolver) { // ContextBase handles multi-tenant database connections } diff --git a/Oqtane.Server/Modules/HtmlText/Scripts/HtmlText.1.0.0.sql b/Oqtane.Server/Modules/HtmlText/Scripts/HtmlText.1.0.0.sql deleted file mode 100644 index 5c54eae2..00000000 --- a/Oqtane.Server/Modules/HtmlText/Scripts/HtmlText.1.0.0.sql +++ /dev/null @@ -1,19 +0,0 @@ -CREATE TABLE [dbo].[HtmlText]( - [HtmlTextId] [int] IDENTITY(1,1) NOT NULL, - [ModuleId] [int] NOT NULL, - [Content] [nvarchar](max) NOT NULL, - [CreatedBy] [nvarchar](256) NOT NULL, - [CreatedOn] [datetime] NOT NULL, - [ModifiedBy] [nvarchar](256) NOT NULL, - [ModifiedOn] [datetime] NOT NULL, - CONSTRAINT [PK_HtmlText] PRIMARY KEY CLUSTERED - ( - [HtmlTextId] ASC - ) -) -GO - -ALTER TABLE [dbo].[HtmlText] WITH CHECK ADD CONSTRAINT [FK_HtmlText_Module] FOREIGN KEY([ModuleId]) -REFERENCES [dbo].[Module] ([ModuleId]) -ON DELETE CASCADE -GO diff --git a/Oqtane.Server/Modules/HtmlText/Scripts/HtmlText.Uninstall.sql b/Oqtane.Server/Modules/HtmlText/Scripts/HtmlText.Uninstall.sql deleted file mode 100644 index b0831b67..00000000 --- a/Oqtane.Server/Modules/HtmlText/Scripts/HtmlText.Uninstall.sql +++ /dev/null @@ -1,2 +0,0 @@ -DROP TABLE [dbo].[HtmlText] -GO diff --git a/Oqtane.Server/Oqtane.Server.csproj b/Oqtane.Server/Oqtane.Server.csproj index db1b8192..a87d0533 100644 --- a/Oqtane.Server/Oqtane.Server.csproj +++ b/Oqtane.Server/Oqtane.Server.csproj @@ -20,6 +20,9 @@ + + + @@ -36,14 +39,12 @@ - - - + all @@ -67,6 +68,9 @@ + + + diff --git a/Oqtane.Server/Repository/Context/DBContextBase.cs b/Oqtane.Server/Repository/Context/DBContextBase.cs index d6535f0c..013675d0 100644 --- a/Oqtane.Server/Repository/Context/DBContextBase.cs +++ b/Oqtane.Server/Repository/Context/DBContextBase.cs @@ -7,48 +7,56 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Oqtane.Extensions; using Oqtane.Models; +// ReSharper disable BuiltInTypeReferenceStyleForMemberAccess namespace Oqtane.Repository { public class DBContextBase : IdentityUserContext { - private ITenantResolver _tenantResolver; - private IHttpContextAccessor _accessor; - private readonly IConfiguration _configuration; + private readonly IDbConfig _dbConfig; + private readonly ITenantResolver _tenantResolver; - public DBContextBase(ITenantResolver tenantResolver, IHttpContextAccessor accessor, IConfiguration configuration) + public DBContextBase(IDbConfig dbConfig, ITenantResolver tenantResolver) { + _dbConfig = dbConfig; _tenantResolver = tenantResolver; - _accessor = accessor; - _configuration = configuration; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { - var tenant = _tenantResolver.GetTenant(); - if (tenant != null) + var connectionString = _dbConfig.ConnectionString; + + if (string.IsNullOrEmpty(connectionString) && _tenantResolver != null) + { + var tenant = _tenantResolver.GetTenant(); + var configuration = _dbConfig.Configuration; + + if (tenant != null) + { + connectionString = tenant.DBConnectionString + .Replace("|DataDirectory|", AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString()); + } + else + { + if (!String.IsNullOrEmpty(configuration.GetConnectionString("DefaultConnection"))) + { + connectionString = configuration.GetConnectionString("DefaultConnection") + .Replace("|DataDirectory|", AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString()); + } + } + } + + if (!string.IsNullOrEmpty(connectionString)) { - var connectionString = tenant.DBConnectionString - .Replace("|DataDirectory|", AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString()); optionsBuilder.UseOqtaneDatabase(connectionString); } - else - { - if (!String.IsNullOrEmpty(_configuration.GetConnectionString("DefaultConnection"))) - { - var connectionString = _configuration.GetConnectionString("DefaultConnection") - .Replace("|DataDirectory|", AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString()); - optionsBuilder.UseOqtaneDatabase(connectionString); - } - - } base.OnConfiguring(optionsBuilder); } public override int SaveChanges() { - DbContextUtils.SaveChanges(this, _accessor); + DbContextUtils.SaveChanges(this, _dbConfig.Accessor); return base.SaveChanges(); } diff --git a/Oqtane.Server/Repository/Context/DbConfig.cs b/Oqtane.Server/Repository/Context/DbConfig.cs new file mode 100644 index 00000000..7bc5cb8a --- /dev/null +++ b/Oqtane.Server/Repository/Context/DbConfig.cs @@ -0,0 +1,20 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Configuration; + +namespace Oqtane.Repository +{ + public class DbConfig : IDbConfig + { + public DbConfig(IHttpContextAccessor accessor, IConfiguration configuration) + { + Accessor = accessor; + Configuration = configuration; + } + + public IHttpContextAccessor Accessor { get; } + + public IConfiguration Configuration { get; } + + public string ConnectionString { get; set; } + } +} diff --git a/Oqtane.Server/Repository/Context/DbContextUtils.cs b/Oqtane.Server/Repository/Context/DbContextUtils.cs index 1b4014cb..aa204cad 100644 --- a/Oqtane.Server/Repository/Context/DbContextUtils.cs +++ b/Oqtane.Server/Repository/Context/DbContextUtils.cs @@ -6,7 +6,7 @@ using Oqtane.Models; namespace Oqtane.Repository { - public class DbContextUtils + public static class DbContextUtils { public static void SaveChanges(DbContext context, IHttpContextAccessor accessor) { diff --git a/Oqtane.Server/Repository/Context/InstallationContext.cs b/Oqtane.Server/Repository/Context/InstallationContext.cs index 6eaf5a65..52036bbe 100644 --- a/Oqtane.Server/Repository/Context/InstallationContext.cs +++ b/Oqtane.Server/Repository/Context/InstallationContext.cs @@ -22,5 +22,8 @@ namespace Oqtane.Repository public virtual DbSet Tenant { get; set; } public virtual DbSet ModuleDefinition { get; set; } public virtual DbSet Job { get; set; } + public virtual DbSet JobLog { get; set; } + + } } diff --git a/Oqtane.Server/Repository/Context/MasterDBContext.cs b/Oqtane.Server/Repository/Context/MasterDBContext.cs index dd94dedd..68ef6b07 100644 --- a/Oqtane.Server/Repository/Context/MasterDBContext.cs +++ b/Oqtane.Server/Repository/Context/MasterDBContext.cs @@ -1,31 +1,42 @@ using System; -using System.Linq; using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; using Oqtane.Models; using Microsoft.Extensions.Configuration; using Oqtane.Extensions; +// ReSharper disable BuiltInTypeReferenceStyleForMemberAccess +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable CheckNamespace + namespace Oqtane.Repository { public class MasterDBContext : DbContext { - private readonly IHttpContextAccessor _accessor; - private readonly IConfiguration _configuration; + private readonly IDbConfig _dbConfig; - public MasterDBContext(DbContextOptions options, IHttpContextAccessor accessor, IConfiguration configuration) : base(options) + public MasterDBContext(DbContextOptions options, IDbConfig dbConfig) : base(options) { - _accessor = accessor; - _configuration = configuration; + _dbConfig = dbConfig; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { - if (!String.IsNullOrEmpty(_configuration.GetConnectionString("DefaultConnection"))) - { - var connectionString = _configuration.GetConnectionString("DefaultConnection") - .Replace("|DataDirectory|", AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString()); + var connectionString = _dbConfig.ConnectionString; + var configuration = _dbConfig.Configuration; + if(string.IsNullOrEmpty(connectionString) && configuration != null) + { + if (!String.IsNullOrEmpty(configuration.GetConnectionString("DefaultConnection"))) + { + connectionString = configuration.GetConnectionString("DefaultConnection") + .Replace("|DataDirectory|", AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString()); + } + + } + + if (!string.IsNullOrEmpty(connectionString)) + { optionsBuilder.UseOqtaneDatabase(connectionString); } base.OnConfiguring(optionsBuilder); @@ -39,7 +50,7 @@ namespace Oqtane.Repository public override int SaveChanges() { - DbContextUtils.SaveChanges(this, _accessor); + DbContextUtils.SaveChanges(this, _dbConfig.Accessor); return base.SaveChanges(); } diff --git a/Oqtane.Server/Repository/Context/TenantDBContext.cs b/Oqtane.Server/Repository/Context/TenantDBContext.cs index 4390739f..3e2f5d3d 100644 --- a/Oqtane.Server/Repository/Context/TenantDBContext.cs +++ b/Oqtane.Server/Repository/Context/TenantDBContext.cs @@ -24,7 +24,7 @@ namespace Oqtane.Repository public virtual DbSet Language { get; set; } - public TenantDBContext(ITenantResolver tenantResolver, IHttpContextAccessor accessor, IConfiguration configuration) : base(tenantResolver, accessor, configuration) + public TenantDBContext(IDbConfig dbConfig, ITenantResolver tenantResolver) : base(dbConfig, tenantResolver) { // DBContextBase handles multi-tenant database connections } diff --git a/Oqtane.Server/Repository/Interfaces/IDbConfig.cs b/Oqtane.Server/Repository/Interfaces/IDbConfig.cs new file mode 100644 index 00000000..6ba4cfbe --- /dev/null +++ b/Oqtane.Server/Repository/Interfaces/IDbConfig.cs @@ -0,0 +1,14 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Configuration; + +namespace Oqtane.Repository +{ + public interface IDbConfig + { + public IHttpContextAccessor Accessor { get; } + + public IConfiguration Configuration { get; } + + public string ConnectionString { get; set; } + } +} diff --git a/Oqtane.Server/Repository/Interfaces/ISqlRepository.cs b/Oqtane.Server/Repository/Interfaces/ISqlRepository.cs index a1402e3b..76a6f701 100644 --- a/Oqtane.Server/Repository/Interfaces/ISqlRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/ISqlRepository.cs @@ -1,5 +1,5 @@ -using System.Data.SqlClient; -using System.Reflection; +using System.Reflection; +using Microsoft.Data.SqlClient; using Oqtane.Models; namespace Oqtane.Repository diff --git a/Oqtane.Server/Repository/SqlRepository.cs b/Oqtane.Server/Repository/SqlRepository.cs index fcc4caf3..037a9e42 100644 --- a/Oqtane.Server/Repository/SqlRepository.cs +++ b/Oqtane.Server/Repository/SqlRepository.cs @@ -1,9 +1,9 @@ using System; using System.Data; -using System.Data.SqlClient; using System.IO; using System.Linq; using System.Reflection; +using Microsoft.Data.SqlClient; using Oqtane.Models; namespace Oqtane.Repository @@ -59,8 +59,8 @@ namespace Oqtane.Repository public int ExecuteNonQuery(Tenant tenant, string query) { - SqlConnection conn = new SqlConnection(FormatConnectionString(tenant.DBConnectionString)); - SqlCommand cmd = conn.CreateCommand(); + var conn = new SqlConnection(FormatConnectionString(tenant.DBConnectionString)); + var cmd = conn.CreateCommand(); using (conn) { PrepareCommand(conn, cmd, query); diff --git a/Oqtane.Server/Startup.cs b/Oqtane.Server/Startup.cs index ba2a7d89..5ba2ff1b 100644 --- a/Oqtane.Server/Startup.cs +++ b/Oqtane.Server/Startup.cs @@ -130,6 +130,7 @@ namespace Oqtane services.AddSingleton(); + services.AddScoped(); services.AddDbContext(options => { }); services.AddDbContext(options => { }); @@ -211,7 +212,7 @@ namespace Oqtane services.AddTransient(); services.AddTransient(); - // load the external assemblies into the app domain, install services + // load the external assemblies into the app domain, install services services.AddOqtane(_runtime, _supportedCultures); services.AddMvc() diff --git a/Oqtane.Server/appsettings.json b/Oqtane.Server/appsettings.json index 1af87b4c..06f423f8 100644 --- a/Oqtane.Server/appsettings.json +++ b/Oqtane.Server/appsettings.json @@ -1,6 +1,10 @@ { + "Database": { + "DatabaseType": "", + "DatabaseEngineVersion": "" + }, "ConnectionStrings": { - "DefaultConnection": "" + "DefaultConnection": "Data Source=.;Initial Catalog=Oqtane-Migrations;Integrated Security=SSPI;" }, "Runtime": "Server", "Installation": { diff --git a/Oqtane.Server/wwwroot/Modules/Oqtane.Blogs/Module.css b/Oqtane.Server/wwwroot/Modules/Oqtane.Blogs/Module.css new file mode 100644 index 00000000..0856a263 --- /dev/null +++ b/Oqtane.Server/wwwroot/Modules/Oqtane.Blogs/Module.css @@ -0,0 +1 @@ +/* Module Custom Styles */ \ No newline at end of file diff --git a/Oqtane.Server/wwwroot/Modules/Oqtane.Blogs/Module.js b/Oqtane.Server/wwwroot/Modules/Oqtane.Blogs/Module.js new file mode 100644 index 00000000..1b415a08 --- /dev/null +++ b/Oqtane.Server/wwwroot/Modules/Oqtane.Blogs/Module.js @@ -0,0 +1 @@ +/* Module Script */ \ No newline at end of file diff --git a/Oqtane.Server/wwwroot/Modules/Oqtane.Blogs/assets.json b/Oqtane.Server/wwwroot/Modules/Oqtane.Blogs/assets.json new file mode 100644 index 00000000..72f421fc --- /dev/null +++ b/Oqtane.Server/wwwroot/Modules/Oqtane.Blogs/assets.json @@ -0,0 +1 @@ +["\\bin\\Debug\\net5.0\\Oqtane.Blogs.Client.Oqtane.dll","\\bin\\Debug\\net5.0\\Oqtane.Blogs.Client.Oqtane.pdb","\\bin\\Debug\\net5.0\\Oqtane.Blogs.Server.Oqtane.dll","\\bin\\Debug\\net5.0\\Oqtane.Blogs.Server.Oqtane.pdb","\\bin\\Debug\\net5.0\\Oqtane.Blogs.Shared.Oqtane.dll","\\bin\\Debug\\net5.0\\Oqtane.Blogs.Shared.Oqtane.pdb","\\wwwroot\\Modules\\Oqtane.Blogs\\Module.css","\\wwwroot\\Modules\\Oqtane.Blogs\\Module.js"] \ No newline at end of file diff --git a/Oqtane.Shared/Enums/MigrationType.cs b/Oqtane.Shared/Enums/MigrationType.cs new file mode 100644 index 00000000..e428520a --- /dev/null +++ b/Oqtane.Shared/Enums/MigrationType.cs @@ -0,0 +1,8 @@ +namespace Oqtane.Enums +{ + public enum MigrationType + { + Up, + Down + } +}