diff --git a/.gitignore b/.gitignore
index 8701ed51acb..4bea19766f4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -302,3 +302,6 @@ samples/Microservice/microservices/OrganizationService.Host/Logs/logs.txt
abp_io/src/Volo.AbpWebSite.Web/TemplateFiles/abp-mvc-app-0.17.0.0.zip
abp_io/src/Volo.AbpWebSite.Web/TemplateFiles/abp-mvc-app-0.17.0.0-filtered.zip
abp_io/src/Volo.AbpWebSite.Web/Logs/logs.txt
+templates/mvc/src/MyCompanyName.MyProjectName.Web.Host/Logs/logs.txt
+templates/mvc/src/MyCompanyName.MyProjectName.IdentityServer/Logs/logs.txt
+templates/mvc/src/MyCompanyName.MyProjectName.HttpApi.Host/Logs/logs.txt
diff --git a/README.md b/README.md
index 3fd46d1e71a..1f0c7c99d1e 100644
--- a/README.md
+++ b/README.md
@@ -58,7 +58,7 @@ This project is in **preview** stage and it's not suggested to use it in product
### Documentation
-See the documentation.
+See the documentation.
### Development
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json
index b44c8833bdd..7224dab07e7 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json
@@ -153,6 +153,7 @@
"Code": "Code",
"Result": "Result",
"SeeTheDocumentForMoreInformation": "See the {0} document for more information",
- "IndexPageHeroSection": "open sourceWeb Application Framework for asp.net core"
+ "IndexPageHeroSection": "open sourceWeb Application Framework for asp.net core",
+ "UiFramework": "UI Framework"
}
}
\ No newline at end of file
diff --git a/build-all.ps1 b/build-all.ps1
index be40f0f0fbd..23c7bd57a09 100644
--- a/build-all.ps1
+++ b/build-all.ps1
@@ -19,8 +19,8 @@ $solutionPaths = (
"modules/audit-logging",
"modules/background-jobs",
"modules/client-simulation",
- "templates/mvc-module",
- "templates/mvc",
+ "templates/module/aspnet-core",
+ "templates/app/aspnet-core",
"samples/MicroserviceDemo",
"samples/Microservice",
"abp_io/AbpIoLocalization"
diff --git a/docs/cs/AspNetCore/Widgets.md b/docs/cs/AspNetCore/Widgets.md
index 9dfcddfa6d9..c885da9c624 100644
--- a/docs/cs/AspNetCore/Widgets.md
+++ b/docs/cs/AspNetCore/Widgets.md
@@ -2,16 +2,16 @@
ABP poskytuje model a infastrukturu k vytváření **znovu použitelných widgetů**. Systém widgetů je rozšíření pro [ASP.NET Core pohledové komponenty](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/view-components). Widgety jsou zvláště užitečné, když chcete;
-* Definovat widgety v znovu použitelných **[modulech](../Module-Development-Basics.md)**.
* Mít závislosti na **skriptech & stylech** ve vašem widgetu.
-* Vytvářet **[řídící panely](Dashboards.md)** za použítí widgetů.
+* Vytvářet **řídící panely** za použítí widgetů.
+* Definovat widgety v znovu použitelných **[modulech](../Module-Development-Basics.md)**.
* Spolupráci widgetů s **[authorizačními](../Authorization.md)** a **[svazovacími](Bundling-Minification.md)** systémy.
## Základní definice widgetu
### Tvorba pohledové komponenty
-Jako první krok, vytvoříme běžnou ASP.NET Core pohledovou komponentu:
+Jako první krok, vytvořte běžnou ASP.NET Core pohledovou komponentu:

@@ -33,7 +33,9 @@ namespace DashboardDemo.Web.Pages.Components.MySimpleWidget
}
````
-Dědění z `AbpViewComponent` není vyžadováno. Můžeme dědit ze standardního ASP.NET Core `ViewComponent`. `AbpViewComponent` pouze definuje pár základních a užitečných vlastnosti.
+Dědění z `AbpViewComponent` není vyžadováno. Můžete dědit ze standardního ASP.NET Core `ViewComponent`. `AbpViewComponent` pouze definuje pár základních a užitečných vlastnosti.
+
+Můžete vložit službu a pomocí metody `Invoke` z ní získat některá data. Možná budete muset provést metodu Invoke jako asynchronní `public async Task InvokeAsync()`. Podívejte se na dokument [ASP.NET Core ViewComponents](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/view-components) pro všechna další použítí.
**Default.cshtml**:
@@ -46,7 +48,7 @@ Dědění z `AbpViewComponent` není vyžadováno. Můžeme dědit ze standardn
### Definice widgetu
-Přidáme atribut `Widget` k třídě `MySimpleWidgetViewComponent` pro označení této pohledové komponenty jako widgetu:
+Přidejte atribut `Widget` k třídě `MySimpleWidgetViewComponent` pro označení této pohledové komponenty jako widgetu:
````csharp
using Microsoft.AspNetCore.Mvc;
@@ -68,7 +70,7 @@ namespace DashboardDemo.Web.Pages.Components.MySimpleWidget
## Vykreslení widgetu
-Vykreslení widgetu je vcelku standardní. Použijeme metodu `Component.InvokeAsync` v razor pohledu/stránce jako s kteroukoliv jinou pohledovou komponentou. Příklady:
+Vykreslení widgetu je vcelku standardní. Použijte metodu `Component.InvokeAsync` v razor pohledu/stránce jako s kteroukoliv jinou pohledovou komponentou. Příklady:
````xml
@await Component.InvokeAsync("MySimpleWidget")
@@ -77,11 +79,61 @@ Vykreslení widgetu je vcelku standardní. Použijeme metodu `Component.InvokeAs
První přístup používá název widgetu, zatímco druhý používá typ pohledové komponenty.
+### Widgety s argumenty
+
+Systém ASP.NET Core pohledových komponent umožňuje přijímat argumenty pro pohledové komponenty. Níže uvedená pohledová komponenta přijímá `startDate` a `endDate` a používá tyto argumenty k získání dat ze služby.
+
+````csharp
+using System;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Mvc;
+using Volo.Abp.AspNetCore.Mvc;
+using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
+
+namespace DashboardDemo.Web.Pages.Shared.Components.CountersWidget
+{
+ [Widget]
+ public class CountersWidgetViewComponent : AbpViewComponent
+ {
+ private readonly IDashboardAppService _dashboardAppService;
+
+ public CountersWidgetViewComponent(IDashboardAppService dashboardAppService)
+ {
+ _dashboardAppService = dashboardAppService;
+ }
+
+ public async Task InvokeAsync(
+ DateTime startDate, DateTime endDate)
+ {
+ var result = await _dashboardAppService.GetCountersWidgetAsync(
+ new CountersWidgetInputDto
+ {
+ StartDate = startDate,
+ EndDate = endDate
+ }
+ );
+
+ return View(result);
+ }
+ }
+}
+````
+
+Nyní musíte předat anonymní objekt k předání argumentů tak jak je ukázáno níže:
+
+````xml
+@await Component.InvokeAsync("CountersWidget", new
+{
+ startDate = DateTime.Now.Subtract(TimeSpan.FromDays(7)),
+ endDate = DateTime.Now
+})
+````
+
## Název widgetu
Výchozí název pohledových komponent je vypočítán na základě názvu typu pohledové komponenty. Pokud je typ pohledové komponenty `MySimpleWidgetViewComponent` potom název widgetu bude `MySimpleWidget` (odstraní se `ViewComponent` postfix). Takto ASP.NET Core vypočítává název pohledové komponenty.
-Chceme-li přizpůsobit název widgetu, stačí použít standardní atribut `ViewComponent` z ASP.NET Core:
+Chcete-li přizpůsobit název widgetu, stačí použít standardní atribut `ViewComponent` z ASP.NET Core:
```csharp
using Microsoft.AspNetCore.Mvc;
@@ -108,7 +160,7 @@ ABP bude respektovat přizpůsobený název při zpracování widgetu.
### Zobrazovaný název
-Můžeme také definovat čitelný & lokalizovatelný zobrazovaný název pro widget. Tento zobrazovaný název může být využít na uživatelském rozhraní kdykoliv je to potřeba. Zobrazovaný název je nepovinný a lze ho definovat pomocí vlastností atributu `Widget`:
+Můžete také definovat čitelný & lokalizovatelný zobrazovaný název pro widget. Tento zobrazovaný název může být využít na uživatelském rozhraní kdykoliv je to potřeba. Zobrazovaný název je nepovinný a lze ho definovat pomocí vlastností atributu `Widget`:
````csharp
using DashboardDemo.Localization;
@@ -222,6 +274,181 @@ Systém přispěvatelů balíků je velmi schopný. Pokud váš widget použív
Podívejte se na dokumentaci [svazování & minifikace](Bundling-Minification.md) pro více informací o tomto systému.
+## RefreshUrl
+
+Widget může navrhnout `RefreshUrl`, který se používá vždy, když je potřeba widget aktualizovat. Je-li definován, widget se při každé aktualizaci znovu vykreslí na straně serveru (viz refresh `methoda` u `WidgetManager` níže).
+
+````csharp
+[Widget(RefreshUrl = "Widgets/Counters")]
+public class CountersWidgetViewComponent : AbpViewComponent
+{
+
+}
+````
+
+Jakmile pro svůj widget definujete `RefreshUrl`, musíte poskytnout koncový bod pro jeho vykreslení a vrátit ho:
+
+````csharp
+[Route("Widgets")]
+public class CountersWidgetController : AbpController
+{
+ [HttpGet]
+ [Route("Counters")]
+ public IActionResult Counters(DateTime startDate, DateTime endDate)
+ {
+ return ViewComponent("CountersWidget", new {startDate, endDate});
+ }
+}
+````
+
+Trasa `Widgets/Counters` předchozímu `RefreshUrl`.
+
+> Widget lze obnovit dvěma způsoby: Prvním způsobem je použití `RefreshUrl`, kdy se znovu vykreslí na serveru a nahradí HTML vrácené tím ze serveru. Druhým způsobem widget získá data (obvykle JSON objekt) ze serveru a obnoví se sám u klienta (viz refresh metoda v sekci Widget JavaScript API).
+
+## JavaScript API
+
+Možná bude potřeba vykreslit a obnovit widget na straně klienta. V takových případech můžete použít ABP `WidgetManager` a definovat API pro vaše widgety.
+
+### WidgetManager
+
+`WidgetManager` se používá k inicializaci a aktualizaci jednoho nebo více widgetů. Vytvořte nový `WidgetManager` jako je ukázáno níže:
+
+````js
+$(function() {
+ var myWidgetManager = new abp.WidgetManager('#MyDashboardWidgetsArea');
+})
+````
+
+`MyDashboardWidgetsArea` může obsahovat jeden nebo více widgetů.
+
+> Použíti `WidgetManager` uvnitř document.ready (jako nahoře) je dobrá praktika jelikož jeho funkce používají DOM a potřebují, aby byl DOM připraven.
+
+#### WidgetManager.init()
+
+`init` jednoduše inicializuje `WidgetManager` a volá metody `init` v souvisejících widgetech pokud je obsahují (podívejte se na sekci Widget JavaScript API section níže)
+
+```js
+myWidgetManager.init();
+```
+
+#### WidgetManager.refresh()
+
+`refresh` metoda obnoví všechny widgety související s tímto `WidgetManager`:
+
+````
+myWidgetManager.refresh();
+````
+
+#### WidgetManager možnosti
+
+WidgetManager má několik dalších možností.
+
+##### Filtrační formulář
+
+Pokud vaše widgety vyžadují parametry/filtry pak budete obvykle mít formulář pro filtrování widgetů. V takových případech můžete vytvořit formulář, který obsahuje prvky formuláře a oblast řídicího panelu s nějakými widgety uvnitř. Příklad:
+
+````xml
+
+
+
+ ...widgety
+
+````
+
+`data-widget-filter` atribut propojuje formulář s widgety. Kdykoli je formulář odeslán, všechny widgety jsou automaticky aktualizovány pomocí polí formuláře jako filtru.
+
+Místo atributu `data-widget-filter`, můžete použít parametr `filterForm` v konstruktoru `WidgetManager`. Příklad:
+
+````js
+var myWidgetManager = new abp.WidgetManager({
+ wrapper: '#MyDashboardWidgetsArea',
+ filterForm: '#MyDashboardFilterForm'
+});
+````
+
+##### Zpětné volání filtru
+
+Možná budete chtít mít lepší kontrolu nad poskytováním filtrů při inicializaci a aktualizaci widgetů. V tomto případě můžete použít volbu `filterCallback`:
+
+````js
+var myWidgetManager = new abp.WidgetManager({
+ wrapper: '#MyDashboardWidgetsArea',
+ filterCallback: function() {
+ return $('#MyDashboardFilterForm').serializeFormToObject();
+ }
+});
+````
+
+Tento příklad ukazuje výchozí implementaci `filterCallback`. Pomocí polí můžete vrátit jakýkoli JavaScript objekt. Příklad:
+
+````js
+filterCallback: function() {
+ return {
+ 'startDate': $('#StartDateInput').val(),
+ 'endDate': $('#EndDateInput').val()
+ };
+}
+````
+
+Vrácené filtry jsou předávány všem widgetům na `init` a` refresh`.
+
+### Widget JavaScript API
+
+Widget může definovat rozhraní API jazyka JavaScript, které je v případě potřeby vyvoláno přes `WidgetManager`. Ukázku kódu níže lze použít k definování API pro widget.
+
+````js
+(function () {
+ abp.widgets.NewUserStatisticWidget = function ($wrapper) {
+
+ var getFilters = function () {
+ return {
+ ...
+ };
+ }
+
+ var refresh = function (filters) {
+ ...
+ };
+
+ var init = function (filters) {
+ ...
+ };
+
+ return {
+ getFilters: getFilters,
+ init: init,
+ refresh: refresh
+ };
+ };
+})();
+````
+
+`NewUserStatisticWidget` je tady název widgetu. Měl by odpovídat názvu widgetu definovanému na straně serveru. Všechny funkce jsou volitelné.
+
+#### getFilters
+
+Pokud má widget vlastní interní filtry, měla by tato funkce vrátit objekt filtru. Příklad:
+
+````js
+var getFilters = function() {
+ return {
+ frequency: $wrapper.find('.frequency-filter option:selected').val()
+ };
+}
+````
+
+Tuto metodu používá `WidgetManager` při vytváření filtrů.
+
+#### init
+
+Slouží k inicializaci widgetu kdykoli je potřeba. Má argument filtru, který lze použít při získávání dat ze serveru. Metoda `init` je použita když je volána funkce `WidgetManager.init()`. Použita je i v případě že váš widget vyžaduje úplné obnovení při aktualizaci. Viz `RefreshUrl` v možnostech widgetu.
+
+#### refresh
+
+Slouží k aktualizaci widgetu kdykoli je potřeba. Má argument filtru, který lze použít při získávání dat ze serveru. Metoda `refresh` se používá kdykoliv je volána funkce `WidgetManager.refresh()`.
+
## Autorizace
Některé widgety budou pravděpodobně muset být dostupné pouze pro ověřené nebo autorizované uživatele. V tomto případě použijte následující vlastnosti atributu `Widget`:
@@ -249,7 +476,7 @@ namespace DashboardDemo.Web.Pages.Components.MySimpleWidget
}
````
-## Možnosti widgetu
+## WidgetOptions
Jako alternativu k atributu `Widget` můžete ke konfiguraci widgetů použít `WidgetOptions`:
@@ -271,4 +498,8 @@ Configure(options =>
});
````
-> Tip: `WidgetOptions` lze také použít k získání existujícího widgetu a ke změně jeho konfigurace. To je obzvláště užitečné, pokud chcete změnit konfiguraci widgetu uvnitř modulu používaného vaší aplikací. Použíjte `options.Widgets.Find` k získání existujícího `WidgetDefinition`.
\ No newline at end of file
+> Tip: `WidgetOptions` lze také použít k získání existujícího widgetu a ke změně jeho konfigurace. To je obzvláště užitečné, pokud chcete změnit konfiguraci widgetu uvnitř modulu používaného vaší aplikací. Použíjte `options.Widgets.Find` k získání existujícího `WidgetDefinition`.
+
+## Podívejte se také na
+
+* [Příklad projektu (zdrojový kód)](https://github.com/abpframework/abp/tree/dev/samples/DashboardDemo).
\ No newline at end of file
diff --git a/docs/cs/images/widget-basic-files.png b/docs/cs/images/widget-basic-files.png
new file mode 100644
index 00000000000..bf5f122d42a
Binary files /dev/null and b/docs/cs/images/widget-basic-files.png differ
diff --git a/docs/en/AspNetCore/Dashboards.md b/docs/en/AspNetCore/Dashboards.md
deleted file mode 100644
index 26b6a774619..00000000000
--- a/docs/en/AspNetCore/Dashboards.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# Dashboards
-
-TODO
\ No newline at end of file
diff --git a/docs/en/AspNetCore/Widgets.md b/docs/en/AspNetCore/Widgets.md
index 00f0433e2e0..f217e248162 100644
--- a/docs/en/AspNetCore/Widgets.md
+++ b/docs/en/AspNetCore/Widgets.md
@@ -2,9 +2,9 @@
ABP provides a model and infrastructure to create **reusable widgets**. Widget system is an extension to [ASP.NET Core's ViewComponents](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/view-components). Widgets are especially useful when you want to;
-* Define widgets in reusable **[modules](../Module-Development-Basics.md)**.
* Have **scripts & styles** dependencies for your widget.
-* Create **[dashboards](Dashboards.md)** with widgets used inside.
+* Create **dashboards** with widgets used inside.
+* Define widgets in reusable **[modules](../Module-Development-Basics.md)**.
* Co-operate widgets with **[authorization](../Authorization.md)** and **[bundling](Bundling-Minification.md)** systems.
## Basic Widget Definition
@@ -35,6 +35,8 @@ namespace DashboardDemo.Web.Pages.Components.MySimpleWidget
Inheriting from `AbpViewComponent` is not required. You could inherit from ASP.NET Core's standard `ViewComponent`. `AbpViewComponent` only defines some base useful properties.
+You can inject a service and use in the `Invoke` method to get some data from the service. You may need to make Invoke method async, like `public async Task InvokeAsync()`. See [ASP.NET Core's ViewComponents](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/view-components) document fore all different usages.
+
**Default.cshtml**:
```xml
@@ -77,6 +79,56 @@ Rendering a widget is pretty standard. Use the `Component.InvokeAsync` method in
First approach uses the widget name while second approach uses the view component type.
+### Widgets with Arguments
+
+ASP.NET Core's view component system allows you to accept arguments for view components. The sample view component below accepts `startDate` and `endDate` and uses these arguments to retrieve data from a service.
+
+````csharp
+using System;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Mvc;
+using Volo.Abp.AspNetCore.Mvc;
+using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
+
+namespace DashboardDemo.Web.Pages.Shared.Components.CountersWidget
+{
+ [Widget]
+ public class CountersWidgetViewComponent : AbpViewComponent
+ {
+ private readonly IDashboardAppService _dashboardAppService;
+
+ public CountersWidgetViewComponent(IDashboardAppService dashboardAppService)
+ {
+ _dashboardAppService = dashboardAppService;
+ }
+
+ public async Task InvokeAsync(
+ DateTime startDate, DateTime endDate)
+ {
+ var result = await _dashboardAppService.GetCountersWidgetAsync(
+ new CountersWidgetInputDto
+ {
+ StartDate = startDate,
+ EndDate = endDate
+ }
+ );
+
+ return View(result);
+ }
+ }
+}
+````
+
+Now, you need to pass an anonymous object to pass arguments as shown below:
+
+````xml
+@await Component.InvokeAsync("CountersWidget", new
+{
+ startDate = DateTime.Now.Subtract(TimeSpan.FromDays(7)),
+ endDate = DateTime.Now
+})
+````
+
## Widget Name
Default name of the view components are calculated based on the name of the view component type. If your view component type is `MySimpleWidgetViewComponent` then the widget name will be `MySimpleWidget` (removes `ViewComponent` postfix). This is how ASP.NET Core calculates a view component's name.
@@ -222,6 +274,181 @@ Bundle contribution system is very powerful. If your widget uses a JavaScript li
See the [bundling & minification](Bundling-Minification.md) documentation for more information about that system.
+## RefreshUrl
+
+A widget may design a `RefreshUrl` that is used whenever the widget needs to be refreshed. If it is defined, the widget is re-rendered on the server side on every refresh (see the refresh `method` of the `WidgetManager` below).
+
+````csharp
+[Widget(RefreshUrl = "Widgets/Counters")]
+public class CountersWidgetViewComponent : AbpViewComponent
+{
+
+}
+````
+
+Once you define a `RefreshUrl` for your widget, you need to provide an endpoint to render and return it:
+
+````csharp
+[Route("Widgets")]
+public class CountersWidgetController : AbpController
+{
+ [HttpGet]
+ [Route("Counters")]
+ public IActionResult Counters(DateTime startDate, DateTime endDate)
+ {
+ return ViewComponent("CountersWidget", new {startDate, endDate});
+ }
+}
+````
+
+`Widgets/Counters` route matches to the `RefreshUrl` declared before.
+
+> A widget supposed to be refreshed in two ways: In the first way, when you use a `RefreshUrl`, it re-rendered on the server and replaced by the HTML returned from server. In the second way the widget gets data (generally a JSON object) from server and refreshes itself in the client side (see the refresh method in the Widget JavaScript API section).
+
+## JavaScript API
+
+A widget may need to be rendered and refreshed in the client side. In such cases, you can use ABP's `WidgetManager` and define APIs for your widgets.
+
+### WidgetManager
+
+`WidgetManager` is used to initialize and refresh one or more widgets. Create a new `WidgetManager` as shown below:
+
+````js
+$(function() {
+ var myWidgetManager = new abp.WidgetManager('#MyDashboardWidgetsArea');
+})
+````
+
+`MyDashboardWidgetsArea` may contain one or more widgets inside.
+
+> Using the `WidgetManager` inside document.ready (like above) is a good practice since its functions use the DOM and need DOM to be ready.
+
+#### WidgetManager.init()
+
+`init` simply initializes the `WidgetManager` and calls `init` methods of the related widgets if they define (see Widget JavaScript API section below)
+
+```js
+myWidgetManager.init();
+```
+
+#### WidgetManager.refresh()
+
+`refresh` method refreshes all widgets related to this `WidgetManager`:
+
+````
+myWidgetManager.refresh();
+````
+
+#### WidgetManager Options
+
+WidgetManager has some additional options.
+
+##### Filter Form
+
+If your widgets require parameters/filters then you will generally have a form to filter the widgets. In such cases, you can create a form that has some form elements and a dashboard area with some widgets inside. Example:
+
+````xml
+
+
+
+ ...widgets
+
+````
+
+`data-widget-filter` attribute relates the form with the widgets. Whenever the form is submitted, all the widgets are automatically refreshed with the form fields as the filter.
+
+Instead of the `data-widget-filter` attribute, you can use the `filterForm` parameter of the `WidgetManager` constructor. Example:
+
+````js
+var myWidgetManager = new abp.WidgetManager({
+ wrapper: '#MyDashboardWidgetsArea',
+ filterForm: '#MyDashboardFilterForm'
+});
+````
+
+##### Filter Callback
+
+You may want to have a better control to provide filters while initializing and refreshing the widgets. In this case, you can use the `filterCallback` option:
+
+````js
+var myWidgetManager = new abp.WidgetManager({
+ wrapper: '#MyDashboardWidgetsArea',
+ filterCallback: function() {
+ return $('#MyDashboardFilterForm').serializeFormToObject();
+ }
+});
+````
+
+This example shows the default implementation of the `filterCallback`. You can return any JavaScript object with fields. Example:
+
+````js
+filterCallback: function() {
+ return {
+ 'startDate': $('#StartDateInput').val(),
+ 'endDate': $('#EndDateInput').val()
+ };
+}
+````
+
+The returning filters are passed to all widgets on `init` and `refresh`.
+
+### Widget JavaScript API
+
+A widget can define a JavaScript API that is invoked by the `WidgetManager` when needed. The code sample below can be used to start to define an API for a widget.
+
+````js
+(function () {
+ abp.widgets.NewUserStatisticWidget = function ($wrapper) {
+
+ var getFilters = function () {
+ return {
+ ...
+ };
+ }
+
+ var refresh = function (filters) {
+ ...
+ };
+
+ var init = function (filters) {
+ ...
+ };
+
+ return {
+ getFilters: getFilters,
+ init: init,
+ refresh: refresh
+ };
+ };
+})();
+````
+
+`NewUserStatisticWidget` is the name of the widget here. It should match the widget name defined in the server side. All of the functions are optional.
+
+#### getFilters
+
+If the widget has internal custom filters, this function should return the filter object. Example:
+
+````js
+var getFilters = function() {
+ return {
+ frequency: $wrapper.find('.frequency-filter option:selected').val()
+ };
+}
+````
+
+This method is used by the `WidgetManager` while building filters.
+
+#### init
+
+Used to initialize the widget when needed. It has a filter argument that can be used while getting data from server. `init` method is used when `WidgetManager.init()` function is called. It is also called if your widget requires a full re-load on refresh. See the `RefreshUrl` widget option.
+
+#### refresh
+
+Used to refresh the widget when needed. It has a filter argument that can be used while getting data from server. `refresh` method is used whenever `WidgetManager.refresh()` function is called.
+
## Authorization
Some widgets may need to be available only for authenticated or authorized users. In this case, use the following properties of the `Widget` attribute:
@@ -249,7 +476,7 @@ namespace DashboardDemo.Web.Pages.Components.MySimpleWidget
}
````
-## Widget Options
+## WidgetOptions
As alternative to the `Widget` attribute, you can use the `WidgetOptions` to configure widgets:
@@ -271,4 +498,8 @@ Configure(options =>
});
````
-> Tip: `WidgetOptions` can also be used to get an existing widget and change its configuration. This is especially useful if you want to modify the configuration of a widget inside a module used by your application. Use `options.Widgets.Find` to get an existing `WidgetDefinition`.
\ No newline at end of file
+> Tip: `WidgetOptions` can also be used to get an existing widget and change its configuration. This is especially useful if you want to modify the configuration of a widget inside a module used by your application. Use `options.Widgets.Find` to get an existing `WidgetDefinition`.
+
+## See Also
+
+* [Example project (source code)](https://github.com/abpframework/abp/tree/dev/samples/DashboardDemo).
\ No newline at end of file
diff --git a/docs/en/CLI.md b/docs/en/CLI.md
index 1f461c9997f..f931d3688fd 100644
--- a/docs/en/CLI.md
+++ b/docs/en/CLI.md
@@ -34,24 +34,29 @@ Example:
abp new Acme.BookStore
````
-* Acme.BookStore is the solution name here.
+* `Acme.BookStore` is the solution name here.
* Common convention is to name a solution is like *YourCompany.YourProject*. However, you can use different naming like *YourProject* (single level namespacing) or *YourCompany.YourProduct.YourModule* (three levels namespacing).
#### Options
-* `--template` or `-t`: Specifies the template name. Default template name is `mvc`. Available templates:
- * `mvc` (default): ASP.NET Core [MVC application template](Startup-Templates/Mvc.md). Additional options:
+* `--template` or `-t`: Specifies the template name. Default template name is `app`, which generates a web application. Available templates:
+ * `app` (default): [Application template](Startup-Templates/Application.md). Additional options:
+ * `--ui` or `-u`: Specifies the UI framework. Default framework is `mvc`. Available frameworks:
+ * `mvc`: ASP.NET Core MVC. There are some additional options for this template:
+ * `--tiered`: Creates a tiered solution where Web and Http API layers are physically separated. If not specified, it creates a layered solution which is less complex and suitable for most scenarios.
+ * `angular`: Angular. There are some additional options for this template:
+ * `--separate-identity-server`: Separates the identity server application from the API host application. If not specified, you will have a single endpoint in the server side.
* `--database-provider` or `-d`: Specifies the database provider. Default provider is `ef`. Available providers:
* `ef`: Entity Framework Core.
* `mongodb`: MongoDB.
- * `--tiered`: Creates a tiered solution where Web and Http API layers are physically separated. If not specified, it creates a layered solution which is less complex and suitable for most scenarios.
- * `mvc-module`: ASP.NET Core [MVC module template](Startup-Templates/Mvc-Module.md). Additional options:
+ * `module`: [Module template](Startup-Templates/Module.md). Additional options:
* `--no-ui`: Specifies to not include the UI. This makes possible to create service-only modules (a.k.a. microservices - without UI).
* `--output-folder` or `-o`: Specifies the output folder. Default value is the current directory.
+* `--version` or `-v`: Specifies the ABP & template version. It can be a [release tag](https://github.com/abpframework/abp/releases) or a [branch name](https://github.com/abpframework/abp/branches). Uses the latest release if not specified. Most of the times, you will want to use the latest version.
### add-package
-Adds a new ABP package to a project by,
+Adds an ABP package to a project by,
* Adding related nuget package as a dependency to the project.
* Adding `[DependsOn(...)]` attribute to the module class in the project (see the [module development document](Module-Development-Basics.md)).
@@ -78,9 +83,9 @@ abp add-package Volo.Abp.MongoDB
### add-module
-Adds a multi-package module to a solution by finding all packages of the module, finding related projects in the solution and adding each package to the corresponding project in the solution.
+Adds a [multi-package application module](Modules/Index) to a solution by finding all packages of the module, finding related projects in the solution and adding each package to the corresponding project in the solution.
-> A business module generally consists of several packages (because of layering, different database providr options or other reasons). Using `add-module` command dramatically simplifies adding a module to a solution. However, each module may require some additional configurations which is generally indicated in the documentation of the related module.
+> A business module generally consists of several packages (because of layering, different database provider options or other reasons). Using `add-module` command dramatically simplifies adding a module to a solution. However, each module may require some additional configurations which is generally indicated in the documentation of the related module.
Basic usage:
@@ -100,10 +105,11 @@ abp add-module Volo.Blogging
* `--solution` or `-s`: Specifies the solution (.sln) file path. If not specified, CLI tries to find a .sln file in the current directory.
* `--skip-db-migrations`: For EF Core database provider, it automatically adds a new code first migration (`Add-Migration`) and updates the database (`Update-Database`) if necessary. Specify this option to skip this operation.
+* `-sp` or `--startup-project`: Relative path to the project folder of the startup project. Default value is the current folder.
### update
-Updating all ABP related packages can be tedious since there are many packages of the framework and modules. This command automatically updates all ABP NuGet related packages and NPM packages in a solution or project to the latest versions.
+Updating all ABP related packages can be tedious since there are many packages of the framework and modules. This command automatically updates all ABP related NuGet and NPM packages in a solution or project to the latest versions.
Usage:
diff --git a/docs/en/Getting-Started-Angular-Template.md b/docs/en/Getting-Started-Angular-Template.md
new file mode 100644
index 00000000000..1bd605cd654
--- /dev/null
+++ b/docs/en/Getting-Started-Angular-Template.md
@@ -0,0 +1,126 @@
+## Getting Started With the Angular Application Template
+
+This tutorial explain how to create a new Angular application using the startup template, configure and run it.
+
+### Creating a New Project
+
+This tutorial uses **ABP CLI** to create a new project. See the [Get Started](https://abp.io/get-started) page for other options.
+
+Install the ABP CLI using a command line window, if you've not installed before:
+
+````bash
+dotnet tool install -g Volo.Abp.Cli
+````
+
+Use `abp new` command in an empty folder to create your project:
+
+````bash
+abp new Acme.BookStore -u angular
+````
+
+> You can use different level of namespaces; e.g. BookStore, Acme.BookStore or Acme.Retail.BookStore.
+
+`-u angular` option specifies the UI framework to be Angular. Default database provider is EF Core. See the [CLI documentation](CLI.md) for all available options.
+
+#### Pre Requirements
+
+The created solution requires;
+
+* [Visual Studio 2017 (v15.9.0+)](https://visualstudio.microsoft.com/tr/downloads/)
+* [.NET Core 2.2+](https://www.microsoft.com/net/download/dotnet-core/)
+* [Node v10.16+](https://nodejs.org)
+* [Yarn v1.17+](https://yarnpkg.com/)
+
+### The Solution Structure
+
+Open the solution in **Visual Studio**:
+
+
+
+The solution has a layered structure (based on [Domain Driven Design](Domain-Driven-Design.md)) and contains unit & integration test projects properly configured to work with **EF Core** & **SQLite in-memory** database.
+
+> See the [Application Template Document](Startup-Templates/Application.md) to understand the solution structure in details.
+
+### Database Connection String
+
+Check the **connection string** in the `appsettings.json` file under the `.HttpApi.Host` project:
+
+````json
+{
+ "ConnectionStrings": {
+ "Default": "Server=localhost;Database=BookStore;Trusted_Connection=True"
+ }
+}
+````
+
+The solution is configured to use **Entity Framework Core** with **MS SQL Server**. EF Core supports [various](https://docs.microsoft.com/en-us/ef/core/providers/) database providers, so you can use another DBMS if you want. Change the connection string if you need.
+
+### Create Database & Apply Database Migrations
+
+You have two options to create the database.
+
+#### Using the DbMigrator Application
+
+The solution contains a console application (named `Acme.BookStore.DbMigrator` in this sample) that can create database, apply migrations and seed initial data. It is useful on development as well as on production environment.
+
+> `.DbMigrator` project has its own `appsettings.json`. So, if you have changed the connection string above, you should also change this one.
+
+Right click to the `.DbMigrator` project and select **Set as StartUp Project**:
+
+
+
+Hit F5 (or Ctrl+F5) to run the application. It will have an output like shown below:
+
+
+
+#### Using EF Core Update-Database Command
+
+Ef Core has `Update-Database` command which creates database if necessary and applies pending migrations. Right click to the `.Web` project and select **Set as StartUp Project**:
+
+
+
+Open the **Package Manager Console**, select `.EntityFrameworkCore.DbMigrations` project as the **Default Project** and run the `Update-Database` command:
+
+
+
+This will create a new database based on the configured connection string.
+
+> Using the `.Migrator` tool is the suggested way, because it also seeds the initial data to be able to properly run the web application.
+
+### Running the Application
+
+#### Run the API Host (Server Side)
+
+Ensure that the `.HttpApi.Host` project is the startup project and un the application which will open a Swagger UI:
+
+
+
+You can see the application APIs and test them here. Get [more info](https://swagger.io/tools/swagger-ui/) about the Swagger UI.
+
+##### Authorization for the Swagger UI
+
+Most of the application APIs require authentication & authorization. If you want to test authorized APIs, manually go to the `/Account/Login` page, enter `admin` as the username and `1q2w3E*` as the password to login to the application. Then you will be able to execute authorized APIs too.
+
+#### Run the Angular Application (Client Side)
+
+Go to the `angular` folder, open a command line terminal, type the `yarn` command (we suggest to the [yarn](https://yarnpkg.com) package manager while npm install will also work in most cases):
+
+````bash
+yarn
+````
+
+Once all node modules are loaded, execute `yarn start` or `npm start` command:
+
+````bash
+yarn start
+````
+
+Open your favorite browser and go to `localhost:4200` URL. Initial username is `admin` and password is `1q2w3E*`.
+
+The startup template includes the **identity management** and **tenant management** modules. Once you login, the Administration menu will be available where you can manage **tenants**, **roles**, **users** and their **permissions**.
+
+> We recommend [Visual Studio Code](https://code.visualstudio.com/) as the editor for the Angular project, but you are free to use your favorite editor.
+
+### What's Next?
+
+* [Application development tutorial](Tutorials/Angular/Part-I.md)
diff --git a/docs/en/Getting-Started-AspNetCore-MVC-Template.md b/docs/en/Getting-Started-AspNetCore-MVC-Template.md
index cfb401c0845..e3aeaa89a72 100644
--- a/docs/en/Getting-Started-AspNetCore-MVC-Template.md
+++ b/docs/en/Getting-Started-AspNetCore-MVC-Template.md
@@ -1,6 +1,6 @@
## Getting Started With the ASP.NET Core MVC Template
-These tutorials explain how to create a new ASP.NET Core MVC web application using the startup template, configure and run it.
+This tutorial explains how to create a new ASP.NET Core MVC web application using the startup template, configure and run it.
### Creating a New Project
@@ -37,7 +37,7 @@ Open the solution in **Visual Studio**:
The solution has a layered structure (based on [Domain Driven Design](Domain-Driven-Design.md)) and contains unit & integration test projects properly configured to work with **EF Core** & **SQLite in-memory** database.
-> See [MVC application template document](Startup-Templates/Mvc.md) to understand the solution structure in details.
+> See [Application template document](Startup-Templates/Application.md) to understand the solution structure in details.
### Database Connection String
diff --git a/docs/en/Samples/Microservice-Demo.md b/docs/en/Samples/Microservice-Demo.md
index bc3c8c09338..ba32878ea61 100644
--- a/docs/en/Samples/Microservice-Demo.md
+++ b/docs/en/Samples/Microservice-Demo.md
@@ -902,7 +902,7 @@ It has a dedicated MongoDB database (MsDemo_Blogging) to store blog and posts. I
````json
"ConnectionStrings": {
"Default": "Server=localhost;Database=MsDemo_Identity;Trusted_Connection=True;MultipleActiveResultSets=true",
- "Blogging": "mongodb://localhost|MsDemo_Blogging"
+ "Blogging": "mongodb://localhost/MsDemo_Blogging"
}
````
diff --git a/docs/en/Startup-Templates/Application.md b/docs/en/Startup-Templates/Application.md
new file mode 100644
index 00000000000..4dabe6a9dad
--- /dev/null
+++ b/docs/en/Startup-Templates/Application.md
@@ -0,0 +1,274 @@
+# Application Startup Template
+
+## Introduction
+
+This template provides a layered application structure based on the [Domain Driven Design](../Domain-Driven-Design.md) (DDD) practices. This document explains the solution structure and projects in details. If you want to start quickly, follow the guides below:
+
+* See [Getting Started With the ASP.NET Core MVC Template](../Getting-Started-AspNetCore-MVC-Template.md) to create a new solution and run it for this template (uses MVC as the UI framework and Entity Framework Core as the database provider).
+* See the [ASP.NET Core MVC Tutorial](../Tutorials/AspNetCore-Mvc/Part-I.md) to learn how to develop applications using this template (uses MVC as the UI framework and Entity Framework Core as the database provider).
+
+## How to Start With?
+
+You can use the [ABP CLI](../CLI.md) to create a new project using this startup template. Alternatively, you can directly create & download from the [Get Started](https://abp.io/get-started) page. CLI approach is used here.
+
+First, install the ABP CLI if you haven't installed before:
+
+````bash
+dotnet tool install -g Volo.Abp.Cli
+````
+
+Then use the `abp new` command in an empty folder to create a new solution:
+
+````bash
+abp new Acme.BookStore -t app
+````
+
+* `Acme.BookStore` is the solution name, like *YourCompany.YourProduct*. You can use single level, two-levels or three-levels naming.
+* This example specified the template name (`-t` or `--template` option). However, `app` is already the default template if you don't specify it.
+
+### Specify the UI Framework
+
+This template provides multiple UI frameworks:
+
+* `mvc`: ASP.NET Core MVC UI with Razor Pages (default)
+* `angular`: Angular UI
+
+Use `-u` or `--ui` option to specify the UI framework:
+
+````bash
+abp new Acme.BookStore -u angular
+````
+
+### Specify the Database Provider
+
+This template supports the following database providers:
+
+- `ef`: Entity Framework Core (default)
+- `mongodb`: MongoDB
+
+Use `-d` (or `--database-provider`) option to specify the database provider:
+
+````bash
+abp new Acme.BookStore -d mongodb
+````
+
+## Solution Structure
+
+Based on the options you've specified, you will get a slightly different solution structure.
+
+### Default Structure
+
+If you don't specify any additional option, you will have a solution like shown below:
+
+
+
+Projects are organized in `src` and `test` folders. `src` folder contains the actual application which is layered based on [DDD](../Domain-Driven-Design.md) principles as mentioned before.
+
+The diagram below shows the layers & project dependencies of the application:
+
+
+
+Each section below will explain the related project & its dependencies.
+
+#### .Domain.Shared Project
+
+This project contains constants, enums and other objects these are actually a part of the domain layer, but needed to be used by all layers/projects in the solution.
+
+A `BookType` enum and a `BookConsts` class (which may have some constant fields for the `Book` entity, like `MaxNameLength`) are good candidates for this project.
+
+* This project has no dependency to other projects in the solution. All other projects depend on this directly or indirectly.
+
+#### .Domain Project
+
+This is the domain layer of the solution. It mainly contains [entities, aggregate roots](../Entities.md), [domain services](../Domain-Services.md), [value types](../Value-Types.md), [repository interfaces](../Repositories.md) and other domain objects.
+
+A `Book` entity, a `BookManager` domain service and an `IBookRepository` interface are good candidates for this project.
+
+* Depends on the `.Domain.Shared` because it uses constants, enums and other objects defined in that project.
+
+#### .Application.Contracts Project
+
+This project mainly contains [application service](../Application-Services.md) **interfaces** and [Data Transfer Objects](../Data-Transfer-Objects.md) (DTO) of the application layer. It does exists to separate interface & implementation of the application layer. In this way, the interface project can be shared to the clients as a contract package.
+
+An `IBookAppService` interface and a `BookCreationDto` class are good candidates for this project.
+
+* Depends on the `.Domain.Shared` because it may use constants, enums and other shared objects of this project in the application service interfaces and DTOs.
+
+#### .Application Project
+
+This project contains the [application service](../Application-Services.md) **implementations** of the interfaces defined in the `.Application.Contracts` project.
+
+A `BookAppService` class is a good candidate for this project.
+
+* Depends on the `.Application.Contracts` project to be able to implement the interfaces and use the DTOs.
+* Depends on the `.Domain` project to be able to use domain objects (entities, repository interfaces... etc.) to perform the application logic.
+
+#### .EntityFrameworkCore Project
+
+This is the integration project for the EF Core. It defines the `DbContext` and implements repository interfaces defined in the `.Domain` project.
+
+* Depends on the `.Domain` project to be able to reference to entities and repository interfaces.
+
+> This project is available only if you are using EF Core as the database provider. If you select another database provider, its name will be different.
+
+#### .EntityFrameworkCore.DbMigrations Project
+
+Contains EF Core database migrations for the solution. It has a separated `DbContext` to dedicated to manage migrations.
+
+ABP is a modular framework and with an ideal design, each module has its own `DbContext` class. This is where the migration `DbContext` comes into play and unifies all `DbContext` configurations into a single model to maintain a single database schema. For more advanced scenarios, you can have multiple databases (each contains a single or a few module tables) and multiple migration `DbContext`s (each maintains a different database schema).
+
+Notice that the migration `DbContext` is only used for database migrations and *not used on runtime*.
+
+* Depends on the `.EntityFrameworkCore` project since it re-uses the configuration defined for the `DbContext` of the application.
+
+> This project is available only if you are using EF Core as the database provider.
+
+#### .DbMigrator Project
+
+This is a console application which simplifies to execute database migrations on development and production environments. When you run this application, it;
+
+* Creates the database if necessary.
+* Applies the pending database migrations.
+* Seeds initial data if needed.
+
+> This project has its own `appsettings.json` file. So, if you want to change the database connection string, remember to change this file too.
+
+Especially, seeding initial data is important at this point. ABP has a modular data seed infrastructure. See [its documentation](../Data-Seeding.md) for more about the data seeding.
+
+While creating database & applying migrations seems only necessary for relational databases, this projects comes even if you choose a NoSQL database provider (like MongoDB). In that case, it still seeds initial data which is necessary for the application.
+
+* Depends on the `.EntityFrameworkCore.DbMigrations` project (for EF Core) since it needs to access to the migrations.
+* Depends on the `.Application.Contracts` project to be able to access permission definitions, because initial data seeder grants all permissions for the admin role by default.
+
+#### .HttpApi Project
+
+This project is used to define your API Controllers.
+
+Most of time you don't need to manually define API Controllers since ABP's [Auto API Controllers](../AspNetCore/Auto-API-Controllers.md) feature creates them automagically based on your application layer. However, in case of you need to write API controllers, this is the best place to do it.
+
+* Depends on the `.Application.Contracts` project to be able to inject the application service interfaces.
+
+#### .HttpApi.Client Project
+
+This is a project that defines C# client proxies to use the HTTP APIs of the solution. You can share this library to 3rd-party clients, so they can easily consume your HTTP APIs in their Dotnet applications (For other type of applications, they can still use your APIs, either manually or using a tool in their own platform)
+
+Most of time you don't need to manually create C# client proxies, thanks to ABP's [Dynamic C# API Clients](../AspNetCore/Dynamic-CSharp-API-Clients.md) feature.
+
+`.HttpApi.Client.ConsoleTestApp` project is a console application created to demonstrate the usage of the client proxies.
+
+* Depends on the `.Application.Contracts` project to be able to share the same application service interfaces and DTOs with the remote service.
+
+> You can delete this project & dependencies if you don't need to create C# client proxies for your APIs.
+
+#### .Web Project
+
+This project contains the User Interface (UI) of the application if you are using ASP.NET Core MVC UI. It contains Razor pages, JavaScript files, CSS files, images and so on...
+
+This project contains the main `appsettings.json` file that contains the connection string and other configuration of the application.
+
+* Depends on the `.HttpApi` since UI layer needs to use APIs and application service interfaces of the solution.
+
+> If you check the source code of the `.Web.csproj` file, you will see the references to the `.Application` and the `.EntityFrameworkCore.DbMigrations` projects.
+>
+> These references are actually not needed while coding your UI layer, because UI layer normally doesn't depend on the EF Core or the Application layer's implementation. This startup templates are ready for the tiered deployment, where API layer is hosted in a separate server than the UI layer.
+>
+> However, if you don't choose the `--tiered` option, these references will be in the .Web project to be able to host the Web, API and application layers in a single application endpoint.
+>
+> This gives you to ability to use domain entities & repositories in your presentation layer. However, this is considered as a bad practice according to the DDD.
+
+#### Test Projects
+
+The solution has multiple test projects, one for each layer:
+
+* `.Domain.Tests` is used to test the domain layer.
+* `.Application.Tests` is used to test the application layer.
+* `.EntityFrameworkCore.Tests` is used to test EF Core configuration and custom repositories.
+* `.Web.Tests` is used to test the UI (if you are using ASP.NET Core MVC UI).
+* `.TestBase` is a base (shared) project for all tests.
+
+In addition, `.HttpApi.Client.ConsoleTestApp` is a console application (not an automated test project) which demonstrate the usage of HTTP APIs from a .NET application.
+
+Test projects are prepared for integration testing;
+
+* It is fully integrated to ABP framework and all services in your application.
+* It uses SQLite in-memory database for EF Core. For MongoDB, it uses the [Mongo2Go](https://github.com/Mongo2Go/Mongo2Go) library.
+* Authorization is disabled, so any application service can be easily used in tests.
+
+You can still create unit tests for your classes which will be harder to write (because you will need to prepare mock/fake objects), but faster to run (because it only tests a single class and skips all initialization process).
+
+#### How to Run?
+
+Set `.Web` as the startup project and run the application. Default username is `admin` and password is `1q2w3E*`.
+
+See [Getting Started With the ASP.NET Core MVC Template](../Getting-Started-AspNetCore-MVC-Template.md) for more information.
+
+### Tiered Structure
+
+If you have selected the ASP.NET Core UI and specified the `--tiered` option, the solution created will be a tiered solution. The purpose of the tiered structure is to be able to **deploy Web application and HTTP API to different servers**:
+
+
+
+* Browser runs your UI by executing HTML, CSS & JavaScript.
+* Web servers hosts static UI files (CSS, JavaScript, image... etc.) & dynamic components (e.g. Razor pages). It performs HTTP requests to the API server to execute the business logic of the application.
+* API Server hosts the HTTP APIs which then use application & domain layers of the application to perform the business logic.
+* Finally, database server hosts your database.
+
+So, the resulting solution allows a 4-tiered deployment, by comparing to 3-tiered deployment of the default structure explained before.
+
+> Unless you actually need to such a 4-tiered deployment, its suggested to go with the default structure which is simpler to develop, deploy and maintain.
+
+The solution structure is shown below:
+
+
+
+As different from the default structure, two new projects come into play: `.IdentityServer` & `.HttpApi.Host`.
+
+#### .IdentityServer Project
+
+This project is used as an authentication server for other projects. `.Web` project uses OpenId Connect Authentication to get identity and access tokens for the current user from the IdentityServer. Then uses the access token to call the HTTP API server. HTTP API server uses bearer token authentication to obtain claims from the access token to authorize the current user.
+
+
+
+ABP uses the open source [IdentityServer4](https://identityserver.io/) framework for the authentication between applications. See [IdentityServer4 documentation](http://docs.identityserver.io) for details about the IdentityServer4 and OpenID Connect protocol.
+
+It has its own `appsettings.json` that contains database connection and other configurations.
+
+#### .HttpApi.Host Project
+
+This project is an application that hosts the API of the solution. It has its own `appsettings.json` that contains database connection and other configurations.
+
+#### .Web Project
+
+Just like the default structure, this project contains the User Interface (UI) of the application. It contains razor pages, JavaScript files, style files, images and so on...
+
+This project contains an `appsettings.json` file, but this time it does not have a connection string because it never connects to the database. Instead, it mainly contains endpoint of the remote API server and the authentication server.
+
+#### Pre-requirements
+
+* [Redis](https://redis.io/): The applications use Redis as as distributed cache. So, you need to have Redis installed & running.
+
+#### How to Run?
+
+You should run the application with the given order:
+
+* First, run the `.IdentityServer` since other applications depends on it.
+* Then run the `.HttpApi.Host` since it is used by the `.Web` application.
+* Finally, you can run the `.Web` project and login to the application (using `admin` as the username and `1q2w3E*` as the password).
+
+### Angular UI
+
+If you choose Angular as the UI framework (using the `-u angular` option), the solution is separated into two folders:
+
+* `angular` folder contains the Angular UI solution, the client side.
+* `aspnet-core` folder contains the ASP.NET Core solution, the server side.
+
+Server side is very similar to the solution described above. `.HttpApi.Host` project serves the API, so the Angular application can consume it.
+
+The files under the `angular/src/environments` folder has the essential configuration of the application.
+
+####
+
+## What's Next?
+
+- See [Getting Started With the ASP.NET Core MVC Template](../Getting-Started-AspNetCore-MVC-Template.md) to create a new solution and run it for this template.
+- See the [ASP.NET Core MVC Tutorial](../Tutorials/AspNetCore-Mvc/Part-I.md) to learn how to develop applications using this template.
diff --git a/docs/en/Startup-Templates/Index.md b/docs/en/Startup-Templates/Index.md
index cb7e020663d..f47fb979103 100644
--- a/docs/en/Startup-Templates/Index.md
+++ b/docs/en/Startup-Templates/Index.md
@@ -1,11 +1,9 @@
# Startup Templates
-While you can start with an empty project and add needed packages manually, startup templates makes easy and comfortable to start a new solution with the ABP framework.
+While you can start with an empty project and add needed packages manually, startup templates make easy and comfortable to start a new solution with the ABP framework. Click the name from the list below to see the documentation of the related startup template:
-Click to the name from the list below to see the documentation of the related startup template:
-
-* [**mvc**](Mvc.md): ASP.NET Core MVC application template.
-* [**mvc-module**](Mvc-Module.md): ASP.NET Core MVC module/service template.
+* [**app**](Application.md): Application template.
+* [**module**](Module.md): Module/service template.
diff --git a/docs/en/Startup-Templates/Module.md b/docs/en/Startup-Templates/Module.md
new file mode 100644
index 00000000000..fa425c67e23
--- /dev/null
+++ b/docs/en/Startup-Templates/Module.md
@@ -0,0 +1,158 @@
+# MVC Module Startup Template
+
+This template can be used to create a **reusable [application module](../Modules/Index.md)** based on the [module development best practices & conventions](../Best-Practices/Index.md). It is also suitable for creating **microservices** (with or without UI).
+
+## How to Start With?
+
+You can use the [ABP CLI](../CLI.md) to create a new project using this startup template. Alternatively, you can directly create & download from the [Get Started](https://abp.io/get-started) page. CLI approach is used here.
+
+First, install the ABP CLI if you haven't installed before:
+
+```bash
+dotnet tool install -g Volo.Abp.Cli
+```
+
+Then use the `abp new` command in an empty folder to create a new solution:
+
+```bash
+abp new Acme.IssueManagement -t module
+```
+
+- `Acme.IssueManagement` is the solution name, like *YourCompany.YourProduct*. You can use single level, two-levels or three-levels naming.
+
+### Without User Interface
+
+The template comes with an MVC UI by default. You can use `--no-ui` option to not include the UI layer.
+
+````bash
+abp new Acme.IssueManagement -t mvc-module --no-ui
+````
+
+## Solution Structure
+
+Based on the options you've specified, you will get a slightly different solution structure. If you don't specify any option, you will have a solution like shown below:
+
+
+
+Projects are organized as `src`, `test` and `host` folders:
+
+* `src` folder contains the actual module which is layered based on [DDD](../Domain-Driven-Design.md) principles.
+* `test` folder contains unit & integration tests.
+* `host` folder contains applications with different configurations to demonstrate how to host the module in an application. These are not a part of the module, but useful on development.
+
+The diagram below shows the layers & project dependencies of the module:
+
+
+
+Each section below will explain the related project & its dependencies.
+
+### .Domain.Shared Project
+
+This project contains constants, enums and other objects these are actually a part of the domain layer, but needed to be used by all layers/projects in the solution.
+
+An `IssueType` enum and an `IssueConsts` class (which may have some constant fields for the `Issue` entity, like `MaxTitleLength`) are good candidates for this project.
+
+- This project has no dependency to other projects in the solution. All other projects depend on this directly or indirectly.
+
+### .Domain Project
+
+This is the domain layer of the solution. It mainly contains [entities, aggregate roots](../Entities.md), [domain services](../Domain-Services.md), [value types](../Value-Types.md), [repository interfaces](../Repositories.md) and other domain objects.
+
+An `Issue` entity, an `IssueManager` domain service and an `IIssueRepository` interface are good candidates for this project.
+
+- Depends on the `.Domain.Shared` because it uses constants, enums and other objects defined in that project.
+
+### .Application.Contracts Project
+
+This project mainly contains [application service](../Application-Services.md) **interfaces** and [Data Transfer Objects](../Data-Transfer-Objects.md) (DTO) of the application layer. It does exists to separate interface & implementation of the application layer. In this way, the interface project can be shared to the clients as a contract package.
+
+An `IIssueAppService` interface and an `IssueCreationDto` class are good candidates for this project.
+
+- Depends on the `.Domain.Shared` because it may use constants, enums and other shared objects of this project in the application service interfaces and DTOs.
+
+### .Application Project
+
+This project contains the [application service](../Application-Services.md) **implementations** of the interfaces defined in the `.Application.Contracts` project.
+
+An `IssueAppService` class is a good candidate for this project.
+
+- Depends on the `.Application.Contracts` project to be able to implement the interfaces and use the DTOs.
+- Depends on the `.Domain` project to be able to use domain objects (entities, repository interfaces... etc.) to perform the application logic.
+
+### .EntityFrameworkCore Project
+
+This is the integration project for EF Core. It defines the `DbContext` and implements repository interfaces defined in the `.Domain` project.
+
+- Depends on the `.Domain` project to be able to reference to entities and repository interfaces.
+
+> You can delete this project if you don't want to support EF Core for your module.
+
+### .MongoDB Project
+
+This is the integration project for MongoDB.
+
+- Depends on the `.Domain` project to be able to reference to entities and repository interfaces.
+
+> You can delete this project if you don't want to support MongoDB for your module.
+
+### Test Projects
+
+The solution has multiple test projects, one for each layer:
+
+- `.Domain.Tests` is used to test the domain layer.
+- `.Application.Tests` is used to test the application layer.
+- `.EntityFrameworkCore.Tests` is used to test EF Core configuration and custom repositories.
+- `.MongoDB.Tests` is used to test MongoDB configuration and custom repositories.
+- `.TestBase` is a base (shared) project for all tests.
+
+In addition, `.HttpApi.Client.ConsoleTestApp` is a console application (not an automated test project) which demonstrate the usage of HTTP APIs from a Dotnet application.
+
+Test projects are prepared for integration testing;
+
+- It is fully integrated to ABP framework and all services in your application.
+- It uses SQLite in-memory database for EF Core. For MongoDB, it uses the [Mongo2Go](https://github.com/Mongo2Go/Mongo2Go) library.
+- Authorization is disabled, so any application service can be easily used in tests.
+
+You can still create unit tests for your classes which will be harder to write (because you will need to prepare mock/fake objects), but faster to run (because it only tests a single class and skips all initialization process).
+
+> Domain & Application tests are using EF Core. If you remove EF Core integration or you want to use MongoDB for testing these layers, you should manually change project references & module dependencies.
+
+### Host Projects
+
+The solution has a few host applications to run your module. Host applications are used to run your module in a fully configured application. It is useful on development. Host applications includes some other modules in addition to the module being developed:
+
+Host applications support two types of scenarios.
+
+#### Single (Unified) Application Scenario
+
+If your module has a UI, then `.Web.Unified` application is used to host the UI and API on a single point. It has its own `appsettings.json` file (that includes the database connection string) and EF Core database migrations.
+
+For the `.Web.Unified` application, there is a single database, named `YourProjectName_Unified` (like *IssueManagement_Unified* for this sample).
+
+> If you've selected the `--no-ui` option, this project will not be in your solution.
+
+##### How to Run?
+
+Set it as the startup project, run `Update-Database` command for the EF Core from Package Manager Console and run your application. Default username is `admin` and password is `1q2w3E*`.
+
+#### Separated Deployment & Databases Scenario
+
+In this scenario, there are three applications;
+
+* `.IdentityServer` application is an authentication server used by other applications. It has its own `appsettings.json` that contains database connection and other configurations.
+* `.HttpApi.Host` hosts the HTTP API of the module. It has its own `appsettings.json` that contains database connections and other configurations.
+* `.Web.Host` host the UI of the module. This project contains an `appsettings.json` file, but it does not have a connection string because it never connects to the database. Instead, it mainly contains endpoint of the remote API server and the authentication server.
+
+The diagram below shows the relation of the applications:
+
+
+
+`.Web.Host` project uses OpenId Connect Authentication to get identity and access tokens for the current user from the `.IdentityServer`. Then uses the access token to call the `.HttpApi.Host`. HTTP API server uses bearer token authentication to obtain claims from the access token to authorize the current user.
+
+##### How to Run?
+
+You should run the application with the given order:
+
+- First, run the `.IdentityServer` since other applications depends on it.
+- Then run the `.HttpApi.Host` since it is used by the `.Web.Host` application.
+- Finally, you can run the `.Web.Host` project and login to the application using `admin` as the username and `1q2w3E*` as the password.
diff --git a/docs/en/Startup-Templates/Mvc-Module.md b/docs/en/Startup-Templates/Mvc-Module.md
deleted file mode 100644
index 1a2c6c275f1..00000000000
--- a/docs/en/Startup-Templates/Mvc-Module.md
+++ /dev/null
@@ -1,158 +0,0 @@
-# MVC Module Startup Template
-
-This template can be used to create a **reusable [application module](../Modules/Index.md)** based on the [module development best practices & conventions](../Best-Practices/Index.md). It is also suitable for creating **microservices** (with or without UI).
-
-## How to Start With?
-
-You can use the [ABP CLI](../CLI.md) to create a new project using this startup template. Alternatively, you can directly create & download from the [Get Started](https://abp.io/get-started) page. CLI approach is used here.
-
-First, install the ABP CLI if you haven't installed before:
-
-```bash
-dotnet tool install -g Volo.Abp.Cli
-```
-
-Then use the `abp new` command in an empty folder to create a new solution:
-
-```bash
-abp new Acme.IssueManagement -t mvc-module
-```
-
-- `Acme.IssueManagement` is the solution name, like *YourCompany.YourProduct*. You can use single level, two-levels or three-levels naming.
-
-### Without User Interface
-
-The template comes with a UI by default. You can use `--no-ui` option to not include the UI layer.
-
-````bash
-abp new Acme.IssueManagement -t mvc-module --no-ui
-````
-
-## Solution Structure
-
-Based on the options you've specified, you will get a slightly different solution structure. If you don't specify any option, you will have a solution like shown below:
-
-
-
-Projects are organized as `src`, `test` and `host` folders:
-
-* `src` folder contains the actual module which is layered based on [DDD](../Domain-Driven-Design.md) principles.
-* `test` folder contains unit & integration tests.
-* `host` folder contains applications with different configurations to demonstrate how to host the module in an application. These are not a part of the module, but useful on development.
-
-The diagram below shows the layers & project dependencies of the module:
-
-
-
-Each section below will explain the related project & its dependencies.
-
-### .Domain.Shared Project
-
-This project contains constants, enums and other objects these are actually a part of the domain layer, but needed to be used by all layers/projects in the solution.
-
-An `IssueType` enum and an `IssueConsts` class (which may have some constant fields for the `Issue` entity, like `MaxTitleLength`) are good candidates for this project.
-
-- This project has no dependency to other projects in the solution. All other projects depend on this directly or indirectly.
-
-### .Domain Project
-
-This is the domain layer of the solution. It mainly contains [entities, aggregate roots](../Entities.md), [domain services](../Domain-Services.md), [value types](../Value-Types.md), [repository interfaces](../Repositories.md) and other domain objects.
-
-An `Issue` entity, an `IssueManager` domain service and an `IIssueRepository` interface are good candidates for this project.
-
-- Depends on the `.Domain.Shared` because it uses constants, enums and other objects defined in that project.
-
-### .Application.Contracts Project
-
-This project mainly contains [application service](../Application-Services.md) **interfaces** and [Data Transfer Objects](../Data-Transfer-Objects.md) (DTO) of the application layer. It does exists to separate interface & implementation of the application layer. In this way, the interface project can be shared to the clients as a contract package.
-
-An `IIssueAppService` interface and an `IssueCreationDto` class are good candidates for this project.
-
-- Depends on the `.Domain.Shared` because it may use constants, enums and other shared objects of this project in the application service interfaces and DTOs.
-
-### .Application Project
-
-This project contains the [application service](../Application-Services.md) **implementations** of the interfaces defined in the `.Application.Contracts` project.
-
-An `IssueAppService` class is a good candidate for this project.
-
-- Depends on the `.Application.Contracts` project to be able to implement the interfaces and use the DTOs.
-- Depends on the `.Domain` project to be able to use domain objects (entities, repository interfaces... etc.) to perform the application logic.
-
-### .EntityFrameworkCore Project
-
-This is the integration project for EF Core. It defines the `DbContext` and implements repository interfaces defined in the `.Domain` project.
-
-- Depends on the `.Domain` project to be able to reference to entities and repository interfaces.
-
-> You can delete this project if you don't want to support EF Core for your module.
-
-### .MongoDB Project
-
-This is the integration project for MongoDB.
-
-- Depends on the `.Domain` project to be able to reference to entities and repository interfaces.
-
-> You can delete this project if you don't want to support MongoDB for your module.
-
-### Test Projects
-
-The solution has multiple test projects, one for each layer:
-
-- `.Domain.Tests` is used to test the domain layer.
-- `.Application.Tests` is used to test the application layer.
-- `.EntityFrameworkCore.Tests` is used to test EF Core configuration and custom repositories.
-- `.MongoDB.Tests` is used to test MongoDB configuration and custom repositories.
-- `.TestBase` is a base (shared) project for all tests.
-
-In addition, `.HttpApi.Client.ConsoleTestApp` is a console application (not an automated test project) which demonstrate the usage of HTTP APIs from a Dotnet application.
-
-Test projects are prepared for integration testing;
-
-- It is fully integrated to ABP framework and all services in your application.
-- It uses SQLite in-memory database for EF Core. For MongoDB, it uses the [Mongo2Go](https://github.com/Mongo2Go/Mongo2Go) library.
-- Authorization is disabled, so any application service can be easily used in tests.
-
-You can still create unit tests for your classes which will be harder to write (because you will need to prepare mock/fake objects), but faster to run (because it only tests a single class and skips all initialization process).
-
-> Domain & Application tests are using EF Core. If you remove EF Core integration or you want to use MongoDB for testing these layers, you should manually change project references & module dependencies.
-
-### Host Projects
-
-The solution has a few host applications to run your module on development. Host applications are used to run your module in a fully configured application. It is useful on development. Host applications includes some other modules in addition to the module being developed:
-
-Host applications support two types of scenarios.
-
-#### Single (Unified) Application Scenario
-
-If your module has a UI, then `.Web.Unified` application is used to host the UI and API on a single point. It has its own `appsettings.json` file (that includes the database connection string) and EF Core database migrations.
-
-For the `.Web.Unified` application, there is a single database, named `YourProjectName_Unified` (like *IssueManagement_Unified* for this sample).
-
-> If you've selected the `--no-ui` option, this project will not be in your solution.
-
-##### How to Run?
-
-Set it as the startup project, run `Update-Database` command for the EF Core from Package Manager Console and run your application. Default username is `admin` and password is `1q2w3E*`.
-
-#### Separated Deployment & Databases Scenario
-
-In this scenario, there are three applications;
-
-* `.IdentityServer` application is an authentication server used by other applications. It has its own `appsettings.json` that contains database connection and other configurations.
-* `.HttpApi.Host` hosts the HTTP API of the module. It has its own `appsettings.json` that contains database connections and other configurations.
-* `.Web.Host` host the UI of the module. This project contains an `appsettings.json` file, but it does not have a connection string because it never connects to the database. Instead, it mainly contains endpoint of the remote API server and the authentication server.
-
-The diagram below shows the relation of the applications:
-
-
-
-`.Web.Host` project uses OpenId Connect Authentication to get identity and access tokens for the current user from the `.IdentityServer`. Then uses the access token to call the `.HttpApi.Host`. HTTP API server uses bearer token authentication to obtain claims from the access token to authorize the current user.
-
-##### How to Run?
-
-You should run the application with the given order:
-
-- First, run the `.IdentityServer` since other applications depends on it.
-- Then run the `.HttpApi.Host` since it is used by the `.Web.Host` application.
-- Finally, you can run the `.Web.Host` project and login to the application using `admin` as the username and `1q2w3E*` as the password.
diff --git a/docs/en/Startup-Templates/Mvc.md b/docs/en/Startup-Templates/Mvc.md
deleted file mode 100644
index 09b278aadc3..00000000000
--- a/docs/en/Startup-Templates/Mvc.md
+++ /dev/null
@@ -1,260 +0,0 @@
-# MVC Application Startup Template
-
-## Introduction
-
-This template provides a layered (or tiered, based on the preference) application structure based on the [Domain Driven Design](../Domain-Driven-Design.md) (DDD) practices.
-
-This document explains the solution structure and projects in details.
-
-* See [Getting Started With the ASP.NET Core MVC Template](../Getting-Started-AspNetCore-MVC-Template.md) to create a new solution and run it for this template.
-* See the [ASP.NET Core MVC Tutorial](../Tutorials/AspNetCore-Mvc/Part-I.md) to learn how to develop applications using this template.
-
-## How to Start With?
-
-You can use the [ABP CLI](../CLI.md) to create a new project using this startup template. Alternatively, you can directly create & download from the [Get Started](https://abp.io/get-started) page. CLI approach is used here.
-
-First, install the ABP CLI if you haven't installed before:
-
-````bash
-dotnet tool install -g Volo.Abp.Cli
-````
-
-Then use the `abp new` command in an empty folder to create a new solution:
-
-````bash
-abp new Acme.BookStore -t mvc
-````
-
-* `Acme.BookStore` is the solution name, like *YourCompany.YourProduct*. You can use single level, two-levels or three-levels naming.
-* This example specified the template name (`-t` or `--template` option). However, `mvc` is already the default template if you don't specify it.
-
-### Specify the Database Provider
-
-This template supports the following database providers:
-
-- `ef`: Entity Framework Core (default)
-- `mongodb`: MongoDB
-
-Use `-d` (or `--database-provider`) option to specify the database provider:
-
-````bash
-abp new Acme.BookStore -d mongodb
-````
-
-### Create a Tiered Solution
-
-`--tiered` option is used to create a tiered solution where Web and Http API layers are physically separated. If not specified, it creates a layered solution which is less complex and suitable for most scenarios.
-
-````bash
-abp new Acme.BookStore --tiered
-````
-
-See the "Tiered Structure" section below for the tiered approach.
-
-## Solution Structure
-
-Based on the options you've specified, you will get a slightly different solution structure.
-
-### Default Structure
-
-If you don't specify any option, you will have a solution like shown below:
-
-
-
-Projects are organized in `src` and `test` folders. `src` folder contains the actual application which is layered based on [DDD](../Domain-Driven-Design.md) principles as mentioned before.
-
-The diagram below shows the layers & project dependencies of the application:
-
-
-
-Each section below will explain the related project & its dependencies.
-
-#### .Domain.Shared Project
-
-This project contains constants, enums and other objects these are actually a part of the domain layer, but needed to be used by all layers/projects in the solution.
-
-A `BookType` enum and a `BookConsts` class (which may have some constant fields for the `Book` entity, like `MaxNameLength`) are good candidates for this project.
-
-* This project has no dependency to other projects in the solution. All other projects depend on this directly or indirectly.
-
-#### .Domain Project
-
-This is the domain layer of the solution. It mainly contains [entities, aggregate roots](../Entities.md), [domain services](../Domain-Services.md), [value types](../Value-Types.md), [repository interfaces](../Repositories.md) and other domain objects.
-
-A `Book` entity, a `BookManager` domain service and an `IBookRepository` interface are good candidates for this project.
-
-* Depends on the `.Domain.Shared` because it uses constants, enums and other objects defined in that project.
-
-#### .Application.Contracts Project
-
-This project mainly contains [application service](../Application-Services.md) **interfaces** and [Data Transfer Objects](../Data-Transfer-Objects.md) (DTO) of the application layer. It does exists to separate interface & implementation of the application layer. In this way, the interface project can be shared to the clients as a contract package.
-
-An `IBookAppService` interface and a `BookCreationDto` class are good candidates for this project.
-
-* Depends on the `.Domain.Shared` because it may use constants, enums and other shared objects of this project in the application service interfaces and DTOs.
-
-#### .Application Project
-
-This project contains the [application service](../Application-Services.md) **implementations** of the interfaces defined in the `.Application.Contracts` project.
-
-A `BookAppService` class is a good candidate for this project.
-
-* Depends on the `.Application.Contracts` project to be able to implement the interfaces and use the DTOs.
-* Depends on the `.Domain` project to be able to use domain objects (entities, repository interfaces... etc.) to perform the application logic.
-
-#### .EntityFrameworkCore Project
-
-This is the integration project for the EF Core. It defines the `DbContext` and implements repository interfaces defined in the `.Domain` project.
-
-* Depends on the `.Domain` project to be able to reference to entities and repository interfaces.
-
-> This project is available only if you are using EF Core as the database provider. If you select another database provider, its name will be different.
-
-#### .EntityFrameworkCore.DbMigrations Project
-
-Contains EF Core database migrations for the solution. It has a separated `DbContext` to dedicated to manage migrations.
-
-ABP is a modular framework and with an ideal design, each module has its own `DbContext` class. This is where the migration `DbContext` comes into play and unifies all `DbContext` configurations into a single model to maintain a single database schema. For more advanced scenarios, you can have multiple databases (each contains a single or a few module tables) and multiple migration `DbContext`s (each maintains a different database schema).
-
-Notice that the migration `DbContext` is only used for database migrations and *not used on runtime*.
-
-* Depends on the `.EntityFrameworkCore` project since it re-uses the configuration defined for the `DbContext` of the application.
-
-> This project is available only if you are using EF Core as the database provider.
-
-#### .DbMigrator Project
-
-This is a console application which simplifies to execute database migrations on development and production environments. When you run this application, it;
-
-* Creates the database if necessary.
-* Applies the pending database migrations.
-* Seeds initial data if needed.
-
-> This project has its own `appsettings.json` file. So, if you want to change the database connection string, remember to change this file too.
-
-Especially, seeding initial data is important at this point. ABP has a modular data seed infrastructure. See [its documentation](../Data-Seeding.md) for more about the data seeding.
-
-While creating database & applying migrations seems only necessary for relational databases, this projects comes even if you choose a NoSQL database provider (like MongoDB). In that case, it still seeds initial data which is necessary for the application.
-
-* Depends on the `.EntityFrameworkCore.DbMigrations` project (for EF Core) since it needs to access to the migrations.
-* Depends on the `.Application.Contracts` project to be able to access permission definitions, because initial data seeder grants all permissions for the admin role by default.
-
-#### .HttpApi Project
-
-This project is used to define your API Controllers.
-
-Most of time you don't need to manually define API Controllers since ABP's [Auto API Controllers](../AspNetCore/Auto-API-Controllers.md) feature creates them automagically based on your application layer. However, in case of you need to write API controllers, this is the best place to do it.
-
-* Depends on the `.Application.Contracts` project to be able to inject the application service interfaces.
-
-#### .HttpApi.Client Project
-
-This is a project that defines C# client proxies to use the HTTP APIs of the solution. You can share this library to 3rd-party clients, so they can easily consume your HTTP APIs in their Dotnet applications (For other type of applications, they can still use your APIs, either manually or using a tool in their own platform)
-
-Most of time you don't need to manually create C# client proxies, thanks to ABP's [Dynamic C# API Clients](../AspNetCore/Dynamic-CSharp-API-Clients.md) feature.
-
-`.HttpApi.Client.ConsoleTestApp` project is a console application created to demonstrate the usage of the client proxies.
-
-* Depends on the `.Application.Contracts` project to be able to share the same application service interfaces and DTOs with the remote service.
-
-> You can delete this project & dependencies if you don't need to create C# client proxies for your APIs.
-
-#### .Web Project
-
-This project contains the User Interface (UI) of the application. It contains razor pages, JavaScript files, style files, images and so on...
-
-This project contains the main `appsettings.json` file that contains the connection string and other configuration of the application.
-
-* Depends on the `.HttpApi` since UI layer needs to use APIs and application service interfaces of the solution.
-
-> If you check the source code of the `.Web.csproj` file, you will see the references to the `.Application` and the `.EntityFrameworkCore.DbMigrations` projects.
->
-> These references are actually not needed while coding your UI layer, because UI layer normally doesn't depend on the EF Core or the Application layer's implementation. This startup templates are ready for the tiered deployment, where API layer is hosted in a separate server than the UI layer.
->
-> However, if you don't choose the `--tiered` option, these references will be in the .Web project to be able to host the Web, API and application layers in a single application endpoint.
->
-> This gives you to ability to use domain entities & repositories in your presentation layer. However, this is considered as a bad practice according to the DDD.
-
-#### Test Projects
-
-The solution has multiple test projects, one for each layer:
-
-* `.Domain.Tests` is used to test the domain layer.
-* `.Application.Tests` is used to test the application layer.
-* `.EntityFrameworkCore.Tests` is used to test EF Core configuration and custom repositories.
-* `.Web.Tests` is used to test the UI.
-* `.TestBase` is a base (shared) project for all tests.
-
-In addition, `.HttpApi.Client.ConsoleTestApp` is a console application (not an automated test project) which demonstrate the usage of HTTP APIs from a Dotnet application.
-
-Test projects are prepared for integration testing;
-
-* It is fully integrated to ABP framework and all services in your application.
-* It uses SQLite in-memory database for EF Core. For MongoDB, it uses the [Mongo2Go](https://github.com/Mongo2Go/Mongo2Go) library.
-* Authorization is disabled, so any application service can be easily used in tests.
-
-You can still create unit tests for your classes which will be harder to write (because you will need to prepare mock/fake objects), but faster to run (because it only tests a single class and skips all initialization process).
-
-#### How to Run?
-
-Set `.Web` as the startup project and run the application. Default username is `admin` and password is `1q2w3E*`.
-
-See [Getting Started With the ASP.NET Core MVC Template](../Getting-Started-AspNetCore-MVC-Template.md) for more information.
-
-### Tiered Structure
-
-If you specify the `--tiered` option as described above, the solution created will be a tiered solution. The purpose of the tiered structure is to be able to **deploy Web application and HTTP API to different servers**:
-
-
-
-* Browser runs your UI by executing HTML, CSS & JavaScript.
-* Web servers hosts static UI files (CSS, JavaScript, image... etc.) & dynamic components (e.g. Razor pages). It performs HTTP requests to the API server to execute the business logic of the application.
-* API Server hosts the HTTP APIs which then use application & domain layers of the application to perform the business logic.
-* Finally, database server hosts your database.
-
-So, the resulting solution allows a 4-tiered deployment, by comparing to 3-tiered deployment of the default structure explained before.
-
-> Unless you actually need to such a 4-tiered deployment, its suggested to go with the default structure which is simpler to develop, deploy and maintain.
-
-The solution structure is shown below:
-
-
-
-As different from the default structure, two new projects come into play: `.IdentityServer` & `.HttpApi.Host`.
-
-#### .IdentityServer Project
-
-This project is used as an authentication server for other projects. `.Web` project uses OpenId Connect Authentication to get identity and access tokens for the current user from the IdentityServer. Then uses the access token to call the HTTP API server. HTTP API server uses bearer token authentication to obtain claims from the access token to authorize the current user.
-
-
-
-ABP uses the open source [IdentityServer4](https://identityserver.io/) framework for the authentication between applications. See [IdentityServer4 documentation](http://docs.identityserver.io) for details about the IdentityServer4 and OpenID Connect protocol.
-
-It has its own `appsettings.json` that contains database connection and other configurations.
-
-#### .HttpApi.Host Project
-
-This project is an application that hosts the API of the solution. It has its own `appsettings.json` that contains database connection and other configurations.
-
-#### .Web Project
-
-Just like the default structure, this project contains the User Interface (UI) of the application. It contains razor pages, JavaScript files, style files, images and so on...
-
-This project contains an `appsettings.json` file, but this time it does not have a connection string because it never connects to the database. Instead, it mainly contains endpoint of the remote API server and the authentication server.
-
-#### Pre-requirements
-
-* [Redis](https://redis.io/): The applications use Redis as as distributed cache. So, you need to have Redis installed & running.
-
-#### How to Run?
-
-You should run the application with the given order:
-
-* First, run the `.IdentityServer` since other applications depends on it.
-* Then run the `.HttpApi.Host` since it is used by the `.Web` application.
-* Finally, you can run the `.Web` project and login to the application (using `admin` as the username and `1q2w3E*` as the password).
-
-## What's Next?
-
-- See [Getting Started With the ASP.NET Core MVC Template](../Getting-Started-AspNetCore-MVC-Template.md) to create a new solution and run it for this template.
-- See the [ASP.NET Core MVC Tutorial](../Tutorials/AspNetCore-Mvc/Part-I.md) to learn how to develop applications using this template.
diff --git a/docs/en/Tutorials/Angular/Part-I.md b/docs/en/Tutorials/Angular/Part-I.md
new file mode 100644
index 00000000000..06ba6aa41d8
--- /dev/null
+++ b/docs/en/Tutorials/Angular/Part-I.md
@@ -0,0 +1,311 @@
+## Angular Tutorial - Part I
+
+### About this Tutorial
+
+In this tutorial series, you will build an application that is used to manage a list of books & their authors. **Angular** will be used as the UI framework and **MongoDB** will be used as the database provider.
+
+This is the first part of the ASP.NET Core MVC tutorial series. See all parts:
+
+- **Part I: Create the project and a book list page (this tutorial)**
+- [Part II: Create, Update and Delete books](Part-II.md)
+
+You can access to the **source code** of the application from the GitHub repository (TOD: link).
+
+### Creating the Project
+
+Create a new project named `Acme.BookStore` by selecting the Angular as the UI framework and MongoDB as the database provider, create the database and run the application by following the [Getting Started document](../../Getting-Started-Angular-Template.md).
+
+### Solution Structure (Backend)
+
+This is how the layered solution structure looks after it's created:
+
+
+
+> You can see the [Application template document](../../Startup-Templates/Application.md) to understand the solution structure in details. However, you will understand the basics with this tutorial.
+
+### Create the Book Entity
+
+Domain layer in the startup template is separated into two projects:
+
+- `Acme.BookStore.Domain` contains your [entities](../../Entities.md), [domain services](../../Domain-Services.md) and other core domain objects.
+- `Acme.BookStore.Domain.Shared` contains constants, enums or other domain related objects those can be shared with clients.
+
+Define [entities](../../Entities.md) in the **domain layer** (`Acme.BookStore.Domain` project) of the solution. The main entity of the application is the `Book`. Create a class, named `Book`, in the `Acme.BookStore.Domain` project as shown below:
+
+```C#
+using System;
+using Volo.Abp.Domain.Entities.Auditing;
+
+namespace Acme.BookStore
+{
+ public class Book : AuditedAggregateRoot
+ {
+ public string Name { get; set; }
+
+ public BookType Type { get; set; }
+
+ public DateTime PublishDate { get; set; }
+
+ public float Price { get; set; }
+ }
+}
+```
+
+- ABP has two fundamental base classes for entities: `AggregateRoot` and `Entity`. **Aggregate Root** is one of the **Domain Driven Design (DDD)** concepts. See [entity document](../../Entities.md) for details and best practices.
+- `Book` entity inherits `AuditedAggregateRoot` which adds some auditing properties (`CreationTime`, `CreatorId`, `LastModificationTime`... etc.) on top of the `AggregateRoot` class.
+- `Guid` is the **primary key type** of the `Book` entity.
+
+#### BookType Enum
+
+Define the `BookType` enum in the `Acme.BookStore.Domain.Shared` project:
+
+```C#
+namespace Acme.BookStore
+{
+ public enum BookType
+ {
+ Undefined,
+ Adventure,
+ Biography,
+ Dystopia,
+ Fantastic,
+ Horror,
+ Science,
+ ScienceFiction,
+ Poetry
+ }
+}
+```
+
+#### Add Book Entity to Your DbContext
+
+Add a `IMongoCollection` property to the `BookStoreMongoDbContext` inside the `Acme.BookStore.MongoDB` project:
+
+````csharp
+public class BookStoreMongoDbContext : AbpMongoDbContext
+{
+ public IMongoCollection Books => Collection();
+ ...
+}
+````
+
+#### Add Seed (Sample) Data
+
+This section is optional, but it would be good to have an initial data in the database in the first run. ABP provides a [data seed system](../../Data-Seeding.md). Create a class deriving from the `IDataSeedContributor` in the `.Domain` project:
+
+````csharp
+using System;
+using System.Threading.Tasks;
+using Volo.Abp.Data;
+using Volo.Abp.DependencyInjection;
+using Volo.Abp.Domain.Repositories;
+
+namespace Acme.BookStore
+{
+ public class BookStoreDataSeederContributor
+ : IDataSeedContributor, ITransientDependency
+ {
+ private readonly IRepository _bookRepository;
+
+ public BookStoreDataSeederContributor(IRepository bookRepository)
+ {
+ _bookRepository = bookRepository;
+ }
+
+ public async Task SeedAsync(DataSeedContext context)
+ {
+ if (await _bookRepository.GetCountAsync() > 0)
+ {
+ return;
+ }
+
+ await _bookRepository.InsertAsync(
+ new Book
+ {
+ Name = "1984",
+ Type = BookType.Dystopia,
+ PublishDate = new DateTime(1949, 6, 8),
+ Price = 19.84f
+ }
+ );
+
+ await _bookRepository.InsertAsync(
+ new Book
+ {
+ Name = "The Hitchhiker's Guide to the Galaxy",
+ Type = BookType.ScienceFiction,
+ PublishDate = new DateTime(1995, 9, 27),
+ Price = 42.0f
+ }
+ );
+ }
+ }
+}
+
+````
+
+`BookStoreDataSeederContributor` simply inserts two books into database if there is no book added before. ABP automatically discovers and executes this class when you seed the database by running the `Acme.BookStore.DbMigrator` project.
+
+### Create the Application Service
+
+The next step is to create an [application service](../../Application-Services.md) to manage (create, list, update, delete...) the books. Application layer in the startup template is separated into two projects:
+
+- `Acme.BookStore.Application.Contracts` mainly contains your DTOs and application service interfaces.
+- `Acme.BookStore.Application` contains the implementations of your application services.
+
+#### BookDto
+
+Create a DTO class named `BookDto` into the `Acme.BookStore.Application.Contracts` project:
+
+```C#
+using System;
+using Volo.Abp.Application.Dtos;
+
+namespace Acme.BookStore
+{
+ public class BookDto : AuditedEntityDto
+ {
+ public string Name { get; set; }
+
+ public BookType Type { get; set; }
+
+ public DateTime PublishDate { get; set; }
+
+ public float Price { get; set; }
+ }
+}
+```
+
+- **DTO** classes are used to **transfer data** between the *presentation layer* and the *application layer*. See the [Data Transfer Objects document](../../Data-Transfer-Objects.md) for more details.
+- `BookDto` is used to transfer book data to the presentation layer in order to show the book information on the UI.
+- `BookDto` is derived from the `AuditedEntityDto` which has audit properties just like the `Book` class defined above.
+
+It will be needed to convert `Book` entities to `BookDto` objects while returning books to the presentation layer. [AutoMapper](https://automapper.org) library can automate this conversion when you define the proper mapping. Startup template comes with AutoMapper configured, so you can just define the mapping in the `BookStoreApplicationAutoMapperProfile` class in the `Acme.BookStore.Application` project:
+
+```csharp
+using AutoMapper;
+
+namespace Acme.BookStore
+{
+ public class BookStoreApplicationAutoMapperProfile : Profile
+ {
+ public BookStoreApplicationAutoMapperProfile()
+ {
+ CreateMap();
+ }
+ }
+}
+```
+
+#### CreateUpdateBookDto
+
+Create a DTO class named `CreateUpdateBookDto` into the `Acme.BookStore.Application.Contracts` project:
+
+```c#
+using System;
+using System.ComponentModel.DataAnnotations;
+
+namespace Acme.BookStore
+{
+ public class CreateUpdateBookDto
+ {
+ [Required]
+ [StringLength(128)]
+ public string Name { get; set; }
+
+ [Required]
+ public BookType Type { get; set; } = BookType.Undefined;
+
+ [Required]
+ public DateTime PublishDate { get; set; }
+
+ [Required]
+ public float Price { get; set; }
+ }
+}
+```
+
+- This DTO class is used to get book information from the user interface while creating or updating a book.
+- It defines data annotation attributes (like `[Required]`) to define validations for the properties. DTOs are [automatically validated](../../Validation.md) by the ABP framework.
+
+Next, add a mapping in `BookStoreApplicationAutoMapperProfile` from the `CreateUpdateBookDto` object to the `Book` entity:
+
+```csharp
+CreateMap();
+```
+
+#### IBookAppService
+
+Define an interface named `IBookAppService` in the `Acme.BookStore.Application.Contracts` project:
+
+```C#
+using System;
+using Volo.Abp.Application.Dtos;
+using Volo.Abp.Application.Services;
+
+namespace Acme.BookStore
+{
+ public interface IBookAppService :
+ ICrudAppService< //Defines CRUD methods
+ BookDto, //Used to show books
+ Guid, //Primary key of the book entity
+ PagedAndSortedResultRequestDto, //Used for paging/sorting on getting a list of books
+ CreateUpdateBookDto, //Used to create a new book
+ CreateUpdateBookDto> //Used to update a book
+ {
+
+ }
+}
+```
+
+- Defining interfaces for application services is not required by the framework. However, it's suggested as a best practice.
+- `ICrudAppService` defines common **CRUD** methods: `GetAsync`, `GetListAsync`, `CreateAsync`, `UpdateAsync` and `DeleteAsync`. It's not required to extend it. Instead, you could inherit from the empty `IApplicationService` interface and define your own methods manually.
+- There are some variations of the `ICrudAppService` where you can use separated DTOs for each method.
+
+#### BookAppService
+
+Implement the `IBookAppService` as named `BookAppService` in the `Acme.BookStore.Application` project:
+
+```C#
+using System;
+using Volo.Abp.Application.Dtos;
+using Volo.Abp.Application.Services;
+using Volo.Abp.Domain.Repositories;
+
+namespace Acme.BookStore
+{
+ public class BookAppService :
+ CrudAppService,
+ IBookAppService
+ {
+ public BookAppService(IRepository repository)
+ : base(repository)
+ {
+
+ }
+ }
+}
+```
+
+- `BookAppService` is derived from `CrudAppService<...>` which implements all the CRUD methods defined above.
+- `BookAppService` injects `IRepository` which is the default repository for the `Book` entity. ABP automatically creates default repositories for each aggregate root (or entity). See the [repository document](../../Repositories.md).
+- `BookAppService` uses `IObjectMapper` to convert `Book` objects to `BookDto` objects and `CreateUpdateBookDto` objects to `Book` objects. The Startup template uses the [AutoMapper](http://automapper.org/) library as the object mapping provider. You defined the mappings before, so it will work as expected.
+
+### Auto API Controllers
+
+You normally create **Controllers** to expose application services as **HTTP API** endpoints. Thus allowing browser or 3rd-party clients to call them via AJAX. ABP can [**automagically**](../../AspNetCore/Auto-API-Controllers.md) configures your application services as MVC API Controllers by convention.
+
+#### Swagger UI
+
+The startup template is configured to run the [swagger UI](https://swagger.io/tools/swagger-ui/) using the [Swashbuckle.AspNetCore](https://github.com/domaindrivendev/Swashbuckle.AspNetCore) library. Run the `Acme.BookStore.HttpApi.Host` application and enter `https://localhost:XXXX/swagger/` (replace XXXX by your own port) as URL on your browser.
+
+You will see some built-in service endpoints as well as the `Book` service and its REST-style endpoints:
+
+
+
+Swagger has a nice UI to test APIs. You can try to execute the `[GET] /api/app/book` API to get a list of books.
+
+### Create the Books Page
+
+It's time to create something visible and usable!
\ No newline at end of file
diff --git a/docs/en/Tutorials/Angular/images/bookstore-backend-solution-v2.png b/docs/en/Tutorials/Angular/images/bookstore-backend-solution-v2.png
new file mode 100644
index 00000000000..79bcecb5612
Binary files /dev/null and b/docs/en/Tutorials/Angular/images/bookstore-backend-solution-v2.png differ
diff --git a/docs/en/Tutorials/Angular/images/bookstore-swagger-api.png b/docs/en/Tutorials/Angular/images/bookstore-swagger-api.png
new file mode 100644
index 00000000000..437c7725038
Binary files /dev/null and b/docs/en/Tutorials/Angular/images/bookstore-swagger-api.png differ
diff --git a/docs/en/Tutorials/AspNetCore-Mvc/Part-I.md b/docs/en/Tutorials/AspNetCore-Mvc/Part-I.md
index 8b2f84e55b7..f8bcf90aa04 100644
--- a/docs/en/Tutorials/AspNetCore-Mvc/Part-I.md
+++ b/docs/en/Tutorials/AspNetCore-Mvc/Part-I.md
@@ -10,11 +10,13 @@ This is the first part of the ASP.NET Core MVC tutorial series. See all parts:
- [Part II: Create, Update and Delete books](Part-II.md)
- [Part III: Integration Tests](Part-III.md)
-You can access to the **source code** of the application from [the GitHub repository](https://github.com/volosoft/abp/tree/master/samples/BookStore).
+You can access to the **source code** of the application from [the GitHub repository](https://github.com/abpframework/abp/tree/master/samples/BookStore).
+
+> You can also watch [this video course](https://amazingsolutions.teachable.com/p/lets-build-the-bookstore-application) prepared by an ABP community member, based on this tutorial.
### Creating the Project
-Create a new project named `Acme.BookStore`, create the database and run the application by following the [Getting Started document](../../Getting-Started-AspNetCore-MVC-Template.md).
+Create a new project named `Acme.BookStore`, create the database and run the application by following the [Getting Started document](../../Getting-Started-AspNetCore-MVC-Template.md).
### Solution Structure
@@ -22,7 +24,7 @@ This is how the layered solution structure looks after it's created:

-> You can see [MVC application template document](../../Startup-Templates/Mvc.md) to understand the solution structure in details. However, you will understand the basics with this tutorial.
+> You can see the [Application template document](../../Startup-Templates/Application.md) to understand the solution structure in details. However, you will understand the basics with this tutorial.
### Create the Book Entity
@@ -55,7 +57,6 @@ namespace Acme.BookStore
* ABP has two fundamental base classes for entities: `AggregateRoot` and `Entity`. **Aggregate Root** is one of the **Domain Driven Design (DDD)** concepts. See [entity document](../../Entities.md) for details and best practices.
* `Book` entity inherits `AuditedAggregateRoot` which adds some auditing properties (`CreationTime`, `CreatorId`, `LastModificationTime`... etc.) on top of the `AggregateRoot` class.
* `Guid` is the **primary key type** of the `Book` entity.
-* Used **data annotation attributes** in this code for EF Core mappings. Alternatively you could use EF Core's [fluent mapping API](https://docs.microsoft.com/en-us/ef/core/modeling) instead.
#### BookType Enum
@@ -99,7 +100,7 @@ Open `BookStoreDbContextModelCreatingExtensions.cs` file in the `Acme.BookStore.
builder.Entity(b =>
{
b.ToTable(BookStoreConsts.DbTablePrefix + "Books", BookStoreConsts.DbSchema);
- b.ConfigureAuditedAggregateRoot(); //auto configure for the base class props
+ b.ConfigureByConvention(); //auto configure for the base class props
b.Property(x => x.Name).IsRequired().HasMaxLength(128);
});
````
@@ -222,7 +223,7 @@ using Volo.Abp.Application.Services;
namespace Acme.BookStore
{
public interface IBookAppService :
- IAsyncCrudAppService< //Defines CRUD methods
+ ICrudAppService< //Defines CRUD methods
BookDto, //Used to show books
Guid, //Primary key of the book entity
PagedAndSortedResultRequestDto, //Used for paging/sorting on getting a list of books
@@ -235,8 +236,8 @@ namespace Acme.BookStore
````
* Defining interfaces for application services is not required by the framework. However, it's suggested as a best practice.
-* `IAsyncCrudAppService` defines common **CRUD** methods: `GetAsync`, `GetListAsync`, `CreateAsync`, `UpdateAsync` and `DeleteAsync`. It's not required to extend it. Instead, you could inherit from the empty `IApplicationService` interface and define your own methods manually.
-* There are some variations of the `IAsyncCrudAppService` where you can use separated DTOs for each method.
+* `ICrudAppService` defines common **CRUD** methods: `GetAsync`, `GetListAsync`, `CreateAsync`, `UpdateAsync` and `DeleteAsync`. It's not required to extend it. Instead, you could inherit from the empty `IApplicationService` interface and define your own methods manually.
+* There are some variations of the `ICrudAppService` where you can use separated DTOs for each method.
#### BookAppService
@@ -251,8 +252,8 @@ using Volo.Abp.Domain.Repositories;
namespace Acme.BookStore
{
public class BookAppService :
- AsyncCrudAppService,
+ CrudAppService,
IBookAppService
{
public BookAppService(IRepository repository)
@@ -264,7 +265,7 @@ namespace Acme.BookStore
}
````
-* `BookAppService` is derived from `AsyncCrudAppService<...>` which implements all the CRUD methods defined above.
+* `BookAppService` is derived from `CrudAppService<...>` which implements all the CRUD methods defined above.
* `BookAppService` injects `IRepository` which is the default repository for the `Book` entity. ABP automatically creates default repositories for each aggregate root (or entity). See the [repository document](../../Repositories.md).
* `BookAppService` uses `IObjectMapper` to convert `Book` objects to `BookDto` objects and `CreateUpdateBookDto` objects to `Book` objects. The Startup template uses the [AutoMapper](http://automapper.org/) library as the object mapping provider. You defined the mappings before, so it will work as expected.
diff --git a/docs/en/Tutorials/AspNetCore-Mvc/Part-II.md b/docs/en/Tutorials/AspNetCore-Mvc/Part-II.md
index eb583c2a519..cab941a9e94 100644
--- a/docs/en/Tutorials/AspNetCore-Mvc/Part-II.md
+++ b/docs/en/Tutorials/AspNetCore-Mvc/Part-II.md
@@ -10,6 +10,8 @@ This is the second part of the ASP.NET Core MVC tutorial series. See all parts:
You can access to the **source code** of the application from [the GitHub repository](https://github.com/volosoft/abp/tree/master/samples/BookStore).
+> You can also watch [this video course](https://amazingsolutions.teachable.com/p/lets-build-the-bookstore-application) prepared by an ABP community member, based on this tutorial.
+
### Creating a New Book
In this section, you will learn how to create a new modal dialog form to create a new book. The result dialog will be like that:
diff --git a/docs/en/Tutorials/AspNetCore-Mvc/Part-III.md b/docs/en/Tutorials/AspNetCore-Mvc/Part-III.md
index 7b97fcf4833..b115eb27ea4 100644
--- a/docs/en/Tutorials/AspNetCore-Mvc/Part-III.md
+++ b/docs/en/Tutorials/AspNetCore-Mvc/Part-III.md
@@ -10,6 +10,8 @@ This is the third part of the ASP.NET Core MVC tutorial series. See all parts:
You can access to the **source code** of the application from [the GitHub repository](https://github.com/volosoft/abp/tree/master/samples/BookStore).
+> You can also watch [this video course](https://amazingsolutions.teachable.com/p/lets-build-the-bookstore-application) prepared by an ABP community member, based on this tutorial.
+
### Test Projects in the Solution
There are multiple test projects in the solution:
diff --git a/docs/en/images/bookstore-swagger-ui-host.png b/docs/en/images/bookstore-swagger-ui-host.png
new file mode 100644
index 00000000000..74ff73f7e33
Binary files /dev/null and b/docs/en/images/bookstore-swagger-ui-host.png differ
diff --git a/docs/en/images/bookstore-visual-studio-solution-for-spa.png b/docs/en/images/bookstore-visual-studio-solution-for-spa.png
new file mode 100644
index 00000000000..5a5a283c7d6
Binary files /dev/null and b/docs/en/images/bookstore-visual-studio-solution-for-spa.png differ
diff --git a/docs/zh-Hans/Samples/Microservice-Demo.md b/docs/zh-Hans/Samples/Microservice-Demo.md
index fdf183d1b67..aaa780188d8 100644
--- a/docs/zh-Hans/Samples/Microservice-Demo.md
+++ b/docs/zh-Hans/Samples/Microservice-Demo.md
@@ -903,7 +903,7 @@ Swagger UI已配置,是此服务的默认页面. 如果你导航到URL`http://lo
````json
"ConnectionStrings": {
"Default": "Server=localhost;Database=MsDemo_Identity;Trusted_Connection=True;MultipleActiveResultSets=true",
- "Blogging": "mongodb://localhost|MsDemo_Blogging"
+ "Blogging": "mongodb://localhost/MsDemo_Blogging"
}
````
diff --git a/framework/Volo.Abp.sln b/framework/Volo.Abp.sln
index 13ef057f974..de41f864c73 100644
--- a/framework/Volo.Abp.sln
+++ b/framework/Volo.Abp.sln
@@ -236,8 +236,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Cli.Core.Tests", "
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.AspNetCore.Mvc.UI.Widgets", "src\Volo.Abp.AspNetCore.Mvc.UI.Widgets\Volo.Abp.AspNetCore.Mvc.UI.Widgets.csproj", "{EE1AAB08-3FBD-487F-B0B4-BEBA4B69528A}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.AspNetCore.Mvc.UI.Dashboards", "src\Volo.Abp.AspNetCore.Mvc.UI.Dashboards\Volo.Abp.AspNetCore.Mvc.UI.Dashboards.csproj", "{054D766D-5992-460E-A4D8-936D80BE2C1A}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Ldap", "src\Volo.Abp.Ldap\Volo.Abp.Ldap.csproj", "{4DADBBD2-4C63-4C90-9661-EBF6252A7D6F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Ldap.Tests", "test\Volo.Abp.Ldap.Tests\Volo.Abp.Ldap.Tests.csproj", "{38FB8F75-426E-4265-8D0E-E121837B6FCC}"
@@ -246,9 +244,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Dapper", "src\Volo
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Dapper.Tests", "test\Volo.Abp.Dapper.Tests\Volo.Abp.Dapper.Tests.csproj", "{E026A085-D881-4AE0-9F08-422AC3903BD7}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.MailKit", "src\Volo.Abp.MailKit\Volo.Abp.MailKit.csproj", "{0CAED4CC-1CFD-4092-A326-AFE4DB3A9AB4}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.MailKit", "src\Volo.Abp.MailKit\Volo.Abp.MailKit.csproj", "{0CAED4CC-1CFD-4092-A326-AFE4DB3A9AB4}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.MailKit.Tests", "test\Volo.Abp.MailKit.Tests\Volo.Abp.MailKit.Tests.csproj", "{70DD6E17-B98B-4B00-8F38-C489E291BB53}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.MailKit.Tests", "test\Volo.Abp.MailKit.Tests\Volo.Abp.MailKit.Tests.csproj", "{70DD6E17-B98B-4B00-8F38-C489E291BB53}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -716,10 +714,6 @@ Global
{EE1AAB08-3FBD-487F-B0B4-BEBA4B69528A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EE1AAB08-3FBD-487F-B0B4-BEBA4B69528A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EE1AAB08-3FBD-487F-B0B4-BEBA4B69528A}.Release|Any CPU.Build.0 = Release|Any CPU
- {054D766D-5992-460E-A4D8-936D80BE2C1A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {054D766D-5992-460E-A4D8-936D80BE2C1A}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {054D766D-5992-460E-A4D8-936D80BE2C1A}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {054D766D-5992-460E-A4D8-936D80BE2C1A}.Release|Any CPU.Build.0 = Release|Any CPU
{4DADBBD2-4C63-4C90-9661-EBF6252A7D6F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4DADBBD2-4C63-4C90-9661-EBF6252A7D6F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4DADBBD2-4C63-4C90-9661-EBF6252A7D6F}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -864,7 +858,6 @@ Global
{D078553A-C70C-4F56-B3E2-9C5BA6384C72} = {447C8A77-E5F0-4538-8687-7383196D04EA}
{F006B0B4-F25D-4511-9FB3-F17AA44BDCEA} = {447C8A77-E5F0-4538-8687-7383196D04EA}
{EE1AAB08-3FBD-487F-B0B4-BEBA4B69528A} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
- {054D766D-5992-460E-A4D8-936D80BE2C1A} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{4DADBBD2-4C63-4C90-9661-EBF6252A7D6F} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{38FB8F75-426E-4265-8D0E-E121837B6FCC} = {447C8A77-E5F0-4538-8687-7383196D04EA}
{D863A3C3-CC1D-426F-BDD4-02E7AE2A3170} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
diff --git a/framework/Volo.Abp.sln.DotSettings b/framework/Volo.Abp.sln.DotSettings
index b1de84b3696..925b5c212a5 100644
--- a/framework/Volo.Abp.sln.DotSettings
+++ b/framework/Volo.Abp.sln.DotSettings
@@ -5,4 +5,5 @@
..\..\common.DotSettingsTrue1
+ True
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundlerBase.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundlerBase.cs
index 50b81d92362..90e86068338 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundlerBase.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundlerBase.cs
@@ -1,3 +1,4 @@
+using System;
using System.Text;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Logging;
@@ -29,27 +30,44 @@ public BundleResult Bundle(IBundlerContext context)
{
Logger.LogInformation($"Bundling {context.BundleRelativePath} ({context.ContentFiles.Count} files)");
- var sb = new StringBuilder();
+ var bundleContentBuilder = new StringBuilder();
Logger.LogDebug("Bundle files:");
foreach (var file in context.ContentFiles)
{
- var fileContent = GetFileContent(context, file);
- Logger.LogDebug($"- {file} ({fileContent.Length} bytes)");
- sb.AppendLine(fileContent);
+ AddFileToBundle(context, bundleContentBuilder, file);
}
- var bundleContent = sb.ToString();
+ var bundleContent = bundleContentBuilder.ToString();
+ Logger.LogInformation($"Bundled {context.BundleRelativePath} ({bundleContent.Length} bytes)");
- if (context.IsMinificationEnabled)
+ return new BundleResult(bundleContent);
+ }
+
+ private void AddFileToBundle(IBundlerContext context, StringBuilder bundleContentBuilder, string fileName)
+ {
+ if (context.IsMinificationEnabled && !IsMinFile(fileName))
{
- Logger.LogInformation($"Minifying {context.BundleRelativePath} ({bundleContent.Length} bytes)");
- bundleContent = Minifier.Minify(bundleContent, context.BundleRelativePath);
+ var minFileInfo = GetMinFileInfoOrNull(fileName);
+ if (minFileInfo != null)
+ {
+ Logger.LogDebug($"- {fileName} ({minFileInfo.Length} bytes)");
+ bundleContentBuilder.Append(NormalizedCode(minFileInfo.ReadAsString()));
+ return;
+ }
}
- Logger.LogInformation($"Bundled {context.BundleRelativePath} ({bundleContent.Length} bytes)");
+ var fileContent = GetFileContent(context, fileName);
+ Logger.LogDebug($"- {fileName} ({fileContent.Length} bytes)");
- return new BundleResult(bundleContent);
+ if (context.IsMinificationEnabled)
+ {
+ var nonMinifiedSize = fileContent.Length;
+ fileContent = Minifier.Minify(fileContent, context.BundleRelativePath);
+ Logger.LogInformation($" -- Minified {context.BundleRelativePath} ({nonMinifiedSize} bytes -> {fileContent.Length} bytes)");
+ }
+
+ bundleContentBuilder.Append(NormalizedCode(fileContent));
}
protected virtual string GetFileContent(IBundlerContext context, string file)
@@ -68,5 +86,22 @@ protected virtual IFileInfo GetFileInfo(IBundlerContext context, string file)
return fileInfo;
}
+
+ protected virtual bool IsMinFile(string fileName)
+ {
+ return fileName.EndsWith($".min.{FileExtension}", StringComparison.InvariantCultureIgnoreCase);
+ }
+
+ protected virtual IFileInfo GetMinFileInfoOrNull(string file)
+ {
+ var fileInfo = WebContentFileProvider.GetFileInfo($"{file.RemovePostFix($".{FileExtension}")}.min.{FileExtension}");
+
+ return fileInfo.Exists ? fileInfo : null;
+ }
+
+ protected virtual string NormalizedCode(string code)
+ {
+ return code;
+ }
}
}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/Scripts/ScriptBundler.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/Scripts/ScriptBundler.cs
index e65379fc748..f5447dc8a3b 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/Scripts/ScriptBundler.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/Scripts/ScriptBundler.cs
@@ -1,3 +1,4 @@
+using System;
using Volo.Abp.AspNetCore.Mvc.UI.Minification.Scripts;
using Volo.Abp.AspNetCore.VirtualFileSystem;
@@ -11,5 +12,10 @@ public ScriptBundler(IWebContentFileProvider webContentFileProvider, IJavascript
: base(webContentFileProvider, minifier)
{
}
+
+ protected override string NormalizedCode(string code)
+ {
+ return code.EnsureEndsWith(';') + Environment.NewLine;
+ }
}
}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperScriptService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperScriptService.cs
index 52e05776e87..b4f2ba1b772 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperScriptService.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperScriptService.cs
@@ -39,7 +39,7 @@ protected override IReadOnlyList GetBundleFiles(string bundleName)
protected override void AddHtmlTag(TagHelperContext context, TagHelperOutput output, string file)
{
- output.Content.AppendHtml($"{Environment.NewLine}");
+ output.Content.AppendHtml($"{Environment.NewLine}");
}
}
}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperStyleService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperStyleService.cs
index 4a798fe0513..2a0946cfd2a 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperStyleService.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperStyleService.cs
@@ -11,12 +11,12 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.TagHelpers
public class AbpTagHelperStyleService : AbpTagHelperResourceService
{
public AbpTagHelperStyleService(
- IBundleManager bundleManager,
+ IBundleManager bundleManager,
IWebContentFileProvider webContentFileProvider,
IOptions options,
IHostingEnvironment hostingEnvironment
) : base(
- bundleManager,
+ bundleManager,
webContentFileProvider,
options,
hostingEnvironment)
@@ -39,7 +39,7 @@ protected override IReadOnlyList GetBundleFiles(string bundleName)
protected override void AddHtmlTag(TagHelperContext context, TagHelperOutput output, string file)
{
- output.Content.AppendHtml($"{Environment.NewLine}");
+ output.Content.AppendHtml($"{Environment.NewLine}");
}
}
}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Pages/Components/Dashboard/Default.js b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Pages/Components/Dashboard/Default.js
deleted file mode 100644
index 272fc751eb5..00000000000
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Pages/Components/Dashboard/Default.js
+++ /dev/null
@@ -1,6 +0,0 @@
-(function ($) {
- $('#GlobalRefreshButton').on('click',
- function () {
- $(document).trigger('RefreshWidgets', $('#DashboardGlobalFiltersForm').serializeFormToObject());
- });
-})(jQuery);
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Properties/launchSettings.json b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Properties/launchSettings.json
deleted file mode 100644
index 0ce91a367d4..00000000000
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Properties/launchSettings.json
+++ /dev/null
@@ -1,27 +0,0 @@
-{
- "iisSettings": {
- "windowsAuthentication": false,
- "anonymousAuthentication": true,
- "iisExpress": {
- "applicationUrl": "http://localhost:57333/",
- "sslPort": 0
- }
- },
- "profiles": {
- "IIS Express": {
- "commandName": "IISExpress",
- "launchBrowser": true,
- "environmentVariables": {
- "ASPNETCORE_ENVIRONMENT": "Development"
- }
- },
- "Volo.Abp.AspNetCore.Mvc.UI.Dashboards": {
- "commandName": "Project",
- "launchBrowser": true,
- "environmentVariables": {
- "ASPNETCORE_ENVIRONMENT": "Development"
- },
- "applicationUrl": "http://localhost:57343/"
- }
- }
-}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo.Abp.AspNetCore.Mvc.UI.Dashboards.csproj b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo.Abp.AspNetCore.Mvc.UI.Dashboards.csproj
deleted file mode 100644
index 69c8816688d..00000000000
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo.Abp.AspNetCore.Mvc.UI.Dashboards.csproj
+++ /dev/null
@@ -1,38 +0,0 @@
-
-
-
-
-
- netstandard2.0
- Volo.Abp.AspNetCore.Mvc.UI.Dashboards
- Volo.Abp.AspNetCore.Mvc.UI.Dashboards
- $(AssetTargetFallback);portable-net45+win8+wp8+wpa81;
- false
- false
- false
- true
- Library
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/AbpAspNetCoreMvcUiDashboardsModule.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/AbpAspNetCoreMvcUiDashboardsModule.cs
deleted file mode 100644
index 6fcb11d28ee..00000000000
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/AbpAspNetCoreMvcUiDashboardsModule.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
-using Volo.Abp.Modularity;
-using Volo.Abp.VirtualFileSystem;
-
-namespace Volo.Abp.AspNetCore.Mvc.UI.Dashboards
-{
- [DependsOn(
- typeof(AbpAspNetCoreMvcUiWidgetsModule)
- )]
- public class AbpAspNetCoreMvcUiDashboardsModule : AbpModule
- {
- public override void ConfigureServices(ServiceConfigurationContext context)
- {
- Configure(options =>
- {
- options.FileSets.AddEmbedded();
- });
- }
- }
-}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/AbpBasicDashboardScriptContributor.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/AbpBasicDashboardScriptContributor.cs
deleted file mode 100644
index 83f5910d68c..00000000000
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/AbpBasicDashboardScriptContributor.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
-
-namespace Volo.Abp.AspNetCore.Mvc.UI.Dashboards
-{
- public class AbpBasicDashboardScriptContributor : BundleContributor
- {
- public override void ConfigureBundle(BundleConfigurationContext context)
- {
- context.Files.Add("/Pages/Components/Dashboard/Default.js");
- }
- }
-}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/AbpBasicDashboardStyleContributor.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/AbpBasicDashboardStyleContributor.cs
deleted file mode 100644
index 5c074783fc2..00000000000
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/AbpBasicDashboardStyleContributor.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
-
-namespace Volo.Abp.AspNetCore.Mvc.UI.Dashboards
-{
- public class AbpBasicDashboardStyleContributor : BundleContributor
- {
- }
-}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/Components/Dashboard/DashboardViewComponent.cshtml.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/Components/Dashboard/DashboardViewComponent.cshtml.cs
deleted file mode 100644
index dd6bdbb2063..00000000000
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/Components/Dashboard/DashboardViewComponent.cshtml.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.Extensions.Options;
-using System.Linq;
-using System.Threading.Tasks;
-using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
-
-namespace Volo.Abp.AspNetCore.Mvc.UI.Dashboards.Components.Dashboard
-{
- public class DashboardViewComponent : AbpViewComponent
- {
- private readonly DashboardOptions _dashboardOptions;
- private readonly WidgetOptions _widgetOptions;
- private readonly GlobalFilterOptions _globalFilterOptions;
-
- public DashboardViewComponent(IOptions dashboardOptions, IOptions widgetOptions, IOptions globalFilterOptions)
- {
- _dashboardOptions = dashboardOptions.Value;
- _widgetOptions = widgetOptions.Value;
- _globalFilterOptions = globalFilterOptions.Value;
- }
-
- public IViewComponentResult Invoke(string dashboardName)
- {
- var dashboard = _dashboardOptions.Dashboards.Single(d => d.Name.Equals(dashboardName));
-
- var model = new DashboardViewModel(dashboard, _widgetOptions.Widgets.GetAll().ToList(), _globalFilterOptions.GlobalFilters);
-
- return View("~/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/Components/Dashboard/Default.cshtml", model);
- }
- }
-}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/Components/Dashboard/DashboardViewModel.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/Components/Dashboard/DashboardViewModel.cs
deleted file mode 100644
index c7caae2dc49..00000000000
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/Components/Dashboard/DashboardViewModel.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Authorization;
-using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
-
-namespace Volo.Abp.AspNetCore.Mvc.UI.Dashboards.Components.Dashboard
-{
- public class DashboardViewModel
- {
- public DashboardDefinition Dashboard { get; set; }
-
- public List Widgets { get; set; }
-
- public List GlobalFilters { get; set; }
-
- public DashboardViewModel(DashboardDefinition dashboard, List widgets, List globalFilters)
- {
- Dashboard = dashboard;
- Widgets = widgets;
- GlobalFilters = globalFilters;
- }
-
- public WidgetDefinition GetWidget(string name)
- {
- return Widgets.Single(d => d.Name.Equals(name));
- }
-
- public GlobalFilterDefinition GetGlobalFilter(string name)
- {
- return GlobalFilters.Single(d => d.Name.Equals(name));
- }
-
- public async Task CheckPermissionsAsync(IAuthorizationService authorizationService, WidgetDefinition widget)
- {
- foreach (var permission in widget.RequiredPolicies)
- {
- if (!await authorizationService.IsGrantedAsync(permission))
- {
- return false;
- }
- }
-
- return true;
- }
- }
-}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/Components/Dashboard/Default.cshtml b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/Components/Dashboard/Default.cshtml
deleted file mode 100644
index 1f51c44d44e..00000000000
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/Components/Dashboard/Default.cshtml
+++ /dev/null
@@ -1,53 +0,0 @@
-@using Localization.Resources.AbpUi
-@using Microsoft.AspNetCore.Authorization
-@using Microsoft.AspNetCore.Mvc.Localization
-@using Microsoft.Extensions.Localization
-@using Volo.Abp.AspNetCore.Mvc.UI.Dashboards
-@using Volo.Abp.AspNetCore.Mvc.UI.Widgets
-@inject IAuthorizationService AuthorizationService
-@inject IGlobalFilterRenderer GlobalFilteRenderer
-@inject IHtmlLocalizer L
-@model Volo.Abp.AspNetCore.Mvc.UI.Dashboards.Components.Dashboard.DashboardViewModel
-@{
-}
-
-
- @if (Model.Dashboard.AvailableGlobalFilters.Any())
- {
-
-
-
-
-
-
-
-
- }
-
-
-
- @foreach (var widgetConfiguration in Model.Dashboard.AvailableWidgets)
- {
- var widgetDefinition = Model.GetWidget(widgetConfiguration.WidgetName);
- if (await Model.CheckPermissionsAsync(AuthorizationService, widgetDefinition))
- {
- widgetDefinition.DefaultDimensions = widgetConfiguration.Dimensions ?? widgetDefinition.DefaultDimensions ?? new WidgetDimensions(6, 4);
-
-
-
- @await Component.InvokeAsync(widgetDefinition.Name)
-
- }
- }
-
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/DashboardDefinition.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/DashboardDefinition.cs
deleted file mode 100644
index ff06fa0d44b..00000000000
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/DashboardDefinition.cs
+++ /dev/null
@@ -1,62 +0,0 @@
-using System.Collections.Generic;
-using JetBrains.Annotations;
-using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
-using Volo.Abp.Localization;
-
-namespace Volo.Abp.AspNetCore.Mvc.UI.Dashboards
-{
- public class DashboardDefinition
- {
- ///
- /// Unique name of the dashboard.
- ///
- [NotNull]
- public string Name { get; }
-
- ///
- /// A list of Widgets available for this dashboard.
- ///
- public List AvailableWidgets { get; }
-
- ///
- /// A list of Global Filters available for this dashboard.
- ///
- public List AvailableGlobalFilters { get; }
-
- ///
- /// Display name of the dashboard.
- ///
- [NotNull]
- public ILocalizableString DisplayName
- {
- get => _displayName;
- set => _displayName = Check.NotNull(value, nameof(value));
- }
- private ILocalizableString _displayName;
-
- public DashboardDefinition(
- [NotNull] string name,
- [CanBeNull] ILocalizableString displayName)
- {
- Name = Check.NotNullOrWhiteSpace(name, nameof(name));
- DisplayName = displayName ?? new FixedLocalizableString(name);
-
- AvailableWidgets = new List();
- AvailableGlobalFilters = new List();
- }
-
- public DashboardDefinition WithWidget(string widgetName, WidgetLocation location = null, WidgetDimensions dimensions = null)
- {
- AvailableWidgets.Add( new DashboardWidgetConfiguration(widgetName, dimensions, location));
-
- return this;
- }
-
- public DashboardDefinition WithGlobalFilter(string globalFilterName)
- {
- AvailableGlobalFilters.Add( new DashboardGlobalFilterConfiguration(globalFilterName));
-
- return this;
- }
- }
-}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/DashboardGlobalFilterConfiguration.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/DashboardGlobalFilterConfiguration.cs
deleted file mode 100644
index 5f097270ca5..00000000000
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/DashboardGlobalFilterConfiguration.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using JetBrains.Annotations;
-
-namespace Volo.Abp.AspNetCore.Mvc.UI.Dashboards
-{
- public class DashboardGlobalFilterConfiguration
- {
- [NotNull]
- public string GlobalFilterName { get; }
-
- public DashboardGlobalFilterConfiguration([NotNull] string globalFilterName)
- {
- GlobalFilterName = Check.NotNullOrWhiteSpace(globalFilterName, nameof(globalFilterName));
- }
- }
-}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/DashboardOptions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/DashboardOptions.cs
deleted file mode 100644
index fc3ca09b74c..00000000000
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/DashboardOptions.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using System.Collections.Generic;
-
-namespace Volo.Abp.AspNetCore.Mvc.UI.Dashboards
-{
- public class DashboardOptions
- {
- public List Dashboards { get; }
-
- public DashboardOptions()
- {
- Dashboards = new List();
- }
- }
-}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/DashboardRenderer.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/DashboardRenderer.cs
deleted file mode 100644
index d35c58f0e24..00000000000
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/DashboardRenderer.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Html;
-using Microsoft.AspNetCore.Mvc;
-using Volo.Abp.AspNetCore.Mvc.UI.Dashboards.Components.Dashboard;
-
-namespace Volo.Abp.AspNetCore.Mvc.UI.Dashboards
-{
- public class DashboardRenderer : IDashboardRenderer
- {
- public async Task RenderAsync(IViewComponentHelper componentHelper, object args = null)
- {
- return await componentHelper.InvokeAsync(typeof(DashboardViewComponent), args ?? new object());
- }
- }
-}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/DashboardWidgetConfiguration.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/DashboardWidgetConfiguration.cs
deleted file mode 100644
index 74a20bc877b..00000000000
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/DashboardWidgetConfiguration.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-using JetBrains.Annotations;
-using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
-
-namespace Volo.Abp.AspNetCore.Mvc.UI.Dashboards
-{
- public class DashboardWidgetConfiguration
- {
- [NotNull]
- public string WidgetName { get; }
-
- [CanBeNull]
- public WidgetDimensions Dimensions { get; set; }
-
- [CanBeNull]
- public WidgetLocation Location { get; set; }
-
- public DashboardWidgetConfiguration(
- [NotNull] string widgetName,
- [CanBeNull] WidgetDimensions dimensions = null,
- [CanBeNull] WidgetLocation location = null
- )
- {
- WidgetName = Check.NotNullOrWhiteSpace(widgetName, nameof(widgetName));
- Dimensions = dimensions;
- Location = location;
- }
- }
-}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/GlobalFilterDefinition.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/GlobalFilterDefinition.cs
deleted file mode 100644
index f599f0c797b..00000000000
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/GlobalFilterDefinition.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-using System;
-using JetBrains.Annotations;
-using Volo.Abp.Localization;
-
-namespace Volo.Abp.AspNetCore.Mvc.UI.Dashboards
-{
- public class GlobalFilterDefinition
- {
- ///
- /// Unique name of the Global Filter.
- ///
- [NotNull]
- public string Name { get; }
-
- ///
- /// Display name of the Global Filter.
- ///
- [NotNull]
- public ILocalizableString DisplayName
- {
- get => _displayName;
- set => _displayName = Check.NotNull(value, nameof(value));
- }
- private ILocalizableString _displayName;
-
- [NotNull]
- public Type ViewComponentType { get; }
-
- public GlobalFilterDefinition(
- [NotNull] string name,
- [CanBeNull] ILocalizableString displayName,
- [NotNull] Type viewComponentType)
- {
- Name = Check.NotNullOrWhiteSpace(name, nameof(name));
- DisplayName = displayName ?? new FixedLocalizableString(name);
- ViewComponentType = Check.NotNull(viewComponentType, nameof(viewComponentType));
- }
- }
-}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/GlobalFilterOptions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/GlobalFilterOptions.cs
deleted file mode 100644
index 70ba40e696c..00000000000
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/GlobalFilterOptions.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using System.Collections.Generic;
-
-namespace Volo.Abp.AspNetCore.Mvc.UI.Dashboards
-{
- public class GlobalFilterOptions
- {
- public List GlobalFilters { get; }
-
- public GlobalFilterOptions()
- {
- GlobalFilters = new List();
- }
- }
-}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/GlobalFilterRenderer.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/GlobalFilterRenderer.cs
deleted file mode 100644
index d1219943ae2..00000000000
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/GlobalFilterRenderer.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using System.Linq;
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Html;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.Extensions.Options;
-
-namespace Volo.Abp.AspNetCore.Mvc.UI.Dashboards
-{
- public class GlobalFilterRenderer : IGlobalFilterRenderer
- {
- private readonly GlobalFilterOptions _globalFilterOptions;
-
- public GlobalFilterRenderer(IOptions widgetOptions)
- {
- _globalFilterOptions = widgetOptions.Value;
- }
-
- public async Task RenderAsync(IViewComponentHelper componentHelper, string globalFilterName, object args = null)
- {
- var globalFilter = _globalFilterOptions.GlobalFilters.Single(w => w.Name.Equals(globalFilterName));
-
- return await componentHelper.InvokeAsync(globalFilter.ViewComponentType, args ?? new object());
- }
- }
-}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/IDashboardRenderer.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/IDashboardRenderer.cs
deleted file mode 100644
index 7cfed80135d..00000000000
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/IDashboardRenderer.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Html;
-using Microsoft.AspNetCore.Mvc;
-using Volo.Abp.DependencyInjection;
-
-namespace Volo.Abp.AspNetCore.Mvc.UI.Dashboards
-{
- public interface IDashboardRenderer : ITransientDependency
- {
- Task RenderAsync(IViewComponentHelper componentHelper, object args = null);
- }
-}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/IGlobalFilterRenderer.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/IGlobalFilterRenderer.cs
deleted file mode 100644
index 6978f4d6b18..00000000000
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/IGlobalFilterRenderer.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Html;
-using Microsoft.AspNetCore.Mvc;
-using Volo.Abp.DependencyInjection;
-
-namespace Volo.Abp.AspNetCore.Mvc.UI.Dashboards
-{
- public interface IGlobalFilterRenderer : ITransientDependency
- {
- Task RenderAsync(IViewComponentHelper componentHelper, string globalFilterName, object args = null);
- }
-}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/WidgetLocation.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/WidgetLocation.cs
deleted file mode 100644
index 7ce21a342ea..00000000000
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/WidgetLocation.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-namespace Volo.Abp.AspNetCore.Mvc.UI.Dashboards
-{
- public class WidgetLocation
- {
- public int X { get; set; }
-
- public int Y { get; set; }
-
- public WidgetLocation(int x, int y)
- {
- X = x;
- Y = y;
- }
- }
-}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/_ViewImports.cshtml b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/_ViewImports.cshtml
deleted file mode 100644
index 225780c2c2c..00000000000
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/_ViewImports.cshtml
+++ /dev/null
@@ -1,3 +0,0 @@
-@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
-@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bootstrap
-@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bundling
\ No newline at end of file
diff --git a/samples/DashboardDemo/src/DashboardDemo.Web/Bundles/ChartjsScriptContributor.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/ChartJs/ChartjsScriptContributor.cs
similarity index 84%
rename from samples/DashboardDemo/src/DashboardDemo.Web/Bundles/ChartjsScriptContributor.cs
rename to framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/ChartJs/ChartjsScriptContributor.cs
index eaacdc0e7a4..6c55d787ee7 100644
--- a/samples/DashboardDemo/src/DashboardDemo.Web/Bundles/ChartjsScriptContributor.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/ChartJs/ChartjsScriptContributor.cs
@@ -1,6 +1,6 @@
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
-namespace DashboardDemo.Web.Bundles
+namespace Volo.Abp.AspNetCore.Mvc.UI.Packages.ChartJs
{
public class ChartjsScriptContributor : BundleContributor
{
diff --git a/samples/DashboardDemo/src/DashboardDemo.Web/Bundles/ChartjsStyleContributor.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/ChartJs/ChartjsStyleContributor.cs
similarity index 84%
rename from samples/DashboardDemo/src/DashboardDemo.Web/Bundles/ChartjsStyleContributor.cs
rename to framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/ChartJs/ChartjsStyleContributor.cs
index 144baaab125..1278023e976 100644
--- a/samples/DashboardDemo/src/DashboardDemo.Web/Bundles/ChartjsStyleContributor.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/ChartJs/ChartjsStyleContributor.cs
@@ -1,6 +1,6 @@
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
-namespace DashboardDemo.Web.Bundles
+namespace Volo.Abp.AspNetCore.Mvc.UI.Packages.ChartJs
{
public class ChartjsStyleContributor : BundleContributor
{
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Account.cshtml b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Account.cshtml
index 2e95453185b..eb69a76abac 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Account.cshtml
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Account.cshtml
@@ -79,8 +79,8 @@
-
-
+
+
@await RenderSectionAsync("scripts", false)
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Application.cshtml b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Application.cshtml
index 9bb54dea09f..d3fecf97272 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Application.cshtml
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Application.cshtml
@@ -64,8 +64,8 @@
-
-
+
+
@await Component.InvokeAsync(typeof(WidgetScriptsViewComponent))
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Empty.cshtml b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Empty.cshtml
index f4f18202c7d..1fe4639d25a 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Empty.cshtml
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Empty.cshtml
@@ -58,8 +58,8 @@
-
-
+
+
@await RenderSectionAsync("scripts", false)
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Bundling/SharedThemeGlobalScriptContributor.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Bundling/SharedThemeGlobalScriptContributor.cs
index 83aa8506d54..e722de8ee35 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Bundling/SharedThemeGlobalScriptContributor.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Bundling/SharedThemeGlobalScriptContributor.cs
@@ -35,6 +35,7 @@ public override void ConfigureBundle(BundleConfigurationContext context)
{
"/libs/abp/aspnetcore-mvc-ui-theme-shared/jquery/jquery-extensions.js",
"/libs/abp/aspnetcore-mvc-ui-theme-shared/jquery-form/jquery-form-extensions.js",
+ "/libs/abp/aspnetcore-mvc-ui-theme-shared/jquery/widget-manager.js",
"/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/dom-event-handlers.js",
"/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/modal-manager.js",
"/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-extensions.js",
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/jquery/jquery-extensions.js b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/jquery/jquery-extensions.js
index b3fa839d105..44c2bdad27b 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/jquery/jquery-extensions.js
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/jquery/jquery-extensions.js
@@ -160,7 +160,7 @@
formSaved = true;
$modal.modal('hide');
}
- });
+ }, false);
}
});
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/jquery/widget-manager.js b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/jquery/widget-manager.js
new file mode 100644
index 00000000000..0b746b5e37a
--- /dev/null
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/jquery/widget-manager.js
@@ -0,0 +1,107 @@
+(function ($) {
+ abp.widgets = abp.widgets || {};
+
+ abp.WidgetManager = function (opts) {
+ if (!opts) {
+ opts = {};
+ } else if (typeof opts === 'string') {
+ opts = {
+ wrapper: opts
+ };
+ }
+
+ if (!opts.wrapper) {
+ opts.wrapper = $('body');
+ } else if (typeof opts.wrapper === 'string') {
+ opts.wrapper = $(opts.wrapper);
+ }
+
+ if (!opts.filterForm) {
+ var widgetFilterAttr = opts.wrapper.attr('data-widget-filter');
+ if (widgetFilterAttr) {
+ opts.filterForm = $(widgetFilterAttr);
+ }
+ } else if (typeof opts.filterForm === 'string') {
+ opts.filterForm = $(opts.filterForm);
+ }
+
+ var getFilters = function ($widgetWrapperDiv) {
+ var filters = {};
+
+ if (opts.filterForm) {
+ opts.filterForm.each(function() {
+ filters = $.extend(filters, opts.filterForm.serializeFormToObject());
+ });
+ }
+
+ if (opts.filterCallback) {
+ filters = $.extend(filters, opts.filterCallback());
+ }
+
+ var widgetApi = $widgetWrapperDiv.data('abp-widget-api');
+ if (widgetApi && widgetApi.getFilters) {
+ filters = $.extend(filters, widgetApi.getFilters());
+ }
+
+ return filters;
+ };
+
+ var init = function () {
+ opts.wrapper.find('.abp-widget-wrapper').each(function () {
+ var $widgetWrapperDiv = $(this);
+ var widgetName = $widgetWrapperDiv.attr('data-widget-name');
+ var widgetApiClass = abp.widgets[widgetName];
+ if (widgetApiClass) {
+ var widgetApi = new widgetApiClass($widgetWrapperDiv);
+ $widgetWrapperDiv.data('abp-widget-api', widgetApi);
+ if (widgetApi.init) {
+ widgetApi.init(getFilters($widgetWrapperDiv));
+ }
+ }
+ });
+ };
+
+ var refresh = function () {
+ opts.wrapper.find('.abp-widget-wrapper').each(function () {
+ var $widgetWrapperDiv = $(this);
+
+ var refreshUrl = $widgetWrapperDiv.attr('data-refresh-url');
+ if (refreshUrl) {
+ abp.ajax({
+ url: refreshUrl,
+ type: 'GET',
+ dataType: 'html',
+ contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
+ data: getFilters($widgetWrapperDiv)
+ }).then(function (result) {
+ $widgetWrapperDiv.replaceWith($(result));
+ });
+ } else {
+ var widgetApi = $widgetWrapperDiv.data('abp-widget-api');
+ if (widgetApi && widgetApi.refresh) {
+ widgetApi.refresh(getFilters($widgetWrapperDiv));
+ }
+ }
+ });
+ };
+
+ if (opts.filterForm) {
+ opts.filterForm.each(function() {
+ $(this).submit(function (e) {
+ e.preventDefault();
+ refresh();
+ });
+ });
+ }
+
+ var publicApi = {
+ init: init,
+ refresh: refresh
+ };
+
+ opts.wrapper.data('abp-widget-manager', publicApi);
+
+ return publicApi;
+ };
+
+})(jQuery);
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/sweetalert/abp-sweetalert.js b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/sweetalert/abp-sweetalert.js
index c96f836f0d1..0ed84609668 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/sweetalert/abp-sweetalert.js
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/sweetalert/abp-sweetalert.js
@@ -73,17 +73,21 @@
return showMessage('error', message, title);
};
- abp.message.confirm = function (message, titleOrCallback, callback) {
+ abp.message.confirm = function (message, titleOrCallback, callback, closeOnEsc) {
+
var userOpts = {
text: message
};
if ($.isFunction(titleOrCallback)) {
+ closeOnEsc = callback;
callback = titleOrCallback;
} else if (titleOrCallback) {
userOpts.title = titleOrCallback;
};
+ userOpts.closeOnEsc = closeOnEsc;
+
var opts = $.extend(
{},
abp.libs.sweetAlert.config['default'],
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Widgets/Volo/Abp/AspNetCore/Mvc/UI/Widgets/AbpViewComponentHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Widgets/Volo/Abp/AspNetCore/Mvc/UI/Widgets/AbpViewComponentHelper.cs
index 8d0dede237f..1ef15ac8e66 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Widgets/Volo/Abp/AspNetCore/Mvc/UI/Widgets/AbpViewComponentHelper.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Widgets/Volo/Abp/AspNetCore/Mvc/UI/Widgets/AbpViewComponentHelper.cs
@@ -1,16 +1,13 @@
using System;
-using System.Linq;
+using System.Text;
using System.Threading.Tasks;
-using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewComponents;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.Extensions.Options;
-using Volo.Abp.Authorization;
using Volo.Abp.DependencyInjection;
-using Volo.Abp.Users;
namespace Volo.Abp.AspNetCore.Mvc.UI.Widgets
{
@@ -19,21 +16,15 @@ public class AbpViewComponentHelper : IViewComponentHelper, IViewContextAware, I
{
protected WidgetOptions Options { get; }
protected IPageWidgetManager PageWidgetManager { get; }
- protected IAuthorizationService AuthorizationService { get; }
- protected ICurrentUser CurrentUser { get; }
protected DefaultViewComponentHelper DefaultViewComponentHelper { get; }
public AbpViewComponentHelper(
DefaultViewComponentHelper defaultViewComponentHelper,
IOptions widgetOptions,
- IPageWidgetManager pageWidgetManager,
- IAuthorizationService authorizationService,
- ICurrentUser currentUser)
+ IPageWidgetManager pageWidgetManager)
{
DefaultViewComponentHelper = defaultViewComponentHelper;
PageWidgetManager = pageWidgetManager;
- AuthorizationService = authorizationService;
- CurrentUser = currentUser;
Options = widgetOptions.Value;
}
@@ -66,22 +57,17 @@ public virtual void Contextualize(ViewContext viewContext)
protected virtual async Task InvokeWidgetAsync(object arguments, WidgetDefinition widget)
{
- if (widget.RequiredPolicies.Any())
- {
- foreach (var requiredPolicy in widget.RequiredPolicies)
- {
- await AuthorizationService.AuthorizeAsync(requiredPolicy);
- }
- }
- else if (widget.RequiresAuthentication && !CurrentUser.IsAuthenticated)
- {
- throw new AbpAuthorizationException("Authorization failed! User has not logged in.");
- }
-
PageWidgetManager.TryAdd(widget);
+ var wrapperAttributesBuilder = new StringBuilder($"class=\"abp-widget-wrapper\" data-widget-name=\"{widget.Name}\"");
+
+ if (widget.RefreshUrl != null)
+ {
+ wrapperAttributesBuilder.Append($" data-refresh-url=\"{widget.RefreshUrl}\"");
+ }
+
return new HtmlContentBuilder()
- .AppendHtml($"