Browse Source

Merge pull request #4961 from abpframework/feat/4760

Add comment to the CMS kit (needs improvement)
pull/4968/head
Halil İbrahim Kalkan 6 years ago
committed by GitHub
parent
commit
7816cf59ab
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/CmsKitWebUnifiedModule.cs
  2. 43
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20200719204212_Added_Reactions.cs
  3. 109
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20200729125121_Initial.Designer.cs
  4. 83
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20200729125121_Initial.cs
  5. 105
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/UnifiedDbContextModelSnapshot.cs
  6. 5
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/Pages/Index.cshtml
  7. 36
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/Pages/Public.cshtml
  8. 11
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/Pages/Public.cshtml.cs
  9. 10
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/Volo.CmsKit.Web.Unified.csproj
  10. 11
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Comments/CommentConsts.cs
  11. 14
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/en.json
  12. 1
      modules/cms-kit/src/Volo.CmsKit.Domain/Volo.CmsKit.Domain.csproj
  13. 2
      modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/CmsKitDomainModule.cs
  14. 7
      modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/CmsKitOptions.cs
  15. 50
      modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Comments/Comment.cs
  16. 11
      modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Comments/CommentWithAuthor.cs
  17. 15
      modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Comments/ICommentRepository.cs
  18. 82
      modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Users/CmsUser.cs
  19. 24
      modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Users/CmsUserLookupService.cs
  20. 43
      modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Users/CmsUserSynchronizer.cs
  21. 9
      modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Users/ICmsUserLookupService.cs
  22. 9
      modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Users/ICmsUserRepository.cs
  23. 3
      modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo.CmsKit.EntityFrameworkCore.csproj
  24. 57
      modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/Comments/EfCoreCommentRepository.cs
  25. 6
      modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/CmsKitDbContext.cs
  26. 26
      modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/CmsKitDbContextModelCreatingExtensions.cs
  27. 13
      modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/CmsKitEntityFrameworkCoreModule.cs
  28. 6
      modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/ICmsKitDbContext.cs
  29. 14
      modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/Users/EfCoreCmsUserRepository.cs
  30. 17
      modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Comments/CmsUserDto.cs
  31. 23
      modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Comments/CommentDto.cs
  32. 24
      modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Comments/CommentWithDetailsDto.cs
  33. 23
      modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Comments/CreateCommentInput.cs
  34. 20
      modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Comments/ICommentPublicAppService.cs
  35. 13
      modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Comments/UpdateCommentInput.cs
  36. 142
      modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Comments/CommentPublicAppService.cs
  37. 9
      modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/PublicApplicationAutoMapperProfile.cs
  38. 49
      modules/cms-kit/src/Volo.CmsKit.Public.HttpApi/Volo/CmsKit/Comments/CommentPublicController.cs
  39. 6
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Controllers/CmsKitPublicWidgetsController.cs
  40. 15
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/CommentingScriptBundleContributor.cs
  41. 13
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/CommentingStyleBundleContributor.cs
  42. 53
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/CommentingViewComponent.cs
  43. 145
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/Default.cshtml
  44. 1
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/default.css
  45. 162
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/default.js

5
modules/cms-kit/host/Volo.CmsKit.Web.Unified/CmsKitWebUnifiedModule.cs

@ -112,6 +112,11 @@ namespace Volo.CmsKit
{
options.IsEnabled = MultiTenancyConsts.IsEnabled;
});
Configure<CmsKitOptions>(options =>
{
options.PublicCommentEntities.Add("publicQuote");
});
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)

43
modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20200719204212_Added_Reactions.cs

@ -1,43 +0,0 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
namespace Volo.CmsKit.Migrations
{
public partial class Added_Reactions : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "CmsUserReactions",
columns: table => new
{
Id = table.Column<Guid>(nullable: false),
EntityType = table.Column<string>(maxLength: 64, nullable: false),
EntityId = table.Column<string>(maxLength: 64, nullable: false),
ReactionName = table.Column<string>(maxLength: 32, nullable: false),
CreatorId = table.Column<Guid>(nullable: false),
CreationTime = table.Column<DateTime>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_CmsUserReactions", x => x.Id);
});
migrationBuilder.CreateIndex(
name: "IX_CmsUserReactions_EntityType_EntityId",
table: "CmsUserReactions",
columns: new[] { "EntityType", "EntityId" });
migrationBuilder.CreateIndex(
name: "IX_CmsUserReactions_CreatorId_EntityType_EntityId_ReactionName",
table: "CmsUserReactions",
columns: new[] { "CreatorId", "EntityType", "EntityId", "ReactionName" });
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "CmsUserReactions");
}
}
}

109
modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20200719204212_Added_Reactions.Designer.cs → modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20200729125121_Initial.Designer.cs

@ -11,8 +11,8 @@ using Volo.CmsKit.EntityFrameworkCore;
namespace Volo.CmsKit.Migrations
{
[DbContext(typeof(UnifiedDbContext))]
[Migration("20200719204212_Added_Reactions")]
partial class Added_Reactions
[Migration("20200729125121_Initial")]
partial class Initial
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
@ -914,6 +914,47 @@ namespace Volo.CmsKit.Migrations
b.ToTable("AbpTenantConnectionStrings");
});
modelBuilder.Entity("Volo.CmsKit.Comments.Comment", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<DateTime>("CreationTime")
.HasColumnName("CreationTime")
.HasColumnType("datetime2");
b.Property<Guid>("CreatorId")
.HasColumnName("CreatorId")
.HasColumnType("uniqueidentifier");
b.Property<string>("EntityId")
.IsRequired()
.HasColumnType("nvarchar(64)")
.HasMaxLength(64);
b.Property<string>("EntityType")
.IsRequired()
.HasColumnType("nvarchar(64)")
.HasMaxLength(64);
b.Property<Guid?>("RepliedCommentId")
.HasColumnType("uniqueidentifier");
b.Property<string>("Text")
.IsRequired()
.HasColumnType("nvarchar(512)")
.HasMaxLength(512);
b.HasKey("Id");
b.HasIndex("RepliedCommentId");
b.HasIndex("EntityType", "EntityId");
b.ToTable("CmsComments");
});
modelBuilder.Entity("Volo.CmsKit.Reactions.UserReaction", b =>
{
b.Property<Guid>("Id")
@ -952,6 +993,70 @@ namespace Volo.CmsKit.Migrations
b.ToTable("CmsUserReactions");
});
modelBuilder.Entity("Volo.CmsKit.Users.CmsUser", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnName("ConcurrencyStamp")
.HasColumnType("nvarchar(40)")
.HasMaxLength(40);
b.Property<string>("Email")
.IsRequired()
.HasColumnName("Email")
.HasColumnType("nvarchar(256)")
.HasMaxLength(256);
b.Property<bool>("EmailConfirmed")
.ValueGeneratedOnAdd()
.HasColumnName("EmailConfirmed")
.HasColumnType("bit")
.HasDefaultValue(false);
b.Property<string>("ExtraProperties")
.HasColumnName("ExtraProperties")
.HasColumnType("nvarchar(max)");
b.Property<string>("Name")
.HasColumnName("Name")
.HasColumnType("nvarchar(64)")
.HasMaxLength(64);
b.Property<string>("PhoneNumber")
.HasColumnName("PhoneNumber")
.HasColumnType("nvarchar(16)")
.HasMaxLength(16);
b.Property<bool>("PhoneNumberConfirmed")
.ValueGeneratedOnAdd()
.HasColumnName("PhoneNumberConfirmed")
.HasColumnType("bit")
.HasDefaultValue(false);
b.Property<string>("Surname")
.HasColumnName("Surname")
.HasColumnType("nvarchar(64)")
.HasMaxLength(64);
b.Property<Guid?>("TenantId")
.HasColumnName("TenantId")
.HasColumnType("uniqueidentifier");
b.Property<string>("UserName")
.IsRequired()
.HasColumnName("UserName")
.HasColumnType("nvarchar(256)")
.HasMaxLength(256);
b.HasKey("Id");
b.ToTable("CmsUsers");
});
modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogAction", b =>
{
b.HasOne("Volo.Abp.AuditLogging.AuditLog", null)

83
modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20200624023340_Initial.cs → modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20200729125121_Initial.cs

@ -195,6 +195,60 @@ namespace Volo.CmsKit.Migrations
table.PrimaryKey("PK_AbpUsers", x => x.Id);
});
migrationBuilder.CreateTable(
name: "CmsComments",
columns: table => new
{
Id = table.Column<Guid>(nullable: false),
EntityType = table.Column<string>(maxLength: 64, nullable: false),
EntityId = table.Column<string>(maxLength: 64, nullable: false),
Text = table.Column<string>(maxLength: 512, nullable: false),
RepliedCommentId = table.Column<Guid>(nullable: true),
CreatorId = table.Column<Guid>(nullable: false),
CreationTime = table.Column<DateTime>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_CmsComments", x => x.Id);
});
migrationBuilder.CreateTable(
name: "CmsUserReactions",
columns: table => new
{
Id = table.Column<Guid>(nullable: false),
EntityType = table.Column<string>(maxLength: 64, nullable: false),
EntityId = table.Column<string>(maxLength: 64, nullable: false),
ReactionName = table.Column<string>(maxLength: 32, nullable: false),
CreatorId = table.Column<Guid>(nullable: false),
CreationTime = table.Column<DateTime>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_CmsUserReactions", x => x.Id);
});
migrationBuilder.CreateTable(
name: "CmsUsers",
columns: table => new
{
Id = table.Column<Guid>(nullable: false),
ExtraProperties = table.Column<string>(nullable: true),
ConcurrencyStamp = table.Column<string>(maxLength: 40, nullable: true),
TenantId = table.Column<Guid>(nullable: true),
UserName = table.Column<string>(maxLength: 256, nullable: false),
Email = table.Column<string>(maxLength: 256, nullable: false),
Name = table.Column<string>(maxLength: 64, nullable: true),
Surname = table.Column<string>(maxLength: 64, nullable: true),
EmailConfirmed = table.Column<bool>(nullable: false, defaultValue: false),
PhoneNumber = table.Column<string>(maxLength: 16, nullable: true),
PhoneNumberConfirmed = table.Column<bool>(nullable: false, defaultValue: false)
},
constraints: table =>
{
table.PrimaryKey("PK_CmsUsers", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AbpAuditLogActions",
columns: table => new
@ -564,6 +618,26 @@ namespace Volo.CmsKit.Migrations
name: "IX_AbpUsers_UserName",
table: "AbpUsers",
column: "UserName");
migrationBuilder.CreateIndex(
name: "IX_CmsComments_RepliedCommentId",
table: "CmsComments",
column: "RepliedCommentId");
migrationBuilder.CreateIndex(
name: "IX_CmsComments_EntityType_EntityId",
table: "CmsComments",
columns: new[] { "EntityType", "EntityId" });
migrationBuilder.CreateIndex(
name: "IX_CmsUserReactions_EntityType_EntityId",
table: "CmsUserReactions",
columns: new[] { "EntityType", "EntityId" });
migrationBuilder.CreateIndex(
name: "IX_CmsUserReactions_CreatorId_EntityType_EntityId_ReactionName",
table: "CmsUserReactions",
columns: new[] { "CreatorId", "EntityType", "EntityId", "ReactionName" });
}
protected override void Down(MigrationBuilder migrationBuilder)
@ -607,6 +681,15 @@ namespace Volo.CmsKit.Migrations
migrationBuilder.DropTable(
name: "AbpUserTokens");
migrationBuilder.DropTable(
name: "CmsComments");
migrationBuilder.DropTable(
name: "CmsUserReactions");
migrationBuilder.DropTable(
name: "CmsUsers");
migrationBuilder.DropTable(
name: "AbpEntityChanges");

105
modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/UnifiedDbContextModelSnapshot.cs

@ -912,6 +912,47 @@ namespace Volo.CmsKit.Migrations
b.ToTable("AbpTenantConnectionStrings");
});
modelBuilder.Entity("Volo.CmsKit.Comments.Comment", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<DateTime>("CreationTime")
.HasColumnName("CreationTime")
.HasColumnType("datetime2");
b.Property<Guid>("CreatorId")
.HasColumnName("CreatorId")
.HasColumnType("uniqueidentifier");
b.Property<string>("EntityId")
.IsRequired()
.HasColumnType("nvarchar(64)")
.HasMaxLength(64);
b.Property<string>("EntityType")
.IsRequired()
.HasColumnType("nvarchar(64)")
.HasMaxLength(64);
b.Property<Guid?>("RepliedCommentId")
.HasColumnType("uniqueidentifier");
b.Property<string>("Text")
.IsRequired()
.HasColumnType("nvarchar(512)")
.HasMaxLength(512);
b.HasKey("Id");
b.HasIndex("RepliedCommentId");
b.HasIndex("EntityType", "EntityId");
b.ToTable("CmsComments");
});
modelBuilder.Entity("Volo.CmsKit.Reactions.UserReaction", b =>
{
b.Property<Guid>("Id")
@ -950,6 +991,70 @@ namespace Volo.CmsKit.Migrations
b.ToTable("CmsUserReactions");
});
modelBuilder.Entity("Volo.CmsKit.Users.CmsUser", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnName("ConcurrencyStamp")
.HasColumnType("nvarchar(40)")
.HasMaxLength(40);
b.Property<string>("Email")
.IsRequired()
.HasColumnName("Email")
.HasColumnType("nvarchar(256)")
.HasMaxLength(256);
b.Property<bool>("EmailConfirmed")
.ValueGeneratedOnAdd()
.HasColumnName("EmailConfirmed")
.HasColumnType("bit")
.HasDefaultValue(false);
b.Property<string>("ExtraProperties")
.HasColumnName("ExtraProperties")
.HasColumnType("nvarchar(max)");
b.Property<string>("Name")
.HasColumnName("Name")
.HasColumnType("nvarchar(64)")
.HasMaxLength(64);
b.Property<string>("PhoneNumber")
.HasColumnName("PhoneNumber")
.HasColumnType("nvarchar(16)")
.HasMaxLength(16);
b.Property<bool>("PhoneNumberConfirmed")
.ValueGeneratedOnAdd()
.HasColumnName("PhoneNumberConfirmed")
.HasColumnType("bit")
.HasDefaultValue(false);
b.Property<string>("Surname")
.HasColumnName("Surname")
.HasColumnType("nvarchar(64)")
.HasMaxLength(64);
b.Property<Guid?>("TenantId")
.HasColumnName("TenantId")
.HasColumnType("uniqueidentifier");
b.Property<string>("UserName")
.IsRequired()
.HasColumnName("UserName")
.HasColumnType("nvarchar(256)")
.HasMaxLength(256);
b.HasKey("Id");
b.ToTable("CmsUsers");
});
modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogAction", b =>
{
b.HasOne("Volo.Abp.AuditLogging.AuditLog", null)

5
modules/cms-kit/host/Volo.CmsKit.Web.Unified/Pages/Index.cshtml

@ -2,13 +2,12 @@
@using Localization.Resources.AbpUi
@using Microsoft.Extensions.Localization
@using Volo.CmsKit.Pages
@using Volo.CmsKit.Web.Pages.CmsKit.Shared.Components.Commenting
@using Volo.CmsKit.Web.Pages.CmsKit.Shared.Components.ReactionSelection
@model IndexModel
@inject IStringLocalizer<AbpUiResource> Localizer
<h1>CMS Kit DEMO</h1>
<h2>Reactions</h2>
<abp-card class="mb-3">
<abp-card-body>
<abp-blockquote>
@ -23,6 +22,7 @@
<abp-card-footer>
@await Component.InvokeAsync(typeof(ReactionSelectionViewComponent), new { entityType = "quote", entityId = "1" })
</abp-card-footer>
@await Component.InvokeAsync(typeof(CommentingViewComponent), new { entityType = "quote", entityId = "1" })
</abp-card>
<abp-card class="mb-3">
@ -39,4 +39,5 @@
<abp-card-footer>
@await Component.InvokeAsync(typeof(ReactionSelectionViewComponent), new { entityType = "quote", entityId = "2" })
</abp-card-footer>
@await Component.InvokeAsync(typeof(CommentingViewComponent), new { entityType = "quote", entityId = "2" })
</abp-card>

36
modules/cms-kit/host/Volo.CmsKit.Web.Unified/Pages/Public.cshtml

@ -0,0 +1,36 @@
@page
@using Localization.Resources.AbpUi
@using Microsoft.Extensions.Localization
@using Volo.CmsKit.Pages
@using Volo.CmsKit.Web.Pages.CmsKit.Shared.Components.Commenting
@model PublicModel
@inject IStringLocalizer<AbpUiResource> Localizer
<h1>CMS Kit DEMO - Public</h1>
<abp-card class="mb-3">
<abp-card-body>
<abp-blockquote>
<p>
Any fool can write code that a computer can understand. Good programmers write code that humans can understand.
</p>
<footer>
Martin Fowler
</footer>
</abp-blockquote>
</abp-card-body>
@await Component.InvokeAsync(typeof(CommentingViewComponent), new { entityType = "publicQuote", entityId = "1", loginUrl="Account/Login?ReturnUrl=%2Fpublic" })
</abp-card>
<abp-card class="mb-3">
<abp-card-body>
<abp-blockquote>
<p>
Writing code is very simple, but writing simple code is the hardest thing there is!
</p>
<footer>
Halil ibrahim Kalkan <small>(inspired from Johan Cruyff)</small>
</footer>
</abp-blockquote>
</abp-card-body>
@await Component.InvokeAsync(typeof(CommentingViewComponent), new { entityType = "publicQuote", entityId = "2", loginUrl="Account/Login?ReturnUrl=%2Fpublic" })
</abp-card>

11
modules/cms-kit/host/Volo.CmsKit.Web.Unified/Pages/Public.cshtml.cs

@ -0,0 +1,11 @@
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace Volo.CmsKit.Pages
{
public class PublicModel : PageModel
{
public void OnGet()
{
}
}
}

10
modules/cms-kit/host/Volo.CmsKit.Web.Unified/Volo.CmsKit.Web.Unified.csproj

@ -47,6 +47,16 @@
<Content Remove="Logs\**" />
<EmbeddedResource Remove="Logs\**" />
<None Remove="Logs\**" />
<Content Update="Pages\Public.cshtml">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Compile Update="Pages\Public.cshtml.cs">
<DependentUpon>public.cshtml</DependentUpon>
</Compile>
<Compile Update="Pages\Public.cshtml.cs">
<DependentUpon>Public.cshtml</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>

11
modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Comments/CommentConsts.cs

@ -0,0 +1,11 @@
namespace Volo.CmsKit.Comments
{
public static class CommentConsts
{
public static int EntityTypeLength { get; set; } = 64;
public static int EntityIdLength { get; set; } = 64;
public static int MaxTextLength { get; set; } = 512;
}
}

14
modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/en.json

@ -1,6 +1,18 @@
{
"culture": "en",
"texts": {
"PickYourReaction": "Pick your reaction"
"PickYourReaction": "Pick your reaction",
"YourComment": "Your comment",
"YourReply": "Your reply",
"Comments": "Comments",
"Send": "Send",
"Delete": "Delete",
"Reply": "Reply",
"Update": "Update",
"Edit": "Edit",
"LoginToAddComment": "Login to add comment",
"LoginToReply": "Login to reply",
"MessageDeletionConfirmationMessage": "This comment will be deleted completely.",
"CommentAuthorizationExceptionMessage": "Those comments are not allowed for public display."
}
}

1
modules/cms-kit/src/Volo.CmsKit.Domain/Volo.CmsKit.Domain.csproj

@ -11,6 +11,7 @@
<ItemGroup>
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.Ddd.Domain\Volo.Abp.Ddd.Domain.csproj" />
<ProjectReference Include="..\Volo.CmsKit.Domain.Shared\Volo.CmsKit.Domain.Shared.csproj" />
<ProjectReference Include="..\..\..\..\modules\users\src\Volo.Abp.Users.Domain\Volo.Abp.Users.Domain.csproj" />
</ItemGroup>
</Project>

2
modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/CmsKitDomainModule.cs

@ -1,11 +1,13 @@
using Volo.Abp.Domain;
using Volo.Abp.Modularity;
using Volo.Abp.Users;
using Volo.CmsKit.Reactions;
namespace Volo.CmsKit
{
[DependsOn(
typeof(CmsKitDomainSharedModule),
typeof(AbpUsersDomainModule),
typeof(AbpDddDomainModule)
)]
public class CmsKitDomainModule : AbpModule

7
modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/CmsKitOptions.cs

@ -1,4 +1,5 @@
using JetBrains.Annotations;
using System.Collections.Generic;
using JetBrains.Annotations;
using Volo.CmsKit.Reactions;
namespace Volo.CmsKit
@ -8,9 +9,13 @@ namespace Volo.CmsKit
[NotNull]
public ReactionDefinitionDictionary Reactions { get; }
[NotNull]
public List<string> PublicCommentEntities { get; }
public CmsKitOptions()
{
Reactions = new ReactionDefinitionDictionary();
PublicCommentEntities = new List<string>();
}
}
}

50
modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Comments/Comment.cs

@ -0,0 +1,50 @@
using System;
using JetBrains.Annotations;
using Volo.Abp;
using Volo.Abp.Auditing;
using Volo.Abp.Domain.Entities;
namespace Volo.CmsKit.Comments
{
public class Comment: Entity<Guid>, IAggregateRoot<Guid>, IHasCreationTime, IMustHaveCreator
{
public virtual string EntityType { get; protected set; }
public virtual string EntityId { get; protected set; }
public virtual string Text { get; protected set; }
public virtual Guid? RepliedCommentId { get; protected set; }
public virtual Guid CreatorId { get; set; }
public virtual DateTime CreationTime { get; set; }
protected Comment()
{
}
public Comment(
Guid id,
[NotNull] string entityType,
[NotNull] string entityId,
[NotNull] string text,
Guid? repliedCommentId,
Guid creatorId)
: base(id)
{
EntityType = Check.NotNullOrWhiteSpace(entityType, nameof(entityType), CommentConsts.EntityTypeLength);
EntityId = Check.NotNullOrWhiteSpace(entityId, nameof(entityId), CommentConsts.EntityIdLength);
RepliedCommentId = repliedCommentId;
CreatorId = creatorId;
SetText(text);
}
public virtual void SetText(string text)
{
Text = Check.NotNullOrWhiteSpace(text, nameof(text), CommentConsts.MaxTextLength);
}
}
}

11
modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Comments/CommentWithAuthor.cs

@ -0,0 +1,11 @@
using Volo.CmsKit.Users;
namespace Volo.CmsKit.Comments
{
public class CommentWithAuthor
{
public Comment Comment { get; set; }
public CmsUser Author { get; set; }
}
}

15
modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Comments/ICommentRepository.cs

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Volo.Abp.Domain.Repositories;
namespace Volo.CmsKit.Comments
{
public interface ICommentRepository : IBasicRepository<Comment, Guid>
{
Task<List<CommentWithAuthor>> GetListWithAuthorsAsync(
[NotNull] string entityType,
[NotNull] string entityId);
}
}

82
modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Users/CmsUser.cs

@ -0,0 +1,82 @@
using System;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Users;
namespace Volo.CmsKit.Users
{
public class CmsUser : AggregateRoot<Guid>, IUser, IUpdateUserData
{
public virtual Guid? TenantId { get; protected set; }
public virtual string UserName { get; protected set; }
public virtual string Email { get; protected set; }
public virtual string Name { get; set; }
public virtual string Surname { get; set; }
public virtual bool EmailConfirmed { get; protected set; }
public virtual string PhoneNumber { get; protected set; }
public virtual bool PhoneNumberConfirmed { get; protected set; }
protected CmsUser()
{
}
public CmsUser(IUserData user)
: base(user.Id)
{
TenantId = user.TenantId;
UpdateInternal(user);
}
public virtual bool Update(IUserData user)
{
if (Id != user.Id)
{
throw new ArgumentException($"Given User's Id '{user.Id}' does not match to this User's Id '{Id}'");
}
if (TenantId != user.TenantId)
{
throw new ArgumentException($"Given User's TenantId '{user.TenantId}' does not match to this User's TenantId '{TenantId}'");
}
if (Equals(user))
{
return false;
}
UpdateInternal(user);
return true;
}
protected virtual bool Equals(IUserData user)
{
return Id == user.Id &&
TenantId == user.TenantId &&
UserName == user.UserName &&
Name == user.Name &&
Surname == user.Surname &&
Email == user.Email &&
EmailConfirmed == user.EmailConfirmed &&
PhoneNumber == user.PhoneNumber &&
PhoneNumberConfirmed == user.PhoneNumberConfirmed;
}
protected virtual void UpdateInternal(IUserData user)
{
Email = user.Email;
Name = user.Name;
Surname = user.Surname;
EmailConfirmed = user.EmailConfirmed;
PhoneNumber = user.PhoneNumber;
PhoneNumberConfirmed = user.PhoneNumberConfirmed;
UserName = user.UserName;
}
}
}

24
modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Users/CmsUserLookupService.cs

@ -0,0 +1,24 @@
using Volo.Abp.Uow;
using Volo.Abp.Users;
namespace Volo.CmsKit.Users
{
public class CmsUserLookupService: UserLookupService<CmsUser, ICmsUserRepository>, ICmsUserLookupService
{
public CmsUserLookupService(
ICmsUserRepository userRepository,
IUnitOfWorkManager unitOfWorkManager)
: base(
userRepository,
unitOfWorkManager)
{
}
protected override CmsUser CreateUser(IUserData externalUser)
{
return new CmsUser(externalUser);
}
}
}

43
modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Users/CmsUserSynchronizer.cs

@ -0,0 +1,43 @@
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities.Events.Distributed;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.Users;
namespace Volo.CmsKit.Users
{
public class CmsUserSynchronizer :
IDistributedEventHandler<EntityUpdatedEto<UserEto>>,
ITransientDependency
{
protected ICmsUserRepository UserRepository { get; }
protected ICmsUserLookupService UserLookupService { get; }
public CmsUserSynchronizer(
ICmsUserRepository userRepository,
ICmsUserLookupService userLookupService)
{
UserRepository = userRepository;
UserLookupService = userLookupService;
}
public virtual async Task HandleEventAsync(EntityUpdatedEto<UserEto> eventData)
{
var user = await UserRepository.FindAsync(eventData.Entity.Id);
if (user == null)
{
user = await UserLookupService.FindByIdAsync(eventData.Entity.Id);
if (user == null)
{
return;
}
}
if (user.Update(eventData.Entity))
{
await UserRepository.UpdateAsync(user);
}
}
}
}

9
modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Users/ICmsUserLookupService.cs

@ -0,0 +1,9 @@
using Volo.Abp.Users;
namespace Volo.CmsKit.Users
{
public interface ICmsUserLookupService : IUserLookupService<CmsUser>
{
}
}

9
modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Users/ICmsUserRepository.cs

@ -0,0 +1,9 @@
using Volo.Abp.Users;
namespace Volo.CmsKit.Users
{
public interface ICmsUserRepository: IUserRepository<CmsUser>
{
}
}

3
modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo.CmsKit.EntityFrameworkCore.csproj

@ -10,7 +10,8 @@
<ItemGroup>
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.EntityFrameworkCore\Volo.Abp.EntityFrameworkCore.csproj" />
<ProjectReference Include="..\Volo.CmsKit.Domain\Volo.CmsKit.Domain.csproj" />
<ProjectReference Include="..\Volo.CmsKit.Domain\Volo.CmsKit.Domain.csproj" />
<ProjectReference Include="..\..\..\..\modules\users\src\Volo.Abp.Users.EntityFrameworkCore\Volo.Abp.Users.EntityFrameworkCore.csproj" />
</ItemGroup>
</Project>

57
modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/Comments/EfCoreCommentRepository.cs

@ -0,0 +1,57 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Volo.Abp;
using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
using Volo.CmsKit.EntityFrameworkCore;
namespace Volo.CmsKit.Comments
{
public class EfCoreCommentRepository : EfCoreRepository<ICmsKitDbContext, Comment, Guid>,
ICommentRepository
{
public EfCoreCommentRepository(IDbContextProvider<ICmsKitDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
public async Task<List<CommentWithAuthor>> GetListWithAuthorsAsync(
string entityType,
string entityId)
{
Check.NotNullOrWhiteSpace(entityType, nameof(entityType));
Check.NotNullOrWhiteSpace(entityId, nameof(entityId));
var query = from comment in DbSet
join user in DbContext.CmsUsers on comment.CreatorId equals user.Id
where entityType == comment.EntityType && entityId == comment.EntityId
orderby comment.CreationTime
select new CommentWithAuthor
{
Comment = comment,
Author = user
};
return await query.ToListAsync();
}
public override async Task DeleteAsync(Guid id, bool autoSave = false, CancellationToken cancellationToken = default)
{
var replies = await DbSet
.Where(x => x.RepliedCommentId == id)
.ToListAsync(GetCancellationToken(cancellationToken));
foreach (var reply in replies)
{
//TODO: Discuss if it is better to mark it as deleted and show in the ui as "This is deleted" instead of deleting it and replies completely
await base.DeleteAsync(reply.Id, autoSave, GetCancellationToken(cancellationToken));
}
await base.DeleteAsync(id, autoSave, GetCancellationToken(cancellationToken));
}
}
}

6
modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/CmsKitDbContext.cs

@ -1,7 +1,9 @@
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Data;
using Volo.Abp.EntityFrameworkCore;
using Volo.CmsKit.Comments;
using Volo.CmsKit.Reactions;
using Volo.CmsKit.Users;
namespace Volo.CmsKit.EntityFrameworkCore
{
@ -10,6 +12,10 @@ namespace Volo.CmsKit.EntityFrameworkCore
{
public DbSet<UserReaction> UserReactions { get; set; }
public DbSet<Comment> Comments { get; set; }
public DbSet<CmsUser> CmsUsers { get; set; }
public CmsKitDbContext(DbContextOptions<CmsKitDbContext> options)
: base(options)
{

26
modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/CmsKitDbContextModelCreatingExtensions.cs

@ -2,7 +2,10 @@
using Microsoft.EntityFrameworkCore;
using Volo.Abp;
using Volo.Abp.EntityFrameworkCore.Modeling;
using Volo.CmsKit.Comments;
using Volo.CmsKit.Reactions;
using Volo.CmsKit.Users;
using Volo.Abp.Users.EntityFrameworkCore;
namespace Volo.CmsKit.EntityFrameworkCore
{
@ -21,6 +24,14 @@ namespace Volo.CmsKit.EntityFrameworkCore
optionsAction?.Invoke(options);
builder.Entity<CmsUser>(b =>
{
b.ToTable(options.TablePrefix + "Users", options.Schema);
b.ConfigureByConvention();
b.ConfigureAbpUser();
});
builder.Entity<UserReaction>(b =>
{
b.ToTable(options.TablePrefix + "UserReactions", options.Schema);
@ -34,6 +45,21 @@ namespace Volo.CmsKit.EntityFrameworkCore
b.HasIndex(x => new { x.EntityType, x.EntityId });
b.HasIndex(x => new { x.CreatorId, x.EntityType, x.EntityId, x.ReactionName });
});
builder.Entity<Comment>(b =>
{
b.ToTable(options.TablePrefix + "Comments", options.Schema);
b.ConfigureByConvention();
b.Property(x => x.EntityType).IsRequired().HasMaxLength(CommentConsts.EntityTypeLength);
b.Property(x => x.EntityId).IsRequired().HasMaxLength(CommentConsts.EntityIdLength);
b.Property(x => x.Text).IsRequired().HasMaxLength(CommentConsts.MaxTextLength);
b.Property(x => x.RepliedCommentId);
b.Property(x => x.CreationTime);
b.HasIndex(x => new { x.EntityType, x.EntityId });
b.HasIndex(x => new { x.RepliedCommentId });
});
}
}
}

13
modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/CmsKitEntityFrameworkCoreModule.cs

@ -1,11 +1,16 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.Modularity;
using Volo.Abp.Users.EntityFrameworkCore;
using Volo.CmsKit.Comments;
using Volo.CmsKit.Reactions;
using Volo.CmsKit.Users;
namespace Volo.CmsKit.EntityFrameworkCore
{
[DependsOn(
typeof(CmsKitDomainModule),
typeof(AbpUsersEntityFrameworkCoreModule),
typeof(AbpEntityFrameworkCoreModule)
)]
public class CmsKitEntityFrameworkCoreModule : AbpModule
@ -14,10 +19,10 @@ namespace Volo.CmsKit.EntityFrameworkCore
{
context.Services.AddAbpDbContext<CmsKitDbContext>(options =>
{
/* Add custom repositories here. Example:
* options.AddRepository<Question, EfCoreQuestionRepository>();
*/
options.AddRepository<UserReaction, EfCoreUserReactionRepository>();
options.AddRepository<Comment, EfCoreCommentRepository>();
options.AddRepository<CmsUser, EfCoreCmsUserRepository>();
});
}
}
}
}

6
modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/ICmsKitDbContext.cs

@ -1,7 +1,9 @@
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Data;
using Volo.Abp.EntityFrameworkCore;
using Volo.CmsKit.Comments;
using Volo.CmsKit.Reactions;
using Volo.CmsKit.Users;
namespace Volo.CmsKit.EntityFrameworkCore
{
@ -9,5 +11,9 @@ namespace Volo.CmsKit.EntityFrameworkCore
public interface ICmsKitDbContext : IEfCoreDbContext
{
DbSet<UserReaction> UserReactions { get; }
DbSet<Comment> Comments { get; }
DbSet<CmsUser> CmsUsers { get; set; }
}
}

14
modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/Users/EfCoreCmsUserRepository.cs

@ -0,0 +1,14 @@
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.Users.EntityFrameworkCore;
using Volo.CmsKit.EntityFrameworkCore;
namespace Volo.CmsKit.Users
{
public class EfCoreCmsUserRepository: EfCoreUserRepositoryBase<ICmsKitDbContext, CmsUser>, ICmsUserRepository
{
public EfCoreCmsUserRepository(IDbContextProvider<ICmsKitDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
}
}

17
modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Comments/CmsUserDto.cs

@ -0,0 +1,17 @@
using System;
namespace Volo.CmsKit.Comments
{
public class CmsUserDto
{
public Guid Id { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
}
}

23
modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Comments/CommentDto.cs

@ -0,0 +1,23 @@
using System;
namespace Volo.CmsKit.Comments
{
public class CommentDto
{
public Guid Id { get; set; }
public string EntityType { get; set; }
public string EntityId { get; set; }
public string Text { get; set; }
public Guid? RepliedCommentId { get; set; }
public Guid CreatorId { get; set; }
public DateTime CreationTime { get; set; }
public CmsUserDto Author { get; set; }
}
}

24
modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Comments/CommentWithDetailsDto.cs

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
namespace Volo.CmsKit.Comments
{
public class CommentWithDetailsDto
{
public Guid Id { get; set; }
public string EntityType { get; set; }
public string EntityId { get; set; }
public string Text { get; set; }
public Guid CreatorId { get; set; }
public DateTime CreationTime { get; set; }
public List<CommentDto> Replies { get; set; }
public CmsUserDto Author { get; set; }
}
}

23
modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Comments/CreateCommentInput.cs

@ -0,0 +1,23 @@
using System;
using System.ComponentModel.DataAnnotations;
using Volo.Abp.Validation;
namespace Volo.CmsKit.Comments
{
public class CreateCommentInput
{
[Required]
[DynamicStringLength(typeof(CommentConsts), nameof(CommentConsts.EntityTypeLength))]
public string EntityType { get; set; }
[Required]
[DynamicStringLength(typeof(CommentConsts), nameof(CommentConsts.EntityIdLength))]
public string EntityId { get; set; }
[Required]
[DynamicStringLength(typeof(CommentConsts), nameof(CommentConsts.MaxTextLength))]
public string Text { get; set; }
public Guid? RepliedCommentId { get; set; }
}
}

20
modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Comments/ICommentPublicAppService.cs

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
namespace Volo.CmsKit.Comments
{
public interface ICommentPublicAppService : IApplicationService
{
Task<ListResultDto<CommentWithDetailsDto>> GetAllForEntityAsync(string entityType, string entityId);
Task<CommentDto> CreateAsync(CreateCommentInput input);
Task<CommentDto> UpdateAsync(Guid id, UpdateCommentInput input);
Task DeleteAsync(Guid id);
}
}

13
modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Comments/UpdateCommentInput.cs

@ -0,0 +1,13 @@
using System;
using System.ComponentModel.DataAnnotations;
using Volo.Abp.Validation;
namespace Volo.CmsKit.Comments
{
public class UpdateCommentInput
{
[Required]
[DynamicStringLength(typeof(CommentConsts), nameof(CommentConsts.MaxTextLength))]
public string Text { get; set; }
}
}

142
modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Comments/CommentPublicAppService.cs

@ -0,0 +1,142 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Options;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Authorization;
using Volo.CmsKit.Users;
namespace Volo.CmsKit.Comments
{
public class CommentPublicAppService : ApplicationService, ICommentPublicAppService
{
protected CmsKitOptions CmsKitOptions { get; }
protected ICommentRepository CommentRepository { get; }
protected ICmsUserLookupService CmsUserLookupService { get; }
public CommentPublicAppService(
ICommentRepository commentRepository,
ICmsUserLookupService cmsUserLookupService,
IOptions<CmsKitOptions> cmsKitOptions)
{
CmsKitOptions = cmsKitOptions.Value;
CommentRepository = commentRepository;
CmsUserLookupService = cmsUserLookupService;
}
public async Task<ListResultDto<CommentWithDetailsDto>> GetAllForEntityAsync(string entityType, string entityId)
{
CheckAuthorizationAsync(entityType);
var commentsWithAuthor = await CommentRepository.GetListWithAuthorsAsync(entityType, entityId);
return new ListResultDto<CommentWithDetailsDto>(
ConvertCommentsToNestedStructure(commentsWithAuthor)
);
}
[Authorize]
public async Task<CommentDto> CreateAsync(CreateCommentInput input)
{
var user = await CmsUserLookupService.FindByIdAsync(CurrentUser.Id.Value);
if (user == null)
{
throw new BusinessException(message: "User Not found!");
}
var comment = await CommentRepository.InsertAsync(new Comment(
GuidGenerator.Create(),
input.EntityType,
input.EntityId,
input.Text,
input.RepliedCommentId,
CurrentUser.Id.Value
));
return ObjectMapper.Map<Comment, CommentDto>(comment);
}
[Authorize]
public async Task<CommentDto> UpdateAsync(Guid id, UpdateCommentInput input)
{
var comment = await CommentRepository.GetAsync(id);
if (comment.CreatorId != CurrentUser.Id)
{
throw new BusinessException();
}
comment.SetText(input.Text);
var updatedComment = await CommentRepository.UpdateAsync(comment);
return ObjectMapper.Map<Comment, CommentDto>(updatedComment);
}
[Authorize]
public async Task DeleteAsync(Guid id)
{
var comment = await CommentRepository.GetAsync(id);
if (comment.CreatorId != CurrentUser.Id)
{
throw new BusinessException();
}
await CommentRepository.DeleteAsync(id);
}
private List<CommentWithDetailsDto> ConvertCommentsToNestedStructure(List<CommentWithAuthor> comments)
{
var parentComments = comments
.Where(c=> c.Comment.RepliedCommentId == null)
.Select(c=> ObjectMapper.Map<Comment, CommentWithDetailsDto>(c.Comment))
.ToList();
foreach (var parentComment in parentComments)
{
parentComment.Author = GetAuthorAsDtoFromCommentList(comments, parentComment.Id);
parentComment.Replies = comments
.Where(c => c.Comment.RepliedCommentId == parentComment.Id)
.Select(c => ObjectMapper.Map<Comment, CommentDto>(c.Comment))
.ToList();
foreach (var reply in parentComment.Replies)
{
reply.Author = GetAuthorAsDtoFromCommentList(comments, reply.Id);
}
}
return parentComments;
}
private async Task CheckAuthorizationAsync(string entityType)
{
if (await IsPublicEntity(entityType))
{
return;
}
if (!CurrentUser.IsAuthenticated)
{
throw new AbpAuthorizationException(L["CommentAuthorizationExceptionMessage"]);
}
}
private async Task<bool> IsPublicEntity(string entityType)
{
return CmsKitOptions.PublicCommentEntities.Contains(entityType);
}
private CmsUserDto GetAuthorAsDtoFromCommentList(List<CommentWithAuthor> comments, Guid commentId)
{
return ObjectMapper.Map<CmsUser, CmsUserDto>(comments.Single(c => c.Comment.Id == commentId).Author);
}
}
}

9
modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/PublicApplicationAutoMapperProfile.cs

@ -1,4 +1,7 @@
using AutoMapper;
using Volo.Abp.AutoMapper;
using Volo.CmsKit.Comments;
using Volo.CmsKit.Users;
namespace Volo.CmsKit
{
@ -9,6 +12,10 @@ namespace Volo.CmsKit
/* You can configure your AutoMapper mapping configuration here.
* Alternatively, you can split your mapping configurations
* into multiple profile classes for a better organization. */
CreateMap<CmsUser, CmsUserDto>();
CreateMap<Comment, CommentDto>().Ignore(x=> x.Author);
CreateMap<Comment, CommentWithDetailsDto>().Ignore(x=> x.Replies).Ignore(x=> x.Author);
}
}
}
}

49
modules/cms-kit/src/Volo.CmsKit.Public.HttpApi/Volo/CmsKit/Comments/CommentPublicController.cs

@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
namespace Volo.CmsKit.Comments
{
[RemoteService(Name = CmsKitPublicRemoteServiceConsts.RemoteServiceName)]
[Area("cms-kit")]
[Route("api/cms-kit-public/comments")]
public class CommentPublicController : CmsKitPublicControllerBase, ICommentPublicAppService
{
public ICommentPublicAppService CommentPublicAppService { get; }
public CommentPublicController(ICommentPublicAppService commentPublicAppService)
{
CommentPublicAppService = commentPublicAppService;
}
[HttpGet]
[Route("{entityType}/{entityId}")]
public Task<ListResultDto<CommentWithDetailsDto>> GetAllForEntityAsync(string entityType, string entityId)
{
return CommentPublicAppService.GetAllForEntityAsync(entityType, entityId);
}
[HttpPost]
public Task<CommentDto> CreateAsync(CreateCommentInput input)
{
return CommentPublicAppService.CreateAsync(input);
}
[HttpPost]
[Route("{id}")]
public Task<CommentDto> UpdateAsync(Guid id, UpdateCommentInput input)
{
return CommentPublicAppService.UpdateAsync(id, input);
}
[HttpDelete]
[Route("update")]
public Task DeleteAsync(Guid id)
{
return CommentPublicAppService.DeleteAsync(id);
}
}
}

6
modules/cms-kit/src/Volo.CmsKit.Public.Web/Controllers/CmsKitPublicWidgetsController.cs

@ -1,5 +1,6 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.CmsKit.Web.Pages.CmsKit.Shared.Components.Commenting;
using Volo.CmsKit.Web.Pages.CmsKit.Shared.Components.ReactionSelection;
namespace Volo.CmsKit.Web.Controllers
@ -11,5 +12,10 @@ namespace Volo.CmsKit.Web.Controllers
{
return ViewComponent(typeof(ReactionSelectionViewComponent), new {entityType, entityId});
}
public async Task<IActionResult> Commenting(string entityType, string entityId)
{
return ViewComponent(typeof(CommentingViewComponent), new {entityType, entityId});
}
}
}

15
modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/CommentingScriptBundleContributor.cs

@ -0,0 +1,15 @@
using System.Collections.Generic;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.Bootstrap;
using Volo.Abp.Modularity;
namespace Volo.CmsKit.Web.Pages.CmsKit.Shared.Components.Commenting
{
public class CommentingScriptBundleContributor : BundleContributor
{
public override void ConfigureBundle(BundleConfigurationContext context)
{
context.Files.AddIfNotContains("/Pages/CmsKit/Shared/Components/Commenting/default.js");
}
}
}

13
modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/CommentingStyleBundleContributor.cs

@ -0,0 +1,13 @@
using System.Collections.Generic;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
namespace Volo.CmsKit.Web.Pages.CmsKit.Shared.Components.Commenting
{
public class CommentingStyleBundleContributor : BundleContributor
{
public override void ConfigureBundle(BundleConfigurationContext context)
{
context.Files.AddIfNotContains("/Pages/CmsKit/Shared/Components/Commenting/default.css");
}
}
}

53
modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/CommentingViewComponent.cs

@ -0,0 +1,53 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
using Volo.CmsKit.Comments;
namespace Volo.CmsKit.Web.Pages.CmsKit.Shared.Components.Commenting
{
[ViewComponent(Name = "CmsCommenting")]
[Widget(
ScriptTypes = new[] {typeof(CommentingScriptBundleContributor)},
StyleTypes = new[] {typeof(CommentingStyleBundleContributor)},
RefreshUrl = "/CmsKitPublicWidgets/Commenting"
)]
public class CommentingViewComponent : AbpViewComponent
{
public ICommentPublicAppService CommentPublicAppService { get; }
public CommentingViewComponent(
ICommentPublicAppService commentPublicAppService)
{
CommentPublicAppService = commentPublicAppService;
}
public virtual async Task<IViewComponentResult> InvokeAsync(string entityType, string entityId, string loginUrl = null)
{
var result = await CommentPublicAppService.GetAllForEntityAsync(entityType, entityId);
var viewModel = new CommentingViewModel
{
EntityId = entityId,
EntityType = entityType,
LoginUrl = loginUrl,
Comments = result.Items.ToList()
};
return View("~/Pages/CmsKit/Shared/Components/Commenting/Default.cshtml", viewModel);
}
public class CommentingViewModel
{
public string EntityType { get; set; }
public string EntityId { get; set; }
public string LoginUrl { get; set; }
public List<CommentWithDetailsDto> Comments { get; set; }
}
}
}

145
modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/Default.cshtml

@ -0,0 +1,145 @@
@using Microsoft.AspNetCore.Html
@using Microsoft.AspNetCore.Mvc.Localization
@using Volo.Abp.Users
@using Volo.CmsKit.Comments
@using Volo.CmsKit.Localization
@inject ICurrentUser CurrentUser
@inject IHtmlLocalizer<CmsKitResource> L
@model Volo.CmsKit.Web.Pages.CmsKit.Shared.Components.Commenting.CommentingViewComponent.CommentingViewModel
@{
Func<dynamic, IHtmlContent> GetCommentTitle(CmsUserDto author, DateTime creationTime) =>
@<span>
@((string.IsNullOrWhiteSpace(author.Name)
? author.UserName
: author.Name + " " + author.Surname).Trim())
<small>@creationTime.ToString()</small>
</span>;
}
@{
Func<dynamic, IHtmlContent> GetCommentArea(Guid? repliedCommentId, bool cancelButton = false) =>
@<div class="cms-comment-form-area" data-reply-id="@(repliedCommentId?.ToString() ?? "")"
style="@(string.IsNullOrEmpty(repliedCommentId?.ToString() ?? "") ? "" : "display:none")">
<form class="cms-comment-form">
<input hidden value="@(repliedCommentId?.ToString() ?? "")" name="repliedCommentId"/>
<div class="form-group">
<label> @(string.IsNullOrEmpty(repliedCommentId?.ToString() ?? "") ? L["YourComment"] : L["YourReply"]) </label>
<textarea class="form-control" name="commentText" rows="3"></textarea>
</div>
@if (cancelButton)
{
<abp-button type="button" button-type="Secondary" class="reply-cancel-button" data-reply-id="@(repliedCommentId?.ToString() ?? "")"> @L["Cancel"] </abp-button>
}
<abp-button type="submit" button-type="Primary"> @L["Send"] </abp-button>
</form>
</div>;
}
@{
Func<dynamic, IHtmlContent> GetCommentContentArea(Guid id, Guid authorId, bool isReply, string text) =>
@<div>
<div class="cms-comment-content-area" data-id="@id.ToString()">
<p>
@text
</p>
</div>
@if (!isReply)
{
@if (CurrentUser.IsAuthenticated)
{
<a href="#" class="comment-reply-link" data-reply-id="@id.ToString()">
<i class="fa fa-reply"></i> @L["Reply"]
</a>
}
else
{
<a href="@Model.LoginUrl"> @L["LoginToReply"]</a>
}
}
@if (authorId == CurrentUser.Id)
{
@if (!isReply)
{
<span> | </span>
}
<a href="#" class="comment-delete-link" data-id="@id.ToString()">
<i class="fa fa-trash"></i> @L["Delete"]
</a>
<span> | </span>
<a href="#" class="comment-edit-link" data-id="@id.ToString()">
<i class="fa fa-pencil"></i> @L["Edit"]
</a>
}
<div class="cms-comment-edit-area" data-id="@id.ToString()" style="display:none">
<form class="cms-comment-update-form">
<input hidden value="@id.ToString()" name="id"/>
<div class="form-group">
<textarea class="form-control" name="commentText" rows="3">@text</textarea>
</div>
<abp-button type="button" button-type="Secondary" class="comment-edit-cancel-button" data-id="@id.ToString()"> @L["Cancel"] </abp-button>
<abp-button type="submit" button-type="Primary"> @L["Update"] </abp-button>
</form>
</div>
</div>;
}
<div class="cms-comment-area" data-entity-type="@Model.EntityType" data-entity-id="@Model.EntityId">
<div class="pl-5 pt-3">
<h4>
<i class="fa fa-comment-o"></i>
@L["Comments"]
</h4>
@foreach (var comment in Model.Comments)
{
<div class="comment pt-2 mt-1 mb-1">
<div class="border-bottom">
<h5>
@GetCommentTitle(comment.Author, comment.CreationTime).Invoke(null)
</h5>
@GetCommentContentArea(comment.Id, comment.Author.Id, false, comment.Text).Invoke(null)
@if (CurrentUser.IsAuthenticated)
{
@GetCommentArea(comment.Id, true).Invoke(null)
}
@if (comment.Replies.Any())
{
<div class="pl-5">
@foreach (var reply in comment.Replies)
{
<div class="comment pt-3">
<div class="border-bottom">
<h6>
@GetCommentTitle(reply.Author, reply.CreationTime).Invoke(null)
</h6>
@GetCommentContentArea(reply.Id, reply.Author.Id, true, reply.Text).Invoke(null)
</div>
</div>
}
</div>
}
</div>
</div>
}
<div class="p-3 pl-5">
@if (CurrentUser.IsAuthenticated)
{
@GetCommentArea(null).Invoke(null)
}
else if (!string.IsNullOrWhiteSpace(Model.LoginUrl))
{
<div class="float-right mb-2">
<a href="@Model.LoginUrl" class="btn btn-primary"> @L["LoginToAddComment"]</a>
</div>
}
</div>
</div>
</div>

1
modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/default.css

@ -0,0 +1 @@


162
modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/default.js

@ -0,0 +1,162 @@
(function ($) {
var l = abp.localization.getResource('CmsKit');
$(document).ready(function () {
abp.widgets.CmsCommenting = function ($widget) {
var widgetManager = $widget.data('abp-widget-manager');
var $commentArea = $widget.find('.cms-comment-area');
function getFilters() {
return {
entityType: $commentArea.attr('data-entity-type'),
entityId: $commentArea.attr('data-entity-id')
};
}
function registerEditLinks($container) {
$container.find('.comment-edit-link').each(function () {
var $link = $(this);
$link.on('click', function (e) {
e.preventDefault();
var commentId = $link.data('id');
var $relatedCommentContentArea = $container.find('.cms-comment-content-area[data-id='+ commentId +']');
var $relatedCommentEditFormArea = $container.find('.cms-comment-edit-area[data-id='+ commentId +']');
$relatedCommentContentArea.hide();
$relatedCommentEditFormArea.show();
$link.removeAttr('href');
});
});
$container.find('.comment-edit-cancel-button').each(function () {
var $button = $(this);
$button.on('click', function (e) {
e.preventDefault();
var commentId = $button.data('id');
var $relatedCommentContentArea = $container.find('.cms-comment-content-area[data-id='+ commentId +']');
var $relatedCommentEditFormArea = $container.find('.cms-comment-edit-area[data-id='+ commentId +']');
var $link = $container.find('.comment-edit-link[data-id='+ commentId +']');
$relatedCommentContentArea.show();
$relatedCommentEditFormArea.hide();
$link.attr('href','#');
});
});
}
function registerReplyLinks($container) {
$container.find('.comment-reply-link').each(function () {
var $link = $(this);
$link.on('click', function (e) {
e.preventDefault();
var replyCommentId = $link.data('reply-id');
var $relatedCommentArea = $container.find('.cms-comment-form-area[data-reply-id='+ replyCommentId +']');
$relatedCommentArea.show();
$link.removeAttr('href');
});
});
$container.find('.reply-cancel-button').each(function () {
var $button = $(this);
$button.on('click', function (e) {
e.preventDefault();
var replyCommentId = $button.data('reply-id');
var $relatedCommentArea = $container.find('.cms-comment-form-area[data-reply-id='+ replyCommentId +']');
var $replyLink = $container.find('.comment-reply-link[data-reply-id='+ replyCommentId +']');
$relatedCommentArea.hide();
$replyLink.attr('href','#');
});
});
}
function registerDeleteLinks($container) {
$container.find('.comment-delete-link').each(function () {
var $link = $(this);
$link.on('click', '', function (e) {
e.preventDefault();
abp.message.confirm(l("MessageDeletionConfirmationMessage"), function (ok) {
if (ok){
volo.cmsKit.comments.commentPublic.delete($link.data('id')
).then(function () {
widgetManager.refresh($widget);
});
}
});
});
});
}
function registerUpdateOfNewComment($container) {
$container.find('.cms-comment-update-form').each(function () {
var $form = $(this);
$form.submit(function (e) {
e.preventDefault();
var formAsObject = $form.serializeFormToObject();
volo.cmsKit.comments.commentPublic.update(
formAsObject.id,
{
text: formAsObject.commentText
}
).then(function () {
widgetManager.refresh($widget);
});
});
});
}
function registerSubmissionOfNewComment($container) {
$container.find('.cms-comment-form').each(function () {
var $form = $(this);
$form.submit(function (e) {
e.preventDefault();
var formAsObject = $form.serializeFormToObject();
console.log(formAsObject);
volo.cmsKit.comments.commentPublic.create(
$.extend(getFilters(), {
repliedCommentId: formAsObject.repliedCommentId,
text: formAsObject.commentText
})
).then(function () {
widgetManager.refresh($widget);
});
});
});
}
function init() {
registerReplyLinks($widget);
registerEditLinks($widget);
registerDeleteLinks($widget);
registerUpdateOfNewComment($widget);
registerSubmissionOfNewComment($widget);
}
return {
init: init,
getFilters: getFilters
};
};
$('.abp-widget-wrapper[data-widget-name="CmsCommenting"]')
.each(function () {
var widgetManager = new abp.WidgetManager({
wrapper: $(this),
});
widgetManager.init();
});
});
})(jQuery);
Loading…
Cancel
Save