[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)">
<md-icon>create_new_folder</md-icon>
</button>
<button md-icon-button>
<button md-icon-button
adf-node-permission="delete"
[adf-nodes]="documentList.selection">
<md-icon>delete</md-icon>
</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).
| 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
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 { 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 { FileSizePipe } from './src/pipes/file-size.pipe';
import { HighlightPipe } from './src/pipes/text-highlight.pipe';
@ -209,6 +210,7 @@ export function createTranslateLoader(http: Http, logService: LogService) {
declarations: [
...obsoleteMdlDirectives(),
UploadDirective,
NodePermissionDirective,
DataColumnComponent,
DataColumnListComponent,
FileSizePipe,
@ -229,6 +231,7 @@ export function createTranslateLoader(http: Http, logService: LogService) {
ToolbarModule,
...obsoleteMdlDirectives(),
UploadDirective,
NodePermissionDirective,
DataColumnComponent,
DataColumnListComponent,
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 |