diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/CmsKitErrorCodes.cs b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/CmsKitErrorCodes.cs index ad1af878a4..9ef2b9bb86 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/CmsKitErrorCodes.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/CmsKitErrorCodes.cs @@ -13,6 +13,11 @@ public const string SlugAlreadyExist = "CmsKit:Page:0001"; } + public static class Ratings + { + public const string EntityCantHaveRating = "CmsKit:Rating:0001"; + } + public static class Reactions { public const string EntityCantHaveReaction = "CmsKit:Reaction:0001"; diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/en.json b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/en.json index 5638d92fa8..52a9fb07b9 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/en.json +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/en.json @@ -21,6 +21,7 @@ "CmsKit:Media:0001": "'{Name}' is not a valid media name.", "CmsKit:Media:0002": "The entity can't have media.", "CmsKit:Page:0001": "The given url ({0}) already exists.", + "CmsKit:Rating:0001": "The entity {EntityType} can't be rated.", "CmsKit:Reaction:0001": "The entity {EntityType} can't have reactions.", "CmsKit:Tag:0002": "The entity is not taggable!", "CommentAuthorizationExceptionMessage": "Those comments are not allowed for public display.", diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/tr.json b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/tr.json index 73c02088ea..11cf395233 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/tr.json +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/tr.json @@ -13,13 +13,14 @@ "CmsKit.Ratings": "Puanlama", "CmsKit.Reactions": "Tepkiler", "CmsKit.Tags": "Etiketler", - "CmsKit:Comments:0001": "{0} ögesi yorumlanabilir değil.", "CmsKit:0002": "İçerik zaten mevcut!", "CmsKit:0003": "{0} ögesi etiketlenebilir değil.", "CmsKit:BlogPost:0001": "Aynı url etiketi zaten mevcut.", + "CmsKit:Comments:0001": "{0} ögesi yorumlanabilir değil.", "CmsKit:Media:0002": "Bu öge için medya eklenemez.", - "CmsKit:Reaction:0001": "Bu ögeye tepki verilemez.", "CmsKit:Page:0001": "Girilen url ({0}) kullanımdadır.", + "CmsKit:Rating:0001": "{EntityType}, puanlanabilir değil.", + "CmsKit:Reaction:0001": "Bu ögeye tepki verilemez.", "CmsKit:Tag:0002": "Bu öge etiketlenebilir değil.", "CommentAuthorizationExceptionMessage": "Bu yorumları görebilmek için yetki gerekir.", "CommentDeletionConfirmationMessage": "Bu yorum ve buna yapılan tüm yorumlan silinecektir!", diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/CmsKitDomainModule.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/CmsKitDomainModule.cs index e8010d1b3b..53261fc8c2 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/CmsKitDomainModule.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/CmsKitDomainModule.cs @@ -1,19 +1,15 @@ using Volo.Abp.BlobStoring; -using Microsoft.Extensions.Options; -using System.Collections.Generic; -using Volo.Abp; using Volo.Abp.Domain; using Volo.Abp.GlobalFeatures; using Volo.Abp.Localization; using Volo.Abp.Modularity; using Volo.Abp.Users; +using Volo.CmsKit.Blogs; +using Volo.CmsKit.Comments; using Volo.CmsKit.GlobalFeatures; using Volo.CmsKit.Localization; -using Volo.CmsKit.Pages; +using Volo.CmsKit.Ratings; using Volo.CmsKit.Reactions; -using Volo.CmsKit.Tags; -using Volo.CmsKit.Blogs; -using Volo.CmsKit.Comments; namespace Volo.CmsKit { @@ -67,6 +63,19 @@ namespace Volo.CmsKit }); } + if (GlobalFeatureManager.Instance.IsEnabled()) + { + Configure(options => + { + if (GlobalFeatureManager.Instance.IsEnabled()) + { + options.EntityTypes.Add(new RatingEntityTypeDefinition(BlogPostConsts.EntityType)); + } + + // TODO: Define entity types here which can be rated. + }); + } + if (GlobalFeatureManager.Instance.IsEnabled()) { // TODO: Configure TagEntityTypes here... diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Comments/CommentEntityTypeDefinition.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Comments/CommentEntityTypeDefinition.cs index fe0f8ebda3..cc235bda4d 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Comments/CommentEntityTypeDefinition.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Comments/CommentEntityTypeDefinition.cs @@ -4,18 +4,11 @@ using Volo.Abp; namespace Volo.CmsKit.Comments { - public class CommentEntityTypeDefinition : IEquatable + public class CommentEntityTypeDefinition : EntityTypeDefinition { - public CommentEntityTypeDefinition([NotNull] string entityType) + public CommentEntityTypeDefinition([NotNull] string entityType) : base(entityType) { EntityType = Check.NotNullOrEmpty(entityType, nameof(entityType)); } - - public string EntityType { get; } - - public bool Equals(CommentEntityTypeDefinition other) - { - return EntityType == other?.EntityType; - } } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Ratings/CmsKitRatingOptions.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Ratings/CmsKitRatingOptions.cs new file mode 100644 index 0000000000..985e726b3c --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Ratings/CmsKitRatingOptions.cs @@ -0,0 +1,11 @@ +using JetBrains.Annotations; +using System.Collections.Generic; + +namespace Volo.CmsKit.Ratings +{ + public class CmsKitRatingOptions + { + [NotNull] + public List EntityTypes { get; } = new (); + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Ratings/DefaultRatingEntityTypeDefinitionStore.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Ratings/DefaultRatingEntityTypeDefinitionStore.cs new file mode 100644 index 0000000000..c0e8136986 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Ratings/DefaultRatingEntityTypeDefinitionStore.cs @@ -0,0 +1,38 @@ +using JetBrains.Annotations; +using Microsoft.Extensions.Options; +using System; +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp; + +namespace Volo.CmsKit.Ratings +{ + public class DefaultRatingEntityTypeDefinitionStore : IRatingEntityTypeDefinitionStore + { + protected CmsKitRatingOptions Options { get; } + + public DefaultRatingEntityTypeDefinitionStore(IOptions options) + { + Options = options.Value; + } + + public virtual Task GetAsync([NotNull] string entityType) + { + Check.NotNullOrWhiteSpace(entityType, nameof(entityType)); + + var definition = Options.EntityTypes.SingleOrDefault(x => x.EntityType.Equals(entityType, StringComparison.InvariantCultureIgnoreCase)) ?? + throw new EntityCantHaveRatingException(entityType); + + return Task.FromResult(definition); + } + + public virtual Task IsDefinedAsync([NotNull] string entityType) + { + Check.NotNullOrWhiteSpace(entityType, nameof(entityType)); + + var isDefined = Options.EntityTypes.Any(x => x.EntityType.Equals(entityType, StringComparison.InvariantCultureIgnoreCase)); + + return Task.FromResult(isDefined); + } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Ratings/EntityCantHaveRatingException.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Ratings/EntityCantHaveRatingException.cs new file mode 100644 index 0000000000..82616482d7 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Ratings/EntityCantHaveRatingException.cs @@ -0,0 +1,22 @@ +using JetBrains.Annotations; +using System.Runtime.Serialization; +using Volo.Abp; + +namespace Volo.CmsKit.Ratings +{ + public class EntityCantHaveRatingException : BusinessException + { + public EntityCantHaveRatingException(SerializationInfo serializationInfo, StreamingContext context) : base(serializationInfo, context) + { + } + + public EntityCantHaveRatingException([NotNull] string entityType) + { + Code = CmsKitErrorCodes.Ratings.EntityCantHaveRating; + EntityType = Check.NotNullOrEmpty(entityType, nameof(entityType)); + WithData(nameof(EntityType), EntityType); + } + + public string EntityType { get; } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Ratings/IRatingEntityTypeDefinitionStore.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Ratings/IRatingEntityTypeDefinitionStore.cs new file mode 100644 index 0000000000..ad964b272b --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Ratings/IRatingEntityTypeDefinitionStore.cs @@ -0,0 +1,9 @@ +using Volo.CmsKit.Ratings; + +namespace Volo.CmsKit.Ratings +{ + public interface IRatingEntityTypeDefinitionStore : IEntityTypeDefinitionStore + { + + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Ratings/Rating.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Ratings/Rating.cs index a3575cf0b4..dd4b5510a1 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Ratings/Rating.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Ratings/Rating.cs @@ -25,7 +25,7 @@ namespace Volo.CmsKit.Ratings } - public Rating( + internal Rating( Guid id, [NotNull] string entityType, [NotNull] string entityId, diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Ratings/RatingEntityTypeDefinition.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Ratings/RatingEntityTypeDefinition.cs new file mode 100644 index 0000000000..47534f98ab --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Ratings/RatingEntityTypeDefinition.cs @@ -0,0 +1,12 @@ +using JetBrains.Annotations; + +namespace Volo.CmsKit.Ratings +{ + public class RatingEntityTypeDefinition : EntityTypeDefinition + { + public RatingEntityTypeDefinition( + [NotNull] string entityType) : base(entityType) + { + } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Ratings/RatingManager.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Ratings/RatingManager.cs new file mode 100644 index 0000000000..7186089deb --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Ratings/RatingManager.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Volo.Abp.Domain.Services; +using Volo.CmsKit.Users; + +namespace Volo.CmsKit.Ratings +{ + public class RatingManager : DomainService + { + protected IRatingRepository RatingRepository { get; } + protected IRatingEntityTypeDefinitionStore RatingDefinitionStore { get; } + + public RatingManager( + IRatingRepository ratingRepository, + IRatingEntityTypeDefinitionStore ratingDefinitionStore) + { + RatingRepository = ratingRepository; + RatingDefinitionStore = ratingDefinitionStore; + } + + public async Task SetStarAsync(CmsUser user, string entityType, string entityId, short starCount) + { + var currentUserRating = await RatingRepository.GetCurrentUserRatingAsync(entityType, entityId, user.Id); + + if (currentUserRating != null) + { + currentUserRating.SetStarCount(starCount); + + return await RatingRepository.UpdateAsync(currentUserRating); + } + + if (!await RatingDefinitionStore.IsDefinedAsync(entityType)) + { + throw new EntityCantHaveRatingException(entityType); + } + + return await RatingRepository.InsertAsync( + new Rating( + GuidGenerator.Create(), + entityType, + entityId, + starCount, + user.Id, + CurrentTenant.Id + ) + ); + } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/TagEntityTypeDefiniton.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/TagEntityTypeDefiniton.cs index 6dedb1410e..37f9d16d39 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/TagEntityTypeDefiniton.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/TagEntityTypeDefiniton.cs @@ -11,10 +11,6 @@ namespace Volo.CmsKit.Tags [CanBeNull] public virtual ILocalizableString DisplayName { get; } - protected TagEntityTypeDefiniton() - { - } - public TagEntityTypeDefiniton( [NotNull] string entityType, [CanBeNull] ILocalizableString displayName = null, @@ -23,7 +19,6 @@ namespace Volo.CmsKit.Tags IEnumerable deletePolicies = null) : base(entityType, createPolicies, updatePolicies, deletePolicies) { DisplayName = displayName; - } public bool Equals(TagEntityTypeDefiniton other) diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Ratings/RatingPublicAppService.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Ratings/RatingPublicAppService.cs index 4341157763..f3a9925d72 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Ratings/RatingPublicAppService.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Ratings/RatingPublicAppService.cs @@ -13,11 +13,16 @@ namespace Volo.CmsKit.Public.Ratings { protected IRatingRepository RatingRepository { get; } public ICmsUserLookupService CmsUserLookupService { get; } + protected RatingManager RatingManager { get; } - public RatingPublicAppService(IRatingRepository ratingRepository, ICmsUserLookupService cmsUserLookupService) + public RatingPublicAppService( + IRatingRepository ratingRepository, + ICmsUserLookupService cmsUserLookupService, + RatingManager ratingManager) { RatingRepository = ratingRepository; CmsUserLookupService = cmsUserLookupService; + RatingManager = ratingManager; } [Authorize] @@ -27,26 +32,7 @@ namespace Volo.CmsKit.Public.Ratings var userId = CurrentUser.GetId(); var user = await CmsUserLookupService.GetByIdAsync(userId); - var currentUserRating = await RatingRepository.GetCurrentUserRatingAsync(entityType, entityId, userId); - - if (currentUserRating != null) - { - currentUserRating.SetStarCount(input.StarCount); - var updatedRating = await RatingRepository.UpdateAsync(currentUserRating); - - return ObjectMapper.Map(updatedRating); - } - - var rating = await RatingRepository.InsertAsync( - new Rating( - GuidGenerator.Create(), - entityType, - entityId, - input.StarCount, - user.Id, - CurrentTenant.Id - ) - ); + var rating = await RatingManager.SetStarAsync(user, entityType, entityId, input.StarCount); return ObjectMapper.Map(rating); } diff --git a/modules/cms-kit/test/Volo.CmsKit.Domain.Tests/Ratings/RatingManager_Test.cs b/modules/cms-kit/test/Volo.CmsKit.Domain.Tests/Ratings/RatingManager_Test.cs new file mode 100644 index 0000000000..43ea0f073c --- /dev/null +++ b/modules/cms-kit/test/Volo.CmsKit.Domain.Tests/Ratings/RatingManager_Test.cs @@ -0,0 +1,66 @@ +using Shouldly; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Volo.CmsKit.Blogs; +using Volo.CmsKit.Users; +using Xunit; + +namespace Volo.CmsKit.Ratings +{ + public class RatingManager_Test : CmsKitDomainTestBase + { + private readonly CmsKitTestData _cmsKitTestData; + private readonly RatingManager _ratingManager; + private readonly ICmsUserRepository _userRepository; + + public RatingManager_Test() + { + _cmsKitTestData = GetRequiredService(); + _ratingManager = GetRequiredService(); + _userRepository = GetRequiredService(); + } + + [Fact] + public async Task SetStarAsync_ShouldCreate_WhenFirstCall() + { + var user = await _userRepository.GetAsync(_cmsKitTestData.User1Id); + short starCount = 4; + + var rating = await _ratingManager.SetStarAsync(user, BlogPostConsts.EntityType, _cmsKitTestData.BlogPost_1_Id.ToString(), starCount); + + rating.ShouldNotBeNull(); + rating.Id.ShouldNotBe(Guid.Empty); + rating.StarCount.ShouldBe(starCount); + } + + [Fact] + public async Task SetStarAsync_ShouldUpdate_WithExistingRating() + { + var user = await _userRepository.GetAsync(_cmsKitTestData.User1Id); + short starCount = 2; + + var rating = await _ratingManager.SetStarAsync(user, _cmsKitTestData.EntityType1, _cmsKitTestData.EntityId1, starCount); + + rating.ShouldNotBeNull(); + rating.Id.ShouldNotBe(Guid.Empty); + rating.StarCount.ShouldBe(starCount); + } + + [Fact] + public async Task SetStarAsync_ShouldThrowException_WithNotConfiguredentityType() + { + var user = await _userRepository.GetAsync(_cmsKitTestData.User1Id); + var notConfiguredEntityType = "AnyOtherEntityType"; + short starCount = 3; + + var exception = await Should.ThrowAsync(async () => + await _ratingManager.SetStarAsync(user, notConfiguredEntityType, "1", starCount)); + + exception.ShouldNotBeNull(); + exception.EntityType.ShouldBe(notConfiguredEntityType); + } + } +} diff --git a/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitDataSeedContributor.cs b/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitDataSeedContributor.cs index 7971972227..02f10de384 100644 --- a/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitDataSeedContributor.cs +++ b/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitDataSeedContributor.cs @@ -46,6 +46,7 @@ namespace Volo.CmsKit private readonly BlogManager _blogManager; private readonly IOptions _mediaOptions; private readonly IOptions _commentsOptions; + private readonly IOptions _ratingOptions; public CmsKitDataSeedContributor( IGuidGenerator guidGenerator, @@ -70,7 +71,8 @@ namespace Volo.CmsKit IBlobContainer mediaBlobContainer, BlogManager blogManager, IOptions cmsMediaOptions, - IOptions commentsOptions) + IOptions commentsOptions, + IOptions ratingOptions) { _guidGenerator = guidGenerator; _cmsUserRepository = cmsUserRepository; @@ -95,6 +97,7 @@ namespace Volo.CmsKit _blogManager = blogManager; _mediaOptions = cmsMediaOptions; _commentsOptions = commentsOptions; + this._ratingOptions = ratingOptions; } public async Task SeedAsync(DataSeedContext context) @@ -159,6 +162,9 @@ namespace Volo.CmsKit _reactionOptions.Value.EntityTypes.Add(new ReactionEntityTypeDefinition(_cmsKitTestData.EntityType1, reactions)); _reactionOptions.Value.EntityTypes.Add(new ReactionEntityTypeDefinition(_cmsKitTestData.EntityType2, reactions)); + _ratingOptions.Value.EntityTypes.Add(new RatingEntityTypeDefinition(_cmsKitTestData.EntityType1)); + _ratingOptions.Value.EntityTypes.Add(new RatingEntityTypeDefinition(_cmsKitTestData.EntityType2)); + return Task.CompletedTask; }