[ADF-4921] upload widget fixes (#5109)

* upload widget fixes

* fix mocks
This commit is contained in:
Denys Vuika 2019-09-29 13:48:01 +01:00 committed by Eugenio Romano
parent 7ebd287728
commit 178740bbde
7 changed files with 123 additions and 107 deletions

View File

@ -33,7 +33,8 @@
"NOT_LESS_THAN": "Can't be less than {{ minValue }}", "NOT_LESS_THAN": "Can't be less than {{ minValue }}",
"AT_LEAST_LONG": "Enter at least {{ minLength }} characters", "AT_LEAST_LONG": "Enter at least {{ minLength }} characters",
"NO_LONGER_THAN": "Enter no more than {{ maxLength }} characters" "NO_LONGER_THAN": "Enter no more than {{ maxLength }} characters"
} },
"FILE_ALREADY_UPLOADED": "A file with the same name is already uploaded."
}, },
"FORM_RENDERER": { "FORM_RENDERER": {
"NAMELESS_TASK": "Nameless task" "NAMELESS_TASK": "Nameless task"

View File

@ -39,32 +39,32 @@
<div id="adf-attach-widget-readonly-list"> <div id="adf-attach-widget-readonly-list">
<mat-list *ngIf="hasFile"> <mat-list *ngIf="hasFile">
<mat-list-item class="adf-attach-files-row" *ngFor="let file of field.value"> <mat-list-item class="adf-attach-files-row" *ngFor="let file of uploadedFiles">
<img mat-list-icon class="adf-attach-widget__icon" <img mat-list-icon class="adf-attach-widget__icon"
[id]="'file-'+file?.nodeId+'-icon'" [id]="'file-'+file?.id+'-icon'"
[src]="file.content ? getIcon(file.content.mimeType) : getIcon(file.mimeType)" [src]="file.content ? getIcon(file.content.mimeType) : getIcon(file.mimeType)"
[alt]="mimeTypeIcon" [alt]="mimeTypeIcon"
role="button" role="button"
tabindex="0"/> tabindex="0"/>
<span matLine id="{{'file-'+file?.nodeId}}" <span matLine id="{{'file-'+file?.id}}"
role="button" tabindex="0" class="adf-file">{{file.name}}</span> role="button" tabindex="0" class="adf-file">{{file.name}}</span>
<button id="{{'file-'+file?.nodeId+'-option-menu'}}" mat-icon-button [matMenuTriggerFor]="fileActionMenu"> <button id="{{'file-'+file?.id+'-option-menu'}}" mat-icon-button [matMenuTriggerFor]="fileActionMenu">
<mat-icon>more_vert</mat-icon> <mat-icon>more_vert</mat-icon>
</button> </button>
<mat-menu #fileActionMenu="matMenu" xPosition="before"> <mat-menu #fileActionMenu="matMenu" xPosition="before">
<button id="{{'file-'+file?.nodeId+'-show-file'}}" <button id="{{'file-'+file?.id+'-show-file'}}"
[disabled]="file.isExternal" [disabled]="file['isExternal']"
mat-menu-item (click)="onAttachFileClicked(file)"> mat-menu-item (click)="onAttachFileClicked(file)">
<mat-icon>image</mat-icon> <mat-icon>image</mat-icon>
<span>{{ 'FORM.FIELD.SHOW_FILE' | translate }}</span> <span>{{ 'FORM.FIELD.SHOW_FILE' | translate }}</span>
</button> </button>
<button id="{{'file-'+file?.nodeId+'-download-file'}}" <button id="{{'file-'+file?.id+'-download-file'}}"
mat-menu-item (click)="downloadContent(file)"> mat-menu-item (click)="downloadContent(file)">
<mat-icon>file_download</mat-icon> <mat-icon>file_download</mat-icon>
<span>{{ 'FORM.FIELD.DOWNLOAD_FILE' | translate }}</span> <span>{{ 'FORM.FIELD.DOWNLOAD_FILE' | translate }}</span>
</button> </button>
<button *ngIf="!field.readOnly" id="{{'file-'+file?.nodeId+'-remove-file'}}" <button *ngIf="!field.readOnly" id="{{'file-'+file?.id+'-remove-file'}}"
mat-menu-item [id]="'file-'+file?.nodeId+'-remove'" mat-menu-item [id]="'file-'+file?.id+'-remove'"
(click)="onRemoveAttachFile(file);" (keyup.enter)="onRemoveAttachFile(file);"> (click)="onRemoveAttachFile(file);" (keyup.enter)="onRemoveAttachFile(file);">
<mat-icon class="mat-24">highlight_off</mat-icon> <mat-icon class="mat-24">highlight_off</mat-icon>
<span>{{ 'FORM.FIELD.REMOVE_FILE' | translate }}</span> <span>{{ 'FORM.FIELD.REMOVE_FILE' | translate }}</span>

View File

@ -45,6 +45,7 @@ describe('AttachFileCloudWidgetComponent', () => {
let formService: FormService; let formService: FormService;
const fakePngAnswer = { const fakePngAnswer = {
id: 1155,
nodeId: 1155, nodeId: 1155,
name: 'a_png_file.png', name: 'a_png_file.png',
created: '2017-07-25T17:17:37.099Z', created: '2017-07-25T17:17:37.099Z',
@ -86,6 +87,7 @@ describe('AttachFileCloudWidgetComponent', () => {
const fakeLocalPngAnswer = { const fakeLocalPngAnswer = {
id: 1155, id: 1155,
nodeId: 1155,
name: 'a_png_file.png', name: 'a_png_file.png',
created: '2017-07-25T17:17:37.099Z', created: '2017-07-25T17:17:37.099Z',
createdBy: { createdBy: {

View File

@ -22,9 +22,10 @@ import {
FormService, FormService,
LogService, LogService,
ThumbnailService, ThumbnailService,
ContentLinkModel ContentLinkModel,
NotificationService
} from '@alfresco/adf-core'; } from '@alfresco/adf-core';
import { RelatedContentRepresentation } from '@alfresco/js-api'; import { Node, RelatedContentRepresentation } from '@alfresco/js-api';
import { ContentCloudNodeSelectorService } from '../../services/content-cloud-node-selector.service'; import { ContentCloudNodeSelectorService } from '../../services/content-cloud-node-selector.service';
import { ProcessCloudContentService } from '../../services/process-cloud-content.service'; import { ProcessCloudContentService } from '../../services/process-cloud-content.service';
import { UploadCloudWidgetComponent } from '../upload-cloud.widget'; import { UploadCloudWidgetComponent } from '../upload-cloud.widget';
@ -55,16 +56,10 @@ export class AttachFileCloudWidgetComponent extends UploadCloudWidgetComponent
public logger: LogService, public logger: LogService,
public thumbnails: ThumbnailService, public thumbnails: ThumbnailService,
public processCloudContentService: ProcessCloudContentService, public processCloudContentService: ProcessCloudContentService,
public contentNodeSelectorService: ContentCloudNodeSelectorService public contentNodeSelectorService: ContentCloudNodeSelectorService,
notificationService: NotificationService
) { ) {
super(formService, thumbnails, processCloudContentService, logger); super(formService, thumbnails, processCloudContentService, notificationService, logger);
}
ngOnInit() {
if (this.field && this.field.value && this.field.value.length > 0) {
this.hasFile = true;
}
this.getMultipleFileParam();
} }
isFileSourceConfigured(): boolean { isFileSourceConfigured(): boolean {
@ -111,18 +106,13 @@ export class AttachFileCloudWidgetComponent extends UploadCloudWidgetComponent
} }
openSelectDialog() { openSelectDialog() {
const filesSaved = []; const filesSaved: Node[] = [];
this.contentNodeSelectorService this.contentNodeSelectorService
.openUploadFileDialog(this.field.form.contentHost) .openUploadFileDialog(this.field.form.contentHost)
.subscribe((selections: any[]) => { .subscribe((selections: Node[]) => {
selections.forEach(node => (node.isExternal = true)); selections.forEach(node => (node['isExternal'] = true));
const result = { filesSaved.push(selections[0]);
nodeId: selections[0].id,
name: selections[0].name,
content: selections[0].content,
createdAt: selections[0].createdAt
};
filesSaved.push(result);
this.fixIncompatibilityFromPreviousAndNewForm(filesSaved); this.fixIncompatibilityFromPreviousAndNewForm(filesSaved);
}); });
} }
@ -136,9 +126,9 @@ export class AttachFileCloudWidgetComponent extends UploadCloudWidgetComponent
); );
} }
downloadContent(file: any): void { downloadContent(file: Node): void {
this.processCloudContentService this.processCloudContentService
.getRawContentNode(file.nodeId, this.field.form.contentHost) .getRawContentNode(file.id, this.field.form.contentHost)
.subscribe( .subscribe(
(blob: Blob) => { (blob: Blob) => {
this.processCloudContentService.downloadNodeContent( this.processCloudContentService.downloadNodeContent(

View File

@ -5,18 +5,18 @@
<div class="adf-cloud-upload-widget-container"> <div class="adf-cloud-upload-widget-container">
<div> <div>
<mat-list *ngIf="hasFile"> <mat-list *ngIf="hasFile">
<mat-list-item class="adf-upload-files-row" *ngFor="let file of field.value"> <mat-list-item class="adf-upload-files-row" *ngFor="let file of uploadedFiles">
<img mat-list-icon class="adf-upload-widget__icon" <img mat-list-icon class="adf-upload-widget__icon"
[id]="'file-'+file.nodeId+'-icon'" [id]="'file-'+file.id+'-icon'"
[src]="getIcon(file.content.mimeType)" [src]="getIcon(file.content.mimeType)"
[alt]="mimeTypeIcon" [alt]="mimeTypeIcon"
(click)="fileClicked(file)" (click)="fileClicked(file)"
(keyup.enter)="fileClicked(file)" (keyup.enter)="fileClicked(file)"
role="button" role="button"
tabindex="0"/> tabindex="0"/>
<span matLine id="{{'file-'+file.nodeId}}" (click)="fileClicked(file)" (keyup.enter)="fileClicked(file)" <span matLine id="{{'file-'+file.id}}" (click)="fileClicked(file)" (keyup.enter)="fileClicked(file)"
role="button" tabindex="0" class="adf-file">{{file.name}}</span> role="button" tabindex="0" class="adf-file">{{file.name}}</span>
<button *ngIf="!field.readOnly" mat-icon-button [id]="'file-'+file.nodeId+'-remove'" <button *ngIf="!field.readOnly" mat-icon-button [id]="'file-'+file.id+'-remove'"
(click)="removeFile(file);" (keyup.enter)="removeFile(file);"> (click)="removeFile(file);" (keyup.enter)="removeFile(file);">
<mat-icon class="mat-24">highlight_off</mat-icon> <mat-icon class="mat-24">highlight_off</mat-icon>
</button> </button>

View File

@ -18,9 +18,10 @@
/* tslint:disable:component-selector */ /* tslint:disable:component-selector */
import { Component, ElementRef, OnInit, ViewChild, ViewEncapsulation } from '@angular/core'; import { Component, ElementRef, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
import { Node } from '@alfresco/js-api';
import { Observable, from } from 'rxjs'; import { Observable, from } from 'rxjs';
import { mergeMap, map, catchError } from 'rxjs/operators'; import { mergeMap } from 'rxjs/operators';
import { WidgetComponent, baseHost, LogService, FormService, ThumbnailService, ContentLinkModel } from '@alfresco/adf-core'; import { WidgetComponent, baseHost, LogService, FormService, ThumbnailService, ContentLinkModel, NotificationService } from '@alfresco/adf-core';
import { ProcessCloudContentService } from '../services/process-cloud-content.service'; import { ProcessCloudContentService } from '../services/process-cloud-content.service';
@Component({ @Component({
@ -44,6 +45,7 @@ export class UploadCloudWidgetComponent extends WidgetComponent implements OnIni
public formService: FormService, public formService: FormService,
private thumbnailService: ThumbnailService, private thumbnailService: ThumbnailService,
public processCloudContentService: ProcessCloudContentService, public processCloudContentService: ProcessCloudContentService,
private notificationService: NotificationService,
private logService: LogService) { private logService: LogService) {
super(formService); super(formService);
} }
@ -53,6 +55,7 @@ export class UploadCloudWidgetComponent extends WidgetComponent implements OnIni
this.field.value && this.field.value &&
this.field.value.length > 0) { this.field.value.length > 0) {
this.hasFile = true; this.hasFile = true;
this.fixIncompatibilityFromPreviousAndNewForm([]);
} }
this.getMultipleFileParam(); this.getMultipleFileParam();
} }
@ -64,8 +67,16 @@ export class UploadCloudWidgetComponent extends WidgetComponent implements OnIni
} }
onFileChanged(event: any) { onFileChanged(event: any) {
const files = event.target.files; const files: File[] = [];
const filesSaved = []; const filesSaved: Node[] = [];
for (const file of Array.from<File>(event.target.files)) {
if (!this.isUploaded(file)) {
files.push(file);
} else {
this.notificationService.showWarning('FORM.FIELD.FILE_ALREADY_UPLOADED');
}
}
if (files && files.length > 0) { if (files && files.length > 0) {
from(files) from(files)
@ -83,34 +94,29 @@ export class UploadCloudWidgetComponent extends WidgetComponent implements OnIni
} }
} }
fixIncompatibilityFromPreviousAndNewForm(filesSaved) { private isUploaded(file: File): boolean {
this.field.value = filesSaved; const current: Node[] = this.field.value || [];
this.field.form.values[this.field.id] = filesSaved; return current.some(entry => entry.name === file.name);
this.hasFile = true;
} }
getIcon(mimeType) { protected fixIncompatibilityFromPreviousAndNewForm(filesSaved: Node[]) {
const value: Node[] = [...this.field.value || []];
value.push(...filesSaved || []);
this.field.value = value;
this.field.form.values[this.field.id] = value;
this.hasFile = value.length > 0;
}
getIcon(mimeType: string): string {
return this.thumbnailService.getMimeTypeIcon(mimeType); return this.thumbnailService.getMimeTypeIcon(mimeType);
} }
private uploadRawContent(file): Observable<any> { private uploadRawContent(file: File): Observable<Node> {
return this.processCloudContentService.createTemporaryRawRelatedContent(file, this.field.form.nodeId, this.field.form.contentHost) return this.processCloudContentService.createTemporaryRawRelatedContent(
.pipe( file, this.field.form.nodeId, this.field.form.contentHost
map((response: any) => { );
this.logService.info(response);
return {
nodeId: response.id,
name: response.name,
content: response.content,
createdAt: response.createdAt
};
}),
catchError((err) => this.handleError(err))
);
}
private handleError(error: any): any {
return this.logService.error(error || 'Server error');
} }
getMultipleFileParam() { getMultipleFileParam() {
@ -121,17 +127,17 @@ export class UploadCloudWidgetComponent extends WidgetComponent implements OnIni
} }
} }
private removeElementFromList(file) { get uploadedFiles(): Node[] {
const savedValues = this.field.form.values[this.field.id] const result = this.field.value || this.field.form.values[this.field.id];
? this.field.form.values[this.field.id] : this.field.value; return result || [];
const index = savedValues.indexOf(file);
if (index !== -1) {
const filteredValues = savedValues.filter((value: any) => value.nodeId !== file.nodeId);
this.resetFormValues(filteredValues);
}
} }
private resetFormValues(values) { private removeElementFromList(file: Node) {
const filteredValues = this.uploadedFiles.filter(value => value.id !== file.id);
this.resetFormValues(filteredValues);
}
private resetFormValues(values: Node[]) {
if (values && values.length > 0) { if (values && values.length > 0) {
this.field.value = values; this.field.value = values;
this.field.form.values[this.field.id] = values; this.field.form.values[this.field.id] = values;

View File

@ -18,47 +18,64 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { throwError, Observable, from } from 'rxjs'; import { throwError, Observable, from } from 'rxjs';
import { catchError, map } from 'rxjs/operators'; import { catchError, map } from 'rxjs/operators';
import { AlfrescoApiService, LogService, ContentService } from '@alfresco/adf-core'; import {
AlfrescoApiService,
LogService,
ContentService
} from '@alfresco/adf-core';
import { Node } from '@alfresco/js-api';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
export class ProcessCloudContentService { export class ProcessCloudContentService {
constructor(
private apiService: AlfrescoApiService,
private logService: LogService,
public contentService: ContentService
) {}
constructor( createTemporaryRawRelatedContent(
private apiService: AlfrescoApiService, file: File,
private logService: LogService, nodeId: string,
public contentService: ContentService contentHost: string
) { } ): Observable<Node> {
const changedConfig = this.apiService.lastConfig;
createTemporaryRawRelatedContent(file, nodeId, contentHost): Observable<any> { changedConfig.provider = 'ALL';
const changedConfig = this.apiService.lastConfig; changedConfig.hostEcm = contentHost.replace('/alfresco', '');
changedConfig.provider = 'ALL';
changedConfig.hostEcm = contentHost.replace('/alfresco', '');
this.apiService.getInstance().setConfig(changedConfig);
return from(this.apiService.getInstance().upload.uploadFile(
file, '', nodeId, '', { overwrite: true })).pipe(
map((res: any) => {
return (res.entry);
}),
catchError((err) => this.handleError(err))
);
}
getRawContentNode(nodeId: string, contentHost: string): Observable<any> { this.apiService.getInstance().setConfig(changedConfig);
const changedConfig = this.apiService.lastConfig;
changedConfig.provider = 'ALL';
changedConfig.hostEcm = contentHost.replace('/alfresco', '');
this.apiService.getInstance().setConfig(changedConfig);
return this.contentService.getNodeContent(nodeId);
}
downloadNodeContent(blob: Blob, fileName: string): void { return from(
this.contentService.downloadBlob(blob, fileName); this.apiService
} .getInstance()
.upload.uploadFile(file, '', nodeId, '', { overwrite: true })
).pipe(
map((res: any) => {
return {
...res.entry,
nodeId: res.entry.id
};
}),
catchError(err => this.handleError(err))
);
}
private handleError(error: any) { getRawContentNode(nodeId: string, contentHost: string): Observable<any> {
this.logService.error(error); const changedConfig = this.apiService.lastConfig;
return throwError(error || 'Server error'); changedConfig.provider = 'ALL';
} changedConfig.hostEcm = contentHost.replace('/alfresco', '');
this.apiService.getInstance().setConfig(changedConfig);
return this.contentService.getNodeContent(nodeId);
}
downloadNodeContent(blob: Blob, fileName: string): void {
this.contentService.downloadBlob(blob, fileName);
}
private handleError(error: any) {
this.logService.error(error);
return throwError(error || 'Server error');
}
} }