diff --git a/README.md b/README.md index 5074216c59..f6304ad5b3 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # ABP -[![Build Status](http://vjenkins.dynu.net:5480/job/abp/badge/icon)](http://vjenkins.dynu.net:5480/blue/organizations/jenkins/abp/activity) +[![Build Status](http://vjenkins.dynu.net:5480/job/abp/badge/icon)](http://ci.volosoft.com:5480/blue/organizations/jenkins/abp/activity) This project is the next generation of the [ASP.NET Boilerplate](https://aspnetboilerplate.com/) web application framework. See [the announcement](https://abp.io/blog/abp/Abp-vNext-Announcement). @@ -22,7 +22,7 @@ See the documentation. #### Pre Requirements -- Visual Studio 2017 15.7.0+ +- Visual Studio 2017 15.9.0+ #### Framework diff --git a/abp_io/src/Volo.AbpWebSite.Application/Volo.AbpWebSite.Application.csproj b/abp_io/src/Volo.AbpWebSite.Application/Volo.AbpWebSite.Application.csproj index ec07e51ed1..4a5c64eb64 100644 --- a/abp_io/src/Volo.AbpWebSite.Application/Volo.AbpWebSite.Application.csproj +++ b/abp_io/src/Volo.AbpWebSite.Application/Volo.AbpWebSite.Application.csproj @@ -1,7 +1,7 @@  - netcoreapp2.1 + netcoreapp2.2 diff --git a/abp_io/src/Volo.AbpWebSite.Domain/Volo.AbpWebSite.Domain.csproj b/abp_io/src/Volo.AbpWebSite.Domain/Volo.AbpWebSite.Domain.csproj index b0ac5294bc..b0c9926b35 100644 --- a/abp_io/src/Volo.AbpWebSite.Domain/Volo.AbpWebSite.Domain.csproj +++ b/abp_io/src/Volo.AbpWebSite.Domain/Volo.AbpWebSite.Domain.csproj @@ -1,7 +1,7 @@  - netcoreapp2.1 + netcoreapp2.2 @@ -12,7 +12,7 @@ - + diff --git a/abp_io/src/Volo.AbpWebSite.EntityFrameworkCore/Migrations/20181227114311_Added_ConcurrencyStamp_IsDeleted_ExtraProperties.Designer.cs b/abp_io/src/Volo.AbpWebSite.EntityFrameworkCore/Migrations/20181227114311_Added_ConcurrencyStamp_IsDeleted_ExtraProperties.Designer.cs new file mode 100644 index 0000000000..9a3dd1e999 --- /dev/null +++ b/abp_io/src/Volo.AbpWebSite.EntityFrameworkCore/Migrations/20181227114311_Added_ConcurrencyStamp_IsDeleted_ExtraProperties.Designer.cs @@ -0,0 +1,826 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Volo.AbpWebSite.EntityFrameworkCore; + +namespace Volo.AbpWebSite.EntityFrameworkCore.Migrations +{ + [DbContext(typeof(AbpWebSiteDbContext))] + [Migration("20181227114311_Added_ConcurrencyStamp_IsDeleted_ExtraProperties")] + partial class Added_ConcurrencyStamp_IsDeleted_ExtraProperties + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.1.4-rtm-31024") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityClaimType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasColumnName("ConcurrencyStamp") + .HasMaxLength(256); + + b.Property("Description") + .HasMaxLength(256); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsStatic"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256); + + b.Property("Regex") + .HasMaxLength(512); + + b.Property("RegexDescription") + .HasMaxLength(128); + + b.Property("Required"); + + b.Property("ValueType"); + + b.HasKey("Id"); + + b.ToTable("AbpClaimTypes"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasColumnName("ConcurrencyStamp") + .HasMaxLength(256); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsDefault") + .HasColumnName("IsDefault"); + + b.Property("IsPublic") + .HasColumnName("IsPublic"); + + b.Property("IsStatic") + .HasColumnName("IsStatic"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256); + + b.Property("NormalizedName") + .IsRequired() + .HasMaxLength(256); + + b.Property("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName"); + + b.ToTable("AbpRoles"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType") + .IsRequired() + .HasMaxLength(256); + + b.Property("ClaimValue") + .HasMaxLength(1024); + + b.Property("RoleId"); + + b.Property("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AbpRoleClaims"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessFailedCount") + .ValueGeneratedOnAdd() + .HasColumnName("AccessFailedCount") + .HasDefaultValue(0); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnName("DeletionTime"); + + b.Property("Email") + .HasColumnName("Email") + .HasMaxLength(256); + + b.Property("EmailConfirmed") + .ValueGeneratedOnAdd() + .HasColumnName("EmailConfirmed") + .HasDefaultValue(false); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnName("IsDeleted") + .HasDefaultValue(false); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId"); + + b.Property("LockoutEnabled") + .ValueGeneratedOnAdd() + .HasColumnName("LockoutEnabled") + .HasDefaultValue(false); + + b.Property("LockoutEnd"); + + b.Property("Name") + .HasColumnName("Name") + .HasMaxLength(64); + + b.Property("NormalizedEmail") + .HasColumnName("NormalizedEmail") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .IsRequired() + .HasColumnName("NormalizedUserName") + .HasMaxLength(256); + + b.Property("PasswordHash") + .HasColumnName("PasswordHash") + .HasMaxLength(256); + + b.Property("PhoneNumber") + .HasColumnName("PhoneNumber") + .HasMaxLength(16); + + b.Property("PhoneNumberConfirmed") + .ValueGeneratedOnAdd() + .HasColumnName("PhoneNumberConfirmed") + .HasDefaultValue(false); + + b.Property("SecurityStamp") + .IsRequired() + .HasColumnName("SecurityStamp") + .HasMaxLength(256); + + b.Property("Surname") + .HasColumnName("Surname") + .HasMaxLength(64); + + b.Property("TenantId") + .HasColumnName("TenantId"); + + b.Property("TwoFactorEnabled") + .ValueGeneratedOnAdd() + .HasColumnName("TwoFactorEnabled") + .HasDefaultValue(false); + + b.Property("UserName") + .IsRequired() + .HasColumnName("UserName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("Email"); + + b.HasIndex("NormalizedEmail"); + + b.HasIndex("NormalizedUserName"); + + b.HasIndex("UserName"); + + b.ToTable("AbpUsers"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType") + .IsRequired() + .HasMaxLength(256); + + b.Property("ClaimValue") + .HasMaxLength(1024); + + b.Property("TenantId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AbpUserClaims"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => + { + b.Property("UserId"); + + b.Property("LoginProvider") + .HasMaxLength(64); + + b.Property("ProviderDisplayName") + .HasMaxLength(128); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(196); + + b.Property("TenantId"); + + b.HasKey("UserId", "LoginProvider"); + + b.HasIndex("LoginProvider", "ProviderKey"); + + b.ToTable("AbpUserLogins"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.Property("TenantId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId", "UserId"); + + b.ToTable("AbpUserRoles"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider") + .HasMaxLength(64); + + b.Property("Name") + .HasMaxLength(128); + + b.Property("TenantId"); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AbpUserTokens"); + }); + + modelBuilder.Entity("Volo.Abp.PermissionManagement.PermissionGrant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(64); + + b.Property("ProviderName") + .IsRequired() + .HasMaxLength(64); + + b.Property("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey"); + + b.ToTable("AbpPermissionGrants"); + }); + + modelBuilder.Entity("Volo.Abp.SettingManagement.Setting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128); + + b.Property("ProviderKey") + .HasMaxLength(64); + + b.Property("ProviderName") + .HasMaxLength(64); + + b.Property("Value") + .IsRequired() + .HasMaxLength(2048); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey"); + + b.ToTable("AbpSettings"); + }); + + modelBuilder.Entity("Volo.Blogging.Blogs.Blog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasColumnName("Description") + .HasMaxLength(1024); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnName("IsDeleted") + .HasDefaultValue(false); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasColumnName("Name") + .HasMaxLength(256); + + b.Property("ShortName") + .IsRequired() + .HasColumnName("ShortName") + .HasMaxLength(32); + + b.HasKey("Id"); + + b.ToTable("BlgBlogs"); + }); + + modelBuilder.Entity("Volo.Blogging.Comments.Comment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnName("DeletionTime"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnName("IsDeleted") + .HasDefaultValue(false); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId"); + + b.Property("PostId") + .HasColumnName("PostId"); + + b.Property("RepliedCommentId") + .HasColumnName("RepliedCommentId"); + + b.Property("Text") + .IsRequired() + .HasColumnName("Text") + .HasMaxLength(1024); + + b.HasKey("Id"); + + b.HasIndex("PostId"); + + b.HasIndex("RepliedCommentId"); + + b.ToTable("BlgComments"); + }); + + modelBuilder.Entity("Volo.Blogging.Posts.Post", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BlogId") + .HasColumnName("BlogId"); + + b.Property("ConcurrencyStamp"); + + b.Property("Content") + .HasColumnName("Content") + .HasMaxLength(1048576); + + b.Property("CoverImage") + .IsRequired() + .HasColumnName("CoverImage"); + + b.Property("CreationTime") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnName("DeletionTime"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnName("IsDeleted") + .HasDefaultValue(false); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId"); + + b.Property("ReadCount"); + + b.Property("Title") + .IsRequired() + .HasColumnName("Title") + .HasMaxLength(512); + + b.Property("Url") + .IsRequired() + .HasColumnName("Url") + .HasMaxLength(64); + + b.HasKey("Id"); + + b.HasIndex("BlogId"); + + b.ToTable("BlgPosts"); + }); + + modelBuilder.Entity("Volo.Blogging.Posts.PostTag", b => + { + b.Property("PostId") + .HasColumnName("PostId"); + + b.Property("TagId") + .HasColumnName("TagId"); + + b.Property("CreationTime"); + + b.Property("CreatorId"); + + b.HasKey("PostId", "TagId"); + + b.HasIndex("TagId"); + + b.ToTable("BlgPostTags"); + }); + + modelBuilder.Entity("Volo.Blogging.Tagging.Tag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BlogId"); + + b.Property("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasColumnName("Description") + .HasMaxLength(512); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnName("IsDeleted") + .HasDefaultValue(false); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasColumnName("Name") + .HasMaxLength(64); + + b.Property("UsageCount") + .HasColumnName("UsageCount"); + + b.HasKey("Id"); + + b.ToTable("BlgTags"); + }); + + modelBuilder.Entity("Volo.Blogging.Users.BlogUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp"); + + b.Property("Email") + .HasColumnName("Email") + .HasMaxLength(256); + + b.Property("EmailConfirmed") + .ValueGeneratedOnAdd() + .HasColumnName("EmailConfirmed") + .HasDefaultValue(false); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("Name") + .HasColumnName("Name") + .HasMaxLength(64); + + b.Property("PhoneNumber") + .HasColumnName("PhoneNumber") + .HasMaxLength(16); + + b.Property("PhoneNumberConfirmed") + .ValueGeneratedOnAdd() + .HasColumnName("PhoneNumberConfirmed") + .HasDefaultValue(false); + + b.Property("Surname") + .HasColumnName("Surname") + .HasMaxLength(64); + + b.Property("TenantId") + .HasColumnName("TenantId"); + + b.Property("UserName") + .IsRequired() + .HasColumnName("UserName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.ToTable("BlgUsers"); + }); + + modelBuilder.Entity("Volo.Docs.Projects.Project", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp"); + + b.Property("DefaultDocumentName") + .IsRequired() + .HasMaxLength(128); + + b.Property("DocumentStoreType"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("Format"); + + b.Property("LatestVersionBranchName") + .HasMaxLength(128); + + b.Property("MainWebsiteUrl"); + + b.Property("MinimumVersion"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128); + + b.Property("NavigationDocumentName") + .IsRequired() + .HasMaxLength(128); + + b.Property("ShortName") + .IsRequired() + .HasMaxLength(32); + + b.HasKey("Id"); + + b.ToTable("DocsProjects"); + }); + + modelBuilder.Entity("Volo.Utils.SolutionTemplating.DownloadInfo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationDuration"); + + b.Property("CreationTime") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnName("CreatorId"); + + b.Property("DatabaseProvider"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("ProjectName") + .IsRequired() + .HasMaxLength(128); + + b.Property("TemplateName") + .IsRequired() + .HasMaxLength(42); + + b.Property("Version") + .IsRequired() + .HasMaxLength(20); + + b.HasKey("Id"); + + b.ToTable("Downloads"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => + { + b.HasOne("Volo.Abp.Identity.IdentityRole") + .WithMany("Claims") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserClaim", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser") + .WithMany("Claims") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser") + .WithMany("Logins") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => + { + b.HasOne("Volo.Abp.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Volo.Abp.Identity.IdentityUser") + .WithMany("Roles") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser") + .WithMany("Tokens") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Blogging.Comments.Comment", b => + { + b.HasOne("Volo.Blogging.Posts.Post") + .WithMany() + .HasForeignKey("PostId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Volo.Blogging.Comments.Comment") + .WithMany() + .HasForeignKey("RepliedCommentId"); + }); + + modelBuilder.Entity("Volo.Blogging.Posts.Post", b => + { + b.HasOne("Volo.Blogging.Blogs.Blog") + .WithMany() + .HasForeignKey("BlogId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Blogging.Posts.PostTag", b => + { + b.HasOne("Volo.Blogging.Posts.Post") + .WithMany("Tags") + .HasForeignKey("PostId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Volo.Blogging.Tagging.Tag") + .WithMany() + .HasForeignKey("TagId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/abp_io/src/Volo.AbpWebSite.EntityFrameworkCore/Migrations/20181227114311_Added_ConcurrencyStamp_IsDeleted_ExtraProperties.cs b/abp_io/src/Volo.AbpWebSite.EntityFrameworkCore/Migrations/20181227114311_Added_ConcurrencyStamp_IsDeleted_ExtraProperties.cs new file mode 100644 index 0000000000..c354338499 --- /dev/null +++ b/abp_io/src/Volo.AbpWebSite.EntityFrameworkCore/Migrations/20181227114311_Added_ConcurrencyStamp_IsDeleted_ExtraProperties.cs @@ -0,0 +1,77 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Volo.AbpWebSite.EntityFrameworkCore.Migrations +{ + public partial class Added_ConcurrencyStamp_IsDeleted_ExtraProperties : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "IsDeleted", + table: "AbpUsers", + nullable: false, + defaultValue: false, + oldClrType: typeof(bool)); + + migrationBuilder.AlterColumn( + name: "ConcurrencyStamp", + table: "AbpUsers", + nullable: true, + oldClrType: typeof(string), + oldMaxLength: 256); + + migrationBuilder.AlterColumn( + name: "ConcurrencyStamp", + table: "AbpRoles", + maxLength: 256, + nullable: false, + oldClrType: typeof(string), + oldNullable: true); + + migrationBuilder.AddColumn( + name: "ConcurrencyStamp", + table: "AbpClaimTypes", + maxLength: 256, + nullable: false, + defaultValue: ""); + + migrationBuilder.AddColumn( + name: "ExtraProperties", + table: "AbpClaimTypes", + nullable: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "ConcurrencyStamp", + table: "AbpClaimTypes"); + + migrationBuilder.DropColumn( + name: "ExtraProperties", + table: "AbpClaimTypes"); + + migrationBuilder.AlterColumn( + name: "IsDeleted", + table: "AbpUsers", + nullable: false, + oldClrType: typeof(bool), + oldDefaultValue: false); + + migrationBuilder.AlterColumn( + name: "ConcurrencyStamp", + table: "AbpUsers", + maxLength: 256, + nullable: false, + oldClrType: typeof(string), + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "ConcurrencyStamp", + table: "AbpRoles", + nullable: true, + oldClrType: typeof(string), + oldMaxLength: 256); + } + } +} diff --git a/abp_io/src/Volo.AbpWebSite.EntityFrameworkCore/Migrations/AbpWebSiteDbContextModelSnapshot.cs b/abp_io/src/Volo.AbpWebSite.EntityFrameworkCore/Migrations/AbpWebSiteDbContextModelSnapshot.cs index f1af866317..b997aae5ca 100644 --- a/abp_io/src/Volo.AbpWebSite.EntityFrameworkCore/Migrations/AbpWebSiteDbContextModelSnapshot.cs +++ b/abp_io/src/Volo.AbpWebSite.EntityFrameworkCore/Migrations/AbpWebSiteDbContextModelSnapshot.cs @@ -15,7 +15,7 @@ namespace Volo.AbpWebSite.EntityFrameworkCore.Migrations { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "2.1.1-rtm-30846") + .HasAnnotation("ProductVersion", "2.1.4-rtm-31024") .HasAnnotation("Relational:MaxIdentifierLength", 128) .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); @@ -24,9 +24,18 @@ namespace Volo.AbpWebSite.EntityFrameworkCore.Migrations b.Property("Id") .ValueGeneratedOnAdd(); + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasColumnName("ConcurrencyStamp") + .HasMaxLength(256); + b.Property("Description") .HasMaxLength(256); + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + b.Property("IsStatic"); b.Property("Name") @@ -53,7 +62,11 @@ namespace Volo.AbpWebSite.EntityFrameworkCore.Migrations b.Property("Id") .ValueGeneratedOnAdd(); - b.Property("ConcurrencyStamp"); + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasColumnName("ConcurrencyStamp") + .HasMaxLength(256); b.Property("ExtraProperties") .HasColumnName("ExtraProperties"); @@ -118,17 +131,20 @@ namespace Volo.AbpWebSite.EntityFrameworkCore.Migrations .HasDefaultValue(0); b.Property("ConcurrencyStamp") - .IsRequired() - .HasColumnName("ConcurrencyStamp") - .HasMaxLength(256); + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp"); - b.Property("CreationTime"); + b.Property("CreationTime") + .HasColumnName("CreationTime"); - b.Property("CreatorId"); + b.Property("CreatorId") + .HasColumnName("CreatorId"); - b.Property("DeleterId"); + b.Property("DeleterId") + .HasColumnName("DeleterId"); - b.Property("DeletionTime"); + b.Property("DeletionTime") + .HasColumnName("DeletionTime"); b.Property("Email") .HasColumnName("Email") @@ -142,11 +158,16 @@ namespace Volo.AbpWebSite.EntityFrameworkCore.Migrations b.Property("ExtraProperties") .HasColumnName("ExtraProperties"); - b.Property("IsDeleted"); + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnName("IsDeleted") + .HasDefaultValue(false); - b.Property("LastModificationTime"); + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime"); - b.Property("LastModifierId"); + b.Property("LastModifierId") + .HasColumnName("LastModifierId"); b.Property("LockoutEnabled") .ValueGeneratedOnAdd() @@ -641,7 +662,9 @@ namespace Volo.AbpWebSite.EntityFrameworkCore.Migrations b.Property("Id") .ValueGeneratedOnAdd(); - b.Property("ConcurrencyStamp"); + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp"); b.Property("DefaultDocumentName") .IsRequired() @@ -685,13 +708,16 @@ namespace Volo.AbpWebSite.EntityFrameworkCore.Migrations .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ConcurrencyStamp") - .IsConcurrencyToken(); + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp"); b.Property("CreationDuration"); - b.Property("CreationTime"); + b.Property("CreationTime") + .HasColumnName("CreationTime"); - b.Property("CreatorId"); + b.Property("CreatorId") + .HasColumnName("CreatorId"); b.Property("DatabaseProvider"); diff --git a/abp_io/src/Volo.AbpWebSite.EntityFrameworkCore/Volo.AbpWebSite.EntityFrameworkCore.csproj b/abp_io/src/Volo.AbpWebSite.EntityFrameworkCore/Volo.AbpWebSite.EntityFrameworkCore.csproj index 892d56593c..936a8cca19 100644 --- a/abp_io/src/Volo.AbpWebSite.EntityFrameworkCore/Volo.AbpWebSite.EntityFrameworkCore.csproj +++ b/abp_io/src/Volo.AbpWebSite.EntityFrameworkCore/Volo.AbpWebSite.EntityFrameworkCore.csproj @@ -1,7 +1,7 @@  - netcoreapp2.1 + netcoreapp2.2 @@ -11,7 +11,7 @@ - + diff --git a/abp_io/src/Volo.AbpWebSite.Web/AbpWebSiteWebModule.cs b/abp_io/src/Volo.AbpWebSite.Web/AbpWebSiteWebModule.cs index d0f5369ff2..c8c021e728 100644 --- a/abp_io/src/Volo.AbpWebSite.Web/AbpWebSiteWebModule.cs +++ b/abp_io/src/Volo.AbpWebSite.Web/AbpWebSiteWebModule.cs @@ -1,10 +1,8 @@ -using System.Globalization; -using System.IO; +using System.IO; using System.Linq; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Localization; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.FileProviders; @@ -26,7 +24,6 @@ using Volo.Abp.Localization; using Volo.Abp.Modularity; using Volo.Abp.Threading; using Volo.Abp.UI; -using Volo.Abp.UI.Navigation; using Volo.Abp.VirtualFileSystem; using Volo.AbpWebSite.Bundling; using Volo.Blogging; @@ -49,14 +46,6 @@ namespace Volo.AbpWebSite )] public class AbpWebSiteWebModule : AbpModule { - public override void PreConfigureServices(ServiceConfigurationContext context) - { - context.Services.PreConfigure(options => - { - options.UserSecretsAssembly = typeof(AbpWebSiteWebModule).Assembly; - }); - } - public override void ConfigureServices(ServiceConfigurationContext context) { var hostingEnvironment = context.Services.GetHostingEnvironment(); diff --git a/abp_io/src/Volo.AbpWebSite.Web/Startup.cs b/abp_io/src/Volo.AbpWebSite.Web/Startup.cs index 471337df1c..efbfda982a 100644 --- a/abp_io/src/Volo.AbpWebSite.Web/Startup.cs +++ b/abp_io/src/Volo.AbpWebSite.Web/Startup.cs @@ -15,6 +15,7 @@ namespace Volo.AbpWebSite services.AddApplication(options => { options.UseAutofac(); + options.Configuration.UserSecretsAssembly = typeof(AbpWebSiteWebModule).Assembly; }); return services.BuildServiceProviderFromFactory(); diff --git a/abp_io/src/Volo.AbpWebSite.Web/Volo.AbpWebSite.Web.csproj b/abp_io/src/Volo.AbpWebSite.Web/Volo.AbpWebSite.Web.csproj index 06fad8f448..5c410ca673 100644 --- a/abp_io/src/Volo.AbpWebSite.Web/Volo.AbpWebSite.Web.csproj +++ b/abp_io/src/Volo.AbpWebSite.Web/Volo.AbpWebSite.Web.csproj @@ -1,7 +1,7 @@  - netcoreapp2.1 + netcoreapp2.2 Volo.AbpWebSite $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; true @@ -14,7 +14,7 @@ - + diff --git a/abp_io/src/Volo.Utils.SolutionTemplating/Volo.Utils.SolutionTemplating.csproj b/abp_io/src/Volo.Utils.SolutionTemplating/Volo.Utils.SolutionTemplating.csproj index f445e989d1..254d8b65c5 100644 --- a/abp_io/src/Volo.Utils.SolutionTemplating/Volo.Utils.SolutionTemplating.csproj +++ b/abp_io/src/Volo.Utils.SolutionTemplating/Volo.Utils.SolutionTemplating.csproj @@ -15,9 +15,9 @@ - + - + diff --git a/build-all.ps1 b/build-all.ps1 index 935cfeb8a9..57a1da9e7b 100644 --- a/build-all.ps1 +++ b/build-all.ps1 @@ -31,6 +31,7 @@ foreach ($solutionPath in $solutionPaths) { dotnet build if (-Not $?) { Write-Host ("Build failed for the solution: " + $solutionPath) + Set-Location $rootFolder exit $LASTEXITCODE } } diff --git a/common.props b/common.props index ab2b655172..1361ab14b9 100644 --- a/common.props +++ b/common.props @@ -1,7 +1,7 @@ latest - 0.10.0 + 0.12.0 $(NoWarn);CS1591 https://abp.io/assets/abp_nupkg.png https://abp.io @@ -11,7 +11,7 @@ - + \ No newline at end of file diff --git a/docs/en/AspNetCore/Dynamic-CSharp-API-Clients.md b/docs/en/AspNetCore/Dynamic-CSharp-API-Clients.md index eee2abacc0..0b0667dd0d 100644 --- a/docs/en/AspNetCore/Dynamic-CSharp-API-Clients.md +++ b/docs/en/AspNetCore/Dynamic-CSharp-API-Clients.md @@ -7,13 +7,13 @@ ABP can dynamically create C# API client proxies to call remote HTTP services (R Your service/controller should implement an interface that is shared between the server and the client. So, first define a service interface in a shared library project. Example: ````csharp -public interface IBookService : IApplicationService +public interface IBookAppService : IApplicationService { Task> GetListAsync(); } ```` -Your interface should implement the `IRemoteService` interface. Since the `IApplicationService` inherits the `IRemoteService` interface, the `IBookService` above satisfies this condition. +Your interface should implement the `IRemoteService` interface to be automatically discovered. Since the `IApplicationService` inherits the `IRemoteService` interface, the `IBookAppService` above satisfies this condition. Implement this class in your service application. You can use [auto API controller system](Auto-API-Controllers.md) to expose the service as a REST API endpoint. @@ -45,13 +45,6 @@ public class MyClientAppModule : AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) { - //Configure remote end point - context.Services.Configure(options => - { - options.RemoteServices.Default = - new RemoteServiceConfiguration("http://localhost:53929/"); - }); - //Create dynamic client proxies context.Services.AddHttpClientProxies( typeof(BookStoreApplicationModule).Assembly @@ -60,10 +53,24 @@ public class MyClientAppModule : AbpModule } ```` -`RemoteServiceOptions` is used to configure endpoints for remote services (This example sets the default endpoint while you can have different service endpoints used by different clients. See the "Multiple Remote Service Endpoint" section). - `AddHttpClientProxies` method gets an assembly, finds all service interfaces in the given assembly, creates and registers proxy classes. +### Endpoint Configuration + +`RemoteServices` section in the `appsettings.json` file is used to get remote service address by default. Simplest configuration is shown below: + +```` +{ + "RemoteServices": { + "Default": { + "BaseUrl": "http://localhost:53929/" + } + } +} +```` + +See the "RemoteServiceOptions" section below for more detailed configuration. + ## Usage It's straightforward to use. Just inject the service interface in the client application code: @@ -71,9 +78,9 @@ It's straightforward to use. Just inject the service interface in the client app ````csharp public class MyService : ITransientDependency { - private readonly IBookService _bookService; + private readonly IBookAppService _bookService; - public MyService(IBookService bookService) + public MyService(IBookAppService bookService) { _bookService = bookService; } @@ -89,40 +96,34 @@ public class MyService : ITransientDependency } ```` -This sample injects the `IBookService` service interface defined above. The dynamic client proxy implementation makes an HTTP call whenever a service method is called by the client. +This sample injects the `IBookAppService` service interface defined above. The dynamic client proxy implementation makes an HTTP call whenever a service method is called by the client. -## Configuration Details +### IHttpClientProxy Interface -### RemoteServiceOptions +While you can inject `IBookAppService` like above to use the client proxy, you could inject `IHttpClientProxy` for a more explicit usage. In this case you will use the `Service` property of the `IHttpClientProxy` interface. -While you can configure `RemoteServiceOptions` as the example shown above, you can let it to read from your `appsettings.json` file. Add a `RemoteServices` section to your `appsettings.json` file: +## Configuration -````json -{ - "RemoteServices": { - "Default": { - "BaseUrl": "http://localhost:53929/" - } - } -} -```` +### RemoteServiceOptions -Then you can pass your `IConfigurationRoot` instance directly to the `Configure()` method as shown below: +`RemoteServiceOptions` is automatically set from the `appsettings.json` by default. Alternatively, you can use `Configure` method to set or override it. Example: ````csharp -var configuration = new ConfigurationBuilder() - .SetBasePath(Directory.GetCurrentDirectory()) - .AddJsonFile("appsettings.json") - .Build(); - -context.Services.Configure(configuration); +public override void ConfigureServices(ServiceConfigurationContext context) +{ + context.Services.Configure(options => + { + options.RemoteServices.Default = + new RemoteServiceConfiguration("http://localhost:53929/"); + }); + + //... +} ```` -This approach is useful since it's easy to change the configuration later without touching to the code. +### Multiple Remote Service Endpoints -#### Multiple Remote Service Endpoint - -The examples above have configured the "Default" remote service endpoint. You may have different endpoints for different services (as like in a microservice approach where each microservice has different endpoint). In this case, you can add other endpoints to your configuration file: +The examples above have configured the "Default" remote service endpoint. You may have different endpoints for different services (as like in a microservice approach where each microservice has different endpoints). In this case, you can add other endpoints to your configuration file: ````json { @@ -137,10 +138,6 @@ The examples above have configured the "Default" remote service endpoint. You ma } ```` -See the next section to learn how to use this new endpoint. - -### AddHttpClientProxies Method - `AddHttpClientProxies` method can get an additional parameter for the remote service name. Example: ````csharp @@ -150,4 +147,19 @@ context.Services.AddHttpClientProxies( ); ```` -`remoteServiceName` parameter matches the service endpoint configured via `RemoteServiceOptions`. If the `BookStore` endpoint is not defined then it fallbacks to the `Default` endpoint. \ No newline at end of file +`remoteServiceName` parameter matches the service endpoint configured via `RemoteServiceOptions`. If the `BookStore` endpoint is not defined then it fallbacks to the `Default` endpoint. + +### As Default Services + +When you create a service proxy for `IBookAppService`, you can directly inject the `IBookAppService` to use the proxy client (as shown in the usage section). You can pass `asDefaultServices: false` to the `AddHttpClientProxies` method to disable this feature. + +````csharp +context.Services.AddHttpClientProxies( + typeof(BookStoreApplicationModule).Assembly, + asDefaultServices: false +); +```` + +Using `asDefaultServices: false` may only be needed if your application has already an implementation of the service and you do not want to override/replace the other implementation by your client proxy. + +> If you disable `asDefaultServices`, you can only use `IHttpClientProxy` interface to use the client proxies (see the related section above). \ No newline at end of file diff --git a/docs/en/Best-Practices/MongoDB-Integration.md b/docs/en/Best-Practices/MongoDB-Integration.md index 32eefadd6a..76b6e8561f 100644 --- a/docs/en/Best-Practices/MongoDB-Integration.md +++ b/docs/en/Best-Practices/MongoDB-Integration.md @@ -103,34 +103,6 @@ public class IdentityMongoModelBuilderConfigurationOptions } ``` -* **Do** explicitly configure `BsonClassMap` for all entities. Create a static method for this purpose. Example: - -````C# -public static class AbpIdentityBsonClassMap -{ - private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); - - public static void Configure() - { - OneTimeRunner.Run(() => - { - BsonClassMap.RegisterClassMap(map => - { - map.AutoMap(); - map.ConfigureExtraProperties(); - }); - - BsonClassMap.RegisterClassMap(map => - { - map.AutoMap(); - }); - }); - } -} -```` - -`BsonClassMap` works with static methods. So, it is only needed to configure entities once in an application. `OneTimeRunner` guarantees that it runs in a thread safe manner and only once in the application life. Such a mapping above ensures that unit test properly run. This code will be called by the **module class** below. - ### Repository Implementation - **Do** **inherit** the repository from the `MongoDbRepository` class and implement the corresponding repository interface. Example: @@ -187,8 +159,6 @@ public class AbpIdentityMongoDbModule : AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) { - AbpIdentityBsonClassMap.Configure(); - context.Services.AddMongoDbContext(options => { options.AddRepository(); diff --git a/docs/en/Entity-Framework-Core.md b/docs/en/Entity-Framework-Core.md index 4f9ff367ee..e608736bcd 100644 --- a/docs/en/Entity-Framework-Core.md +++ b/docs/en/Entity-Framework-Core.md @@ -1,8 +1,8 @@ -## Entity Framework Core Integration +# Entity Framework Core Integration This document explains how to integrate EF Core as an ORM provider to ABP based applications and how to configure it. -### Installation +## Installation `Volo.Abp.EntityFrameworkCore` is the main nuget package for the EF Core integration. Install it to your project (for a layered application, to your data/infrastructure layer): @@ -28,7 +28,7 @@ namespace MyCompany.MyProject > Note: Instead, you can directly download a [startup template](https://abp.io/Templates) with EF Core pre-installed. -### Creating DbContext +## Creating DbContext You can create your DbContext as you normally do. It should be derived from `AbpDbContext` as shown below: @@ -50,7 +50,21 @@ namespace MyCompany.MyProject } ```` -### Registering DbContext To Dependency Injection +### Configure the Connection String Selection + +If you have multiple databases in your application, you can configure the connection string name for your DbContext using the `[ConnectionStringName]` attribute. Example: + +```csharp +[ConnectionStringName("MySecondConnString")] +public class MyDbContext : AbpDbContext +{ + +} +``` + +If you don't configure, the `Default` connection string is used. If you configure a specific connection string name, but not define this connection string name in the application configuration then it fallbacks to the `Default` connection string. + +## Registering DbContext To Dependency Injection Use `AddAbpDbContext` method in your module to register your DbContext class for [dependency injection](Dependency-Injection.md) system. @@ -74,7 +88,7 @@ namespace MyCompany.MyProject } ```` -#### Add Default Repositories +### Add Default Repositories ABP can automatically create default [generic repositories](Repositories.md) for the entities in your DbContext. Just use `AddDefaultRepositories()` option on the registration: @@ -137,7 +151,7 @@ public class BookManager : DomainService This sample uses `InsertAsync` method to insert a new entity to the database. -#### Add Custom Repositories +### Add Custom Repositories Default generic repositories are powerful enough in most cases (since they implement `IQueryable`). However, you may need to create a custom repository to add your own repository methods. @@ -173,7 +187,7 @@ public class BookRepository : EfCoreRepository, Now, it's possible to [inject](Dependency-Injection.md) the `IBookRepository` and use the `DeleteBooksByType` method when needed. -##### Override Default Generic Repository +#### Override Default Generic Repository Even if you create a custom repository, you can still inject the default generic repository (`IRepository` for this example). Default repository implementation will not use the class you have created. @@ -199,7 +213,7 @@ public override async Task DeleteAsync( } ```` -#### Access to the EF Core API +### Access to the EF Core API In most cases, you want to hide EF Core APIs behind a repository (this is the main purpose of the repository). However, if you want to access the DbContext instance over the repository, you can use `GetDbContext()` or `GetDbSet()` extension methods. Example: @@ -225,9 +239,9 @@ public class BookService > Important: You must reference to the `Volo.Abp.EntityFrameworkCore` package from the project you want to access to the DbContext. This breaks encapsulation, but this is what you want in that case. -#### Advanced Topics +### Advanced Topics -##### Set Default Repository Classes +#### Set Default Repository Classes Default generic repositories are implemented by `EfCoreRepository` class by default. You can create your own implementation and use it for default repository implementation. @@ -272,7 +286,7 @@ context.Services.AddAbpDbContext(options => }); ``` -##### Set Base DbContext Class or Interface for Default Repositories +#### Set Base DbContext Class or Interface for Default Repositories If your DbContext inherits from another DbContext or implements an interface, you can use that base class or interface as DbContext for default repositories. Example: @@ -304,7 +318,7 @@ public class BookRepository : EfCoreRepository, One advantage of using interface for a DbContext is then it becomes replaceable by another implementation. -##### Replace Other DbContextes +#### Replace Other DbContextes Once you properly define and use an interface for DbContext, then any other implementation can replace it using the `ReplaceDbContext` option: diff --git a/docs/en/Index.md b/docs/en/Index.md index bd65ed5c7f..b1bccab0ea 100644 --- a/docs/en/Index.md +++ b/docs/en/Index.md @@ -6,25 +6,25 @@ Explore the left navigation menu to deep dive in the documentation. ## Project Status -ABP is the **next generation** of the open source [ASP.NET Boilerplate](https://aspnetboilerplate.com/) framework. It's currently in early preview stage and not ready to use in production. The documentation is in progress and currently is very incomplete. +ABP is the **next generation** of the open source [ASP.NET Boilerplate](https://aspnetboilerplate.com/) framework. It's currently in early preview stage and not ready to use in production. The documentation is still in progress and it is incomplete yet. -For short-term and production applications, it's suggested to use the [ASP.NET Boilerplate](https://aspnetboilerplate.com/) framework which is feature rich, mature, maintained and up-to-date. +For short-term and production level applications, it's suggested to use [ASP.NET Boilerplate](https://aspnetboilerplate.com/) framework which has rich feature set, mature, actively maintained and up-to-date. ## Getting Started -Easiest way to start a new project with ABP is to use the Startup templates: +Easiest way to start a new project with ABP is to use the startup templates: * [ASP.NET Core MVC Template](Getting-Started-AspNetCore-MVC-Template.md) -If you want to start from scratch (with an empty project) then manually install the ABP framework, use the following tutorials: +If you want to start from scratch (with an empty project) then manually install the ABP Framework and use the following tutorials: * [Console Application](Getting-Started-Console-Application.md) * [ASP.NET Core Web Application](Getting-Started-AspNetCore-Application.md) ## Source Code -ABP is being developed on GitHub. See [the source code](https://github.com/abpframework/abp). +ABP is hosted on GitHub. See [the source code](https://github.com/abpframework/abp). ## Want to Contribute? -ABP is a community-driven open source project. See [the contribution guide](Contribution/Index.md) if you want to be a part of this project. \ No newline at end of file +ABP is a community-driven open source project. See [the contribution guide](Contribution/Index.md) if you want to be a part of this project. diff --git a/docs/en/Localization.md b/docs/en/Localization.md index b6d27cc9ce..d34dc8232a 100644 --- a/docs/en/Localization.md +++ b/docs/en/Localization.md @@ -1,8 +1,8 @@ -## Localization +# Localization ABP's localization system is seamlessly integrated to the `Microsoft.Extensions.Localization` package and compatible with the [Microsoft's localization documentation](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/localization). It adds some useful features and enhancements to make it easier to use in real life application scenarios. -### Volo.Abp.Localization Package +## Volo.Abp.Localization Package > This package is already installed by default with the startup template. So, most of the time, you don't need to install it manually. @@ -28,9 +28,9 @@ namespace MyCompany.MyProject } ``` -#### Creating A Localization Resource +## Creating A Localization Resource -A localization resource is used to group related localization strings together and separate them from other localization strings of the application. A module generally defines its own localization resource. Localization resource is just a plain class. Example: +A localization resource is used to group related localization strings together and separate them from other localization strings of the application. A [module](Module-Development-Basics.md) generally defines its own localization resource. Localization resource is just a plain class. Example: ````C# public class TestResource @@ -52,7 +52,8 @@ public class MyModule : AbpModule }); Configure(options => - { + { + //Define a new localization resource (TestResource) options.Resources .Add("en") .AddVirtualJson("/Localization/Resources/Test"); @@ -65,7 +66,7 @@ In this example; * Added a new localization resource with "en" (English) as the default culture. * Used JSON files to store the localization strings. -* JSON files are embedded into the assembly using the [virtual file system](Virtual-File-System.md). +* JSON files are embedded into the assembly using `VirtualFileSystemOptions` (see [virtual file system](Virtual-File-System.md)). JSON files are located under "/Localization/Resources/Test" project folder as shown below: @@ -85,7 +86,7 @@ A JSON localization file content is shown below: * Every localization file should define the `culture` code for the file (like "en" or "en-US"). * `texts` section just contains key-value collection of the localization strings (keys may have spaces too). -##### Short Localization Resource Name +### Short Localization Resource Name Localization resources are also available in the client (JavaScript) side. So, setting a short name for the localization resource makes it easy to use localization texts. Example: @@ -98,7 +99,7 @@ public class TestResource See the Getting Localized Test / Client Side section below. -##### Inherit From Other Resources +### Inherit From Other Resources A resource can inherit from other resources which makes possible to re-use existing localization strings without referring the existing resource. Example: @@ -124,7 +125,7 @@ services.Configure(options => * A resource may inherit from multiple resources. * If the new resource defines the same localized string, it overrides the string. -##### Extending Existing Resource +### Extending Existing Resource Inheriting from a resource creates a new resource without modifying the existing one. In some cases, you may want to not create a new resource but directly extend an existing resource. Example: @@ -139,13 +140,13 @@ services.Configure(options => * If an extension file defines the same localized string, it overrides the string. -#### Getting Localized Texts +## Getting Localized Texts -##### Server Side +### Server Side Getting the localized text on the server side is pretty standard. -###### Simplest Usage In A Class +#### Simplest Usage In A Class ````C# public class MyService @@ -164,7 +165,7 @@ public class MyService } ```` -###### Simplest Usage In A Razor View/Page +#### Simplest Usage In A Razor View/Page ````c# @inject IHtmlLocalizer Localizer @@ -174,7 +175,7 @@ public class MyService Refer to the [Microsoft's localization documentation](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/localization) for details about using localization on the server side. -##### Client Side +### Client Side ABP provides JavaScript services to use the same localized texts in the client side. diff --git a/docs/en/MongoDB.md b/docs/en/MongoDB.md index c03c6f4f2c..e7d8084f41 100644 --- a/docs/en/MongoDB.md +++ b/docs/en/MongoDB.md @@ -1,8 +1,8 @@ -## MongoDB Integration +# MongoDB Integration This document explains how to integrate MongoDB as a database provider to ABP based applications and how to configure it. -### Installation +## Installation `Volo.Abp.MongoDB` is the main nuget package for the MongoDB integration. Install it to your project (for a layered application, to your data/infrastructure layer): @@ -26,7 +26,7 @@ namespace MyCompany.MyProject } ``` -### Creating a Mongo Db Context +## Creating a Mongo Db Context ABP introduces **Mongo Db Context** concept (which is similar to Entity Framework Core's DbContext) to make it easier to use collections and configure them. An example is shown below: @@ -39,19 +39,62 @@ public class MyDbContext : AbpMongoDbContext protected override void CreateModel(IMongoModelBuilder modelBuilder) { - modelBuilder.Entity(b => - { - b.CollectionName = "Questions"; - }); + base.CreateModel(modelBuilder); + + //Customize the configuration for your collections. } } ``` * It's derived from `AbpMongoDbContext` class. * Adds a public `IMongoCollection` property for each mongo collection. ABP uses these properties to create default repositories by default. -* Overriding `CreateModel` method allows to configure collections (like their collection name in the database). +* Overriding `CreateModel` method allows to configure collection configuration. + +### Configure Mapping for a Collection + +ABP automatically register entities to MongoDB client library for all `IMongoCollection` properties in your DbContext. For the example above, `Question` and `Category` entities are automatically registered. + +For each registered entity, it calls `AutoMap()` and configures known properties of your entity. For instance, if your entity implements `IHasExtraProperties` interface (which is already implemented for every aggregate root by default), it automatically configures `ExtraProperties`. + +So, most of times you don't need to explicitly configure registration for your entities. However, if you need it you can do it by overriding the `CreateModel` method in your DbContext. Example: + +````csharp +protected override void CreateModel(IMongoModelBuilder modelBuilder) +{ + base.CreateModel(modelBuilder); + + modelBuilder.Entity(b => + { + b.CollectionName = "MyQuestions"; //Sets the collection name + b.BsonMap.UnmapProperty(x => x.MyProperty); //Ignores 'MyProperty' + }); +} +```` + +This example changes the mapped collection name to 'MyQuestions' in the database and ignores a property in the `Question` class. + +If you only need to configure the collection name, you can also use `[MongoCollection]` attribute for the collection in your DbContext. Example: + +````csharp +[MongoCollection("MyQuestions")] //Sets the collection name +public IMongoCollection Questions => Collection(); +```` + +### Configure the Connection String Selection + +If you have multiple databases in your application, you can configure the connection string name for your DbContext using the `[ConnectionStringName]` attribute. Example: + +````csharp +[ConnectionStringName("MySecondConnString")] +public class MyDbContext : AbpMongoDbContext +{ + +} +```` + +If you don't configure, the `Default` connection string is used. If you configure a specific connection string name, but not define this connection string name in the application configuration then it fallbacks to the `Default` connection string. -### Registering DbContext To Dependency Injection +## Registering DbContext To Dependency Injection Use `AddAbpDbContext` method in your module to register your DbContext class for [dependency injection](Dependency-Injection.md) system. @@ -75,7 +118,7 @@ namespace MyCompany.MyProject } ``` -#### Add Default Repositories +### Add Default Repositories ABP can automatically create default [generic repositories](Repositories.md) for the entities in your DbContext. Just use `AddDefaultRepositories()` option on the registration: @@ -138,7 +181,7 @@ public class BookManager : DomainService This sample uses `InsertAsync` method to insert a new entity to the database. -#### Add Custom Repositories +### Add Custom Repositories Default generic repositories are powerful enough in most cases (since they implement `IQueryable`). However, you may need to create a custom repository to add your own repository methods. @@ -182,7 +225,7 @@ public class BookRepository : Now, it's possible to [inject](Dependency-Injection.md) the `IBookRepository` and use the `DeleteBooksByType` method when needed. -##### Override Default Generic Repository +#### Override Default Generic Repository Even if you create a custom repository, you can still inject the default generic repository (`IRepository` for this example). Default repository implementation will not use the class you have created. @@ -208,7 +251,7 @@ public override async Task DeleteAsync( } ``` -#### Access to the MongoDB API +### Access to the MongoDB API In most cases, you want to hide MongoDB APIs behind a repository (this is the main purpose of the repository). However, if you want to access the MongoDB API over the repository, you can use `GetDatabase()` or `GetCollection()` extension methods. Example: @@ -232,9 +275,9 @@ public class BookService > Important: You must reference to the `Volo.Abp.MongoDB` package from the project you want to access to the MongoDB API. This breaks encapsulation, but this is what you want in that case. -#### Advanced Topics +### Advanced Topics -##### Set Default Repository Classes +#### Set Default Repository Classes Default generic repositories are implemented by `MongoDbRepository` class by default. You can create your own implementation and use it for default repository implementation. @@ -279,7 +322,7 @@ context.Services.AddMongoDbContext(options => }); ``` -##### Set Base MongoDbContext Class or Interface for Default Repositories +#### Set Base MongoDbContext Class or Interface for Default Repositories If your MongoDbContext inherits from another MongoDbContext or implements an interface, you can use that base class or interface as the MongoDbContext for default repositories. Example: @@ -313,7 +356,7 @@ public class BookRepository One advantage of using interface for a MongoDbContext is then it becomes replaceable by another implementation. -##### Replace Other DbContextes +#### Replace Other DbContextes Once you properly define and use an interface for a MongoDbContext , then any other implementation can replace it using the `ReplaceDbContext` option: diff --git a/docs/en/Tutorials/AspNetCore-Mvc/Part-I.md b/docs/en/Tutorials/AspNetCore-Mvc/Part-I.md index 9f766ae3b0..6db6766c0b 100644 --- a/docs/en/Tutorials/AspNetCore-Mvc/Part-I.md +++ b/docs/en/Tutorials/AspNetCore-Mvc/Part-I.md @@ -82,11 +82,11 @@ namespace Acme.BookStore EF Core requires you to relate entities with your DbContext. The easiest way to do this is to add a `DbSet` property to the `BookStoreDbContext` class in the `Acme.BookStore.EntityFrameworkCore` project, as shown below: ````C# -public class BookStoreDbContext : AbpDbContext -{ - public DbSet Book { get; set; } - ... -} + public class BookStoreDbContext : AbpDbContext + { + public DbSet Book { get; set; } + ... + } ```` #### Add New Migration & Update the Database diff --git a/framework/Volo.Abp.sln b/framework/Volo.Abp.sln index 92c46299f4..08104ef769 100644 --- a/framework/Volo.Abp.sln +++ b/framework/Volo.Abp.sln @@ -214,9 +214,17 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.EntityFrameworkCor EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Localization.Abstractions", "src\Volo.Abp.Localization.Abstractions\Volo.Abp.Localization.Abstractions.csproj", "{20513A4E-FAC7-4106-8976-5D79A3BDFED1}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Security.Tests", "test\Volo.Abp.Security.Tests\Volo.Abp.Security.Tests.csproj", "{7CE07034-7E02-4C78-B981-F1039412CA5E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Security.Tests", "test\Volo.Abp.Security.Tests\Volo.Abp.Security.Tests.csproj", "{7CE07034-7E02-4C78-B981-F1039412CA5E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Settings.Tests", "test\Volo.Abp.Settings.Tests\Volo.Abp.Settings.Tests.csproj", "{5F3A2D1E-EA89-40A7-8D2F-FB4EB2092403}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Settings.Tests", "test\Volo.Abp.Settings.Tests\Volo.Abp.Settings.Tests.csproj", "{5F3A2D1E-EA89-40A7-8D2F-FB4EB2092403}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Http.Client.IdentityModel", "src\Volo.Abp.Http.Client.IdentityModel\Volo.Abp.Http.Client.IdentityModel.csproj", "{D211A446-38FA-4F97-9A95-1F004A0FFF69}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.IdentityModel", "src\Volo.Abp.IdentityModel\Volo.Abp.IdentityModel.csproj", "{64D99E19-EE25-465A-82E5-17B25F4C4E18}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.AspNetCore.Mvc.Client", "src\Volo.Abp.AspNetCore.Mvc.Client\Volo.Abp.AspNetCore.Mvc.Client.csproj", "{E803DDB8-81EA-454B-9A66-9C2941100B67}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.AspNetCore.Mvc.Contracts", "src\Volo.Abp.AspNetCore.Mvc.Contracts\Volo.Abp.AspNetCore.Mvc.Contracts.csproj", "{88F6D091-CA16-4B71-9499-8D5B8FA2E712}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -616,6 +624,22 @@ Global {5F3A2D1E-EA89-40A7-8D2F-FB4EB2092403}.Debug|Any CPU.Build.0 = Debug|Any CPU {5F3A2D1E-EA89-40A7-8D2F-FB4EB2092403}.Release|Any CPU.ActiveCfg = Release|Any CPU {5F3A2D1E-EA89-40A7-8D2F-FB4EB2092403}.Release|Any CPU.Build.0 = Release|Any CPU + {D211A446-38FA-4F97-9A95-1F004A0FFF69}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D211A446-38FA-4F97-9A95-1F004A0FFF69}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D211A446-38FA-4F97-9A95-1F004A0FFF69}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D211A446-38FA-4F97-9A95-1F004A0FFF69}.Release|Any CPU.Build.0 = Release|Any CPU + {64D99E19-EE25-465A-82E5-17B25F4C4E18}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {64D99E19-EE25-465A-82E5-17B25F4C4E18}.Debug|Any CPU.Build.0 = Debug|Any CPU + {64D99E19-EE25-465A-82E5-17B25F4C4E18}.Release|Any CPU.ActiveCfg = Release|Any CPU + {64D99E19-EE25-465A-82E5-17B25F4C4E18}.Release|Any CPU.Build.0 = Release|Any CPU + {E803DDB8-81EA-454B-9A66-9C2941100B67}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E803DDB8-81EA-454B-9A66-9C2941100B67}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E803DDB8-81EA-454B-9A66-9C2941100B67}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E803DDB8-81EA-454B-9A66-9C2941100B67}.Release|Any CPU.Build.0 = Release|Any CPU + {88F6D091-CA16-4B71-9499-8D5B8FA2E712}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {88F6D091-CA16-4B71-9499-8D5B8FA2E712}.Debug|Any CPU.Build.0 = Debug|Any CPU + {88F6D091-CA16-4B71-9499-8D5B8FA2E712}.Release|Any CPU.ActiveCfg = Release|Any CPU + {88F6D091-CA16-4B71-9499-8D5B8FA2E712}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -721,6 +745,10 @@ Global {20513A4E-FAC7-4106-8976-5D79A3BDFED1} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} {7CE07034-7E02-4C78-B981-F1039412CA5E} = {447C8A77-E5F0-4538-8687-7383196D04EA} {5F3A2D1E-EA89-40A7-8D2F-FB4EB2092403} = {447C8A77-E5F0-4538-8687-7383196D04EA} + {D211A446-38FA-4F97-9A95-1F004A0FFF69} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} + {64D99E19-EE25-465A-82E5-17B25F4C4E18} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} + {E803DDB8-81EA-454B-9A66-9C2941100B67} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} + {88F6D091-CA16-4B71-9499-8D5B8FA2E712} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5} diff --git a/framework/src/Volo.Abp.AspNetCore.Authentication.OAuth/Volo.Abp.AspNetCore.Authentication.OAuth.csproj b/framework/src/Volo.Abp.AspNetCore.Authentication.OAuth/Volo.Abp.AspNetCore.Authentication.OAuth.csproj index 42b81370fd..722b6033ed 100644 --- a/framework/src/Volo.Abp.AspNetCore.Authentication.OAuth/Volo.Abp.AspNetCore.Authentication.OAuth.csproj +++ b/framework/src/Volo.Abp.AspNetCore.Authentication.OAuth/Volo.Abp.AspNetCore.Authentication.OAuth.csproj @@ -19,7 +19,7 @@ - + diff --git a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/AbpAspNetCoreMultiTenancyModule.cs b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/AbpAspNetCoreMultiTenancyModule.cs index 7f00a58069..7ba8766dd8 100644 --- a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/AbpAspNetCoreMultiTenancyModule.cs +++ b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/AbpAspNetCoreMultiTenancyModule.cs @@ -14,10 +14,10 @@ namespace Volo.Abp.AspNetCore.MultiTenancy { Configure(options => { - options.TenantResolvers.Add(new QueryStringTenantResolveContributer()); - options.TenantResolvers.Add(new RouteTenantResolveContributer()); - options.TenantResolvers.Add(new HeaderTenantResolveContributer()); - options.TenantResolvers.Add(new CookieTenantResolveContributer()); + options.TenantResolvers.Add(new QueryStringTenantResolveContributor()); + options.TenantResolvers.Add(new RouteTenantResolveContributor()); + options.TenantResolvers.Add(new HeaderTenantResolveContributor()); + options.TenantResolvers.Add(new CookieTenantResolveContributor()); }); } } diff --git a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/CookieTenantResolveContributer.cs b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/CookieTenantResolveContributor.cs similarity index 81% rename from framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/CookieTenantResolveContributer.cs rename to framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/CookieTenantResolveContributor.cs index ebce890ea9..5b27a9d93c 100644 --- a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/CookieTenantResolveContributer.cs +++ b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/CookieTenantResolveContributor.cs @@ -3,7 +3,7 @@ using Volo.Abp.MultiTenancy; namespace Volo.Abp.AspNetCore.MultiTenancy { - public class CookieTenantResolveContributer : HttpTenantResolveContributerBase + public class CookieTenantResolveContributor : HttpTenantResolveContributorBase { protected override string GetTenantIdOrNameFromHttpContextOrNull(ITenantResolveContext context, HttpContext httpContext) { diff --git a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/DomainTenantResolveContributer.cs b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/DomainTenantResolveContributor.cs similarity index 87% rename from framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/DomainTenantResolveContributer.cs rename to framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/DomainTenantResolveContributor.cs index 2894fcf0df..e342cb8f40 100644 --- a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/DomainTenantResolveContributer.cs +++ b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/DomainTenantResolveContributor.cs @@ -7,11 +7,11 @@ namespace Volo.Abp.AspNetCore.MultiTenancy { //TODO: Create a better domain format. We can accept regex for example. - public class DomainTenantResolveContributer : HttpTenantResolveContributerBase + public class DomainTenantResolveContributor : HttpTenantResolveContributorBase { private readonly string _domainFormat; - public DomainTenantResolveContributer(string domainFormat) + public DomainTenantResolveContributor(string domainFormat) { _domainFormat = domainFormat.RemovePreFix("http://", "https://"); } diff --git a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/HeaderTenantResolveContributer.cs b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/HeaderTenantResolveContributor.cs similarity index 93% rename from framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/HeaderTenantResolveContributer.cs rename to framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/HeaderTenantResolveContributor.cs index 58acdc493c..3f5e93bd36 100644 --- a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/HeaderTenantResolveContributer.cs +++ b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/HeaderTenantResolveContributor.cs @@ -7,7 +7,7 @@ using Volo.Abp.MultiTenancy; namespace Volo.Abp.AspNetCore.MultiTenancy { - public class HeaderTenantResolveContributer : HttpTenantResolveContributerBase + public class HeaderTenantResolveContributor : HttpTenantResolveContributorBase { protected override string GetTenantIdOrNameFromHttpContextOrNull(ITenantResolveContext context, HttpContext httpContext) { @@ -36,7 +36,7 @@ namespace Volo.Abp.AspNetCore.MultiTenancy { context .ServiceProvider - .GetRequiredService>() + .GetRequiredService>() .LogWarning(text); } } diff --git a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/HttpTenantResolveContributerBase.cs b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/HttpTenantResolveContributerBase.cs index 51bdc57323..d2971063a5 100644 --- a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/HttpTenantResolveContributerBase.cs +++ b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/HttpTenantResolveContributerBase.cs @@ -7,7 +7,7 @@ using Volo.Abp.MultiTenancy; namespace Volo.Abp.AspNetCore.MultiTenancy { - public abstract class HttpTenantResolveContributerBase : ITenantResolveContributer + public abstract class HttpTenantResolveContributorBase : ITenantResolveContributor { public virtual void Resolve(ITenantResolveContext context) { @@ -24,7 +24,7 @@ namespace Volo.Abp.AspNetCore.MultiTenancy catch (Exception e) { context.ServiceProvider - .GetRequiredService>() + .GetRequiredService>() .LogWarning(e.ToString()); } } diff --git a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/QueryStringTenantResolveContributer.cs b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/QueryStringTenantResolveContributor.cs similarity index 85% rename from framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/QueryStringTenantResolveContributer.cs rename to framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/QueryStringTenantResolveContributor.cs index 88bd3bcc0d..9899e2613e 100644 --- a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/QueryStringTenantResolveContributer.cs +++ b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/QueryStringTenantResolveContributor.cs @@ -3,7 +3,7 @@ using Volo.Abp.MultiTenancy; namespace Volo.Abp.AspNetCore.MultiTenancy { - public class QueryStringTenantResolveContributer : HttpTenantResolveContributerBase + public class QueryStringTenantResolveContributor : HttpTenantResolveContributorBase { protected override string GetTenantIdOrNameFromHttpContextOrNull(ITenantResolveContext context, HttpContext httpContext) { diff --git a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/RouteTenantResolveContributer.cs b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/RouteTenantResolveContributor.cs similarity index 87% rename from framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/RouteTenantResolveContributer.cs rename to framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/RouteTenantResolveContributor.cs index 10d4f1ebe1..3be155c68d 100644 --- a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/RouteTenantResolveContributer.cs +++ b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/RouteTenantResolveContributor.cs @@ -5,7 +5,7 @@ using Volo.Abp.MultiTenancy; namespace Volo.Abp.AspNetCore.MultiTenancy { - public class RouteTenantResolveContributer : HttpTenantResolveContributerBase + public class RouteTenantResolveContributor : HttpTenantResolveContributorBase { protected override string GetTenantIdOrNameFromHttpContextOrNull(ITenantResolveContext context, HttpContext httpContext) { diff --git a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/MultiTenancy/MultiTenancyOptionsExtensions.cs b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/MultiTenancy/MultiTenancyOptionsExtensions.cs index 8e6d3ae7ea..a162f1750e 100644 --- a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/MultiTenancy/MultiTenancyOptionsExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/MultiTenancy/MultiTenancyOptionsExtensions.cs @@ -8,8 +8,8 @@ namespace Volo.Abp.MultiTenancy public static void AddDomainTenantResolver(this TenantResolveOptions options, string domainFormat) { options.TenantResolvers.InsertAfter( - r => r is CurrentClaimsPrincipalTenantResolveContributer, - new DomainTenantResolveContributer(domainFormat) + r => r is CurrentClaimsPrincipalTenantResolveContributor, + new DomainTenantResolveContributor(domainFormat) ); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo.Abp.AspNetCore.Mvc.Client.csproj b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo.Abp.AspNetCore.Mvc.Client.csproj new file mode 100644 index 0000000000..911782697b --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo.Abp.AspNetCore.Mvc.Client.csproj @@ -0,0 +1,22 @@ + + + + + + netstandard2.0 + Volo.Abp.AspNetCore.Mvc.Client + Volo.Abp.AspNetCore.Mvc.Client + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + + + + + + + + + + diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/AbpAspNetCoreMvcClientModule.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/AbpAspNetCoreMvcClientModule.cs new file mode 100644 index 0000000000..3f1ed47435 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/AbpAspNetCoreMvcClientModule.cs @@ -0,0 +1,26 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Caching; +using Volo.Abp.Http.Client; +using Volo.Abp.Modularity; + +namespace Volo.Abp.AspNetCore.Mvc.Client +{ + [DependsOn( + typeof(AbpHttpClientModule), + typeof(AbpAspNetCoreMvcContractsModule), + typeof(AbpCachingModule) + )] + public class AbpAspNetCoreMvcClientModule : AbpModule + { + public const string RemoteServiceName = "AbpMvcClient"; + + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddHttpClientProxies( + typeof(AbpAspNetCoreMvcContractsModule).Assembly, + RemoteServiceName, + asDefaultServices: false + ); + } + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/CachedApplicationConfigurationClient.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/CachedApplicationConfigurationClient.cs new file mode 100644 index 0000000000..60338e6546 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/CachedApplicationConfigurationClient.cs @@ -0,0 +1,65 @@ +using System; +using Microsoft.AspNetCore.Http; +using System.Threading.Tasks; +using Microsoft.Extensions.Caching.Distributed; +using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; +using Volo.Abp.Caching; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Http.Client.DynamicProxying; +using Volo.Abp.Users; + +namespace Volo.Abp.AspNetCore.Mvc.Client +{ + public class CachedApplicationConfigurationClient : ICachedApplicationConfigurationClient, ITransientDependency + { + public IHttpContextAccessor HttpContextAccessor { get; set; } + + protected IHttpClientProxy Proxy { get; } + protected ICurrentUser CurrentUser { get; } + protected IDistributedCache Cache { get; } + + public CachedApplicationConfigurationClient( + IDistributedCache cache, + IHttpClientProxy proxy, + ICurrentUser currentUser, + IHttpContextAccessor httpContextAccessor) + { + Proxy = proxy; + CurrentUser = currentUser; + HttpContextAccessor = httpContextAccessor; + Cache = cache; + } + + public async Task GetAsync() + { + var cacheKey = CreateCacheKey(); + var httpContext = HttpContextAccessor?.HttpContext; + + if (httpContext != null && httpContext.Items[cacheKey] is ApplicationConfigurationDto configuration) + { + return configuration; + } + + configuration = await Cache.GetOrAddAsync( + CreateCacheKey(), + async () => await Proxy.Service.GetAsync(), + () => new DistributedCacheEntryOptions + { + AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5) + } + ); + + if (httpContext != null) + { + httpContext.Items[cacheKey] = configuration; + } + + return configuration; + } + + protected virtual string CreateCacheKey() + { + return $"ApplicationConfiguration_{CurrentUser.Id?.ToString("N") ?? "Anonymous"}"; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/ICachedApplicationConfigurationClient.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/ICachedApplicationConfigurationClient.cs new file mode 100644 index 0000000000..71d9d8cddf --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/ICachedApplicationConfigurationClient.cs @@ -0,0 +1,10 @@ +using System.Threading.Tasks; +using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; + +namespace Volo.Abp.AspNetCore.Mvc.Client +{ + public interface ICachedApplicationConfigurationClient + { + Task GetAsync(); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/RemotePermissionChecker.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/RemotePermissionChecker.cs new file mode 100644 index 0000000000..46e13bcc3d --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/RemotePermissionChecker.cs @@ -0,0 +1,32 @@ +using System.Security.Claims; +using System.Threading.Tasks; +using Volo.Abp.Authorization.Permissions; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.AspNetCore.Mvc.Client +{ + public class RemotePermissionChecker : IPermissionChecker, ITransientDependency + { + protected ICachedApplicationConfigurationClient ConfigurationClient { get; } + + public RemotePermissionChecker(ICachedApplicationConfigurationClient configurationClient) + { + ConfigurationClient = configurationClient; + } + + public async Task CheckAsync(string name) + { + var configuration = await ConfigurationClient.GetAsync(); + + return new PermissionGrantInfo( + name, + configuration.Auth.GrantedPolicies.ContainsKey(name) + ); + } + + public Task CheckAsync(ClaimsPrincipal claimsPrincipal, string name) + { + return CheckAsync(name); + } + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo.Abp.AspNetCore.Mvc.Contracts.csproj b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo.Abp.AspNetCore.Mvc.Contracts.csproj new file mode 100644 index 0000000000..8141097dc6 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo.Abp.AspNetCore.Mvc.Contracts.csproj @@ -0,0 +1,20 @@ + + + + + + netstandard2.0 + Volo.Abp.AspNetCore.Mvc.Contracts + Volo.Abp.AspNetCore.Mvc.Contracts + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + + + + + + + + diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcContractsModule.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcContractsModule.cs new file mode 100644 index 0000000000..54a7589dd4 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcContractsModule.cs @@ -0,0 +1,13 @@ +using Volo.Abp.Application; +using Volo.Abp.Modularity; + +namespace Volo.Abp.AspNetCore.Mvc +{ + [DependsOn( + typeof(AbpDddApplicationModule) + )] + public class AbpAspNetCoreMvcContractsModule : AbpModule + { + + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationAuthConfigurationDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationAuthConfigurationDto.cs similarity index 86% rename from framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationAuthConfigurationDto.cs rename to framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationAuthConfigurationDto.cs index 4981955ba0..39b3ca394b 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationAuthConfigurationDto.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationAuthConfigurationDto.cs @@ -1,7 +1,9 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations { + [Serializable] public class ApplicationAuthConfigurationDto { public Dictionary Policies { get; set; } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationDto.cs similarity index 57% rename from framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationDto.cs rename to framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationDto.cs index d7aab53482..ecafcb3274 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationDto.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationDto.cs @@ -1,9 +1,14 @@ -namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations +using System; + +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations { + [Serializable] public class ApplicationConfigurationDto { public ApplicationLocalizationConfigurationDto Localization { get; set; } public ApplicationAuthConfigurationDto Auth { get; set; } + + public CurrentUserDto CurrentUser { get; set; } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationLocalizationConfigurationDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationLocalizationConfigurationDto.cs similarity index 83% rename from framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationLocalizationConfigurationDto.cs rename to framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationLocalizationConfigurationDto.cs index 716061d507..05b5d8c7a6 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationLocalizationConfigurationDto.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationLocalizationConfigurationDto.cs @@ -1,7 +1,9 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations { + [Serializable] public class ApplicationLocalizationConfigurationDto { public Dictionary> Values { get; set; } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/CurrentUserDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/CurrentUserDto.cs new file mode 100644 index 0000000000..702af56dba --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/CurrentUserDto.cs @@ -0,0 +1,16 @@ +using System; + +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations +{ + [Serializable] + public class CurrentUserDto + { + public bool IsAuthenticated { get; set; } + + public Guid? Id { get; set; } + + public Guid? TenantId { get; set; } + + public string UserName { get; set; } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/IApplicationConfigurationBuilder.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/IAbpApplicationConfigurationAppService.cs similarity index 57% rename from framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/IApplicationConfigurationBuilder.cs rename to framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/IAbpApplicationConfigurationAppService.cs index 2b820f48c6..4c521b9efc 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/IApplicationConfigurationBuilder.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/IAbpApplicationConfigurationAppService.cs @@ -1,8 +1,9 @@ using System.Threading.Tasks; +using Volo.Abp.Application.Services; namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations { - public interface IApplicationConfigurationBuilder + public interface IAbpApplicationConfigurationAppService : IApplicationService { Task GetAsync(); } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/AbpTagHelperLocalizer.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/AbpTagHelperLocalizer.cs new file mode 100644 index 0000000000..72d32ce10b --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/AbpTagHelperLocalizer.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Microsoft.Extensions.Localization; +using Microsoft.Extensions.Options; +using Volo.Abp.AspNetCore.Mvc.Localization; + +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers +{ + public class AbpTagHelperLocalizer : IAbpTagHelperLocalizer + { + private readonly IStringLocalizerFactory _stringLocalizerFactory; + private readonly AbpMvcDataAnnotationsLocalizationOptions _options; + + public AbpTagHelperLocalizer(IOptions options, IStringLocalizerFactory stringLocalizerFactory) + { + _stringLocalizerFactory = stringLocalizerFactory; + _options = options.Value; + } + + public string GetLocalizedText(string text, ModelExplorer explorer) + { + var localizer = GetStringLocalizer(explorer); + + return localizer == null ? text : localizer[text].Value; + } + + public IStringLocalizer GetLocalizer(ModelExplorer explorer) + { + return GetStringLocalizer(explorer); + } + + private IStringLocalizer GetStringLocalizer(ModelExplorer explorer) + { + IStringLocalizer localizer = null; + var resourceType = _options.AssemblyResources.GetOrDefault(explorer.Container.ModelType.Assembly); + + if (resourceType != null) + { + localizer = _stringLocalizerFactory.Create(resourceType); + } + + return localizer; + } + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Carousel/AbpCarouselTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Carousel/AbpCarouselTagHelperService.cs index 749218cab0..ffba80fb6f 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Carousel/AbpCarouselTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Carousel/AbpCarouselTagHelperService.cs @@ -127,7 +127,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Carousel { if (string.IsNullOrWhiteSpace(TagHelper.Id)) { - TagHelper.Id = Guid.NewGuid().ToString("N"); + TagHelper.Id = "C" + Guid.NewGuid().ToString("N"); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Collapse/AbpAccordionItemTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Collapse/AbpAccordionItemTagHelperService.cs index fabdb36452..1f9f57c567 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Collapse/AbpAccordionItemTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Collapse/AbpAccordionItemTagHelperService.cs @@ -59,7 +59,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Collapse { if (string.IsNullOrWhiteSpace(TagHelper.Id)) { - TagHelper.Id = Guid.NewGuid().ToString("N"); + TagHelper.Id = "A" + Guid.NewGuid().ToString("N"); } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Collapse/AbpAccordionTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Collapse/AbpAccordionTagHelperService.cs index 1d0a9234fd..20e62604a9 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Collapse/AbpAccordionTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Collapse/AbpAccordionTagHelperService.cs @@ -55,7 +55,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Collapse { if (string.IsNullOrWhiteSpace(TagHelper.Id)) { - TagHelper.Id = Guid.NewGuid().ToString("N"); + TagHelper.Id = "A" + Guid.NewGuid().ToString("N"); } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelper.cs index a6c5c80c06..141d995fd7 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelper.cs @@ -18,6 +18,8 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form public bool? SubmitButton { get; set; } + public bool? RequiredSymbols { get; set; } = true; + #region MvcFormTagHelperAttiributes private const string ActionAttributeName = "asp-action"; diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelperService.cs index 3d7504f72d..fa3ec9cce6 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelperService.cs @@ -192,6 +192,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form var abpInputTagHelper = _serviceProvider.GetRequiredService(); abpInputTagHelper.AspFor = model; abpInputTagHelper.ViewContext = TagHelper.ViewContext; + abpInputTagHelper.DisplayRequiredSymbol = TagHelper.RequiredSymbols ?? true; RenderTagHelper(new TagHelperAttributeList(), context, abpInputTagHelper, _htmlEncoder, "div", TagMode.StartTagAndEndTag); } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelper.cs index 8bd4021458..0c5ac5dd22 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelper.cs @@ -17,12 +17,15 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form public bool IsDisabled { get; set; } = false; [HtmlAttributeName("readonly")] - public AbpReadonlyInputType IsReadonly { get; set; } = AbpReadonlyInputType.False; + public bool? IsReadonly { get; set; } = false; public bool AutoFocus { get; set; } public AbpFormControlSize Size { get; set; } = AbpFormControlSize.Default; + [HtmlAttributeNotBound] + public bool DisplayRequiredSymbol { get; set; } = true; + [HtmlAttributeNotBound] [ViewContext] public ViewContext ViewContext { get; set; } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs index a4a0193e97..48cf748f24 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using System.Linq; using System.Text.Encodings.Web; using Microsoft.AspNetCore.Mvc.TagHelpers; @@ -16,15 +17,13 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form { private readonly IHtmlGenerator _generator; private readonly HtmlEncoder _encoder; - private readonly IStringLocalizerFactory _stringLocalizerFactory; - private readonly AbpMvcDataAnnotationsLocalizationOptions _options; + private readonly IAbpTagHelperLocalizer _tagHelperLocalizer; - public AbpInputTagHelperService(IHtmlGenerator generator, HtmlEncoder encoder, IOptions options, IStringLocalizerFactory stringLocalizerFactory) + public AbpInputTagHelperService(IHtmlGenerator generator, HtmlEncoder encoder, IAbpTagHelperLocalizer tagHelperLocalizer) { _generator = generator; _encoder = encoder; - _stringLocalizerFactory = stringLocalizerFactory; - _options = options.Value; + _tagHelperLocalizer = tagHelperLocalizer; } public override void Process(TagHelperContext context, TagHelperOutput output) @@ -51,6 +50,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form output.TagName = "div"; LeaveOnlyGroupAttributes(context, output); output.Attributes.AddClass(isCheckbox ? "form-check" : "form-group"); + output.Attributes.AddClass(isCheckbox ? "mb-2" : ""); output.Content.SetHtmlContent(output.Content.GetContent() + innerHtml); } } @@ -69,7 +69,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form protected virtual string GetValidationAsHtml(TagHelperContext context, TagHelperOutput output, TagHelperOutput inputTag) { - if (inputTag.Attributes.Any(a => a.Name.ToLowerInvariant() == "type" && a.Value.ToString().ToLowerInvariant() == "hidden")) + if (IsOutputHidden(inputTag)) { return ""; } @@ -142,16 +142,11 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form private void AddFormControlClass(TagHelperContext context, TagHelperOutput output, bool isCheckbox, TagHelperOutput inputTagHelperOutput) { var className = "form-control"; - var readonlyAttribute = GetAttribute(TagHelper.AspFor.ModelExplorer); if (isCheckbox) { className = "form-check-input"; } - else if (TagHelper.IsReadonly == AbpReadonlyInputType.True_PlainText || (readonlyAttribute != null && readonlyAttribute.PlainText)) - { - className = "form-control-plaintext"; - } inputTagHelperOutput.Attributes.AddClass(className + " " + GetSize(context, output)); } @@ -176,7 +171,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form protected virtual void AddReadOnlyAttribute(TagHelperOutput inputTagHelperOutput) { if (inputTagHelperOutput.Attributes.ContainsName("readonly") == false && - (TagHelper.IsReadonly != AbpReadonlyInputType.False || GetAttribute(TagHelper.AspFor.ModelExplorer) != null)) + (TagHelper.IsReadonly != false || GetAttribute(TagHelper.AspFor.ModelExplorer) != null)) { inputTagHelperOutput.Attributes.Add("readonly", ""); } @@ -193,7 +188,9 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form if (attribute != null) { - inputTagHelperOutput.Attributes.Add("placeholder", LocalizeText(attribute.Value)); + var placeholderLocalized = _tagHelperLocalizer.GetLocalizedText(attribute.Value, TagHelper.AspFor.ModelExplorer); + + inputTagHelperOutput.Attributes.Add("placeholder", placeholderLocalized); } } @@ -211,20 +208,9 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form return; } - inputTagHelperOutput.Attributes.Add("aria-describedby", LocalizeText(idAttr.Value + "InfoText")); - } - - protected virtual string LocalizeText(string text) - { - IStringLocalizer localizer = null; - var resourceType = _options.AssemblyResources.GetOrDefault(TagHelper.AspFor.ModelExplorer.ModelType.Assembly); - - if (resourceType != null) - { - localizer = _stringLocalizerFactory.Create(resourceType); - } + var infoText = _tagHelperLocalizer.GetLocalizedText(idAttr.Value + "InfoText", TagHelper.AspFor.ModelExplorer); - return localizer == null? text: localizer[text].Value; + inputTagHelperOutput.Attributes.Add("aria-describedby", infoText); } protected virtual bool IsInputCheckbox(TagHelperContext context, TagHelperOutput output, TagHelperAttributeList attributes) @@ -234,31 +220,46 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form protected virtual string GetLabelAsHtml(TagHelperContext context, TagHelperOutput output, TagHelperOutput inputTag, bool isCheckbox) { - if (inputTag.Attributes.Any(a => a.Name.ToLowerInvariant() == "type" && a.Value.ToString().ToLowerInvariant() == "hidden")) + if (IsOutputHidden(inputTag)) { return ""; } if (string.IsNullOrEmpty(TagHelper.Label)) { - return GetLabelAsHtmlUsingTagHelper(context, output, isCheckbox); + return GetLabelAsHtmlUsingTagHelper(context, output, isCheckbox) + GetRequiredSymbol(context, output, inputTag); } var checkboxClass = isCheckbox ? "class=\"form-check-label\" " : ""; return ""; + "" + GetRequiredSymbol(context, output, inputTag); + } + + protected virtual string GetRequiredSymbol(TagHelperContext context, TagHelperOutput output, TagHelperOutput inputTag) + { + if (!TagHelper.DisplayRequiredSymbol) + { + return ""; + } + + return GetAttribute(TagHelper.AspFor.ModelExplorer) != null ? " * ":""; } protected virtual string GetInfoAsHtml(TagHelperContext context, TagHelperOutput output, TagHelperOutput inputTag, bool isCheckbox) { + if (IsOutputHidden(inputTag)) + { + return ""; + } + if (isCheckbox) { return ""; } - string text = ""; + var text = ""; if (!string.IsNullOrEmpty(TagHelper.InfoText)) { @@ -278,9 +279,10 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form } var idAttr = inputTag.Attributes.FirstOrDefault(a => a.Name == "id"); + var localizedText = _tagHelperLocalizer.GetLocalizedText(text, TagHelper.AspFor.ModelExplorer); return "" + - LocalizeText(text) + + localizedText + ""; } @@ -375,5 +377,10 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form return ""; } + + protected virtual bool IsOutputHidden(TagHelperOutput inputTag) + { + return inputTag.Attributes.Any(a => a.Name.ToLowerInvariant() == "type" && a.Value.ToString().ToLowerInvariant() == "hidden"); + } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpRadioInputTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpRadioInputTagHelperService.cs index 13585eeaf2..f67eafe791 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpRadioInputTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpRadioInputTagHelperService.cs @@ -14,18 +14,17 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form { public class AbpRadioInputTagHelperService : AbpTagHelperService { - private readonly AbpMvcDataAnnotationsLocalizationOptions _options; - private readonly IStringLocalizerFactory _stringLocalizerFactory; + private readonly IAbpTagHelperLocalizer _tagHelperLocalizer; - public AbpRadioInputTagHelperService(IOptions options, IStringLocalizerFactory stringLocalizerFactory) + public AbpRadioInputTagHelperService(IAbpTagHelperLocalizer tagHelperLocalizer) { - _options = options.Value; - _stringLocalizerFactory = stringLocalizerFactory; + _tagHelperLocalizer = tagHelperLocalizer; } public override void Process(TagHelperContext context, TagHelperOutput output) { var selectItems = GetSelectItems(context,output); + SetSelectedValue(context, output, selectItems); var order = GetInputOrder(TagHelper.AspFor.ModelExplorer); @@ -71,34 +70,33 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form protected virtual List GetSelectItems(TagHelperContext context, TagHelperOutput output) { - var selectItems = TagHelper.AspItems?.ToList(); + if (TagHelper.AspItems != null) + { + return TagHelper.AspItems.ToList(); + } - if (TagHelper.AspItems == null && - !GetSelectItemsIfProvidedByEnum(context, output, TagHelper.AspFor.ModelExplorer, out selectItems) && - !GetSelectItemsIfProvidedFromAttribute(context, output, TagHelper.AspFor.ModelExplorer, out selectItems)) + if (TagHelper.AspFor.ModelExplorer.Metadata.IsEnum) { - throw new Exception("No items provided for select attribute."); + return GetSelectItemsFromEnum(context, output, TagHelper.AspFor.ModelExplorer); } - SetSelectedValue(context, output, selectItems); + var selectItemsAttribute = GetAttribute(TagHelper.AspFor.ModelExplorer); + if (selectItemsAttribute != null) + { + return GetSelectItemsFromAttribute(selectItemsAttribute, TagHelper.AspFor.ModelExplorer); + } - return selectItems; + throw new Exception("No items provided for select attribute."); } - protected virtual bool GetSelectItemsIfProvidedByEnum(TagHelperContext context, TagHelperOutput output, ModelExplorer explorer, out List selectItems) + protected virtual List GetSelectItemsFromEnum(TagHelperContext context, TagHelperOutput output, ModelExplorer explorer) { - IStringLocalizer localizer = null; - var resourceType = _options.AssemblyResources.GetOrDefault(explorer.ModelType.Assembly); + var localizer = _tagHelperLocalizer.GetLocalizer(explorer); - if (resourceType != null) - { - localizer = _stringLocalizerFactory.Create(resourceType); - } - - selectItems = explorer.Metadata.IsEnum ? explorer.ModelType.GetTypeInfo().GetMembers(BindingFlags.Public | BindingFlags.Static) + var selectItems = explorer.Metadata.IsEnum ? explorer.ModelType.GetTypeInfo().GetMembers(BindingFlags.Public | BindingFlags.Static) .Select((t, i) => new SelectListItem { Value = i.ToString(), Text = GetLocalizedPropertyName(localizer, explorer.ModelType, t.Name) }).ToList() : null; - return selectItems != null; + return selectItems; } protected virtual string GetLocalizedPropertyName(IStringLocalizer localizer, Type enumType, string propertyName) @@ -113,11 +111,18 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form return !localizedString.ResourceNotFound ? localizedString.Value : localizer[propertyName].Value; } - protected virtual bool GetSelectItemsIfProvidedFromAttribute(TagHelperContext context, TagHelperOutput output, ModelExplorer explorer, out List selectItems) + protected virtual List GetSelectItemsFromAttribute( + SelectItems selectItemsAttribute, + ModelExplorer explorer) { - selectItems = GetAttribute(explorer)?.GetItems(explorer)?.ToList(); + var selectItems = selectItemsAttribute.GetItems(explorer)?.ToList(); - return selectItems != null; + if (selectItems == null) + { + return new List(); + } + + return selectItems; } protected virtual void SetSelectedValue(TagHelperContext context, TagHelperOutput output, List selectItems) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelper.cs index 8a1890f4dc..f8cdd35a7e 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelper.cs @@ -18,6 +18,9 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form [HtmlAttributeName("info")] public string InfoText { get; set; } + [HtmlAttributeNotBound] + public bool DisplayRequiredSymbol { get; set; } = true; + [HtmlAttributeNotBound] [ViewContext] public ViewContext ViewContext { get; set; } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelperService.cs index 5437cf0bc8..6176472406 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelperService.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using System.Linq; using System.Reflection; using System.Text.Encodings.Web; @@ -19,15 +20,13 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form { private readonly IHtmlGenerator _generator; private readonly HtmlEncoder _encoder; - private readonly IStringLocalizerFactory _stringLocalizerFactory; - private readonly AbpMvcDataAnnotationsLocalizationOptions _options; + private readonly IAbpTagHelperLocalizer _tagHelperLocalizer; - public AbpSelectTagHelperService(IHtmlGenerator generator, HtmlEncoder encoder, IOptions options, IStringLocalizerFactory stringLocalizerFactory) + public AbpSelectTagHelperService(IHtmlGenerator generator, HtmlEncoder encoder, IAbpTagHelperLocalizer tagHelperLocalizer) { _generator = generator; _encoder = encoder; - _stringLocalizerFactory = stringLocalizerFactory; - _options = options.Value; + _tagHelperLocalizer = tagHelperLocalizer; } public override void Process(TagHelperContext context, TagHelperOutput output) @@ -57,10 +56,10 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form var selectTag = GetSelectTag(context, output); var selectAsHtml = RenderTagHelperOutput(selectTag, _encoder); var label = GetLabelAsHtml(context, output, selectTag); - var validation = GetValidationAsHtml(context, output, selectTag); + var validation = GetValidationAsHtml(context, output, selectTag); var infoText = GetInfoAsHtml(context, output, selectTag); - return label + Environment.NewLine + selectAsHtml + Environment.NewLine + infoText+ Environment.NewLine + validation; + return label + Environment.NewLine + selectAsHtml + Environment.NewLine + infoText + Environment.NewLine + validation; } protected virtual string SurroundInnerHtmlAndGet(TagHelperContext context, TagHelperOutput output, string innerHtml) @@ -80,7 +79,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form var selectTagHelperOutput = GetInnerTagHelper(GetInputAttributes(context, output), context, selectTagHelper, "select", TagMode.StartTagAndEndTag); selectTagHelperOutput.Attributes.AddClass("form-control"); - selectTagHelperOutput.Attributes.AddClass(GetSize(context,output)); + selectTagHelperOutput.Attributes.AddClass(GetSize(context, output)); AddDisabledAttribute(selectTagHelperOutput); AddInfoTextId(selectTagHelperOutput); @@ -99,26 +98,44 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form protected virtual List GetSelectItems(TagHelperContext context, TagHelperOutput output) { - var selectItems = TagHelper.AspItems?.ToList(); + if (TagHelper.AspItems != null) + { + return TagHelper.AspItems.ToList(); + } - if (TagHelper.AspItems == null && - !GetSelectItemsIfProvidedByEnum(context, output, TagHelper.AspFor.ModelExplorer, out selectItems) && - !GetSelectItemsIfProvidedFromAttribute(context, output, TagHelper.AspFor.ModelExplorer, out selectItems)) + if (TagHelper.AspFor.ModelExplorer.Metadata.IsEnum) { - throw new Exception("No items provided for select attribute."); + return GetSelectItemsFromEnum(context, output, TagHelper.AspFor.ModelExplorer); } - return selectItems; + var selectItemsAttribute = GetAttribute(TagHelper.AspFor.ModelExplorer); + if (selectItemsAttribute != null) + { + return GetSelectItemsFromAttribute(selectItemsAttribute, TagHelper.AspFor.ModelExplorer); + } + + throw new Exception("No items provided for select attribute."); } protected virtual string GetLabelAsHtml(TagHelperContext context, TagHelperOutput output, TagHelperOutput selectTag) { if (!string.IsNullOrEmpty(TagHelper.Label)) { - return ""; + return "" + GetRequiredSymbol(context, output); + } + + return GetLabelAsHtmlUsingTagHelper(context, output) + GetRequiredSymbol(context, output); + } + + + protected virtual string GetRequiredSymbol(TagHelperContext context, TagHelperOutput output) + { + if (!TagHelper.DisplayRequiredSymbol) + { + return ""; } - return GetLabelAsHtmlUsingTagHelper(context, output); + return GetAttribute(TagHelper.AspFor.ModelExplorer) != null ? " * " : ""; } protected virtual void AddInfoTextId(TagHelperOutput inputTagHelperOutput) @@ -135,12 +152,14 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form return; } - inputTagHelperOutput.Attributes.Add("aria-describedby", LocalizeText(idAttr.Value + "InfoText")); + var infoText = _tagHelperLocalizer.GetLocalizedText(idAttr.Value + "InfoText", TagHelper.AspFor.ModelExplorer); + + inputTagHelperOutput.Attributes.Add("aria-describedby", infoText); } protected virtual string GetInfoAsHtml(TagHelperContext context, TagHelperOutput output, TagHelperOutput inputTag) { - string text = ""; + var text = ""; if (!string.IsNullOrEmpty(TagHelper.InfoText)) { @@ -160,40 +179,21 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form } var idAttr = inputTag.Attributes.FirstOrDefault(a => a.Name == "id"); + var localizedText = _tagHelperLocalizer.GetLocalizedText(text, TagHelper.AspFor.ModelExplorer); return "" + - LocalizeText(text) + + localizedText + ""; } - protected virtual string LocalizeText(string text) + protected virtual List GetSelectItemsFromEnum(TagHelperContext context, TagHelperOutput output, ModelExplorer explorer) { - var localizer = GetLocalizer(); - - return localizer == null ? text : localizer[text].Value; - } + var localizer = _tagHelperLocalizer.GetLocalizer(explorer); - protected virtual IStringLocalizer GetLocalizer() - { - IStringLocalizer localizer = null; - var resourceType = _options.AssemblyResources.GetOrDefault(TagHelper.AspFor.ModelExplorer.ModelType.Assembly); - - if (resourceType != null) - { - localizer = _stringLocalizerFactory.Create(resourceType); - } - - return localizer; - } - - protected virtual bool GetSelectItemsIfProvidedByEnum(TagHelperContext context, TagHelperOutput output, ModelExplorer explorer, out List selectItems) - { - var localizer = GetLocalizer(); - - selectItems = explorer.Metadata.IsEnum ? explorer.ModelType.GetTypeInfo().GetMembers(BindingFlags.Public | BindingFlags.Static) + var selectItems = explorer.Metadata.IsEnum ? explorer.ModelType.GetTypeInfo().GetMembers(BindingFlags.Public | BindingFlags.Static) .Select((t, i) => new SelectListItem { Value = i.ToString(), Text = GetLocalizedPropertyName(localizer, explorer.ModelType, t.Name) }).ToList() : null; - return selectItems != null; + return selectItems; } protected virtual string GetLocalizedPropertyName(IStringLocalizer localizer, Type enumType, string propertyName) @@ -208,11 +208,18 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form return !localizedString.ResourceNotFound ? localizedString.Value : localizer[propertyName].Value; } - protected virtual bool GetSelectItemsIfProvidedFromAttribute(TagHelperContext context, TagHelperOutput output, ModelExplorer explorer, out List selectItems) + protected virtual List GetSelectItemsFromAttribute( + SelectItems selectItemsAttribute, + ModelExplorer explorer) { - selectItems = GetAttribute(explorer)?.GetItems(explorer)?.ToList(); + var selectItems = selectItemsAttribute.GetItems(explorer)?.ToList(); - return selectItems != null; + if (selectItems == null) + { + return new List(); + } + + return selectItems; } protected virtual string GetLabelAsHtmlUsingTagHelper(TagHelperContext context, TagHelperOutput output) @@ -228,11 +235,6 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form protected virtual string GetValidationAsHtml(TagHelperContext context, TagHelperOutput output, TagHelperOutput inputTag) { - if (inputTag.Attributes.Any(a => a.Name.ToLowerInvariant() == "type" && a.Value.ToString().ToLowerInvariant() == "hidden")) - { - return ""; - } - var validationMessageTagHelper = new ValidationMessageTagHelper(_generator) { For = TagHelper.AspFor, diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/IAbpTagHelperLocalizer.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/IAbpTagHelperLocalizer.cs new file mode 100644 index 0000000000..8107f58d23 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/IAbpTagHelperLocalizer.cs @@ -0,0 +1,13 @@ +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Microsoft.Extensions.Localization; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers +{ + public interface IAbpTagHelperLocalizer : ITransientDependency + { + string GetLocalizedText(string text, ModelExplorer explorer); + + IStringLocalizer GetLocalizer(ModelExplorer explorer); + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Nav/AbpNavbarToggleTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Nav/AbpNavbarToggleTagHelperService.cs index 400769a992..1b53894ac2 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Nav/AbpNavbarToggleTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Nav/AbpNavbarToggleTagHelperService.cs @@ -28,7 +28,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Nav { if (string.IsNullOrWhiteSpace(TagHelper.Id)) { - TagHelper.Id = Guid.NewGuid().ToString("N"); + TagHelper.Id = "N" + Guid.NewGuid().ToString("N"); } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Pagination/AbpPaginationTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Pagination/AbpPaginationTagHelperService.cs index 6fda6ccd63..629cad0b66 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Pagination/AbpPaginationTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Pagination/AbpPaginationTagHelperService.cs @@ -158,7 +158,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Pagination pagerInfo + "
\r\n" + "