Browse Source

Merge pull request #7828 from abpframework/auto-merge/rel-4-2/168

Merge branch dev with rel-4.2
pull/8021/head
Mehmet Erim 6 years ago
committed by GitHub
parent
commit
9df1500fa0
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 16
      docs/en/Tutorials/Part-2.md
  2. 23
      docs/en/Tutorials/Part-3.md
  3. 2
      docs/en/Tutorials/Part-5.md
  4. 6
      docs/en/Tutorials/Part-6.md
  5. 2
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/AbpCliCoreModule.cs
  6. 65
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CreateMigrationAndRunMigrator.cs
  7. 98
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CreateMigrationAndRunMigratorCommand.cs

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

@ -103,7 +103,7 @@ Before starting to the UI development, we first want to prepare the localization
Localization texts are located under the `Localization/BookStore` folder of the `Acme.BookStore.Domain.Shared` project:
![bookstore-localization-files](./images/bookstore-localization-files-v2.png)
![bookstore-localization-files](images/bookstore-localization-files-v2.png)
Open the `en.json` (*the English translations*) file and change the content as below:
@ -157,7 +157,7 @@ It's time to create something visible and usable! Instead of classic MVC, we wil
Create `Books` folder under the `Pages` folder of the `Acme.BookStore.Web` project. Add a new Razor Page by right clicking the Books folder then selecting **Add > Razor Page** menu item. Name it as `Index`:
![bookstore-add-index-page](./images/bookstore-add-index-page-v2.png)
![bookstore-add-index-page](images/bookstore-add-index-page-v2.png)
Open the `Index.cshtml` and change the whole content as shown below:
@ -208,7 +208,7 @@ context.Menu.AddItem(
Run the project, login to the application with the username `admin` and the password `1q2w3E*` and see the new menu item has been added to the main menu:
![bookstore-menu-items](./images/bookstore-new-menu-item.png)
![bookstore-menu-items](images/bookstore-new-menu-item.png)
When you click to the Books menu item under the Book Store parent, you are being redirected to the new empty Books Page.
@ -250,7 +250,7 @@ Change the `Pages/Books/Index.cshtml` as following:
Create an `Index.js` file under the `Pages/Books` folder:
![bookstore-index-js-file](./images/bookstore-index-js-file-v3.png)
![bookstore-index-js-file](images/bookstore-index-js-file-v3.png)
The content of the file is shown below:
@ -344,8 +344,6 @@ It's time to create something visible and usable! There are some tools that we w
- [Ng Bootstrap](https://ng-bootstrap.github.io/#/home) will be used as the UI component library.
- [Ngx-Datatable](https://swimlane.gitbook.io/ngx-datatable/) will be used as the datatable library.
### BookModule
Run the following command line to create a new module, named `BookModule` in the root folder of the angular application:
```bash
@ -457,7 +455,7 @@ abp generate-proxy
This command will create the following files under the `/src/app/proxy/books` folder:
![Generated files](./images/generated-proxies-3.png)
![Generated files](images/generated-proxies-3.png)
### BookComponent
@ -490,7 +488,7 @@ export class BookComponent implements OnInit {
```
* We imported and injected the generated `BookService`.
* We are using the [ListService](https://docs.abp.io/en/abp/latest/UI/Angular/List-Service), a utility service of the ABP Framework which provides easy pagination, sorting and searching.
* We are using the [ListService](../UI/Angular/List-Service.md), a utility service of the ABP Framework which provides easy pagination, sorting and searching.
Open the `/src/app/book/book.component.html` and replace the content as below:
@ -531,7 +529,7 @@ Open the `/src/app/book/book.component.html` and replace the content as below:
Now you can see the final result on your browser:
![Book list final result](./images/bookstore-book-list.png)
![Book list final result](images/bookstore-book-list.png)
{{else if UI == "Blazor"}}

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

@ -48,13 +48,13 @@ This part is also recorded as a video tutorial and **<a href="https://www.youtub
In this section, you will learn how to create a new modal dialog form to create a new book. The modal dialog will look like in the image below:
![bookstore-create-dialog](./images/bookstore-create-dialog-2.png)
![bookstore-create-dialog](images/bookstore-create-dialog-2.png)
### Create the Modal Form
Create a new razor page, named `CreateModal.cshtml` under the `Pages/Books` folder of the `Acme.BookStore.Web` project.
![bookstore-add-create-dialog](./images/bookstore-add-create-dialog-v2.png)
![bookstore-add-create-dialog](images/bookstore-add-create-dialog-v2.png)
#### CreateModal.cshtml.cs
@ -186,7 +186,7 @@ The final content of the `Index.cshtml` is shown below:
This adds a new button called **New book** to the **top-right** of the table:
![bookstore-new-book-button](./images/bookstore-new-book-button-2.png)
![bookstore-new-book-button](images/bookstore-new-book-button-2.png)
Open the `Pages/Books/Index.js` and add the following code just after the `Datatable` configuration:
@ -281,7 +281,7 @@ Now, you can **run the application** and add some new books using the new modal
Create a new razor page, named `EditModal.cshtml` under the `Pages/Books` folder of the `Acme.BookStore.Web` project:
![bookstore-add-edit-dialog](./images/bookstore-add-edit-dialog.png)
![bookstore-add-edit-dialog](images/bookstore-add-edit-dialog.png)
### EditModal.cshtml.cs
@ -484,7 +484,7 @@ You can run the application and edit any book by selecting the edit action on a
The final UI looks as below:
![bookstore-books-table-actions](./images/bookstore-edit-button-2.png)
![bookstore-books-table-actions](images/bookstore-edit-button-2.png)
## Deleting a Book
@ -721,7 +721,7 @@ Open `/src/app/book/book.component.html` and make the following changes:
You can open your browser and click **New book** button to see the new modal.
![Empty modal for new book](./images/bookstore-empty-new-book-modal.png)
![Empty modal for new book](images/bookstore-empty-new-book-modal.png)
### Create a Reactive Form
@ -732,8 +732,7 @@ Open `/src/app/book/book.component.ts` and replace the content as below:
```js
import { ListService, PagedResultDto } from '@abp/ng.core';
import { Component, OnInit } from '@angular/core';
// import bookTypeOptions from @proxy/books
import { BookService, BookDto, bookTypeOptions } from '@proxy/books';
import { BookService, BookDto, bookTypeOptions } from '@proxy/books'; // add bookTypeOptions
import { FormGroup, FormBuilder, Validators } from '@angular/forms'; // add this
@Component({
@ -959,7 +958,7 @@ export class BookComponent implements OnInit {
Now, you can open your browser to see the changes:
![Save button to the modal](./images/bookstore-new-book-form-v2.png)
![Save button to the modal](images/bookstore-new-book-form-v2.png)
## Updating a Book
@ -1087,7 +1086,7 @@ Open the `/src/app/book/book.component.html`  and add the following `ngx-datata
Added an "Actions" dropdown as the first column of the table that is shown below:
![Action buttons](./images/bookstore-actions-buttons.png)
![Action buttons](images/bookstore-actions-buttons.png)
Also, change the `ng-template #abpHeader` section as shown below:
@ -1151,11 +1150,11 @@ Open `/src/app/book/book.component.html` and modify the `ngbDropdownMenu` to add
The final actions dropdown UI looks like below:
![bookstore-final-actions-dropdown](./images/bookstore-final-actions-dropdown.png)
![bookstore-final-actions-dropdown](images/bookstore-final-actions-dropdown.png)
Clicking the "Delete" action calls the `delete` method which then shows a confirmation popup as shown below:
![bookstore-confirmation-popup](./images/bookstore-confirmation-popup.png)
![bookstore-confirmation-popup](images/bookstore-confirmation-popup.png)
{{end}}

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

@ -117,7 +117,7 @@ Finally, edit the localization file (`en.json` under the `Localization/BookStore
"Permission:Books.Delete": "Deleting the books"
````
> Localization key names are arbitrary and no forcing rule. But we prefer the convention used above.
> Localization key names are arbitrary and there is no forcing rule. But we prefer the convention used above.
### Permission Management UI

6
docs/en/Tutorials/Part-6.md

@ -124,7 +124,7 @@ Created this class inside the `Acme.BookStore.Domain.Shared` project since we wi
## AuthorManager: The Domain Service
`Author` constructor and `ChangeName` method is `internal`, so they can be usable only in the domain layer. Create an `AuthorManager` class in the `Authors` folder (namespace) of the `Acme.BookStore.Domain` project:
`Author` constructor and `ChangeName` methods are `internal`, so they can be used only in the domain layer. Create an `AuthorManager` class in the `Authors` folder (namespace) of the `Acme.BookStore.Domain` project:
````csharp
using System;
@ -186,7 +186,7 @@ namespace Acme.BookStore.Authors
* `AuthorManager` forces to create an author and change name of an author in a controlled way. The application layer (will be introduced later) will use these methods.
> **DDD tip**: Do not introduce domain service methods unless they are really needed and perform some core business rules. For this case, we needed to this service to be able to force the unique name constraint.
> **DDD tip**: Do not introduce domain service methods unless they are really needed and perform some core business rules. For this case, we needed this service to be able to force the unique name constraint.
Both methods checks if there is already an author with the given name and throws a special business exception, `AuthorAlreadyExistsException`, defined in the `Acme.BookStore.Domain` project (in the `Authors` folder) as shown below:
@ -226,7 +226,7 @@ This is a unique string represents the error code thrown by your application and
"BookStore:00001": "There is already an author with the same name: {name}"
````
Whenever you throw an `AuthorAlreadyExistsException`, the end use will see a this message on the UI.
Whenever you throw an `AuthorAlreadyExistsException`, the end user will see a nice error message on the UI.
## IAuthorRepository

2
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/AbpCliCoreModule.cs

@ -45,7 +45,7 @@ namespace Volo.Abp.Cli
options.Commands["translate"] = typeof(TranslateCommand);
options.Commands["build"] = typeof(BuildCommand);
options.Commands["bundle"] = typeof(BundleCommand);
options.Commands["create-migration-and-run-migrator"] = typeof(CreateMigrationAndRunMigrator);
options.Commands["create-migration-and-run-migrator"] = typeof(CreateMigrationAndRunMigratorCommand);
});
}
}

65
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CreateMigrationAndRunMigrator.cs

@ -1,65 +0,0 @@
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.Cli.Args;
using Volo.Abp.Cli.Utils;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Cli.Commands
{
public class CreateMigrationAndRunMigrator : IConsoleCommand, ITransientDependency
{
public virtual async Task ExecuteAsync(CommandLineArgs commandLineArgs)
{
if (commandLineArgs.Target.IsNullOrEmpty())
{
throw new CliUsageException(
"DbMigrations folder path is missing!"
);
}
var dbMigratorProjectPath = GetDbMigratorProjectPath(commandLineArgs.Target);
if (dbMigratorProjectPath == null)
{
throw new Exception("DbMigrator is not found!");
}
var output = CmdHelper.RunCmdAndGetOutput($"cd \"{commandLineArgs.Target}\" && dotnet ef migrations add Initial -s \"{dbMigratorProjectPath}\"");
if (output.Contains("Done.") && output.Contains("To undo this action") && output.Contains("ef migrations remove")) // Migration added successfully
{
CmdHelper.RunCmd("cd \"" + Path.GetDirectoryName(dbMigratorProjectPath) + "\" && dotnet run");
}
else
{
throw new Exception("Migrations failed: " + output);
}
}
private string GetDbMigratorProjectPath(string dbMigrationsFolderPath)
{
var srcFolder = Directory.GetParent(dbMigrationsFolderPath);
var dbMigratorFolderPath = Directory.GetDirectories(srcFolder.FullName).FirstOrDefault(d => d.EndsWith(".DbMigrator"));
if (dbMigratorFolderPath == null)
{
return null;
}
return Directory.GetFiles(dbMigratorFolderPath).FirstOrDefault(f => f.EndsWith(".csproj"));
}
public string GetUsageInfo()
{
return string.Empty;
}
public string GetShortDescription()
{
return string.Empty;
}
}
}

98
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CreateMigrationAndRunMigratorCommand.cs

@ -0,0 +1,98 @@
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Volo.Abp.Cli.Args;
using Volo.Abp.Cli.Utils;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Cli.Commands
{
public class CreateMigrationAndRunMigratorCommand : IConsoleCommand, ITransientDependency
{
public ILogger<CreateMigrationAndRunMigratorCommand> Logger { get; set; }
public CreateMigrationAndRunMigratorCommand()
{
Logger = NullLogger<CreateMigrationAndRunMigratorCommand>.Instance;
}
public virtual async Task ExecuteAsync(CommandLineArgs commandLineArgs)
{
if (commandLineArgs.Target.IsNullOrEmpty())
{
throw new CliUsageException("DbMigrations folder path is missing!");
}
var dbMigratorProjectPath = GetDbMigratorProjectPath(commandLineArgs.Target);
if (dbMigratorProjectPath == null)
{
throw new Exception("DbMigrator is not found!");
}
if (!IsDotNetEfToolInstalled())
{
InstallDotnetEfTool();
}
var addMigrationCmd = $"cd \"{commandLineArgs.Target}\" && " +
$"dotnet ef migrations add Initial -s \"{dbMigratorProjectPath}\"";
var output = CmdHelper.RunCmdAndGetOutput(addMigrationCmd);
if (output.Contains("Done.") &&
output.Contains("To undo this action") &&
output.Contains("ef migrations remove"))
{
// Migration added successfully
CmdHelper.RunCmd("cd \"" + Path.GetDirectoryName(dbMigratorProjectPath) + "\" && dotnet run");
await Task.CompletedTask;
}
else
{
var exceptionMsg = "Migrations failed! The following command didn't run successfully:" +
Environment.NewLine +
addMigrationCmd +
Environment.NewLine + output;
Logger.LogError(exceptionMsg);
throw new Exception(exceptionMsg);
}
}
private static bool IsDotNetEfToolInstalled()
{
var output = CmdHelper.RunCmdAndGetOutput("dotnet tool list -g");
return output.Contains("dotnet-ef");
}
private void InstallDotnetEfTool()
{
Logger.LogInformation("Installing dotnet-ef tool...");
CmdHelper.RunCmd("dotnet tool install --global dotnet-ef");
Logger.LogInformation("dotnet-ef tool is installed.");
}
private static string GetDbMigratorProjectPath(string dbMigrationsFolderPath)
{
var srcFolder = Directory.GetParent(dbMigrationsFolderPath);
var dbMigratorDirectory = Directory.GetDirectories(srcFolder.FullName)
.FirstOrDefault(d => d.EndsWith(".DbMigrator"));
return dbMigratorDirectory == null
? null
: Directory.GetFiles(dbMigratorDirectory).FirstOrDefault(f => f.EndsWith(".csproj"));
}
public string GetUsageInfo()
{
return string.Empty;
}
public string GetShortDescription()
{
return string.Empty;
}
}
}
Loading…
Cancel
Save