mirror of https://github.com/abpframework/abp.git
62 changed files with 976 additions and 297 deletions
@ -1 +1,45 @@ |
|||
TODO... |
|||
# Redis 缓存 |
|||
|
|||
ABP Framework [缓存系统](Caching.md) 拓展了 [ASP.NET Core 分布式缓存](https://docs.microsoft.com/zh-cn/aspnet/core/performance/caching/distributed). 因此, 标准 ASP.NET Core 分布式缓存支持的 **任何提供程序** 都可以在你的应用程序中使用,并且可以像 **微软文档** 那样进行配置. |
|||
|
|||
不过, ABP为 Redis Cache 还提供了一个集成包: [Volo.Abp.Caching.StackExchangeRedis](https://www.nuget.org/packages/Volo.Abp.Caching.StackExchangeRedis). |
|||
为何使用中这个包而不是标准的[Microsoft.Extensions.Caching.StackExchangeRedis](https://www.nuget.org/packages/Microsoft.Extensions.Caching.StackExchangeRedis/)包有两个原因. |
|||
|
|||
1. 它实现 `SetManyAsync` 和 `GetManyAsync` 方法. 这些都不是 Microsoft 缓存库的标准方法,而是由ABP框架[缓存](Caching.md)系统添加的. 当你需要通过单个方法调用设置/获取多个缓存项时,它们**显著提高了性能**. |
|||
2. 它 **简化** 了 Redis 缓存 **配置** (将在下面解释). |
|||
|
|||
> Volo.Abp.Caching.StackExchangeRedis 已经使用了 Microsoft.Extensions.Caching.StackExchangeRedis 包,但对其进行了扩展和改进. |
|||
## 安装 |
|||
|
|||
> 如果使用Redis,则此软件包已安装在应用程序启动模板中. |
|||
在项目`.csproj`的文件夹中打开命令行窗口并输入命令: |
|||
|
|||
````bash |
|||
abp add-package Volo.Abp.Caching.StackExchangeRedis |
|||
```` |
|||
|
|||
## 配置 |
|||
|
|||
Volo.Abp.Caching.StackExchangeRedis 包自动从`IConfiguration`获取Redis[配置](Configuration.md). 因此,你可以在以下位置设置配置 `appsettings.json`: |
|||
|
|||
````js |
|||
"Redis": { |
|||
"IsEnabled": "true", |
|||
"Configuration": "127.0.0.1" |
|||
} |
|||
```` |
|||
设置`IsEnabled`为可选的,如果未设置将默认视为`true`. |
|||
|
|||
|
|||
或者,你可以在[模块](Module-Development-Basics.md)的ConfigureServices方法中配置标准的[RedisCacheOptions](https://docs.microsoft.com/zh-cn/dotnet/api/microsoft.extensions.caching.stackexchangeredis.rediscacheoptions)类: |
|||
|
|||
````csharp |
|||
Configure<RedisCacheOptions>(options => |
|||
{ |
|||
//... |
|||
}); |
|||
```` |
|||
|
|||
## 另请参阅 |
|||
|
|||
* [缓存](Caching.md) |
|||
|
|||
@ -0,0 +1,13 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; |
|||
|
|||
public class AbpApplicationConfigurationOptions |
|||
{ |
|||
public List<IApplicationConfigurationContributor> Contributors { get; } |
|||
|
|||
public AbpApplicationConfigurationOptions() |
|||
{ |
|||
Contributors = new List<IApplicationConfigurationContributor>(); |
|||
} |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
using System; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; |
|||
|
|||
public class ApplicationConfigurationContributorContext : IServiceProviderAccessor |
|||
{ |
|||
public IServiceProvider ServiceProvider { get; } |
|||
|
|||
public ApplicationConfigurationDto ApplicationConfiguration { get; } |
|||
|
|||
public ApplicationConfigurationContributorContext(IServiceProvider serviceProvider, ApplicationConfigurationDto applicationConfiguration) |
|||
{ |
|||
ServiceProvider = serviceProvider; |
|||
ApplicationConfiguration = applicationConfiguration; |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; |
|||
|
|||
public interface IApplicationConfigurationContributor |
|||
{ |
|||
Task ContributeAsync(ApplicationConfigurationContributorContext context); |
|||
} |
|||
@ -1,14 +0,0 @@ |
|||
using System; |
|||
using System.Linq; |
|||
|
|||
namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps; |
|||
|
|||
public class ChangeApplicationIdGuidStep: ProjectBuildPipelineStep |
|||
{ |
|||
public override void Execute(ProjectBuildContext context) |
|||
{ |
|||
var projectFile = context.Files.FirstOrDefault(f => f.Name.EndsWith("MyCompanyName.MyProjectName.csproj")); |
|||
|
|||
projectFile?.SetContent(projectFile.Content.Replace("27317750-B571-4690-B433-B358B2480E01", Guid.NewGuid().ToString())); |
|||
} |
|||
} |
|||
@ -0,0 +1,6 @@ |
|||
namespace Volo.Abp.Cli.ProjectBuilding.Events; |
|||
|
|||
public class ProjectCreationProgressEvent |
|||
{ |
|||
public string Message { get; set; } |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
using System; |
|||
using System.Linq; |
|||
using System.Xml; |
|||
using Volo.Abp.Cli.ProjectBuilding.Building; |
|||
using Volo.Abp.Cli.Utils; |
|||
|
|||
namespace Volo.Abp.Cli.ProjectBuilding.Templates.Maui; |
|||
|
|||
public class MauiChangeApplicationIdGuidStep: ProjectBuildPipelineStep |
|||
{ |
|||
public override void Execute(ProjectBuildContext context) |
|||
{ |
|||
var projectFile = context.Files.FirstOrDefault(f => f.Name.EndsWith("MyCompanyName.MyProjectName.csproj") || f.Name.EndsWith("MyCompanyName.MyProjectName.Maui.csproj")); |
|||
|
|||
if (projectFile == null) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
using (var stream = StreamHelper.GenerateStreamFromString(projectFile.Content)) |
|||
{ |
|||
var doc = new XmlDocument { PreserveWhitespace = true }; |
|||
doc.Load(stream); |
|||
|
|||
var node = doc.SelectSingleNode("/Project/PropertyGroup/ApplicationIdGuid"); |
|||
if (node != null) |
|||
{ |
|||
node.InnerText = Guid.NewGuid().ToString(); |
|||
} |
|||
|
|||
projectFile.SetContent(doc.OuterXml); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Data; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; |
|||
|
|||
public class TestApplicationConfigurationContributor : IApplicationConfigurationContributor |
|||
{ |
|||
public Task ContributeAsync(ApplicationConfigurationContributorContext context) |
|||
{ |
|||
context.ApplicationConfiguration.SetProperty("TestKey", "TestValue"); |
|||
return Task.CompletedTask; |
|||
} |
|||
} |
|||
@ -1,5 +1,5 @@ |
|||
{ |
|||
"ConnectionStrings": { |
|||
"Default": "Server=(localdb)\\.\\MSSQLLocalDB;Database=CmsKit_Unified;Trusted_Connection=True" |
|||
"Default": "Server=(localdb)\\MSSQLLocalDB;Database=CmsKit_Unified;Trusted_Connection=True" |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,8 @@ |
|||
namespace Volo.CmsKit.Contents; |
|||
public static class ContentConsts |
|||
{ |
|||
public static string Delimeter { get; set; } = "----"; |
|||
public static string Widget { get; set; } = "Widget"; |
|||
public static string Markdown { get; set; } = "Markdown"; |
|||
public static string Content { get; set; } = "Content"; |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
using System; |
|||
using Volo.Abp.ObjectExtending; |
|||
|
|||
namespace Volo.CmsKit.Contents; |
|||
|
|||
[Serializable] |
|||
public class ContentFragment : ExtensibleObject |
|||
{ |
|||
public string Type { get; set; } |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Volo.CmsKit.Contents; |
|||
|
|||
public class CmsKitContentWidgetOptions |
|||
{ |
|||
public Dictionary<string, ContentWidgetConfig> WidgetConfigs { get; } |
|||
|
|||
public CmsKitContentWidgetOptions() |
|||
{ |
|||
WidgetConfigs = new(); |
|||
} |
|||
|
|||
public void AddWidget(string widgetName, string widgetKey) |
|||
{ |
|||
WidgetConfigs.Add(widgetName, new ContentWidgetConfig(widgetKey)); |
|||
} |
|||
} |
|||
@ -0,0 +1,120 @@ |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text.RegularExpressions; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.Options; |
|||
using Volo.Abp.Data; |
|||
using Volo.Abp.DependencyInjection; |
|||
using static Volo.CmsKit.Contents.ContentConsts; |
|||
|
|||
namespace Volo.CmsKit.Contents; |
|||
|
|||
public class ContentParser : ITransientDependency |
|||
{ |
|||
private readonly CmsKitContentWidgetOptions _options; |
|||
|
|||
public ContentParser(IOptions<CmsKitContentWidgetOptions> options) |
|||
{ |
|||
_options = options.Value; |
|||
} |
|||
|
|||
public Task<List<ContentFragment>> ParseAsync(string content) |
|||
{ |
|||
if (!_options.WidgetConfigs.Any()) |
|||
{ |
|||
return Task.FromResult(new List<ContentFragment> |
|||
{ |
|||
new ContentFragment { Type = Markdown }.SetProperty(Content, content), |
|||
}); |
|||
} |
|||
|
|||
var parsedList = new List<string>(); |
|||
ParseContent(content, parsedList); |
|||
|
|||
var contentFragments = new List<ContentFragment>(); |
|||
FillContentFragment(content, parsedList, contentFragments); |
|||
|
|||
return Task.FromResult(contentFragments); |
|||
} |
|||
|
|||
private void ParseContent(string content, List<string> parsedList) |
|||
{ |
|||
var replacedText = Regex.Replace(content, @"\[.*?\]", Delimeter); |
|||
if (!replacedText.Contains(Delimeter)) |
|||
{ |
|||
parsedList.Add(replacedText); |
|||
} |
|||
|
|||
while (replacedText.Contains(Delimeter)) |
|||
{ |
|||
//For parsing delimeter
|
|||
var index = replacedText.IndexOf(Delimeter); |
|||
if (index != 0) |
|||
{ |
|||
parsedList.Add(replacedText.Substring(0, index)); |
|||
replacedText = replacedText.Substring(index, replacedText.Length - index); |
|||
index = 0; |
|||
} |
|||
|
|||
parsedList.Add(replacedText.Substring(index, Delimeter.Length)); |
|||
replacedText = replacedText.Substring(Delimeter.Length, replacedText.Length - Delimeter.Length); |
|||
|
|||
//for parsing the other side
|
|||
index = replacedText.IndexOf(Delimeter); |
|||
if (index != -1) |
|||
{ |
|||
parsedList.Add(replacedText.Substring(0, index)); |
|||
replacedText = replacedText.Substring(index, replacedText.Length - index); |
|||
} |
|||
else if (replacedText.Length > 0) |
|||
{ |
|||
parsedList.Add(replacedText); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private void FillContentFragment(string content, List<string> parsedList, List<ContentFragment> contentFragments) |
|||
{ |
|||
content = Regex.Replace(content, @"=\s*""", @"="""); |
|||
content = Regex.Replace(content, @"""\s*=", @"""="); |
|||
var widgets = Regex.Matches(content, @"(?<=\[Widget)(.*?)(?=\])").Cast<Match>().Select(p => p.Value).ToList(); |
|||
for (int i = 0, k = 0; i < parsedList.Count; i++) |
|||
{ |
|||
if (parsedList[i] == Delimeter) |
|||
{ |
|||
if (widgets.Count > k) |
|||
{ |
|||
var preparedContent = string.Join("", widgets[k]); |
|||
var keys = Regex.Matches(preparedContent, @"(?<=\s)(.*?)(?==\s*"")").Cast<Match>() |
|||
.Select(p => p.Value).Where(p => p != string.Empty).ToList(); |
|||
var values = Regex.Matches(preparedContent, @"(?<=\s*[a-zA-Z]*=\s*"")(.*?)(?="")").Cast<Match>() |
|||
.Select(p => p.Value).ToList(); |
|||
|
|||
var widgetTypeIndex = keys.IndexOf("Type"); |
|||
if (widgetTypeIndex != -1) |
|||
{ |
|||
var widgetType = values[widgetTypeIndex]; |
|||
var name = _options.WidgetConfigs.Where(p => p.Key == widgetType).Select(p => p.Value.Name).FirstOrDefault(); |
|||
if (name is not null && widgets.Count > k) |
|||
{ |
|||
values[0] = name; |
|||
var contentFragment = new ContentFragment() { Type = Widget }; |
|||
contentFragments.Add(contentFragment); |
|||
for (int kv = 0; kv < values.Count; kv++) |
|||
{ |
|||
contentFragments.FindLast(p => p == contentFragment) |
|||
.SetProperty(keys[kv], values[kv]); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
k++; |
|||
} |
|||
else |
|||
{ |
|||
contentFragments.Add(new ContentFragment() { Type = Markdown } |
|||
.SetProperty(Content, parsedList[i])); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
namespace Volo.CmsKit.Contents; |
|||
|
|||
public class ContentWidgetConfig |
|||
{ |
|||
public string Name { get; } |
|||
|
|||
public ContentWidgetConfig(string widgetName) |
|||
{ |
|||
Name = widgetName; |
|||
} |
|||
} |
|||
@ -1,17 +0,0 @@ |
|||
@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bootstrap |
|||
|
|||
@using Volo.CmsKit.Public.Web.Renderers |
|||
@using Volo.Abp.AspNetCore.Mvc.UI.Packages.HighlightJs |
|||
|
|||
@inject IMarkdownToHtmlRenderer MarkdownRenderer |
|||
|
|||
@model Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Pages.PageViewModel |
|||
|
|||
<abp-card> |
|||
<abp-card-body> |
|||
@if (!Model.Content.IsNullOrEmpty()) |
|||
{ |
|||
@Html.Raw(await MarkdownRenderer.RenderAsync(Model.Content)) |
|||
} |
|||
</abp-card-body> |
|||
</abp-card> |
|||
@ -1,54 +0,0 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Volo.Abp.AspNetCore.Mvc; |
|||
using Volo.CmsKit.Public.Pages; |
|||
using Volo.CmsKit.Public.Web.Renderers; |
|||
using Volo.Abp.AspNetCore.Mvc.UI.Packages.HighlightJs; |
|||
using Volo.Abp.AspNetCore.Mvc.UI.Widgets; |
|||
|
|||
namespace Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Pages; |
|||
|
|||
[Widget( |
|||
StyleTypes = new[] |
|||
{ |
|||
typeof(HighlightJsStyleContributor) |
|||
}, |
|||
ScriptTypes = new[] |
|||
{ |
|||
typeof(HighlightJsScriptContributor) |
|||
}, |
|||
ScriptFiles = new[] |
|||
{ |
|||
"/Pages/Public/CmsKit/highlightOnLoad.js" |
|||
})] |
|||
public class DefaultPageViewComponent : AbpViewComponent |
|||
{ |
|||
protected IPagePublicAppService PagePublicAppService { get; } |
|||
|
|||
public DefaultPageViewComponent(IPagePublicAppService pagePublicAppService) |
|||
{ |
|||
PagePublicAppService = pagePublicAppService; |
|||
} |
|||
|
|||
public virtual async Task<IViewComponentResult> InvokeAsync(Guid pageId, string title, string content) |
|||
{ |
|||
var model = new PageViewModel |
|||
{ |
|||
Id = pageId, |
|||
Title = title, |
|||
Content = content |
|||
}; |
|||
|
|||
return View("~/Pages/CmsKit/Shared/Components/Pages/Default.cshtml", model); |
|||
} |
|||
} |
|||
|
|||
public class PageViewModel |
|||
{ |
|||
public Guid Id { get; set; } |
|||
|
|||
public string Title { get; set; } |
|||
|
|||
public string Content { get; set; } |
|||
} |
|||
@ -1,31 +1,46 @@ |
|||
@page |
|||
@using Microsoft.AspNetCore.Mvc.Localization |
|||
@using Volo.CmsKit.Localization |
|||
@using Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Pages |
|||
@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bootstrap |
|||
@using Volo.CmsKit.Contents |
|||
@using System.Dynamic |
|||
@using Volo.CmsKit.Public.Web.Renderers |
|||
@using Volo.Abp.Data |
|||
@using Volo.Abp.AspNetCore.Mvc.UI.Packages.HighlightJs; |
|||
@using Volo.Abp.AspNetCore.Mvc.UI.Widgets; |
|||
|
|||
@inject IMarkdownToHtmlRenderer MarkdownRenderer |
|||
|
|||
|
|||
@model Volo.CmsKit.Public.Web.Pages.Public.CmsKit.Pages.IndexModel |
|||
|
|||
@inject IHtmlLocalizer<CmsKitResource> L |
|||
|
|||
@section styles{ |
|||
|
|||
<abp-style src="/Pages/Public/CmsKit/Pages/index.css" /> |
|||
|
|||
<style> |
|||
@Html.Raw(Model.Page.Style) |
|||
@Html.Raw(Model.PageDto.Style) |
|||
</style> |
|||
} |
|||
|
|||
@section scripts{ |
|||
<script> |
|||
@Html.Raw(Model.Page.Script) |
|||
@Html.Raw(Model.PageDto.Script) |
|||
</script> |
|||
} |
|||
|
|||
@await Component.InvokeAsync(typeof(DefaultPageViewComponent), |
|||
new |
|||
{ |
|||
pageId = Model.Page.Id, |
|||
title = Model.Page.Title, |
|||
content = Model.Page.Content |
|||
}) |
|||
<abp-card> |
|||
<abp-card-body> |
|||
@foreach (ContentFragment contentFragment in Model.PageDto.ContentFragments) |
|||
{ |
|||
if (contentFragment.Type == ContentConsts.Markdown) |
|||
{ |
|||
@Html.Raw(await MarkdownRenderer.RenderAsync(contentFragment.GetProperty<string>("Content"))) |
|||
} |
|||
else if (contentFragment.Type == ContentConsts.Widget) |
|||
{ |
|||
@await Component.InvokeAsync(contentFragment.GetProperty<string>("Type"), contentFragment.ExtraProperties.ConvertToDynamicObject()) |
|||
} |
|||
} |
|||
</abp-card-body> |
|||
</abp-card> |
|||
|
|||
|
|||
@ -0,0 +1,14 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Volo.CmsKit.Contents; |
|||
|
|||
namespace Volo.CmsKit.Public.Web.Pages.Public.CmsKit.Pages; |
|||
|
|||
public class PageViewModel |
|||
{ |
|||
public Guid Id { get; set; } |
|||
|
|||
public string Title { get; set; } |
|||
|
|||
public List<ContentFragment> ContentFragments { get; set; } |
|||
} |
|||
@ -0,0 +1,138 @@ |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.Options; |
|||
using Shouldly; |
|||
using Xunit; |
|||
|
|||
namespace Volo.CmsKit.Contents; |
|||
public class ContentParser_Test : CmsKitDomainTestBase |
|||
{ |
|||
private readonly CmsKitTestData testData; |
|||
private readonly IOptions<CmsKitContentWidgetOptions> _options; |
|||
private ContentParser contentParser; |
|||
|
|||
public ContentParser_Test() |
|||
{ |
|||
testData = GetRequiredService<CmsKitTestData>(); |
|||
_options = GetRequiredService<IOptions<CmsKitContentWidgetOptions>>(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task ParseAsync_ShouldWorkWithDifferentWidgetTypes() |
|||
{ |
|||
_options.Value.AddWidget(testData.PollName, testData.WidgetName); |
|||
_options.Value.AddWidget("ImageGallery", "ImageGallery"); |
|||
contentParser = new ContentParser(_options); |
|||
|
|||
var content = @"**ABP Framework** is completely open source and developed in a community-driven manner.
|
|||
[Widget Type=""Poll"" Code=""poll-name""] |
|||
Thanks _for_ *your * feedback. |
|||
[Widget GalleryName=""Xyz"" Type=""ImageGallery"" Source=""GoogleDrive""]";
|
|||
|
|||
var widgets = await contentParser.ParseAsync(content); |
|||
|
|||
widgets.ShouldNotBeNull(); |
|||
widgets.Count.ShouldBe(4); |
|||
widgets[1].ExtraProperties.Count.ShouldBe(2); |
|||
widgets[3].ExtraProperties.Count.ShouldBe(3); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task ParseAsync_ShouldWorkWithoutConfigOptions() |
|||
{ |
|||
var content = @"**ABP Framework** is completely open source and developed in a community-driven manner.
|
|||
[Widget Type= ""Poll"" Code =""poll-name""] |
|||
Thanks _for_ *your * feedback.";
|
|||
|
|||
contentParser = new ContentParser(_options); |
|||
var widgets = await contentParser.ParseAsync(content); |
|||
|
|||
widgets.ShouldNotBeNull(); |
|||
widgets.Count.ShouldBe(1);//Ignored Widget
|
|||
} |
|||
|
|||
[Fact] |
|||
public async Task ParseAsync_ShouldWorkWithWrongConfigOptions() |
|||
{ |
|||
_options.Value.AddWidget(testData.WidgetName, testData.PollName); |
|||
contentParser = new ContentParser(_options); |
|||
|
|||
var content = @"**ABP Framework** is completely open source and developed in a community-driven manner.
|
|||
[Widget Type= ""Poll"" Code =""poll-name""] |
|||
Thanks _for_ *your * feedback.";
|
|||
|
|||
var widgets = await contentParser.ParseAsync(content); |
|||
|
|||
widgets.ShouldNotBeNull(); |
|||
widgets.Count.ShouldBe(2); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task ParseAsync_ShouldWorkWithWrongWidgetType() |
|||
{ |
|||
_options.Value.AddWidget(testData.PollName, testData.WidgetName); |
|||
contentParser = new ContentParser(_options); |
|||
|
|||
var content = @"**ABP Framework** is completely open source and developed in a community-driven manner.
|
|||
[Widget Wrong Type= ""Poll"" Code =""poll-name""] |
|||
Thanks _for_ *your * feedback.";
|
|||
|
|||
var widgets = await contentParser.ParseAsync(content); |
|||
|
|||
widgets.ShouldNotBeNull(); |
|||
widgets.Count.ShouldBe(2); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task ParseAsync_ShouldWorkWithWrongPollName() |
|||
{ |
|||
_options.Value.AddWidget(testData.PollName, testData.WidgetName); |
|||
contentParser = new ContentParser(_options); |
|||
|
|||
var content = @"**ABP Framework** is completely open source and developed in a community-driven manner.
|
|||
[Widget Type= ""Poll"" PollWrongName =""poll-name""] |
|||
Thanks _for_ *your * feedback.";
|
|||
|
|||
var widgets = await contentParser.ParseAsync(content); |
|||
|
|||
widgets.ShouldNotBeNull(); |
|||
widgets.Count.ShouldBe(3); |
|||
} |
|||
|
|||
[Theory] |
|||
[MemberData(nameof(ExampleData))] |
|||
public async Task ParseAsync_ShouldWorkProperlyWithCorrectInputs(string content, int expectedLine) |
|||
{ |
|||
_options.Value.AddWidget(testData.PollName, testData.WidgetName); |
|||
contentParser = new ContentParser(_options); |
|||
|
|||
var widgets = await contentParser.ParseAsync(content); |
|||
|
|||
widgets.ShouldNotBeNull(); |
|||
widgets.Count.ShouldBe(expectedLine); |
|||
} |
|||
|
|||
public static IEnumerable<object[]> ExampleData => |
|||
new List<object[]> |
|||
{ |
|||
new object[] { @"**ABP Framework** is completely open source and developed in a community-driven manner.
|
|||
[Widget Type=""Poll"" Code=""poll-name""] |
|||
Thanks _for_ *your * feedback.", 3},
|
|||
|
|||
new object[] { @"**ABP Framework** is completely open source and developed in a community-driven manner.
|
|||
[Widget Type=""Poll"" Code=""poll-name""] |
|||
Thanks _for_ *your * feedback. |
|||
[Widget Type=""Poll"" Code=""poll-name1""]", 4 },
|
|||
|
|||
new object[] { @"**ABP Framework** is completely open source and developed in a community-driven manner.
|
|||
Thanks _for_ *your * feedback. |
|||
[Widget Type=""Poll"" Code=""poll-name""]", 2 },
|
|||
|
|||
new object[] { @"[Widget Type=""Poll"" Code=""poll-name""] gg [Widget Type=""Poll"" Code=""poll-name1""]**ABP Framework** is completely open source and developed in a community-driven manner.
|
|||
Thanks _for_ *your * feedback. |
|||
Thanks _for_ *your * feedback.", 4},
|
|||
|
|||
new object[] { @"Thanks _for_ *your * feedback.
|
|||
Thanks _for_ *your * feedback.", 1}
|
|||
}; |
|||
} |
|||
@ -1,22 +0,0 @@ |
|||
@using Microsoft.Extensions.Primitives |
|||
@using Volo.Abp.OpenIddict.Localization |
|||
@using Microsoft.AspNetCore.Mvc.Localization |
|||
@model AuthorizeViewModel |
|||
@inject IHtmlLocalizer<AbpOpenIddictResource> L |
|||
|
|||
<div class="jumbotron"> |
|||
<h1>@L["LogOut"]</h1> |
|||
<p class="lead text-left">@L["AreYouSureYouWantToSignOut"]</p> |
|||
|
|||
<form method="post" action="~/connect/logout"> |
|||
@Html.AntiForgeryToken() |
|||
@* Flow the request parameters so they can be received by the LogoutPost action: *@ |
|||
@foreach (var parameter in Context.Request.HasFormContentType ? (IEnumerable<KeyValuePair<string, StringValues>>) Context.Request.Form : Context.Request.Query) |
|||
{ |
|||
<input type="hidden" name="@parameter.Key" value="@parameter.Value"/> |
|||
} |
|||
|
|||
<input class="btn btn-primary" name="accept" type="submit" value="@L["Accept"]"/> |
|||
<input class="btn btn-danger ms-1" name="deny" type="submit" value="@L["Deny"]"/> |
|||
</form> |
|||
</div> |
|||
@ -1,34 +1,119 @@ |
|||
@page |
|||
@using Microsoft.AspNetCore.Http.Extensions |
|||
@using Microsoft.AspNetCore.Mvc.Localization |
|||
@using MyCompanyName.MyProjectName.Pages |
|||
@using MyCompanyName.MyProjectName.Localization |
|||
@using Volo.Abp.Users |
|||
@using Volo.Abp.AspNetCore.Mvc.UI.Theming |
|||
@using Volo.Abp.Ui.Branding |
|||
@model IndexModel |
|||
@inject IHtmlLocalizer<MyProjectNameResource> L |
|||
@inject ICurrentUser CurrentUser |
|||
@if (CurrentUser.IsAuthenticated) |
|||
{ |
|||
<div> |
|||
@inject IBrandingProvider BrandingProvider |
|||
@inject ITheme Theme |
|||
|
|||
@{ |
|||
Layout = Theme.GetEmptyLayout(); |
|||
} |
|||
|
|||
<div class="d-flex align-items-center" style="min-height: 100vh;"> |
|||
<div class="container"> |
|||
<abp-row> |
|||
<abp-column size-md="_3" class="text-center"> |
|||
<i class="fa fa-user d-block" style="font-size: 10em; color: #12b900"></i> |
|||
<a abp-button="Primary" href="/Account/Logout">Logout</a> |
|||
</abp-column> |
|||
<abp-column size-md="_9"> |
|||
<h2>@CurrentUser.UserName</h2> |
|||
<h5 class="text-muted">@CurrentUser.Email</h5> |
|||
<div> |
|||
<strong>Roles</strong>: @CurrentUser.Roles.JoinAsString(", ") |
|||
<br /> |
|||
<strong>Claims</strong>: <br /> |
|||
@Html.Raw(CurrentUser.GetAllClaims().Select(c => $"{c.Type}={c.Value}").JoinAsString(" <br /> ")) |
|||
|
|||
<div class="col mx-auto account-column"> |
|||
<div class="account-brand p-4 text-center mb-1"> |
|||
|
|||
@if (!BrandingProvider.LogoUrl.IsNullOrEmpty()) |
|||
{ |
|||
<a class="navbar-brand" href="/" alt="@BrandingProvider.AppName"></a> |
|||
} |
|||
else |
|||
{ |
|||
<h1>@BrandingProvider.AppName</h1> |
|||
} |
|||
</div> |
|||
</abp-column> |
|||
</abp-row> |
|||
</div> |
|||
} |
|||
<abp-card> |
|||
|
|||
<abp-card-body> |
|||
|
|||
<div class="container"> |
|||
<abp-row> |
|||
<abp-column size="_9"> |
|||
|
|||
<div class="d-flex align-items-center"> |
|||
<div class="me-3 p-2"> |
|||
<i class="fa fa-user d-block" style="font-size: 10em; color: #12b900"></i> |
|||
</div> |
|||
<div class="p2"> |
|||
@if (CurrentUser.IsAuthenticated) |
|||
{ |
|||
<span class="fs-16"> |
|||
@L["Welcome"] <span class="fw-7">@CurrentUser.UserName</span> |
|||
</span> |
|||
<span class="fs-14 d-block text-dark-800 opacity-75 mb-1">@CurrentUser.Email</span> |
|||
<div class="d-grid gap-2"> |
|||
<a abp-button="Outline_Primary" asp-controller="Manage" asp-action="Index" asp-area="Account">@L["MyAccount"]</a> |
|||
<a abp-button="Primary" asp-controller="Logout" asp-action="Index" asp-area="Account">@L["Logout"]</a> |
|||
</div> |
|||
} |
|||
else |
|||
{ |
|||
<a abp-button="Primary" asp-controller="Login" asp-action="Index" asp-area="Account">@L["Login"]</a> |
|||
} |
|||
</div> |
|||
|
|||
@if (!CurrentUser.IsAuthenticated) |
|||
{ |
|||
<div class="text-center"> |
|||
<i class="fa fa-user d-block" style="font-size: 10em; color: #aaa"></i><br/><br /> |
|||
<a abp-button="Primary" asp-page="/Account/Login">Login</a> |
|||
</div> |
|||
</abp-column> |
|||
<abp-column size="_3"> |
|||
<div class="ml-auto p-2 float-end"> |
|||
<abp-dropdown> |
|||
<abp-dropdown-button text="@Model.CurrentLanguage" /> |
|||
<abp-dropdown-menu> |
|||
@foreach (var language in Model.Languages) |
|||
{ |
|||
<abp-dropdown-item href="~/Abp/Languages/Switch?culture=@(language.CultureName)&uiCulture=@(language.UiCultureName)&returnUrl=@(System.Net.WebUtility.UrlEncode(Request.GetEncodedPathAndQuery()))">@language.DisplayName</abp-dropdown-item> |
|||
} |
|||
</abp-dropdown-menu> |
|||
</abp-dropdown> |
|||
</div> |
|||
</abp-column> |
|||
|
|||
</abp-row> |
|||
<hr class="m-4" /> |
|||
|
|||
<abp-row> |
|||
|
|||
@foreach (var application in Model.Applications) |
|||
{ |
|||
<abp-column size-md="_4" class="mb-2"> |
|||
<abp-card> |
|||
<abp-card-body> |
|||
|
|||
@if (!application.LogoUri.IsNullOrEmpty()) |
|||
{ |
|||
<div class="mx-auto"> |
|||
<img src="@application.LogoUri" style="height:64px" class="mb-3" /> |
|||
</div> |
|||
} |
|||
|
|||
<h4>@application.DisplayName</h4> |
|||
<span class="text-muted">@application.ClientUri</span> |
|||
<div class="mt-1"> |
|||
<a abp-button="Outline_Secondary" href="@application.ClientUri">@L["Visit"]</a> |
|||
</div> |
|||
</abp-card-body> |
|||
</abp-card> |
|||
</abp-column> |
|||
} |
|||
|
|||
</abp-row> |
|||
</div> |
|||
|
|||
</abp-card-body> |
|||
|
|||
</abp-card> |
|||
</div> |
|||
|
|||
</abp-row> |
|||
</div> |
|||
} |
|||
</div> |
|||
@ -1,10 +1,36 @@ |
|||
using System.Collections.Generic; |
|||
using System.Globalization; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.AspNetCore.Mvc.UI.RazorPages; |
|||
using Volo.Abp.Localization; |
|||
using Volo.Abp.OpenIddict.Applications; |
|||
|
|||
namespace MyCompanyName.MyProjectName.Pages; |
|||
|
|||
public class IndexModel : AbpPageModel |
|||
{ |
|||
public void OnGet() |
|||
public List<OpenIddictApplication> Applications { get; protected set; } |
|||
|
|||
public IReadOnlyList<LanguageInfo> Languages { get; protected set; } |
|||
|
|||
public string CurrentLanguage { get; protected set; } |
|||
|
|||
protected IOpenIddictApplicationRepository OpenIdApplicationRepository { get; } |
|||
|
|||
protected ILanguageProvider LanguageProvider { get; } |
|||
|
|||
public IndexModel(IOpenIddictApplicationRepository openIdApplicationmRepository, ILanguageProvider languageProvider) |
|||
{ |
|||
OpenIdApplicationRepository = openIdApplicationmRepository; |
|||
LanguageProvider = languageProvider; |
|||
} |
|||
|
|||
public async Task OnGetAsync() |
|||
{ |
|||
Applications = await OpenIdApplicationRepository.GetListAsync(); |
|||
|
|||
Languages = await LanguageProvider.GetLanguagesAsync(); |
|||
CurrentLanguage = CultureInfo.CurrentCulture.DisplayName; |
|||
} |
|||
} |
|||
|
|||
Loading…
Reference in new issue