mirror of https://github.com/abpframework/abp.git
80 changed files with 2380 additions and 11783 deletions
@ -1,6 +1,6 @@ |
|||||
{ |
{ |
||||
"sdk": { |
"sdk": { |
||||
"version": "5.0.100", |
"version": "5.0.201", |
||||
"rollForward": "latestFeature" |
"rollForward": "latestFeature" |
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -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,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; |
|
||||
} |
|
||||
} |
|
||||
@ -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(); |
|
||||
} |
|
||||
} |
|
||||
@ -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.Linq; |
||||
using System.Threading.Tasks; |
using System.Threading.Tasks; |
||||
|
using JetBrains.Annotations; |
||||
using Microsoft.Extensions.Options; |
using Microsoft.Extensions.Options; |
||||
|
using Volo.Abp; |
||||
using Volo.Abp.DependencyInjection; |
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
namespace Volo.CmsKit.Reactions |
namespace Volo.CmsKit.Reactions |
||||
{ |
{ |
||||
public class DefaultReactionDefinitionStore : IReactionDefinitionStore, ITransientDependency |
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; |
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; } |
||||
|
} |
||||
|
} |
||||
@ -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(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,12 +1,12 @@ |
|||||
using Slugify; |
using Slugify; |
||||
using Unidecode.NET; |
using Unidecode.NET; |
||||
|
|
||||
namespace Volo.CmsKit.Blogs.Extensions |
namespace Volo.CmsKit |
||||
{ |
{ |
||||
public static class SlugExtensions |
public static class SlugNormalizer |
||||
{ |
{ |
||||
static readonly SlugHelper SlugHelper = new (); |
static readonly SlugHelper SlugHelper = new (); |
||||
public static string NormalizeSlug(this string value) |
public static string Normalize(string value) |
||||
{ |
{ |
||||
return SlugHelper.GenerateSlug(value?.Unidecode()); |
return SlugHelper.GenerateSlug(value?.Unidecode()); |
||||
} |
} |
||||
@ -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(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,4 +1,4 @@ |
|||||
export function getPathName(url: string): string { |
export function getPathName(url: string): string { |
||||
const { pathname } = new URL(url); |
const { pathname } = new URL(url, window.location.origin); |
||||
return pathname; |
return pathname; |
||||
} |
} |
||||
|
|||||
File diff suppressed because it is too large
@ -0,0 +1,9 @@ |
|||||
|
export const shouldQuoteProp = (key: string) => { |
||||
|
try { |
||||
|
new Function('return {}.' + key); |
||||
|
} catch (_) { |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
}; |
||||
Loading…
Reference in new issue