diff --git a/demo-shell-ng2/app/components/files/files.component.html b/demo-shell-ng2/app/components/files/files.component.html index 7bc0c55c54..60d0e7553b 100644 --- a/demo-shell-ng2/app/components/files/files.component.html +++ b/demo-shell-ng2/app/components/files/files.component.html @@ -6,7 +6,8 @@ + [adf-node-permission]="'create'" + [adf-nodes]="getCurrentDocumentListNode()">
@@ -290,7 +292,8 @@ [multipleFiles]="multipleFileUpload" [uploadFolders]="folderUpload" [versioning]="versioning" - [disableWithNoPermission]="disableWithNoPermission" + [adf-node-permission]="'create'" + [adf-nodes]="getCurrentDocumentListNode()" (permissionEvent)="handlePermissionError($event)"> diff --git a/demo-shell-ng2/app/components/files/files.component.ts b/demo-shell-ng2/app/components/files/files.component.ts index df924d2853..26430ea5c3 100644 --- a/demo-shell-ng2/app/components/files/files.component.ts +++ b/demo-shell-ng2/app/components/files/files.component.ts @@ -143,6 +143,14 @@ export class FilesComponent implements OnInit { // this.permissionsStyle.push(new PermissionStyleModel('document-list__disable', PermissionsEnum.NOT_CREATE, false, true)); } + getCurrentDocumentListNode(): MinimalNodeEntity[] { + if (this.documentList.folderNode) { + return [ { entry: this.documentList.folderNode } ]; + } else { + return []; + } + } + onNavigationError(err: any) { if (err) { this.errorMessage = err.message || 'Navigation error'; diff --git a/ng2-components/ng2-alfresco-core/index.ts b/ng2-components/ng2-alfresco-core/index.ts index 759914f50e..eb0e94d42a 100644 --- a/ng2-components/ng2-alfresco-core/index.ts +++ b/ng2-components/ng2-alfresco-core/index.ts @@ -127,11 +127,13 @@ export { CardViewModule } from './src/components/view/card-view.module'; export { CollapsableModule } from './src/components/collapsable/collapsable.module'; export { CardViewItem } from './src/interface/card-view-item.interface'; export { TimeAgoPipe } from './src/pipes/time-ago.pipe'; +export { EXTENDIBLE_COMPONENT } from './src/interface/injection.tokens'; export * from './src/components/data-column/data-column.component'; export * from './src/components/data-column/data-column-list.component'; export * from './src/directives/upload.directive'; export * from './src/directives/highlight.directive'; +export * from './src/directives/node-permission.directive'; export * from './src/utils/index'; export * from './src/events/base.event'; export * from './src/events/base-ui.event'; 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 index 5504594e9b..d7cd557923 100644 --- 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 @@ -15,98 +15,138 @@ * limitations under the License. */ -import { ElementRef, SimpleChange } from '@angular/core'; +import { Component, ElementRef, SimpleChange } from '@angular/core'; import { AlfrescoContentService } from './../services/alfresco-content.service'; -import { NodePermissionDirective } from './node-permission.directive'; +import { NodePermissionDirective, NodePermissionSubject } from './node-permission.directive'; + +@Component({ + selector: 'adf-text-subject' +}) +class TestComponent implements NodePermissionSubject { + disabled: boolean = false; +} describe('NodePermissionDirective', () => { - it('updates element once it is loaded', () => { - const directive = new NodePermissionDirective(null, null, null); - spyOn(directive, 'updateElement').and.stub(); + describe('HTML nativeElement as subject', () => { - directive.ngAfterViewInit(); + it('updates element once it is loaded', () => { + const directive = new NodePermissionDirective(null, null, null); + spyOn(directive, 'updateElement').and.stub(); - expect(directive.updateElement).toHaveBeenCalled(); + 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(); + }); }); - it('updates element on nodes change', () => { - const directive = new NodePermissionDirective(null, null, null); - spyOn(directive, 'updateElement').and.stub(); + describe('Angular component as subject', () => { - const nodes = [{}, {}]; - const change = new SimpleChange([], nodes, false); - directive.ngOnChanges({ nodes: change }); + it('disables decorated component', () => { + const contentService = new AlfrescoContentService(null, null, null); + spyOn(contentService, 'hasPermission').and.returnValue(false); - expect(directive.updateElement).toHaveBeenCalled(); + let testComponent = new TestComponent(); + testComponent.disabled = false; + const directive = new NodePermissionDirective(null, null, contentService, testComponent); + directive.nodes = [{}, {}]; + + directive.updateElement(); + + expect(testComponent.disabled).toBeTruthy(); + }); + + it('enables decorated component', () => { + const contentService = new AlfrescoContentService(null, null, null); + spyOn(contentService, 'hasPermission').and.returnValue(true); + + let testComponent = new TestComponent(); + testComponent.disabled = true; + const directive = new NodePermissionDirective(null, null, contentService, testComponent); + directive.nodes = [{}, {}]; + + directive.updateElement(); + + expect(testComponent.disabled).toBeFalsy(); + }); }); - - 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 index 959d487d3d..f707b6a2fa 100644 --- a/ng2-components/ng2-alfresco-core/src/directives/node-permission.directive.ts +++ b/ng2-components/ng2-alfresco-core/src/directives/node-permission.directive.ts @@ -15,10 +15,15 @@ * limitations under the License. */ -import { AfterViewInit, Directive, ElementRef, Input, OnChanges, Renderer2, SimpleChanges } from '@angular/core'; +import { AfterViewInit, Directive, ElementRef, Host, Inject, Input, OnChanges, Optional, Renderer2, SimpleChanges } from '@angular/core'; import { MinimalNodeEntity } from 'alfresco-js-api'; +import { EXTENDIBLE_COMPONENT } from './../interface/injection.tokens'; import { AlfrescoContentService } from './../services/alfresco-content.service'; +export interface NodePermissionSubject { + disabled: boolean; +} + @Directive({ selector: '[adf-node-permission]' }) @@ -32,7 +37,11 @@ export class NodePermissionDirective implements OnChanges, AfterViewInit { constructor(private elementRef: ElementRef, private renderer: Renderer2, - private contentService: AlfrescoContentService) { + private contentService: AlfrescoContentService, + + @Host() + @Optional() + @Inject(EXTENDIBLE_COMPONENT) private parentComponent?: NodePermissionSubject) { } ngAfterViewInit() { @@ -55,14 +64,30 @@ export class NodePermissionDirective implements OnChanges, AfterViewInit { let enable = this.hasPermission(this.nodes, this.permission); if (enable) { - this.enableElement(); + this.enable(); } else { - this.disableElement(); + this.disable(); } return enable; } + private enable(): void { + if (this.parentComponent) { + this.parentComponent.disabled = false; + } else { + this.enableElement(); + } + } + + private disable(): void { + if (this.parentComponent) { + this.parentComponent.disabled = true; + } else { + this.disableElement(); + } + } + /** * Enables decorated element * diff --git a/ng2-components/ng2-alfresco-core/src/directives/node-permission.md b/ng2-components/ng2-alfresco-core/src/directives/node-permission.md index 2504475e28..ef5f8725e3 100644 --- a/ng2-components/ng2-alfresco-core/src/directives/node-permission.md +++ b/ng2-components/ng2-alfresco-core/src/directives/node-permission.md @@ -1,5 +1,19 @@ # Node Permission Directive + + + + +- [Properties](#properties) +- [HTML element example](#html-element-example) +- [Angular component example](#angular-component-example) + * [Implementing the NodePermissionSubject interface](#implementing-the-nodepermissionsubject-interface) + * [Defining your components as an EXTENDIBLE_COMPONENT parent component](#defining-your-components-as-an-extendible_component-parent-component) + + + + + 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. @@ -8,7 +22,14 @@ 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 +## Properties + +| Name | Type | Default | Description | +| --- | --- | --- | --- | +| adf-node-permission | [Permissions](https://github.com/Alfresco/alfresco-ng2-components/blob/master/ng2-components/ng2-alfresco-core/src/models/permissions.enum.ts) | null | Node permission to check (create, delete, update, updatePermissions, !create, !delete, !update, !updatePermissions)| +| adf-nodes | MinimalNodeEntity[] | [] | Nodes to check permission for | + +## HTML element example The best example to show `NodePermissionDirective` in action is by binding DocumentList selection property to a toolbar button. @@ -30,9 +51,47 @@ For example the "Delete" button should be disabled if no selection is present or 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 +## Angular component example -| Name | Type | Default | Description | -| --- | --- | --- | --- | -| adf-node-permission | [Permissions](https://github.com/Alfresco/alfresco-ng2-components/blob/master/ng2-components/ng2-alfresco-core/src/models/permissions.enum.ts) | null | Node permission to check (create, delete, update, updatePermissions, !create, !delete, !update, !updatePermissions)| -| adf-nodes | MinimalNodeEntity[] | [] | Nodes to check permission for | \ No newline at end of file +You can apply the directive on any angular component which implements the NodePermissionSubject interface. The upload drag area component can be a good candidate, since this one implements that interface. Applying the directive on an angular component is pretty much the same as applying it on an html element. + +```html + + ... + +``` + +When designing a component you want to work this directive with, you have two important things to care about. + +### Implementing the NodePermissionSubject interface + +The component has to implement the NodePermissionSubject interface which basically means it has to have a boolean **disabled** property. This is the property which will be set by the directive. + +```js +import { NodePermissionSubject } from 'ng2-alfresco-core'; + +@Component({...}) +export class UploadDragAreaComponent implements NodePermissionSubject { + public disabled: boolean = false; +} +``` + +### Defining your components as an EXTENDIBLE_COMPONENT parent component + +The directive will look up the component in the dependency injection tree, up to at most one step above the current DI level (@Host). Because of this, you have to provide your component with forward referencing as the EXTENDIBLE_COMPONENT. + +```js +import { EXTENDIBLE_COMPONENT } from 'ng2-alfresco-core'; + +@Component({ + ... + providers: [ + { provide: EXTENDIBLE_COMPONENT, useExisting: forwardRef(() => UploadDragAreaComponent)} + ] +}) +export class UploadDragAreaComponent implements NodePermissionSubject { ... } +``` diff --git a/ng2-components/ng2-alfresco-core/src/interface/injection.tokens.ts b/ng2-components/ng2-alfresco-core/src/interface/injection.tokens.ts new file mode 100644 index 0000000000..82ce8c26a2 --- /dev/null +++ b/ng2-components/ng2-alfresco-core/src/interface/injection.tokens.ts @@ -0,0 +1,20 @@ +/*! + * @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 { Component, InjectionToken } from '@angular/core'; + +export const EXTENDIBLE_COMPONENT = new InjectionToken('extendible.component'); diff --git a/ng2-components/ng2-alfresco-upload/README.md b/ng2-components/ng2-alfresco-upload/README.md index 541f5fd033..512953f3c8 100644 --- a/ng2-components/ng2-alfresco-upload/README.md +++ b/ng2-components/ng2-alfresco-upload/README.md @@ -92,7 +92,7 @@ npm install ng2-alfresco-upload | parentId | string | empty | The ID of the root. It can be the nodeId if you are using the upload for the Content Service or taskId/processId for the Process Service. | | versioning | boolean | false | Versioning false is the default uploader behaviour and it renames the file using an integer suffix if there is a name clash. Versioning true to indicate that a major version should be created | | staticTitle | string | (predefined) | define the text of the upload button | -| disableWithNoPermission | boolean | false | If the value is true and the user doesn't have the permission to delete the node the button will be disabled | +| **(deprecated)** disableWithNoPermission ***use node permission directive from core instead*** | boolean | false | If the value is true and the user doesn't have the permission to delete the node the button will be disabled | | tooltip | string | | Custom tooltip | ### Events @@ -171,7 +171,8 @@ export class AppComponent { | Name | Type | Default | Description | | --- | --- | --- | --- | -| enabled | boolean | true | Toggle component enabled state | +| disabled | boolean | false | Toggle component disabled state | +| **(deprecated)** enabled | boolean | true | Toggle component enabled state | | **(deprecated)** showNotificationBar | boolean | true | Hide/show notification bar. **Deprecated in 1.6.0: use UploadService events and NotificationService api instead.** | | rootFolderId | string | '-root-' | The ID of the root folder node. | | **(deprecated)** currentFolderPath | string | '/' | define the path where the files are uploaded. **Deprecated in 1.6.0: use rootFolderId instead.** | diff --git a/ng2-components/ng2-alfresco-upload/src/components/upload-button.component.ts b/ng2-components/ng2-alfresco-upload/src/components/upload-button.component.ts index 3f222785ee..4294987f49 100644 --- a/ng2-components/ng2-alfresco-upload/src/components/upload-button.component.ts +++ b/ng2-components/ng2-alfresco-upload/src/components/upload-button.component.ts @@ -15,31 +15,47 @@ * limitations under the License. */ -import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core'; +import { Component, EventEmitter, forwardRef, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core'; import { MinimalNodeEntryEntity } from 'alfresco-js-api'; -import { AlfrescoApiService, AlfrescoTranslationService, FileModel, FileUtils, LogService, NotificationService, UploadService } from 'ng2-alfresco-core'; +import { + AlfrescoApiService, + AlfrescoTranslationService, + EXTENDIBLE_COMPONENT, + FileModel, + FileUtils, + LogService, + NodePermissionSubject, + NotificationService, + UploadService +} from 'ng2-alfresco-core'; import { Observable, Subject } from 'rxjs/Rx'; import { PermissionModel } from '../models/permissions.model'; @Component({ selector: 'adf-upload-button, alfresco-upload-button', templateUrl: './upload-button.component.html', - styleUrls: ['./upload-button.component.css'] + styleUrls: ['./upload-button.component.css'], + providers: [ + { provide: EXTENDIBLE_COMPONENT, useExisting: forwardRef(() => UploadButtonComponent)} + ] }) -export class UploadButtonComponent implements OnInit, OnChanges { +export class UploadButtonComponent implements OnInit, OnChanges, NodePermissionSubject { + + /** @deprecated Deprecated in 1.6.0, you can use UploadService events and NotificationService api instead. */ + @Input() + showNotificationBar: boolean = true; + + /** @deprecated Deprecated in 1.6.0, this property is not used for couple of releases already. */ + @Input() + currentFolderPath: string = '/'; + + /** @deprecated Deprecated in 1.8.0, use the button with combination of adf-node-permission directive */ + @Input() + disableWithNoPermission: boolean = false; @Input() disabled: boolean = false; - /** - * @deprecated Deprecated in 1.6.0, you can use UploadService events and NotificationService api instead. - * - * @type {boolean} - * @memberof UploadButtonComponent - */ - @Input() - showNotificationBar: boolean = true; - @Input() uploadFolders: boolean = false; @@ -58,21 +74,9 @@ export class UploadButtonComponent implements OnInit, OnChanges { @Input() tooltip: string = null; - /** - * @deprecated Deprecated in 1.6.0, this property is not used for couple of releases already. - * - * @type {string} - * @memberof UploadDragAreaComponent - */ - @Input() - currentFolderPath: string = '/'; - @Input() rootFolderId: string = '-root-'; - @Input() - disableWithNoPermission: boolean = false; - @Output() onSuccess = new EventEmitter(); @@ -117,6 +121,7 @@ export class UploadButtonComponent implements OnInit, OnChanges { return this.disabled ? true : undefined; } + /** @deprecated Deprecated in 1.8.0, use the button with combination of adf-node-permission directive */ isDisableWithNoPermission(): boolean { return !this.hasPermission && this.disableWithNoPermission ? true : undefined; } diff --git a/ng2-components/ng2-alfresco-upload/src/components/upload-drag-area.component.html b/ng2-components/ng2-alfresco-upload/src/components/upload-drag-area.component.html index 682099393c..d027a83076 100644 --- a/ng2-components/ng2-alfresco-upload/src/components/upload-drag-area.component.html +++ b/ng2-components/ng2-alfresco-upload/src/components/upload-drag-area.component.html @@ -1,4 +1,4 @@ -
{ @@ -100,6 +98,96 @@ describe('UploadDragAreaComponent', () => { TestBed.resetTestingModule(); }); + describe('When disabled', () => { + + it('should NOT upload the list of files dropped', () => { + component.enabled = false; + spyOn(uploadService, 'addToQueue'); + spyOn(uploadService, 'uploadFilesInTheQueue'); + fixture.detectChanges(); + + const file = {name: 'fake-name-1', size: 10, webkitRelativePath: 'fake-folder1/fake-name-1.json'}; + let filesList = [file]; + component.onFilesDropped(filesList); + + expect(uploadService.addToQueue).not.toHaveBeenCalled(); + expect(uploadService.uploadFilesInTheQueue).not.toHaveBeenCalled(); + }); + + it('should NOT upload the file dropped', () => { + component.enabled = false; + spyOn(uploadService, 'addToQueue'); + spyOn(uploadService, 'uploadFilesInTheQueue'); + fixture.detectChanges(); + + let itemEntity = { + fullPath: '/folder-fake/file-fake.png', + isDirectory: false, + isFile: true, + name: 'file-fake.png', + file: (callbackFile) => { + let fileFake = new File(['fakefake'], 'file-fake.png', {type: 'image/png'}); + callbackFile(fileFake); + } + }; + component.onFilesEntityDropped(itemEntity); + + expect(uploadService.addToQueue).not.toHaveBeenCalled(); + expect(uploadService.uploadFilesInTheQueue).not.toHaveBeenCalled(); + }); + + it('should NOT upload the folder dropped', (done) => { + component.enabled = false; + spyOn(uploadService, 'addToQueue'); + spyOn(uploadService, 'uploadFilesInTheQueue'); + fixture.detectChanges(); + + let itemEntity = { + isDirectory: true, + createReader: () => { + return { + readEntries: (cb) => { + cb([]); + } + }; + } + }; + component.onFolderEntityDropped(itemEntity); + + setTimeout(() => { + expect(uploadService.addToQueue).not.toHaveBeenCalled(); + expect(uploadService.uploadFilesInTheQueue).not.toHaveBeenCalled(); + done(); + }, 0); + }); + + it('should NOT upload the files', () => { + component.enabled = false; + spyOn(uploadService, 'addToQueue'); + spyOn(uploadService, 'uploadFilesInTheQueue'); + + let fakeItem = { + fullPath: '/folder-fake/file-fake.png', + isDirectory: false, + isFile: true, + name: 'file-fake.png', + file: (callbackFile) => { + let fileFake = new File(['fakefake'], 'file-fake.png', {type: 'image/png'}); + callbackFile(fileFake); + } + }; + fixture.detectChanges(); + + let fakeCustomEvent: CustomEvent = new CustomEvent('CustomEvent', { + detail: { data: getFakeShareDataRow([]), files: [fakeItem] } + }); + component.onUploadFiles(fakeCustomEvent); + + expect(uploadService.addToQueue).not.toHaveBeenCalled(); + expect(uploadService.uploadFilesInTheQueue).not.toHaveBeenCalled(); + }); + }); + it('should upload the list of files dropped', (done) => { component.currentFolderPath = '/root-fake-/sites-fake/folder-fake'; component.onSuccess = null; @@ -203,7 +291,7 @@ describe('UploadDragAreaComponent', () => { let fakeCustomEvent: CustomEvent = new CustomEvent('CustomEvent', { detail: { - data: fakeShareDataRow, + data: getFakeShareDataRow(), files: [fakeItem] } }); diff --git a/ng2-components/ng2-alfresco-upload/src/components/upload-drag-area.component.ts b/ng2-components/ng2-alfresco-upload/src/components/upload-drag-area.component.ts index bc8188ba92..a8c57ca11d 100644 --- a/ng2-components/ng2-alfresco-upload/src/components/upload-drag-area.component.ts +++ b/ng2-components/ng2-alfresco-upload/src/components/upload-drag-area.component.ts @@ -15,49 +15,59 @@ * limitations under the License. */ -import { Component, EventEmitter, Input, Output } from '@angular/core'; -import { AlfrescoTranslationService, FileInfo, FileModel, FileUtils, NotificationService, UploadService } from 'ng2-alfresco-core'; +import { Component, EventEmitter, forwardRef, Input, Output } from '@angular/core'; +import { + AlfrescoTranslationService, + EXTENDIBLE_COMPONENT, + FileInfo, + FileModel, + FileUtils, + NodePermissionSubject, + NotificationService, + UploadService +} from 'ng2-alfresco-core'; @Component({ selector: 'adf-upload-drag-area, alfresco-upload-drag-area', templateUrl: './upload-drag-area.component.html', - styleUrls: ['./upload-drag-area.component.css'] + styleUrls: ['./upload-drag-area.component.css'], + providers: [ + { provide: EXTENDIBLE_COMPONENT, useExisting: forwardRef(() => UploadDragAreaComponent)} + ] }) -export class UploadDragAreaComponent { +export class UploadDragAreaComponent implements NodePermissionSubject { + /** @deprecated Deprecated in favor of disabled input property */ @Input() - enabled: boolean = true; + set enabled(enabled: boolean) { + console.warn('Deprecated: enabled input property should not be used for UploadDragAreaComponent. Please use disabled instead.'); + this.disabled = !enabled; + } - /** - * @deprecated Deprecated in 1.6.0, you can use UploadService events and NotificationService api instead. - * - * @type {boolean} - * @memberof UploadButtonComponent - */ + /** @deprecated Deprecated in favor of disabled input property */ + get enabled(): boolean { + console.warn('Deprecated: enabled input property should not be used for UploadDragAreaComponent. Please use disabled instead.'); + return !this.disabled; + } + + /** @deprecated Deprecated in 1.6.0, you can use UploadService events and NotificationService api instead. */ @Input() showNotificationBar: boolean = true; - @Input() - versioning: boolean = false; - - /** - * @deprecated Deprecated in 1.6.0, this property is not used for couple of releases already. Use rootFolderId instead. - * - * @type {string} - * @memberof UploadDragAreaComponent - */ + /** @deprecated Deprecated in 1.6.0, this property is not used for couple of releases already. Use rootFolderId instead. */ @Input() currentFolderPath: string = '/'; - /** - * @deprecated Deprecated in 1.6.2, this property is not used for couple of releases already. Use parentId instead. - * - * @type {string} - * @memberof UploadDragAreaComponent - */ + /** @deprecated Deprecated in 1.6.2, this property is not used for couple of releases already. Use parentId instead. */ @Input() rootFolderId: string = '-root-'; + @Input() + disabled: boolean = false; + + @Input() + versioning: boolean = false; + @Input() parentId: string; @@ -69,38 +79,13 @@ export class UploadDragAreaComponent { private notificationService: NotificationService) { } - /** - * Handles 'upload-files' events raised by child components. - * @param event DOM event - */ - onUploadFiles(event: CustomEvent) { - event.stopPropagation(); - event.preventDefault(); - let isAllowed: boolean = this.isAllowed(event.detail.data.obj.entry); - if (isAllowed) { - let files: FileInfo[] = event.detail.files; - if (files && files.length > 0) { - let parentId = this.parentId || this.rootFolderId; - if (event.detail.data && event.detail.data.obj.entry.isFolder) { - parentId = event.detail.data.obj.entry.id || this.parentId || this.rootFolderId; - } - const fileModels = files.map(fileInfo => new FileModel(fileInfo.file, { - newVersion: this.versioning, - path: fileInfo.relativeFolder, - parentId: parentId - })); - this.uploadFiles(fileModels, isAllowed); - } - } - } - /** * Method called when files are dropped in the drag area. * * @param {File[]} files - files dropped in the drag area. */ onFilesDropped(files: File[]): void { - if (this.enabled && files.length) { + if (!this.disabled && files.length) { const fileModels = files.map(file => new FileModel(file, { newVersion: this.versioning, path: '/', @@ -117,10 +102,11 @@ export class UploadDragAreaComponent { /** * Called when the file are dropped in the drag area + * * @param item - FileEntity */ onFilesEntityDropped(item: any): void { - if (this.enabled) { + if (!this.disabled) { item.file((file: File) => { const fileModel = new FileModel(file, { newVersion: this.versioning, @@ -138,10 +124,11 @@ export class UploadDragAreaComponent { /** * Called when a folder are dropped in the drag area + * * @param folder - name of the dropped folder */ onFolderEntityDropped(folder: any): void { - if (this.enabled && folder.isDirectory) { + if (!this.disabled && folder.isDirectory) { FileUtils.flattern(folder).then(entries => { let files = entries.map(entry => { return new FileModel(entry.file, { @@ -178,6 +165,7 @@ export class UploadDragAreaComponent { /** * Show the error inside Notification bar + * * @param Error message * @private */ @@ -185,8 +173,44 @@ export class UploadDragAreaComponent { this.notificationService.openSnackMessage(errorMessage, 3000); } - private uploadFiles(files: FileModel[], isAllowed: boolean): void { - if (isAllowed && files.length) { + /** Returns true or false considering the component options and node permissions */ + isDroppable(): boolean { + return !this.disabled; + } + + /** + * Handles 'upload-files' events raised by child components. + * + * @param event DOM event + */ + onUploadFiles(event: CustomEvent) { + event.stopPropagation(); + event.preventDefault(); + let isAllowed: boolean = this.hasCreatePermission(event.detail.data.obj.entry); + if (isAllowed) { + let files: FileInfo[] = event.detail.files; + if (files && files.length > 0) { + let parentId = this.parentId || this.rootFolderId; + if (event.detail.data && event.detail.data.obj.entry.isFolder) { + parentId = event.detail.data.obj.entry.id || this.parentId || this.rootFolderId; + } + const fileModels = files.map(fileInfo => new FileModel(fileInfo.file, { + newVersion: this.versioning, + path: fileInfo.relativeFolder, + parentId: parentId + })); + this.uploadFiles(fileModels); + } + } + } + + /** + * Does the actual file uploading and show the notification + * + * @param files + */ + private uploadFiles(files: FileModel[]): void { + if (files.length) { this.uploadService.addToQueue(...files); this.uploadService.uploadFilesInTheQueue(this.onSuccess); let latestFilesAdded = this.uploadService.getQueue(); @@ -196,6 +220,11 @@ export class UploadDragAreaComponent { } } + /** + * Check if "create" permission is present on the given node + * + * @param node + */ private hasCreatePermission(node: any): boolean { let isPermitted = false; if (node && node['allowableOperations']) { @@ -204,8 +233,4 @@ export class UploadDragAreaComponent { } return isPermitted; } - - private isAllowed(node: any) { - return this.enabled || this.hasCreatePermission(node); - } }