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": { |
"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 |
@page |
||||
@using Microsoft.AspNetCore.Mvc.Localization |
@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bootstrap |
||||
@using Volo.CmsKit.Localization |
@using Volo.CmsKit.Contents |
||||
@using Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Pages |
@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 |
@model Volo.CmsKit.Public.Web.Pages.Public.CmsKit.Pages.IndexModel |
||||
|
|
||||
@inject IHtmlLocalizer<CmsKitResource> L |
|
||||
|
|
||||
@section styles{ |
@section styles{ |
||||
|
|
||||
<abp-style src="/Pages/Public/CmsKit/Pages/index.css" /> |
<abp-style src="/Pages/Public/CmsKit/Pages/index.css" /> |
||||
|
|
||||
<style> |
<style> |
||||
@Html.Raw(Model.Page.Style) |
@Html.Raw(Model.PageDto.Style) |
||||
</style> |
</style> |
||||
} |
} |
||||
|
|
||||
@section scripts{ |
@section scripts{ |
||||
<script> |
<script> |
||||
@Html.Raw(Model.Page.Script) |
@Html.Raw(Model.PageDto.Script) |
||||
</script> |
</script> |
||||
} |
} |
||||
|
|
||||
@await Component.InvokeAsync(typeof(DefaultPageViewComponent), |
<abp-card> |
||||
new |
<abp-card-body> |
||||
{ |
@foreach (ContentFragment contentFragment in Model.PageDto.ContentFragments) |
||||
pageId = Model.Page.Id, |
{ |
||||
title = Model.Page.Title, |
if (contentFragment.Type == ContentConsts.Markdown) |
||||
content = Model.Page.Content |
{ |
||||
}) |
@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 |
@page |
||||
|
@using Microsoft.AspNetCore.Http.Extensions |
||||
|
@using Microsoft.AspNetCore.Mvc.Localization |
||||
@using MyCompanyName.MyProjectName.Pages |
@using MyCompanyName.MyProjectName.Pages |
||||
|
@using MyCompanyName.MyProjectName.Localization |
||||
@using Volo.Abp.Users |
@using Volo.Abp.Users |
||||
|
@using Volo.Abp.AspNetCore.Mvc.UI.Theming |
||||
|
@using Volo.Abp.Ui.Branding |
||||
@model IndexModel |
@model IndexModel |
||||
|
@inject IHtmlLocalizer<MyProjectNameResource> L |
||||
@inject ICurrentUser CurrentUser |
@inject ICurrentUser CurrentUser |
||||
@if (CurrentUser.IsAuthenticated) |
@inject IBrandingProvider BrandingProvider |
||||
{ |
@inject ITheme Theme |
||||
<div> |
|
||||
|
@{ |
||||
|
Layout = Theme.GetEmptyLayout(); |
||||
|
} |
||||
|
|
||||
|
<div class="d-flex align-items-center" style="min-height: 100vh;"> |
||||
|
<div class="container"> |
||||
<abp-row> |
<abp-row> |
||||
<abp-column size-md="_3" class="text-center"> |
|
||||
<i class="fa fa-user d-block" style="font-size: 10em; color: #12b900"></i> |
<div class="col mx-auto account-column"> |
||||
<a abp-button="Primary" href="/Account/Logout">Logout</a> |
<div class="account-brand p-4 text-center mb-1"> |
||||
</abp-column> |
|
||||
<abp-column size-md="_9"> |
@if (!BrandingProvider.LogoUrl.IsNullOrEmpty()) |
||||
<h2>@CurrentUser.UserName</h2> |
{ |
||||
<h5 class="text-muted">@CurrentUser.Email</h5> |
<a class="navbar-brand" href="/" alt="@BrandingProvider.AppName"></a> |
||||
<div> |
} |
||||
<strong>Roles</strong>: @CurrentUser.Roles.JoinAsString(", ") |
else |
||||
<br /> |
{ |
||||
<strong>Claims</strong>: <br /> |
<h1>@BrandingProvider.AppName</h1> |
||||
@Html.Raw(CurrentUser.GetAllClaims().Select(c => $"{c.Type}={c.Value}").JoinAsString(" <br /> ")) |
} |
||||
</div> |
</div> |
||||
</abp-column> |
<abp-card> |
||||
</abp-row> |
|
||||
</div> |
<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> |
||||
{ |
</abp-column> |
||||
<div class="text-center"> |
<abp-column size="_3"> |
||||
<i class="fa fa-user d-block" style="font-size: 10em; color: #aaa"></i><br/><br /> |
<div class="ml-auto p-2 float-end"> |
||||
<a abp-button="Primary" asp-page="/Account/Login">Login</a> |
<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> |
||||
} |
</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.AspNetCore.Mvc.UI.RazorPages; |
||||
|
using Volo.Abp.Localization; |
||||
|
using Volo.Abp.OpenIddict.Applications; |
||||
|
|
||||
namespace MyCompanyName.MyProjectName.Pages; |
namespace MyCompanyName.MyProjectName.Pages; |
||||
|
|
||||
public class IndexModel : AbpPageModel |
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