diff --git a/modules/cms-kit/host/Volo.CmsKit.Host.Shared/FeatureConfigurer.cs b/modules/cms-kit/host/Volo.CmsKit.Host.Shared/FeatureConfigurer.cs index 9046c60aca..b108d08b02 100644 --- a/modules/cms-kit/host/Volo.CmsKit.Host.Shared/FeatureConfigurer.cs +++ b/modules/cms-kit/host/Volo.CmsKit.Host.Shared/FeatureConfigurer.cs @@ -1,5 +1,6 @@ using Volo.Abp.GlobalFeatures; using Volo.Abp.Threading; +using Volo.CmsKit.GlobalFeatures; namespace Volo.CmsKit { @@ -12,6 +13,7 @@ namespace Volo.CmsKit OneTimeRunner.Run(() => { GlobalFeatureManager.Instance.Modules.CmsKit().EnableAll(); + GlobalFeatureManager.Instance.Modules.CmsKit().Disable(); }); } } diff --git a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20210209082122_UrlSlugToSlug.Designer.cs b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20210209082122_UrlSlugToSlug.Designer.cs new file mode 100644 index 0000000000..ef06bb2fd9 --- /dev/null +++ b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20210209082122_UrlSlugToSlug.Designer.cs @@ -0,0 +1,1891 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Volo.Abp.EntityFrameworkCore; +using Volo.CmsKit.EntityFrameworkCore; + +namespace Volo.CmsKit.Migrations +{ + [DbContext(typeof(UnifiedDbContext))] + [Migration("20210209082122_UrlSlugToSlug")] + partial class UrlSlugToSlug + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .UseIdentityColumns() + .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("ProductVersion", "5.0.2"); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ApplicationName") + .HasMaxLength(96) + .HasColumnType("nvarchar(96)") + .HasColumnName("ApplicationName"); + + b.Property("BrowserInfo") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)") + .HasColumnName("BrowserInfo"); + + b.Property("ClientId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("ClientId"); + + b.Property("ClientIpAddress") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("ClientIpAddress"); + + b.Property("ClientName") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("ClientName"); + + b.Property("Comments") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("Comments"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CorrelationId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("CorrelationId"); + + b.Property("Exceptions") + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)") + .HasColumnName("Exceptions"); + + b.Property("ExecutionDuration") + .HasColumnType("int") + .HasColumnName("ExecutionDuration"); + + b.Property("ExecutionTime") + .HasColumnType("datetime2"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("HttpMethod") + .HasMaxLength(16) + .HasColumnType("nvarchar(16)") + .HasColumnName("HttpMethod"); + + b.Property("HttpStatusCode") + .HasColumnType("int") + .HasColumnName("HttpStatusCode"); + + b.Property("ImpersonatorTenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("ImpersonatorTenantId"); + + b.Property("ImpersonatorUserId") + .HasColumnType("uniqueidentifier") + .HasColumnName("ImpersonatorUserId"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("TenantName") + .HasColumnType("nvarchar(max)"); + + b.Property("Url") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("Url"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier") + .HasColumnName("UserId"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("UserName"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "ExecutionTime"); + + b.HasIndex("TenantId", "UserId", "ExecutionTime"); + + b.ToTable("AbpAuditLogs"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogAction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AuditLogId") + .HasColumnType("uniqueidentifier") + .HasColumnName("AuditLogId"); + + b.Property("ExecutionDuration") + .HasColumnType("int") + .HasColumnName("ExecutionDuration"); + + b.Property("ExecutionTime") + .HasColumnType("datetime2") + .HasColumnName("ExecutionTime"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("MethodName") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("MethodName"); + + b.Property("Parameters") + .HasMaxLength(2000) + .HasColumnType("nvarchar(2000)") + .HasColumnName("Parameters"); + + b.Property("ServiceName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("ServiceName"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("AuditLogId"); + + b.HasIndex("TenantId", "ServiceName", "MethodName", "ExecutionTime"); + + b.ToTable("AbpAuditLogActions"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AuditLogId") + .HasColumnType("uniqueidentifier") + .HasColumnName("AuditLogId"); + + b.Property("ChangeTime") + .HasColumnType("datetime2") + .HasColumnName("ChangeTime"); + + b.Property("ChangeType") + .HasColumnType("tinyint") + .HasColumnName("ChangeType"); + + b.Property("EntityId") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("EntityId"); + + b.Property("EntityTenantId") + .HasColumnType("uniqueidentifier"); + + b.Property("EntityTypeFullName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("EntityTypeFullName"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("AuditLogId"); + + b.HasIndex("TenantId", "EntityTypeFullName", "EntityId"); + + b.ToTable("AbpEntityChanges"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityPropertyChange", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("EntityChangeId") + .HasColumnType("uniqueidentifier"); + + b.Property("NewValue") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)") + .HasColumnName("NewValue"); + + b.Property("OriginalValue") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)") + .HasColumnName("OriginalValue"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("PropertyName"); + + b.Property("PropertyTypeFullName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("PropertyTypeFullName"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("EntityChangeId"); + + b.ToTable("AbpEntityPropertyChanges"); + }); + + modelBuilder.Entity("Volo.Abp.BlobStoring.Database.DatabaseBlob", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("ContainerId") + .HasColumnType("uniqueidentifier"); + + b.Property("Content") + .HasMaxLength(2147483647) + .HasColumnType("varbinary(max)"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("ContainerId"); + + b.HasIndex("TenantId", "ContainerId", "Name"); + + b.ToTable("AbpBlobs"); + }); + + modelBuilder.Entity("Volo.Abp.BlobStoring.Database.DatabaseBlobContainer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Name"); + + b.ToTable("AbpBlobContainers"); + }); + + modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureValue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderKey") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ProviderName") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey"); + + b.ToTable("AbpFeatureValues"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityClaimType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("Description") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsStatic") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("Regex") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("RegexDescription") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Required") + .HasColumnType("bit"); + + b.Property("ValueType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("AbpClaimTypes"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityLinkUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("SourceTenantId") + .HasColumnType("uniqueidentifier"); + + b.Property("SourceUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("TargetTenantId") + .HasColumnType("uniqueidentifier"); + + b.Property("TargetUserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("SourceUserId", "SourceTenantId", "TargetUserId", "TargetTenantId") + .IsUnique() + .HasFilter("[SourceTenantId] IS NOT NULL AND [TargetTenantId] IS NOT NULL"); + + b.ToTable("AbpLinkUsers"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDefault") + .HasColumnType("bit") + .HasColumnName("IsDefault"); + + b.Property("IsPublic") + .HasColumnType("bit") + .HasColumnName("IsPublic"); + + b.Property("IsStatic") + .HasColumnType("bit") + .HasColumnName("IsStatic"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName"); + + b.ToTable("AbpRoles"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ClaimType") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ClaimValue") + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AbpRoleClaims"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentitySecurityLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Action") + .HasMaxLength(96) + .HasColumnType("nvarchar(96)"); + + b.Property("ApplicationName") + .HasMaxLength(96) + .HasColumnType("nvarchar(96)"); + + b.Property("BrowserInfo") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("ClientId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ClientIpAddress") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CorrelationId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("CreationTime") + .HasColumnType("datetime2"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("Identity") + .HasMaxLength(96) + .HasColumnType("nvarchar(96)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("TenantName") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Action"); + + b.HasIndex("TenantId", "ApplicationName"); + + b.HasIndex("TenantId", "Identity"); + + b.HasIndex("TenantId", "UserId"); + + b.ToTable("AbpSecurityLogs"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AccessFailedCount") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("AccessFailedCount"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("Email"); + + b.Property("EmailConfirmed") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("EmailConfirmed"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsExternal") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsExternal"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("LockoutEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("LockoutEnabled"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("Name") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("Name"); + + b.Property("NormalizedEmail") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("NormalizedEmail"); + + b.Property("NormalizedUserName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("NormalizedUserName"); + + b.Property("PasswordHash") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("PasswordHash"); + + b.Property("PhoneNumber") + .HasMaxLength(16) + .HasColumnType("nvarchar(16)") + .HasColumnName("PhoneNumber"); + + b.Property("PhoneNumberConfirmed") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("PhoneNumberConfirmed"); + + b.Property("SecurityStamp") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("SecurityStamp"); + + b.Property("Surname") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("Surname"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("TwoFactorEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("TwoFactorEnabled"); + + b.Property("UserName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("UserName"); + + 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") + .HasColumnType("uniqueidentifier"); + + b.Property("ClaimType") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ClaimValue") + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AbpUserClaims"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("LoginProvider") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ProviderDisplayName") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(196) + .HasColumnType("nvarchar(196)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("UserId", "LoginProvider"); + + b.HasIndex("LoginProvider", "ProviderKey"); + + b.ToTable("AbpUserLogins"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserOrganizationUnit", b => + { + b.Property("OrganizationUnitId") + .HasColumnType("uniqueidentifier"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("OrganizationUnitId", "UserId"); + + b.HasIndex("UserId", "OrganizationUnitId"); + + b.ToTable("AbpUserOrganizationUnits"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId", "UserId"); + + b.ToTable("AbpUserRoles"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("LoginProvider") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Name") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AbpUserTokens"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(95) + .HasColumnType("nvarchar(95)") + .HasColumnName("Code"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("DisplayName"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("ParentId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("Code"); + + b.HasIndex("ParentId"); + + b.ToTable("AbpOrganizationUnits"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnitRole", b => + { + b.Property("OrganizationUnitId") + .HasColumnType("uniqueidentifier"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("OrganizationUnitId", "RoleId"); + + b.HasIndex("RoleId", "OrganizationUnitId"); + + b.ToTable("AbpOrganizationUnitRoles"); + }); + + modelBuilder.Entity("Volo.Abp.PermissionManagement.PermissionGrant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ProviderName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey"); + + b.ToTable("AbpPermissionGrants"); + }); + + modelBuilder.Entity("Volo.Abp.SettingManagement.Setting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderKey") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ProviderName") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("nvarchar(2048)"); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey"); + + b.ToTable("AbpSettings"); + }); + + modelBuilder.Entity("Volo.Abp.TenantManagement.Tenant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.HasKey("Id"); + + b.HasIndex("Name"); + + b.ToTable("AbpTenants"); + }); + + modelBuilder.Entity("Volo.Abp.TenantManagement.TenantConnectionString", b => + { + b.Property("TenantId") + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)"); + + b.HasKey("TenantId", "Name"); + + b.ToTable("AbpTenantConnectionStrings"); + }); + + modelBuilder.Entity("Volo.CmsKit.Blogs.Blog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.ToTable("CmsBlogs"); + }); + + modelBuilder.Entity("Volo.CmsKit.Blogs.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("BlogId") + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("ShortDescription") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.HasKey("Id"); + + b.HasIndex("CreatorId"); + + b.HasIndex("DeleterId"); + + b.HasIndex("LastModifierId"); + + b.HasIndex("Slug", "BlogId"); + + b.ToTable("CmsBlogPosts"); + }); + + modelBuilder.Entity("Volo.CmsKit.Comments.Comment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("EntityId") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("EntityType") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("RepliedCommentId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("Text") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "RepliedCommentId"); + + b.HasIndex("TenantId", "EntityType", "EntityId"); + + b.ToTable("CmsComments"); + }); + + modelBuilder.Entity("Volo.CmsKit.Contents.Content", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("EntityId") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("EntityType") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(2147483647) + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "EntityType", "EntityId"); + + b.ToTable("CmsContents"); + }); + + modelBuilder.Entity("Volo.CmsKit.Pages.Page", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("Url") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Url"); + + b.ToTable("CmsPages"); + }); + + modelBuilder.Entity("Volo.CmsKit.Ratings.Rating", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("EntityId") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("EntityType") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("StarCount") + .HasColumnType("smallint"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "EntityType", "EntityId", "CreatorId"); + + b.ToTable("CmsRatings"); + }); + + modelBuilder.Entity("Volo.CmsKit.Reactions.UserReaction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("EntityId") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("EntityType") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ReactionName") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("nvarchar(32)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "EntityType", "EntityId", "ReactionName"); + + b.HasIndex("TenantId", "CreatorId", "EntityType", "EntityId", "ReactionName"); + + b.ToTable("CmsUserReactions"); + }); + + modelBuilder.Entity("Volo.CmsKit.Tags.EntityTag", b => + { + b.Property("EntityId") + .HasColumnType("nvarchar(450)"); + + b.Property("TagId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("EntityId", "TagId"); + + b.HasIndex("TenantId", "EntityId", "TagId"); + + b.ToTable("CmsEntityTags"); + }); + + modelBuilder.Entity("Volo.CmsKit.Tags.Tag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("EntityType") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("nvarchar(32)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Name"); + + b.ToTable("CmsTags"); + }); + + modelBuilder.Entity("Volo.CmsKit.Users.CmsUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("Email"); + + b.Property("EmailConfirmed") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("EmailConfirmed"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("Name") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("Name"); + + b.Property("PhoneNumber") + .HasMaxLength(16) + .HasColumnType("nvarchar(16)") + .HasColumnName("PhoneNumber"); + + b.Property("PhoneNumberConfirmed") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("PhoneNumberConfirmed"); + + b.Property("Surname") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("Surname"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("UserName"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Email"); + + b.HasIndex("TenantId", "UserName"); + + b.ToTable("CmsUsers"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogAction", b => + { + b.HasOne("Volo.Abp.AuditLogging.AuditLog", null) + .WithMany("Actions") + .HasForeignKey("AuditLogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => + { + b.HasOne("Volo.Abp.AuditLogging.AuditLog", null) + .WithMany("EntityChanges") + .HasForeignKey("AuditLogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityPropertyChange", b => + { + b.HasOne("Volo.Abp.AuditLogging.EntityChange", null) + .WithMany("PropertyChanges") + .HasForeignKey("EntityChangeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.BlobStoring.Database.DatabaseBlob", b => + { + b.HasOne("Volo.Abp.BlobStoring.Database.DatabaseBlobContainer", null) + .WithMany() + .HasForeignKey("ContainerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => + { + b.HasOne("Volo.Abp.Identity.IdentityRole", null) + .WithMany("Claims") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserClaim", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Claims") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Logins") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserOrganizationUnit", b => + { + b.HasOne("Volo.Abp.Identity.OrganizationUnit", null) + .WithMany() + .HasForeignKey("OrganizationUnitId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("OrganizationUnits") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => + { + b.HasOne("Volo.Abp.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Roles") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Tokens") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnit", b => + { + b.HasOne("Volo.Abp.Identity.OrganizationUnit", null) + .WithMany() + .HasForeignKey("ParentId"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnitRole", b => + { + b.HasOne("Volo.Abp.Identity.OrganizationUnit", null) + .WithMany("Roles") + .HasForeignKey("OrganizationUnitId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.TenantManagement.TenantConnectionString", b => + { + b.HasOne("Volo.Abp.TenantManagement.Tenant", null) + .WithMany("ConnectionStrings") + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.CmsKit.Blogs.BlogPost", b => + { + b.HasOne("Volo.CmsKit.Users.CmsUser", "Creator") + .WithMany() + .HasForeignKey("CreatorId"); + + b.HasOne("Volo.CmsKit.Users.CmsUser", "Deleter") + .WithMany() + .HasForeignKey("DeleterId"); + + b.HasOne("Volo.CmsKit.Users.CmsUser", "LastModifier") + .WithMany() + .HasForeignKey("LastModifierId"); + + b.Navigation("Creator"); + + b.Navigation("Deleter"); + + b.Navigation("LastModifier"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLog", b => + { + b.Navigation("Actions"); + + b.Navigation("EntityChanges"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => + { + b.Navigation("PropertyChanges"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRole", b => + { + b.Navigation("Claims"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUser", b => + { + b.Navigation("Claims"); + + b.Navigation("Logins"); + + b.Navigation("OrganizationUnits"); + + b.Navigation("Roles"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnit", b => + { + b.Navigation("Roles"); + }); + + modelBuilder.Entity("Volo.Abp.TenantManagement.Tenant", b => + { + b.Navigation("ConnectionStrings"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20210209082122_UrlSlugToSlug.cs b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20210209082122_UrlSlugToSlug.cs new file mode 100644 index 0000000000..d4ee6baf56 --- /dev/null +++ b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20210209082122_UrlSlugToSlug.cs @@ -0,0 +1,106 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Volo.CmsKit.Migrations +{ + public partial class UrlSlugToSlug : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "CmsBlogPosts", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + BlogId = table.Column(type: "uniqueidentifier", nullable: false), + Title = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), + Slug = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + ShortDescription = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true), + ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: true), + CreationTime = table.Column(type: "datetime2", nullable: false), + CreatorId = table.Column(type: "uniqueidentifier", nullable: true), + LastModificationTime = table.Column(type: "datetime2", nullable: true), + LastModifierId = table.Column(type: "uniqueidentifier", nullable: true), + IsDeleted = table.Column(type: "bit", nullable: false, defaultValue: false), + DeleterId = table.Column(type: "uniqueidentifier", nullable: true), + DeletionTime = table.Column(type: "datetime2", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_CmsBlogPosts", x => x.Id); + table.ForeignKey( + name: "FK_CmsBlogPosts_CmsUsers_CreatorId", + column: x => x.CreatorId, + principalTable: "CmsUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_CmsBlogPosts_CmsUsers_DeleterId", + column: x => x.DeleterId, + principalTable: "CmsUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_CmsBlogPosts_CmsUsers_LastModifierId", + column: x => x.LastModifierId, + principalTable: "CmsUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "CmsBlogs", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + Name = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), + Slug = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true), + ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: true), + CreationTime = table.Column(type: "datetime2", nullable: false), + CreatorId = table.Column(type: "uniqueidentifier", nullable: true), + LastModificationTime = table.Column(type: "datetime2", nullable: true), + LastModifierId = table.Column(type: "uniqueidentifier", nullable: true), + IsDeleted = table.Column(type: "bit", nullable: false, defaultValue: false), + DeleterId = table.Column(type: "uniqueidentifier", nullable: true), + DeletionTime = table.Column(type: "datetime2", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_CmsBlogs", x => x.Id); + }); + + migrationBuilder.CreateIndex( + name: "IX_CmsBlogPosts_CreatorId", + table: "CmsBlogPosts", + column: "CreatorId"); + + migrationBuilder.CreateIndex( + name: "IX_CmsBlogPosts_DeleterId", + table: "CmsBlogPosts", + column: "DeleterId"); + + migrationBuilder.CreateIndex( + name: "IX_CmsBlogPosts_LastModifierId", + table: "CmsBlogPosts", + column: "LastModifierId"); + + migrationBuilder.CreateIndex( + name: "IX_CmsBlogPosts_Slug_BlogId", + table: "CmsBlogPosts", + columns: new[] { "Slug", "BlogId" }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "CmsBlogPosts"); + + migrationBuilder.DropTable( + name: "CmsBlogs"); + } + } +} diff --git a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/UnifiedDbContextModelSnapshot.cs b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/UnifiedDbContextModelSnapshot.cs index 074d655f16..bdfc4be963 100644 --- a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/UnifiedDbContextModelSnapshot.cs +++ b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/UnifiedDbContextModelSnapshot.cs @@ -19,7 +19,7 @@ namespace Volo.CmsKit.Migrations .UseIdentityColumns() .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) .HasAnnotation("Relational:MaxIdentifierLength", 128) - .HasAnnotation("ProductVersion", "5.0.1"); + .HasAnnotation("ProductVersion", "5.0.2"); modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLog", b => { @@ -1124,6 +1124,151 @@ namespace Volo.CmsKit.Migrations b.ToTable("AbpTenantConnectionStrings"); }); + modelBuilder.Entity("Volo.CmsKit.Blogs.Blog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.ToTable("CmsBlogs"); + }); + + modelBuilder.Entity("Volo.CmsKit.Blogs.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("BlogId") + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("ShortDescription") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.HasKey("Id"); + + b.HasIndex("CreatorId"); + + b.HasIndex("DeleterId"); + + b.HasIndex("LastModifierId"); + + b.HasIndex("Slug", "BlogId"); + + b.ToTable("CmsBlogPosts"); + }); + modelBuilder.Entity("Volo.CmsKit.Comments.Comment", b => { b.Property("Id") @@ -1678,6 +1823,27 @@ namespace Volo.CmsKit.Migrations .IsRequired(); }); + modelBuilder.Entity("Volo.CmsKit.Blogs.BlogPost", b => + { + b.HasOne("Volo.CmsKit.Users.CmsUser", "Creator") + .WithMany() + .HasForeignKey("CreatorId"); + + b.HasOne("Volo.CmsKit.Users.CmsUser", "Deleter") + .WithMany() + .HasForeignKey("DeleterId"); + + b.HasOne("Volo.CmsKit.Users.CmsUser", "LastModifier") + .WithMany() + .HasForeignKey("LastModifierId"); + + b.Navigation("Creator"); + + b.Navigation("Deleter"); + + b.Navigation("LastModifier"); + }); + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLog", b => { b.Navigation("Actions"); diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Blogs/BlogDto.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Blogs/BlogDto.cs new file mode 100644 index 0000000000..6bf7adcfaa --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Blogs/BlogDto.cs @@ -0,0 +1,19 @@ +using System; +using System.ComponentModel.DataAnnotations; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Validation; +using Volo.CmsKit.Blogs; + +namespace Volo.CmsKit.Admin.Blogs +{ + public class BlogDto : EntityDto + { + [Required] + [DynamicMaxLength(typeof(BlogConsts), nameof(BlogConsts.MaxNameLength))] + public string Name { get; set; } + + [Required] + [DynamicMaxLength(typeof(BlogConsts), nameof(BlogConsts.MaxSlugLength))] + public string Slug { get; set; } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Blogs/BlogGetListInput.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Blogs/BlogGetListInput.cs new file mode 100644 index 0000000000..e26307c6d0 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Blogs/BlogGetListInput.cs @@ -0,0 +1,9 @@ +using Volo.Abp.Application.Dtos; + +namespace Volo.CmsKit.Admin.Blogs +{ + public class BlogGetListInput : PagedAndSortedResultRequestDto + { + public string Filter { get; set; } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Blogs/BlogLookupDto.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Blogs/BlogLookupDto.cs new file mode 100644 index 0000000000..ed514d839d --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Blogs/BlogLookupDto.cs @@ -0,0 +1,10 @@ +using System; + +namespace Volo.CmsKit.Admin.Blogs +{ + public class BlogLookupDto + { + public Guid Id { get; set; } + public string Name { get; set; } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Blogs/BlogPostDto.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Blogs/BlogPostDto.cs new file mode 100644 index 0000000000..9d1e74a578 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Blogs/BlogPostDto.cs @@ -0,0 +1,15 @@ +using System; +using Volo.Abp.Application.Dtos; + +namespace Volo.CmsKit.Admin.Blogs +{ + public class BlogPostDto : EntityDto + { + public Guid BlogId { get; set; } + + public string Title { get; set; } + + public string Slug { get; set; } + public string ShortDescription { get; protected set; } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Blogs/CreateUpdateBlogPostDto.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Blogs/CreateUpdateBlogPostDto.cs new file mode 100644 index 0000000000..4c0532129d --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Blogs/CreateUpdateBlogPostDto.cs @@ -0,0 +1,27 @@ +using System; +using System.ComponentModel.DataAnnotations; +using Volo.Abp.Validation; +using Volo.CmsKit.Blogs; + +namespace Volo.CmsKit.Admin.Blogs +{ + public class CreateUpdateBlogPostDto + { + [Required] + public Guid BlogId { get; set; } + + [Required] + [DynamicMaxLength(typeof(BlogPostConsts), nameof(BlogPostConsts.MaxTitleLength))] + public string Title { get; set; } + + [Required] + [DynamicStringLength( + typeof(BlogPostConsts), + nameof(BlogPostConsts.MaxSlugLength), + nameof(BlogPostConsts.MinSlugLength))] + public string Slug { get; set; } + + [DynamicMaxLength(typeof(BlogPostConsts), nameof(BlogPostConsts.MaxShortDescriptionLength))] + public string ShortDescription { get; set; } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Blogs/IBlogAdminAppService.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Blogs/IBlogAdminAppService.cs new file mode 100644 index 0000000000..f0485cc344 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Blogs/IBlogAdminAppService.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Volo.Abp.Application.Services; + +namespace Volo.CmsKit.Admin.Blogs +{ + public interface IBlogAdminAppService : ICrudAppService + { + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Blogs/IBlogPostAdminAppService.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Blogs/IBlogPostAdminAppService.cs new file mode 100644 index 0000000000..28d614d289 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Blogs/IBlogPostAdminAppService.cs @@ -0,0 +1,22 @@ +using System; +using System.Threading.Tasks; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Application.Services; +using Volo.Abp.Content; + +namespace Volo.CmsKit.Admin.Blogs +{ + public interface IBlogPostAdminAppService + : ICrudAppService< + BlogPostDto, + Guid, + PagedAndSortedResultRequestDto, + CreateUpdateBlogPostDto> + { + Task GetBySlugAsync(string blogSlug, string slug); + + Task SetCoverImageAsync(Guid id, RemoteStreamContent streamContent); + + Task GetCoverImageAsync(Guid id); + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Tags/EntityTagSetDto.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Tags/EntityTagSetDto.cs new file mode 100644 index 0000000000..d80c1aa62a --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Tags/EntityTagSetDto.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Volo.CmsKit.Admin.Tags +{ + public class EntityTagSetDto + { + public string EntityId { get; set; } + public string EntityType { get; set; } + public List Tags { get; set; } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Tags/IEntityTagAdminAppService.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Tags/IEntityTagAdminAppService.cs index aece4d5c00..a6660a324f 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Tags/IEntityTagAdminAppService.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Tags/IEntityTagAdminAppService.cs @@ -7,5 +7,7 @@ namespace Volo.CmsKit.Admin.Tags Task AddTagToEntityAsync(EntityTagCreateDto input); Task RemoveTagFromEntityAsync(EntityTagRemoveDto input); + + Task SetEntityTagsAsync(EntityTagSetDto input); } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Permissions/CmsKitAdminPermissionDefinitionProvider.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Permissions/CmsKitAdminPermissionDefinitionProvider.cs index bc55cc14e1..0303af762e 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Permissions/CmsKitAdminPermissionDefinitionProvider.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Permissions/CmsKitAdminPermissionDefinitionProvider.cs @@ -1,4 +1,5 @@ -using Volo.Abp.Authorization.Permissions; + +using Volo.Abp.Authorization.Permissions; using Volo.Abp.GlobalFeatures; using Volo.Abp.Localization; using Volo.CmsKit.GlobalFeatures; @@ -12,7 +13,6 @@ namespace Volo.CmsKit.Permissions { var cmsGroup = context.GetGroupOrNull(CmsKitAdminPermissions.GroupName) ?? context.AddGroup(CmsKitAdminPermissions.GroupName, L("Permission:CmsKit")); - if (GlobalFeatureManager.Instance.IsEnabled()) { var contentGroup = cmsGroup.AddPermission(CmsKitAdminPermissions.Contents.Default, L("Permission:Contents")); @@ -35,6 +35,19 @@ namespace Volo.CmsKit.Permissions pageManagement.AddChild(CmsKitAdminPermissions.Pages.Update, L("Permission:PageManagement:Update")); pageManagement.AddChild(CmsKitAdminPermissions.Pages.Delete, L("Permission:PageManagement:Delete")); } + + if (GlobalFeatureManager.Instance.IsEnabled()) + { + var blogManagement = cmsGroup.AddPermission(CmsKitAdminPermissions.Blogs.Default, L("Permission:BlogManagement")); + blogManagement.AddChild(CmsKitAdminPermissions.Blogs.Create, L("Permission:BlogManagement.Create")); + blogManagement.AddChild(CmsKitAdminPermissions.Blogs.Update, L("Permission:BlogManagement.Update")); + blogManagement.AddChild(CmsKitAdminPermissions.Blogs.Delete, L("Permission:BlogManagement.Delete")); + + var blogPostManagement = cmsGroup.AddPermission(CmsKitAdminPermissions.BlogPosts.Default, L("Permission:BlogPostManagement")); + blogManagement.AddChild(CmsKitAdminPermissions.BlogPosts.Create, L("Permission:BlogPostManagement.Create")); + blogManagement.AddChild(CmsKitAdminPermissions.BlogPosts.Update, L("Permission:BlogPostManagement.Update")); + blogManagement.AddChild(CmsKitAdminPermissions.BlogPosts.Delete, L("Permission:BlogPostManagement.Delete")); + } } private static LocalizableString L(string name) diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Permissions/CmsKitAdminPermissions.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Permissions/CmsKitAdminPermissions.cs index a08ba9d6c8..38c95ed83d 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Permissions/CmsKitAdminPermissions.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Permissions/CmsKitAdminPermissions.cs @@ -29,5 +29,21 @@ namespace Volo.CmsKit.Permissions public const string Update = Default + ".Update"; public const string Delete = Default + ".Delete"; } + + public static class Blogs + { + public const string Default = GroupName + ".Blogs"; + public const string Create = Default + ".Create"; + public const string Update = Default + ".Update"; + public const string Delete = Default + ".Delete"; + } + + public static class BlogPosts + { + public const string Default = GroupName + ".BlogPosts"; + public const string Create = Default + ".Create"; + public const string Update = Default + ".Update"; + public const string Delete = Default + ".Delete"; + } } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Blogs/BlogAdminAppService.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Blogs/BlogAdminAppService.cs new file mode 100644 index 0000000000..5c833f5256 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Blogs/BlogAdminAppService.cs @@ -0,0 +1,37 @@ +using Microsoft.AspNetCore.Authorization; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp.Application.Services; +using Volo.Abp.Domain.Repositories; +using Volo.Abp.GlobalFeatures; +using Volo.CmsKit.Blogs; +using Volo.CmsKit.GlobalFeatures; +using Volo.CmsKit.Permissions; + +namespace Volo.CmsKit.Admin.Blogs +{ + [RequiresGlobalFeature(typeof(BlogsFeature))] + [Authorize(CmsKitAdminPermissions.Blogs.Default)] + public class BlogAdminAppService : CrudAppService, IBlogAdminAppService + { + public BlogAdminAppService(IRepository repository) : base(repository) + { + GetListPolicyName = CmsKitAdminPermissions.Blogs.Default; + GetPolicyName = CmsKitAdminPermissions.Blogs.Default; + CreatePolicyName = CmsKitAdminPermissions.Blogs.Create; + UpdatePolicyName = CmsKitAdminPermissions.Blogs.Update; + DeletePolicyName = CmsKitAdminPermissions.Blogs.Delete; + } + + protected override async Task> CreateFilteredQueryAsync(BlogGetListInput input) + { + var queryable = await base.CreateFilteredQueryAsync(input); + return queryable + .WhereIf( + !input.Filter.IsNullOrWhiteSpace(), + x => x.Name.ToLower().Contains(input.Filter)); + } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Blogs/BlogPostAdminAppService.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Blogs/BlogPostAdminAppService.cs new file mode 100644 index 0000000000..8fa6fb9ff7 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Blogs/BlogPostAdminAppService.cs @@ -0,0 +1,118 @@ +using Microsoft.AspNetCore.Authorization; +using System; +using System.Threading.Tasks; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Application.Services; +using Volo.Abp.BlobStoring; +using Volo.Abp.Content; +using Volo.Abp.Domain.Repositories; +using Volo.Abp.GlobalFeatures; +using Volo.Abp.Users; +using Volo.CmsKit.Blogs; +using Volo.CmsKit.GlobalFeatures; +using Volo.CmsKit.Permissions; +using Volo.CmsKit.Users; + +namespace Volo.CmsKit.Admin.Blogs +{ + [RequiresGlobalFeature(typeof(BlogsFeature))] + [Authorize(CmsKitAdminPermissions.BlogPosts.Default)] + public class BlogPostAdminAppService + : CrudAppService< + BlogPost, + BlogPostDto, + Guid, + PagedAndSortedResultRequestDto, + CreateUpdateBlogPostDto> + , IBlogPostAdminAppService + { + protected readonly IBlogPostManager BlogPostManager; + protected readonly IBlogPostRepository BlogPostRepository; + protected readonly IBlogRepository BlogRepository; + protected readonly IBlobContainer BlobContainer; + protected readonly ICmsUserLookupService UserLookupService; + + public BlogPostAdminAppService( + IRepository repository, + IBlogPostManager blogPostManager, + IBlogPostRepository blogPostRepository, + IBlogRepository blogRepository, + IBlobContainer blobContainer, + ICmsUserLookupService userLookupService) : base(repository) + { + BlogPostManager = blogPostManager; + BlogPostRepository = blogPostRepository; + BlogRepository = blogRepository; + BlobContainer = blobContainer; + UserLookupService = userLookupService; + + GetListPolicyName = CmsKitAdminPermissions.BlogPosts.Default; + GetPolicyName = CmsKitAdminPermissions.BlogPosts.Default; + CreatePolicyName = CmsKitAdminPermissions.BlogPosts.Create; + UpdatePolicyName = CmsKitAdminPermissions.BlogPosts.Update; + DeletePolicyName = CmsKitAdminPermissions.BlogPosts.Delete; + } + + public virtual async Task GetBySlugAsync(string blogSlug, string blogPostSlug) + { + var blog = await BlogRepository.GetBySlugAsync(blogSlug); + + var blogPost = await BlogPostRepository.GetBySlugAsync(blog.Id, blogPostSlug); + + return MapToGetOutputDto(blogPost); + } + + [Authorize(CmsKitAdminPermissions.BlogPosts.Create)] + public override async Task CreateAsync(CreateUpdateBlogPostDto input) + { + _ = await UserLookupService.GetByIdAsync(CurrentUser.GetId()); + + var entity = await BlogPostManager + .CreateAsync( + new BlogPost( + GuidGenerator.Create(), + input.BlogId, + input.Title, + input.Slug, + input.ShortDescription)); + + return MapToGetOutputDto(entity); + } + + [Authorize(CmsKitAdminPermissions.BlogPosts.Update)] + public override async Task UpdateAsync(Guid id, CreateUpdateBlogPostDto input) + { + var blogPost = await BlogPostRepository.GetAsync(id); + + blogPost.SetTitle(input.Title); + + if (blogPost.Slug != input.Slug) + { + await BlogPostManager.SetSlugUrlAsync(blogPost, input.Slug); + } + + MapToEntity(input, blogPost); + + await BlogPostManager.UpdateAsync(blogPost); + + return MapToGetOutputDto(blogPost); + } + + public virtual async Task SetCoverImageAsync(Guid id, RemoteStreamContent streamContent) + { + await Repository.GetAsync(id); + + using (var stream = streamContent.GetStream()) + { + await BlobContainer.SaveAsync(id.ToString(), stream, overrideExisting: true); + } + } + + public virtual async Task GetCoverImageAsync(Guid id) + { + var stream = await BlobContainer.GetAsync(id.ToString()); + + return new RemoteStreamContent(stream); + } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/CmsKitAdminApplicationAutoMapperProfile.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/CmsKitAdminApplicationAutoMapperProfile.cs index a7ea1b5124..8fc2567093 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/CmsKitAdminApplicationAutoMapperProfile.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/CmsKitAdminApplicationAutoMapperProfile.cs @@ -1,6 +1,8 @@ using AutoMapper; +using Volo.CmsKit.Admin.Blogs; using Volo.CmsKit.Admin.Contents; using Volo.CmsKit.Admin.Pages; +using Volo.CmsKit.Blogs; using Volo.CmsKit.Admin.Tags; using Volo.CmsKit.Contents; using Volo.CmsKit.Pages; @@ -19,6 +21,14 @@ namespace Volo.CmsKit.Admin CreateMap(MemberList.Source); CreateMap(MemberList.Source); + CreateMap(MemberList.Destination); + CreateMap(MemberList.Source); + + CreateMap(MemberList.Destination) + .ReverseMap(); + + CreateMap(MemberList.Destination); + CreateMap(MemberList.Destination); } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/CmsKitAdminApplicationModule.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/CmsKitAdminApplicationModule.cs index b34e918058..2ad96af963 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/CmsKitAdminApplicationModule.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/CmsKitAdminApplicationModule.cs @@ -1,6 +1,14 @@ using Microsoft.Extensions.DependencyInjection; +using System.Collections.Generic; using Volo.Abp.AutoMapper; +using Volo.Abp.GlobalFeatures; +using Volo.Abp.Localization; using Volo.Abp.Modularity; +using Volo.CmsKit.Blogs; +using Volo.CmsKit.GlobalFeatures; +using Volo.CmsKit.Localization; +using Volo.CmsKit.Permissions; +using Volo.CmsKit.Tags; namespace Volo.CmsKit.Admin { @@ -11,6 +19,11 @@ namespace Volo.CmsKit.Admin )] public class CmsKitAdminApplicationModule : AbpModule { + public override void PreConfigureServices(ServiceConfigurationContext context) + { + ConfigureTagOptions(); + } + public override void ConfigureServices(ServiceConfigurationContext context) { context.Services.AddAutoMapperObjectMapper(); @@ -20,5 +33,22 @@ namespace Volo.CmsKit.Admin options.AddMaps(validate: true); }); } + + private void ConfigureTagOptions() + { + Configure(opts => + { + if (GlobalFeatureManager.Instance.IsEnabled()) + { + opts.EntityTypes.AddIfNotContains( + new TagEntityTypeDefiniton( + BlogPostConsts.EntityType, + LocalizableString.Create("BlogPost"), + CmsKitAdminPermissions.BlogPosts.Update, + CmsKitAdminPermissions.BlogPosts.Update, + CmsKitAdminPermissions.BlogPosts.Update)); + } + }); + } } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Contents/ContentAdminAppService.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Contents/ContentAdminAppService.cs index 300cf31181..8ec8559e34 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Contents/ContentAdminAppService.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Contents/ContentAdminAppService.cs @@ -6,7 +6,6 @@ using Volo.Abp; using Volo.Abp.Application.Services; using Volo.Abp.Domain.Repositories; using Volo.CmsKit.Contents; -using Volo.CmsKit.Domain.Volo.CmsKit.Contents; using Volo.CmsKit.Permissions; namespace Volo.CmsKit.Admin.Contents diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Tags/EntityTagAdminAppService.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Tags/EntityTagAdminAppService.cs index 1ef6a8bdf9..b36ffedbd2 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Tags/EntityTagAdminAppService.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Tags/EntityTagAdminAppService.cs @@ -1,24 +1,32 @@ using System; +using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using Volo.CmsKit.Admin.Tags; using Volo.CmsKit.Tags; -namespace Volo.CmsKit.Admin.Application.Volo.CmsKit.Admin.Tags +namespace Volo.CmsKit.Admin.Tags { public class EntityTagAdminAppService : CmsKitAdminAppServiceBase, IEntityTagAdminAppService { - protected ITagDefinitionStore _tagDefinitionStore; - protected IEntityTagManager _entityTagManager; - protected ITagManager _tagManager; + protected readonly ITagDefinitionStore _tagDefinitionStore; + protected readonly IEntityTagManager _entityTagManager; + protected readonly ITagManager _tagManager; + protected readonly ITagRepository _tagRepository; + protected readonly IEntityTagRepository _entityTagRepository; public EntityTagAdminAppService( ITagDefinitionStore tagDefinitionStore, IEntityTagManager entityTagManager, - ITagManager tagManager) + ITagManager tagManager, + ITagRepository tagRepository, + IEntityTagRepository entityTagRepository) { _tagDefinitionStore = tagDefinitionStore; _entityTagManager = entityTagManager; _tagManager = tagManager; + _tagRepository = tagRepository; + _entityTagRepository = entityTagRepository; } public async Task AddTagToEntityAsync(EntityTagCreateDto input) @@ -48,5 +56,26 @@ namespace Volo.CmsKit.Admin.Application.Volo.CmsKit.Admin.Tags input.EntityId, CurrentTenant?.Id); } + + public async Task SetEntityTagsAsync(EntityTagSetDto input) + { + var definition = await _tagDefinitionStore.GetTagEntityTypeDefinitionsAsync(input.EntityType); + + await CheckPolicyAsync(definition.UpdatePolicy); + + var existingTags = await _tagRepository.GetAllRelatedTagsAsync(input.EntityType, input.EntityId, CurrentTenant?.Id); + + var deletedTags = existingTags.Where(x => !input.Tags.Contains(x.Name)).ToList(); + var addedTags = input.Tags.Where(x => !existingTags.Any(a => a.Name == x)); + + await _entityTagRepository.DeleteManyAsync(deletedTags.Select(s => s.Id).ToArray()); + + foreach (var addedTag in addedTags) + { + var tag = await _tagManager.GetOrAddAsync(input.EntityType, addedTag, CurrentTenant?.Id); + + await _entityTagManager.AddTagToEntityAsync(tag.Id, input.EntityType, input.EntityId, CurrentTenant?.Id); + } + } } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi/Volo/CmsKit/Admin/Blogs/BlogAdminController.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi/Volo/CmsKit/Admin/Blogs/BlogAdminController.cs new file mode 100644 index 0000000000..d63a3317ce --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi/Volo/CmsKit/Admin/Blogs/BlogAdminController.cs @@ -0,0 +1,68 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Volo.Abp; +using Volo.Abp.Application.Dtos; +using Volo.Abp.GlobalFeatures; +using Volo.CmsKit.GlobalFeatures; +using Volo.CmsKit.Permissions; + +namespace Volo.CmsKit.Admin.Blogs +{ + [RequiresGlobalFeature(typeof(BlogsFeature))] + [RemoteService(Name = CmsKitCommonRemoteServiceConsts.RemoteServiceName)] + [Area("cms-kit")] + [Authorize(CmsKitAdminPermissions.Blogs.Default)] + [Route("api/cms-kit-admin/blogs/blogs")] + public class BlogAdminController : CmsKitAdminController, IBlogAdminAppService + { + protected IBlogAdminAppService BlogAdminAppService { get; } + + public BlogAdminController(IBlogAdminAppService blogAdminAppService) + { + BlogAdminAppService = blogAdminAppService; + } + + [HttpPost] + [Authorize(CmsKitAdminPermissions.Blogs.Create)] + public Task CreateAsync(BlogDto input) + { + return BlogAdminAppService.CreateAsync(input); + } + + [HttpDelete] + [Route("{id}")] + [Authorize(CmsKitAdminPermissions.Blogs.Delete)] + public Task DeleteAsync(Guid id) + { + return BlogAdminAppService.DeleteAsync(id); + } + + [HttpGet] + [Route("{id}")] + [Authorize(CmsKitAdminPermissions.Blogs.Default)] + public Task GetAsync(Guid id) + { + return BlogAdminAppService.GetAsync(id); + } + + [HttpGet] + [Authorize(CmsKitAdminPermissions.Blogs.Default)] + public Task> GetListAsync([FromQuery] BlogGetListInput input) + { + return BlogAdminAppService.GetListAsync(input); + } + + [HttpPut] + [Route("{id}")] + [Authorize(CmsKitAdminPermissions.Blogs.Update)] + public Task UpdateAsync(Guid id, BlogDto input) + { + return BlogAdminAppService.UpdateAsync(id, input); + } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi/Volo/CmsKit/Admin/Blogs/BlogPostAdminController.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi/Volo/CmsKit/Admin/Blogs/BlogPostAdminController.cs new file mode 100644 index 0000000000..6c7e78de50 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi/Volo/CmsKit/Admin/Blogs/BlogPostAdminController.cs @@ -0,0 +1,115 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Volo.Abp; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Content; +using Volo.Abp.GlobalFeatures; +using Volo.CmsKit.GlobalFeatures; +using Volo.CmsKit.Permissions; + +namespace Volo.CmsKit.Admin.Blogs +{ + [RequiresGlobalFeature(typeof(BlogsFeature))] + [RemoteService(Name = CmsKitCommonRemoteServiceConsts.RemoteServiceName)] + [Area("cms-kit")] + [Authorize(CmsKitAdminPermissions.BlogPosts.Default)] + [Route("api/cms-kit-admin/blogs/blog-posts")] + public class BlogPostAdminController : CmsKitAdminController, IBlogPostAdminAppService + { + protected readonly IBlogPostAdminAppService BlogPostAdminAppService; + + public BlogPostAdminController(IBlogPostAdminAppService blogPostAdminAppService) + { + BlogPostAdminAppService = blogPostAdminAppService; + } + + [HttpPost] + [Authorize(CmsKitAdminPermissions.BlogPosts.Create)] + public virtual Task CreateAsync(CreateUpdateBlogPostDto input) + { + return BlogPostAdminAppService.CreateAsync(input); + } + + [HttpDelete] + [Route("{id}")] + [Authorize(CmsKitAdminPermissions.BlogPosts.Delete)] + public virtual Task DeleteAsync(Guid id) + { + return BlogPostAdminAppService.DeleteAsync(id); + } + + [HttpGet] + [Route("{id:Guid}")] + [Authorize(CmsKitAdminPermissions.BlogPosts.Default)] + public virtual Task GetAsync(Guid id) + { + return BlogPostAdminAppService.GetAsync(id); + } + + [HttpGet] + [Route("{blogSlug}/{blogPostSlug}")] + [Authorize(CmsKitAdminPermissions.BlogPosts.Default)] + public virtual Task GetBySlugAsync(string blogSlug, string blogPostSlug) + { + return BlogPostAdminAppService.GetBySlugAsync(blogSlug, blogPostSlug); + } + + [HttpGet] + [Route("{id}/cover-image")] + [Authorize(CmsKitAdminPermissions.BlogPosts.Default)] + public virtual Task GetCoverImageAsync(Guid id) + { + Response.Headers.Add("Content-Disposition", $"inline;filename=\"{id}\""); + Response.Headers.Add("Accept-Ranges", "bytes"); + Response.Headers.Add("Cache-Control", "max-age=120"); + Response.ContentType = "image"; + + return BlogPostAdminAppService.GetCoverImageAsync(id); + } + + [HttpGet] + [Authorize(CmsKitAdminPermissions.BlogPosts.Default)] + public virtual Task> GetListAsync(PagedAndSortedResultRequestDto input) + { + return BlogPostAdminAppService.GetListAsync(input); + } + + [NonAction] + public virtual Task SetCoverImageAsync(Guid id, RemoteStreamContent streamContent) + { + return BlogPostAdminAppService.SetCoverImageAsync(id, streamContent); + } + + [HttpPost] + [Route("{id}/cover-image")] + [Authorize(CmsKitAdminPermissions.BlogPosts.Update)] + public virtual async Task UploadCoverImageAsync(Guid id, IFormFile file) + { + if (file == null) + { + return BadRequest(); + } + + using (var stream = file.OpenReadStream()) + { + await BlogPostAdminAppService.SetCoverImageAsync(id, new RemoteStreamContent(stream)); + } + + return CreatedAtAction(nameof(GetCoverImageAsync), new { id }); + } + + [HttpPut] + [Route("{id}")] + [Authorize(CmsKitAdminPermissions.BlogPosts.Update)] + public virtual Task UpdateAsync(Guid id, CreateUpdateBlogPostDto input) + { + return BlogPostAdminAppService.UpdateAsync(id, input); + } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi/Volo/CmsKit/Admin/Tags/EntityTagAdminController.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi/Volo/CmsKit/Admin/Tags/EntityTagAdminController.cs new file mode 100644 index 0000000000..532fae8554 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi/Volo/CmsKit/Admin/Tags/EntityTagAdminController.cs @@ -0,0 +1,42 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using System.Threading.Tasks; +using Volo.Abp; +using Volo.Abp.GlobalFeatures; +using Volo.CmsKit.GlobalFeatures; +using Volo.CmsKit.Permissions; + +namespace Volo.CmsKit.Admin.Tags +{ + [RequiresGlobalFeature(typeof(TagsFeature))] + [RemoteService(Name = CmsKitCommonRemoteServiceConsts.RemoteServiceName)] + [Area("cms-kit")] + [Route("api/cms-kit-admin/entity-tags")] + public class EntityTagAdminController : CmsKitAdminController, IEntityTagAdminAppService + { + protected IEntityTagAdminAppService EntityTagAdminAppService { get; } + + public EntityTagAdminController(IEntityTagAdminAppService entityTagAdminAppService) + { + EntityTagAdminAppService = entityTagAdminAppService; + } + + [HttpPost] + public Task AddTagToEntityAsync(EntityTagCreateDto input) + { + return EntityTagAdminAppService.AddTagToEntityAsync(input); + } + + [HttpDelete] + public Task RemoveTagFromEntityAsync(EntityTagRemoveDto input) + { + return EntityTagAdminAppService.RemoveTagFromEntityAsync(input); + } + + [HttpPut] + public Task SetEntityTagsAsync(EntityTagSetDto input) + { + return EntityTagAdminAppService.SetEntityTagsAsync(input); + } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Tags/ITagAppService.cs b/modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Tags/ITagAppService.cs similarity index 89% rename from modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Tags/ITagAppService.cs rename to modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Tags/ITagAppService.cs index 34d54b9006..5ab01940e1 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Tags/ITagAppService.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Tags/ITagAppService.cs @@ -3,7 +3,7 @@ using System.Threading.Tasks; using Volo.Abp.Application.Services; using Volo.CmsKit.Tags; -namespace Volo.CmsKit.Public.Tags +namespace Volo.CmsKit.Tags { public interface ITagAppService : IApplicationService { diff --git a/modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Users/CmsUserDto.cs b/modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Users/CmsUserDto.cs new file mode 100644 index 0000000000..3600688f4b --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Users/CmsUserDto.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Volo.Abp.Application.Dtos; + +namespace Volo.CmsKit.Users +{ + public class CmsUserDto : EntityDto + { + public virtual Guid? TenantId { get; protected set; } + + public virtual string UserName { get; protected set; } + + public virtual string Name { get; set; } + + public virtual string Surname { get; set; } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Common.Application/Volo/CmsKit/CmsKitCommonApplicationAutoMapperProfile.cs b/modules/cms-kit/src/Volo.CmsKit.Common.Application/Volo/CmsKit/CmsKitCommonApplicationAutoMapperProfile.cs index e5044dc200..9d89ddadeb 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Common.Application/Volo/CmsKit/CmsKitCommonApplicationAutoMapperProfile.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Common.Application/Volo/CmsKit/CmsKitCommonApplicationAutoMapperProfile.cs @@ -2,6 +2,7 @@ using Volo.CmsKit.Contents; using Volo.CmsKit.Pages; using Volo.CmsKit.Tags; +using Volo.CmsKit.Users; namespace Volo.CmsKit { @@ -10,6 +11,8 @@ namespace Volo.CmsKit public CmsKitCommonApplicationAutoMapperProfile() { CreateMap(); + + CreateMap(); } } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Tags/TagAppService.cs b/modules/cms-kit/src/Volo.CmsKit.Common.Application/Volo/CmsKit/Tags/TagAppService.cs similarity index 95% rename from modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Tags/TagAppService.cs rename to modules/cms-kit/src/Volo.CmsKit.Common.Application/Volo/CmsKit/Tags/TagAppService.cs index 593f011b02..caa6dc4181 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Tags/TagAppService.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Common.Application/Volo/CmsKit/Tags/TagAppService.cs @@ -5,7 +5,7 @@ using System.Text; using System.Threading.Tasks; using Volo.CmsKit.Tags; -namespace Volo.CmsKit.Public.Tags +namespace Volo.CmsKit.Tags { public class TagAppService : CmsKitAppServiceBase, ITagAppService { diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Blogs/BlogConsts.cs b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Blogs/BlogConsts.cs new file mode 100644 index 0000000000..def370dca8 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Blogs/BlogConsts.cs @@ -0,0 +1,8 @@ +namespace Volo.CmsKit.Blogs +{ + public class BlogConsts + { + public static int MaxNameLength { get; set; } = 64; + public static int MaxSlugLength { get; set; } = 64; + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Blogs/BlogPostConsts.cs b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Blogs/BlogPostConsts.cs new file mode 100644 index 0000000000..97b67416cf --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Blogs/BlogPostConsts.cs @@ -0,0 +1,15 @@ +namespace Volo.CmsKit.Blogs +{ + public static class BlogPostConsts + { + public static int MaxTitleLength { get; set; } = 64; + + public static int MaxSlugLength { get; set; } = 256; + + public static int MinSlugLength { get; set; } = 2; + + public static int MaxShortDescriptionLength { get; set; } = 256; + + public const string EntityType = "BlogPost"; + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/CmsKitErrorCodes.cs b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/CmsKitErrorCodes.cs index 35cbad6374..da6f2ae1a2 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/CmsKitErrorCodes.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/CmsKitErrorCodes.cs @@ -14,5 +14,10 @@ { public const string UrlAlreadyExist = "CmsKit:Page:0001"; } + + public static class Blogs + { + public const string SlugAlreadyExist = "CmsKit:BlogPost:0001"; + } } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/GlobalFeatures/BlogsFeature.cs b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/GlobalFeatures/BlogsFeature.cs new file mode 100644 index 0000000000..1b094ecf2d --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/GlobalFeatures/BlogsFeature.cs @@ -0,0 +1,17 @@ +using JetBrains.Annotations; +using Volo.Abp.GlobalFeatures; + +namespace Volo.CmsKit.GlobalFeatures +{ + [GlobalFeatureName(Name)] + public class BlogsFeature : GlobalFeature + { + public const string Name = "CmsKit.Blogs"; + + internal BlogsFeature( + [NotNull] GlobalCmsKitFeatures cmsKit + ) : base(cmsKit) + { + } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/GlobalFeatures/GlobalCmsKitFeatures.cs b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/GlobalFeatures/GlobalCmsKitFeatures.cs index 5a7adc34b2..f6293fc659 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/GlobalFeatures/GlobalCmsKitFeatures.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/GlobalFeatures/GlobalCmsKitFeatures.cs @@ -28,6 +28,7 @@ namespace Volo.CmsKit.GlobalFeatures AddFeature(new TagsFeature(this)); AddFeature(new ContentsFeature(this)); AddFeature(new PagesFeature(this)); + AddFeature(new BlogsFeature(this)); } } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/en.json b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/en.json index 00eccab1be..8b2588a12f 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/en.json +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/en.json @@ -3,10 +3,14 @@ "texts": { "CmsKit:0002": "Content already exists!", "CmsKit:0003": "The entity {0} is not taggable.", + "CmsKit:BlogPost:0001": "Same url slug already exists!", + "CmsKit:Page:0001": "The given url ({0}) already exists.", + "CmsKit:Tag:0002": "The entity is not taggable!", "CommentAuthorizationExceptionMessage": "Those comments are not allowed for public display.", "Comments": "Comments", "Delete": "Delete", "Edit": "Edit", + "LastModification": "Last Modification", "LoginToAddComment": "Login to add comment", "LoginToRate": "Login to rate", "LoginToReply": "Login to reply", @@ -17,16 +21,17 @@ "Permission:Contents.Create": "Create Content", "Permission:Contents.Delete": "Delete Content", "Permission:Contents.Update": "Update Content", - "Permission:TagManagement": "Tag Management", - "Permission:TagManagement.Create": "Create", - "Permission:TagManagement.Delete": "Delete", - "Permission:TagManagement.Update": "Update", "Permission:PageManagement": "Page Management", "Permission:PageManagement:Create": "Create", "Permission:PageManagement:Delete": "Delete", "Permission:PageManagement:Update": "Update", + "Permission:TagManagement": "Tag Management", + "Permission:TagManagement.Create": "Create", + "Permission:TagManagement.Delete": "Delete", + "Permission:TagManagement.Update": "Update", "PickYourReaction": "Pick your reaction", "RatingUndoMessage": "Your rating will be undo.", + "Read": "Read", "Reply": "Reply", "Send": "Send", "Star": "Star", @@ -35,7 +40,6 @@ "Undo": "Undo", "Update": "Update", "YourComment": "Your comment", - "YourReply": "Your reply", - "CmsKit:Page:0001": "The given url ({0}) already exists." + "YourReply": "Your reply" } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/tr.json b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/tr.json index 1ebb0c126b..7d77c0649b 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/tr.json +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/tr.json @@ -3,10 +3,14 @@ "texts": { "CmsKit:0002": "İçerik zaten mevcut!", "CmsKit:0003": "{0} ögesi etiketlenebilir değil.", + "CmsKit:BlogPost:0001": "Aynı url etiketi zaten mevcut.", + "CmsKit:Page:0001": "Girilen url ({0}) kullanımdadır.", + "CmsKit:Tag:0002": "Bu öge etiketlenebilir değil.", "CommentAuthorizationExceptionMessage": "Bu yorumları görebilmek için yetki gerekir.", "Comments": "Yorumlar", "Delete": "Sil", "Edit": "Düzenle", + "LastModification": "Son Güncellenme", "LoginToAddComment": "Yorum yapmak için giriş yap", "LoginToRate": "Oylamak için giriş yapın", "LoginToReply": "Cevap vermek için giriş yap", @@ -23,6 +27,7 @@ "Permission:TagManagement.Update": "Etiket Güncelleme", "PickYourReaction": "Tepkinizi seçin", "RatingUndoMessage": "Oylamanız geri alınacak.", + "Read": "Oku", "Reply": "Cevapla", "Send": "Gönder", "Star": "Yıldız", @@ -31,7 +36,6 @@ "Undo": "Geri al", "Update": "Güncelle", "YourComment": "Yorumunuz", - "YourReply": "Cevabınız", - "CmsKit:Page:0001": "Girilen url ({0}) kullanımdadır." + "YourReply": "Cevabınız" } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo.CmsKit.Domain.csproj b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo.CmsKit.Domain.csproj index ee064cbedc..f6d895a98d 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo.CmsKit.Domain.csproj +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo.CmsKit.Domain.csproj @@ -1,4 +1,4 @@ - + @@ -8,10 +8,16 @@ + + + + + + diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/Blog.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/Blog.cs new file mode 100644 index 0000000000..79c496970c --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/Blog.cs @@ -0,0 +1,41 @@ +using JetBrains.Annotations; +using System; +using Volo.Abp; +using Volo.Abp.Domain.Entities.Auditing; +using Volo.Abp.MultiTenancy; +using Volo.CmsKit.Blogs.Extensions; + +namespace Volo.CmsKit.Blogs +{ + public class Blog : FullAuditedAggregateRoot, IMultiTenant + { + public Blog( + Guid id, + [NotNull] string name, + [NotNull] string slug, + [CanBeNull] Guid? tenantId = null) : base(id) + { + SetName(name); + SetSlug(slug); + TenantId = tenantId; + } + + public string Name { get; protected set; } + + public string Slug { get; protected set; } + + public Guid? TenantId { get; protected set; } + + public void SetName(string name) + { + Name = Check.NotNullOrWhiteSpace(name, nameof(name), maxLength: BlogConsts.MaxNameLength); + } + + public void SetSlug(string slug) + { + Check.NotNullOrWhiteSpace(slug, nameof(slug), maxLength: BlogConsts.MaxNameLength); + + Slug = slug.NormalizeSlug(); + } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/BlogPost.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/BlogPost.cs new file mode 100644 index 0000000000..7a52fe0d9f --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/BlogPost.cs @@ -0,0 +1,59 @@ +using JetBrains.Annotations; +using System; +using System.Text.RegularExpressions; +using Volo.Abp; +using Volo.Abp.Domain.Entities.Auditing; +using Volo.Abp.MultiTenancy; +using Volo.CmsKit.Blogs; +using Volo.CmsKit.Blogs.Extensions; +using Volo.CmsKit.Users; + +namespace Volo.CmsKit.Blogs +{ + public class BlogPost : FullAuditedAggregateRootWithUser, IMultiTenant + { + public Guid BlogId { get; protected set; } + + public string Title { get; protected set; } + + public string Slug { get; protected set; } + + public string ShortDescription { get; protected set; } + + public Guid? TenantId { get; protected set; } + + protected BlogPost() + { + } + + public BlogPost( + Guid id, + Guid blogId, + [NotNull] string title, + [NotNull] string slug, + [CanBeNull] string shortDescription = null) : base(id) + { + BlogId = blogId; + SetTitle(title); + SetSlug(slug); + ShortDescription = shortDescription; + } + + public void SetTitle(string title) + { + Title = Check.NotNullOrWhiteSpace(title, nameof(title), BlogPostConsts.MaxTitleLength); + } + + internal void SetSlug(string slug) + { + Check.NotNullOrWhiteSpace(slug, nameof(slug), BlogPostConsts.MaxSlugLength, BlogPostConsts.MinSlugLength); + + Slug = slug.NormalizeSlug(); + } + + public void SetShortDescription(string shortDescription) + { + ShortDescription = Check.Length(shortDescription, nameof(shortDescription), BlogPostConsts.MaxShortDescriptionLength); + } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/BlogPostCoverImageContainer.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/BlogPostCoverImageContainer.cs new file mode 100644 index 0000000000..824db4fe3a --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/BlogPostCoverImageContainer.cs @@ -0,0 +1,9 @@ +using Volo.Abp.BlobStoring; + +namespace Volo.CmsKit.Blogs +{ + [BlobContainerName("blog-post-cover-images")] + public class BlogPostCoverImageContainer + { + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/BlogPostManager.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/BlogPostManager.cs new file mode 100644 index 0000000000..aed2b9fffd --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/BlogPostManager.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Volo.Abp.Domain.Entities; +using Volo.Abp.Domain.Services; + +namespace Volo.CmsKit.Blogs +{ + public class BlogPostManager : DomainService, IBlogPostManager + { + protected readonly IBlogPostRepository blogPostRepository; + protected readonly IBlogRepository blogRepository; + + public BlogPostManager( + IBlogPostRepository blogPostRepository, + IBlogRepository blogRepository) + { + this.blogPostRepository = blogPostRepository; + this.blogRepository = blogRepository; + } + + public async Task CreateAsync(BlogPost blogPost) + { + await CheckBlogExistenceAsync(blogPost.BlogId); + + await CheckSlugExistenceAsync(blogPost.BlogId, blogPost.Slug); + + return await blogPostRepository.InsertAsync(blogPost); + } + + public async Task UpdateAsync(BlogPost blogPost) + { + await CheckBlogExistenceAsync(blogPost.BlogId); + + await blogPostRepository.UpdateAsync(blogPost); + } + + public async Task SetSlugUrlAsync(BlogPost blogPost, string newSlug) + { + await CheckSlugExistenceAsync(blogPost.BlogId, newSlug); + + blogPost.SetSlug(newSlug); + } + + private async Task CheckSlugExistenceAsync(Guid blogId, string slug) + { + if (await blogPostRepository.SlugExistsAsync(blogId, slug)) + { + throw new BlogPostSlugAlreadyExistException(blogId, slug); + } + } + + private async Task CheckBlogExistenceAsync(Guid blogId) + { + if (!await blogRepository.ExistsAsync(blogId)) + throw new EntityNotFoundException(typeof(Blog), blogId); + } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/BlogPostSlugAlreadyExistException.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/BlogPostSlugAlreadyExistException.cs new file mode 100644 index 0000000000..8e59249299 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/BlogPostSlugAlreadyExistException.cs @@ -0,0 +1,31 @@ +using System; +using Volo.Abp; + +namespace Volo.CmsKit.Blogs +{ + public class BlogPostSlugAlreadyExistException : BusinessException + { + internal BlogPostSlugAlreadyExistException(string code = null, string message = null, string details = null, Exception innerException = null, Microsoft.Extensions.Logging.LogLevel logLevel = Microsoft.Extensions.Logging.LogLevel.Warning) : base(code, message, details, innerException, logLevel) + { + } + + internal BlogPostSlugAlreadyExistException(System.Runtime.Serialization.SerializationInfo serializationInfo, System.Runtime.Serialization.StreamingContext context) : base(serializationInfo, context) + { + } + + public BlogPostSlugAlreadyExistException(Guid blogId, string slug) + { + Slug = slug; + BlogId = blogId; + + Code = CmsKitErrorCodes.Blogs.SlugAlreadyExist; + + WithData(nameof(Slug), Slug); + WithData(nameof(BlogId), BlogId); + } + + public string Slug { get; } + + public Guid BlogId { get; } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/Extensions/SlugExtensions.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/Extensions/SlugExtensions.cs new file mode 100644 index 0000000000..9b3908d8cb --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/Extensions/SlugExtensions.cs @@ -0,0 +1,15 @@ +using Slugify; +using Unidecode.NET; + +namespace Volo.CmsKit.Blogs.Extensions +{ + public static class SlugExtensions + { + public static string NormalizeSlug(this string value) + { + var slugHelper = new SlugHelper(); + + return slugHelper.GenerateSlug(value?.Unidecode()); + } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/IBlogPostManager.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/IBlogPostManager.cs new file mode 100644 index 0000000000..5027fa8e38 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/IBlogPostManager.cs @@ -0,0 +1,14 @@ +using System; +using System.Threading.Tasks; + +namespace Volo.CmsKit.Blogs +{ + public interface IBlogPostManager + { + Task CreateAsync(BlogPost blogPost); + + Task UpdateAsync(BlogPost blogPost); + + Task SetSlugUrlAsync(BlogPost blogPost, string newSlug); + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/IBlogPostRepository.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/IBlogPostRepository.cs new file mode 100644 index 0000000000..198cfca104 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/IBlogPostRepository.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories; + +namespace Volo.CmsKit.Blogs +{ + public interface IBlogPostRepository : IBasicRepository + { + Task SlugExistsAsync(Guid blogId, string slug, CancellationToken cancellationToken = default); + + Task GetBySlugAsync(Guid blogId, string slug, CancellationToken cancellationToken = default); + + Task> GetPagedListAsync(Guid blogId, int skipCount, int maxResultCount, string sorting, bool includeDetails = false, CancellationToken cancellationToken = default); + + Task GetCountAsync(Guid blogId, CancellationToken cancellationToken = default); + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/IBlogRepository.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/IBlogRepository.cs new file mode 100644 index 0000000000..4213884057 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/IBlogRepository.cs @@ -0,0 +1,12 @@ +using System; +using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories; + +namespace Volo.CmsKit.Blogs +{ + public interface IBlogRepository : IBasicRepository + { + public Task GetBySlugAsync(string slug); + Task ExistsAsync(Guid blogId); + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/CmsKitDomainModule.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/CmsKitDomainModule.cs index a60cb0d521..cb189571f7 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/CmsKitDomainModule.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/CmsKitDomainModule.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.Options; +using Volo.Abp.BlobStoring; +using Microsoft.Extensions.Options; using Volo.Abp.Domain; using Volo.Abp.GlobalFeatures; using Volo.Abp.Localization; @@ -15,7 +16,8 @@ namespace Volo.CmsKit [DependsOn( typeof(CmsKitDomainSharedModule), typeof(AbpUsersDomainModule), - typeof(AbpDddDomainModule) + typeof(AbpDddDomainModule), + typeof(AbpBlobStoringModule) )] public class CmsKitDomainModule : AbpModule { diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Contents/ContentManager.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Contents/ContentManager.cs index 6eb2521ba2..047d923ad6 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Contents/ContentManager.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Contents/ContentManager.cs @@ -7,7 +7,7 @@ using System.Threading.Tasks; using Volo.Abp.Domain.Services; using Volo.CmsKit.Contents; -namespace Volo.CmsKit.Domain.Volo.CmsKit.Contents +namespace Volo.CmsKit.Contents { public class ContentManager : DomainService, IContentManager { diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Contents/IContentManager.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Contents/IContentManager.cs index e795e7895c..d64bd0effe 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Contents/IContentManager.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Contents/IContentManager.cs @@ -2,7 +2,7 @@ using System.Threading.Tasks; using Volo.CmsKit.Contents; -namespace Volo.CmsKit.Domain.Volo.CmsKit.Contents +namespace Volo.CmsKit.Contents { public interface IContentManager { diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/IEntityTagRepository.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/IEntityTagRepository.cs index f2ed160671..6ea4f009a2 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/IEntityTagRepository.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/IEntityTagRepository.cs @@ -13,5 +13,7 @@ namespace Volo.CmsKit.Tags [NotNull] string entityId, [CanBeNull] Guid? tenantId, CancellationToken cancellationToken = default); + + Task DeleteManyAsync(Guid[] tagIds, CancellationToken cancellationToken = default); } } \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/Blogs/EfCoreBlogPostRepository.cs b/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/Blogs/EfCoreBlogPostRepository.cs new file mode 100644 index 0000000000..5a9c5d526a --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/Blogs/EfCoreBlogPostRepository.cs @@ -0,0 +1,66 @@ +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Linq.Dynamic.Core; +using System.Security.Cryptography.X509Certificates; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore; +using Volo.CmsKit.EntityFrameworkCore; +using System.Linq; +using System.Data.Common; +using Volo.Abp.Domain.Entities; + +namespace Volo.CmsKit.Blogs +{ + public class EfCoreBlogPostRepository : EfCoreRepository, IBlogPostRepository + { + public EfCoreBlogPostRepository(IDbContextProvider dbContextProvider) : base(dbContextProvider) + { + } + + public async Task GetBySlugAsync(Guid blogId, string slug, CancellationToken cancellationToken = default) + { + var dbSet = await GetDbSetAsync(); + + return await dbSet + .Include(i=> i.Creator) + .Where(x => + x.BlogId == blogId && x.Slug.ToLower() == slug) + .FirstOrDefaultAsync(cancellationToken: cancellationToken) + ?? throw new EntityNotFoundException(typeof(BlogPost)); + } + + public async Task GetCountAsync(Guid blogId, CancellationToken cancellationToken = default) + { + return await (await GetQueryableAsync()).CountAsync( + x => x.BlogId == blogId, + cancellationToken); + } + + public async Task> GetPagedListAsync(Guid blogId, int skipCount, int maxResultCount, string sorting, bool includeDetails = false, CancellationToken cancellationToken = default) + { + var queryable = (await GetQueryableAsync()) + .Include(i => i.Creator) + .Where(x => x.BlogId == blogId); + + if (!sorting.IsNullOrWhiteSpace()) + { + queryable = queryable.OrderBy(sorting); + } + + return await queryable + .Skip(skipCount) + .Take(maxResultCount) + .ToListAsync(cancellationToken); + } + + public async Task SlugExistsAsync(Guid blogId, string slug, CancellationToken cancellationToken = default) + { + var dbSet = await GetDbSetAsync(); + + return await dbSet.AnyAsync(x => x.BlogId == blogId && x.Slug.ToLower() == slug, cancellationToken); + } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/Blogs/EfCoreBlogRepository.cs b/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/Blogs/EfCoreBlogRepository.cs new file mode 100644 index 0000000000..d2ddbf35aa --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/Blogs/EfCoreBlogRepository.cs @@ -0,0 +1,27 @@ +using Microsoft.EntityFrameworkCore; +using System; +using System.Security.Cryptography.X509Certificates; +using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore; +using Volo.CmsKit.EntityFrameworkCore; + +namespace Volo.CmsKit.Blogs +{ + public class EfCoreBlogRepository : EfCoreRepository, IBlogRepository + { + public EfCoreBlogRepository(IDbContextProvider dbContextProvider) : base(dbContextProvider) + { + } + + public async Task ExistsAsync(Guid blogId) + { + return await (await GetQueryableAsync()).AnyAsync(x => x.Id == blogId); + } + + public Task GetBySlugAsync(string slug) + { + return GetAsync(x => x.Slug == slug); + } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/CmsKitDbContext.cs b/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/CmsKitDbContext.cs index 0a4213e1b4..e7694574a9 100644 --- a/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/CmsKitDbContext.cs +++ b/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/CmsKitDbContext.cs @@ -1,6 +1,7 @@ using Microsoft.EntityFrameworkCore; using Volo.Abp.Data; using Volo.Abp.EntityFrameworkCore; +using Volo.CmsKit.Blogs; using Volo.CmsKit.Comments; using Volo.CmsKit.Contents; using Volo.CmsKit.Pages; @@ -22,6 +23,8 @@ namespace Volo.CmsKit.EntityFrameworkCore public DbSet Tags { get; set; } public DbSet EntityTags { get; set; } public DbSet Pages { get; set; } + public DbSet Blogs { get; set; } + public DbSet BlogPosts { get; set; } public CmsKitDbContext(DbContextOptions options) : base(options) diff --git a/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/CmsKitDbContextModelCreatingExtensions.cs b/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/CmsKitDbContextModelCreatingExtensions.cs index 60cf1332f8..d3f40afeed 100644 --- a/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/CmsKitDbContextModelCreatingExtensions.cs +++ b/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/CmsKitDbContextModelCreatingExtensions.cs @@ -13,6 +13,7 @@ using Volo.CmsKit.GlobalFeatures; using Volo.CmsKit.Pages; using Volo.CmsKit.Ratings; using Volo.CmsKit.Tags; +using Volo.CmsKit.Blogs; namespace Volo.CmsKit.EntityFrameworkCore { @@ -58,8 +59,8 @@ namespace Volo.CmsKit.EntityFrameworkCore b.Property(x => x.EntityId).IsRequired().HasMaxLength(UserReactionConsts.MaxEntityIdLength); b.Property(x => x.ReactionName).IsRequired().HasMaxLength(UserReactionConsts.MaxReactionNameLength); - b.HasIndex(x => new {x.TenantId, x.EntityType, x.EntityId, x.ReactionName}); - b.HasIndex(x => new {x.TenantId, x.CreatorId, x.EntityType, x.EntityId, x.ReactionName}); + b.HasIndex(x => new { x.TenantId, x.EntityType, x.EntityId, x.ReactionName }); + b.HasIndex(x => new { x.TenantId, x.CreatorId, x.EntityType, x.EntityId, x.ReactionName }); }); } else @@ -80,8 +81,8 @@ namespace Volo.CmsKit.EntityFrameworkCore b.Property(x => x.Text).IsRequired().HasMaxLength(CommentConsts.MaxTextLength); b.Property(x => x.RepliedCommentId); - b.HasIndex(x => new {x.TenantId, x.EntityType, x.EntityId}); - b.HasIndex(x => new {x.TenantId, x.RepliedCommentId}); + b.HasIndex(x => new { x.TenantId, x.EntityType, x.EntityId }); + b.HasIndex(x => new { x.TenantId, x.RepliedCommentId }); }); } else @@ -179,13 +180,47 @@ namespace Volo.CmsKit.EntityFrameworkCore b.Property(x => x.Url).IsRequired().HasMaxLength(PageConsts.MaxUrlLength); b.Property(x => x.Description).HasMaxLength(PageConsts.MaxDescriptionLength); - b.HasIndex(x => new {x.TenantId, x.Url}); + b.HasIndex(x => new { x.TenantId, x.Url }); }); } else { builder.Ignore(); } + + if (GlobalFeatureManager.Instance.IsEnabled()) + { + builder.Entity(b => + { + b.ToTable(options.TablePrefix + "Blogs", options.Schema); + + b.ConfigureByConvention(); + + b.Property(p => p.Name).IsRequired().HasMaxLength(BlogConsts.MaxNameLength); + + b.Property(p => p.Slug).IsRequired().HasMaxLength(BlogConsts.MaxSlugLength); + }); + + builder.Entity(b => + { + b.ToTable(options.TablePrefix + "BlogPosts", options.Schema); + + b.ConfigureByConvention(); + + b.Property(p => p.Title).IsRequired().HasMaxLength(BlogPostConsts.MaxTitleLength); + + b.Property(p => p.Slug).IsRequired().HasMaxLength(BlogPostConsts.MaxSlugLength); + + b.Property(p => p.ShortDescription).HasMaxLength(BlogPostConsts.MaxShortDescriptionLength); + + b.HasIndex(x => new { x.Slug, x.BlogId }); + }); + } + else + { + builder.Ignore(); + builder.Ignore(); + } } } } \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/CmsKitEntityFrameworkCoreModule.cs b/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/CmsKitEntityFrameworkCoreModule.cs index c995c21dfb..61cd667aed 100644 --- a/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/CmsKitEntityFrameworkCoreModule.cs +++ b/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/CmsKitEntityFrameworkCoreModule.cs @@ -2,6 +2,7 @@ using Volo.Abp.EntityFrameworkCore; using Volo.Abp.Modularity; using Volo.Abp.Users.EntityFrameworkCore; +using Volo.CmsKit.Blogs; using Volo.CmsKit.Comments; using Volo.CmsKit.Contents; using Volo.CmsKit.Pages; @@ -31,6 +32,8 @@ namespace Volo.CmsKit.EntityFrameworkCore options.AddRepository(); options.AddRepository(); options.AddRepository(); + options.AddRepository(); + options.AddRepository(); }); } } diff --git a/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/ICmsKitDbContext.cs b/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/ICmsKitDbContext.cs index a5981fd000..feb1a5aacd 100644 --- a/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/ICmsKitDbContext.cs +++ b/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/ICmsKitDbContext.cs @@ -1,6 +1,7 @@ using Microsoft.EntityFrameworkCore; using Volo.Abp.Data; using Volo.Abp.EntityFrameworkCore; +using Volo.CmsKit.Blogs; using Volo.CmsKit.Comments; using Volo.CmsKit.Contents; using Volo.CmsKit.Pages; @@ -22,5 +23,7 @@ namespace Volo.CmsKit.EntityFrameworkCore DbSet Tags { get; set; } DbSet EntityTags { get; set; } DbSet Pages { get; set; } + DbSet Blogs { get; set; } + DbSet BlogPosts { get; set; } } } \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/Tags/EfCoreEntityTagRepository.cs b/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/Tags/EfCoreEntityTagRepository.cs index a82fb7005a..f1d0119885 100644 --- a/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/Tags/EfCoreEntityTagRepository.cs +++ b/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/Tags/EfCoreEntityTagRepository.cs @@ -1,5 +1,8 @@ using JetBrains.Annotations; +using Nito.Disposables; using System; +using System.Linq; +using System.Linq.Dynamic.Core; using System.Threading; using System.Threading.Tasks; using Volo.Abp.Domain.Repositories.EntityFrameworkCore; @@ -14,6 +17,13 @@ namespace Volo.CmsKit.Tags { } + public async Task DeleteManyAsync(Guid[] tagIds, CancellationToken cancellationToken = default) + { + var dbSet = await GetDbSetAsync(); + + dbSet.RemoveRange(dbSet.Where(x => tagIds.Contains(x.TagId))); + } + public Task FindAsync( [NotNull] Guid tagId, [NotNull] string entityId, diff --git a/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Blogs/MongoBlogPostRepository.cs b/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Blogs/MongoBlogPostRepository.cs new file mode 100644 index 0000000000..85dc1b86e0 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Blogs/MongoBlogPostRepository.cs @@ -0,0 +1,67 @@ +using MongoDB.Driver; +using MongoDB.Driver.Linq; +using Nito.AsyncEx; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Dynamic.Core; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories.MongoDB; +using Volo.Abp.MongoDB; +using Volo.CmsKit.Blogs; +using Volo.CmsKit.Users; + +namespace Volo.CmsKit.MongoDB.Blogs +{ + public class MongoBlogPostRepository : MongoDbRepository, IBlogPostRepository + { + public MongoBlogPostRepository(IMongoDbContextProvider dbContextProvider) : base(dbContextProvider) + { + } + + public Task GetBySlugAsync(Guid blogId, string slug, CancellationToken cancellationToken = default) + { + return GetAsync(x => + x.BlogId == blogId && + x.Slug.ToLower() == slug, + includeDetails: true, + cancellationToken: cancellationToken); + } + + public async Task GetCountAsync(Guid blogId, CancellationToken cancellationToken = default) + { + return await AsyncExecuter.CountAsync( + await WithDetailsAsync(), + x => x.BlogId == blogId, + cancellationToken); + } + + public async Task> GetPagedListAsync(Guid blogId, int skipCount, int maxResultCount, string sorting, bool includeDetails = false, CancellationToken cancellationToken = default) + { + var dbContext = await GetDbContextAsync(cancellationToken); + var blogPostQueryable = await WithDetailsAsync(); + + var queryable = blogPostQueryable + .Where(x => x.BlogId == blogId); + + if (!sorting.IsNullOrWhiteSpace()) + { + queryable = queryable.OrderBy(sorting); + } + + queryable = queryable + .Skip(skipCount) + .Take(maxResultCount); + + return await AsyncExecuter.ToListAsync(queryable, cancellationToken); + } + + public async Task SlugExistsAsync(Guid blogId, string slug, CancellationToken cancellationToken = default) + { + var queryable = await GetMongoQueryableAsync(); + + return await queryable.AnyAsync(x => x.BlogId == blogId && x.Slug.ToLower() == slug, cancellationToken); + } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Blogs/MongoBlogRepository.cs b/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Blogs/MongoBlogRepository.cs new file mode 100644 index 0000000000..d733f82bf6 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Blogs/MongoBlogRepository.cs @@ -0,0 +1,28 @@ +using MongoDB.Driver.Core.Operations; +using System; +using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories.MongoDB; +using Volo.Abp.MongoDB; +using Volo.CmsKit.Blogs; + +namespace Volo.CmsKit.MongoDB.Blogs +{ + public class MongoBlogRepository : MongoDbRepository, IBlogRepository + { + public MongoBlogRepository(IMongoDbContextProvider dbContextProvider) : base(dbContextProvider) + { + } + + public async Task ExistsAsync(Guid blogId) + { + return await AsyncExecuter.AnyAsync( + await GetQueryableAsync(), + x => x.Id == blogId); + } + + public Task GetBySlugAsync(string slug) + { + return GetAsync(x => x.Slug == slug); + } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/CmsKitMongoDbContext.cs b/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/CmsKitMongoDbContext.cs index c61d8d9a13..b61ad967cd 100644 --- a/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/CmsKitMongoDbContext.cs +++ b/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/CmsKitMongoDbContext.cs @@ -3,6 +3,7 @@ using Volo.Abp.Data; using Volo.Abp.MongoDB; using Volo.CmsKit.Comments; using Volo.CmsKit.Contents; +using Volo.CmsKit.Blogs; using Volo.CmsKit.Pages; using Volo.CmsKit.Ratings; using Volo.CmsKit.Reactions; @@ -31,6 +32,10 @@ namespace Volo.CmsKit.MongoDB public IMongoCollection Pages => Collection(); + public IMongoCollection Blogs => Collection(); + + public IMongoCollection BlogPosts => Collection(); + protected override void CreateModel(IMongoModelBuilder modelBuilder) { base.CreateModel(modelBuilder); diff --git a/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/CmsKitMongoDbContextExtensions.cs b/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/CmsKitMongoDbContextExtensions.cs index 7f6dcfecf3..46eecd1077 100644 --- a/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/CmsKitMongoDbContextExtensions.cs +++ b/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/CmsKitMongoDbContextExtensions.cs @@ -3,6 +3,7 @@ using Volo.Abp; using Volo.Abp.MongoDB; using Volo.CmsKit.Comments; using Volo.CmsKit.Contents; +using Volo.CmsKit.Blogs; using Volo.CmsKit.Pages; using Volo.CmsKit.Ratings; using Volo.CmsKit.Reactions; @@ -64,6 +65,16 @@ namespace Volo.CmsKit.MongoDB { x.CollectionName = CmsKitDbProperties.DbTablePrefix + "Pages"; }); + + builder.Entity(x => + { + x.CollectionName = CmsKitDbProperties.DbTablePrefix + "Blogs"; + }); + + builder.Entity(x => + { + x.CollectionName = CmsKitDbProperties.DbTablePrefix + "BlogPosts"; + }); } } } diff --git a/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/CmsKitMongoDbModule.cs b/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/CmsKitMongoDbModule.cs index 898b1687a8..b5ef74ac34 100644 --- a/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/CmsKitMongoDbModule.cs +++ b/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/CmsKitMongoDbModule.cs @@ -2,8 +2,10 @@ using Volo.Abp.Modularity; using Volo.Abp.MongoDB; using Volo.Abp.Users.MongoDB; +using Volo.CmsKit.Blogs; using Volo.CmsKit.Comments; using Volo.CmsKit.Contents; +using Volo.CmsKit.MongoDB.Blogs; using Volo.CmsKit.MongoDB.Comments; using Volo.CmsKit.MongoDB.Contents; using Volo.CmsKit.MongoDB.Pages; @@ -38,6 +40,8 @@ namespace Volo.CmsKit.MongoDB options.AddRepository(); options.AddRepository(); options.AddRepository(); + options.AddRepository(); + options.AddRepository(); }); } } diff --git a/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/ICmsKitMongoDbContext.cs b/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/ICmsKitMongoDbContext.cs index d33a5ee371..cc0faf912f 100644 --- a/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/ICmsKitMongoDbContext.cs +++ b/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/ICmsKitMongoDbContext.cs @@ -3,6 +3,7 @@ using Volo.Abp.Data; using Volo.Abp.MongoDB; using Volo.CmsKit.Comments; using Volo.CmsKit.Contents; +using Volo.CmsKit.Blogs; using Volo.CmsKit.Pages; using Volo.CmsKit.Ratings; using Volo.CmsKit.Reactions; @@ -20,15 +21,19 @@ namespace Volo.CmsKit.MongoDB IMongoCollection Comments { get; } IMongoCollection CmsUsers { get; } - + IMongoCollection Ratings { get; } - + IMongoCollection Contents { get; } - + IMongoCollection Tags { get; } - + IMongoCollection EntityTags { get; } - + IMongoCollection Pages { get; } + + IMongoCollection Blogs { get; } + + IMongoCollection BlogPosts { get; } } } diff --git a/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Tags/MongoEntityTagRepository.cs b/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Tags/MongoEntityTagRepository.cs index 3f9e1814fc..c073f8206f 100644 --- a/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Tags/MongoEntityTagRepository.cs +++ b/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Tags/MongoEntityTagRepository.cs @@ -1,5 +1,7 @@ using JetBrains.Annotations; +using MongoDB.Driver; using System; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Volo.Abp.Domain.Repositories.MongoDB; @@ -14,6 +16,13 @@ namespace Volo.CmsKit.MongoDB.Tags { } + public async Task DeleteManyAsync(Guid[] tagIds, CancellationToken cancellationToken = default) + { + var collection = await GetCollectionAsync(); + + await collection.DeleteManyAsync(Builders.Filter.In(x => x.TagId, tagIds)); + } + public Task FindAsync( [NotNull] Guid tagId, [NotNull] string entityId, diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Blogs/BlogPostPublicDto.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Blogs/BlogPostPublicDto.cs new file mode 100644 index 0000000000..5cbbda938e --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Blogs/BlogPostPublicDto.cs @@ -0,0 +1,17 @@ +using System; +using Volo.Abp.Application.Dtos; +using Volo.CmsKit.Users; + +namespace Volo.CmsKit.Public.Blogs +{ + public class BlogPostPublicDto : AuditedEntityWithUserDto + { + public Guid BlogId { get; set; } + + public string Title { get; set; } + + public string Slug { get; set; } + + public string ShortDescription { get; set; } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Blogs/IBlogPostPublicAppService.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Blogs/IBlogPostPublicAppService.cs new file mode 100644 index 0000000000..0c9079ee7e --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Blogs/IBlogPostPublicAppService.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Content; + +namespace Volo.CmsKit.Public.Blogs +{ + public interface IBlogPostPublicAppService + { + Task> GetListAsync(string blogSlug, PagedAndSortedResultRequestDto input); + + Task GetAsync(string blogSlug, string blogPostSlug); + + Task GetCoverImageAsync(Guid id); + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Blogs/BlogPostPublicAppService.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Blogs/BlogPostPublicAppService.cs new file mode 100644 index 0000000000..3994720d73 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Blogs/BlogPostPublicAppService.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Volo.Abp.Application.Dtos; +using Volo.Abp.BlobStoring; +using Volo.Abp.Content; +using Volo.CmsKit.Blogs; + +namespace Volo.CmsKit.Public.Blogs +{ + public class BlogPostPublicAppService : CmsKitPublicAppServiceBase, IBlogPostPublicAppService + { + protected IBlogRepository BlogRepository { get; } + + protected IBlogPostRepository BlogPostRepository { get; } + + protected IBlobContainer BlobContainer { get; } + + public BlogPostPublicAppService( + IBlogRepository blogRepository, + IBlogPostRepository blogPostRepository, + IBlobContainer blobContainer) + { + BlogRepository = blogRepository; + BlogPostRepository = blogPostRepository; + BlobContainer = blobContainer; + } + + public async Task GetAsync(string blogSlug, string blogPostSlug) + { + var blog = await BlogRepository.GetBySlugAsync(blogSlug); + + var blogPost = await BlogPostRepository.GetBySlugAsync(blog.Id, blogPostSlug); + + return ObjectMapper.Map(blogPost); + } + + public async Task> GetListAsync(string blogSlug, PagedAndSortedResultRequestDto input) + { + var blog = await BlogRepository.GetBySlugAsync(blogSlug); + + var blogPosts = await BlogPostRepository.GetPagedListAsync(blog.Id, input.SkipCount, input.MaxResultCount, input.Sorting); + + return new PagedResultDto( + await BlogPostRepository.GetCountAsync(blog.Id), + ObjectMapper.Map, List>(blogPosts)); + } + + public async Task GetCoverImageAsync(Guid id) + { + var stream = await BlobContainer.GetAsync(id.ToString()); + + return new RemoteStreamContent(stream); + } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/PublicApplicationAutoMapperProfile.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/PublicApplicationAutoMapperProfile.cs index a45eaff0b2..46dab5a9a1 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/PublicApplicationAutoMapperProfile.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/PublicApplicationAutoMapperProfile.cs @@ -1,8 +1,10 @@ using AutoMapper; using Volo.Abp.AutoMapper; +using Volo.CmsKit.Blogs; using Volo.CmsKit.Comments; using Volo.CmsKit.Contents; using Volo.CmsKit.Pages; +using Volo.CmsKit.Public.Blogs; using Volo.CmsKit.Public.Comments; using Volo.CmsKit.Public.Contents; using Volo.CmsKit.Public.Pages; @@ -16,7 +18,7 @@ namespace Volo.CmsKit.Public { public PublicApplicationAutoMapperProfile() { - CreateMap(); + CreateMap(); CreateMap() .Ignore(x=> x.Author); @@ -30,6 +32,8 @@ namespace Volo.CmsKit.Public CreateMap(); CreateMap(); + + CreateMap(MemberList.None); } } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.HttpApi/Volo/CmsKit/Public/Blogs/BlogPostPublicController.cs b/modules/cms-kit/src/Volo.CmsKit.Public.HttpApi/Volo/CmsKit/Public/Blogs/BlogPostPublicController.cs new file mode 100644 index 0000000000..7f3545ad9c --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Public.HttpApi/Volo/CmsKit/Public/Blogs/BlogPostPublicController.cs @@ -0,0 +1,55 @@ +using Microsoft.AspNetCore.Mvc; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Volo.Abp; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Content; +using Volo.Abp.GlobalFeatures; +using Volo.CmsKit.GlobalFeatures; +using Volo.CmsKit.Public.Blogs; + +namespace Volo.CmsKit.Public.Blogs +{ + [RequiresGlobalFeature(typeof(BlogsFeature))] + [RemoteService(Name = CmsKitPublicRemoteServiceConsts.RemoteServiceName)] + [Area("cms-kit")] + [Route("api/cms-kit-public/blog-posts")] + public class BlogPostPublicController : CmsKitPublicControllerBase, IBlogPostPublicAppService + { + protected IBlogPostPublicAppService BlogPostPublicAppService { get; } + + public BlogPostPublicController(IBlogPostPublicAppService blogPostPublicAppService) + { + BlogPostPublicAppService = blogPostPublicAppService; + } + + [HttpGet] + [Route("{blogSlug}/{blogPostSlug}")] + public Task GetAsync(string blogSlug, string blogPostSlug) + { + return BlogPostPublicAppService.GetAsync(blogSlug, blogPostSlug); + } + + [HttpGet] + [Route("{id}/cover-image")] + public Task GetCoverImageAsync(Guid id) + { + Response.Headers.Add("Content-Disposition", $"inline;filename=\"{id}\""); + Response.Headers.Add("Accept-Ranges", "bytes"); + Response.Headers.Add("Cache-Control", "max-age=120"); + Response.ContentType = "image"; + + return BlogPostPublicAppService.GetCoverImageAsync(id); + } + + [HttpGet] + [Route("{blogSlug}")] + public Task> GetListAsync(string blogSlug, PagedAndSortedResultRequestDto input) + { + return BlogPostPublicAppService.GetListAsync(blogSlug, input); + } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/CmsKitPublicWebModule.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Web/CmsKitPublicWebModule.cs index 953102aa79..b5c7b9bb3f 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/CmsKitPublicWebModule.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/CmsKitPublicWebModule.cs @@ -61,10 +61,6 @@ namespace Volo.CmsKit.Public.Web { Configure(options => { - if (GlobalFeatureManager.Instance.IsEnabled()) - { - options.Conventions.AddPageRoute("/CmsKit/Pages/Index", @"{*pageUrl:minlength(1)}"); - } }); } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Controllers/PageController.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Controllers/PageController.cs new file mode 100644 index 0000000000..5d3d5acc9c --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Controllers/PageController.cs @@ -0,0 +1,37 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.ActionConstraints; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Volo.Abp.GlobalFeatures; +using Volo.CmsKit.GlobalFeatures; +using Volo.CmsKit.Public.Pages; + +namespace Volo.CmsKit.Public.Web.Controllers +{ + [RequiresGlobalFeature(typeof(PagesFeature))] + public class PageController : CmsKitPublicControllerBase + { + protected IPageAppService PageAppService { get; } + + public PageController(IPageAppService pageAppService) + { + PageAppService = pageAppService; + } + + //[HttpGet("/{*url}", Order = int.MaxValue)] + public async Task IndexAsync(string url) + { + var page = await PageAppService.FindByUrlAsync(url); + + if (page == null) + { + return NotFound(); + } + + return View("~/Views/Page/Index.cshtml", page); + } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Blogs/BlogPost.cshtml b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Blogs/BlogPost.cshtml new file mode 100644 index 0000000000..51472ce7c1 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Blogs/BlogPost.cshtml @@ -0,0 +1,63 @@ +@page "/blogs/{blogSlug}/{blogPostSlug}" + +@using Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Blogs.BlogPost +@using Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Blogs.BlogPostComment +@using Volo.CmsKit.Public.Web.Pages.CmsKit.Blogs +@using Volo.CmsKit.Public.Web.Pages +@using Volo.Abp.GlobalFeatures +@using Volo.CmsKit.GlobalFeatures +@using Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.ReactionSelection +@using Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Rating +@using Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Tags + +@inherits CmsKitPublicPageBase + +@model BlogPostModel + +@{ + PageLayout.Content.Title = Model.BlogPost.Title; +} + +@await Component.InvokeAsync(typeof(DefaultBlogPostViewComponent), new +{ + Model.BlogSlug, + Model.BlogPostSlug +}) + +@if (GlobalFeatureManager.Instance.IsEnabled()) +{ + @await Component.InvokeAsync(typeof(TagViewComponent), new + { + entityType = Volo.CmsKit.Blogs.BlogPostConsts.EntityType, + entityId = Model.BlogPost.Id.ToString() + }) +} + +@if (GlobalFeatureManager.Instance.IsEnabled()) +{ + @await Component.InvokeAsync(typeof(ReactionSelectionViewComponent), new + { + entityType = Volo.CmsKit.Blogs.BlogPostConsts.EntityType, + entityId = Model.BlogPost.Id.ToString() + }) +} + +@if (GlobalFeatureManager.Instance.IsEnabled()) +{ + @await Component.InvokeAsync(typeof(RatingViewComponent), new + { + entityType = Volo.CmsKit.Blogs.BlogPostConsts.EntityType, + entityId = Model.BlogPost.Id.ToString() + }) +} + +
+ +@if (GlobalFeatureManager.Instance.IsEnabled()) +{ + @await Component.InvokeAsync(typeof(DefaultBlogPostCommentViewComponent), new + { + entityType = Volo.CmsKit.Blogs.BlogPostConsts.EntityType, + entityId = Model.BlogPost.Id.ToString() + }) +} \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Blogs/BlogPost.cshtml.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Blogs/BlogPost.cshtml.cs new file mode 100644 index 0000000000..a3f31bb9a2 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Blogs/BlogPost.cshtml.cs @@ -0,0 +1,33 @@ +using Microsoft.AspNetCore.Mvc; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Volo.CmsKit.Public.Blogs; + +namespace Volo.CmsKit.Public.Web.Pages.CmsKit.Blogs +{ + public class BlogPostModel : CmsKitPublicPageModelBase + { + [BindProperty(SupportsGet = true)] + public string BlogSlug { get; set; } + + [BindProperty(SupportsGet = true)] + public string BlogPostSlug { get; set; } + + public BlogPostPublicDto BlogPost { get; private set; } + + protected IBlogPostPublicAppService BlogPostPublicAppService { get; } + + public BlogPostModel(IBlogPostPublicAppService blogPostPublicAppService) + { + BlogPostPublicAppService = blogPostPublicAppService; + } + + public async Task OnGetAsync() + { + BlogPost = await BlogPostPublicAppService.GetAsync(BlogSlug, BlogPostSlug); + } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Blogs/Index.cshtml b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Blogs/Index.cshtml new file mode 100644 index 0000000000..066e4dd7c2 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Blogs/Index.cshtml @@ -0,0 +1,47 @@ +@page "/blogs/{blogSlug}" + +@using Volo.CmsKit.Public.Web.Pages + +@inherits CmsKitPublicPageBase + +@model Volo.CmsKit.Public.Web.Pages.CmsKit.Blogs.IndexModel + + + @foreach (var blog in Model.Blogs.Items) + { + + + + + @blog.Title + + + @@@blog.Creator?.UserName + + + + + + @blog.ShortDescription + + + + + + + + + + @blog.CreationTime + + + + + + } + + + + + diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Blogs/Index.cshtml.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Blogs/Index.cshtml.cs new file mode 100644 index 0000000000..9f44d9beaa --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Blogs/Index.cshtml.cs @@ -0,0 +1,45 @@ +using Microsoft.AspNetCore.Mvc; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Volo.Abp.Application.Dtos; +using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Pagination; +using Volo.CmsKit.Public.Blogs; + +namespace Volo.CmsKit.Public.Web.Pages.CmsKit.Blogs +{ + public class IndexModel : CmsKitPublicPageModelBase + { + public const int PageSize = 12; + + [BindProperty(SupportsGet = true)] + public string BlogSlug { get; set; } + + [BindProperty(SupportsGet = true)] + public int CurrentPage { get; set; } = 1; + + public PagedResultDto Blogs { get; private set; } + + public PagerModel PagerModel => new PagerModel(Blogs.TotalCount, Blogs.Items.Count, CurrentPage, PageSize, Request.Path.ToString()); + + protected IBlogPostPublicAppService BlogPostPublicAppService { get; } + + public IndexModel(IBlogPostPublicAppService blogPostPublicAppService) + { + BlogPostPublicAppService = blogPostPublicAppService; + } + + public async Task OnGetAsync() + { + Blogs = await BlogPostPublicAppService.GetListAsync( + BlogSlug, + new PagedAndSortedResultRequestDto + { + SkipCount = PageSize * (CurrentPage - 1), + MaxResultCount = PageSize + }); + } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Pages/Index.cshtml b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Pages/Index.cshtml deleted file mode 100644 index 73c7637b01..0000000000 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Pages/Index.cshtml +++ /dev/null @@ -1,13 +0,0 @@ -@page "{*pageUrl}" -@using Microsoft.AspNetCore.Mvc.Localization -@using Volo.CmsKit.Localization -@using Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Pages -@model Volo.CmsKit.Public.Web.Pages.CmsKit.Pages.IndexModel -@inject IHtmlLocalizer L - -@await Component.InvokeAsync(typeof(DefaultPageViewComponent), - new - { - pageId = Model.Page.Id, - title = Model.Page.Title - }) \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Pages/Index.cshtml.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Pages/Index.cshtml.cs deleted file mode 100644 index 5443608845..0000000000 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Pages/Index.cshtml.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Volo.CmsKit.Public.Pages; -using Volo.CmsKit.Web.Pages; - -namespace Volo.CmsKit.Public.Web.Pages.CmsKit.Pages -{ - public class IndexModel : CommonPageModel - { - [BindProperty(SupportsGet = true)] - public string PageUrl { get; set; } - - protected readonly IPageAppService PageAppService; - - public PageDto Page; - - public IndexModel(IPageAppService pageAppService) - { - PageAppService = pageAppService; - } - - public async Task OnGetAsync() - { - Page = await PageAppService.FindByUrlAsync(PageUrl); - - if (Page == null) - { - return NotFound(); - } - - return Page(); - } - } -} \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Blogs/BlogPost/Default.cshtml b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Blogs/BlogPost/Default.cshtml new file mode 100644 index 0000000000..a6cef52512 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Blogs/BlogPost/Default.cshtml @@ -0,0 +1,33 @@ +@model Volo.CmsKit.Public.Blogs.BlogPostPublicDto +@using Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Contents +@using Volo.Abp.AspNetCore.Mvc.UI.Layout +@using Volo.CmsKit.Localization +@using Microsoft.Extensions.Localization + +@inject IStringLocalizer L + + + + + @Model.Title + @Model.Creator?.UserName + + + + @**@ + @await Component.InvokeAsync(typeof(ContentViewComponent), new + { + entityType = Volo.CmsKit.Blogs.BlogPostConsts.EntityType, + entityId = Model.Id.ToString() + }) + + + @Model.CreationTime + @if (Model.LastModificationTime != null) + { + @L["LastModification"].Value : @Model.LastModificationTime + } + + + + diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Blogs/BlogPost/DefaultBlogPostViewComponent.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Blogs/BlogPost/DefaultBlogPostViewComponent.cs new file mode 100644 index 0000000000..90dc2a6ab0 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Blogs/BlogPost/DefaultBlogPostViewComponent.cs @@ -0,0 +1,29 @@ +using Microsoft.AspNetCore.Mvc; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Volo.Abp.AspNetCore.Mvc; +using Volo.CmsKit.Public.Blogs; + +namespace Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Blogs.BlogPost +{ + [ViewComponent(Name = "CmsDefaultBlogPost")] + public class DefaultBlogPostViewComponent : AbpViewComponent + { + protected IBlogPostPublicAppService BlogPostPublicAppService { get; } + + public DefaultBlogPostViewComponent(IBlogPostPublicAppService blogPostPublicAppService) + { + BlogPostPublicAppService = blogPostPublicAppService; + } + + public virtual async Task InvokeAsync(string blogSlug, string blogPostSlug) + { + var blogPost = await BlogPostPublicAppService.GetAsync(blogSlug, blogPostSlug); + + return View("~/Pages/CmsKit/Shared/Components/Blogs/BlogPost/Default.cshtml", blogPost); + } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Blogs/BlogPostComment/Default.cshtml b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Blogs/BlogPostComment/Default.cshtml new file mode 100644 index 0000000000..c34f64eb0c --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Blogs/BlogPostComment/Default.cshtml @@ -0,0 +1,22 @@ +@using Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Commenting +@using Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Blogs.BlogPostComment +@using Volo.CmsKit.Localization +@using Microsoft.Extensions.Localization + +@inject IStringLocalizer L + +@model DefaultBlogPostCommentViewComponent.DefaultBlogPostCommentViewModel + + + + + @L["Comments"] + + + @await Component.InvokeAsync(typeof(CommentingViewComponent), new + { + entityType = Model.EntityType, + entityId = Model.EntityId + }) + + \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Blogs/BlogPostComment/DefaultBlogPostCommentViewComponent.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Blogs/BlogPostComment/DefaultBlogPostCommentViewComponent.cs new file mode 100644 index 0000000000..c551146f5f --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Blogs/BlogPostComment/DefaultBlogPostCommentViewComponent.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Mvc; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Volo.Abp.AspNetCore.Mvc; +using Volo.CmsKit.Public.Blogs; + +namespace Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Blogs.BlogPostComment +{ + [ViewComponent(Name = "CmsDefaultBlogPostComment")] + public class DefaultBlogPostCommentViewComponent : AbpViewComponent + { + protected IBlogPostPublicAppService BlogPostPublicAppService; + + public DefaultBlogPostCommentViewComponent(IBlogPostPublicAppService blogPostPublicAppService) + { + BlogPostPublicAppService = blogPostPublicAppService; + } + + public virtual async Task InvokeAsync(string entityType, string entityId) + { + return View( + "~/Pages/CmsKit/Shared/Components/Blogs/BlogPostComment/Default.cshtml", + new DefaultBlogPostCommentViewModel + { + EntityType = entityType, + EntityId = entityId + }); + } + + public class DefaultBlogPostCommentViewModel + { + public string EntityType { get; set; } + public string EntityId { get; set; } + } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKitPublicPageBase.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKitPublicPageBase.cs new file mode 100644 index 0000000000..879745bf24 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKitPublicPageBase.cs @@ -0,0 +1,26 @@ +using Microsoft.AspNetCore.Mvc.Razor.Internal; +using Microsoft.Extensions.Localization; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Volo.Abp.AspNetCore.Mvc.UI.Layout; +using Volo.CmsKit.Localization; + +namespace Volo.CmsKit.Public.Web.Pages +{ + public class CmsKitPublicPageBase : Microsoft.AspNetCore.Mvc.RazorPages.Page + { + [RazorInject] + public IStringLocalizer L { get; set; } + + [RazorInject] + public IPageLayout PageLayout { get; set; } + + public override Task ExecuteAsync() + { + return Task.CompletedTask; // Will be overriden by razor pages. (.cshtml) + } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Properties/launchSettings.json b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Properties/launchSettings.json new file mode 100644 index 0000000000..60e2c66615 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:60873/", + "sslPort": 44300 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "Volo.CmsKit.Public.Web": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:5001;http://localhost:5000" + } + } +} \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Views/Page/Index.cshtml b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Views/Page/Index.cshtml new file mode 100644 index 0000000000..de094041e4 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Views/Page/Index.cshtml @@ -0,0 +1,9 @@ +@model Volo.CmsKit.Public.Pages.PageDto + +@using Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Pages + +@await Component.InvokeAsync(typeof(DefaultPageViewComponent), new +{ + pageId = Model.Id, + title = Model.Title +}) \ No newline at end of file diff --git a/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Blogs/BlogPostAdminAppService_Tests.cs b/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Blogs/BlogPostAdminAppService_Tests.cs new file mode 100644 index 0000000000..efef70cb9b --- /dev/null +++ b/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Blogs/BlogPostAdminAppService_Tests.cs @@ -0,0 +1,225 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using Castle.DynamicProxy.Generators; +using Shouldly; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Content; +using Volo.Abp.Domain.Entities; +using Volo.CmsKit.Admin.Blogs; +using Xunit; + +namespace Volo.CmsKit.Blogs +{ + public class BlogPostAdminAppService_Tests : CmsKitApplicationTestBase + { + private readonly IBlogPostAdminAppService blogPostAdminAppService; + private readonly CmsKitTestData cmsKitTestData; + private readonly IBlogPostRepository blogPostRepository; + + public BlogPostAdminAppService_Tests() + { + blogPostAdminAppService = GetRequiredService(); + cmsKitTestData = GetRequiredService(); + blogPostRepository = GetRequiredService(); + } + + [Fact] + public async Task CreateAsync_ShouldWorkProperly_WithCorrectData() + { + var title = "My awesome new Post"; + var slug = "my-awesome-new-post"; + var shortDescription = "This blog is all about awesomeness 🤗!"; + + var created = await blogPostAdminAppService.CreateAsync(new CreateUpdateBlogPostDto + { + BlogId = cmsKitTestData.Blog_Id, + Title = title, + Slug = slug, + ShortDescription = shortDescription + }); + + created.Id.ShouldNotBe(Guid.Empty); + + var blogPost = await blogPostRepository.GetAsync(created.Id); + + blogPost.Title.ShouldBe(title); + blogPost.Slug.ShouldBe(slug); + blogPost.ShortDescription.ShouldBe(shortDescription); + } + + [Fact] + public async Task CreateAsync_ShouldThrowException_WithNonExistingBlogId() + { + var title = "Another My Awesome New Post"; + var slug = "another-my-awesome-new-post"; + var shortDescription = "This blog is all about awesomeness 🤗!"; + + var dto = new CreateUpdateBlogPostDto + { + // Non-existing Id + BlogId = Guid.NewGuid(), + Title = title, + Slug = slug, + ShortDescription = shortDescription + }; + + var exception = await Should.ThrowAsync(async () => + await blogPostAdminAppService.CreateAsync(dto)); + + exception.EntityType.ShouldBe(typeof(Blog)); + } + + [Fact] + public async Task GetAsync_ShouldWorkProperly_WithExistingId() + { + var blogPost = await blogPostAdminAppService.GetAsync(cmsKitTestData.BlogPost_1_Id); + + blogPost.Title.ShouldBe(cmsKitTestData.BlogPost_1_Title); + blogPost.Slug.ShouldBe(cmsKitTestData.BlogPost_1_Slug); + } + + [Fact] + public async Task GetAsync_ShouldThrowException_WithNonExistingId() + { + var nonExistingId = Guid.NewGuid(); + var exception = await Should.ThrowAsync(async () => + await blogPostAdminAppService.GetAsync(nonExistingId)); + + exception.EntityType.ShouldBe(typeof(BlogPost)); + exception.Id.ShouldBe(nonExistingId); + } + + [Fact] + public async Task GetBySlugAsync_ShouldWorkProperly_WithExistingSlug() + { + var blogPost = await blogPostAdminAppService.GetBySlugAsync(cmsKitTestData.BlogSlug, cmsKitTestData.BlogPost_1_Slug); + + blogPost.Id.ShouldBe(cmsKitTestData.BlogPost_1_Id); + blogPost.Title.ShouldBe(cmsKitTestData.BlogPost_1_Title); + } + + [Fact] + public async Task GetBySlugAsync_ShouldThrowException_WithNonExistingBlogPostSlug() + { + var nonExistingSlug = "any-other-url"; + var exception = await Should.ThrowAsync(async () => + await blogPostAdminAppService.GetBySlugAsync(cmsKitTestData.BlogSlug, nonExistingSlug)); + + exception.EntityType.ShouldBe(typeof(BlogPost)); + } + + [Fact] + public async Task GetBySlugAsync_ShouldThrowException_WithNonExistingBlogSlug() + { + var nonExistingSlug = "any-other-url"; + var exception = await Should.ThrowAsync(async () => + await blogPostAdminAppService.GetBySlugAsync(nonExistingSlug, cmsKitTestData.Page_1_Url)); + + exception.EntityType.ShouldBe(typeof(Blog)); + } + + [Fact] + public async Task GetListAsync_ShouldWorkProperly_WithDefaultParameters() + { + var list = await blogPostAdminAppService.GetListAsync(new PagedAndSortedResultRequestDto()); + + list.ShouldNotBeNull(); + list.TotalCount.ShouldBe(2); + list.Items.ShouldNotBeEmpty(); + list.Items.Count.ShouldBe(2); + } + + [Fact] + public async Task UpdateAsync_ShouldWorkProperly_WithRegularDatas() + { + var shortDescription = "Another short description"; + var title = "[Solved] Another Blog Post"; + var slug = "another-short-blog-post"; + + await blogPostAdminAppService.UpdateAsync(cmsKitTestData.BlogPost_2_Id, new CreateUpdateBlogPostDto + { + BlogId = cmsKitTestData.Blog_Id, + ShortDescription = shortDescription, + Title = title, + Slug = slug, + }); + + var blogPost = await blogPostRepository.GetAsync(cmsKitTestData.BlogPost_2_Id); + + blogPost.Title.ShouldBe(title); + blogPost.ShortDescription.ShouldBe(shortDescription); + blogPost.Slug.ShouldBe(slug); + } + + [Fact] + public async Task UpdateAsync_ShouldThrowException_WhileChangingWithNonExistingBlogId() + { + var nonExistingId = Guid.NewGuid(); + + var dto = new CreateUpdateBlogPostDto + { + BlogId = nonExistingId, + Title = cmsKitTestData.Page_2_Title, + Slug = cmsKitTestData.BlogPost_2_Slug + }; + + var exception = await Should.ThrowAsync(async () => + await blogPostAdminAppService.UpdateAsync(cmsKitTestData.BlogPost_2_Id, dto)); + + exception.EntityType.ShouldBe(typeof(Blog)); + exception.Id.ShouldBe(nonExistingId); + } + + [Fact] + public async Task UpdateAsync_ShouldThrowException_WhileUpdatingWithAlreadyExistingSlug() + { + var dto = new CreateUpdateBlogPostDto + { + BlogId = cmsKitTestData.Blog_Id, + Title = "Some new title", + Slug = cmsKitTestData.BlogPost_1_Slug + }; + + var exception = await Should.ThrowAsync(async () => + await blogPostAdminAppService.UpdateAsync(cmsKitTestData.BlogPost_2_Id, dto)); + + exception.Slug.ShouldBe(cmsKitTestData.BlogPost_1_Slug); + } + + [Fact] + public async Task DeleteAsync_ShouldWorkProperly_WithExistingId() + { + await blogPostAdminAppService.DeleteAsync(cmsKitTestData.Page_2_Id); + + var exception = await Should.ThrowAsync(async () => + await blogPostRepository.GetAsync(cmsKitTestData.Page_2_Id)); + + exception.EntityType.ShouldBe(typeof(BlogPost)); + exception.Id.ShouldBe(cmsKitTestData.Page_2_Id); + } + + [Fact] + public async Task SetCoverImage_ShouldNotThrowException_WithCorrectData() + { + using (var imageStream = GetSampleImageStream()) + { + await WithUnitOfWorkAsync(async () => + { + await blogPostAdminAppService.SetCoverImageAsync( + cmsKitTestData.BlogPost_2_Id, + new RemoteStreamContent(imageStream)); + }); + } + } + + private Stream GetSampleImageStream() + { + var assembly = GetType().Assembly; + var resourceName = "Volo.CmsKit.Data.BlogPostSample.png"; + + return assembly.GetManifestResourceStream(resourceName); + } + // SetCoverImage + } +} diff --git a/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Data/BlogPostSample.png b/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Data/BlogPostSample.png new file mode 100644 index 0000000000..117bc57c3d Binary files /dev/null and b/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Data/BlogPostSample.png differ diff --git a/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Tags/TagPublicAppService_Tests.cs b/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Tags/TagPublicAppService_Tests.cs index 50eefd186b..26a7454a2b 100644 --- a/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Tags/TagPublicAppService_Tests.cs +++ b/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Tags/TagPublicAppService_Tests.cs @@ -3,7 +3,7 @@ using NSubstitute; using Shouldly; using System.Threading.Tasks; using Volo.Abp.Users; -using Volo.CmsKit.Public.Tags; +using Volo.CmsKit.Tags; using Xunit; namespace Volo.CmsKit.Tags diff --git a/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Volo.CmsKit.Application.Tests.csproj b/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Volo.CmsKit.Application.Tests.csproj index 26c4be533f..3fac76bfd9 100644 --- a/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Volo.CmsKit.Application.Tests.csproj +++ b/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Volo.CmsKit.Application.Tests.csproj @@ -5,6 +5,10 @@ Volo.CmsKit + + + + diff --git a/modules/cms-kit/test/Volo.CmsKit.Domain.Tests/Blogs/BlogPostManager_Tests.cs b/modules/cms-kit/test/Volo.CmsKit.Domain.Tests/Blogs/BlogPostManager_Tests.cs new file mode 100644 index 0000000000..2da60c8bff --- /dev/null +++ b/modules/cms-kit/test/Volo.CmsKit.Domain.Tests/Blogs/BlogPostManager_Tests.cs @@ -0,0 +1,92 @@ +using Shouldly; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Volo.Abp.Guids; +using Volo.CmsKit.Tags; +using Xunit; + +namespace Volo.CmsKit.Blogs +{ + public class BlogPostManager_Tests : CmsKitDomainTestBase + { + private readonly IBlogPostManager blogPostManager; + private readonly IGuidGenerator guidGenerator; + private readonly IBlogPostRepository blogPostRepository; + private readonly CmsKitTestData cmsKitTestData; + + public BlogPostManager_Tests() + { + this.blogPostManager = GetRequiredService(); + this.guidGenerator = GetRequiredService(); + this.blogPostRepository = GetRequiredService(); + this.cmsKitTestData = GetRequiredService(); + } + + [Fact] + public async Task CreateAsync_ShouldWorkProperly_WithCorrectData() + { + var title = "New blog post"; + var slug = "new-blog-post"; + + var created = await blogPostManager.CreateAsync( + new BlogPost(guidGenerator.Create(), cmsKitTestData.Blog_Id, title, slug)); + + created.Id.ShouldNotBe(Guid.Empty); + + var blogPost = await blogPostRepository.GetAsync(created.Id); + blogPost.Title.ShouldBe(title); + blogPost.Slug.ShouldBe(slug); + } + + [Fact] + public async Task CreateAsync_ShouldThrowException_WhenSlugAlreadyExists() + { + var blogPost = new BlogPost(guidGenerator.Create(), cmsKitTestData.Blog_Id, "Any New Title", cmsKitTestData.BlogPost_1_Slug); + + await Should.ThrowAsync(async () => + await blogPostManager.CreateAsync(blogPost)); + } + + [Fact] + public async Task UpdateAsync_ShoudlWorkProperly_WithCorrectData() + { + var newTitle = "Yet Another Post"; + + var blogPost = await blogPostRepository.GetAsync(cmsKitTestData.BlogPost_1_Id); + + blogPost.SetTitle(newTitle); + + await blogPostManager.UpdateAsync(blogPost); + + var updated = await blogPostRepository.GetAsync(cmsKitTestData.BlogPost_1_Id); + updated.Title.ShouldBe(newTitle); + } + + [Fact] + public async Task SetSlugAsync_ShouldWorkProperly_WithNonExistingSlug() + { + var newSlug = "yet-another-post"; + + var blogPost = await blogPostRepository.GetAsync(cmsKitTestData.BlogPost_1_Id); + + await blogPostManager.SetSlugUrlAsync(blogPost, newSlug); + + blogPost.Slug.ShouldBe(newSlug); + } + + [Fact] + public async Task SetSlugAsync_ShouldThrowException_WithExistingSlug() + { + var blogPost = await blogPostRepository.GetAsync(cmsKitTestData.BlogPost_1_Id); + + var exception = await Should.ThrowAsync(async () => + await blogPostManager.SetSlugUrlAsync(blogPost, cmsKitTestData.BlogPost_2_Slug)); + + exception.BlogId.ShouldBe(blogPost.BlogId); + exception.Slug.ShouldBe(cmsKitTestData.BlogPost_2_Slug); + } + } +} diff --git a/modules/cms-kit/test/Volo.CmsKit.Domain.Tests/Blogs/SlugExtensions_Tests.cs b/modules/cms-kit/test/Volo.CmsKit.Domain.Tests/Blogs/SlugExtensions_Tests.cs new file mode 100644 index 0000000000..3eb7ee52f7 --- /dev/null +++ b/modules/cms-kit/test/Volo.CmsKit.Domain.Tests/Blogs/SlugExtensions_Tests.cs @@ -0,0 +1,140 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xunit; +using Volo.CmsKit.Blogs.Extensions; +using Shouldly; +using Unidecode.NET; + +namespace Volo.CmsKit.Blogs +{ + public class SlugExtensions_Tests + { + [Fact] + public void NormalizeSlug_ShouldWorkProperly() + { + // Arrange + var name = "My awesome name"; + var expected = "my-awesome-name"; + + // Act + var actual = name.NormalizeSlug(); + + // Assert + actual.ShouldBe(expected); + } + [Fact] + public void NormalizeSlug_ShouldWorkProperly_WithDot() + { + // Arrange + var name = "My Perfect Title v.2"; + var expected = "my-perfect-title-v.2"; + + // Act + var actual = name.NormalizeSlug(); + + // Assert + actual.ShouldBe(expected); + } + + [Fact] + public void NormalizeSlug_ShouldWorkProperly_WithQuestionMark() + { + // Arrange + var name = "Are you gonna die?"; + var expected = "are-you-gonna-die"; + + // Act + var actual = name.NormalizeSlug(); + + // Assert + actual.ShouldBe(expected); + } + + [Fact] + public void NormalizeSlug_ShouldWorkProperly_WithAmpersand() + { + // Arrange + var name = "We & Machines Challenge"; + var expected = "we-machines-challenge"; + + // Act + var actual = name.NormalizeSlug(); + + // Assert + actual.ShouldBe(expected); + } + + [Fact] + public void NormalizeSlug_ShouldWorkProperly_WithDoubleDash() + { + // Arrange + var name = "Go and Code --part 2"; + var expected = "go-and-code-part-2"; + + // Act + var actual = name.NormalizeSlug(); + + // Assert + actual.ShouldBe(expected); + } + + [Fact] + public void NormalizeSlug_ShouldWorkProperly_WithCyrillicChars() + { + // Arrange + var name = "Мое классное название"; + var expected = "moe-klassnoe-nazvanie"; + + // Act + var actual = name.NormalizeSlug(); + + // Assert + actual.ShouldBe(expected); + } + + [Fact] + public void NormalizeSlug_ShouldWorkProperly_WithTurkishChars() + { + // Arrange + var name = "Özel Türkçe karakterler: ğüşiöç"; + var expected = "ozel-turkce-karakterler-gusioc"; + + // Act + var actual = name.NormalizeSlug(); + + // Assert + actual.ShouldBe(expected); + } + + [Fact] + public void NormalizeSlug_ShouldWorkProperly_WithChineseChars() + { + // Arrange + var name = "我的真棒头衔"; + var expected = "wo-de-zhen-bang-tou-xian"; + + // Act + var actual = name.NormalizeSlug(); + + // Assert + actual.ShouldBe(expected); + } + + [Fact] + public void NormalizeSlug_ShouldWorkProperly_WithEmoji() + { + // Arrange + var name = "Let's Rock 👊"; + var expected = "lets-rock"; + + // Act + var actual = name.NormalizeSlug(); + + // Assert + actual.ShouldBe(expected); + } + } +} diff --git a/modules/cms-kit/test/Volo.CmsKit.EntityFrameworkCore.Tests/EntityFrameworkCore/Blogs/BlogPostRepository_Test.cs b/modules/cms-kit/test/Volo.CmsKit.EntityFrameworkCore.Tests/EntityFrameworkCore/Blogs/BlogPostRepository_Test.cs new file mode 100644 index 0000000000..d393ef943e --- /dev/null +++ b/modules/cms-kit/test/Volo.CmsKit.EntityFrameworkCore.Tests/EntityFrameworkCore/Blogs/BlogPostRepository_Test.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Volo.CmsKit.Blogs; +using Volo.CmsKit.Pages; + +namespace Volo.CmsKit.EntityFrameworkCore.Blogs +{ + public class BlogPostRepository_Test : BlogPostRepository_Test + { + } +} diff --git a/modules/cms-kit/test/Volo.CmsKit.EntityFrameworkCore.Tests/EntityFrameworkCore/Blogs/BlogRepository_Test.cs b/modules/cms-kit/test/Volo.CmsKit.EntityFrameworkCore.Tests/EntityFrameworkCore/Blogs/BlogRepository_Test.cs new file mode 100644 index 0000000000..40b74d4064 --- /dev/null +++ b/modules/cms-kit/test/Volo.CmsKit.EntityFrameworkCore.Tests/EntityFrameworkCore/Blogs/BlogRepository_Test.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Volo.CmsKit.Blogs; + +namespace Volo.CmsKit.EntityFrameworkCore.Blogs +{ + public class BlogRepository_Test : BlogRepository_Test + { + + } +} diff --git a/modules/cms-kit/test/Volo.CmsKit.EntityFrameworkCore.Tests/EntityFrameworkCore/Tags/EntityTagRepository_Test.cs b/modules/cms-kit/test/Volo.CmsKit.EntityFrameworkCore.Tests/EntityFrameworkCore/Tags/EntityTagRepository_Test.cs new file mode 100644 index 0000000000..01464f46e8 --- /dev/null +++ b/modules/cms-kit/test/Volo.CmsKit.EntityFrameworkCore.Tests/EntityFrameworkCore/Tags/EntityTagRepository_Test.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Volo.CmsKit.Tags; + +namespace Volo.CmsKit.EntityFrameworkCore.Tags +{ + public class EntityTagRepository_Test : EntityTagRepository_Test + { + + } +} diff --git a/modules/cms-kit/test/Volo.CmsKit.MongoDB.Tests/MongoDB/Blogs/BlogPostRepository_Test.cs b/modules/cms-kit/test/Volo.CmsKit.MongoDB.Tests/MongoDB/Blogs/BlogPostRepository_Test.cs new file mode 100644 index 0000000000..4bdec74132 --- /dev/null +++ b/modules/cms-kit/test/Volo.CmsKit.MongoDB.Tests/MongoDB/Blogs/BlogPostRepository_Test.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Volo.CmsKit.Blogs; +using Xunit; + +namespace Volo.CmsKit.MongoDB.Blogs +{ + [Collection(MongoTestCollection.Name)] + public class BlogPostRepository_Test : BlogPostRepository_Test + { + + } +} diff --git a/modules/cms-kit/test/Volo.CmsKit.MongoDB.Tests/MongoDB/Blogs/BlogRepository_Test.cs b/modules/cms-kit/test/Volo.CmsKit.MongoDB.Tests/MongoDB/Blogs/BlogRepository_Test.cs new file mode 100644 index 0000000000..9cac7b473c --- /dev/null +++ b/modules/cms-kit/test/Volo.CmsKit.MongoDB.Tests/MongoDB/Blogs/BlogRepository_Test.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Volo.CmsKit.Blogs; +using Xunit; + +namespace Volo.CmsKit.MongoDB.Blogs +{ + [Collection(MongoTestCollection.Name)] + public class BlogRepository_Test : BlogRepository_Test + { + + } +} diff --git a/modules/cms-kit/test/Volo.CmsKit.MongoDB.Tests/MongoDB/Tags/EntityTagRepository_Test.cs b/modules/cms-kit/test/Volo.CmsKit.MongoDB.Tests/MongoDB/Tags/EntityTagRepository_Test.cs new file mode 100644 index 0000000000..919455b109 --- /dev/null +++ b/modules/cms-kit/test/Volo.CmsKit.MongoDB.Tests/MongoDB/Tags/EntityTagRepository_Test.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Volo.CmsKit.Tags; +using Xunit; + +namespace Volo.CmsKit.MongoDB.Tags +{ + [Collection(MongoTestCollection.Name)] + public class EntityTagRepository_Test : EntityTagRepository_Test + { + + } +} diff --git a/modules/cms-kit/test/Volo.CmsKit.TestBase/Blogs/BlogPostRepository_Test.cs b/modules/cms-kit/test/Volo.CmsKit.TestBase/Blogs/BlogPostRepository_Test.cs new file mode 100644 index 0000000000..1b44e3c5b6 --- /dev/null +++ b/modules/cms-kit/test/Volo.CmsKit.TestBase/Blogs/BlogPostRepository_Test.cs @@ -0,0 +1,125 @@ +using Shouldly; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Volo.Abp.Domain.Entities; +using Volo.Abp.Modularity; +using Xunit; + +namespace Volo.CmsKit.Blogs +{ + public abstract class BlogPostRepository_Test : CmsKitTestBase + where TStartupModule : IAbpModule + { + private readonly CmsKitTestData testData; + private readonly IBlogPostRepository blogPostRepository; + + public BlogPostRepository_Test() + { + testData = GetRequiredService(); + blogPostRepository = GetRequiredService(); + } + + [Fact] + public async Task SlugExistsAsync_ShouldReturnTrue_WithExistingSlug() + { + var result = await blogPostRepository.SlugExistsAsync(testData.Blog_Id, testData.BlogPost_1_Slug); + + result.ShouldBeTrue(); + } + + [Fact] + public async Task SlugExistsAsync_ShouldReturnFalse_WithNonExistingSlug() + { + var nonExistingSlug = "any-other-url-slug"; + + var result = await blogPostRepository.SlugExistsAsync(testData.Blog_Id, nonExistingSlug); + + result.ShouldBeFalse(); + } + + [Fact] + public async Task SlugExistsAsync_ShouldReturnFalse_WithNonExistingBlogId() + { + var nonExistingBlogId = Guid.NewGuid(); + + var result = await blogPostRepository.SlugExistsAsync(nonExistingBlogId, testData.BlogPost_1_Slug); + + result.ShouldBeFalse(); + } + + [Fact] + public async Task GetBySlugAsync_ShouldWorkProperly_WithCorrectParameters() + { + var blogPost = await blogPostRepository.GetBySlugAsync(testData.Blog_Id, testData.BlogPost_1_Slug); + + blogPost.ShouldNotBeNull(); + blogPost.Id.ShouldBe(testData.BlogPost_1_Id); + blogPost.Slug.ShouldBe(testData.BlogPost_1_Slug); + } + + [Fact] + public async Task GetBySlugAsync_ShouldThrowException_WithNonExistingBlogPostSlug() + { + var nonExistingSlugUrl = "absolutely-non-existing-url"; + var exception = await Should.ThrowAsync( + async () => await blogPostRepository.GetBySlugAsync(testData.Blog_Id, nonExistingSlugUrl)); + + exception.ShouldNotBeNull(); + exception.EntityType.ShouldBe(typeof(BlogPost)); + } + + [Fact] + public async Task GetBySlugAsync_ShouldThrowException_WithNonExistingBlogId() + { + var nonExistingBlogId = Guid.NewGuid(); + var exception = await Should.ThrowAsync( + async () => await blogPostRepository.GetBySlugAsync(nonExistingBlogId, testData.BlogPost_1_Slug)); + + exception.ShouldNotBeNull(); + exception.EntityType.ShouldBe(typeof(BlogPost)); + } + + [Fact] + public async Task GetPagedListAsync_ShouldWorkProperly_WithBlogId_WhileGetting10_WithoutSorting() + { + var result = await blogPostRepository.GetPagedListAsync(testData.Blog_Id, 0, 10, default); + + result.ShouldNotBeNull(); + result.ShouldNotBeEmpty(); + result.Count.ShouldBe(2); + } + + [Fact] + public async Task GetPagedListAsync_ShouldWorkProperly_WithBlogId_WhileGetting1_WithoutSorting() + { + var result = await blogPostRepository.GetPagedListAsync(testData.Blog_Id, default, 1, default); + + result.ShouldNotBeNull(); + result.ShouldNotBeEmpty(); + result.Count.ShouldBe(1); + } + + [Fact] + public async Task GetPagedListAsync_ShouldWorkProperly_WithBlogId_WhileGetting1InPage2_WithoutSorting() + { + var result = await blogPostRepository.GetPagedListAsync(testData.Blog_Id, 1, 1, default); + + result.ShouldNotBeNull(); + result.ShouldNotBeEmpty(); + result.Count.ShouldBe(1); + } + + [Fact] + public async Task GetPagedListAsync_ShouldWorkProperly_WithBlogId_WhileGetting10_WithSortingByTitle() + { + var result = await blogPostRepository.GetPagedListAsync(testData.Blog_Id, default, 10, nameof(BlogPost.Title)); + + result.ShouldNotBeNull(); + result.ShouldNotBeEmpty(); + result.Count.ShouldBe(2); + } + } +} diff --git a/modules/cms-kit/test/Volo.CmsKit.TestBase/Blogs/BlogRepository_Test.cs b/modules/cms-kit/test/Volo.CmsKit.TestBase/Blogs/BlogRepository_Test.cs new file mode 100644 index 0000000000..50a99519eb --- /dev/null +++ b/modules/cms-kit/test/Volo.CmsKit.TestBase/Blogs/BlogRepository_Test.cs @@ -0,0 +1,65 @@ +using Shouldly; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Volo.Abp.Domain.Entities; +using Volo.Abp.Modularity; +using Xunit; + +namespace Volo.CmsKit.Blogs +{ + public abstract class BlogRepository_Test : CmsKitTestBase + where TStartupModule : IAbpModule + { + private readonly CmsKitTestData testData; + private readonly IBlogRepository blogRepository; + + public BlogRepository_Test() + { + this.testData = GetRequiredService(); + this.blogRepository = GetRequiredService(); + } + + [Fact] + public async Task GetBySlugAsync_ShouldWorkProperly_WithExistingSlug() + { + var blog = await blogRepository.GetBySlugAsync(testData.BlogSlug); + + blog.ShouldNotBeNull(); + blog.Slug.ShouldBe(testData.BlogSlug); + blog.Id.ShouldBe(testData.Blog_Id); + } + + [Fact] + public async Task GetBySlugAsync_ShouldThrowException_WithNonExistingSlug() + { + var nonExistingSlug = "some-blog-slug-that-doesnt-exist"; + + var exception = await Should.ThrowAsync( + async () => await blogRepository.GetBySlugAsync(nonExistingSlug)); + + exception.ShouldNotBeNull(); + exception.EntityType.ShouldBe(typeof(Blog)); + } + + [Fact] + public async Task ExistsAsync_ShouldReturnTrue_WithExistingId() + { + var result = await blogRepository.ExistsAsync(testData.Blog_Id); + + result.ShouldBeTrue(); + } + + [Fact] + public async Task ExistsAsync_ShouldReturnFalse_WithExistingId() + { + var nonExistingId = Guid.NewGuid(); + + var result = await blogRepository.ExistsAsync(nonExistingId); + + result.ShouldBeFalse(); + } + } +} diff --git a/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitDataSeedContributor.cs b/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitDataSeedContributor.cs index e916dd22b3..2ac4a23171 100644 --- a/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitDataSeedContributor.cs +++ b/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitDataSeedContributor.cs @@ -5,9 +5,11 @@ using System.Linq; using System.Threading.Tasks; using Volo.Abp.Data; using Volo.Abp.DependencyInjection; +using Volo.Abp.Domain.Repositories; using Volo.Abp.Guids; using Volo.Abp.MultiTenancy; using Volo.Abp.Users; +using Volo.CmsKit.Blogs; using Volo.CmsKit.Comments; using Volo.CmsKit.Contents; using Volo.CmsKit.Pages; @@ -30,7 +32,10 @@ namespace Volo.CmsKit private readonly IContentRepository _contentRepository; private readonly IEntityTagManager _entityTagManager; private readonly ITagManager _tagManager; + private readonly IEntityTagRepository _entityTagRepository; private readonly IPageRepository _pageRepository; + private readonly IBlogRepository _blogRepository; + private readonly IBlogPostRepository _blogPostRepository; private readonly IOptions _options; private readonly IOptions _tagOptions; @@ -44,8 +49,11 @@ namespace Volo.CmsKit ICurrentTenant currentTenant, IContentRepository contentRepository, ITagManager tagManager, - IEntityTagManager entityTagManager, + IEntityTagRepository entityTagRepository, IPageRepository pageRepository, + IBlogRepository blogRepository, + IBlogPostRepository blogPostRepository, + IEntityTagManager entityTagManager, IOptions options, IOptions tagOptions) { @@ -59,7 +67,10 @@ namespace Volo.CmsKit _contentRepository = contentRepository; _tagManager = tagManager; _entityTagManager = entityTagManager; + _entityTagRepository = entityTagRepository; _pageRepository = pageRepository; + _blogRepository = blogRepository; + _blogPostRepository = blogPostRepository; _options = options; _tagOptions = tagOptions; } @@ -83,6 +94,8 @@ namespace Volo.CmsKit await SeedTagsAsync(); await SeedPagesAsync(); + + await SeedBlogsAsync(); } } @@ -292,5 +305,14 @@ namespace Volo.CmsKit await _pageRepository.InsertAsync(page2); await _contentRepository.InsertAsync(page2Content); } + + private async Task SeedBlogsAsync() + { + var blog = await _blogRepository.InsertAsync(new Blog(_cmsKitTestData.Blog_Id, _cmsKitTestData.BlogName, _cmsKitTestData.BlogSlug)); + + await _blogPostRepository.InsertAsync(new BlogPost(_cmsKitTestData.BlogPost_1_Id, blog.Id, _cmsKitTestData.BlogPost_1_Title, _cmsKitTestData.BlogPost_1_Slug, "Short desc 1")); + + await _blogPostRepository.InsertAsync(new BlogPost(_cmsKitTestData.BlogPost_2_Id, blog.Id, _cmsKitTestData.BlogPost_2_Title, _cmsKitTestData.BlogPost_2_Slug, "Short desc 2")); + } } } diff --git a/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitFakeCurrentUser.cs b/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitFakeCurrentUser.cs new file mode 100644 index 0000000000..ba9c492889 --- /dev/null +++ b/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitFakeCurrentUser.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Claims; +using System.Text; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Users; + +namespace Volo.CmsKit +{ + public class CmsKitFakeCurrentUser : ITransientDependency, ICurrentUser + { + private readonly CmsKitTestData cmsKitTestData; + + public CmsKitFakeCurrentUser(CmsKitTestData cmsKitTestData) + { + this.cmsKitTestData = cmsKitTestData; + } + + public bool IsAuthenticated { get; } + public Guid? Id => cmsKitTestData.User1Id; + public string UserName => cmsKitTestData.User1UserName; + public string Name { get; } + public string SurName { get; } + public string PhoneNumber { get; } + public bool PhoneNumberVerified { get; } + public string Email { get; } + public bool EmailVerified { get; } + public Guid? TenantId { get; } + public string[] Roles { get; } + + public Claim FindClaim(string claimType) + { + throw new NotImplementedException(); + } + + public Claim[] FindClaims(string claimType) + { + throw new NotImplementedException(); + } + + public Claim[] GetAllClaims() + { + throw new NotImplementedException(); + } + + public bool IsInRole(string roleName) + { + throw new NotImplementedException(); + } + } +} diff --git a/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitTestBase.cs b/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitTestBase.cs index fd440ab8c8..0f12b87949 100644 --- a/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitTestBase.cs +++ b/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitTestBase.cs @@ -5,6 +5,7 @@ using Volo.Abp; using Volo.Abp.Modularity; using Volo.Abp.Uow; using Volo.Abp.Testing; +using Volo.Abp.GlobalFeatures; namespace Volo.CmsKit { diff --git a/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitTestBaseModule.cs b/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitTestBaseModule.cs index e12f3d31e8..a0330dadbc 100644 --- a/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitTestBaseModule.cs +++ b/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitTestBaseModule.cs @@ -22,13 +22,16 @@ namespace Volo.CmsKit { private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); - public override void ConfigureServices(ServiceConfigurationContext context) + public override void PreConfigureServices(ServiceConfigurationContext context) { OneTimeRunner.Run(() => { GlobalFeatureManager.Instance.Modules.CmsKit().EnableAll(); }); + } + public override void ConfigureServices(ServiceConfigurationContext context) + { context.Services.AddSingleton(Substitute.For()); Configure(options => @@ -53,7 +56,7 @@ namespace Volo.CmsKit { using (var scope = context.ServiceProvider.CreateScope()) { - await scope.ServiceProvider + await scope.ServiceProvider .GetRequiredService() .SeedAsync(); } diff --git a/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitTestData.cs b/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitTestData.cs index 4415cd7eb3..2d6bf30da6 100644 --- a/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitTestData.cs +++ b/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitTestData.cs @@ -8,6 +8,8 @@ namespace Volo.CmsKit { public Guid User1Id { get; } = Guid.NewGuid(); + public string User1UserName => "fake.user"; + public Guid User2Id { get; } = Guid.NewGuid(); public Guid CommentWithChildId { get; } = Guid.NewGuid(); @@ -70,5 +72,23 @@ namespace Volo.CmsKit public Guid TagId_2 { get; } = Guid.NewGuid(); public string TagName_2 => "News"; + + public Guid Blog_Id { get; } = Guid.NewGuid(); + + public string BlogName => "Cms Blog"; + + public string BlogSlug => "cms-blog"; + + public Guid BlogPost_1_Id { get; } = Guid.NewGuid(); + + public string BlogPost_1_Title => "How to install CmsKit?"; + + public string BlogPost_1_Slug => "how-to-install-cms-kit"; + + public Guid BlogPost_2_Id { get; } = Guid.NewGuid(); + + public string BlogPost_2_Title => "How to use CmsKit"; + + public string BlogPost_2_Slug => "how-to-use-cms-kit"; } } diff --git a/modules/cms-kit/test/Volo.CmsKit.TestBase/Tags/EntityTagRepository_Test.cs b/modules/cms-kit/test/Volo.CmsKit.TestBase/Tags/EntityTagRepository_Test.cs new file mode 100644 index 0000000000..3d60ac0b6a --- /dev/null +++ b/modules/cms-kit/test/Volo.CmsKit.TestBase/Tags/EntityTagRepository_Test.cs @@ -0,0 +1,38 @@ +using Shouldly; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Volo.Abp.Modularity; +using Xunit; + +namespace Volo.CmsKit.Tags +{ + public abstract class EntityTagRepository_Test : CmsKitTestBase + where TStartupModule : IAbpModule + { + private CmsKitTestData _cmsKitTestData; + private IEntityTagRepository _entityTagRepository; + private ITagRepository _tagRepository; + + public EntityTagRepository_Test() + { + _cmsKitTestData = GetRequiredService(); + _entityTagRepository = GetRequiredService(); + _tagRepository = GetRequiredService(); + } + + [Fact] + public async Task DeleteManyAsync_ShouldWorkProperly_WithExistingIds() + { + var relatedTags = await _tagRepository.GetAllRelatedTagsAsync(_cmsKitTestData.Content_1_EntityType, _cmsKitTestData.Content_1_EntityId); + + await _entityTagRepository.DeleteManyAsync(relatedTags.Select(s => s.Id).ToArray()); + + relatedTags = await _tagRepository.GetAllRelatedTagsAsync(_cmsKitTestData.Content_1_EntityType, _cmsKitTestData.Content_1_EntityId); + + relatedTags.ShouldBeEmpty(); + } + } +}