Merge branch 'dev' into stsrki/dev-blazor-server

pull/8074/head
Halil İbrahim Kalkan 5 years ago
commit 09b0e2cdf4

@ -17,7 +17,7 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-dotnet@master
with:
dotnet-version: 5.0.100
dotnet-version: 5.0.201
- name: Build All
run: .\build-all.ps1

@ -207,7 +207,7 @@
"DeleteCoverImageConfirmationMessage": "Are you sure you want to delete the cover image for \"{0}\"?",
"DeleteCoverImageSuccessMessage": "Cover image successfully deleted",
"PaymentsOf": "Payments of",
"ShowPaymentsOfOrganization": "Show payments",
"ShowPaymentsOfOrganization": "Payments",
"Date": "Date",
"Products": "Products",
"TotalAmount": "Total amount",
@ -234,6 +234,19 @@
"Permission:CliAnalyticses": "Cli Analyticses",
"Permission:CliAnalytics": "Cli Analytics",
"Search": "Search",
"ClearFilter": "Clear filter"
"ClearFilter": "Clear filter",
"LicensePrivateKey": "License private key",
"LicensePublicKey": "License public key",
"ApiKey": "NuGet API key",
"ShowInvoiceRequestsOfOrganization": "Invoice requests",
"ShowQuestionsOfOrganization": "Questions",
"Question": "Question",
"Open": "Open",
"Questions": "Questions",
"InvoiceRequests": "Invoice Requests",
"Address": "Address",
"TaxNo": "Tax No",
"Permission:InvoiceRequest": "Invoice Request",
"Permission:Question": "Question"
}
}

@ -182,6 +182,20 @@
"Permission:CliAnalyticses": "Cli Analitikleri",
"Permission:CliAnalytics": "Cli Analitik",
"Search": "Ara",
"ClearFilter": "Filtreyi temizle"
"ShowPaymentsOfOrganization": "Ödemeler",
"ClearFilter": "Filtreyi temizle",
"LicensePrivateKey": "Lisans özel anahtar",
"LicensePublicKey": "Lisans genel key",
"ApiKey": "NuGet API anahatar",
"ShowInvoiceRequestsOfOrganization": "Fatura talepleri",
"ShowQuestionsOfOrganization": "Sorular",
"Question": "Soru",
"Open": "Aç",
"Questions": "Sorular",
"InvoiceRequests": "Fatura talepleri",
"Address": "Adres",
"TaxNo": "Vergi no",
"Permission:InvoiceRequest": "Fatura Talebi",
"Permission:Question": "Soru"
}
}

@ -217,6 +217,21 @@
"FailReason": "失败原因",
"ReIndexAllPosts": "重新索引所有帖子",
"ReIndexAllPostsConfirmationMessage": "你确定要重新索引所有的帖子吗",
"SuccessfullyReIndexAllPosts": "成功索引所有的帖子"
"SuccessfullyReIndexAllPosts": "成功索引所有的帖子",
"ClearFilter": "清除过滤",
"LicensePrivateKey": "许可私有密钥",
"LicensePublicKey": "许可公有密钥",
"ApiKey": "NuGet API密钥",
"ShowInvoiceRequestsOfOrganization": "发票请求",
"ShowQuestionsOfOrganization": "问题",
"Question": "问题",
"Open": "打开",
"Questions": "问题",
"InvoiceRequests": "发票请求",
"Address": "地址",
"TaxNo": "税号",
"Permission:InvoiceRequest": "发票请求",
"Permission:Question": "问题"
}
}

@ -108,12 +108,12 @@ namespace Volo.Abp.AspNetCore.Mvc
return localizer;
}
protected RedirectResult RedirectSafely(string returnUrl, string returnUrlHash = null)
protected virtual RedirectResult RedirectSafely(string returnUrl, string returnUrlHash = null)
{
return Redirect(GetRedirectUrl(returnUrl, returnUrlHash));
}
private string GetRedirectUrl(string returnUrl, string returnUrlHash = null)
protected virtual string GetRedirectUrl(string returnUrl, string returnUrlHash = null)
{
returnUrl = NormalizeReturnUrl(returnUrl);
@ -125,7 +125,7 @@ namespace Volo.Abp.AspNetCore.Mvc
return returnUrl;
}
private string NormalizeReturnUrl(string returnUrl)
protected virtual string NormalizeReturnUrl(string returnUrl)
{
if (returnUrl.IsNullOrEmpty())
{

@ -23,20 +23,8 @@ namespace Volo.Abp.AspNetCore.Mvc.Authentication
{
return RedirectSafely(returnUrl, returnUrlHash);
}
else
{
return Challenge(
new AuthenticationProperties
{
Parameters =
{
{"returnUrl", returnUrl},
{"returnUrlHash", returnUrlHash}
}
},
ChallengeAuthenticationSchemas
);
}
return Challenge(new AuthenticationProperties {RedirectUri = GetRedirectUrl(returnUrl, returnUrlHash)}, ChallengeAuthenticationSchemas);
}
[HttpGet]
@ -44,20 +32,20 @@ namespace Volo.Abp.AspNetCore.Mvc.Authentication
{
await HttpContext.SignOutAsync();
if (HttpContext.User.Identity.AuthenticationType == AuthenticationType)
if (HttpContext.User.Identity?.AuthenticationType == AuthenticationType)
{
return RedirectSafely(returnUrl, returnUrlHash);
}
return new SignOutResult(ChallengeAuthenticationSchemas);
return SignOut(new AuthenticationProperties {RedirectUri = GetRedirectUrl(returnUrl, returnUrlHash)}, ChallengeAuthenticationSchemas);
}
[HttpGet]
public async Task<IActionResult> FrontChannelLogout(string sid)
{
if (User.Identity.IsAuthenticated)
if (User.Identity != null && User.Identity.IsAuthenticated)
{
var currentSid = User.FindFirst("sid").Value ?? string.Empty;
var currentSid = User.FindFirst("sid")?.Value ?? string.Empty;
if (string.Equals(currentSid, sid, StringComparison.Ordinal))
{
await Logout();

@ -1,6 +1,6 @@
{
"sdk": {
"version": "5.0.100",
"version": "5.0.201",
"rollForward": "latestFeature"
}
}

@ -39,6 +39,11 @@ using Volo.Abp.Threading;
using Volo.Abp.VirtualFileSystem;
using Volo.CmsKit.Admin.Web;
using Volo.CmsKit.Public.Web;
using System;
using Volo.CmsKit.Tags;
using Volo.CmsKit.Comments;
using Volo.CmsKit.MediaDescriptors;
using Volo.CmsKit.Reactions;
namespace Volo.CmsKit
{
@ -81,6 +86,8 @@ namespace Volo.CmsKit
var hostingEnvironment = context.Services.GetHostingEnvironment();
var configuration = context.Services.GetConfiguration();
ConfigureCmsKit(context);
Configure<AbpDbContextOptions>(options =>
{
options.UseSqlServer();
@ -131,6 +138,35 @@ namespace Volo.CmsKit
});
}
private void ConfigureCmsKit(ServiceConfigurationContext context)
{
Configure<CmsKitTagOptions>(options =>
{
options.EntityTypes.Add(new TagEntityTypeDefiniton("quote"));
});
Configure<CmsKitCommentOptions>(options =>
{
options.EntityTypes.Add(new CommentEntityTypeDefinition("quote"));
});
Configure<CmsKitMediaOptions>(options =>
{
options.EntityTypes.Add(new MediaDescriptorDefinition("quote"));
});
Configure<CmsKitReactionOptions>(options =>
{
options.EntityTypes.Add(
new ReactionEntityTypeDefinition("quote",
reactions: new[]
{
new ReactionDefinition(StandardReactions.ThumbsUp),
new ReactionDefinition(StandardReactions.ThumbsDown),
}));
});
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var app = context.GetApplicationBuilder();

@ -43,24 +43,6 @@
</abp-card>
<hr />
<h3>Media added content editor</h3>
<abp-card>
<abp-card-body>
@if (CurrentUser.Id != null)
{
@if (GlobalFeatureManager.Instance.IsEnabled<MediaFeature>())
{
<div id="content-editor"></div>
}
}
else
{
<span>Please Login</span>
}
</abp-card-body>
</abp-card>
<abp-row>
<abp-column size-md="_6">

@ -0,0 +1,12 @@
using System;
using Volo.Abp.Application.Dtos;
namespace Volo.CmsKit.Admin.Blogs
{
public class BlogPostGetListInput : PagedAndSortedResultRequestDto
{
public string Filter { get; set; }
public Guid? BlogId { get; set; }
}
}

@ -1,8 +1,5 @@
using System;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Content;
namespace Volo.CmsKit.Admin.Blogs
{
@ -10,7 +7,7 @@ namespace Volo.CmsKit.Admin.Blogs
: ICrudAppService<
BlogPostDto,
Guid,
PagedAndSortedResultRequestDto,
BlogPostGetListInput,
CreateBlogPostDto,
UpdateBlogPostDto>
{

@ -1,7 +1,6 @@
using System;
using System.ComponentModel.DataAnnotations;
using Volo.Abp.Validation;
using Volo.CmsKit.Contents;
using Volo.CmsKit.Pages;
namespace Volo.CmsKit.Admin.Pages

@ -1,7 +1,6 @@
using System;
using System.ComponentModel.DataAnnotations;
using Volo.Abp.Validation;
using Volo.CmsKit.Contents;
using Volo.CmsKit.Pages;
namespace Volo.CmsKit.Admin.Pages

@ -16,13 +16,18 @@ namespace Volo.CmsKit.Admin.Blogs
{
protected IBlogRepository BlogRepository { get; }
protected BlogManager BlogManager { get; }
public BlogAdminAppService(IBlogRepository blogRepository, BlogManager blogManager)
protected BlogFeatureManager BlogFeatureManager{ get; }
public BlogAdminAppService(
IBlogRepository blogRepository,
BlogManager blogManager,
BlogFeatureManager blogFeatureManager = null)
{
BlogRepository = blogRepository;
BlogManager = blogManager;
BlogFeatureManager = blogFeatureManager;
}
public virtual async Task<BlogDto> GetAsync(Guid id)
{
var blog = await BlogRepository.GetAsync(id);
@ -48,7 +53,9 @@ namespace Volo.CmsKit.Admin.Blogs
{
var blog = await BlogManager.CreateAsync(input.Name, input.Slug);
await BlogRepository.InsertAsync(blog);
await BlogRepository.InsertAsync(blog, autoSave: true);
await BlogFeatureManager.SetDefaultsAsync(blog.Id);
return ObjectMapper.Map<Blog, BlogDto>(blog);
}

@ -1,12 +1,9 @@
using Microsoft.AspNetCore.Authorization;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.GlobalFeatures;
using Volo.CmsKit.Admin.Blogs;
using Volo.CmsKit.Blogs;
using Volo.CmsKit.GlobalFeatures;
using Volo.CmsKit.Permissions;
@ -14,6 +11,7 @@ using Volo.CmsKit.Permissions;
namespace Volo.CmsKit.Admin.Blogs
{
[RequiresGlobalFeature(typeof(BlogsFeature))]
[Authorize(CmsKitAdminPermissions.Blogs.Features)]
public class BlogFeatureAdminAppService : CmsKitAdminAppServiceBase, IBlogFeatureAdminAppService
{
protected IBlogFeatureRepository BlogFeatureRepository { get; }
@ -32,15 +30,13 @@ namespace Volo.CmsKit.Admin.Blogs
EventBus = eventBus;
}
[Authorize(CmsKitAdminPermissions.Blogs.Features)]
public virtual async Task<List<BlogFeatureDto>> GetListAsync(Guid blogId)
{
var blogFeatures = await BlogFeatureManager.GetListAsync(blogId);
var blogFeatures = await BlogFeatureRepository.GetListAsync(blogId);
return ObjectMapper.Map<List<BlogFeature>, List<BlogFeatureDto>>(blogFeatures);
}
[Authorize(CmsKitAdminPermissions.Blogs.Features)]
public virtual Task SetAsync(Guid blogId, BlogFeatureInputDto dto)
{
return BlogFeatureManager.SetAsync(blogId, dto.FeatureName, dto.IsEnabled);

@ -3,10 +3,6 @@ using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.BlobStoring;
using Volo.Abp.Content;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.GlobalFeatures;
using Volo.Abp.Users;
using Volo.CmsKit.Blogs;
@ -87,20 +83,15 @@ namespace Volo.CmsKit.Admin.Blogs
}
[Authorize(CmsKitAdminPermissions.BlogPosts.Default)]
public virtual async Task<PagedResultDto<BlogPostDto>> GetListAsync(PagedAndSortedResultRequestDto input)
public virtual async Task<PagedResultDto<BlogPostDto>> GetListAsync(BlogPostGetListInput input)
{
if (input.Sorting.IsNullOrEmpty())
{
input.Sorting = nameof(BlogPost.CreationTime) + " desc";
}
var blogPosts = await BlogPostRepository.GetPagedListAsync(input.SkipCount, input.MaxResultCount, input.Sorting, includeDetails: true);
var blogPosts = await BlogPostRepository.GetListAsync(input.Filter, input.BlogId, input.MaxResultCount, input.SkipCount, input.Sorting);
var count = await BlogPostRepository.GetCountAsync();
var count = await BlogPostRepository.GetCountAsync(input.Filter);
return new PagedResultDto<BlogPostDto>(
count,
ObjectMapper.Map<List<BlogPost>, List<BlogPostDto>>(blogPosts));
var dtoList = ObjectMapper.Map<List<BlogPost>, List<BlogPostDto>>(blogPosts);
return new PagedResultDto<BlogPostDto>(count, dtoList);
}
[Authorize(CmsKitAdminPermissions.BlogPosts.Delete)]

@ -33,6 +33,7 @@ namespace Volo.CmsKit.Admin.MediaDescriptors
{
var definition = await MediaDescriptorDefinitionStore.GetDefinitionAsync(inputStream.EntityType);
/* TODO: Shouldn't CreatePolicies be a dictionary and we check for inputStream.EntityType? */
await CheckAnyOfPoliciesAsync(definition.CreatePolicies);
using (var stream = inputStream.GetStream())
@ -52,6 +53,7 @@ namespace Volo.CmsKit.Admin.MediaDescriptors
var definition = await MediaDescriptorDefinitionStore.GetDefinitionAsync(mediaDescriptor.EntityType);
/* TODO: Shouldn't DeletePolicies be a dictionary and we check for inputStream.EntityType? */
await CheckAnyOfPoliciesAsync(definition.DeletePolicies);
await MediaContainer.DeleteAsync(id.ToString());

@ -16,10 +16,15 @@ namespace Volo.CmsKit.Admin.Pages
public class PageAdminAppService : CmsKitAdminAppServiceBase, IPageAdminAppService
{
protected IPageRepository PageRepository { get; }
public PageAdminAppService(IPageRepository pageRepository)
protected PageManager PageManager { get; }
public PageAdminAppService(
IPageRepository pageRepository,
PageManager pageManager)
{
PageRepository = pageRepository;
PageManager = pageManager;
}
public virtual async Task<PageDto> GetAsync(Guid id)
@ -48,9 +53,7 @@ namespace Volo.CmsKit.Admin.Pages
[Authorize(CmsKitAdminPermissions.Pages.Create)]
public virtual async Task<PageDto> CreateAsync(CreatePageInputDto input)
{
await CheckPageSlugAsync(input.Slug);
var page = new Page(GuidGenerator.Create(), input.Title, input.Slug, input.Content, CurrentTenant.Id);
var page = await PageManager.CreateAsync(input.Title, input.Slug, input.Content);
await PageRepository.InsertAsync(page);
@ -62,13 +65,9 @@ namespace Volo.CmsKit.Admin.Pages
{
var page = await PageRepository.GetAsync(id);
if (page.Slug != input.Slug)
{
await CheckPageSlugAsync(input.Slug);
}
await PageManager.SetSlugAsync(page, input.Slug);
page.SetTitle(input.Title);
page.SetSlug(input.Slug);
page.SetContent(input.Content);
await PageRepository.UpdateAsync(page);
@ -81,13 +80,5 @@ namespace Volo.CmsKit.Admin.Pages
{
await PageRepository.DeleteAsync(id);
}
protected virtual async Task CheckPageSlugAsync(string slug)
{
if (await PageRepository.ExistsAsync(slug))
{
throw new PageSlugAlreadyExistsException(slug);
}
}
}
}

@ -49,7 +49,7 @@ namespace Volo.CmsKit.Admin.Blogs
[HttpGet]
[Authorize(CmsKitAdminPermissions.BlogPosts.Default)]
public virtual Task<PagedResultDto<BlogPostDto>> GetListAsync(PagedAndSortedResultRequestDto input)
public virtual Task<PagedResultDto<BlogPostDto>> GetListAsync(BlogPostGetListInput input)
{
return BlogPostAdminAppService.GetListAsync(input);
}

@ -1,5 +1,6 @@
@page
@using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Pages.Shared.Components.AbpPageSearchBox
@using Volo.CmsKit.Admin.Web.Pages.CmsKit.BlogPosts
@using Volo.CmsKit.Admin.Web.Pages
@using Volo.CmsKit.Admin.Web.Menus
@ -27,7 +28,13 @@
<div id="CmsKitBlogPostsWrapper">
<abp-card>
<abp-card-body>
<abp-table striped-rows="true" id="BlogPostsTable" class="nowrap"></abp-table>
<abp-row>
<abp-column>
@await Component.InvokeAsync(typeof(AbpPageSearchBoxViewComponent))
</abp-column>
</abp-row>
</abp-card-body>
</abp-card>
<abp-table striped-rows="true" id="BlogPostsTable" class="nowrap"></abp-table>
</div>

@ -4,8 +4,12 @@ $(function () {
var blogsService = volo.cmsKit.admin.blogs.blogPostAdmin;
var $blogPostWrapper = $('#CmsKitBlogPostsWrapper');
var getFilter = function () {
return {
filter: $('#CmsKitBlogPostsWrapper input.page-search-filter-text').val()
};
};
var dataTable = $("#BlogPostsTable").DataTable(abp.libs.datatables.normalizeConfiguration({
processing: true,
serverSide: true,
@ -16,7 +20,7 @@ $(function () {
scrollX: true,
ordering: true,
order: [[1, "desc"]],
ajax: abp.libs.datatables.createAjax(blogsService.getList),
ajax: abp.libs.datatables.createAjax(blogsService.getList, getFilter),
columnDefs: [
{
title: l("Details"),
@ -61,6 +65,11 @@ $(function () {
]
}));
$('#CmsKitBlogPostsWrapper form.page-search-form').submit(function (e) {
e.preventDefault();
dataTable.ajax.reload();
});
$('#AbpContentToolbar button[name=CreateBlogPost]').on('click', function (e) {
e.preventDefault();
window.location.href = "BlogPosts/Create"

@ -1,7 +1,5 @@
using AutoMapper;
using Volo.CmsKit.Blogs;
using Volo.CmsKit.Contents;
using Volo.CmsKit.Pages;
using Volo.CmsKit.Tags;
using Volo.CmsKit.Users;

@ -8,13 +8,16 @@
public const string EntityNotTaggable = "CmsKit:Tag:0002";
}
public const string ContentAlreadyExist = "CmsKit:0002";
public static class Pages
{
public const string SlugAlreadyExist = "CmsKit:Page:0001";
}
public static class Reactions
{
public const string EntityCantHaveReaction = "CmsKit:Reaction:0001";
}
public static class Blogs
{
public const string SlugAlreadyExists = "CmsKit:Blog:0001";

@ -4,6 +4,7 @@ namespace Volo.CmsKit.Comments
{
public static class CommentConsts
{
public const string EntityType = "Comment";
public static int MaxEntityTypeLength { get; set; } = CmsEntityConsts.MaxEntityTypeLength;
public static int MaxEntityIdLength { get; set; } = CmsEntityConsts.MaxEntityIdLength;

@ -1,15 +0,0 @@
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 { get; set; } = int.MaxValue;
}
}

@ -17,10 +17,11 @@
"CmsKit:0003": "The entity {0} is not taggable.",
"CmsKit:Blog:0001": "The given slug ({Slug}) already exists!",
"CmsKit:BlogPost:0001": "The given slug already exists!",
"CmsKit:Comments:0001": "The entity {0} is not commentable.",
"CmsKit:Comments:0001": "The entity {EntityType} is not commentable.",
"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: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.",
"CommentDeletionConfirmationMessage": "This comment and all replies will be deleted!",

@ -18,6 +18,7 @@
"CmsKit:0003": "{0} ögesi etiketlenebilir değil.",
"CmsKit:BlogPost:0001": "Aynı url etiketi zaten mevcut.",
"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:Tag:0002": "Bu öge etiketlenebilir değil.",
"CommentAuthorizationExceptionMessage": "Bu yorumları görebilmek için yetki gerekir.",

@ -3,7 +3,6 @@ using System;
using Volo.Abp;
using Volo.Abp.Domain.Entities.Auditing;
using Volo.Abp.MultiTenancy;
using Volo.CmsKit.Blogs.Extensions;
namespace Volo.CmsKit.Blogs
{
@ -17,7 +16,12 @@ namespace Volo.CmsKit.Blogs
public virtual Guid? TenantId { get; protected set; }
protected internal Blog(Guid id, [NotNull] string name, [NotNull] string slug, [CanBeNull] Guid? tenantId = null) : base(id)
protected internal Blog(
Guid id,
[NotNull] string name,
[NotNull] string slug,
[CanBeNull] Guid? tenantId = null
) : base(id)
{
SetName(name);
SetSlug(slug);
@ -33,7 +37,7 @@ namespace Volo.CmsKit.Blogs
{
Check.NotNullOrWhiteSpace(slug, nameof(slug), BlogConsts.MaxNameLength);
Slug = slug.NormalizeSlug();
Slug = SlugNormalizer.Normalize(slug);
}
}
}

@ -5,13 +5,13 @@ using Volo.Abp.Domain.Entities.Auditing;
namespace Volo.CmsKit.Blogs
{
public class BlogFeature : FullAuditedAggregateRoot<Guid>, IEquatable<BlogFeature>
public class BlogFeature : FullAuditedAggregateRoot<Guid>
{
public Guid BlogId { get; protected set; }
public string FeatureName { get; protected set; }
public bool IsEnabled { get; set; } = true;
public bool IsEnabled { get; protected internal set; }
public BlogFeature(Guid blogId, [NotNull] string featureName, bool isEnabled = true)
{
@ -19,10 +19,5 @@ namespace Volo.CmsKit.Blogs
FeatureName = Check.NotNullOrWhiteSpace(featureName, nameof(featureName));
IsEnabled = isEnabled;
}
public bool Equals(BlogFeature other)
{
return BlogId == other?.BlogId && FeatureName == other?.FeatureName;
}
}
}

@ -1,10 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.Domain.Services;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.EventBus.Local;
using Volo.Abp.Uow;
namespace Volo.CmsKit.Blogs
{
@ -22,17 +20,6 @@ namespace Volo.CmsKit.Blogs
DefaultBlogFeatureProvider = defaultBlogFeatureProvider;
}
public async Task<List<BlogFeature>> GetListAsync(Guid blogId)
{
var blogFeatures = await BlogFeatureRepository.GetListAsync(blogId);
var defaultFeatures = await DefaultBlogFeatureProvider.GetDefaultFeaturesAsync(blogId);
defaultFeatures.ForEach(x => blogFeatures.AddIfNotContains(x));
return blogFeatures;
}
public async Task SetAsync(Guid blogId, string featureName, bool isEnabled)
{
var blogFeature = await BlogFeatureRepository.FindAsync(blogId, featureName);
@ -47,5 +34,15 @@ namespace Volo.CmsKit.Blogs
await BlogFeatureRepository.UpdateAsync(blogFeature);
}
}
public async Task SetDefaultsAsync(Guid blogId)
{
var defaultFeatures = await DefaultBlogFeatureProvider.GetDefaultFeaturesAsync(blogId);
foreach (var feature in defaultFeatures)
{
await SetAsync(blogId, feature.FeatureName, isEnabled: true);
}
}
}
}

@ -3,7 +3,6 @@ using System;
using Volo.Abp;
using Volo.Abp.Domain.Entities.Auditing;
using Volo.Abp.MultiTenancy;
using Volo.CmsKit.Blogs.Extensions;
using Volo.CmsKit.Users;
namespace Volo.CmsKit.Blogs
@ -65,7 +64,7 @@ namespace Volo.CmsKit.Blogs
{
Check.NotNullOrWhiteSpace(slug, nameof(slug), BlogPostConsts.MaxSlugLength, BlogPostConsts.MinSlugLength);
Slug = slug.NormalizeSlug();
Slug = SlugNormalizer.Normalize(slug);
}
public virtual void SetShortDescription(string shortDescription)

@ -12,13 +12,17 @@ namespace Volo.CmsKit.Blogs
{
protected IBlogPostRepository BlogPostRepository { get; }
protected IBlogRepository BlogRepository { get; }
protected IDefaultBlogFeatureProvider BlogFeatureProvider { get; }
public BlogPostManager(
IBlogPostRepository blogPostRepository,
IBlogRepository blogRepository)
IBlogRepository blogRepository,
IDefaultBlogFeatureProvider blogFeatureProvider)
{
BlogPostRepository = blogPostRepository;
BlogRepository = blogRepository;
BlogFeatureProvider = blogFeatureProvider;
}
public virtual async Task<BlogPost> CreateAsync(
@ -35,8 +39,6 @@ namespace Volo.CmsKit.Blogs
Check.NotNullOrEmpty(title, nameof(title));
Check.NotNullOrEmpty(slug, nameof(slug));
await CheckBlogExistenceAsync(blog.Id);
var blogPost = new BlogPost(
GuidGenerator.Create(),
blog.Id,

@ -6,11 +6,8 @@ namespace Volo.CmsKit.Blogs
{
public class BlogPostSlugAlreadyExistException : BusinessException
{
internal BlogPostSlugAlreadyExistException(string code = null, string message = null, string details = null, Exception innerException = null, Microsoft.Extensions.Logging.LogLevel logLevel = Microsoft.Extensions.Logging.LogLevel.Warning) : base(code, message, details, innerException, logLevel)
{
}
internal BlogPostSlugAlreadyExistException(SerializationInfo serializationInfo, System.Runtime.Serialization.StreamingContext context) : base(serializationInfo, context)
public BlogPostSlugAlreadyExistException(SerializationInfo serializationInfo, StreamingContext context)
: base(serializationInfo, context)
{
}

@ -8,14 +8,13 @@ namespace Volo.CmsKit.Blogs
public class BlogSlugAlreadyExistException : BusinessException
{
public BlogSlugAlreadyExistException(string slug)
: base(code: CmsKitErrorCodes.Blogs.SlugAlreadyExists)
{
Code = CmsKitErrorCodes.Blogs.SlugAlreadyExists;
WithData(nameof(Blog.Slug), slug);
}
public BlogSlugAlreadyExistException(
SerializationInfo serializationInfo, StreamingContext context) : base(serializationInfo, context)
public BlogSlugAlreadyExistException(SerializationInfo serializationInfo, StreamingContext context)
: base(serializationInfo, context)
{
}
}

@ -8,12 +8,21 @@ namespace Volo.CmsKit.Blogs
{
public interface IBlogPostRepository : IBasicRepository<BlogPost, Guid>
{
Task<int> GetCountAsync(
string filter = null,
Guid? blogId = null,
CancellationToken cancellationToken = default);
Task<List<BlogPost>> GetListAsync(
string filter = null,
Guid? blogId = null,
int maxResultCount = int.MaxValue,
int skipCount = 0,
string sorting = null,
CancellationToken cancellationToken = default);
Task<bool> SlugExistsAsync(Guid blogId, string slug, CancellationToken cancellationToken = default);
Task<BlogPost> GetBySlugAsync(Guid blogId, string slug, CancellationToken cancellationToken = default);
Task<List<BlogPost>> GetPagedListAsync(Guid blogId, int skipCount, int maxResultCount, string sorting, bool includeDetails = false, CancellationToken cancellationToken = default);
Task<int> GetCountAsync(Guid blogId, CancellationToken cancellationToken = default);
}
}

@ -1,5 +1,7 @@
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;
@ -10,6 +12,8 @@ using Volo.CmsKit.Localization;
using Volo.CmsKit.Pages;
using Volo.CmsKit.Reactions;
using Volo.CmsKit.Tags;
using Volo.CmsKit.Blogs;
using Volo.CmsKit.Comments;
namespace Volo.CmsKit
{
@ -23,22 +27,45 @@ namespace Volo.CmsKit
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<CmsKitOptions>(options =>
if (GlobalFeatureManager.Instance.IsEnabled<ReactionsFeature>())
{
options.Reactions.AddOrReplace(StandardReactions.ThumbsUp);
options.Reactions.AddOrReplace(StandardReactions.ThumbsDown);
options.Reactions.AddOrReplace(StandardReactions.Smile);
options.Reactions.AddOrReplace(StandardReactions.Wink);
options.Reactions.AddOrReplace(StandardReactions.Confused);
options.Reactions.AddOrReplace(StandardReactions.Victory);
options.Reactions.AddOrReplace(StandardReactions.Rock);
options.Reactions.AddOrReplace(StandardReactions.Eyes);
options.Reactions.AddOrReplace(StandardReactions.Heart);
options.Reactions.AddOrReplace(StandardReactions.HeartBroken);
options.Reactions.AddOrReplace(StandardReactions.Rocket);
options.Reactions.AddOrReplace(StandardReactions.Pray);
Configure<CmsKitReactionOptions>(options =>
{
if (GlobalFeatureManager.Instance.IsEnabled<BlogsFeature>())
{
options.EntityTypes.Add(
new ReactionEntityTypeDefinition(
BlogPostConsts.EntityType,
reactions: new[]
{
new ReactionDefinition(StandardReactions.Smile),
new ReactionDefinition(StandardReactions.ThumbsUp),
new ReactionDefinition(StandardReactions.ThumbsDown),
new ReactionDefinition(StandardReactions.Confused),
new ReactionDefinition(StandardReactions.Eyes),
new ReactionDefinition(StandardReactions.Heart),
new ReactionDefinition(StandardReactions.HeartBroken),
new ReactionDefinition(StandardReactions.Wink),
new ReactionDefinition(StandardReactions.Pray),
new ReactionDefinition(StandardReactions.Rocket),
new ReactionDefinition(StandardReactions.Victory),
new ReactionDefinition(StandardReactions.Rock),
}));
}
});
if (GlobalFeatureManager.Instance.IsEnabled<CommentsFeature>())
{
options.EntityTypes.Add(
new ReactionEntityTypeDefinition(
CommentConsts.EntityType,
reactions: new[]
{
new ReactionDefinition(StandardReactions.ThumbsUp),
new ReactionDefinition(StandardReactions.ThumbsDown),
}));
}
});
}
if (GlobalFeatureManager.Instance.IsEnabled<TagsFeature>())
{

@ -1,12 +0,0 @@
using JetBrains.Annotations;
using Volo.CmsKit.Reactions;
using Volo.CmsKit.Tags;
namespace Volo.CmsKit
{
public class CmsKitOptions
{
[NotNull]
public ReactionDefinitionDictionary Reactions { get; } = new ReactionDefinitionDictionary();
}
}

@ -29,17 +29,20 @@ namespace Volo.CmsKit.MediaDescriptors
{
Check.NotNullOrWhiteSpace(entityType, nameof(entityType));
var result = Options.EntityTypes.SingleOrDefault(x => x.EntityType.Equals(entityType, StringComparison.InvariantCultureIgnoreCase)) ??
throw new EntityCantHaveMediaException(entityType);
var definition = Options.EntityTypes.SingleOrDefault(
x => x.EntityType.Equals(entityType, StringComparison.InvariantCultureIgnoreCase)
) ?? throw new EntityCantHaveMediaException(entityType);
return Task.FromResult(result);
return Task.FromResult(definition);
}
public virtual Task<bool> IsDefinedAsync([NotNull] string entityType)
{
Check.NotNullOrEmpty(entityType, nameof(entityType));
var isDefined = Options.EntityTypes.Any(a => a.EntityType.Equals(entityType, StringComparison.InvariantCultureIgnoreCase));
var isDefined = Options.EntityTypes.Any(
a => a.EntityType.Equals(entityType, StringComparison.InvariantCultureIgnoreCase)
);
return Task.FromResult(isDefined);
}

@ -9,10 +9,10 @@ namespace Volo.CmsKit.MediaDescriptors
{
}
public EntityCantHaveMediaException(string entityType)
public EntityCantHaveMediaException(string entityType)
: base(code: CmsKitErrorCodes.MediaDescriptors.EntityTypeDoesntExist)
{
EntityType = entityType;
Code = CmsKitErrorCodes.MediaDescriptors.EntityTypeDoesntExist;
WithData(nameof(entityType), EntityType);
}

@ -20,7 +20,7 @@ namespace Volo.CmsKit.Pages
{
}
public Page(Guid id, [NotNull] string title, [NotNull] string slug, string content = null, Guid? tenantId = null) : base(id)
internal Page(Guid id, [NotNull] string title, [NotNull] string slug, string content = null, Guid? tenantId = null) : base(id)
{
TenantId = tenantId;
@ -34,7 +34,7 @@ namespace Volo.CmsKit.Pages
Title = Check.NotNullOrEmpty(title, nameof(title), PageConsts.MaxTitleLength);
}
public virtual void SetSlug(string slug)
internal virtual void SetSlug(string slug)
{
Slug = Check.NotNullOrEmpty(slug, nameof(slug), PageConsts.MaxSlugLength);
}

@ -0,0 +1,56 @@
using JetBrains.Annotations;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Domain.Services;
namespace Volo.CmsKit.Pages
{
public class PageManager : DomainService
{
protected IPageRepository PageRepository { get; }
public PageManager(IPageRepository pageRepository)
{
PageRepository = pageRepository;
}
public virtual async Task<Page> CreateAsync(
[NotNull] string title,
[NotNull] string slug,
[CanBeNull] string content = null)
{
Check.NotNullOrEmpty(title, nameof(title));
Check.NotNullOrEmpty(slug, nameof(slug));
await CheckPageSlugAsync(slug);
return new Page(
GuidGenerator.Create(),
title,
slug,
content,
CurrentTenant.Id);
}
public virtual async Task SetSlugAsync(Page page, string newSlug)
{
if (page.Slug != newSlug)
{
await CheckPageSlugAsync(newSlug);
page.SetSlug(newSlug);
}
}
protected virtual async Task CheckPageSlugAsync(string slug)
{
if (await PageRepository.ExistsAsync(slug))
{
throw new PageSlugAlreadyExistsException(slug);
}
}
}
}

@ -0,0 +1,11 @@
using JetBrains.Annotations;
using System.Collections.Generic;
namespace Volo.CmsKit.Reactions
{
public class CmsKitReactionOptions
{
[NotNull]
public List<ReactionEntityTypeDefinition> EntityTypes { get; } = new ();
}
}

@ -1,28 +1,59 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.Extensions.Options;
using Volo.Abp;
using Volo.Abp.DependencyInjection;
namespace Volo.CmsKit.Reactions
{
public class DefaultReactionDefinitionStore : IReactionDefinitionStore, ITransientDependency
{
protected CmsKitOptions Options { get; }
protected CmsKitReactionOptions Options { get; }
public DefaultReactionDefinitionStore(IOptions<CmsKitOptions> options)
public DefaultReactionDefinitionStore(IOptions<CmsKitReactionOptions> options)
{
Options = options.Value;
}
public virtual Task<List<ReactionDefinition>> GetReactionsAsync(string entityType = null)
public virtual async Task<List<ReactionDefinition>> GetReactionsAsync([NotNull] string entityType)
{
return Task.FromResult(Options.Reactions.Values.ToList());
Check.NotNullOrEmpty(entityType, nameof(entityType));
var definition = await GetAsync(entityType);
return definition.Reactions;
}
public virtual async Task<ReactionDefinition> GetReactionOrNullAsync([NotNull] string reactionName, [NotNull] string entityType)
{
Check.NotNullOrEmpty(entityType, nameof(entityType));
Check.NotNullOrEmpty(reactionName, nameof(reactionName));
var definition = await GetAsync(entityType);
return definition.Reactions.SingleOrDefault(x => x.Name == reactionName);
}
public virtual Task<ReactionDefinition> GetReactionOrNullAsync(string reactionName, string entityType = null)
public virtual Task<bool> IsDefinedAsync([NotNull] string entityType)
{
return Task.FromResult(Options.Reactions.GetOrDefault(reactionName));
Check.NotNullOrWhiteSpace(entityType, nameof(entityType));
var isDefined = Options.EntityTypes.Any(x => x.EntityType.Equals(entityType, StringComparison.InvariantCultureIgnoreCase));
return Task.FromResult(isDefined);
}
public virtual Task<ReactionEntityTypeDefinition> GetAsync([NotNull] string entityType)
{
Check.NotNullOrWhiteSpace(entityType, nameof(entityType));
var definition = Options.EntityTypes.SingleOrDefault(x => x.EntityType.Equals(entityType, StringComparison.InvariantCultureIgnoreCase)) ??
throw new EntityCantHaveReactionException(entityType);
return Task.FromResult(definition);
}
}
}

@ -0,0 +1,23 @@

using JetBrains.Annotations;
using System.Runtime.Serialization;
using Volo.Abp;
namespace Volo.CmsKit.Reactions
{
public class EntityCantHaveReactionException : BusinessException
{
public EntityCantHaveReactionException(SerializationInfo serializationInfo, StreamingContext context) : base(serializationInfo, context)
{
}
public EntityCantHaveReactionException([NotNull] string entityType)
{
EntityType = Check.NotNullOrEmpty(entityType, nameof(entityType));
Code = CmsKitErrorCodes.Reactions.EntityCantHaveReaction;
WithData(nameof(EntityType), EntityType);
}
public string EntityType { get; }
}
}

@ -6,8 +6,12 @@ namespace Volo.CmsKit.Reactions
{
public interface IReactionDefinitionStore
{
Task<List<ReactionDefinition>> GetReactionsAsync([CanBeNull] string entityType = null);
Task<bool> IsDefinedAsync([NotNull]string entityType);
Task<ReactionDefinition> GetReactionOrNullAsync([NotNull] string reactionName, [CanBeNull] string entityType = null);
Task<ReactionEntityTypeDefinition> GetAsync([NotNull] string entityType);
Task<List<ReactionDefinition>> GetReactionsAsync([NotNull] string entityType);
Task<ReactionDefinition> GetReactionOrNullAsync([NotNull] string reactionName, [NotNull] string entityType);
}
}

@ -1,4 +1,5 @@
using JetBrains.Annotations;
using System;
using Volo.Abp;
using Volo.Abp.Localization;

@ -1,14 +0,0 @@
using System.Collections.Generic;
using JetBrains.Annotations;
using Volo.Abp.Localization;
namespace Volo.CmsKit.Reactions
{
public class ReactionDefinitionDictionary : Dictionary<string, ReactionDefinition>
{
public void AddOrReplace([NotNull] string name, ILocalizableString displayName = null)
{
this[name] = new ReactionDefinition(name, displayName);
}
}
}

@ -0,0 +1,26 @@
using JetBrains.Annotations;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp;
namespace Volo.CmsKit.Reactions
{
public class ReactionEntityTypeDefinition : PolicySpecifiedDefinition
{
[NotNull]
public List<ReactionDefinition> Reactions { get; } = new();
public ReactionEntityTypeDefinition(
[NotNull] string entityType,
[NotNull] IEnumerable<ReactionDefinition> reactions,
IEnumerable<string> createPolicies = null,
IEnumerable<string> updatePolicies = null,
IEnumerable<string> deletePolicies = null) : base(entityType, createPolicies, updatePolicies, deletePolicies)
{
Reactions = Check.NotNull(reactions, nameof(reactions)).ToList();
}
}
}

@ -22,8 +22,10 @@ namespace Volo.CmsKit.Reactions
}
public virtual async Task<List<ReactionDefinition>> GetReactionsAsync(
[CanBeNull] string entityType = null)
[NotNull] string entityType)
{
Check.NotNullOrEmpty(entityType, nameof(entityType));
return await ReactionDefinitionStore.GetReactionsAsync(entityType);
}
@ -51,7 +53,7 @@ namespace Volo.CmsKit.Reactions
.ToList();
}
public virtual async Task<UserReaction> CreateAsync(
public virtual async Task<UserReaction> GetOrCreateAsync(
Guid creatorId,
[NotNull] string entityType,
[NotNull] string entityId,
@ -67,6 +69,11 @@ namespace Volo.CmsKit.Reactions
return existingReaction;
}
if (!await ReactionDefinitionStore.IsDefinedAsync(entityType))
{
throw new EntityCantHaveReactionException(entityType);
}
return await UserReactionRepository.InsertAsync(
new UserReaction(
GuidGenerator.Create(),

@ -1,12 +1,12 @@
using Slugify;
using Unidecode.NET;
namespace Volo.CmsKit.Blogs.Extensions
namespace Volo.CmsKit
{
public static class SlugExtensions
public static class SlugNormalizer
{
static readonly SlugHelper SlugHelper = new ();
public static string NormalizeSlug(this string value)
public static string Normalize(string value)
{
return SlugHelper.GenerateSlug(value?.Unidecode());
}

@ -33,48 +33,58 @@ namespace Volo.CmsKit.Blogs
blogPost.Author = await (await GetDbContextAsync())
.Set<CmsUser>()
.FirstOrDefaultAsync(x =>x.Id == blogPost.AuthorId);
.FirstOrDefaultAsync(x =>x.Id == blogPost.AuthorId, GetCancellationToken(cancellationToken));
return blogPost;
}
public async Task<int> GetCountAsync(Guid blogId, CancellationToken cancellationToken = default)
public virtual async Task<int> GetCountAsync(
string filter = null,
Guid? blogId = null,
CancellationToken cancellationToken = default)
{
return await (await GetQueryableAsync()).CountAsync(
x => x.BlogId == blogId,
GetCancellationToken(cancellationToken));
var queryable = (await GetDbSetAsync())
.WhereIf(blogId.HasValue, x => x.BlogId == blogId)
.WhereIf(!string.IsNullOrEmpty(filter), x => x.Title.Contains(filter) || x.Slug.Contains(filter));
var count = await queryable.CountAsync(GetCancellationToken(cancellationToken));
return count;
}
public async Task<List<BlogPost>> GetPagedListAsync(Guid blogId, int skipCount, int maxResultCount,
string sorting, bool includeDetails = false, CancellationToken cancellationToken = default)
public virtual async Task<List<BlogPost>> GetListAsync(
string filter = null,
Guid? blogId = null,
int maxResultCount = int.MaxValue,
int skipCount = 0,
string sorting = null,
CancellationToken cancellationToken = default)
{
var dbContext = await GetDbContextAsync();
var blogPostsDbSet = dbContext.Set<BlogPost>();
var usersDbSet = dbContext.Set<CmsUser>();
var queryable = blogPostsDbSet
.Where(x => x.BlogId == blogId);
if (!sorting.IsNullOrWhiteSpace())
{
queryable = queryable.OrderBy(sorting);
}
.WhereIf(blogId.HasValue, x => x.BlogId == blogId)
.WhereIf(!string.IsNullOrWhiteSpace(filter), x => x.Title.Contains(filter) || x.Slug.Contains(filter));
queryable = queryable.OrderBy(sorting ?? $"{nameof(BlogPost.CreationTime)} desc");
var combinedResult = await queryable
.Join(
usersDbSet,
o => o.AuthorId,
i => i.Id,
(blogPost,user) => new { blogPost, user })
.Skip(skipCount)
.Take(maxResultCount)
.ToListAsync(GetCancellationToken(cancellationToken));
.Join(
usersDbSet,
o => o.AuthorId,
i => i.Id,
(blogPost,user) => new { blogPost, user })
.Skip(skipCount)
.Take(maxResultCount)
.ToListAsync(GetCancellationToken(cancellationToken));
return combinedResult.Select(s =>
{
s.blogPost.Author = s.user;
return s.blogPost;
}).ToList();
{
s.blogPost.Author = s.user;
return s.blogPost;
}).ToList();
}
public async Task<bool> SlugExistsAsync(Guid blogId, [NotNull] string slug,

@ -4,7 +4,6 @@ using Volo.Abp.Modularity;
using Volo.Abp.Users.EntityFrameworkCore;
using Volo.CmsKit.Blogs;
using Volo.CmsKit.Comments;
using Volo.CmsKit.Contents;
using Volo.CmsKit.MediaDescriptors;
using Volo.CmsKit.Pages;
using Volo.CmsKit.Ratings;

@ -27,41 +27,51 @@ namespace Volo.CmsKit.MongoDB.Blogs
{
Check.NotNullOrEmpty(slug, nameof(slug));
var token = GetCancellationToken(cancellationToken);
var blogPost = await GetAsync(x =>
x.BlogId == blogId &&
x.Slug.ToLower() == slug,
cancellationToken: GetCancellationToken(cancellationToken));
cancellationToken: token);
var dbContext = await GetDbContextAsync();
var dbContext = await GetDbContextAsync(token);
blogPost.Author = await dbContext.Collection<CmsUser>().AsQueryable().FirstOrDefaultAsync(x => x.Id == blogPost.AuthorId);
blogPost.Author = await dbContext.Collection<CmsUser>().AsQueryable().FirstOrDefaultAsync(x => x.Id == blogPost.AuthorId, token);
return blogPost;
}
public async Task<int> GetCountAsync(Guid blogId, CancellationToken cancellationToken = default)
public async Task<int> GetCountAsync(
string filter = null,
Guid? blogId = null,
CancellationToken cancellationToken = default)
{
return await AsyncExecuter.CountAsync(
await WithDetailsAsync(),
x => x.BlogId == blogId,
GetCancellationToken(cancellationToken));
var token = GetCancellationToken(cancellationToken);
return await (await GetMongoQueryableAsync(token))
.WhereIf<BlogPost, IMongoQueryable<BlogPost>>(!string.IsNullOrWhiteSpace(filter), x => x.Title.Contains(filter) || x.Slug.Contains(filter))
.WhereIf<BlogPost, IMongoQueryable<BlogPost>>(blogId.HasValue, x => x.BlogId == blogId)
.CountAsync(GetCancellationToken(cancellationToken));
}
public async Task<List<BlogPost>> GetPagedListAsync(Guid blogId, int skipCount, int maxResultCount,
string sorting, bool includeDetails = false, CancellationToken cancellationToken = default)
public async Task<List<BlogPost>> GetListAsync(
string filter = null,
Guid? blogId = null,
int maxResultCount = int.MaxValue,
int skipCount = 0,
string sorting = null,
CancellationToken cancellationToken = default)
{
var token = GetCancellationToken(cancellationToken);
var dbContext = await GetDbContextAsync(token);
var blogPostQueryable = dbContext.Collection<BlogPost>().AsQueryable();
var usersQueryable = dbContext.Collection<CmsUser>().AsQueryable();
IQueryable<BlogPost> queryable = blogPostQueryable
.Where(x => x.BlogId == blogId);
var queryable = blogPostQueryable
.WhereIf(blogId.HasValue, x => x.BlogId == blogId)
.WhereIf(!string.IsNullOrWhiteSpace(filter), x => x.Title.Contains(filter) || x.Slug.Contains(filter));
if (!sorting.IsNullOrWhiteSpace())
{
queryable = queryable.OrderBy(sorting);
}
queryable = queryable.OrderBy(sorting ?? $"{nameof(BlogPost.CreationTime)} desc");
var combinedQueryable = queryable
.Join(

@ -1,9 +1,8 @@
using MongoDB.Driver;
using Volo.Abp.Data;
using Volo.Abp.MongoDB;
using Volo.CmsKit.Comments;
using Volo.CmsKit.Contents;
using Volo.CmsKit.Blogs;
using Volo.CmsKit.Comments;
using Volo.CmsKit.MediaDescriptors;
using Volo.CmsKit.Pages;
using Volo.CmsKit.Ratings;

@ -1,9 +1,8 @@
using System;
using Volo.Abp;
using Volo.Abp.MongoDB;
using Volo.CmsKit.Comments;
using Volo.CmsKit.Contents;
using Volo.CmsKit.Blogs;
using Volo.CmsKit.Comments;
using Volo.CmsKit.MediaDescriptors;
using Volo.CmsKit.Pages;
using Volo.CmsKit.Ratings;

@ -1,9 +1,8 @@
using MongoDB.Driver;
using Volo.Abp.Data;
using Volo.Abp.MongoDB;
using Volo.CmsKit.Comments;
using Volo.CmsKit.Contents;
using Volo.CmsKit.Blogs;
using Volo.CmsKit.Comments;
using Volo.CmsKit.MediaDescriptors;
using Volo.CmsKit.Pages;
using Volo.CmsKit.Ratings;

@ -36,10 +36,10 @@ namespace Volo.CmsKit.Public.Blogs
{
var blog = await BlogRepository.GetBySlugAsync(blogSlug);
var blogPosts = await BlogPostRepository.GetPagedListAsync(blog.Id, input.SkipCount, input.MaxResultCount, input.Sorting);
var blogPosts = await BlogPostRepository.GetListAsync(null, blog.Id,input.MaxResultCount, input.SkipCount, input.Sorting);
return new PagedResultDto<BlogPostPublicDto>(
await BlogPostRepository.GetCountAsync(blog.Id),
await BlogPostRepository.GetCountAsync(blogId: blog.Id),
ObjectMapper.Map<List<BlogPost>, List<BlogPostPublicDto>>(blogPosts));
}
}

@ -7,6 +7,7 @@ using Microsoft.Extensions.Options;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Authorization;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.Uow;
using Volo.Abp.Users;
@ -17,7 +18,6 @@ namespace Volo.CmsKit.Public.Comments
{
public class CommentPublicAppService : ApplicationService, ICommentPublicAppService
{
protected CmsKitOptions CmsKitOptions { get; }
protected ICommentRepository CommentRepository { get; }
protected ICmsUserLookupService CmsUserLookupService { get; }
public IDistributedEventBus DistributedEventBus { get; }
@ -27,10 +27,8 @@ namespace Volo.CmsKit.Public.Comments
ICommentRepository commentRepository,
ICmsUserLookupService cmsUserLookupService,
IDistributedEventBus distributedEventBus,
IOptions<CmsKitOptions> cmsKitOptions,
CommentManager commentManager)
{
CmsKitOptions = cmsKitOptions.Value;
CommentRepository = commentRepository;
CmsUserLookupService = cmsUserLookupService;
DistributedEventBus = distributedEventBus;
@ -85,7 +83,7 @@ namespace Volo.CmsKit.Public.Comments
if (comment.CreatorId != CurrentUser.GetId())
{
throw new BusinessException(); //TODO: AbpAuthorizationException!
throw new AbpAuthorizationException();
}
comment.SetText(input.Text);
@ -102,7 +100,7 @@ namespace Volo.CmsKit.Public.Comments
if (comment.CreatorId != CurrentUser.GetId())
{
throw new BusinessException(); //TODO: AbpAuthorizationException!
throw new AbpAuthorizationException();
}
await CommentRepository.DeleteWithRepliesAsync(comment);

@ -59,7 +59,7 @@ namespace Volo.CmsKit.Public.Reactions
[Authorize]
public virtual async Task CreateAsync(string entityType, string entityId, string reaction)
{
await ReactionManager.CreateAsync(
await ReactionManager.GetOrCreateAsync(
CurrentUser.GetId(),
entityType,
entityId,

@ -1,9 +1,6 @@
@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bootstrap
@using Volo.Abp.AspNetCore.Mvc.UI.Layout
@model Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Pages.PageViewModel
@inject IPageLayout PageLayout
<h1>@Model.Title</h1>
@model Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Pages.PageViewModel
<abp-card>
<abp-card-body>

@ -61,7 +61,7 @@ namespace Volo.CmsKit.Blogs
result.ShouldNotBeNull();
result.ShouldNotBeEmpty();
result.Count.ShouldBeGreaterThan(2); // 2 are seeded and there are built-in Features.
result.Count.ShouldBeGreaterThanOrEqualTo(2); // 2 are seeded and there are built-in Features.
}
}
}

@ -98,7 +98,7 @@ namespace Volo.CmsKit.Blogs
[Fact]
public async Task GetListAsync_ShouldWorkProperly_WithDefaultParameters()
{
var list = await blogPostAdminAppService.GetListAsync(new PagedAndSortedResultRequestDto());
var list = await blogPostAdminAppService.GetListAsync(new BlogPostGetListInput());
list.ShouldNotBeNull();
list.TotalCount.ShouldBe(2);

@ -1,10 +1,8 @@
using System;
using Shouldly;
using System;
using System.Linq;
using System.Threading.Tasks;
using Shouldly;
using Volo.Abp.Uow;
using Volo.CmsKit.Admin.Pages;
using Volo.CmsKit.Contents;
using Xunit;
namespace Volo.CmsKit.Pages

@ -1,12 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Shouldly;
using Xunit;
using Volo.CmsKit.Blogs.Extensions;
using Shouldly;
using Unidecode.NET;
namespace Volo.CmsKit.Blogs
{
@ -20,7 +13,7 @@ namespace Volo.CmsKit.Blogs
var expected = "my-awesome-name";
// Act
var actual = name.NormalizeSlug();
var actual = SlugNormalizer.Normalize(name);
// Assert
actual.ShouldBe(expected);
@ -33,7 +26,7 @@ namespace Volo.CmsKit.Blogs
var expected = "my-perfect-title-v.2";
// Act
var actual = name.NormalizeSlug();
var actual = SlugNormalizer.Normalize(name);
// Assert
actual.ShouldBe(expected);
@ -47,7 +40,7 @@ namespace Volo.CmsKit.Blogs
var expected = "are-you-gonna-die";
// Act
var actual = name.NormalizeSlug();
var actual = SlugNormalizer.Normalize(name);
// Assert
actual.ShouldBe(expected);
@ -61,7 +54,7 @@ namespace Volo.CmsKit.Blogs
var expected = "we-machines-challenge";
// Act
var actual = name.NormalizeSlug();
var actual = SlugNormalizer.Normalize(name);
// Assert
actual.ShouldBe(expected);
@ -75,7 +68,7 @@ namespace Volo.CmsKit.Blogs
var expected = "go-and-code-part-2";
// Act
var actual = name.NormalizeSlug();
var actual = SlugNormalizer.Normalize(name);
// Assert
actual.ShouldBe(expected);
@ -89,7 +82,7 @@ namespace Volo.CmsKit.Blogs
var expected = "moe-klassnoe-nazvanie";
// Act
var actual = name.NormalizeSlug();
var actual = SlugNormalizer.Normalize(name);
// Assert
actual.ShouldBe(expected);
@ -103,7 +96,7 @@ namespace Volo.CmsKit.Blogs
var expected = "ozel-turkce-karakterler-gusioc";
// Act
var actual = name.NormalizeSlug();
var actual = SlugNormalizer.Normalize(name);
// Assert
actual.ShouldBe(expected);
@ -117,7 +110,7 @@ namespace Volo.CmsKit.Blogs
var expected = "wo-de-zhen-bang-tou-xian";
// Act
var actual = name.NormalizeSlug();
var actual = SlugNormalizer.Normalize(name);
// Assert
actual.ShouldBe(expected);
@ -131,7 +124,7 @@ namespace Volo.CmsKit.Blogs
var expected = "lets-rock";
// Act
var actual = name.NormalizeSlug();
var actual = SlugNormalizer.Normalize(name);
// Assert
actual.ShouldBe(expected);

@ -0,0 +1,70 @@
using Shouldly;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Volo.CmsKit.Pages
{
public class PageManager_Test : CmsKitDomainTestBase
{
private readonly PageManager pageManager;
private readonly CmsKitTestData testData;
private readonly IPageRepository pageRepository;
public PageManager_Test()
{
pageManager = GetRequiredService<PageManager>();
testData = GetRequiredService<CmsKitTestData>();
pageRepository = GetRequiredService<IPageRepository>();
}
public async Task CreateAsync_ShouldWorkProperly_WithNonExistingSlug()
{
var title = "My awesome page";
var slug = "my-awesome-page";
var content = "<h1>My Awesome Page</h1><p>This is my awesome page content!</p>";
var page = await pageManager.CreateAsync(title, slug, content);
page.ShouldNotBeNull();
page.Title.ShouldBe(title);
page.Slug.ShouldBe(slug);
page.Content.ShouldBe(content);
}
public async Task CreateAsync_ShouldThrowException_WithExistingSlug()
{
var title = "My awesome page";
var slug = testData.Page_1_Slug;
var content = "<h1>My Awesome Page</h1><p>This is my awesome page content!</p>";
var exception = await Should.ThrowAsync<PageSlugAlreadyExistsException>(async () =>
await pageManager.CreateAsync(title, slug, content));
exception.ShouldNotBeNull();
}
public async Task SetSlugAsync_ShouldWorkProperly_WithNonExistingSlug()
{
var newSlug = "freshly-generated-new-slug";
var page = await pageRepository.GetAsync(testData.Page_1_Id);
await pageManager.SetSlugAsync(page, newSlug);
page.Slug.ShouldBe(newSlug);
}
public async Task SetSlugAsync_ShouldThrowException_WithExistingSlug()
{
var newSlug = testData.Page_2_Slug;
var page = await pageRepository.GetAsync(testData.Page_1_Id);
var exception = await Should.ThrowAsync<PageSlugAlreadyExistsException>(async () =>
await pageManager.SetSlugAsync(page, newSlug));
exception.ShouldNotBeNull();
}
}
}

@ -19,7 +19,7 @@ namespace Volo.CmsKit.Reactions
[Fact]
public async Task GetReactionsAsync()
{
var reactions = await _reactionManager.GetReactionsAsync();
var reactions = await _reactionManager.GetReactionsAsync(_cmsKitTestData.EntityType1);
reactions.Count.ShouldBe(12);
@ -44,7 +44,7 @@ namespace Volo.CmsKit.Reactions
[Fact]
public async Task CreateAsync()
{
var reaction = await _reactionManager.CreateAsync(
var reaction = await _reactionManager.GetOrCreateAsync(
_cmsKitTestData.User2Id,
_cmsKitTestData.EntityType1,
_cmsKitTestData.EntityId2,

@ -97,7 +97,7 @@ namespace Volo.CmsKit.Blogs
[Fact]
public async Task GetPagedListAsync_ShouldWorkProperly_WithBlogId_WhileGetting10_WithoutSorting()
{
var result = await blogPostRepository.GetPagedListAsync(testData.Blog_Id, 0, 10, default);
var result = await blogPostRepository.GetListAsync(null, testData.Blog_Id);
result.ShouldNotBeNull();
result.ShouldNotBeEmpty();
@ -107,7 +107,7 @@ namespace Volo.CmsKit.Blogs
[Fact]
public async Task GetPagedListAsync_ShouldHaveAuthor_WithBlogId_WhileGetting10_WithoutSorting()
{
var result = await blogPostRepository.GetPagedListAsync(testData.Blog_Id, 0, 10, default);
var result = await blogPostRepository.GetListAsync(null, testData.Blog_Id);
result.ShouldNotBeNull();
result.ShouldNotBeEmpty();
@ -119,7 +119,7 @@ namespace Volo.CmsKit.Blogs
[Fact]
public async Task GetPagedListAsync_ShouldWorkProperly_WithBlogId_WhileGetting1_WithoutSorting()
{
var result = await blogPostRepository.GetPagedListAsync(testData.Blog_Id, default, 1, default);
var result = await blogPostRepository.GetListAsync(blogId: testData.Blog_Id, maxResultCount: 1);
result.ShouldNotBeNull();
result.ShouldNotBeEmpty();
@ -129,7 +129,7 @@ namespace Volo.CmsKit.Blogs
[Fact]
public async Task GetPagedListAsync_ShouldWorkProperly_WithBlogId_WhileGetting1InPage2_WithoutSorting()
{
var result = await blogPostRepository.GetPagedListAsync(testData.Blog_Id, 1, 1, default);
var result = await blogPostRepository.GetListAsync(blogId: testData.Blog_Id, skipCount: 1, maxResultCount: 1);
result.ShouldNotBeNull();
result.ShouldNotBeEmpty();
@ -139,7 +139,7 @@ namespace Volo.CmsKit.Blogs
[Fact]
public async Task GetPagedListAsync_ShouldWorkProperly_WithBlogId_WhileGetting10_WithSortingByTitle()
{
var result = await blogPostRepository.GetPagedListAsync(testData.Blog_Id, default, 10, nameof(BlogPost.Title));
var result = await blogPostRepository.GetListAsync(blogId: testData.Blog_Id, sorting: $"{nameof(BlogPost.Title)} asc");
result.ShouldNotBeNull();
result.ShouldNotBeEmpty();

@ -39,7 +39,7 @@ namespace Volo.CmsKit
private readonly IBlogFeatureRepository _blogFeatureRepository;
private readonly IBlogPostRepository _blogPostRepository;
private readonly BlogPostManager _blogPostManager;
private readonly IOptions<CmsKitOptions> _options;
private readonly IOptions<CmsKitReactionOptions> _reactionOptions;
private readonly IOptions<CmsKitTagOptions> _tagOptions;
private readonly IMediaDescriptorRepository _mediaDescriptorRepository;
private readonly IBlobContainer<MediaContainer> _mediaBlobContainer;
@ -64,7 +64,7 @@ namespace Volo.CmsKit
BlogPostManager blogPostmanager,
IBlogFeatureRepository blogFeatureRepository,
EntityTagManager entityTagManager,
IOptions<CmsKitOptions> options,
IOptions<CmsKitReactionOptions> reactionOptions,
IOptions<CmsKitTagOptions> tagOptions,
IMediaDescriptorRepository mediaDescriptorRepository,
IBlobContainer<MediaContainer> mediaBlobContainer,
@ -88,7 +88,7 @@ namespace Volo.CmsKit
_blogPostRepository = blogPostRepository;
_blogPostManager = blogPostmanager;
_blogFeatureRepository = blogFeatureRepository;
_options = options;
_reactionOptions = reactionOptions;
_tagOptions = tagOptions;
_mediaDescriptorRepository = mediaDescriptorRepository;
_mediaBlobContainer = mediaBlobContainer;
@ -140,6 +140,25 @@ namespace Volo.CmsKit
_commentsOptions.Value.EntityTypes.Add(
new CommentEntityTypeDefinition(_cmsKitTestData.EntityType1));
List<ReactionDefinition> reactions = new()
{
new ReactionDefinition(StandardReactions.Smile),
new ReactionDefinition(StandardReactions.ThumbsUp),
new ReactionDefinition(StandardReactions.ThumbsDown),
new ReactionDefinition(StandardReactions.Confused),
new ReactionDefinition(StandardReactions.Eyes),
new ReactionDefinition(StandardReactions.Heart),
new ReactionDefinition(StandardReactions.HeartBroken),
new ReactionDefinition(StandardReactions.Wink),
new ReactionDefinition(StandardReactions.Pray),
new ReactionDefinition(StandardReactions.Rocket),
new ReactionDefinition(StandardReactions.Victory),
new ReactionDefinition(StandardReactions.Rock),
};
_reactionOptions.Value.EntityTypes.Add(new ReactionEntityTypeDefinition(_cmsKitTestData.EntityType1, reactions));
_reactionOptions.Value.EntityTypes.Add(new ReactionEntityTypeDefinition(_cmsKitTestData.EntityType2, reactions));
return Task.CompletedTask;
}
@ -209,31 +228,31 @@ namespace Volo.CmsKit
private async Task SeedReactionsAsync()
{
await _reactionManager.CreateAsync(
await _reactionManager.GetOrCreateAsync(
_cmsKitTestData.User1Id,
_cmsKitTestData.EntityType1,
_cmsKitTestData.EntityId1,
StandardReactions.Confused);
await _reactionManager.CreateAsync(
await _reactionManager.GetOrCreateAsync(
_cmsKitTestData.User1Id,
_cmsKitTestData.EntityType1,
_cmsKitTestData.EntityId1,
StandardReactions.ThumbsUp);
await _reactionManager.CreateAsync(
await _reactionManager.GetOrCreateAsync(
_cmsKitTestData.User1Id,
_cmsKitTestData.EntityType1,
_cmsKitTestData.EntityId2,
StandardReactions.Heart);
await _reactionManager.CreateAsync(
await _reactionManager.GetOrCreateAsync(
_cmsKitTestData.User1Id,
_cmsKitTestData.EntityType2,
_cmsKitTestData.EntityId1,
StandardReactions.Rocket);
await _reactionManager.CreateAsync(
await _reactionManager.GetOrCreateAsync(
_cmsKitTestData.User2Id,
_cmsKitTestData.EntityType1,
_cmsKitTestData.EntityId1,

@ -8,6 +8,11 @@
$('li:not(.last-link) span.plus-icon i.fa-chevron-right').click(
function () {
var $element = $(this).parent();
var $filter = $('.docs-version #filter');
if ($filter && $filter.val() != ''){
return;
}
$element.parent().children('ul.tree').toggle(100);
$element.closest('li').toggleClass('selected-tree');

@ -23,7 +23,13 @@ const routes: Routes = [
children: [
{
path: '',
component: AuthWrapperComponent,
component: ReplaceableRouteContainerComponent,
data: {
replaceableComponent: {
key: eAccountComponents.AuthWrapper,
defaultComponent: AuthWrapperComponent,
} as ReplaceableComponents.RouteData<AuthWrapperComponent>,
},
children: [
{
path: 'login',

@ -1,6 +1,6 @@
import { Injector } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { ACCOUNT_CONFIG_OPTIONS } from '../tokens';
import { ACCOUNT_CONFIG_OPTIONS } from '../tokens/config-options.token';
export function getRedirectUrl(injector: Injector) {
const route = injector.get(ActivatedRoute);

@ -1,4 +1,4 @@
export function getPathName(url: string): string {
const { pathname } = new URL(url);
const { pathname } = new URL(url, window.location.origin);
return pathname;
}

@ -13,7 +13,7 @@ export class <%= name %>Service {
method: '<%= body.method %>',<%
if (body.responseType === 'string') { %>
responseType: 'text',<% } %>
url: `/<%= body.url %>`,<%
url: <%= body.url %>,<%
if (body.params.length) { %>
params: { <%= body.params.join(', ') %> },<% }
if (body.body) { %>

File diff suppressed because it is too large Load Diff

@ -16,6 +16,7 @@ export interface Type {
export interface PropertyDef {
name: string;
jsonName: string | null;
type: string;
typeSimple: string;
isRequired: boolean;
@ -61,6 +62,7 @@ export interface ParameterInSignature {
export interface ParameterInBody {
nameOnMethod: string;
name: string;
jsonName: string | null;
type: string;
typeSimple: string;
isOptional: boolean;

@ -1,5 +1,6 @@
import { eBindingSourceId, eMethodModifier } from '../enums';
import { camel } from '../utils';
import { shouldQuoteProp } from '../utils/prop';
import { camel } from '../utils/text';
import { ParameterInBody } from './api-definition';
import { Property } from './model';
import { Omissible } from './util';
@ -41,20 +42,25 @@ export class Body {
url: string;
registerActionParameter = (param: ParameterInBody) => {
const { bindingSourceId, descriptorName, name, nameOnMethod } = param;
const { bindingSourceId, descriptorName, jsonName, name, nameOnMethod } = param;
const camelName = camel(name);
const value = descriptorName ? `${descriptorName}.${camelName}` : nameOnMethod;
const paramName = jsonName || camelName;
const value = descriptorName
? shouldQuoteProp(paramName)
? `${descriptorName}['${paramName}']`
: `${descriptorName}.${paramName}`
: nameOnMethod;
switch (bindingSourceId) {
case eBindingSourceId.Model:
case eBindingSourceId.Query:
this.params.push(`${camelName}: ${value}`);
this.params.push(paramName === value ? value : `${paramName}: ${value}`);
break;
case eBindingSourceId.Body:
this.body = value;
break;
case eBindingSourceId.Path:
const regex = new RegExp('{(' + camelName + '|' + name + ')}', 'g');
const regex = new RegExp('{(' + paramName + '|' + camelName + '|' + name + ')}', 'g');
this.url = this.url.replace(regex, '${' + value + '}');
break;
default:
@ -64,6 +70,11 @@ export class Body {
constructor(options: BodyOptions) {
Object.assign(this, options);
this.setUrlQuotes();
}
private setUrlQuotes() {
this.url = /{/.test(this.url) ? `\`/${this.url}\`` : `'/${this.url}'`;
}
}

@ -3,6 +3,7 @@ import { Exception } from '../enums';
import { Type } from '../models';
import { interpolate } from './common';
import { parseNamespace } from './namespace';
import { shouldQuoteProp } from './prop';
export interface EnumGeneratorParams {
targetPath: string;
@ -32,7 +33,10 @@ export function createImportRefToEnumMapper({ solution, types }: EnumGeneratorPa
throw new SchematicsException(interpolate(Exception.NoTypeDefinition, ref));
const namespace = parseNamespace(solution, ref);
const members = enumNames!.map((key, i) => ({ key, value: enumValues[i] }));
const members = enumNames!.map((key, i) => ({
key: shouldQuoteProp(key) ? `'${key}'` : key,
value: enumValues[i],
}));
return {
namespace,

@ -10,6 +10,7 @@ export * from './import';
export * from './model';
export * from './namespace';
export * from './path';
export * from './prop';
export * from './rule';
export * from './service';
export * from './source';

@ -8,6 +8,7 @@ import {
} from './generics';
import { parseNamespace } from './namespace';
import { relativePathToModel } from './path';
import { shouldQuoteProp } from './prop';
import { camel } from './text';
import { parseGenerics } from './tree';
import {
@ -134,7 +135,8 @@ export function createImportRefToInterfaceReducerCreator(params: ModelGeneratorP
genericsCollector.reset();
typeDef.properties?.forEach(prop => {
const name = camel(prop.name);
let name = prop.jsonName || camel(prop.name);
name = shouldQuoteProp(name) ? `'${name}'` : name;
const type = simplifyType(prop.typeSimple);
const refs = parseType(prop.type).reduce(
(acc: string[], r) => acc.concat(parseGenerics(r).toGenerics()),

@ -0,0 +1,9 @@
export const shouldQuoteProp = (key: string) => {
try {
new Function('return {}.' + key);
} catch (_) {
return true;
}
return false;
};
Loading…
Cancel
Save