[ADF-4926] fix attachments downloading (#5134)

* fix attachments downloading

* update code and tests
This commit is contained in:
Denys Vuika
2019-10-10 12:06:37 +01:00
committed by Eugenio Romano
parent 2def8d0557
commit b1d0c50e88
7 changed files with 110 additions and 85 deletions

View File

@@ -18,7 +18,7 @@
import { Injectable, Output, EventEmitter } from '@angular/core';
import { Node, NodeEntry } from '@alfresco/js-api';
import { Subject } from 'rxjs';
import { AlfrescoApiService, ContentService, NodeDownloadDirective } from '@alfresco/adf-core';
import { AlfrescoApiService, ContentService, NodeDownloadDirective, DownloadService } from '@alfresco/adf-core';
import { MatDialog } from '@angular/material';
import { DocumentListService } from './document-list.service';
@@ -30,17 +30,18 @@ import { ContentNodeDialogService } from '../../content-node-selector/content-no
export class NodeActionsService {
@Output()
error: EventEmitter<any> = new EventEmitter<any>();
error = new EventEmitter<any>();
constructor(private contentDialogService: ContentNodeDialogService,
public dialogRef: MatDialog,
public content: ContentService,
private documentListService?: DocumentListService,
private apiService?: AlfrescoApiService,
private dialog?: MatDialog) {}
private dialog?: MatDialog,
private downloadService?: DownloadService) {}
downloadNode(node: NodeEntry) {
new NodeDownloadDirective(this.apiService, this.dialog)
new NodeDownloadDirective(this.apiService, this.downloadService, this.dialog)
.downloadNode(node);
}
@@ -50,7 +51,7 @@ export class NodeActionsService {
* @param contentEntry node to copy
* @param permission permission which is needed to apply the action
*/
public copyContent(contentEntry: Node, permission?: string): Subject<string> {
copyContent(contentEntry: Node, permission?: string): Subject<string> {
return this.doFileOperation('copy', 'content', contentEntry, permission);
}
@@ -60,7 +61,7 @@ export class NodeActionsService {
* @param contentEntry node to copy
* @param permission permission which is needed to apply the action
*/
public copyFolder(contentEntry: Node, permission?: string): Subject<string> {
copyFolder(contentEntry: Node, permission?: string): Subject<string> {
return this.doFileOperation('copy', 'folder', contentEntry, permission);
}
@@ -70,7 +71,7 @@ export class NodeActionsService {
* @param contentEntry node to move
* @param permission permission which is needed to apply the action
*/
public moveContent(contentEntry: Node, permission?: string): Subject<string> {
moveContent(contentEntry: Node, permission?: string): Subject<string> {
return this.doFileOperation('move', 'content', contentEntry, permission);
}
@@ -80,7 +81,7 @@ export class NodeActionsService {
* @param contentEntry node to move
* @param permission permission which is needed to apply the action
*/
public moveFolder(contentEntry: Node, permission?: string): Subject<string> {
moveFolder(contentEntry: Node, permission?: string): Subject<string> {
return this.doFileOperation('move', 'folder', contentEntry, permission);
}
@@ -93,7 +94,7 @@ export class NodeActionsService {
* @param permission permission which is needed to apply the action
*/
private doFileOperation(action: string, type: string, contentEntry: Node, permission?: string): Subject<string> {
const observable: Subject<string> = new Subject<string>();
const observable = new Subject<string>();
this.contentDialogService
.openCopyMoveDialog(action, contentEntry, permission)

View File

@@ -20,17 +20,18 @@ import { MatDialog } from '@angular/material';
import { AlfrescoApiService } from '../services/alfresco-api.service';
import { DownloadZipDialogComponent } from '../dialogs/download-zip/download-zip.dialog';
import { NodeEntry } from '@alfresco/js-api';
import { DownloadService } from '../services/download.service';
/**
* Directive selectors without adf- prefix will be deprecated on 3.0.0
*/
@Directive({
selector: '[adf-node-download], [adfNodeDownload]'
// tslint:disable-next-line: directive-selector
selector: '[adfNodeDownload]'
})
export class NodeDownloadDirective {
/** Nodes to download. */
// tslint:disable-next-line:no-input-rename
@Input('adfNodeDownload')
nodes: NodeEntry | NodeEntry[];
@@ -41,6 +42,7 @@ export class NodeDownloadDirective {
constructor(
private apiService: AlfrescoApiService,
private downloadService: DownloadService,
private dialog: MatDialog) {
}
@@ -102,7 +104,7 @@ export class NodeDownloadDirective {
const url = contentApi.getContentUrl(id, true);
const fileName = node.entry.name;
this.download(url, fileName);
this.downloadService.downloadUrl(url, fileName);
}
}
@@ -120,18 +122,4 @@ export class NodeDownloadDirective {
});
}
}
private download(url: string, fileName: string) {
if (url && fileName) {
const link = document.createElement('a');
link.style.display = 'none';
link.download = fileName;
link.href = url;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
}
}

View File

@@ -86,4 +86,23 @@ export class DownloadService {
downloadJSON(json: any, fileName: string): void {
this.saveData(json, 'json', fileName);
}
/**
* Invokes the download of the file by its URL address.
* @param url Url address pointing to the file.
* @param fileName Name of the file download.
*/
downloadUrl(url: string, fileName: string): void {
if (url && fileName) {
const link = document.createElement('a');
link.style.display = 'none';
link.download = fileName;
link.href = url;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
}
}

View File

@@ -26,7 +26,8 @@ import {
FormModel,
FormFieldTypes,
FormFieldMetadata,
FormService
FormService,
DownloadService
} from '@alfresco/adf-core';
import { ProcessServiceCloudTestingModule } from '../../../testing/process-service-cloud.testing.module';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
@@ -43,6 +44,7 @@ describe('AttachFileCloudWidgetComponent', () => {
let contentCloudNodeSelectorService: ContentCloudNodeSelectorService;
let processCloudContentService: ProcessCloudContentService;
let formService: FormService;
let downloadService: DownloadService;
const fakePngAnswer = {
id: 1155,
@@ -116,6 +118,7 @@ describe('AttachFileCloudWidgetComponent', () => {
});
beforeEach(async(() => {
downloadService = TestBed.get(DownloadService);
fixture = TestBed.createComponent(AttachFileCloudWidgetComponent);
widget = fixture.componentInstance;
element = fixture.nativeElement;
@@ -278,22 +281,29 @@ describe('AttachFileCloudWidgetComponent', () => {
it('should download file when download is clicked', (done) => {
spyOn(processCloudContentService, 'getRawContentNode').and.returnValue(of(new Blob()));
spyOn(processCloudContentService, 'downloadNodeContent').and.stub();
spyOn(processCloudContentService, 'getAuthTicket').and.returnValue(Promise.resolve('ticket'));
spyOn(downloadService, 'downloadUrl').and.stub();
fixture.detectChanges();
const menuButton: HTMLButtonElement = <HTMLButtonElement> (
fixture.debugElement.query(By.css('#file-1155-option-menu'))
.nativeElement
);
menuButton.click();
fixture.detectChanges();
const downloadOption: HTMLButtonElement = <HTMLButtonElement> (
fixture.debugElement.query(By.css('#file-1155-download-file'))
.nativeElement
);
downloadOption.click();
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(processCloudContentService.downloadNodeContent).toHaveBeenCalled();
expect(downloadService.downloadUrl).toHaveBeenCalled();
done();
});
});

View File

@@ -23,7 +23,8 @@ import {
LogService,
ThumbnailService,
ContentLinkModel,
NotificationService
NotificationService,
baseHost
} from '@alfresco/adf-core';
import { Node, RelatedContentRepresentation } from '@alfresco/js-api';
import { ContentCloudNodeSelectorService } from '../../services/content-cloud-node-selector.service';
@@ -34,17 +35,7 @@ import { UploadCloudWidgetComponent } from '../upload-cloud.widget';
selector: 'adf-cloud-attach-file-cloud-widget',
templateUrl: './attach-file-cloud-widget.component.html',
styleUrls: ['./attach-file-cloud-widget.component.scss'],
host: {
'(click)': 'event($event)',
'(blur)': 'event($event)',
'(change)': 'event($event)',
'(focus)': 'event($event)',
'(focusin)': 'event($event)',
'(focusout)': 'event($event)',
'(input)': 'event($event)',
'(invalid)': 'event($event)',
'(select)': 'event($event)'
},
host: baseHost,
encapsulation: ViewEncapsulation.None
})
export class AttachFileCloudWidgetComponent extends UploadCloudWidgetComponent
@@ -52,12 +43,12 @@ export class AttachFileCloudWidgetComponent extends UploadCloudWidgetComponent
static ACS_SERVICE = 'alfresco-content';
constructor(
public formService: FormService,
public logger: LogService,
public thumbnails: ThumbnailService,
public processCloudContentService: ProcessCloudContentService,
public contentNodeSelectorService: ContentCloudNodeSelectorService,
notificationService: NotificationService
formService: FormService,
logger: LogService,
thumbnails: ThumbnailService,
processCloudContentService: ProcessCloudContentService,
notificationService: NotificationService,
private contentNodeSelectorService: ContentCloudNodeSelectorService
) {
super(formService, thumbnails, processCloudContentService, notificationService, logger);
}
@@ -127,30 +118,20 @@ export class AttachFileCloudWidgetComponent extends UploadCloudWidgetComponent
}
downloadContent(file: Node): void {
this.processCloudContentService
.getRawContentNode(file.id, this.field.form.contentHost)
.subscribe(
(blob: Blob) => {
this.processCloudContentService.downloadNodeContent(
blob,
file.name
);
},
() => {
this.logger.error(
'Impossible retrieve content for download'
);
}
);
this.processCloudContentService.downloadFile(
file.id,
this.field.form.contentHost
);
}
onAttachFileClicked(file: ContentLinkModel) {
this.processCloudContentService
.getRawContentNode(file.nodeId, this.field.form.contentHost)
.subscribe(
(blob: Blob) => {
file.contentBlob = blob;
this.fileClicked(file);
});
.getRawContentNode(file.nodeId, this.field.form.contentHost)
.subscribe(
blob => {
file.contentBlob = blob;
this.fileClicked(file);
}
);
}
}

View File

@@ -42,11 +42,11 @@ export class UploadCloudWidgetComponent extends WidgetComponent implements OnIni
fileInput: ElementRef;
constructor(
public formService: FormService,
formService: FormService,
private thumbnailService: ThumbnailService,
public processCloudContentService: ProcessCloudContentService,
private notificationService: NotificationService,
private logService: LogService) {
protected processCloudContentService: ProcessCloudContentService,
protected notificationService: NotificationService,
protected logService: LogService) {
super(formService);
}

View File

@@ -21,7 +21,8 @@ import { catchError, map } from 'rxjs/operators';
import {
AlfrescoApiService,
LogService,
ContentService
ContentService,
DownloadService
} from '@alfresco/adf-core';
import { Node } from '@alfresco/js-api';
@@ -32,7 +33,8 @@ export class ProcessCloudContentService {
constructor(
private apiService: AlfrescoApiService,
private logService: LogService,
public contentService: ContentService
public contentService: ContentService,
private downloadService: DownloadService
) {}
createTemporaryRawRelatedContent(
@@ -40,12 +42,7 @@ export class ProcessCloudContentService {
nodeId: string,
contentHost: string
): Observable<Node> {
const changedConfig = this.apiService.lastConfig;
changedConfig.provider = 'ALL';
changedConfig.hostEcm = contentHost.replace('/alfresco', '');
this.apiService.getInstance().setConfig(changedConfig);
this.updateConfig(contentHost);
return from(
this.apiService
@@ -62,11 +59,8 @@ export class ProcessCloudContentService {
);
}
getRawContentNode(nodeId: string, contentHost: string): Observable<any> {
const changedConfig = this.apiService.lastConfig;
changedConfig.provider = 'ALL';
changedConfig.hostEcm = contentHost.replace('/alfresco', '');
this.apiService.getInstance().setConfig(changedConfig);
getRawContentNode(nodeId: string, contentHost: string): Observable<Blob> {
this.updateConfig(contentHost);
return this.contentService.getNodeContent(nodeId);
}
@@ -74,6 +68,38 @@ export class ProcessCloudContentService {
this.contentService.downloadBlob(blob, fileName);
}
async downloadFile(nodeId: string, contentHost: string) {
this.updateConfig(contentHost);
const ticket = await this.getAuthTicket();
const url = this.contentService.getContentUrl(nodeId, true, ticket);
this.downloadService.downloadUrl(url, nodeId);
}
async getAuthTicket(): Promise<string> {
const { auth } = this.apiService.getInstance();
const ticket = await auth.authenticationApi.getTicket();
if (ticket && ticket.entry) {
return ticket.entry.id || '';
}
return '';
}
private updateConfig(contentHost: string) {
const changedConfig = this.apiService.lastConfig;
changedConfig.provider = 'ALL';
if (contentHost) {
changedConfig.hostEcm = contentHost.replace('/alfresco', '');
}
this.apiService.getInstance().setConfig(changedConfig);
}
private handleError(error: any) {
this.logService.error(error);
return throwError(error || 'Server error');