Browse Source

Merge branch 'dev' into stsrki/dev-blazor-server

pull/8074/head
Halil İbrahim Kalkan 5 years ago
parent
commit
f690ec9a3b
  1. 6
      abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json
  2. 6
      abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/tr.json
  3. 6
      abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/zh-Hans.json
  4. 98
      docs/en/Community-Articles/2021-03-12-Simple-SignalR-Notification/POST.md
  5. BIN
      docs/en/Community-Articles/2021-03-12-Simple-SignalR-Notification/add-endpoint.jpg
  6. BIN
      docs/en/Community-Articles/2021-03-12-Simple-SignalR-Notification/add-script-bundles.jpg
  7. BIN
      docs/en/Community-Articles/2021-03-12-Simple-SignalR-Notification/article-signalr-banner.png
  8. BIN
      docs/en/Community-Articles/2021-03-12-Simple-SignalR-Notification/notification-hub.jpg
  9. BIN
      docs/en/Community-Articles/2021-03-12-Simple-SignalR-Notification/preconfigureservices.jpg
  10. BIN
      docs/en/Community-Articles/2021-03-12-Simple-SignalR-Notification/resource-mappings.jpg
  11. BIN
      docs/en/Community-Articles/2021-03-12-Simple-SignalR-Notification/result.jpg
  12. BIN
      docs/en/Community-Articles/2021-03-12-Simple-SignalR-Notification/signalr-architecture.png
  13. BIN
      docs/en/Community-Articles/2021-03-12-Simple-SignalR-Notification/signalr-folder.jpg
  14. BIN
      docs/en/Community-Articles/2021-03-12-Simple-SignalR-Notification/signalr-package.jpg
  15. 90
      docs/en/Migration-Guides/Abp-4_3-Angular.md
  16. 5
      docs/en/Migration-Guides/Abp-4_3.md
  17. 1
      docs/en/Migration-Guides/Index.md
  18. 20
      docs/en/UI/AspNetCore/Navigation-Menu.md
  19. 17
      docs/en/UI/AspNetCore/Toolbars.md
  20. 10
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Localization/zh-Hant.json
  21. 54
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RemoveProjectFromPrometheusStep.cs
  22. 4
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RemoveProjectFromTyeStep.cs
  23. 3
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Microservice/MicroserviceTemplateBase.cs
  24. 6
      framework/src/Volo.Abp.ExceptionHandling/Volo/Abp/ExceptionHandling/Localization/zh-Hant.json
  25. 8
      framework/src/Volo.Abp.Features/Volo/Abp/Features/Localization/zh-Hant.json
  26. 6
      framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/zh-Hant.json
  27. 7
      framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/Localization/zh-Hant.json
  28. 3
      modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/zh-Hant.json
  29. 6
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/CmsKitWebUnifiedModule.cs
  30. 4
      modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/MediaDescriptors/MediaDescriptorAdminAppService.cs
  31. 6
      modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Tags/EntityTagAdminAppService.cs
  32. 20
      modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/BlogPosts/Index.cshtml
  33. 30
      modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Comments/Index.cshtml
  34. 26
      modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/Index.cshtml
  35. 18
      modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Tags/Index.cshtml
  36. 5
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/CmsKitErrorCodes.cs
  37. 4
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/en.json
  38. 8
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/tr.json
  39. 125
      modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/zh-Hant.json
  40. 23
      modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/CmsKitDomainModule.cs
  41. 11
      modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Comments/CommentEntityTypeDefinition.cs
  42. 4
      modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Comments/DefaultCommentEntityTypeDefinitionStore.cs
  43. 14
      modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Comments/ICommentEntityTypeDefinitionStore.cs
  44. 22
      modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/EntityTypeDefinition.cs
  45. 14
      modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/IEntityTypeDefinitionStore.cs
  46. 4
      modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/DefaultMediaDescriptorDefinitionStore.cs
  47. 9
      modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/IMediaDescriptorDefinitionStore.cs
  48. 13
      modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/PolicySpecifiedDefinition.cs
  49. 11
      modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Ratings/CmsKitRatingOptions.cs
  50. 38
      modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Ratings/DefaultRatingEntityTypeDefinitionStore.cs
  51. 22
      modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Ratings/EntityCantHaveRatingException.cs
  52. 9
      modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Ratings/IRatingEntityTypeDefinitionStore.cs
  53. 2
      modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Ratings/Rating.cs
  54. 12
      modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Ratings/RatingEntityTypeDefinition.cs
  55. 52
      modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Ratings/RatingManager.cs
  56. 8
      modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Reactions/DefaultReactionDefinitionStore.cs
  57. 6
      modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Reactions/IReactionDefinitionStore.cs
  58. 4
      modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/DefaultTagDefinitionStore.cs
  59. 6
      modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/ITagDefinitionStore.cs
  60. 5
      modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/TagEntityTypeDefiniton.cs
  61. 28
      modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Ratings/RatingPublicAppService.cs
  62. 28
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Blogs/BlogPost/Default.cshtml
  63. 29
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Blogs/BlogPost/DefaultBlogPostViewComponent.cs
  64. 11
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Blogs/BlogPostComment/Default.cshtml
  65. 57
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/Default.cshtml
  66. 105
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Rating/Default.cshtml
  67. 18
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Rating/default.css
  68. 36
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/ReactionSelection/Default.cshtml
  69. 15
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/ReactionSelection/ReactionSelectionViewComponent.cs
  70. 12
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/ReactionSelection/default.css
  71. 22
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Tags/Default.cshtml
  72. 7
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Tags/TagViewComponent.cs
  73. 14
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Tags/default.css
  74. 108
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/BlogPost.cshtml
  75. 60
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/Index.cshtml
  76. 23
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/blogPost.css
  77. 6
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/index.css
  78. 3
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Pages/Index.cshtml
  79. 6
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Pages/index.css
  80. 27
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Properties/launchSettings.json
  81. 5
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Volo.CmsKit.Public.Web.csproj
  82. 66
      modules/cms-kit/test/Volo.CmsKit.Domain.Tests/Ratings/RatingManager_Test.cs
  83. 8
      modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitDataSeedContributor.cs
  84. 2
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/zh-Hant.json
  85. 14
      modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/zh-Hant.json
  86. 16
      npm/ng-packs/packages/core/src/lib/strategies/auth-flow.strategy.ts
  87. 1
      templates/app/angular/package.json
  88. 4
      templates/app/angular/src/app/app-routing.module.ts
  89. 2
      templates/app/angular/src/app/app.module.ts
  90. 1
      templates/module/angular/package.json
  91. 4
      templates/module/angular/projects/dev-app/src/app/app-routing.module.ts
  92. 6
      templates/module/angular/projects/dev-app/src/app/app.module.ts

6
abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json

@ -247,6 +247,10 @@
"Address": "Address",
"TaxNo": "Tax No",
"Permission:InvoiceRequest": "Invoice Request",
"Permission:Question": "Question"
"Permission:Question": "Question",
"AddNoteSuccessMessage": "Note successfully added",
"NameSurname": "Name Surname",
"Note": "Note",
"Add": "Add"
}
}

6
abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/tr.json

@ -196,6 +196,10 @@
"Address": "Adres",
"TaxNo": "Vergi no",
"Permission:InvoiceRequest": "Fatura Talebi",
"Permission:Question": "Soru"
"Permission:Question": "Soru",
"AddNoteSuccessMessage": "Not başarıyla eklendi",
"NameSurname": "Adı Soyadı",
"Note": "Not",
"Add": "Ekle"
}
}

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

@ -231,7 +231,11 @@
"Address": "地址",
"TaxNo": "税号",
"Permission:InvoiceRequest": "发票请求",
"Permission:Question": "问题"
"Permission:Question": "问题",
"AddNoteSuccessMessage": "注释添加成功",
"NameSurname": "姓",
"Note": "注释",
"Add": "添加"
}
}

98
docs/en/Community-Articles/2021-03-12-Simple-SignalR-Notification/POST.md

@ -0,0 +1,98 @@
# Send Real-time Notifications via SignalR in ABP Project
SignalR is an open source library that adds real-time operation functionality to applications. Real-time web functionality enables server-side code to instantly send content to clients without refreshing the page. I'll show you how to add SignalR and use it to send notifications from backend. I'll implement this functionality in MVC template of ABP Framework.
![signalr-architecture](signalr-architecture.png)
## Implement Backend
### Create Notification Hub
Create a new folder named `SignalR` in your root directory of your Web project.
![SignalR Folder](signalr-folder.jpg)
Then add the following classes to the folder:
1. [INotificationClient.cs](https://gist.github.com/ebicoglu/f7dc22cca2d353f8bf7f68a03e3395b8#file-inotificationclient-cs)
2. [UiNotificationClient.cs](https://gist.github.com/ebicoglu/f7dc22cca2d353f8bf7f68a03e3395b8#file-uinotificationclient-cs)
3. [UiNotificationHub.cs](https://gist.github.com/ebicoglu/f7dc22cca2d353f8bf7f68a03e3395b8#file-uinotificationhub-cs)
### Configure Module
These 3 steps will be done in your web module class.
#### 1- Add SignalR
Open `YourProjectWebModule.cs` class and add the following line to the `PreConfigureServices` method:
```csharp
context.Services.AddSignalR();
```
![PreConfigureServices](preconfigureservices.jpg)
#### 2- Add Client Scripts
2- In the `ConfigureServices` method of your web module add the following code to add the `signalr.js` and `notification-hub.js`. We'll add these packages in the next steps.
![Script Bundles](add-script-bundles.jpg)
#### 3- Add Hub Endpoint
Add the following code to add the notification hub endpoint in `OnApplicationInitialization` method:
```csharp
app.UseEndpoints(endpoints =>
{
endpoints.MapHub<UiNotificationHub>("/notification-hub");
});
```
![Add endpoint](add-endpoint.jpg)
### Implement Frontend
We'll write the client-side code to be able to handle the SignalR response.
#### 1- Add Notification Hub
Add the following JavaScript class into your `Pages` folder in your Web project. We already added this script to our global scripts.
[notification-hub.js](https://gist.github.com/ebicoglu/f7dc22cca2d353f8bf7f68a03e3395b8#file-notification-hub-js)
![notification-hub.js](notification-hub.jpg)
#### 2- Add SignalR NPM package
Add [Microsoft.SignalR](https://www.npmjs.com/package/@microsoft/signalr) JavaScript package to the `package.json` which is located in your root folder of the Web project. After you add it, run `yarn` command in your Web directory to be able to install this package.
![Add SignalR package](signalr-package.jpg)
#### 3- Add resource Mapping
We added SignalR to the `package.json` but it comes into your `node_modules` folder. We need to copy the related files to `wwwroot/libs` folder. To do this copy the content of the following file to your `abp.resourcemappings.js` file. It's in your root directory of Web folder. After you do this, go to your web directory and run `gulp` command. By doing this, it'll copy the related files into your `wwwroot/libs` folder.
[abp.resourcemappings.js](https://gist.github.com/ebicoglu/f7dc22cca2d353f8bf7f68a03e3395b8#file-abp-resourcemapping-js)
![Resource mappings](resource-mappings.jpg)
#### 4- Usage
We have completed the implementation part. Let's check if it's running...
To do this easily, open your `Index.cshtml` which is in the Pages folder of your Web project. And replace the content with the following. Also replace the `Index.cshtml.cs` as well.
[Index.cshtml](https://gist.github.com/ebicoglu/f7dc22cca2d353f8bf7f68a03e3395b8#file-index-cshtml)
[Index.cshtml.cs](https://gist.github.com/ebicoglu/f7dc22cca2d353f8bf7f68a03e3395b8#file-index-cshtml-cs)
#### 5- See it in action
Run your web project and in the Index page you'll see a button named as "Get Notification". Click the button and see the notification that comes from SignalR. This is a basic usage of SignalR notification system. You can implement it according to your own requirements.
![Result](result.jpg)

BIN
docs/en/Community-Articles/2021-03-12-Simple-SignalR-Notification/add-endpoint.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

BIN
docs/en/Community-Articles/2021-03-12-Simple-SignalR-Notification/add-script-bundles.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

BIN
docs/en/Community-Articles/2021-03-12-Simple-SignalR-Notification/article-signalr-banner.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
docs/en/Community-Articles/2021-03-12-Simple-SignalR-Notification/notification-hub.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

BIN
docs/en/Community-Articles/2021-03-12-Simple-SignalR-Notification/preconfigureservices.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

BIN
docs/en/Community-Articles/2021-03-12-Simple-SignalR-Notification/resource-mappings.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

BIN
docs/en/Community-Articles/2021-03-12-Simple-SignalR-Notification/result.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

BIN
docs/en/Community-Articles/2021-03-12-Simple-SignalR-Notification/signalr-architecture.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

BIN
docs/en/Community-Articles/2021-03-12-Simple-SignalR-Notification/signalr-folder.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

BIN
docs/en/Community-Articles/2021-03-12-Simple-SignalR-Notification/signalr-package.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

90
docs/en/Migration-Guides/Abp-4_3-Angular.md

@ -0,0 +1,90 @@
# Angular UI v4.3 Migration Guide
## Breaking Changes
### Manage Profile Page
Before v4.3, the "Manage Your Profile" link in the current user dropdown on the top bar redirected the user to MVC's profile management page. As of v4.3, the same link will land on a page in the Angular UI account module instead. So you have to install and implement the account module to your Angular project when you update the ABP to v4.3.
#### Account Module Implementation
Install the `@abp/ng.account` NPM package by running the below command:
```bash
npm install @abp/ng.account@next
```
> Make sure v4.3-rc or higher version is installed.
Open the `app.module.ts` and add `AccountConfigModule.forRoot()` to the imports array as shown below:
```js
// app.module.ts
import { AccountConfigModule } from '@abp/ng.account/config';
//...
@NgModule({
imports: [
//...
AccountConfigModule.forRoot()
],
//...
})
export class AppModule {}
```
Open the `app-routing.module.ts` and add the `account` route to `routes` array as follows:
```js
// app-routing.module.ts
const routes: Routes = [
//...
{
path: 'account',
loadChildren: () => import('@abp/ng.account').then(m => m.AccountModule.forLazy()),
},
//...
export class AppRoutingModule {}
```
#### Account Module Implementation for Commercial Templates
The pro startup template comes with `@volo/abp.ng.account` package. You should update the package version to v4.3-rc or higher version. The package can be updated by running the following command:
```bash
npm install @volo/abp.ng.account@next
```
> Make sure v4.3-rc or higher version is installed.
`AccountConfigModule` is already imported to `app.module.ts` in the startup template. So no need to import the module to the `AppModule`. If you removed the `AccountConfigModule` from the `AppModule`, you can import it as shown below:
```js
// app.module.ts
import { AccountConfigModule } from '@volo/abp.ng.account/config';
//...
@NgModule({
imports: [
//...
AccountConfigModule.forRoot()
],
//...
})
export class AppModule {}
```
Open the `app-routing.module.ts` and add the `account` route to `routes` array as follows:
```js
// app-routing.module.ts
const routes: Routes = [
//...
{
path: 'account',
loadChildren: () => import('@volo/abp.ng.account').then(m => m.AccountPublicModule.forLazy()),
},
//...
export class AppRoutingModule {}
```

5
docs/en/Migration-Guides/Abp-4_3.md

@ -0,0 +1,5 @@
# ABP v4.3 Migration Guide
## Angular UI
See the [Angular UI Migration Guide](Abp-4_3-Angular.md).

1
docs/en/Migration-Guides/Index.md

@ -1,5 +1,6 @@
# ABP Framework Migration Guides
* [4.2 to 4.3](Abp-4_3.md)
* [4.x to 4.2](Abp-4_2.md)
* [3.3.x to 4.0](Abp-4_0.md)
* [2.9.x to 3.0](../UI/Angular/Migration-Guide-v3.md)

20
docs/en/UI/AspNetCore/Navigation-Menu.md

@ -104,6 +104,7 @@ There are more options of a menu item (the constructor of the `ApplicationMenuIt
* `target` (`string`): Target of the menu item. Can be `null` (default), "\_*blank*", "\_*self*", "\_*parent*", "\_*top*" or a frame name for web applications.
* `elementId` (`string`): Can be used to render the element with a specific HTML `id` attribute.
* `cssClass` (`string`): Additional string classes for the menu item.
* `RequiredPermissionName` (`string`): The required permission name, this menu item will be removed if this permission is not granted.
### Authorization
@ -120,6 +121,25 @@ if (await context.IsGrantedAsync("MyPermissionName"))
}
````
For the authorization, you can use `RequiredPermissionName` as a shortcut. It is also more performant, ABP optimizes the permission check for all the items.
````csharp
context.Menu.AddItem(
new ApplicationMenuItem("MyProject.Crm", l["Menu:CRM"])
.AddItem(new ApplicationMenuItem(
name: "MyProject.Crm.Customers",
displayName: l["Menu:Customers"],
url: "/crm/customers",
requiredPermissionName: "MyProject.Crm.Customers")
).AddItem(new ApplicationMenuItem(
name: "MyProject.Crm.Orders",
displayName: l["Menu:Orders"],
url: "/crm/orders",
requiredPermissionName: "MyProject.Crm.Orders")
)
);
````
> You can use `context.AuthorizationService` to directly access to the `IAuthorizationService`.
### Resolving Dependencies

17
docs/en/UI/AspNetCore/Toolbars.md

@ -52,6 +52,21 @@ public class MyToolbarContributor : IToolbarContributor
}
````
You can use the [authorization](../../Authorization.md) to decide whether to add a `ToolbarItem`.
````csharp
if (await context.IsGrantedAsync("MyPermissionName"))
{
//...add Toolbar items
}
````
You can use `RequiredPermissionName` as a shortcut. It is also more performant, ABP optimizes the permission check for all the items.
````csharp
context.Toolbar.Items.Insert(0, new ToolbarItem(typeof(NotificationViewComponent), requiredPermissionName: "MyPermissionName"));
````
This class adds the `NotificationViewComponent` as the first item in the `Main` toolbar.
Finally, you need to add this contributor to the `AbpToolbarOptions`, in the `ConfigureServices` of your [module](../../Module-Development-Basics.md):
@ -71,4 +86,4 @@ That's all, you will see the notification icon on the toolbar when you run the a
## IToolbarManager
`IToolbarManager` is used to render the toolbar. It returns the toolbar items by a toolbar name. This is generally used by the [themes](Theming.md) to render the toolbar on the layout.
`IToolbarManager` is used to render the toolbar. It returns the toolbar items by a toolbar name. This is generally used by the [themes](Theming.md) to render the toolbar on the layout.

10
framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Localization/zh-Hant.json

@ -0,0 +1,10 @@
{
"culture": "zh-Hant",
"texts": {
"Volo.Authorization:010001": "認證失敗! Given policy has not granted.",
"Volo.Authorization:010002": "認證失敗! Given policy has not granted: {PolicyName}",
"Volo.Authorization:010003": "認證失敗! Given policy has not granted for given resource: {ResourceName}",
"Volo.Authorization:010004": "認證失敗! Given requirement has not granted for given resource: {ResourceName}",
"Volo.Authorization:010005": "認證失敗! Given requirements has not granted for given resource: {ResourceName}"
}
}

54
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RemoveProjectFromPrometheusStep.cs

@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps
{
public class RemoveProjectFromPrometheusStep : ProjectBuildPipelineStep
{
private readonly string _name;
public RemoveProjectFromPrometheusStep(string name)
{
_name = name;
}
public override void Execute(ProjectBuildContext context)
{
var tyeFile = context.Files.FirstOrDefault(f => f.Name == "/etc/prometheus/prometheus.yml");
if (tyeFile == null)
{
return;
}
var lines = tyeFile.GetLines();
var newLines = new List<string>();
var nameLine = $"- job_name:";
var isOneOfTargetLines = false;
foreach (var line in lines)
{
if (line.Trim().Equals($"{nameLine} '{_name}'"))
{
isOneOfTargetLines = true;
continue;
}
if (line.Trim().StartsWith(nameLine))
{
isOneOfTargetLines = false;
}
if (!isOneOfTargetLines)
{
newLines.Add(line);
}
}
tyeFile.SetContent(String.Join(Environment.NewLine, newLines));
}
}
}

4
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RemoveProjectFromTyeStep.cs

@ -30,13 +30,13 @@ namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps
foreach (var line in lines)
{
if (line.Equals($"{nameLine} {_name}"))
if (line.Trim().Equals($"{nameLine} {_name}"))
{
isOneOfTargetLines = true;
continue;
}
if (line.StartsWith(nameLine))
if (line.Trim().StartsWith(nameLine))
{
isOneOfTargetLines = false;
}

3
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Microservice/MicroserviceTemplateBase.cs

@ -37,6 +37,7 @@ namespace Volo.Abp.Cli.ProjectBuilding.Templates.Microservice
"/applications/web/src/MyCompanyName.MyProjectName.Web"));
steps.Add(new RemoveFolderStep("/applications/web"));
steps.Add(new RemoveProjectFromTyeStep("web"));
steps.Add(new RemoveProjectFromPrometheusStep("web"));
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.Blazor",null,
"/applications/blazor/src/MyCompanyName.MyProjectName.Blazor"));
@ -51,6 +52,7 @@ namespace Volo.Abp.Cli.ProjectBuilding.Templates.Microservice
"/applications/web/src/MyCompanyName.MyProjectName.Web"));
steps.Add(new RemoveFolderStep("/applications/web"));
steps.Add(new RemoveProjectFromTyeStep("web"));
steps.Add(new RemoveProjectFromPrometheusStep("web"));
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.Blazor",null,
"/applications/blazor/src/MyCompanyName.MyProjectName.Blazor"));
@ -64,6 +66,7 @@ namespace Volo.Abp.Cli.ProjectBuilding.Templates.Microservice
steps.Add(new RemoveFolderStep("/applications/web"));
steps.Add(new RemoveFolderStep("/angular"));
steps.Add(new RemoveProjectFromTyeStep("web"));
steps.Add(new RemoveProjectFromPrometheusStep("web"));
break;
case UiFramework.Mvc:

6
framework/src/Volo.Abp.ExceptionHandling/Volo/Abp/ExceptionHandling/Localization/zh-Hant.json

@ -18,6 +18,8 @@
"401Message": "未授權",
"403Message": "禁止訪問",
"404Message": "網頁未找到",
"500Message": "內部伺服器錯誤"
"500Message": "內部伺服器錯誤",
"403MessageDetail": "你不被授權執行此操作",
"404MessageDetail": "對不起,地址是空的"
}
}
}

8
framework/src/Volo.Abp.Features/Volo/Abp/Features/Localization/zh-Hant.json

@ -0,0 +1,8 @@
{
"culture": "zh-Hant",
"texts": {
"Volo.Feature:010001": "功能尚未啟用: {FeatureName}",
"Volo.Feature:010002": "請求的功能尚未啟用。這些功能被須全部啟用: {FeatureNames}",
"Volo.Feature:010003": "請求的功能尚未啟用。這些功能至少需啟用一個: {FeatureNames}"
}
}

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

@ -0,0 +1,6 @@
{
"culture": "zh-Hant",
"texts": {
"hello": "嗨"
}
}

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

@ -0,0 +1,7 @@
{
"culture": "zh-Hant",
"texts": {
"HelloText": "嗨 {0}",
"HowAreYou": "你好嗎?"
}
}

3
modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/zh-Hant.json

@ -44,6 +44,7 @@
"LoggedOutTitle": "註銷",
"LoggedOutText": "你已成功註銷並將馬上返回.",
"ReturnToText": "點擊此處返回到 {0}",
"OrLoginWith": "或是登入用:",
"ForgotPassword": "忘記密碼?",
"SendPasswordResetLink_Information": "密碼重置鏈接將發送到您的電子郵件以重置密碼. 如果您在幾分鐘內沒有收到電子郵件,請重試.",
"PasswordResetMailSentMessage": "帳戶恢復電子郵件已發送到您的電子郵件地址. 如果您在15分鐘內未在收件箱中看到此電子郵件,請檢查垃圾郵件,並標記為非垃圾郵件.",
@ -62,4 +63,4 @@
"AccessDenied": "拒絕訪問!",
"AccessDeniedMessage": "您無權訪問此資源."
}
}
}

6
modules/cms-kit/host/Volo.CmsKit.Web.Unified/CmsKitWebUnifiedModule.cs

@ -44,6 +44,7 @@ using Volo.CmsKit.Tags;
using Volo.CmsKit.Comments;
using Volo.CmsKit.MediaDescriptors;
using Volo.CmsKit.Reactions;
using Volo.CmsKit.Ratings;
namespace Volo.CmsKit
{
@ -165,6 +166,11 @@ namespace Volo.CmsKit
new ReactionDefinition(StandardReactions.ThumbsDown),
}));
});
Configure<CmsKitRatingOptions>(options =>
{
options.EntityTypes.Add(new RatingEntityTypeDefinition("quote"));
});
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)

4
modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/MediaDescriptors/MediaDescriptorAdminAppService.cs

@ -31,7 +31,7 @@ namespace Volo.CmsKit.Admin.MediaDescriptors
public virtual async Task<MediaDescriptorDto> CreateAsync(CreateMediaInputStream inputStream)
{
var definition = await MediaDescriptorDefinitionStore.GetDefinitionAsync(inputStream.EntityType);
var definition = await MediaDescriptorDefinitionStore.GetAsync(inputStream.EntityType);
/* TODO: Shouldn't CreatePolicies be a dictionary and we check for inputStream.EntityType? */
await CheckAnyOfPoliciesAsync(definition.CreatePolicies);
@ -51,7 +51,7 @@ namespace Volo.CmsKit.Admin.MediaDescriptors
{
var mediaDescriptor = await MediaDescriptorRepository.GetAsync(id);
var definition = await MediaDescriptorDefinitionStore.GetDefinitionAsync(mediaDescriptor.EntityType);
var definition = await MediaDescriptorDefinitionStore.GetAsync(mediaDescriptor.EntityType);
/* TODO: Shouldn't DeletePolicies be a dictionary and we check for inputStream.EntityType? */
await CheckAnyOfPoliciesAsync(definition.DeletePolicies);

6
modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Tags/EntityTagAdminAppService.cs

@ -29,7 +29,7 @@ namespace Volo.CmsKit.Admin.Tags
public virtual async Task AddTagToEntityAsync(EntityTagCreateDto input)
{
var definition = await TagDefinitionStore.GetTagEntityTypeDefinitionAsync(input.EntityType);
var definition = await TagDefinitionStore.GetAsync(input.EntityType);
await CheckAnyOfPoliciesAsync(definition.CreatePolicies);
@ -44,7 +44,7 @@ namespace Volo.CmsKit.Admin.Tags
public virtual async Task RemoveTagFromEntityAsync(EntityTagRemoveDto input)
{
var definition = await TagDefinitionStore.GetTagEntityTypeDefinitionAsync(input.EntityType);
var definition = await TagDefinitionStore.GetAsync(input.EntityType);
await CheckAnyOfPoliciesAsync(definition.DeletePolicies);
@ -57,7 +57,7 @@ namespace Volo.CmsKit.Admin.Tags
public virtual async Task SetEntityTagsAsync(EntityTagSetDto input)
{
var definition = await TagDefinitionStore.GetTagEntityTypeDefinitionAsync(input.EntityType);
var definition = await TagDefinitionStore.GetAsync(input.EntityType);
await CheckAnyOfPoliciesAsync(definition.UpdatePolicies);

20
modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/BlogPosts/Index.cshtml

@ -25,16 +25,20 @@
@await Component.InvokeAsync(typeof(AbpPageToolbarViewComponent), new { pageName = typeof(IndexModel).FullName })
}
<div id="CmsKitBlogPostsWrapper">
<abp-card>
<abp-card-body>
<abp-card class="mb-4">
<abp-card-body>
<div id="CmsKitBlogPostsWrapper">
<abp-row>
<abp-column>
@await Component.InvokeAsync(typeof(AbpPageSearchBoxViewComponent))
</abp-column>
</abp-row>
</abp-card-body>
</abp-card>
<abp-table striped-rows="true" id="BlogPostsTable" class="nowrap"></abp-table>
</div>
</div>
</abp-card-body>
</abp-card>
<abp-card>
<abp-card-body>
<abp-table striped-rows="true" id="BlogPostsTable" class="nowrap"></abp-table>
</abp-card-body>
</abp-card>

30
modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Comments/Index.cshtml

@ -15,14 +15,14 @@
PageLayout.Content.Title = L["Comments"].Value;
PageLayout.Content.BreadCrumb.Add(L["Menu:CMS"].Value);
PageLayout.Content.MenuItemName = CmsKitAdminMenus.Comments.CommentsMenu;
var defaultStartDate = DateTime.Now.AddDays(-7).Date.ToShortDateString();
}
@section styles{
<abp-style-bundle>
<abp-style src="/Pages/CmsKit/Comments/index.css" />
</abp-style-bundle>
</abp-style-bundle>
}
@section scripts {
@ -31,9 +31,9 @@
</abp-script-bundle>
}
<div id="CmsKitCommentsWrapper">
<abp-card>
<abp-card-body>
<abp-card class="mb-4">
<abp-card-body>
<div id="CmsKitCommentsWrapper">
<abp-row>
<abp-column>
<form id="CmsKitCommentsFilterForm" method="post">
@ -47,13 +47,13 @@
</abp-column>
</abp-column>
<abp-column size-lg="_2" size-md="_6">
<abp-input asp-for="@Model.EntityType" label="@L["EntityType"].Value" type="text"/>
<abp-input asp-for="@Model.EntityType" label="@L["EntityType"].Value" type="text" />
</abp-column>
<abp-column size-lg="_2" size-md="_6">
<abp-input asp-for="@Model.EntityId" label="@L["EntityId"].Value" type="text"/>
<abp-input asp-for="@Model.EntityId" label="@L["EntityId"].Value" type="text" />
</abp-column>
<abp-column size-lg="_2" size-md="_12">
<abp-input asp-for="@Model.Author" label="@L["Username"].Value" type="text"/>
<abp-input asp-for="@Model.Author" label="@L["Username"].Value" type="text" />
</abp-column>
<abp-column size-lg="_2" size-md="_12">
<abp-button class="mt-md-4" size="Block" button-type="Primary" type="submit">
@ -64,8 +64,12 @@
</form>
</abp-column>
</abp-row>
</abp-card-body>
</abp-card>
<abp-table striped-rows="true" id="CommentsTable" class="nowrap"></abp-table>
</div>
</div>
</abp-card-body>
</abp-card>
<abp-card>
<abp-card-body>
<abp-table striped-rows="true" id="CommentsTable" class="nowrap"></abp-table>
</abp-card-body>
</abp-card>

26
modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Pages/Index.cshtml

@ -1,6 +1,6 @@
@page
@using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Pages.Shared.Components.AbpPageToolbar
@using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Pages.Shared.Components.AbpPageSearchBox
@using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Pages.Shared.Components.AbpPageSearchBox
@using Volo.CmsKit.Admin.Web.Pages
@using Volo.CmsKit.Admin.Web.Menus
@using Volo.CmsKit.Admin.Web.Pages.CmsKit.Pages
@ -16,23 +16,27 @@
}
@section scripts {
<abp-script src="/Pages/CmsKit/Pages/index.js"/>
<abp-script src="/Pages/CmsKit/Pages/index.js" />
}
@section content_toolbar {
@await Component.InvokeAsync(typeof(AbpPageToolbarViewComponent), new {pageName = typeof(IndexModel).FullName})
@await Component.InvokeAsync(typeof(AbpPageToolbarViewComponent), new { pageName = typeof(IndexModel).FullName })
}
<div id="CmsKitPagesWrapper">
<abp-card>
<abp-card-body>
<abp-card class="mb-4">
<abp-card-body>
<div id="CmsKitPagesWrapper">
<abp-row>
<abp-column>
@await Component.InvokeAsync(typeof(AbpPageSearchBoxViewComponent))
</abp-column>
</abp-row>
</abp-card-body>
</abp-card>
<abp-table striped-rows="true" id="PagesTable" class="nowrap"></abp-table>
</div>
</div>
</abp-card-body>
</abp-card>
<abp-card>
<abp-card-body>
<abp-table striped-rows="true" id="PagesTable" class="nowrap"></abp-table>
</abp-card-body>
</abp-card>

18
modules/cms-kit/src/Volo.CmsKit.Admin.Web/Pages/CmsKit/Tags/Index.cshtml

@ -23,16 +23,20 @@
@await Component.InvokeAsync(typeof(AbpPageToolbarViewComponent), new { pageName = typeof(IndexModel).FullName })
}
<div id="CmsKitTagsWrapper">
<abp-card>
<abp-card-body>
<abp-card class="mb-4">
<abp-card-body>
<div id="CmsKitTagsWrapper">
<abp-row>
<abp-column>
@await Component.InvokeAsync(typeof(AbpPageSearchBoxViewComponent))
</abp-column>
</abp-row>
</abp-card-body>
</abp-card>
<abp-table striped-rows="true" id="TagsTable"></abp-table>
</div>
</div>
</abp-card-body>
</abp-card>
<abp-card>
<abp-card-body>
<abp-table striped-rows="true" id="TagsTable"></abp-table>
</abp-card-body>
</abp-card>

5
modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/CmsKitErrorCodes.cs

@ -13,6 +13,11 @@
public const string SlugAlreadyExist = "CmsKit:Page:0001";
}
public static class Ratings
{
public const string EntityCantHaveRating = "CmsKit:Rating:0001";
}
public static class Reactions
{
public const string EntityCantHaveReaction = "CmsKit:Reaction:0001";

4
modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/en.json

@ -21,6 +21,7 @@
"CmsKit:Media:0001": "'{Name}' is not a valid media name.",
"CmsKit:Media:0002": "The entity can't have media.",
"CmsKit:Page:0001": "The given url ({0}) already exists.",
"CmsKit:Rating:0001": "The entity {EntityType} can't be rated.",
"CmsKit:Reaction:0001": "The entity {EntityType} can't have reactions.",
"CmsKit:Tag:0002": "The entity is not taggable!",
"CommentAuthorizationExceptionMessage": "Those comments are not allowed for public display.",
@ -45,6 +46,7 @@
"LastModification": "Last Modification",
"LoginToAddComment": "Login to add comment",
"LoginToRate": "Login to rate",
"LoginToReact": "Login to react",
"LoginToReply": "Login to reply",
"Menu:CMS": "CMS",
"Message": "Message",
@ -82,7 +84,9 @@
"Permission:TagManagement.Delete": "Delete",
"Permission:TagManagement.Update": "Update",
"PickYourReaction": "Pick your reaction",
"Rating": "Rating",
"RatingUndoMessage": "Your rating will be undo.",
"Reactions": "Reactions",
"Read": "Read",
"RepliesToThisComment": "Replies to this comment",
"Reply": "Reply",

8
modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/tr.json

@ -13,13 +13,14 @@
"CmsKit.Ratings": "Puanlama",
"CmsKit.Reactions": "Tepkiler",
"CmsKit.Tags": "Etiketler",
"CmsKit:Comments:0001": "{0} ögesi yorumlanabilir değil.",
"CmsKit:0002": "İçerik zaten mevcut!",
"CmsKit:0003": "{0} ögesi etiketlenebilir değil.",
"CmsKit:BlogPost:0001": "Aynı url etiketi zaten mevcut.",
"CmsKit:Comments:0001": "{0} ögesi yorumlanabilir değil.",
"CmsKit:Media:0002": "Bu öge için medya eklenemez.",
"CmsKit:Reaction:0001": "Bu ögeye tepki verilemez.",
"CmsKit:Page:0001": "Girilen url ({0}) kullanımdadır.",
"CmsKit:Rating:0001": "{EntityType}, puanlanabilir değil.",
"CmsKit:Reaction:0001": "Bu ögeye tepki verilemez.",
"CmsKit:Tag:0002": "Bu öge etiketlenebilir değil.",
"CommentAuthorizationExceptionMessage": "Bu yorumları görebilmek için yetki gerekir.",
"CommentDeletionConfirmationMessage": "Bu yorum ve buna yapılan tüm yorumlan silinecektir!",
@ -44,6 +45,7 @@
"LastModification": "Son Güncellenme",
"LoginToAddComment": "Yorum yapmak için giriş yap",
"LoginToRate": "Oylamak için giriş yapın",
"LoginToReact": "Reaksiyon vermek için giriş yap",
"LoginToReply": "Cevap vermek için giriş yap",
"Menu:CMS": "CMS",
"Message": "Mesaj",
@ -72,7 +74,9 @@
"Permission:TagManagement.Delete": "Etiket Silme",
"Permission:TagManagement.Update": "Etiket Güncelleme",
"PickYourReaction": "Tepkinizi seçin",
"Rating": "Puan",
"RatingUndoMessage": "Oylamanız geri alınacak.",
"Reactions": "Reaksiyonlar",
"Read": "Oku",
"RepliesToThisComment": "Bu yoruma yapılan yorumlar",
"Reply": "Cevapla",

125
modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/zh-Hant.json

@ -0,0 +1,125 @@
{
"culture": "zh-Hant",
"texts": {
"BlogDeletionConfirmationMessage": "部落格 '{0}' 將被刪除. 你確定嗎?",
"BlogFeatureNotAvailable": "目前此功能不可使用. 請用 `GlobalFeatureManager` 來啟用它.",
"BlogId": "部落格",
"BlogPostDeletionConfirmationMessage": "部落格貼文 '{0}' 將被刪除. 你確定嗎?",
"BlogPosts": "部落格貼文",
"Blogs": "部落格",
"ChoosePreference": "選擇 Preference...",
"Cms": "Cms",
"CmsKit.Comments": "評論",
"CmsKit.Ratings": "評分",
"CmsKit.Reactions": "反應",
"CmsKit.Tags": "標籤",
"CmsKit:0002": "內容已經存在!",
"CmsKit:0003": "實體 {0} 不可標記.",
"CmsKit:Blog:0001": "給定的slug ({Slug}) 已經存在!",
"CmsKit:BlogPost:0001": "給定的slug已經存在!",
"CmsKit:Comments:0001": "實體不可 {0} 不可評論.",
"CmsKit:Media:0001": "'{Name}' 不是有效的媒體名稱.",
"CmsKit:Media:0002": "實體不可以含有媒體",
"CmsKit:Page:0001": "給定的url ({0}) 已經存在.",
"CmsKit:Reaction:0001": "實體 {EntityType} 不能有反應",
"CmsKit:Tag:0002": "實體不可標記!",
"CommentAuthorizationExceptionMessage": "些評論不允許公開顯示",
"CommentDeletionConfirmationMessage": "此評論和所有回覆將被刪除!",
"Comments": "評論",
"ContentDeletionConfirmationMessage": "你確定要刪除這個內容嗎?",
"Contents": "內容",
"CoverImage": "封面圖片",
"CreateBlogPostPage": "新部落格貼文",
"CreationTime": "建立時間",
"Delete": "刪除",
"Detail": "詳情",
"Details": "詳情",
"DoYouPreferAdditionalEmails": "你是否更喜歡額外的郵件",
"Edit": "修改",
"EndDate": "結束時間",
"EntityId": "實體Id",
"EntityType": "實體類型",
"ExportCSV": "匯出 CSV",
"Features": "功能",
"GenericDeletionConfirmationMessage": "你確定刪除 '{0}' 嗎?",
"LastModification": "最後一次修改",
"LoginToAddComment": "登錄後添加評論",
"LoginToRate": "登錄後進行評分",
"LoginToReply": "登錄後進行回覆",
"Menu:CMS": "CMS",
"Message": "消息",
"MessageDeletionConfirmationMessage": "這條評論將被完全刪除",
"Name": "名稱",
"New": "新",
"OK": "好",
"PageDeletionConfirmationMessage": "你確定刪除這個頁面嗎?",
"PageSlugInformation": "Slug用於網址. 你的網址將是 '/pages/{{slug}}'.",
"Permission:BlogManagement": "部落格管理",
"Permission:BlogManagement.Create": "創建",
"Permission:BlogManagement.Delete": "刪除",
"Permission:BlogManagement.Features": "功能",
"Permission:BlogManagement.Update": "更新",
"Permission:BlogPostManagement": "部落格貼文管理",
"Permission:BlogPostManagement.Create": "創建",
"Permission:BlogPostManagement.Delete": "刪除",
"Permission:BlogPostManagement.Update": "更新",
"Permission:CmsKit": "Cms工具包",
"Permission:Comments": "評論管理",
"Permission:Comments.Delete": "刪除",
"Permission:Contents": "內容管理",
"Permission:Contents.Create": "創建內容",
"Permission:Contents.Delete": "刪除內容",
"Permission:Contents.Update": "更新內容",
"Permission:MediaDescriptorManagement": "媒體管理",
"Permission:MediaDescriptorManagement:Create": "創建",
"Permission:MediaDescriptorManagement:Delete": "刪除",
"Permission:PageManagement": "頁面管理",
"Permission:PageManagement:Create": "創建",
"Permission:PageManagement:Delete": "刪除",
"Permission:PageManagement:Update": "更新",
"Permission:TagManagement": "標籤管理",
"Permission:TagManagement.Create": "創建",
"Permission:TagManagement.Delete": "刪除",
"Permission:TagManagement.Update": "更新",
"PickYourReaction": "選擇你的回應",
"RatingUndoMessage": "您的評分將被收回",
"Read": "閱讀",
"RepliesToThisComment": "回覆此評論",
"Reply": "回覆",
"ReplyTo": "回覆給",
"SamplePageMessage": "Pro模組的展示頁面",
"SaveChanges": "保存更改",
"SelectAll": "選擇全部",
"Send": "發送",
"SendMessage": "發送消息",
"ShortDescription": "簡介",
"Slug": "Slug",
"Source": "來源",
"SourceUrl": "來源 Url",
"Star": "星",
"StartDate": "開始時間",
"Subject": "主題",
"SubjectPlaceholder": "請輸入主題",
"Submit": "提交",
"Subscribe": "訂閱",
"SuccessfullyDeleted": "刪除成功!",
"SuccessfullySaved": "保存成功!",
"TagDeletionConfirmationMessage": "你確定刪除 '{0}' 標籤嗎?",
"Tags": "標籤",
"Text": "文本",
"ThankYou": "謝謝你",
"Title": "標題",
"Undo": "復原",
"Update": "更新",
"UpdatePreferenceSuccessMessage": "您的 preferences 已經保存",
"UpdateYourEmailPreferences": "更新你的郵件preferences",
"UploadFailedMessage": "上傳失敗",
"UserId": "用戶Id",
"Username": "用戶名稱",
"YourComment": "你的評論",
"YourEmailAddress": "你的郵件地址",
"YourFullName": "你的全名",
"YourMessage": "你的消息",
"YourReply": "你的回覆"
}
}

23
modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/CmsKitDomainModule.cs

@ -1,19 +1,15 @@
using Volo.Abp.BlobStoring;
using Microsoft.Extensions.Options;
using System.Collections.Generic;
using Volo.Abp;
using Volo.Abp.Domain;
using Volo.Abp.GlobalFeatures;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Volo.Abp.Users;
using Volo.CmsKit.Blogs;
using Volo.CmsKit.Comments;
using Volo.CmsKit.GlobalFeatures;
using Volo.CmsKit.Localization;
using Volo.CmsKit.Pages;
using Volo.CmsKit.Ratings;
using Volo.CmsKit.Reactions;
using Volo.CmsKit.Tags;
using Volo.CmsKit.Blogs;
using Volo.CmsKit.Comments;
namespace Volo.CmsKit
{
@ -67,6 +63,19 @@ namespace Volo.CmsKit
});
}
if (GlobalFeatureManager.Instance.IsEnabled<RatingsFeature>())
{
Configure<CmsKitRatingOptions>(options =>
{
if (GlobalFeatureManager.Instance.IsEnabled<BlogsFeature>())
{
options.EntityTypes.Add(new RatingEntityTypeDefinition(BlogPostConsts.EntityType));
}
// TODO: Define entity types here which can be rated.
});
}
if (GlobalFeatureManager.Instance.IsEnabled<TagsFeature>())
{
// TODO: Configure TagEntityTypes here...

11
modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Comments/CommentEntityTypeDefinition.cs

@ -4,18 +4,11 @@ using Volo.Abp;
namespace Volo.CmsKit.Comments
{
public class CommentEntityTypeDefinition : IEquatable<CommentEntityTypeDefinition>
public class CommentEntityTypeDefinition : EntityTypeDefinition
{
public CommentEntityTypeDefinition([NotNull] string entityType)
public CommentEntityTypeDefinition([NotNull] string entityType) : base(entityType)
{
EntityType = Check.NotNullOrEmpty(entityType, nameof(entityType));
}
public string EntityType { get; }
public bool Equals(CommentEntityTypeDefinition other)
{
return EntityType == other?.EntityType;
}
}
}

4
modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Comments/DefaultCommentEntityTypeDefinitionStore.cs

@ -10,7 +10,7 @@ using Volo.Abp.DependencyInjection;
namespace Volo.CmsKit.Comments
{
public class DefaultCommentEntityTypeDefinitionStore : ICommentEntityTypeDefinitionStore, ITransientDependency
public class DefaultCommentEntityTypeDefinitionStore : ICommentEntityTypeDefinitionStore
{
protected CmsKitCommentOptions Options { get; }
@ -19,7 +19,7 @@ namespace Volo.CmsKit.Comments
Options = options.Value;
}
public virtual Task<CommentEntityTypeDefinition> GetDefinitionAsync([NotNull] string entityType)
public virtual Task<CommentEntityTypeDefinition> GetAsync([NotNull] string entityType)
{
Check.NotNullOrWhiteSpace(entityType, nameof(entityType));

14
modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Comments/ICommentEntityTypeDefinitionStore.cs

@ -1,17 +1,7 @@
using JetBrains.Annotations;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Volo.CmsKit.Comments;
namespace Volo.CmsKit.Comments
namespace Volo.CmsKit.Comments
{
public interface ICommentEntityTypeDefinitionStore
public interface ICommentEntityTypeDefinitionStore : IEntityTypeDefinitionStore<CommentEntityTypeDefinition>
{
Task<CommentEntityTypeDefinition> GetDefinitionAsync([NotNull] string entityType);
Task<bool> IsDefinedAsync([NotNull] string entityType);
}
}

22
modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/EntityTypeDefinition.cs

@ -0,0 +1,22 @@
using JetBrains.Annotations;
using System;
using Volo.Abp;
namespace Volo.CmsKit
{
public abstract class EntityTypeDefinition : IEquatable<EntityTypeDefinition>
{
public EntityTypeDefinition([NotNull] string entityType)
{
EntityType = Check.NotNullOrEmpty(entityType, nameof(entityType));
}
[NotNull]
public string EntityType { get; protected set; }
public bool Equals(EntityTypeDefinition other)
{
return EntityType == other?.EntityType;
}
}
}

14
modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/IEntityTypeDefinitionStore.cs

@ -0,0 +1,14 @@
using JetBrains.Annotations;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
namespace Volo.CmsKit
{
public interface IEntityTypeDefinitionStore<TPolicyDefinition> : ITransientDependency
where TPolicyDefinition : EntityTypeDefinition
{
Task<TPolicyDefinition> GetAsync([NotNull] string entityType);
Task<bool> IsDefinedAsync([NotNull] string entityType);
}
}

4
modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/DefaultMediaDescriptorDefinitionStore.cs

@ -10,7 +10,7 @@ using Volo.Abp.DependencyInjection;
namespace Volo.CmsKit.MediaDescriptors
{
public class DefaultMediaDescriptorDefinitionStore : IMediaDescriptorDefinitionStore, ITransientDependency
public class DefaultMediaDescriptorDefinitionStore : IMediaDescriptorDefinitionStore
{
protected CmsKitMediaOptions Options { get; }
@ -25,7 +25,7 @@ namespace Volo.CmsKit.MediaDescriptors
/// <param name="entityType">EntityType to get definition.</param>
/// <exception cref="EntityCantHaveMediaException">Thrown when EntityType is not configured as taggable.</exception>
/// <exception cref="InvalidOperationException">More than one element satisfies the condition in predicate.</exception>
public virtual Task<MediaDescriptorDefinition> GetDefinitionAsync([NotNull] string entityType)
public virtual Task<MediaDescriptorDefinition> GetAsync([NotNull] string entityType)
{
Check.NotNullOrWhiteSpace(entityType, nameof(entityType));

9
modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/MediaDescriptors/IMediaDescriptorDefinitionStore.cs

@ -1,12 +1,7 @@
using JetBrains.Annotations;
using System.Threading.Tasks;
namespace Volo.CmsKit.MediaDescriptors
namespace Volo.CmsKit.MediaDescriptors
{
public interface IMediaDescriptorDefinitionStore
public interface IMediaDescriptorDefinitionStore : IEntityTypeDefinitionStore<MediaDescriptorDefinition>
{
Task<bool> IsDefinedAsync([NotNull] string entityType);
Task<MediaDescriptorDefinition> GetDefinitionAsync([NotNull] string entityType);
}
}

13
modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/PolicySpecifiedDefinition.cs

@ -6,20 +6,14 @@ using Volo.Abp;
namespace Volo.CmsKit
{
public abstract class PolicySpecifiedDefinition : IEquatable<PolicySpecifiedDefinition>
public abstract class PolicySpecifiedDefinition : EntityTypeDefinition, IEquatable<PolicySpecifiedDefinition>
{
protected PolicySpecifiedDefinition()
{
}
public PolicySpecifiedDefinition(
[NotNull] string entityType,
IEnumerable<string> createPolicies = null,
IEnumerable<string> updatePolicies = null,
IEnumerable<string> deletePolicies = null)
IEnumerable<string> deletePolicies = null) : base(entityType)
{
EntityType = Check.NotNullOrEmpty(entityType, nameof(entityType));
if (createPolicies != null)
{
CreatePolicies = CreatePolicies.Concat(createPolicies).ToList();
@ -36,9 +30,6 @@ namespace Volo.CmsKit
}
}
[NotNull]
public string EntityType { get; set; }
[NotNull]
public virtual ICollection<string> CreatePolicies { get; } = new List<string>();

11
modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Ratings/CmsKitRatingOptions.cs

@ -0,0 +1,11 @@
using JetBrains.Annotations;
using System.Collections.Generic;
namespace Volo.CmsKit.Ratings
{
public class CmsKitRatingOptions
{
[NotNull]
public List<RatingEntityTypeDefinition> EntityTypes { get; } = new ();
}
}

38
modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Ratings/DefaultRatingEntityTypeDefinitionStore.cs

@ -0,0 +1,38 @@
using JetBrains.Annotations;
using Microsoft.Extensions.Options;
using System;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp;
namespace Volo.CmsKit.Ratings
{
public class DefaultRatingEntityTypeDefinitionStore : IRatingEntityTypeDefinitionStore
{
protected CmsKitRatingOptions Options { get; }
public DefaultRatingEntityTypeDefinitionStore(IOptions<CmsKitRatingOptions> options)
{
Options = options.Value;
}
public virtual Task<RatingEntityTypeDefinition> GetAsync([NotNull] string entityType)
{
Check.NotNullOrWhiteSpace(entityType, nameof(entityType));
var definition = Options.EntityTypes.SingleOrDefault(x => x.EntityType.Equals(entityType, StringComparison.InvariantCultureIgnoreCase)) ??
throw new EntityCantHaveRatingException(entityType);
return Task.FromResult(definition);
}
public virtual Task<bool> IsDefinedAsync([NotNull] string entityType)
{
Check.NotNullOrWhiteSpace(entityType, nameof(entityType));
var isDefined = Options.EntityTypes.Any(x => x.EntityType.Equals(entityType, StringComparison.InvariantCultureIgnoreCase));
return Task.FromResult(isDefined);
}
}
}

22
modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Ratings/EntityCantHaveRatingException.cs

@ -0,0 +1,22 @@
using JetBrains.Annotations;
using System.Runtime.Serialization;
using Volo.Abp;
namespace Volo.CmsKit.Ratings
{
public class EntityCantHaveRatingException : BusinessException
{
public EntityCantHaveRatingException(SerializationInfo serializationInfo, StreamingContext context) : base(serializationInfo, context)
{
}
public EntityCantHaveRatingException([NotNull] string entityType)
{
Code = CmsKitErrorCodes.Ratings.EntityCantHaveRating;
EntityType = Check.NotNullOrEmpty(entityType, nameof(entityType));
WithData(nameof(EntityType), EntityType);
}
public string EntityType { get; }
}
}

9
modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Ratings/IRatingEntityTypeDefinitionStore.cs

@ -0,0 +1,9 @@
using Volo.CmsKit.Ratings;
namespace Volo.CmsKit.Ratings
{
public interface IRatingEntityTypeDefinitionStore : IEntityTypeDefinitionStore<RatingEntityTypeDefinition>
{
}
}

2
modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Ratings/Rating.cs

@ -25,7 +25,7 @@ namespace Volo.CmsKit.Ratings
}
public Rating(
internal Rating(
Guid id,
[NotNull] string entityType,
[NotNull] string entityId,

12
modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Ratings/RatingEntityTypeDefinition.cs

@ -0,0 +1,12 @@
using JetBrains.Annotations;
namespace Volo.CmsKit.Ratings
{
public class RatingEntityTypeDefinition : EntityTypeDefinition
{
public RatingEntityTypeDefinition(
[NotNull] string entityType) : base(entityType)
{
}
}
}

52
modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Ratings/RatingManager.cs

@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp.Domain.Services;
using Volo.CmsKit.Users;
namespace Volo.CmsKit.Ratings
{
public class RatingManager : DomainService
{
protected IRatingRepository RatingRepository { get; }
protected IRatingEntityTypeDefinitionStore RatingDefinitionStore { get; }
public RatingManager(
IRatingRepository ratingRepository,
IRatingEntityTypeDefinitionStore ratingDefinitionStore)
{
RatingRepository = ratingRepository;
RatingDefinitionStore = ratingDefinitionStore;
}
public async Task<Rating> SetStarAsync(CmsUser user, string entityType, string entityId, short starCount)
{
var currentUserRating = await RatingRepository.GetCurrentUserRatingAsync(entityType, entityId, user.Id);
if (currentUserRating != null)
{
currentUserRating.SetStarCount(starCount);
return await RatingRepository.UpdateAsync(currentUserRating);
}
if (!await RatingDefinitionStore.IsDefinedAsync(entityType))
{
throw new EntityCantHaveRatingException(entityType);
}
return await RatingRepository.InsertAsync(
new Rating(
GuidGenerator.Create(),
entityType,
entityId,
starCount,
user.Id,
CurrentTenant.Id
)
);
}
}
}

8
modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Reactions/DefaultReactionDefinitionStore.cs

@ -9,7 +9,7 @@ using Volo.Abp.DependencyInjection;
namespace Volo.CmsKit.Reactions
{
public class DefaultReactionDefinitionStore : IReactionDefinitionStore, ITransientDependency
public class DefaultReactionDefinitionStore : IReactionDefinitionStore
{
protected CmsKitReactionOptions Options { get; }
@ -46,6 +46,12 @@ namespace Volo.CmsKit.Reactions
return Task.FromResult(isDefined);
}
/// <summary>
/// Gets single <see cref="ReactionEntityTypeDefinition"/> by entityType.
/// </summary>
/// <param name="entityType">EntityType to get definition.</param>
/// <exception cref="EntityCantHaveReactionException">Thrown when EntityType is not configured as taggable.</exception>
/// <exception cref="InvalidOperationException">More than one element satisfies the condition in predicate.</exception>
public virtual Task<ReactionEntityTypeDefinition> GetAsync([NotNull] string entityType)
{
Check.NotNullOrWhiteSpace(entityType, nameof(entityType));

6
modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Reactions/IReactionDefinitionStore.cs

@ -4,12 +4,8 @@ using JetBrains.Annotations;
namespace Volo.CmsKit.Reactions
{
public interface IReactionDefinitionStore
public interface IReactionDefinitionStore : IEntityTypeDefinitionStore<ReactionEntityTypeDefinition>
{
Task<bool> IsDefinedAsync([NotNull]string entityType);
Task<ReactionEntityTypeDefinition> GetAsync([NotNull] string entityType);
Task<List<ReactionDefinition>> GetReactionsAsync([NotNull] string entityType);
Task<ReactionDefinition> GetReactionOrNullAsync([NotNull] string reactionName, [NotNull] string entityType);

4
modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/DefaultTagDefinitionStore.cs

@ -9,7 +9,7 @@ using Volo.Abp.DependencyInjection;
namespace Volo.CmsKit.Tags
{
public class DefaultTagDefinitionStore : ITagDefinitionStore, ITransientDependency
public class DefaultTagDefinitionStore : ITagDefinitionStore
{
protected CmsKitTagOptions CmsKitTagOptions { get; }
@ -24,7 +24,7 @@ namespace Volo.CmsKit.Tags
/// <param name="entityType">EntityType to get definition.</param>
/// <exception cref="EntityNotTaggableException">Thrown when EntityType is not configured as taggable.</exception>
/// <exception cref="InvalidOperationException">More than one element satisfies the condition in predicate.</exception>
public virtual Task<TagEntityTypeDefiniton> GetTagEntityTypeDefinitionAsync([NotNull] string entityType)
public virtual Task<TagEntityTypeDefiniton> GetAsync([NotNull] string entityType)
{
Check.NotNullOrWhiteSpace(entityType, nameof(entityType));

6
modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/ITagDefinitionStore.cs

@ -4,12 +4,8 @@ using System.Threading.Tasks;
namespace Volo.CmsKit.Tags
{
public interface ITagDefinitionStore
public interface ITagDefinitionStore : IEntityTypeDefinitionStore<TagEntityTypeDefiniton>
{
Task<List<TagEntityTypeDefiniton>> GetTagEntityTypeDefinitionListAsync();
Task<TagEntityTypeDefiniton> GetTagEntityTypeDefinitionAsync([NotNull] string entityType);
Task<bool> IsDefinedAsync([NotNull] string entityType);
}
}

5
modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/TagEntityTypeDefiniton.cs

@ -11,10 +11,6 @@ namespace Volo.CmsKit.Tags
[CanBeNull]
public virtual ILocalizableString DisplayName { get; }
protected TagEntityTypeDefiniton()
{
}
public TagEntityTypeDefiniton(
[NotNull] string entityType,
[CanBeNull] ILocalizableString displayName = null,
@ -23,7 +19,6 @@ namespace Volo.CmsKit.Tags
IEnumerable<string> deletePolicies = null) : base(entityType, createPolicies, updatePolicies, deletePolicies)
{
DisplayName = displayName;
}
public bool Equals(TagEntityTypeDefiniton other)

28
modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Ratings/RatingPublicAppService.cs

@ -13,11 +13,16 @@ namespace Volo.CmsKit.Public.Ratings
{
protected IRatingRepository RatingRepository { get; }
public ICmsUserLookupService CmsUserLookupService { get; }
protected RatingManager RatingManager { get; }
public RatingPublicAppService(IRatingRepository ratingRepository, ICmsUserLookupService cmsUserLookupService)
public RatingPublicAppService(
IRatingRepository ratingRepository,
ICmsUserLookupService cmsUserLookupService,
RatingManager ratingManager)
{
RatingRepository = ratingRepository;
CmsUserLookupService = cmsUserLookupService;
RatingManager = ratingManager;
}
[Authorize]
@ -27,26 +32,7 @@ namespace Volo.CmsKit.Public.Ratings
var userId = CurrentUser.GetId();
var user = await CmsUserLookupService.GetByIdAsync(userId);
var currentUserRating = await RatingRepository.GetCurrentUserRatingAsync(entityType, entityId, userId);
if (currentUserRating != null)
{
currentUserRating.SetStarCount(input.StarCount);
var updatedRating = await RatingRepository.UpdateAsync(currentUserRating);
return ObjectMapper.Map<Rating, RatingDto>(updatedRating);
}
var rating = await RatingRepository.InsertAsync(
new Rating(
GuidGenerator.Create(),
entityType,
entityId,
input.StarCount,
user.Id,
CurrentTenant.Id
)
);
var rating = await RatingManager.SetStarAsync(user, entityType, entityId, input.StarCount);
return ObjectMapper.Map<Rating, RatingDto>(rating);
}

28
modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Blogs/BlogPost/Default.cshtml

@ -1,28 +0,0 @@
@model Volo.CmsKit.Public.Blogs.BlogPostPublicDto
@using Volo.CmsKit.Localization
@using Microsoft.Extensions.Localization
@inject IStringLocalizer<CmsKitResource> L
@{
const string dummyImageSource = "https://dummyimage.com/300x200/a3a3a3/fff.png";
}
<abp-card>
<img src="/api/cms-kit/media/@Model.CoverImageMediaId" class="card-img-top" onerror="this.src='@dummyImageSource'" />
<abp-card-body>
<abp-card-title>@Model.Title</abp-card-title>
<abp-card-subtitle>@Model.Author?.UserName</abp-card-subtitle>
@Html.Raw(Model.Content)
</abp-card-body>
<abp-card-footer>
<abp-card-subtitle>@Model.CreationTime</abp-card-subtitle>
@if (Model.LastModificationTime != null)
{
<abp-card-text><i>@L["LastModification"].Value : @Model.LastModificationTime</i></abp-card-text>
}
</abp-card-footer>
</abp-card>

29
modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Blogs/BlogPost/DefaultBlogPostViewComponent.cs

@ -1,29 +0,0 @@
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp.AspNetCore.Mvc;
using Volo.CmsKit.Public.Blogs;
namespace Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Blogs.BlogPost
{
[ViewComponent(Name = "CmsDefaultBlogPost")]
public class DefaultBlogPostViewComponent : AbpViewComponent
{
protected IBlogPostPublicAppService BlogPostPublicAppService { get; }
public DefaultBlogPostViewComponent(IBlogPostPublicAppService blogPostPublicAppService)
{
BlogPostPublicAppService = blogPostPublicAppService;
}
public virtual async Task<IViewComponentResult> InvokeAsync(string blogSlug, string blogPostSlug)
{
var blogPost = await BlogPostPublicAppService.GetAsync(blogSlug, blogPostSlug);
return View("~/Pages/CmsKit/Shared/Components/Blogs/BlogPost/Default.cshtml", blogPost);
}
}
}

11
modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Blogs/BlogPostComment/Default.cshtml

@ -7,16 +7,9 @@
@model DefaultBlogPostCommentViewComponent.DefaultBlogPostCommentViewModel
<abp-card>
<abp-card-header>
<abp-card-title>@L["Comments"]</abp-card-title>
</abp-card-header>
<abp-card-body>
@await Component.InvokeAsync(typeof(CommentingViewComponent), new
{
entityType = Model.EntityType,
entityId = Model.EntityId
})
</abp-card-body>
</abp-card>
})

57
modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/Default.cshtml

@ -25,7 +25,7 @@
}
@{
Func<dynamic, IHtmlContent> GetCommentArea(Guid? repliedCommentId, bool cancelButton = false) =>
@<div class="cms-comment-form-area bg-light card p-3 mx-0 mt-4 @(repliedCommentId.HasValue ? "my-1" : "mb-0")"
@<div class="cms-comment-form-area bg-light card p-3 mx-0 @(repliedCommentId.HasValue ? "my-3" : "mt-3")"
data-reply-id="@(repliedCommentId?.ToString() ?? "")"
style="@(string.IsNullOrEmpty(repliedCommentId?.ToString() ?? "") ? "" : "display:none")">
<form class="cms-comment-form">
@ -113,7 +113,7 @@
</div>;
}
<div class="cms-comment-area" data-entity-type="@Model.EntityType" data-entity-id="@Model.EntityId">
<div class="cms-comment-area mb-5" data-entity-type="@Model.EntityType" data-entity-id="@Model.EntityId">
@if (CurrentUser.IsAuthenticated)
{
<div id="@($"cms-comment_{Model.EntityType}_{Model.EntityId}")">
@ -129,28 +129,28 @@
@foreach (var comment in Model.Comments)
{
<div class="comment">
<div class="card p-3 mx-0 my-4">
<div class="card p-3 mx-0 my-3">
<h5>
@GetCommentTitle(comment.Author, comment.CreationTime).Invoke(null)
</h5>
@GetCommentContentArea(comment.Id, comment.Text).Invoke(null)
<div class="form-row mt-2">
<div class="col">
<div class="my-2 ">
@GetCommentActionArea(comment.Id, comment.Author.Id, false).Invoke(null)
<div class="form-row mt-2">
<div class="col">
<div class="my-2 ">
@GetCommentActionArea(comment.Id, comment.Author.Id, false).Invoke(null)
</div>
</div>
</div>
<div class="col-auto">
<div class="reaction-in-comment">
@if (cmsKitUiOptions.Value.CommentsOptions.IsReactionsEnabled && GlobalFeatureManager.Instance.IsEnabled<ReactionsFeature>())
{
@await Component.InvokeAsync(typeof(ReactionSelectionViewComponent), new { entityType = "comment", entityId = comment.Id.ToString() })
}
<div class="col-auto">
<div class="reaction-in-comment">
@if (cmsKitUiOptions.Value.CommentsOptions.IsReactionsEnabled && GlobalFeatureManager.Instance.IsEnabled<ReactionsFeature>())
{
@await Component.InvokeAsync(typeof(ReactionSelectionViewComponent), new { entityType = "comment", entityId = comment.Id.ToString() })
}
</div>
</div>
</div>
</div>
@GetEditArea(comment.Id, comment.Text).Invoke(null)
@if (comment.Replies.Any())
@ -165,21 +165,21 @@
@GetCommentContentArea(reply.Id, reply.Text).Invoke(null)
<div class="form-row mt-2">
<div class="col">
<div class="my-2 ">
@GetCommentActionArea(reply.Id, reply.Author.Id, true).Invoke(null)
<div class="form-row mt-2">
<div class="col">
<div class="my-2 ">
@GetCommentActionArea(reply.Id, reply.Author.Id, true).Invoke(null)
</div>
</div>
</div>
<div class="col-auto">
<div class="reaction-in-comment">
@if (cmsKitUiOptions.Value.CommentsOptions.IsReactionsEnabled && GlobalFeatureManager.Instance.IsEnabled<ReactionsFeature>())
{
@await Component.InvokeAsync(typeof(ReactionSelectionViewComponent), new { entityType = "comment", entityId = reply.Id.ToString() })
}
<div class="col-auto">
<div class="reaction-in-comment">
@if (cmsKitUiOptions.Value.CommentsOptions.IsReactionsEnabled && GlobalFeatureManager.Instance.IsEnabled<ReactionsFeature>())
{
@await Component.InvokeAsync(typeof(ReactionSelectionViewComponent), new { entityType = "comment", entityId = reply.Id.ToString() })
}
</div>
</div>
</div>
</div>
@GetEditArea(reply.Id, reply.Text).Invoke(null)
</div>
@ -190,7 +190,7 @@
<div class=" mt-2">
@if (CurrentUser.IsAuthenticated)
{
<a href="#" class="comment-links comment-reply-link btn btn-primary btn-sm" data-reply-id="@comment.Id.ToString()">
<a href="#" class="comment-links comment-reply-link btn btn-primary btn-sm" data-reply-id="@comment.Id.ToString()">
<i class="fa fa-reply mr-1"></i> @L["Reply"]
</a>
}
@ -210,3 +210,4 @@
</div>
}
</div>

105
modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Rating/Default.cshtml

@ -5,57 +5,68 @@
@model Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Rating.RatingViewModel
@inject IHtmlLocalizer<CmsKitResource> L
<div class="text-center text-md-left">
<div class="cms-rating-area d-inline-block px-2 py-1 my-2 card border-0 shadow-sm " data-entity-type="@Model.EntityType" data-entity-id="@Model.EntityId" id="cms-rating_{@Model.EntityType}_{@Model.EntityId}">
@if (CurrentUser.IsAuthenticated)
{
<span class="my-rating" data-rating="@(Model.CurrentRating ?? 0)" data-authenticated="@(Model.CurrentRating != null)"></span>
<small class="live-rating text-center d-inline-block" style="width: 24px">@(Model.CurrentRating != null ? Model.CurrentRating + " " : 0 + "")</small>
@if (Model.CurrentRating != null)
{
<a href="#" class="rating-undo-link">
<small class="text-muted"><i class="fa fa-undo"></i> @L["Undo"]</small>
</a>
}
if (Model.Ratings != null)
{
<a href="#" class="text-muted ml-1" data-toggle="modal" data-target="#ratingDetail">
<i class="fas fa-info-circle"></i>
</a>
<div class="p-3 my-3 card">
<div class="row form-row">
<div class="col-auto text-left">
<span class="area-title">
@L["Rating"]
</span>
</div>
<div class="col text-right">
<div class="cms-rating-area" data-entity-type="@Model.EntityType" data-entity-id="@Model.EntityId" id="cms-rating_{@Model.EntityType}_{@Model.EntityId}">
@if (CurrentUser.IsAuthenticated)
{
@if (Model.CurrentRating != null)
{
<a href="#" class="rating-undo-link">
<small class="text-muted"><i class="fa fa-undo"></i> @L["Undo"]</small>
</a>
}
if (Model.Ratings != null)
{
<a href="#" class="text-muted ml-1" data-toggle="modal" data-target="#ratingDetail">
<i class="far fa-question-circle"></i>
</a>
<div class="modal fade" id="ratingDetail" tabindex="-1" role="dialog" aria-labelledby="ratingDetail" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Rating Detail</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="row text-center">
@foreach (var rating in Model.Ratings)
{
<div class="col">
<label>@rating.StarCount @L["Star"]</label>
<div class="bar-container">
<div class="bar bar-@rating.StarCount" style="width: @(rating.Count * 100 / Model.TotalRating)%"></div>
</div>
<div><small class="text-muted">@rating.Count Rate(s)</small> </div>
<div class="modal fade" id="ratingDetail" tabindex="-1" role="dialog" aria-labelledby="ratingDetail" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Rating Detail</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="row text-center">
@foreach (var rating in Model.Ratings)
{
<div class="col">
<label>@rating.StarCount @L["Star"]</label>
<div class="bar-container">
<div class="bar bar-@rating.StarCount" style="width: @(rating.Count * 100 / Model.TotalRating)%"></div>
</div>
<div><small class="text-muted">@rating.Count Rate(s)</small> </div>
</div>
}
</div>
}
</div>
</div>
</div>
</div>
</div>
</div>
}
}
else
{
<span class="my-rating" data-authenticated="True" data-toggle="popover" data-placement="right" data-html="true" data-content="<div class='text-center'><a href='@Model.LoginUrl' class='btn btn-primary btn-block'>@L["LoginToRate"]</a></div>"></span>
<span class="rating-login"></span>
}
}
<small class="live-rating text-center d-inline-block" style="width: 24px">@(Model.CurrentRating != null ? Model.CurrentRating + " " : 0 + "")</small>
<span class="my-rating btn btn-secondary btn-sm py-1 ml-2" data-rating="@(Model.CurrentRating ?? 0)" data-authenticated="@(Model.CurrentRating != null)">
</span>
}
else
{
<span class="my-rating btn btn-secondary btn-sm py-1 ml-2" data-authenticated="True" data-toggle="popover" data-placement="right" data-html="true" data-content="<div class='text-center'><a href='@Model.LoginUrl' class='btn btn-primary btn-block'>@L["LoginToRate"]</a></div>"></span>
<span class="rating-login"></span>
}
</div>
</div>
</div>
</div>

18
modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Rating/default.css

@ -49,4 +49,22 @@
}
.rating-undo-link:hover {
text-decoration:none;
}
.reaction-in-comment span.area-title {
display: none;
}
span.area-title {
font-weight: 600;
padding: 3px;
display: block;
}
.reaction-in-comment .card {
border: 0 !important;
padding: 0 !important;
margin: 0 !important;
}
.popover {
min-width: 276px;
}

36
modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/ReactionSelection/Default.cshtml

@ -1,17 +1,32 @@
@inject ICurrentUser CurrentUser
@inject IHtmlLocalizer<CmsKitResource> L
@using Volo.Abp.Users
@using Volo.CmsKit.Localization
@using Microsoft.AspNetCore.Mvc.Localization
@model Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.ReactionSelection.ReactionSelectionViewComponent.ReactionSelectionViewModel
@if (CurrentUser.IsAuthenticated || Model.Reactions.Count(r => r.Count > 0) > 0)
{
<div class="text-center text-md-right">
<div class="px-2 py-1 my-2 card border-0 shadow-sm d-inline-block">
<div class="p-3 my-3 card">
<div class="row form-row">
<div class="col-auto text-left">
<span class="area-title">
@L["Reactions"]
</span>
</div>
<div class="col text-right">
<span class="cms-reaction-area" data-entity-type="@Model.EntityType" data-entity-id="@Model.EntityId">
@foreach (var reaction in Model.Reactions.Where(r => r.Count > 0))
{
<span class="ml-1 cms-reaction-icon @(reaction.IsSelectedByCurrentUser ? "cms-reaction-icon-selected" : "")" data-reaction-name="@reaction.Name" data-click-action="@(CurrentUser.IsAuthenticated ? "true" : "false")">
<i class="@reaction.Icon"></i>
<small class="text-muted" style="opacity: .45;">@(reaction.Count)</small>
</span>
}
@if (CurrentUser.IsAuthenticated)
{
<a class="cms-reaction-select-icon" href="javascript:;">
<i class="fa fa-smile-o text-muted"></i>
<a class="cms-reaction-select-icon btn btn-secondary text-light btn-sm py-1 ml-2" href="javascript:;">
<i class="fa fa-smile-o"></i>
</a>
<div class="cms-reaction-selection-popover-content" style="display: none">
@foreach (var reaction in Model.Reactions)
@ -22,14 +37,13 @@
}
</div>
}
@foreach (var reaction in Model.Reactions.Where(r => r.Count > 0))
else
{
<span class="ml-1 cms-reaction-icon @(reaction.IsSelectedByCurrentUser ? "cms-reaction-icon-selected" : "")" data-reaction-name="@reaction.Name" data-click-action="@(CurrentUser.IsAuthenticated ? "true" : "false")">
<i class="@reaction.Icon"></i>
<small class="text-muted" style="opacity: .45;">@(reaction.Count)</small>
<span class="ms-reaction-select-icon btn btn-secondary text-light btn-sm py-1 ml-2" data-authenticated="True" data-toggle="popover" data-placement="right" data-html="true" data-content="<div class='text-center'><a href='@Model.LoginUrl' class='btn btn-primary btn-block'>@L["LoginToReact"]</a></div>">
<i class="fa fa-smile-o"></i>
</span>
}
</span>
</div>
</div>
}
</div>

15
modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/ReactionSelection/ReactionSelectionViewComponent.cs

@ -4,6 +4,7 @@ using JetBrains.Annotations;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI;
using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
using Volo.CmsKit.Public.Reactions;
using Volo.CmsKit.Web;
@ -23,12 +24,16 @@ namespace Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.ReactionSelectio
protected CmsKitUiOptions Options { get; }
public AbpMvcUiOptions AbpMvcUiOptions { get; }
public ReactionSelectionViewComponent(
IReactionPublicAppService reactionPublicAppService,
IOptions<CmsKitUiOptions> options)
IOptions<CmsKitUiOptions> options,
IOptions<AbpMvcUiOptions> abpMvcUiOptions)
{
ReactionPublicAppService = reactionPublicAppService;
Options = options.Value;
AbpMvcUiOptions = abpMvcUiOptions.Value;
}
public virtual async Task<IViewComponentResult> InvokeAsync(
@ -37,11 +42,15 @@ namespace Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.ReactionSelectio
{
var result = await ReactionPublicAppService.GetForSelectionAsync(entityType, entityId);
var loginUrl =
$"{AbpMvcUiOptions.LoginUrl}?returnUrl={HttpContext.Request.Path.ToString()}&returnUrlHash=#cms-rating_{entityType}_{entityId}";
var viewModel = new ReactionSelectionViewModel
{
EntityType = entityType,
EntityId = entityId,
Reactions = new List<ReactionViewModel>()
Reactions = new List<ReactionViewModel>(),
LoginUrl = loginUrl
};
foreach (var reactionDto in result.Items)
@ -67,6 +76,8 @@ namespace Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.ReactionSelectio
public string EntityId { get; set; }
public List<ReactionViewModel> Reactions { get; set; }
public string LoginUrl { get; set; }
}
public class ReactionViewModel

12
modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/ReactionSelection/default.css

@ -5,4 +5,16 @@
width: 25%;
display: inline-block;
float: left;
}
.reaction-in-comment span.area-title {
display: none;
}
span.area-title {
font-weight: 600;
padding: 3px;
display: block;
}
.popover {
min-width: 276px;
}

22
modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Tags/Default.cshtml

@ -2,12 +2,16 @@
@model TagViewComponent.TagViewModel
@if (Model.Tags != null)
{
foreach (var tag in Model.Tags)
{
<span class="cmskit-tag">
@tag.Name
</span>
}
}
<div class="my-3">
<div class="cms-tags-area">
@if (Model.Tags != null)
{
foreach (var tag in Model.Tags)
{
<span class="badge badge-light px-3 py-2 cmskit-tag font-weight-normal">
@tag.Name
</span>
}
}
</div>
</div>

7
modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Tags/TagViewComponent.cs

@ -5,12 +5,17 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
using Volo.CmsKit.Public.Tags;
using Volo.CmsKit.Tags;
namespace Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Tags
{
[ViewComponent(Name = "CmsTags")]
[Widget(
StyleFiles = new[]
{
"/Pages/CmsKit/Shared/Components/Tags/default.css"
})]
public class TagViewComponent : AbpViewComponent
{
protected readonly ITagAppService TagAppService;

14
modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Tags/default.css

@ -0,0 +1,14 @@

.reaction-in-comment span.area-title {
display: none;
}
span.area-title {
font-weight: 600;
padding: 3px;
display: block;
}
.popover {
min-width: 276px;
}

108
modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/BlogPost.cshtml

@ -1,6 +1,5 @@
@page
@using Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Blogs.BlogPost
@using Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Blogs.BlogPostComment
@using Volo.CmsKit.Public.Web.Pages
@using Volo.Abp.GlobalFeatures
@ -13,49 +12,82 @@
@model Volo.CmsKit.Public.Web.Pages.Public.CmsKit.Blogs.BlogPostModel
@await Component.InvokeAsync(typeof(DefaultBlogPostViewComponent), new
{
Model.BlogSlug,
Model.BlogPostSlug
})
@if (GlobalFeatureManager.Instance.IsEnabled<TagsFeature>())
{
if (Model.TagsFeature?.IsEnabled == true)
{
@await Component.InvokeAsync(typeof(TagViewComponent), new
{
entityType = Volo.CmsKit.Blogs.BlogPostConsts.EntityType,
entityId = Model.BlogPost.Id.ToString()
})
}
@section styles{
<abp-style src="/Pages/Public/CmsKit/Blogs/blogPost.css" />
}
@if (GlobalFeatureManager.Instance.IsEnabled<ReactionsFeature>())
{
if (Model.ReactionsFeature?.IsEnabled == true)
{
@await Component.InvokeAsync(typeof(ReactionSelectionViewComponent), new
{
entityType = Volo.CmsKit.Blogs.BlogPostConsts.EntityType,
entityId = Model.BlogPost.Id.ToString()
})
}
@{
string dummyImageSource = "https://dummyimage.com/1280x720/a3a3a3/fff.png?text=" + Model.BlogPost.Title;
}
@if (GlobalFeatureManager.Instance.IsEnabled<RatingsFeature>())
{
if (Model.RatingsFeature?.IsEnabled == true)
{
@await Component.InvokeAsync(typeof(RatingViewComponent), new
{
entityType = Volo.CmsKit.Blogs.BlogPostConsts.EntityType,
entityId = Model.BlogPost.Id.ToString()
})
}
}
<abp-card class="mb-4">
<img src="/api/cms-kit/media/@Model.BlogPost.CoverImageMediaId" class="card-img-top" onerror="this.src='@dummyImageSource'" />
<abp-card-body>
<abp-row>
<div class="col-lg-8 col-md-10 mx-auto pb-4">
<h1 class="mt-lg-4 mt-md-3">@Model.BlogPost.Title</h1>
<p class="mb-lg-5 mb-md-3">
<span class="font-weight-bold">@@@Model.BlogPost.Author?.UserName</span>
<small style="opacity:.65;">@Model.BlogPost.CreationTime</small>
</p>
@Html.Raw(Model.BlogPost.Content)
<p class="mb-3">
@if (Model.BlogPost.LastModificationTime != null)
{
<small style="opacity:.65;">@L["LastModification"].Value : @Model.BlogPost.LastModificationTime</small>
}
</p>
<hr />
@if (GlobalFeatureManager.Instance.IsEnabled<TagsFeature>())
{
if (Model.TagsFeature?.IsEnabled == true)
{
@await Component.InvokeAsync(typeof(TagViewComponent), new
{
entityType = Volo.CmsKit.Blogs.BlogPostConsts.EntityType,
entityId = Model.BlogPost.Id.ToString()
})
}
}
</div>
</abp-row>
<abp-row class="form-row">
<abp-column size-lg="_6" size-md="_12">
@if (GlobalFeatureManager.Instance.IsEnabled<ReactionsFeature>())
{
if (Model.ReactionsFeature?.IsEnabled == true)
{
@await Component.InvokeAsync(typeof(ReactionSelectionViewComponent), new
{
entityType = Volo.CmsKit.Blogs.BlogPostConsts.EntityType,
entityId = Model.BlogPost.Id.ToString()
})
}
}
</abp-column>
<abp-column size-lg="_6" size-md="_12">
@if (GlobalFeatureManager.Instance.IsEnabled<RatingsFeature>())
{
if (Model.RatingsFeature?.IsEnabled == true)
{
@await Component.InvokeAsync(typeof(RatingViewComponent), new
{
entityType = Volo.CmsKit.Blogs.BlogPostConsts.EntityType,
entityId = Model.BlogPost.Id.ToString()
})
}
}
</abp-column>
</abp-row>
</abp-card-body>
</abp-card>
<hr />
@if (GlobalFeatureManager.Instance.IsEnabled<CommentsFeature>())
{

60
modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/Index.cshtml

@ -7,53 +7,45 @@
@model IndexModel
@section styles{
<abp-style src="/Pages/Public/CmsKit/Blogs/index.css" />
}
@{
const string dummyImageSource = "https://dummyimage.com/300x200/a3a3a3/fff.png";
const string dummyImageSource = "https://dummyimage.com/320x180/a3a3a3/fff.png";
}
<abp-row id="blogs-container">
@foreach (var blog in Model.Blogs.Items)
{
<abp-column size="_12" class="m-3">
<abp-column size="_12" size-md="_6" size-lg="_4">
<abp-card>
<abp-card-header>
<abp-card-title>
@blog.Title
</abp-card-title>
<abp-card-subtitle>
@@@blog.Author?.UserName
</abp-card-subtitle>
</abp-card-header>
<abp-card-body>
@if (blog.CoverImageMediaId != null)
{
<img src="/api/cms-kit/media/@blog.CoverImageMediaId" class="card-img-top" style="max-width:200px;max-height:200px;" onerror="this.src='@dummyImageSource'" />
}
else
{
<img src="@dummyImageSource" class="card-img-top" style="max-width:200px;max-height:200px;" />
}
<abp-card-text>
@blog.ShortDescription
</abp-card-text>
<abp-card-text>
<a href="/blogs/@Model.BlogSlug/@blog.Slug">
<abp-button text="@L["Read"]"
button-type="Outline_Primary" />
</a>
</abp-card-text>
@if (blog.CoverImageMediaId != null)
{
<img src="/api/cms-kit/media/@blog.CoverImageMediaId" class="card-img-top" onerror="this.src='@dummyImageSource'" />
}
else
{
<img src="@(dummyImageSource)?text=@blog.Title" class="card-img-top" />
}
<abp-card-body class="p-4">
<h5>@blog.Title</h5>
<p class="mb-2">
<span class="font-weight-bold">@@@blog.Author?.UserName</span>
<small style="opacity:.65;">@blog.CreationTime</small>
</p>
<p style="min-height: 60px;">@blog.ShortDescription</p>
<a href="/blogs/@Model.BlogSlug/@blog.Slug" class="btn btn-block btn-light">
@L["Read"]
</a>
</abp-card-body>
<abp-card-footer>
<abp-card-text>
@blog.CreationTime
</abp-card-text>
</abp-card-footer>
</abp-card>
</abp-column>
}
</abp-row>
<abp-row>
<abp-paginator model="Model.PagerModel" />
<abp-column>
<abp-paginator model="Model.PagerModel" />
</abp-column>
</abp-row>

23
modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/blogPost.css

@ -0,0 +1,23 @@
.card-body img {
max-width: 100%;
border-radius: 4px;
margin: 20px 0;
}
.badge.badge-light {
color: #757575;
background: #f2f2f2;
}
.reaction-in-comment span.area-title {
display: none;
}
span.area-title {
font-weight: 600;
padding: 3px;
display: block;
}
.popover {
min-width: 276px;
}

6
modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/index.css

@ -0,0 +1,6 @@
.card-img-top {
}
.popover {
min-width: 276px;
}

3
modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Pages/Index.cshtml

@ -5,6 +5,9 @@
@model Volo.CmsKit.Public.Web.Pages.Public.CmsKit.Pages.IndexModel
@inject IHtmlLocalizer<CmsKitResource> L
@section styles{
<abp-style src="/Pages/Public/CmsKit/Pages/index.css" />
}
@await Component.InvokeAsync(typeof(DefaultPageViewComponent),
new
{

6
modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Pages/index.css

@ -0,0 +1,6 @@
.card-img-top {
}
.popover {
min-width: 276px;
}

27
modules/cms-kit/src/Volo.CmsKit.Public.Web/Properties/launchSettings.json

@ -1,27 +0,0 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:60873/",
"sslPort": 44300
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Volo.CmsKit.Public.Web": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "https://localhost:5001;http://localhost:5000"
}
}
}

5
modules/cms-kit/src/Volo.CmsKit.Public.Web/Volo.CmsKit.Public.Web.csproj

@ -29,4 +29,9 @@
<Content Remove="Components\**\*.css" />
</ItemGroup>
<ItemGroup>
<None Remove="Pages\Public\CmsKit\Blogs\index.css" />
<None Remove="Pages\Public\CmsKit\Pages\index.css" />
</ItemGroup>
</Project>

66
modules/cms-kit/test/Volo.CmsKit.Domain.Tests/Ratings/RatingManager_Test.cs

@ -0,0 +1,66 @@
using Shouldly;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Volo.CmsKit.Blogs;
using Volo.CmsKit.Users;
using Xunit;
namespace Volo.CmsKit.Ratings
{
public class RatingManager_Test : CmsKitDomainTestBase
{
private readonly CmsKitTestData _cmsKitTestData;
private readonly RatingManager _ratingManager;
private readonly ICmsUserRepository _userRepository;
public RatingManager_Test()
{
_cmsKitTestData = GetRequiredService<CmsKitTestData>();
_ratingManager = GetRequiredService<RatingManager>();
_userRepository = GetRequiredService<ICmsUserRepository>();
}
[Fact]
public async Task SetStarAsync_ShouldCreate_WhenFirstCall()
{
var user = await _userRepository.GetAsync(_cmsKitTestData.User1Id);
short starCount = 4;
var rating = await _ratingManager.SetStarAsync(user, _cmsKitTestData.EntityType1, _cmsKitTestData.BlogPost_1_Id.ToString(), starCount);
rating.ShouldNotBeNull();
rating.Id.ShouldNotBe(Guid.Empty);
rating.StarCount.ShouldBe(starCount);
}
[Fact]
public async Task SetStarAsync_ShouldUpdate_WithExistingRating()
{
var user = await _userRepository.GetAsync(_cmsKitTestData.User1Id);
short starCount = 2;
var rating = await _ratingManager.SetStarAsync(user, _cmsKitTestData.EntityType1, _cmsKitTestData.EntityId1, starCount);
rating.ShouldNotBeNull();
rating.Id.ShouldNotBe(Guid.Empty);
rating.StarCount.ShouldBe(starCount);
}
[Fact]
public async Task SetStarAsync_ShouldThrowException_WithNotConfiguredentityType()
{
var user = await _userRepository.GetAsync(_cmsKitTestData.User1Id);
var notConfiguredEntityType = "AnyOtherEntityType";
short starCount = 3;
var exception = await Should.ThrowAsync<EntityCantHaveRatingException>(async () =>
await _ratingManager.SetStarAsync(user, notConfiguredEntityType, "1", starCount));
exception.ShouldNotBeNull();
exception.EntityType.ShouldBe(notConfiguredEntityType);
}
}
}

8
modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitDataSeedContributor.cs

@ -46,6 +46,7 @@ namespace Volo.CmsKit
private readonly BlogManager _blogManager;
private readonly IOptions<CmsKitMediaOptions> _mediaOptions;
private readonly IOptions<CmsKitCommentOptions> _commentsOptions;
private readonly IOptions<CmsKitRatingOptions> _ratingOptions;
public CmsKitDataSeedContributor(
IGuidGenerator guidGenerator,
@ -70,7 +71,8 @@ namespace Volo.CmsKit
IBlobContainer<MediaContainer> mediaBlobContainer,
BlogManager blogManager,
IOptions<CmsKitMediaOptions> cmsMediaOptions,
IOptions<CmsKitCommentOptions> commentsOptions)
IOptions<CmsKitCommentOptions> commentsOptions,
IOptions<CmsKitRatingOptions> ratingOptions)
{
_guidGenerator = guidGenerator;
_cmsUserRepository = cmsUserRepository;
@ -95,6 +97,7 @@ namespace Volo.CmsKit
_blogManager = blogManager;
_mediaOptions = cmsMediaOptions;
_commentsOptions = commentsOptions;
this._ratingOptions = ratingOptions;
}
public async Task SeedAsync(DataSeedContext context)
@ -159,6 +162,9 @@ namespace Volo.CmsKit
_reactionOptions.Value.EntityTypes.Add(new ReactionEntityTypeDefinition(_cmsKitTestData.EntityType1, reactions));
_reactionOptions.Value.EntityTypes.Add(new ReactionEntityTypeDefinition(_cmsKitTestData.EntityType2, reactions));
_ratingOptions.Value.EntityTypes.Add(new RatingEntityTypeDefinition(_cmsKitTestData.EntityType1));
_ratingOptions.Value.EntityTypes.Add(new RatingEntityTypeDefinition(_cmsKitTestData.EntityType2));
return Task.CompletedTask;
}

2
modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/zh-Hant.json

@ -9,6 +9,7 @@
"Edit": "編輯",
"LastEditTime": "上次編輯",
"Delete": "刪除",
"ClearCacheConfirmationMessage": "你確定刪除此專案全部的快取 \"{0}\"",
"InThisDocument": "在此文件中",
"GoToTop": "到最上方",
"Projects": "專案",
@ -20,6 +21,7 @@
"FilterTopics": "過濾主題",
"FullSearch": "搜索文件",
"Volo.Docs.Domain:010001": "Elastic search未啟用.",
"MultipleVersionDocumentInfo": "此份文件有多個版本。請選擇一個最適合的。",
"New": "新文檔",
"Upd": "更新",
"NewExplanation": "在最近兩周內創建.",

14
modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/zh-Hant.json

@ -2,6 +2,18 @@
"culture": "zh-Hant",
"texts": {
"Settings": "設置",
"SuccessfullySaved": "保存成功"
"SuccessfullySaved": "保存成功",
"Permission:SettingManagement": "設定管理",
"Permission:Emailing": "信箱",
"Menu:Emailing": "信箱",
"SmtpHost": "主機",
"SmtpPort": "Port",
"SmtpUserName": "帳號",
"SmtpPassword": "密碼",
"SmtpDomain": "網域",
"SmtpEnableSsl": "啟用 SSL",
"SmtpUseDefaultCredentials": "使用預設Credentials",
"DefaultFromAddress": "預設發信信箱",
"DefaultFromDisplayName": "預設信件顯示名稱"
}
}

16
npm/ng-packs/packages/core/src/lib/strategies/auth-flow.strategy.ts

@ -58,10 +58,14 @@ export abstract class AuthFlowStrategy {
return Promise.resolve();
}
return this.oAuthService.refreshToken() as Promise<any>;
return this.refreshToken();
})
.catch(this.catchError);
}
protected refreshToken() {
return this.oAuthService.refreshToken().catch(() => clearOAuthStorage());
}
}
export class AuthCodeFlowStrategy extends AuthFlowStrategy {
@ -111,9 +115,10 @@ export class AuthPasswordFlowStrategy extends AuthFlowStrategy {
)
.subscribe(() => {
if (this.oAuthService.getRefreshToken()) {
this.oAuthService.refreshToken();
this.refreshToken();
} else {
this.oAuthService.logOut();
this.removeRememberMe();
this.appConfigService.get().subscribe(res => {
this.configState.setState(res);
});
@ -184,6 +189,13 @@ export class AuthPasswordFlowStrategy extends AuthFlowStrategy {
}),
);
}
protected refreshToken() {
return this.oAuthService.refreshToken().catch(() => {
clearOAuthStorage();
this.removeRememberMe();
});
}
}
export const AUTH_FLOW_STRATEGY = {

1
templates/app/angular/package.json

@ -12,6 +12,7 @@
},
"private": true,
"dependencies": {
"@abp/ng.account": "~4.2.2",
"@abp/ng.components": "~4.2.2",
"@abp/ng.core": "~4.2.2",
"@abp/ng.identity": "~4.2.2",

4
templates/app/angular/src/app/app-routing.module.ts

@ -7,6 +7,10 @@ const routes: Routes = [
pathMatch: 'full',
loadChildren: () => import('./home/home.module').then(m => m.HomeModule),
},
{
path: 'account',
loadChildren: () => import('@abp/ng.account').then(m => m.AccountModule.forLazy()),
},
{
path: 'identity',
loadChildren: () => import('@abp/ng.identity').then(m => m.IdentityModule.forLazy()),

2
templates/app/angular/src/app/app.module.ts

@ -1,3 +1,4 @@
import { AccountConfigModule } from '@abp/ng.account/config';
import { CoreModule } from '@abp/ng.core';
import { registerLocale } from '@abp/ng.core/locale';
import { IdentityConfigModule } from '@abp/ng.identity/config';
@ -24,6 +25,7 @@ import { APP_ROUTE_PROVIDER } from './route.provider';
registerLocaleFn: registerLocale(),
}),
ThemeSharedModule.forRoot(),
AccountConfigModule.forRoot(),
IdentityConfigModule.forRoot(),
TenantManagementConfigModule.forRoot(),
SettingManagementConfigModule.forRoot(),

1
templates/module/angular/package.json

@ -15,6 +15,7 @@
},
"private": true,
"dependencies": {
"@abp/ng.account": "~4.2.2",
"@abp/ng.components": "~4.2.2",
"@abp/ng.core": "~4.2.2",
"@abp/ng.identity": "~4.2.2",

4
templates/module/angular/projects/dev-app/src/app/app-routing.module.ts

@ -7,6 +7,10 @@ const routes: Routes = [
pathMatch: 'full',
loadChildren: () => import('./home/home.module').then(m => m.HomeModule),
},
{
path: 'account',
loadChildren: () => import('@abp/ng.account').then(m => m.AccountModule.forLazy()),
},
{
path: 'identity',
loadChildren: () => import('@abp/ng.identity').then(m => m.IdentityModule.forLazy()),

6
templates/module/angular/projects/dev-app/src/app/app.module.ts

@ -1,3 +1,4 @@
import { AccountConfigModule } from '@abp/ng.account/config';
import { CoreModule } from '@abp/ng.core';
import { registerLocale } from '@abp/ng.core/locale';
import { IdentityConfigModule } from '@abp/ng.identity/config';
@ -27,12 +28,13 @@ import { ThemeBasicModule } from '@abp/ng.theme.basic';
skipGetAppConfiguration: false,
}),
ThemeSharedModule.forRoot(),
AccountConfigModule.forRoot(),
IdentityConfigModule.forRoot(),
MyProjectNameConfigModule.forRoot(),
TenantManagementConfigModule.forRoot(),
SettingManagementConfigModule.forRoot(),
NgxsModule.forRoot(),
MyProjectNameConfigModule.forRoot(),
ThemeBasicModule.forRoot(),
NgxsModule.forRoot(),
],
providers: [APP_ROUTE_PROVIDER],
declarations: [AppComponent],

Loading…
Cancel
Save