diff --git a/src/app/common/directives/node-copy.directive.ts b/src/app/common/directives/node-copy.directive.ts index 5cb250cce..bd68d573f 100644 --- a/src/app/common/directives/node-copy.directive.ts +++ b/src/app/common/directives/node-copy.directive.ts @@ -122,7 +122,7 @@ export class NodeCopyDirective { Observable.forkJoin(...batch) .subscribe( () => { - this.content.deleteNode.next(null); + this.content.nodeDeleted.next(null); }, (error) => { let i18nMessageString = 'APP.MESSAGES.ERRORS.GENERIC'; diff --git a/src/app/common/directives/node-delete.directive.spec.ts b/src/app/common/directives/node-delete.directive.spec.ts index 323bc281e..e263a8e94 100644 --- a/src/app/common/directives/node-delete.directive.spec.ts +++ b/src/app/common/directives/node-delete.directive.spec.ts @@ -229,7 +229,7 @@ describe('NodeDeleteDirective', () => { }); it('signals files restored', () => { - spyOn(contentService.restoreNode, 'next'); + spyOn(contentService.nodeRestored, 'next'); spyOn(nodeApiService, 'restoreNode').and.callFake((id) => { if (id === '1') { return Observable.of(null); @@ -246,7 +246,7 @@ describe('NodeDeleteDirective', () => { fixture.detectChanges(); element.triggerEventHandler('click', null); - expect(contentService.restoreNode.next).toHaveBeenCalled(); + expect(contentService.nodeRestored.next).toHaveBeenCalled(); }); }); }); diff --git a/src/app/common/directives/node-delete.directive.ts b/src/app/common/directives/node-delete.directive.ts index 42017244d..90e3bcd4a 100644 --- a/src/app/common/directives/node-delete.directive.ts +++ b/src/app/common/directives/node-delete.directive.ts @@ -74,7 +74,7 @@ export class NodeDeleteDirective { .subscribe(() => this.restore(processedData.success)); if (processedData.someSucceeded) { - this.content.deleteNode.next(null); + this.content.nodeDeleted.next(null); } }); } @@ -103,7 +103,7 @@ export class NodeDeleteDirective { } if (processedData.someSucceeded) { - this.content.restoreNode.next(null); + this.content.nodeRestored.next(null); } } ); diff --git a/src/app/common/directives/node-move.directive.ts b/src/app/common/directives/node-move.directive.ts index 6bb4bcbfe..678792eda 100644 --- a/src/app/common/directives/node-move.directive.ts +++ b/src/app/common/directives/node-move.directive.ts @@ -64,7 +64,7 @@ export class NodeMoveDirective { const [ operationResult, moveResponse ] = result; this.toastMessage(operationResult, moveResponse); - this.content.moveNode.next(null); + this.content.nodeMoved.next(null); }, (error) => { this.toastMessage(error); @@ -190,7 +190,7 @@ export class NodeMoveDirective { }) .subscribe( () => { - this.content.moveNode.next(null); + this.content.nodeMoved.next(null); }, (error) => { diff --git a/src/app/common/services/content-management.service.ts b/src/app/common/services/content-management.service.ts index 477a0e970..125876f5c 100644 --- a/src/app/common/services/content-management.service.ts +++ b/src/app/common/services/content-management.service.ts @@ -25,10 +25,75 @@ import { Subject } from 'rxjs/Rx'; import { Injectable } from '@angular/core'; +import { AlfrescoApiService, NotificationService, TranslationService } from '@alfresco/adf-core'; +import { Node } from 'alfresco-js-api'; + @Injectable() export class ContentManagementService { - deleteNode = new Subject(); - moveNode = new Subject(); - restoreNode = new Subject(); + + nodeDeleted = new Subject(); + nodeMoved = new Subject(); + nodeRestored = new Subject(); + + constructor(private api: AlfrescoApiService, + private notification: NotificationService, + private translation: TranslationService) { + } + + nodeHasPermission(node: Node, permission: string): boolean { + if (node && permission) { + const allowableOperations = node.allowableOperations || []; + + if (allowableOperations.indexOf(permission) > -1) { + return true; + } + } + + return false; + } + + canDeleteNode(node: Node): boolean { + return this.nodeHasPermission(node, 'delete'); + } + + async deleteNode(node: Node) { + if (this.canDeleteNode(node)) { + try { + await this.api.nodesApi.deleteNode(node.id); + + this.notification + .openSnackMessageAction( + this.translation.instant('APP.MESSAGES.INFO.NODE_DELETION.SINGULAR', { name: node.name }), + this.translation.translate.instant('APP.ACTIONS.UNDO'), + 10000 + ) + .onAction() + .subscribe(() => { + this.restoreNode(node); + }); + + this.nodeDeleted.next(node.id); + } catch { + this.notification.openSnackMessage( + this.translation.instant('APP.MESSAGES.ERRORS.NODE_DELETION', { name: node.name }), + 10000 + ); + } + } + } + + async restoreNode(node: Node) { + if (node) { + try { + await this.api.nodesApi.restoreNode(node.id); + this.nodeRestored.next(node.id); + } catch { + this.notification.openSnackMessage( + this.translation.instant('APP.MESSAGES.ERRORS.NODE_RESTORE', { name: node.name }), + 3000 + ); + } + } + } } diff --git a/src/app/components/favorites/favorites.component.spec.ts b/src/app/components/favorites/favorites.component.spec.ts index 8a0c2a189..0e1cfa73e 100644 --- a/src/app/components/favorites/favorites.component.spec.ts +++ b/src/app/components/favorites/favorites.component.spec.ts @@ -114,7 +114,7 @@ describe('Favorites Routed Component', () => { spyOn(component, 'refresh'); fixture.detectChanges(); - contentService.moveNode.next(null); + contentService.nodeMoved.next(null); expect(component.refresh).toHaveBeenCalled(); }); diff --git a/src/app/components/favorites/favorites.component.ts b/src/app/components/favorites/favorites.component.ts index e1f0131d7..b3dcd385a 100644 --- a/src/app/components/favorites/favorites.component.ts +++ b/src/app/components/favorites/favorites.component.ts @@ -62,10 +62,10 @@ export class FavoritesComponent extends PageComponent implements OnInit, OnDestr ngOnInit() { this.subscriptions = this.subscriptions.concat([ - this.content.deleteNode.subscribe(() => this.refresh()), - this.content.restoreNode.subscribe(() => this.refresh()), + this.content.nodeDeleted.subscribe(() => this.refresh()), + this.content.nodeRestored.subscribe(() => this.refresh()), this.contentService.folderEdit.subscribe(() => this.refresh()), - this.content.moveNode.subscribe(() => this.refresh()) + this.content.nodeMoved.subscribe(() => this.refresh()) ]); } diff --git a/src/app/components/files/files.component.spec.ts b/src/app/components/files/files.component.spec.ts index 1cf5d10bf..1538f5978 100644 --- a/src/app/components/files/files.component.spec.ts +++ b/src/app/components/files/files.component.spec.ts @@ -194,19 +194,19 @@ describe('FilesComponent', () => { }); it('calls refresh deleteNode event', () => { - contentManagementService.deleteNode.next(); + contentManagementService.nodeDeleted.next(); expect(component.load).toHaveBeenCalled(); }); it('calls refresh moveNode event', () => { - contentManagementService.moveNode.next(); + contentManagementService.nodeMoved.next(); expect(component.load).toHaveBeenCalled(); }); it('calls refresh restoreNode event', () => { - contentManagementService.restoreNode.next(); + contentManagementService.nodeRestored.next(); expect(component.load).toHaveBeenCalled(); }); diff --git a/src/app/components/files/files.component.ts b/src/app/components/files/files.component.ts index b8d650ae1..febb2936c 100644 --- a/src/app/components/files/files.component.ts +++ b/src/app/components/files/files.component.ts @@ -107,9 +107,9 @@ export class FilesComponent extends PageComponent implements OnInit, OnDestroy { nodeActionsService.contentCopied.subscribe((nodes) => this.onContentCopied(nodes)), contentService.folderCreate.subscribe(() => this.load(false, this.pagination)), contentService.folderEdit.subscribe(() => this.load(false, this.pagination)), - contentManagementService.deleteNode.subscribe(() => this.load(false, this.pagination)), - contentManagementService.moveNode.subscribe(() => this.load(false, this.pagination)), - contentManagementService.restoreNode.subscribe(() => this.load(false, this.pagination)), + contentManagementService.nodeDeleted.subscribe(() => this.load(false, this.pagination)), + contentManagementService.nodeMoved.subscribe(() => this.load(false, this.pagination)), + contentManagementService.nodeRestored.subscribe(() => this.load(false, this.pagination)), uploadService.fileUploadComplete.subscribe(file => this.onFileUploadedEvent(file)), uploadService.fileUploadDeleted.subscribe((file) => this.onFileUploadedEvent(file)) ]); diff --git a/src/app/components/preview/preview.component.html b/src/app/components/preview/preview.component.html index 2a537f83c..77925f753 100644 --- a/src/app/components/preview/preview.component.html +++ b/src/app/components/preview/preview.component.html @@ -8,5 +8,14 @@ (showViewerChange)="onVisibilityChanged($event)" (navigateBefore)="onNavigateBefore()" (navigateNext)="onNavigateNext()"> + + + + diff --git a/src/app/components/preview/preview.component.spec.ts b/src/app/components/preview/preview.component.spec.ts index d3398145e..a181ac4a6 100644 --- a/src/app/components/preview/preview.component.spec.ts +++ b/src/app/components/preview/preview.component.spec.ts @@ -353,7 +353,9 @@ describe('PreviewComponent', () => { component.previewLocation = 'personal-files'; await component.displayNode('folder1'); - expect(alfrescoApi.nodesApi.getNodeInfo).toHaveBeenCalledWith('folder1'); + expect(alfrescoApi.nodesApi.getNodeInfo).toHaveBeenCalledWith( + 'folder1', { include: [ 'allowableOperations' ] } + ); expect(router.navigate).toHaveBeenCalledWith(['personal-files', 'folder1']); }); @@ -368,7 +370,9 @@ describe('PreviewComponent', () => { component.previewLocation = 'personal-files'; await component.displayNode('folder1'); - expect(alfrescoApi.nodesApi.getNodeInfo).toHaveBeenCalledWith('folder1'); + expect(alfrescoApi.nodesApi.getNodeInfo).toHaveBeenCalledWith( + 'folder1', { include: [ 'allowableOperations' ] } + ); expect(router.navigate).toHaveBeenCalledWith(['personal-files', 'folder1']); }); @@ -381,7 +385,9 @@ describe('PreviewComponent', () => { component.previewLocation = 'personal-files'; await component.displayNode('folder1'); - expect(alfrescoApi.nodesApi.getNodeInfo).toHaveBeenCalledWith('folder1'); + expect(alfrescoApi.nodesApi.getNodeInfo).toHaveBeenCalledWith( + 'folder1', { include: [ 'allowableOperations' ] } + ); expect(router.navigate).toHaveBeenCalledWith(['personal-files', 'folder1']); }); @@ -399,7 +405,9 @@ describe('PreviewComponent', () => { component.previewLocation = 'personal-files'; await component.displayNode('folder1'); - expect(alfrescoApi.nodesApi.getNodeInfo).toHaveBeenCalledWith('folder1'); + expect(alfrescoApi.nodesApi.getNodeInfo).toHaveBeenCalledWith( + 'folder1', { include: [ 'allowableOperations' ] } + ); expect(router.navigate).toHaveBeenCalledWith(['personal-files', 'folder1']); }); diff --git a/src/app/components/preview/preview.component.ts b/src/app/components/preview/preview.component.ts index 72e9ec1bd..b671b39a3 100644 --- a/src/app/components/preview/preview.component.ts +++ b/src/app/components/preview/preview.component.ts @@ -27,6 +27,7 @@ import { Component, OnInit, ViewEncapsulation } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { AlfrescoApiService, UserPreferencesService, ObjectUtils } from '@alfresco/adf-core'; import { MinimalNodeEntryEntity } from 'alfresco-js-api'; +import { ContentManagementService } from '../../common/services/content-management.service'; @Component({ selector: 'app-preview', @@ -53,7 +54,8 @@ export class PreviewComponent implements OnInit { constructor(private router: Router, private route: ActivatedRoute, private apiService: AlfrescoApiService, - private preferences: UserPreferencesService) { + private preferences: UserPreferencesService, + private content: ContentManagementService) { } ngOnInit() { @@ -90,7 +92,9 @@ export class PreviewComponent implements OnInit { async displayNode(id: string) { if (id) { try { - this.node = await this.apiService.nodesApi.getNodeInfo(id); + this.node = await this.apiService.nodesApi.getNodeInfo(id, { + include: [ 'allowableOperations'] + }); if (this.node && this.node.isFile) { const nearest = await this.getNearestNodes(this.node.id, this.node.parentId); @@ -315,4 +319,16 @@ export class PreviewComponent implements OnInit { } return path; } + + canDeleteFile(): boolean { + return this.content.canDeleteNode(this.node); + } + + async deleteFile() { + try { + await this.content.deleteNode(this.node); + this.onVisibilityChanged(false); + } catch { + } + } } diff --git a/src/app/components/recent-files/recent-files.component.spec.ts b/src/app/components/recent-files/recent-files.component.spec.ts index 5310cefec..c90ad6164 100644 --- a/src/app/components/recent-files/recent-files.component.spec.ts +++ b/src/app/components/recent-files/recent-files.component.spec.ts @@ -92,7 +92,7 @@ describe('RecentFiles Routed Component', () => { it('should reload nodes on onDeleteNode event', () => { fixture.detectChanges(); - contentService.deleteNode.next(); + contentService.nodeDeleted.next(); expect(component.refresh).toHaveBeenCalled(); }); @@ -100,7 +100,7 @@ describe('RecentFiles Routed Component', () => { it('should reload on onRestoreNode event', () => { fixture.detectChanges(); - contentService.restoreNode.next(); + contentService.nodeRestored.next(); expect(component.refresh).toHaveBeenCalled(); }); @@ -108,7 +108,7 @@ describe('RecentFiles Routed Component', () => { it('should reload on move node event', () => { fixture.detectChanges(); - contentService.moveNode.next(); + contentService.nodeMoved.next(); expect(component.refresh).toHaveBeenCalled(); }); diff --git a/src/app/components/recent-files/recent-files.component.ts b/src/app/components/recent-files/recent-files.component.ts index e127a2f5a..63601861f 100644 --- a/src/app/components/recent-files/recent-files.component.ts +++ b/src/app/components/recent-files/recent-files.component.ts @@ -60,9 +60,9 @@ export class RecentFilesComponent extends PageComponent implements OnInit, OnDes ngOnInit() { this.subscriptions = this.subscriptions.concat([ - this.content.deleteNode.subscribe(() => this.refresh()), - this.content.moveNode.subscribe(() => this.refresh()), - this.content.restoreNode.subscribe(() => this.refresh()) + this.content.nodeDeleted.subscribe(() => this.refresh()), + this.content.nodeMoved.subscribe(() => this.refresh()), + this.content.nodeRestored.subscribe(() => this.refresh()) ]); } diff --git a/src/app/components/shared-files/shared-files.component.spec.ts b/src/app/components/shared-files/shared-files.component.spec.ts index 064b4a9cd..cca450e60 100644 --- a/src/app/components/shared-files/shared-files.component.spec.ts +++ b/src/app/components/shared-files/shared-files.component.spec.ts @@ -89,7 +89,7 @@ describe('SharedFilesComponent', () => { it('should refresh on deleteNode event', () => { fixture.detectChanges(); - contentService.deleteNode.next(); + contentService.nodeDeleted.next(); expect(component.refresh).toHaveBeenCalled(); }); @@ -97,7 +97,7 @@ describe('SharedFilesComponent', () => { it('should refresh on restoreNode event', () => { fixture.detectChanges(); - contentService.restoreNode.next(); + contentService.nodeRestored.next(); expect(component.refresh).toHaveBeenCalled(); }); @@ -105,7 +105,7 @@ describe('SharedFilesComponent', () => { it('should reload on move node event', () => { fixture.detectChanges(); - contentService.moveNode.next(); + contentService.nodeMoved.next(); expect(component.refresh).toHaveBeenCalled(); }); diff --git a/src/app/components/shared-files/shared-files.component.ts b/src/app/components/shared-files/shared-files.component.ts index 7db5f6808..c3b3a940c 100644 --- a/src/app/components/shared-files/shared-files.component.ts +++ b/src/app/components/shared-files/shared-files.component.ts @@ -60,9 +60,9 @@ export class SharedFilesComponent extends PageComponent implements OnInit, OnDes ngOnInit() { this.subscriptions = this.subscriptions.concat([ - this.content.deleteNode.subscribe(() => this.refresh()), - this.content.moveNode.subscribe(() => this.refresh()), - this.content.restoreNode.subscribe(() => this.refresh()) + this.content.nodeDeleted.subscribe(() => this.refresh()), + this.content.nodeMoved.subscribe(() => this.refresh()), + this.content.nodeRestored.subscribe(() => this.refresh()) ]); } diff --git a/src/app/components/sidenav/sidenav.component.spec.ts b/src/app/components/sidenav/sidenav.component.spec.ts index 99899f67c..9d2ed7dcb 100644 --- a/src/app/components/sidenav/sidenav.component.spec.ts +++ b/src/app/components/sidenav/sidenav.component.spec.ts @@ -23,13 +23,14 @@ * along with Alfresco. If not, see . */ +import { NO_ERRORS_SCHEMA } from '@angular/core'; import { TestBed, async } from '@angular/core/testing'; import { RouterTestingModule } from '@angular/router/testing'; -import { ContentService, AppConfigService } from '@alfresco/adf-core'; + +import { CoreModule, ContentService, AppConfigService } from '@alfresco/adf-core'; import { BrowsingFilesService } from '../../common/services/browsing-files.service'; import { SidenavComponent } from './sidenav.component'; -import { CommonModule } from './../../common/common.module'; describe('SidenavComponent', () => { let fixture; @@ -50,11 +51,15 @@ describe('SidenavComponent', () => { TestBed.configureTestingModule({ imports: [ RouterTestingModule, - CommonModule + CoreModule ], declarations: [ SidenavComponent - ] + ], + providers: [ + BrowsingFilesService + ], + schemas: [ NO_ERRORS_SCHEMA ] }) .compileComponents() .then(() => { diff --git a/src/app/components/trashcan/trashcan.component.spec.ts b/src/app/components/trashcan/trashcan.component.spec.ts index 370c243fa..912bd6f2f 100644 --- a/src/app/components/trashcan/trashcan.component.spec.ts +++ b/src/app/components/trashcan/trashcan.component.spec.ts @@ -84,7 +84,7 @@ describe('TrashcanComponent', () => { spyOn(component, 'refresh'); fixture.detectChanges(); - contentService.restoreNode.next(); + contentService.nodeRestored.next(); expect(component.refresh).toHaveBeenCalled(); }); diff --git a/src/app/components/trashcan/trashcan.component.ts b/src/app/components/trashcan/trashcan.component.ts index 8c2852965..53dcf03ea 100644 --- a/src/app/components/trashcan/trashcan.component.ts +++ b/src/app/components/trashcan/trashcan.component.ts @@ -48,7 +48,7 @@ export class TrashcanComponent implements OnInit, OnDestroy { } ngOnInit() { - this.subscriptions.push(this.contentManagementService.restoreNode.subscribe(() => this.refresh())); + this.subscriptions.push(this.contentManagementService.nodeRestored.subscribe(() => this.refresh())); } refresh(): void {