From def5167177309620f90fa33b41598bc5381dce88 Mon Sep 17 00:00:00 2001 From: Yunus Emre Kalkan Date: Fri, 26 Feb 2021 14:41:57 +0300 Subject: [PATCH 01/43] Cli: AppTemplate ConfigureTenantSchema --- .../Templates/App/AppTemplateBase.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AppTemplateBase.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AppTemplateBase.cs index 2a596b73a7..a725a513e7 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AppTemplateBase.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AppTemplateBase.cs @@ -24,6 +24,7 @@ namespace Volo.Abp.Cli.ProjectBuilding.Templates.App { var steps = new List(); + ConfigureTenantSchema(context, steps); SwitchDatabaseProvider(context, steps); DeleteUnrelatedProjects(context, steps); RemoveMigrations(context, steps); @@ -38,6 +39,19 @@ namespace Volo.Abp.Cli.ProjectBuilding.Templates.App return steps; } + private static void ConfigureTenantSchema(ProjectBuildContext context, List steps) + { + if (context.BuildArgs.ExtraProperties.ContainsKey("separate-tenant-schema")) + { + steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations")); + steps.Add(new AppTemplateProjectRenameStep("MyCompanyName.MyProjectName.EntityFrameworkCore.SeparateDbMigrations", "MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations")); + } + else + { + steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.EntityFrameworkCore.SeparateDbMigrations")); + } + } + private static void SwitchDatabaseProvider(ProjectBuildContext context, List steps) { if (context.BuildArgs.DatabaseProvider == DatabaseProvider.MongoDb) From 055e33cb50cd7d5bedb9012d584f40392308bd01 Mon Sep 17 00:00:00 2001 From: PM Extra Date: Mon, 1 Mar 2021 10:10:32 +0800 Subject: [PATCH 02/43] Improve MongoDbCoreRepositoryExtensions As same as #5697, and fix a bug. --- .../MongoDbCoreRepositoryExtensions.cs | 44 +++++++++---------- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDbCoreRepositoryExtensions.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDbCoreRepositoryExtensions.cs index bcbdc00d9e..0f74592391 100644 --- a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDbCoreRepositoryExtensions.cs +++ b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDbCoreRepositoryExtensions.cs @@ -11,60 +11,56 @@ namespace Volo.Abp.Domain.Repositories public static class MongoDbCoreRepositoryExtensions { [Obsolete("Use GetDatabaseAsync method.")] - public static IMongoDatabase GetDatabase(this IBasicRepository repository) - where TEntity : class, IEntity + public static IMongoDatabase GetDatabase(this IBasicRepository repository) + where TEntity : class, IEntity { return repository.ToMongoDbRepository().Database; } - public static Task GetDatabaseAsync(this IBasicRepository repository, CancellationToken cancellationToken = default) - where TEntity : class, IEntity + public static Task GetDatabaseAsync(this IBasicRepository repository, CancellationToken cancellationToken = default) + where TEntity : class, IEntity { return repository.ToMongoDbRepository().GetDatabaseAsync(cancellationToken); } [Obsolete("Use GetCollectionAsync method.")] - public static IMongoCollection GetCollection(this IBasicRepository repository) - where TEntity : class, IEntity + public static IMongoCollection GetCollection(this IBasicRepository repository) + where TEntity : class, IEntity { return repository.ToMongoDbRepository().Collection; } - public static Task> GetCollectionAsync(this IBasicRepository repository, CancellationToken cancellationToken = default) - where TEntity : class, IEntity + public static Task> GetCollectionAsync(this IBasicRepository repository, CancellationToken cancellationToken = default) + where TEntity : class, IEntity { return repository.ToMongoDbRepository().GetCollectionAsync(cancellationToken); } [Obsolete("Use GetMongoQueryableAsync method.")] - public static IMongoQueryable GetMongoQueryable(this IBasicRepository repository) - where TEntity : class, IEntity + public static IMongoQueryable GetMongoQueryable(this IBasicRepository repository) + where TEntity : class, IEntity { return repository.ToMongoDbRepository().GetMongoQueryable(); } - public static Task> GetMongoQueryableAsync(this IBasicRepository repository, CancellationToken cancellationToken = default) - where TEntity : class, IEntity + public static Task> GetMongoQueryableAsync(this IBasicRepository repository, CancellationToken cancellationToken = default) + where TEntity : class, IEntity { return repository.ToMongoDbRepository().GetMongoQueryableAsync(cancellationToken); } - public static Task> GetAggregateAsync(this IBasicRepository repository, CancellationToken cancellationToken = default) - where TEntity : class, IEntity + public static async Task> GetAggregateAsync(this IBasicRepository repository, CancellationToken cancellationToken = default) + where TEntity : class, IEntity { - return repository.ToMongoDbRepository().GetAggregateAsync(cancellationToken); + var collection = await repository.ToMongoDbRepository().GetCollectionAsync(cancellationToken); + return collection.Aggregate(); } - public static IMongoDbRepository ToMongoDbRepository(this IBasicRepository repository) - where TEntity : class, IEntity + public static IMongoDbRepository ToMongoDbRepository(this IBasicRepository repository) + where TEntity : class, IEntity { - var mongoDbRepository = repository as IMongoDbRepository; - if (mongoDbRepository == null) - { - throw new ArgumentException("Given repository does not implement " + typeof(IMongoDbRepository).AssemblyQualifiedName, nameof(repository)); - } - - return mongoDbRepository; + if (repository is IMongoDbRepository mongoDbRepository) return mongoDbRepository; + throw new ArgumentException("Given repository does not implement " + typeof(IMongoDbRepository).AssemblyQualifiedName, nameof(repository)); } } } From 020bffaa90d99c0929d5050ecc03c424e2dbcccc Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Mon, 1 Mar 2021 10:21:22 +0800 Subject: [PATCH 03/43] Update MongoDbCoreRepositoryExtensions.cs --- .../Domain/Repositories/MongoDbCoreRepositoryExtensions.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDbCoreRepositoryExtensions.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDbCoreRepositoryExtensions.cs index 0f74592391..3d27de6df6 100644 --- a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDbCoreRepositoryExtensions.cs +++ b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDbCoreRepositoryExtensions.cs @@ -59,7 +59,10 @@ namespace Volo.Abp.Domain.Repositories public static IMongoDbRepository ToMongoDbRepository(this IBasicRepository repository) where TEntity : class, IEntity { - if (repository is IMongoDbRepository mongoDbRepository) return mongoDbRepository; + if (repository is IMongoDbRepository mongoDbRepository) + { + return mongoDbRepository; + } throw new ArgumentException("Given repository does not implement " + typeof(IMongoDbRepository).AssemblyQualifiedName, nameof(repository)); } } From af3881ba80238272f732008cf125735b1bbe016a Mon Sep 17 00:00:00 2001 From: Yunus Emre Kalkan Date: Mon, 1 Mar 2021 09:46:49 +0300 Subject: [PATCH 04/43] Update CreateMigrationAndRunMigrator to support --separate-tenant-schema --- .../CreateMigrationAndRunMigratorCommand.cs | 76 ++++++++++++++++--- 1 file changed, 65 insertions(+), 11 deletions(-) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CreateMigrationAndRunMigratorCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CreateMigrationAndRunMigratorCommand.cs index 68fae779ce..5152e7af2e 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CreateMigrationAndRunMigratorCommand.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CreateMigrationAndRunMigratorCommand.cs @@ -26,7 +26,9 @@ namespace Volo.Abp.Cli.Commands throw new CliUsageException("DbMigrations folder path is missing!"); } - var dbMigratorProjectPath = GetDbMigratorProjectPath(commandLineArgs.Target); + var dbMigrationsFolder = commandLineArgs.Target; + + var dbMigratorProjectPath = GetDbMigratorProjectPath(dbMigrationsFolder); if (dbMigratorProjectPath == null) { throw new Exception("DbMigrator is not found!"); @@ -37,13 +39,17 @@ namespace Volo.Abp.Cli.Commands InstallDotnetEfTool(); } - var addMigrationCmd = $"cd \"{commandLineArgs.Target}\" && " + - $"dotnet ef migrations add Initial -s \"{dbMigratorProjectPath}\""; + var tenantDbContextName = FindTenantDbContextName(dbMigrationsFolder); + var dbContextName = tenantDbContextName != null ? + FindDbContextName(dbMigrationsFolder) + : null; + + var migrationOutput = AddMigrationAndGetOutput(dbMigrationsFolder, dbMigratorProjectPath, dbContextName); + var tenantMigrationOutput = tenantDbContextName != null ? + AddMigrationAndGetOutput(dbMigrationsFolder, dbMigratorProjectPath, tenantDbContextName) + : null; - var output = CmdHelper.RunCmdAndGetOutput(addMigrationCmd); - if (output.Contains("Done.") && - output.Contains("To undo this action") && - output.Contains("ef migrations remove")) + if (CheckMigrationOutput(migrationOutput) && CheckMigrationOutput(tenantMigrationOutput)) { // Migration added successfully CmdHelper.RunCmd("cd \"" + Path.GetDirectoryName(dbMigratorProjectPath) + "\" && dotnet run"); @@ -51,22 +57,70 @@ namespace Volo.Abp.Cli.Commands } else { - var exceptionMsg = "Migrations failed! The following command didn't run successfully:" + + var exceptionMsg = "Migrations failed! A migration command didn't run successfully:" + Environment.NewLine + - addMigrationCmd + - Environment.NewLine + output; + Environment.NewLine + migrationOutput + + Environment.NewLine + + Environment.NewLine + tenantMigrationOutput; Logger.LogError(exceptionMsg); throw new Exception(exceptionMsg); } } + private string FindTenantDbContextName(string dbMigrationsFolder) + { + var tenantDbContext = Directory + .GetFiles(dbMigrationsFolder, "*TenantMigrationsDbContext.cs", SearchOption.AllDirectories) + .FirstOrDefault(); + + if (tenantDbContext == null) + { + return null; + } + + return Path.GetFileName(tenantDbContext).RemovePostFix(".cs"); + } + + private string FindDbContextName(string dbMigrationsFolder) + { + var dbContext = Directory + .GetFiles(dbMigrationsFolder, "*MigrationsDbContext.cs", SearchOption.AllDirectories) + .FirstOrDefault(fp => !fp.EndsWith("TenantMigrationsDbContext.cs")); + + if (dbContext == null) + { + return null; + } + + return Path.GetFileName(dbContext).RemovePostFix(".cs"); + } + + private static string AddMigrationAndGetOutput(string dbMigrationsFolder, string dbMigratorProjectPath, string dbContext) + { + var dbContextOption = string.IsNullOrWhiteSpace(dbContext) + ? string.Empty + : $"--context {dbContext}"; + + var addMigrationCmd = $"cd \"{dbMigrationsFolder}\" && " + + $"dotnet ef migrations add Initial -s \"{dbMigratorProjectPath}\" {dbContextOption}"; + + return CmdHelper.RunCmdAndGetOutput(addMigrationCmd); + } + private static bool IsDotNetEfToolInstalled() { var output = CmdHelper.RunCmdAndGetOutput("dotnet tool list -g"); return output.Contains("dotnet-ef"); } + private static bool CheckMigrationOutput(string output) + { + return output == null || (output.Contains("Done.") && + output.Contains("To undo this action") && + output.Contains("ef migrations remove")); + } + private void InstallDotnetEfTool() { Logger.LogInformation("Installing dotnet-ef tool..."); @@ -95,4 +149,4 @@ namespace Volo.Abp.Cli.Commands return string.Empty; } } -} \ No newline at end of file +} From 6836cf9e7192047d243488c135e6ae4e7ea3bdb6 Mon Sep 17 00:00:00 2001 From: Yunus Emre Kalkan Date: Mon, 1 Mar 2021 10:04:05 +0300 Subject: [PATCH 05/43] Update RemoveCmsKitStep to support --separate-tenant-schema --- .../Abp/Cli/ProjectBuilding/Building/Steps/RemoveCmsKitStep.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RemoveCmsKitStep.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RemoveCmsKitStep.cs index 4f63d377ea..789f9e47cb 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RemoveCmsKitStep.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RemoveCmsKitStep.cs @@ -11,6 +11,7 @@ namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps f.Name.EndsWith(".csproj") || f.Name.EndsWith("Module.cs") || f.Name.EndsWith("MyProjectNameMigrationsDbContext.cs") || + f.Name.EndsWith("MyProjectNameMigrationsDbContextBase.cs") || f.Name.EndsWith("MyProjectNameGlobalFeatureConfigurator.cs") || (f.Name.EndsWith(".cshtml") && f.Name.Contains("MyCompanyName.MyProjectName.Web.Public")) ); From 43d16e1defbc46653bedbbc4ad2b795800ccaf47 Mon Sep 17 00:00:00 2001 From: Yunus Emre Kalkan Date: Mon, 1 Mar 2021 10:04:28 +0300 Subject: [PATCH 06/43] update DatabaseManagementSystemChangeStep to support --separate-tenant-schema --- .../Building/Steps/DatabaseManagementSystemChangeStep.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/DatabaseManagementSystemChangeStep.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/DatabaseManagementSystemChangeStep.cs index 4286710899..605cc842f5 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/DatabaseManagementSystemChangeStep.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/DatabaseManagementSystemChangeStep.cs @@ -54,7 +54,8 @@ namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps private void AdjustOracleDbContextOptionsBuilder(ProjectBuildContext context) { - var dbContextFactoryFile = context.Files.First(f => f.Name.EndsWith("MigrationsDbContextFactory.cs", StringComparison.OrdinalIgnoreCase)); + var dbContextFactoryFile = context.Files.FirstOrDefault(f => f.Name.EndsWith("MigrationsDbContextFactoryBase.cs", StringComparison.OrdinalIgnoreCase)) + ?? context.Files.First(f => f.Name.EndsWith("MigrationsDbContextFactory.cs", StringComparison.OrdinalIgnoreCase)); dbContextFactoryFile.ReplaceText("new DbContextOptionsBuilder", $"(DbContextOptionsBuilder<{context.BuildArgs.SolutionName.ProjectName}MigrationsDbContext>) new DbContextOptionsBuilder"); @@ -62,7 +63,8 @@ namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps private void AddMySqlServerVersion(ProjectBuildContext context) { - var dbContextFactoryFile = context.Files.First(f => f.Name.EndsWith("MigrationsDbContextFactory.cs", StringComparison.OrdinalIgnoreCase)); + var dbContextFactoryFile = context.Files.FirstOrDefault(f => f.Name.EndsWith("MigrationsDbContextFactoryBase.cs", StringComparison.OrdinalIgnoreCase)) + ?? context.Files.First(f => f.Name.EndsWith("MigrationsDbContextFactory.cs", StringComparison.OrdinalIgnoreCase)); dbContextFactoryFile.ReplaceText("configuration.GetConnectionString(\"Default\")", "configuration.GetConnectionString(\"Default\"), MySqlServerVersion.LatestSupportedServerVersion"); @@ -90,7 +92,8 @@ namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps var efCoreModuleClass = context.Files.First(f => f.Name.EndsWith("EntityFrameworkCoreModule.cs", StringComparison.OrdinalIgnoreCase)); efCoreModuleClass.ReplaceText(oldUseMethod, newUseMethodForEfModule); - var dbContextFactoryFile = context.Files.First(f => f.Name.EndsWith("MigrationsDbContextFactory.cs", StringComparison.OrdinalIgnoreCase)); + var dbContextFactoryFile = context.Files.FirstOrDefault(f => f.Name.EndsWith("MigrationsDbContextFactoryBase.cs", StringComparison.OrdinalIgnoreCase)) + ?? context.Files.First(f => f.Name.EndsWith("MigrationsDbContextFactory.cs", StringComparison.OrdinalIgnoreCase)); dbContextFactoryFile.ReplaceText(oldUseMethod, newUseMethodForDbContext); } } From 9ba7fb885957e471382d9b65de8b8a4981f61ac7 Mon Sep 17 00:00:00 2001 From: maliming Date: Mon, 1 Mar 2021 16:00:32 +0800 Subject: [PATCH 07/43] Use RequiredPermissionName for all ApplicationMenuItem & ToolbarItem. Resolve #7883 --- .../Toolbars/ToolbarManager.cs | 2 +- .../Volo/Abp/Ui/Navigation/MenuManager.cs | 2 +- .../BloggingAdminMenuContributor.cs | 13 +++---------- .../Navigation/DocsMenuContributor.cs | 11 ++--------- .../AbpTenantManagementWebMainMenuContributor.cs | 5 +---- 5 files changed, 8 insertions(+), 25 deletions(-) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Toolbars/ToolbarManager.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Toolbars/ToolbarManager.cs index d6fc02f153..2e97cebecb 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Toolbars/ToolbarManager.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Toolbars/ToolbarManager.cs @@ -52,7 +52,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Toolbars if (requiredPermissionItems.Any()) { var permissionChecker = serviceProvider.GetRequiredService(); - var grantResult = await permissionChecker.IsGrantedAsync(requiredPermissionItems.Select(x => x.RequiredPermissionName).ToArray()); + var grantResult = await permissionChecker.IsGrantedAsync(requiredPermissionItems.Select(x => x.RequiredPermissionName).Distinct().ToArray()); var toBeDeleted = new HashSet(); foreach (var item in requiredPermissionItems) diff --git a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/MenuManager.cs b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/MenuManager.cs index 28ac86d4d2..960389230e 100644 --- a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/MenuManager.cs +++ b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/MenuManager.cs @@ -51,7 +51,7 @@ namespace Volo.Abp.UI.Navigation if (requiredPermissionItems.Any()) { var permissionChecker = serviceProvider.GetRequiredService(); - var grantResult = await permissionChecker.IsGrantedAsync(requiredPermissionItems.Select(x => x.RequiredPermissionName).ToArray()); + var grantResult = await permissionChecker.IsGrantedAsync(requiredPermissionItems.Select(x => x.RequiredPermissionName).Distinct().ToArray()); var toBeDeleted = new HashSet(); foreach (var menu in requiredPermissionItems) diff --git a/modules/blogging/src/Volo.Blogging.Admin.Web/BloggingAdminMenuContributor.cs b/modules/blogging/src/Volo.Blogging.Admin.Web/BloggingAdminMenuContributor.cs index 95a403678f..e2011acf43 100644 --- a/modules/blogging/src/Volo.Blogging.Admin.Web/BloggingAdminMenuContributor.cs +++ b/modules/blogging/src/Volo.Blogging.Admin.Web/BloggingAdminMenuContributor.cs @@ -18,18 +18,11 @@ namespace Volo.Blogging.Admin { var l = context.GetLocalizer(); - if (await context.IsGrantedAsync(BloggingPermissions.Blogs.Management)) - { - var managementRootMenuItem = new ApplicationMenuItem("BlogManagement", l["Menu:BlogManagement"]); + var managementRootMenuItem = new ApplicationMenuItem("BlogManagement", l["Menu:BlogManagement"], requiredPermissionName: BloggingPermissions.Blogs.Management); - //TODO: Using the same permission. Reconsider. - if (await context.IsGrantedAsync(BloggingPermissions.Blogs.Management)) - { - managementRootMenuItem.AddItem(new ApplicationMenuItem("BlogManagement.Blogs", l["Menu:Blogs"], "~/Blogging/Admin/Blogs")); - } + managementRootMenuItem.AddItem(new ApplicationMenuItem("BlogManagement.Blogs", l["Menu:Blogs"], "~/Blogging/Admin/Blogs", requiredPermissionName: BloggingPermissions.Blogs.Management)); - context.Menu.GetAdministration().AddItem(managementRootMenuItem); - } + context.Menu.GetAdministration().AddItem(managementRootMenuItem); } } } diff --git a/modules/docs/src/Volo.Docs.Admin.Web/Navigation/DocsMenuContributor.cs b/modules/docs/src/Volo.Docs.Admin.Web/Navigation/DocsMenuContributor.cs index 9bc55d543f..8b53069132 100644 --- a/modules/docs/src/Volo.Docs.Admin.Web/Navigation/DocsMenuContributor.cs +++ b/modules/docs/src/Volo.Docs.Admin.Web/Navigation/DocsMenuContributor.cs @@ -27,15 +27,8 @@ namespace Volo.Docs.Admin.Navigation administrationMenu.AddItem(rootMenuItem); - if (await context.IsGrantedAsync(DocsAdminPermissions.Projects.Default)) - { - rootMenuItem.AddItem(new ApplicationMenuItem(DocsMenuNames.Projects, l["Menu:ProjectManagement"], "~/Docs/Admin/Projects")); - } - - if (await context.IsGrantedAsync(DocsAdminPermissions.Documents.Default)) - { - rootMenuItem.AddItem(new ApplicationMenuItem(DocsMenuNames.Documents, l["Menu:DocumentManagement"], "~/Docs/Admin/Documents")); - } + rootMenuItem.AddItem(new ApplicationMenuItem(DocsMenuNames.Projects, l["Menu:ProjectManagement"], "~/Docs/Admin/Projects", requiredPermissionName: DocsAdminPermissions.Projects.Default)); + rootMenuItem.AddItem(new ApplicationMenuItem(DocsMenuNames.Documents, l["Menu:DocumentManagement"], "~/Docs/Admin/Documents", requiredPermissionName: DocsAdminPermissions.Documents.Default)); } } } diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Navigation/AbpTenantManagementWebMainMenuContributor.cs b/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Navigation/AbpTenantManagementWebMainMenuContributor.cs index 0a36e9d130..45f9b75487 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Navigation/AbpTenantManagementWebMainMenuContributor.cs +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Navigation/AbpTenantManagementWebMainMenuContributor.cs @@ -23,10 +23,7 @@ namespace Volo.Abp.TenantManagement.Web.Navigation var tenantManagementMenuItem = new ApplicationMenuItem(TenantManagementMenuNames.GroupName, l["Menu:TenantManagement"], icon: "fa fa-users"); administrationMenu.AddItem(tenantManagementMenuItem); - if (await context.IsGrantedAsync(TenantManagementPermissions.Tenants.Default)) - { - tenantManagementMenuItem.AddItem(new ApplicationMenuItem(TenantManagementMenuNames.Tenants, l["Tenants"], url: "~/TenantManagement/Tenants")); - } + tenantManagementMenuItem.AddItem(new ApplicationMenuItem(TenantManagementMenuNames.Tenants, l["Tenants"], url: "~/TenantManagement/Tenants", requiredPermissionName: TenantManagementPermissions.Tenants.Default)); } } } From 216fd95548e173a57c3b6472150f551a690e56d8 Mon Sep 17 00:00:00 2001 From: Yunus Emre Kalkan Date: Mon, 1 Mar 2021 11:16:27 +0300 Subject: [PATCH 08/43] Update EfCoreMigrationManager to support --separate-tenant-schema --- .../EfCoreMigrationManager.cs | 57 ++++++++++++++++++- 1 file changed, 54 insertions(+), 3 deletions(-) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/EfCoreMigrationManager.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/EfCoreMigrationManager.cs index b15ffaa274..7bb3929f45 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/EfCoreMigrationManager.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/EfCoreMigrationManager.cs @@ -19,12 +19,35 @@ namespace Volo.Abp.Cli.ProjectModification public void AddMigration(string dbMigrationsCsprojFile, string module, string startupProject) { + var dbMigrationsProjectFolder = Path.GetDirectoryName(dbMigrationsCsprojFile); var moduleName = ParseModuleName(module); var migrationName = "Added_" + moduleName + "_Module" + GetUniquePostFix(); - CmdHelper.RunCmd("cd \"" + Path.GetDirectoryName(dbMigrationsCsprojFile) + - "\" && dotnet ef migrations add " + migrationName + - GetStartupProjectOption(startupProject)); + var tenantDbContextName = FindTenantDbContextName(dbMigrationsProjectFolder); + var dbContextName = tenantDbContextName != null ? + FindDbContextName(dbMigrationsProjectFolder) + : null; + + if (!string.IsNullOrEmpty(tenantDbContextName)) + { + RunAddMigrationCommand(dbMigrationsProjectFolder, migrationName, startupProject, tenantDbContextName); + } + + RunAddMigrationCommand(dbMigrationsProjectFolder, migrationName, startupProject, dbContextName); + } + + protected virtual void RunAddMigrationCommand( + string dbMigrationsProjectFolder, + string migrationName, + string startupProject, + string dbContext) + { + var dbContextOption = string.IsNullOrWhiteSpace(dbContext) + ? string.Empty + : $"--context {dbContext}"; + + CmdHelper.RunCmd($"cd \"{dbMigrationsProjectFolder}\" && dotnet ef migrations add {migrationName}" + + $" {GetStartupProjectOption(startupProject)} {dbContextOption}"); } protected virtual string ParseModuleName(string fullModuleName) @@ -47,5 +70,33 @@ namespace Volo.Abp.Cli.ProjectModification { return startupProject.IsNullOrWhiteSpace() ? "" : $" -s {startupProject}"; } + + protected virtual string FindDbContextName(string dbMigrationsFolder) + { + var dbContext = Directory + .GetFiles(dbMigrationsFolder, "*MigrationsDbContext.cs", SearchOption.AllDirectories) + .FirstOrDefault(fp => !fp.EndsWith("TenantMigrationsDbContext.cs")); + + if (dbContext == null) + { + return null; + } + + return Path.GetFileName(dbContext).RemovePostFix(".cs"); + } + + protected virtual string FindTenantDbContextName(string dbMigrationsFolder) + { + var tenantDbContext = Directory + .GetFiles(dbMigrationsFolder, "*TenantMigrationsDbContext.cs", SearchOption.AllDirectories) + .FirstOrDefault(); + + if (tenantDbContext == null) + { + return null; + } + + return Path.GetFileName(tenantDbContext).RemovePostFix(".cs"); + } } } From 17239580d9926664172affece543e1530437b374 Mon Sep 17 00:00:00 2001 From: PM Extra Date: Mon, 1 Mar 2021 16:30:32 +0800 Subject: [PATCH 09/43] Rollback GetAggregateAsync --- .../Domain/Repositories/MongoDbCoreRepositoryExtensions.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDbCoreRepositoryExtensions.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDbCoreRepositoryExtensions.cs index 3d27de6df6..f3ca43b80e 100644 --- a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDbCoreRepositoryExtensions.cs +++ b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDbCoreRepositoryExtensions.cs @@ -49,11 +49,10 @@ namespace Volo.Abp.Domain.Repositories return repository.ToMongoDbRepository().GetMongoQueryableAsync(cancellationToken); } - public static async Task> GetAggregateAsync(this IBasicRepository repository, CancellationToken cancellationToken = default) + public static Task> GetAggregateAsync(this IBasicRepository repository, CancellationToken cancellationToken = default) where TEntity : class, IEntity { - var collection = await repository.ToMongoDbRepository().GetCollectionAsync(cancellationToken); - return collection.Aggregate(); + return repository.ToMongoDbRepository().GetAggregateAsync(cancellationToken); } public static IMongoDbRepository ToMongoDbRepository(this IBasicRepository repository) From 149755af7170321f9fbf7ca00fa020097ff8a613 Mon Sep 17 00:00:00 2001 From: PM Extra Date: Mon, 1 Mar 2021 16:37:08 +0800 Subject: [PATCH 10/43] Replace IBasicRepository with IReadOnlyBasicRepository. --- .../MongoDbCoreRepositoryExtensions.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDbCoreRepositoryExtensions.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDbCoreRepositoryExtensions.cs index f3ca43b80e..27e031fe58 100644 --- a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDbCoreRepositoryExtensions.cs +++ b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDbCoreRepositoryExtensions.cs @@ -11,51 +11,51 @@ namespace Volo.Abp.Domain.Repositories public static class MongoDbCoreRepositoryExtensions { [Obsolete("Use GetDatabaseAsync method.")] - public static IMongoDatabase GetDatabase(this IBasicRepository repository) + public static IMongoDatabase GetDatabase(this IReadOnlyBasicRepository repository) where TEntity : class, IEntity { return repository.ToMongoDbRepository().Database; } - public static Task GetDatabaseAsync(this IBasicRepository repository, CancellationToken cancellationToken = default) + public static Task GetDatabaseAsync(this IReadOnlyBasicRepository repository, CancellationToken cancellationToken = default) where TEntity : class, IEntity { return repository.ToMongoDbRepository().GetDatabaseAsync(cancellationToken); } [Obsolete("Use GetCollectionAsync method.")] - public static IMongoCollection GetCollection(this IBasicRepository repository) + public static IMongoCollection GetCollection(this IReadOnlyBasicRepository repository) where TEntity : class, IEntity { return repository.ToMongoDbRepository().Collection; } - public static Task> GetCollectionAsync(this IBasicRepository repository, CancellationToken cancellationToken = default) + public static Task> GetCollectionAsync(this IReadOnlyBasicRepository repository, CancellationToken cancellationToken = default) where TEntity : class, IEntity { return repository.ToMongoDbRepository().GetCollectionAsync(cancellationToken); } [Obsolete("Use GetMongoQueryableAsync method.")] - public static IMongoQueryable GetMongoQueryable(this IBasicRepository repository) + public static IMongoQueryable GetMongoQueryable(this IReadOnlyBasicRepository repository) where TEntity : class, IEntity { return repository.ToMongoDbRepository().GetMongoQueryable(); } - public static Task> GetMongoQueryableAsync(this IBasicRepository repository, CancellationToken cancellationToken = default) + public static Task> GetMongoQueryableAsync(this IReadOnlyBasicRepository repository, CancellationToken cancellationToken = default) where TEntity : class, IEntity { return repository.ToMongoDbRepository().GetMongoQueryableAsync(cancellationToken); } - public static Task> GetAggregateAsync(this IBasicRepository repository, CancellationToken cancellationToken = default) + public static Task> GetAggregateAsync(this IReadOnlyBasicRepository repository, CancellationToken cancellationToken = default) where TEntity : class, IEntity { return repository.ToMongoDbRepository().GetAggregateAsync(cancellationToken); } - public static IMongoDbRepository ToMongoDbRepository(this IBasicRepository repository) + public static IMongoDbRepository ToMongoDbRepository(this IReadOnlyBasicRepository repository) where TEntity : class, IEntity { if (repository is IMongoDbRepository mongoDbRepository) From a36f9bdec7dea5c8e41f006a91317e4b61ba7eae Mon Sep 17 00:00:00 2001 From: Yunus Emre Kalkan Date: Mon, 1 Mar 2021 11:37:44 +0300 Subject: [PATCH 11/43] Cli: RemoveFolderStep TenantMigrations --- .../Abp/Cli/ProjectBuilding/Templates/App/AppTemplateBase.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AppTemplateBase.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AppTemplateBase.cs index a725a513e7..7541894181 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AppTemplateBase.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AppTemplateBase.cs @@ -298,6 +298,7 @@ namespace Volo.Abp.Cli.ProjectBuilding.Templates.App SemanticVersion.Parse(context.BuildArgs.Version) > new SemanticVersion(4,1,99)) { steps.Add(new RemoveFolderStep("/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/Migrations")); + steps.Add(new RemoveFolderStep("/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/TenantMigrations")); } } From a317f19694aa265b2917b41377cd8c0c10b2f7a8 Mon Sep 17 00:00:00 2001 From: maliming Date: Mon, 1 Mar 2021 18:54:15 +0800 Subject: [PATCH 12/43] Update AbpIdentityWebMainMenuContributor --- .../AbpIdentityWebMainMenuContributor.cs | 25 ++++--------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/modules/identity/src/Volo.Abp.Identity.Web/Navigation/AbpIdentityWebMainMenuContributor.cs b/modules/identity/src/Volo.Abp.Identity.Web/Navigation/AbpIdentityWebMainMenuContributor.cs index 279ea16278..e5d1f31f13 100644 --- a/modules/identity/src/Volo.Abp.Identity.Web/Navigation/AbpIdentityWebMainMenuContributor.cs +++ b/modules/identity/src/Volo.Abp.Identity.Web/Navigation/AbpIdentityWebMainMenuContributor.cs @@ -16,28 +16,13 @@ namespace Volo.Abp.Identity.Web.Navigation return; } - var hasRolePermission = await context.IsGrantedAsync(IdentityPermissions.Roles.Default); - var hasUserPermission = await context.IsGrantedAsync(IdentityPermissions.Users.Default); + var l = context.GetLocalizer(); - if (hasRolePermission || hasUserPermission) - { - var administrationMenu = context.Menu.GetAdministration(); - - var l = context.GetLocalizer(); - - var identityMenuItem = new ApplicationMenuItem(IdentityMenuNames.GroupName, l["Menu:IdentityManagement"], icon: "fa fa-id-card-o"); - administrationMenu.AddItem(identityMenuItem); + var identityMenuItem = new ApplicationMenuItem(IdentityMenuNames.GroupName, l["Menu:IdentityManagement"], icon: "fa fa-id-card-o"); + identityMenuItem.AddItem(new ApplicationMenuItem(IdentityMenuNames.Roles, l["Roles"], url: "~/Identity/Roles", requiredPermissionName: IdentityPermissions.Roles.Default)); + identityMenuItem.AddItem(new ApplicationMenuItem(IdentityMenuNames.Users, l["Users"], url: "~/Identity/Users", requiredPermissionName: IdentityPermissions.Users.Default)); - if (hasRolePermission) - { - identityMenuItem.AddItem(new ApplicationMenuItem(IdentityMenuNames.Roles, l["Roles"], url: "~/Identity/Roles")); - } - - if (hasUserPermission) - { - identityMenuItem.AddItem(new ApplicationMenuItem(IdentityMenuNames.Users, l["Users"], url: "~/Identity/Users")); - } - } + context.Menu.GetAdministration().AddItem(identityMenuItem); } } } From 9b64278e52d2e281acd3b472fd1d416ddacfe5e7 Mon Sep 17 00:00:00 2001 From: Yunus Emre Kalkan Date: Mon, 1 Mar 2021 14:46:53 +0300 Subject: [PATCH 13/43] Cli: remove startup-project option when adding migration --- .../Volo/Abp/Cli/Commands/AddModuleCommand.cs | 7 ------- .../CreateMigrationAndRunMigratorCommand.cs | 8 ++++---- .../ProjectModification/EfCoreMigrationManager.cs | 15 ++++----------- .../ProjectModification/SolutionModuleAdder.cs | 13 +++---------- 4 files changed, 11 insertions(+), 32 deletions(-) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/AddModuleCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/AddModuleCommand.cs index 8129435266..a2c47af69b 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/AddModuleCommand.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/AddModuleCommand.cs @@ -59,7 +59,6 @@ namespace Volo.Abp.Cli.Commands await SolutionModuleAdder.AddAsync( solutionFile, commandLineArgs.Target, - commandLineArgs.Options.GetOrNull(Options.StartupProject.Short, Options.StartupProject.Long), version, skipDbMigrations, withSourceCode, @@ -167,12 +166,6 @@ namespace Volo.Abp.Cli.Commands public const string Skip = "skip-db-migrations"; } - public static class StartupProject - { - public const string Short = "sp"; - public const string Long = "startup-project"; - } - public static class SourceCode { public const string Long = "with-source-code"; diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CreateMigrationAndRunMigratorCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CreateMigrationAndRunMigratorCommand.cs index 5152e7af2e..c1bb6e6aca 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CreateMigrationAndRunMigratorCommand.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CreateMigrationAndRunMigratorCommand.cs @@ -44,9 +44,9 @@ namespace Volo.Abp.Cli.Commands FindDbContextName(dbMigrationsFolder) : null; - var migrationOutput = AddMigrationAndGetOutput(dbMigrationsFolder, dbMigratorProjectPath, dbContextName); + var migrationOutput = AddMigrationAndGetOutput(dbMigrationsFolder, dbContextName); var tenantMigrationOutput = tenantDbContextName != null ? - AddMigrationAndGetOutput(dbMigrationsFolder, dbMigratorProjectPath, tenantDbContextName) + AddMigrationAndGetOutput(dbMigrationsFolder, tenantDbContextName) : null; if (CheckMigrationOutput(migrationOutput) && CheckMigrationOutput(tenantMigrationOutput)) @@ -96,14 +96,14 @@ namespace Volo.Abp.Cli.Commands return Path.GetFileName(dbContext).RemovePostFix(".cs"); } - private static string AddMigrationAndGetOutput(string dbMigrationsFolder, string dbMigratorProjectPath, string dbContext) + private static string AddMigrationAndGetOutput(string dbMigrationsFolder, string dbContext) { var dbContextOption = string.IsNullOrWhiteSpace(dbContext) ? string.Empty : $"--context {dbContext}"; var addMigrationCmd = $"cd \"{dbMigrationsFolder}\" && " + - $"dotnet ef migrations add Initial -s \"{dbMigratorProjectPath}\" {dbContextOption}"; + $"dotnet ef migrations add Initial {dbContextOption}"; return CmdHelper.RunCmdAndGetOutput(addMigrationCmd); } diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/EfCoreMigrationManager.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/EfCoreMigrationManager.cs index 7bb3929f45..ee93b6c04d 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/EfCoreMigrationManager.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/EfCoreMigrationManager.cs @@ -17,7 +17,7 @@ namespace Volo.Abp.Cli.ProjectModification Logger = NullLogger.Instance; } - public void AddMigration(string dbMigrationsCsprojFile, string module, string startupProject) + public void AddMigration(string dbMigrationsCsprojFile, string module) { var dbMigrationsProjectFolder = Path.GetDirectoryName(dbMigrationsCsprojFile); var moduleName = ParseModuleName(module); @@ -30,24 +30,22 @@ namespace Volo.Abp.Cli.ProjectModification if (!string.IsNullOrEmpty(tenantDbContextName)) { - RunAddMigrationCommand(dbMigrationsProjectFolder, migrationName, startupProject, tenantDbContextName); + RunAddMigrationCommand(dbMigrationsProjectFolder, migrationName, tenantDbContextName); } - RunAddMigrationCommand(dbMigrationsProjectFolder, migrationName, startupProject, dbContextName); + RunAddMigrationCommand(dbMigrationsProjectFolder, migrationName, dbContextName); } protected virtual void RunAddMigrationCommand( string dbMigrationsProjectFolder, string migrationName, - string startupProject, string dbContext) { var dbContextOption = string.IsNullOrWhiteSpace(dbContext) ? string.Empty : $"--context {dbContext}"; - CmdHelper.RunCmd($"cd \"{dbMigrationsProjectFolder}\" && dotnet ef migrations add {migrationName}" + - $" {GetStartupProjectOption(startupProject)} {dbContextOption}"); + CmdHelper.RunCmd($"cd \"{dbMigrationsProjectFolder}\" && dotnet ef migrations add {migrationName} {dbContextOption}"); } protected virtual string ParseModuleName(string fullModuleName) @@ -66,11 +64,6 @@ namespace Volo.Abp.Cli.ProjectModification return "_" + new Random().Next(1, 99999); } - protected virtual string GetStartupProjectOption(string startupProject) - { - return startupProject.IsNullOrWhiteSpace() ? "" : $" -s {startupProject}"; - } - protected virtual string FindDbContextName(string dbMigrationsFolder) { var dbContext = Directory diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs index c84713ffcb..d550912691 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs @@ -78,7 +78,6 @@ namespace Volo.Abp.Cli.ProjectModification public virtual async Task AddAsync( [NotNull] string solutionFile, [NotNull] string moduleName, - string startupProject, string version, bool skipDbMigrations = false, bool withSourceCode = false, @@ -128,7 +127,7 @@ namespace Volo.Abp.Cli.ProjectModification await RunBundleForBlazorAsync(projectFiles, module); - ModifyDbContext(projectFiles, module, startupProject, skipDbMigrations); + ModifyDbContext(projectFiles, module, skipDbMigrations); } private async Task RunBundleForBlazorAsync(string[] projectFiles, ModuleWithMastersInfo module) @@ -450,8 +449,7 @@ namespace Volo.Abp.Cli.ProjectModification } } - protected void ModifyDbContext(string[] projectFiles, ModuleInfo module, string startupProject, - bool skipDbMigrations = false) + protected void ModifyDbContext(string[] projectFiles, ModuleInfo module, bool skipDbMigrations = false) { if (string.IsNullOrWhiteSpace(module.EfCoreConfigureMethodName)) { @@ -463,11 +461,6 @@ namespace Volo.Abp.Cli.ProjectModification return; } - if (string.IsNullOrWhiteSpace(startupProject)) - { - startupProject = projectFiles.FirstOrDefault(p => p.EndsWith(".DbMigrator.csproj")); - } - var dbMigrationsProject = projectFiles.FirstOrDefault(p => p.EndsWith(".DbMigrations.csproj")); if (dbMigrationsProject == null) @@ -498,7 +491,7 @@ namespace Volo.Abp.Cli.ProjectModification { if (addedNewBuilder) { - EfCoreMigrationManager.AddMigration(dbMigrationsProject, module.Name, startupProject); + EfCoreMigrationManager.AddMigration(dbMigrationsProject, module.Name); } RunMigrator(projectFiles); From 15c69d047b3a07aaf07ecde6569dbc826d6662a4 Mon Sep 17 00:00:00 2001 From: Yunus Emre Kalkan Date: Mon, 1 Mar 2021 15:00:05 +0300 Subject: [PATCH 14/43] Cli migrations: specify --output-dir --- .../Commands/CreateMigrationAndRunMigratorCommand.cs | 8 ++++---- .../Cli/ProjectModification/EfCoreMigrationManager.cs | 11 +++++++---- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CreateMigrationAndRunMigratorCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CreateMigrationAndRunMigratorCommand.cs index c1bb6e6aca..2a3c4652af 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CreateMigrationAndRunMigratorCommand.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CreateMigrationAndRunMigratorCommand.cs @@ -44,9 +44,9 @@ namespace Volo.Abp.Cli.Commands FindDbContextName(dbMigrationsFolder) : null; - var migrationOutput = AddMigrationAndGetOutput(dbMigrationsFolder, dbContextName); + var migrationOutput = AddMigrationAndGetOutput(dbMigrationsFolder, dbContextName, "Migrations"); var tenantMigrationOutput = tenantDbContextName != null ? - AddMigrationAndGetOutput(dbMigrationsFolder, tenantDbContextName) + AddMigrationAndGetOutput(dbMigrationsFolder, tenantDbContextName, "TenantMigrations") : null; if (CheckMigrationOutput(migrationOutput) && CheckMigrationOutput(tenantMigrationOutput)) @@ -96,14 +96,14 @@ namespace Volo.Abp.Cli.Commands return Path.GetFileName(dbContext).RemovePostFix(".cs"); } - private static string AddMigrationAndGetOutput(string dbMigrationsFolder, string dbContext) + private static string AddMigrationAndGetOutput(string dbMigrationsFolder, string dbContext, string outputDirectory) { var dbContextOption = string.IsNullOrWhiteSpace(dbContext) ? string.Empty : $"--context {dbContext}"; var addMigrationCmd = $"cd \"{dbMigrationsFolder}\" && " + - $"dotnet ef migrations add Initial {dbContextOption}"; + $"dotnet ef migrations add Initial --output-dir {outputDirectory} {dbContextOption}"; return CmdHelper.RunCmdAndGetOutput(addMigrationCmd); } diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/EfCoreMigrationManager.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/EfCoreMigrationManager.cs index ee93b6c04d..8143acf7b8 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/EfCoreMigrationManager.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/EfCoreMigrationManager.cs @@ -30,22 +30,25 @@ namespace Volo.Abp.Cli.ProjectModification if (!string.IsNullOrEmpty(tenantDbContextName)) { - RunAddMigrationCommand(dbMigrationsProjectFolder, migrationName, tenantDbContextName); + RunAddMigrationCommand(dbMigrationsProjectFolder, migrationName, tenantDbContextName, "TenantMigrations"); } - RunAddMigrationCommand(dbMigrationsProjectFolder, migrationName, dbContextName); + RunAddMigrationCommand(dbMigrationsProjectFolder, migrationName, dbContextName, "Migrations"); } protected virtual void RunAddMigrationCommand( string dbMigrationsProjectFolder, string migrationName, - string dbContext) + string dbContext, + string outputDirectory) { var dbContextOption = string.IsNullOrWhiteSpace(dbContext) ? string.Empty : $"--context {dbContext}"; - CmdHelper.RunCmd($"cd \"{dbMigrationsProjectFolder}\" && dotnet ef migrations add {migrationName} {dbContextOption}"); + CmdHelper.RunCmd($"cd \"{dbMigrationsProjectFolder}\" && dotnet ef migrations add {migrationName}" + + $" --output-dir {outputDirectory}" + + $" {dbContextOption}"); } protected virtual string ParseModuleName(string fullModuleName) From 23e263678f23d0377bf99cc928c14c3c85c3380a Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Mon, 1 Mar 2021 21:07:10 +0800 Subject: [PATCH 15/43] Mark all *.abp.io websites available in Chinese --- .../Base/Localization/Resources/en.json | 3 +- .../Base/Localization/Resources/zh-Hans.json | 3 +- .../Commercial/Localization/Resources/en.json | 247 ++++++++++++++++- .../Localization/Resources/zh-Hans.json | 256 +++++++++++++++++- 4 files changed, 502 insertions(+), 7 deletions(-) diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json index 71dc425dc6..f28ff1c4a1 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json @@ -30,6 +30,7 @@ "Permission:License": "License", "Permission:UserInfo": "Usere info", "SeeDocuments": "See Documents", - "Samples": "Samples" + "Samples": "Samples", + "Framework": "Framework" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/zh-Hans.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/zh-Hans.json index f061408c98..5b9952993f 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/zh-Hans.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/zh-Hans.json @@ -30,6 +30,7 @@ "Permission:License": "许可", "Permission:UserInfo": "用户信息", "SeeDocuments": "查看文档", - "Samples": "示例" + "Samples": "示例", + "Framework": "框架" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json index 4de287778f..050d4df164 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json @@ -41,6 +41,249 @@ "Unsubscribe": "Unsubscribe", "NotOrganizationMember": "You are not member of any organization.", "UnsubscribeLicenseExpirationEmailSuccessTitle": "Successfully unsubscribed", - "UnsubscribeLicenseExpirationEmailSuccessMessage": "You will not receive license expiration date reminder emails anymore." - } + "UnsubscribeLicenseExpirationEmailSuccessMessage": "You will not receive license expiration date reminder emails anymore.", + "IndexPageHeroSection": "A complete web development platformbuilt-on framework", + "AbpCommercialShortDescription": "ABP Commercial provides pre-built application modules, rapid application development tooling, professional UI themes, premium support and more.", + "LiveDemo": "Live Demo", + "GetLicence" :"Get a Licence", + "Application": "Application", + "StartupTemplates": "Startup Templates", + "Startup": "Startup", + "Templates": "Templates", + "Developer": "Developer", + "Tools": "Tools", + "Premium": "Premium", + "Support": "Support", + "UI": "UI", + "Themes": "Themes", + "JoinOurNewsletter": "Join Our Newsletter", + "Send": "Send", + "Learn": "Learn", + "AdditionalServices": "Additional Services", + "WhatIsABPFramework": "WHAT IS THE ABP FRAMEWORK?", + "OpenSourceBaseFramework": "Open Source Base Framework", + "ABPFrameworkExplanation": "

ABP Commercial is based on the ABP Framework, an open source and community driven web application framework for ASP.NET Core.

ABP Framework provides an excellent infrastructure to write maintainable, extensible and testable code with best practices.

Built on and integrated to popular tools you already know. Low learning curve, easy adaptation, comfortable development.

", + "Modular": "Modular", + "MicroserviceCompatible": "Microservice compatible", + "DomainDrivenDesignInfrastructure": "Domain Driven Design Infrastructure", + "MultiTenancy": "Multi-Tenancy", + "DistributedMessaging": "Distributed Messaging", + "DynamicProxying": "Dynamic Proxying", + "BackgroundJobs": "Background Jobs", + "AuditLogging": "Audit Logging", + "BLOBStoring": "BLOB Storing", + "BundlingMinification": "Bundling & Minification", + "AdvancedLocalization": "Advanced Localization", + "ManyMore": "Many more", + "ExploreTheABPFramework": "Explore the ABP Framework", + "WhyUseTheABPCommercial": "Why Use The ABP Commercial?", + "WhyUseTheABPCommercialExplanation": "

Building enterprise-grade web applications can be complex and time-consuming.

ABP Commercial offers the perfect base infrastructure necessary for all modern enterprise-grade ASP.NET Core based solutions. Right from the design to deployment, the entire development cycle is empowered by ABP's built-in features & modules.

", + "StartupTemplatesShortDescription": "Startup templates make you jump-start to your project in a few seconds.", + "UIFrameworksOptions": "UI frameworks options;", + "DatabaseProviderOptions": "Database provider options;", + "PreBuiltApplicationModules": "Pre-Built Application Modules", + "PreBuiltApplicationModulesShortDescription": "Most common application requirements are already developed for you as reusable modules.", + "Account": "Account", + "Blogging": "Blogging", + "Identity": "Identity", + "IdentityServer": "Identity Server", + "Saas": "Saas", + "LanguageManagement": "Language Management", + "TextTemplateManagement": "Text Template Management", + "See All Modules": "SeeAllModules", + "ABPSuite": "ABP Suite", + "AbpSuiteShortDescription": "ABP Suite is a complementary tool to the ABP Commercial.", + "AbpSuiteExplanation": "It allows you to build web pages in a matter of minutes. It's a .NET Core Global tool which can be installed from the command line. It can create a new ABP solution, generate CRUD pages from the database to the front-end.", + "Details": "Details", + "LeptonTheme": "Lepton Theme", + "ProfessionalModernUIThemes": "Professional, modern UI themes", + "LeptonThemeExplanation": "Lepton provides a gamut of Bootstrap admin themes that serve as a solid foundation for any project requiring admin dashboard.", + "DefaultTheme": "Default Theme", + "MaterialTheme": "Material Theme", + "Default2Theme": "Default 2 Theme", + "DarkTheme": "Dark Theme", + "DarkBlueTheme": "Dark Blue Theme", + "LightTheme": "Light Theme", + "ProudToWorkWith": "Proud to Work With", + "OurConsumers": "Hundreds of enterprises and developers over 50 countries worldwide rely on ABP Commercial.", + "JoinOurConsumers": "Join them and build amazing products fast.", + "AdditionalServicesExplanation": "Do you need additional or custom services? We and our partners can provide;", + "CustomProjectDevelopment": "Custom Project Development", + "CustomProjectDevelopmentExplanation": "Dedicated developers for your custom projects.", + "PortingExistingProjects": "Porting Existing Projects", + "PortingExistingProjectsExplanation": "Migrating your legacy projects to the ABP platform.", + "LiveSupport": "Live Support", + "LiveSupportExplanation": "Live remote support option when you need it.", + "Training": "Training", + "TrainingExplanation": "Dedicated training for your developers.", + "OnBoarding": "Onboarding", + "OnBoardingExplanation": "Help to setup your development, CI & CD environments.", + "PrioritizedTechnicalSupport": "Prioritized Technical Support", + "PremiumSupportExplanation": "Beside the great community support of the ABP framework, our support team answers technical questions and problems of the commercial users with high priority.", + "SeeTheSupportOptions": "See the Support Options", + "Contact": "Contact", + "TellUsWhatYouNeed": "Tell us what you need.", + "YourMessage": "Your Message", + "YourFullName": "Your full name", + "EmailField": "E-mail Address", + "YourEmailAddress": "Your e-mail address", + "HowMayWeHelpYou": "How may we help you", + "SendMessage": "Send Message", + "Success": "Success", + "WeWillReplyYou.": "Your message is sent! We will reply you in a short time.", + "GoHome": "Go Home", + "CreateLiveDemo": "Create Live Demo", + "RegisterToTheNewsletter": "Register to the newsletter to get information on happenings about ABP.IO, like new releases.", + "EnterYourEmailOrLogin": "Enter your e-mail address to create your demo or Login using your existing account.", + "ApplicationTemplate": "Application Template", + "ApplicationTemplateExplanation": "Application startup template is used to create a new web application.", + "EfCoreProvider": "Entity Framework (Supports SQL Server, MySQL, PostgreSQL, Oracle and others)", + "AlreadyIncludedInTemplateModules": "Following modules are already included and configured in this template:", + "ApplicationTemplateArchitecture": "This application template also supports tiered architecture where UI layer, API layer and the authentication service are physically separated.", + "SeeTheGuideOrGoToTheLiveDemo": "See the developer guide for technical information about this template or go to the live demo.", + "DeveloperGuide": "Developer Guide", + "ModuleTemplate": "Module Template", + "ModuleTemplateExplanation1": "You want to create a module and reuse across different applications? This startup template prepares everything to start to create a reusable application module or a microservice.", + "ModuleTemplateExplanation2": "

You can support single or multiple UI frameworks, single or multiple database providers for a single module. The startup template is configured to run and test your module in a minimal application in addition to the unit and integration test infrastructure.

See the developer guide for technical information about this template.

", + "WithAllStyleOptions": "with all style options", + "Demo": "Demo", + "SeeAllModules": "See All Modules", + "ABPCLIExplanation": "ABP CLI (Command Line Interface) is a command line tool to perform some common operations for ABP based solutions.", + "ABPSuiteEasilyCURD": "ABP Suite is a tool which allows you to easily create CRUD pages", + "WeAreHereToHelp": "We are Here to Help", + "BrowseOrAskQuestion": "You can browse our help topics or search in frequently asked questions, or you can ask us a question by using the contact form.", + "SearchQuestionPlaceholder": "Search in frequently asked questions", + "WhatIsTheABPCommercial": "What is the ABP Commercial?", + "WhatAreDifferencesThanAbpFramework": "What are the differences between the open source ABP Framework and the ABP Commercial?", + "ABPCommercialExplanation": "ABP Commercial is a set of premium modules, tools, themes and services built on top of the open source ABP framework. ABP Commercial is being developed and supported by the same team behind the ABP framework.", + "WhatAreDifferencesThanABPFrameworkExplanation": "

ABP framework is a modular, themeable, micro-service compatible application development framework for ASP.NET Core. It provides a complete architecture and a strong infrastructure to make you focusing on your own business code rather than repeating yourself for every new project. It is based on software development best practices and popular tools you already know.

ABP framework is completely free, open source and community-driven. It also provides a free theme and some pre-built modules (e.g. identity management and tenant management).

", + "VisitTheFrameworkVSCommercialDocument": "Visit the following link, for more information {1} ", + "ABPCommercialFollowingBenefits": "ABP Commercial adds the following benefits on top of the ABP framework;", + "Professional": "Professional", + "UIThemes": "UI themes", + "EnterpriseModules": "Enterprise ready, feature rich, pre-built application modules (e.g. Identity Server management, SaaS management, language management)", + "ToolingToSupport": "Tooling to support your development productivity (e.g. ABP Suite)", + "PremiumSupportLink": "Premium support", + "WhatDoIDownloadABPCommercial": "What do I download when I purchase the ABP Commercial?", + "CreateUnlimitedSolutions": "Once you purchase an ABP Commercial license, you will be able to create unlimited solutions like described in the Getting Started document.", + "ABPCommercialSolutionExplanation": "When you create a new application, you get a Visual Studio solution (a startup template) based on your preferences. The downloaded solution has commercial modules and themes already installed and configured for you. You can remove a pre-installed module or add another module if you like. All modules and themes are used a NuGet/NPM packages by default.", + "StartDevelopWithTutorials": "The downloaded solution is well architected and documented. You can start to develop your own business code based on it following the tutorials", + "TryTheCommercialDemo": "You can try the demo to see a sample application created using the ABP Commercial startup template.", + "HowManyProducts": "How many different products/solutions can I build using the ABP Commercial?", + "HowManyProductsExplanation": "There is no limit to create an ABP project. You can create as many project as you want, develop and upload them to different servers.", + "HowManyDevelopers": "How many developers can work on the ABP Commercial?", + "HowManyDevelopersExplanation": "ABP Commercial licenses are per developer. Different license types have different developer limits. However, you can add more developers to any license type whenever you need. See the prices page for license types, developer limits and additional developer costs.", + "ChangingLicenseType": "Can I change my license type in the future?", + "ChangingLicenseTypeExplanation": "You can always add new developers in your same license type. See also \"How many developers can work on the ABP Commercial?\". You can also upgrade to a higher license by paying the calculated price difference. When you upgrade to a higher license plan, you get the benefits of the new plan, but the license upgrade does not change the license expiry date.", + "LicenseExtendUpgradeDiff": "What is the difference between license extend and upgrade?", + "LicenseExtendUpgradeDiffExplanation": "Extending: By extending/renewing your license, you will continue to get premium support and get major updates for the modules and themes. Besides, you will be able to continue creating new projects. And you will still be able to use ABP Suite which speeds up your development.
Upgrading: By upgrading your license, you will promote to a higher license plan which will allow you to get additional benefits. See the license comparison table to check the differences between the license plans.On the other hand, when you upgrade, your license expiry date will not change!To extend your license end date, you need to extend your license.", + "LicenseRenewalCost": "What is the license renewal cost after 1 year?", + "LicenseRenewalCostExplanation": "You can renew (extend) your license by paying %80 of the current license price. For example; Standard ABP Team License is ${0} and renewal price of the Team License is ${1}.", + "IsSourceCodeIncluded": "Does my license include the source code of the commercial modules and themes?", + "IsSourceCodeIncludedExplanation1": "Depends on the license type you've purchased:", + "IsSourceCodeIncludedExplanation2": "Team: Your solution uses the modules and the themes as NuGet and NPM packages. It doesn't include their source code. In this way, you can easily upgrade these modules and themes whenever a new version is available. However, you can not get the source code of the modules and the themes.", + "IsSourceCodeIncludedExplanation3": "Business/Enterprise: In addition to the Team license, you are able to download the source code of any module or theme you need. You can even remove the NuGet/NPM package references for a particular module and add its source code directly to your solution to fully change it.", + "IsSourceCodeIncludedExplanation4": "

Including the source code of a module to your solution gives you the maximum freedom to customize that module. However, then it will not be possible to automatically upgrade the module when a new version is released.

None of the licenses include the ABP Suite source code, which is an external tool that generates code for you and assist to your development.

See the pricing page for other differences between the license types.

", + "ChangingDevelopers": "Can I change the registered developers of my organization in the future?", + "ChangingDevelopersExplanation": "In addition to add new developers to your license, you can also change the existing developers (you can remove a developer and add a new one to the same seat) without any additional cost.", + "WhatHappensWhenLicenseEnds": "What happens when my license period ends?", + "WhatHappensWhenLicenseEndsExplanation1": "ABP Commercial has perpetual license type.After your license expires, you can continue developing your project. And you are not obliged to renew your license. After your license expires;", + "WhatHappensWhenLicenseEndsExplanation2": "You can not create new solutions using the ABP Commercial, but you can continue to develop your existing applications forever.", + "WhatHappensWhenLicenseEndsExplanation3": "You will be able to get updates for the modules and themes within your MAJOR version. For example; if you are using v3.2.0 of a module, you can still get updates for v3.x.x (v3.3.0, v3.5.2... etc.) of that module. But you cannot get updates for the next major version (like v4.x, v5.x)", + "WhatHappensWhenLicenseEndsExplanation4": "You can not install new modules and themes added to the ABP Commercial platform after your license ends.", + "WhatHappensWhenLicenseEndsExplanation5": "You can not use the ABP Suite.", + "WhatHappensWhenLicenseEndsExplanation6": "You can not get the premium support anymore.", + "WhatHappensWhenLicenseEndsExplanation7": "You can renew your subscription if you want to continue to get these benefits. There is a 20% discount when you renew your subscription.", + "WhenShouldIRenewMyLicense": "When should I renew my license?", + "WhenShouldIRenewMyLicenseExplanation1": "If you renew your license within 1 month after your license expires, %20 discount will be applied to total license price.", + "WhenShouldIRenewMyLicenseExplanation2": "If you renew your license after 1 month from your license expire date, the renew price will be same as license purchase price and there will be no discount for your renewal.", + "TrialPlan": "Do you have a trial plan?", + "TrialPlanExplanation": "For now, ABP Commercial doesn't have a trial plan. For the Team licenses we provide 30 days money back guarantee. You can just request a refund in the first 30 days. For the Business and Enterprise licenses, we provide 60% refund in 30 days. This is because Business and Enterprise licenses include the full source code of all the modules and the themes.", + "DoYouAcceptBankWireTransfer": "Do you accept bank wire transfer?", + "DoYouAcceptBankWireTransferExplanation": "Yes, we accept bank wire transfer.
After sending the license amount via bank wire transfer, send us your receipt and the requested license type via e-mail.
Our international bank account information:", + "HowToUpgrade": "How to upgrade existing applications when a new version is available?", + "HowToUpgradeExplanation1": "When you create a new application using ABP Commercial, all the modules and the theme are used as NuGet and NPM packages. So, you can easily upgrade the packages when a new version is available.", + "HowToUpgradeExplanation2": "In addition to the standard NuGet/NPM upgrades, ABP CLI provides an update command that automatically finds and upgrades all ABP related packages in your solution.", + "DatabaseSupport": "Which database systems are supported?", + "DatabaseSupportExplanation": "ABP Framework itself is database agnostic and can work with any database provider by its nature. See the data access document for a list of currently implemented providers.", + "UISupport": "Which UI frameworks are supported?", + "Supported": "Supported", + "UISupportExplanation": "ABP Framework itself is UI framework agnostic and can work with any UI framework. However, startup templates, module UIs and themes were not implemented for all UI frameworks. See the getting started document for the up-to-date list of UI options.", + "MicroserviceSupport": "Does it support the micro-service architecture?", + "MicroserviceSupportExplanation1": "One of the major goals of the ABP framework is to provide a convenient infrastructure to create micro-service solutions. See the micro-service architecture document to understand how it helps to create micro-service systems.", + "MicroserviceSupportExplanation2": "All the ABP Commercial modules are designed to support micro-service deployment scenarios (with its own API and database) by following the module development best practices document.", + "MicroserviceSupportExplanation3": "We provide a sample micro-service demo solutionthat demonstrates a micro-service architecture implementation to help you to create your own solution.", + "MicroserviceSupportExplanation4": "So, the short answer is \"yes, it supports micro-service architecture\".", + "MicroserviceSupportExplanation5": "However, a micro-service system is a solution and every solution will have different requirements, network topology, communication scenarios, authentication possibilities, database separation/sharing decisions, runtime configurations, 3rd party system integrations and many more.", + "MicroserviceSupportExplanation6": "The ABP Framework and the ABP Commercial provides infrastructure for micro-service scenarios, micro-service compatible modules, samples and documentation to help you to build your own solution. But don't expect to directly download your dream solution pre-built for you. You will need to understand it and bring some parts together based on your requirements.", + "WhereCanIDownloadSourceCode": "Where can I download source-code?", + "WhereCanIDownloadSourceCodeExplanation": "You can download the source code of all ABP modules, Angular packages and themes via ABP Suite or ABP CLI. See How to download source-code?", + "ComputerLimitation": "How many computers can a developer login when developing ABP?", + "ComputerLimitationExplanation": "We specifically permit {0} computers per individual/licensed developer. Whenever there is a need for a developer to develop ABP Commercial products on a third machine, an e-mail should be sent to license@abp.io explaining the situation and we will then make the appropriate allocation in our system.", + "RefundPolicy": "Do you have a refund policy?", + "RefundPolicyExplanation": "You can request a refund within 30 days of your license purchase. The Business and Enterprise license types have source-code download option, therefore refunds are not available for the Business and Enterprise (and any licenses that include a right to receive source-code). In addition, no refunds are made for renewals and second license purchases.", + "HowCanIRefundVat": "How can I refund VAT?", + "HowCanIRefundVatExplanation1": "If you made the payment using 2Checkout, you can refund VAT via your 2Checkout account:", + "HowCanIRefundVatExplanation2": "Log in into your 2Checkout account", + "HowCanIRefundVatExplanation3": "Find the appropriate order and press \"Refund Belated VAT\" (enter your VAT ID)", + "HowCanIGetMyInvoice": "How can I get my invoice?", + "HowCanIGetMyInvoiceExplanation": "There are 2 payment gateways for purchasing a license: PayU and 2Checkout. If you purchase your license through 2Checkout gateway, it sends the PDF invoice to your email address, see 2Checkout invoicing. If you purchase through the PayU gateway or via bank wire transfer, we will prepare and send your invoice. You can request your invoice from the organization management page.", + "Forum": "Forum", + "SupportExplanation": "ABP Commercial licenses provides a premium forum support by a team consists of the ABP Framework experts.", + "PrivateTicket": "Private Ticket", + "PrivateTicketExplanation": "Enterprise License also includes a private support with e-mail and ticket system.", + "AbpSuiteExplanation1": "ABP Suite allows you to build web pages in a matter of minutes. It's a .NET Core Global tool that can be installed from the command line.", + "AbpSuiteExplanation2": "It can create a new ABP solution, generate CRUD pages from the database to the front-end. For technical overview see the document", + "FastEasy": "Fast & Easy", + "AbpSuiteExplanation3": "ABP Suite allows you to easily create CRUD pages. You just need to define your entity and its properties, let the rest to ABP Suite for you! ABP Suite generates all the necessary code for your CRUD page in a few seconds. It supports Angular, MVC and Blazor user interfaces.", + "RichOptions": "Rich Options", + "AbpSuiteExplanation4": "ABP Suite supports multiple UI options like Razor Pages and Angular.It also supports multiple databases like MongoDB and all databases supported by EntityFramework Core (MS SQL Server, Oracle, MySql, PostgreSQL and more).", + "AbpSuiteExplanation5": "Good thing is that, you don't have to worry about those options. ABP Suite understands your project type and generates the code for your project and places the generated code in correct place in your project.", + "SourceCode": "Source Code", + "AbpSuiteExplanation6": "ABP Suite generates the source code for you! It doesn't generate magic files to generate the web page. ABP Suite generates the source code for Entity, Repository, Application Service, Code First Migration, JavaScript/TypeScript and CSHTML/HTML and necessary Interfaces as well. ABP Suite also generates the code according to Best Practices of software development, so you don't have to worry about generated code's quality.", + "AbpSuiteExplanation7": "Since you have the source code of building blocks of the generated CRUD page in correct application layers, you can easily modify the source code and inject your custom/bussiness logic to the generated code.", + "CrossPlatform": "Cross Platform", + "AbpSuiteExplanation8": "ABP Suite is built with .NET Core and it is cross platform. It runs as a web application on your local computer. You can run it on Windows, Mac and Linux", + "OtherFeatures": "Other Features", + "OtherFeatures1": "Updates NuGet and NPM packages on your solution easily.", + "OtherFeatures2": "Regenerates already generated pages from scratch.", + "OtherFeatures3": "Creates new solutions", + "ThanksForCreatingProject": "Thanks for Creating Your Project!", + "HotToRunSolution": "How to run your solution?", + "HotToRunSolutionExplanation": "See getting started document to learn how to configure and run your solution.", + "GettingStarted": "Getting Started", + "WebAppDevTutorial": "Web App Dev Tutorial", + "WebAppDevTutorialExplanation": "See web application development tutorial document for a step by step development sample.", + "Document": "Document", + "UsingABPSuiteToCURD": "Using ABP Suite for CRUD Page Generation & Tooling", + "SeeABPSuiteDocument": "See ABP Suite document to learn the usage of ABP Suite.", + "AskQuestionsOnSupport": "You can ask questions on ABP Commercial Support.", + "Documentation": "Documentation", + "SeeModulesDocument": "See modules document for a list of all commercial (pro) modules and their documents.", + "Pricing": "Pricing", + "PricingExplanation": "Choose the features and functionality your business needs today. Easily upgrade as your business grows.", + "Team": "Team", + "Business": "Business", + "Enterprise": "Enterprise", + "Custom": "Custom", + "IncludedDeveloperLicenses": "Included developer licenses", + "CustomLicenceOrAdditionalServices": "Need custom licence or additional services?", + "CustomOrVolumeLicense": "Custom or volume license", + "LiveTrainingSupport": "Live training & support", + "AndMore": "and more", + "AdditionalDeveloperLicense": "Additional developer license", + "ProjectCount": "Project count", + "AllProModules": "All pro modules", + "AllProThemes": "All pro themes", + "AllProStartupTemplates": "All pro startup templates", + "SourceCodeOfAllModules": "Source code of all modules", + "SourceCodeOfAllThemes": "Source code of all themes", + "PerpetualLicense": "Perpetual license", + "UnlimitedServerDeployment": "Unlimited server deployment", + "YearUpgrade": "1 year upgrade", + "YearPremiumForumSupport": "1 year premium forum support", + "ForumSupportIncidentCountYear": "Forum support incident count/year", + "PrivateTicketEmailSupport": "Private ticket & email support", + "BuyNow": "Buy Now" + } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hans.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hans.json index 82c23450a0..bc634de3c2 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hans.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hans.json @@ -16,7 +16,7 @@ "Name": "名称", "EmailAddress": "电子邮件地址", "Developers": "开发者", - "LicenseType": "许可证类型", + "LicenseType": "许可类型", "Manage": "管理", "StartDate": "开始日期", "EndDate": "结束日期", @@ -33,7 +33,257 @@ "EmailNotValid": "请输入有效的电子邮件地址", "JoinOurMarketingNewsletter": "加入我们的营销简讯", "WouldLikeToReceiveMarketingMaterials": "我想收到市场营销资料,例如产品交易和特别优惠.", - "StartUsingYourLicenseNow": "立即开始使用你的许可证", - "WelcomePage": "欢迎页面" + "StartUsingYourLicenseNow": "立即开始使用你的许可", + "WelcomePage": "欢迎页面", + "UnsubscriptionExpireEmail": "退订许可到期提醒邮件", + "UnsubscribeLicenseExpireEmailReminderMessage": "此电子邮件订阅仅提醒你许可的到期日期.", + "UnsubscribeFromLicenseExpireEmails": "如果你不想收到关于许可到期邮件,你可以在任何时间取消订阅.", + "Unsubscribe": "取消订阅", + "NotOrganizationMember": "你不是任何组织的成员.", + "UnsubscribeLicenseExpirationEmailSuccessTitle": "退订成功", + "UnsubscribeLicenseExpirationEmailSuccessMessage": "你不会再收到任何许可到期提醒邮件.", + "IndexPageHeroSection": "一个完整的web开发平台基于 框架", + "AbpCommercialShortDescription": "ABP商业版提供了预构建的应用程序模块, 快速的应用程序开发工具, 专业的UI主题, 高级支持等.", + "LiveDemo": "在线演示", + "GetLicence" :"获得许可", + "Application": "应用程序", + "StartupTemplates": "启动模板", + "Startup": "启动", + "Templates": "模板", + "Developer": "开发人员", + "Tools": "工具", + "Premium": "高级", + "Support": "支持", + "UI": "UI", + "Themes": "主题", + "JoinOurNewsletter": "加入我们的时事通讯", + "Send": "发送", + "Learn": "学习", + "AdditionalServices": "额外的服务", + "WhatIsABPFramework": "什么是ABP框架?", + "OpenSourceBaseFramework": "开源的框架", + "ABPFrameworkExplanation": "

ABP商业版基于ABP框架, 这是一个开源和社区驱动的ASP.NET Core web应用程序开发框架.

ABP框架提供了出色的基础设施, 使用最佳实践编写可维护,可扩展,可测试的代码.

基于你已经知道的流行工具. 低学习曲线,容易适应,舒适的开发体检.

", + "Modular": "模块化", + "MicroserviceCompatible": "微服务兼容", + "DomainDrivenDesignInfrastructure": "领域驱动设计基础设施", + "MultiTenancy": "多租户", + "DistributedMessaging": "分布式消息", + "DynamicProxying": "动态代理", + "BackgroundJobs": "后台作业", + "AuditLogging": "审计日志", + "BLOBStoring": "BLOB存储", + "BundlingMinification": "捆绑 & 压缩", + "AdvancedLocalization": "高级本地化", + "ManyMore": "以及更多", + "ExploreTheABPFramework": "探索ABP框架", + "WhyUseTheABPCommercial": "为什么使用ABP商业版?", + "WhyUseTheABPCommercialExplanation": "

构建企业级的web应用程序可能复杂和耗时.

ABP商业版提供所有现代企业级基于ASP.NET Core的解决方案所需要的基础设施. 从设计到部署,整个开发生命周期都由ABP的内置功能和模块提供支持.

", + "StartupTemplatesShortDescription": "启动模板使你在几秒内快速启动项目.", + "UIFrameworksOptions": "UI框架选项;", + "DatabaseProviderOptions": "数据库提供程序选项;", + "PreBuiltApplicationModules": "预构建的应用程序模块", + "PreBuiltApplicationModulesShortDescription": "大多数常见的应用程序需求已经做为可重用的模块为你开发了.", + "Account": "账户", + "Blogging": "博客", + "Identity": "Identity", + "IdentityServer": "Identity Server", + "Saas": "Saas", + "LanguageManagement": "语言管理", + "TextTemplateManagement": "文本模板管理", + "See All Modules": "查看所有的模块", + "ABPSuite": "ABP Suite", + "AbpSuiteShortDescription": "ABP Suite是ABP商业版的辅助工具.", + "AbpSuiteExplanation": "它让你在几分钟内构建web页面. 它是一个.Net Core全局工具,可以使用命令行安装. 它可以创建一个新的ABP解决方案,从数据库生成前端CURD页面.", + "Details": "详情", + "LeptonTheme": "Lepton主题", + "ProfessionalModernUIThemes": "专业, 现代化的UI主题", + "LeptonThemeExplanation": "Lepton提供了一系列的Bootstrap管理主题,为任何需要管理仪表盘的项目提供了坚实的基础.", + "DefaultTheme": "默认主题", + "MaterialTheme": "Material主题", + "Default2Theme": "默认主题2", + "DarkTheme": "深色主题", + "DarkBlueTheme": "深蓝主题", + "LightTheme": "浅色主题", + "ProudToWorkWith": "荣幸与你合作", + "OurConsumers": "全球50多个国家的数百家企业和开发商使用ABP商业版.", + "JoinOurConsumers": "加它他们并快速构建令人惊叹的产品.", + "AdditionalServicesExplanation": "你是否需要额外或自定义的服务? 我们和我们的合作伙伴可以提供;", + "CustomProjectDevelopment": "自定义项目开发", + "CustomProjectDevelopmentExplanation": "专为你的自定义的开发人员.", + "PortingExistingProjects": "移植现有项目", + "PortingExistingProjectsExplanation": "将你的迁移传统项目迁移到ABP平台.", + "LiveSupport": "实时支持", + "LiveSupportExplanation": "在你需要时,可以实时提供远程支持选项.", + "Training": "培训", + "TrainingExplanation": "为你的开发人员提供专门培训.", + "OnBoarding": "管理", + "OnBoardingExplanation": "帮助设置你的开发,CI和CD环境.", + "PrioritizedTechnicalSupport": "优先的技术支持", + "PremiumSupportExplanation": "除了社区对ABP框架出色支持外,我们的支持团队会优先回答商业用户的技术问题.", + "SeeTheSupportOptions": "查看支持选项", + "Contact": "联系", + "TellUsWhatYouNeed": "告诉我们你需要什么.", + "YourMessage": "你的消息", + "YourFullName": "你的全名", + "EmailField": "E-mail地址", + "YourEmailAddress": "你的e-mail地址", + "HowMayWeHelpYou": "我们如何帮助你", + "SendMessage": "发送消息", + "Success": "成功", + "WeWillReplyYou.": "你的消息已经发送! 我们会在短时间内给你答复.", + "GoHome": "回到主页面", + "CreateLiveDemo": "创建在线演示", + "RegisterToTheNewsletter": "注册到时事简报以获取有关ABP.IO的消息,比如新发布的内容.", + "EnterYourEmailOrLogin": "输入你的e-mail地址来创建你的演示或者使用你的已有账号登录.", + "ApplicationTemplate": "应用程序模板", + "ApplicationTemplateExplanation": "应用程序启动模板用于创建新的web应用程序.", + "EfCoreProvider": "Entity Framework (支持 SQL Server, MySQL, PostgreSQL, Oracle 和其它)", + "AlreadyIncludedInTemplateModules": "以下模块已经包含并配置在此模板中:", + "ApplicationTemplateArchitecture": "应用程序模板还支持UI,API,身份验证服务器物理分离的分层架构.", + "SeeTheGuideOrGoToTheLiveDemo": "查看开发人员指南或者查看在线演示了解更多关于模板的信息.", + "DeveloperGuide": "开发人员指南", + "ModuleTemplate": "模块模块", + "ModuleTemplateExplanation1": "你是否想要创建可在不同应用程序之间重用的模块? 这个启动模板准备好了一切来创建可重用的应用程序模块微服务.", + "ModuleTemplateExplanation2": "

你可以为一个模块支持单个或多个UI框架, 单个或多个数据库提供程序. 除了单元测试和集成测试基础架构外,启动模板被配置为在一个最小的应用程序中运行和测试你的模块.

查看开发人员指南了解有关此模板的技术信息.

", + "WithAllStyleOptions": "使用所有的样式选项", + "Demo": "演示", + "SeeAllModules": "查看所有的模块", + "ABPCLIExplanation": "ABP CLI(命令行页面)是一个执行基于ABP解决方案的一些常见操作的命令行工具.", + "ABPSuiteEasilyCURD": "ABP Suite是一个使你轻松创建CURD页面的工具", + "WeAreHereToHelp": "我们在这里为你提供帮助", + "BrowseOrAskQuestion": "你可以浏览我们的帮助主题或搜索常见的问题, 或者你可以使用联系表单向我们提问.", + "SearchQuestionPlaceholder": "搜索常见的问题", + "WhatIsTheABPCommercial": "什么是ABP商业版?", + "WhatAreDifferencesThanAbpFramework": "ABP框架与ABP商业版有什么不同?", + "ABPCommercialExplanation": "ABP商业版是一套基于开源ABP框架之上的高级模块,工具,主题和服务. ABP商业版由ABP框架背后的同一团队开发和支持.", + "WhatAreDifferencesThanABPFrameworkExplanation": "

ABP框架是模块化,主题化,微服务兼容的ASP.NET Core应用程序开发框架. 它提供了一个完整的架构和强大的基础设施,让你专注于自己的业务代码而不是重复自己的每一个项目. 它基于软件开发的最佳实践和你已经知道的流行工具

ABP框架是完全免费,开源和由社区驱动的. 它还提供了一个免费的主题和一些预构建的模块 (如 identity管理和租户管理).

", + "VisitTheFrameworkVSCommercialDocument": "访问以下链接,了解更多信息 {1} ", + "ABPCommercialFollowingBenefits": "ABP商业版在ABP框架之上添加了以下好处;", + "Professional": "专业的", + "UIThemes": "UI主题", + "EnterpriseModules": "企业就绪,功能丰富,预构建的应用程序模块 (如 Identity Server管理, SaaS管理, 语言管理)", + "ToolingToSupport": "支持你的生产力的工具(如. ABP Suite)", + "PremiumSupportLink": "高级支持", + "WhatDoIDownloadABPCommercial": "购买ABP商业版后我可以下载什么?", + "CreateUnlimitedSolutions": "一旦你购买了ABP商业许可, 你可以创建入门文档中描述的无限的解决方案.", + "ABPCommercialSolutionExplanation": "创建新应用程序时,将根据你的首选项获得Visual Studio解决方案(启动模板). 下载的解决方案已为你安装并配置了商业模块和主题. 你可以删除预装的模块,也可以根据需要添加其他模块. 默认情况下所有模块和主题都使用NuGet/NPM软件包.", + "StartDevelopWithTutorials": "下载的解决方案经过精心设计和记录, 你可以根据教程来开发自己的业务代码.", + "TryTheCommercialDemo": "你可以尝试示例来查看使用ABP商业模板创建的示例应用程序.", + "HowManyProducts": "使用ABP商业版,我可以构建多少不同的产品/解决方案?", + "HowManyProductsExplanation": "创建ABP项目没有限制. 你可以根据需要创建任意数量的项目,进行开发并将其部署到其他服务器.", + "HowManyDevelopers": "有多少开发者可以参与ABP商业版工作?", + "HowManyDevelopersExplanation": "ABP商业许可是针对每个开发人员的. 不同的许可类型具有不同的开发人员限制. 但是你可以在需要时将更多开发人员添加到任何许可类型. 查看许可类型,开发人员限制和额外的开发人员成本的价格页面.", + "ChangingLicenseType": "将来更改我的许可类型吗?", + "ChangingLicenseTypeExplanation": "你始终可以在同一许可中添加新的开发人员. 参阅 \"有多少开发者可以参与ABP商业版工作?\". 你还可以通过支付计算出的价格差来升级到更高的许可. 当你升级到更高的许可计划时,可以享受新计划的好处,但是许可升级不会更改许可的到期日期.", + "LicenseExtendUpgradeDiff": "许可扩展和升级有什么区别?", + "LicenseExtendUpgradeDiffExplanation": "扩展: 通过扩展/更新许可,你将继续获得高级支持,并获得有关模块和主题的重大更新. 此外你将能够继续创建新项目. 而且你仍然可以使用ABP Suite来加快开发速度.
升级: 通过升级许可,你将升级到更高的许可计划,这将使你获得更多好处. 查看 许可比较表来检查许可计划之间的差异. 另一方面,当你升级时你的许可到期日期不会改变!要延长你的许可终止日期,你需要延长你的许可.", + "LicenseRenewalCost": "一年后的许可续期费用是多少?", + "LicenseRenewalCostExplanation": "你可以通过支付当前许可价格的%80来续订(扩展)你的许可. 例如; 标准ABP团队许可为 ${0} 续订价格是 ${1}.", + "IsSourceCodeIncluded": "我的许可是否包括商业模块和主题的源代码?", + "IsSourceCodeIncludedExplanation1": "取决于你购买的许可类型:", + "IsSourceCodeIncludedExplanation2": "团队: 你的解决方案将这些模块和主题作为NuGet和NPM包使用. 它不包括其源代码. 这样,只要有新版本可用,你就可以轻松升级这些模块和主题. 但是,你无法获取模块和主题的源代码.", + "IsSourceCodeIncludedExplanation3": "商业/企业: 除了团队许可外,你还可以下载所需的任何模块或主题的源代码. 你甚至可以删除特定模块的NuGet/NPM软件包引用,并将其源代码直接添加到你的解决方案中以完全更改它.", + "IsSourceCodeIncludedExplanation4": "

将模块的源代码包含到解决方案中,可以最大程度地自定义该模块. 但是当新版本发布时,将无法自动升级模块.

这些许可均不包含ABP Suite源代码,该源代码是一个外部工具,可以为你生成代码并帮助你进行开发

有关许可类型之间的其它差异查看定价页面.

", + "ChangingDevelopers": "我将来可以更改我组织的注册开发人员吗?", + "ChangingDevelopersExplanation": "除了将新的开发人员添加到你的许可中之外,你还可以更改现有的开发人员(可以删除一个开发人员并将新的开发人员添加到同一位置),而无需任何额外费用.", + "WhatHappensWhenLicenseEnds": "我的许可期限结束后会怎样?", + "WhatHappensWhenLicenseEndsExplanation1": "ABP商业版具有永久许可类型.许可到期后, 你可以继续开发你的项目.并且你没有义务续订许可.许可到期后;", + "WhatHappensWhenLicenseEndsExplanation2": "你不能使用ABP商业版创建新的解决方案,但可以永远继续开发现有的应用程序.", + "WhatHappensWhenLicenseEndsExplanation3": "你能够在你的主版本中获得模块和主题的更新.例如;如果你使用的是模块的v3.2.0版本,你仍然可以获得v3.x.x版本的更新.(v3.3.0 v3.5.2…等等)的.但是你无法获得下一个主要版本(如v4.x,v5.x)的更新", + "WhatHappensWhenLicenseEndsExplanation4": "许可到期后,你无法安装添加到ABP商业平台的新模块和主题.", + "WhatHappensWhenLicenseEndsExplanation5": "你不能使用ABP Suite.", + "WhatHappensWhenLicenseEndsExplanation6": "你不再获得高级支持.", + "WhatHappensWhenLicenseEndsExplanation7": "如果你想继续获得这些好处,可以继续续订. 续订时有20%的折扣.", + "WhenShouldIRenewMyLicense": "我什么时候应该续订我的许可?", + "WhenShouldIRenewMyLicenseExplanation1": "如果你在许可到期后的 1个月内续订许可,则许可总价将享受%20的折扣.", + "WhenShouldIRenewMyLicenseExplanation2": "如果你在你的许可过期日期1个月后更新你的许可,更新价格将与许可购买价格相同,你的更新将没有折扣.", + "TrialPlan": "你们有试用计划吗?", + "TrialPlanExplanation": "目前,ABP商业版还没有试用计划.对于团队许可,我们提供30天的退款保证.你可以在30天内要求退款.对于企业营业执照,我们提供30天内60%的退款.这是因为商业和企业许可包含所有模块和主题的完整源代码.", + "DoYouAcceptBankWireTransfer": "你们接受银行电汇吗?", + "DoYouAcceptBankWireTransferExplanation": "是的,我们接受银行电汇.
通过银行电汇转账,通过电子邮件将收据和所要求的许可类型发送给我们.
我们的国际银行帐户信息:", + "HowToUpgrade": "可用新版本时如何升级现有应用程序?", + "HowToUpgradeExplanation1": "使用ABP商业版创建新应用程序时,所有模块和主题均用作NuGet和NPM软件包. 因此,当有新版本可用时,你可以轻松升级软件包.", + "HowToUpgradeExplanation2": "除了标准的NuGet/NPM升级之外, ABP CLI 还提供了一条更新命令,该命令可自动查找和升级解决方案中的所有与ABP相关的软件包.", + "DatabaseSupport": "支持哪些数据库系统?", + "DatabaseSupportExplanation": "ABP框架本身与数据库无关,并且就其性质而言,可以与任何数据库提供程序一起使用. 请参阅数据访问文档以获取当前实现的提供程序的列表.", + "UISupport": "支持哪些UI框架?", + "Supported": "支持的", + "UISupportExplanation": "ABP框架本身与UI框架无关,并且可以与任何UI框架一起使用. 但是,并非针对所有UI框架都实现了启动模板,模块UI和主题. 有关UI选项的最新列表,请参见入门文档.", + "MicroserviceSupport": "它是否支持微服务架构?", + "MicroserviceSupportExplanation1": "ABP框架的主要目标之一是为创建微服务解决方案提供便利的基础架构. 请参阅微服务体系结构文档,以了解它如何帮助创建微服务系统.", + "MicroserviceSupportExplanation2": "所有的ABP商业模块通过模块开发最佳实践文档被设计为支持微服务部署场景(使用自己的API和数据库).", + "MicroserviceSupportExplanation3": "我们提供了一个示例微服务演示解决方案,该示例演示了一种微服务架构实现,可帮助你创建自己的解决方案.", + "MicroserviceSupportExplanation4": "所以简短的答案是 \"是的, 它支持微服务体系结构\".", + "MicroserviceSupportExplanation5": "但是,微服务系统是一个解决方案,每个解决方案都有不同的要求,网络拓扑,通信场景,身份验证可能性,数据库分离/共享决策,运行时配置,第三方系统集成等等.", + "MicroserviceSupportExplanation6": "ABP框架和ABP商业版提供了微服务方案,微服务兼容模块,示例和文档的基础结构,以帮助你构建自己的解决方案. 但是不要期望直接下载为你预先构建的梦想解决方案. 你将需要了解它,并根据需要将某些部分组合在一起.", + "WhereCanIDownloadSourceCode": "在哪里可以下载源代码?", + "WhereCanIDownloadSourceCodeExplanation": "你可以通过ABP Suite或ABP CLI下载所有ABP模块,Angular软件包和主题的源代码. 请参见如何下载源代码?", + "ComputerLimitation": "开发人员在开发ABP时可以登录到多少台计算机?", + "ComputerLimitationExplanation": "我们特别允许每个个人/许可的开发人员使用 {0}台计算机. 每当需要开发人员在第三台计算机上开发ABP商业产品时,都应将电子邮件发送到license@abp.io,以说明情况,然后我们将在系统中进行适当分配.", + "RefundPolicy": "你们有退款政策吗?", + "RefundPolicyExplanation": "你可以在购买许可的30天内请求退款.商业和企业许可类型都有源代码下载选项,因此商业和企业(以及任何包含获得源代码的权利的许可)都不能退款.此外,续展和购买第二次许可也不退款.", + "HowCanIRefundVat": "我该如何退还增值税?", + "HowCanIRefundVatExplanation1": "如果你使用2Checkout付款,则可以通过2Checkout帐户退还增值税:", + "HowCanIRefundVatExplanation2": "登录到你的2Checkout账户", + "HowCanIRefundVatExplanation3": "找到适当的订单,然后按\"退还已延期的增值税\"(输入你的增值税ID", + "HowCanIGetMyInvoice": "我如何获得发票?", + "HowCanIGetMyInvoiceExplanation": "有两个用于购买许可的支付网关:PayU和2Checkout. 如果你通过2Checkout网关购买许可,则它将PDF发票发送到你的电子邮件地址,请参阅2Checkout发票.如果你是通过PayU网关或通过银行电汇购买的,我们将准备并发送你的发票. 你可以从组织管理页面索取发票.", + "Forum": "论坛", + "SupportExplanation": "ABP商业版许可包含由ABP框架专家组成的团队提供的高级论坛支持.", + "PrivateTicket": "私有票", + "PrivateTicketExplanation": "企业许可还包含带有电子邮件和票系统的私人支持.", + "AbpSuiteExplanation1": "ABP Suite使你在几分钟内构建web页面. 它是一个.NET Core Global工具,可以使用命令行安装.", + "AbpSuiteExplanation2": "它可以创建一个新的ABP解决方案, 从数据库生成前端CURD页面. 有关技术概述查看文档", + "FastEasy": "快速并且简单", + "AbpSuiteExplanation3": "ABP Suite允许你轻松的创建CURD页面. 你只需要定义你的实体和它的属性, 其它的让ABP Suite帮你完成! ABP Suite会在几秒内为你生成CURD页面和必要的代码. 它支持Angular, MVC和 Blazor用户界面.", + "RichOptions": "丰富的选项", + "AbpSuiteExplanation4": "ABP Suite支持多个UI选项,例如 Razor PagesAngular.它还支持多个数据库, 如 MongoDBEntityFramework Core支持的所有数据库(MS SQL Server, Oracle, MySql, PostgreSQL 和 更多).", + "AbpSuiteExplanation5": "好消息是, 你不必担心这些选项. ABP Suite了解你的项目类型并且将生成的代码放到项目中正确的位置.", + "SourceCode": "源码", + "AbpSuiteExplanation6": "ABP Suite为你生成源代码! 它不会生成魔法文件来生成web页面. ABP Suite为实体, 仓储, 应用程序, Code First迁移, JavaScript/TypeScript 和 CSHTML/HTML以及必要的页面生成源代码. ABP Suite根据软件开发的最佳实践来生成代码, 所以你不必担心生成的代码的质量.", + "AbpSuiteExplanation7": "由于你有应用程序层中生成的CURD页面的构建块的源代码, 因此你可以轻松修改源码并将自定义/业务逻辑注入到所生成的代码中.", + "CrossPlatform": "跨平台", + "AbpSuiteExplanation8": "ABP Suite使用.NET Core构建并且它是跨平台的. 它在你的本地电脑中运行为web应用程序. 你可以在Windows, MacLinux上运行它", + "OtherFeatures": "其他功能", + "OtherFeatures1": "轻松更新你的解决方案的NuGetNPM包.", + "OtherFeatures2": "重新生成已经生成的页面.", + "OtherFeatures3": "创建新的解决方案", + "ThanksForCreatingProject": "感谢你创建你的项目!", + "HotToRunSolution": "如何运行你的解决方案?", + "HotToRunSolutionExplanation": "查看入门文档来学习如何配置和运行你的解决方案.", + "GettingStarted": "入门", + "WebAppDevTutorial": "Web应用开发教程", + "WebAppDevTutorialExplanation": "查看web应用程序开发教程文档来一步一步的了解开发示例.", + "Document": "文档", + "UsingABPSuiteToCURD": "使用ABP Suite来生成CURD页面", + "SeeABPSuiteDocument": "查看ABP Suite文档学习如何使用ABP Suite.", + "AskQuestionsOnSupport": "你可以在ABP商业支持提出问题.", + "Documentation": "文档", + "SeeModulesDocument": "查看模块文档获取所有商业(专业)模块列表和它们的文档.", + "Pricing": "价格", + "PricingExplanation": "选择当前业务需要的特性和功能. 随着业务增长轻松升级.", + "Team": "团队", + "Business": "商业", + "Enterprise": "企业", + "Custom": "自定义", + "IncludedDeveloperLicenses": "包括开发人员许可", + "CustomLicenceOrAdditionalServices": "需要自定义许可与额外服务?", + "CustomOrVolumeLicense": "自定义或批量许可", + "LiveTrainingSupport": "现场培训和支持", + "AndMore": "和更多", + "AdditionalDeveloperLicense": "额外的开发人员许可", + "ProjectCount": "项目数量", + "AllProModules": "所有模块", + "AllProThemes": "所有主题", + "AllProStartupTemplates": "所有专业启动模板", + "SourceCodeOfAllModules": "所有模块的源码", + "SourceCodeOfAllThemes": "所有主题的源码", + "PerpetualLicense": "永久的许可", + "UnlimitedServerDeployment": "无限制的服务器部署", + "YearUpgrade": "1年升级", + "YearPremiumForumSupport": "1年高级论坛支持", + "ForumSupportIncidentCountYear": "论坛支持事件数量/年", + "PrivateTicketEmailSupport": "私有票和email支持", + "BuyNow": "现在购买" } } \ No newline at end of file From 52c40009d4927471efb46369c8f0d5fc9819571c Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Mon, 1 Mar 2021 21:15:53 +0800 Subject: [PATCH 16/43] Improve --- .../AbpIoLocalization/Base/Localization/Resources/en.json | 3 ++- .../AbpIoLocalization/Base/Localization/Resources/zh-Hans.json | 3 ++- .../Commercial/Localization/Resources/en.json | 3 ++- .../Commercial/Localization/Resources/zh-Hans.json | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json index f28ff1c4a1..eedfd4bccd 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json @@ -31,6 +31,7 @@ "Permission:UserInfo": "Usere info", "SeeDocuments": "See Documents", "Samples": "Samples", - "Framework": "Framework" + "Framework": "Framework", + "Support": "Support" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/zh-Hans.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/zh-Hans.json index 5b9952993f..01550612d4 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/zh-Hans.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/zh-Hans.json @@ -31,6 +31,7 @@ "Permission:UserInfo": "用户信息", "SeeDocuments": "查看文档", "Samples": "示例", - "Framework": "框架" + "Framework": "框架", + "Support": "支持" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json index 050d4df164..b03d57cee0 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json @@ -53,7 +53,8 @@ "Developer": "Developer", "Tools": "Tools", "Premium": "Premium", - "Support": "Support", + "PremiumSupport": "Premium Support", + "PremiumForumSupport": "Premium Forum Support", "UI": "UI", "Themes": "Themes", "JoinOurNewsletter": "Join Our Newsletter", diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hans.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hans.json index bc634de3c2..0e1e61dba8 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hans.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hans.json @@ -53,7 +53,8 @@ "Developer": "开发人员", "Tools": "工具", "Premium": "高级", - "Support": "支持", + "PremiumSupport": "高级支持", + "PremiumForumSupport": "高级论坛支持", "UI": "UI", "Themes": "主题", "JoinOurNewsletter": "加入我们的时事通讯", From 1debba1829e270c6b426da27c15ad56c0a880e53 Mon Sep 17 00:00:00 2001 From: GameBelial <243387971@qq.com> Date: Mon, 1 Mar 2021 22:32:37 +0800 Subject: [PATCH 17/43] Add support for RabbitMQ cluster. --- .../Volo/Abp/RabbitMQ/ConnectionPool.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/ConnectionPool.cs b/framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/ConnectionPool.cs index 5856f7bb16..7ca8346d11 100644 --- a/framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/ConnectionPool.cs +++ b/framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/ConnectionPool.cs @@ -26,10 +26,13 @@ namespace Volo.Abp.RabbitMQ return Connections.GetOrAdd( connectionName, - () => Options - .Connections - .GetOrDefault(connectionName) - .CreateConnection() + () => + { + var connection = Options.Connections.GetOrDefault(connectionName); + var hostnames = connection.HostName.TrimEnd(';').Split(';'); + // Handle Rabbit MQ Cluster. + return hostnames.Length == 1 ? connection.CreateConnection() : connection.CreateConnection(hostnames); + } ); } From c7f12500f4e73f70b32c76b2260ff6d77ceb7285 Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Tue, 2 Mar 2021 10:38:08 +0800 Subject: [PATCH 18/43] Remote async keyword --- .../Volo.Blogging.Admin.Web/BloggingAdminMenuContributor.cs | 4 +++- .../Volo.Docs.Admin.Web/Navigation/DocsMenuContributor.cs | 4 +++- .../Navigation/AbpIdentityWebMainMenuContributor.cs | 6 ++++-- .../Navigation/AbpTenantManagementWebMainMenuContributor.cs | 6 ++++-- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/modules/blogging/src/Volo.Blogging.Admin.Web/BloggingAdminMenuContributor.cs b/modules/blogging/src/Volo.Blogging.Admin.Web/BloggingAdminMenuContributor.cs index e2011acf43..43e4bfca5b 100644 --- a/modules/blogging/src/Volo.Blogging.Admin.Web/BloggingAdminMenuContributor.cs +++ b/modules/blogging/src/Volo.Blogging.Admin.Web/BloggingAdminMenuContributor.cs @@ -14,7 +14,7 @@ namespace Volo.Blogging.Admin } } - private async Task ConfigureMainMenuAsync(MenuConfigurationContext context) + private Task ConfigureMainMenuAsync(MenuConfigurationContext context) { var l = context.GetLocalizer(); @@ -23,6 +23,8 @@ namespace Volo.Blogging.Admin managementRootMenuItem.AddItem(new ApplicationMenuItem("BlogManagement.Blogs", l["Menu:Blogs"], "~/Blogging/Admin/Blogs", requiredPermissionName: BloggingPermissions.Blogs.Management)); context.Menu.GetAdministration().AddItem(managementRootMenuItem); + + return Task.CompletedTask; } } } diff --git a/modules/docs/src/Volo.Docs.Admin.Web/Navigation/DocsMenuContributor.cs b/modules/docs/src/Volo.Docs.Admin.Web/Navigation/DocsMenuContributor.cs index 8b53069132..644dd037c4 100644 --- a/modules/docs/src/Volo.Docs.Admin.Web/Navigation/DocsMenuContributor.cs +++ b/modules/docs/src/Volo.Docs.Admin.Web/Navigation/DocsMenuContributor.cs @@ -17,7 +17,7 @@ namespace Volo.Docs.Admin.Navigation } } - private async Task ConfigureMainMenuAsync(MenuConfigurationContext context) + private Task ConfigureMainMenuAsync(MenuConfigurationContext context) { var administrationMenu = context.Menu.GetAdministration(); @@ -29,6 +29,8 @@ namespace Volo.Docs.Admin.Navigation rootMenuItem.AddItem(new ApplicationMenuItem(DocsMenuNames.Projects, l["Menu:ProjectManagement"], "~/Docs/Admin/Projects", requiredPermissionName: DocsAdminPermissions.Projects.Default)); rootMenuItem.AddItem(new ApplicationMenuItem(DocsMenuNames.Documents, l["Menu:DocumentManagement"], "~/Docs/Admin/Documents", requiredPermissionName: DocsAdminPermissions.Documents.Default)); + + return Task.CompletedTask; } } } diff --git a/modules/identity/src/Volo.Abp.Identity.Web/Navigation/AbpIdentityWebMainMenuContributor.cs b/modules/identity/src/Volo.Abp.Identity.Web/Navigation/AbpIdentityWebMainMenuContributor.cs index e5d1f31f13..2f48318511 100644 --- a/modules/identity/src/Volo.Abp.Identity.Web/Navigation/AbpIdentityWebMainMenuContributor.cs +++ b/modules/identity/src/Volo.Abp.Identity.Web/Navigation/AbpIdentityWebMainMenuContributor.cs @@ -9,11 +9,11 @@ namespace Volo.Abp.Identity.Web.Navigation { public class AbpIdentityWebMainMenuContributor : IMenuContributor { - public virtual async Task ConfigureMenuAsync(MenuConfigurationContext context) + public virtual Task ConfigureMenuAsync(MenuConfigurationContext context) { if (context.Menu.Name != StandardMenus.Main) { - return; + return Task.CompletedTask; } var l = context.GetLocalizer(); @@ -23,6 +23,8 @@ namespace Volo.Abp.Identity.Web.Navigation identityMenuItem.AddItem(new ApplicationMenuItem(IdentityMenuNames.Users, l["Users"], url: "~/Identity/Users", requiredPermissionName: IdentityPermissions.Users.Default)); context.Menu.GetAdministration().AddItem(identityMenuItem); + + return Task.CompletedTask; } } } diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Navigation/AbpTenantManagementWebMainMenuContributor.cs b/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Navigation/AbpTenantManagementWebMainMenuContributor.cs index 45f9b75487..c3ebaa39b5 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Navigation/AbpTenantManagementWebMainMenuContributor.cs +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Navigation/AbpTenantManagementWebMainMenuContributor.cs @@ -9,11 +9,11 @@ namespace Volo.Abp.TenantManagement.Web.Navigation { public class AbpTenantManagementWebMainMenuContributor : IMenuContributor { - public virtual async Task ConfigureMenuAsync(MenuConfigurationContext context) + public virtual Task ConfigureMenuAsync(MenuConfigurationContext context) { if (context.Menu.Name != StandardMenus.Main) { - return; + return Task.CompletedTask; } var administrationMenu = context.Menu.GetAdministration(); @@ -24,6 +24,8 @@ namespace Volo.Abp.TenantManagement.Web.Navigation administrationMenu.AddItem(tenantManagementMenuItem); tenantManagementMenuItem.AddItem(new ApplicationMenuItem(TenantManagementMenuNames.Tenants, l["Tenants"], url: "~/TenantManagement/Tenants", requiredPermissionName: TenantManagementPermissions.Tenants.Default)); + + return Task.CompletedTask; } } } From 1ac196427c4cbf8a5d36bba0861ab1ab053ffa38 Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Tue, 2 Mar 2021 14:46:30 +0800 Subject: [PATCH 19/43] Use RequiredPermissionName for all ApplicationMenuItem of Blazor --- .../AbpIdentityWebMainMenuContributor.cs | 38 +++++++++---------- .../TenantManagementBlazorMenuContributor.cs | 18 +++++---- 2 files changed, 30 insertions(+), 26 deletions(-) diff --git a/modules/identity/src/Volo.Abp.Identity.Blazor/AbpIdentityWebMainMenuContributor.cs b/modules/identity/src/Volo.Abp.Identity.Blazor/AbpIdentityWebMainMenuContributor.cs index 5e5714a851..609da7e6a7 100644 --- a/modules/identity/src/Volo.Abp.Identity.Blazor/AbpIdentityWebMainMenuContributor.cs +++ b/modules/identity/src/Volo.Abp.Identity.Blazor/AbpIdentityWebMainMenuContributor.cs @@ -6,35 +6,35 @@ namespace Volo.Abp.Identity.Blazor { public class AbpIdentityWebMainMenuContributor : IMenuContributor { - public virtual async Task ConfigureMenuAsync(MenuConfigurationContext context) + public virtual Task ConfigureMenuAsync(MenuConfigurationContext context) { if (context.Menu.Name != StandardMenus.Main) { - return; + return Task.CompletedTask; } - var hasRolePermission = await context.IsGrantedAsync(IdentityPermissions.Roles.Default); - var hasUserPermission = await context.IsGrantedAsync(IdentityPermissions.Users.Default); + var administrationMenu = context.Menu.GetAdministration(); - if (hasRolePermission || hasUserPermission) - { - var administrationMenu = context.Menu.GetAdministration(); + var l = context.GetLocalizer(); - var l = context.GetLocalizer(); + var identityMenuItem = new ApplicationMenuItem(IdentityMenuNames.GroupName, l["Menu:IdentityManagement"], + icon: "far fa-id-card"); + administrationMenu.AddItem(identityMenuItem); - var identityMenuItem = new ApplicationMenuItem(IdentityMenuNames.GroupName, l["Menu:IdentityManagement"], icon: "far fa-id-card"); - administrationMenu.AddItem(identityMenuItem); + identityMenuItem.AddItem(new ApplicationMenuItem( + IdentityMenuNames.Roles, + l["Roles"], + url: "identity/roles", + requiredPermissionName: IdentityPermissions.Roles.Default)); - if (hasRolePermission) - { - identityMenuItem.AddItem(new ApplicationMenuItem(IdentityMenuNames.Roles, l["Roles"], url: "identity/roles")); - } - if (hasUserPermission) - { - identityMenuItem.AddItem(new ApplicationMenuItem(IdentityMenuNames.Users, l["Users"], url: "identity/users")); - } - } + identityMenuItem.AddItem(new ApplicationMenuItem( + IdentityMenuNames.Users, + l["Users"], + url: "identity/users", + requiredPermissionName: IdentityPermissions.Users.Default)); + + return Task.CompletedTask; } } } diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Blazor/Navigation/TenantManagementBlazorMenuContributor.cs b/modules/tenant-management/src/Volo.Abp.TenantManagement.Blazor/Navigation/TenantManagementBlazorMenuContributor.cs index 03bfd6b2d2..32cac00b2c 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Blazor/Navigation/TenantManagementBlazorMenuContributor.cs +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Blazor/Navigation/TenantManagementBlazorMenuContributor.cs @@ -6,24 +6,28 @@ namespace Volo.Abp.TenantManagement.Blazor.Navigation { public class TenantManagementBlazorMenuContributor : IMenuContributor { - public virtual async Task ConfigureMenuAsync(MenuConfigurationContext context) + public virtual Task ConfigureMenuAsync(MenuConfigurationContext context) { if (context.Menu.Name != StandardMenus.Main) { - return; + return Task.CompletedTask; } var administrationMenu = context.Menu.GetAdministration(); var l = context.GetLocalizer(); - var tenantManagementMenuItem = new ApplicationMenuItem(TenantManagementMenuNames.GroupName, l["Menu:TenantManagement"], icon: "fa fa-users"); + var tenantManagementMenuItem = new ApplicationMenuItem(TenantManagementMenuNames.GroupName, + l["Menu:TenantManagement"], icon: "fa fa-users"); administrationMenu.AddItem(tenantManagementMenuItem); - if (await context.IsGrantedAsync(TenantManagementPermissions.Tenants.Default)) - { - tenantManagementMenuItem.AddItem(new ApplicationMenuItem(TenantManagementMenuNames.Tenants, l["Tenants"], url: "tenant-management/tenants")); - } + tenantManagementMenuItem.AddItem(new ApplicationMenuItem( + TenantManagementMenuNames.Tenants, + l["Tenants"], + url: "tenant-management/tenants", + requiredPermissionName: TenantManagementPermissions.Tenants.Default)); + + return Task.CompletedTask; } } } From f2d3f62a17ddbd72233bf20547a13d76020be2a4 Mon Sep 17 00:00:00 2001 From: enisn Date: Tue, 2 Mar 2021 11:06:57 +0300 Subject: [PATCH 20/43] CmsKit - Add definitions to MediaDescriptor --- .../Admin/CmsKitAdminApplicationModule.cs | 26 +++++++++++ .../MediaDescriptorAdminAppService.cs | 38 ++++++++++++---- .../CmsKit/CmsKitCommonApplicationModule.cs | 5 +++ .../Volo/CmsKit/CmsKitErrorCodes.cs | 1 + .../CmsKit/Localization/Resources/en.json | 1 + .../CmsKit/Localization/Resources/tr.json | 1 + .../Volo/CmsKit/Pages/PageConsts.cs | 2 + .../MediaDescriptors/CmsKitMediaOptions.cs | 15 +++++++ .../DefaultMediaDescriptorDefinitionStore.cs | 43 +++++++++++++++++++ .../EntityCantHaveMediaException.cs | 21 +++++++++ .../IMediaDescriptorDefinitionStore.cs | 12 ++++++ .../MediaDescriptors/MediaDescriptor.cs | 4 +- .../MediaDescriptorDefinition.cs | 18 ++++++++ .../MediaDescriptorManager.cs | 36 ++++++++++++++++ .../Volo/CmsKit/PolicySpecifiedDefinition.cs | 14 +++++- .../CmsKit/Tags/TagEntityTypeDefiniton.cs | 4 +- 16 files changed, 226 insertions(+), 15 deletions(-) create mode 100644 modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/CmsKitMediaOptions.cs create mode 100644 modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/DefaultMediaDescriptorDefinitionStore.cs create mode 100644 modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/EntityCantHaveMediaException.cs create mode 100644 modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/IMediaDescriptorDefinitionStore.cs create mode 100644 modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/MediaDescriptorDefinition.cs create mode 100644 modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/MediaDescriptorManager.cs diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/CmsKitAdminApplicationModule.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/CmsKitAdminApplicationModule.cs index 2ad96af963..6da0e9233a 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/CmsKitAdminApplicationModule.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/CmsKitAdminApplicationModule.cs @@ -7,6 +7,8 @@ using Volo.Abp.Modularity; using Volo.CmsKit.Blogs; using Volo.CmsKit.GlobalFeatures; using Volo.CmsKit.Localization; +using Volo.CmsKit.MediaDescriptors; +using Volo.CmsKit.Pages; using Volo.CmsKit.Permissions; using Volo.CmsKit.Tags; @@ -49,6 +51,30 @@ namespace Volo.CmsKit.Admin CmsKitAdminPermissions.BlogPosts.Update)); } }); + + if (GlobalFeatureManager.Instance.IsEnabled()) + { + Configure(options => + { + if (GlobalFeatureManager.Instance.IsEnabled()) + { + options.EntityTypes.AddIfNotContains( + new MediaDescriptorDefinition( + BlogPostConsts.EntityType, + createPolicy: CmsKitAdminPermissions.BlogPosts.Update, + deletePolicy: CmsKitAdminPermissions.BlogPosts.Delete)); + } + + if (GlobalFeatureManager.Instance.IsEnabled()) + { + options.EntityTypes.AddIfNotContains( + new MediaDescriptorDefinition( + PageConsts.EntityType, + createPolicy: CmsKitAdminPermissions.Pages.Update, + deletePolicy: CmsKitAdminPermissions.Pages.Delete)); + } + }); + } } } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/MediaDescriptors/MediaDescriptorAdminAppService.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/MediaDescriptors/MediaDescriptorAdminAppService.cs index ee2b5af152..dfa752aeb5 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/MediaDescriptors/MediaDescriptorAdminAppService.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/MediaDescriptors/MediaDescriptorAdminAppService.cs @@ -13,31 +13,51 @@ namespace Volo.CmsKit.Admin.MediaDescriptors [Authorize(CmsKitAdminPermissions.MediaDescriptors.Default)] public class MediaDescriptorAdminAppService : CmsKitAdminAppServiceBase, IMediaDescriptorAdminAppService { - protected readonly IBlobContainer MediaContainer; - protected readonly IMediaDescriptorRepository MediaDescriptorRepository; - - public MediaDescriptorAdminAppService(IBlobContainer mediaContainer, IMediaDescriptorRepository mediaDescriptorRepository) + protected IBlobContainer MediaContainer { get; } + protected IMediaDescriptorRepository MediaDescriptorRepository { get; } + protected MediaDescriptorManager MediaDescriptorManager { get; } + protected IMediaDescriptorDefinitionStore MediaDescriptorDefinitionStore { get; } + + public MediaDescriptorAdminAppService( + IBlobContainer mediaContainer, + IMediaDescriptorRepository mediaDescriptorRepository, + MediaDescriptorManager mediaDescriptorManager, + IMediaDescriptorDefinitionStore mediaDescriptorDefinitionStore) { MediaContainer = mediaContainer; MediaDescriptorRepository = mediaDescriptorRepository; + MediaDescriptorManager = mediaDescriptorManager; + MediaDescriptorDefinitionStore = mediaDescriptorDefinitionStore; } [Authorize(CmsKitAdminPermissions.MediaDescriptors.Create)] public virtual async Task CreateAsync(CreateMediaInputStream inputStream) { + var definition = await MediaDescriptorDefinitionStore.GetDefinitionAsync(inputStream.EntityType); + + await CheckPolicyAsync(definition.CreatePolicy); + var newId = GuidGenerator.Create(); - var stream = inputStream.GetStream(); - var newEntity = new MediaDescriptor(newId, inputStream.EntityType, inputStream.Name, inputStream.ContentType, stream.Length, CurrentTenant.Id); + using (var stream = inputStream.GetStream()) + { + var newEntity = await MediaDescriptorManager.Create(inputStream.ContentType, inputStream.Name, inputStream.ContentType, inputStream.ContentLength ?? 0); - await MediaContainer.SaveAsync(newId.ToString(), stream); - await MediaDescriptorRepository.InsertAsync(newEntity); + await MediaContainer.SaveAsync(newId.ToString(), stream); + await MediaDescriptorRepository.InsertAsync(newEntity); - return ObjectMapper.Map(newEntity); + return ObjectMapper.Map(newEntity); + } } [Authorize(CmsKitAdminPermissions.MediaDescriptors.Delete)] public virtual async Task DeleteAsync(Guid id) { + var mediaDescriptor = await MediaDescriptorRepository.GetAsync(id); + + var definition = await MediaDescriptorDefinitionStore.GetDefinitionAsync(mediaDescriptor.EntityType); + + await CheckPolicyAsync(definition.DeletePolicy); + await MediaContainer.DeleteAsync(id.ToString()); await MediaDescriptorRepository.DeleteAsync(id); } diff --git a/modules/cms-kit/src/Volo.CmsKit.Common.Application/Volo/CmsKit/CmsKitCommonApplicationModule.cs b/modules/cms-kit/src/Volo.CmsKit.Common.Application/Volo/CmsKit/CmsKitCommonApplicationModule.cs index 8fa08fa950..1778e7e234 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Common.Application/Volo/CmsKit/CmsKitCommonApplicationModule.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Common.Application/Volo/CmsKit/CmsKitCommonApplicationModule.cs @@ -1,6 +1,11 @@ using Volo.Abp.Application; using Volo.Abp.AutoMapper; +using Volo.Abp.GlobalFeatures; using Volo.Abp.Modularity; +using Volo.CmsKit.Blogs; +using Volo.CmsKit.GlobalFeatures; +using Volo.CmsKit.MediaDescriptors; +using Volo.CmsKit.Permissions; namespace Volo.CmsKit { diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/CmsKitErrorCodes.cs b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/CmsKitErrorCodes.cs index 64956b8683..b358672a1a 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/CmsKitErrorCodes.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/CmsKitErrorCodes.cs @@ -28,6 +28,7 @@ public static class MediaDescriptors { public const string InvalidName = "CmsKit:Media:0001"; + public const string EntityTypeDoesntExist = "CmsKit:Media:0002"; } } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/en.json b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/en.json index 8102350c6a..1107d5e5db 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/en.json +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/en.json @@ -20,6 +20,7 @@ "CmsKit:Media:0001": "'{Name}' is not a valid media name.", "CmsKit:Page:0001": "The given url ({0}) already exists.", "CmsKit:Tag:0002": "The entity is not taggable!", + "CmsKit:Media:0002": "The entity can't have media.", "CommentAuthorizationExceptionMessage": "Those comments are not allowed for public display.", "CommentDeletionConfirmationMessage": "This comment and all replies will be deleted!", "Comments": "Comments", diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/tr.json b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/tr.json index 5701c631f2..f41639325a 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/tr.json +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/tr.json @@ -16,6 +16,7 @@ "CmsKit:0002": "İçerik zaten mevcut!", "CmsKit:0003": "{0} ögesi etiketlenebilir değil.", "CmsKit:BlogPost:0001": "Aynı url etiketi zaten mevcut.", + "CmsKit:Media:0002": "Bu öge için medya eklenemez.", "CmsKit:Page:0001": "Girilen url ({0}) kullanımdadır.", "CmsKit:Tag:0002": "Bu öge etiketlenebilir değil.", "CommentAuthorizationExceptionMessage": "Bu yorumları görebilmek için yetki gerekir.", diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Pages/PageConsts.cs b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Pages/PageConsts.cs index 47d82b5d07..fc4c5c75ad 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Pages/PageConsts.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Pages/PageConsts.cs @@ -2,6 +2,8 @@ { public class PageConsts { + public const string EntityType = "Page"; + public static int MaxTitleLength { get; set; } = 256; public static int MaxSlugLength { get; set; } = 256; diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/CmsKitMediaOptions.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/CmsKitMediaOptions.cs new file mode 100644 index 0000000000..89f5d0dca3 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/CmsKitMediaOptions.cs @@ -0,0 +1,15 @@ +using JetBrains.Annotations; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Volo.CmsKit.MediaDescriptors +{ + public class CmsKitMediaOptions + { + [NotNull] + public List EntityTypes { get; } = new(); + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/DefaultMediaDescriptorDefinitionStore.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/DefaultMediaDescriptorDefinitionStore.cs new file mode 100644 index 0000000000..4cf5889dcc --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/DefaultMediaDescriptorDefinitionStore.cs @@ -0,0 +1,43 @@ +using JetBrains.Annotations; +using Microsoft.Extensions.Options; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Volo.Abp; +using Volo.Abp.DependencyInjection; + +namespace Volo.CmsKit.MediaDescriptors +{ + public class DefaultMediaDescriptorDefinitionStore : IMediaDescriptorDefinitionStore, ITransientDependency + { + protected CmsKitMediaOptions Options { get; } + + public DefaultMediaDescriptorDefinitionStore(IOptions options) + { + Options = options.Value; + } + + /// + /// Gets single by entityType. + /// + /// EntityType to get definition. + /// Thrown when EntityType is not configured as taggable. + /// More than one element satisfies the condition in predicate. + public Task GetDefinitionAsync([NotNull] string entityType) + { + Check.NotNullOrWhiteSpace(entityType, nameof(entityType)); + + var result = Options.EntityTypes.SingleOrDefault(x => x.EntityType == entityType) ?? + throw new EntityCantHaveMediaException(entityType); + + return Task.FromResult(result); + } + + public Task IsDefinedAsync([NotNull] string entityType) + { + return Task.Run(() => Options.EntityTypes.Any(a => a.EntityType == entityType)); + } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/EntityCantHaveMediaException.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/EntityCantHaveMediaException.cs new file mode 100644 index 0000000000..29d60126a1 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/EntityCantHaveMediaException.cs @@ -0,0 +1,21 @@ +using System.Runtime.Serialization; +using Volo.Abp; + +namespace Volo.CmsKit.MediaDescriptors +{ + public class EntityCantHaveMediaException : BusinessException + { + public EntityCantHaveMediaException(SerializationInfo serializationInfo, StreamingContext context) : base(serializationInfo, context) + { + } + + public EntityCantHaveMediaException(string entityType) + { + EntityType = entityType; + Code = CmsKitErrorCodes.MediaDescriptors.EntityTypeDoesntExist; + WithData(nameof(entityType), EntityType); + } + + public string EntityType { get; } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/IMediaDescriptorDefinitionStore.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/IMediaDescriptorDefinitionStore.cs new file mode 100644 index 0000000000..9cd5c5c40d --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/IMediaDescriptorDefinitionStore.cs @@ -0,0 +1,12 @@ +using JetBrains.Annotations; +using System.Threading.Tasks; + +namespace Volo.CmsKit.MediaDescriptors +{ + public interface IMediaDescriptorDefinitionStore + { + Task IsDefinedAsync([NotNull] string entityType); + + Task GetDefinitionAsync([NotNull] string entityType); + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/MediaDescriptor.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/MediaDescriptor.cs index c97eb26b9a..ee0df74b59 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/MediaDescriptor.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/MediaDescriptor.cs @@ -9,7 +9,7 @@ namespace Volo.CmsKit.MediaDescriptors { public Guid? TenantId { get; protected set; } - public string EntityType { get; set; } + public string EntityType { get; protected set; } public string Name { get; protected set; } @@ -22,7 +22,7 @@ namespace Volo.CmsKit.MediaDescriptors } - public MediaDescriptor(Guid id, string entityType, string name, string mimeType, long size, Guid? tenantId = null) : base(id) + internal MediaDescriptor(Guid id, string entityType, string name, string mimeType, long size, Guid? tenantId = null) : base(id) { TenantId = tenantId; diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/MediaDescriptorDefinition.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/MediaDescriptorDefinition.cs new file mode 100644 index 0000000000..b2ccb01460 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/MediaDescriptorDefinition.cs @@ -0,0 +1,18 @@ +using JetBrains.Annotations; + +namespace Volo.CmsKit.MediaDescriptors +{ + public class MediaDescriptorDefinition : PolicySpecifiedDefinition + { + public MediaDescriptorDefinition( + [NotNull] string entityType, + [CanBeNull] string createPolicy = null, + [CanBeNull] string updatePolicy = null, + [CanBeNull] string deletePolicy = null) : base(entityType, + createPolicy, + updatePolicy, + deletePolicy) + { + } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/MediaDescriptorManager.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/MediaDescriptorManager.cs new file mode 100644 index 0000000000..f0773ad7ff --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/MediaDescriptorManager.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Volo.Abp.Domain.Services; +using Volo.CmsKit.MediaDescriptors; + +namespace Volo.CmsKit.MediaDescriptors +{ + public class MediaDescriptorManager : DomainService + { + protected IMediaDescriptorDefinitionStore MediaDescriptorDefinitionStore { get; } + + public MediaDescriptorManager(IMediaDescriptorDefinitionStore mediaDescriptorDefinitionStore) + { + MediaDescriptorDefinitionStore = mediaDescriptorDefinitionStore; + } + + public virtual async Task Create(string entityType, string name, string mimeType, long size) + { + if(!await MediaDescriptorDefinitionStore.IsDefinedAsync(entityType)) + { + throw new EntityCantHaveMediaException(entityType); + } + + return new MediaDescriptor( + GuidGenerator.Create(), + entityType, + name, + mimeType, + size, + CurrentTenant.Id); + } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/PolicySpecifiedDefinition.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/PolicySpecifiedDefinition.cs index 54527621ec..2a5f69b69e 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/PolicySpecifiedDefinition.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/PolicySpecifiedDefinition.cs @@ -1,23 +1,30 @@ using JetBrains.Annotations; +using System; +using Volo.Abp; namespace Volo.CmsKit { - public abstract class PolicySpecifiedDefinition + public abstract class PolicySpecifiedDefinition : IEquatable { protected PolicySpecifiedDefinition() { } public PolicySpecifiedDefinition( + [NotNull] string entityType, [CanBeNull] string createPolicy = null, [CanBeNull] string updatePolicy = null, [CanBeNull] string deletePolicy = null) { + EntityType = Check.NotNullOrEmpty(entityType, nameof(entityType)); CreatePolicy = createPolicy; DeletePolicy = deletePolicy; UpdatePolicy = updatePolicy; } + [NotNull] + public string EntityType { get; set; } + [CanBeNull] public virtual string CreatePolicy { get; set; } @@ -26,5 +33,10 @@ namespace Volo.CmsKit [CanBeNull] public virtual string DeletePolicy { get; set; } + + public bool Equals(PolicySpecifiedDefinition other) + { + return other?.EntityType == EntityType; + } } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/TagEntityTypeDefiniton.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/TagEntityTypeDefiniton.cs index e714d91c0b..a6fb4cde7a 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/TagEntityTypeDefiniton.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/TagEntityTypeDefiniton.cs @@ -7,8 +7,6 @@ namespace Volo.CmsKit.Tags { public class TagEntityTypeDefiniton : PolicySpecifiedDefinition, IEquatable { - public string EntityType { get; } - [CanBeNull] public virtual ILocalizableString DisplayName { get; } @@ -21,7 +19,7 @@ namespace Volo.CmsKit.Tags [CanBeNull] ILocalizableString displayName = null, [CanBeNull] string createPolicy = null, [CanBeNull] string updatePolicy = null, - [CanBeNull] string deletePolicy = null) : base(createPolicy, updatePolicy, deletePolicy) + [CanBeNull] string deletePolicy = null) : base(entityType, createPolicy, updatePolicy, deletePolicy) { EntityType = Check.NotNullOrWhiteSpace(entityType, nameof(entityType)); DisplayName = displayName; From 39b449569aedb700e7dfc8f3cb9efcd9faf97c0a Mon Sep 17 00:00:00 2001 From: enisn Date: Tue, 2 Mar 2021 12:19:11 +0300 Subject: [PATCH 21/43] CmsKit - Update tests for MediaDescriptor --- .../MediaDescriptorAdminAppService.cs | 2 +- .../Volo/CmsKit/AssemblyInfo.cs | 4 ++ .../DefaultMediaDescriptorDefinitionStore.cs | 6 ++- .../MediaDescriptorDefinition.cs | 8 +++- .../MediaDescriptorManager.cs | 9 +--- .../MediaDescriptorAdminAppService_Tests.cs | 3 +- .../MediaDescriptorManager_Test.cs | 44 +++++++++++++++++++ .../CmsKitDataSeedContributor.cs | 7 ++- .../Volo.CmsKit.TestBase/CmsKitTestData.cs | 12 ++--- 9 files changed, 77 insertions(+), 18 deletions(-) create mode 100644 modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/AssemblyInfo.cs create mode 100644 modules/cms-kit/test/Volo.CmsKit.Domain.Tests/MediaDescriptors/MediaDescriptorManager_Test.cs diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/MediaDescriptors/MediaDescriptorAdminAppService.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/MediaDescriptors/MediaDescriptorAdminAppService.cs index dfa752aeb5..5307591447 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/MediaDescriptors/MediaDescriptorAdminAppService.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/MediaDescriptors/MediaDescriptorAdminAppService.cs @@ -40,7 +40,7 @@ namespace Volo.CmsKit.Admin.MediaDescriptors var newId = GuidGenerator.Create(); using (var stream = inputStream.GetStream()) { - var newEntity = await MediaDescriptorManager.Create(inputStream.ContentType, inputStream.Name, inputStream.ContentType, inputStream.ContentLength ?? 0); + var newEntity = await MediaDescriptorManager.CreateAsync(inputStream.EntityType, inputStream.Name, inputStream.ContentType, inputStream.ContentLength ?? 0); await MediaContainer.SaveAsync(newId.ToString(), stream); await MediaDescriptorRepository.InsertAsync(newEntity); diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/AssemblyInfo.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/AssemblyInfo.cs new file mode 100644 index 0000000000..f8d4fc09f3 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/AssemblyInfo.cs @@ -0,0 +1,4 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Volo.CmsKit.Domain.Tests", AllInternalsVisible = true)] +[assembly: InternalsVisibleTo("Volo.CmsKit.TestBase", AllInternalsVisible = true)] \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/DefaultMediaDescriptorDefinitionStore.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/DefaultMediaDescriptorDefinitionStore.cs index 4cf5889dcc..4bdd4d6e98 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/DefaultMediaDescriptorDefinitionStore.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/DefaultMediaDescriptorDefinitionStore.cs @@ -37,7 +37,11 @@ namespace Volo.CmsKit.MediaDescriptors public Task IsDefinedAsync([NotNull] string entityType) { - return Task.Run(() => Options.EntityTypes.Any(a => a.EntityType == entityType)); + Check.NotNullOrEmpty(entityType, nameof(entityType)); + + var isDefined = Options.EntityTypes.Any(a => a.EntityType == entityType); + + return Task.FromResult(isDefined); } } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/MediaDescriptorDefinition.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/MediaDescriptorDefinition.cs index b2ccb01460..073ed694f6 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/MediaDescriptorDefinition.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/MediaDescriptorDefinition.cs @@ -1,8 +1,9 @@ using JetBrains.Annotations; +using System; namespace Volo.CmsKit.MediaDescriptors { - public class MediaDescriptorDefinition : PolicySpecifiedDefinition + public class MediaDescriptorDefinition : PolicySpecifiedDefinition, IEquatable { public MediaDescriptorDefinition( [NotNull] string entityType, @@ -14,5 +15,10 @@ namespace Volo.CmsKit.MediaDescriptors deletePolicy) { } + + public bool Equals(MediaDescriptorDefinition other) + { + return base.Equals(other); + } } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/MediaDescriptorManager.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/MediaDescriptorManager.cs index f0773ad7ff..730e4e3ccf 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/MediaDescriptorManager.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/MediaDescriptorManager.cs @@ -1,10 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Threading.Tasks; using Volo.Abp.Domain.Services; -using Volo.CmsKit.MediaDescriptors; namespace Volo.CmsKit.MediaDescriptors { @@ -17,7 +12,7 @@ namespace Volo.CmsKit.MediaDescriptors MediaDescriptorDefinitionStore = mediaDescriptorDefinitionStore; } - public virtual async Task Create(string entityType, string name, string mimeType, long size) + public virtual async Task CreateAsync(string entityType, string name, string mimeType, long size) { if(!await MediaDescriptorDefinitionStore.IsDefinedAsync(entityType)) { diff --git a/modules/cms-kit/test/Volo.CmsKit.Application.Tests/MediaDescriptors/MediaDescriptorAdminAppService_Tests.cs b/modules/cms-kit/test/Volo.CmsKit.Application.Tests/MediaDescriptors/MediaDescriptorAdminAppService_Tests.cs index 20bfaf2c12..52169eddbe 100644 --- a/modules/cms-kit/test/Volo.CmsKit.Application.Tests/MediaDescriptors/MediaDescriptorAdminAppService_Tests.cs +++ b/modules/cms-kit/test/Volo.CmsKit.Application.Tests/MediaDescriptors/MediaDescriptorAdminAppService_Tests.cs @@ -34,7 +34,8 @@ namespace Volo.CmsKit.MediaDescriptors var inputStream = new CreateMediaInputStream(stream) { ContentType = mediaType, - Name = mediaName + Name = mediaName, + EntityType = _cmsKitTestData.Media_1_EntityType }; var media = await _mediaDescriptorAdminAppService.CreateAsync(inputStream); diff --git a/modules/cms-kit/test/Volo.CmsKit.Domain.Tests/MediaDescriptors/MediaDescriptorManager_Test.cs b/modules/cms-kit/test/Volo.CmsKit.Domain.Tests/MediaDescriptors/MediaDescriptorManager_Test.cs new file mode 100644 index 0000000000..6bf183564e --- /dev/null +++ b/modules/cms-kit/test/Volo.CmsKit.Domain.Tests/MediaDescriptors/MediaDescriptorManager_Test.cs @@ -0,0 +1,44 @@ +using Shouldly; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Volo.CmsKit.Blogs; +using Xunit; + +namespace Volo.CmsKit.MediaDescriptors +{ + public class MediaDescriptorManager_Test : CmsKitDomainTestBase + { + private readonly MediaDescriptorManager manager; + private readonly CmsKitTestData testData; + + public MediaDescriptorManager_Test() + { + manager = GetRequiredService(); + testData = GetRequiredService(); + } + + [Fact] + public async Task CreateAsync_ShouldWorkProperly_WithDefinedEntityType() + { + var created = await manager.CreateAsync(testData.Media_1_EntityType, "MyAwesomeImage.png", "image/png", 128000); + + created.ShouldNotBeNull(); + created.Id.ShouldNotBe(Guid.Empty); + } + + [Fact] + public async Task CreateAsync_ShouldThrowException_WithUndefinedEntityType() + { + var undefinedEntityType = "My.Any.EntityType"; + + var exception = await Should.ThrowAsync(async () => + await manager.CreateAsync(undefinedEntityType, "import.json", "application/json", 256000)); + + exception.ShouldNotBeNull(); + exception.EntityType.ShouldBe(undefinedEntityType); + } + } +} diff --git a/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitDataSeedContributor.cs b/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitDataSeedContributor.cs index 9bdd1cfb96..e2c928be2a 100644 --- a/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitDataSeedContributor.cs +++ b/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitDataSeedContributor.cs @@ -48,6 +48,7 @@ namespace Volo.CmsKit private readonly IMediaDescriptorRepository _mediaDescriptorRepository; private readonly IBlobContainer _mediaBlobContainer; private readonly BlogManager _blogManager; + private readonly IOptions _mediaOptions; public CmsKitDataSeedContributor( IGuidGenerator guidGenerator, @@ -71,7 +72,8 @@ namespace Volo.CmsKit IOptions tagOptions, IMediaDescriptorRepository mediaDescriptorRepository, IBlobContainer mediaBlobContainer, - BlogManager blogManager) + BlogManager blogManager, + IOptions cmsMediaOptions) { _guidGenerator = guidGenerator; _cmsUserRepository = cmsUserRepository; @@ -95,6 +97,7 @@ namespace Volo.CmsKit _mediaDescriptorRepository = mediaDescriptorRepository; _mediaBlobContainer = mediaBlobContainer; _blogManager = blogManager; + _mediaOptions = cmsMediaOptions; } public async Task SeedAsync(DataSeedContext context) @@ -133,6 +136,8 @@ namespace Volo.CmsKit _tagOptions.Value.EntityTypes.AddIfNotContains(new TagEntityTypeDefiniton(_cmsKitTestData.Content_2_EntityType)); _tagOptions.Value.EntityTypes.AddIfNotContains(new TagEntityTypeDefiniton(_cmsKitTestData.TagDefinition_1_EntityType)); + _mediaOptions.Value.EntityTypes.AddIfNotContains(new MediaDescriptorDefinition(_cmsKitTestData.Media_1_EntityType)); + return Task.CompletedTask; } diff --git a/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitTestData.cs b/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitTestData.cs index 32e4cd9f84..396ae5a013 100644 --- a/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitTestData.cs +++ b/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitTestData.cs @@ -101,13 +101,13 @@ namespace Volo.CmsKit public bool BlogFeature_2_Enabled => false; public Guid Media_1_Id { get; } = Guid.NewGuid(); - - public string Media_1_EntityType = nameof(Blog); - - public string Media_1_Content = "Hi, this is text file"; - public string Media_1_Name = "hello.txt"; + public string Media_1_EntityType => nameof(Blog); - public string Media_1_ContentType = "text/plain"; + public string Media_1_Content { get; } = "Hi, this is text file"; + + public string Media_1_Name { get; } = "hello.txt"; + + public string Media_1_ContentType { get; } = "text/plain"; } } From db84676bfd10f5b33541e2fe86844401efcb9950 Mon Sep 17 00:00:00 2001 From: enisn Date: Tue, 2 Mar 2021 13:10:31 +0300 Subject: [PATCH 22/43] CmsKit - Change policies of PolicySpecifiedDefinition to collection --- .../Permissions/CmsKitAdminPermissions.cs | 8 ++-- .../CmsKit/Admin/CmsKitAdminAppServiceBase.cs | 35 ++++++++++++++++- .../Admin/CmsKitAdminApplicationModule.cs | 16 ++++---- .../MediaDescriptorAdminAppService.cs | 4 +- .../Admin/Tags/EntityTagAdminAppService.cs | 6 +-- .../MediaDescriptorDefinition.cs | 20 ++++------ .../Volo/CmsKit/PolicySpecifiedDefinition.cs | 38 +++++++++++++------ .../CmsKit/Tags/TagEntityTypeDefiniton.cs | 9 +++-- ...TagEntityTypeDefinitionDictionary_Tests.cs | 8 ++-- 9 files changed, 94 insertions(+), 50 deletions(-) diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Permissions/CmsKitAdminPermissions.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Permissions/CmsKitAdminPermissions.cs index 6de7bad5e4..baa26b0fff 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Permissions/CmsKitAdminPermissions.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Permissions/CmsKitAdminPermissions.cs @@ -5,13 +5,13 @@ namespace Volo.CmsKit.Permissions public class CmsKitAdminPermissions { public const string GroupName = "CmsKit"; - + public static class Comments { public const string Default = GroupName + ".Comments"; public const string Delete = Default + ".Delete"; } - + public static class Tags { public const string Default = GroupName + ".Tags"; @@ -19,7 +19,7 @@ namespace Volo.CmsKit.Permissions public const string Update = Default + ".Update"; public const string Delete = Default + ".Delete"; } - + public static class Contents { public const string Default = GroupName + ".Contents"; @@ -52,7 +52,7 @@ namespace Volo.CmsKit.Permissions public const string Update = Default + ".Update"; public const string Delete = Default + ".Delete"; } - + public static class MediaDescriptors { public const string Default = GroupName + ".MediaDescriptors"; diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/CmsKitAdminAppServiceBase.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/CmsKitAdminAppServiceBase.cs index 3f4596f586..83d36431c8 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/CmsKitAdminAppServiceBase.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/CmsKitAdminAppServiceBase.cs @@ -1,4 +1,13 @@ -namespace Volo.CmsKit.Admin +using JetBrains.Annotations; +using Microsoft.AspNetCore.Authorization; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.InteropServices.ComTypes; +using System.Threading.Tasks; +using Volo.Abp; +using Volo.Abp.Authorization; + +namespace Volo.CmsKit.Admin { public abstract class CmsKitAdminAppServiceBase : CmsKitAppServiceBase { @@ -6,5 +15,29 @@ { ObjectMapperContext = typeof(CmsKitAdminApplicationModule); } + + /// + /// Checks given policies until finding granted policy. If none of them is granted, throws + /// + /// Policies to be checked. + /// Thrown when none of policies is granted. + protected async Task CheckAnyOfPoliciesAsync([NotNull] IEnumerable policies) + { + Check.NotNull(policies, nameof(policies)); + + foreach (var policy in policies) + { + if (await AuthorizationService.IsGrantedAsync(policy)) + { + return; + } + } + + var exception = new AbpAuthorizationException(); + + exception.Data[nameof(policies)] = policies; + + throw exception; + } } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/CmsKitAdminApplicationModule.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/CmsKitAdminApplicationModule.cs index 6da0e9233a..95dbe44a04 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/CmsKitAdminApplicationModule.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/CmsKitAdminApplicationModule.cs @@ -46,9 +46,9 @@ namespace Volo.CmsKit.Admin new TagEntityTypeDefiniton( BlogPostConsts.EntityType, LocalizableString.Create("BlogPost"), - CmsKitAdminPermissions.BlogPosts.Update, - CmsKitAdminPermissions.BlogPosts.Update, - CmsKitAdminPermissions.BlogPosts.Update)); + new[] { CmsKitAdminPermissions.BlogPosts.Create, CmsKitAdminPermissions.BlogPosts.Update }, + new[] { CmsKitAdminPermissions.BlogPosts.Create, CmsKitAdminPermissions.BlogPosts.Update }, + new[] { CmsKitAdminPermissions.BlogPosts.Create, CmsKitAdminPermissions.BlogPosts.Update })); } }); @@ -60,9 +60,9 @@ namespace Volo.CmsKit.Admin { options.EntityTypes.AddIfNotContains( new MediaDescriptorDefinition( - BlogPostConsts.EntityType, - createPolicy: CmsKitAdminPermissions.BlogPosts.Update, - deletePolicy: CmsKitAdminPermissions.BlogPosts.Delete)); + BlogPostConsts.EntityType, + createPolicies: new[] { CmsKitAdminPermissions.BlogPosts.Create, CmsKitAdminPermissions.BlogPosts.Update }, + deletePolicies: new[] { CmsKitAdminPermissions.BlogPosts.Delete })); } if (GlobalFeatureManager.Instance.IsEnabled()) @@ -70,8 +70,8 @@ namespace Volo.CmsKit.Admin options.EntityTypes.AddIfNotContains( new MediaDescriptorDefinition( PageConsts.EntityType, - createPolicy: CmsKitAdminPermissions.Pages.Update, - deletePolicy: CmsKitAdminPermissions.Pages.Delete)); + createPolicies: new[] { CmsKitAdminPermissions.Pages.Create, CmsKitAdminPermissions.Pages.Update }, + deletePolicies: new[] { CmsKitAdminPermissions.Pages.Delete })); } }); } diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/MediaDescriptors/MediaDescriptorAdminAppService.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/MediaDescriptors/MediaDescriptorAdminAppService.cs index 5307591447..ba2b1a70b5 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/MediaDescriptors/MediaDescriptorAdminAppService.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/MediaDescriptors/MediaDescriptorAdminAppService.cs @@ -35,7 +35,7 @@ namespace Volo.CmsKit.Admin.MediaDescriptors { var definition = await MediaDescriptorDefinitionStore.GetDefinitionAsync(inputStream.EntityType); - await CheckPolicyAsync(definition.CreatePolicy); + await CheckAnyOfPoliciesAsync(definition.CreatePolicies); var newId = GuidGenerator.Create(); using (var stream = inputStream.GetStream()) @@ -56,7 +56,7 @@ namespace Volo.CmsKit.Admin.MediaDescriptors var definition = await MediaDescriptorDefinitionStore.GetDefinitionAsync(mediaDescriptor.EntityType); - await CheckPolicyAsync(definition.DeletePolicy); + await CheckAnyOfPoliciesAsync(definition.DeletePolicies); await MediaContainer.DeleteAsync(id.ToString()); await MediaDescriptorRepository.DeleteAsync(id); diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Tags/EntityTagAdminAppService.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Tags/EntityTagAdminAppService.cs index cf3de19933..d118ddece7 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Tags/EntityTagAdminAppService.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Tags/EntityTagAdminAppService.cs @@ -31,7 +31,7 @@ namespace Volo.CmsKit.Admin.Tags { var definition = await TagDefinitionStore.GetTagEntityTypeDefinitionAsync(input.EntityType); - await CheckPolicyAsync(definition.CreatePolicy); + await CheckAnyOfPoliciesAsync(definition.CreatePolicies); var tag = await TagManager.GetOrAddAsync(input.EntityType, input.TagName); @@ -46,7 +46,7 @@ namespace Volo.CmsKit.Admin.Tags { var definition = await TagDefinitionStore.GetTagEntityTypeDefinitionAsync(input.EntityType); - await CheckPolicyAsync(definition.DeletePolicy); + await CheckAnyOfPoliciesAsync(definition.DeletePolicies); await EntityTagManager.RemoveTagFromEntityAsync( input.TagId, @@ -59,7 +59,7 @@ namespace Volo.CmsKit.Admin.Tags { var definition = await TagDefinitionStore.GetTagEntityTypeDefinitionAsync(input.EntityType); - await CheckPolicyAsync(definition.UpdatePolicy); + await CheckAnyOfPoliciesAsync(definition.UpdatePolicies); await EntityTagManager.SetEntityTagsAsync(input.EntityType, input.EntityId, input.Tags); } diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/MediaDescriptorDefinition.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/MediaDescriptorDefinition.cs index 073ed694f6..95bfaacb61 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/MediaDescriptorDefinition.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/MediaDescriptorDefinition.cs @@ -1,24 +1,20 @@ using JetBrains.Annotations; using System; +using System.Collections.Generic; namespace Volo.CmsKit.MediaDescriptors { - public class MediaDescriptorDefinition : PolicySpecifiedDefinition, IEquatable + public class MediaDescriptorDefinition : PolicySpecifiedDefinition { public MediaDescriptorDefinition( [NotNull] string entityType, - [CanBeNull] string createPolicy = null, - [CanBeNull] string updatePolicy = null, - [CanBeNull] string deletePolicy = null) : base(entityType, - createPolicy, - updatePolicy, - deletePolicy) + IEnumerable createPolicies = null, + IEnumerable updatePolicies = null, + IEnumerable deletePolicies = null) : base(entityType, + createPolicies, + updatePolicies, + deletePolicies) { } - - public bool Equals(MediaDescriptorDefinition other) - { - return base.Equals(other); - } } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/PolicySpecifiedDefinition.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/PolicySpecifiedDefinition.cs index 2a5f69b69e..edfee13337 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/PolicySpecifiedDefinition.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/PolicySpecifiedDefinition.cs @@ -1,5 +1,7 @@ using JetBrains.Annotations; using System; +using System.Collections.Generic; +using System.Linq; using Volo.Abp; namespace Volo.CmsKit @@ -12,27 +14,39 @@ namespace Volo.CmsKit public PolicySpecifiedDefinition( [NotNull] string entityType, - [CanBeNull] string createPolicy = null, - [CanBeNull] string updatePolicy = null, - [CanBeNull] string deletePolicy = null) + IEnumerable createPolicies = null, + IEnumerable updatePolicies = null, + IEnumerable deletePolicies = null) { EntityType = Check.NotNullOrEmpty(entityType, nameof(entityType)); - CreatePolicy = createPolicy; - DeletePolicy = deletePolicy; - UpdatePolicy = updatePolicy; + + if (createPolicies != null) + { + CreatePolicies = CreatePolicies.Concat(createPolicies).ToList(); + } + + if (updatePolicies != null) + { + UpdatePolicies = UpdatePolicies.Concat(updatePolicies).ToList(); + } + + if (deletePolicies != null) + { + DeletePolicies = DeletePolicies.Concat(deletePolicies).ToList(); + } } [NotNull] public string EntityType { get; set; } - [CanBeNull] - public virtual string CreatePolicy { get; set; } + [NotNull] + public virtual ICollection CreatePolicies { get; } = new List(); - [CanBeNull] - public virtual string UpdatePolicy { get; set; } + [NotNull] + public virtual ICollection UpdatePolicies { get; } = new List(); - [CanBeNull] - public virtual string DeletePolicy { get; set; } + [NotNull] + public virtual ICollection DeletePolicies { get; } = new List(); public bool Equals(PolicySpecifiedDefinition other) { diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/TagEntityTypeDefiniton.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/TagEntityTypeDefiniton.cs index a6fb4cde7a..6dedb1410e 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/TagEntityTypeDefiniton.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/TagEntityTypeDefiniton.cs @@ -1,5 +1,6 @@ using JetBrains.Annotations; using System; +using System.Collections.Generic; using Volo.Abp; using Volo.Abp.Localization; @@ -17,12 +18,12 @@ namespace Volo.CmsKit.Tags public TagEntityTypeDefiniton( [NotNull] string entityType, [CanBeNull] ILocalizableString displayName = null, - [CanBeNull] string createPolicy = null, - [CanBeNull] string updatePolicy = null, - [CanBeNull] string deletePolicy = null) : base(entityType, createPolicy, updatePolicy, deletePolicy) + IEnumerable createPolicies = null, + IEnumerable updatePolicies = null, + IEnumerable deletePolicies = null) : base(entityType, createPolicies, updatePolicies, deletePolicies) { - EntityType = Check.NotNullOrWhiteSpace(entityType, nameof(entityType)); DisplayName = displayName; + } public bool Equals(TagEntityTypeDefiniton other) diff --git a/modules/cms-kit/test/Volo.CmsKit.Domain.Tests/Tags/TagEntityTypeDefinitionDictionary_Tests.cs b/modules/cms-kit/test/Volo.CmsKit.Domain.Tests/Tags/TagEntityTypeDefinitionDictionary_Tests.cs index a405c27190..2f2013cd0b 100644 --- a/modules/cms-kit/test/Volo.CmsKit.Domain.Tests/Tags/TagEntityTypeDefinitionDictionary_Tests.cs +++ b/modules/cms-kit/test/Volo.CmsKit.Domain.Tests/Tags/TagEntityTypeDefinitionDictionary_Tests.cs @@ -42,11 +42,11 @@ namespace Volo.CmsKit.Tags { cmsKitTagOptions.EntityTypes.Add( new TagEntityTypeDefiniton( - "My.Entity.Type", + "My.Entity.Type", LocalizableString.Create("MyEntity"), - "SomeCreatePolicy", - "SomeUpdatePolicy", - "SomeDeletePolicy" + new[] { "SomeCreatePolicy" }, + new[] { "SomeUpdatePolicy" }, + new[] { "SomeDeletePolicy" } )); } From 40bab24cca024ecf18ef0b4c0c533be7a277caf7 Mon Sep 17 00:00:00 2001 From: enisn Date: Tue, 2 Mar 2021 13:23:17 +0300 Subject: [PATCH 23/43] CmsKit - Remove Media Permissions --- .../Permissions/CmsKitAdminPermissionDefinitionProvider.cs | 7 ------- .../Volo/CmsKit/Permissions/CmsKitAdminPermissions.cs | 7 ------- .../MediaDescriptors/MediaDescriptorAdminAppService.cs | 3 --- .../MediaDescriptors/MediaDescriptorAdminController.cs | 4 ---- 4 files changed, 21 deletions(-) diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Permissions/CmsKitAdminPermissionDefinitionProvider.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Permissions/CmsKitAdminPermissionDefinitionProvider.cs index ccb4d1048b..a8317b6b6b 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Permissions/CmsKitAdminPermissionDefinitionProvider.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Permissions/CmsKitAdminPermissionDefinitionProvider.cs @@ -56,13 +56,6 @@ namespace Volo.CmsKit.Permissions blogManagement.AddChild(CmsKitAdminPermissions.BlogPosts.Update, L("Permission:BlogPostManagement.Update")); blogManagement.AddChild(CmsKitAdminPermissions.BlogPosts.Delete, L("Permission:BlogPostManagement.Delete")); } - - if (GlobalFeatureManager.Instance.IsEnabled()) - { - var mediaDescriptorManagement = cmsGroup.AddPermission(CmsKitAdminPermissions.MediaDescriptors.Default, L("Permission:MediaDescriptorManagement")); - mediaDescriptorManagement.AddChild(CmsKitAdminPermissions.MediaDescriptors.Create, L("Permission:MediaDescriptorManagement:Create")); - mediaDescriptorManagement.AddChild(CmsKitAdminPermissions.MediaDescriptors.Delete, L("Permission:MediaDescriptorManagement:Delete")); - } } private static LocalizableString L(string name) diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Permissions/CmsKitAdminPermissions.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Permissions/CmsKitAdminPermissions.cs index baa26b0fff..c524e7e6b5 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Permissions/CmsKitAdminPermissions.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Permissions/CmsKitAdminPermissions.cs @@ -52,12 +52,5 @@ namespace Volo.CmsKit.Permissions public const string Update = Default + ".Update"; public const string Delete = Default + ".Delete"; } - - public static class MediaDescriptors - { - public const string Default = GroupName + ".MediaDescriptors"; - public const string Create = Default + ".Create"; - public const string Delete = Default + ".Delete"; - } } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/MediaDescriptors/MediaDescriptorAdminAppService.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/MediaDescriptors/MediaDescriptorAdminAppService.cs index ba2b1a70b5..45774ca1f2 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/MediaDescriptors/MediaDescriptorAdminAppService.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/MediaDescriptors/MediaDescriptorAdminAppService.cs @@ -10,7 +10,6 @@ using Volo.CmsKit.Permissions; namespace Volo.CmsKit.Admin.MediaDescriptors { [RequiresGlobalFeature(typeof(MediaFeature))] - [Authorize(CmsKitAdminPermissions.MediaDescriptors.Default)] public class MediaDescriptorAdminAppService : CmsKitAdminAppServiceBase, IMediaDescriptorAdminAppService { protected IBlobContainer MediaContainer { get; } @@ -30,7 +29,6 @@ namespace Volo.CmsKit.Admin.MediaDescriptors MediaDescriptorDefinitionStore = mediaDescriptorDefinitionStore; } - [Authorize(CmsKitAdminPermissions.MediaDescriptors.Create)] public virtual async Task CreateAsync(CreateMediaInputStream inputStream) { var definition = await MediaDescriptorDefinitionStore.GetDefinitionAsync(inputStream.EntityType); @@ -49,7 +47,6 @@ namespace Volo.CmsKit.Admin.MediaDescriptors } } - [Authorize(CmsKitAdminPermissions.MediaDescriptors.Delete)] public virtual async Task DeleteAsync(Guid id) { var mediaDescriptor = await MediaDescriptorRepository.GetAsync(id); diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi/Volo/CmsKit/Admin/MediaDescriptors/MediaDescriptorAdminController.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi/Volo/CmsKit/Admin/MediaDescriptors/MediaDescriptorAdminController.cs index 124cfcba1c..91cd5170a5 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi/Volo/CmsKit/Admin/MediaDescriptors/MediaDescriptorAdminController.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi/Volo/CmsKit/Admin/MediaDescriptors/MediaDescriptorAdminController.cs @@ -13,7 +13,6 @@ namespace Volo.CmsKit.Admin.MediaDescriptors { [RequiresGlobalFeature(typeof(MediaFeature))] [RemoteService(Name = CmsKitCommonRemoteServiceConsts.RemoteServiceName)] - [Authorize(CmsKitAdminPermissions.MediaDescriptors.Default)] [Area("cms-kit")] [Route("api/cms-kit-admin/media")] public class MediaDescriptorAdminController : CmsKitAdminController, IMediaDescriptorAdminAppService @@ -26,7 +25,6 @@ namespace Volo.CmsKit.Admin.MediaDescriptors } [HttpPost] - [Authorize(CmsKitAdminPermissions.MediaDescriptors.Create)] [NonAction] public virtual Task CreateAsync(CreateMediaInputStream inputStream) { @@ -35,7 +33,6 @@ namespace Volo.CmsKit.Admin.MediaDescriptors [HttpDelete] [Route("{id}")] - [Authorize(CmsKitAdminPermissions.MediaDescriptors.Delete)] public virtual Task DeleteAsync(Guid id) { return MediaDescriptorAdminAppService.DeleteAsync(id); @@ -43,7 +40,6 @@ namespace Volo.CmsKit.Admin.MediaDescriptors [HttpPost] [Route("{entityType}")] - [Authorize(CmsKitAdminPermissions.MediaDescriptors.Create)] public virtual async Task UploadAsync(string entityType, IFormFile file) { if (file == null) From 0d9b602f7e5454822abdb6d0f1d5a1323724cc63 Mon Sep 17 00:00:00 2001 From: enisn Date: Tue, 2 Mar 2021 13:23:30 +0300 Subject: [PATCH 24/43] CmsKit - Add policies to test data --- .../test/Volo.CmsKit.TestBase/CmsKitDataSeedContributor.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitDataSeedContributor.cs b/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitDataSeedContributor.cs index e2c928be2a..8fd8b14f46 100644 --- a/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitDataSeedContributor.cs +++ b/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitDataSeedContributor.cs @@ -136,7 +136,11 @@ namespace Volo.CmsKit _tagOptions.Value.EntityTypes.AddIfNotContains(new TagEntityTypeDefiniton(_cmsKitTestData.Content_2_EntityType)); _tagOptions.Value.EntityTypes.AddIfNotContains(new TagEntityTypeDefiniton(_cmsKitTestData.TagDefinition_1_EntityType)); - _mediaOptions.Value.EntityTypes.AddIfNotContains(new MediaDescriptorDefinition(_cmsKitTestData.Media_1_EntityType)); + _mediaOptions.Value.EntityTypes.AddIfNotContains( + new MediaDescriptorDefinition( + _cmsKitTestData.Media_1_EntityType, + createPolicies: new[] { "SomeCreatePolicy" }, + deletePolicies: new[] { "SomeDeletePolicy" })); return Task.CompletedTask; } From 5e32bfd579220ae0779032b5ce13e84d789789e8 Mon Sep 17 00:00:00 2001 From: enisn Date: Tue, 2 Mar 2021 13:29:07 +0300 Subject: [PATCH 25/43] CmsKit - Refactoring for DefaultMediaDescriptorDefinitionStore --- .../MediaDescriptors/DefaultMediaDescriptorDefinitionStore.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/DefaultMediaDescriptorDefinitionStore.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/DefaultMediaDescriptorDefinitionStore.cs index 4bdd4d6e98..5a1553c3fc 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/DefaultMediaDescriptorDefinitionStore.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/DefaultMediaDescriptorDefinitionStore.cs @@ -25,7 +25,7 @@ namespace Volo.CmsKit.MediaDescriptors /// EntityType to get definition. /// Thrown when EntityType is not configured as taggable. /// More than one element satisfies the condition in predicate. - public Task GetDefinitionAsync([NotNull] string entityType) + public virtual Task GetDefinitionAsync([NotNull] string entityType) { Check.NotNullOrWhiteSpace(entityType, nameof(entityType)); @@ -35,7 +35,7 @@ namespace Volo.CmsKit.MediaDescriptors return Task.FromResult(result); } - public Task IsDefinedAsync([NotNull] string entityType) + public virtual Task IsDefinedAsync([NotNull] string entityType) { Check.NotNullOrEmpty(entityType, nameof(entityType)); From cf8986a13d4fcef4bd784c6754171c78ace6fac9 Mon Sep 17 00:00:00 2001 From: Zony Date: Tue, 2 Mar 2021 21:21:32 +0800 Subject: [PATCH 26/43] Improve the RabbitMQ documentation. --- docs/en/Background-Jobs-RabbitMq.md | 20 +++++++++++++++++ ...tributed-Event-Bus-RabbitMQ-Integration.md | 22 ++++++++++++++++++- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/docs/en/Background-Jobs-RabbitMq.md b/docs/en/Background-Jobs-RabbitMq.md index 2ee478ed17..16b8b0981a 100644 --- a/docs/en/Background-Jobs-RabbitMq.md +++ b/docs/en/Background-Jobs-RabbitMq.md @@ -66,6 +66,26 @@ Defining multiple connections is allowed. In this case, you can use different co } ```` +If you need to connect to the RabbitMQ cluster, you can use the `;` character to separate the host names. + +**Example: Connect to the RabbitMQ cluster** + +```json +{ + "RabbitMQ": { + "Connections": { + "Default": { + "HostName": "123.123.123.123;234.234.234.234" + } + }, + "EventBus": { + "ClientName": "MyClientName", + "ExchangeName": "MyExchangeName" + } + } +} +``` + #### AbpRabbitMqOptions `AbpRabbitMqOptions` class can be used to configure the connection strings for the RabbitMQ. You can configure this options inside the `ConfigureServices` of your [module](Module-Development-Basics.md). diff --git a/docs/en/Distributed-Event-Bus-RabbitMQ-Integration.md b/docs/en/Distributed-Event-Bus-RabbitMQ-Integration.md index 9587a111bf..3d99e1e3cf 100644 --- a/docs/en/Distributed-Event-Bus-RabbitMQ-Integration.md +++ b/docs/en/Distributed-Event-Bus-RabbitMQ-Integration.md @@ -90,7 +90,7 @@ You can use any of the [ConnectionFactry](http://rabbitmq.github.io/rabbitmq-dot **Example: Specify the connection port** -````csharp +````json { "RabbitMQ": { "Connections": { @@ -103,6 +103,26 @@ You can use any of the [ConnectionFactry](http://rabbitmq.github.io/rabbitmq-dot } ```` +If you need to connect to the RabbitMQ cluster, you can use the `;` character to separate the host names. + +**Example: Connect to the RabbitMQ cluster** + +```json +{ + "RabbitMQ": { + "Connections": { + "Default": { + "HostName": "123.123.123.123;234.234.234.234" + } + }, + "EventBus": { + "ClientName": "MyClientName", + "ExchangeName": "MyExchangeName" + } + } +} +``` + ### The Options Classes `AbpRabbitMqOptions` and `AbpRabbitMqEventBusOptions` classes can be used to configure the connection strings and event bus options for the RabbitMQ. From d43370e0fb3a12847f83aa3f0a1c2be7439eb75f Mon Sep 17 00:00:00 2001 From: Arman Ozak Date: Tue, 2 Mar 2021 16:40:16 +0300 Subject: [PATCH 27/43] make url and rootNamespace optional for api config --- npm/ng-packs/packages/core/src/lib/models/environment.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/npm/ng-packs/packages/core/src/lib/models/environment.ts b/npm/ng-packs/packages/core/src/lib/models/environment.ts index b29f0d338d..da04384cab 100644 --- a/npm/ng-packs/packages/core/src/lib/models/environment.ts +++ b/npm/ng-packs/packages/core/src/lib/models/environment.ts @@ -21,12 +21,11 @@ export interface ApplicationInfo { export type ApiConfig = { [key: string]: string; url: string; -} & Partial<{ rootNamespace: string; -}>; +}; export interface Apis { - [key: string]: ApiConfig; + [key: string]: Partial; default: ApiConfig; } From 55fa6468744570e743cb1167509b1aba871a25ac Mon Sep 17 00:00:00 2001 From: Arman Ozak Date: Tue, 2 Mar 2021 16:40:35 +0300 Subject: [PATCH 28/43] remove url from dev-app api configs --- npm/ng-packs/apps/dev-app/src/environments/environment.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/npm/ng-packs/apps/dev-app/src/environments/environment.ts b/npm/ng-packs/apps/dev-app/src/environments/environment.ts index 87ca8d1447..771698e394 100644 --- a/npm/ng-packs/apps/dev-app/src/environments/environment.ts +++ b/npm/ng-packs/apps/dev-app/src/environments/environment.ts @@ -23,23 +23,18 @@ export const environment = { rootNamespace: 'MyCompanyName.MyProjectName', }, AbpFeatureManagement: { - url: 'https://localhost:44305', rootNamespace: 'Volo.Abp', }, AbpPermissionManagement: { - url: 'https://localhost:44305', rootNamespace: 'Volo.Abp.PermissionManagement', }, AbpTenantManagement: { - url: 'https://localhost:44305', rootNamespace: 'Volo.Abp.TenantManagement', }, AbpIdentity: { - url: 'https://localhost:44305', rootNamespace: 'Volo.Abp', }, AbpSettingManagement: { - url: 'https://localhost:44305', rootNamespace: 'Volo.Abp.SettingManagement', }, }, From 3b633f631e818532853e2983eb003f1628ecfc7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Tue, 2 Mar 2021 16:44:27 +0300 Subject: [PATCH 29/43] Static property must be initialized in static constructor. --- .../Configuration/AbpApiProxyScriptingOptions.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/framework/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Configuration/AbpApiProxyScriptingOptions.cs b/framework/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Configuration/AbpApiProxyScriptingOptions.cs index 21bc6783bb..76c7dc74f5 100644 --- a/framework/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Configuration/AbpApiProxyScriptingOptions.cs +++ b/framework/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Configuration/AbpApiProxyScriptingOptions.cs @@ -10,10 +10,8 @@ namespace Volo.Abp.Http.ProxyScripting.Configuration public static Func PropertyNameGenerator { get; set; } - public AbpApiProxyScriptingOptions() + static AbpApiProxyScriptingOptions() { - Generators = new Dictionary(); - PropertyNameGenerator = propertyInfo => { var jsonPropertyNameAttribute = propertyInfo.GetSingleAttributeOrNull(true); @@ -33,5 +31,10 @@ namespace Volo.Abp.Http.ProxyScripting.Configuration return null; }; } + + public AbpApiProxyScriptingOptions() + { + Generators = new Dictionary(); + } } } \ No newline at end of file From dc83964fe829ba7662cc8c499e0d4efec8e329c6 Mon Sep 17 00:00:00 2001 From: Arman Ozak Date: Tue, 2 Mar 2021 16:57:27 +0300 Subject: [PATCH 30/43] deprecate legacy object extensions types --- .../lib/models/internal/object-extensions.ts | 143 ------------------ 1 file changed, 143 deletions(-) diff --git a/npm/ng-packs/packages/theme-shared/extensions/src/lib/models/internal/object-extensions.ts b/npm/ng-packs/packages/theme-shared/extensions/src/lib/models/internal/object-extensions.ts index 448183b8a0..ea8a9dad8f 100644 --- a/npm/ng-packs/packages/theme-shared/extensions/src/lib/models/internal/object-extensions.ts +++ b/npm/ng-packs/packages/theme-shared/extensions/src/lib/models/internal/object-extensions.ts @@ -106,146 +106,3 @@ export interface PropContributors { createForm: PropContributorCallbacks>; editForm: PropContributorCallbacks>; } - -/** - * @deprecated To be deleted in v4.2. - */ -export interface Config { - objectExtensions: Item; -} - -/** - * @deprecated Use ObjectExtensionsDto. To be deleted in v4.2. - */ -export interface Item { - modules: Modules; - enums: Enums; -} - -/** - * @deprecated Use Record. To be deleted in v4.2. - */ -export type Modules = Configuration; - -/** - * @deprecated Use ModuleExtensionDto. To be deleted in v4.2. - */ -export interface Module { - configuration: Configuration; - entities: Entities; -} - -/** - * @deprecated Use Record. To be deleted in v4.2. - */ -export type Entities = Configuration; - -/** - * @deprecated Use EntityExtensionDto. To be deleted in v4.2. - */ -export interface Entity { - configuration: Configuration; - properties: Properties; -} - -/** - * @deprecated Use Record. To be deleted in v4.2. - */ -export type Properties = Configuration; - -/** - * @deprecated Use ExtensionPropertyDto. To be deleted in v4.2. - */ -export interface Property { - type: string; - typeSimple: ePropType; - displayName: DisplayName | null; - api: Api; - ui: Ui; - attributes: Attribute[]; - configuration: Configuration; - defaultValue?: any; -} - -/** - * @deprecated Use LocalizableStringDto. To be deleted in v4.2. - */ -export interface DisplayName { - name: string; - resource: string; -} - -/** - * @deprecated Use ExtensionPropertyApiDto. To be deleted in v4.2. - */ -export interface Api { - onGet: ApiConfig; - onCreate: ApiConfig; - onUpdate: ApiConfig; -} - -/** - * @deprecated Use ExtensionPropertyApiCreateDto. To be deleted in v4.2. - */ -export interface ApiConfig { - isAvailable: boolean; -} - -/** - * @deprecated Use ExtensionPropertyUiDto. To be deleted in v4.2. - */ -export interface Ui { - onTable: UiPropConfig; - onCreateForm: UiFormConfig; - onEditForm: UiFormConfig; -} - -/** - * @deprecated Use ExtensionPropertyUiTableDto. To be deleted in v4.2. - */ -export interface UiPropConfig { - isSortable: boolean; - isVisible: boolean; -} - -/** - * @deprecated Use ExtensionPropertyUiFormDto. To be deleted in v4.2. - */ -export interface UiFormConfig { - isVisible: boolean; -} - -/** - * @deprecated Use ExtensionPropertyAttributeDto. To be deleted in v4.2. - */ -export interface Attribute { - typeSimple: string; - config: Configuration; -} - -/** - * @deprecated To be deleted in v4.2. - */ -export type Configuration = Record; - -/** - * @deprecated Use Record. To be deleted in v4.2. - */ -export type Enums = Record; - -/** - * @deprecated Use ExtensionEnumDto. To be deleted in v4.2. - */ -export interface Enum { - fields: EnumMember[]; - localizationResource?: string; - transformed?: any; -} - -/** - * @deprecated Use ExtensionEnumFieldDto. To be deleted in v4.2. - */ -export interface EnumMember { - name: string; - value: any; -} From bce5c1660dd94a16934afb6b482585b07951e245 Mon Sep 17 00:00:00 2001 From: Arman Ozak Date: Tue, 2 Mar 2021 17:00:45 +0300 Subject: [PATCH 31/43] make ApiConfig interface instead of type literal --- npm/ng-packs/packages/core/src/lib/models/environment.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/npm/ng-packs/packages/core/src/lib/models/environment.ts b/npm/ng-packs/packages/core/src/lib/models/environment.ts index da04384cab..dc5fab9ef7 100644 --- a/npm/ng-packs/packages/core/src/lib/models/environment.ts +++ b/npm/ng-packs/packages/core/src/lib/models/environment.ts @@ -18,11 +18,11 @@ export interface ApplicationInfo { logoUrl?: string; } -export type ApiConfig = { +export interface ApiConfig { [key: string]: string; url: string; rootNamespace: string; -}; +} export interface Apis { [key: string]: Partial; From 17ad81cbd9301f6f7ebcaf6130830ee862deef7e Mon Sep 17 00:00:00 2001 From: enisn Date: Tue, 2 Mar 2021 17:03:33 +0300 Subject: [PATCH 32/43] CmsKit - Remove required permissions from AbpAuthorizationException --- .../Volo/CmsKit/Admin/CmsKitAdminAppServiceBase.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/CmsKitAdminAppServiceBase.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/CmsKitAdminAppServiceBase.cs index 83d36431c8..9d4c109698 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/CmsKitAdminAppServiceBase.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/CmsKitAdminAppServiceBase.cs @@ -33,11 +33,7 @@ namespace Volo.CmsKit.Admin } } - var exception = new AbpAuthorizationException(); - - exception.Data[nameof(policies)] = policies; - - throw exception; + throw new AbpAuthorizationException(); } } } From d0a52b1b1e16a717c864e77f3b0a8522c53d587c Mon Sep 17 00:00:00 2001 From: Arman Ozak Date: Tue, 2 Mar 2021 17:04:26 +0300 Subject: [PATCH 33/43] make rootNamespace optional again --- npm/ng-packs/packages/core/src/lib/models/environment.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/npm/ng-packs/packages/core/src/lib/models/environment.ts b/npm/ng-packs/packages/core/src/lib/models/environment.ts index dc5fab9ef7..46596685b0 100644 --- a/npm/ng-packs/packages/core/src/lib/models/environment.ts +++ b/npm/ng-packs/packages/core/src/lib/models/environment.ts @@ -21,7 +21,7 @@ export interface ApplicationInfo { export interface ApiConfig { [key: string]: string; url: string; - rootNamespace: string; + rootNamespace?: string; } export interface Apis { From 272361159d7ee4e0d7fb1b41cfddbf95a03fdc0f Mon Sep 17 00:00:00 2001 From: Arman Ozak Date: Tue, 2 Mar 2021 17:04:45 +0300 Subject: [PATCH 34/43] update environment service to handle optional urls --- .../src/lib/services/environment.service.ts | 9 +++--- .../src/lib/tests/environment.service.spec.ts | 31 +++++++++++++------ 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/npm/ng-packs/packages/core/src/lib/services/environment.service.ts b/npm/ng-packs/packages/core/src/lib/services/environment.service.ts index 2062294b7b..10d7664acb 100644 --- a/npm/ng-packs/packages/core/src/lib/services/environment.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/environment.service.ts @@ -4,6 +4,9 @@ import { map } from 'rxjs/operators'; import { Apis, Environment } from '../models/environment'; import { InternalStore } from '../utils/internal-store-utils'; +const mapToApiUrl = (key: string) => (apis: Apis) => + (apis[key] || apis.default).url || apis.default.url; + @Injectable({ providedIn: 'root' }) export class EnvironmentService { private readonly store = new InternalStore({} as Environment); @@ -21,13 +24,11 @@ export class EnvironmentService { } getApiUrl(key?: string) { - return (this.store.state.apis[key || 'default'] || this.store.state.apis.default).url; + return mapToApiUrl(key)(this.store.state.apis); } getApiUrl$(key?: string) { - return this.store - .sliceState(state => state.apis) - .pipe(map((apis: Apis) => (apis[key || 'default'] || apis.default).url)); + return this.store.sliceState(state => state.apis).pipe(map(mapToApiUrl(key))); } setState(environment: Environment) { diff --git a/npm/ng-packs/packages/core/src/lib/tests/environment.service.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/environment.service.spec.ts index fe9f50a546..95c2b58a6c 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/environment.service.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/environment.service.spec.ts @@ -1,3 +1,4 @@ +import { waitForAsync } from '@angular/core/testing'; import { createServiceFactory, SpectatorService } from '@ngneat/spectator/jest'; import { Environment } from '../models'; import { EnvironmentService } from '../services'; @@ -17,6 +18,7 @@ export const ENVIRONMENT_DATA = ({ other: { url: 'https://localhost:44306', }, + yetAnother: {}, }, localization: { defaultResourceName: 'MyProjectName', @@ -39,19 +41,28 @@ describe('ConfigState', () => { }); describe('#getEnvironment', () => { - it('should return ENVIRONMENT_DATA', () => { - expect(environment.getEnvironment()).toEqual(ENVIRONMENT_DATA); - environment.getEnvironment$().subscribe(data => expect(data).toEqual(ENVIRONMENT_DATA)); - }); + it( + 'should return ENVIRONMENT_DATA', + waitForAsync(() => { + expect(environment.getEnvironment()).toEqual(ENVIRONMENT_DATA); + environment.getEnvironment$().subscribe(data => expect(data).toEqual(ENVIRONMENT_DATA)); + }), + ); }); describe('#getApiUrl', () => { - it('should return api url', () => { - expect(environment.getApiUrl()).toEqual(ENVIRONMENT_DATA.apis.default.url); - environment - .getApiUrl$('other') - .subscribe(data => expect(data).toEqual(ENVIRONMENT_DATA.apis.other.url)); - }); + it( + 'should return api url', + waitForAsync(() => { + expect(environment.getApiUrl()).toEqual(ENVIRONMENT_DATA.apis.default.url); + environment + .getApiUrl$('other') + .subscribe(data => expect(data).toEqual(ENVIRONMENT_DATA.apis.other.url)); + environment + .getApiUrl$('yetAnother') + .subscribe(data => expect(data).toEqual(ENVIRONMENT_DATA.apis.default.url)); + }), + ); }); // TODO: create permission.service.spec.ts From b7217ab9127be8184e32c718a01e39c406b56630 Mon Sep 17 00:00:00 2001 From: enisn Date: Tue, 2 Mar 2021 17:06:43 +0300 Subject: [PATCH 35/43] CmsKit - Add Create & Update policies to delete Media & Tags --- .../Admin/CmsKitAdminApplicationModule.cs | 44 ++++++++++++++++--- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/CmsKitAdminApplicationModule.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/CmsKitAdminApplicationModule.cs index 95dbe44a04..17c242755e 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/CmsKitAdminApplicationModule.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/CmsKitAdminApplicationModule.cs @@ -46,9 +46,21 @@ namespace Volo.CmsKit.Admin new TagEntityTypeDefiniton( BlogPostConsts.EntityType, LocalizableString.Create("BlogPost"), - new[] { CmsKitAdminPermissions.BlogPosts.Create, CmsKitAdminPermissions.BlogPosts.Update }, - new[] { CmsKitAdminPermissions.BlogPosts.Create, CmsKitAdminPermissions.BlogPosts.Update }, - new[] { CmsKitAdminPermissions.BlogPosts.Create, CmsKitAdminPermissions.BlogPosts.Update })); + createPolicies: new[] + { + CmsKitAdminPermissions.BlogPosts.Create, + CmsKitAdminPermissions.BlogPosts.Update + }, + updatePolicies: new[] + { + CmsKitAdminPermissions.BlogPosts.Create, + CmsKitAdminPermissions.BlogPosts.Update + }, + deletePolicies: new[] + { + CmsKitAdminPermissions.BlogPosts.Create, + CmsKitAdminPermissions.BlogPosts.Update + })); } }); @@ -61,8 +73,17 @@ namespace Volo.CmsKit.Admin options.EntityTypes.AddIfNotContains( new MediaDescriptorDefinition( BlogPostConsts.EntityType, - createPolicies: new[] { CmsKitAdminPermissions.BlogPosts.Create, CmsKitAdminPermissions.BlogPosts.Update }, - deletePolicies: new[] { CmsKitAdminPermissions.BlogPosts.Delete })); + createPolicies: new[] + { + CmsKitAdminPermissions.BlogPosts.Create, + CmsKitAdminPermissions.BlogPosts.Update + }, + deletePolicies: new[] + { + CmsKitAdminPermissions.BlogPosts.Create, + CmsKitAdminPermissions.BlogPosts.Update, + CmsKitAdminPermissions.BlogPosts.Delete + })); } if (GlobalFeatureManager.Instance.IsEnabled()) @@ -70,8 +91,17 @@ namespace Volo.CmsKit.Admin options.EntityTypes.AddIfNotContains( new MediaDescriptorDefinition( PageConsts.EntityType, - createPolicies: new[] { CmsKitAdminPermissions.Pages.Create, CmsKitAdminPermissions.Pages.Update }, - deletePolicies: new[] { CmsKitAdminPermissions.Pages.Delete })); + createPolicies: new[] + { + CmsKitAdminPermissions.Pages.Create, + CmsKitAdminPermissions.Pages.Update + }, + deletePolicies: new[] + { + CmsKitAdminPermissions.Pages.Create, + CmsKitAdminPermissions.Pages.Update, + CmsKitAdminPermissions.Pages.Delete + })); } }); } From 12da9394e6932c972d50c6693da4bdc523ca98df Mon Sep 17 00:00:00 2001 From: Zony Date: Tue, 2 Mar 2021 22:47:03 +0800 Subject: [PATCH 36/43] Improve RabbitMQ Chinese documentation. --- ...tributed-Event-Bus-RabbitMQ-Integration.md | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/docs/zh-Hans/Distributed-Event-Bus-RabbitMQ-Integration.md b/docs/zh-Hans/Distributed-Event-Bus-RabbitMQ-Integration.md index 2a1810e309..ab7cb68bd7 100644 --- a/docs/zh-Hans/Distributed-Event-Bus-RabbitMQ-Integration.md +++ b/docs/zh-Hans/Distributed-Event-Bus-RabbitMQ-Integration.md @@ -103,6 +103,26 @@ } ```` +如果需要连接到 RabbitMQ 集群,你可以指定多个 HostName。 + +**示例: 连接到 RabbitMQ 集群** + +```json +{ + "RabbitMQ": { + "Connections": { + "Default": { + "HostName": "123.123.123.123;234.234.234.234" + } + }, + "EventBus": { + "ClientName": "MyClientName", + "ExchangeName": "MyExchangeName" + } + } +} +``` + ### 选项类 `AbpRabbitMqOptions` 和 `AbpRabbitMqEventBusOptions` 类用于配置RabbitMQ的连接字符串和事件总线选项. From e4b7a6adc0473795140f250fb2843a60eba31158 Mon Sep 17 00:00:00 2001 From: Zony Date: Tue, 2 Mar 2021 22:48:48 +0800 Subject: [PATCH 37/43] Submit the `RabbitMQ Background Job Manager` Chinese document. --- docs/zh-Hans/Background-Jobs-RabbitMq.md | 154 ++++++++++++++++++++++- 1 file changed, 152 insertions(+), 2 deletions(-) diff --git a/docs/zh-Hans/Background-Jobs-RabbitMq.md b/docs/zh-Hans/Background-Jobs-RabbitMq.md index 979beec872..45c2978593 100644 --- a/docs/zh-Hans/Background-Jobs-RabbitMq.md +++ b/docs/zh-Hans/Background-Jobs-RabbitMq.md @@ -1,3 +1,153 @@ -# RabbitMQ Background Job Manager +# RabbitMQ 后台作业管理 -待添加 \ No newline at end of file +RabbitMQ 是一个标准的消息队列中间件,虽然它常用于消息传递/分布式事件,但也非常适合存储 FIFO(先进先出) 顺序的后台作业。 + +ABP Framework 提供了 [Volo.Abp.BackgroundJobs.RabbitMQ](https://www.nuget.org/packages/Volo.Abp.BackgroundJobs.RabbitMQ) 包,将使用 RabbitMQ 来执行后台作业。 + +> 参阅 [后台作业文档](Background-Jobs.md) 学习如何使用后台作业系统,本文只介绍了如何安装和配置 RabbitMQ 集成。 + +## 安装 + +使用 ABP CLI 将 [Volo.Abp.BackgroundJobs.RabbitMQ](https://www.nuget.org/packages/Volo.Abp.BackgroundJobs.RabbitMQ) 包添加到你的项目: + +- 如果之前没有安装过 [ABP CLI](https://docs.abp.io/en/abp/latest/CLI),请先安装它。 +- 跳转到待安装后台作业管理的项目目录中(包含 `.csproj` 文件的目录),打开终端管理器。 +- 执行 `abp add-package Volo.Abp.BackgroundJobs.RabbitMQ` 命令。 + +如果你想要手动安装,请先用 NuGet 包管理器安装 [Volo.Abp.BackgroundJobs.RabbitMQ](https://www.nuget.org/packages/Volo.Abp.BackgroundJobs.RabbitMQ) 包到指定项目,之后使在你的 [模块](Module-Development-Basics.md) 上面添加 `[DependsOn(typeof(AbpBackgroundJobsRabbitMqModule))]` 配置依赖。 + +## 配置 + +### 默认配置 + +默认配置将会使用标准端口和主机名(localhost)连接到 RabbitMQ 服务,**你不需要进行额外配置**。 + +### RabbitMQ 连接 + +你可以使用 ASP.NET Core 的 [标准配置系统](Configuration.md) 对 RabbitMQ 进行详细配置,比如 `appsettings.json` 或者是 [选项类](Options.md)。 + +#### 通过 `appsettings.json` 文件配置 + +这种方式是配置 RabbitMQ 连接最简单的方式,你可以使用其他的配置源(例如环境变量)。这些强大的功能都是由 [ASP.NET Core](https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/configuration/) 提供的支持。 + +**示例: 配置默认的 RabbitMQ 连接** + +```json +{ + "RabbitMQ": { + "Connections": { + "Default": { + "HostName": "123.123.123.123", + "Port": "5672" + } + } + } +} +``` + +你可以在配置文件使用所有 [ConnectionFactry](http://rabbitmq.github.io/rabbitmq-dotnet-client/api/RabbitMQ.Client.ConnectionFactory.html#properties) 的属性,关于这些属性的具体含义,可以查看 RabbitMQ 的 [官方文档](https://www.rabbitmq.com/dotnet-api-guide.html#exchanges-and-queues)。 + +目前我们允许定义多个连接,多连接的情况适用于不同的后台作业,具体配置信息可以参考下面的 RabbitMQ 后台作业配置说明。 + +**示例: 定义两个 RabbitMQ 连接** + +```json +{ + "RabbitMQ": { + "Connections": { + "Default": { + "HostName": "123.123.123.123" + }, + "SecondConnection": { + "HostName": "321.321.321.321" + } + } + } +} +``` + +如果需要连接到 RabbitMQ 集群,你可以指定多个 HostName。 + +**示例: 连接到 RabbitMQ 集群** + +```json +{ + "RabbitMQ": { + "Connections": { + "Default": { + "HostName": "123.123.123.123;234.234.234.234" + } + }, + "EventBus": { + "ClientName": "MyClientName", + "ExchangeName": "MyExchangeName" + } + } +} +``` + +#### 使用选项类 + +`AbpRabbitMqOptions` 类型用于配置 RabbitMQ 的连接字符串,你可以在 [模块](Module-Development-Basics.md) 的 `ConfigureService` 方法中进行配置。 + +**示例: 配置 RabbitMQ 连接** + +```csharp +Configure(options => +{ + options.Connections.Default.UserName = "user"; + options.Connections.Default.Password = "pass"; + options.Connections.Default.HostName = "123.123.123.123"; + options.Connections.Default.Port = 5672; +}); +``` + +关于选项类,可以结合 `appsettings.json` 文件一起使用。针对同一个属性,在选项类里面对该值进行了设定,会覆盖掉 `appsettings.json` 的值。 + +### RabbitMQ 后台作业配置说明 + +#### 后台作业队列的名称 + +默认情况下,每个后台作业都会使用一个单独的队列,结合标准前缀和作业名称来构造一个完整的队列名称。默认的前缀为 `AbpBackgroundJobs`,所以有一个作业的名称是 `EmailSending` 的话,在 RabbitMQ 的队列名称就是 `AbpBackgroundJobs.EmailSending`。 + +> 在后台作业的参数类上,可以使用 `BackgroundJobName` 特性指定后台作业的名称。否则的话,后台作业的名称将会是后台作业类的全名(Full Name)(也包含命名空间)。 + +#### 后台作业使用的连接 + +默认情况下,后台作业都会使用 `Default` 作为默认连接。 + +#### 自定义 + +`AbpRabbitMqBackgroundJobOptions` 可以自定义队列名和作业使用的 RabbitMQ 连接。 + +**示例: ** + +```csharp +Configure(options => +{ + options.DefaultQueueNamePrefix = "my_app_jobs."; + options.JobQueues[typeof(EmailSendingArgs)] = + new JobQueueConfiguration( + typeof(EmailSendingArgs), + queueName: "my_app_jobs.emails", + connectionName: "SecondConnection" + ); +}); +``` + +- 这个示例将默认的队列名前缀设置为 `my_app_jobs.`,如果多个项目都使用的同一个 RabbitMQ 服务,设置不同的前缀可以避免执行其他项目的后台作业。 +- 这里还设置了 `EmailSendingArgs` 绑定的 RabbitMQ 连接。 + +`JobQueueConfiguration` 类的构造函数中,还有一些其他的可选参数。 + +- `queueName`: 指定后台作业对应的队列名称(全名)。 +- `connectionName`: 后台作业对应的 RabbitMQ 连接名称,默认是 `Default`。 +- `durable`: 可选参数,默认为 `true`。 +- `exclusive`: 可选参数,默认为 `false`。 +- `autoDelete`: 可选参数,默认为 `false`。 + +如果你想要更多地了解 `durable`、`exclusive`、`autoDelete` 的用法,请阅读 RabbitMQ 提供的文档。 + +## 参考 + +- [后台作业](Background-Jobs.md) \ No newline at end of file From 36097d2e402df345eaf16b98ae9a577f81612f13 Mon Sep 17 00:00:00 2001 From: Ahmet Date: Tue, 2 Mar 2021 18:06:30 +0300 Subject: [PATCH 38/43] PropertyNameGenerator moved to static class --- .../Modeling/PropertyApiDescriptionModel.cs | 2 +- .../AbpApiPropertyNameConfiguration.cs | 32 +++++++++++++++++++ .../AbpApiProxyScriptingOptions.cs | 25 --------------- 3 files changed, 33 insertions(+), 26 deletions(-) create mode 100644 framework/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Configuration/AbpApiPropertyNameConfiguration.cs diff --git a/framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/PropertyApiDescriptionModel.cs b/framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/PropertyApiDescriptionModel.cs index 678798ae39..8eb307c630 100644 --- a/framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/PropertyApiDescriptionModel.cs +++ b/framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/PropertyApiDescriptionModel.cs @@ -24,7 +24,7 @@ namespace Volo.Abp.Http.Modeling return new PropertyApiDescriptionModel { Name = propertyInfo.Name, - JsonName = AbpApiProxyScriptingOptions.PropertyNameGenerator.Invoke(propertyInfo), + JsonName = AbpApiPropertyNameConfiguration.PropertyNameGenerator.Invoke(propertyInfo), Type = ApiTypeNameHelper.GetTypeName(propertyInfo.PropertyType), TypeSimple = ApiTypeNameHelper.GetSimpleTypeName(propertyInfo.PropertyType), IsRequired = propertyInfo.IsDefined(typeof(RequiredAttribute), true) diff --git a/framework/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Configuration/AbpApiPropertyNameConfiguration.cs b/framework/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Configuration/AbpApiPropertyNameConfiguration.cs new file mode 100644 index 0000000000..4320e9905f --- /dev/null +++ b/framework/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Configuration/AbpApiPropertyNameConfiguration.cs @@ -0,0 +1,32 @@ +using System; +using System.Reflection; + +namespace Volo.Abp.Http.ProxyScripting.Configuration +{ + public static class AbpApiPropertyNameConfiguration + { + public static Func PropertyNameGenerator { get; set; } + + static AbpApiPropertyNameConfiguration() + { + PropertyNameGenerator = propertyInfo => + { + var jsonPropertyNameAttribute = propertyInfo.GetSingleAttributeOrNull(true); + + if (jsonPropertyNameAttribute != null) + { + return jsonPropertyNameAttribute.Name; + } + + var jsonPropertyAttribute = propertyInfo.GetSingleAttributeOrNull(true); + + if (jsonPropertyAttribute != null) + { + return jsonPropertyAttribute.PropertyName; + } + + return null; + }; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Configuration/AbpApiProxyScriptingOptions.cs b/framework/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Configuration/AbpApiProxyScriptingOptions.cs index 76c7dc74f5..e698d00416 100644 --- a/framework/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Configuration/AbpApiProxyScriptingOptions.cs +++ b/framework/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Configuration/AbpApiProxyScriptingOptions.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Reflection; namespace Volo.Abp.Http.ProxyScripting.Configuration { @@ -8,30 +7,6 @@ namespace Volo.Abp.Http.ProxyScripting.Configuration { public IDictionary Generators { get; } - public static Func PropertyNameGenerator { get; set; } - - static AbpApiProxyScriptingOptions() - { - PropertyNameGenerator = propertyInfo => - { - var jsonPropertyNameAttribute = propertyInfo.GetSingleAttributeOrNull(true); - - if (jsonPropertyNameAttribute != null) - { - return jsonPropertyNameAttribute.Name; - } - - var jsonPropertyAttribute = propertyInfo.GetSingleAttributeOrNull(true); - - if (jsonPropertyAttribute != null) - { - return jsonPropertyAttribute.PropertyName; - } - - return null; - }; - } - public AbpApiProxyScriptingOptions() { Generators = new Dictionary(); From dc95eabf4305bace4a20100c82ae32f7f9325dfd Mon Sep 17 00:00:00 2001 From: Ahmet Date: Tue, 2 Mar 2021 18:10:29 +0300 Subject: [PATCH 39/43] rename configuration class name --- .../Volo/Abp/Http/Modeling/PropertyApiDescriptionModel.cs | 2 +- ...eConfiguration.cs => AbpApiProxyScriptingConfiguration.cs} | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) rename framework/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Configuration/{AbpApiPropertyNameConfiguration.cs => AbpApiProxyScriptingConfiguration.cs} (89%) diff --git a/framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/PropertyApiDescriptionModel.cs b/framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/PropertyApiDescriptionModel.cs index 8eb307c630..fc9f13edbc 100644 --- a/framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/PropertyApiDescriptionModel.cs +++ b/framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/PropertyApiDescriptionModel.cs @@ -24,7 +24,7 @@ namespace Volo.Abp.Http.Modeling return new PropertyApiDescriptionModel { Name = propertyInfo.Name, - JsonName = AbpApiPropertyNameConfiguration.PropertyNameGenerator.Invoke(propertyInfo), + JsonName = AbpApiProxyScriptingConfiguration.PropertyNameGenerator.Invoke(propertyInfo), Type = ApiTypeNameHelper.GetTypeName(propertyInfo.PropertyType), TypeSimple = ApiTypeNameHelper.GetSimpleTypeName(propertyInfo.PropertyType), IsRequired = propertyInfo.IsDefined(typeof(RequiredAttribute), true) diff --git a/framework/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Configuration/AbpApiPropertyNameConfiguration.cs b/framework/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Configuration/AbpApiProxyScriptingConfiguration.cs similarity index 89% rename from framework/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Configuration/AbpApiPropertyNameConfiguration.cs rename to framework/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Configuration/AbpApiProxyScriptingConfiguration.cs index 4320e9905f..1f17d0d2cf 100644 --- a/framework/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Configuration/AbpApiPropertyNameConfiguration.cs +++ b/framework/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Configuration/AbpApiProxyScriptingConfiguration.cs @@ -3,11 +3,11 @@ using System.Reflection; namespace Volo.Abp.Http.ProxyScripting.Configuration { - public static class AbpApiPropertyNameConfiguration + public static class AbpApiProxyScriptingConfiguration { public static Func PropertyNameGenerator { get; set; } - static AbpApiPropertyNameConfiguration() + static AbpApiProxyScriptingConfiguration() { PropertyNameGenerator = propertyInfo => { From e7f908e44a9fbef5b5ddccd1859baf7831c129de Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Wed, 3 Mar 2021 09:44:49 +0800 Subject: [PATCH 40/43] Update Background-Jobs-RabbitMq.md --- docs/zh-Hans/Background-Jobs-RabbitMq.md | 58 ++++++++++++------------ 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/docs/zh-Hans/Background-Jobs-RabbitMq.md b/docs/zh-Hans/Background-Jobs-RabbitMq.md index 45c2978593..8fb5cc58b7 100644 --- a/docs/zh-Hans/Background-Jobs-RabbitMq.md +++ b/docs/zh-Hans/Background-Jobs-RabbitMq.md @@ -1,34 +1,34 @@ # RabbitMQ 后台作业管理 -RabbitMQ 是一个标准的消息队列中间件,虽然它常用于消息传递/分布式事件,但也非常适合存储 FIFO(先进先出) 顺序的后台作业。 +RabbitMQ 是一个标准的消息队列中间件,虽然它常用于消息传递/分布式事件,但也非常适合存储 FIFO(先进先出) 顺序的后台作业. -ABP Framework 提供了 [Volo.Abp.BackgroundJobs.RabbitMQ](https://www.nuget.org/packages/Volo.Abp.BackgroundJobs.RabbitMQ) 包,将使用 RabbitMQ 来执行后台作业。 +ABP Framework 提供了 [Volo.Abp.BackgroundJobs.RabbitMQ](https://www.nuget.org/packages/Volo.Abp.BackgroundJobs.RabbitMQ) 包,将使用 RabbitMQ 来执行后台作业. -> 参阅 [后台作业文档](Background-Jobs.md) 学习如何使用后台作业系统,本文只介绍了如何安装和配置 RabbitMQ 集成。 +> 参阅 [后台作业文档](Background-Jobs.md) 学习如何使用后台作业系统,本文只介绍了如何安装和配置 RabbitMQ 集成. ## 安装 使用 ABP CLI 将 [Volo.Abp.BackgroundJobs.RabbitMQ](https://www.nuget.org/packages/Volo.Abp.BackgroundJobs.RabbitMQ) 包添加到你的项目: -- 如果之前没有安装过 [ABP CLI](https://docs.abp.io/en/abp/latest/CLI),请先安装它。 -- 跳转到待安装后台作业管理的项目目录中(包含 `.csproj` 文件的目录),打开终端管理器。 -- 执行 `abp add-package Volo.Abp.BackgroundJobs.RabbitMQ` 命令。 +- 如果之前没有安装过 [ABP CLI](https://docs.abp.io/en/abp/latest/CLI),请先安装它. +- 跳转到待安装后台作业管理的项目目录中(包含 `.csproj` 文件的目录),打开终端管理器. +- 执行 `abp add-package Volo.Abp.BackgroundJobs.RabbitMQ` 命令. -如果你想要手动安装,请先用 NuGet 包管理器安装 [Volo.Abp.BackgroundJobs.RabbitMQ](https://www.nuget.org/packages/Volo.Abp.BackgroundJobs.RabbitMQ) 包到指定项目,之后使在你的 [模块](Module-Development-Basics.md) 上面添加 `[DependsOn(typeof(AbpBackgroundJobsRabbitMqModule))]` 配置依赖。 +如果你想要手动安装,请先用 NuGet 包管理器安装 [Volo.Abp.BackgroundJobs.RabbitMQ](https://www.nuget.org/packages/Volo.Abp.BackgroundJobs.RabbitMQ) 包到指定项目,之后使在你的 [模块](Module-Development-Basics.md) 上面添加 `[DependsOn(typeof(AbpBackgroundJobsRabbitMqModule))]` 配置依赖. ## 配置 ### 默认配置 -默认配置将会使用标准端口和主机名(localhost)连接到 RabbitMQ 服务,**你不需要进行额外配置**。 +默认配置将会使用标准端口和主机名(localhost)连接到 RabbitMQ 服务,**你不需要进行额外配置**. ### RabbitMQ 连接 -你可以使用 ASP.NET Core 的 [标准配置系统](Configuration.md) 对 RabbitMQ 进行详细配置,比如 `appsettings.json` 或者是 [选项类](Options.md)。 +你可以使用 ASP.NET Core 的 [标准配置系统](Configuration.md) 对 RabbitMQ 进行详细配置,比如 `appsettings.json` 或者是 [选项类](Options.md). #### 通过 `appsettings.json` 文件配置 -这种方式是配置 RabbitMQ 连接最简单的方式,你可以使用其他的配置源(例如环境变量)。这些强大的功能都是由 [ASP.NET Core](https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/configuration/) 提供的支持。 +这种方式是配置 RabbitMQ 连接最简单的方式,你可以使用其他的配置源(例如环境变量).这些强大的功能都是由 [ASP.NET Core](https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/configuration/) 提供的支持. **示例: 配置默认的 RabbitMQ 连接** @@ -45,9 +45,9 @@ ABP Framework 提供了 [Volo.Abp.BackgroundJobs.RabbitMQ](https://www.nuget.org } ``` -你可以在配置文件使用所有 [ConnectionFactry](http://rabbitmq.github.io/rabbitmq-dotnet-client/api/RabbitMQ.Client.ConnectionFactory.html#properties) 的属性,关于这些属性的具体含义,可以查看 RabbitMQ 的 [官方文档](https://www.rabbitmq.com/dotnet-api-guide.html#exchanges-and-queues)。 +你可以在配置文件使用所有 [ConnectionFactry](http://rabbitmq.github.io/rabbitmq-dotnet-client/api/RabbitMQ.Client.ConnectionFactory.html#properties) 的属性,关于这些属性的具体含义,可以查看 RabbitMQ 的 [官方文档](https://www.rabbitmq.com/dotnet-api-guide.html#exchanges-and-queues). -目前我们允许定义多个连接,多连接的情况适用于不同的后台作业,具体配置信息可以参考下面的 RabbitMQ 后台作业配置说明。 +目前我们允许定义多个连接,多连接的情况适用于不同的后台作业,具体配置信息可以参考下面的 RabbitMQ 后台作业配置说明. **示例: 定义两个 RabbitMQ 连接** @@ -66,7 +66,7 @@ ABP Framework 提供了 [Volo.Abp.BackgroundJobs.RabbitMQ](https://www.nuget.org } ``` -如果需要连接到 RabbitMQ 集群,你可以指定多个 HostName。 +如果需要连接到 RabbitMQ 集群,你可以指定多个 HostName. **示例: 连接到 RabbitMQ 集群** @@ -88,7 +88,7 @@ ABP Framework 提供了 [Volo.Abp.BackgroundJobs.RabbitMQ](https://www.nuget.org #### 使用选项类 -`AbpRabbitMqOptions` 类型用于配置 RabbitMQ 的连接字符串,你可以在 [模块](Module-Development-Basics.md) 的 `ConfigureService` 方法中进行配置。 +`AbpRabbitMqOptions` 类型用于配置 RabbitMQ 的连接字符串,你可以在 [模块](Module-Development-Basics.md) 的 `ConfigureService` 方法中进行配置. **示例: 配置 RabbitMQ 连接** @@ -102,23 +102,23 @@ Configure(options => }); ``` -关于选项类,可以结合 `appsettings.json` 文件一起使用。针对同一个属性,在选项类里面对该值进行了设定,会覆盖掉 `appsettings.json` 的值。 +关于选项类,可以结合 `appsettings.json` 文件一起使用.针对同一个属性,在选项类里面对该值进行了设定,会覆盖掉 `appsettings.json` 的值. ### RabbitMQ 后台作业配置说明 #### 后台作业队列的名称 -默认情况下,每个后台作业都会使用一个单独的队列,结合标准前缀和作业名称来构造一个完整的队列名称。默认的前缀为 `AbpBackgroundJobs`,所以有一个作业的名称是 `EmailSending` 的话,在 RabbitMQ 的队列名称就是 `AbpBackgroundJobs.EmailSending`。 +默认情况下,每个后台作业都会使用一个单独的队列,结合标准前缀和作业名称来构造一个完整的队列名称.默认的前缀为 `AbpBackgroundJobs`,所以有一个作业的名称是 `EmailSending` 的话,在 RabbitMQ 的队列名称就是 `AbpBackgroundJobs.EmailSending`. -> 在后台作业的参数类上,可以使用 `BackgroundJobName` 特性指定后台作业的名称。否则的话,后台作业的名称将会是后台作业类的全名(Full Name)(也包含命名空间)。 +> 在后台作业的参数类上,可以使用 `BackgroundJobName` 特性指定后台作业的名称.否则的话,后台作业的名称将会是后台作业类的全名(也包含命名空间). #### 后台作业使用的连接 -默认情况下,后台作业都会使用 `Default` 作为默认连接。 +默认情况下,后台作业都会使用 `Default` 作为默认连接. #### 自定义 -`AbpRabbitMqBackgroundJobOptions` 可以自定义队列名和作业使用的 RabbitMQ 连接。 +`AbpRabbitMqBackgroundJobOptions` 可以自定义队列名和作业使用的 RabbitMQ 连接. **示例: ** @@ -135,19 +135,19 @@ Configure(options => }); ``` -- 这个示例将默认的队列名前缀设置为 `my_app_jobs.`,如果多个项目都使用的同一个 RabbitMQ 服务,设置不同的前缀可以避免执行其他项目的后台作业。 -- 这里还设置了 `EmailSendingArgs` 绑定的 RabbitMQ 连接。 +- 这个示例将默认的队列名前缀设置为 `my_app_jobs.`,如果多个项目都使用的同一个 RabbitMQ 服务,设置不同的前缀可以避免执行其他项目的后台作业. +- 这里还设置了 `EmailSendingArgs` 绑定的 RabbitMQ 连接. -`JobQueueConfiguration` 类的构造函数中,还有一些其他的可选参数。 +`JobQueueConfiguration` 类的构造函数中,还有一些其他的可选参数. -- `queueName`: 指定后台作业对应的队列名称(全名)。 -- `connectionName`: 后台作业对应的 RabbitMQ 连接名称,默认是 `Default`。 -- `durable`: 可选参数,默认为 `true`。 -- `exclusive`: 可选参数,默认为 `false`。 -- `autoDelete`: 可选参数,默认为 `false`。 +- `queueName`: 指定后台作业对应的队列名称(全名). +- `connectionName`: 后台作业对应的 RabbitMQ 连接名称,默认是 `Default`. +- `durable`: 可选参数,默认为 `true`. +- `exclusive`: 可选参数,默认为 `false`. +- `autoDelete`: 可选参数,默认为 `false`. -如果你想要更多地了解 `durable`、`exclusive`、`autoDelete` 的用法,请阅读 RabbitMQ 提供的文档。 +如果你想要更多地了解 `durable`,`exclusive`,`autoDelete` 的用法,请阅读 RabbitMQ 提供的文档. ## 参考 -- [后台作业](Background-Jobs.md) \ No newline at end of file +- [后台作业](Background-Jobs.md) From eb3b320f1ea76c1f98427bd40427d3e861a4f210 Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Wed, 3 Mar 2021 09:45:09 +0800 Subject: [PATCH 41/43] Update Background-Jobs-RabbitMq.md --- docs/zh-Hans/Background-Jobs-RabbitMq.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/zh-Hans/Background-Jobs-RabbitMq.md b/docs/zh-Hans/Background-Jobs-RabbitMq.md index 8fb5cc58b7..0f9fb21d8a 100644 --- a/docs/zh-Hans/Background-Jobs-RabbitMq.md +++ b/docs/zh-Hans/Background-Jobs-RabbitMq.md @@ -148,6 +148,6 @@ Configure(options => 如果你想要更多地了解 `durable`,`exclusive`,`autoDelete` 的用法,请阅读 RabbitMQ 提供的文档. -## 参考 +## 另请参阅 - [后台作业](Background-Jobs.md) From 9ac0cb04d89b8fec2f0ff0ad4c2098ba5f6fc59c Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Wed, 3 Mar 2021 10:33:35 +0800 Subject: [PATCH 42/43] Use Index API to add or update documents --- .../Elastic/ElasticDocumentFullSearch.cs | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/FullSearch/Elastic/ElasticDocumentFullSearch.cs b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/FullSearch/Elastic/ElasticDocumentFullSearch.cs index 8e0a51324a..7160ca405b 100644 --- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/FullSearch/Elastic/ElasticDocumentFullSearch.cs +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/FullSearch/Elastic/ElasticDocumentFullSearch.cs @@ -60,11 +60,6 @@ namespace Volo.Docs.Documents.FullSearch.Elastic var client = _clientProvider.GetClient(); - var existsResponse = await client.DocumentExistsAsync(DocumentPath.Id(document.Id), - x => x.Index(_options.IndexName), cancellationToken); - - HandleError(existsResponse); - var esDocument = new EsDocument { Id = NormalizeField(document.Id), @@ -76,17 +71,7 @@ namespace Volo.Docs.Documents.FullSearch.Elastic Version = NormalizeField(document.Version) }; - if (!existsResponse.Exists) - { - HandleError(await client.IndexAsync(esDocument, - x => x.Id(document.Id).Index(_options.IndexName), cancellationToken)); - } - else - { - HandleError(await client.UpdateAsync(DocumentPath.Id(document.Id), - x => x.Doc(esDocument).Index(_options.IndexName), cancellationToken)); - } - + await client.IndexAsync(esDocument , x=>x.Index(_options.IndexName), cancellationToken); } public virtual async Task DeleteAsync(Guid id, CancellationToken cancellationToken = default) @@ -94,7 +79,7 @@ namespace Volo.Docs.Documents.FullSearch.Elastic ValidateElasticSearchEnabled(); HandleError(await _clientProvider.GetClient() - .DeleteAsync(DocumentPath.Id(id), x => x.Index(_options.IndexName), cancellationToken)); + .DeleteAsync(DocumentPath.Id( NormalizeField(id)), x => x.Index(_options.IndexName), cancellationToken)); } public virtual async Task DeleteAllAsync(CancellationToken cancellationToken = default) From 706986715ca682008eeefea70df5cd2bb20517a7 Mon Sep 17 00:00:00 2001 From: maliming Date: Wed, 3 Mar 2021 13:53:05 +0800 Subject: [PATCH 43/43] Check whether the controller implements the IRemoteService interface. Resolve #7920 --- .../AbpRemoteServiceApiDescriptionProvider.cs | 34 +++++++++++++++---- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApiExploring/AbpRemoteServiceApiDescriptionProvider.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApiExploring/AbpRemoteServiceApiDescriptionProvider.cs index ae09573d60..1a017e9925 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApiExploring/AbpRemoteServiceApiDescriptionProvider.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApiExploring/AbpRemoteServiceApiDescriptionProvider.cs @@ -3,6 +3,7 @@ using System.Linq; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Mvc.ApiExplorer; +using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.AspNetCore.Mvc.Formatters; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.Extensions.Options; @@ -19,7 +20,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ApiExploring public AbpRemoteServiceApiDescriptionProvider( IModelMetadataProvider modelMetadataProvider, - IOptions mvcOptionsAccessor, + IOptions mvcOptionsAccessor, IOptions optionsAccessor) { _modelMetadataProvider = modelMetadataProvider; @@ -41,7 +42,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ApiExploring { foreach (var apiResponseType in GetApiResponseTypes()) { - foreach (var result in context.Results.Where(x => IsRemoteService(x.ActionDescriptor))) + foreach (var result in context.Results.Where(IsRemoteService)) { var actionProducesResponseTypeAttributes = ReflectionHelper.GetAttributesOfMemberOrDeclaringType( @@ -62,7 +63,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ApiExploring foreach (var apiResponse in _options.SupportedResponseTypes) { apiResponse.ModelMetadata = _modelMetadataProvider.GetMetadataForType(apiResponse.Type); - + foreach (var responseTypeMetadataProvider in _mvcOptions.OutputFormatters.OfType()) { var formatterSupportedContentTypes = responseTypeMetadataProvider.GetSupportedContentTypes(null, apiResponse.Type); @@ -85,10 +86,29 @@ namespace Volo.Abp.AspNetCore.Mvc.ApiExploring return _options.SupportedResponseTypes; } - protected virtual bool IsRemoteService(ActionDescriptor actionDescriptor) + protected virtual bool IsRemoteService(ApiDescription actionDescriptor) { - var remoteServiceAttr = ReflectionHelper.GetSingleAttributeOfMemberOrDeclaringTypeOrDefault(actionDescriptor.GetMethodInfo()); - return remoteServiceAttr != null && remoteServiceAttr.IsEnabled; + if (actionDescriptor.ActionDescriptor is ControllerActionDescriptor controllerActionDescriptor) + { + var remoteServiceAttr = ReflectionHelper.GetSingleAttributeOfMemberOrDeclaringTypeOrDefault(controllerActionDescriptor.MethodInfo); + if (remoteServiceAttr != null && remoteServiceAttr.IsEnabled) + { + return true; + } + + remoteServiceAttr = ReflectionHelper.GetSingleAttributeOfMemberOrDeclaringTypeOrDefault(controllerActionDescriptor.ControllerTypeInfo); + if (remoteServiceAttr != null && remoteServiceAttr.IsEnabled) + { + return true; + } + + if (typeof(IRemoteService).IsAssignableFrom(controllerActionDescriptor.ControllerTypeInfo)) + { + return true; + } + } + + return false; } } -} \ No newline at end of file +}