Browse Source

Merge pull request #5216 from abpframework/EngincanV/cmskit-rating

CmsKit Rating Widget
pull/5334/head
Halil İbrahim Kalkan 6 years ago
committed by GitHub
parent
commit
97b56837dc
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/CmsKitWebUnifiedModule.cs
  2. 1235
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20200826063729_CmsRatings_Added.Designer.cs
  3. 39
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20200826063729_CmsRatings_Added.cs
  4. 2429
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/UnifiedDbContextModelSnapshot.cs
  5. 9
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/Pages/Index.cshtml
  6. 37
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/wwwroot/libs/star-rating-svg/css/star-rating-svg.css
  7. 1
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/wwwroot/libs/star-rating-svg/js/jquery.star-rating-svg.min.js
  8. 3
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/GlobalFeatures/GlobalCmsKitFeatures.cs
  9. 18
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/GlobalFeatures/RatingsFeature.cs
  10. 6
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/en.json
  11. 6
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/tr.json
  12. 15
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Ratings/RatingConsts.cs
  13. 25
      modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Ratings/IRatingRepository.cs
  14. 57
      modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Ratings/Rating.cs
  15. 9
      modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Ratings/RatingWithStarCountQueryResultItem.cs
  16. 17
      modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/CmsKitDbContextModelCreatingExtensions.cs
  17. 2
      modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/CmsKitEntityFrameworkCoreModule.cs
  18. 57
      modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/Ratings/EfCoreRatingRepository.cs
  19. 3
      modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/CmsKitMongoDbContext.cs
  20. 6
      modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/CmsKitMongoDbContextExtensions.cs
  21. 3
      modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/CmsKitMongoDbModule.cs
  22. 3
      modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/ICmsKitMongoDbContext.cs
  23. 58
      modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Ratings/MongoRatingRepository.cs
  24. 30
      modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo.CmsKit.Public.Application.Contracts.csproj
  25. 13
      modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Ratings/CreateUpdateRatingInput.cs
  26. 19
      modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Ratings/IRatingPublicAppService.cs
  27. 19
      modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Ratings/RatingDto.cs
  28. 11
      modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Ratings/RatingWithStarCountDto.cs
  29. 32
      modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo.CmsKit.Public.Application.csproj
  30. 4
      modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/PublicApplicationAutoMapperProfile.cs
  31. 104
      modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Ratings/RatingPublicAppService.cs
  32. 32
      modules/cms-kit/src/Volo.CmsKit.Public.HttpApi/Volo.CmsKit.Public.HttpApi.csproj
  33. 51
      modules/cms-kit/src/Volo.CmsKit.Public.HttpApi/Volo/CmsKit/Public/Ratings/RatingPublicController.cs
  34. 6
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Controllers/CmsKitPublicWidgetsController.cs
  35. 46
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Rating/Default.cshtml
  36. 16
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Rating/RatingScriptBundleContributor.cs
  37. 16
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Rating/RatingStyleBundleContributor.cs
  38. 75
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Rating/RatingViewComponent.cs
  39. 44
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Rating/default.css
  40. 87
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Rating/default.js
  41. 137
      modules/cms-kit/test/Volo.CmsKit.Application.Tests/Ratings/RatingPublicAppService_Tests.cs
  42. 9
      modules/cms-kit/test/Volo.CmsKit.EntityFrameworkCore.Tests/EntityFrameworkCore/Ratings/RatingRepository_Tests.cs
  43. 11
      modules/cms-kit/test/Volo.CmsKit.MongoDB.Tests/MongoDB/Ratings/RatingRepository_Tests.cs
  44. 47
      modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitDataSeedContributor.cs
  45. 41
      modules/cms-kit/test/Volo.CmsKit.TestBase/Ratings/RatingRepository_Tests.cs

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

@ -1,4 +1,5 @@
using System.IO;
using System.Linq;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

1235
modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20200826063729_CmsRatings_Added.Designer.cs

File diff suppressed because it is too large

39
modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20200826063729_CmsRatings_Added.cs

@ -0,0 +1,39 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
namespace Volo.CmsKit.Migrations
{
public partial class CmsRatings_Added : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "CmsRatings",
columns: table => new
{
Id = table.Column<Guid>(nullable: false),
TenantId = table.Column<Guid>(nullable: true),
EntityType = table.Column<string>(maxLength: 64, nullable: false),
EntityId = table.Column<string>(maxLength: 64, nullable: false),
StarCount = table.Column<short>(nullable: false),
CreatorId = table.Column<Guid>(nullable: false),
CreationTime = table.Column<DateTime>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_CmsRatings", x => x.Id);
});
migrationBuilder.CreateIndex(
name: "IX_CmsRatings_TenantId_EntityType_EntityId",
table: "CmsRatings",
columns: new[] { "TenantId", "EntityType", "EntityId" });
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "CmsRatings");
}
}
}

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

File diff suppressed because it is too large

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

@ -5,6 +5,7 @@
@using Volo.CmsKit.GlobalFeatures
@using Volo.CmsKit.Pages
@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
@model IndexModel
@inject IStringLocalizer<AbpUiResource> Localizer
@ -41,6 +42,12 @@
</abp-card-body>
</abp-card>
<hr>
@if (GlobalFeatureManager.Instance.IsEnabled<RatingsFeature>())
{
@await Component.InvokeAsync(typeof(RatingViewComponent), new { entityType = "quote", entityId = "2" })
}
<hr>
@if (GlobalFeatureManager.Instance.IsEnabled<ReactionsFeature>())
{
@await Component.InvokeAsync(typeof(ReactionSelectionViewComponent), new { entityType = "quote", entityId = "2" })
@ -49,4 +56,4 @@
{
@await Component.InvokeAsync(typeof(CommentingViewComponent), new { entityType = "quote", entityId = "2" })
}
<hr class="my-5" />
<hr class="my-5" />

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);

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

@ -11,11 +11,14 @@ namespace Volo.CmsKit.GlobalFeatures
public CommentsFeature Comments => GetFeature<CommentsFeature>();
public RatingsFeature Ratings => GetFeature<RatingsFeature>();
public GlobalCmsKitFeatures([NotNull] GlobalFeatureManager featureManager)
: base(featureManager)
{
AddFeature(new ReactionsFeature(this));
AddFeature(new CommentsFeature(this));
AddFeature(new RatingsFeature(this));
}
}
}

18
modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/GlobalFeatures/RatingsFeature.cs

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

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

@ -13,6 +13,10 @@
"LoginToAddComment": "Login to add comment",
"LoginToReply": "Login to reply",
"MessageDeletionConfirmationMessage": "This comment will be deleted completely.",
"CommentAuthorizationExceptionMessage": "Those comments are not allowed for public display."
"CommentAuthorizationExceptionMessage": "Those comments are not allowed for public display.",
"Undo": "Undo",
"RatingUndoMessage": "Your rating will be undo.",
"LoginToRate": "Login to rate",
"Star": "Star"
}
}

6
modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/tr.json

@ -13,6 +13,10 @@
"LoginToAddComment": "Yorum yapmak için giriş yap",
"LoginToReply": "Cevap vermek için giriş yap",
"MessageDeletionConfirmationMessage": "Bu yorum tamamen silinecektir",
"CommentAuthorizationExceptionMessage": "Bu yorumları görebilmek için yetki gerekir."
"CommentAuthorizationExceptionMessage": "Bu yorumları görebilmek için yetki gerekir.",
"Undo": "Geri al",
"RatingUndoMessage": "Oylamanız geri alınacak.",
"LoginToRate": "Oylamak için giriş yapın",
"Star": "Yıldız"
}
}

15
modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Ratings/RatingConsts.cs

@ -0,0 +1,15 @@
using Volo.CmsKit.Entities;
namespace Volo.CmsKit.Ratings
{
public static class RatingConsts
{
public static int MaxEntityTypeLength { get; set; } = CmsEntityConsts.MaxEntityTypeLength;
public static int MaxEntityIdLength { get; set; } = CmsEntityConsts.MaxEntityIdLength;
public static int MaxStarCount { get; set; } = 5;
public static int MinStarCount { get; set; } = 1;
}
}

25
modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Ratings/IRatingRepository.cs

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Volo.Abp.Domain.Repositories;
namespace Volo.CmsKit.Ratings
{
public interface IRatingRepository : IBasicRepository<Rating, Guid>
{
Task<Rating> GetCurrentUserRatingAsync(
[NotNull] string entityType,
[NotNull] string entityId,
Guid userId,
CancellationToken cancellationToken = default
);
Task<List<RatingWithStarCountQueryResultItem>> GetGroupedStarCountsAsync(
[NotNull] string entityType,
[NotNull] string entityId,
CancellationToken cancellationToken = default
);
}
}

57
modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Ratings/Rating.cs

@ -0,0 +1,57 @@
using System;
using JetBrains.Annotations;
using Volo.Abp;
using Volo.Abp.Auditing;
using Volo.Abp.Domain.Entities;
namespace Volo.CmsKit.Ratings
{
public class Rating : BasicAggregateRoot<Guid>, IHasCreationTime, IMustHaveCreator
{
public virtual Guid? TenantId { get; protected set; }
public virtual string EntityType { get; protected set; }
public virtual string EntityId { get; protected set; }
public virtual short StarCount { get; protected set; }
public virtual Guid CreatorId { get; set; }
public virtual DateTime CreationTime { get; set; }
protected Rating()
{
}
public Rating(
Guid id,
[NotNull] string entityType,
[NotNull] string entityId,
short starCount,
Guid creatorId,
Guid? tenantId = null
)
: base(id)
{
EntityType = Check.NotNullOrWhiteSpace(entityType, nameof(entityType), RatingConsts.MaxEntityTypeLength);
EntityId = Check.NotNullOrWhiteSpace(entityId, nameof(entityId), RatingConsts.MaxEntityIdLength);
SetStarCount(starCount);
CreatorId = creatorId;
TenantId = tenantId;
}
public virtual void SetStarCount(short starCount)
{
if(starCount <= RatingConsts.MaxStarCount && starCount >= RatingConsts.MinStarCount)
{
StarCount = starCount;
}
else
{
throw new ArgumentOutOfRangeException($"Choosen star must between {RatingConsts.MinStarCount} and {RatingConsts.MaxStarCount}");
}
}
}
}

9
modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Ratings/RatingWithStarCountQueryResultItem.cs

@ -0,0 +1,9 @@
namespace Volo.CmsKit.Ratings
{
public class RatingWithStarCountQueryResultItem
{
public short StarCount { get; set; }
public int Count { get; set; }
}
}

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

@ -8,6 +8,7 @@ using Volo.CmsKit.Reactions;
using Volo.CmsKit.Users;
using Volo.Abp.Users.EntityFrameworkCore;
using Volo.CmsKit.GlobalFeatures;
using Volo.CmsKit.Ratings;
namespace Volo.CmsKit.EntityFrameworkCore
{
@ -71,6 +72,22 @@ namespace Volo.CmsKit.EntityFrameworkCore
b.HasIndex(x => new { x.TenantId, x.RepliedCommentId });
});
}
if (GlobalFeatureManager.Instance.IsEnabled<RatingsFeature>())
{
builder.Entity<Rating>(r =>
{
r.ToTable(options.TablePrefix + "Ratings", options.Schema);
r.ConfigureByConvention();
r.Property(x => x.StarCount).IsRequired();
r.Property(x => x.EntityType).IsRequired().HasMaxLength(RatingConsts.MaxEntityTypeLength);
r.Property(x => x.EntityId).IsRequired().HasMaxLength(RatingConsts.MaxEntityIdLength);
r.HasIndex(x => new {x.TenantId, x.EntityType, x.EntityId, x.CreatorId});
});
}
}
}
}

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

@ -3,6 +3,7 @@ using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.Modularity;
using Volo.Abp.Users.EntityFrameworkCore;
using Volo.CmsKit.Comments;
using Volo.CmsKit.Ratings;
using Volo.CmsKit.Reactions;
using Volo.CmsKit.Users;
@ -22,6 +23,7 @@ namespace Volo.CmsKit.EntityFrameworkCore
options.AddRepository<CmsUser, EfCoreCmsUserRepository>();
options.AddRepository<UserReaction, EfCoreUserReactionRepository>();
options.AddRepository<Comment, EfCoreCommentRepository>();
options.AddRepository<Rating, EfCoreRatingRepository>();
});
}
}

57
modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/Ratings/EfCoreRatingRepository.cs

@ -0,0 +1,57 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Volo.Abp;
using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
using Volo.CmsKit.EntityFrameworkCore;
namespace Volo.CmsKit.Ratings
{
public class EfCoreRatingRepository : EfCoreRepository<ICmsKitDbContext, Rating, Guid>, IRatingRepository
{
public EfCoreRatingRepository(IDbContextProvider<ICmsKitDbContext> dbContextProvider) : base(dbContextProvider)
{
}
public async Task<Rating> GetCurrentUserRatingAsync(string entityType, string entityId, Guid userId,
CancellationToken cancellationToken = default)
{
Check.NotNullOrWhiteSpace(entityType, nameof(entityType));
Check.NotNullOrWhiteSpace(entityId, nameof(entityId));
var rating = await DbSet.FirstOrDefaultAsync(
r => r.EntityType == entityType && r.EntityId == entityId && r.CreatorId == userId,
GetCancellationToken(cancellationToken));
return rating;
}
public async Task<List<RatingWithStarCountQueryResultItem>> GetGroupedStarCountsAsync(string entityType,
string entityId, CancellationToken cancellationToken = default)
{
Check.NotNullOrWhiteSpace(entityType, nameof(entityType));
Check.NotNullOrWhiteSpace(entityId, nameof(entityId));
var query = (
from rating in DbSet
where rating.EntityType == entityType && rating.EntityId == entityId
group rating by rating.StarCount
into g
select new RatingWithStarCountQueryResultItem
{
StarCount = g.Key,
Count = g.Count()
}
).OrderByDescending(r => r.StarCount);
var ratings = await query.ToListAsync(GetCancellationToken(cancellationToken));
return ratings;
}
}
}

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

@ -2,6 +2,7 @@
using Volo.Abp.Data;
using Volo.Abp.MongoDB;
using Volo.CmsKit.Comments;
using Volo.CmsKit.Ratings;
using Volo.CmsKit.Reactions;
using Volo.CmsKit.Users;
@ -16,6 +17,8 @@ namespace Volo.CmsKit.MongoDB
public IMongoCollection<CmsUser> CmsUsers => Collection<CmsUser>();
public IMongoCollection<Rating> Ratings => Collection<Rating>();
protected override void CreateModel(IMongoModelBuilder modelBuilder)
{
base.CreateModel(modelBuilder);

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

@ -2,6 +2,7 @@
using Volo.Abp;
using Volo.Abp.MongoDB;
using Volo.CmsKit.Comments;
using Volo.CmsKit.Ratings;
using Volo.CmsKit.Reactions;
using Volo.CmsKit.Users;
@ -35,6 +36,11 @@ namespace Volo.CmsKit.MongoDB
{
x.CollectionName = CmsKitDbProperties.DbTablePrefix + "Comments";
});
builder.Entity<Rating>(x =>
{
x.CollectionName = CmsKitDbProperties.DbTablePrefix + "Ratings";
});
}
}
}

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

@ -4,8 +4,10 @@ using Volo.Abp.MongoDB;
using Volo.Abp.Users.MongoDB;
using Volo.CmsKit.Comments;
using Volo.CmsKit.MongoDB.Comments;
using Volo.CmsKit.MongoDB.Ratings;
using Volo.CmsKit.MongoDB.Reactions;
using Volo.CmsKit.MongoDB.Users;
using Volo.CmsKit.Ratings;
using Volo.CmsKit.Reactions;
using Volo.CmsKit.Users;
@ -25,6 +27,7 @@ namespace Volo.CmsKit.MongoDB
options.AddRepository<CmsUser, MongoCmsUserRepository>();
options.AddRepository<UserReaction, MongoUserReactionRepository>();
options.AddRepository<Comment, MongoCommentRepository>();
options.AddRepository<Rating, MongoRatingRepository>();
});
}
}

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

@ -2,6 +2,7 @@
using Volo.Abp.Data;
using Volo.Abp.MongoDB;
using Volo.CmsKit.Comments;
using Volo.CmsKit.Ratings;
using Volo.CmsKit.Reactions;
using Volo.CmsKit.Users;
@ -15,5 +16,7 @@ namespace Volo.CmsKit.MongoDB
IMongoCollection<Comment> Comments { get; }
IMongoCollection<CmsUser> CmsUsers { get; }
IMongoCollection<Rating> Ratings { get; }
}
}

58
modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Ratings/MongoRatingRepository.cs

@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MongoDB.Driver;
using MongoDB.Driver.Linq;
using Volo.Abp;
using Volo.Abp.Domain.Repositories.MongoDB;
using Volo.Abp.MongoDB;
using Volo.CmsKit.Ratings;
namespace Volo.CmsKit.MongoDB.Ratings
{
public class MongoRatingRepository : MongoDbRepository<ICmsKitMongoDbContext, Rating, Guid>, IRatingRepository
{
public MongoRatingRepository(IMongoDbContextProvider<ICmsKitMongoDbContext> dbContextProvider) : base(
dbContextProvider)
{
}
public async Task<Rating> GetCurrentUserRatingAsync(string entityType, string entityId, Guid userId,
CancellationToken cancellationToken = default)
{
Check.NotNullOrWhiteSpace(entityType, nameof(entityType));
Check.NotNullOrWhiteSpace(entityId, nameof(entityId));
var rating = await GetMongoQueryable()
.FirstOrDefaultAsync(r => r.EntityType == entityType && r.EntityId == entityId && r.CreatorId == userId,
GetCancellationToken(cancellationToken));
return rating;
}
public async Task<List<RatingWithStarCountQueryResultItem>> GetGroupedStarCountsAsync(string entityType,
string entityId, CancellationToken cancellationToken = default)
{
Check.NotNullOrWhiteSpace(entityType, nameof(entityType));
Check.NotNullOrWhiteSpace(entityId, nameof(entityId));
var query = (
from rating in GetMongoQueryable()
where rating.EntityType == entityType && rating.EntityId == entityId
group rating by rating.StarCount
into g
select new RatingWithStarCountQueryResultItem
{
StarCount = g.Key,
Count = g.Count()
}
).OrderByDescending(r => r.StarCount);
var ratings = await query.ToListAsync(GetCancellationToken(cancellationToken));
return ratings;
}
}
}

30
modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo.CmsKit.Public.Application.Contracts.csproj

@ -1,15 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\..\common.props" />
<Import Project="..\..\..\..\configureawait.props" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.CmsKit.Common.Application.Contracts\Volo.CmsKit.Common.Application.Contracts.csproj" />
</ItemGroup>
</Project>
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\..\common.props" />
<Import Project="..\..\..\..\configureawait.props" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.CmsKit.Common.Application.Contracts\Volo.CmsKit.Common.Application.Contracts.csproj" />
</ItemGroup>
</Project>

13
modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Ratings/CreateUpdateRatingInput.cs

@ -0,0 +1,13 @@
using System.ComponentModel.DataAnnotations;
using Volo.Abp.Validation;
using Volo.CmsKit.Ratings;
namespace Volo.CmsKit.Public.Ratings
{
public class CreateUpdateRatingInput
{
[Required]
[DynamicRange(typeof(RatingConsts), typeof(int), nameof(RatingConsts.MinStarCount), nameof(RatingConsts.MaxStarCount))]
public short StarCount { get; set; }
}
}

19
modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Ratings/IRatingPublicAppService.cs

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
namespace Volo.CmsKit.Public.Ratings
{
public interface IRatingPublicAppService : IApplicationService
{
Task<RatingDto> CreateAsync(string entityType, string entityId, CreateUpdateRatingInput input);
Task DeleteAsync(string entityType, string entityId);
Task<RatingDto> GetCurrentUserRatingAsync(string entityType, string entityId);
Task<List<RatingWithStarCountDto>> GetGroupedStarCountsAsync(string entityType, string entityId);
}
}

19
modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Ratings/RatingDto.cs

@ -0,0 +1,19 @@
using System;
namespace Volo.CmsKit.Public.Ratings
{
public class RatingDto
{
public Guid Id { get; set; }
public string EntityType { get; set; }
public string EntityId { get; set; }
public short StarCount { get; set; }
public Guid CreatorId { get; set; }
public DateTime CreationTime { get; set; }
}
}

11
modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Ratings/RatingWithStarCountDto.cs

@ -0,0 +1,11 @@
namespace Volo.CmsKit.Public.Ratings
{
public class RatingWithStarCountDto
{
public short StarCount { get; set; }
public int Count { get; set; }
public bool IsSelectedByCurrentUser { get; set; }
}
}

32
modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo.CmsKit.Public.Application.csproj

@ -1,16 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\..\common.props" />
<Import Project="..\..\..\..\configureawait.props" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.CmsKit.Common.Application\Volo.CmsKit.Common.Application.csproj" />
<ProjectReference Include="..\Volo.CmsKit.Public.Application.Contracts\Volo.CmsKit.Public.Application.Contracts.csproj" />
</ItemGroup>
</Project>
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\..\common.props" />
<Import Project="..\..\..\..\configureawait.props" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.CmsKit.Common.Application\Volo.CmsKit.Common.Application.csproj" />
<ProjectReference Include="..\Volo.CmsKit.Public.Application.Contracts\Volo.CmsKit.Public.Application.Contracts.csproj" />
</ItemGroup>
</Project>

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

@ -2,6 +2,8 @@
using Volo.Abp.AutoMapper;
using Volo.CmsKit.Comments;
using Volo.CmsKit.Public.Comments;
using Volo.CmsKit.Public.Ratings;
using Volo.CmsKit.Ratings;
using Volo.CmsKit.Users;
namespace Volo.CmsKit.Public
@ -18,6 +20,8 @@ namespace Volo.CmsKit.Public
CreateMap<Comment, CommentWithDetailsDto>()
.Ignore(x=> x.Replies)
.Ignore(x=> x.Author);
CreateMap<Rating, RatingDto>();
}
}
}

104
modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Ratings/RatingPublicAppService.cs

@ -0,0 +1,104 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Authorization;
using Volo.Abp.Users;
using Volo.CmsKit.Ratings;
using Volo.CmsKit.Users;
namespace Volo.CmsKit.Public.Ratings
{
public class RatingPublicAppService : ApplicationService, IRatingPublicAppService
{
protected IRatingRepository RatingRepository { get; }
public ICmsUserLookupService CmsUserLookupService { get; }
public RatingPublicAppService(IRatingRepository ratingRepository, ICmsUserLookupService cmsUserLookupService)
{
RatingRepository = ratingRepository;
CmsUserLookupService = cmsUserLookupService;
}
[Authorize]
public virtual async Task<RatingDto> CreateAsync(string entityType, string entityId,
CreateUpdateRatingInput input)
{
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<Rating, RatingDto>(updatedRating);
}
var rating = await RatingRepository.InsertAsync(
new Rating(
GuidGenerator.Create(),
entityType,
entityId,
input.StarCount,
user.Id,
CurrentTenant.Id
)
);
return ObjectMapper.Map<Rating, RatingDto>(rating);
}
[Authorize]
public virtual async Task DeleteAsync(string entityType, string entityId)
{
var rating = await RatingRepository.GetCurrentUserRatingAsync(entityType, entityId, CurrentUser.GetId());
if (rating.CreatorId != CurrentUser.GetId())
{
throw new AbpAuthorizationException();
}
await RatingRepository.DeleteAsync(rating.Id);
}
[Authorize]
public virtual async Task<RatingDto> GetCurrentUserRatingAsync(string entityType, string entityId)
{
var currentUserId = CurrentUser.GetId();
var rating = await RatingRepository.GetCurrentUserRatingAsync(entityType, entityId, currentUserId);
return ObjectMapper.Map<Rating, RatingDto>(rating);
}
public virtual async Task<List<RatingWithStarCountDto>> GetGroupedStarCountsAsync(string entityType,
string entityId)
{
var ratings = await RatingRepository.GetGroupedStarCountsAsync(entityType, entityId);
var userRatingOrNull = CurrentUser.IsAuthenticated
? await RatingRepository.GetCurrentUserRatingAsync(entityType, entityId, CurrentUser.GetId())
: null;
var ratingWithStarCountDto = new List<RatingWithStarCountDto>();
foreach (var rating in ratings)
{
ratingWithStarCountDto.Add(
new RatingWithStarCountDto
{
StarCount = rating.StarCount,
Count = rating.Count,
IsSelectedByCurrentUser = userRatingOrNull != null && userRatingOrNull.StarCount == rating.StarCount
});
}
return ratingWithStarCountDto;
}
}
}

32
modules/cms-kit/src/Volo.CmsKit.Public.HttpApi/Volo.CmsKit.Public.HttpApi.csproj

@ -1,16 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\..\common.props" />
<Import Project="..\..\..\..\configureawait.props" />
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.CmsKit.Common.HttpApi\Volo.CmsKit.Common.HttpApi.csproj" />
<ProjectReference Include="..\Volo.CmsKit.Public.Application.Contracts\Volo.CmsKit.Public.Application.Contracts.csproj" />
</ItemGroup>
</Project>
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\..\common.props" />
<Import Project="..\..\..\..\configureawait.props" />
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.CmsKit.Common.HttpApi\Volo.CmsKit.Common.HttpApi.csproj" />
<ProjectReference Include="..\Volo.CmsKit.Public.Application.Contracts\Volo.CmsKit.Public.Application.Contracts.csproj" />
</ItemGroup>
</Project>

51
modules/cms-kit/src/Volo.CmsKit.Public.HttpApi/Volo/CmsKit/Public/Ratings/RatingPublicController.cs

@ -0,0 +1,51 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp;
using Volo.Abp.GlobalFeatures;
using Volo.CmsKit.GlobalFeatures;
namespace Volo.CmsKit.Public.Ratings
{
[RequiresGlobalFeature(typeof(RatingsFeature))]
[RemoteService(Name = CmsKitPublicRemoteServiceConsts.RemoteServiceName)]
[Area("cms-kit")]
[Route("api/cms-kit-public/ratings")]
public class RatingPublicController : CmsKitPublicControllerBase, IRatingPublicAppService
{
protected IRatingPublicAppService RatingPublicAppService { get; }
public RatingPublicController(IRatingPublicAppService ratingPublicAppService)
{
RatingPublicAppService = ratingPublicAppService;
}
[HttpPut]
[Route("{entityType}/{entityId}")]
public virtual Task<RatingDto> CreateAsync(string entityType, string entityId, CreateUpdateRatingInput input)
{
return RatingPublicAppService.CreateAsync(entityType, entityId, input);
}
[HttpDelete]
[Route("{entityType}/{entityId}")]
public virtual Task DeleteAsync(string entityType, string entityId)
{
return RatingPublicAppService.DeleteAsync(entityType, entityId);
}
[HttpGet]
[Route("{entityType}/{entityId}")]
public virtual Task<RatingDto> GetCurrentUserRatingAsync(string entityType, string entityId)
{
return RatingPublicAppService.GetCurrentUserRatingAsync(entityType, entityId);
}
[HttpGet]
[Route("{entityType}/{entityId}")]
public virtual Task<List<RatingWithStarCountDto>> GetGroupedStarCountsAsync(string entityType, string entityId)
{
return RatingPublicAppService.GetGroupedStarCountsAsync(entityType, entityId);
}
}
}

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

@ -1,6 +1,7 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
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;
namespace Volo.CmsKit.Public.Web.Controllers
@ -16,5 +17,10 @@ namespace Volo.CmsKit.Public.Web.Controllers
{
return Task.FromResult((IActionResult)ViewComponent(typeof(CommentingViewComponent), new {entityType, entityId}));
}
public Task<IActionResult> Rating(string entityType, string entityId)
{
return Task.FromResult((IActionResult) ViewComponent(typeof(RatingViewComponent), new {entityType, entityId}));
}
}
}

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

@ -0,0 +1,46 @@
@using Microsoft.AspNetCore.Mvc.Localization
@using Volo.Abp.Users
@using Volo.CmsKit.Localization
@inject ICurrentUser CurrentUser
@model Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Rating.RatingViewModel
@inject IHtmlLocalizer<CmsKitResource> L
<div class="cms-rating-area container" data-entity-type="@Model.EntityType" data-entity-id="@Model.EntityId" id="cms-rating_{@Model.EntityType}_{@Model.EntityId}">
@if (CurrentUser.IsAuthenticated)
{
<div class="m-auto">
<span class="my-rating-5" data-rating="@(Model.CurrentRating ?? 0)" data-authenticated="@(Model.CurrentRating != null)"></span>
<span class="live-rating">@(Model.CurrentRating != null ? Model.CurrentRating + " | " : 0 + "")</span>
@if (Model.CurrentRating != null)
{
<a href="#" class="rating-undo-link m-auto">
<i class="fa fa-undo"></i> @L["Undo"]
</a>
}
</div>
if (Model.Ratings != null)
{
<div class="row text-center col-6 mt-3">
@foreach (var rating in Model.Ratings)
{
<div class="side col-2">
<div>@rating.StarCount @L["Star"]</div>
</div>
<div class="middle col-8">
<div class="bar-container">
<div class="bar bar-@rating.StarCount" style="width: @(rating.Count * 100 / Model.TotalRating)%"></div>
</div>
</div>
<div class="side col-2">
<div>@rating.Count</div>
</div>
}
</div>
}
}
else
{
<span class="my-rating-5" data-authenticated="True" data-toggle="popover" data-placement="right" data-html="true" data-content="<div class='text-center'><a href='@Model.LoginUrl' class='btn btn-primary btn-block'>@L["LoginToRate"]</a></div>"></span>
<span class="rating-login"></span>
}
</div>

16
modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Rating/RatingScriptBundleContributor.cs

@ -0,0 +1,16 @@
using System.Collections.Generic;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.StarRatingSvg;
using Volo.Abp.Modularity;
namespace Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Rating
{
[DependsOn(typeof(StarRatingSvgScriptContributor))]
public class RatingScriptBundleContributor : BundleContributor
{
public override void ConfigureBundle(BundleConfigurationContext context)
{
context.Files.AddIfNotContains("/Pages/CmsKit/Shared/Components/Rating/default.js");
}
}
}

16
modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Rating/RatingStyleBundleContributor.cs

@ -0,0 +1,16 @@
using System.Collections.Generic;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.StarRatingSvg;
using Volo.Abp.Modularity;
namespace Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Rating
{
[DependsOn(typeof(StarRatingSvgStyleContributor))]
public class RatingStyleBundleContributor : BundleContributor
{
public override void ConfigureBundle(BundleConfigurationContext context)
{
context.Files.AddIfNotContains("/Pages/CmsKit/Shared/Components/Rating/default.css");
}
}
}

75
modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Rating/RatingViewComponent.cs

@ -0,0 +1,75 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI;
using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
using Volo.Abp.Users;
using Volo.CmsKit.Public.Ratings;
namespace Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Rating
{
[ViewComponent(Name = "CmsRating")]
[Widget(
StyleTypes = new[] {typeof(RatingStyleBundleContributor)},
ScriptTypes = new[] {typeof(RatingScriptBundleContributor)},
RefreshUrl = "/CmsKitPublicWidgets/Rating"
)]
public class RatingViewComponent : AbpViewComponent
{
public IRatingPublicAppService RatingPublicAppService { get; }
public AbpMvcUiOptions AbpMvcUiOptions { get; }
public ICurrentUser CurrentUser { get; }
public RatingViewComponent(IRatingPublicAppService ratingPublicAppService, IOptions<AbpMvcUiOptions> options, ICurrentUser currentUser)
{
RatingPublicAppService = ratingPublicAppService;
AbpMvcUiOptions = options.Value;
CurrentUser = currentUser;
}
public virtual async Task<IViewComponentResult> InvokeAsync(string entityType, string entityId)
{
var ratings = await RatingPublicAppService.GetGroupedStarCountsAsync(entityType, entityId);
var totalRating = ratings.Sum(x => x.Count);
short? currentUserRating = null;
if (CurrentUser.IsAuthenticated)
{
currentUserRating = ratings.Find(x => x.IsSelectedByCurrentUser)?.StarCount;
}
var loginUrl =
$"{AbpMvcUiOptions.LoginUrl}?returnUrl={HttpContext.Request.Path.ToString()}&returnUrlHash=#cms-rating_{entityType}_{entityId}";
var viewModel = new RatingViewModel
{
EntityId = entityId,
EntityType = entityType,
LoginUrl = loginUrl,
Ratings = ratings,
CurrentRating = currentUserRating,
TotalRating = totalRating
};
return View("~/Pages/CmsKit/Shared/Components/Rating/Default.cshtml", viewModel);
}
}
public class RatingViewModel
{
public string EntityType { get; set; }
public string EntityId { get; set; }
public string LoginUrl { get; set; }
public List<RatingWithStarCountDto> Ratings { get; set; }
public short? CurrentRating { get; set; }
public int TotalRating { get; set; }
}
}

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

@ -0,0 +1,44 @@
.side {
width: 15%;
margin-top:10px;
}
.middle {
float: left;
width: 70%;
margin-top:10px;
padding-top: 4px;
}
.row:after {
content: "";
display: table;
clear: both;
}
.bar-container {
width: 100%;
background-color: #f1f1f1;
text-align: center;
color: white;
}
.bar {
height: 18px;
}
/* Individual bars */
.bar-5 { background-color: #173F5F; }
.bar-4 { background-color: #20639B; }
.bar-3 { background-color: #3CAEA3; }
.bar-2 { background-color: #F6D55C; }
.bar-1 { background-color: #ED553B; }
@media (max-width: 400px) {
.side, .middle {
width: 100%;
}
.right {
display: none;
}
}

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

@ -0,0 +1,87 @@
(function () {
var l = abp.localization.getResource("CmsKit");
$(document).ready(function () {
abp.widgets.CmsRating = function ($widget) {
var widgetManager = $widget.data("abp-widget-manager");
var $ratingArea = $widget.find(".cms-rating-area");
function getFilters() {
return {
entityType: $ratingArea.attr("data-entity-type"),
entityId: $ratingArea.attr("data-entity-id")
};
}
function registerCreateOfNewRating() {
$widget.find(".my-rating-5").each(function () {
var authenticated = $(this).attr("data-authenticated");
$(this).starRating({
initialRating: 0,
disableAfterRate: true,
useFullStars: true,
readOnly: authenticated === "True",
onHover: function (currentIndex, currentRating, $el) {
$widget.find(".live-rating").text(currentIndex);
},
onLeave: function (currentIndex, currentRating, $el) {
$widget.find(".live-rating").text(currentRating);
},
callback: function (currentRating, $el) {
volo.cmsKit.public.ratings.ratingPublic.create(
$ratingArea.attr("data-entity-type"),
$ratingArea.attr("data-entity-id"),
{
starCount: parseInt(currentRating)
}
).then(function () {
widgetManager.refresh($widget);
})
}
});
}
);
}
function registerUndoLink() {
$widget.find(".rating-undo-link").each(function () {
$(this).on('click', '', function (e) {
e.preventDefault();
abp.message.confirm(l("RatingUndoMessage"), function (ok) {
if (ok) {
volo.cmsKit.public.ratings.ratingPublic.delete(
$ratingArea.attr("data-entity-type"),
$ratingArea.attr("data-entity-id")
).then(function () {
widgetManager.refresh($widget);
});
}
})
});
});
}
function init() {
registerCreateOfNewRating();
registerUndoLink();
}
return {
init: init,
getFilters: getFilters
}
};
$('.abp-widget-wrapper[data-widget-name="CmsRating"]')
.each(function () {
var widgetManager = new abp.WidgetManager({
wrapper: $(this),
});
widgetManager.init($(this));
});
});
})
(jQuery);

137
modules/cms-kit/test/Volo.CmsKit.Application.Tests/Ratings/RatingPublicAppService_Tests.cs

@ -0,0 +1,137 @@
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using NSubstitute;
using Shouldly;
using Volo.Abp.Users;
using Volo.CmsKit.Public.Ratings;
using Xunit;
namespace Volo.CmsKit.Ratings
{
public class RatingPublicAppService_Tests : CmsKitApplicationTestBase
{
private readonly IRatingPublicAppService _ratingAppService;
private ICurrentUser _currentUser;
private readonly CmsKitTestData _cmsKitTestData;
public RatingPublicAppService_Tests()
{
_ratingAppService = GetRequiredService<IRatingPublicAppService>();
_cmsKitTestData = GetRequiredService<CmsKitTestData>();
}
protected override void AfterAddApplication(IServiceCollection services)
{
_currentUser = Substitute.For<ICurrentUser>();
services.AddSingleton(_currentUser);
}
[Fact]
public async Task CreateAsync()
{
_currentUser.Id.Returns(_cmsKitTestData.User1Id);
var newRating = await _ratingAppService.CreateAsync(
_cmsKitTestData.EntityType1,
_cmsKitTestData.EntityId2,
new CreateUpdateRatingInput
{
StarCount = 4
});
UsingDbContext(context =>
{
var ratings = context.Set<Rating>().Where(x =>
x.EntityId == _cmsKitTestData.EntityId1 && x.EntityType == _cmsKitTestData.EntityType1).ToList();
ratings
.Any(c => c.Id == newRating.Id && c.CreatorId == newRating.CreatorId &&
c.StarCount == newRating.StarCount)
.ShouldBeTrue();
});
}
[Fact]
public async Task CreateAsync_Should_Update_If_Rating_Is_Exist()
{
_currentUser.Id.Returns(_cmsKitTestData.User1Id);
var entity =
await _ratingAppService.GetCurrentUserRatingAsync(_cmsKitTestData.EntityType1,
_cmsKitTestData.EntityId1);
var updatedEntity = await _ratingAppService.CreateAsync(
_cmsKitTestData.EntityType1,
_cmsKitTestData.EntityId1,
new CreateUpdateRatingInput
{
StarCount = 5
});
entity.Id.ShouldBe(updatedEntity.Id);
entity.EntityId.ShouldBe(updatedEntity.EntityId);
entity.EntityType.ShouldBe(updatedEntity.EntityType);
entity.StarCount.ShouldBe(updatedEntity.StarCount);
}
[Fact]
public async Task GetCurrentUserRatingAsync()
{
_currentUser.Id.Returns(_cmsKitTestData.User1Id);
var rating = await _ratingAppService.GetCurrentUserRatingAsync(
_cmsKitTestData.EntityType1,
_cmsKitTestData.EntityId1
);
UsingDbContext(context =>
{
var ratings = context.Set<Rating>().Where(x =>
x.EntityId == _cmsKitTestData.EntityId1 && x.EntityType == _cmsKitTestData.EntityType1).ToList();
ratings
.Any(c => c.Id == rating.Id && c.EntityId == rating.EntityId && c.EntityType == rating.EntityType)
.ShouldBeTrue();
});
}
[Fact]
public async Task GetGroupedStarCountsAsync()
{
_currentUser.Id.Returns(_cmsKitTestData.User1Id);
var ratings = await _ratingAppService.GetGroupedStarCountsAsync(
_cmsKitTestData.EntityType1,
_cmsKitTestData.EntityId1
);
ratings.ShouldNotBeNull();
ratings.Count.ShouldBeGreaterThan(0);
}
[Fact]
public async Task DeleteAsync()
{
_currentUser.Id.Returns(_cmsKitTestData.User1Id);
var rating = await _ratingAppService.CreateAsync(
_cmsKitTestData.EntityType1,
_cmsKitTestData.EntityId1,
new CreateUpdateRatingInput
{
StarCount = 4
});
await _ratingAppService.DeleteAsync(_cmsKitTestData.EntityType1, _cmsKitTestData.EntityId1);
UsingDbContext(context =>
{
var deletedComment = context.Set<Rating>().FirstOrDefault(x => x.Id == rating.Id);
deletedComment.ShouldBeNull();
});
}
}
}

9
modules/cms-kit/test/Volo.CmsKit.EntityFrameworkCore.Tests/EntityFrameworkCore/Ratings/RatingRepository_Tests.cs

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

11
modules/cms-kit/test/Volo.CmsKit.MongoDB.Tests/MongoDB/Ratings/RatingRepository_Tests.cs

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

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

@ -4,6 +4,7 @@ using Volo.Abp.DependencyInjection;
using Volo.Abp.Guids;
using Volo.Abp.Users;
using Volo.CmsKit.Comments;
using Volo.CmsKit.Ratings;
using Volo.CmsKit.Reactions;
using Volo.CmsKit.Users;
@ -16,19 +17,22 @@ namespace Volo.CmsKit
private readonly CmsKitTestData _cmsKitTestData;
private readonly ICommentRepository _commentRepository;
private readonly ReactionManager _reactionManager;
private readonly IRatingRepository _ratingRepository;
public CmsKitDataSeedContributor(
IGuidGenerator guidGenerator,
ICmsUserRepository cmsUserRepository,
CmsKitTestData cmsKitTestData,
ICommentRepository commentRepository,
ReactionManager reactionManager)
ReactionManager reactionManager,
IRatingRepository ratingRepository)
{
_guidGenerator = guidGenerator;
_cmsUserRepository = cmsUserRepository;
_cmsKitTestData = cmsKitTestData;
_commentRepository = commentRepository;
_reactionManager = reactionManager;
_ratingRepository = ratingRepository;
}
public async Task SeedAsync(DataSeedContext context)
@ -38,13 +42,17 @@ namespace Volo.CmsKit
await SeedCommentsAsync();
await SeedReactionsAsync();
await SeedRatingsAsync();
}
private async Task SeedUsersAsync()
{
await _cmsUserRepository.InsertAsync(new CmsUser(new UserData(_cmsKitTestData.User1Id, "user1", "user1@volo.com",
await _cmsUserRepository.InsertAsync(new CmsUser(new UserData(_cmsKitTestData.User1Id, "user1",
"user1@volo.com",
"user", "1")));
await _cmsUserRepository.InsertAsync(new CmsUser(new UserData(_cmsKitTestData.User2Id, "user2", "user2@volo.com",
await _cmsUserRepository.InsertAsync(new CmsUser(new UserData(_cmsKitTestData.User2Id, "user2",
"user2@volo.com",
"user", "2")));
}
@ -131,5 +139,36 @@ namespace Volo.CmsKit
_cmsKitTestData.EntityId1,
StandardReactions.ThumbsUp);
}
private async Task SeedRatingsAsync()
{
await _ratingRepository.InsertAsync(new Rating(_guidGenerator.Create(),
_cmsKitTestData.EntityType1,
_cmsKitTestData.EntityId1,
4,
_cmsKitTestData.User1Id
));
await _ratingRepository.InsertAsync(new Rating(_guidGenerator.Create(),
_cmsKitTestData.EntityType1,
_cmsKitTestData.EntityId1,
5,
_cmsKitTestData.User1Id
));
await _ratingRepository.InsertAsync(new Rating(_guidGenerator.Create(),
_cmsKitTestData.EntityType2,
_cmsKitTestData.EntityId2,
5,
_cmsKitTestData.User2Id
));
await _ratingRepository.InsertAsync(new Rating(_guidGenerator.Create(),
_cmsKitTestData.EntityType2,
_cmsKitTestData.EntityId2,
1,
_cmsKitTestData.User2Id
));
}
}
}
}

41
modules/cms-kit/test/Volo.CmsKit.TestBase/Ratings/RatingRepository_Tests.cs

@ -0,0 +1,41 @@
using System.Threading.Tasks;
using Shouldly;
using Volo.Abp.Modularity;
using Xunit;
namespace Volo.CmsKit.Ratings
{
public abstract class RatingRepository_Tests<TStartupModule> : CmsKitTestBase<TStartupModule>
where TStartupModule : IAbpModule
{
private readonly CmsKitTestData _cmsKitTestData;
private readonly IRatingRepository _ratingRepository;
public RatingRepository_Tests()
{
_cmsKitTestData = GetRequiredService<CmsKitTestData>();
_ratingRepository = GetRequiredService<IRatingRepository>();
}
[Fact]
public async Task GetCurrentUserRatingAsync()
{
var userRating = await _ratingRepository.GetCurrentUserRatingAsync(_cmsKitTestData.EntityType1,
_cmsKitTestData.EntityId1, _cmsKitTestData.User1Id);
userRating.ShouldNotBeNull();
userRating.EntityId.ShouldBe(_cmsKitTestData.EntityId1);
userRating.EntityType.ShouldBe(_cmsKitTestData.EntityType1);
userRating.CreatorId.ShouldBe(_cmsKitTestData.User1Id);
}
[Fact]
public async Task GetGroupedStarCountsAsync()
{
var list = await _ratingRepository.GetGroupedStarCountsAsync(_cmsKitTestData.EntityType1,
_cmsKitTestData.EntityId1);
list.ShouldNotBeNull();
}
}
}
Loading…
Cancel
Save