mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-24 17:32:15 +00:00
Disable upload button with no permission (#1820)
* Add the permission check on the activiti button * Fix code after unit test * Add basic documentation
This commit is contained in:
committed by
Mario Romano
parent
721e96c01b
commit
0e4dab8b66
@@ -178,6 +178,43 @@ Attribute | Options | Default | Description | Mandatory
|
||||
`currentFolderPath` | *string* | '/Sites/swsdp/documentLibrary' | define the path where the files are uploaded |
|
||||
`versioning` | *boolean* | false | Versioning false is the default uploader behaviour and it rename using an integer suffix if there is a name clash. Versioning true to indicate that a major version should be created |
|
||||
`staticTitle` | *string* | 'FILE_UPLOAD.BUTTON.UPLOAD_FILE' or 'FILE_UPLOAD.BUTTON.UPLOAD_FOLDER' string in the JSON text file | 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 |
|
||||
|
||||
### How to show notification message with no permission
|
||||
You can show a notification error when the user doesn't have the right permission to perform the action.
|
||||
The UploadButtonComponent provides the event permissionEvent that is raised when the delete permission is missing
|
||||
You can subscribe to this event from your component and use the NotificationService to show a message.
|
||||
|
||||
```html
|
||||
<alfresco-upload-button
|
||||
[rootFolderId]="currentFolderId"
|
||||
(permissionEvent)="onUploadPermissionFailed($event)">
|
||||
</alfresco-upload-button>
|
||||
|
||||
export class MyComponent {
|
||||
|
||||
onUploadPermissionFailed(event: any) {
|
||||
this.notificationService.openSnackMessage(`you don't have the ${event.permission} permission to ${event.action} the ${event.type} `, 4000);
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||

|
||||
|
||||
#### How to disable the button when the delete permission is missing
|
||||
You can easily disable the button when the user doesn't own the permission to perform the action.
|
||||
The UploadButtonComponent provides the property disableWithNoPermission that can be true. In this way the button should be disabled if the delete permission is missing for the node.
|
||||
|
||||
```html
|
||||
<alfresco-upload-button
|
||||
[rootFolderId]="currentFolderId"
|
||||
[disableWithNoPermission]="true">
|
||||
</alfresco-upload-button>
|
||||
```
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
### Drag and drop
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 155 KiB |
Binary file not shown.
After Width: | Height: | Size: 171 KiB |
@@ -9,6 +9,27 @@
|
||||
z-index: 4;
|
||||
}
|
||||
|
||||
|
||||
.mdl-button--file--disabled {
|
||||
background: rgba(0,0,0,.12);
|
||||
color: rgba(0,0,0,.26);
|
||||
}
|
||||
|
||||
.mdl-button--file--disabled:hover {
|
||||
background: rgba(0,0,0,.12);
|
||||
color: rgba(0,0,0,.26);
|
||||
}
|
||||
|
||||
.mdl-button--file--disabled:focus:not(:active) {
|
||||
box-shadow: 0 0 8px rgba(0,0,0,.18), 0 8px 16px rgba(0,0,0,.36);
|
||||
background-color: rgba(158,158,158,.4);
|
||||
}
|
||||
|
||||
.mdl-button--file--disabled input[type="file"]:disabled {
|
||||
cursor: default;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.mdl-textfield--file .mdl-textfield__input {
|
||||
box-sizing: border-box;
|
||||
width: calc(100% - 32px);
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<form>
|
||||
<!--Files Upload-->
|
||||
<div *ngIf="!uploadFolders" class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored mdl-button--file">
|
||||
<div *ngIf="!uploadFolders" [class.mdl-button--file--disabled]="disableButton" class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored mdl-button--file">
|
||||
<i class="material-icons">file_upload</i>
|
||||
|
||||
<!--Multiple Files Upload-->
|
||||
@@ -11,6 +11,7 @@
|
||||
(change)="onFilesAdded($event)"
|
||||
multiple="multiple"
|
||||
accept="{{acceptedFilesType}}"
|
||||
[attr.disabled]="disableButton"
|
||||
#uploadFiles>
|
||||
</span>
|
||||
|
||||
@@ -21,6 +22,7 @@
|
||||
<input id="upload-single-file" data-automation-id="upload-single-file" type="file" name="uploadFiles"
|
||||
(change)="onFilesAdded($event)"
|
||||
accept="{{acceptedFilesType}}"
|
||||
[attr.disabled]="disableButton"
|
||||
#uploadFiles>
|
||||
</span>
|
||||
</div>
|
||||
@@ -34,6 +36,7 @@
|
||||
(change)="onDirectoryAdded($event)"
|
||||
multiple="multiple"
|
||||
accept="{{acceptedFilesType}}"
|
||||
[attr.disabled]="disableButton"
|
||||
webkitdirectory directory
|
||||
multiple #uploadFolders>
|
||||
</div>
|
||||
|
@@ -21,6 +21,7 @@ import { DebugElement } from '@angular/core';
|
||||
import { CoreModule, AlfrescoTranslationService, NotificationService } from 'ng2-alfresco-core';
|
||||
import { TranslationMock } from '../assets/translation.service.mock';
|
||||
import { UploadService } from '../services/upload.service';
|
||||
import { Observable } from 'rxjs/Rx';
|
||||
|
||||
describe('UploadButtonComponent', () => {
|
||||
|
||||
@@ -53,6 +54,25 @@ describe('UploadButtonComponent', () => {
|
||||
}
|
||||
};
|
||||
|
||||
let fakeFolderNodeWithoutPermission = {
|
||||
allowableOperations: [
|
||||
'update'
|
||||
],
|
||||
isFolder: true,
|
||||
name: 'Folder Fake Name',
|
||||
nodeType: 'cm:folder'
|
||||
};
|
||||
|
||||
let fakeFolderNodeWithPermission = {
|
||||
allowableOperations: [
|
||||
'create',
|
||||
'update'
|
||||
],
|
||||
isFolder: true,
|
||||
name: 'Folder Fake Name',
|
||||
nodeType: 'cm:folder'
|
||||
};
|
||||
|
||||
let fakeRejectPromise = new Promise(function (resolve, reject) {
|
||||
reject(fakeRejectRest);
|
||||
});
|
||||
@@ -117,9 +137,70 @@ describe('UploadButtonComponent', () => {
|
||||
expect(compiled.querySelector('#uploadFolder')).toBeDefined();
|
||||
});
|
||||
|
||||
it('should emit the permissionEvent, without permission and disableWithNoPermission false', (done) => {
|
||||
component.rootFolderId = '-my-';
|
||||
component.disableWithNoPermission = false;
|
||||
|
||||
spyOn(uploadService, 'getFolderNode').and.returnValue(Observable.of(fakeFolderNodeWithoutPermission));
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
component.permissionEvent.subscribe( permission => {
|
||||
expect(permission).toBeDefined();
|
||||
expect(permission.type).toEqual('content');
|
||||
expect(permission.action).toEqual('upload');
|
||||
expect(permission.permission).toEqual('create');
|
||||
done();
|
||||
});
|
||||
|
||||
component.onFilesAdded(fakeEvent);
|
||||
});
|
||||
|
||||
it('should show the disabled button, without permission and disableWithNoPermission true', () => {
|
||||
component.rootFolderId = '-my-';
|
||||
component.disableWithNoPermission = true;
|
||||
|
||||
spyOn(uploadService, 'getFolderNode').and.returnValue(Observable.of(fakeFolderNodeWithoutPermission));
|
||||
|
||||
component.onFilesAdded(fakeEvent);
|
||||
let compiled = fixture.debugElement.nativeElement;
|
||||
fixture.detectChanges();
|
||||
expect(compiled.querySelector('#upload-single-file')).toBeDefined();
|
||||
expect(compiled.querySelector('#upload-single-file').disabled).toBe(true);
|
||||
});
|
||||
|
||||
it('should show the enabled button with permission and disableWithNoPermission true', () => {
|
||||
component.rootFolderId = '-my-';
|
||||
component.disableWithNoPermission = true;
|
||||
|
||||
spyOn(uploadService, 'getFolderNode').and.returnValue(Observable.of(fakeFolderNodeWithPermission));
|
||||
|
||||
component.onFilesAdded(fakeEvent);
|
||||
let compiled = fixture.debugElement.nativeElement;
|
||||
fixture.detectChanges();
|
||||
expect(compiled.querySelector('#upload-single-file')).toBeDefined();
|
||||
expect(compiled.querySelector('#upload-single-file').disabled).toBe(false);
|
||||
});
|
||||
|
||||
it('should show the enabled button with permission and disableWithNoPermission false', () => {
|
||||
component.rootFolderId = '-my-';
|
||||
component.disableWithNoPermission = false;
|
||||
|
||||
spyOn(uploadService, 'getFolderNode').and.returnValue(Observable.of(fakeFolderNodeWithPermission));
|
||||
|
||||
component.onFilesAdded(fakeEvent);
|
||||
let compiled = fixture.debugElement.nativeElement;
|
||||
fixture.detectChanges();
|
||||
expect(compiled.querySelector('#upload-single-file')).toBeDefined();
|
||||
expect(compiled.querySelector('#upload-single-file').disabled).toBe(false);
|
||||
});
|
||||
|
||||
it('should call uploadFile with the default root folder', () => {
|
||||
component.currentFolderPath = '/root-fake-/sites-fake/folder-fake';
|
||||
component.onSuccess = null;
|
||||
|
||||
spyOn(uploadService, 'getFolderNode').and.returnValue(Observable.of(fakeFolderNodeWithPermission));
|
||||
|
||||
uploadService.uploadFilesInTheQueue = jasmine.createSpy('uploadFilesInTheQueue');
|
||||
|
||||
fixture.detectChanges();
|
||||
@@ -132,6 +213,8 @@ describe('UploadButtonComponent', () => {
|
||||
component.currentFolderPath = '/root-fake-/sites-fake/folder-fake';
|
||||
component.rootFolderId = '-my-';
|
||||
component.onSuccess = null;
|
||||
|
||||
spyOn(uploadService, 'getFolderNode').and.returnValue(Observable.of(fakeFolderNodeWithPermission));
|
||||
uploadService.uploadFilesInTheQueue = jasmine.createSpy('uploadFilesInTheQueue');
|
||||
|
||||
fixture.detectChanges();
|
||||
@@ -144,6 +227,7 @@ describe('UploadButtonComponent', () => {
|
||||
component.currentFolderPath = '/fake-root-path';
|
||||
fixture.detectChanges();
|
||||
|
||||
spyOn(uploadService, 'getFolderNode').and.returnValue(Observable.of(fakeFolderNodeWithPermission));
|
||||
spyOn(uploadService, 'callApiCreateFolder').and.returnValue(fakeResolvePromise);
|
||||
|
||||
component.onSuccess.subscribe(e => {
|
||||
@@ -161,6 +245,7 @@ describe('UploadButtonComponent', () => {
|
||||
});
|
||||
|
||||
it('should emit an onError event when the folder already exist', (done) => {
|
||||
spyOn(uploadService, 'getFolderNode').and.returnValue(Observable.of(fakeFolderNodeWithPermission));
|
||||
spyOn(uploadService, 'callApiCreateFolder').and.returnValue(fakeRejectPromise);
|
||||
component.onError.subscribe(e => {
|
||||
expect(e.value).toEqual('FILE_UPLOAD.MESSAGES.FOLDER_ALREADY_EXIST');
|
||||
|
@@ -15,11 +15,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Component, ElementRef, Input, Output, EventEmitter } from '@angular/core';
|
||||
import 'rxjs/Rx';
|
||||
import { Component, ElementRef, Input, Output, EventEmitter, OnInit, OnChanges } from '@angular/core';
|
||||
import { Subject } from 'rxjs/Rx';
|
||||
import { AlfrescoTranslationService, LogService, NotificationService } from 'ng2-alfresco-core';
|
||||
import { UploadService } from '../services/upload.service';
|
||||
import { FileModel } from '../models/file.model';
|
||||
import { PermissionModel } from '../models/permissions.model';
|
||||
|
||||
declare let componentHandler: any;
|
||||
|
||||
@@ -51,7 +52,7 @@ const ERROR_FOLDER_ALREADY_EXIST = 409;
|
||||
templateUrl: './upload-button.component.html',
|
||||
styleUrls: ['./upload-button.component.css']
|
||||
})
|
||||
export class UploadButtonComponent {
|
||||
export class UploadButtonComponent implements OnInit, OnChanges {
|
||||
|
||||
private static DEFAULT_ROOT_ID: string = '-root-';
|
||||
|
||||
@@ -79,6 +80,9 @@ export class UploadButtonComponent {
|
||||
@Input()
|
||||
rootFolderId: string = UploadButtonComponent.DEFAULT_ROOT_ID;
|
||||
|
||||
@Input()
|
||||
disableWithNoPermission: boolean = false;
|
||||
|
||||
@Output()
|
||||
onSuccess = new EventEmitter();
|
||||
|
||||
@@ -88,6 +92,13 @@ export class UploadButtonComponent {
|
||||
@Output()
|
||||
createFolder = new EventEmitter();
|
||||
|
||||
@Output()
|
||||
permissionEvent: EventEmitter<PermissionModel> = new EventEmitter<PermissionModel>();
|
||||
|
||||
private disableButton: boolean = false;
|
||||
|
||||
private permissionValue: Subject<boolean> = new Subject<boolean>();
|
||||
|
||||
constructor(private el: ElementRef,
|
||||
private uploadService: UploadService,
|
||||
private translateService: AlfrescoTranslationService,
|
||||
@@ -98,7 +109,18 @@ export class UploadButtonComponent {
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.permissionValue.subscribe((hasPermission: boolean) => {
|
||||
if (!hasPermission && this.disableWithNoPermission) {
|
||||
this.disableButton = true;
|
||||
} else {
|
||||
this.disableButton = undefined;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnChanges(changes) {
|
||||
this.checkPermission();
|
||||
let formFields = this.createFormFields();
|
||||
this.uploadService.setOptions(formFields, this.versioning);
|
||||
}
|
||||
@@ -110,7 +132,15 @@ export class UploadButtonComponent {
|
||||
*/
|
||||
onFilesAdded($event: any): void {
|
||||
let files = $event.currentTarget.files;
|
||||
this.uploadFiles(this.currentFolderPath, files);
|
||||
this.permissionValue.subscribe((hasPermission: boolean) => {
|
||||
if (hasPermission) {
|
||||
this.uploadFiles(this.currentFolderPath, files);
|
||||
} else {
|
||||
this.permissionEvent.emit(new PermissionModel({type: 'content', action: 'upload', permission: 'create'}));
|
||||
}
|
||||
});
|
||||
|
||||
this.checkPermission();
|
||||
// reset the value of the input file
|
||||
$event.target.value = '';
|
||||
}
|
||||
@@ -122,31 +152,39 @@ export class UploadButtonComponent {
|
||||
*/
|
||||
onDirectoryAdded($event: any): void {
|
||||
let files = $event.currentTarget.files;
|
||||
let hashMapDir = this.convertIntoHashMap(files);
|
||||
this.permissionValue.subscribe((hasPermission: boolean) => {
|
||||
if (hasPermission) {
|
||||
let hashMapDir = this.convertIntoHashMap(files);
|
||||
|
||||
hashMapDir.forEach((filesDir, directoryPath) => {
|
||||
let directoryName = this.getDirectoryName(directoryPath);
|
||||
let absolutePath = this.currentFolderPath + this.getDirectoryPath(directoryPath);
|
||||
hashMapDir.forEach((filesDir, directoryPath) => {
|
||||
let directoryName = this.getDirectoryName(directoryPath);
|
||||
let absolutePath = this.currentFolderPath + this.getDirectoryPath(directoryPath);
|
||||
|
||||
this.uploadService.createFolder(absolutePath, directoryName, this.rootFolderId)
|
||||
.subscribe(
|
||||
res => {
|
||||
let relativeDir = this.currentFolderPath + '/' + directoryPath;
|
||||
this.uploadFiles(relativeDir, filesDir);
|
||||
},
|
||||
error => {
|
||||
let errorMessagePlaceholder = this.getErrorMessage(error.response);
|
||||
if (errorMessagePlaceholder) {
|
||||
this.onError.emit({value: errorMessagePlaceholder});
|
||||
let errorMessage = this.formatString(errorMessagePlaceholder, [directoryName]);
|
||||
if (errorMessage) {
|
||||
this._showErrorNotificationBar(errorMessage);
|
||||
this.uploadService.createFolder(absolutePath, directoryName, this.rootFolderId)
|
||||
.subscribe(
|
||||
res => {
|
||||
let relativeDir = this.currentFolderPath + '/' + directoryPath;
|
||||
this.uploadFiles(relativeDir, filesDir);
|
||||
},
|
||||
error => {
|
||||
let errorMessagePlaceholder = this.getErrorMessage(error.response);
|
||||
if (errorMessagePlaceholder) {
|
||||
this.onError.emit({value: errorMessagePlaceholder});
|
||||
let errorMessage = this.formatString(errorMessagePlaceholder, [directoryName]);
|
||||
if (errorMessage) {
|
||||
this._showErrorNotificationBar(errorMessage);
|
||||
}
|
||||
}
|
||||
this.logService.error(error);
|
||||
}
|
||||
}
|
||||
this.logService.error(error);
|
||||
}
|
||||
);
|
||||
);
|
||||
});
|
||||
} else {
|
||||
this.permissionEvent.emit(new PermissionModel({type: 'content', action: 'upload', permission: 'create'}));
|
||||
}
|
||||
});
|
||||
|
||||
this.checkPermission();
|
||||
// reset the value of the input file
|
||||
$event.target.value = '';
|
||||
}
|
||||
@@ -272,4 +310,28 @@ export class UploadButtonComponent {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
checkPermission() {
|
||||
if (this.rootFolderId) {
|
||||
this.uploadService.getFolderNode(this.rootFolderId).subscribe(
|
||||
res => {
|
||||
this.permissionValue.next(this.hasCreatePermission(res));
|
||||
},
|
||||
error => {
|
||||
this.logService.error(error);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private hasCreatePermission(node: any): boolean {
|
||||
if (this.hasPermissions(node)) {
|
||||
return node.allowableOperations.find(permision => permision === 'create') ? true : false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private hasPermissions(node: any): boolean {
|
||||
return node && node.allowableOperations ? true : false;
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,30 @@
|
||||
/*!
|
||||
* @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.
|
||||
*/
|
||||
|
||||
export class PermissionModel {
|
||||
type: string;
|
||||
action: string;
|
||||
permission: string;
|
||||
|
||||
constructor(obj?: any) {
|
||||
if (obj) {
|
||||
this.type = obj.type || null;
|
||||
this.action = obj.action || null;
|
||||
this.permission = obj.permission || null;
|
||||
}
|
||||
}
|
||||
}
|
@@ -20,7 +20,7 @@ import { Response } from '@angular/http';
|
||||
import { Observer, Observable } from 'rxjs/Rx';
|
||||
import { AlfrescoApiService, LogService } from 'ng2-alfresco-core';
|
||||
import { FileModel } from '../models/file.model';
|
||||
import { MinimalNodeEntity } from 'alfresco-js-api';
|
||||
import { MinimalNodeEntity, MinimalNodeEntryEntity } from 'alfresco-js-api';
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -184,4 +184,17 @@ export class UploadService {
|
||||
this.totalCompletedObserver.next(total);
|
||||
}
|
||||
}
|
||||
|
||||
getFolderNode(nodeId: string): Observable<MinimalNodeEntryEntity> {
|
||||
let opts: any = {
|
||||
includeSource: true,
|
||||
include: ['allowableOperations']
|
||||
};
|
||||
|
||||
return Observable.fromPromise(this.apiService.getInstance().nodes.getNodeInfo(nodeId, opts))
|
||||
.map((response: any) => {
|
||||
return response;
|
||||
})
|
||||
.catch(err => this.handleError(err));
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user