Browse Source

Merge branch 'dev' into stsrki/dev-page-progress

pull/6853/head
Mladen Macanovic 6 years ago
parent
commit
29f161df0f
  1. 104
      docs/en/Module-Entity-Extensions.md
  2. 4
      docs/en/Object-Extensions.md
  3. BIN
      docs/en/images/extension-navigation-property-form.png
  4. BIN
      docs/en/images/extension-navigation-property-table.png
  5. 164
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20201224140744_Initial.Designer.cs
  6. 85
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20201224140744_Initial.cs
  7. 1647
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20201225120048_Added_Page.Designer.cs
  8. 46
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20201225120048_Added_Page.cs
  9. 1643
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20201228083120_Updated_Tag_Removed_HexColor.Designer.cs
  10. 24
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20201228083120_Updated_Tag_Removed_HexColor.cs
  11. 229
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/UnifiedDbContextModelSnapshot.cs
  12. 48
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/Pages/Index.cshtml
  13. 1
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/Pages/_ViewImports.cshtml
  14. 3
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/package.json
  15. 37
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/wwwroot/libs/star-rating-svg/css/star-rating-svg.css
  16. 1
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/wwwroot/libs/star-rating-svg/js/jquery.star-rating-svg.min.js
  17. 20
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/yarn.lock
  18. 7
      modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/CmsKitCommonRemoteServiceConsts.cs
  19. 10
      modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Contents/ContentDto.cs
  20. 8
      modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Contents/GetContentInput.cs
  21. 9
      modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Contents/IContentAppService.cs
  22. 11
      modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Pages/CheckUrlInputDto.cs
  23. 16
      modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Pages/CreatePageInputDto.cs
  24. 20
      modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Pages/CreatePageWithContentInputDto.cs
  25. 25
      modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Pages/IPageAppService.cs
  26. 14
      modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Pages/PageDto.cs
  27. 17
      modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Pages/PageWithContentDto.cs
  28. 12
      modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Pages/UpdatePageContentInputDto.cs
  29. 16
      modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Pages/UpdatePageInputDto.cs
  30. 14
      modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Tags/GetRelatedTagsInput.cs
  31. 11
      modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Tags/ITagAppService.cs
  32. 12
      modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Tags/TagDto.cs
  33. 0
      modules/cms-kit/src/Volo.CmsKit.Common.Application/Volo/CmsKit/CmsKitAppServiceBase.cs
  34. 19
      modules/cms-kit/src/Volo.CmsKit.Common.Application/Volo/CmsKit/CmsKitCommonApplicationAutoMapperProfile.cs
  35. 7
      modules/cms-kit/src/Volo.CmsKit.Common.Application/Volo/CmsKit/CmsKitCommonApplicationModule.cs
  36. 28
      modules/cms-kit/src/Volo.CmsKit.Common.Application/Volo/CmsKit/Contents/ContentAppService.cs
  37. 114
      modules/cms-kit/src/Volo.CmsKit.Common.Application/Volo/CmsKit/Pages/PageAppService.cs
  38. 34
      modules/cms-kit/src/Volo.CmsKit.Common.Application/Volo/CmsKit/Tags/TagAppService.cs
  39. 27
      modules/cms-kit/src/Volo.CmsKit.Common.HttpApi/Volo/CmsKit/Controllers/Contents/ContentController.cs
  40. 77
      modules/cms-kit/src/Volo.CmsKit.Common.HttpApi/Volo/CmsKit/Controllers/Pages/PageController.cs
  41. 27
      modules/cms-kit/src/Volo.CmsKit.Common.HttpApi/Volo/CmsKit/Controllers/Tags/TagController.cs
  42. 9
      modules/cms-kit/src/Volo.CmsKit.Common.Web/Contents/IContentRenderer.cs
  43. 13
      modules/cms-kit/src/Volo.CmsKit.Common.Web/Contents/PlainTextContentRenderer.cs
  44. 15
      modules/cms-kit/src/Volo.CmsKit.Common.Web/Pages/CmsKit/Pages/Index.cshtml
  45. 26
      modules/cms-kit/src/Volo.CmsKit.Common.Web/Pages/CmsKit/Pages/Index.cshtml.cs
  46. 55
      modules/cms-kit/src/Volo.CmsKit.Common.Web/Pages/CmsKit/Shared/Components/Contents/ContentViewComponent.cs
  47. 7
      modules/cms-kit/src/Volo.CmsKit.Common.Web/Pages/CmsKit/Shared/Components/Contents/Default.cshtml
  48. 21
      modules/cms-kit/src/Volo.CmsKit.Common.Web/Pages/CmsKit/Shared/Components/Pages/Default.cshtml
  49. 40
      modules/cms-kit/src/Volo.CmsKit.Common.Web/Pages/CmsKit/Shared/Components/Pages/DefaultPageViewComponent.cs
  50. 13
      modules/cms-kit/src/Volo.CmsKit.Common.Web/Pages/CmsKit/Shared/Components/Tags/Default.cshtml
  51. 49
      modules/cms-kit/src/Volo.CmsKit.Common.Web/Pages/CmsKit/Shared/Components/Tags/TagViewComponent.cs
  52. 4
      modules/cms-kit/src/Volo.CmsKit.Common.Web/Pages/CmsKit/_ViewImports.cshtml
  53. 13
      modules/cms-kit/src/Volo.CmsKit.Common.Web/Pages/CommonPageModel.cs
  54. 15
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Contents/ContentConsts.cs
  55. 17
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/GlobalFeatures/ContentsFeature.cs
  56. 4
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/GlobalFeatures/GlobalCmsKitFeatures.cs
  57. 17
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/GlobalFeatures/TagsFeature.cs
  58. 11
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Pages/PageConsts.cs
  59. 11
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Tags/TagConsts.cs
  60. 38
      modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Contents/Content.cs
  61. 25
      modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Contents/IContentRepository.cs
  62. 15
      modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Pages/IPageRepository.cs
  63. 33
      modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Pages/Page.cs
  64. 32
      modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/EntityTag.cs
  65. 9
      modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/IEntityTagRepository.cs
  66. 30
      modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/ITagManager.cs
  67. 36
      modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/ITagRepository.cs
  68. 37
      modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/Tag.cs
  69. 73
      modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/TagManager.cs
  70. 55
      modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/Contents/EfCoreContentRepository.cs
  71. 55
      modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/CmsKitDbContextModelCreatingExtensions.cs
  72. 7
      modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/CmsKitEntityFrameworkCoreModule.cs
  73. 31
      modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/Pages/EfCorePageRepository.cs
  74. 13
      modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/Tags/EfCoreEntityTagRepository.cs
  75. 80
      modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/Tags/EfCoreTagRepository.cs
  76. 12
      modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/CmsKitMongoDbContext.cs
  77. 23
      modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/CmsKitMongoDbContextExtensions.cs
  78. 10
      modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/CmsKitMongoDbModule.cs
  79. 55
      modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Contents/MongoContentRepository.cs
  80. 12
      modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/ICmsKitMongoDbContext.cs
  81. 31
      modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Pages/MongoPageRepository.cs
  82. 13
      modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Tags/MongoEntityTagRepository.cs
  83. 83
      modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Tags/MongoTagRepository.cs
  84. 104
      modules/cms-kit/test/Volo.CmsKit.Domain.Tests/Tags/TagManager_Tests.cs
  85. 9
      modules/cms-kit/test/Volo.CmsKit.EntityFrameworkCore.Tests/EntityFrameworkCore/Contents/ContentRepository_Test.cs
  86. 9
      modules/cms-kit/test/Volo.CmsKit.EntityFrameworkCore.Tests/EntityFrameworkCore/Tags/TagRepository_Test.cs
  87. 11
      modules/cms-kit/test/Volo.CmsKit.MongoDB.Tests/MongoDB/Contents/ContentRepository_Test.cs
  88. 11
      modules/cms-kit/test/Volo.CmsKit.MongoDB.Tests/MongoDB/Tags/TagRepository_Test.cs
  89. 59
      modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitDataSeedContributor.cs
  90. 17
      modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitTestData.cs
  91. 63
      modules/cms-kit/test/Volo.CmsKit.TestBase/Contents/ContentRepository_Tests.cs
  92. 116
      modules/cms-kit/test/Volo.CmsKit.TestBase/Tags/TagRepository_Test.cs

104
docs/en/Module-Entity-Extensions.md

@ -1,5 +1,7 @@
# Module Entity Extensions
> This feature is not supported by the Blazor UI yet.
## Introduction
Module entity extension system is a **high level** extension system that allows you to **define new properties** for existing entities of the depended modules. It automatically **adds properties to the entity, database, HTTP API and the user interface** in a single point.
@ -325,6 +327,108 @@ One of the following names can be used as the localization key:
Localization system searches for the key with the given order. Localized text are used on the table and the create/edit forms.
### Navigation Properties / Foreign Keys
It is supported to add an extension property to an entity that is Id of another entity (foreign key).
#### Example: Associate a department to a user
````csharp
ObjectExtensionManager.Instance.Modules()
.ConfigureIdentity(identity =>
{
identity.ConfigureUser(user =>
{
user.AddOrUpdateProperty<Guid>(
"DepartmentId",
property =>
{
property.UI.Lookup.Url = "/api/departments";
property.UI.Lookup.DisplayPropertyName = "name";
}
);
});
});
````
`UI.Lookup.Url` option takes a URL to get list of departments to select on edit/create forms. This endpoint can be a typical controller, an [auto API controller](API/Auto-API-Controllers.md) or any type of endpoint that returns a proper JSON response.
An example implementation that returns a fixed list of departments (in real life, you get the list from a data source):
````csharp
[Route("api/departments")]
public class DepartmentController : AbpController
{
[HttpGet]
public async Task<ListResultDto<DepartmentDto>> GetAsync()
{
return new ListResultDto<DepartmentDto>(
new[]
{
new DepartmentDto
{
Id = Guid.Parse("6267f0df-870f-4173-be44-d74b4b56d2bd"),
Name = "Human Resources"
},
new DepartmentDto
{
Id = Guid.Parse("21c7b61f-330c-489e-8b8c-80e0a78a5cc5"),
Name = "Production"
}
}
);
}
}
````
This API returns such a JSON response:
````json
{
"items": [{
"id": "6267f0df-870f-4173-be44-d74b4b56d2bd",
"name": "Human Resources"
}, {
"id": "21c7b61f-330c-489e-8b8c-80e0a78a5cc5",
"name": "Production"
}]
}
````
ABP can now show an auto-complete select component to pick the department while creating or editing a user:
![extension-navigation-property-form](images/extension-navigation-property-form.png)
And shows the department name on the data table:
![extension-navigation-property-form](images/extension-navigation-property-table.png)
#### Lookup Options
`UI.Lookup` has the following options to customize how to read the response returned from the `Url`:
* `Url`: The endpoint to get the list of target entities. This is used on edit and create forms.
* `DisplayPropertyName`: The property in the JSON response to read the display name of the target entity to show on the UI. Default: `text`.
* `ValuePropertyName`: The property in the JSON response to read the Id of the target entity. Default: `id`.
* `FilterParamName`: ABP allows to search/filter the entity list on edit/create forms. This is especially useful if the target list contains a lot of items. In this case, you can return a limited list (top 100, for example) and allow user to search on the list. ABP sends filter text to the server (as a simple query string) with the name of this option. Default: `filter`.
* `ResultListPropertyName`: By default, returned JSON result should contain the entity list in an `items` array. You can change the name of this field. Default: `items`.
#### Lookup Properties: How Display Name Works?
You may wonder how ABP shows the department name on the data table above.
It is easy to understand how to fill the dropdown on edit and create forms: ABP makes an AJAX request to the given URL. It re-requests whenever user types to filter the items.
However, for the data table, multiple items are shown on the UI and performing a separate AJAX call to get display name of the department for each row would not be so efficient.
Instead, the display name of the foreign entity is also saved as an extra property of the entity (see *Extra Properties* section of the [Entities](Entities.md) document) in addition to Id of the foreign entity. If you check the database, you can see the `DepartmentId_Text` in the `ExtraProperties` field in the database table:
````json
{"DepartmentId":"21c7b61f-330c-489e-8b8c-80e0a78a5cc5","DepartmentId_Text":"Production"}
````
So, this is a type of *data duplication*. If your target entity's name changes in the database later, there is no automatic synchronization system. The system works as expected, but you see the old name on the data tables. If that's a problem for you, you should care yourself to update this information when display name of your entity changes.
## Database Mapping
For relational databases, all extension property values are stored in a single field in the table:

4
docs/en/Object-Extensions.md

@ -410,3 +410,7 @@ ObjectExtensionManager.Instance
````
See the [Entity Framework Core Integration document](Entity-Framework-Core.md) for more.
## See Also
* [Module Entity Extensions](Module-Entity-Extensions.md)

BIN
docs/en/images/extension-navigation-property-form.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
docs/en/images/extension-navigation-property-table.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

164
modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20201210071514_Initial.Designer.cs → modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20201224140744_Initial.Designer.cs

@ -11,7 +11,7 @@ using Volo.CmsKit.EntityFrameworkCore;
namespace Volo.CmsKit.Migrations
{
[DbContext(typeof(UnifiedDbContext))]
[Migration("20201210071514_Initial")]
[Migration("20201224140744_Initial")]
partial class Initial
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
@ -1098,6 +1098,78 @@ namespace Volo.CmsKit.Migrations
b.ToTable("CmsComments");
});
modelBuilder.Entity("Volo.CmsKit.Contents.Content", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasMaxLength(40)
.HasColumnType("nvarchar(40)")
.HasColumnName("ConcurrencyStamp");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("uniqueidentifier")
.HasColumnName("CreatorId");
b.Property<Guid?>("DeleterId")
.HasColumnType("uniqueidentifier")
.HasColumnName("DeleterId");
b.Property<DateTime?>("DeletionTime")
.HasColumnType("datetime2")
.HasColumnName("DeletionTime");
b.Property<string>("EntityId")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("EntityType")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("ExtraProperties")
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnType("bit")
.HasDefaultValue(false)
.HasColumnName("IsDeleted");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime2")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("uniqueidentifier")
.HasColumnName("LastModifierId");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.Property<string>("Value")
.IsRequired()
.HasMaxLength(2147483647)
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.HasIndex("TenantId", "EntityType", "EntityId");
b.ToTable("CmsContents");
});
modelBuilder.Entity("Volo.CmsKit.Ratings.Rating", b =>
{
b.Property<Guid>("Id")
@ -1177,6 +1249,96 @@ namespace Volo.CmsKit.Migrations
b.ToTable("CmsUserReactions");
});
modelBuilder.Entity("Volo.CmsKit.Tags.EntityTag", b =>
{
b.Property<string>("EntityId")
.HasColumnType("nvarchar(450)");
b.Property<Guid>("TagId")
.HasColumnType("uniqueidentifier");
b.Property<Guid?>("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<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasMaxLength(40)
.HasColumnType("nvarchar(40)")
.HasColumnName("ConcurrencyStamp");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("uniqueidentifier")
.HasColumnName("CreatorId");
b.Property<Guid?>("DeleterId")
.HasColumnType("uniqueidentifier")
.HasColumnName("DeleterId");
b.Property<DateTime?>("DeletionTime")
.HasColumnType("datetime2")
.HasColumnName("DeletionTime");
b.Property<string>("EntityType")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("ExtraProperties")
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<string>("HexColor")
.HasMaxLength(6)
.HasColumnType("nvarchar(6)");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnType("bit")
.HasDefaultValue(false)
.HasColumnName("IsDeleted");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime2")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("uniqueidentifier")
.HasColumnName("LastModifierId");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(32)
.HasColumnType("nvarchar(32)");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.HasKey("Id");
b.HasIndex("TenantId", "Name");
b.ToTable("CmsTags");
});
modelBuilder.Entity("Volo.CmsKit.Users.CmsUser", b =>
{
b.Property<Guid>("Id")

85
modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20201210071514_Initial.cs → modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20201224140744_Initial.cs

@ -269,6 +269,43 @@ namespace Volo.CmsKit.Migrations
table.PrimaryKey("PK_CmsComments", x => x.Id);
});
migrationBuilder.CreateTable(
name: "CmsContents",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
EntityType = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
EntityId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
Value = table.Column<string>(type: "nvarchar(max)", maxLength: 2147483647, nullable: false),
ExtraProperties = table.Column<string>(type: "nvarchar(max)", nullable: true),
ConcurrencyStamp = table.Column<string>(type: "nvarchar(40)", maxLength: 40, nullable: true),
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
LastModifierId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
IsDeleted = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
DeleterId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_CmsContents", x => x.Id);
});
migrationBuilder.CreateTable(
name: "CmsEntityTags",
columns: table => new
{
TagId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
EntityId = table.Column<string>(type: "nvarchar(450)", nullable: false),
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_CmsEntityTags", x => new { x.EntityId, x.TagId });
});
migrationBuilder.CreateTable(
name: "CmsRatings",
columns: table => new
@ -286,6 +323,30 @@ namespace Volo.CmsKit.Migrations
table.PrimaryKey("PK_CmsRatings", x => x.Id);
});
migrationBuilder.CreateTable(
name: "CmsTags",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
EntityType = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
Name = table.Column<string>(type: "nvarchar(32)", maxLength: 32, nullable: false),
HexColor = table.Column<string>(type: "nvarchar(6)", maxLength: 6, nullable: true),
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
ExtraProperties = table.Column<string>(type: "nvarchar(max)", nullable: true),
ConcurrencyStamp = table.Column<string>(type: "nvarchar(40)", maxLength: 40, nullable: true),
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
LastModifierId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
IsDeleted = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
DeleterId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_CmsTags", x => x.Id);
});
migrationBuilder.CreateTable(
name: "CmsUserReactions",
columns: table => new
@ -736,11 +797,26 @@ namespace Volo.CmsKit.Migrations
table: "CmsComments",
columns: new[] { "TenantId", "RepliedCommentId" });
migrationBuilder.CreateIndex(
name: "IX_CmsContents_TenantId_EntityType_EntityId",
table: "CmsContents",
columns: new[] { "TenantId", "EntityType", "EntityId" });
migrationBuilder.CreateIndex(
name: "IX_CmsEntityTags_TenantId_EntityId_TagId",
table: "CmsEntityTags",
columns: new[] { "TenantId", "EntityId", "TagId" });
migrationBuilder.CreateIndex(
name: "IX_CmsRatings_TenantId_EntityType_EntityId_CreatorId",
table: "CmsRatings",
columns: new[] { "TenantId", "EntityType", "EntityId", "CreatorId" });
migrationBuilder.CreateIndex(
name: "IX_CmsTags_TenantId_Name",
table: "CmsTags",
columns: new[] { "TenantId", "Name" });
migrationBuilder.CreateIndex(
name: "IX_CmsUserReactions_TenantId_CreatorId_EntityType_EntityId_ReactionName",
table: "CmsUserReactions",
@ -815,9 +891,18 @@ namespace Volo.CmsKit.Migrations
migrationBuilder.DropTable(
name: "CmsComments");
migrationBuilder.DropTable(
name: "CmsContents");
migrationBuilder.DropTable(
name: "CmsEntityTags");
migrationBuilder.DropTable(
name: "CmsRatings");
migrationBuilder.DropTable(
name: "CmsTags");
migrationBuilder.DropTable(
name: "CmsUserReactions");

1647
modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20201225120048_Added_Page.Designer.cs

File diff suppressed because it is too large

46
modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20201225120048_Added_Page.cs

@ -0,0 +1,46 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
namespace Volo.CmsKit.Migrations
{
public partial class Added_Page : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "CmsPages",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
Title = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
Url = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
Description = table.Column<string>(type: "nvarchar(515)", maxLength: 515, nullable: true),
ExtraProperties = table.Column<string>(type: "nvarchar(max)", nullable: true),
ConcurrencyStamp = table.Column<string>(type: "nvarchar(40)", maxLength: 40, nullable: true),
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
LastModifierId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
IsDeleted = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
DeleterId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_CmsPages", x => x.Id);
});
migrationBuilder.CreateIndex(
name: "IX_CmsPages_TenantId_Url",
table: "CmsPages",
columns: new[] { "TenantId", "Url" });
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "CmsPages");
}
}
}

1643
modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20201228083120_Updated_Tag_Removed_HexColor.Designer.cs

File diff suppressed because it is too large

24
modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20201228083120_Updated_Tag_Removed_HexColor.cs

@ -0,0 +1,24 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace Volo.CmsKit.Migrations
{
public partial class Updated_Tag_Removed_HexColor : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "HexColor",
table: "CmsTags");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "HexColor",
table: "CmsTags",
type: "nvarchar(6)",
maxLength: 6,
nullable: true);
}
}
}

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

@ -1096,6 +1096,149 @@ namespace Volo.CmsKit.Migrations
b.ToTable("CmsComments");
});
modelBuilder.Entity("Volo.CmsKit.Contents.Content", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasMaxLength(40)
.HasColumnType("nvarchar(40)")
.HasColumnName("ConcurrencyStamp");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("uniqueidentifier")
.HasColumnName("CreatorId");
b.Property<Guid?>("DeleterId")
.HasColumnType("uniqueidentifier")
.HasColumnName("DeleterId");
b.Property<DateTime?>("DeletionTime")
.HasColumnType("datetime2")
.HasColumnName("DeletionTime");
b.Property<string>("EntityId")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("EntityType")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("ExtraProperties")
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnType("bit")
.HasDefaultValue(false)
.HasColumnName("IsDeleted");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime2")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("uniqueidentifier")
.HasColumnName("LastModifierId");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.Property<string>("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<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasMaxLength(40)
.HasColumnType("nvarchar(40)")
.HasColumnName("ConcurrencyStamp");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("uniqueidentifier")
.HasColumnName("CreatorId");
b.Property<Guid?>("DeleterId")
.HasColumnType("uniqueidentifier")
.HasColumnName("DeleterId");
b.Property<DateTime?>("DeletionTime")
.HasColumnType("datetime2")
.HasColumnName("DeletionTime");
b.Property<string>("Description")
.HasMaxLength(515)
.HasColumnType("nvarchar(515)");
b.Property<string>("ExtraProperties")
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnType("bit")
.HasDefaultValue(false)
.HasColumnName("IsDeleted");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime2")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("uniqueidentifier")
.HasColumnName("LastModifierId");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.Property<string>("Title")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("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<Guid>("Id")
@ -1175,6 +1318,92 @@ namespace Volo.CmsKit.Migrations
b.ToTable("CmsUserReactions");
});
modelBuilder.Entity("Volo.CmsKit.Tags.EntityTag", b =>
{
b.Property<string>("EntityId")
.HasColumnType("nvarchar(450)");
b.Property<Guid>("TagId")
.HasColumnType("uniqueidentifier");
b.Property<Guid?>("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<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasMaxLength(40)
.HasColumnType("nvarchar(40)")
.HasColumnName("ConcurrencyStamp");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("uniqueidentifier")
.HasColumnName("CreatorId");
b.Property<Guid?>("DeleterId")
.HasColumnType("uniqueidentifier")
.HasColumnName("DeleterId");
b.Property<DateTime?>("DeletionTime")
.HasColumnType("datetime2")
.HasColumnName("DeletionTime");
b.Property<string>("EntityType")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("ExtraProperties")
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnType("bit")
.HasDefaultValue(false)
.HasColumnName("IsDeleted");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime2")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("uniqueidentifier")
.HasColumnName("LastModifierId");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(32)
.HasColumnType("nvarchar(32)");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.HasKey("Id");
b.HasIndex("TenantId", "Name");
b.ToTable("CmsTags");
});
modelBuilder.Entity("Volo.CmsKit.Users.CmsUser", b =>
{
b.Property<Guid>("Id")

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

@ -7,6 +7,8 @@
@using Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Commenting
@using Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Rating
@using Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.ReactionSelection
@using Volo.CmsKit.Web.Pages.CmsKit.Shared.Components.Contents
@using Volo.CmsKit.Web.Pages.CmsKit.Shared.Components.Tags
@model IndexModel
@inject IStringLocalizer<AbpUiResource> Localizer
<h1 class="text-center">CMS Kit DEMO</h1>
@ -14,10 +16,19 @@
<abp-card>
<abp-card-body class="p-5">
<abp-blockquote class="text-center">
<p class="h2 mb-4">
"Any fool can write code that a computer can understand. Good programmers write code that humans can understand."
</p>
<p class="m-0">- Martin Fowler</p>
@if (GlobalFeatureManager.Instance.IsEnabled<ContentsFeature>())
{
@await Component.InvokeAsync(typeof(ContentViewComponent), new {entityType = "quote", entityId = "1"})
}
@if (GlobalFeatureManager.Instance.IsEnabled<TagsFeature>())
{
@await Component.InvokeAsync(typeof(TagViewComponent), new
{
entityType = "quote",
entityId = "1"
})
}
</abp-blockquote>
</abp-card-body>
</abp-card>
@ -26,19 +37,19 @@
<abp-column size-md="_6">
@if (GlobalFeatureManager.Instance.IsEnabled<RatingsFeature>())
{
@await Component.InvokeAsync(typeof(RatingViewComponent), new { entityType = "quote", entityId = "1" })
@await Component.InvokeAsync(typeof(RatingViewComponent), new {entityType = "quote", entityId = "1"})
}
</abp-column>
<abp-column size-md="_6">
@if (GlobalFeatureManager.Instance.IsEnabled<ReactionsFeature>())
{
@await Component.InvokeAsync(typeof(ReactionSelectionViewComponent), new { entityType = "quote", entityId = "1" })
@await Component.InvokeAsync(typeof(ReactionSelectionViewComponent), new {entityType = "quote", entityId = "1"})
}
</abp-column>
<abp-column size-md="_12">
@if (GlobalFeatureManager.Instance.IsEnabled<CommentsFeature>())
{
@await Component.InvokeAsync(typeof(CommentingViewComponent), new { entityType = "quote", entityId = "1" })
@await Component.InvokeAsync(typeof(CommentingViewComponent), new {entityType = "quote", entityId = "1"})
}
</abp-column>
</abp-row>
@ -48,7 +59,7 @@
<p class="h2 mb-4">
"Writing code is very simple, but writing simple code is the hardest thing there is!"
</p>
<p class="m-0"> - Halil ibrahim Kalkan <small class="d-block text-muted">Inspired from Johan Cruyff</small></p>
<p class="m-0"> - Halil ibrahim Kalkan <small class="d-block text-muted">Inspired from Johan Cruyff</small></p>
</abp-blockquote>
</abp-card-body>
</abp-card>
@ -56,19 +67,32 @@
<abp-column size-md="_6">
@if (GlobalFeatureManager.Instance.IsEnabled<RatingsFeature>())
{
@await Component.InvokeAsync(typeof(RatingViewComponent), new { entityType = "quote", entityId = "2" })
@await Component.InvokeAsync(typeof(RatingViewComponent), new {entityType = "quote", entityId = "2"})
}
</abp-column>
<abp-column size-md="_6">
@if (GlobalFeatureManager.Instance.IsEnabled<ReactionsFeature>())
{
@await Component.InvokeAsync(typeof(ReactionSelectionViewComponent), new { entityType = "quote", entityId = "2" })
@await Component.InvokeAsync(typeof(ReactionSelectionViewComponent), new {entityType = "quote", entityId = "2"})
}
</abp-column>
<abp-column size-md="_12">
@if (GlobalFeatureManager.Instance.IsEnabled<CommentsFeature>())
{
@await Component.InvokeAsync(typeof(CommentingViewComponent), new { entityType = "quote", entityId = "2" })
@await Component.InvokeAsync(typeof(CommentingViewComponent), new {entityType = "quote", entityId = "2"})
}
</abp-column>
</abp-row>
<abp-row>
<abp-column size="_12">
@if (GlobalFeatureManager.Instance.IsEnabled<TagsFeature>())
{
@await Component.InvokeAsync(typeof(TagViewComponent), new
{
entityType = "IndexPage",
entityId = Guid.Empty.ToString()
})
}
</abp-column>
</abp-row>
</abp-row>

1
modules/cms-kit/host/Volo.CmsKit.Web.Unified/Pages/_ViewImports.cshtml

@ -3,3 +3,4 @@
@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bootstrap
@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bundling
@addTagHelper *, Volo.CmsKit.Public.Web
@addTagHelper *, Volo.CmsKit.Common.Web

3
modules/cms-kit/host/Volo.CmsKit.Web.Unified/package.json

@ -3,6 +3,7 @@
"name": "my-app",
"private": true,
"dependencies": {
"@abp/aspnetcore.mvc.ui.theme.basic": "^4.1.0-rc.2"
"@abp/aspnetcore.mvc.ui.theme.basic": "^4.1.0-rc.2",
"@abp/cms-kit": "4.1.0-rc.2"
}
}

37
modules/cms-kit/host/Volo.CmsKit.Web.Unified/wwwroot/libs/star-rating-svg/css/star-rating-svg.css

@ -0,0 +1,37 @@
.jq-stars {
display: inline-block;
}
.jq-rating-label {
font-size: 22px;
display: inline-block;
position: relative;
vertical-align: top;
font-family: helvetica, arial, verdana;
}
.jq-star {
width: 100px;
height: 100px;
display: inline-block;
cursor: pointer;
}
.jq-star-svg {
width: 100%;
height: 100% ;
}
.jq-star:hover .fs-star-svg polygon {
}
.jq-star-svg polygon {
stroke: #000;
stroke-linejoin: round;
}
/* un-used */
.jq-shadow {
-webkit-filter: drop-shadow( -2px -2px 2px #888 );
filter: drop-shadow( -2px -2px 2px #888 );
}

1
modules/cms-kit/host/Volo.CmsKit.Web.Unified/wwwroot/libs/star-rating-svg/js/jquery.star-rating-svg.min.js

@ -0,0 +1 @@
!function(a){"use strict";var b="starRating",c=function(){},d={totalStars:5,useFullStars:!1,emptyColor:"lightgray",hoverColor:"orange",activeColor:"gold",useGradient:!0,readOnly:!1,disableAfterRate:!0,starGradient:{start:"#FEF7CD",end:"#FF9511"},strokeWidth:0,strokeColor:"black",initialRating:0,starSize:40,callback:c,onHover:c,onLeave:c},e=function(c,e){var f;this.element=c,this.$el=a(c),this.settings=a.extend({},d,e),f=this.$el.data("rating")||this.settings.initialRating,this._state={rating:(Math.round(2*f)/2).toFixed(1)},this._uid=Math.floor(999*Math.random()),e.starGradient||this.settings.useGradient||(this.settings.starGradient.start=this.settings.starGradient.end=this.settings.activeColor),this._defaults=d,this._name=b,this.init()},f={init:function(){this.renderMarkup(),this.addListeners(),this.initRating()},addListeners:function(){this.settings.readOnly||(this.$stars.on("mouseover",this.hoverRating.bind(this)),this.$stars.on("mouseout",this.restoreState.bind(this)),this.$stars.on("click",this.handleRating.bind(this)))},hoverRating:function(a){var b=this.getIndex(a);this.paintStars(b,"hovered"),this.settings.onHover(b+1,this._state.rating,this.$el)},handleRating:function(a){var b=this.getIndex(a),c=b+1;this.applyRating(c,this.$el),this.executeCallback(c,this.$el),this.settings.disableAfterRate&&this.$stars.off()},applyRating:function(a){var b=a-1;this.paintStars(b,"active"),this._state.rating=b+1},restoreState:function(a){var b=this.getIndex(a),c=this._state.rating||-1;this.paintStars(c-1,"active"),this.settings.onLeave(b+1,this._state.rating,this.$el)},getIndex:function(b){var c=a(b.currentTarget),d=c.width(),e=a(b.target).attr("data-side");e=e?e:this.getOffsetByPixel(b,c,d),e=this.settings.useFullStars?"right":e;var f=c.index()-("left"===e?.5:0);return f=.5>f&&b.offsetX<d/4?-1:f},getOffsetByPixel:function(a,b,c){var d=a.pageX-b.offset().left;return c/2>=d&&!this.settings.useFullStars?"left":"right"},initRating:function(){this.paintStars(this._state.rating-1,"active")},paintStars:function(b,c){var d,e,f,g;a.each(this.$stars,function(h,i){d=a(i).find('polygon[data-side="left"]'),e=a(i).find('polygon[data-side="right"]'),f=g=b>=h?c:"empty",f=h-b===.5?c:f,d.attr("class","svg-"+f+"-"+this._uid),e.attr("class","svg-"+g+"-"+this._uid)}.bind(this))},renderMarkup:function(){for(var a='<div class="jq-star" style="width:'+this.settings.starSize+"px; height:"+this.settings.starSize+'px;"><svg version="1.0" class="jq-star-svg" shape-rendering="geometricPrecision" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="305px" height="305px" viewBox="60 -62 309 309" style="enable-background:new 64 -59 305 305; stroke-width:'+this.settings.strokeWidth+'px;" xml:space="preserve"><style type="text/css">.svg-empty-'+this._uid+"{fill:url(#"+this._uid+"_SVGID_1_);}.svg-hovered-"+this._uid+"{fill:url(#"+this._uid+"_SVGID_2_);}.svg-active-"+this._uid+"{fill:url(#"+this._uid+"_SVGID_3_);}</style>"+this.getLinearGradient(this._uid+"_SVGID_1_",this.settings.emptyColor,this.settings.emptyColor)+this.getLinearGradient(this._uid+"_SVGID_2_",this.settings.hoverColor,this.settings.hoverColor)+this.getLinearGradient(this._uid+"_SVGID_3_",this.settings.starGradient.start,this.settings.starGradient.end)+'<polygon data-side="left" class="svg-empty-'+this._uid+'" points="281.1,129.8 364,55.7 255.5,46.8 214,-59 172.5,46.8 64,55.4 146.8,129.7 121.1,241 213.9,181.1 213.9,181 306.5,241 " style="stroke: '+this.settings.strokeColor+'"/><polygon data-side="right" class="svg-empty-'+this._uid+'" points="364,55.7 255.5,46.8 214,-59 213.9,181 306.5,241 281.1,129.8 " style="stroke-dasharray: 230 232 210 0; stroke: '+this.settings.strokeColor+'"/></svg></div>',b="",c=0;c<this.settings.totalStars;c++)b+=a;this.$el.append(b),this.$stars=this.$el.find(".jq-star")},getLinearGradient:function(a,b,c){return'<linearGradient id="'+a+'" gradientUnits="userSpaceOnUse" x1="121.1501" y1="-70.35" x2="121.15" y2="125.0045"><stop offset="0" style="stop-color:'+b+'"/><stop offset="1" style="stop-color:'+c+'"/> </linearGradient>'},executeCallback:function(a,b){var c=this.settings.callback;c(a,b)}},g={unload:function(){var c="plugin_"+b,d=a(this),e=d.data(c).$stars;e.off(),d.removeData(c).remove()},setRating:function(c,d){var e="plugin_"+b,f=a(this),g=f.data(e);c>g.settings.totalStars||0>c||(d&&(c=Math.round(c)),g.applyRating(c))},getRating:function(){var c="plugin_"+b,d=a(this),e=d.data(c);return e._state.rating}};a.extend(e.prototype,f),a.fn[b]=function(c){if(!a.isPlainObject(c)){if(g.hasOwnProperty(c))return g[c].apply(this,Array.prototype.slice.call(arguments,1));a.error("Method "+c+" does not exist on "+b+".js")}return this.each(function(){a.data(this,"plugin_"+b)||a.data(this,"plugin_"+b,new e(this,c))})}}(jQuery,window,document);

20
modules/cms-kit/host/Volo.CmsKit.Web.Unified/yarn.lock

@ -59,6 +59,13 @@
bootstrap "^4.5.0"
bootstrap-v4-rtl "4.4.1-2"
"@abp/cms-kit@4.1.0-rc.2":
version "4.1.0-rc.2"
resolved "https://registry.yarnpkg.com/@abp/cms-kit/-/cms-kit-4.1.0-rc.2.tgz#d2790dd3812214ab0c8eba6e9ad2b8019889e056"
integrity sha512-bS632Qp3tN+LHVj505lUgwtu2JVOhSf3W6VAXlYG0pSy8n1ltonDZHNKO6iwvnskdPaAA4wgJPWgqjXigChx0Q==
dependencies:
"@abp/star-rating-svg" "~4.1.0-rc.2"
"@abp/core@~4.1.0-rc.2":
version "4.1.0-rc.2"
resolved "https://registry.yarnpkg.com/@abp/core/-/core-4.1.0-rc.2.tgz#d306106a53fa56109751be0099785c94f43be56f"
@ -154,6 +161,14 @@
"@abp/core" "~4.1.0-rc.2"
select2 "^4.0.13"
"@abp/star-rating-svg@~4.1.0-rc.2":
version "4.1.0-rc.2"
resolved "https://registry.yarnpkg.com/@abp/star-rating-svg/-/star-rating-svg-4.1.0-rc.2.tgz#558c681eb591847b3e8f114cbe610e4331f8a0a2"
integrity sha512-LNFCfytarGhqS+91lxYHQKMvlbHMDpjU0Knj8qyefNtQ+rbM2CEspWEtcducim+Qn4brqIHRudUvLI+eEC/0yQ==
dependencies:
"@abp/jquery" "~4.1.0-rc.2"
star-rating-svg "^3.5.0"
"@abp/sweetalert@~4.1.0-rc.2":
version "4.1.0-rc.2"
resolved "https://registry.yarnpkg.com/@abp/sweetalert/-/sweetalert-4.1.0-rc.2.tgz#cdcaf8e9a33cf218a687fd6a42cfe1c85d89c6b7"
@ -2321,6 +2336,11 @@ stack-trace@0.0.10:
resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0"
integrity sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=
star-rating-svg@^3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/star-rating-svg/-/star-rating-svg-3.5.0.tgz#72b147015a433da63153fb607af6d51c7c1ea364"
integrity sha1-crFHAVpDPaYxU/tgevbVHHweo2Q=
static-extend@^0.1.1:
version "0.1.2"
resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6"

7
modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/CmsKitCommonRemoteServiceConsts.cs

@ -0,0 +1,7 @@
namespace Volo.CmsKit
{
public class CmsKitCommonRemoteServiceConsts
{
public const string RemoteServiceName = "CmsKitCommon";
}
}

10
modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Contents/ContentDto.cs

@ -0,0 +1,10 @@
using System;
using Volo.Abp.Application.Dtos;
namespace Volo.CmsKit.Contents
{
public class ContentDto : EntityDto<Guid>
{
public string Value { get; set; }
}
}

8
modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Contents/GetContentInput.cs

@ -0,0 +1,8 @@
namespace Volo.CmsKit.Contents
{
public class GetContentInput
{
public string EntityType { get; set; }
public string EntityId { get; set; }
}
}

9
modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Contents/IContentAppService.cs

@ -0,0 +1,9 @@
using System.Threading.Tasks;
namespace Volo.CmsKit.Contents
{
public interface IContentAppService
{
Task<ContentDto> GetAsync(GetContentInput input);
}
}

11
modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Pages/CheckUrlInputDto.cs

@ -0,0 +1,11 @@
using System.ComponentModel.DataAnnotations;
using Volo.Abp.Validation;
namespace Volo.CmsKit.Pages
{
public class CheckUrlInputDto
{
[Required]
public string Url { get; set; }
}
}

16
modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Pages/CreatePageInputDto.cs

@ -0,0 +1,16 @@
using System.ComponentModel.DataAnnotations;
using Volo.Abp.Validation;
namespace Volo.CmsKit.Pages
{
public class CreatePageInputDto
{
[Required]
public string Title { get; set; }
[Required]
public string Url { get; set; }
public string Description { get; set; }
}
}

20
modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Pages/CreatePageWithContentInputDto.cs

@ -0,0 +1,20 @@
using System.ComponentModel.DataAnnotations;
using Volo.Abp.Validation;
using Volo.CmsKit.Contents;
namespace Volo.CmsKit.Pages
{
public class CreatePageWithContentInputDto
{
[Required]
public string Title { get; set; }
[Required]
public string Url { get; set; }
public string Description { get; set; }
[Required]
public string Content { get; set; }
}
}

25
modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Pages/IPageAppService.cs

@ -0,0 +1,25 @@
using System;
using System.Threading.Tasks;
using JetBrains.Annotations;
namespace Volo.CmsKit.Pages
{
public interface IPageAppService
{
Task<PageDto> GetAsync(Guid id);
Task<PageDto> GetByUrlAsync([NotNull] string url);
Task<PageDto> CreatePageAsync(CreatePageInputDto input);
Task<PageDto> CreatePageWithContentAsync(CreatePageWithContentInputDto input);
Task<PageDto> UpdatePageAsync(Guid id, UpdatePageInputDto input);
Task<bool> DoesUrlExistAsync(CheckUrlInputDto input);
Task UpdatePageContentAsync(Guid id, UpdatePageContentInputDto input);
Task DeleteAsync(Guid id);
}
}

14
modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Pages/PageDto.cs

@ -0,0 +1,14 @@
using System;
using Volo.Abp.Application.Dtos;
namespace Volo.CmsKit.Pages
{
public class PageDto : EntityDto<Guid>
{
public string Title { get; set; }
public string Url { get; set; }
public string Description { get; set; }
}
}

17
modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Pages/PageWithContentDto.cs

@ -0,0 +1,17 @@
using System;
using Volo.Abp.Application.Dtos;
using Volo.CmsKit.Contents;
namespace Volo.CmsKit.Pages
{
public class PageWithContentDto : EntityDto<Guid>
{
public string Title { get; set; }
public string Url { get; set; }
public string Description { get; set; }
public ContentDto Content { get; set; }
}
}

12
modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Pages/UpdatePageContentInputDto.cs

@ -0,0 +1,12 @@
using System.ComponentModel.DataAnnotations;
using Volo.Abp.Validation;
using Volo.CmsKit.Contents;
namespace Volo.CmsKit.Pages
{
public class UpdatePageContentInputDto
{
[Required]
public string Content { get; set; }
}
}

16
modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Pages/UpdatePageInputDto.cs

@ -0,0 +1,16 @@
using System.ComponentModel.DataAnnotations;
using Volo.Abp.Validation;
namespace Volo.CmsKit.Pages
{
public class UpdatePageInputDto
{
[Required]
public string Title { get; set; }
[Required]
public string Url { get; set; }
public string Description { get; set; }
}
}

14
modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Tags/GetRelatedTagsInput.cs

@ -0,0 +1,14 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace Volo.CmsKit.Tags
{
public class GetRelatedTagsInput
{
[Required]
public string EntityType { get; set; }
[Required]
public string EntityId { get; set; }
}
}

11
modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Tags/ITagAppService.cs

@ -0,0 +1,11 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.Application.Services;
namespace Volo.CmsKit.Tags
{
public interface ITagAppService : IApplicationService
{
Task<List<TagDto>> GetAllRelatedTagsAsync(GetRelatedTagsInput input);
}
}

12
modules/cms-kit/src/Volo.CmsKit.Common.Application.Contracts/Volo/CmsKit/Tags/TagDto.cs

@ -0,0 +1,12 @@
using System;
using Volo.Abp.Application.Dtos;
namespace Volo.CmsKit.Tags
{
public class TagDto : EntityDto<Guid>
{
public string EntityType { get; set; }
public string Name { get; protected set; }
}
}

0
modules/cms-kit/src/Volo.CmsKit.Common.Application/Volo/CmsKit/CmsKitAdminAppService.cs → modules/cms-kit/src/Volo.CmsKit.Common.Application/Volo/CmsKit/CmsKitAppServiceBase.cs

19
modules/cms-kit/src/Volo.CmsKit.Common.Application/Volo/CmsKit/CmsKitCommonApplicationAutoMapperProfile.cs

@ -0,0 +1,19 @@
using AutoMapper;
using Volo.CmsKit.Contents;
using Volo.CmsKit.Pages;
using Volo.CmsKit.Tags;
namespace Volo.CmsKit
{
public class CmsKitCommonApplicationAutoMapperProfile : Profile
{
public CmsKitCommonApplicationAutoMapperProfile()
{
CreateMap<Content, ContentDto>();
CreateMap<Tag, TagDto>();
CreateMap<Page, PageDto>();
}
}
}

7
modules/cms-kit/src/Volo.CmsKit.Common.Application/Volo/CmsKit/CmsKitCommonApplicationModule.cs

@ -12,5 +12,12 @@ namespace Volo.CmsKit
)]
public class CmsKitCommonApplicationModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpAutoMapperOptions>(options =>
{
options.AddMaps<CmsKitCommonApplicationModule>(validate: true);
});
}
}
}

28
modules/cms-kit/src/Volo.CmsKit.Common.Application/Volo/CmsKit/Contents/ContentAppService.cs

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Volo.CmsKit.Contents
{
public class ContentAppService : CmsKitAppServiceBase, IContentAppService
{
protected readonly IContentRepository ContentRepository;
public ContentAppService(IContentRepository contentRepository)
{
ContentRepository = contentRepository;
}
public virtual async Task<ContentDto> GetAsync(GetContentInput input)
{
var entity = await ContentRepository.GetAsync(
input.EntityType,
input.EntityId,
CurrentTenant.Id);
return ObjectMapper.Map<Content, ContentDto>(entity);
}
}
}

114
modules/cms-kit/src/Volo.CmsKit.Common.Application/Volo/CmsKit/Pages/PageAppService.cs

@ -0,0 +1,114 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.CmsKit.Contents;
namespace Volo.CmsKit.Pages
{
public class PageAppService : CmsKitAppServiceBase, IPageAppService
{
protected readonly IPageRepository PageRepository;
protected readonly IContentRepository ContentRepository;
public PageAppService(IPageRepository pageRepository, IContentRepository contentRepository)
{
PageRepository = pageRepository;
ContentRepository = contentRepository;
}
public virtual async Task<PageDto> GetAsync(Guid id)
{
var page = await PageRepository.GetAsync(id);
return ObjectMapper.Map<Page, PageDto>(page);
}
public virtual async Task<PageDto> GetByUrlAsync(string url)
{
var page = await PageRepository.GetByUrlAsync(url);
return ObjectMapper.Map<Page, PageDto>(page);
}
public virtual async Task<PageDto> CreatePageAsync(CreatePageInputDto input)
{
var page = await CreatePageAsync(input.Title, input.Url, input.Description);
await PageRepository.InsertAsync(page);
return ObjectMapper.Map<Page, PageDto>(page);
}
public virtual async Task<PageDto> CreatePageWithContentAsync(CreatePageWithContentInputDto input)
{
var page = await CreatePageAsync(input.Title, input.Url, input.Description);
await PageRepository.InsertAsync(page);
var content = new Content(
GuidGenerator.Create(),
nameof(Page),
page.Id.ToString(),
input.Content,
page.TenantId);
await ContentRepository.InsertAsync(content);
return ObjectMapper.Map<Page, PageDto>(page);
}
public virtual async Task<PageDto> UpdatePageAsync(Guid id, UpdatePageInputDto input)
{
var page = await PageRepository.GetAsync(id);
if (page.Url != input.Url)
{
await CheckPageUrlAsync(input.Url);
}
page.Title = input.Title;
page.Url = input.Url;
page.Description = input.Description;
await PageRepository.UpdateAsync(page);
return ObjectMapper.Map<Page, PageDto>(page);
}
public virtual Task<bool> DoesUrlExistAsync(CheckUrlInputDto input)
{
return PageRepository.DoesExistAsync(input.Url);
}
public virtual async Task UpdatePageContentAsync(Guid id, UpdatePageContentInputDto input)
{
var pageContent = await ContentRepository.GetAsync(nameof(Page), id.ToString());
pageContent.SetValue(input.Content);
await ContentRepository.UpdateAsync(pageContent);
}
public virtual async Task DeleteAsync(Guid id)
{
await ContentRepository.DeleteAsync(nameof(Page), id.ToString(), CurrentTenant?.Id, CancellationToken.None);
await PageRepository.DeleteAsync(id, cancellationToken: CancellationToken.None);
}
protected virtual async Task<Page> CreatePageAsync(string title, string url, string description)
{
await CheckPageUrlAsync(url);
return new Page(GuidGenerator.Create(), title, url, description, CurrentTenant?.Id);
}
protected virtual async Task CheckPageUrlAsync(string url)
{
if (await PageRepository.DoesExistAsync(url))
{
throw new UserFriendlyException("Url exist");
}
}
}
}

34
modules/cms-kit/src/Volo.CmsKit.Common.Application/Volo/CmsKit/Tags/TagAppService.cs

@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Volo.CmsKit.Tags
{
public class TagAppService : CmsKitAppServiceBase, ITagAppService
{
protected readonly ITagManager TagManager;
protected readonly ITagRepository TagRepository;
protected readonly IEntityTagRepository EntityTagRepository;
public TagAppService(
ITagManager tagManager,
ITagRepository tagRepository,
IEntityTagRepository entityTagRepository)
{
TagManager = tagManager;
TagRepository = tagRepository;
EntityTagRepository = entityTagRepository;
}
public virtual async Task<List<TagDto>> GetAllRelatedTagsAsync(GetRelatedTagsInput input)
{
var entities = await TagRepository.GetAllRelatedTagsAsync(
input.EntityType,
input.EntityId,
CurrentTenant.Id);
return ObjectMapper.Map<List<Tag>, List<TagDto>>(entities);
}
}
}

27
modules/cms-kit/src/Volo.CmsKit.Common.HttpApi/Volo/CmsKit/Controllers/Contents/ContentController.cs

@ -0,0 +1,27 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp;
using Volo.CmsKit.Contents;
namespace Volo.CmsKit.Controllers.Contents
{
[RemoteService(Name = CmsKitCommonRemoteServiceConsts.RemoteServiceName)]
[Area("cms-kit")]
[Route("api/cms-kit/contents")]
public class ContentController : CmsKitControllerBase, IContentAppService
{
protected readonly IContentAppService _contentAppService;
public ContentController(IContentAppService contentAppService)
{
_contentAppService = contentAppService;
}
[HttpGet]
public virtual Task<ContentDto> GetAsync(GetContentInput input)
{
return _contentAppService.GetAsync(input);
}
}
}

77
modules/cms-kit/src/Volo.CmsKit.Common.HttpApi/Volo/CmsKit/Controllers/Pages/PageController.cs

@ -0,0 +1,77 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp;
using Volo.CmsKit.Pages;
namespace Volo.CmsKit.Controllers.Pages
{
[RemoteService(Name = CmsKitCommonRemoteServiceConsts.RemoteServiceName)]
[Area("cms-kit")]
[Route("api/cms-kit/pages")]
public class PageController : CmsKitControllerBase, IPageAppService
{
protected readonly IPageAppService PageAppService;
public PageController(IPageAppService pageAppService)
{
PageAppService = pageAppService;
}
[HttpGet]
[Route("{id}")]
public virtual Task<PageDto> GetAsync(Guid id)
{
return PageAppService.GetAsync(id);
}
[HttpGet]
[Route("url/{url}")]
public Task<PageDto> GetByUrlAsync(string url)
{
return PageAppService.GetByUrlAsync(url);
}
[HttpPost]
[Route("create")]
public virtual Task<PageDto> CreatePageAsync(CreatePageInputDto input)
{
return PageAppService.CreatePageAsync(input);
}
[HttpPost]
[Route("create-with-content")]
public virtual Task<PageDto> CreatePageWithContentAsync(CreatePageWithContentInputDto input)
{
return PageAppService.CreatePageWithContentAsync(input);
}
[HttpPut]
[Route("{id}")]
public virtual Task<PageDto> UpdatePageAsync(Guid id, UpdatePageInputDto input)
{
return PageAppService.UpdatePageAsync(id, input);
}
[HttpPost]
[Route("does-url-exist")]
public virtual Task<bool> DoesUrlExistAsync(CheckUrlInputDto input)
{
return PageAppService.DoesUrlExistAsync(input);
}
[HttpPut]
[Route("update-content/{id}")]
public virtual Task UpdatePageContentAsync(Guid id, UpdatePageContentInputDto input)
{
return PageAppService.UpdatePageContentAsync(id, input);
}
[HttpDelete]
[Route("{id}")]
public virtual Task DeleteAsync(Guid id)
{
return PageAppService.DeleteAsync(id);
}
}
}

27
modules/cms-kit/src/Volo.CmsKit.Common.HttpApi/Volo/CmsKit/Controllers/Tags/TagController.cs

@ -0,0 +1,27 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp;
using Volo.CmsKit.Tags;
namespace Volo.CmsKit.Controllers.Tags
{
[RemoteService(Name = CmsKitCommonRemoteServiceConsts.RemoteServiceName)]
[Area("cms-kit")]
[Route("api/cms-kit/tags")]
public class TagController : CmsKitControllerBase, ITagAppService
{
protected readonly ITagAppService TagAppService;
public TagController(ITagAppService tagAppService)
{
TagAppService = tagAppService;
}
[HttpGet]
public Task<List<TagDto>> GetAllRelatedTagsAsync(GetRelatedTagsInput input)
{
return TagAppService.GetAllRelatedTagsAsync(input);
}
}
}

9
modules/cms-kit/src/Volo.CmsKit.Common.Web/Contents/IContentRenderer.cs

@ -0,0 +1,9 @@
using System.Threading.Tasks;
namespace Volo.CmsKit.Web.Contents
{
public interface IContentRenderer
{
Task<string> RenderAsync(string value);
}
}

13
modules/cms-kit/src/Volo.CmsKit.Common.Web/Contents/PlainTextContentRenderer.cs

@ -0,0 +1,13 @@
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
namespace Volo.CmsKit.Web.Contents
{
public class PlainTextContentRenderer : IContentRenderer, ITransientDependency
{
public Task<string> RenderAsync(string value)
{
return Task.FromResult(value);
}
}
}

15
modules/cms-kit/src/Volo.CmsKit.Common.Web/Pages/CmsKit/Pages/Index.cshtml

@ -0,0 +1,15 @@
@page "{pageUrl}"
@using Microsoft.AspNetCore.Mvc.Localization
@using Volo.CmsKit.Localization
@using Volo.CmsKit.Web.Pages.CmsKit.Pages
@using Volo.CmsKit.Web.Pages.CmsKit.Shared.Components.Pages
@model IndexModel
@inject IHtmlLocalizer<CmsKitResource> L
@await Component.InvokeAsync(typeof(DefaultPageViewComponent),
new
{
pageId = Model.Page.Id,
title = Model.Page.Title,
description = Model.Page.Description
})

26
modules/cms-kit/src/Volo.CmsKit.Common.Web/Pages/CmsKit/Pages/Index.cshtml.cs

@ -0,0 +1,26 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.CmsKit.Pages;
namespace Volo.CmsKit.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.GetByUrlAsync(PageUrl);
}
}
}

55
modules/cms-kit/src/Volo.CmsKit.Common.Web/Pages/CmsKit/Shared/Components/Contents/ContentViewComponent.cs

@ -0,0 +1,55 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
using Volo.CmsKit.Contents;
using Volo.CmsKit.Web.Contents;
namespace Volo.CmsKit.Web.Pages.CmsKit.Shared.Components.Contents
{
[ViewComponent(Name = "CmsContent")]
public class ContentViewComponent : AbpViewComponent
{
private readonly IContentRenderer contentRenderer;
private readonly IContentAppService contentAppService;
public ContentViewComponent(
IContentRenderer contentRenderer,
IContentAppService contentAppService)
{
this.contentRenderer = contentRenderer;
this.contentAppService = contentAppService;
}
public virtual async Task<IViewComponentResult> InvokeAsync(
string entityType,
string entityId)
{
var content = await contentAppService.GetAsync(new GetContentInput
{
EntityId = entityId,
EntityType = entityType
});
var viewModel = new ContentViewModel
{
EntityId = entityId,
EntityType = entityType,
ContentId = content.Id,
Rendered = await contentRenderer.RenderAsync(content.Value)
};
return View("~/Pages/CmsKit/Shared/Components/Contents/Default.cshtml", viewModel);
}
public class ContentViewModel
{
public Guid ContentId { get; set; }
public string EntityType { get; set; }
public string EntityId { get; set; }
public string Rendered { get; set; }
}
}
}

7
modules/cms-kit/src/Volo.CmsKit.Common.Web/Pages/CmsKit/Shared/Components/Contents/Default.cshtml

@ -0,0 +1,7 @@
@using Microsoft.AspNetCore.Mvc.Localization
@using Volo.CmsKit.Localization
@model Volo.CmsKit.Web.Pages.CmsKit.Shared.Components.Contents.ContentViewComponent.ContentViewModel
<div>
@Html.Raw(Model.Rendered)
</div>

21
modules/cms-kit/src/Volo.CmsKit.Common.Web/Pages/CmsKit/Shared/Components/Pages/Default.cshtml

@ -0,0 +1,21 @@
@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bootstrap
@using Microsoft.AspNetCore.Mvc.RazorPages
@using Volo.CmsKit.Web.Pages.CmsKit.Shared.Components.Contents
@model Volo.CmsKit.Web.Pages.CmsKit.Shared.Components.Pages.PageViewModel
<abp-card>
<abp-card-header>
<h3>
@Model.Title
</h3>
</abp-card-header>
<abp-card-body>
<p>
@Model.Description
</p>
@await Component.InvokeAsync(typeof(ContentViewComponent), new { entityType = nameof(Page), entityId = Model.Id.ToString() })
</abp-card-body>
</abp-card>

40
modules/cms-kit/src/Volo.CmsKit.Common.Web/Pages/CmsKit/Shared/Components/Pages/DefaultPageViewComponent.cs

@ -0,0 +1,40 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
using Volo.CmsKit.Pages;
namespace Volo.CmsKit.Web.Pages.CmsKit.Shared.Components.Pages
{
[ViewComponent(Name = "CmsDefaultPage")]
public class DefaultPageViewComponent : AbpViewComponent
{
protected readonly IPageAppService PageAppService;
public DefaultPageViewComponent(IPageAppService pageAppService)
{
PageAppService = pageAppService;
}
public virtual async Task<IViewComponentResult> InvokeAsync(Guid pageId, string title, string description)
{
var model = new PageViewModel
{
Id = pageId,
Title = title,
Description = description
};
return View("~/Pages/CmsKit/Shared/Components/Pages/Default.cshtml", model);
}
}
public class PageViewModel
{
public Guid Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
}
}

13
modules/cms-kit/src/Volo.CmsKit.Common.Web/Pages/CmsKit/Shared/Components/Tags/Default.cshtml

@ -0,0 +1,13 @@
@using Volo.CmsKit.Web.Pages.CmsKit.Shared.Components.Tags
@model TagViewComponent.TagViewModel
@if (Model.Tags != null)
{
foreach (var tag in Model.Tags)
{
<span class="cmskit-tag">
@tag.Name
</span>
}
}

49
modules/cms-kit/src/Volo.CmsKit.Common.Web/Pages/CmsKit/Shared/Components/Tags/TagViewComponent.cs

@ -0,0 +1,49 @@
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.Tags;
namespace Volo.CmsKit.Web.Pages.CmsKit.Shared.Components.Tags
{
[ViewComponent(Name = "CmsTags")]
public class TagViewComponent : AbpViewComponent
{
protected readonly ITagAppService TagAppService;
public TagViewComponent(ITagAppService tagAppService)
{
TagAppService = tagAppService;
}
public virtual async Task<IViewComponentResult> InvokeAsync(
string entityType,
string entityId)
{
var tagDtos = await TagAppService.GetAllRelatedTagsAsync(new GetRelatedTagsInput
{
EntityId = entityId,
EntityType = entityType
});
var viewModel = new TagViewModel
{
EntityId = entityId,
EntityType = entityType,
Tags = tagDtos
};
return View("~/Pages/CmsKit/Shared/Components/Tags/Default.cshtml", viewModel);
}
public class TagViewModel
{
public List<TagDto> Tags { get; set; }
public string EntityId { get; set; }
public string EntityType { get; set; }
}
}
}

4
modules/cms-kit/src/Volo.CmsKit.Common.Web/Pages/CmsKit/_ViewImports.cshtml

@ -0,0 +1,4 @@
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI
@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bootstrap
@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bundling

13
modules/cms-kit/src/Volo.CmsKit.Common.Web/Pages/CommonPageModel.cs

@ -0,0 +1,13 @@
using Volo.Abp.AspNetCore.Mvc.UI.RazorPages;
using Volo.CmsKit.Localization;
namespace Volo.CmsKit.Web.Pages
{
public abstract class CommonPageModel : AbpPageModel
{
protected CommonPageModel()
{
LocalizationResourceType = typeof(CmsKitResource);
}
}
}

15
modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Contents/ContentConsts.cs

@ -0,0 +1,15 @@
using System;
using Volo.CmsKit.Entities;
namespace Volo.CmsKit.Contents
{
public static class ContentConsts
{
public static int MaxEntityTypeLength { get; set; } = CmsEntityConsts.MaxEntityTypeLength;
public static int MaxEntityIdLength { get; set; } = CmsEntityConsts.MaxEntityIdLength;
// TODO: consider
public static int MaxValueLength = int.MaxValue;
}
}

17
modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/GlobalFeatures/ContentsFeature.cs

@ -0,0 +1,17 @@
using JetBrains.Annotations;
using Volo.Abp.GlobalFeatures;
namespace Volo.CmsKit.GlobalFeatures
{
[GlobalFeatureName(Name)]
public class ContentsFeature : GlobalFeature
{
public const string Name = "CmsKit.Contents";
internal ContentsFeature(
[NotNull] GlobalCmsKitFeatures cmsKit
) : base(cmsKit)
{
}
}
}

4
modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/GlobalFeatures/GlobalCmsKitFeatures.cs

@ -13,12 +13,16 @@ namespace Volo.CmsKit.GlobalFeatures
public RatingsFeature Ratings => GetFeature<RatingsFeature>();
public TagsFeature Tags => GetFeature<TagsFeature>();
public GlobalCmsKitFeatures([NotNull] GlobalFeatureManager featureManager)
: base(featureManager)
{
AddFeature(new ReactionsFeature(this));
AddFeature(new CommentsFeature(this));
AddFeature(new RatingsFeature(this));
AddFeature(new TagsFeature(this));
AddFeature(new ContentsFeature(this));
}
}
}

17
modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/GlobalFeatures/TagsFeature.cs

@ -0,0 +1,17 @@
using JetBrains.Annotations;
using Volo.Abp.GlobalFeatures;
namespace Volo.CmsKit.GlobalFeatures
{
[GlobalFeatureName(Name)]
public class TagsFeature : GlobalFeature
{
public const string Name = "CmsKit.Tags";
internal TagsFeature(
[NotNull] GlobalCmsKitFeatures cmsKit
) : base(cmsKit)
{
}
}
}

11
modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Pages/PageConsts.cs

@ -0,0 +1,11 @@
namespace Volo.CmsKit.Pages
{
public class PageConsts
{
public static int MaxTitleLength = 256;
public static int MaxUrlLength = 256;
public static int MaxDescriptionLength = 515;
}
}

11
modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Tags/TagConsts.cs

@ -0,0 +1,11 @@
using Volo.CmsKit.Entities;
namespace Volo.CmsKit.Tags
{
public static class TagConsts
{
public static int MaxEntityTypeLength { get; set; } = CmsEntityConsts.MaxEntityTypeLength;
public static int MaxNameLength { get; set; } = 32;
}
}

38
modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Contents/Content.cs

@ -0,0 +1,38 @@
using System;
using JetBrains.Annotations;
using Volo.Abp;
using Volo.Abp.Domain.Entities.Auditing;
using Volo.Abp.MultiTenancy;
namespace Volo.CmsKit.Contents
{
public class Content : FullAuditedAggregateRoot<Guid>, IMultiTenant
{
public virtual Guid? TenantId { get; set; }
public virtual string EntityType { get; set; }
public virtual string EntityId { get; set; }
public virtual string Value { get; protected set; }
protected Content()
{
}
public Content(Guid id, [NotNull] string entityType, [NotNull] string entityId, [NotNull] string value, Guid? tenantId = null) : base(id)
{
EntityType = Check.NotNullOrWhiteSpace(entityType, nameof(entityType), ContentConsts.MaxEntityTypeLength);
EntityId = Check.NotNullOrWhiteSpace(entityId, nameof(entityId), ContentConsts.MaxEntityIdLength);
SetValue(value);
TenantId = tenantId;
}
public void SetValue([NotNull] string value)
{
Value = Check.NotNullOrWhiteSpace(value, nameof(value), ContentConsts.MaxValueLength);
}
}
}

25
modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Contents/IContentRepository.cs

@ -0,0 +1,25 @@
using JetBrains.Annotations;
using System;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories;
namespace Volo.CmsKit.Contents
{
public interface IContentRepository : IBasicRepository<Content, Guid>
{
Task<Content> GetAsync(
[NotNull] string entityType,
[NotNull] string entityId,
Guid? tenantId = null,
CancellationToken cancellationToken = default);
Task<Content> FindAsync(
[NotNull] string entityType,
[NotNull] string entityId,
Guid? tenantId = null,
CancellationToken cancellationToken = default);
Task DeleteAsync([NotNull] string entityType, [NotNull] string entityId, Guid? tenantId = null, CancellationToken cancellationToken = default);
}
}

15
modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Pages/IPageRepository.cs

@ -0,0 +1,15 @@
using System;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories;
namespace Volo.CmsKit.Pages
{
public interface IPageRepository : IBasicRepository<Page, Guid>
{
Task<Page> GetByUrlAsync(string url);
Task<Page> FindByUrlAsync(string url);
Task<bool> DoesExistAsync(string url);
}
}

33
modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Pages/Page.cs

@ -0,0 +1,33 @@
using System;
using JetBrains.Annotations;
using Volo.Abp;
using Volo.Abp.Domain.Entities.Auditing;
using Volo.Abp.MultiTenancy;
namespace Volo.CmsKit.Pages
{
public class Page : FullAuditedAggregateRoot<Guid>, IMultiTenant
{
public Guid? TenantId { get; set; }
public virtual string Title { get; set; }
public virtual string Url { get; set; }
public virtual string Description { get; set; }
protected Page()
{
}
public Page(Guid id, [NotNull] string title, [NotNull] string url, [CanBeNull] string description, Guid? tenantId = null) : base(id)
{
Title = Check.NotNullOrWhiteSpace(title, nameof(title), PageConsts.MaxTitleLength);
Url = Check.NotNullOrWhiteSpace(url, nameof(url), PageConsts.MaxUrlLength);
Description = Check.Length(description, nameof(description), PageConsts.MaxDescriptionLength);
TenantId = tenantId;
}
}
}

32
modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/EntityTag.cs

@ -0,0 +1,32 @@
using System;
using Volo.Abp.Domain.Entities;
using Volo.Abp.MultiTenancy;
namespace Volo.CmsKit.Tags
{
public class EntityTag : Entity, IMultiTenant
{
public virtual Guid TagId { get; set; }
public virtual string EntityId { get; set; }
public virtual Guid? TenantId { get; set; }
protected EntityTag()
{
}
public EntityTag(Guid tagId, string entityId, Guid? tenantId = null)
{
TagId = tagId;
EntityId = entityId;
TenantId = tenantId;
}
public override object[] GetKeys()
{
return new object[] { TagId, EntityId };
}
}
}

9
modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/IEntityTagRepository.cs

@ -0,0 +1,9 @@
using Volo.Abp.Domain.Repositories;
namespace Volo.CmsKit.Tags
{
public interface IEntityTagRepository : IBasicRepository<EntityTag>
{
}
}

30
modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/ITagManager.cs

@ -0,0 +1,30 @@
using JetBrains.Annotations;
using System;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Domain.Services;
using Volo.CmsKit.Tags;
namespace Volo.CmsKit.Tags
{
public interface ITagManager : IDomainService
{
Task<Tag> InsertAsync(
Guid id,
[NotNull] string entityType,
[NotNull] string name,
Guid? tenantId = null,
CancellationToken cancellationToken = default);
Task<Tag> UpdateAsync(
Guid id,
[NotNull] string name,
CancellationToken cancellationToken = default);
Task<Tag> GetOrAddAsync(
[NotNull] string entityType,
[NotNull] string name,
Guid? tenantId = null,
CancellationToken cancellationToken = default);
}
}

36
modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/ITagRepository.cs

@ -0,0 +1,36 @@
using JetBrains.Annotations;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories;
namespace Volo.CmsKit.Tags
{
public interface ITagRepository : IBasicRepository<Tag, Guid>
{
Task<Tag> GetAsync(
[NotNull] string entityType,
[NotNull] string name,
Guid? tenantId = null,
CancellationToken cancellationToken = default);
Task<bool> AnyAsync(
[NotNull] string entityType,
[NotNull] string name,
Guid? tenantId = null,
CancellationToken cancellationToken = default);
Task<Tag> FindAsync(
[NotNull] string entityType,
[NotNull] string name,
Guid? tenantId = null,
CancellationToken cancellationToken = default);
Task<List<Tag>> GetAllRelatedTagsAsync(
[NotNull] string entityType,
[NotNull] string entityId,
Guid? tenantId = null,
CancellationToken cancellationToken = default);
}
}

37
modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/Tag.cs

@ -0,0 +1,37 @@
using JetBrains.Annotations;
using System;
using Volo.Abp;
using Volo.Abp.Domain.Entities.Auditing;
using Volo.Abp.MultiTenancy;
namespace Volo.CmsKit.Tags
{
public class Tag : FullAuditedAggregateRoot<Guid>, IMultiTenant
{
public string EntityType { get; set; }
public string Name { get; protected set; }
public Guid? TenantId { get; set; }
protected Tag()
{
}
public Tag(
Guid id,
[NotNull] string entityType,
[NotNull] string name,
Guid? tenantId = null) : base(id)
{
EntityType = Check.NotNullOrWhiteSpace(entityType, nameof(entityType), TagConsts.MaxEntityTypeLength);
SetName(name);
TenantId = tenantId;
}
public void SetName(string name)
{
Name = Check.NotNullOrWhiteSpace(name, nameof(name), TagConsts.MaxNameLength);
}
}
}

73
modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/TagManager.cs

@ -0,0 +1,73 @@
using JetBrains.Annotations;
using System;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Domain.Services;
namespace Volo.CmsKit.Tags
{
public class TagManager : DomainService, ITagManager
{
private readonly ITagRepository _tagRepository;
public TagManager(ITagRepository tagRepository)
{
_tagRepository = tagRepository;
}
public async Task<Tag> GetOrAddAsync(
[NotNull] string entityType,
[NotNull] string name,
Guid? tenantId = null,
CancellationToken cancellationToken = default)
{
var entity = await _tagRepository.FindAsync(entityType, name, tenantId, cancellationToken);
if (entity == null)
{
entity = await InsertAsync(GuidGenerator.Create(), entityType, name, tenantId, cancellationToken);
}
return entity;
}
public async Task<Tag> InsertAsync(
Guid id,
[NotNull] string entityType,
[NotNull] string name,
Guid? tenantId = null,
CancellationToken cancellationToken = default)
{
if (await _tagRepository.AnyAsync(entityType, name, tenantId, cancellationToken))
{
throw new BusinessException(message: "Tag already exist!"); // Already Exist
}
return await _tagRepository.InsertAsync(
new Tag(
id,
entityType,
name,
tenantId),
cancellationToken: cancellationToken);
}
public async Task<Tag> UpdateAsync(
Guid id,
[NotNull] string name,
CancellationToken cancellationToken = default)
{
var entity = await _tagRepository.GetAsync(id, cancellationToken: cancellationToken);
if (name != entity.Name && await _tagRepository.AnyAsync(entity.EntityType, name, entity.TenantId, cancellationToken))
{
throw new BusinessException(message: "Tag already exist!"); // Already Exist
}
entity.SetName(name);
return await _tagRepository.UpdateAsync(entity);
}
}
}

55
modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/Contents/EfCoreContentRepository.cs

@ -0,0 +1,55 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
using Volo.CmsKit.EntityFrameworkCore;
namespace Volo.CmsKit.Contents
{
public class EfCoreContentRepository : EfCoreRepository<ICmsKitDbContext, Content, Guid>, IContentRepository
{
public EfCoreContentRepository(IDbContextProvider<ICmsKitDbContext> dbContextProvider) : base(dbContextProvider)
{
}
public Task<Content> GetAsync(
string entityType,
string entityId,
Guid? tenantId = null,
CancellationToken cancellationToken = default)
{
return GetAsync(x =>
!x.IsDeleted &&
x.EntityType == entityType &&
x.EntityId == entityId &&
x.TenantId == tenantId,
cancellationToken: cancellationToken
);
}
public Task<Content> FindAsync(
string entityType,
string entityId,
Guid? tenantId = null,
CancellationToken cancellationToken = default)
{
return FindAsync(x =>
!x.IsDeleted &&
x.EntityType == entityType &&
x.EntityId == entityId &&
x.TenantId == tenantId,
cancellationToken: cancellationToken
);
}
public Task DeleteAsync(string entityType, string entityId, Guid? tenantId = null, CancellationToken cancellationToken = default)
{
return DeleteAsync(x =>
x.EntityType == entityType &&
x.EntityId == entityId &&
x.TenantId == tenantId,
cancellationToken: cancellationToken);
}
}
}

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

@ -8,8 +8,11 @@ using Volo.CmsKit.Comments;
using Volo.CmsKit.Reactions;
using Volo.CmsKit.Users;
using Volo.Abp.Users.EntityFrameworkCore;
using Volo.CmsKit.Contents;
using Volo.CmsKit.GlobalFeatures;
using Volo.CmsKit.Pages;
using Volo.CmsKit.Ratings;
using Volo.CmsKit.Tags;
namespace Volo.CmsKit.EntityFrameworkCore
{
@ -93,6 +96,58 @@ namespace Volo.CmsKit.EntityFrameworkCore
r.HasIndex(x => new {x.TenantId, x.EntityType, x.EntityId, x.CreatorId});
});
}
builder.Entity<Content>(b =>
{
b.ToTable(options.TablePrefix + "Contents", options.Schema);
b.ConfigureByConvention();
b.Property(x => x.EntityType).IsRequired().HasMaxLength(ContentConsts.MaxEntityTypeLength);
b.Property(x => x.EntityId).IsRequired().HasMaxLength(ContentConsts.MaxEntityIdLength);
b.Property(x => x.Value).IsRequired().HasMaxLength(ContentConsts.MaxValueLength);
b.HasIndex(x => new { x.TenantId, x.EntityType, x.EntityId });
});
builder.Entity<Tag>(b =>
{
b.ToTable(options.TablePrefix + "Tags", options.Schema);
b.ConfigureByConvention();
b.Property(x => x.EntityType).IsRequired().HasMaxLength(TagConsts.MaxEntityTypeLength);
b.Property(x => x.Name).IsRequired().HasMaxLength(TagConsts.MaxNameLength);
b.HasIndex(x => new { x.TenantId, x.Name });
});
builder.Entity<EntityTag>(b =>
{
b.ToTable(options.TablePrefix + "EntityTags", options.Schema);
b.ConfigureByConvention();
b.HasKey(x => new { x.EntityId, x.TagId });
b.Property(x => x.EntityId).IsRequired();
b.Property(x => x.TagId).IsRequired();
b.HasIndex(x => new { x.TenantId, x.EntityId, x.TagId });
});
builder.Entity<Page>(b =>
{
b.ToTable(options.TablePrefix + "Pages", options.Schema);
b.ConfigureByConvention();
b.Property(x => x.Title).IsRequired().HasMaxLength(PageConsts.MaxTitleLength);
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 });
});
}
}
}

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

@ -3,8 +3,11 @@ using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.Modularity;
using Volo.Abp.Users.EntityFrameworkCore;
using Volo.CmsKit.Comments;
using Volo.CmsKit.Contents;
using Volo.CmsKit.Pages;
using Volo.CmsKit.Ratings;
using Volo.CmsKit.Reactions;
using Volo.CmsKit.Tags;
using Volo.CmsKit.Users;
namespace Volo.CmsKit.EntityFrameworkCore
@ -24,6 +27,10 @@ namespace Volo.CmsKit.EntityFrameworkCore
options.AddRepository<UserReaction, EfCoreUserReactionRepository>();
options.AddRepository<Comment, EfCoreCommentRepository>();
options.AddRepository<Rating, EfCoreRatingRepository>();
options.AddRepository<Content, EfCoreContentRepository>();
options.AddRepository<Tag, EfCoreTagRepository>();
options.AddRepository<EntityTag, EfCoreEntityTagRepository>();
options.AddRepository<Page, EfCorePageRepository>();
});
}
}

31
modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/Pages/EfCorePageRepository.cs

@ -0,0 +1,31 @@
using System;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
using Volo.CmsKit.EntityFrameworkCore;
namespace Volo.CmsKit.Pages
{
public class EfCorePageRepository : EfCoreRepository<ICmsKitDbContext, Page, Guid>, IPageRepository
{
public EfCorePageRepository(IDbContextProvider<ICmsKitDbContext> dbContextProvider) : base(dbContextProvider)
{
}
public Task<Page> GetByUrlAsync(string url)
{
return GetAsync(x => x.Url == url);
}
public Task<Page> FindByUrlAsync(string url)
{
return FindAsync(x => x.Url == url);
}
public Task<bool> DoesExistAsync(string url)
{
return DbSet.AnyAsync(x => x.Url == url);
}
}
}

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

@ -0,0 +1,13 @@
using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
using Volo.CmsKit.EntityFrameworkCore;
namespace Volo.CmsKit.Tags
{
public class EfCoreEntityTagRepository: EfCoreRepository<ICmsKitDbContext, EntityTag>, IEntityTagRepository
{
public EfCoreEntityTagRepository(IDbContextProvider<ICmsKitDbContext> dbContextProvider) : base(dbContextProvider)
{
}
}
}

80
modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/Tags/EfCoreTagRepository.cs

@ -0,0 +1,80 @@
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.VirtualFileSystem;
using Volo.CmsKit.EntityFrameworkCore;
namespace Volo.CmsKit.Tags
{
public class EfCoreTagRepository : EfCoreRepository<ICmsKitDbContext, Tag, Guid>, ITagRepository
{
public EfCoreTagRepository(IDbContextProvider<ICmsKitDbContext> dbContextProvider) : base(dbContextProvider)
{
}
public virtual Task<bool> AnyAsync(
[NotNull] string entityType,
[NotNull] string name,
Guid? tenantId = null,
CancellationToken cancellationToken = default)
{
return DbSet.AnyAsync(x =>
x.EntityType == entityType &&
x.Name == name &&
x.TenantId == tenantId,
cancellationToken);
}
public virtual Task<Tag> GetAsync(
[NotNull] string entityType,
[NotNull] string name,
Guid? tenantId = null,
CancellationToken cancellationToken = default)
{
return GetAsync(x =>
x.EntityType == entityType &&
x.Name == name &&
x.TenantId == tenantId,
cancellationToken: cancellationToken);
}
public virtual Task<Tag> FindAsync(
[NotNull] string entityType,
[NotNull] string name,
Guid? tenantId = null,
CancellationToken cancellationToken = default)
{
return FindAsync(x =>
x.EntityType == entityType &&
x.Name == name &&
x.TenantId == tenantId,
cancellationToken: cancellationToken);
}
public virtual async Task<List<Tag>> GetAllRelatedTagsAsync(
[NotNull] string entityType,
[NotNull] string entityId,
Guid? tenantId = null,
CancellationToken cancellationToken = default)
{
var entityTagIds = await DbContext.Set<EntityTag>()
.Where(q => q.EntityId == entityId && q.TenantId == tenantId)
.Select(q => q.TagId)
.ToListAsync(cancellationToken: GetCancellationToken(cancellationToken));
var query = DbSet
.Where(x => x.EntityType == entityType &&
x.TenantId == tenantId &&
entityTagIds.Contains(x.Id));
return await query.ToListAsync(cancellationToken: GetCancellationToken(cancellationToken));
}
}
}

12
modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/CmsKitMongoDbContext.cs

@ -2,9 +2,13 @@
using Volo.Abp.Data;
using Volo.Abp.MongoDB;
using Volo.CmsKit.Comments;
using Volo.CmsKit.Contents;
using Volo.CmsKit.Pages;
using Volo.CmsKit.Ratings;
using Volo.CmsKit.Reactions;
using Volo.CmsKit.Tags;
using Volo.CmsKit.Users;
using Tag = MongoDB.Driver.Tag;
namespace Volo.CmsKit.MongoDB
{
@ -19,6 +23,14 @@ namespace Volo.CmsKit.MongoDB
public IMongoCollection<Rating> Ratings => Collection<Rating>();
public IMongoCollection<Content> Contents => Collection<Content>();
public IMongoCollection<Tag> Tags => Collection<Tag>();
public IMongoCollection<EntityTag> EntityTags => Collection<EntityTag>();
public IMongoCollection<Page> Pages => Collection<Page>();
protected override void CreateModel(IMongoModelBuilder modelBuilder)
{
base.CreateModel(modelBuilder);

23
modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/CmsKitMongoDbContextExtensions.cs

@ -2,8 +2,11 @@
using Volo.Abp;
using Volo.Abp.MongoDB;
using Volo.CmsKit.Comments;
using Volo.CmsKit.Contents;
using Volo.CmsKit.Pages;
using Volo.CmsKit.Ratings;
using Volo.CmsKit.Reactions;
using Volo.CmsKit.Tags;
using Volo.CmsKit.Users;
namespace Volo.CmsKit.MongoDB
@ -41,6 +44,26 @@ namespace Volo.CmsKit.MongoDB
{
x.CollectionName = CmsKitDbProperties.DbTablePrefix + "Ratings";
});
builder.Entity<Content>(x =>
{
x.CollectionName = CmsKitDbProperties.DbTablePrefix + "Contents";
});
builder.Entity<Tag>(x =>
{
x.CollectionName = CmsKitDbProperties.DbTablePrefix + "Tags";
});
builder.Entity<EntityTag>(x =>
{
x.CollectionName = CmsKitDbProperties.DbTablePrefix + "EntityTags";
});
builder.Entity<Page>(x =>
{
x.CollectionName = CmsKitDbProperties.DbTablePrefix + "Pages";
});
}
}
}

10
modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/CmsKitMongoDbModule.cs

@ -3,12 +3,18 @@ using Volo.Abp.Modularity;
using Volo.Abp.MongoDB;
using Volo.Abp.Users.MongoDB;
using Volo.CmsKit.Comments;
using Volo.CmsKit.Contents;
using Volo.CmsKit.MongoDB.Comments;
using Volo.CmsKit.MongoDB.Contents;
using Volo.CmsKit.MongoDB.Pages;
using Volo.CmsKit.MongoDB.Ratings;
using Volo.CmsKit.MongoDB.Reactions;
using Volo.CmsKit.MongoDB.Tags;
using Volo.CmsKit.MongoDB.Users;
using Volo.CmsKit.Pages;
using Volo.CmsKit.Ratings;
using Volo.CmsKit.Reactions;
using Volo.CmsKit.Tags;
using Volo.CmsKit.Users;
namespace Volo.CmsKit.MongoDB
@ -28,6 +34,10 @@ namespace Volo.CmsKit.MongoDB
options.AddRepository<UserReaction, MongoUserReactionRepository>();
options.AddRepository<Comment, MongoCommentRepository>();
options.AddRepository<Rating, MongoRatingRepository>();
options.AddRepository<Content, MongoContentRepository>();
options.AddRepository<Tag, MongoTagRepository>();
options.AddRepository<EntityTag, MongoEntityTagRepository>();
options.AddRepository<Page, MongoPageRepository>();
});
}
}

55
modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Contents/MongoContentRepository.cs

@ -0,0 +1,55 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories.MongoDB;
using Volo.Abp.MongoDB;
using Volo.CmsKit.Contents;
namespace Volo.CmsKit.MongoDB.Contents
{
public class MongoContentRepository : MongoDbRepository<ICmsKitMongoDbContext, Content, Guid>, IContentRepository
{
public MongoContentRepository(IMongoDbContextProvider<ICmsKitMongoDbContext> dbContextProvider) : base(dbContextProvider)
{
}
public Task<Content> GetAsync(
string entityType,
string entityId,
Guid? tenantId = null,
CancellationToken cancellationToken = default)
{
return GetAsync(x =>
!x.IsDeleted &&
x.EntityType == entityType &&
x.EntityId == entityId &&
x.TenantId == tenantId,
cancellationToken: cancellationToken
);
}
public Task<Content> FindAsync(
string entityType,
string entityId,
Guid? tenantId = null,
CancellationToken cancellationToken = default)
{
return FindAsync(x =>
!x.IsDeleted &&
x.EntityType == entityType &&
x.EntityId == entityId &&
x.TenantId == tenantId,
cancellationToken: cancellationToken
);
}
public Task DeleteAsync(string entityType, string entityId, Guid? tenantId = null, CancellationToken cancellationToken = default)
{
return DeleteAsync(x =>
x.EntityType == entityType &&
x.EntityId == entityId &&
x.TenantId == tenantId,
cancellationToken: cancellationToken);
}
}
}

12
modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/ICmsKitMongoDbContext.cs

@ -2,9 +2,13 @@
using Volo.Abp.Data;
using Volo.Abp.MongoDB;
using Volo.CmsKit.Comments;
using Volo.CmsKit.Contents;
using Volo.CmsKit.Pages;
using Volo.CmsKit.Ratings;
using Volo.CmsKit.Reactions;
using Volo.CmsKit.Tags;
using Volo.CmsKit.Users;
using Tag = MongoDB.Driver.Tag;
namespace Volo.CmsKit.MongoDB
{
@ -18,5 +22,13 @@ namespace Volo.CmsKit.MongoDB
IMongoCollection<CmsUser> CmsUsers { get; }
IMongoCollection<Rating> Ratings { get; }
IMongoCollection<Content> Contents { get; }
IMongoCollection<Tag> Tags { get; }
IMongoCollection<EntityTag> EntityTags { get; }
IMongoCollection<Page> Pages { get; }
}
}

31
modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Pages/MongoPageRepository.cs

@ -0,0 +1,31 @@
using System;
using System.Threading.Tasks;
using MongoDB.Driver.Linq;
using Volo.Abp.Domain.Repositories.MongoDB;
using Volo.Abp.MongoDB;
using Volo.CmsKit.Pages;
namespace Volo.CmsKit.MongoDB.Pages
{
public class MongoPageRepository : MongoDbRepository<ICmsKitMongoDbContext, Page, Guid>, IPageRepository
{
public MongoPageRepository(IMongoDbContextProvider<ICmsKitMongoDbContext> dbContextProvider) : base(dbContextProvider)
{
}
public Task<Page> GetByUrlAsync(string url)
{
return GetAsync(x => x.Url == url);
}
public Task<Page> FindByUrlAsync(string url)
{
return FindAsync(x => x.Url == url);
}
public Task<bool> DoesExistAsync(string url)
{
return GetMongoQueryable().AnyAsync(x => x.Url == url);
}
}
}

13
modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Tags/MongoEntityTagRepository.cs

@ -0,0 +1,13 @@
using Volo.Abp.Domain.Repositories.MongoDB;
using Volo.Abp.MongoDB;
using Volo.CmsKit.Tags;
namespace Volo.CmsKit.MongoDB.Tags
{
public class MongoEntityTagRepository: MongoDbRepository<ICmsKitMongoDbContext, EntityTag>, IEntityTagRepository
{
public MongoEntityTagRepository(IMongoDbContextProvider<ICmsKitMongoDbContext> dbContextProvider) : base(dbContextProvider)
{
}
}
}

83
modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Tags/MongoTagRepository.cs

@ -0,0 +1,83 @@
using JetBrains.Annotations;
using MongoDB.Driver;
using MongoDB.Driver.Linq;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories.MongoDB;
using Volo.Abp.MongoDB;
using Volo.CmsKit.Tags;
using Tag = Volo.CmsKit.Tags.Tag;
namespace Volo.CmsKit.MongoDB.Tags
{
public class MongoTagRepository : MongoDbRepository<ICmsKitMongoDbContext, Volo.CmsKit.Tags.Tag, Guid>, ITagRepository
{
public MongoTagRepository(IMongoDbContextProvider<ICmsKitMongoDbContext> dbContextProvider) : base(dbContextProvider)
{
}
public Task<bool> AnyAsync(
[NotNull] string entityType,
[NotNull] string name,
Guid? tenantId = null,
CancellationToken cancellationToken = default)
{
return GetMongoQueryable()
.AnyAsync(x =>
x.EntityType == entityType &&
x.Name == name &&
x.TenantId == tenantId,
cancellationToken);
}
public Task<Tag> GetAsync(
[NotNull] string entityType,
[NotNull] string name,
Guid? tenantId = null,
CancellationToken cancellationToken = default)
{
return GetAsync(x =>
x.EntityType == entityType &&
x.Name == name &&
x.TenantId == tenantId,
cancellationToken: cancellationToken);
}
public Task<Tag> FindAsync(
[NotNull] string entityType,
[NotNull] string name,
Guid? tenantId = null,
CancellationToken cancellationToken = default)
{
return FindAsync(x =>
x.EntityType == entityType &&
x.Name == name &&
x.TenantId == tenantId,
cancellationToken: cancellationToken);
}
public virtual async Task<List<Tag>> GetAllRelatedTagsAsync(
[NotNull] string entityType,
[NotNull] string entityId,
Guid? tenantId = null,
CancellationToken cancellationToken = default)
{
var entityTagIds = await DbContext.EntityTags.AsQueryable()
.Where(q => q.EntityId == entityId && q.TenantId == tenantId)
.Select(q => q.TagId)
.ToListAsync(cancellationToken: GetCancellationToken(cancellationToken));
var query = await Collection.FindAsync(Builders<Tag>.Filter.And(new[]
{
Builders<Tag>.Filter.Eq(x =>x.EntityType, entityType),
Builders<Tag>.Filter.Eq(x =>x.TenantId, tenantId),
Builders<Tag>.Filter.In(x => x.Id, entityTagIds),
}));
var result = await query.ToListAsync(cancellationToken: GetCancellationToken(cancellationToken));
return result;
}
}
}

104
modules/cms-kit/test/Volo.CmsKit.Domain.Tests/Tags/TagManager_Tests.cs

@ -0,0 +1,104 @@
using System;
using System.Threading.Tasks;
using Shouldly;
using Xunit;
namespace Volo.CmsKit.Tags
{
public class TagManager_Tests : CmsKitDomainTestBase
{
private readonly CmsKitTestData _cmsKitTestData;
private readonly TagManager _tagManager;
private readonly ITagRepository _tagRepository;
public TagManager_Tests()
{
_cmsKitTestData = GetRequiredService<CmsKitTestData>();
_tagManager = GetRequiredService<TagManager>();
_tagRepository = GetRequiredService<ITagRepository>();
}
[Fact]
public async Task ShouldAddWhenGettingAsync()
{
var newTagEntityType = "testEntity";
var newTagName = "test_tag_2123";
var doesExist = await _tagRepository.AnyAsync(newTagEntityType, newTagName);
doesExist.ShouldBeFalse();
var newTag = await _tagManager.GetOrAddAsync(newTagEntityType, newTagName);
newTag.ShouldNotBeNull();
var doesExistAgain = await _tagRepository.AnyAsync(newTagEntityType, newTagName);
doesExistAgain.ShouldBeTrue();
}
[Fact]
public async Task ShouldNotAddWhenGettingAsync()
{
var tagName = _cmsKitTestData.Content_1_Tags[0];
var tag = await _tagRepository.GetAsync(_cmsKitTestData.Content_1_EntityType, tagName);
var newTag = await _tagManager.GetOrAddAsync(_cmsKitTestData.Content_1_EntityType, tag.Name);
newTag.ShouldNotBeNull();
newTag.Id.ShouldBe(tag.Id);
}
[Fact]
public async Task ShouldInsert()
{
var tag = await _tagManager.InsertAsync(Guid.NewGuid(), "test", "test");
tag.ShouldNotBeNull();
var doesExist = await _tagRepository.AnyAsync("test", "test");
doesExist.ShouldBeTrue();
}
[Fact]
public async Task ShouldNotInsert()
{
var type = _cmsKitTestData.Content_1_EntityType;
var name = _cmsKitTestData.Content_1_Tags[0];
Should.Throw<Exception>(async () => await _tagManager.InsertAsync(Guid.NewGuid(), type, name));
}
[Fact]
public async Task ShouldUpdate()
{
var type = _cmsKitTestData.Content_1_EntityType;
var name = _cmsKitTestData.Content_1_Tags[0];
var newName = name + "-updated";
var tag = await _tagRepository.GetAsync(type, name);
await _tagManager.UpdateAsync(tag.Id, newName);
var updatedTag = await _tagRepository.GetAsync(type, newName);
updatedTag.Id.ShouldBe(tag.Id);
}
[Fact]
public async Task ShouldNotUpdate()
{
var type = _cmsKitTestData.Content_1_EntityType;
var name = _cmsKitTestData.Content_1_Tags[0];
var newName = _cmsKitTestData.Content_1_Tags[1];
var tag = await _tagRepository.GetAsync(type, name);
Should.Throw<Exception>(async () => await _tagManager.UpdateAsync(tag.Id, newName));
}
}
}

9
modules/cms-kit/test/Volo.CmsKit.EntityFrameworkCore.Tests/EntityFrameworkCore/Contents/ContentRepository_Test.cs

@ -0,0 +1,9 @@
using Volo.CmsKit.Contents;
namespace Volo.CmsKit.EntityFrameworkCore.Contents
{
public class ContentRepository_Test : ContentRepository_Tests<CmsKitEntityFrameworkCoreTestModule>
{
}
}

9
modules/cms-kit/test/Volo.CmsKit.EntityFrameworkCore.Tests/EntityFrameworkCore/Tags/TagRepository_Test.cs

@ -0,0 +1,9 @@
using Volo.CmsKit.Tags;
namespace Volo.CmsKit.EntityFrameworkCore.Tags
{
public class TagRepository_Test : TagRepository_Test<CmsKitEntityFrameworkCoreTestModule>
{
}
}

11
modules/cms-kit/test/Volo.CmsKit.MongoDB.Tests/MongoDB/Contents/ContentRepository_Test.cs

@ -0,0 +1,11 @@
using Volo.CmsKit.Contents;
using Xunit;
namespace Volo.CmsKit.MongoDB.Contents
{
[Collection(MongoTestCollection.Name)]
public class ContentRepository_Test : ContentRepository_Tests<CmsKitMongoDbTestModule>
{
}
}

11
modules/cms-kit/test/Volo.CmsKit.MongoDB.Tests/MongoDB/Tags/TagRepository_Test.cs

@ -0,0 +1,11 @@
using Volo.CmsKit.Tags;
using Xunit;
namespace Volo.CmsKit.MongoDB.Tags
{
[Collection(MongoTestCollection.Name)]
public class TagRepository_Test : TagRepository_Test<CmsKitMongoDbTestModule>
{
}
}

59
modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitDataSeedContributor.cs

@ -1,12 +1,15 @@
using System.Threading.Tasks;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Guids;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Users;
using Volo.CmsKit.Comments;
using Volo.CmsKit.Contents;
using Volo.CmsKit.Ratings;
using Volo.CmsKit.Reactions;
using Volo.CmsKit.Tags;
using Volo.CmsKit.Users;
namespace Volo.CmsKit
@ -20,7 +23,10 @@ namespace Volo.CmsKit
private readonly ReactionManager _reactionManager;
private readonly IRatingRepository _ratingRepository;
private readonly ICurrentTenant _currentTenant;
private readonly IContentRepository _contentRepository;
private readonly IEntityTagRepository _entityTagRepository;
private readonly ITagManager _tagManager;
public CmsKitDataSeedContributor(
IGuidGenerator guidGenerator,
ICmsUserRepository cmsUserRepository,
@ -28,7 +34,10 @@ namespace Volo.CmsKit
ICommentRepository commentRepository,
ReactionManager reactionManager,
IRatingRepository ratingRepository,
ICurrentTenant currentTenant)
ICurrentTenant currentTenant,
IContentRepository contentRepository,
ITagManager tagManager,
IEntityTagRepository entityTagRepository)
{
_guidGenerator = guidGenerator;
_cmsUserRepository = cmsUserRepository;
@ -37,6 +46,9 @@ namespace Volo.CmsKit
_reactionManager = reactionManager;
_ratingRepository = ratingRepository;
_currentTenant = currentTenant;
_contentRepository = contentRepository;
_tagManager = tagManager;
_entityTagRepository = entityTagRepository;
}
public async Task SeedAsync(DataSeedContext context)
@ -50,6 +62,10 @@ namespace Volo.CmsKit
await SeedReactionsAsync();
await SeedRatingsAsync();
await SeedContentsAsync();
await SeedTagsAsync();
}
}
@ -177,5 +193,42 @@ namespace Volo.CmsKit
_cmsKitTestData.User2Id
));
}
private async Task SeedContentsAsync()
{
var content1 = new Content(
_guidGenerator.Create(),
_cmsKitTestData.Content_1_EntityType,
_cmsKitTestData.Content_1_Id,
_cmsKitTestData.Content_1
);
var content2 = new Content(
_guidGenerator.Create(),
_cmsKitTestData.Content_2_EntityType,
_cmsKitTestData.Content_2_Id,
_cmsKitTestData.Content_2
);
await _contentRepository.InsertAsync(content1);
await _contentRepository.InsertAsync(content2);
}
private async Task SeedTagsAsync()
{
foreach (var tag in _cmsKitTestData.Content_1_Tags)
{
var tagEntity = await _tagManager.InsertAsync(_guidGenerator.Create(), _cmsKitTestData.Content_1_EntityType, tag);
await _entityTagRepository.InsertAsync(new EntityTag(tagEntity.Id, _cmsKitTestData.Content_1_Id));
}
foreach (var tag in _cmsKitTestData.Content_2_Tags)
{
var tagEntity = await _tagManager.InsertAsync(_guidGenerator.Create(), _cmsKitTestData.Content_2_EntityType, tag);
await _entityTagRepository.InsertAsync(new EntityTag(tagEntity.Id, _cmsKitTestData.Content_2_Id));
}
}
}
}

17
modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitTestData.cs

@ -1,4 +1,5 @@
using System;
using JetBrains.Annotations;
using Volo.Abp.DependencyInjection;
namespace Volo.CmsKit
@ -18,5 +19,21 @@ namespace Volo.CmsKit
public string EntityId1 { get; } = "1";
public string EntityId2 { get; } = "2";
public string Content_1_EntityType = "Lyrics";
public string Content_1 { get; } = "First things first\nI'ma say all the words inside my head\nI'm fired up and tired of the way that things have been, oh-ooh\nThe way that things have been, oh-ooh";
public string Content_1_Id = "1";
public string[] Content_1_Tags => new string[] {"Imagine Dragons", "Music"};
public string Content_2_EntityType = "LyricsAlso";
public string Content_2 { get; } = "Second thing second\nDon't you tell me what you think that I could be\nI'm the one at the sail, I'm the master of my sea, oh-ooh\nThe master of my sea, oh-ooh";
public string Content_2_Id = "2";
public string[] Content_2_Tags => new string[] {"Imagine Dragons", "Music", "Most Loved Part"};
}
}

63
modules/cms-kit/test/Volo.CmsKit.TestBase/Contents/ContentRepository_Tests.cs

@ -0,0 +1,63 @@
using System;
using System.Threading.Tasks;
using Shouldly;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Modularity;
using Xunit;
namespace Volo.CmsKit.Contents
{
public abstract class ContentRepository_Tests<TStartupModule> : CmsKitTestBase<TStartupModule>
where TStartupModule : IAbpModule
{
private readonly CmsKitTestData _cmsKitTestData;
private readonly IContentRepository _contentRepository;
protected ContentRepository_Tests()
{
_cmsKitTestData = GetRequiredService<CmsKitTestData>();
_contentRepository = GetRequiredService<IContentRepository>();
}
[Theory]
[InlineData("Lyrics", "1", "First things first")]
[InlineData("LyricsAlso", "2", "Second thing second")]
public async Task ShouldGetAsync(string entityType, string entityId, string contentStartsWith = null)
{
var content = await _contentRepository.GetAsync(entityType, entityId);
content.ShouldNotBeNull();
if (contentStartsWith != null)
{
content.Value.ShouldStartWith(contentStartsWith);
}
}
[Fact]
public async Task ShouldNotGetAsync()
{
Should.Throw<EntityNotFoundException>(
async ()
=>
await _contentRepository.GetAsync("not_exist_entity", Guid.NewGuid().ToString()));
}
[Fact]
public async Task ShouldFindAsync()
{
var content =
await _contentRepository.FindAsync(_cmsKitTestData.Content_1_EntityType, _cmsKitTestData.Content_1_Id);
content.ShouldNotBeNull();
}
[Fact]
public async Task ShouldNotFindAsync()
{
var content = await _contentRepository.FindAsync("not_exist_entity", Guid.NewGuid().ToString());
content.ShouldBeNull();
}
}
}

116
modules/cms-kit/test/Volo.CmsKit.TestBase/Tags/TagRepository_Test.cs

@ -0,0 +1,116 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Shouldly;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Modularity;
using Xunit;
namespace Volo.CmsKit.Tags
{
public abstract class TagRepository_Test<TStartupModule> : CmsKitTestBase<TStartupModule>
where TStartupModule : IAbpModule
{
private readonly CmsKitTestData _cmsKitTestData;
private readonly ITagRepository _tagRepository;
protected TagRepository_Test()
{
_cmsKitTestData = GetRequiredService<CmsKitTestData>();
_tagRepository = GetRequiredService<ITagRepository>();
}
[Theory]
[InlineData("Lyrics", "Imagine Dragons")]
[InlineData("Lyrics", "Music")]
[InlineData("LyricsAlso", "Imagine Dragons")]
[InlineData("LyricsAlso", "Music")]
[InlineData("LyricsAlso", "Most Loved Part")]
public async Task ShouldGetAsync(string entityType, string name)
{
var tag = await _tagRepository.GetAsync(entityType, name);
tag.ShouldNotBeNull();
}
[Fact]
public async Task ShouldNotGetAsync()
{
Should.Throw<EntityNotFoundException>(
async () => await _tagRepository.GetAsync("not_exist_entity", Guid.NewGuid().ToString())
);
}
[Theory]
[InlineData("Lyrics", "Imagine Dragons")]
[InlineData("Lyrics", "Music")]
[InlineData("LyricsAlso", "Imagine Dragons")]
[InlineData("LyricsAlso", "Music")]
[InlineData("LyricsAlso", "Most Loved Part")]
public async Task ShouldExistAsync(string entityType, string name)
{
var tag = await _tagRepository.AnyAsync(entityType, name);
tag.ShouldBeTrue();
}
[Fact]
public async Task ShouldNotExistAsync()
{
var tag = await _tagRepository.AnyAsync("not_exist_entity", Guid.NewGuid().ToString());
tag.ShouldBeFalse();
}
[Theory]
[InlineData("Lyrics", "Imagine Dragons")]
[InlineData("Lyrics", "Music")]
[InlineData("LyricsAlso", "Imagine Dragons")]
[InlineData("LyricsAlso", "Music")]
[InlineData("LyricsAlso", "Most Loved Part")]
public async Task ShouldFindAsync(string entityType, string name)
{
var tag = await _tagRepository.FindAsync(entityType, name);
tag.ShouldNotBeNull();
}
[Fact]
public async Task ShouldNotFindAsync()
{
var tag = await _tagRepository.FindAsync("not_exist_entity", Guid.NewGuid().ToString());
tag.ShouldBeNull();
}
[Fact]
public async Task Should_Get_Related_Tags()
{
var content_1_tags = await _tagRepository.GetAllRelatedTagsAsync(_cmsKitTestData.Content_1_EntityType, _cmsKitTestData.EntityId1);
content_1_tags.Count.ShouldBe(_cmsKitTestData.Content_1_Tags.Length);
foreach (var tag in content_1_tags)
{
_cmsKitTestData.Content_1_Tags.Contains(tag.Name).ShouldBeTrue();
}
var content_2_tags = await _tagRepository.GetAllRelatedTagsAsync(_cmsKitTestData.Content_2_EntityType, _cmsKitTestData.EntityId2);
content_2_tags.Count.ShouldBe(_cmsKitTestData.Content_2_Tags.Length);
foreach (var tag in content_2_tags)
{
_cmsKitTestData.Content_2_Tags.Contains(tag.Name).ShouldBeTrue();
}
}
[Fact]
public async Task Should_Not_Get_Related_Tags()
{
var tags = await _tagRepository.GetAllRelatedTagsAsync("not_exist_entity", Guid.NewGuid().ToString());
tags.Count.ShouldBe(0);
}
}
}
Loading…
Cancel
Save