mirror of https://github.com/abpframework/abp.git
212 changed files with 9732 additions and 6602 deletions
@ -1,64 +1,89 @@ |
|||
{ |
|||
"culture": "zh-Hans", |
|||
"texts": { |
|||
"Permission:CommunityArticle": "社区文章", |
|||
"Permission:Edit": "修改", |
|||
"Waiting": "等待中", |
|||
"Approved": "已批准", |
|||
"Rejected": "已拒绝", |
|||
"Wait": "等待", |
|||
"Approve": "批准", |
|||
"Reject": "拒绝", |
|||
"ReadArticle": "阅读文章", |
|||
"Status": "状态", |
|||
"ContentSource": "内容来源", |
|||
"Details": "详情", |
|||
"Url": "url", |
|||
"Title": "标题", |
|||
"CreationTime": "创建时间", |
|||
"Save": "保存", |
|||
"SameUrlAlreadyExist": "url已存在,如果你想要添加这篇文章,你需要更改url!", |
|||
"UrlIsNotValid": "Url无效.", |
|||
"UrlNotFound" : "Url未找到.", |
|||
"UrlContentNotFound": "Url内容未找到.", |
|||
"Summary": "摘要", |
|||
"MostRead": "阅读最多", |
|||
"Latest": "最新", |
|||
"ContributeAbpCommunity": "为ABP社区做贡献", |
|||
"SubmitYourArticle": "提交你的文章", |
|||
"ContributionGuide": "贡献指南", |
|||
"BugReport": "Bug报告", |
|||
"SeeAllArticles": "查看所有的文章", |
|||
"WelcomeToABPCommunity!": "欢迎来到ABP社区!", |
|||
"MyProfile": "我的资料", |
|||
"MyOrganizations": "我的组织", |
|||
"EmailNotValid": "请输入有效的电子邮箱地址.", |
|||
"FeatureRequest": "功能请求", |
|||
"CreateArticleTitleInfo": "文章标题显示在文章列表中.", |
|||
"CreateArticleUrlInfo": "文章的原始GitHub/外部URL.", |
|||
"CreateArticleSummaryInfo": "文章的简短摘要将显示在文章列表中.", |
|||
"CreateArticleCoverInfo": "为了创建有效的文章,请添加封面图. 仅支持16:9的图片!", |
|||
"ThisExtensionIsNotAllowed": "不允许此扩展名.", |
|||
"TheFileIsTooLarge": "文件过大.", |
|||
"GoToTheArticle": "转到文章", |
|||
"Contribute": "贡献", |
|||
"OverallProgress": "总体流程", |
|||
"Done": "完成", |
|||
"Open": "打开", |
|||
"Closed": "关闭", |
|||
"LatestQuestionOnThe": "有关的最新问题", |
|||
"Stackoverflow": "Stackoverflow", |
|||
"Votes": "票数", |
|||
"Answer": "回答", |
|||
"Views": "观看次数", |
|||
"Answered": "已回答", |
|||
"WaitingForYourAnswer": "等待你的回答", |
|||
"Asked": "提问", |
|||
"AllQuestions": "所有的问题", |
|||
"NextVersion": "下一个版本", |
|||
"MilestoneErrorMessage": "无法从Github获取当前的里程碑详细信息.", |
|||
"QuestionItemErrorMessage": "无法从Stackoverflow获取最新的问题详细信息.", |
|||
"Oops": "哎呀!" |
|||
} |
|||
"culture": "zh-Hans", |
|||
"texts": { |
|||
"Permission:CommunityArticle": "社区文章", |
|||
"Permission:Edit": "修改", |
|||
"Waiting": "等待中", |
|||
"Approved": "已批准", |
|||
"Rejected": "已拒绝", |
|||
"Wait": "等待", |
|||
"Approve": "批准", |
|||
"Reject": "拒绝", |
|||
"ReadArticle": "阅读文章", |
|||
"Status": "状态", |
|||
"ContentSource": "内容来源", |
|||
"Details": "详情", |
|||
"Url": "url", |
|||
"Title": "标题", |
|||
"CreationTime": "创建时间", |
|||
"Save": "保存", |
|||
"SameUrlAlreadyExist": "url已存在,如果你想要添加这篇文章,你需要更改url!", |
|||
"UrlIsNotValid": "Url无效.", |
|||
"UrlNotFound": "Url未找到.", |
|||
"UrlContentNotFound": "Url内容未找到.", |
|||
"Summary": "摘要", |
|||
"MostRead": "阅读最多", |
|||
"Latest": "最新", |
|||
"ContributeAbpCommunity": "为ABP社区做贡献", |
|||
"SubmitYourArticle": "提交你的文章", |
|||
"ContributionGuide": "贡献指南", |
|||
"BugReport": "Bug报告", |
|||
"SeeAllArticles": "查看所有的文章", |
|||
"WelcomeToABPCommunity!": "欢迎来到ABP社区!", |
|||
"MyProfile": "我的资料", |
|||
"MyOrganizations": "我的组织", |
|||
"EmailNotValid": "请输入有效的电子邮箱地址.", |
|||
"FeatureRequest": "功能请求", |
|||
"CreateArticleTitleInfo": "文章标题显示在文章列表中.", |
|||
"CreateArticleUrlInfo": "文章的原始GitHub/外部URL.", |
|||
"CreateArticleSummaryInfo": "文章的简短摘要将显示在文章列表中.", |
|||
"CreateArticleCoverInfo": "为了创建有效的文章,请添加封面图. 仅支持16:9的图片!", |
|||
"ThisExtensionIsNotAllowed": "不允许此扩展名.", |
|||
"TheFileIsTooLarge": "文件过大.", |
|||
"GoToTheArticle": "转到文章", |
|||
"Contribute": "贡献", |
|||
"OverallProgress": "总体流程", |
|||
"Done": "完成", |
|||
"Open": "打开", |
|||
"Closed": "关闭", |
|||
"LatestQuestionOnThe": "有关的最新问题", |
|||
"Stackoverflow": "Stackoverflow", |
|||
"Votes": "票数", |
|||
"Answer": "回答", |
|||
"Views": "观看次数", |
|||
"Answered": "已回答", |
|||
"WaitingForYourAnswer": "等待你的回答", |
|||
"Asked": "提问", |
|||
"AllQuestions": "所有的问题", |
|||
"NextVersion": "下一个版本", |
|||
"MilestoneErrorMessage": "无法从Github获取当前的里程碑详细信息.", |
|||
"QuestionItemErrorMessage": "无法从Stackoverflow获取最新的问题详细信息.", |
|||
"Oops": "哎呀!", |
|||
"CreateArticleSuccessMessage": "文章提交成功. 网站管理员审核通过后将被发布.", |
|||
"ChooseCoverImage": "选项一张封面图片", |
|||
"CoverImage": "封面图片", |
|||
"ShareYourExperiencesWithTheABPFramework": "分享你的ABP Framework经验!", |
|||
"Optional": "可选的", |
|||
"UpdateUserWebSiteInfo": "示例: https://johndoe.com", |
|||
"UpdateUserTwitterInfo": "示例: johndoe", |
|||
"UpdateUserGithubInfo": "示例: johndoe", |
|||
"UpdateUserLinkedinInfo": "示例: https://www.linkedin.com/...", |
|||
"UpdateUserCompanyInfo": "示例v: Volosoft", |
|||
"UpdateUserJobTitleInfo": "示例: Software Developer", |
|||
"UserName": "用户名", |
|||
"Company": "公司", |
|||
"PersonalWebsite": "个人网站", |
|||
"RegistrationDate": "注册时间", |
|||
"Social": "社交", |
|||
"Biography": "传记", |
|||
"HasNoPublishedArticlesYet": "尚未发表文章", |
|||
"Author": "作者", |
|||
"LatestGithubAnnouncements": "最新的Github公告", |
|||
"SeeAllAnnouncements": "查看所有公告", |
|||
"LatestBlogPost": "最新博客文章", |
|||
"Edit": "修改", |
|||
"ProfileImageChange": "更改资料图片", |
|||
"BlogItemErrorMessage": "无法从ABP获取最新的博客文章详细信息.", |
|||
"PlannedReleaseDate": "计划发布日期" |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,50 @@ |
|||
# ABP Framework 3.3 to 4.0 Migration Guide |
|||
|
|||
## Identity Server Changes |
|||
|
|||
ABP Framework upgrades the [IdentityServer4](https://www.nuget.org/packages/IdentityServer4) library from 3.x to 4.x with the ABP Framework version 4.0. IdentityServer 4.x has a lot of changes, some of them are **breaking changes in the data structure**. |
|||
|
|||
### Database Changes |
|||
|
|||
**So, if you are upgrading from 3.x, then there are some change should be done in your database.** |
|||
|
|||
#### ApiScope |
|||
|
|||
As the **most important breaking change**, Identity Server 4.x places the `ApiScope` as an independent aggregate root. Previously it was a part of the to `ApiResource` aggregate. This requires manual operation. See the *Database Migration* section. |
|||
|
|||
Also, added `Enabled(string)` and `Description(bool,true)` properties. |
|||
|
|||
#### Client |
|||
|
|||
* Added `RequireRequestObject (bool)` and `AllowedIdentityTokenSigningAlgorithms (string)` properties. |
|||
* Changed default value of `RequireConsent` from `true` to `false`. |
|||
* Changed default value of `RequirePkce` from `false` to `true`. |
|||
|
|||
#### DeviceFlowCodes |
|||
|
|||
* Added `SessionId (string)` and `Description (string)` properties. |
|||
|
|||
#### PersistedGrant |
|||
|
|||
* Added `SessionId (string)` and `ShowInDiscoveryDocument (bool, default: true)` properties |
|||
|
|||
#### ApiResource |
|||
|
|||
* Added `AllowedAccessTokenSigningAlgorithms (string)` and `Description (string)` and `ConsumedTime (DateTime?)` properties |
|||
|
|||
## Migrating the Database |
|||
|
|||
> Attention: **Please backup your database** before the migration! |
|||
|
|||
If you are using **Entity Framework Core**, you need to add a new database migration, using the `Add-Migration` command, and apply changes to the database. Please **review the migration** script to understand if it effects your existing data. Otherwise, you may **loose some of your configuration**. |
|||
|
|||
If you haven't customize the `IdentityServerDataSeedContributor` and haven't customized the initial data inside the `IdentityServer*` tables; |
|||
|
|||
1. Update `IdentityServerDataSeedContributor` class by comparing to [the latest code](https://github.com/abpframework/abp/blob/dev/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain/IdentityServer/IdentityServerDataSeedContributor.cs). You probably only need to add the `CreateApiScopesAsync` method and the code related to it. |
|||
2. Then you can simply clear all the **table data** in these tables then execute the `DbMigrator` application again to fill it with the new configuration. |
|||
|
|||
If you've customize your IdentityServer configuration in the database or in the seed data, you should understand the changes and upgrade your code/data accordingly. |
|||
|
|||
### Related Resources |
|||
* https://leastprivilege.com/2020/06/19/announcing-identityserver4-v4-0/ |
|||
* https://github.com/IdentityServer/IdentityServer4/issues/4592 |
|||
@ -0,0 +1,48 @@ |
|||
@using Volo.Abp.UI.Navigation |
|||
@{ |
|||
var elementId = MenuItem.ElementId ?? "MenuItem_" + MenuItem.Name.Replace(".", "_"); |
|||
var cssClass = string.IsNullOrEmpty(MenuItem.CssClass) ? string.Empty : MenuItem.CssClass; |
|||
var disabled = MenuItem.IsDisabled ? "disabled" : string.Empty; |
|||
var url = string.IsNullOrEmpty(MenuItem.Url) ? "#" : MenuItem.Url; |
|||
} |
|||
@if (MenuItem.IsLeaf) |
|||
{ |
|||
if (MenuItem.Url != null) |
|||
{ |
|||
<li class="nav-item @cssClass @disabled" id="@elementId"> |
|||
<a class="nav-link" href="@url"> |
|||
@if (MenuItem.Icon != null) |
|||
{ |
|||
if (MenuItem.Icon.StartsWith("fa")) |
|||
{ |
|||
<i class="@MenuItem.Icon"></i> |
|||
} |
|||
} |
|||
@MenuItem.DisplayName |
|||
</a> |
|||
</li> |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
<li class="nav-item"> |
|||
<div class="dropdown"> |
|||
<a class="nav-link dropdown-toggle" @onclick="ToggleSubMenu" id="Menu_@(MenuItem.Name)" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> |
|||
@if (MenuItem.Icon != null) |
|||
{ |
|||
if (MenuItem.Icon.StartsWith("fa")) |
|||
{ |
|||
<i class="@MenuItem.Icon"></i> |
|||
} |
|||
} |
|||
@MenuItem.DisplayName |
|||
</a> |
|||
<div class="dropdown-menu border-0 shadow-sm @(IsSubMenuOpen ? "show" : "")" aria-labelledby="Menu_@(MenuItem.Name)"> |
|||
@foreach (var childMenuItem in MenuItem.Items) |
|||
{ |
|||
<SecondLevelNavMenuItem MenuItem="childMenuItem"/> |
|||
} |
|||
</div> |
|||
</div> |
|||
</li> |
|||
} |
|||
@ -0,0 +1,38 @@ |
|||
using System; |
|||
using Microsoft.AspNetCore.Components; |
|||
using Microsoft.AspNetCore.Components.Routing; |
|||
using Volo.Abp.UI.Navigation; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme.Themes.Basic |
|||
{ |
|||
public partial class FirstLevelNavMenuItem : IDisposable |
|||
{ |
|||
[Inject] private NavigationManager NavigationManager { get; set; } |
|||
|
|||
[Parameter] |
|||
public ApplicationMenuItem MenuItem { get; set; } |
|||
|
|||
public bool IsSubMenuOpen { get; set; } |
|||
|
|||
protected override void OnInitialized() |
|||
{ |
|||
NavigationManager.LocationChanged += OnLocationChanged; |
|||
} |
|||
|
|||
private void ToggleSubMenu() |
|||
{ |
|||
IsSubMenuOpen = !IsSubMenuOpen; |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
NavigationManager.LocationChanged -= OnLocationChanged; |
|||
} |
|||
|
|||
private void OnLocationChanged(object sender, LocationChangedEventArgs e) |
|||
{ |
|||
IsSubMenuOpen = false; |
|||
StateHasChanged(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
using System; |
|||
using Microsoft.AspNetCore.Components; |
|||
using Microsoft.AspNetCore.Components.Routing; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme.Themes.Basic |
|||
{ |
|||
public partial class MainLayout : IDisposable |
|||
{ |
|||
[Inject] private NavigationManager NavigationManager { get; set; } |
|||
|
|||
private bool IsCollapseShown { get; set; } |
|||
|
|||
protected override void OnInitialized() |
|||
{ |
|||
NavigationManager.LocationChanged += OnLocationChanged; |
|||
} |
|||
|
|||
private void ToggleCollapse() |
|||
{ |
|||
IsCollapseShown = !IsCollapseShown; |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
NavigationManager.LocationChanged -= OnLocationChanged; |
|||
} |
|||
|
|||
private void OnLocationChanged(object sender, LocationChangedEventArgs e) |
|||
{ |
|||
IsCollapseShown = false; |
|||
StateHasChanged(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,38 @@ |
|||
using System; |
|||
using Microsoft.AspNetCore.Components; |
|||
using Microsoft.AspNetCore.Components.Routing; |
|||
using Volo.Abp.UI.Navigation; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme.Themes.Basic |
|||
{ |
|||
public partial class SecondLevelNavMenuItem : IDisposable |
|||
{ |
|||
[Inject] private NavigationManager NavigationManager { get; set; } |
|||
|
|||
[Parameter] |
|||
public ApplicationMenuItem MenuItem { get; set; } |
|||
|
|||
public bool IsSubMenuOpen { get; set; } |
|||
|
|||
protected override void OnInitialized() |
|||
{ |
|||
NavigationManager.LocationChanged += OnLocationChanged; |
|||
} |
|||
|
|||
private void ToggleSubMenu() |
|||
{ |
|||
IsSubMenuOpen = !IsSubMenuOpen; |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
NavigationManager.LocationChanged -= OnLocationChanged; |
|||
} |
|||
|
|||
private void OnLocationChanged(object sender, LocationChangedEventArgs e) |
|||
{ |
|||
IsSubMenuOpen = false; |
|||
StateHasChanged(); |
|||
} |
|||
} |
|||
} |
|||
@ -1,16 +0,0 @@ |
|||
$(function () { |
|||
$('.dropdown-menu a.dropdown-toggle').on('click', function (e) { |
|||
if (!$(this).next().hasClass('show')) { |
|||
$(this).parents('.dropdown-menu').first().find('.show').removeClass("show"); |
|||
} |
|||
|
|||
var $subMenu = $(this).next(".dropdown-menu"); |
|||
$subMenu.toggleClass('show'); |
|||
|
|||
$(this).parents('li.nav-item.dropdown.show').on('hidden.bs.dropdown', function (e) { |
|||
$('.dropdown-submenu .show').removeClass("show"); |
|||
}); |
|||
|
|||
return false; |
|||
}); |
|||
}); |
|||
@ -1,35 +0,0 @@ |
|||
var abp = abp || {}; |
|||
(function () { |
|||
abp.utils = abp.utils || {}; |
|||
|
|||
abp.utils.addClassToTag = function (tagName, className) { |
|||
var tags = document.getElementsByTagName(tagName); |
|||
for (var i = 0; i < tags.length; i++) { |
|||
tags[i].classList.add(className); |
|||
} |
|||
}; |
|||
|
|||
abp.utils.removeClassFromTag = function (tagName, className) { |
|||
var tags = document.getElementsByTagName(tagName); |
|||
for (var i = 0; i < tags.length; i++) { |
|||
tags[i].classList.remove(className); |
|||
} |
|||
}; |
|||
|
|||
abp.utils.hasClassOnTag = function (tagName, className) { |
|||
var tags = document.getElementsByTagName(tagName); |
|||
if (tags.length) { |
|||
return tags[0].classList.contains(className); |
|||
} |
|||
|
|||
return false; |
|||
}; |
|||
|
|||
abp.utils.replaceLinkHrefById = function (linkId, hrefValue) { |
|||
var link = document.getElementById(linkId); |
|||
|
|||
if (link && link.href !== hrefValue) { |
|||
link.href = hrefValue; |
|||
} |
|||
}; |
|||
})(); |
|||
@ -0,0 +1,11 @@ |
|||
using System; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Components.WebAssembly |
|||
{ |
|||
public class CookieOptions |
|||
{ |
|||
public DateTimeOffset? ExpireDate { get; set; } |
|||
public string Path { get; set; } |
|||
public bool Secure { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
using System.Threading.Tasks; |
|||
using Microsoft.JSInterop; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Components.WebAssembly |
|||
{ |
|||
[Dependency(ReplaceServices = true)] |
|||
public class CookieService : ICookieService, ITransientDependency |
|||
{ |
|||
public IJSRuntime JsRuntime { get; } |
|||
|
|||
public CookieService(IJSRuntime jsRuntime) |
|||
{ |
|||
JsRuntime = jsRuntime; |
|||
} |
|||
|
|||
public async ValueTask SetAsync(string key, string value, CookieOptions options) |
|||
{ |
|||
await JsRuntime.InvokeVoidAsync("abp.utils.setCookieValue", key, value, options?.ExpireDate?.ToString("r"), options?.Path, options?.Secure); |
|||
} |
|||
|
|||
public async ValueTask<string> GetAsync(string key) |
|||
{ |
|||
return await JsRuntime.InvokeAsync<string>("abp.utils.getCookieValue", key); |
|||
} |
|||
|
|||
public async ValueTask DeleteAsync(string key, string path = null) |
|||
{ |
|||
await JsRuntime.InvokeVoidAsync("abp.utils.deleteCookie", key); |
|||
} |
|||
} |
|||
} |
|||
@ -1,6 +1,6 @@ |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Components.WebAssembly.Theming |
|||
namespace Volo.Abp.AspNetCore.Components.WebAssembly |
|||
{ |
|||
public interface IAbpUtilsService |
|||
{ |
|||
@ -0,0 +1,12 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Components.WebAssembly |
|||
{ |
|||
public interface ICookieService |
|||
{ |
|||
public ValueTask SetAsync(string key, string value, CookieOptions options = null); |
|||
public ValueTask<string> GetAsync(string key); |
|||
public ValueTask DeleteAsync(string key, string path = null); |
|||
} |
|||
} |
|||
@ -0,0 +1,116 @@ |
|||
var abp = abp || {}; |
|||
(function () { |
|||
abp.utils = abp.utils || {}; |
|||
|
|||
// COOKIES ///////////////////////////////////////////////////////
|
|||
|
|||
/** |
|||
* Sets a cookie value for given key. |
|||
* This is a simple implementation created to be used by ABP. |
|||
* Please use a complete cookie library if you need. |
|||
* @param {string} key |
|||
* @param {string} value |
|||
* @param {string} expireDate (optional). If not specified the cookie will expire at the end of session. |
|||
* @param {string} path (optional) |
|||
* @param {bool} secure (optional) |
|||
*/ |
|||
abp.utils.setCookieValue = function (key, value, expireDate, path, secure) { |
|||
var cookieValue = encodeURIComponent(key) + '='; |
|||
if (value) { |
|||
cookieValue = cookieValue + encodeURIComponent(value); |
|||
} |
|||
|
|||
if (expireDate) { |
|||
cookieValue = cookieValue + "; expires=" + expireDate; |
|||
} |
|||
|
|||
if (path) { |
|||
cookieValue = cookieValue + "; path=" + path; |
|||
} |
|||
|
|||
if (secure) { |
|||
cookieValue = cookieValue + "; secure"; |
|||
} |
|||
|
|||
document.cookie = cookieValue; |
|||
}; |
|||
|
|||
/** |
|||
* Gets a cookie with given key. |
|||
* This is a simple implementation created to be used by ABP. |
|||
* Please use a complete cookie library if you need. |
|||
* @param {string} key |
|||
* @returns {string} Cookie value or null |
|||
*/ |
|||
abp.utils.getCookieValue = function (key) { |
|||
var equalities = document.cookie.split('; '); |
|||
for (var i = 0; i < equalities.length; i++) { |
|||
if (!equalities[i]) { |
|||
continue; |
|||
} |
|||
|
|||
var splitted = equalities[i].split('='); |
|||
if (splitted.length != 2) { |
|||
continue; |
|||
} |
|||
|
|||
if (decodeURIComponent(splitted[0]) === key) { |
|||
return decodeURIComponent(splitted[1] || ''); |
|||
} |
|||
} |
|||
|
|||
return null; |
|||
}; |
|||
|
|||
/** |
|||
* Deletes cookie for given key. |
|||
* This is a simple implementation created to be used by ABP. |
|||
* Please use a complete cookie library if you need. |
|||
* @param {string} key |
|||
* @param {string} path (optional) |
|||
*/ |
|||
abp.utils.deleteCookie = function (key, path) { |
|||
var cookieValue = encodeURIComponent(key) + '='; |
|||
|
|||
cookieValue = cookieValue + "; expires=" + (new Date(new Date().getTime() - 86400000)).toUTCString(); |
|||
|
|||
if (path) { |
|||
cookieValue = cookieValue + "; path=" + path; |
|||
} |
|||
|
|||
document.cookie = cookieValue; |
|||
} |
|||
|
|||
// DOM MANIPULATION
|
|||
|
|||
abp.utils.addClassToTag = function (tagName, className) { |
|||
var tags = document.getElementsByTagName(tagName); |
|||
for (var i = 0; i < tags.length; i++) { |
|||
tags[i].classList.add(className); |
|||
} |
|||
}; |
|||
|
|||
abp.utils.removeClassFromTag = function (tagName, className) { |
|||
var tags = document.getElementsByTagName(tagName); |
|||
for (var i = 0; i < tags.length; i++) { |
|||
tags[i].classList.remove(className); |
|||
} |
|||
}; |
|||
|
|||
abp.utils.hasClassOnTag = function (tagName, className) { |
|||
var tags = document.getElementsByTagName(tagName); |
|||
if (tags.length) { |
|||
return tags[0].classList.contains(className); |
|||
} |
|||
|
|||
return false; |
|||
}; |
|||
|
|||
abp.utils.replaceLinkHrefById = function (linkId, hrefValue) { |
|||
var link = document.getElementById(linkId); |
|||
|
|||
if (link && link.href !== hrefValue) { |
|||
link.href = hrefValue; |
|||
} |
|||
}; |
|||
})(); |
|||
@ -0,0 +1,20 @@ |
|||
using Blazorise; |
|||
|
|||
namespace Volo.Abp.BlazoriseUI |
|||
{ |
|||
public class BreadcrumbItem |
|||
{ |
|||
public string Text { get; set; } |
|||
|
|||
public object Icon { get; set; } |
|||
|
|||
public string Url { get; set; } |
|||
|
|||
public BreadcrumbItem(string text, string url = null, object icon = null) |
|||
{ |
|||
Text = text; |
|||
Url = url; |
|||
Icon = icon; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,37 @@ |
|||
<Row Class="entry-row"> |
|||
<Column ColumnSize="ColumnSize.IsAuto"> |
|||
<h1 class="content-header-title">@Title</h1> |
|||
</Column> |
|||
@if (BreadcrumbItems.Any()) |
|||
{ |
|||
<Column ColumnSize="ColumnSize.IsAuto.OnWidescreen" Padding="Padding.Is0.FromLeft.OnWidescreen"> |
|||
<Breadcrumb Mode="@(BreadcrumbShowCurrent ? BreadcrumbMode.Auto : BreadcrumbMode.None)"> |
|||
@if (BreadcrumbShowHome) |
|||
{ |
|||
<BreadcrumbItem> |
|||
<BreadcrumbLink To="/"> |
|||
<Icon Name="IconName.Home" /> |
|||
</BreadcrumbLink> |
|||
</BreadcrumbItem> |
|||
} |
|||
@foreach (var item in BreadcrumbItems) |
|||
{ |
|||
<BreadcrumbItem> |
|||
<BreadcrumbLink To="@item.Url"> |
|||
@if (item.Icon != null) |
|||
{ |
|||
<Icon Name="@item.Icon" /> |
|||
} |
|||
@item.Text |
|||
</BreadcrumbLink> |
|||
</BreadcrumbItem> |
|||
} |
|||
</Breadcrumb> |
|||
</Column> |
|||
} |
|||
<Column> |
|||
<Row Class="justify-content-end mx-n1"> |
|||
@ChildContent |
|||
</Row> |
|||
</Column> |
|||
</Row> |
|||
@ -0,0 +1,29 @@ |
|||
using System.Collections.Generic; |
|||
using Blazorise; |
|||
using Microsoft.AspNetCore.Components; |
|||
|
|||
namespace Volo.Abp.BlazoriseUI.Components |
|||
{ |
|||
public partial class PageHeader : ComponentBase |
|||
{ |
|||
[Parameter] |
|||
public string Title { get; set; } |
|||
|
|||
[Parameter] |
|||
public bool BreadcrumbShowHome { get; set; } = true; |
|||
|
|||
[Parameter] |
|||
public bool BreadcrumbShowCurrent { get; set; } = true; |
|||
|
|||
[Parameter] |
|||
public RenderFragment ChildContent { get; set; } |
|||
|
|||
[Parameter] |
|||
public List<BreadcrumbItem> BreadcrumbItems { get; set; } |
|||
|
|||
public PageHeader() |
|||
{ |
|||
BreadcrumbItems = new List<BreadcrumbItem>(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,6 @@ |
|||
{ |
|||
"culture": "zh-Hans", |
|||
"texts": { |
|||
"hello": "你好" |
|||
} |
|||
} |
|||
@ -0,0 +1,6 @@ |
|||
{ |
|||
"culture": "zh-Hans", |
|||
"texts": { |
|||
"Volo.Abp.Http.DynamicProxying:10001": "业务异常: {0}" |
|||
} |
|||
} |
|||
@ -0,0 +1,6 @@ |
|||
{ |
|||
"culture": "zh-Hans", |
|||
"texts": { |
|||
"SeeYou": "再见" |
|||
} |
|||
} |
|||
@ -0,0 +1,90 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Shouldly; |
|||
using Volo.Abp.Domain.Repositories; |
|||
using Volo.Abp.TestApp.Domain; |
|||
using Volo.Abp.TestApp.Testing; |
|||
using Volo.Abp.Uow; |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.MongoDB.Transactions |
|||
{ |
|||
public class Transaction_Tests : TestAppTestBase<AbpMongoDbTestModule> |
|||
{ |
|||
private readonly IBasicRepository<Person, Guid> _personRepository; |
|||
private readonly IUnitOfWorkManager _unitOfWorkManager; |
|||
|
|||
public Transaction_Tests() |
|||
{ |
|||
_personRepository = GetRequiredService<IBasicRepository<Person, Guid>>(); |
|||
_unitOfWorkManager = GetRequiredService<IUnitOfWorkManager>(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_Rollback_Transaction_When_An_Exception_Is_Thrown() |
|||
{ |
|||
var personId = Guid.NewGuid(); |
|||
const string exceptionMessage = "thrown to rollback the transaction!"; |
|||
|
|||
try |
|||
{ |
|||
await WithUnitOfWorkAsync(new AbpUnitOfWorkOptions { IsTransactional = true }, async () => |
|||
{ |
|||
await _personRepository.InsertAsync(new Person(personId, "Adam", 42)); |
|||
throw new Exception(exceptionMessage); |
|||
}); |
|||
} |
|||
catch (Exception e) when (e.Message == exceptionMessage) |
|||
{ |
|||
|
|||
} |
|||
|
|||
var person = await _personRepository.FindAsync(personId); |
|||
person.ShouldBeNull(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_Rollback_Transaction_Manually() |
|||
{ |
|||
var personId = Guid.NewGuid(); |
|||
|
|||
await WithUnitOfWorkAsync(new AbpUnitOfWorkOptions { IsTransactional = true }, async () => |
|||
{ |
|||
_unitOfWorkManager.Current.ShouldNotBeNull(); |
|||
|
|||
await _personRepository.InsertAsync(new Person(personId, "Adam", 42)); |
|||
|
|||
await _unitOfWorkManager.Current.RollbackAsync(); |
|||
}); |
|||
|
|||
var person = await _personRepository.FindAsync(personId); |
|||
person.ShouldBeNull(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_Rollback_Transaction_Manually_With_Double_DbContext_Transaction() |
|||
{ |
|||
var personId = Guid.NewGuid(); |
|||
var bookId = Guid.NewGuid(); |
|||
|
|||
using (var scope = ServiceProvider.CreateScope()) |
|||
{ |
|||
var uowManager = scope.ServiceProvider.GetRequiredService<IUnitOfWorkManager>(); |
|||
|
|||
using (uowManager.Begin(new AbpUnitOfWorkOptions { IsTransactional = true })) |
|||
{ |
|||
_unitOfWorkManager.Current.ShouldNotBeNull(); |
|||
|
|||
await _personRepository.InsertAsync(new Person(personId, "Adam", 42)); |
|||
|
|||
await _unitOfWorkManager.Current.SaveChangesAsync(); |
|||
|
|||
//Will automatically rollback since not called the Complete!
|
|||
} |
|||
} |
|||
|
|||
(await _personRepository.FindAsync(personId)).ShouldBeNull(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
{ |
|||
"culture": "zh-Hans", |
|||
"texts": { |
|||
"HelloText": "你好 {0}", |
|||
"HowAreYou": "你好吗?" |
|||
} |
|||
} |
|||
@ -1,4 +1,4 @@ |
|||
@using Localization.Resources.AbpUi |
|||
@using Microsoft.AspNetCore.Mvc.Localization |
|||
@inject IHtmlLocalizer<AbpUiResource> L |
|||
<a href="~/Account/Login">@L["Login"]</a> |
|||
<a class="nav-link" role="button" href="~/Account/Login">@L["Login"]</a> |
|||
|
|||
@ -1,56 +1,58 @@ |
|||
{ |
|||
"culture": "zh-Hans", |
|||
"texts": { |
|||
"Menu:Blogs": "博客", |
|||
"Menu:BlogManagement": "博客管理", |
|||
"Permission:Management": "管理", |
|||
"Permission:Edit": "编辑", |
|||
"Permission:Create": "创建", |
|||
"Permission:Delete": "删除", |
|||
"Permission:Blogging": "博客", |
|||
"Permission:Blogs": "博客", |
|||
"Permission:Posts": "帖子", |
|||
"Permission:Tags": "标签", |
|||
"Permission:Comments": "评论", |
|||
"Title": "标题", |
|||
"Delete": "删除", |
|||
"Reply": "回复", |
|||
"ReplyTo": "回复 {0}", |
|||
"ContinueReading": "继续阅读", |
|||
"DaysAgo": "{0}天前", |
|||
"YearsAgo": "{0}年前", |
|||
"MonthsAgo": "{0}月前", |
|||
"WeeksAgo": "{0}周前", |
|||
"MinutesAgo": "{0}分前", |
|||
"SecondsAgo": "{0}秒前", |
|||
"HoursAgo": "{0}小时前", |
|||
"Now": "刚刚", |
|||
"Content": "内容", |
|||
"SeeAll": "查看所有", |
|||
"PopularTags": "热门标签", |
|||
"WiewsWithCount": "{0}人查看", |
|||
"LastPosts": "最后的帖子", |
|||
"LeaveComment": "发布评论", |
|||
"TagsInThisArticle": "文章中的标签", |
|||
"Posts": "帖子", |
|||
"Edit": "编辑", |
|||
"BLOG": "博客", |
|||
"CommentDeletionWarningMessage": "评论将被删除.", |
|||
"PostDeletionWarningMessage": "帖子将被删除", |
|||
"BlogDeletionWarningMessage": "博客将被删除.", |
|||
"AreYouSure": "你确定吗?", |
|||
"CommentWithCount": "{0}个评论", |
|||
"Comment": "评论", |
|||
"ShareOnTwitter": "分享到Twitter", |
|||
"CoverImage": "封面图", |
|||
"CreateANewPost": "创建一个新帖子", |
|||
"CreateANewBlog": "创建一个新博客", |
|||
"WhatIsNew": "最新消息", |
|||
"Name": "名称", |
|||
"ShortName": "简称", |
|||
"CreationTime": "创建时间", |
|||
"Description": "描述", |
|||
"Blogs": "博客", |
|||
"Tags": "标签" |
|||
"culture": "zh-Hans", |
|||
"texts": { |
|||
"Menu:Blogs": "博客", |
|||
"Menu:BlogManagement": "博客管理", |
|||
"Permission:Management": "管理", |
|||
"Permission:Edit": "编辑", |
|||
"Permission:Create": "创建", |
|||
"Permission:Delete": "删除", |
|||
"Permission:Blogging": "博客", |
|||
"Permission:Blogs": "博客", |
|||
"Permission:Posts": "帖子", |
|||
"Permission:Tags": "标签", |
|||
"Permission:Comments": "评论", |
|||
"Title": "标题", |
|||
"Delete": "删除", |
|||
"Reply": "回复", |
|||
"ReplyTo": "回复 {0}", |
|||
"ContinueReading": "继续阅读", |
|||
"DaysAgo": "{0}天前", |
|||
"YearsAgo": "{0}年前", |
|||
"MonthsAgo": "{0}月前", |
|||
"WeeksAgo": "{0}周前", |
|||
"MinutesAgo": "{0}分前", |
|||
"SecondsAgo": "{0}秒前", |
|||
"HoursAgo": "{0}小时前", |
|||
"Now": "刚刚", |
|||
"Content": "内容", |
|||
"SeeAll": "查看所有", |
|||
"PopularTags": "热门标签", |
|||
"WiewsWithCount": "{0}人查看", |
|||
"LastPosts": "最后的帖子", |
|||
"LeaveComment": "发布评论", |
|||
"TagsInThisArticle": "文章中的标签", |
|||
"Posts": "帖子", |
|||
"Edit": "编辑", |
|||
"BLOG": "博客", |
|||
"CommentDeletionWarningMessage": "评论将被删除.", |
|||
"PostDeletionWarningMessage": "帖子将被删除", |
|||
"BlogDeletionWarningMessage": "博客将被删除.", |
|||
"AreYouSure": "你确定吗?", |
|||
"CommentWithCount": "{0}个评论", |
|||
"Comment": "评论", |
|||
"ShareOnTwitter": "分享到Twitter", |
|||
"CoverImage": "封面图", |
|||
"CreateANewPost": "创建一个新帖子", |
|||
"CreateANewBlog": "创建一个新博客", |
|||
"WhatIsNew": "最新消息", |
|||
"Name": "名称", |
|||
"ShortName": "简称", |
|||
"CreationTime": "创建时间", |
|||
"Description": "描述", |
|||
"Blogs": "博客", |
|||
"Tags": "标签", |
|||
"ShareOn": "分享", |
|||
"TitleLengthWarning": "保持标题大小不超过60个字符,实现SEO友好!" |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
{ |
|||
"culture": "zh-Hans", |
|||
"texts": { |
|||
"PickYourReaction": "选择你的回应", |
|||
"YourComment": "你的评论", |
|||
"YourReply": "你的回复", |
|||
"Comments": "评论", |
|||
"Send": "发送", |
|||
"Delete": "删除", |
|||
"Reply": "回复", |
|||
"Update": "更新", |
|||
"Edit": "修改", |
|||
"LoginToAddComment": "登录添加评论", |
|||
"LoginToReply": "登录进行回复", |
|||
"MessageDeletionConfirmationMessage": "这条评论将被完全删除", |
|||
"CommentAuthorizationExceptionMessage": "这些评论不允许公开显示", |
|||
"Undo": "撤消", |
|||
"RatingUndoMessage": "您的评分将被撤消", |
|||
"LoginToRate": "登录进行评分", |
|||
"Star": "星" |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
namespace Volo.Abp.IdentityServer.ApiResources |
|||
{ |
|||
public class ApiResourcePropertyConsts |
|||
{ |
|||
public static int KeyMaxLength { get; set; } = 250; |
|||
|
|||
public static int ValueMaxLength { get; set; } = 2000; |
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
namespace Volo.Abp.IdentityServer.ApiResources |
|||
{ |
|||
public class ApiResourceScopeConsts |
|||
{ |
|||
public static int ScopeMaxLength { get; set; } = 200; |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
namespace Volo.Abp.IdentityServer.ApiResources |
|||
{ |
|||
public class ApiResourceSecretConsts |
|||
{ |
|||
/// <summary>
|
|||
/// Default value: 250
|
|||
/// </summary>
|
|||
public static int TypeMaxLength { get; set; } = 250; |
|||
|
|||
/// <summary>
|
|||
/// Default value: 4000
|
|||
/// </summary>
|
|||
public static int ValueMaxLength { get; set; } = 4000; |
|||
|
|||
/// <summary>
|
|||
/// Default value: 1000
|
|||
/// </summary>
|
|||
public static int DescriptionMaxLength { get; set; } = 1000; |
|||
} |
|||
} |
|||
@ -1,9 +0,0 @@ |
|||
namespace Volo.Abp.IdentityServer.ApiResources |
|||
{ |
|||
public class ApiScopeConsts |
|||
{ |
|||
public const int NameMaxLength = 200; |
|||
public const int DisplayNameMaxLength = 200; |
|||
public const int DescriptionMaxLength = 1000; |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
namespace Volo.Abp.IdentityServer.ApiScopes |
|||
{ |
|||
public class ApiScopeConsts |
|||
{ |
|||
public static int NameMaxLength { get; set; } = 200; |
|||
|
|||
public static int DisplayNameMaxLength { get; set; } = 200; |
|||
|
|||
public static int DescriptionMaxLength { get; set; } = 1000; |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
namespace Volo.Abp.IdentityServer.ApiScopes |
|||
{ |
|||
public class ApiScopePropertyConsts |
|||
{ |
|||
public static int KeyMaxLength { get; set; } = 250; |
|||
|
|||
public static int ValueMaxLength { get; set; } = 2000; |
|||
} |
|||
} |
|||
@ -1,22 +1,20 @@ |
|||
namespace Volo.Abp.IdentityServer |
|||
namespace Volo.Abp.IdentityServer.Clients |
|||
{ |
|||
public class SecretConsts |
|||
public class ClientSecretConsts |
|||
{ |
|||
/// <summary>
|
|||
/// Default value: 250
|
|||
/// </summary>
|
|||
public static int TypeMaxLength { get; set; } = 250; |
|||
|
|||
|
|||
/// <summary>
|
|||
/// Default value: 4000
|
|||
/// </summary>
|
|||
public static int ValueMaxLength { get; set; } = 4000; |
|||
|
|||
public static int ValueMaxLengthValue { get; set; } = ValueMaxLength; |
|||
|
|||
|
|||
/// <summary>
|
|||
/// Default value: 2000
|
|||
/// </summary>
|
|||
public static int DescriptionMaxLength { get; set; } = 2000; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
namespace Volo.Abp.IdentityServer.Devices |
|||
{ |
|||
public class DeviceFlowCodesConsts |
|||
{ |
|||
public static int DeviceCodeMaxLength { get; set; } = 200; |
|||
|
|||
public static int UserCodeMaxLength { get; set; } = 200; |
|||
|
|||
public static int SubjectIdMaxLength { get; set; } = 200; |
|||
|
|||
public static int SessionIdMaxLength { get; set; } = 100; |
|||
|
|||
public static int DescriptionMaxLength { get; set; } = 200; |
|||
|
|||
public static int ClientIdMaxLength { get; set; } = 200; |
|||
|
|||
public static int DataMaxLength { get; set; } = 50000; |
|||
} |
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue