[AAE-3966] FE - Disable the upload button when the user doesn't have permissions in a folder (#6304)

* [AAE-3966] FE - Disable the upload button when the user doesn't have permissions in a folder

* * Handled both error messages while searching

* * Refactored names

* * Fixed unit tests and added some * Updated doc

* * Fixed comment
This commit is contained in:
siva kumar 2020-11-04 14:35:55 +05:30 committed by GitHub
parent 64ad7923c3
commit b8cacd3b4e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 130 additions and 44 deletions

View File

@ -54,6 +54,7 @@ Opens a [Content Node Selector](content-node-selector.component.md) in its own
| select | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`[`Node`](https://github.com/Alfresco/alfresco-js-api/blob/develop/src/api/content-rest-api/docs/Node.md)`[]>` | Emitted when the user has chosen an item. | | select | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`[`Node`](https://github.com/Alfresco/alfresco-js-api/blob/develop/src/api/content-rest-api/docs/Node.md)`[]>` | Emitted when the user has chosen an item. |
| showingSearch | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<boolean>` | Emitted when search is running. | | showingSearch | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<boolean>` | Emitted when search is running. |
| siteChange | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<string>` | Emitted when the select site changes. | | siteChange | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<string>` | Emitted when the select site changes. |
| currentFolder | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`[`Node`](https://github.com/Alfresco/alfresco-js-api/blob/develop/src/api/content-rest-api/docs/Node.md)`[]>` | Emitted when current folder loaded. |
## Details ## Details

View File

@ -213,6 +213,10 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
@Output() @Output()
showingSearch: EventEmitter<boolean> = new EventEmitter<boolean>(); showingSearch: EventEmitter<boolean> = new EventEmitter<boolean>();
/** Emitted when current folder loaded. */
@Output()
currentFolder: EventEmitter<Node> = new EventEmitter<Node>();
@ViewChild('documentList', { static: true }) @ViewChild('documentList', { static: true })
documentList: DocumentListComponent; documentList: DocumentListComponent;
@ -314,6 +318,12 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
this.onFileUploadEvent(); this.onFileUploadEvent();
this.resetPagination(); this.resetPagination();
this.setSearchScopeToNodes(); this.setSearchScopeToNodes();
this.documentList.$folderNode
.pipe(takeUntil(this.onDestroy$))
.subscribe((currentNode: Node) => {
this.currentFolder.emit(currentNode);
});
} }
ngOnDestroy() { ngOnDestroy() {

View File

@ -20,6 +20,7 @@
[showSearch]="data?.showSearch" [showSearch]="data?.showSearch"
[showDropdownSiteList]="data?.showDropdownSiteList" [showDropdownSiteList]="data?.showDropdownSiteList"
[showFilesInResult]="data?.showFilesInResult" [showFilesInResult]="data?.showFilesInResult"
(currentFolder)="onCurrentFolder($event)"
(select)="onSelect($event)" (select)="onSelect($event)"
(showingSearch)="onShowingSearch($event)" (showingSearch)="onShowingSearch($event)"
(siteChange)="onSiteChange($event)" (siteChange)="onSiteChange($event)"
@ -29,19 +30,24 @@
<mat-dialog-actions> <mat-dialog-actions>
<div> <div>
<adf-upload-button <ng-container *ngIf="data?.showLocalUploadButton">
*ngIf="data?.showLocalUploadButton" <adf-upload-button
[staticTitle]="'FORM.FIELD.UPLOAD' | translate " [staticTitle]="'FORM.FIELD.UPLOAD' | translate "
[multipleFiles]="isMultipleSelection()" [multipleFiles]="isMultipleSelection()"
[rootFolderId]="currentDirectoryId" [rootFolderId]="currentDirectoryId"
[disabled]="disableUploadButton" [disabled]="isNotAllowedToUpload()"
(error)="onError($event)"> (error)="onError($event)">
</adf-upload-button> </adf-upload-button>
<ng-container *ngIf="data?.showLocalUploadButton && disableUploadButton"> <ng-container>
<div class="adf-content-node-upload-button-warning-message"> <div class="adf-content-node-upload-button-warning-message" *ngIf="showingSearch">
<mat-icon>warning</mat-icon> <mat-icon>warning</mat-icon>
<span>{{ 'NODE_SELECTOR.UPLOAD_BUTTON_WARNING_MESSAGE' | translate }}</span> <span>{{ 'NODE_SELECTOR.UPLOAD_BUTTON_SEARCH_WARNING_MESSAGE' | translate }}</span>
</div> </div>
<div class="adf-content-node-upload-button-warning-message" *ngIf="!hasAllowableOperations && !showingSearch">
<mat-icon>warning</mat-icon>
<span>{{ 'NODE_SELECTOR.UPLOAD_BUTTON_PERMISSION_WARNING_MESSAGE' | translate }}</span>
</div>
</ng-container>
</ng-container> </ng-container>
</div> </div>
<div> <div>

View File

@ -34,6 +34,7 @@
.mat-dialog-actions { .mat-dialog-actions {
padding: 8px; padding: 8px;
height: 61px;
background-color: mat-color($background, background); background-color: mat-color($background, background);
display: flex; display: flex;
flex-direction: row; flex-direction: row;

View File

@ -19,10 +19,10 @@ import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { CUSTOM_ELEMENTS_SCHEMA, EventEmitter } from '@angular/core'; import { CUSTOM_ELEMENTS_SCHEMA, EventEmitter } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ContentNodeSelectorComponent } from './content-node-selector.component'; import { ContentNodeSelectorComponent } from './content-node-selector.component';
import { Node } from '@alfresco/js-api'; import { Node, NodeEntry } from '@alfresco/js-api';
import { ContentNodeSelectorPanelComponent } from '@alfresco/adf-content-services'; import { ContentNodeSelectorPanelComponent, UploadModule } from '@alfresco/adf-content-services';
import { By } from '@angular/platform-browser'; import { By } from '@angular/platform-browser';
import { setupTestBed, SitesService } from '@alfresco/adf-core'; import { setupTestBed, SitesService, ContentService } from '@alfresco/adf-core';
import { of } from 'rxjs'; import { of } from 'rxjs';
import { ContentTestingModule } from '../testing/content.testing.module'; import { ContentTestingModule } from '../testing/content.testing.module';
import { DocumentListService } from '../document-list/services/document-list.service'; import { DocumentListService } from '../document-list/services/document-list.service';
@ -41,13 +41,27 @@ describe('ContentNodeSelectorComponent', () => {
rowFilter: (shareDataRow: ShareDataRow) => shareDataRow.node.entry.name === 'impossible-name', rowFilter: (shareDataRow: ShareDataRow) => shareDataRow.node.entry.name === 'impossible-name',
imageResolver: () => 'piccolo', imageResolver: () => 'piccolo',
currentFolderId: 'cat-girl-nuku-nuku', currentFolderId: 'cat-girl-nuku-nuku',
selectionMode: 'multiple',
showLocalUploadButton: true showLocalUploadButton: true
}; };
const fakeFolderNodeWithPermission = new NodeEntry({
entry: {
allowableOperations: [
'create',
'update'
],
isFolder: true,
name: 'Folder Fake Name',
nodeType: 'cm:folder'
}
});
setupTestBed({ setupTestBed({
imports: [ imports: [
TranslateModule.forRoot(), TranslateModule.forRoot(),
ContentTestingModule ContentTestingModule,
UploadModule
], ],
providers: [ providers: [
{ provide: MAT_DIALOG_DATA, useValue: data } { provide: MAT_DIALOG_DATA, useValue: data }
@ -58,18 +72,26 @@ describe('ContentNodeSelectorComponent', () => {
beforeEach(() => { beforeEach(() => {
const documentListService: DocumentListService = TestBed.inject(DocumentListService); const documentListService: DocumentListService = TestBed.inject(DocumentListService);
const sitesService: SitesService = TestBed.inject(SitesService); const sitesService: SitesService = TestBed.inject(SitesService);
spyOn(documentListService, 'getFolder').and.returnValue(of({ list: [] })); spyOn(documentListService, 'getFolder').and.returnValue(of({ list: [] }));
spyOn(documentListService, 'getFolderNode').and.returnValue(of({ entry: {} })); spyOn(documentListService, 'getFolderNode').and.returnValue(of({ entry: {} }));
spyOn(sitesService, 'getSites').and.returnValue(of({ list: { entries: [] } })); spyOn(sitesService, 'getSites').and.returnValue(of({ list: { entries: [] } }));
fixture = TestBed.createComponent(ContentNodeSelectorComponent); fixture = TestBed.createComponent(ContentNodeSelectorComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
const contentService = TestBed.inject(ContentService);
spyOn(contentService, 'hasAllowableOperations').and.returnValue(true);
spyOn(contentService, 'getNode').and.returnValue(of(fakeFolderNodeWithPermission));
component.data.showLocalUploadButton = true;
component.hasAllowableOperations = true;
component.showingSearch = false;
fixture.detectChanges(); fixture.detectChanges();
}); });
afterEach(() => { afterEach(() => {
fixture.destroy(); fixture.destroy();
TestBed.resetTestingModule();
}); });
describe('Data injecting with the "Material dialog way"', () => { describe('Data injecting with the "Material dialog way"', () => {
@ -202,8 +224,10 @@ describe('ContentNodeSelectorComponent', () => {
expect(adfUploadButton.nativeElement.innerText).toEqual('file_uploadFORM.FIELD.UPLOAD'); expect(adfUploadButton.nativeElement.innerText).toEqual('file_uploadFORM.FIELD.UPLOAD');
}); });
it('should be able to disable UploadButton if disableUploadButton set to true', () => { it('should be able to disable UploadButton if showingSearch set to true', () => {
component.disableUploadButton = true; component.showingSearch = true;
component.hasAllowableOperations = true;
fixture.detectChanges(); fixture.detectChanges();
const adfUploadButton = fixture.debugElement.query(By.css('adf-upload-button button')); const adfUploadButton = fixture.debugElement.query(By.css('adf-upload-button button'));
@ -211,8 +235,10 @@ describe('ContentNodeSelectorComponent', () => {
expect(adfUploadButton.nativeElement.disabled).toBe(true); expect(adfUploadButton.nativeElement.disabled).toBe(true);
}); });
it('should be able to enable UploadButton if disableUploadButton set to false', () => { it('should be able to enable UploadButton if showingSearch set to false', () => {
component.disableUploadButton = false; component.showingSearch = false;
component.hasAllowableOperations = true;
fixture.detectChanges(); fixture.detectChanges();
const adfUploadButton = fixture.debugElement.query(By.css('adf-upload-button button')); const adfUploadButton = fixture.debugElement.query(By.css('adf-upload-button button'));
@ -220,38 +246,69 @@ describe('ContentNodeSelectorComponent', () => {
expect(adfUploadButton.nativeElement.disabled).toBe(false); expect(adfUploadButton.nativeElement.disabled).toBe(false);
}); });
it('should be able to show warning message if showLocalUploadButton and disableUploadButton set to true', () => { it('should be able to show warning message while searching', () => {
component.disableUploadButton = true;
component.data.showLocalUploadButton = true; component.data.showLocalUploadButton = true;
component.showingSearch = true;
component.hasAllowableOperations = false;
fixture.detectChanges(); fixture.detectChanges();
const warnningMessage = fixture.debugElement.query(By.css('.adf-content-node-upload-button-warning-message span')); const warnningMessage = fixture.debugElement.query(By.css('.adf-content-node-upload-button-warning-message span'));
expect(warnningMessage).not.toBeNull(); expect(warnningMessage).not.toBeNull();
expect(warnningMessage.nativeElement.innerText).toEqual('NODE_SELECTOR.UPLOAD_BUTTON_WARNING_MESSAGE'); expect(warnningMessage.nativeElement.innerText).toEqual('NODE_SELECTOR.UPLOAD_BUTTON_SEARCH_WARNING_MESSAGE');
}); });
it('should not be able to show warning message if showLocalUploadButton set to false', () => { it('should not be able to show warning message if it is not in search mode', () => {
component.data.showLocalUploadButton = false;
component.disableUploadButton = false;
const warnningMessage = fixture.debugElement.query(By.css('.adf-content-node-upload-button-warning-message'));
expect(warnningMessage).toBeNull();
});
it('should not be able to show warning message if disableUploadButton set to false', () => {
component.data.showLocalUploadButton = true; component.data.showLocalUploadButton = true;
component.disableUploadButton = false; component.showingSearch = false;
const warnningMessage = fixture.debugElement.query(By.css('.adf-content-node-upload-button-warning-message'));
fixture.detectChanges();
const warnningMessage = fixture.debugElement.query(By.css('.adf-content-node-upload-button-warning-message span'));
expect(warnningMessage).toBeNull(); expect(warnningMessage).toBeNull();
}); });
it('should not be able to show upload button if showLocalUploadButton set to false', () => { it('should be able to disable UploadButton if user does not have allowable operations', () => {
component.data.showLocalUploadButton = false; component.hasAllowableOperations = false;
fixture.detectChanges();
const adfUploadButton = fixture.debugElement.query(By.css('adf-upload-button span'));
expect(adfUploadButton).toBeNull(); fixture.detectChanges();
const adfUploadButton = fixture.debugElement.query(By.css('adf-upload-button button'));
expect(adfUploadButton).not.toBeNull();
expect(adfUploadButton.nativeElement.disabled).toBe(true);
});
it('should be able to enable UploadButton if user has allowable operations', () => {
component.hasAllowableOperations = true;
fixture.detectChanges();
const adfUploadButton = fixture.debugElement.query(By.css('adf-upload-button button'));
expect(adfUploadButton).not.toBeNull();
expect(adfUploadButton.nativeElement.disabled).toBe(false);
});
it('should not be able to show warning message if user has allowable operations', () => {
component.data.showLocalUploadButton = true;
component.hasAllowableOperations = true;
component.showingSearch = false;
fixture.detectChanges();
const warnningMessage = fixture.debugElement.query(By.css('.adf-content-node-upload-button-warning-message span'));
expect(warnningMessage).toBeNull();
});
it('should be able to show warning message if user does not have allowable operations', () => {
component.data.showLocalUploadButton = true;
component.hasAllowableOperations = false;
component.showingSearch = false;
fixture.detectChanges();
const warnningMessage = fixture.debugElement.query(By.css('.adf-content-node-upload-button-warning-message span'));
expect(warnningMessage).not.toBeNull();
expect(warnningMessage.nativeElement.innerText).toEqual('NODE_SELECTOR.UPLOAD_BUTTON_PERMISSION_WARNING_MESSAGE');
}); });
}); });
}); });

View File

@ -17,7 +17,7 @@
import { Component, Inject, ViewEncapsulation } from '@angular/core'; import { Component, Inject, ViewEncapsulation } from '@angular/core';
import { MAT_DIALOG_DATA } from '@angular/material/dialog'; import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { TranslationService, NotificationService } from '@alfresco/adf-core'; import { TranslationService, NotificationService, AllowableOperationsEnum, ContentService } from '@alfresco/adf-core';
import { Node } from '@alfresco/js-api'; import { Node } from '@alfresco/js-api';
import { ContentNodeSelectorComponentData } from './content-node-selector.component-data.interface'; import { ContentNodeSelectorComponentData } from './content-node-selector.component-data.interface';
import { NodeEntryEvent } from '../document-list/components/node.event'; import { NodeEntryEvent } from '../document-list/components/node.event';
@ -34,9 +34,11 @@ export class ContentNodeSelectorComponent {
buttonActionName: string; buttonActionName: string;
chosenNode: Node[]; chosenNode: Node[];
currentDirectoryId: string; currentDirectoryId: string;
disableUploadButton = false; showingSearch = false;
hasAllowableOperations = false;
constructor(private translation: TranslationService, constructor(private translation: TranslationService,
private contentService: ContentService,
private notificationService: NotificationService, private notificationService: NotificationService,
@Inject(MAT_DIALOG_DATA) public data: ContentNodeSelectorComponentData) { @Inject(MAT_DIALOG_DATA) public data: ContentNodeSelectorComponentData) {
this.action = data.actionName ? data.actionName.toUpperCase() : 'CHOOSE'; this.action = data.actionName ? data.actionName.toUpperCase() : 'CHOOSE';
@ -89,6 +91,14 @@ export class ContentNodeSelectorComponent {
} }
onShowingSearch(value: boolean) { onShowingSearch(value: boolean) {
this.disableUploadButton = value; this.showingSearch = value;
}
onCurrentFolder(currentFolder: Node) {
this.hasAllowableOperations = this.contentService.hasAllowableOperations(currentFolder, AllowableOperationsEnum.CREATE);
}
isNotAllowedToUpload() {
return this.showingSearch || !this.hasAllowableOperations;
} }
} }

View File

@ -90,7 +90,8 @@
"SEARCH": "Search", "SEARCH": "Search",
"SEARCH_RESULTS": "Search results", "SEARCH_RESULTS": "Search results",
"SELECT_LOCATION": "Select Location", "SELECT_LOCATION": "Select Location",
"UPLOAD_BUTTON_WARNING_MESSAGE": "You can't upload a file whilst a search is still running" "UPLOAD_BUTTON_SEARCH_WARNING_MESSAGE": "You can't upload a file whilst a search is still running",
"UPLOAD_BUTTON_PERMISSION_WARNING_MESSAGE": "User doesn't have permission to upload content to the folder"
}, },
"OPERATION": { "OPERATION": {
"SUCCESS": { "SUCCESS": {