diff --git a/docs/content-services/components/content-node-selector-panel.component.md b/docs/content-services/components/content-node-selector-panel.component.md index b2c2307c13..ff31d078ba 100644 --- a/docs/content-services/components/content-node-selector-panel.component.md +++ b/docs/content-services/components/content-node-selector-panel.component.md @@ -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. | | showingSearch | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`` | Emitted when search is running. | | siteChange | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`` | 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 diff --git a/lib/content-services/src/lib/content-node-selector/content-node-selector-panel.component.ts b/lib/content-services/src/lib/content-node-selector/content-node-selector-panel.component.ts index 909f6e00bf..fbab2bcc86 100644 --- a/lib/content-services/src/lib/content-node-selector/content-node-selector-panel.component.ts +++ b/lib/content-services/src/lib/content-node-selector/content-node-selector-panel.component.ts @@ -213,6 +213,10 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy { @Output() showingSearch: EventEmitter = new EventEmitter(); + /** Emitted when current folder loaded. */ + @Output() + currentFolder: EventEmitter = new EventEmitter(); + @ViewChild('documentList', { static: true }) documentList: DocumentListComponent; @@ -314,6 +318,12 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy { this.onFileUploadEvent(); this.resetPagination(); this.setSearchScopeToNodes(); + + this.documentList.$folderNode + .pipe(takeUntil(this.onDestroy$)) + .subscribe((currentNode: Node) => { + this.currentFolder.emit(currentNode); + }); } ngOnDestroy() { diff --git a/lib/content-services/src/lib/content-node-selector/content-node-selector.component.html b/lib/content-services/src/lib/content-node-selector/content-node-selector.component.html index be08f37312..cd3f894e90 100644 --- a/lib/content-services/src/lib/content-node-selector/content-node-selector.component.html +++ b/lib/content-services/src/lib/content-node-selector/content-node-selector.component.html @@ -20,6 +20,7 @@ [showSearch]="data?.showSearch" [showDropdownSiteList]="data?.showDropdownSiteList" [showFilesInResult]="data?.showFilesInResult" + (currentFolder)="onCurrentFolder($event)" (select)="onSelect($event)" (showingSearch)="onShowingSearch($event)" (siteChange)="onSiteChange($event)" @@ -29,19 +30,24 @@
- - - -
- warning - {{ 'NODE_SELECTOR.UPLOAD_BUTTON_WARNING_MESSAGE' | translate }} -
+ + + + +
+ warning + {{ 'NODE_SELECTOR.UPLOAD_BUTTON_SEARCH_WARNING_MESSAGE' | translate }} +
+
+ warning + {{ 'NODE_SELECTOR.UPLOAD_BUTTON_PERMISSION_WARNING_MESSAGE' | translate }} +
+
diff --git a/lib/content-services/src/lib/content-node-selector/content-node-selector.component.scss b/lib/content-services/src/lib/content-node-selector/content-node-selector.component.scss index c4285ee137..2955a6590c 100644 --- a/lib/content-services/src/lib/content-node-selector/content-node-selector.component.scss +++ b/lib/content-services/src/lib/content-node-selector/content-node-selector.component.scss @@ -34,6 +34,7 @@ .mat-dialog-actions { padding: 8px; + height: 61px; background-color: mat-color($background, background); display: flex; flex-direction: row; diff --git a/lib/content-services/src/lib/content-node-selector/content-node-selector.component.spec.ts b/lib/content-services/src/lib/content-node-selector/content-node-selector.component.spec.ts index c332ad4914..4e6c596eac 100644 --- a/lib/content-services/src/lib/content-node-selector/content-node-selector.component.spec.ts +++ b/lib/content-services/src/lib/content-node-selector/content-node-selector.component.spec.ts @@ -19,10 +19,10 @@ import { MAT_DIALOG_DATA } from '@angular/material/dialog'; import { CUSTOM_ELEMENTS_SCHEMA, EventEmitter } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ContentNodeSelectorComponent } from './content-node-selector.component'; -import { Node } from '@alfresco/js-api'; -import { ContentNodeSelectorPanelComponent } from '@alfresco/adf-content-services'; +import { Node, NodeEntry } from '@alfresco/js-api'; +import { ContentNodeSelectorPanelComponent, UploadModule } from '@alfresco/adf-content-services'; 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 { ContentTestingModule } from '../testing/content.testing.module'; import { DocumentListService } from '../document-list/services/document-list.service'; @@ -41,13 +41,27 @@ describe('ContentNodeSelectorComponent', () => { rowFilter: (shareDataRow: ShareDataRow) => shareDataRow.node.entry.name === 'impossible-name', imageResolver: () => 'piccolo', currentFolderId: 'cat-girl-nuku-nuku', + selectionMode: 'multiple', showLocalUploadButton: true }; + const fakeFolderNodeWithPermission = new NodeEntry({ + entry: { + allowableOperations: [ + 'create', + 'update' + ], + isFolder: true, + name: 'Folder Fake Name', + nodeType: 'cm:folder' + } + }); + setupTestBed({ imports: [ TranslateModule.forRoot(), - ContentTestingModule + ContentTestingModule, + UploadModule ], providers: [ { provide: MAT_DIALOG_DATA, useValue: data } @@ -58,18 +72,26 @@ describe('ContentNodeSelectorComponent', () => { beforeEach(() => { const documentListService: DocumentListService = TestBed.inject(DocumentListService); const sitesService: SitesService = TestBed.inject(SitesService); + spyOn(documentListService, 'getFolder').and.returnValue(of({ list: [] })); spyOn(documentListService, 'getFolderNode').and.returnValue(of({ entry: {} })); spyOn(sitesService, 'getSites').and.returnValue(of({ list: { entries: [] } })); fixture = TestBed.createComponent(ContentNodeSelectorComponent); 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(); }); afterEach(() => { fixture.destroy(); + TestBed.resetTestingModule(); }); describe('Data injecting with the "Material dialog way"', () => { @@ -202,8 +224,10 @@ describe('ContentNodeSelectorComponent', () => { expect(adfUploadButton.nativeElement.innerText).toEqual('file_uploadFORM.FIELD.UPLOAD'); }); - it('should be able to disable UploadButton if disableUploadButton set to true', () => { - component.disableUploadButton = true; + it('should be able to disable UploadButton if showingSearch set to true', () => { + component.showingSearch = true; + component.hasAllowableOperations = true; + fixture.detectChanges(); const adfUploadButton = fixture.debugElement.query(By.css('adf-upload-button button')); @@ -211,8 +235,10 @@ describe('ContentNodeSelectorComponent', () => { expect(adfUploadButton.nativeElement.disabled).toBe(true); }); - it('should be able to enable UploadButton if disableUploadButton set to false', () => { - component.disableUploadButton = false; + it('should be able to enable UploadButton if showingSearch set to false', () => { + component.showingSearch = false; + component.hasAllowableOperations = true; + fixture.detectChanges(); const adfUploadButton = fixture.debugElement.query(By.css('adf-upload-button button')); @@ -220,38 +246,69 @@ describe('ContentNodeSelectorComponent', () => { expect(adfUploadButton.nativeElement.disabled).toBe(false); }); - it('should be able to show warning message if showLocalUploadButton and disableUploadButton set to true', () => { - component.disableUploadButton = true; + it('should be able to show warning message while searching', () => { component.data.showLocalUploadButton = true; + component.showingSearch = true; + component.hasAllowableOperations = 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_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', () => { - 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', () => { + it('should not be able to show warning message if it is not in search mode', () => { component.data.showLocalUploadButton = true; - component.disableUploadButton = false; - const warnningMessage = fixture.debugElement.query(By.css('.adf-content-node-upload-button-warning-message')); + 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 not be able to show upload button if showLocalUploadButton set to false', () => { - component.data.showLocalUploadButton = false; - fixture.detectChanges(); - const adfUploadButton = fixture.debugElement.query(By.css('adf-upload-button span')); + it('should be able to disable UploadButton if user does not have allowable operations', () => { + component.hasAllowableOperations = false; - 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'); }); }); }); diff --git a/lib/content-services/src/lib/content-node-selector/content-node-selector.component.ts b/lib/content-services/src/lib/content-node-selector/content-node-selector.component.ts index 86caf39fc9..01f36bcafd 100644 --- a/lib/content-services/src/lib/content-node-selector/content-node-selector.component.ts +++ b/lib/content-services/src/lib/content-node-selector/content-node-selector.component.ts @@ -17,7 +17,7 @@ import { Component, Inject, ViewEncapsulation } from '@angular/core'; 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 { ContentNodeSelectorComponentData } from './content-node-selector.component-data.interface'; import { NodeEntryEvent } from '../document-list/components/node.event'; @@ -34,9 +34,11 @@ export class ContentNodeSelectorComponent { buttonActionName: string; chosenNode: Node[]; currentDirectoryId: string; - disableUploadButton = false; + showingSearch = false; + hasAllowableOperations = false; constructor(private translation: TranslationService, + private contentService: ContentService, private notificationService: NotificationService, @Inject(MAT_DIALOG_DATA) public data: ContentNodeSelectorComponentData) { this.action = data.actionName ? data.actionName.toUpperCase() : 'CHOOSE'; @@ -89,6 +91,14 @@ export class ContentNodeSelectorComponent { } 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; } } diff --git a/lib/content-services/src/lib/i18n/en.json b/lib/content-services/src/lib/i18n/en.json index c34c0253dc..c528088e28 100644 --- a/lib/content-services/src/lib/i18n/en.json +++ b/lib/content-services/src/lib/i18n/en.json @@ -90,7 +90,8 @@ "SEARCH": "Search", "SEARCH_RESULTS": "Search results", "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": { "SUCCESS": {