From 7197e1e13a9cc6d1c8b7d519c991e649b7e3b260 Mon Sep 17 00:00:00 2001 From: davidcanonieto Date: Wed, 5 Dec 2018 16:39:21 +0000 Subject: [PATCH] [ADF-1443] Refactor Download directive (#4028) * [ADF-1443] Refactor Download directive * [ADF-1443] Node Download Directive now accepts single node and an array as input * [ADF-1443] Fix Unit tests * [ADF-1443] Fix unit test related to viewer component --- lib/content-services/dialogs/dialog.module.ts | 4 - lib/content-services/dialogs/public-api.ts | 1 - .../directives/content-directive.module.ts | 3 - lib/content-services/directives/public-api.ts | 2 - .../services/node-actions.service.ts | 3 +- lib/core/core.module.ts | 5 + lib/core/dialogs/dialog.module.ts | 43 +++++ .../dialogs/download-zip.dialog.html | 2 +- .../dialogs/download-zip.dialog.scss | 0 lib/core/dialogs/download-zip.dialog.spec.ts | 149 ++++++++++++++++++ .../dialogs/download-zip.dialog.ts | 31 ++-- lib/core/dialogs/index.ts | 18 +++ lib/core/dialogs/public-api.ts | 20 +++ lib/core/directives/directive.module.ts | 3 + .../node-download.directive.spec.ts | 10 +- .../directives/node-download.directive.ts | 27 ++-- lib/core/directives/public-api.ts | 1 + lib/core/index.ts | 1 + lib/core/services/download-zip.service.ts | 60 +++++++ lib/core/services/public-api.ts | 1 + .../viewer/components/viewer.component.html | 2 +- .../components/viewer.component.spec.ts | 66 ++++---- .../viewer/components/viewer.component.ts | 41 +---- lib/core/viewer/viewer.module.ts | 4 +- 24 files changed, 386 insertions(+), 111 deletions(-) create mode 100755 lib/core/dialogs/dialog.module.ts rename lib/{content-services => core}/dialogs/download-zip.dialog.html (79%) mode change 100644 => 100755 rename lib/{content-services => core}/dialogs/download-zip.dialog.scss (100%) mode change 100644 => 100755 create mode 100755 lib/core/dialogs/download-zip.dialog.spec.ts rename lib/{content-services => core}/dialogs/download-zip.dialog.ts (72%) mode change 100644 => 100755 create mode 100755 lib/core/dialogs/index.ts create mode 100755 lib/core/dialogs/public-api.ts rename lib/{content-services => core}/directives/node-download.directive.spec.ts (95%) mode change 100644 => 100755 rename lib/{content-services => core}/directives/node-download.directive.ts (83%) mode change 100644 => 100755 create mode 100755 lib/core/services/download-zip.service.ts diff --git a/lib/content-services/dialogs/dialog.module.ts b/lib/content-services/dialogs/dialog.module.ts index 2b30def510..79fa316e80 100644 --- a/lib/content-services/dialogs/dialog.module.ts +++ b/lib/content-services/dialogs/dialog.module.ts @@ -21,7 +21,6 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { CoreModule } from '@alfresco/adf-core'; import { MaterialModule } from '../material.module'; -import { DownloadZipDialogComponent } from './download-zip.dialog'; import { FolderDialogComponent } from './folder.dialog'; import { NodeLockDialogComponent } from './node-lock.dialog'; import { ConfirmDialogComponent } from './confirm.dialog'; @@ -40,21 +39,18 @@ import { LibraryDialogComponent } from './library/library.dialog'; MatDatetimepickerModule ], declarations: [ - DownloadZipDialogComponent, FolderDialogComponent, NodeLockDialogComponent, ConfirmDialogComponent, LibraryDialogComponent ], exports: [ - DownloadZipDialogComponent, FolderDialogComponent, NodeLockDialogComponent, ConfirmDialogComponent, LibraryDialogComponent ], entryComponents: [ - DownloadZipDialogComponent, FolderDialogComponent, NodeLockDialogComponent, ConfirmDialogComponent, diff --git a/lib/content-services/dialogs/public-api.ts b/lib/content-services/dialogs/public-api.ts index 6ff9033c9b..a28149ce72 100644 --- a/lib/content-services/dialogs/public-api.ts +++ b/lib/content-services/dialogs/public-api.ts @@ -15,7 +15,6 @@ * limitations under the License. */ -export * from './download-zip.dialog'; export * from './folder.dialog'; export * from './node-lock.dialog'; export * from './confirm.dialog'; diff --git a/lib/content-services/directives/content-directive.module.ts b/lib/content-services/directives/content-directive.module.ts index 1a51d261d9..61487bee88 100644 --- a/lib/content-services/directives/content-directive.module.ts +++ b/lib/content-services/directives/content-directive.module.ts @@ -19,7 +19,6 @@ import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { MaterialModule } from '../material.module'; -import { NodeDownloadDirective } from './node-download.directive'; import { NodeLockDirective } from './node-lock.directive'; @NgModule({ @@ -28,11 +27,9 @@ import { NodeLockDirective } from './node-lock.directive'; MaterialModule ], declarations: [ - NodeDownloadDirective, NodeLockDirective ], exports: [ - NodeDownloadDirective, NodeLockDirective ] }) diff --git a/lib/content-services/directives/public-api.ts b/lib/content-services/directives/public-api.ts index 1df33c4d8b..5873bf189e 100644 --- a/lib/content-services/directives/public-api.ts +++ b/lib/content-services/directives/public-api.ts @@ -15,6 +15,4 @@ * limitations under the License. */ -export * from './node-download.directive'; - export * from './content-directive.module'; diff --git a/lib/content-services/document-list/services/node-actions.service.ts b/lib/content-services/document-list/services/node-actions.service.ts index 40bc084818..c256d249b5 100644 --- a/lib/content-services/document-list/services/node-actions.service.ts +++ b/lib/content-services/document-list/services/node-actions.service.ts @@ -18,12 +18,11 @@ import { Injectable, Output, EventEmitter } from '@angular/core'; import { MinimalNodeEntryEntity, MinimalNodeEntity } from 'alfresco-js-api'; import { Subject } from 'rxjs'; -import { AlfrescoApiService, ContentService } from '@alfresco/adf-core'; +import { AlfrescoApiService, ContentService, NodeDownloadDirective } from '@alfresco/adf-core'; import { MatDialog } from '@angular/material'; import { DocumentListService } from './document-list.service'; import { ContentNodeDialogService } from '../../content-node-selector/content-node-dialog.service'; -import { NodeDownloadDirective } from '../../directives/node-download.directive'; @Injectable({ providedIn: 'root' diff --git a/lib/core/core.module.ts b/lib/core/core.module.ts index 955fb1141c..e2faca214a 100644 --- a/lib/core/core.module.ts +++ b/lib/core/core.module.ts @@ -45,6 +45,7 @@ import { TemplateModule } from './templates/template.module'; import { ClipboardModule } from './clipboard/clipboard.module'; import { DirectiveModule } from './directives/directive.module'; +import { DialogModule } from './dialogs/dialog.module'; import { PipeModule } from './pipes/pipe.module'; import { AlfrescoApiService } from './services/alfresco-api.service'; @@ -66,6 +67,7 @@ export function createTranslateLoader(http: HttpClient) { PipeModule, CommonModule, DirectiveModule, + DialogModule, ClipboardModule, FormsModule, ReactiveFormsModule, @@ -103,6 +105,7 @@ export function createTranslateLoader(http: HttpClient) { PipeModule, CommonModule, DirectiveModule, + DialogModule, ClipboardModule, FormsModule, ReactiveFormsModule, @@ -139,6 +142,7 @@ export class CoreModuleLazy { PipeModule, CommonModule, DirectiveModule, + DialogModule, FormsModule, ReactiveFormsModule, HttpClientModule, @@ -175,6 +179,7 @@ export class CoreModuleLazy { PipeModule, CommonModule, DirectiveModule, + DialogModule, ClipboardModule, FormsModule, ReactiveFormsModule, diff --git a/lib/core/dialogs/dialog.module.ts b/lib/core/dialogs/dialog.module.ts new file mode 100755 index 0000000000..5ca8401801 --- /dev/null +++ b/lib/core/dialogs/dialog.module.ts @@ -0,0 +1,43 @@ +/*! + * @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 { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; + +import { MaterialModule } from '../material.module'; +import { DownloadZipDialogComponent } from './download-zip.dialog'; +import { TranslateModule } from '@ngx-translate/core'; +import { PipeModule } from '../pipes/pipe.module'; + +@NgModule({ + imports: [ + CommonModule, + MaterialModule, + TranslateModule.forChild(), + PipeModule + ], + declarations: [ + DownloadZipDialogComponent + ], + exports: [ + DownloadZipDialogComponent + ], + entryComponents: [ + DownloadZipDialogComponent + ] +}) +export class DialogModule {} diff --git a/lib/content-services/dialogs/download-zip.dialog.html b/lib/core/dialogs/download-zip.dialog.html old mode 100644 new mode 100755 similarity index 79% rename from lib/content-services/dialogs/download-zip.dialog.html rename to lib/core/dialogs/download-zip.dialog.html index 88a9231fa9..5ad3014ab5 --- a/lib/content-services/dialogs/download-zip.dialog.html +++ b/lib/core/dialogs/download-zip.dialog.html @@ -4,7 +4,7 @@
-
diff --git a/lib/content-services/dialogs/download-zip.dialog.scss b/lib/core/dialogs/download-zip.dialog.scss old mode 100644 new mode 100755 similarity index 100% rename from lib/content-services/dialogs/download-zip.dialog.scss rename to lib/core/dialogs/download-zip.dialog.scss diff --git a/lib/core/dialogs/download-zip.dialog.spec.ts b/lib/core/dialogs/download-zip.dialog.spec.ts new file mode 100755 index 0000000000..e55ff45570 --- /dev/null +++ b/lib/core/dialogs/download-zip.dialog.spec.ts @@ -0,0 +1,149 @@ +/*! + * @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 { TestBed } from '@angular/core/testing'; +import { ComponentFixture } from '@angular/core/testing'; +import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material'; +import { DownloadZipDialogComponent } from './download-zip.dialog'; +import { setupTestBed } from '../testing/setupTestBed'; +import { CoreTestingModule } from '../testing/core.testing.module'; +import { DownloadZipService } from '../services/download-zip.service'; +import { of } from 'rxjs'; + +describe('DownloadZipDialogComponent', () => { + + let fixture: ComponentFixture; + let component: DownloadZipDialogComponent; + let element: HTMLElement; + let downloadZipService: DownloadZipService; + let dialogRef = { + close: jasmine.createSpy('close') + }; + + let dataMock = { + nodeIds: [ + '123' + ] + }; + + let pendingDownloadEntry = { + entry: { + bytesAdded: 0, + filesAdded: 0, + id: '5bfb0907', + status: 'PENDING', + totalBytes: 0, + totalFiles: 0 + } + }; + + setupTestBed({ + imports: [CoreTestingModule], + providers: [ + { provide: MatDialogRef, useValue: dialogRef }, + { provide: MAT_DIALOG_DATA, useValue: dataMock } + ] + }); + + beforeEach(() => { + dialogRef.close.calls.reset(); + fixture = TestBed.createComponent(DownloadZipDialogComponent); + downloadZipService = TestBed.get(DownloadZipService); + component = fixture.componentInstance; + element = fixture.nativeElement; + }); + + afterEach(() => { + fixture.destroy(); + }); + + it('should call downloadZip when it is not cancelled', () => { + component.cancelled = false; + spyOn(component, 'downloadZip'); + + component.ngOnInit(); + + expect(component.downloadZip).toHaveBeenCalledWith(['123']); + }); + + it('should not call downloadZip when it is cancelled', () => { + component.cancelled = true; + spyOn(component, 'downloadZip'); + + component.ngOnInit(); + + expect(component.downloadZip).not.toHaveBeenCalled(); + }); + + it('should not call downloadZip when it contains zero nodeIds', () => { + component.data = { + nodeIds: [] + }; + spyOn(component, 'downloadZip'); + + component.ngOnInit(); + + expect(component.downloadZip).not.toHaveBeenCalled(); + }); + + it('should call cancelDownload when CANCEL button is clicked', () => { + fixture.detectChanges(); + spyOn(component, 'cancelDownload').and.callThrough(); + + const cancelButton: HTMLButtonElement = element.querySelector('#cancel-button'); + cancelButton.click(); + + expect(component.cancelDownload).toHaveBeenCalled(); + }); + + it('should call createDownload when component is initialize', () => { + const createDownloadSpy = spyOn(downloadZipService, 'createDownload').and.returnValue(of(pendingDownloadEntry)); + fixture.detectChanges(); + expect(createDownloadSpy).toHaveBeenCalled(); + }); + + it('should close dialog when download is completed', () => { + component.download('fakeUrl', 'fileName'); + spyOn(downloadZipService, 'cancelDownload'); + fixture.detectChanges(); + expect(dialogRef.close).toHaveBeenCalled(); + }); + + it('should close dialog when download is cancelled', () => { + fixture.detectChanges(); + component.download('url', 'filename'); + spyOn(downloadZipService, 'cancelDownload'); + component.cancelDownload(); + expect(dialogRef.close).toHaveBeenCalled(); + }); + + it('should interrupt download when cancel button is clicked', () => { + spyOn(component, 'downloadZip'); + spyOn(component, 'download'); + spyOn(component, 'cancelDownload').and.callThrough(); + + fixture.detectChanges(); + + expect(component.downloadZip).toHaveBeenCalled(); + + const cancelButton: HTMLButtonElement = element.querySelector('#cancel-button'); + cancelButton.click(); + + expect(component.cancelDownload).toHaveBeenCalled(); + expect(component.download).not.toHaveBeenCalled(); + }); +}); diff --git a/lib/content-services/dialogs/download-zip.dialog.ts b/lib/core/dialogs/download-zip.dialog.ts old mode 100644 new mode 100755 similarity index 72% rename from lib/content-services/dialogs/download-zip.dialog.ts rename to lib/core/dialogs/download-zip.dialog.ts index 8ecf77f013..1c6a321438 --- a/lib/content-services/dialogs/download-zip.dialog.ts +++ b/lib/core/dialogs/download-zip.dialog.ts @@ -18,7 +18,8 @@ import { Component, Inject, OnInit, ViewEncapsulation } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material'; import { DownloadEntry, MinimalNodeEntity } from 'alfresco-js-api'; -import { LogService, AlfrescoApiService } from '@alfresco/adf-core'; +import { LogService } from '../services/log.service'; +import { DownloadZipService } from '../services/download-zip.service'; @Component({ selector: 'adf-download-zip-dialog', @@ -30,12 +31,14 @@ import { LogService, AlfrescoApiService } from '@alfresco/adf-core'; export class DownloadZipDialogComponent implements OnInit { // flag for async threads - private cancelled = false; + cancelled = false; + downloadId: string; - constructor(private apiService: AlfrescoApiService, - private dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) private data: any, - private logService: LogService) { + constructor(private dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) + public data: any, + private logService: LogService, + private downloadZipService: DownloadZipService) { } ngOnInit() { @@ -50,25 +53,21 @@ export class DownloadZipDialogComponent implements OnInit { cancelDownload() { this.cancelled = true; + this.downloadZipService.cancelDownload(this.downloadId); this.dialogRef.close(false); } downloadZip(nodeIds: string[]) { if (nodeIds && nodeIds.length > 0) { - const promise: any = this.apiService.getInstance().core.downloadsApi.createDownload({ nodeIds }); - - promise.on('progress', (progress) => this.logService.log('Progress', progress)); - promise.on('error', (error) => this.logService.error('Error', error)); - promise.on('abort', (data) => this.logService.log('Abort', data)); - - promise.on('success', (data: DownloadEntry) => { + this.downloadZipService.createDownload({ nodeIds }).subscribe((data: DownloadEntry) => { if (data && data.entry && data.entry.id) { - const url = this.apiService.getInstance().content.getContentUrl(data.entry.id, true); + const url = this.downloadZipService.getContentUrl(data.entry.id, true); - this.apiService.getInstance().core.nodesApi.getNode(data.entry.id).then((downloadNode: MinimalNodeEntity) => { + this.downloadZipService.getNode(data.entry.id).subscribe((downloadNode: MinimalNodeEntity) => { this.logService.log(downloadNode); const fileName = downloadNode.entry.name; + this.downloadId = data.entry.id; this.waitAndDownload(data.entry.id, url, fileName); }); } @@ -81,7 +80,7 @@ export class DownloadZipDialogComponent implements OnInit { return; } - this.apiService.getInstance().core.downloadsApi.getDownload(downloadId).then((downloadEntry: DownloadEntry) => { + this.downloadZipService.getDownload(downloadId).subscribe((downloadEntry: DownloadEntry) => { if (downloadEntry.entry) { if (downloadEntry.entry.status === 'DONE') { this.download(url, fileName); diff --git a/lib/core/dialogs/index.ts b/lib/core/dialogs/index.ts new file mode 100755 index 0000000000..4c6ac1d58f --- /dev/null +++ b/lib/core/dialogs/index.ts @@ -0,0 +1,18 @@ +/*! + * @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 * from './public-api'; diff --git a/lib/core/dialogs/public-api.ts b/lib/core/dialogs/public-api.ts new file mode 100755 index 0000000000..01b43a0623 --- /dev/null +++ b/lib/core/dialogs/public-api.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. + */ + +export * from './download-zip.dialog'; + +export * from './dialog.module'; diff --git a/lib/core/directives/directive.module.ts b/lib/core/directives/directive.module.ts index 3f260652f3..65298e8b6e 100644 --- a/lib/core/directives/directive.module.ts +++ b/lib/core/directives/directive.module.ts @@ -26,6 +26,7 @@ import { NodeFavoriteDirective } from './node-favorite.directive'; import { NodePermissionDirective } from './node-permission.directive'; import { NodeRestoreDirective } from './node-restore.directive'; import { UploadDirective } from './upload.directive'; +import { NodeDownloadDirective } from './node-download.directive'; @NgModule({ imports: [ @@ -39,6 +40,7 @@ import { UploadDirective } from './upload.directive'; NodeFavoriteDirective, NodePermissionDirective, NodeRestoreDirective, + NodeDownloadDirective, UploadDirective ], exports: [ @@ -48,6 +50,7 @@ import { UploadDirective } from './upload.directive'; NodeFavoriteDirective, NodePermissionDirective, NodeRestoreDirective, + NodeDownloadDirective, UploadDirective ] }) diff --git a/lib/content-services/directives/node-download.directive.spec.ts b/lib/core/directives/node-download.directive.spec.ts old mode 100644 new mode 100755 similarity index 95% rename from lib/content-services/directives/node-download.directive.spec.ts rename to lib/core/directives/node-download.directive.spec.ts index 2fe26f68b7..4e56e82e92 --- a/lib/content-services/directives/node-download.directive.spec.ts +++ b/lib/core/directives/node-download.directive.spec.ts @@ -19,9 +19,11 @@ import { TestBed, ComponentFixture } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { MatDialog } from '@angular/material'; import { Component, DebugElement } from '@angular/core'; -import { AlfrescoApiService, setupTestBed, CoreModule, AlfrescoApiServiceMock } from '@alfresco/adf-core'; - +import { setupTestBed } from '../testing/setupTestBed'; +import { CoreModule } from '../core.module'; import { DialogModule } from '../dialogs/dialog.module'; +import { AlfrescoApiServiceMock } from '../mock/alfresco-api.service.mock'; +import { AlfrescoApiService } from '../services/alfresco-api.service'; import { NodeDownloadDirective } from './node-download.directive'; @Component({ @@ -47,11 +49,9 @@ describe('NodeDownloadDirective', () => { ], providers: [ { provide: AlfrescoApiService, useClass: AlfrescoApiServiceMock } - // { provide: AppConfigService, useValue: AppConfigServiceMock }, ], declarations: [ - TestComponent, - NodeDownloadDirective + TestComponent ] }); diff --git a/lib/content-services/directives/node-download.directive.ts b/lib/core/directives/node-download.directive.ts old mode 100644 new mode 100755 similarity index 83% rename from lib/content-services/directives/node-download.directive.ts rename to lib/core/directives/node-download.directive.ts index a5e38c1ec9..531747c394 --- a/lib/content-services/directives/node-download.directive.ts +++ b/lib/core/directives/node-download.directive.ts @@ -17,10 +17,9 @@ import { Directive, Input, HostListener } from '@angular/core'; import { MatDialog } from '@angular/material'; -import { MinimalNodeEntity } from 'alfresco-js-api'; -import { AlfrescoApiService } from '@alfresco/adf-core'; - +import { AlfrescoApiService } from '../services/alfresco-api.service'; import { DownloadZipDialogComponent } from '../dialogs/download-zip.dialog'; +import { MinimalNodeEntity } from 'alfresco-js-api'; @Directive({ selector: '[adfNodeDownload]' @@ -30,7 +29,7 @@ export class NodeDownloadDirective { /** Nodes to download. */ // tslint:disable-next-line:no-input-rename @Input('adfNodeDownload') - nodes: MinimalNodeEntity[]; + nodes: MinimalNodeEntity | MinimalNodeEntity[]; @HostListener('click') onClick() { @@ -47,15 +46,19 @@ export class NodeDownloadDirective { * Packs result into a .ZIP archive if there is more than one node selected. * @param selection Multiple selected nodes to download */ - downloadNodes(selection: Array) { - if (!selection || selection.length === 0) { + downloadNodes(selection: MinimalNodeEntity | Array) { + + if (!this.isSelectionValid(selection)) { return; } - - if (selection.length === 1) { - this.downloadNode(selection[0]); + if (selection instanceof Array) { + if (selection.length === 1) { + this.downloadNode(selection[0]); + } else { + this.downloadZip(selection); + } } else { - this.downloadZip(selection); + this.downloadNode(selection); } } @@ -83,6 +86,10 @@ export class NodeDownloadDirective { } } + private isSelectionValid(selection: MinimalNodeEntity | Array) { + return selection || (selection instanceof Array && selection.length > 0); + } + private downloadFile(node: MinimalNodeEntity) { if (node && node.entry) { const contentApi = this.apiService.getInstance().content; diff --git a/lib/core/directives/public-api.ts b/lib/core/directives/public-api.ts index da1370d4af..5a70ff3029 100644 --- a/lib/core/directives/public-api.ts +++ b/lib/core/directives/public-api.ts @@ -21,6 +21,7 @@ export * from './node-delete.directive'; export * from './node-favorite.directive'; export * from './node-permission.directive'; export * from './node-restore.directive'; +export * from './node-download.directive'; export * from './upload.directive'; export * from './directive.module'; diff --git a/lib/core/index.ts b/lib/core/index.ts index bfae4e1cd1..8719abdd89 100644 --- a/lib/core/index.ts +++ b/lib/core/index.ts @@ -39,6 +39,7 @@ export * from './pipes/index'; export * from './services/index'; export * from './directives/index'; export * from './clipboard/index'; +export * from './dialogs/index'; export * from './utils/index'; export * from './interface/index'; diff --git a/lib/core/services/download-zip.service.ts b/lib/core/services/download-zip.service.ts new file mode 100755 index 0000000000..7b0e9b7bd7 --- /dev/null +++ b/lib/core/services/download-zip.service.ts @@ -0,0 +1,60 @@ +/*! + * @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 { NodeEntry, DownloadEntry, DownloadBodyCreate } from 'alfresco-js-api'; +import { Injectable } from '@angular/core'; +import { Observable, from, throwError } from 'rxjs'; +import { LogService } from './log.service'; +import { AlfrescoApiService } from './alfresco-api.service'; +import { catchError } from 'rxjs/operators'; + +@Injectable({ + providedIn: 'root' +}) +export class DownloadZipService { + + constructor(private apiService: AlfrescoApiService, + private logService: LogService) { + } + + createDownload(payload: DownloadBodyCreate): Observable { + return from(this.apiService.getInstance().core.downloadsApi.createDownload(payload)).pipe( + catchError((err) => this.handleError(err)) + ); + } + + getContentUrl(nodeId: string, attachment?: boolean): string { + return this.apiService.getInstance().content.getContentUrl(nodeId, attachment); + } + + getNode(nodeId: string): Observable { + return from(this.apiService.getInstance().core.nodesApi.getNode(nodeId)); + } + + getDownload(downloadId: string): Observable { + return from(this.apiService.getInstance().core.downloadsApi.getDownload(downloadId)); + } + + cancelDownload(downloadId: string) { + this.apiService.getInstance().core.downloadsApi.cancelDownload(downloadId); + } + + private handleError(error: any) { + this.logService.error(error); + return throwError(error || 'Server error'); + } +} diff --git a/lib/core/services/public-api.ts b/lib/core/services/public-api.ts index 78059ea9c2..84d25929d5 100644 --- a/lib/core/services/public-api.ts +++ b/lib/core/services/public-api.ts @@ -51,3 +51,4 @@ export * from './comment-content.service'; export * from './login-dialog.service'; export * from './external-alfresco-api.service'; export * from './jwt-helper.service'; +export * from './download-zip.service'; diff --git a/lib/core/viewer/components/viewer.component.html b/lib/core/viewer/components/viewer.component.html index cb3c350204..592b196cd6 100644 --- a/lib/core/viewer/components/viewer.component.html +++ b/lib/core/viewer/components/viewer.component.html @@ -76,7 +76,7 @@ mat-icon-button title="{{ 'ADF_VIEWER.ACTIONS.DOWNLOAD' | translate }}" data-automation-id="adf-toolbar-download" - (click)="downloadContent()"> + [adfNodeDownload]="node"> file_download diff --git a/lib/core/viewer/components/viewer.component.spec.ts b/lib/core/viewer/components/viewer.component.spec.ts index 62aaec49a4..10d31bd09b 100644 --- a/lib/core/viewer/components/viewer.component.spec.ts +++ b/lib/core/viewer/components/viewer.component.spec.ts @@ -345,7 +345,10 @@ describe('ViewerComponent', () => { const nodeDetails = { name: displayName, id: '12' }; const contentUrl = '/content/url/path'; const alfrescoApiInstanceMock = { - nodes: { getNodeInfo: () => Promise.resolve(nodeDetails) }, + nodes: { + getNodeInfo: () => Promise.resolve(nodeDetails), + getNode: () => Promise.resolve({ id: 'fake-node' }) + }, content: { getContentUrl: () => contentUrl } }; @@ -370,6 +373,8 @@ describe('ViewerComponent', () => { Promise.resolve({ name: 'file2', content: {} }) ); + spyOn(alfrescoApiService.nodesApi, 'getNode').and.returnValue(Promise.resolve({ id: 'fake-node' })); + component.urlFile = null; component.displayName = null; component.blobFile = null; @@ -549,35 +554,6 @@ describe('ViewerComponent', () => { }); }); - it('should invoke download action with the toolbar button', (done) => { - component.allowDownload = true; - spyOn(component, 'downloadContent').and.stub(); - fixture.detectChanges(); - - const button: HTMLButtonElement = element.querySelector('[data-automation-id="adf-toolbar-download"]') as HTMLButtonElement; - button.click(); - - fixture.whenStable().then(() => { - expect(component.downloadContent).toHaveBeenCalled(); - done(); - }); - }); - - it('should raise download event with the toolbar button', (done) => { - component.allowDownload = true; - component.downloadUrl = 'URL'; - component.fileName = 'fileName'; - fixture.detectChanges(); - - component.download.subscribe((e) => { - expect(e).not.toBeNull(); - done(); - }); - - const button: HTMLButtonElement = element.querySelector('[data-automation-id="adf-toolbar-download"]') as HTMLButtonElement; - button.click(); - }); - it('should render default print button', (done) => { component.allowPrint = true; fixture.detectChanges(); @@ -674,6 +650,30 @@ describe('ViewerComponent', () => { button.click(); }); + it('should get and assign node for download', (done) => { + const node = { id: 'fake-node' }; + component.fileNodeId = '12'; + component.urlFile = ''; + const displayName = 'the-name'; + const nodeDetails = { name: displayName, id: '12', content: { mimeType: 'txt' } }; + const contentUrl = '/content/url/path'; + const alfrescoApiInstanceMock = { + nodes: { + getNodeInfo: () => Promise.resolve(nodeDetails), + getNode: () => Promise.resolve(node) + }, + content: { getContentUrl: () => contentUrl } + }; + spyOn(alfrescoApiService, 'getInstance').and.returnValue(alfrescoApiInstanceMock); + + component.ngOnChanges(null); + fixture.whenStable().then(() => { + fixture.detectChanges(); + expect(component.node).toBe(node); + done(); + }); + }); + }); describe('View', () => { @@ -923,7 +923,10 @@ describe('ViewerComponent', () => { const nodeDetails = { name: displayName, id: '12', content: { mimeType: 'txt' } }; const contentUrl = '/content/url/path'; const alfrescoApiInstanceMock = { - nodes: { getNodeInfo: () => Promise.resolve(nodeDetails) }, + nodes: { + getNodeInfo: () => Promise.resolve(nodeDetails), + getNode: () => Promise.resolve({ id: 'fake-node' }) + }, content: { getContentUrl: () => contentUrl } }; @@ -998,7 +1001,6 @@ describe('ViewerComponent', () => { component.enterFullScreen(); expect(domElement.msRequestFullscreen).toHaveBeenCalled(); }); - }); }); diff --git a/lib/core/viewer/components/viewer.component.ts b/lib/core/viewer/components/viewer.component.ts index 087f28dc7b..a66366495b 100644 --- a/lib/core/viewer/components/viewer.component.ts +++ b/lib/core/viewer/components/viewer.component.ts @@ -21,7 +21,7 @@ import { Input, OnChanges, Output, SimpleChanges, TemplateRef, ViewEncapsulation, OnInit, OnDestroy } from '@angular/core'; -import { MinimalNodeEntryEntity, RenditionEntry } from 'alfresco-js-api'; +import { MinimalNodeEntryEntity, RenditionEntry, MinimalNodeEntity } from 'alfresco-js-api'; import { BaseEvent } from '../../events'; import { AlfrescoApiService } from '../../services/alfresco-api.service'; import { LogService } from '../../services/log.service'; @@ -196,10 +196,6 @@ export class ViewerComponent implements OnChanges, OnInit, OnDestroy { @Input() fileName: string; - /** URL to download. */ - @Input() - downloadUrl: string = null; - /** Number of times the Viewer will retry fetching content Rendition. * There is a delay of at least one second between attempts. */ @@ -210,10 +206,6 @@ export class ViewerComponent implements OnChanges, OnInit, OnDestroy { @Output() goBack = new EventEmitter>(); - /** Emitted when user clicks the 'Download' button. */ - @Output() - download = new EventEmitter>(); - /** Emitted when user clicks the 'Print' button. */ @Output() print = new EventEmitter>(); @@ -244,7 +236,7 @@ export class ViewerComponent implements OnChanges, OnInit, OnDestroy { viewerType = 'unknown'; isLoading = false; - node: MinimalNodeEntryEntity; + node: MinimalNodeEntity; extensionTemplates: { template: TemplateRef, isVisible: boolean }[] = []; externalExtensions: string[] = []; @@ -332,6 +324,12 @@ export class ViewerComponent implements OnChanges, OnInit, OnDestroy { this.logService.error('This node does not exist'); } ); + + this.apiService.nodesApi.getNode(this.nodeId).then( + (node) => { + this.node = node; + } + ); } else if (this.sharedLinkId) { this.apiService.sharedLinksApi.getSharedLink(this.sharedLinkId).then( @@ -366,7 +364,6 @@ export class ViewerComponent implements OnChanges, OnInit, OnDestroy { this.extension = this.getFileExtension(filenameFromUrl); this.urlFileContent = this.urlFile; - this.downloadUrl = this.urlFile; this.fileName = this.displayName; this.viewerType = this.urlFileViewer || this.getViewerTypeByExtension(this.extension); @@ -393,7 +390,6 @@ export class ViewerComponent implements OnChanges, OnInit, OnDestroy { this.extension = this.getFileExtension(data.name); this.fileName = data.name; - this.downloadUrl = this.apiService.contentApi.getContentUrl(data.id, true); this.viewerType = this.getViewerTypeByExtension(this.extension); if (this.viewerType === 'unknown') { @@ -419,7 +415,6 @@ export class ViewerComponent implements OnChanges, OnInit, OnDestroy { this.fileName = details.entry.name; this.urlFileContent = this.apiService.contentApi.getSharedLinkContentUrl(this.sharedLinkId, false); - this.downloadUrl = this.apiService.contentApi.getSharedLinkContentUrl(this.sharedLinkId, true); this.viewerType = this.getViewerTypeByMimeType(this.mimeType); if (this.viewerType === 'unknown') { @@ -608,25 +603,6 @@ export class ViewerComponent implements OnChanges, OnInit, OnDestroy { } } - downloadContent() { - if (this.allowDownload && this.downloadUrl && this.fileName) { - const args = new BaseEvent(); - this.download.next(args); - - if (!args.defaultPrevented) { - const link = document.createElement('a'); - - link.style.display = 'none'; - link.download = this.fileName; - link.href = this.downloadUrl; - - document.body.appendChild(link); - link.click(); - document.body.removeChild(link); - } - } - } - printContent() { if (this.allowPrint) { const args = new BaseEvent(); @@ -767,5 +743,4 @@ export class ViewerComponent implements OnChanges, OnInit, OnDestroy { private generateCacheBusterNumber() { this.cacheBusterNumber = Date.now(); } - } diff --git a/lib/core/viewer/viewer.module.ts b/lib/core/viewer/viewer.module.ts index fa877f18d4..a8ccd70fd9 100644 --- a/lib/core/viewer/viewer.module.ts +++ b/lib/core/viewer/viewer.module.ts @@ -39,6 +39,7 @@ import { ViewerToolbarComponent } from './components/viewer-toolbar.component'; import { ViewerComponent } from './components/viewer.component'; import { ViewerExtensionDirective } from './directives/viewer-extension.directive'; import { ViewerToolbarActionsComponent } from './components/viewer-toolbar-actions.component'; +import { DirectiveModule } from '../directives/directive.module'; @NgModule({ imports: [ @@ -49,7 +50,8 @@ import { ViewerToolbarActionsComponent } from './components/viewer-toolbar-actio ReactiveFormsModule, ToolbarModule, PipeModule, - FlexLayoutModule + FlexLayoutModule, + DirectiveModule ], declarations: [ PdfPasswordDialogComponent,