(mvcBuilder =>
+ {
+ // 添加插件程序集
+ mvcBuilder.PartManager.ApplicationParts.Add(new AssemblyPart(typeof(MyMvcUIPlugInModule).Assembly));
+
+ // 添加视图程序集
+ var viewDllPath = Path.Combine(Path.GetDirectoryName(typeof(MyMvcUIPlugInModule).Assembly.Location), "MyMvcUIPlugIn.Views.dll");
+ var viewAssembly = new CompiledRazorAssemblyPart(Assembly.LoadFrom(viewDllPath));
+ mvcBuilder.PartManager.ApplicationParts.Add(viewAssembly);
+ });
+ }
+ }
+}
+````
+
+* 由于我们添加了相关的NuGet包,因此取决于`AbpAspNetCoreMvcUiThemeSharedModule`.
+* 添加插件程序集到ASP.NET Core MVC的`PartManager`中.这是ASP.NET Core所必需的.否则,你插件中的控制器将无法正常工作.
+* 添加插件的视图程序集到ASP.NET Core MVC的`PartManager`中.这是ASP.NET Core所必需的.否则,你在插件中的视图将不起作用.
+
+现在,你可以在`Pages`目录下添加一个razor页面,例如`MyPlugInPage.cshtml`:
+
+````html
+@page
+@model MyMvcUIPlugIn.Pages.MyPlugInPage
+Welcome to my plug-in page
+This page is located inside a plug-in module! :)
+````
+
+现在,你可以构建插件项目.它将产生以下输出:
+
+
+
+将`MyMvcUIPlugIn.dll`和`MyMvcUIPlugIn.Views.dll`复制到到插件目录下(此示例中为`D:\Temp\MyPlugIns`).
+
+如果你已经按照上述方式配置了主应用程序(参见“基础用法”部分),那么在应用程序启动的时候,你应该能够访问`/MyPlugInPage`URL:
+
+
+
+## 讨论
+
+在现实世界中,你的插件可能具有一些外部依赖性.另外,你的应用程序可能被设计为支持插件.所有这些都是你自己的系统要求.ABP做的仅仅是在应用程序启动时加载模块.你在这些模块中执行什么操作由你决定.
+
+但是,我们可以为一些常见情况提供一些建议.
+
+### 库依赖
+
+对于包/dll依赖,你可以将相关的dll复制到插件目录下.ABP会自动将所有程序集加载到该目录下,并且你的插件将按预期工作.
+
+> 请参见[Microsoft文档](https://docs.microsoft.com/zh-cn/dotnet/core/tutorials/creating-app-with-plugin-support#plugin-with-library-dependencies).
+
+### 数据库模式
+
+如果你的模块使用关系型数据库和[Entity Framework Core](Entity-Framework-Core.md), 那么它需要在数据库中提供表.有多种不同的方法可确保在应用程序使用插件时创建表.一些例子;
+
+1. 插件可以检查数据库表是否存在,并在应用程序启动时创建表,或者如果插件已更新且需要进行某些架构更改时,则会迁移它们.你可以使用EF Core的迁移API来做到这一点.
+2. 你可以改进`DbMigrator`应用程序,用于查找插件的迁移并执行它们.
+
+可能还有其它解决方案.例如,如果你的数据库管理员不允许你在应用程序代码中更改数据库模式,则可能需要手动将SQL文件发送给数据库管理员,以将其应用于数据库.
diff --git a/docs/zh-Hans/Redis-Cache.md b/docs/zh-Hans/Redis-Cache.md
new file mode 100644
index 0000000000..cb82a640d5
--- /dev/null
+++ b/docs/zh-Hans/Redis-Cache.md
@@ -0,0 +1 @@
+TODO...
\ No newline at end of file
diff --git a/docs/zh-Hans/docs-nav.json b/docs/zh-Hans/docs-nav.json
index 5d231099c9..4f47dd2760 100644
--- a/docs/zh-Hans/docs-nav.json
+++ b/docs/zh-Hans/docs-nav.json
@@ -313,7 +313,8 @@
"path": "Module-Development-Basics.md"
},
{
- "text": "模块插件"
+ "text": "模块插件",
+ "path": "PlugIn-Modules.md"
},
{
"text": "自定义应用模块",
diff --git a/docs/zh-Hans/images/simple-plug-in-dll-file.png b/docs/zh-Hans/images/simple-plug-in-dll-file.png
new file mode 100644
index 0000000000..3155708d68
Binary files /dev/null and b/docs/zh-Hans/images/simple-plug-in-dll-file.png differ
diff --git a/docs/zh-Hans/images/simple-plugin-library.png b/docs/zh-Hans/images/simple-plugin-library.png
new file mode 100644
index 0000000000..9fefd57dda
Binary files /dev/null and b/docs/zh-Hans/images/simple-plugin-library.png differ
diff --git a/docs/zh-Hans/images/simple-plugin-output.png b/docs/zh-Hans/images/simple-plugin-output.png
new file mode 100644
index 0000000000..71f6a78c0e
Binary files /dev/null and b/docs/zh-Hans/images/simple-plugin-output.png differ
diff --git a/docs/zh-Hans/images/simple-razor-plug-in-dll-file.png b/docs/zh-Hans/images/simple-razor-plug-in-dll-file.png
new file mode 100644
index 0000000000..06b7a565fe
Binary files /dev/null and b/docs/zh-Hans/images/simple-razor-plug-in-dll-file.png differ
diff --git a/docs/zh-Hans/images/simple-razor-plugin.png b/docs/zh-Hans/images/simple-razor-plugin.png
new file mode 100644
index 0000000000..92e0e00d29
Binary files /dev/null and b/docs/zh-Hans/images/simple-razor-plugin.png differ
diff --git a/framework/Volo.Abp.sln b/framework/Volo.Abp.sln
index 963472ada8..8c2670fa27 100644
--- a/framework/Volo.Abp.sln
+++ b/framework/Volo.Abp.sln
@@ -359,6 +359,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Json.Tests", "test
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Http.Tests", "test\Volo.Abp.Http.Tests\Volo.Abp.Http.Tests.csproj", "{A37BFEB5-7C57-4CDC-93B8-B5CE4BB9ACE1}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.EventBus.Abstractions", "src\Volo.Abp.EventBus.Abstractions\Volo.Abp.EventBus.Abstractions.csproj", "{8FDB3BF7-AD89-43F6-8DEB-C3E29B8801FE}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -1069,6 +1071,10 @@ Global
{A37BFEB5-7C57-4CDC-93B8-B5CE4BB9ACE1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A37BFEB5-7C57-4CDC-93B8-B5CE4BB9ACE1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A37BFEB5-7C57-4CDC-93B8-B5CE4BB9ACE1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8FDB3BF7-AD89-43F6-8DEB-C3E29B8801FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8FDB3BF7-AD89-43F6-8DEB-C3E29B8801FE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8FDB3BF7-AD89-43F6-8DEB-C3E29B8801FE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8FDB3BF7-AD89-43F6-8DEB-C3E29B8801FE}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -1250,6 +1256,7 @@ Global
{DD9519E0-5A68-48DC-A051-7BF2AC922F3E} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{00D07595-993C-40FC-BD90-0DD6331414D3} = {447C8A77-E5F0-4538-8687-7383196D04EA}
{A37BFEB5-7C57-4CDC-93B8-B5CE4BB9ACE1} = {447C8A77-E5F0-4538-8687-7383196D04EA}
+ {8FDB3BF7-AD89-43F6-8DEB-C3E29B8801FE} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5}
diff --git a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme/Themes/Basic/SecondLevelNavMenuItem.razor b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme/Themes/Basic/SecondLevelNavMenuItem.razor
index f9e47c4c27..d6067021ab 100644
--- a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme/Themes/Basic/SecondLevelNavMenuItem.razor
+++ b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme/Themes/Basic/SecondLevelNavMenuItem.razor
@@ -10,14 +10,12 @@
if (MenuItem.Url != null)
{
- @if (MenuItem.Icon != null)
- {
- if (MenuItem.Icon.StartsWith("fa"))
- {
-
- }
- }
- @MenuItem.DisplayName
+
+
+
+
+ @MenuItem.DisplayName
+
}
}
diff --git a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/MultiTenancyMiddleware.cs b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/MultiTenancyMiddleware.cs
index 0aeb97e89d..eaf5c753fd 100644
--- a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/MultiTenancyMiddleware.cs
+++ b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/MultiTenancyMiddleware.cs
@@ -1,7 +1,14 @@
-using System.Threading.Tasks;
+using System;
+using System.Globalization;
+using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Localization;
+using Microsoft.AspNetCore.RequestLocalization;
+using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.DependencyInjection;
+using Volo.Abp.Localization;
using Volo.Abp.MultiTenancy;
+using Volo.Abp.Settings;
namespace Volo.Abp.AspNetCore.MultiTenancy
{
@@ -21,10 +28,71 @@ namespace Volo.Abp.AspNetCore.MultiTenancy
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
var tenant = await _tenantConfigurationProvider.GetAsync(saveResolveResult: true);
- using (_currentTenant.Change(tenant?.Id, tenant?.Name))
+ if (tenant?.Id != _currentTenant.Id)
+ {
+ using (_currentTenant.Change(tenant?.Id, tenant?.Name))
+ {
+ var requestCulture = await TryGetRequestCultureAsync(context);
+ if (requestCulture != null)
+ {
+ CultureInfo.CurrentCulture = requestCulture.Culture;
+ CultureInfo.CurrentUICulture = requestCulture.UICulture;
+ AbpRequestCultureCookieHelper.SetCultureCookie(
+ context,
+ requestCulture
+ );
+ }
+
+ await next(context);
+ }
+ }
+ else
{
await next(context);
}
}
+
+ private async Task TryGetRequestCultureAsync(HttpContext httpContext)
+ {
+ var requestCultureFeature = httpContext.Features.Get();
+
+ /* If requestCultureFeature == null, that means the RequestLocalizationMiddleware was not used
+ * and we don't want to set the culture. */
+ if (requestCultureFeature == null)
+ {
+ return null;
+ }
+
+ /* If requestCultureFeature.Provider is not null, that means RequestLocalizationMiddleware
+ * already picked a language, so we don't need to set the default. */
+ if (requestCultureFeature.Provider != null)
+ {
+ return null;
+ }
+
+ var settingProvider = httpContext.RequestServices.GetRequiredService();
+ var defaultLanguage = await settingProvider.GetOrNullAsync(LocalizationSettingNames.DefaultLanguage);
+ if (defaultLanguage.IsNullOrWhiteSpace())
+ {
+ return null;
+ }
+
+ string culture;
+ string uiCulture;
+
+ if (defaultLanguage.Contains(';'))
+ {
+ var splitted = defaultLanguage.Split(';');
+ culture = splitted[0];
+ uiCulture = splitted[1];
+ }
+ else
+ {
+ culture = defaultLanguage;
+ uiCulture = defaultLanguage;
+ }
+
+ return new RequestCulture(culture, uiCulture);
+ }
}
}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcCachedApplicationConfigurationClientHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcCachedApplicationConfigurationClientHelper.cs
index 4b4a0e6301..ae013c4343 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcCachedApplicationConfigurationClientHelper.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcCachedApplicationConfigurationClientHelper.cs
@@ -7,7 +7,8 @@ namespace Volo.Abp.AspNetCore.Mvc.Client
{
public static string CreateCacheKey(ICurrentUser currentUser)
{
- return $"ApplicationConfiguration_{currentUser.Id?.ToString("N") ?? "Anonymous"}_{CultureInfo.CurrentUICulture.Name}";
+ var userKey = currentUser.Id?.ToString("N") ?? "Anonymous";
+ return $"ApplicationConfiguration_{userKey}_{CultureInfo.CurrentUICulture.Name}";
}
}
}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/dom-event-handlers.js b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/dom-event-handlers.js
index 431d9093f7..55c604cf4a 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/dom-event-handlers.js
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/dom-event-handlers.js
@@ -159,7 +159,7 @@
abp.dom.initializers.initializeDatepickers = function ($rootElement) {
$rootElement
- .findWithSelf('input.datepicker,input[type=date]')
+ .findWithSelf('input.datepicker,input[type=date][abp-data-datepicker!=false]')
.each(function () {
var $input = $(this);
$input
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/modal-manager.js b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/modal-manager.js
index a4d32550f8..75d080feda 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/modal-manager.js
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/modal-manager.js
@@ -39,7 +39,7 @@ $.validator.defaults.ignore = ''; //TODO: Would be better if we can apply only f
var _$modal = null;
var _$form = null;
- var _modalId = 'Modal_' + (Math.floor((Math.random() * 1000000))) + new Date().getTime();
+ var _modalId = 'Abp_Modal_' + (Math.floor((Math.random() * 1000000))) + new Date().getTime();
var _modalObject = null;
var _publicApi = null;
@@ -56,7 +56,12 @@ $.validator.defaults.ignore = ''; //TODO: Would be better if we can apply only f
function _createContainer() {
_removeContainer();
_$modalContainer = $('');
- $('body').prepend(_$modalContainer);
+ var existsModals = $('[id^="Abp_Modal_"]');
+ if (existsModals.length) {
+ existsModals.last().after(_$modalContainer)
+ } else {
+ $('body').prepend(_$modalContainer);
+ }
return _$modalContainer;
}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-extensions.js b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-extensions.js
index b0c4873b59..9895097d99 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-extensions.js
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-extensions.js
@@ -30,7 +30,7 @@
var htmlEncode = function (html) {
return $('').text(html).html();
}
-
+
var _createDropdownItem = function (record, fieldItem, tableInstance) {
var $li = $('');
var $a = $('');
@@ -90,15 +90,15 @@
}
$button.append(htmlEncode(firstItem.text));
}
-
+
if (firstItem.enabled && !firstItem.enabled({ record: record, table: tableInstance })) {
$button.addClass('disabled');
}
-
+
if (firstItem.action) {
$button.click(function (e) {
e.preventDefault();
-
+
if (!$(this).hasClass('disabled')) {
if (firstItem.confirmMessage) {
abp.message.confirm(firstItem.confirmMessage({ record: record, table: tableInstance }))
@@ -217,7 +217,7 @@
var renderRowActions = function (tableInstance, nRow, aData, iDisplayIndex, iDisplayIndexFull) {
var columns;
-
+
if (tableInstance.aoColumns) {
columns = tableInstance.aoColumns;
} else {
@@ -334,7 +334,10 @@
};
}
return function (requestData, callback, settings) {
- var input = inputAction ? inputAction(requestData, settings) : {};
+ var input = typeof inputAction === 'function'
+ ? inputAction(requestData, settings)
+ : typeof inputAction === 'object'
+ ? inputAction : {};
//Paging
if (settings.oInit.paging) {
@@ -463,7 +466,7 @@
datatables.defaultConfigurations.scrollX = true;
- datatables.defaultConfigurations.responsive = true;
+ datatables.defaultConfigurations.responsive = true;
datatables.defaultConfigurations.language = function () {
return {
@@ -484,6 +487,6 @@
};
};
- datatables.defaultConfigurations.dom = '<"dataTable_filters"f>rt<"row dataTable_footer"<"col-auto"l><"col-auto"i><"col"p>>';
+ datatables.defaultConfigurations.dom = '<"dataTable_filters"f>rt<"row dataTable_footer"<"col-auto"l><"col-auto mr-auto"i><"col-auto"p>>';
})(jQuery);
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo.Abp.AspNetCore.Mvc.UI.csproj b/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo.Abp.AspNetCore.Mvc.UI.csproj
index ad0066db55..a379c8057d 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo.Abp.AspNetCore.Mvc.UI.csproj
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo.Abp.AspNetCore.Mvc.UI.csproj
@@ -15,7 +15,7 @@