Browse Source

Merge pull request #6275 from abpframework/refactor/6273

Deprecated the attribute usage of PermissonDirective
pull/6279/head
Bunyamin Coskuner 6 years ago
committed by GitHub
parent
commit
5f5d886e9e
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 12
      docs/en/Tutorials/Part-5.md
  2. 2
      docs/en/UI/Angular/Permission-Management.md
  3. 2
      npm/ng-packs/angular.json
  4. 47
      npm/ng-packs/packages/core/src/lib/directives/permission.directive.ts
  5. 2
      npm/ng-packs/packages/core/src/lib/services/config-state.service.ts
  6. 2
      npm/ng-packs/packages/core/src/lib/services/environment.service.ts
  7. 18
      npm/ng-packs/packages/core/src/lib/services/permission.service.ts
  8. 5
      npm/ng-packs/packages/core/src/lib/utils/internal-store-utils.ts
  9. 17
      npm/ng-packs/packages/identity/src/lib/components/roles/roles.component.html
  10. 27
      npm/ng-packs/packages/setting-management/src/lib/components/setting-management.component.html
  11. 24
      npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/nav-items.component.html
  12. 65
      npm/ng-packs/packages/theme-basic/src/lib/components/routes/routes.component.html
  13. 2
      npm/ng-packs/packages/theme-shared/extensions/src/lib/components/extensible-form/extensible-form-prop.component.html
  14. 12
      npm/ng-packs/packages/theme-shared/extensions/src/lib/components/extensible-table/extensible-table.component.html
  15. 31
      npm/ng-packs/packages/theme-shared/extensions/src/lib/components/grid-actions/grid-actions.component.html
  16. 26
      npm/ng-packs/packages/theme-shared/extensions/src/lib/components/page-toolbar/page-toolbar.component.html

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

@ -423,13 +423,13 @@ Open the `/src/app/book/book.component.html` file and replace the create button
````html ````html
<!-- Add the abpPermission directive --> <!-- Add the abpPermission directive -->
<button abpPermission="BookStore.Books.Create" id="create" class="btn btn-primary" type="button" (click)="createBook()"> <button *abpPermission="'BookStore.Books.Create'" id="create" class="btn btn-primary" type="button" (click)="createBook()">
<i class="fa fa-plus mr-1"></i> <i class="fa fa-plus mr-1"></i>
<span>{%{{{ '::NewBook' | abpLocalization }}}%}</span> <span>{%{{{ '::NewBook' | abpLocalization }}}%}</span>
</button> </button>
```` ````
* Just added `abpPermission="BookStore.Books.Create"` that hides the button if the current user has no permission. * Just added `*abpPermission="'BookStore.Books.Create'"` that hides the button if the current user has no permission.
### Hide the Edit and Delete Actions ### Hide the Edit and Delete Actions
@ -443,18 +443,18 @@ Open the `/src/app/book/book.component.html` file and replace the edit and delet
````html ````html
<!-- Add the abpPermission directive --> <!-- Add the abpPermission directive -->
<button abpPermission="BookStore.Books.Edit" ngbDropdownItem (click)="editBook(row.id)"> <button *abpPermission="'BookStore.Books.Edit'" ngbDropdownItem (click)="editBook(row.id)">
{%{{{ '::Edit' | abpLocalization }}}%} {%{{{ '::Edit' | abpLocalization }}}%}
</button> </button>
<!-- Add the abpPermission directive --> <!-- Add the abpPermission directive -->
<button abpPermission="BookStore.Books.Delete" ngbDropdownItem (click)="delete(row.id)"> <button *abpPermission="'BookStore.Books.Delete'" ngbDropdownItem (click)="delete(row.id)">
{%{{{ '::Delete' | abpLocalization }}}%} {%{{{ '::Delete' | abpLocalization }}}%}
</button> </button>
```` ````
* Added `abpPermission="BookStore.Books.Edit"` that hides the edit action if the current user has no editing permission. * Added `*abpPermission="'BookStore.Books.Edit'"` that hides the edit action if the current user has no editing permission.
* Added `abpPermission="BookStore.Books.Delete"` that hides the delete action if the current user has no delete permission. * Added `*abpPermission="'BookStore.Books.Delete'"` that hides the delete action if the current user has no delete permission.
{{else if UI == "Blazor"}} {{else if UI == "Blazor"}}

2
docs/en/UI/Angular/Permission-Management.md

@ -54,8 +54,6 @@ You can use the `PermissionDirective` to manage visibility of a DOM Element acco
As shown above you can remove elements from DOM with `abpPermission` structural directive. As shown above you can remove elements from DOM with `abpPermission` structural directive.
The directive can also be used as an attribute directive but we recommend to you to use it as a structural directive.
## Permission Guard ## Permission Guard
You can use `PermissionGuard` if you want to control authenticated user's permission to access to the route during navigation. You can use `PermissionGuard` if you want to control authenticated user's permission to access to the route during navigation.

2
npm/ng-packs/angular.json

@ -378,7 +378,6 @@
"polyfills": "apps/dev-app/src/polyfills.ts", "polyfills": "apps/dev-app/src/polyfills.ts",
"tsConfig": "apps/dev-app/tsconfig.dev.json", "tsConfig": "apps/dev-app/tsconfig.dev.json",
"aot": true, "aot": true,
"extractCss": true,
"allowedCommonJsDependencies": ["chart.js", "js-sha256"], "allowedCommonJsDependencies": ["chart.js", "js-sha256"],
"assets": ["apps/dev-app/src/favicon.ico", "apps/dev-app/src/assets"], "assets": ["apps/dev-app/src/favicon.ico", "apps/dev-app/src/assets"],
"styles": [ "styles": [
@ -433,7 +432,6 @@
"optimization": true, "optimization": true,
"outputHashing": "all", "outputHashing": "all",
"sourceMap": false, "sourceMap": false,
"extractCss": true,
"namedChunks": false, "namedChunks": false,
"extractLicenses": true, "extractLicenses": true,
"vendorChunk": false, "vendorChunk": false,

47
npm/ng-packs/packages/core/src/lib/directives/permission.directive.ts

@ -1,59 +1,64 @@
import { import {
ChangeDetectorRef,
Directive, Directive,
ElementRef, ElementRef,
Input, Input,
OnChanges, OnChanges,
OnDestroy, OnDestroy,
OnInit,
Optional, Optional,
Renderer2, Renderer2,
SimpleChanges,
TemplateRef, TemplateRef,
ViewContainerRef, ViewContainerRef,
} from '@angular/core'; } from '@angular/core';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { distinctUntilChanged } from 'rxjs/operators';
import { PermissionService } from '../services/permission.service'; import { PermissionService } from '../services/permission.service';
@Directive({ @Directive({
selector: '[abpPermission]', selector: '[abpPermission]',
}) })
export class PermissionDirective implements OnInit, OnDestroy, OnChanges { export class PermissionDirective implements OnDestroy, OnChanges {
@Input('abpPermission') condition: string; @Input('abpPermission') condition: string;
subscription: Subscription; subscription: Subscription;
constructor( constructor(
private elRef: ElementRef, private elRef: ElementRef<HTMLElement>,
private renderer: Renderer2, private renderer: Renderer2,
@Optional() private templateRef: TemplateRef<any>, @Optional() private templateRef: TemplateRef<any>,
private vcRef: ViewContainerRef, private vcRef: ViewContainerRef,
private permissionService: PermissionService, private permissionService: PermissionService,
private cdRef: ChangeDetectorRef,
) {} ) {}
private check() { private check() {
if (this.subscription) { if (this.subscription) {
this.subscription.unsubscribe(); this.subscription.unsubscribe();
} }
this.subscription = this.permissionService this.subscription = this.permissionService
.getGrantedPolicy$(this.condition) .getGrantedPolicy$(this.condition)
.pipe(distinctUntilChanged())
.subscribe(isGranted => { .subscribe(isGranted => {
if (this.templateRef && isGranted) { if (this.templateRef) this.initStructural(isGranted);
this.vcRef.clear(); else this.initAttribute(isGranted);
this.vcRef.createEmbeddedView(this.templateRef);
} else if (this.templateRef && !isGranted) { this.cdRef.detectChanges();
this.vcRef.clear();
} else if (!isGranted && !this.templateRef) {
this.renderer.removeChild(
(this.elRef.nativeElement as HTMLElement).parentElement,
this.elRef.nativeElement,
);
}
}); });
} }
ngOnInit() { private initStructural(isGranted: boolean) {
if (this.templateRef && !this.condition) { this.vcRef.clear();
this.vcRef.createEmbeddedView(this.templateRef);
if (isGranted) this.vcRef.createEmbeddedView(this.templateRef);
}
/**
* @deprecated Will be deleted in v5.0
*/
private initAttribute(isGranted: boolean) {
if (!isGranted) {
this.renderer.removeChild(this.elRef.nativeElement.parentElement, this.elRef.nativeElement);
} }
} }
@ -61,9 +66,7 @@ export class PermissionDirective implements OnInit, OnDestroy, OnChanges {
if (this.subscription) this.subscription.unsubscribe(); if (this.subscription) this.subscription.unsubscribe();
} }
ngOnChanges({ condition }: SimpleChanges) { ngOnChanges() {
if ((condition || { currentValue: null }).currentValue) { this.check();
this.check();
}
} }
} }

2
npm/ng-packs/packages/core/src/lib/services/config-state.service.ts

@ -15,7 +15,7 @@ export class ConfigStateService {
} }
setState = (state: ApplicationConfiguration.Response) => { setState = (state: ApplicationConfiguration.Response) => {
this.store.patch(state); this.store.set(state);
}; };
getOne$(key: string) { getOne$(key: string) {

2
npm/ng-packs/packages/core/src/lib/services/environment.service.ts

@ -31,6 +31,6 @@ export class EnvironmentService {
} }
setState(environment: Environment) { setState(environment: Environment) {
this.store.patch(environment); this.store.set(environment);
} }
} }

18
npm/ng-packs/packages/core/src/lib/services/permission.service.ts

@ -1,5 +1,5 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { map } from 'rxjs/operators'; import { map, tap } from 'rxjs/operators';
import snq from 'snq'; import snq from 'snq';
import { ApplicationConfiguration } from '../models/application-configuration'; import { ApplicationConfiguration } from '../models/application-configuration';
import { ConfigStateService } from './config-state.service'; import { ConfigStateService } from './config-state.service';
@ -9,7 +9,9 @@ export class PermissionService {
constructor(private configState: ConfigStateService) {} constructor(private configState: ConfigStateService) {}
getGrantedPolicy$(key: string) { getGrantedPolicy$(key: string) {
return this.getStream().pipe(map(policies => this.isPolicyGranted(key, policies))); return this.getStream().pipe(
map(grantedPolicies => this.isPolicyGranted(key, grantedPolicies)),
);
} }
getGrantedPolicy(key: string) { getGrantedPolicy(key: string) {
@ -17,7 +19,7 @@ export class PermissionService {
return this.isPolicyGranted(key, policies); return this.isPolicyGranted(key, policies);
} }
private isPolicyGranted(key: string, policies: ApplicationConfiguration.Policy) { private isPolicyGranted(key: string, grantedPolicies: ApplicationConfiguration.Policy) {
if (!key) return true; if (!key) return true;
const orRegexp = /\|\|/g; const orRegexp = /\|\|/g;
@ -29,16 +31,16 @@ export class PermissionService {
if (keys.length < 2) return false; if (keys.length < 2) return false;
return keys.some(k => this.getPolicy(k.trim(), policies)); return keys.some(k => this.getPolicy(k.trim(), grantedPolicies));
} else if (andRegexp.test(key)) { } else if (andRegexp.test(key)) {
const keys = key.split('&&').filter(Boolean); const keys = key.split('&&').filter(Boolean);
if (keys.length < 2) return false; if (keys.length < 2) return false;
return keys.every(k => this.getPolicy(k.trim(), policies)); return keys.every(k => this.getPolicy(k.trim(), grantedPolicies));
} }
return this.getPolicy(key, policies); return this.getPolicy(key, grantedPolicies);
} }
private getStream() { private getStream() {
@ -53,7 +55,7 @@ export class PermissionService {
return snq(() => applicationConfiguration.auth.grantedPolicies); return snq(() => applicationConfiguration.auth.grantedPolicies);
} }
private getPolicy(policy: string, policies: ApplicationConfiguration.Policy) { private getPolicy(key: string, grantedPolicies: ApplicationConfiguration.Policy) {
return snq(() => policies[policy], false); return snq(() => grantedPolicies[key], false);
} }
} }

5
npm/ng-packs/packages/core/src/lib/utils/internal-store-utils.ts

@ -30,6 +30,11 @@ export class InternalStore<State> {
this.update$.next(state); this.update$.next(state);
} }
set(state: State) {
this.state$.next(state);
this.update$.next(state);
}
reset() { reset() {
this.patch(this.initialState); this.patch(this.initialState);
} }

17
npm/ng-packs/packages/identity/src/lib/components/roles/roles.component.html

@ -51,14 +51,15 @@
> >
{{ 'AbpIdentity::Permissions' | abpLocalization }} {{ 'AbpIdentity::Permissions' | abpLocalization }}
</button> </button>
<button <ng-container *ngIf="!row.isStatic">
*ngIf="!row.isStatic" <button
[abpPermission]="'AbpIdentity.Roles.Delete'" *abpPermission="'AbpIdentity.Roles.Delete'"
ngbDropdownItem ngbDropdownItem
(click)="delete(row.id, row.name)" (click)="delete(row.id, row.name)"
> >
{{ 'AbpIdentity::Delete' | abpLocalization }} {{ 'AbpIdentity::Delete' | abpLocalization }}
</button> </button>
</ng-container>
</div> </div>
</div> </div>
</ng-template> </ng-template>

27
npm/ng-packs/packages/setting-management/src/lib/components/setting-management.component.html

@ -16,20 +16,21 @@
<div class="row"> <div class="row">
<div class="col-12 col-md-3"> <div class="col-12 col-md-3">
<ul class="nav flex-column nav-pills" id="nav-tab" role="tablist"> <ul class="nav flex-column nav-pills" id="nav-tab" role="tablist">
<li <ng-container *abpFor="let setting of settings; trackBy: trackByFn">
*abpFor="let setting of settings; trackBy: trackByFn" <li
(click)="selected = setting" (click)="selected = setting"
class="nav-item pointer" class="nav-item pointer"
[abpPermission]="setting.requiredPolicy" *abpPermission="setting.requiredPolicy"
>
<a
class="nav-link"
[id]="setting.name + '-tab'"
role="tab"
[class.active]="setting.name === selected.name"
>{{ setting.name | abpLocalization }}</a
> >
</li> <a
class="nav-link"
[id]="setting.name + '-tab'"
role="tab"
[class.active]="setting.name === selected.name"
>{{ setting.name | abpLocalization }}</a
>
</li>
</ng-container>
</ul> </ul>
</div> </div>
<div class="col-12 col-md-9"> <div class="col-12 col-md-9">

24
npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/nav-items.component.html

@ -1,18 +1,16 @@
<ul class="navbar-nav"> <ul class="navbar-nav">
<ng-container *ngFor="let item of navItems.items$ | async; trackBy: trackByFn"> <ng-container *ngFor="let item of navItems.items$ | async; trackBy: trackByFn">
<li <ng-container *ngIf="item.visible()">
class="nav-item d-flex align-items-center" <li class="nav-item d-flex align-items-center" *abpPermission="item.requiredPolicy">
*ngIf="item.visible()" <ng-container
[abpPermission]="item.requiredPolicy" *ngIf="item.component; else htmlTemplate"
> [ngComponentOutlet]="item.component"
<ng-container ></ng-container>
*ngIf="item.component; else htmlTemplate"
[ngComponentOutlet]="item.component"
></ng-container>
<ng-template #htmlTemplate> <ng-template #htmlTemplate>
<div [innerHTML]="item.html" (click)="item.action ? item.action() : null"></div> <div [innerHTML]="item.html" (click)="item.action ? item.action() : null"></div>
</ng-template> </ng-template>
</li> </li>
</ng-container>
</ng-container> </ng-container>
</ul> </ul>

65
npm/ng-packs/packages/theme-basic/src/lib/components/routes/routes.component.html

@ -16,37 +16,40 @@
</ng-template> </ng-template>
<ng-template #dropdownLink let-route> <ng-template #dropdownLink let-route>
<li <ng-container *ngIf="route.children?.length">
#navbarRootDropdown <li
class="nav-item dropdown" #navbarRootDropdown
display="static" class="nav-item dropdown"
*ngIf="route.children?.length" display="static"
[abpPermission]="route.requiredPolicy" *abpPermission="route.requiredPolicy"
(click)=" (click)="
navbarRootDropdown.expand navbarRootDropdown.expand
? (navbarRootDropdown.expand = false) ? (navbarRootDropdown.expand = false)
: (navbarRootDropdown.expand = true) : (navbarRootDropdown.expand = true)
" "
>
<a
class="nav-link dropdown-toggle"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
href="javascript:void(0)"
>
<i *ngIf="route.iconClass" [ngClass]="route.iconClass"></i>
{{ route.name | abpLocalization }}
</a>
<div
#routeContainer
class="dropdown-menu border-0 shadow-sm"
(click)="$event.preventDefault(); $event.stopPropagation()"
[class.d-block]="smallScreen && navbarRootDropdown.expand"
> >
<ng-container *ngTemplateOutlet="forTemplate; context: { $implicit: route }"></ng-container> <a
</div> class="nav-link dropdown-toggle"
</li> data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
href="javascript:void(0)"
>
<i *ngIf="route.iconClass" [ngClass]="route.iconClass"></i>
{{ route.name | abpLocalization }}
</a>
<div
#routeContainer
class="dropdown-menu border-0 shadow-sm"
(click)="$event.preventDefault(); $event.stopPropagation()"
[class.d-block]="smallScreen && navbarRootDropdown.expand"
>
<ng-container
*ngTemplateOutlet="forTemplate; context: { $implicit: route }"
></ng-container>
</div>
</li>
</ng-container>
</ng-template> </ng-template>
<ng-template #forTemplate let-route> <ng-template #forTemplate let-route>
@ -74,7 +77,7 @@
#dropdownSubmenu="ngbDropdown" #dropdownSubmenu="ngbDropdown"
placement="right-top" placement="right-top"
[autoClose]="true" [autoClose]="true"
[abpPermission]="child.requiredPolicy" *abpPermission="child.requiredPolicy"
> >
<div ngbDropdownToggle [class.dropdown-toggle]="false"> <div ngbDropdownToggle [class.dropdown-toggle]="false">
<a <a

2
npm/ng-packs/packages/theme-shared/extensions/src/lib/components/extensible-form/extensible-form-prop.component.html

@ -1,4 +1,4 @@
<div class="form-group" [abpPermission]="prop.permission" [ngSwitch]="getComponent(prop)"> <div class="form-group" *abpPermission="prop.permission" [ngSwitch]="getComponent(prop)">
<ng-template ngSwitchCase="input"> <ng-template ngSwitchCase="input">
<label [htmlFor]="prop.id">{{ prop.displayName | abpLocalization }} {{ asterisk }}</label> <label [htmlFor]="prop.id">{{ prop.displayName | abpLocalization }} {{ asterisk }}</label>
<input <input

12
npm/ng-packs/packages/theme-shared/extensions/src/lib/components/extensible-table/extensible-table.component.html

@ -11,11 +11,7 @@
*ngTemplateOutlet="actionsTemplate || gridActions; context: { $implicit: row, index: i }" *ngTemplateOutlet="actionsTemplate || gridActions; context: { $implicit: row, index: i }"
></ng-container> ></ng-container>
<ng-template #gridActions> <ng-template #gridActions>
<abp-grid-actions <abp-grid-actions [index]="i" [record]="row" text="AbpUi::Actions"></abp-grid-actions>
[index]="i"
[record]="row"
text="AbpUi::Actions"
></abp-grid-actions>
</ng-template> </ng-template>
</ng-template> </ng-template>
</ngx-datatable-column> </ngx-datatable-column>
@ -28,11 +24,13 @@
[sortable]="prop.sortable" [sortable]="prop.sortable"
> >
<ng-template let-row="row" let-i="index" ngx-datatable-cell-template> <ng-template let-row="row" let-i="index" ngx-datatable-cell-template>
<ng-container [abpPermission]="prop.permission"> <ng-container *abpPermission="prop.permission">
<div <div
*ngIf="row['_' + prop.name].visible" *ngIf="row['_' + prop.name].visible"
[innerHTML]="row['_' + prop.name].value | async" [innerHTML]="row['_' + prop.name].value | async"
(click)="prop.action && prop.action({ getInjected: getInjected, record: row, index: i })" (click)="
prop.action && prop.action({ getInjected: getInjected, record: row, index: i })
"
[class.pointer]="prop.action" [class.pointer]="prop.action"
></div> ></div>
</ng-container> </ng-container>

31
npm/ng-packs/packages/theme-shared/extensions/src/lib/components/grid-actions/grid-actions.component.html

@ -24,19 +24,20 @@
></ng-container> ></ng-container>
<ng-template #btnItem let-action> <ng-template #btnItem let-action>
<button <ng-container *ngIf="action.visible(data)">
*ngIf="action.visible(data)" <button
ngbDropdownItem ngbDropdownItem
[abpPermission]="action.permission" *abpPermission="action.permission"
(click)="action.action(data)" (click)="action.action(data)"
type="button" type="button"
class="{{ actionList.length === 1 ? 'btn btn-primary' : '' }}" class="{{ actionList.length === 1 ? 'btn btn-primary' : '' }}"
[class.text-center]="actionList.length === 1" [class.text-center]="actionList.length === 1"
> >
<i [ngClass]="action.icon" [class.mr-1]="action.icon"></i> <i [ngClass]="action.icon" [class.mr-1]="action.icon"></i>
<span *ngIf="action.icon; else ellipsis">{{ action.text | abpLocalization }}</span> <span *ngIf="action.icon; else ellipsis">{{ action.text | abpLocalization }}</span>
<ng-template #ellipsis> <ng-template #ellipsis>
<div abpEllipsis>{{ action.text | abpLocalization }}</div> <div abpEllipsis>{{ action.text | abpLocalization }}</div>
</ng-template> </ng-template>
</button> </button>
</ng-container>
</ng-template> </ng-template>

26
npm/ng-packs/packages/theme-shared/extensions/src/lib/components/page-toolbar/page-toolbar.component.html

@ -1,18 +1,20 @@
<div class="row justify-content-end mx-n1" id="AbpContentToolbar"> <div class="row justify-content-end mx-n1" id="AbpContentToolbar">
<div class="col-auto px-1 pt-2" *ngFor="let action of actionList; trackBy: trackByFn"> <div class="col-auto px-1 pt-2" *ngFor="let action of actionList; trackBy: trackByFn">
<ng-container *ngIf="action.visible(data)" [abpPermission]="action.permission"> <ng-container *ngIf="action.visible(data)">
<ng-container *ngIf="action.component as component; else button"> <ng-container *abpPermission="action.permission">
<ng-container <ng-container *ngIf="action.component as component; else button">
*ngComponentOutlet="component; injector: createInjector(action)" <ng-container
></ng-container> *ngComponentOutlet="component; injector: createInjector(action)"
</ng-container> ></ng-container>
</ng-container>
<ng-template #button> <ng-template #button>
<button (click)="action.action(data)" type="button" class="btn btn-primary btn-sm"> <button (click)="action.action(data)" type="button" class="btn btn-primary btn-sm">
<i [ngClass]="action.icon" [class.mr-1]="action.icon"></i> <i [ngClass]="action.icon" [class.mr-1]="action.icon"></i>
{{ action.text | abpLocalization }} {{ action.text | abpLocalization }}
</button> </button>
</ng-template> </ng-template>
</ng-container>
</ng-container> </ng-container>
</div> </div>
</div> </div>

Loading…
Cancel
Save