[ASF-1227] new adf-node-permission directive (#2154)

* directive implementation

* update docs

* unit tests and api docs

* code improvements

* simplify code

* update type def and docs
This commit is contained in:
Denys Vuika 2017-08-02 17:34:48 +01:00 committed by Mario Romano
parent 152e7805bc
commit 91ef0b6947
6 changed files with 261 additions and 1 deletions

View File

@ -30,7 +30,9 @@
<button md-icon-button (click)="onCreateFolderClicked($event)"> <button md-icon-button (click)="onCreateFolderClicked($event)">
<md-icon>create_new_folder</md-icon> <md-icon>create_new_folder</md-icon>
</button> </button>
<button md-icon-button> <button md-icon-button
adf-node-permission="delete"
[adf-nodes]="documentList.selection">
<md-icon>delete</md-icon> <md-icon>delete</md-icon>
</button> </button>

View File

@ -90,6 +90,11 @@ You should see result similar to the following one:
For more details about the [toolbar](src/components/toolbar/toolbar.md). For more details about the [toolbar](src/components/toolbar/toolbar.md).
| Feature | Notes | Docs |
| --- | --- | --- |
| toolbar | toolbar component | [Docs](src/components/toolbar/toolbar.md) |
| node-permission | disable elements based on node permissions | [Docs](src/directives/node-permission.md)
## Upload Directive ## Upload Directive
Allows your components or common HTML elements reacting on File drag and drop in order to upload content. Allows your components or common HTML elements reacting on File drag and drop in order to upload content.

View File

@ -98,6 +98,7 @@ export { DiscoveryApiService } from './src/services/discovery-api.service';
import { DataColumnListComponent } from './src/components/data-column/data-column-list.component'; import { DataColumnListComponent } from './src/components/data-column/data-column-list.component';
import { DataColumnComponent } from './src/components/data-column/data-column.component'; import { DataColumnComponent } from './src/components/data-column/data-column.component';
import { NodePermissionDirective } from './src/directives/node-permission.directive';
import { UploadDirective } from './src/directives/upload.directive'; import { UploadDirective } from './src/directives/upload.directive';
import { FileSizePipe } from './src/pipes/file-size.pipe'; import { FileSizePipe } from './src/pipes/file-size.pipe';
import { HighlightPipe } from './src/pipes/text-highlight.pipe'; import { HighlightPipe } from './src/pipes/text-highlight.pipe';
@ -209,6 +210,7 @@ export function createTranslateLoader(http: Http, logService: LogService) {
declarations: [ declarations: [
...obsoleteMdlDirectives(), ...obsoleteMdlDirectives(),
UploadDirective, UploadDirective,
NodePermissionDirective,
DataColumnComponent, DataColumnComponent,
DataColumnListComponent, DataColumnListComponent,
FileSizePipe, FileSizePipe,
@ -229,6 +231,7 @@ export function createTranslateLoader(http: Http, logService: LogService) {
ToolbarModule, ToolbarModule,
...obsoleteMdlDirectives(), ...obsoleteMdlDirectives(),
UploadDirective, UploadDirective,
NodePermissionDirective,
DataColumnComponent, DataColumnComponent,
DataColumnListComponent, DataColumnListComponent,
FileSizePipe, FileSizePipe,

View File

@ -0,0 +1,112 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ElementRef, SimpleChange } from '@angular/core';
import { AlfrescoContentService } from './../services/alfresco-content.service';
import { NodePermissionDirective } from './node-permission.directive';
describe('NodePermissionDirective', () => {
it('updates element once it is loaded', () => {
const directive = new NodePermissionDirective(null, null, null);
spyOn(directive, 'updateElement').and.stub();
directive.ngAfterViewInit();
expect(directive.updateElement).toHaveBeenCalled();
});
it('updates element on nodes change', () => {
const directive = new NodePermissionDirective(null, null, null);
spyOn(directive, 'updateElement').and.stub();
const nodes = [{}, {}];
const change = new SimpleChange([], nodes, false);
directive.ngOnChanges({ nodes: change });
expect(directive.updateElement).toHaveBeenCalled();
});
it('updates element only on subsequent change', () => {
const directive = new NodePermissionDirective(null, null, null);
spyOn(directive, 'updateElement').and.stub();
const nodes = [{}, {}];
const change = new SimpleChange([], nodes, true);
directive.ngOnChanges({ nodes: change });
expect(directive.updateElement).not.toHaveBeenCalled();
});
it('enables decorated element', () => {
const renderer = jasmine.createSpyObj('renderer', ['removeAttribute']);
const elementRef = new ElementRef({});
const directive = new NodePermissionDirective(elementRef, renderer, null);
directive.enableElement();
expect(renderer.removeAttribute).toHaveBeenCalledWith(elementRef.nativeElement, 'disabled');
});
it('disables decorated element', () => {
const renderer = jasmine.createSpyObj('renderer', ['setAttribute']);
const elementRef = new ElementRef({});
const directive = new NodePermissionDirective(elementRef, renderer, null);
directive.disableElement();
expect(renderer.setAttribute).toHaveBeenCalledWith(elementRef.nativeElement, 'disabled', 'true');
});
it('disables element when nodes not available', () => {
const directive = new NodePermissionDirective(null, null, null);
spyOn(directive, 'disableElement').and.stub();
directive.nodes = null;
expect(directive.updateElement()).toBeFalsy();
directive.nodes = [];
expect(directive.updateElement()).toBeFalsy();
});
it('enables element when all nodes have expected permission', () => {
const contentService = new AlfrescoContentService(null, null, null);
spyOn(contentService, 'hasPermission').and.returnValue(true);
const directive = new NodePermissionDirective(null, null, contentService);
spyOn(directive, 'enableElement').and.stub();
directive.nodes = <any> [{}, {}];
expect(directive.updateElement()).toBeTruthy();
expect(directive.enableElement).toHaveBeenCalled();
});
it('disables element when one of the nodes have no permission', () => {
const contentService = new AlfrescoContentService(null, null, null);
spyOn(contentService, 'hasPermission').and.returnValue(false);
const directive = new NodePermissionDirective(null, null, contentService);
spyOn(directive, 'disableElement').and.stub();
directive.nodes = <any> [{}, {}];
expect(directive.updateElement()).toBeFalsy();
expect(directive.disableElement).toHaveBeenCalled();
});
});

View File

@ -0,0 +1,100 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { AfterViewInit, Directive, ElementRef, Input, OnChanges, Renderer2, SimpleChanges } from '@angular/core';
import { MinimalNodeEntity } from 'alfresco-js-api';
import { AlfrescoContentService } from './../services/alfresco-content.service';
@Directive({
selector: '[adf-node-permission]'
})
export class NodePermissionDirective implements OnChanges, AfterViewInit {
@Input('adf-node-permission')
permission: string = null;
@Input('adf-nodes')
nodes: MinimalNodeEntity[] = [];
constructor(private elementRef: ElementRef,
private renderer: Renderer2,
private contentService: AlfrescoContentService) {
}
ngAfterViewInit() {
this.updateElement();
}
ngOnChanges(changes: SimpleChanges) {
if (changes.nodes && !changes.nodes.firstChange) {
this.updateElement();
}
}
/**
* Updates disabled state for the decorated elememtn
*
* @returns {boolean} True if decorated element got disabled, otherwise False
* @memberof NodePermissionDirective
*/
updateElement(): boolean {
let enable = this.hasPermission(this.nodes, this.permission);
if (enable) {
this.enableElement();
} else {
this.disableElement();
}
return enable;
}
/**
* Enables decorated element
*
* @memberof NodePermissionDirective
*/
enableElement(): void {
this.renderer.removeAttribute(this.elementRef.nativeElement, 'disabled');
}
/**
* Disables decorated element
*
* @memberof NodePermissionDirective
*/
disableElement(): void {
this.renderer.setAttribute(this.elementRef.nativeElement, 'disabled', 'true');
}
/**
* Checks whether all nodes have a particular permission
*
* @param {MinimalNodeEntity[]} nodes Node collection to check
* @param {string} permission Permission to check for each node
* @returns {boolean} True if all nodes have provided permission, otherwise False
* @memberof NodePermissionDirective
*/
hasPermission(nodes: MinimalNodeEntity[], permission: string): boolean {
if (nodes && nodes.length > 0) {
return nodes.every(node => this.contentService.hasPermission(node.entry, permission));
}
return false;
}
}

View File

@ -0,0 +1,38 @@
# Node Permission Directive
The `NodePermissionDirective` allows you to disable an HTML element or Angular component
by taking a collection of the `MinimalNodeEntity` instances and checking the particular permission.
The decorated element will be disabled if:
- there are no nodes in the collection
- at least one of the nodes has no expected permission
## Basic example
The best example to show `NodePermissionDirective` in action is by binding DocumentList selection property to a toolbar button.
For example the "Delete" button should be disabled if no selection is present or if user has no rights to delete at least one node in the selection.
```html
<adf-toolbar title="toolbar example">
<button md-icon-button
adf-node-permission="delete"
[adf-nodes]="documentList.selection">
<md-icon>delete</md-icon>
</button>
</adf-toolbar>
<adf-document-list #documentList ...>
...
</adf-document-list>
```
The button will become disabled by default, and is going to change its state once user selects/unselects one or multiple documents that current user has permission to delete.
## Properties
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| adf-node-permission | string | null | Node permission to check |
| adf-nodes | MinimalNodeEntity[] | [] | Nodes to check permission for |