diff --git a/demo-shell-ng2/app/components/files/files.component.html b/demo-shell-ng2/app/components/files/files.component.html index cf436b5242..5b918760ac 100644 --- a/demo-shell-ng2/app/components/files/files.component.html +++ b/demo-shell-ng2/app/components/files/files.component.html @@ -30,7 +30,9 @@ - diff --git a/ng2-components/ng2-alfresco-core/README.md b/ng2-components/ng2-alfresco-core/README.md index 2116caf75a..7456832a7a 100644 --- a/ng2-components/ng2-alfresco-core/README.md +++ b/ng2-components/ng2-alfresco-core/README.md @@ -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. diff --git a/ng2-components/ng2-alfresco-core/index.ts b/ng2-components/ng2-alfresco-core/index.ts index 2fbaa4c9da..0b1f410f51 100644 --- a/ng2-components/ng2-alfresco-core/index.ts +++ b/ng2-components/ng2-alfresco-core/index.ts @@ -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, diff --git a/ng2-components/ng2-alfresco-core/src/directives/node-permission.directive.spec.ts b/ng2-components/ng2-alfresco-core/src/directives/node-permission.directive.spec.ts new file mode 100644 index 0000000000..5504594e9b --- /dev/null +++ b/ng2-components/ng2-alfresco-core/src/directives/node-permission.directive.spec.ts @@ -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 = [{}, {}]; + + 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 = [{}, {}]; + + expect(directive.updateElement()).toBeFalsy(); + expect(directive.disableElement).toHaveBeenCalled(); + }); + +}); diff --git a/ng2-components/ng2-alfresco-core/src/directives/node-permission.directive.ts b/ng2-components/ng2-alfresco-core/src/directives/node-permission.directive.ts new file mode 100644 index 0000000000..78c5f66740 --- /dev/null +++ b/ng2-components/ng2-alfresco-core/src/directives/node-permission.directive.ts @@ -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; + } + +} diff --git a/ng2-components/ng2-alfresco-core/src/directives/node-permission.md b/ng2-components/ng2-alfresco-core/src/directives/node-permission.md new file mode 100644 index 0000000000..24ee478859 --- /dev/null +++ b/ng2-components/ng2-alfresco-core/src/directives/node-permission.md @@ -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 + + + + + + ... + +``` + +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 | \ No newline at end of file