mirror of https://github.com/abpframework/abp.git
69 changed files with 642 additions and 149 deletions
@ -0,0 +1,288 @@ |
|||||
|
# How Replaceable Components Work with Extensions |
||||
|
|
||||
|
Additional UI extensibility points ([Entity action extensions](https://docs.abp.io/en/abp/latest/UI/Angular/Entity-Action-Extensions), [data table column extensions](https://docs.abp.io/en/abp/latest/UI/Angular/Data-Table-Column-Extensions), [page toolbar extensions](https://docs.abp.io/en/abp/latest/UI/Angular/Page-Toolbar-Extensions) and others) are used in ABP pages to allow to control entity actions, table columns and page toolbar of a page. If you replace a page, you need to apply some configurations to be able to work extension components in your component. Let's see how to do this by replacing the roles page. |
||||
|
|
||||
|
Create a new module called `MyRolesModule`: |
||||
|
|
||||
|
```bash |
||||
|
yarn ng generate module my-roles --module app |
||||
|
``` |
||||
|
|
||||
|
Create a new component called `MyRolesComponent`: |
||||
|
|
||||
|
```bash |
||||
|
yarn ng generate component my-roles/my-roles --flat --export |
||||
|
``` |
||||
|
|
||||
|
Open the generated `src/app/my-roles/my-roles.component.ts` file and replace its content with the following: |
||||
|
|
||||
|
```js |
||||
|
import { ListService, PagedAndSortedResultRequestDto } from '@abp/ng.core'; |
||||
|
import { |
||||
|
CreateRole, |
||||
|
DeleteRole, |
||||
|
eIdentityComponents, |
||||
|
GetRoleById, |
||||
|
GetRoles, |
||||
|
IdentityRoleDto, |
||||
|
IdentityState, |
||||
|
RolesComponent, |
||||
|
UpdateRole, |
||||
|
} from '@abp/ng.identity'; |
||||
|
import { ePermissionManagementComponents } from '@abp/ng.permission-management'; |
||||
|
import { Confirmation, ConfirmationService } from '@abp/ng.theme.shared'; |
||||
|
import { |
||||
|
EXTENSIONS_IDENTIFIER, |
||||
|
FormPropData, |
||||
|
generateFormFromProps, |
||||
|
} from '@abp/ng.theme.shared/extensions'; |
||||
|
import { Component, Injector, OnInit } from '@angular/core'; |
||||
|
import { FormGroup } from '@angular/forms'; |
||||
|
import { Select, Store } from '@ngxs/store'; |
||||
|
import { Observable } from 'rxjs'; |
||||
|
import { finalize, pluck } from 'rxjs/operators'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'app-my-roles', |
||||
|
templateUrl: './my-roles.component.html', |
||||
|
providers: [ |
||||
|
ListService, |
||||
|
{ |
||||
|
provide: EXTENSIONS_IDENTIFIER, |
||||
|
useValue: eIdentityComponents.Roles, |
||||
|
}, |
||||
|
{ provide: RolesComponent, useExisting: MyRolesComponent }, |
||||
|
], |
||||
|
}) |
||||
|
export class MyRolesComponent implements OnInit { |
||||
|
@Select(IdentityState.getRoles) |
||||
|
data$: Observable<IdentityRoleDto[]>; |
||||
|
|
||||
|
@Select(IdentityState.getRolesTotalCount) |
||||
|
totalCount$: Observable<number>; |
||||
|
|
||||
|
form: FormGroup; |
||||
|
|
||||
|
selected: IdentityRoleDto; |
||||
|
|
||||
|
isModalVisible: boolean; |
||||
|
|
||||
|
visiblePermissions = false; |
||||
|
|
||||
|
providerKey: string; |
||||
|
|
||||
|
modalBusy = false; |
||||
|
|
||||
|
permissionManagementKey = ePermissionManagementComponents.PermissionManagement; |
||||
|
|
||||
|
onVisiblePermissionChange = event => { |
||||
|
this.visiblePermissions = event; |
||||
|
}; |
||||
|
|
||||
|
constructor( |
||||
|
public readonly list: ListService<PagedAndSortedResultRequestDto>, |
||||
|
protected confirmationService: ConfirmationService, |
||||
|
protected store: Store, |
||||
|
protected injector: Injector |
||||
|
) {} |
||||
|
|
||||
|
ngOnInit() { |
||||
|
this.hookToQuery(); |
||||
|
} |
||||
|
|
||||
|
buildForm() { |
||||
|
const data = new FormPropData(this.injector, this.selected); |
||||
|
this.form = generateFormFromProps(data); |
||||
|
} |
||||
|
|
||||
|
openModal() { |
||||
|
this.buildForm(); |
||||
|
this.isModalVisible = true; |
||||
|
} |
||||
|
|
||||
|
add() { |
||||
|
this.selected = {} as IdentityRoleDto; |
||||
|
this.openModal(); |
||||
|
} |
||||
|
|
||||
|
edit(id: string) { |
||||
|
this.store |
||||
|
.dispatch(new GetRoleById(id)) |
||||
|
.pipe(pluck('IdentityState', 'selectedRole')) |
||||
|
.subscribe(selectedRole => { |
||||
|
this.selected = selectedRole; |
||||
|
this.openModal(); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
save() { |
||||
|
if (!this.form.valid) return; |
||||
|
this.modalBusy = true; |
||||
|
|
||||
|
this.store |
||||
|
.dispatch( |
||||
|
this.selected.id |
||||
|
? new UpdateRole({ ...this.selected, ...this.form.value, id: this.selected.id }) |
||||
|
: new CreateRole(this.form.value) |
||||
|
) |
||||
|
.pipe(finalize(() => (this.modalBusy = false))) |
||||
|
.subscribe(() => { |
||||
|
this.isModalVisible = false; |
||||
|
this.list.get(); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
delete(id: string, name: string) { |
||||
|
this.confirmationService |
||||
|
.warn('AbpIdentity::RoleDeletionConfirmationMessage', 'AbpIdentity::AreYouSure', { |
||||
|
messageLocalizationParams: [name], |
||||
|
}) |
||||
|
.subscribe((status: Confirmation.Status) => { |
||||
|
if (status === Confirmation.Status.confirm) { |
||||
|
this.store.dispatch(new DeleteRole(id)).subscribe(() => this.list.get()); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
private hookToQuery() { |
||||
|
this.list.hookToQuery(query => this.store.dispatch(new GetRoles(query))).subscribe(); |
||||
|
} |
||||
|
|
||||
|
openPermissionsModal(providerKey: string) { |
||||
|
this.providerKey = providerKey; |
||||
|
setTimeout(() => { |
||||
|
this.visiblePermissions = true; |
||||
|
}, 0); |
||||
|
} |
||||
|
|
||||
|
sort(data) { |
||||
|
const { prop, dir } = data.sorts[0]; |
||||
|
this.list.sortKey = prop; |
||||
|
this.list.sortOrder = dir; |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
```js |
||||
|
{ |
||||
|
provide: EXTENSIONS_IDENTIFIER, |
||||
|
useValue: eIdentityComponents.Roles, |
||||
|
}, |
||||
|
{ |
||||
|
provide: RolesComponent, |
||||
|
useExisting: MyRolesComponent |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
The two providers we have defined in `MyRolesComponent` are required for the extension components to work correctly. |
||||
|
|
||||
|
* With the first provider, we defined the extension identifier for using `RolesComponent`'s extension actions in the `MyRolesComponent`. |
||||
|
* With the second provider, we have replaced the `RolesComponent` injection with the `MyRolesComponent`. Default extension actions of the `RolesComponent` try to get `RolesComponent` instance. However, the actions can get the `MyRolesComponent` instance after defining the second provider. |
||||
|
|
||||
|
Open the generated `src/app/my-role/my-role.component.html` file and replace its content with the following: |
||||
|
|
||||
|
```html |
||||
|
<div id="identity-roles-wrapper" class="card"> |
||||
|
<div class="card-header"> |
||||
|
<div class="row"> |
||||
|
<div class="col col-md-6"> |
||||
|
<h5 class="card-title">My Roles</h5> |
||||
|
</div> |
||||
|
<div class="text-right col col-md-6"> |
||||
|
<abp-page-toolbar [record]="data$ | async"></abp-page-toolbar> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="card-body"> |
||||
|
<abp-extensible-table |
||||
|
[data]="data$ | async" |
||||
|
[recordsTotal]="totalCount$ | async" |
||||
|
[list]="list" |
||||
|
></abp-extensible-table> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<abp-modal size="md" [(visible)]="isModalVisible" [busy]="modalBusy"> |
||||
|
<ng-template #abpHeader> |
||||
|
<h3>{%{{{ (selected?.id ? 'AbpIdentity::Edit' : 'AbpIdentity::NewRole') | abpLocalization }}}%}</h3> |
||||
|
</ng-template> |
||||
|
|
||||
|
<ng-template #abpBody> |
||||
|
<form [formGroup]="form" (ngSubmit)="save()" validateOnSubmit> |
||||
|
<abp-extensible-form [selectedRecord]="selected"></abp-extensible-form> |
||||
|
</form> |
||||
|
</ng-template> |
||||
|
|
||||
|
<ng-template #abpFooter> |
||||
|
<button type="button" class="btn btn-secondary" #abpClose> |
||||
|
{%{{{ 'AbpIdentity::Cancel' | abpLocalization }}}%} |
||||
|
</button> |
||||
|
<abp-button iconClass="fa fa-check" [disabled]="form?.invalid" (click)="save()">{%{{{ |
||||
|
'AbpIdentity::Save' | abpLocalization |
||||
|
}}}%}</abp-button> |
||||
|
</ng-template> |
||||
|
</abp-modal> |
||||
|
|
||||
|
<abp-permission-management |
||||
|
#abpPermissionManagement="abpPermissionManagement" |
||||
|
*abpReplaceableTemplate=" |
||||
|
{ |
||||
|
inputs: { |
||||
|
providerName: { value: 'R' }, |
||||
|
providerKey: { value: providerKey }, |
||||
|
visible: { value: visiblePermissions, twoWay: true }, |
||||
|
hideBadges: { value: true } |
||||
|
}, |
||||
|
outputs: { visibleChange: onVisiblePermissionChange }, |
||||
|
componentKey: permissionManagementKey |
||||
|
}; |
||||
|
let init = initTemplate |
||||
|
" |
||||
|
(abpInit)="init(abpPermissionManagement)" |
||||
|
> |
||||
|
</abp-permission-management> |
||||
|
``` |
||||
|
|
||||
|
We have added the `abp-page-toolbar`, `abp-extensible-table`, and `abp-extensible-form` extension components to template of the `MyRolesComponent`. |
||||
|
|
||||
|
You should import the required modules for the `MyRolesComponent` to `MyRolesModule`. Open the `src/my-roles/my-roles.module.ts` file and replace the content with the following: |
||||
|
|
||||
|
```js |
||||
|
import { UiExtensionsModule } from '@abp/ng.theme.shared/extensions'; |
||||
|
import { NgModule } from '@angular/core'; |
||||
|
import { SharedModule } from '../shared/shared.module'; |
||||
|
import { MyRolesComponent } from './my-roles.component'; |
||||
|
import { PermissionManagementModule } from '@abp/ng.permission-management'; |
||||
|
|
||||
|
@NgModule({ |
||||
|
declarations: [MyRolesComponent], |
||||
|
imports: [SharedModule, UiExtensionsModule, PermissionManagementModule], |
||||
|
exports: [MyRolesComponent], |
||||
|
}) |
||||
|
export class MyRolesModule {} |
||||
|
``` |
||||
|
|
||||
|
- `UiExtensionsModule` imported to be able to use the extension components in your component. |
||||
|
- `PermissionManagementModule` imported to be able to use the `abp-permission-*management` in your component. |
||||
|
|
||||
|
As the last step, it is needs to be replaced the `RolesComponent` with the `MyRolesComponent`. Open the `app.component.ts` and modify its content as shown below: |
||||
|
|
||||
|
```js |
||||
|
import { ReplaceableComponentsService } from '@abp/ng.core'; |
||||
|
import { eIdentityComponents } from '@abp/ng.identity'; |
||||
|
import { MyRolesComponent } from './my-roles/my-roles.component'; |
||||
|
|
||||
|
@Component(/* component metadata */) |
||||
|
export class AppComponent { |
||||
|
constructor(private replaceableComponents: ReplaceableComponentsService) { |
||||
|
this.replaceableComponents.add({ component: MyRolesComponent, key: eIdentityComponents.Roles }); |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
After the steps above, the `RolesComponent` has been successfully replaced with the `MyRolesComponent`. When you navigate to the `/identity/roles` URL, you will see the `MyRolesComponent`'s template and see the extension components working correctly. |
||||
|
|
||||
|
 |
||||
|
|
||||
|
 |
||||
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 59 KiB |
@ -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; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -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; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,79 @@ |
|||||
|
using System; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using Microsoft.Extensions.Logging.Abstractions; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Cli.Args; |
||||
|
using Volo.Abp.Cli.ProjectBuilding; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace Volo.Abp.Cli.Commands |
||||
|
{ |
||||
|
public class ListModulesCommand : IConsoleCommand, ITransientDependency |
||||
|
{ |
||||
|
public ModuleInfoProvider ModuleInfoProvider { get; } |
||||
|
public ILogger<ListModulesCommand> Logger { get; set; } |
||||
|
|
||||
|
|
||||
|
public ListModulesCommand(ModuleInfoProvider moduleInfoProvider) |
||||
|
{ |
||||
|
ModuleInfoProvider = moduleInfoProvider; |
||||
|
Logger = NullLogger<ListModulesCommand>.Instance; |
||||
|
} |
||||
|
|
||||
|
public async Task ExecuteAsync(CommandLineArgs commandLineArgs) |
||||
|
{ |
||||
|
var modules = await ModuleInfoProvider.GetModuleListAsync(); |
||||
|
var freeModules = modules.Where(m => !m.IsPro).ToList(); |
||||
|
var proModules = modules.Where(m => m.IsPro).ToList(); |
||||
|
|
||||
|
var output = new StringBuilder(Environment.NewLine); |
||||
|
output.AppendLine("Open Source Application Modules"); |
||||
|
output.AppendLine(); |
||||
|
|
||||
|
foreach (var module in freeModules) |
||||
|
{ |
||||
|
output.AppendLine($"> {module.DisplayName.PadRight(50)} ({module.Name})"); |
||||
|
} |
||||
|
|
||||
|
if (commandLineArgs.Options.ContainsKey("include-pro-modules")) |
||||
|
{ |
||||
|
output.AppendLine(); |
||||
|
output.AppendLine("Commercial (Pro) Application Modules"); |
||||
|
output.AppendLine(); |
||||
|
foreach (var module in proModules) |
||||
|
{ |
||||
|
output.AppendLine($"> {module.DisplayName.PadRight(50)} ({module.Name})"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
Logger.LogInformation(output.ToString()); |
||||
|
} |
||||
|
|
||||
|
public string GetUsageInfo() |
||||
|
{ |
||||
|
var sb = new StringBuilder(); |
||||
|
|
||||
|
sb.AppendLine(""); |
||||
|
sb.AppendLine("'list-modules' command is used for listing open source application modules."); |
||||
|
sb.AppendLine(""); |
||||
|
sb.AppendLine("Usage:"); |
||||
|
sb.AppendLine(" abp list-modules"); |
||||
|
sb.AppendLine(" abp list-modules --include-pro-modules"); |
||||
|
sb.AppendLine(""); |
||||
|
sb.AppendLine("Options:"); |
||||
|
sb.AppendLine(" --include-pro-modules Includes commercial (pro) modules in the output."); |
||||
|
sb.AppendLine(""); |
||||
|
sb.AppendLine("See the documentation for more info: https://docs.abp.io/en/abp/latest/CLI"); |
||||
|
|
||||
|
return sb.ToString(); |
||||
|
} |
||||
|
|
||||
|
public string GetShortDescription() |
||||
|
{ |
||||
|
return "List open source application modules"; |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
} |
||||
@ -1,11 +1,14 @@ |
|||||
using System.ComponentModel.DataAnnotations; |
using System; |
||||
|
using System.ComponentModel.DataAnnotations; |
||||
|
|
||||
namespace Volo.CmsKit.Admin.Blogs |
namespace Volo.CmsKit.Admin.Blogs |
||||
{ |
{ |
||||
|
[Serializable] |
||||
public class BlogFeatureInputDto |
public class BlogFeatureInputDto |
||||
{ |
{ |
||||
[Required] |
[Required] |
||||
public string FeatureName { get; set; } |
public string FeatureName { get; set; } |
||||
|
|
||||
public bool IsEnabled { get; set; } |
public bool IsEnabled { get; set; } |
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -1,8 +1,12 @@ |
|||||
namespace Volo.CmsKit.Public.Contents |
using System; |
||||
|
|
||||
|
namespace Volo.CmsKit.Public.Contents |
||||
{ |
{ |
||||
|
[Serializable] |
||||
public class GetContentInput |
public class GetContentInput |
||||
{ |
{ |
||||
public string EntityType { get; set; } |
public string EntityType { get; set; } |
||||
|
|
||||
public string EntityId { get; set; } |
public string EntityId { get; set; } |
||||
} |
} |
||||
} |
} |
||||
|
|||||
Loading…
Reference in new issue