Browse Source

Merge branch 'dev' into cookie-service

pull/5828/head
Halil İbrahim Kalkan 6 years ago
committed by GitHub
parent
commit
7b6a024ce8
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      .gitignore
  2. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/zh-Hans.json
  3. 116
      abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/zh-Hans.json
  4. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json
  5. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/zh-Hans.json
  6. 7
      abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hans.json
  7. 149
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/zh-Hans.json
  8. 30
      abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json
  9. 36
      abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/zh-Hans.json
  10. 62
      docs/en/Application-Services.md
  11. 7
      docs/en/Blog-Posts/2020-10-15 v3_3_Preview/POST.md
  12. 54
      docs/en/Migration-Guides/Abp-4_0.md
  13. 49
      docs/en/Repositories.md
  14. 2
      docs/en/Tutorials/Part-10.md
  15. 10
      docs/en/Tutorials/Part-2.md
  16. 20
      docs/en/Tutorials/Part-3.md
  17. 3
      docs/en/Tutorials/Part-4.md
  18. 8
      docs/en/Tutorials/Part-5.md
  19. 4
      docs/en/Tutorials/Part-7.md
  20. 14
      docs/en/Tutorials/Part-9.md
  21. BIN
      docs/en/Tutorials/images/bookstore-authors-blazor-ui.png
  22. 24
      docs/en/UI/AspNetCore/JavaScript-API/Auth.md
  23. 56
      docs/en/UI/AspNetCore/JavaScript-API/Block-Busy.md
  24. 49
      docs/en/UI/AspNetCore/JavaScript-API/CurrentUser.md
  25. 117
      docs/en/UI/AspNetCore/JavaScript-API/DOM.md
  26. 15
      docs/en/UI/AspNetCore/JavaScript-API/Features.md
  27. 28
      docs/en/UI/AspNetCore/JavaScript-API/Index.md
  28. 49
      docs/en/UI/AspNetCore/JavaScript-API/Logging.md
  29. 40
      docs/en/UI/AspNetCore/JavaScript-API/ResourceLoader.md
  30. 28
      docs/en/docs-nav.json
  31. BIN
      docs/en/images/ui-busy.png
  32. 11
      framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme/Themes/Basic/LoginDisplay.razor
  33. 16
      framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme/wwwroot/theme.js
  34. 7
      framework/src/Volo.Abp.BlazoriseUI/AbpCrudPageBase.cs
  35. 20
      framework/src/Volo.Abp.BlazoriseUI/BreadcrumbItem.cs
  36. 37
      framework/src/Volo.Abp.BlazoriseUI/Components/PageHeader.razor
  37. 29
      framework/src/Volo.Abp.BlazoriseUI/Components/PageHeader.razor.cs
  38. 4
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Localization/zh-Hans.json
  39. 8
      framework/src/Volo.Abp.Settings/Volo/Abp/Settings/ISettingDefinitionContext.cs
  40. 13
      framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/zh-Hans.json
  41. 6
      framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/zh-Hans.json
  42. 6
      framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/Localization/zh-Hans.json
  43. 6
      framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/SourceExt/zh-Hans.json
  44. 7
      framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/Localization/zh-Hans.json
  45. 7
      modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/zh-Hans.json
  46. 9
      modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLoginModel.cs
  47. 6
      modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Consent.cshtml
  48. 56
      modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Consent.cshtml.cs
  49. 110
      modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Localization/Resources/zh-Hans.json
  50. 2
      modules/cms-kit/host/Volo.CmsKit.IdentityServer/IdentityServer/IdentityServerDataSeedContributor.cs
  51. 22
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/zh-Hans.json
  52. 24
      modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/zh-Hans.json
  53. 11
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/zh-Hans.json
  54. 2
      modules/feature-management/src/Volo.Abp.FeatureManagement.Application/Volo/Abp/FeatureManagement/FeatureAppService.cs
  55. 5
      modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/zh-Hans.json
  56. 27
      modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserLinkManager.cs
  57. 12
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/ApiResources/ApiResourceConsts.cs
  58. 2
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/ApiResources/ApiResourceEto.cs
  59. 9
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/ApiResources/ApiResourcePropertyConsts.cs
  60. 7
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/ApiResources/ApiResourceScopeConsts.cs
  61. 20
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/ApiResources/ApiResourceSecretConsts.cs
  62. 9
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/ApiResources/ApiScopeConsts.cs
  63. 11
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/ApiScopes/ApiResourceConsts.cs
  64. 9
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/ApiScopes/ApiScopePropertyConsts.cs
  65. 7
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Clients/ClientClaimConsts.cs
  66. 26
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Clients/ClientConsts.cs
  67. 4
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Clients/ClientCorsOriginConsts.cs
  68. 4
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Clients/ClientGrantTypeConsts.cs
  69. 12
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Clients/ClientSecretConsts.cs
  70. 19
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Devices/DeviceFlowCodesConsts.cs
  71. 6
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Devices/DeviceFlowCodesEto.cs
  72. 22
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Grants/PersistedGrantConsts.cs
  73. 8
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/IdentityResources/IdentityResourceConsts.cs
  74. 9
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/IdentityResources/IdentityResourcePropertyConsts.cs
  75. 1
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/en.json
  76. 1
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/zh-Hans.json
  77. 9
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/ObjectExtending/IdentityServerModuleExtensionConfiguration.cs
  78. 4
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/ObjectExtending/IdentityServerModuleExtensionConsts.cs
  79. 4
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo.Abp.IdentityServer.Domain.csproj
  80. 4
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpIdentityServerBuilderExtensions.cs
  81. 3
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpIdentityServerDomainModule.cs
  82. 37
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AllowedSigningAlgorithmsConverter.cs
  83. 72
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/ApiResources/ApiResource.cs
  84. 2
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/ApiResources/ApiResourceClaim.cs
  85. 39
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/ApiResources/ApiResourceProperty.cs
  86. 38
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/ApiResources/ApiResourceScope.cs
  87. 20
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/ApiResources/ApiResourceSecret.cs
  88. 37
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/ApiResources/ApiScopeClaim.cs
  89. 15
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/ApiResources/IApiResourceRepository.cs
  90. 63
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/ApiScopes/ApiScope.cs
  91. 31
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/ApiScopes/ApiScopeClaim.cs
  92. 39
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/ApiScopes/ApiScopeProperty.cs
  93. 38
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/ApiScopes/IApiScopeeRepository.cs
  94. 10
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AspNetIdentity/AbpResourceOwnerPasswordValidator.cs
  95. 9
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Clients/Client.cs
  96. 4
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Clients/ClientProperty.cs
  97. 2
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Clients/ClientStore.cs
  98. 4
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Clients/IClientRepository.cs
  99. 6
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Devices/DeviceFlowCodes.cs
  100. 21
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Grants/IPersistentGrantRepository.cs

3
.gitignore

@ -305,3 +305,6 @@ modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/Logs/
/modules/client-simulation/demo/Volo.ClientSimulation.Demo/package-lock.json
abp-build-config.json
/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor/Pages/Test/
# Identity Server temp signature file
tempkey.jwk

3
abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/zh-Hans.json

@ -8,6 +8,7 @@
"FrameworkDocumentation": "框架文档",
"OfficialBlog": "官方博客",
"CommercialHomePage": "商业版首页",
"CommercialSupportWebSite": "商业版支持网站"
"CommercialSupportWebSite": "商业版支持网站",
"CommunityWebSite": "ABP社区网站"
}
}

116
abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/zh-Hans.json

@ -3,6 +3,12 @@
"texts": {
"Permission:Organizations": "组织",
"Permission:Manage": "管理组织",
"Permission:DiscountRequests": "折扣请求",
"Permission:DiscountManage": "管理折扣请求",
"Permission:Disable": "禁用",
"Permission:Enable": "启动",
"Permission:EnableSendEmail": "启用发送邮件",
"Permission:SendEmail": "发送邮件",
"Permission:NpmPackages": "NPM包",
"Permission:NugetPackages": "Nuget包",
"Permission:Maintenance": "维护",
@ -13,8 +19,13 @@
"Permission:Edit": "编辑",
"Permission:Delete": "删除",
"Permission:Create": "创建",
"Permission:Accounting": "账单",
"Permission:Accounting:Quotation": "报价",
"Permission:Accounting:Invoice": "发票",
"Menu:Organizations": "组织",
"Menu:Accounting": "账单",
"Menu:Packages": "包",
"Menu:DiscountRequests": "折扣请求",
"NpmPackageDeletionWarningMessage": "该NPM包将会被删除. 你确定吗?",
"NugetPackageDeletionWarningMessage": "该Nuget包将会被删除. 你确定吗?",
"ModuleDeletionWarningMessage": "该模块将会被删除. 你确定吗?",
@ -24,6 +35,7 @@
"NameFilter": "名称",
"CreationTime": "创建时间",
"IsPro": "是否为专业版",
"ShowOnModuleList": "展示模块列表",
"EfCoreConfigureMethodName": "配置方法法",
"IsProFilter": "是否为专业版",
"ApplicationType": "应用程序类型",
@ -53,6 +65,7 @@
"CreateANugetPackage": "创建Nuget包",
"AddNew": "新建",
"PackageAlreadyExist{0}": "\"{0}\"已经被添加.",
"ModuleAlreadyExist{0}": "\"{0}\" 模块已经添加.",
"ClearCache": "清除缓存",
"SuccessfullyCleared": "清除成功",
"Menu:NpmPackages": "NPM包",
@ -63,6 +76,7 @@
"Organizations": "组织",
"LongName": "完整名称",
"LicenseType": "授权类型",
"MissingLicenseTypeField": "许可证类型字段是必需的!",
"LicenseStartTime": "授权开始时间",
"LicenseEndTime": "授权结束时间",
"AllowedDeveloperCount": "允许的开发人员数量",
@ -74,14 +88,112 @@
"AddDeveloper": "添加开发者",
"Create": "创建",
"UserNotFound": "用户不存在",
"{0}WillBeRemovedFromMembers": "{0} 将从成员中删除",
"{0}WillBeRemovedFromDevelopers": "{0} 将从开发者中移除, 你确定吗?",
"{0}WillBeRemovedFromOwners": "{0} 将从所有者中移除, 你确定吗?",
"Computers": "计算机",
"UniqueComputerId": "计算机唯一ID",
"LastSeenDate": "上次查看日期",
"{0}Computer{1}WillBeRemovedFromRecords": "计算机 {0} ({1}) 将从记录中删除",
"OrganizationDeletionWarningMessage": "组织将被删除",
"DeletingLastOwnerWarningMessage": "一个组织必须至少有一个所有者!因此你不能删除此所有者",
"This{0}AlreadyExistInThisOrganization": "该组织中已经存在此 {0}",
"AreYouSureYouWantToDeleteAllComputers": "您确定要删除所有计算机吗?",
"DeleteAll": "删除所有"
"DeleteAll": "删除所有",
"DoYouWantToCreateNewUser": "你想要创建一个新用户吗",
"MasterModules": "主模块",
"OrganizationName": "组织名称",
"CreationDate": "创建时间",
"LicenseStartDate": "许可开始时间",
"LicenseEndDate": "许可结束时间",
"OrganizationNamePlaceholder": "组织名称...",
"TotalQuestionCountPlaceholder": "问题总数...",
"RemainingQuestionCountPlaceholder": "剩下的问题数...",
"LicenseTypePlaceholder": "许可类型...",
"CreationDatePlaceholder": "创建时间...",
"LicenseStartDatePlaceholder": "许可开始时间...",
"LicenseEndDatePlaceholder": "许可结束时间...",
"UsernameOrEmail": "用户名或邮箱",
"UsernameOrEmailPlaceholder": "用户名或邮箱...",
"Member": "成员",
"PurchaseOrderNo": "订单编号",
"QuotationDate": "报价日期",
"CompanyName": "公司名称",
"CompanyAddress": "公司地址",
"Price": "价格",
"DiscountText": "折扣文字",
"DiscountQuantity": "折扣数量",
"DiscountPrice": "折扣价格",
"Quotation": "报价单",
"ExtraText": "额外文字",
"ExtraAmount": "额外金额",
"DownloadQuotation": "下载报价单",
"Invoice": "发票",
"TaxNumber": "税号",
"InvoiceNumber": "发票数字",
"InvoiceDate": "发票日期",
"InvoiceNote": "发票说明",
"Quantity": "数量",
"AddProduct": "添加产品",
"AddProductWarning": "你需要添加产品",
"TotalPrice": "总价",
"Generate": "生成",
"MissingQuantityField": "数量字段为必填项!",
"MissingPriceField": "价格字段为必填项!",
"CodeUsageStatus": "状态",
"Country": "国家",
"DeveloperCount": "开发者数量",
"RequestCode": "请求码",
"WebSite": "网站",
"GithubUsername": "Github用户名",
"PhoneNumber": "手机号",
"ProjectDescription": "产品描述",
"Referrer": "推荐人",
"DiscountRequests": "折扣请求",
"Copylink": "拷贝链接",
"Disable": "禁用",
"Enable": "启用",
"EnableSendEmail": "启用发送邮件",
"SendEmail": "发送邮件",
"SuccessfullyDisabled": "禁用成功",
"SuccessfullyEnabled": "启用成功",
"EmailSent": "邮件发送",
"SuccessfullySent": "发送成功",
"SuccessfullyDeleted": "删除成功",
"DiscountRequestDeletionWarningMessage": "折扣成功将被删除",
"BusinessType": "商业类型",
"TotalQuestionCount": "问题总数",
"RemainingQuestionCount": "剩余问题数",
"TotalQuestionMustBeGreaterWarningMessage": "TotalQuestionCount必须大于RemainingQuestionCount!",
"QuestionCountsMustBeGreaterThanZero": "TotalQuestionCount和RemainingQuestionCount必须为0或大于0",
"UnlimitedQuestionCount": "无限的问题计数",
"Notes": "笔记",
"Menu:Community": "社区",
"Menu:Articles": "文章",
"Wait": "等待",
"Approve": "批准",
"Reject": "拒绝",
"Details": "详情",
"Url": "Url",
"Title": "标题",
"ContentSource": "内容源",
"Status": "状态",
"ReadArticle": "阅读文章",
"ArticleHasBeenWaiting": "文章还在等待",
"ArticleHasBeenApproved": "文章已被批准",
"ArticleHasBeenRejected": "文章已被拒绝",
"Permission:Community": "社区",
"Permission:CommunityArticle": "文字",
"Link": "链接",
"Enum:ContentSource:0": "Github",
"Enum:ContentSource:1": "外部",
"Enum:Status:0": "等待中",
"Enum:Status:1": "已拒绝",
"Enum:Status:2": "已批准",
"Summary": "概要",
"AuthorName": "作者名称",
"CoverImage": "封面图片",
"RemoveCacheConfirmationMessage": "你确定要删除\"{0}\" 文章缓存?",
"SuccessfullyRemoved": "清除成功",
"RemoveCache": "删除缓存"
}
}

3
abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json

@ -27,6 +27,7 @@
"Blog": "Blog",
"Commercial": "Commercial",
"MyAccount": "My account",
"SeeDocuments": "See Documents"
"SeeDocuments": "See Documents",
"Samples": "Samples"
}
}

4
abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/zh-Hans.json

@ -26,6 +26,8 @@
"ContributionGuide": "贡献指南",
"Blog": "博客",
"Commercial": "商业版",
"SeeDocuments": "查看文档"
"MyAccount": "我的账户",
"SeeDocuments": "查看文档",
"Samples": "示例"
}
}

7
abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hans.json

@ -6,6 +6,8 @@
"Volo.AbpIo.Commercial:010003": "你不是该组织的所有者!",
"OrganizationNotFoundMessage": "找不到任何组织!",
"DeveloperCount": "开发者数量",
"QuestionCount": "剩余 / 问题总数",
"Unlimited": "无限制",
"Owners": "所有者",
"AddMember": "添加成员",
"AddOwner": "添加所有者",
@ -25,6 +27,9 @@
"Volo.AbpIo.Commercial:010004": "不能找到指定的用户! 用户必须已经注册.",
"MyOrganizations": "我的组织",
"ApiKey": "API key",
"UserNameNotFound": "没有用户名为{0}的用户"
"UserNameNotFound": "没有用户名为{0}的用户",
"SuccessfullyAddedToNewsletter": "感谢你订阅我们的新闻通讯!",
"MyProfile": "我的资料",
"EmailNotValid": "请输入有效的电子邮件地址"
}
}

149
abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/zh-Hans.json

@ -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": "计划发布日期"
}
}

30
abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json

@ -93,6 +93,7 @@
"DynamicForms": "Dynamic Forms",
"BundlingMinification": "Bundling & Minification",
"BackgroundJobs": "Background Jobs",
"BackgroundJobsExplanation": "Define simple classes to execute jobs in background as queued. Use built-in job manager or integrate your own. <a href=\"{0}\">Hangfire</a> & <a href=\"{1}\">RabbitMQ</a> integrations are already available.",
"DDDInfrastructure": "DDD Infrastructure",
"DomainDrivenDesignInfrastructure": "Domain Driven Design Infrastructure",
"AutoRESTAPIs": "Auto REST APIs",
@ -102,6 +103,7 @@
"TestInfrastructure": "Test Infrastructure",
"AuditLoggingEntityHistories": "Audit Logging & Entity Histories",
"ObjectToObjectMapping": "Object to Object Mapping",
"ObjectToObjectMappingExplanation": "<a href=\"{0}\">Object to object mapping</a> abstraction with AutoMapper integration.",
"EmailSMSAbstractions": "Email & SMS Abstractions",
"EmailSMSAbstractionsWithTemplatingSupport": "Email & SMS Abstractions with Templating Support",
"Localization": "Localization",
@ -156,6 +158,32 @@
"UiFramework": "UI Framework",
"EmailAddress": "Email address",
"Mobile": "Mobile",
"ReactNative": "React Native"
"ReactNative": "React Native",
"Strong": "Strong",
"Complete": "Complete",
"BasedLayeringModel": "Based Layering Model",
"Microservice": "Microservice",
"Compatible": "Compatible",
"MeeTTheABPCommunityInfo": "Our mission is to create an environment where developers can help each other with articles, tutorials, case studies, etc. and meet like-minded people.",
"JoinTheABPCommunityInfo": "Get involved with a vibrant community and become a contributor to the ABP Framework!",
"AllArticles": "All Articles",
"SubmitYourArticle": "Submit Your Article",
"DynamicClientProxyDocument": "See the dynamic client proxy documentations for <a href=\"{0}\">JavaScript</a> & <a href=\"{1}\">C#</a>.",
"EmailSMSAbstractionsDocument": "See the <a href=\"{0}\">emailing</a> and <a href=\"{1}\">SMS sending</a> documents for more information.",
"CreateProjectWizard": "This wizard creates a new project from the startup template which is properly configured to jump start to your project.",
"TieredOption": "Creates a tiered solution where Web and Http API layers are physically separated. If not checked, creates a layered solution which is less complex and suitable for most scenarios.",
"SeparateIdentityServerOption": "Separates server side into two applications: First one is for the identity server and the second one is for your server side HTTP API.",
"UseslatestPreVersion": "Uses latest pre-release version",
"ReadTheDocumentation": "<span class=\"text-primary\">Read</span><span class=\"text-success\">The Documentation</span>",
"Documentation": "Documentation",
"GettingStartedTutorial": "Getting Started Tutorial",
"ApplicationDevelopmentTutorial": "Application Development Tutorial",
"TheStartupTemplate": "The Startup Template",
"InstallABPCLIInfo": "ABP CLI is the fastest way to start a new solution with the ABP framework. Install the ABP CLI using a command line window:",
"DifferentLevelOfNamespaces": "You can use different level of namespaces; e.g. BookStore, Acme.BookStore or Acme.Retail.BookStore.",
"ABPCLIExamplesInfo": "<strong>new</strong> command creates a <strong>layered MVC application</strong> with <strong>Entity Framework Core</strong> as the database provider. However, it has additional options. Examples:",
"SeeCliDocumentForMoreInformation": "See the <a href=\"{0}\">ABP CLI document</a> for more options or select the \"Direct Download\" tab above.",
"Optional": "Optional",
"LocalFrameworkRef": "Keep local project reference for the framework packages."
}
}

36
abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/zh-Hans.json

@ -89,10 +89,11 @@
"Features": "功能",
"ABPCLI": "ABP CLI",
"Modularity": "模块化",
"BootstrapTagHelpers": "Bootstrap Tag Helpers",
"BootstrapTagHelpers": "Bootstrap 标签助手",
"DynamicForms": "动态表单",
"BundlingMinification": "Bundling & Minification",
"BackgroundJobs": "后台作业",
"BackgroundJobsExplanation": "定义简单的类以在队列中在后台执行作业. 使用内置的作业管理器或集成你自己的作业管理器. <a href=\"{0}\">Hangfire</a> & <a href=\"{1}\">RabbitMQ</a>集成已经可用.",
"DDDInfrastructure": "DDD基础设施",
"DomainDrivenDesignInfrastructure": "Domain Driven Design基础设施",
"AutoRESTAPIs": "自动REST APIs",
@ -102,6 +103,7 @@
"TestInfrastructure": "测试基础设施",
"AuditLoggingEntityHistories": "审计日志和实体历史",
"ObjectToObjectMapping": "对象映射",
"ObjectToObjectMappingExplanation": "<a href=\"{0}\">对象到对象映射</a> 抽象AutoMapper集成.",
"EmailSMSAbstractions": "电子邮件和短信抽象",
"EmailSMSAbstractionsWithTemplatingSupport": "具有模板支持的电子邮件和短信抽象",
"Localization": "本土化",
@ -112,7 +114,7 @@
"DependencyInjection": "依赖注入",
"DependencyInjectionByConventions": "依据约定的依赖注入",
"ABPCLIExplanation": "ABP CLI(命令行界面)是用于对ABP解决方案执行常见操作的命令行工具.",
"ModularityExplanation": "ABP提供了一个完整的基础设施来构建你自己的应用程序模块,这些模块可能具有实体,服务,数据库集成,API,UI组件等.", //TODO: strong "your own application modules",-
"ModularityExplanation": "ABP提供了一个完整的基础设施来构建你自己的应用程序模块,这些模块可能具有实体,服务,数据库集成,API,UI组件等.",
"MultiTenancyExplanation": "ABP框架不仅支持开发多租户应用程序,而且使你的代码几乎无需知道多租户.",
"MultiTenancyExplanation2": "可以自动确定当前租户,将不同租户的数据相互隔离.",
"MultiTenancyExplanation3": "支持单一数据库,或每个租户单独数据库或者混合方式.",
@ -154,6 +156,34 @@
"SeeTheDocumentForMoreInformation": "查看<a href=\"{1}\">{0} 文档</a>获得更多信息",
"IndexPageHeroSection": "<span class=\"third-line shine2\"><strong>asp.net core的</strong></span><span class=\"first-line shine\"><strong>开源</strong></span><span class=\"second-line text-uppercase\">Web应用程序<br />框架 </span>",
"UiFramework": "UI框架",
"EmailAddress": "电子邮件地址"
"EmailAddress": "电子邮件地址",
"Mobile": "移动",
"ReactNative": "React Native",
"Strong": "强大的",
"Complete": "完整的",
"BasedLayeringModel": "基于分层模型",
"Microservice": "微服务",
"Compatible": "兼容",
"MeeTTheABPCommunityInfo": "我们的使命是创建一个环境使开发人员可以在文章,教程,案例研究等方面互相帮助并结识志趣相投的人.",
"JoinTheABPCommunityInfo": "参与充满活力的社区并成为ABP框架的贡献者",
"AllArticles": "所有的文章",
"SubmitYourArticle": "提交你的文章",
"DynamicClientProxyDocument": "参阅<a href=\"{0}\">JavaScript</a> & <a href=\"{1}\">C#</a>的动态客户端代理文档.",
"EmailSMSAbstractionsDocument": "参阅<a href=\"{0}\">emailing</a> 和 <a href=\"{1}\">SMS sending</a> 文档获得更多信息.",
"CreateProjectWizard": "此向导让你从启动模板创建一个新项目,该启动模板已正确配置为可以快速启动你的项目.",
"TieredOption": "创建一个分层解决方案,其中Web和Http API层在物理上是分离的. 如果没有选中则创建一个不那么复杂且适合大多数场景的分层解决方案.",
"SeparateIdentityServerOption": "将服务器端分离为两个应用程序:第一个应用程序用于身份服务器,第二个应用程序用于服务器端HTTP API.",
"UseslatestPreVersion": "使用最新的预发布版本",
"ReadTheDocumentation": "<span class=\"text-primary\">阅读</span><span class=\"text-success\">文档</span>",
"Documentation": "文档",
"GettingStartedTutorial": "入门教程",
"ApplicationDevelopmentTutorial": "应用程序开发教程",
"TheStartupTemplate": "启动模板",
"InstallABPCLIInfo": "ABP CLI是使用ABP框架启动新解决方案的最快方式. 使用命令行窗口安装ABP CLI:",
"DifferentLevelOfNamespaces": "你可以使用不同级别的名称空间. 例如 BookStore, Acme.BookStore 或 Acme.Retail.BookStore",
"ABPCLIExamplesInfo": "<strong>new</strong>命令创建一个 <strong>分层的MVC应用程序</strong> 使用 <strong>Entity Framework Core</strong> 做为数据库提供程序. 它还有其他选项. 示例:",
"SeeCliDocumentForMoreInformation": "参阅 <a href=\"{0}\">ABP CLI 文档</a> 获得更多选项或选择上方的 \"直接下载\" 标签.",
"Optional": "可选的",
"LocalFrameworkRef": "保留框架包的本地项目引用."
}
}

62
docs/en/Application-Services.md

@ -445,6 +445,68 @@ These methods are used to convert Entities to DTOs and vice verse. They uses the
* `MapToEntityAsync(TCreateInput)` is used to create an entity from `TCreateInput`.
* `MapToEntityAsync(TUpdateInput, TEntity)` is used to update an existing entity from `TUpdateInput`.
## Miscellaneous
### Working with Streams
`Stream` object itself is not serializable. So, you may have problems if you directly use `Stream` as the parameter or the return value for your application service. ABP Framework provides a special type, `IRemoteStreamContent` to be used to get or return streams in the application services.
**Example: Application Service Interface that can be used to get and return streams**
````csharp
using System;
using System.Threading.Tasks;
using Volo.Abp.Application.Services;
using Volo.Abp.Content;
namespace MyProject.Test
{
public interface ITestAppService : IApplicationService
{
Task Upload(Guid id, IRemoteStreamContent streamContent);
Task<IRemoteStreamContent> Download(Guid id);
}
}
````
**Example: Application Service Implementation that can be used to get and return streams**
````csharp
using System;
using System.IO;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Application.Services;
using Volo.Abp.Content;
namespace MyProject.Test
{
public class TestAppService : ApplicationService, ITestAppService
{
public Task<IRemoteStreamContent> Download(Guid id)
{
var fs = new FileStream("C:\\Temp\\" + id + ".blob", FileMode.OpenOrCreate);
return Task.FromResult(
(IRemoteStreamContent) new RemoteStreamContent(fs) {
ContentType = "application/octet-stream"
}
);
}
public async Task Upload(Guid id, IRemoteStreamContent streamContent)
{
using (var fs = new FileStream("C:\\Temp\\" + id + ".blob", FileMode.Create))
{
await streamContent.GetStream().CopyToAsync(fs);
await fs.FlushAsync();
}
}
}
}
````
`IRemoteStreamContent` is compatible with the [Auto API Controller](API/Auto-API-Controllers.md) and [Dynamic C# HTTP Proxy](API/Dynamic-CSharp-API-Clients.md) systems.
## Lifetime
Lifetime of application services are [transient](Dependency-Injection.md) and they are automatically registered to the dependency injection system.

7
docs/en/Blog-Posts/2020-10-15 v3_3_Preview/POST.md

@ -208,6 +208,13 @@ There are still missing features and modules. However, we are working on it to h
There are some breaking changes with the Blazor UI. If you've built an application and upgrade it, your application might not properly work. See the [ABP Commercial Blazor UI v 3.3 Migration Guide](https://docs.abp.io/en/commercial/3.3/migration-guides/blazor-ui-3_3) for the changes you need to do after upgrading your application.
#### Known Issues
When you create a new project, profile management doesn't work, you get an exception because it can't find the `/libs/cropperjs/css/cropper.min.css` file. To fix the issue;
* Add `"@volo/account": "^3.3.0-rc.1"` to the `package.json` in the `.Host` project.
* Run `yarn` (or `npm install`), then `gulp` on a command line terminal in the root folder of the `.Host` project.
### Multi-Tenant Social Logins
[Account module](https://commercial.abp.io/modules/Volo.Account.Pro) now supports to manage the social/external logins in the UI. You can **enable/disable** and **set options** in the settings page. It also supports to use **different credentials for the tenants** and it is also **configured on the runtime**.

54
docs/en/Migration-Guides/Abp-4_0.md

@ -0,0 +1,54 @@
# 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
#### ApiScope
* Before it was a property of `ApiResource`, now it becomes an independent aggregate root.
## 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

49
docs/en/Repositories.md

@ -142,7 +142,7 @@ var people = _personRepository
You normally want to use `.ToListAsync()`, `.CountAsync()`... instead, to be able to write a **truly async code**.
However, you see that you can't use these async extension methods in your application or domain layer when you create a new project using the standard [application startup template](Startup-Templates/Application.md), because;
However, you see that you can't use all the async extension methods in your application or domain layer when you create a new project using the standard [application startup template](Startup-Templates/Application.md), because;
* These async methods **are not standard LINQ methods** and they are defined in the [Microsoft.EntityFrameworkCore](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore) NuGet package.
* The standard template **doesn't have a reference** to the EF Core package from the domain and application layers, to be independent from the database provider.
@ -151,9 +151,9 @@ Based on your requirements and development model, you have the following options
> Using async methods is strongly suggested! Don't use sync LINQ methods while executing database queries to be able to develop a scalable application.
### Option-1: Reference to the EF Core
### Option-1: Reference to the Database Provider Package
The easiest solution is to directly add the EF Core package from the project you want to use these async methods.
**The easiest solution** is to directly add the EF Core package from the project you want to use these async methods.
> Add the [Volo.Abp.EntityFrameworkCore](https://www.nuget.org/packages/Volo.Abp.EntityFrameworkCore) NuGet package to your project, which indirectly reference to the EF Core package. This ensures that you use the correct version of the EF Core compatible to the rest of your application.
@ -183,14 +183,34 @@ var people = ((IMongoQueryable<Person>)_personRepository
.ToListAsync();
````
### Option-2: Custom Repository Methods
### Option-2: Use the IRepository Async Extension Methods
You can always create custom repository methods and use the database provider specific APIs, like async extension methods here. See [EF Core](Entity-Framework-Core.md) or [MongoDb](MongoDB.md) document for more info about the custom repositories.
ABP Framework provides async extension methods for the repositories, just similar to async LINQ extension methods.
This method is suggested;
**Example: Use `CountAsync` and `FirstOrDefaultAsync` methods on the repositories**
* If you want to **completely isolate** your domain & application layers from the database provider.
* If you develop a **reusable [application module](Modules/Index.md)** and don't want to force to a specific database provider, which should be done as a [best practice](Best-Practices/Index.md).
````csharp
var countAll = await _personRepository
.CountAsync();
var count = await _personRepository
.CountAsync(x => x.Name.StartsWith("A"));
var book1984 = await _bookRepository
.FirstOrDefaultAsync(x => x.Name == "John");
````
The standard LINQ extension methods are supported: *AllAsync, AnyAsync, AverageAsync, ContainsAsync, CountAsync, FirstAsync, FirstOrDefaultAsync, LastAsync, LastOrDefaultAsync, LongCountAsync, MaxAsync, MinAsync, SingleAsync, SingleOrDefaultAsync, SumAsync, ToArrayAsync, ToListAsync*.
This approach still **has a limitation**. You need to call the extension method directly on the repository object. For example, the below usage is **not supported**:
```csharp
var count = await _bookRepository.Where(x => x.Name.Contains("A")).CountAsync();
```
This is because the object returned from the `Where` method is not a repository object, it is a standard `IQueryable` interface. See the other options for such cases.
This method is suggested **wherever possible**.
### Option-3: IAsyncQueryableExecuter
@ -245,6 +265,17 @@ ABP Framework executes the query asynchronously using the actual database provid
This method is suggested;
* If you want to develop your application code **without depending** on the database provider.
* If you are building a **reusable library** that doesn't have a database provider integration package, but needs to execute an `IQueryable<T>` object in some case.
For example, ABP Framework uses the `IAsyncQueryableExecuter` in the `CrudAppService` base class (see the [application services](Application-Services.md) document).
For example, ABP Framework uses the `IAsyncQueryableExecuter` in the `CrudAppService` base class (see the [application services](Application-Services.md) document).
### Option-4: Custom Repository Methods
You can always create custom repository methods and use the database provider specific APIs, like async extension methods here. See [EF Core](Entity-Framework-Core.md) or [MongoDb](MongoDB.md) document for more info about the custom repositories.
This method is suggested;
* If you want to **completely isolate** your domain & application layers from the database provider.
* If you develop a **reusable [application module](Modules/Index.md)** and don't want to force to a specific database provider, which should be done as a [best practice](Best-Practices/Index.md).

2
docs/en/Tutorials/Part-10.md

@ -38,7 +38,7 @@ This tutorial has multiple versions based on your **UI** and **Database** prefer
We have created `Book` and `Author` functionalities for the book store application. However, currently there is no relation between these entities.
In this tutorial, we will establish a **1 to N** relation between the `Book` and the `Author`.
In this tutorial, we will establish a **1 to N** relation between the `Author` and the `Book` entities.
## Add Relation to The Book Entity

10
docs/en/Tutorials/Part-2.md

@ -583,7 +583,7 @@ When you click to the Books menu item under the Book Store parent, you are being
We will use the [Blazorise library](https://blazorise.com/) as the UI component kit. It is a very powerful library that supports major HTML/CSS frameworks, including the Bootstrap.
ABP Framework provides a generic base class, `BlazoriseCrudPageBase<...>`, to create CRUD style pages. This base class is compatible to the `ICrudAppService` that was used to build the `IBookAppService`. So, we can inherit from the `BlazoriseCrudPageBase` to automate the standard CRUD stuff.
ABP Framework provides a generic base class, `AbpCrudPageBase<...>`, to create CRUD style pages. This base class is compatible to the `ICrudAppService` that was used to build the `IBookAppService`. So, we can inherit from the `AbpCrudPageBase` to automate the code behind for the standard CRUD stuff.
Open the `Books.razor` and replace the content as the following:
@ -595,7 +595,7 @@ Open the `Books.razor` and replace the content as the following:
@using Acme.BookStore.Localization
@using Microsoft.Extensions.Localization
@inject IStringLocalizer<BookStoreResource> L
@inherits BlazoriseCrudPageBase<IBookAppService, BookDto, Guid, PagedAndSortedResultRequestDto, CreateUpdateBookDto>
@inherits AbpCrudPageBase<IBookAppService, BookDto, Guid, PagedAndSortedResultRequestDto, CreateUpdateBookDto>
<Card>
<CardHeader>
@ -645,15 +645,15 @@ Open the `Books.razor` and replace the content as the following:
> If you see some syntax errors, you can ignore them if your application properly built and run. Visual Studio still has some bugs with Blazor.
* Inherited from the `BlazoriseCrudPageBase<IBookAppService, BookDto, Guid, PagedAndSortedResultRequestDto, CreateUpdateBookDto>` which implements all the CRUD details for us.
* Inherited from the `AbpCrudPageBase<IBookAppService, BookDto, Guid, PagedAndSortedResultRequestDto, CreateUpdateBookDto>` which implements all the CRUD details for us.
* `Entities`, `TotalCount`, `PageSize`, `OnDataGridReadAsync` are defined in the base blass.
* Injected `IStringLocalizer<BookStoreResource>` (as `L` object) and used for localization.
While the code above pretty easy to understand, you can check the Blazorise [Card](https://blazorise.com/docs/components/card/) and [DataGrid](https://blazorise.com/docs/extensions/datagrid/) documents to understand them better.
#### About the BlazoriseCrudPageBase
#### About the AbpCrudPageBase
We will continue to benefit from the `BlazoriseCrudPageBase` for the books page. You could just inject the `IBookAppService` and perform all the server side calls yourself (thanks to the [Dynamic C# HTTP API Client Proxy](../API/Dynamic-CSharp-API-Clients.md) system of the ABP Framework). We will do it manually for the authors page to demonstrate how to call server side HTTP APIs in your Blazor applications.
We will continue to benefit from the `AbpCrudPageBase` for the books page. You could just inject the `IBookAppService` and perform all the server side calls yourself (thanks to the [Dynamic C# HTTP API Client Proxy](../API/Dynamic-CSharp-API-Clients.md) system of the ABP Framework). We will do it manually for the authors page to demonstrate how to call server side HTTP APIs in your Blazor applications.
## Run the Final Application

20
docs/en/Tutorials/Part-3.md

@ -1163,7 +1163,7 @@ Clicking the "Delete" action calls the `delete` method which then shows a confir
## Creating a New Book
In this section, you will learn how to create a new modal dialog form to create a new book. Since we've inherited from the `BlazoriseCrudPage`, we only need to develop the view part.
In this section, you will learn how to create a new modal dialog form to create a new book. Since we've inherited from the `AbpCrudPageBase`, we only need to develop the view part.
### Add "New Button" Button
@ -1328,7 +1328,7 @@ We can now define a modal to edit the book. Add the following code to the end of
### AutoMapper Configuration
The base `BlazoriseCrudPage` uses the [object to object mapping](../Object-To-Object-Mapping.md) system to convert an incoming `BookDto` object to a `CreateUpdateBookDto` object. So, we need to define the mapping.
The base `AbpCrudPageBase` uses the [object to object mapping](../Object-To-Object-Mapping.md) system to convert an incoming `BookDto` object to a `CreateUpdateBookDto` object. So, we need to define the mapping.
Open the `BookStoreBlazorAutoMapperProfile` inside the `Acme.BookStore.Blazor` project and change the content as the following:
@ -1382,7 +1382,7 @@ Here the complete code to create the book management CRUD page, that has been de
@using Acme.BookStore.Localization
@using Microsoft.Extensions.Localization
@inject IStringLocalizer<BookStoreResource> L
@inherits BlazoriseCrudPageBase<IBookAppService, BookDto, Guid, PagedAndSortedResultRequestDto, CreateUpdateBookDto>
@inherits AbpCrudPageBase<IBookAppService, BookDto, Guid, PagedAndSortedResultRequestDto, CreateUpdateBookDto>
<Card>
<CardHeader>
@ -1392,8 +1392,10 @@ Here the complete code to create the book management CRUD page, that has been de
</Column>
<Column ColumnSize="ColumnSize.Is6">
<Paragraph Alignment="TextAlignment.Right">
<Button Color="Color.Primary"
Clicked="OpenCreateModalAsync">@L["NewBook"]</Button>
<Button Color="Color.Primary"
Clicked="OpenCreateModalAsync">
@L["NewBook"]
</Button>
</Paragraph>
</Column>
</Row>
@ -1406,10 +1408,10 @@ Here the complete code to create the book management CRUD page, that has been de
ShowPager="true"
PageSize="PageSize">
<DataGridColumns>
<DataGridColumn Width="150px"
<DataGridColumn Width="150px"
TItem="BookDto"
Field="@nameof(BookDto.Id)"
Sortable="false"
Field="@nameof(BookDto.Id)"
Sortable="false"
Caption="@L["Actions"]">
<DisplayTemplate>
<Dropdown>
@ -1434,7 +1436,7 @@ Here the complete code to create the book management CRUD page, that has been de
Field="@nameof(BookDto.Type)"
Caption="@L["Type"]">
<DisplayTemplate>
@L[$"Enum:BookType:{(int) context.Type}"]
@L[$"Enum:BookType:{(int)context.Type}"]
</DisplayTemplate>
</DataGridColumn>
<DataGridColumn TItem="BookDto"

3
docs/en/Tutorials/Part-4.md

@ -75,9 +75,12 @@ If you had created a data seed contributor as described in the [first part](Part
Add a new test class, named `BookAppService_Tests` in the `Books` namespace (folder) of the `Acme.BookStore.Application.Tests` project:
````csharp
using System;
using System.Linq;
using System.Threading.Tasks;
using Shouldly;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Validation;
using Xunit;
namespace Acme.BookStore.Books

8
docs/en/Tutorials/Part-5.md

@ -460,7 +460,7 @@ Open the `/src/app/book/book.component.html` file and replace the edit and delet
### Authorize the Razor Component
Open the `/Pages/Books.razor` file in the `Acme.BookStore.Blazor` project and add an `Authorize` attribute just after the `@page` directive, as shown below:
Open the `/Pages/Books.razor` file in the `Acme.BookStore.Blazor` project and add an `Authorize` attribute just after the `@page` directive and the following namespace imports (`@using` lines), as shown below:
````html
@page "/books"
@ -518,7 +518,9 @@ Wrap the *New Book* button by an `if` block as shown below:
@if (canCreateBook)
{
<Button Color="Color.Primary"
Clicked="OpenCreateModalAsync">@L["NewBook"]</Button>
Clicked="OpenCreateModalAsync">
@L["NewBook"]
</Button>
}
````
@ -545,7 +547,7 @@ As similar to the *New Book* button, we can use `if` blocks to conditionally sho
You can run and test the permissions. Remove a book related permission from the admin role to see the related button/action disappears from the UI.
However, ABP Framework caches the permissions of the current user in the client side. So, when you change a permission for yourself, you need to manually **refresh the page** to take the effect. If you don't refresh and try to use the prohibited action you get an HTTP 403 (forbidden) response from the server.
**ABP Framework caches the permissions** of the current user in the client side. So, when you change a permission for yourself, you need to manually **refresh the page** to take the effect. If you don't refresh and try to use the prohibited action you get an HTTP 403 (forbidden) response from the server.
> Changing a permission for a role or user immediately available on the server side. So, this cache system doesn't cause any security problem.

4
docs/en/Tutorials/Part-7.md

@ -74,6 +74,10 @@ Open the **Package Manager Console** on Visual Studio and ensure that the **Defa
Run the following command to create a new database migration:
````bash
Add-Migration "Added_Authors"
````
![bookstore-add-migration-authors](images/bookstore-add-migration-authors.png)
This will create a new migration class. Then run the `Update-Database` command to create the table on the database.

14
docs/en/Tutorials/Part-9.md

@ -1177,6 +1177,20 @@ We should complete the localizations we've used above. Open the `en.json` file u
"NewAuthor": "New author"
````
### Run the Application
Run and login to the application. **If you don't see the Authors menu item under the Book Store menu, that means you don't have the permission yet.** Go to the `identity/roles` page, click to the *Actions* button and select the *Permissions* action for the **admin role**:
![bookstore-author-permissions](images/bookstore-author-permissions.png)
As you see, the admin role has no *Author Management* permissions yet. Click to the checkboxes and save the modal to grant the necessary permissions. You will see the *Authors* menu item under the *Book Store* in the main menu, after **refreshing the page**:
![bookstore-authors-page](images/bookstore-authors-blazor-ui.png)
That's all! This is a fully working CRUD page, you can create, edit and delete the authors.
> **Tip**: If you run the `.DbMigrator` console application after defining a new permission, it automatically grants these new permissions to the admin role and you don't need to manually grant the permissions yourself.
{{end}}
## The Next Part

BIN
docs/en/Tutorials/images/bookstore-authors-blazor-ui.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

24
docs/en/UI/AspNetCore/JavaScript-API/Auth.md

@ -0,0 +1,24 @@
# ASP.NET Core MVC / Razor Pages UI: JavaScript Auth API
Auth API allows you to check permissions (policies) for the current user in the client side. In this way, you can conditionally show/hide UI parts or perform your client side logic based on the current permissions.
> This document only explains the JavaScript API. See the [authorization document](../../../Authorization.md) to understand the ABP authorization & permission system.
## Basic Usage
`abp.auth.isGranted(...)` function is used to check if a permission/policy has granted or not:
````js
if (abp.auth.isGranted('DeleteUsers')) {
//TODO: Delete the user
} else {
alert("You don't have permission to delete a user!");
}
````
## Other Fields & Functions
* ` abp.auth.isAnyGranted(...)`: Gets one or more permission/policy names and returns `true` if at least one of them has granted.
* `abp.auth.areAllGranted(...)`: Gets one or more permission/policy names and returns `true` if all of them of them have granted.
* `abp.auth.policies`: This is an object where its keys are the permission/policy names. You can find all permission/policy names here.
* `abp.auth.grantedPolicies`: This is an object where its keys are the permission/policy names. You can find the granted permission/policy names here.

56
docs/en/UI/AspNetCore/JavaScript-API/Block-Busy.md

@ -0,0 +1,56 @@
# ASP.NET Core MVC / Razor Pages UI: JavaScript UI Block/Busy API
UI Block API disables (blocks) the page or a part of the page.
## Basic Usage
**Example: Block (disable) the complete page**
````js
abp.ui.block();
````
**Example: Block (disable) an HTML element**
````js
abp.ui.block('#MyContainer');
````
**Example: Enables the previously blocked element or page:**
````js
abp.ui.unblock();
````
## Options
`abp.ui.block()` method can get an options object which may contain the following fields:
* `elm`: An optional selector to find the element to be blocked (e.g. `#MyContainerId`). If not provided, the entire page is blocked. The selector can also be directly passed to the `block()` method as shown above.
* `busy`: Set to `true` to show a progress indicator on the blocked area.
* `promise`: A promise object with `always` or `finally` callbacks. This can be helpful if you want to automatically unblock the blocked area when a deferred operation completes.
**Example: Block an element with busy indicator**
````js
abp.ui.block({
elm: '#MySection',
busy: true
});
````
The resulting UI will look like below:
![ui-busy](../../../images/ui-busy.png)
## setBusy
`abp.ui.setBusy(...)` and `abp.ui.clearBusy()` are shortcut functions if you want to use the block with `busy` option.
**Example: Block with busy**
````js
abp.ui.setBusy('#MySection');
````
Then you can use `abp.ui.clearBusy();` to re-enable the busy area/page.

49
docs/en/UI/AspNetCore/JavaScript-API/CurrentUser.md

@ -0,0 +1,49 @@
# ASP.NET Core MVC / Razor Pages UI: JavaScript CurrentUser API
`abp.currentUser` is an object that contains information about the current user of the application.
> This document only explains the JavaScript API. See the [CurrentUser document](../../../CurrentUser.md) to get information about the current user in the server side.
## Authenticated User
If the user was authenticated, this object will be something like below:
````js
{
isAuthenticated: true,
id: "34f1f4a7-13cc-4b91-84d1-b91c87afa95f",
tenantId: null,
userName: "john",
name: "John",
surName: "Nash",
email: "john.nash@abp.io",
emailVerified: true,
phoneNumber: null,
phoneNumberVerified: false,
roles: ["moderator","supporter"]
}
````
So, `abp.currentUser.userName` returns `john` in this case.
## Anonymous User
If the user was not authenticated, this object will be something like below:
````js
{
isAuthenticated: false,
id: null,
tenantId: null,
userName: null,
name: null,
surName: null,
email: null,
emailVerified: false,
phoneNumber: null,
phoneNumberVerified: false,
roles: []
}
````
You can check `abp.currentUser.isAuthenticated` to understand if the use was authenticated or not.

117
docs/en/UI/AspNetCore/JavaScript-API/DOM.md

@ -0,0 +1,117 @@
# ASP.NET Core MVC / Razor Pages UI: JavaScript DOM API
`abp.dom` (Document Object Model) provides events that you can subscribe to get notified when elements dynamically added to and removed from the page (DOM).
It is especially helpful if you want to initialize the new loaded elements. This is generally needed when you dynamically add elements to DOM (for example, get some HTML elements via AJAX) after page initialization.
> ABP uses the [MutationObserver](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver) to observe the changes made on the DOM.
## Node Events
### onNodeAdded
This event is triggered when an element is added to the DOM. Example:
````js
abp.dom.onNodeAdded(function(args){
console.log(args.$el);
});
````
`args` object has the following fields;
* `$el`: The JQuery selection to get the new element inserted to the DOM.
### onNodeRemoved
This event is triggered when an element is removed from the DOM. Example:
````js
abp.dom.onNodeRemoved(function(args){
console.log(args.$el);
});
````
`args` object has the following fields;
* `$el`: The JQuery selection to get the element removed from the DOM.
## Pre-Build Initializers
ABP Framework is using the DOM events to initialize some kind of HTML elements when they are added to the DOM after than the page was already initialized.
> Note that the same initializers also work if these elements were already included in the initial DOM. So, whether they are initially or lazy loaded, they work as expected.
### Form Initializer
The Form initializer (defined as `abp.dom.initializers.initializeForms`) initializes the lazy loaded forms;
* Automatically enabled the `unobtrusive` validation on the form.
* Can automatically show a confirmation message when you submit the form. To enable this feature, just add `data-confirm` attribute with a message (like `data-confirm="Are you sure?"`) to the `form` element.
* If the `form` element has `data-ajaxForm="true"` attribute, then automatically calls the `.abpAjaxForm()` on the `form` element, to make the form posted via AJAX.
See the [Forms & Validation](../Forms-Validation.md) document for more.
### Script Initializer
Script initializer (`abp.dom.initializers.initializeScript`) can execute a JavaScript code for a DOM element.
**Example: Lazy load a component and execute some code when the element has loaded**
Assume that you've a container to load the element inside:
````html
<div id="LazyComponent"></div>
````
And this is the component that will be loaded via AJAX from the server and inserted into the container:
````html
<div data-script-class="MyCustomClass">
<p>Sample message</p>
</div>
````
`data-script-class="MyCustomClass"` indicates the JavaScript class that will be used to perform some logic on this element:
`MyCustomClass` is a global object defined as shown below:
````js
MyCustomClass = function(){
function initDom($el){
$el.css('color', 'red');
}
return {
initDom: initDom
}
};
````
`initDom` is the function that is called by the ABP Framework. The `$el` argument is the loaded HTML element as a JQuery selection.
Finally, you can load the component inside the container after an AJAX call:
````js
$(function () {
setTimeout(function(){
$.get('/get-my-element').then(function(response){
$('#LazyComponent').html(response);
});
}, 2000);
});
````
Script Initialization system is especially helpful if you don't know how and when the component will be loaded into the DOM. This can be possible if you've developed a reusable UI component in a library and you want the application developer shouldn't care how to initialize the component in different use cases.
> Script initialization doesn't work if the component was loaded in the initial DOM. In this case, you are responsible to initialize it.
### Other Initializers
The following Bootstrap components and libraries are automatically initialized when they are added to the DOM:
* Tooltip
* Popover
* Timeage

15
docs/en/UI/AspNetCore/JavaScript-API/Features.md

@ -0,0 +1,15 @@
# ASP.NET Core MVC / Razor Pages UI: JavaScript Features API
`abp.features` API allows you to check features or get the values of the features on the client side. You can read the current value of a feature in the client side only if it is allowed by the feature definition (on the server side).
> This document only explains the JavaScript API. See the [Features](../../../Features.md) document to understand the ABP Features system.
## Basic Usage
`abp.features.values` can be used to access to the all feature values.
````js
var excelExportFeatureValue = abp.features.values["ExportingToExcel"];
````
Then you can check the value of the feature to perform your logic.

28
docs/en/UI/AspNetCore/JavaScript-API/Index.md

@ -4,18 +4,16 @@ ABP provides a set of JavaScript APIs for ASP.NET Core MVC / Razor Pages applica
## APIs
* [abp.ajax](Ajax.md)
* abp.auth
* abp.currentUser
* abp.dom
* [abp.event](Events.md)
* abp.features
* [abp.localization](Localization.md)
* abp.log
* [abp.message](Message.md)
* [abp.notify](Notify.md)
* abp.security
* [abp.setting](Settings.md)
* abp.ui
* abp.utils
* abp.ResourceLoader
* [AJAX](Ajax.md)
* [Auth](Auth.md)
* [CurrentUser](CurrentUser.md)
* [DOM](DOM.md)
* [Events](Events.md)
* [Features](Features.md)
* [Localization](Localization.md)
* [Logging](Logging.md)
* [ResourceLoader](ResourceLoader.md)
* [Settings](Settings.md)
* [UI Block/Busy](Block-Busy.md)
* [UI Message](Message.md)
* [UI Notification](Notify.md)

49
docs/en/UI/AspNetCore/JavaScript-API/Logging.md

@ -1,3 +1,50 @@
# ASP.NET Core MVC / Razor Pages UI: JavaScript Logging API
TODO
`abp.log` API is used to write simple logs in the client side.
> The logs are written to console, using the `console.log`, by default.
> This document is for simple client side logging. See the [Logging](../../../Logging.md) document for server side logging system.
## Basic Usage
Use one of the `abp.log.xxx(...)` methods based on the severity of your log message.
````js
abp.log.debug("Some debug log here..."); //Logging a simple debug message
abp.log.info({ name: "john", age: 42 }); //Logging an object as an information log
abp.log.warn("A warning message"); //Logging a warning message
abp.log.error('An error happens...'); //Error message
abp.log.fatal('Network connection has gone away!'); //Fatal error
````
## Log Levels
There are 5 levels for a log message:
* DEBUG = 1
* INFO = 2
* WARN = 3
* ERROR = 4
* FATAL = 5
These are defined in the `abp.log.levels` object (like `abp.log.levels.WARN`).
### Changing the Current Log Level
You can control the log level as shown below:
````js
abp.log.level = abp.log.levels.WARN;
````
Default log level is `DEBUG`.
### Logging with Specifying the Level
Instead of calling `abp.log.info(...)` function, you can use the `abp.log.log` by specifying the log level as a parameter:
````js
abp.log.log("log message...", abp.log.levels.INFO);
````

40
docs/en/UI/AspNetCore/JavaScript-API/ResourceLoader.md

@ -0,0 +1,40 @@
# ASP.NET Core MVC / Razor Pages UI: JavaScript Resource Loader API
`abp.ResourceLoader` is a service that can load a JavaScript or CSS file on demand. It guarantees to load the file only once even if you request multiple times.
## Loading Script Files
`abp.ResourceLoader.loadScript(...)` function **loads** a JavaScript file from the server and **executes** it.
**Example: Load a JavaScript file**
````js
abp.ResourceLoader.loadScript('/Pages/my-script.js');
````
### Parameters
`loadScript` function can get three parameters;
* `url` (required, `string`): The URL of the script file to be loaded.
* `loadCallback` (optional, `function`): A callback function that is called once the script is loaded & executed. In this callback you can safely use the code in the script file. This callback is called even if the file was loaded before.
* `failCallback` (optional, `function`): A callback function that is called if loading the script fails.
**Example: Provide the `loadCallback` argument**
````js
abp.ResourceLoader.loadScript('/Pages/my-script.js', function() {
console.log('successfully loaded :)');
});
````
## Loading Style Files
`abp.ResourceLoader.loadStyle(...)` function adds a `link` element to the `head` of the document for the given URL, so the CSS file is automatically loaded by the browser.
**Example: Load a CSS file**
````js
abp.ResourceLoader.loadStyle('/Pages/my-styles.css');
````

28
docs/en/docs-nav.json

@ -463,10 +463,22 @@
"text": "Localization",
"path": "UI/AspNetCore/JavaScript-API/Localization.md"
},
{
"text": "Auth",
"path": "UI/AspNetCore/JavaScript-API/Auth.md"
},
{
"text": "Current User",
"path": "UI/AspNetCore/JavaScript-API/CurrentUser.md"
},
{
"text": "Settings",
"path": "UI/AspNetCore/JavaScript-API/Settings.md"
},
{
"text": "Features",
"path": "UI/AspNetCore/JavaScript-API/Features.md"
},
{
"text": "AJAX",
"path": "UI/AspNetCore/JavaScript-API/Ajax.md"
@ -479,9 +491,25 @@
"text": "Notify",
"path": "UI/AspNetCore/JavaScript-API/Notify.md"
},
{
"text": "Block/Busy",
"path": "UI/AspNetCore/JavaScript-API/Block-Busy.md"
},
{
"text": "Events",
"path": "UI/AspNetCore/JavaScript-API/Events.md"
},
{
"text": "DOM",
"path": "UI/AspNetCore/JavaScript-API/DOM.md"
},
{
"text": "Logging",
"path": "UI/AspNetCore/JavaScript-API/Logging.md"
},
{
"text": "Resource Loader",
"path": "UI/AspNetCore/JavaScript-API/ResourceLoader.md"
}
]
},

BIN
docs/en/images/ui-busy.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

11
framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme/Themes/Basic/LoginDisplay.razor

@ -1,13 +1,22 @@
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@using Volo.Abp.Users
@using Volo.Abp.MultiTenancy
@inject ICurrentUser CurrentUser
@inject ICurrentTenant CurrentTenant
@inject NavigationManager Navigation
@inject SignOutSessionStateManager SignOutManager
<AuthorizeView>
<Authorized>
<Dropdown>
<DropdownToggle Color="Color.None">
@CurrentUser.UserName
@if (CurrentTenant.Name != null)
{
<span><i>@CurrentTenant.Name</i>\@CurrentUser.UserName</span>
}
else
{
<span>@CurrentUser.UserName</span>
}
</DropdownToggle>
<DropdownMenu>
@if (Menu != null)

16
framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme/wwwroot/theme.js

@ -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;
});
});

7
framework/src/Volo.Abp.BlazoriseUI/AbpCrudPageBase.cs

@ -184,6 +184,7 @@ namespace Volo.Abp.BlazoriseUI
protected TUpdateViewModel EditingEntity;
protected Modal CreateModal;
protected Modal EditModal;
protected List<BreadcrumbItem> BreadcrumbItems = new List<BreadcrumbItem>(2);
protected string CreatePolicyName { get; set; }
protected string UpdatePolicyName { get; set; }
@ -239,6 +240,7 @@ namespace Volo.Abp.BlazoriseUI
protected override async Task OnInitializedAsync()
{
await SetBreadcrumbItemsAsync();
await SetPermissionsAsync();
await GetEntitiesAsync();
}
@ -438,5 +440,10 @@ namespace Volo.Abp.BlazoriseUI
await AuthorizationService.CheckAsync(policyName);
}
protected virtual ValueTask SetBreadcrumbItemsAsync()
{
return ValueTask.CompletedTask;
}
}
}

20
framework/src/Volo.Abp.BlazoriseUI/BreadcrumbItem.cs

@ -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;
}
}
}

37
framework/src/Volo.Abp.BlazoriseUI/Components/PageHeader.razor

@ -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>

29
framework/src/Volo.Abp.BlazoriseUI/Components/PageHeader.razor.cs

@ -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>();
}
}
}

4
framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Localization/zh-Hans.json

@ -18,6 +18,8 @@
"Description:Abp.Mailing.Smtp.Password": "凭据关联的用户名的密码.",
"Description:Abp.Mailing.Smtp.Domain": "验证凭据的域名或计算机名.",
"Description:Abp.Mailing.Smtp.EnableSsl": "指定 SmtpClient 是否使用安全套接字层 (SSL) 加密连接.",
"Description:Abp.Mailing.Smtp.UseDefaultCredentials": "控制默认凭据是否随请求一起发送."
"Description:Abp.Mailing.Smtp.UseDefaultCredentials": "控制默认凭据是否随请求一起发送.",
"TextTemplate:StandardEmailTemplates.Layout": "默认电子邮件模板",
"TextTemplate:StandardEmailTemplates.Message": "电子邮件的简单消息模板"
}
}

8
framework/src/Volo.Abp.Settings/Volo/Abp/Settings/ISettingDefinitionContext.cs

@ -1,9 +1,13 @@
namespace Volo.Abp.Settings
using System.Collections.Generic;
namespace Volo.Abp.Settings
{
public interface ISettingDefinitionContext
{
SettingDefinition GetOrNull(string name);
IReadOnlyList<SettingDefinition> GetAll();
void Add(params SettingDefinition[] definitions);
}
}
}

13
framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/zh-Hans.json

@ -4,8 +4,10 @@
"Languages": "语言",
"AreYouSure": "你确定吗?",
"Cancel": "取消",
"Clear": "清楚",
"Yes": "是",
"No": "否",
"Ok": "Ok",
"Close": "关闭",
"Save": "保存",
"SavingWithThreeDot": "保存中...",
@ -13,6 +15,8 @@
"Delete": "删除",
"Edit": "编辑",
"Refresh": "刷新",
"Language": "语言",
"LoadMore": "加载更多",
"ProcessingWithThreeDot": "处理中...",
"LoadingWithThreeDot": "加载中...",
"Welcome": "欢迎",
@ -31,6 +35,8 @@
"PagerInfoEmpty": "显示0个条目中的0到0",
"PagerInfoFiltered": "(从 _MAX_ 总条目中过滤掉)",
"NoDataAvailableInDatatable": "表中没有数据",
"Total": "总计",
"Selected": "已选",
"PagerShowMenuEntries": "显示 _MENU_ 条数据",
"DatatableActionDropdownDefaultText": "操作",
"ChangePassword": "修改密码",
@ -38,6 +44,9 @@
"AreYouSureYouWantToCancelEditingWarningMessage": "你有未保存的更改.",
"GoHomePage": "返回主页",
"GoBack": "返回",
"Search" : "搜索"
"Search": "搜索",
"ItemWillBeDeletedMessageWithFormat": "{0} 将被删除!",
"ItemWillBeDeletedMessage": "此项将被删除!",
"ManageYourAccount": "管理你的账户"
}
}
}

6
framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/zh-Hans.json

@ -0,0 +1,6 @@
{
"culture": "zh-Hans",
"texts": {
"hello": "你好"
}
}

6
framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/Localization/zh-Hans.json

@ -0,0 +1,6 @@
{
"culture": "zh-Hans",
"texts": {
"Volo.Abp.Http.DynamicProxying:10001": "业务异常: {0}"
}
}

6
framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/SourceExt/zh-Hans.json

@ -0,0 +1,6 @@
{
"culture": "zh-Hans",
"texts": {
"SeeYou": "再见"
}
}

7
framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/Localization/zh-Hans.json

@ -0,0 +1,7 @@
{
"culture": "zh-Hans",
"texts": {
"HelloText": "你好 {0}",
"HowAreYou": "你好吗?"
}
}

7
modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/zh-Hans.json

@ -44,6 +44,7 @@
"LoggedOutTitle": "注销",
"LoggedOutText": "你已成功注销并将马上返回.",
"ReturnToText": "点击此处返回到 {0}",
"OrLoginWith": "或登录",
"ForgotPassword": "忘记密码?",
"SendPasswordResetLink_Information": "密码重置链接将发送到您的电子邮件以重置密码. 如果您在几分钟内没有收到电子邮件,请重试.",
"PasswordResetMailSentMessage": "帐户恢复电子邮件已发送到您的电子邮件地址. 如果您在15分钟内未在收件箱中看到此电子邮件,请检查垃圾邮件,并标记为非垃圾邮件.",
@ -56,8 +57,6 @@
"ProfileTab:Password": "更改密码",
"ProfileTab:PersonalInfo": "个人信息",
"ReturnToApplication": "返回到应用程序",
"Volo.Account:InvalidEmailAddress": "找不到给定的电子邮件地址:{0}",
"Volo.Account:SelfRegistrationDisabled": "自我注册已禁用!",
"Volo.Account:InvalidEmailAddress": "找不到给定的电子邮件地址:{0}"
}
}
}

9
modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLoginModel.cs

@ -76,9 +76,9 @@ namespace Volo.Abp.Account.Web.Pages.Account
EnableLocalLogin = await SettingProvider.IsTrueAsync(AccountSettingNames.EnableLocalLogin);
if (context?.ClientId != null)
if (context?.Client?.ClientId != null)
{
var client = await ClientStore.FindEnabledClientByIdAsync(context.ClientId);
var client = await ClientStore.FindEnabledClientByIdAsync(context?.Client?.ClientId);
if (client != null)
{
EnableLocalLogin = client.EnableLocalLogin;
@ -108,7 +108,10 @@ namespace Volo.Abp.Account.Web.Pages.Account
return Redirect("~/");
}
await Interaction.GrantConsentAsync(context, ConsentResponse.Denied);
await Interaction.GrantConsentAsync(context, new ConsentResponse()
{
Error = AuthorizationError.AccessDenied
});
return Redirect(ReturnUrl);
}

6
modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Consent.cshtml

@ -96,8 +96,8 @@
}
<div>
<button name="UserDecision" value="yes" class="btn btn-primary" autofocus>Yes, Allow</button>
<button name="UserDecision" value="no" class="btn">No, Do Not Allow</button>
<button name="ConsentInput.UserDecision" value="yes" class="btn btn-primary" autofocus>Yes, Allow</button>
<button name="ConsentInput.UserDecision" value="no" class="btn">No, Do Not Allow</button>
@if (Model.ClientInfo.ClientUrl != null)
{
<a class="pull-right btn btn-secondary" target="_blank" href="@Model.ClientInfo.ClientUrl">
@ -110,4 +110,4 @@
</form>
</abp-card-body>
</abp-card>
</abp-card>

56
modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Consent.cshtml.cs

@ -6,6 +6,7 @@ using System.Threading.Tasks;
using IdentityServer4.Models;
using IdentityServer4.Services;
using IdentityServer4.Stores;
using IdentityServer4.Validation;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.RazorPages;
using Volo.Abp.UI;
@ -50,16 +51,16 @@ namespace Volo.Abp.Account.Web.Pages
throw new ApplicationException($"No consent request matching request: {ReturnUrl}");
}
var client = await _clientStore.FindEnabledClientByIdAsync(request.ClientId);
var client = await _clientStore.FindEnabledClientByIdAsync(request.Client.ClientId);
if (client == null)
{
throw new ApplicationException($"Invalid client id: {request.ClientId}");
throw new ApplicationException($"Invalid client id: {request.Client.ClientId}");
}
var resources = await _resourceStore.FindEnabledResourcesByScopeAsync(request.ScopesRequested);
var resources = await _resourceStore.FindEnabledResourcesByScopeAsync(request.ValidatedResources.RawScopeValues);
if (resources == null || (!resources.IdentityResources.Any() && !resources.ApiResources.Any()))
{
throw new ApplicationException($"No scopes matching: {request.ScopesRequested.Aggregate((x, y) => x + ", " + y)}");
throw new ApplicationException($"No scopes matching: {request.ValidatedResources.RawScopeValues.Aggregate((x, y) => x + ", " + y)}");
}
ClientInfo = new ClientInfoModel(client);
@ -67,14 +68,26 @@ namespace Volo.Abp.Account.Web.Pages
{
RememberConsent = true,
IdentityScopes = resources.IdentityResources.Select(x => CreateScopeViewModel(x, true)).ToList(),
ApiScopes = resources.ApiResources.SelectMany(x => x.Scopes).Select(x => CreateScopeViewModel(x, true)).ToList()
};
var apiScopes = new List<ScopeViewModel>();
foreach(var parsedScope in request.ValidatedResources.ParsedScopes)
{
var apiScope = request.ValidatedResources.Resources.FindApiScope(parsedScope.ParsedName);
if (apiScope != null)
{
var scopeVm = CreateScopeViewModel(parsedScope, apiScope, true);
apiScopes.Add(scopeVm);
}
}
if (resources.OfflineAccess)
{
ConsentInput.ApiScopes.Add(GetOfflineAccessScope(true));
apiScopes.Add(GetOfflineAccessScope(true));
}
ConsentInput.ApiScopes = apiScopes;
return Page();
}
@ -104,7 +117,10 @@ namespace Volo.Abp.Account.Web.Pages
if (ConsentInput.UserDecision == "no")
{
grantedConsent = ConsentResponse.Denied;
grantedConsent = new ConsentResponse
{
Error = AuthorizationError.AccessDenied
};
}
else
{
@ -113,7 +129,7 @@ namespace Volo.Abp.Account.Web.Pages
grantedConsent = new ConsentResponse
{
RememberConsent = ConsentInput.RememberConsent,
ScopesConsented = ConsentInput.GetAllowedScopeNames()
ScopesValuesConsented = ConsentInput.GetAllowedScopeNames()
};
}
else
@ -151,16 +167,22 @@ namespace Volo.Abp.Account.Web.Pages
};
}
protected virtual ConsentModel.ScopeViewModel CreateScopeViewModel(Scope scope, bool check)
protected virtual ConsentModel.ScopeViewModel CreateScopeViewModel(ParsedScopeValue parsedScopeValue, ApiScope apiScope, bool check)
{
return new ConsentModel.ScopeViewModel
var displayName = apiScope.DisplayName ?? apiScope.Name;
if (!string.IsNullOrWhiteSpace(parsedScopeValue.ParsedParameter))
{
displayName += ":" + parsedScopeValue.ParsedParameter;
}
return new ScopeViewModel
{
Name = scope.Name,
DisplayName = scope.DisplayName,
Description = scope.Description,
Emphasize = scope.Emphasize,
Required = scope.Required,
Checked = check || scope.Required
Name = parsedScopeValue.RawValue,
DisplayName = displayName,
Description = apiScope.Description,
Emphasize = apiScope.Emphasize,
Required = apiScope.Required,
Checked = check || apiScope.Required
};
}
@ -241,4 +263,4 @@ namespace Volo.Abp.Account.Web.Pages
}
}
}
}
}

110
modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Localization/Resources/zh-Hans.json

@ -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友好!"
}
}
}

2
modules/cms-kit/host/Volo.CmsKit.IdentityServer/IdentityServer/IdentityServerDataSeedContributor.cs

@ -142,7 +142,7 @@ namespace Volo.CmsKit.IdentityServer
string postLogoutRedirectUri = null,
IEnumerable<string> permissions = null)
{
var client = await _clientRepository.FindByCliendIdAsync(name);
var client = await _clientRepository.FindByClientIdAsync(name);
if (client == null)
{
client = await _clientRepository.InsertAsync(

22
modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/zh-Hans.json

@ -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": "星"
}
}

24
modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/zh-Hans.json

@ -7,6 +7,7 @@
"Permission:Delete": "删除",
"Permission:Create": "创建",
"Permission:Documents": "文档",
"Menu:Documents": "文档",
"Menu:DocumentManagement": "文档",
"Menu:ProjectManagement": "项目",
"CreateANewProject": "创建新项目",
@ -30,8 +31,29 @@
"DisplayName:GitHubRootUrl": "GitHub根网址",
"DisplayName:GitHubAccessToken": "GitHub访问令牌",
"DisplayName:GitHubUserAgent": "GitHub用户代理",
"DisplayName:GithubVersionProviderSource": "GitHub版本提供源",
"DisplayName:VersionBranchPrefix": "版本分支前缀",
"DisplayName:All": "拉取所有",
"DisplayName:LanguageCode": "语言代码",
"DisplayName:Version": "版本"
"DisplayName:Version": "版本",
"Documents": "文档",
"RemoveFromCache": "从缓存中删除",
"Reindex": "重新索引",
"ReindexCompleted": "重新索引完成",
"RemovedFromCache": "已从缓存中删除",
"RemoveFromCacheConfirmation": "你确定要从缓存中删除该项吗?",
"ReIndexDocumentConfirmation": "你确定要为该项重新索引吗",
"DeleteFromDatabase": "从数据库中删除",
"Deleted": "删除",
"Search": "搜索",
"StartDate": "开始时间",
"EndDate": "结束时间",
"CreationTime": "创建时间",
"LastUpdateTime": "最后修改",
"LastSignificantUpdateTime": "上次重大更新",
"Version": "版本",
"LanguageCode": "语言代码",
"FileName": "文件名称",
"LastCachedTime": "缓存项"
}
}

11
modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/zh-Hans.json

@ -1,6 +1,6 @@
{
"culture": "zh-Hans",
"texts": {
"texts": {
"Documents": "文档",
"BackToWebsite": "返回主网站",
"Contributors": "贡献者",
@ -9,6 +9,14 @@
"Edit": "编辑",
"LastEditTime": "上次编辑",
"Delete": "删除",
"ClearCache": "清除缓存",
"ClearCacheConfirmationMessage": "你确定清除 \"{0}\" 项目所有的缓存吗",
"ReIndexAllProjects": "重新索引所有的项目",
"ReIndexProject": "重新索引项目",
"ReIndexProjectConfirmationMessage": "你确定重新索引 \"{0}\" 项目",
"SuccessfullyReIndexProject": "成功重新索引 \"{0}\" 项目",
"ReIndexAllProjectConfirmationMessage": "你确定重新索引所有的项目",
"SuccessfullyReIndexAllProject": "成功重新索引所有的项目",
"InThisDocument": "在本文中",
"GoToTop": "到顶部",
"Projects": "项目",
@ -20,6 +28,7 @@
"FilterTopics": "过滤主题",
"FullSearch": "搜索文档",
"Volo.Docs.Domain:010001": "Elastic search未启用.",
"MultipleVersionDocumentInfo": "本文档有多个版本. 选择最适合你的选项",
"New": "新文档",
"Upd": "更新",
"NewExplanation": "在最近两周内创建.",

2
modules/feature-management/src/Volo.Abp.FeatureManagement.Application/Volo/Abp/FeatureManagement/FeatureAppService.cs

@ -114,7 +114,7 @@ namespace Volo.Abp.FeatureManagement
policyName = Options.ProviderPolicies.GetOrDefault(providerName);
if (policyName.IsNullOrEmpty())
{
throw new AbpException($"No policy defined to get/set permissions for the provider '{policyName}'. Use {nameof(FeatureManagementOptions)} to map the policy.");
throw new AbpException($"No policy defined to get/set permissions for the provider '{providerName}'. Use {nameof(FeatureManagementOptions)} to map the policy.");
}
}

5
modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/zh-Hans.json

@ -69,6 +69,7 @@
"Volo.Abp.Identity:010006": "无法删除静态角色.",
"Volo.Abp.Identity:010007": "你不能修改你的双因素身份验证设置",
"Volo.Abp.Identity:010008": "不允许修改双因素身份验证设置.",
"Identity.OrganizationUnit.MaxUserMembershipCount": "组织单位最大允许的成员资格计数",
"Permission:IdentityManagement": "身份标识管理",
"Permission:RoleManagement": "角色管理",
"Permission:Create": "创建",
@ -93,6 +94,7 @@
"DisplayName:Abp.Identity.Lockout.LockoutDuration": "锁定时间(秒)",
"DisplayName:Abp.Identity.Lockout.MaxFailedAccessAttempts": "最大失败访问尝试次数",
"DisplayName:Abp.Identity.SignIn.RequireConfirmedEmail": "要求验证的电子邮箱",
"DisplayName:Abp.Identity.SignIn.EnablePhoneNumberConfirmation": "允许用户确认他们的电话号码",
"DisplayName:Abp.Identity.SignIn.RequireConfirmedPhoneNumber": "要求验证的手机号码",
"DisplayName:Abp.Identity.User.IsUserNameUpdateEnabled": "启用用户名更新",
"DisplayName:Abp.Identity.User.IsEmailUpdateEnabled": "启用电子邮箱更新",
@ -106,6 +108,7 @@
"Description:Abp.Identity.Lockout.LockoutDuration": "当锁定发生时用户被的锁定的时间(秒).",
"Description:Abp.Identity.Lockout.MaxFailedAccessAttempts": "如果启用锁定, 当用户被锁定前失败的访问尝试次数.",
"Description:Abp.Identity.SignIn.RequireConfirmedEmail": "登录时是否需要验证的电子邮箱.",
"Description:Abp.Identity.SignIn.EnablePhoneNumberConfirmation": "用户是否可以确认电话号码",
"Description:Abp.Identity.SignIn.RequireConfirmedPhoneNumber": "登录时是否需要验证的手机号码.",
"Description:Abp.Identity.User.IsUserNameUpdateEnabled": "是否允许用户更新用户名.",
"Description:Abp.Identity.User.IsEmailUpdateEnabled": "是否允许用户更新电子邮箱.",
@ -114,4 +117,4 @@
"DisplayName:Abp.Identity.UsersCanChange": "允许用户更改其因素身份验证.",
"Description:Abp.Identity.UsersCanChange": "允许用户更改其因素身份验证."
}
}
}

27
modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserLinkManager.cs

@ -32,16 +32,22 @@ namespace Volo.Abp.Identity
return;
}
var userLink = new IdentityLinkUser(
GuidGenerator.Create(),
sourceLinkUser,
targetLinkUser);
await IdentityLinkUserRepository.InsertAsync(userLink, true);
using (CurrentTenant.Change(null))
{
var userLink = new IdentityLinkUser(
GuidGenerator.Create(),
sourceLinkUser,
targetLinkUser);
await IdentityLinkUserRepository.InsertAsync(userLink, true);
}
}
public virtual async Task<bool> IsLinkedAsync(IdentityLinkUserInfo sourceLinkUser, IdentityLinkUserInfo targetLinkUser)
{
return await IdentityLinkUserRepository.FindAsync(sourceLinkUser, targetLinkUser) != null;
using (CurrentTenant.Change(null))
{
return await IdentityLinkUserRepository.FindAsync(sourceLinkUser, targetLinkUser) != null;
}
}
public virtual async Task UnlinkAsync(IdentityLinkUserInfo sourceLinkUser, IdentityLinkUserInfo targetLinkUser)
@ -51,10 +57,13 @@ namespace Volo.Abp.Identity
return;
}
var linkedUser = await IdentityLinkUserRepository.FindAsync(sourceLinkUser, targetLinkUser);
if (linkedUser != null)
using (CurrentTenant.Change(null))
{
await IdentityLinkUserRepository.DeleteAsync(linkedUser);
var linkedUser = await IdentityLinkUserRepository.FindAsync(sourceLinkUser, targetLinkUser);
if (linkedUser != null)
{
await IdentityLinkUserRepository.DeleteAsync(linkedUser);
}
}
}

12
modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/ApiResources/ApiResourceConsts.cs

@ -2,8 +2,12 @@
{
public class ApiResourceConsts
{
public const int NameMaxLength = 200;
public const int DisplayNameMaxLength = 200;
public const int DescriptionMaxLength = 1000;
public static int NameMaxLength { get; set; } = 200;
public static int DisplayNameMaxLength { get; set; } = 200;
public static int DescriptionMaxLength { get; set; } = 1000;
public static int AllowedAccessTokenSigningAlgorithmsMaxLength { get; set; } = 100;
}
}
}

2
modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/ApiResources/ApiResourceEto.cs

@ -17,4 +17,4 @@ namespace Volo.Abp.IdentityServer.ApiResources
public bool Enabled { get; set; }
}
}
}

9
modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/ApiResources/ApiResourcePropertyConsts.cs

@ -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;
}
}

7
modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/ApiResources/ApiResourceScopeConsts.cs

@ -0,0 +1,7 @@
namespace Volo.Abp.IdentityServer.ApiResources
{
public class ApiResourceScopeConsts
{
public static int ScopeMaxLength { get; set; } = 200;
}
}

20
modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/ApiResources/ApiResourceSecretConsts.cs

@ -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;
}
}

9
modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/ApiResources/ApiScopeConsts.cs

@ -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;
}
}

11
modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/ApiScopes/ApiResourceConsts.cs

@ -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;
}
}

9
modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/ApiScopes/ApiScopePropertyConsts.cs

@ -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;
}
}

7
modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Clients/ClientClaimConsts.cs

@ -2,7 +2,8 @@
{
public class ClientClaimConsts
{
public const int TypeMaxLength = 250;
public const int ValueMaxLength = 250;
public static int TypeMaxLength { get; set; } = 250;
public static int ValueMaxLength { get; set; } = 250;
}
}
}

26
modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Clients/ClientConsts.cs

@ -2,26 +2,28 @@
{
public class ClientConsts
{
public const int ClientIdMaxLength = 200;
public static int ClientIdMaxLength { get; set; } = 200;
public const int ProtocolTypeMaxLength = 200;
public static int ProtocolTypeMaxLength { get; set; } = 200;
public const int ClientNameMaxLength = 200;
public static int ClientNameMaxLength { get; set; } = 200;
public const int ClientUriMaxLength = 2000;
public static int ClientUriMaxLength { get; set; } = 2000;
public const int LogoUriMaxLength = 2000;
public static int LogoUriMaxLength { get; set; } = 2000;
public const int DescriptionMaxLength = 1000;
public static int DescriptionMaxLength { get; set; } = 1000;
public const int FrontChannelLogoutUriMaxLength = 2000;
public static int FrontChannelLogoutUriMaxLength { get; set; } = 2000;
public const int BackChannelLogoutUriMaxLength = 2000;
public static int BackChannelLogoutUriMaxLength { get; set; } = 2000;
public const int ClientClaimsPrefixMaxLength = 200;
public static int ClientClaimsPrefixMaxLength { get; set; } = 200;
public const int PairWiseSubjectSaltMaxLength = 200;
public static int PairWiseSubjectSaltMaxLength { get; set; } = 200;
public const int UserCodeTypeMaxLength = 100;
public static int UserCodeTypeMaxLength { get; set; } = 100;
public static int AllowedIdentityTokenSigningAlgorithms { get; set; } = 100;
}
}
}

4
modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Clients/ClientCorsOriginConsts.cs

@ -2,6 +2,6 @@
{
public class ClientCorsOriginConsts
{
public const int OriginMaxLength = 150;
public static int OriginMaxLength { get; set; } = 150;
}
}
}

4
modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Clients/ClientGrantTypeConsts.cs

@ -2,6 +2,6 @@
{
public class ClientGrantTypeConsts
{
public const int GrantTypeMaxLength = 250;
public static int GrantTypeMaxLength { get; set; } = 250;
}
}
}

12
modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/SecretConsts.cs → modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Clients/ClientSecretConsts.cs

@ -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;
}
}
}

19
modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Devices/DeviceFlowCodesConsts.cs

@ -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;
}
}

6
modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Devices/DeviceFlowCodesEto.cs

@ -13,10 +13,14 @@ namespace Volo.Abp.IdentityServer.Devices
public string SubjectId { get; set; }
public string SessionId { get; set; }
public string ClientId { get; set; }
public string Description { get; set; }
public DateTime? Expiration { get; set; }
public string Data { get; set; }
}
}
}

22
modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Grants/PersistedGrantConsts.cs

@ -6,30 +6,40 @@
/// Default value: 200
/// </summary>
public static int KeyMaxLength { get; set; } = 200;
/// <summary>
/// Default value: 50
/// </summary>
public static int TypeMaxLength { get; set; } = 50;
/// <summary>
/// Default value: 200
/// </summary>
public static int SubjectIdMaxLength { get; set; } = 200;
/// <summary>
/// Default value: 100
/// </summary>
public static int SessionIdMaxLength { get; set; } = 100;
/// <summary>
/// Default value: 200
/// </summary>
public static int ClientIdMaxLength { get; set; } = 200;
/// <summary>
/// Default value: 200
/// </summary>
public static int DescriptionMaxLength { get; set; } = 200;
/// <summary>
/// Default value: 50000
/// </summary>
public static int DataMaxLength { get; set; } = 50000;
/// <summary>
/// Default value: 50000
/// </summary>
public static int DataMaxLengthValue { get; set; } = 50000;
}
}
}

8
modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/IdentityResources/IdentityResourceConsts.cs

@ -6,15 +6,15 @@
/// Default value: 200
/// </summary>
public static int NameMaxLength { get; set; } = 200;
/// <summary>
/// Default value: 200
/// </summary>
public static int DisplayNameMaxLength { get; set; } = 200;
/// <summary>
/// Default value: 200
/// Default value: 1000
/// </summary>
public static int DescriptionMaxLength { get; set; } = 1000;
}
}
}

9
modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/IdentityResources/IdentityResourcePropertyConsts.cs

@ -0,0 +1,9 @@
namespace Volo.Abp.IdentityServer.IdentityResources
{
public class IdentityResourcePropertyConsts
{
public static int KeyMaxLength { get; set; } = 250;
public static int ValueMaxLength { get; set; } = 2000;
}
}

1
modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/en.json

@ -3,6 +3,7 @@
"texts": {
"Volo.IdentityServer:DuplicateIdentityResourceName": "Identity Resource name already exist: {Name}",
"Volo.IdentityServer:DuplicateApiResourceName": "Api Resource name already exist: {Name}",
"Volo.IdentityServer:DuplicateApiScopeName": "Api Scope name already exist: {Name}",
"Volo.IdentityServer:DuplicateClientId": "ClientId already exist: {ClientId}",
"UserLockedOut": "The user account has been locked out due to invalid login attempts. Please wait a while and try again.",
"InvalidUserNameOrPassword": "Invalid username or password!",

1
modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/zh-Hans.json

@ -3,6 +3,7 @@
"texts": {
"Volo.IdentityServer:DuplicateIdentityResourceName": "Identity资源名称已存在: {Name}",
"Volo.IdentityServer:DuplicateApiResourceName": "Api资源名称已存在: {Name}",
"Volo.IdentityServer:DuplicateApiScopeName": "Api Scope已存在: {Name}",
"Volo.IdentityServer:DuplicateClientId": "ClientId已经存在: {ClientId}",
"UserLockedOut": "登录失败,用户账户已被锁定.请稍后再试.",
"InvalidUserNameOrPassword": "用户名或密码错误!",

9
modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/ObjectExtending/IdentityServerModuleExtensionConfiguration.cs

@ -23,6 +23,15 @@ namespace Volo.Abp.ObjectExtending
);
}
public IdentityServerModuleExtensionConfiguration ConfigureApiScope(
Action<EntityExtensionConfiguration> configureAction)
{
return this.ConfigureEntity(
IdentityServerModuleExtensionConsts.EntityNames.ApiScope,
configureAction
);
}
public IdentityServerModuleExtensionConfiguration ConfigureIdentityResource(
Action<EntityExtensionConfiguration> configureAction)
{

4
modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/ObjectExtending/IdentityServerModuleExtensionConsts.cs

@ -11,6 +11,8 @@
public const string IdentityResource = "IdentityResource";
public const string ApiResource = "ApiResource";
public const string ApiScope = "ApiScope";
}
}
}
}

4
modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo.Abp.IdentityServer.Domain.csproj

@ -25,8 +25,8 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="IdentityServer4" Version="3.1.3" />
<PackageReference Include="IdentityServer4.AspNetIdentity" Version="3.1.3" />
<PackageReference Include="IdentityServer4" Version="4.1.1" />
<PackageReference Include="IdentityServer4.AspNetIdentity" Version="4.1.1" />
</ItemGroup>
</Project>

4
modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpIdentityServerBuilderExtensions.cs

@ -55,7 +55,9 @@ namespace Volo.Abp.IdentityServer
return builder;
}
public static IIdentityServerBuilder AddAbpDeveloperSigningCredential(
//TODO: Use the latest Identity server code to optimize performance.
// https://github.com/IdentityServer/IdentityServer4/blob/main/src/IdentityServer4/src/Configuration/DependencyInjection/BuilderExtensions/Crypto.cs
private static IIdentityServerBuilder AddAbpDeveloperSigningCredential(
this IIdentityServerBuilder builder,
bool persistKey = true,
string filename = null,

3
modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpIdentityServerDomainModule.cs

@ -10,6 +10,7 @@ using Volo.Abp.Domain.Entities.Events.Distributed;
using Volo.Abp.Identity;
using Volo.Abp.IdentityServer.ApiResources;
using Volo.Abp.IdentityServer.AspNetIdentity;
using Volo.Abp.IdentityServer.ApiScopes;
using Volo.Abp.IdentityServer.Clients;
using Volo.Abp.IdentityServer.Devices;
using Volo.Abp.IdentityServer.IdentityResources;
@ -68,7 +69,7 @@ namespace Volo.Abp.IdentityServer
if (builderOptions.AddDeveloperSigningCredential)
{
identityServerBuilder = identityServerBuilder.AddAbpDeveloperSigningCredential();
identityServerBuilder = identityServerBuilder.AddDeveloperSigningCredential();
}
identityServerBuilder.AddAbpIdentityServer(builderOptions);

37
modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AllowedSigningAlgorithmsConverter.cs

@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.Linq;
using AutoMapper;
namespace Volo.Abp.IdentityServer
{
public class AllowedSigningAlgorithmsConverter :
IValueConverter<ICollection<string>, string>,
IValueConverter<string, ICollection<string>>
{
public static AllowedSigningAlgorithmsConverter Converter = new AllowedSigningAlgorithmsConverter();
public string Convert(ICollection<string> sourceMember, ResolutionContext context)
{
if (sourceMember == null || !sourceMember.Any())
{
return null;
}
return sourceMember.Aggregate((x, y) => $"{x},{y}");
}
public ICollection<string> Convert(string sourceMember, ResolutionContext context)
{
var list = new HashSet<string>();
if (!String.IsNullOrWhiteSpace(sourceMember))
{
sourceMember = sourceMember.Trim();
foreach (var item in sourceMember.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Distinct())
{
list.Add(item);
}
}
return list;
}
}
}

72
modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/ApiResources/ApiResource.cs

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using IdentityServer4;
@ -18,13 +18,17 @@ namespace Volo.Abp.IdentityServer.ApiResources
public virtual bool Enabled { get; set; }
public virtual List<ApiSecret> Secrets { get; protected set; }
public virtual string AllowedAccessTokenSigningAlgorithms { get; set; }
public virtual List<ApiScope> Scopes { get; protected set; }
public virtual bool ShowInDiscoveryDocument { get; set; } = true;
public virtual List<ApiResourceSecret> Secrets { get; protected set; }
public virtual List<ApiResourceScope> Scopes { get; protected set; }
public virtual List<ApiResourceClaim> UserClaims { get; protected set; }
public virtual Dictionary<string, string> Properties { get; protected set; }
public virtual List<ApiResourceProperty> Properties { get; protected set; }
protected ApiResource()
{
@ -44,21 +48,21 @@ namespace Volo.Abp.IdentityServer.ApiResources
Enabled = true;
Secrets = new List<ApiSecret>();
Scopes = new List<ApiScope>();
Secrets = new List<ApiResourceSecret>();
Scopes = new List<ApiResourceScope>();
UserClaims = new List<ApiResourceClaim>();
Properties = new Dictionary<string, string>();
Properties = new List<ApiResourceProperty>();
Scopes.Add(new ApiScope(id, name, displayName, description));
Scopes.Add(new ApiResourceScope(id, name));
}
public virtual void AddSecret(
[NotNull] string value,
[NotNull] string value,
DateTime? expiration = null,
string type = IdentityServerConstants.SecretTypes.SharedSecret,
string description = null)
{
Secrets.Add(new ApiSecret(Id, value, expiration, type, description));
Secrets.Add(new ApiResourceSecret(Id, value, expiration, type, description));
}
public virtual void RemoveSecret([NotNull] string value, string type = IdentityServerConstants.SecretTypes.SharedSecret)
@ -66,22 +70,16 @@ namespace Volo.Abp.IdentityServer.ApiResources
Secrets.RemoveAll(s => s.Value == value && s.Type == type);
}
public virtual ApiSecret FindSecret([NotNull] string value, string type = IdentityServerConstants.SecretTypes.SharedSecret)
public virtual ApiResourceSecret FindSecret([NotNull] string value, string type = IdentityServerConstants.SecretTypes.SharedSecret)
{
return Secrets.FirstOrDefault(s => s.Type == type && s.Value == value);
}
public virtual ApiScope AddScope(
[NotNull] string name,
string displayName = null,
string description = null,
bool required = false,
bool emphasize = false,
bool showInDiscoveryDocument = true)
public virtual ApiResourceScope AddScope([NotNull] string scope)
{
var scope = new ApiScope(Id, name, displayName, description, required, emphasize, showInDiscoveryDocument);
Scopes.Add(scope);
return scope;
var apiResourceScope = new ApiResourceScope(Id, scope);
Scopes.Add(apiResourceScope);
return apiResourceScope;
}
public virtual void AddUserClaim([NotNull] string type)
@ -111,21 +109,37 @@ namespace Volo.Abp.IdentityServer.ApiResources
public virtual void RemoveAllScopes()
{
foreach (var scope in Scopes)
{
scope.RemoveAllUserClaims();
}
Scopes.Clear();
}
public virtual void RemoveScope(string name)
public virtual void RemoveScope(string scope)
{
Scopes.RemoveAll(r => r.Scope == scope);
}
public virtual ApiResourceScope FindScope(string scope)
{
return Scopes.FirstOrDefault(r => r.Scope == scope);
}
public virtual void AddProperty([NotNull] string key, string value)
{
Properties.Add(new ApiResourceProperty(Id, key, value));
}
public virtual void RemoveAllProperties()
{
Properties.Clear();
}
public virtual void RemoveProperty(string key)
{
Scopes.RemoveAll(r => r.Name == name);
Properties.RemoveAll(r => r.Key == key);
}
public virtual ApiScope FindScope(string name)
public virtual ApiResourceProperty FindProperty(string key)
{
return Scopes.FirstOrDefault(r => r.Name == name);
return Properties.FirstOrDefault(r => r.Key == key);
}
}
}

2
modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/ApiResources/ApiResourceClaim.cs

@ -28,4 +28,4 @@ namespace Volo.Abp.IdentityServer.ApiResources
return new object[] {ApiResourceId, Type};
}
}
}
}

39
modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/ApiResources/ApiResourceProperty.cs

@ -0,0 +1,39 @@
using System;
using JetBrains.Annotations;
using Volo.Abp.Domain.Entities;
namespace Volo.Abp.IdentityServer.ApiResources
{
public class ApiResourceProperty : Entity
{
public virtual Guid ApiResourceId { get; protected set; }
public virtual string Key { get; set; }
public virtual string Value { get; set; }
protected ApiResourceProperty()
{
}
public virtual bool Equals(Guid aiResourceId, [NotNull] string key, string value)
{
return ApiResourceId == aiResourceId && Key == key && Value == value;
}
protected internal ApiResourceProperty(Guid aiResourceId, [NotNull] string key, [NotNull] string value)
{
Check.NotNull(key, nameof(key));
ApiResourceId = aiResourceId;
Key = key;
Value = value;
}
public override object[] GetKeys()
{
return new object[] { ApiResourceId, Key };
}
}
}

38
modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/ApiResources/ApiResourceScope.cs

@ -0,0 +1,38 @@
using System;
using JetBrains.Annotations;
using Volo.Abp.Domain.Entities;
namespace Volo.Abp.IdentityServer.ApiResources
{
public class ApiResourceScope : Entity
{
public virtual Guid ApiResourceId { get; protected set; }
public virtual string Scope { get; set; }
protected ApiResourceScope()
{
}
public virtual bool Equals(Guid apiResourceId, [NotNull] string scope)
{
return ApiResourceId == apiResourceId && Scope == scope;
}
protected internal ApiResourceScope(
Guid apiResourceId,
[NotNull] string scope)
{
Check.NotNull(scope, nameof(scope));
ApiResourceId = apiResourceId;
Scope = scope;
}
public override object[] GetKeys()
{
return new object[] { ApiResourceId, Scope };
}
}
}

20
modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/ApiResources/ApiSecret.cs → modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/ApiResources/ApiResourceSecret.cs

@ -4,11 +4,11 @@ using JetBrains.Annotations;
namespace Volo.Abp.IdentityServer.ApiResources
{
public class ApiSecret : Secret
public class ApiResourceSecret : Secret
{
public virtual Guid ApiResourceId { get; protected set; }
protected ApiSecret()
protected ApiResourceSecret()
{
}
@ -18,16 +18,16 @@ namespace Volo.Abp.IdentityServer.ApiResources
return ApiResourceId == apiResourceId && Value == value && Type == type;
}
protected internal ApiSecret(
protected internal ApiResourceSecret(
Guid apiResourceId,
[NotNull] string value,
DateTime? expiration = null,
string type = IdentityServerConstants.SecretTypes.SharedSecret,
[NotNull] string value,
DateTime? expiration = null,
string type = IdentityServerConstants.SecretTypes.SharedSecret,
string description = null
) : base(
value,
expiration,
type,
value,
expiration,
type,
description)
{
ApiResourceId = apiResourceId;
@ -38,4 +38,4 @@ namespace Volo.Abp.IdentityServer.ApiResources
return new object[] { ApiResourceId, Type, Value };
}
}
}
}

37
modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/ApiResources/ApiScopeClaim.cs

@ -1,37 +0,0 @@
using System;
using JetBrains.Annotations;
namespace Volo.Abp.IdentityServer.ApiResources
{
public class ApiScopeClaim : UserClaim
{
public Guid ApiResourceId { get; protected set; }
[NotNull]
public string Name { get; protected set; }
protected ApiScopeClaim()
{
}
public virtual bool Equals(Guid apiResourceId, [NotNull] string name, [NotNull] string type)
{
return ApiResourceId == apiResourceId && Name == name && Type == type;
}
protected internal ApiScopeClaim(Guid apiResourceId, [NotNull] string name, [NotNull] string type)
: base(type)
{
Check.NotNull(name, nameof(name));
ApiResourceId = apiResourceId;
Name = name;
}
public override object[] GetKeys()
{
return new object[] { ApiResourceId, Name, Type };
}
}
}

15
modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/ApiResources/IApiResourceRepository.cs

@ -9,7 +9,13 @@ namespace Volo.Abp.IdentityServer.ApiResources
public interface IApiResourceRepository : IBasicRepository<ApiResource, Guid>
{
Task<ApiResource> FindByNameAsync(
string name,
string apiResourceName,
bool includeDetails = true,
CancellationToken cancellationToken = default
);
Task<List<ApiResource>> FindByNameAsync(
string[] apiResourceNames,
bool includeDetails = true,
CancellationToken cancellationToken = default
);
@ -29,15 +35,10 @@ namespace Volo.Abp.IdentityServer.ApiResources
CancellationToken cancellationToken = default
);
Task<List<ApiResource>> GetListAsync(
bool includeDetails = false,
CancellationToken cancellationToken = default
);
Task<bool> CheckNameExistAsync(
string name,
Guid? expectedId = null,
CancellationToken cancellationToken = default
);
}
}
}

63
modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/ApiResources/ApiScope.cs → modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/ApiScopes/ApiScope.cs

@ -1,14 +1,14 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Entities.Auditing;
namespace Volo.Abp.IdentityServer.ApiResources
namespace Volo.Abp.IdentityServer.ApiScopes
{
public class ApiScope : Entity
public class ApiScope : FullAuditedAggregateRoot<Guid>
{
public virtual Guid ApiResourceId { get; protected set; }
public virtual bool Enabled { get; set; }
[NotNull]
public virtual string Name { get; protected set; }
@ -25,41 +25,41 @@ namespace Volo.Abp.IdentityServer.ApiResources
public virtual List<ApiScopeClaim> UserClaims { get; protected set; }
public virtual List<ApiScopeProperty> Properties { get; protected set; }
protected ApiScope()
{
}
public virtual bool Equals(Guid apiResourceId, [NotNull] string name)
{
return ApiResourceId == apiResourceId && Name == name;
}
protected internal ApiScope(
Guid apiResourceId,
[NotNull] string name,
string displayName = null,
string description = null,
bool required = false,
bool emphasize = false,
bool showInDiscoveryDocument = true)
public ApiScope(
Guid id,
[NotNull] string name,
string displayName = null,
string description = null,
bool required = false,
bool emphasize = false,
bool showInDiscoveryDocument = true,
bool enabled = true)
{
Check.NotNull(name, nameof(name));
ApiResourceId = apiResourceId;
Id = id;
Name = name;
DisplayName = displayName ?? name;
Description = description;
Required = required;
Emphasize = emphasize;
ShowInDiscoveryDocument = showInDiscoveryDocument;
Enabled = enabled;
UserClaims = new List<ApiScopeClaim>();
Properties = new List<ApiScopeProperty>();
}
public virtual void AddUserClaim([NotNull] string type)
{
UserClaims.Add(new ApiScopeClaim(ApiResourceId, Name, type));
UserClaims.Add(new ApiScopeClaim(Id, type));
}
public virtual void RemoveAllUserClaims()
@ -74,12 +74,27 @@ namespace Volo.Abp.IdentityServer.ApiResources
public virtual ApiScopeClaim FindClaim(string type)
{
return UserClaims.FirstOrDefault(r => r.Name == Name && r.Type == type);
return UserClaims.FirstOrDefault(r => r.Type == type);
}
public virtual void AddProperty([NotNull] string key, string value)
{
Properties.Add(new ApiScopeProperty(Id, key, value));
}
public virtual void RemoveAllProperties()
{
Properties.Clear();
}
public virtual void RemoveProperty(string key)
{
Properties.RemoveAll(r => r.Key == key);
}
public override object[] GetKeys()
public virtual ApiScopeProperty FindProperty(string key)
{
return new object[] { ApiResourceId, Name };
return Properties.FirstOrDefault(r => r.Key == key);
}
}
}
}

31
modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/ApiScopes/ApiScopeClaim.cs

@ -0,0 +1,31 @@
using System;
using JetBrains.Annotations;
namespace Volo.Abp.IdentityServer.ApiScopes
{
public class ApiScopeClaim : UserClaim
{
public Guid ApiScopeId { get; protected set; }
protected ApiScopeClaim()
{
}
public virtual bool Equals(Guid apiScopeId, [NotNull] string type)
{
return ApiScopeId == apiScopeId && Type == type;
}
protected internal ApiScopeClaim(Guid apiScopeId, [NotNull] string type)
: base(type)
{
ApiScopeId = apiScopeId;
}
public override object[] GetKeys()
{
return new object[] { ApiScopeId, Type };
}
}
}

39
modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/ApiScopes/ApiScopeProperty.cs

@ -0,0 +1,39 @@
using System;
using JetBrains.Annotations;
using Volo.Abp.Domain.Entities;
namespace Volo.Abp.IdentityServer.ApiScopes
{
public class ApiScopeProperty : Entity
{
public virtual Guid ApiScopeId { get; set; }
public virtual string Key { get; set; }
public virtual string Value { get; set; }
protected ApiScopeProperty()
{
}
public virtual bool Equals(Guid apiScopeId, [NotNull] string key, string value)
{
return ApiScopeId == apiScopeId && Key == key && Value == value;
}
protected internal ApiScopeProperty(Guid apiScopeId, [NotNull] string key, [NotNull] string value)
{
Check.NotNull(key, nameof(key));
ApiScopeId = apiScopeId;
Key = key;
Value = value;
}
public override object[] GetKeys()
{
return new object[] { ApiScopeId, Key };
}
}
}

38
modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/ApiScopes/IApiScopeeRepository.cs

@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories;
namespace Volo.Abp.IdentityServer.ApiScopes
{
public interface IApiScopeRepository : IBasicRepository<ApiScope, Guid>
{
Task<ApiScope> GetByNameAsync(
string scopeName,
bool includeDetails = true,
CancellationToken cancellationToken = default
);
Task<List<ApiScope>> GetListByNameAsync(
string[] scopeNames,
bool includeDetails = false,
CancellationToken cancellationToken = default
);
Task<List<ApiScope>> GetListAsync(
string sorting,
int skipCount,
int maxResultCount,
string filter = null,
bool includeDetails = false,
CancellationToken cancellationToken = default
);
Task<bool> CheckNameExistAsync(
string name,
Guid? expectedId = null,
CancellationToken cancellationToken = default
);
}
}

10
modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AspNetIdentity/AbpResourceOwnerPasswordValidator.cs

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
@ -27,7 +27,6 @@ namespace Volo.Abp.IdentityServer.AspNetIdentity
public class AbpResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator
{
protected SignInManager<IdentityUser> SignInManager { get; }
protected IEventService Events { get; }
protected UserManager<IdentityUser> UserManager { get; }
protected IdentitySecurityLogManager IdentitySecurityLogManager { get; }
protected ILogger<ResourceOwnerPasswordValidator<IdentityUser>> Logger { get; }
@ -39,7 +38,6 @@ namespace Volo.Abp.IdentityServer.AspNetIdentity
UserManager<IdentityUser> userManager,
SignInManager<IdentityUser> signInManager,
IdentitySecurityLogManager identitySecurityLogManager,
IEventService events,
ILogger<ResourceOwnerPasswordValidator<IdentityUser>> logger,
IStringLocalizer<AbpIdentityServerResource> localizer,
IOptions<AbpIdentityOptions> abpIdentityOptions,
@ -48,7 +46,6 @@ namespace Volo.Abp.IdentityServer.AspNetIdentity
UserManager = userManager;
SignInManager = signInManager;
IdentitySecurityLogManager = identitySecurityLogManager;
Events = events;
Logger = logger;
Localizer = localizer;
ServiceScopeFactory = serviceScopeFactory;
@ -75,7 +72,6 @@ namespace Volo.Abp.IdentityServer.AspNetIdentity
var sub = await UserManager.GetUserIdAsync(user);
Logger.LogInformation("Credentials validated for username: {username}", context.UserName);
await Events.RaiseAsync(new UserLoginSuccessEvent(context.UserName, sub, context.UserName, interactive: false));
var additionalClaims = new List<Claim>();
@ -136,19 +132,16 @@ namespace Volo.Abp.IdentityServer.AspNetIdentity
else if (result.IsLockedOut)
{
Logger.LogInformation("Authentication failed for username: {username}, reason: locked out", context.UserName);
await Events.RaiseAsync(new UserLoginFailureEvent(context.UserName, "locked out", interactive: false));
errorDescription = Localizer["UserLockedOut"];
}
else if (result.IsNotAllowed)
{
Logger.LogInformation("Authentication failed for username: {username}, reason: not allowed", context.UserName);
await Events.RaiseAsync(new UserLoginFailureEvent(context.UserName, "not allowed", interactive: false));
errorDescription = Localizer["LoginIsNotAllowed"];
}
else
{
Logger.LogInformation("Authentication failed for username: {username}, reason: invalid credentials", context.UserName);
await Events.RaiseAsync(new UserLoginFailureEvent(context.UserName, "invalid credentials", interactive: false));
errorDescription = Localizer["InvalidUserNameOrPassword"];
}
@ -163,7 +156,6 @@ namespace Volo.Abp.IdentityServer.AspNetIdentity
else
{
Logger.LogInformation("No user found matching username: {username}", context.UserName);
await Events.RaiseAsync(new UserLoginFailureEvent(context.UserName, "invalid username", interactive: false));
errorDescription = Localizer["InvalidUsername"];
await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext()

9
modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Clients/Client.cs

@ -36,6 +36,8 @@ namespace Volo.Abp.IdentityServer.Clients
public virtual bool AllowPlainTextPkce { get; set; }
public virtual bool RequireRequestObject { get; set; }
public virtual bool AllowAccessTokensViaBrowser { get; set; }
public virtual string FrontChannelLogoutUri { get; set; }
@ -50,6 +52,8 @@ namespace Volo.Abp.IdentityServer.Clients
public virtual int IdentityTokenLifetime { get; set; }
public virtual string AllowedIdentityTokenSigningAlgorithms { get; set; }
public virtual int AccessTokenLifetime { get; set; }
public virtual int AuthorizationCodeLifetime { get; set; }
@ -118,8 +122,9 @@ namespace Volo.Abp.IdentityServer.Clients
ProtocolType = IdentityServerConstants.ProtocolTypes.OpenIdConnect;
RequireClientSecret = true;
RequireConsent = true;
RequireConsent = false;
AllowRememberConsent = true;
RequirePkce = true;
FrontChannelLogoutSessionRequired = true;
BackChannelLogoutSessionRequired = true;
IdentityTokenLifetime = 300;
@ -319,4 +324,4 @@ namespace Volo.Abp.IdentityServer.Clients
return IdentityProviderRestrictions.FirstOrDefault(r => r.Provider == provider);
}
}
}
}

4
modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Clients/ClientProperty.cs

@ -17,7 +17,7 @@ namespace Volo.Abp.IdentityServer.Clients
}
public virtual bool Equals(Guid clientId, [NotNull] string key, string value)
public virtual bool Equals(Guid clientId, [NotNull] string key, [NotNull] string value)
{
return ClientId == clientId && Key == key && Value == value;
}
@ -36,4 +36,4 @@ namespace Volo.Abp.IdentityServer.Clients
return new object[] { ClientId, Key };
}
}
}
}

2
modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Clients/ClientStore.cs

@ -17,7 +17,7 @@ namespace Volo.Abp.IdentityServer.Clients
public virtual async Task<IdentityServer4.Models.Client> FindClientByIdAsync(string clientId)
{
var client = await ClientRepository.FindByCliendIdAsync(clientId);
var client = await ClientRepository.FindByClientIdAsync(clientId);
return ObjectMapper.Map<Client, IdentityServer4.Models.Client>(client);
}
}

4
modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Clients/IClientRepository.cs

@ -9,7 +9,7 @@ namespace Volo.Abp.IdentityServer.Clients
{
public interface IClientRepository : IBasicRepository<Client, Guid>
{
Task<Client> FindByCliendIdAsync(
Task<Client> FindByClientIdAsync(
[NotNull] string clientId,
bool includeDetails = true,
CancellationToken cancellationToken = default
@ -32,4 +32,4 @@ namespace Volo.Abp.IdentityServer.Clients
CancellationToken cancellationToken = default
);
}
}
}

6
modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Devices/DeviceFlowCodes.cs

@ -11,8 +11,12 @@ namespace Volo.Abp.IdentityServer.Devices
public virtual string SubjectId { get; set; }
public virtual string SessionId { get; set; }
public virtual string ClientId { get; set; }
public virtual string Description { get; set; }
public virtual DateTime? Expiration { get; set; }
public virtual string Data { get; set; }
@ -28,4 +32,4 @@ namespace Volo.Abp.IdentityServer.Devices
}
}
}
}

21
modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Grants/IPersistentGrantRepository.cs

@ -8,6 +8,12 @@ namespace Volo.Abp.IdentityServer.Grants
{
public interface IPersistentGrantRepository : IBasicRepository<PersistedGrant, Guid>
{
Task<List<PersistedGrant>> GetListAsync(
string subjectId,
string sessionId,
string clientId,
string type, bool includeDetails = false, CancellationToken cancellationToken = default);
Task<PersistedGrant> FindByKeyAsync(
string key,
CancellationToken cancellationToken = default
@ -25,16 +31,11 @@ namespace Volo.Abp.IdentityServer.Grants
);
Task DeleteAsync(
string subjectId,
string clientId,
CancellationToken cancellationToken = default
);
Task DeleteAsync(
string subjectId,
string clientId,
string type,
string subjectId = null,
string sessionId = null,
string clientId = null,
string type = null,
CancellationToken cancellationToken = default
);
}
}
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save