diff --git a/docs/en/Startup-Templates/Application-Single-Layer.md b/docs/en/Startup-Templates/Application-Single-Layer.md new file mode 100644 index 0000000000..ce194d7585 --- /dev/null +++ b/docs/en/Startup-Templates/Application-Single-Layer.md @@ -0,0 +1,106 @@ +# Application (Single Layer) Startup Template + +## Introduction + +This template provides a single-layered application for a quick start with ABP Framework. + +This document explains the **solution structure** and project in detail. + +### What is the Difference Between the Application Startup Templates? + +ABP's [Application Startup Template](Application.md) provides a well-organized and layered solution to create maintainable business applications based on the [Domain Driven Design](../Domain-Driven-Design.md) (DDD) practices. However, some developers find this template a little bit complex (or unnecessary) for simple and short-time applications. + +At this point, a single-layer application template has been created for such applications. This template has the same functionality, features and modules on runtime with the [Application Startup Template](Application.md) but the development model is minimal and everything is in a single project (`.csproj`). + +## How to Start with It? + +You can use the [ABP CLI](../CLI.md) to create a new project using this startup template. Alternatively, you can directly create & download this startup template from the [Get Started](https://abp.io/get-started) page. How to download via CLI is explained in this section. + +Firstly, install the ABP CLI if you haven't installed it 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-nolayers +``` + +* `Acme.BookStore` is the solution name, like *YourCompany.YourProduct*. You can use single-level, two-level or three-level naming. +* In this example, the `-t` option (or `--template` option) specifies the template name. + +### Specify the UI Framework + +This template provides multiple UI frameworks: + +* `mvc`: ASP.NET Core MVC UI with Razor Pages (default) +* `blazor-server`: Blazor Server UI +* `angular`: Angular UI +* `none`: Without UI + +> Currently, this template doesn't have Blazor WASM UI, because it requires 3 projects at least (server-side, UI and shared library between these two projects). + +Use the `-u` or `--ui` option to specify the UI framework while creating the solution: + +```bash +abp new Acme.BookStore -t app-nolayers -u angular +``` + +* This example specifies the UI type (`-u` option) as `angular`, you can also specify `mvc` or `blazor-server` for UI type. + +### Specify the Database Provider + +This template supports the following database providers: + +- `ef`: Entity Framework Core (default) +- `mongodb`: MongoDB + +Use the `-d` (or `--database-provider`) option to specify the database provider while creating the solution: + +```bash +abp new Acme.BookStore -t app-nolayers -d mongodb +``` + +## Solution Structure + +If you don't specify any additional options while creating an `app-nolayers` template, you will have a solution as shown below: + +![](../images/bookstore-single-layer-solution-structure.png) + +It's a single-layer template rather than a layered-architecture solution (like the `Application Startup Template`). It's helpful to create a running application quickly without considering the layered architecture. + +### Folder Structure + +Since this template provides a single-layer solution, we've separated concerns into folders instead of layers and you can see the pre-defined folders as shown below: + +![](../images/single-layer-folder-structure.png) + +* You can define your `entities`, `application services`, `DTOs`, etc. in this single project (in the related folders). +* For example, you can define your `application services` and `DTOs` under the **Services** folder. + +### How to Run? + +Before running the application, you need to create the database and seed the initial data. To do that, you can run the following command in the directory of your project: + +```bash +dotnet run --migrate-database +``` + +* This command will create the database and seed the initial data for you. Then you can run the application with any IDE that supports .NET or by running the `dotnet run` CLI command in the directory of your project. The default username is `admin` and the password is `1q2w3E*`. + +> While creating a database & applying migrations seem only necessary for relational databases, this project comes even if you choose a NoSQL database provider (like MongoDB). In that case, it still seeds the initial data which is necessary for the application. + +### Angular UI + +If you choose `Angular` as the UI framework, the solution will be separated into two folders: + +* An `angular` folder that contains the Angular UI application, the client-side code. +* An `aspnet-core` folder that contains the ASP.NET Core solution (a single project), the server-side code. + +The server-side is similar to the solution described in the [Solution Structure](#solution-structure) section above. This project serves the API, so the `Angular` application can consume it. + +The client-side application consumes the HTTP APIs as mentioned. You can see the folder structure of the Angular project shown below: + +![](../images/single-layer-angular-folder-structure.png) diff --git a/docs/en/Tutorials/Todo/Single-Layer/Index.md b/docs/en/Tutorials/Todo/Single-Layer/Index.md new file mode 100644 index 0000000000..9f4927f5e4 --- /dev/null +++ b/docs/en/Tutorials/Todo/Single-Layer/Index.md @@ -0,0 +1,760 @@ +# Quick Start + +````json +//[doc-params] +{ + "UI": ["MVC", "BlazorServer", "NG"], + "DB": ["EF", "Mongo"] +} +```` + +This is a single-part quick-start tutorial to build a simple todo application with the ABP Framework. Here's a screenshot from the final application: + +![todo-list](../todo-list.png) + +You can find the source code of the completed application [here](https://github.com/abpframework/abp-samples/tree/master/TodoApp-SingleLayer). + +## Pre-Requirements + +* An IDE (e.g. [Visual Studio](https://visualstudio.microsoft.com/vs/)) that supports [.NET 6.0+](https://dotnet.microsoft.com/download/dotnet) development. + +{{if DB=="Mongo"}} + +* [MongoDB Server 4.0+](https://docs.mongodb.com/manual/administration/install-community/) + +{{end}} + +{{if UI=="NG"}} + +* [Node v14.x](https://nodejs.org/) + +{{end}} + +## Creating a New Solution + +In this tutorial, we will use the [ABP CLI](../../CLI.md) to create the sample application with the ABP Framework. You can run the following command in a command-line terminal to install the **ABP CLI**, if you haven't installed it yet: + +````bash +dotnet tool install -g Volo.Abp.Cli +```` + +Then create an empty folder, open a command-line terminal and execute the following command in the terminal: + +````bash +abp new TodoApp -t app-nolayers{{if UI=="BlazorServer"}} -u blazor-server{{else if UI=="NG"}} -u angular{{end}}{{if DB=="Mongo"}} -d mongodb{{end}} +```` + +{{if UI=="NG"}} + +This will create a new solution with a single project, named *TodoApp* with `angular` and `aspnet-core` folders. Once the solution is ready, open the solution in your favorite IDE. + +{{else}} + +This will create a new solution with a single project, named *TodoApp*. Once the solution is ready, open it in your favorite IDE. + +{{end}} + +### Create the Database + +You can run the following command in the directory of your project to create the database and seed the initial data: + +```bash +dotnet run --migrate-database +``` + +This command will create the database and seed the initial data for you. Then you can run the application. + +### Run the Application + +{{if UI=="MVC" || UI=="BlazorServer"}} + +It is good to run the application before starting the development. Running the application is pretty straight-forward, you can run the application with any IDE that supports .NET or by running the `dotnet run` CLI command in the directory of your project: + +{{else if UI=="NG"}} + +It is good to run the application before starting the development. The solution has two main applications: + +* `TodoApp` (in the .NET solution) hosts the server-side HTTP API, so the Angular application can consume it. (server-side application) +* `angular` folder contains the Angular application. (client-side application) + +Firstly, run the `TodoApp` project in your favorite IDE (or run the `dotnet run` CLI command on your project directory) to see the server-side HTTP API on [Swagger UI](https://swagger.io/tools/swagger-ui/): + +![todo-swagger-ui-initial](../todo-swagger-ui-initial.png) + +You can explore and test your HTTP API with this UI. If it works, then we can run the Angular client application. + +You can run the application using the following (or `yarn start`) command: + +````bash +npm start +```` + +This command takes time, but eventually runs and opens the application in your default browser: + +{{end}} + +![todo-ui-initial](../todo-ui-initial.png) + +You can click on the *Login* button and use `admin` as the username and `1q2w3E*` as the password to login to the application. + +All right. We can start coding! + +## Defining Entities + +This application will have a single [entity](../../../Entities.md) and we can start by creating it. So, create a new `TodoItem` class under the **Entities** folder of the project: + +````csharp +using Volo.Abp.Domain.Entities; + +namespace TodoApp.Entities; + +public class TodoItem : BasicAggregateRoot +{ + public string Text { get; set; } +} +```` + +`BasicAggregateRoot` is the simplest base class to create root entities, and `Guid` is the primary key (`Id`) of the entity here. + +## Database Integration + +{{if DB=="EF"}} + +Next step is to setup the [Entity Framework Core](../../../Entity-Framework-Core.md) configuration. + +### Mapping Configuration + +Open the `TodoAppDbContext` class (under the **Data** folder) and add a new `DbSet` property to this class: + +````csharp +public DbSet TodoItems { get; set; } +```` + +Then navigate to the `OnModelCreating` method in the same class and add the following mapping code for the `TodoItem ` entity: + +````csharp +protected override void OnModelCreating(ModelBuilder builder) +{ + base.OnModelCreating(builder); + + /* Include modules to your migration db context */ + + builder.ConfigurePermissionManagement(); + ... + + /* Configure your own tables/entities inside here */ + builder.Entity(b => + { + b.ToTable("TodoItems"); + }); +} +```` + +We've mapped the `TodoItem` entity to the `TodoItems` table in the database. The next step is to create a migration and apply the changes to the database. + +### Code First Migrations + +The startup solution is configured to use Entity Framework Core [Code First Migrations](https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations). Since we've changed the database mapping configuration, we should create a new migration and apply changes to the database. + +Open a command-line terminal in the directory of your project and type the following command: + +````bash +dotnet ef migrations add Added_TodoItem +```` + +This will add a new migration class to the project. You should see the new migration in the **Migrations** folder. + +![todo-efcore-migration](todo-efcore-migration-single-layer.png) + +Then, you can apply changes to the database using the following command, in the same command-line terminal: + +````bash +dotnet ef database update +```` + +{{else if DB=="Mongo"}} + +The next step is to setup the [MongoDB](../../../MongoDB.md) configuration. Open the `TodoAppDbContext` class (under the **Data** folder) in your project and make the following changes: + +1. Add a new property to the class: + +````csharp +public IMongoCollection TodoItems => Collection(); +```` + +2. Add the following code inside the `CreateModel` method: + +````csharp +modelBuilder.Entity(b => +{ + b.CollectionName = "TodoItems"; +}); +```` + +{{end}} + +After the database integrations, now we can start to create application service methods and implement our use-cases. + +## Creating the Application Service + +An [Application Service](../../Application-Services.md) is used to perform the use cases of the application. We need to perform the following use cases in this application: + +* Get the list of the todo items +* Create a new todo item +* Delete an existing todo item + +Before starting to implement these use cases, first we need to create DTOs. + +### Creating the Data Transfer Object (DTO) + +`ApplicationService` typically gets and returns DTOs ([Data Transfer Objects](../../../Data-Transfer-Objects.md)) instead of entities. So, create a new `TodoItemDto` class under the **Dtos** folder (under the **Services** folder): + +```csharp +namespace TodoApp.Services.Dtos; + +public class TodoItemDto +{ + public Guid Id { get; set; } + public string Text { get; set; } +} +``` + +* This is a very simple DTO class that has the same properties as the `TodoItem` entity. Now, we are ready to implement our use-cases. + +### Application Service Implementation + +Create a `TodoAppService` class under the **Services** folder of your project, as shown below: + +```csharp +using TodoApp.Entities; +using Volo.Abp.Application.Services; +using Volo.Abp.Domain.Repositories; + +namespace TodoApp.Services; + +public class TodoAppService : ApplicationService +{ + private readonly IRepository _todoItemRepository; + + public TodoAppService(IRepository todoItemRepository) + { + _todoItemRepository = todoItemRepository; + } + + // TODO: Implement the methods here... +} +``` + +This class inherits from the `ApplicationService` class of the ABP Framework and implements our use-cases. ABP provides default generic [repositories](../../../Repositories.md) for the entities. We can use them to perform the fundamental database operations. This class [injects](../../../Dependency-Injection.md) `IRepository`, which is the default repository for the `TodoItem` entity. We will use it to implement the use cases (`GetListAsync`, `CreateAsync` and `DeleteAsync`) described before. + +#### Getting the Todo Items + +Let's start by implementing the `GetListAsync` method: + +````csharp +public async Task> GetListAsync() +{ + var items = await _todoItemRepository.GetListAsync(); + return items + .Select(item => new TodoItemDto + { + Id = item.Id, + Text = item.Text + }).ToList(); +} +```` + +We are simply getting the `TodoItem` list from the database, mapping them to the `TodoItemDto` objects and returning as the result. + +#### Creating a New Todo Item + +The next method is `CreateAsync` and we can implement it as shown below: + +````csharp +public async Task CreateAsync(string text) +{ + var todoItem = await _todoItemRepository.InsertAsync( + new TodoItem {Text = text} + ); + + return new TodoItemDto + { + Id = todoItem.Id, + Text = todoItem.Text + }; +} +```` + +The repository's `InsertAsync` method inserts the given `TodoItem` to the database and returns the same `TodoItem` object. It also sets the `Id`, so we can use it on the returning object. We are simply returning a `TodoItemDto` by creating from the new `TodoItem` entity. + +#### Deleting a Todo Item + +Finally, we can implement the `DeleteAsync` as the following code block: + +````csharp +public async Task DeleteAsync(Guid id) +{ + await _todoItemRepository.DeleteAsync(id); +} +```` + +The application service is ready to be used from the UI layer. So, let's implement it. + +## User Interface + +It is time to show the todo items on the UI! Before starting to write the code, it would be good to remember what we are trying to build. Here's a sample screenshot from the final UI: + +![todo-list](../todo-list.png) + +{{if UI=="MVC"}} + +### Index.cshtml.cs + +Open the `Index.cshtml.cs` file in the `Pages` folder and replace the content with the following code block: + +```csharp +using TodoApp.Services; +using TodoApp.Services.Dtos; +using Volo.Abp.AspNetCore.Mvc.UI.RazorPages; + +namespace TodoApp.Pages; + +public class IndexModel : AbpPageModel +{ + public List TodoItems { get; set; } + + private readonly TodoAppService _todoAppService; + + public IndexModel(TodoAppService todoAppService) + { + _todoAppService = todoAppService; + } + + public async Task OnGetAsync() + { + TodoItems = await _todoAppService.GetListAsync(); + } +} +``` + +This class uses `TodoAppService` to get the list of todo items and assign the `TodoItems` property. We will use it to render the todo items on the razor page. + +### Index.cshtml + +Open the `Index.cshtml` file in the `Pages` folder and replace it with the following content: + +```xml +@page +@model TodoApp.Pages.IndexModel + +@section styles { + +} +@section scripts { + +} + +
+ + + + TODO LIST + + + + +
+
+
+ +
+
+
+ +
+
+ +
    + @foreach (var todoItem in Model.TodoItems) + { +
  • + @todoItem.Text +
  • + } +
+
+
+
+``` + +We are using ABP's [card tag helper](../../UI/AspNetCore/Tag-Helpers/Cards.md) to create a simple card view. You could directly use the standard bootstrap HTML structure, however the ABP [tag helpers](../../UI/AspNetCore/Tag-Helpers/Index.md) make it much easier and type safe. + +This page imports a CSS and a JavaScript file, so we should also create them. + +### Index.cshtml.js + +Open the `Index.cshtml.js` file in the `Pages` folder and replace with the following content: + +````js +$(function () { + + // DELETING ITEMS ///////////////////////////////////////// + $('#TodoList').on('click', 'li i', function(){ + var $li = $(this).parent(); + var id = $li.attr('data-id'); + + todoApp.services.todo.delete(id).then(function(){ + $li.remove(); + abp.notify.info('Deleted the todo item.'); + }); + }); + + // CREATING NEW ITEMS ///////////////////////////////////// + $('#NewItemForm').submit(function(e){ + e.preventDefault(); + + var todoText = $('#NewItemText').val(); + todoApp.services.todo.create(todoText).then(function(result){ + $('
  • ') + .html(' ' + result.text) + .appendTo($('#TodoList')); + $('#NewItemText').val(''); + }); + }); +}); +```` + +In the first part, we subscribed to the click events of the trash icons near the todo items, deleted the related item on the server and showed a notification on the UI. Also, we removed the deleted item from the DOM, so we wouldn't need to refresh the page. + +In the second part, we created a new todo item on the server. If it succeeded, we would then manipulate the DOM to insert a new `
  • ` element to the todo list. This way, we wouldn't need to refresh the whole page after creating a new todo item. + +The interesting part here is how we communicate with the server. See the [*Dynamic JavaScript Proxies & Auto API Controllers*](#dynamic-javascript-proxies--auto-api-controllers) section to understand how it works. But now, let's continue and complete the application. + +### Index.cshtml.css + +As for the final touch, open the `Index.cshtml.css` file in the `Pages` folder and replace with the following content: + +````css +#TodoList{ + list-style: none; + margin: 0; + padding: 0; +} + +#TodoList li { + padding: 5px; + margin: 5px 0px; + border: 1px solid #cccccc; + background-color: #f5f5f5; +} + +#TodoList li i +{ + opacity: 0.5; +} + +#TodoList li i:hover +{ + opacity: 1; + color: #ff0000; + cursor: pointer; +} +```` + +This is a simple styling for the todo page. We believe that you can do much better :) + +Now, you can run the application again and see the result. + +### Dynamic JavaScript Proxies & Auto API Controllers + +In the `Index.cshtml.js` file, we've used the `todoApp.services.todo.delete(...)` and `todoApp.services.todo.create(...)` functions to communicate with the server. These functions are dynamically created by the ABP Framework, thanks to the [Dynamic JavaScript Client Proxy](../../../UI/AspNetCore/Dynamic-JavaScript-Proxies.md) system. They perform HTTP API calls to the server and return a promise, so you can register a callback to the `then` function as we've done above. + +> **services** keyword comes from the namespace (`namespace TodoApp.Services;`). It's a naming convention. + +However, you may notice that we haven't created any API Controllers, so how does the server handle these requests? This question brings us to the [Auto API Controller](../../../API/Auto-API-Controllers.md) feature of the ABP Framework. It automatically converts the application services to **API Controllers** by convention. + +If you open [Swagger UI](https://swagger.io/tools/swagger-ui/) by entering the `/swagger` URL in your application, you can see the Todo API: + +![todo-api](../todo-api.png) + +{{else if UI=="BlazorServer"}} + +### Index.razor.cs + +Open the `Index.razor.cs` file in the `Pages` folder and replace the content with the following code block: + +```csharp +using Microsoft.AspNetCore.Components; +using TodoApp.Services; +using TodoApp.Services.Dtos; + +namespace TodoApp.Pages; + +public partial class Index +{ + [Inject] + private TodoAppService TodoAppService { get; set; } + + private List TodoItems { get; set; } = new List(); + private string NewTodoText { get; set; } + + protected override async Task OnInitializedAsync() + { + TodoItems = await TodoAppService.GetListAsync(); + } + + private async Task Create() + { + var result = await TodoAppService.CreateAsync(NewTodoText); + TodoItems.Add(result); + NewTodoText = null; + } + + private async Task Delete(TodoItemDto todoItem) + { + await TodoAppService.DeleteAsync(todoItem.Id); + await Notify.Info("Deleted the todo item."); + TodoItems.Remove(todoItem); + } +} +``` + +This class uses the `TodoAppService` to get the list of todo items. It manipulates the `TodoItems` list after create and delete operations. This way, we don't need to refresh the whole todo list from the server. + +### Index.razor + +Open the `Index.razor` file in the `Pages` folder and replace the content with the following code block: + +```xml +@page "/" +@inherits TodoAppComponentBase + +
    + + + + TODO LIST + + + + +
    +
    +
    + +
    +
    +
    + +
    +
    + +
      + @foreach (var todoItem in TodoItems) + { +
    • + + @todoItem.Text +
    • + } +
    +
    +
    +
    +``` + +### Index.razor.css + +As the final touch, open the `Index.razor.css` file in the `Pages` folder and replace it with the following content: + +````css +#TodoList{ + list-style: none; + margin: 0; + padding: 0; +} + +#TodoList li { + padding: 5px; + margin: 5px 0px; + border: 1px solid #cccccc; + background-color: #f5f5f5; +} + +#TodoList li i +{ + opacity: 0.5; +} + +#TodoList li i:hover +{ + opacity: 1; + color: #ff0000; + cursor: pointer; +} +```` + +This is a simple styling for the todo page. We believe that you can do much better :) + +Now, you can run the application again to see the result. + +{{else if UI=="NG"}} + +### Service Proxy Generation + +ABP provides a handy feature to automatically create client-side services to easily consume HTTP APIs provided by the server. + +You first need to run the `TodoApp` project since the proxy generator reads API definitions from the server application. + +Once you run the `TodoApp` project (**Swagger API Definition** will be shown), open a command-line terminal in the directory of `angular` folder and run the following command: + +```bash +abp generate-proxy -t ng +``` + +If everything goes well, it should generate an output as shown below: + +```bash +CREATE src/app/proxy/generate-proxy.json (182755 bytes) +CREATE src/app/proxy/README.md (1000 bytes) +CREATE src/app/proxy/services/todo.service.ts (833 bytes) +CREATE src/app/proxy/services/dtos/models.ts (71 bytes) +CREATE src/app/proxy/services/dtos/index.ts (26 bytes) +CREATE src/app/proxy/services/index.ts (81 bytes) +CREATE src/app/proxy/index.ts (61 bytes) +``` + +Then, we can use the `TodoService` to use the server-side HTTP APIs, as we'll do in the next section. + +### home.component.ts + +Open the `/angular/src/app/home/home.component.ts` file and replace its content with the following code block: + +```ts +import { ToasterService } from "@abp/ng.theme.shared"; +import { Component, OnInit } from '@angular/core'; +import { TodoItemDto } from "@proxy/services/dtos"; +import { TodoService } from "@proxy/services"; + +@Component({ + selector: 'app-home', + templateUrl: './home.component.html', + styleUrls: ['./home.component.scss'], +}) + +export class HomeComponent implements OnInit { + + todoItems: TodoItemDto[]; + newTodoText: string; + + constructor( + private todoService: TodoService, + private toasterService: ToasterService) + { } + + ngOnInit(): void { + this.todoService.getList().subscribe(response => { + this.todoItems = response; + }); + } + + create(): void{ + this.todoService.create(this.newTodoText).subscribe((result) => { + this.todoItems = this.todoItems.concat(result); + this.newTodoText = null; + }); + } + + delete(id: string): void { + this.todoService.delete(id).subscribe(() => { + this.todoItems = this.todoItems.filter(item => item.id !== id); + this.toasterService.info('Deleted the todo item.'); + }); + } +} +``` + +We've used `TodoService` to get the list of todo items and assigned the returning value to the `todoItems` array. We've also added `create` and `delete` methods. These methods will be used on the view side. + +### home.component.html + +Open the `/angular/src/app/home/home.component.html` file and replace its content with the following code block: + +````html +
    +
    +
    +
    TODO LIST
    +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    +
    + +
      +
    • + {%{{{ todoItem.text }}}%} +
    • +
    +
    +
    +
    +```` + +### home.component.scss + +As the final touch, open the `/angular/src/app/home/home.component.scss` file and replace its content with the following code block: + +````css +#TodoList{ + list-style: none; + margin: 0; + padding: 0; +} + +#TodoList li { + padding: 5px; + margin: 5px 0px; + border: 1px solid #cccccc; + background-color: #f5f5f5; +} + +#TodoList li i +{ + opacity: 0.5; +} + +#TodoList li i:hover +{ + opacity: 1; + color: #ff0000; + cursor: pointer; +} +```` + +This is a simple styling for the todo page. We believe that you can do much better :) + +Now, you can run the application again to see the result. + +{{end}} + +## Conclusion + +In this tutorial, we've built a very simple application to warm up with the ABP Framework. + +## Source Code + +You can find the source code of the completed application [here](https://github.com/abpframework/abp-samples/tree/master/TodoApp-SingleLayer). + +## See Also + +* Check the [Web Application Development Tutorial](../Part-1.md) to see a real-life web application development in a layered architecture using the [Application Startup Template](../../../Startup-Templates/Application.md). diff --git a/docs/en/Tutorials/Todo/Single-Layer/todo-efcore-migration-single-layer.png b/docs/en/Tutorials/Todo/Single-Layer/todo-efcore-migration-single-layer.png new file mode 100644 index 0000000000..686d8a5f87 Binary files /dev/null and b/docs/en/Tutorials/Todo/Single-Layer/todo-efcore-migration-single-layer.png differ diff --git a/docs/en/docs-nav.json b/docs/en/docs-nav.json index 186eef1e07..610f0f60a4 100644 --- a/docs/en/docs-nav.json +++ b/docs/en/docs-nav.json @@ -118,6 +118,10 @@ "text": "Application", "path": "Startup-Templates/Application.md" }, + { + "text": "Application (Single Layer)", + "path": "Startup-Templates/Application-Single-Layer.md" + }, { "text": "Module", "path": "Startup-Templates/Module.md" diff --git a/docs/en/images/bookstore-single-layer-solution-structure.png b/docs/en/images/bookstore-single-layer-solution-structure.png new file mode 100644 index 0000000000..edc495d909 Binary files /dev/null and b/docs/en/images/bookstore-single-layer-solution-structure.png differ diff --git a/docs/en/images/single-layer-angular-folder-structure.png b/docs/en/images/single-layer-angular-folder-structure.png new file mode 100644 index 0000000000..91fe09d61e Binary files /dev/null and b/docs/en/images/single-layer-angular-folder-structure.png differ diff --git a/docs/en/images/single-layer-folder-structure.png b/docs/en/images/single-layer-folder-structure.png new file mode 100644 index 0000000000..06aa77049c Binary files /dev/null and b/docs/en/images/single-layer-folder-structure.png differ diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/SuiteCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/SuiteCommand.cs index aabd5c6dfd..6ee6a74a47 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/SuiteCommand.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/SuiteCommand.cs @@ -132,6 +132,11 @@ public class SuiteCommand : IConsoleCommand, ITransientDependency if (!response.IsNullOrWhiteSpace()) { Logger.LogError(response); + + if (response.Contains("Commercial.SuiteTemplates.dll")) + { + Logger.LogInformation("The solution should be built before generating an entity! Use `dotnet build` to build your solution."); + } } else { diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Blazor/Components/PermissionManagementModal.razor.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Blazor/Components/PermissionManagementModal.razor.cs index e81eed859d..b477de008d 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Blazor/Components/PermissionManagementModal.razor.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Blazor/Components/PermissionManagementModal.razor.cs @@ -133,7 +133,7 @@ public partial class PermissionManagementModal if (!updateDto.Permissions.Any(x => x.IsGranted)) { - if (!await Message.Confirm(L["RemoveAllPermissionsWarningMessage"].Value)) + if (!await Message.Confirm(L["SaveWithoutAnyPermissionsWarningMessage"].Value)) { return; } diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/en.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/en.json index 98f3a60216..8fbe3e9eab 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/en.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/en.json @@ -6,6 +6,6 @@ "All": "All", "SelectAllInAllTabs": "Grant all permissions", "SelectAllInThisTab": "Select all", - "RemoveAllPermissionsWarningMessage": "Are you sure you want to remove all permissions?" + "SaveWithoutAnyPermissionsWarningMessage": "Are you sure you want to save without any permissions?" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/tr.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/tr.json index e462d91a51..dececfcce5 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/tr.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/tr.json @@ -5,6 +5,7 @@ "OnlyProviderPermissons": "Sadece bu sağlayıcı", "All": "Hepsi", "SelectAllInAllTabs": "Tüm izinleri ver", - "SelectAllInThisTab": "Hepsini seç" + "SelectAllInThisTab": "Hepsini seç", + "SaveWithoutAnyPermissionsWarningMessage": "Hiçbir izin olmadan kaydetmek istediğinize emin misiniz?" } -} \ No newline at end of file +} diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/permission-management-modal.js b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/permission-management-modal.js index 208047314c..9577d38696 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/permission-management-modal.js +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/permission-management-modal.js @@ -265,7 +265,7 @@ var abp = abp || {}; e.preventDefault(); if(!$form.find("input:checked").length > 0) { - abp.message.confirm(l("RemoveAllPermissionsWarningMessage")) + abp.message.confirm(l("SaveWithoutAnyPermissionsWarningMessage")) .then(function (confirmed) { if(confirmed) { $form.submit();