[ADF-4823] added download and show preview for attach cloud widget (#4996)

* [ADF-4823] fixed download and start fixing show preview

* [ADF-4823] fixed preview files

* [ADF-4823] - added unit test

* [ADF-4823] - fixed lint problem

* [ADF-4823] - rebased and fixed lint problem

* added a new method in BrowserActions to check that the action menu is visible, and using that in the tests.

* linting fixes
This commit is contained in:
Vito
2019-09-03 11:10:40 +01:00
committed by Eugenio Romano
parent c142371222
commit 19a17ca6e6
14 changed files with 478 additions and 281 deletions

View File

@@ -10,6 +10,7 @@
(taskCompleted)="onTaskCompleted()" (taskCompleted)="onTaskCompleted()"
(taskClaimed)="onTaskClaimed()" (taskClaimed)="onTaskClaimed()"
(taskUnclaimed)="onTaskUnclaimed()" (taskUnclaimed)="onTaskUnclaimed()"
(formContentClicked)="onFormContentClicked($event)"
(formSaved)="onFormSaved()"> (formSaved)="onFormSaved()">
</adf-cloud-task-form> </adf-cloud-task-form>
</div> </div>

View File

@@ -19,6 +19,7 @@ import { Component, ViewChild } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { NotificationService } from '@alfresco/adf-core'; import { NotificationService } from '@alfresco/adf-core';
import { TaskHeaderCloudComponent } from '@alfresco/adf-process-services-cloud'; import { TaskHeaderCloudComponent } from '@alfresco/adf-process-services-cloud';
import { PreviewService } from 'app/services/preview.service';
@Component({ @Component({
templateUrl: './task-details-cloud-demo.component.html', templateUrl: './task-details-cloud-demo.component.html',
@@ -35,7 +36,8 @@ export class TaskDetailsCloudDemoComponent {
constructor( constructor(
private route: ActivatedRoute, private route: ActivatedRoute,
private router: Router, private router: Router,
private notificationService: NotificationService private notificationService: NotificationService,
private previewService: PreviewService
) { ) {
this.route.params.subscribe((params) => { this.route.params.subscribe((params) => {
this.taskId = params.taskId; this.taskId = params.taskId;
@@ -66,8 +68,8 @@ export class TaskDetailsCloudDemoComponent {
this.taskHeader.ngOnInit(); this.taskHeader.ngOnInit();
} }
onFormContentClicked(resourceId) { onFormContentClicked(resourceClicked: any) {
this.router.navigate([`/cloud/${this.appName}/task-details/${this.taskId}/files/${resourceId.nodeId}/view`]); this.previewService.showResource(resourceClicked.nodeId);
} }
onFormSaved() { onFormSaved() {

View File

@@ -25,6 +25,7 @@
created: Date; created: Date;
createdBy: any; createdBy: any;
id: number; id: number;
nodeId: string;
link: boolean; link: boolean;
mimeType: string; mimeType: string;
name: string; name: string;
@@ -48,6 +49,7 @@
this.relatedContent = obj && obj.relatedContent; this.relatedContent = obj && obj.relatedContent;
this.simpleType = obj && obj.simpleType; this.simpleType = obj && obj.simpleType;
this.thumbnailStatus = obj && obj.thumbnailStatus; this.thumbnailStatus = obj && obj.thumbnailStatus;
this.nodeId = obj && obj.nodeId;
} }
hasPreviewStatus(): boolean { hasPreviewStatus(): boolean {

View File

@@ -48,10 +48,28 @@
tabindex="0"/> tabindex="0"/>
<span matLine id="{{'file-'+file?.nodeId}}" <span matLine id="{{'file-'+file?.nodeId}}"
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 id="{{'file-'+file?.nodeId+'-option-menu'}}" mat-icon-button [matMenuTriggerFor]="fileActionMenu">
(click)="onRemoveAttachFile(file);" (keyup.enter)="onRemoveAttachFile(file);"> <mat-icon>more_vert</mat-icon>
<mat-icon class="mat-24">highlight_off</mat-icon>
</button> </button>
<mat-menu #fileActionMenu="matMenu" xPosition="before">
<button id="{{'file-'+file?.nodeId+'-show-file'}}"
[disabled]="file.isExternal"
mat-menu-item (click)="onAttachFileClicked(file)">
<mat-icon>image</mat-icon>
<span>{{ 'FORM.FIELD.SHOW_FILE' | translate }}</span>
</button>
<button id="{{'file-'+file?.nodeId+'-download-file'}}"
mat-menu-item (click)="downloadContent(file)">
<mat-icon>file_download</mat-icon>
<span>{{ 'FORM.FIELD.DOWNLOAD_FILE' | translate }}</span>
</button>
<button *ngIf="!field.readOnly" id="{{'file-'+file?.nodeId+'-remove-file'}}"
mat-menu-item [id]="'file-'+file?.nodeId+'-remove'"
(click)="onRemoveAttachFile(file);" (keyup.enter)="onRemoveAttachFile(file);">
<mat-icon class="mat-24">highlight_off</mat-icon>
<span>{{ 'FORM.FIELD.REMOVE_FILE' | translate }}</span>
</button>
</mat-menu>
</mat-list-item> </mat-list-item>
</mat-list> </mat-list>
</div> </div>

View File

@@ -20,7 +20,14 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing';
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 { AttachFileCloudWidgetComponent } from './attach-file-cloud-widget.component'; import { AttachFileCloudWidgetComponent } from './attach-file-cloud-widget.component';
import { setupTestBed, FormFieldModel, FormModel, FormFieldTypes, FormFieldMetadata } from '@alfresco/adf-core'; import {
setupTestBed,
FormFieldModel,
FormModel,
FormFieldTypes,
FormFieldMetadata,
FormService
} from '@alfresco/adf-core';
import { ProcessServiceCloudTestingModule } from '../../../testing/process-service-cloud.testing.module'; import { ProcessServiceCloudTestingModule } from '../../../testing/process-service-cloud.testing.module';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { ContentModule } from '@alfresco/adf-content-services'; import { ContentModule } from '@alfresco/adf-content-services';
@@ -30,193 +37,288 @@ import { Node } from '@alfresco/js-api';
import { FormCloudModule } from '../../form-cloud.module'; import { FormCloudModule } from '../../form-cloud.module';
describe('AttachFileCloudWidgetComponent', () => { describe('AttachFileCloudWidgetComponent', () => {
let widget: AttachFileCloudWidgetComponent; let widget: AttachFileCloudWidgetComponent;
let fixture: ComponentFixture<AttachFileCloudWidgetComponent>; let fixture: ComponentFixture<AttachFileCloudWidgetComponent>;
let element: HTMLInputElement; let element: HTMLInputElement;
let contentCloudNodeSelectorService: ContentCloudNodeSelectorService; let contentCloudNodeSelectorService: ContentCloudNodeSelectorService;
let processCloudContentService: ProcessCloudContentService; let processCloudContentService: ProcessCloudContentService;
let formService: FormService;
const fakePngAnswer = { const fakePngAnswer = {
'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',
'createdBy': { 'id': 1001, 'firstName': 'Admin', 'lastName': 'admin', 'email': 'admin' }, createdBy: {
'relatedContent': false, id: 1001,
'contentAvailable': true, firstName: 'Admin',
'link': false, lastName: 'admin',
'mimeType': 'image/png', email: 'admin'
'simpleType': 'image', },
'previewStatus': 'queued', relatedContent: false,
'thumbnailStatus': 'queued' contentAvailable: true,
}; link: false,
mimeType: 'image/png',
simpleType: 'image',
previewStatus: 'queued',
thumbnailStatus: 'queued'
};
const onlyLocalParams = { const onlyLocalParams = {
fileSource: { fileSource: {
serviceId: 'local-file' serviceId: 'local-file'
} }
}; };
const contentSourceParam = { const contentSourceParam = {
fileSource: { fileSource: {
name: 'mock-alf-content', name: 'mock-alf-content',
serviceId: 'alfresco-content' serviceId: 'alfresco-content'
} }
}; };
const fakeMinimalNode: Node = <Node> { const fakeMinimalNode: Node = <Node> {
id: 'fake', id: 'fake',
name: 'fake-name', name: 'fake-name',
content: { content: {
mimeType: 'application/pdf' mimeType: 'application/pdf'
} }
}; };
const fakeLocalPngAnswer = { const fakeLocalPngAnswer = {
'id': 1155, id: 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': { 'id': 1001, 'firstName': 'Admin', 'lastName': 'admin', 'email': 'admin' }, createdBy: {
'relatedContent': false, id: 1001,
'contentAvailable': true, firstName: 'Admin',
'link': false, lastName: 'admin',
'mimeType': 'image/png', email: 'admin'
'simpleType': 'image', },
'previewStatus': 'queued', relatedContent: false,
'thumbnailStatus': 'queued' contentAvailable: true,
}; link: false,
mimeType: 'image/png',
simpleType: 'image',
previewStatus: 'queued',
thumbnailStatus: 'queued'
};
setupTestBed({ setupTestBed({
imports: [ imports: [
ProcessServiceCloudTestingModule, ProcessServiceCloudTestingModule,
FormCloudModule, FormCloudModule,
ContentModule.forRoot() ContentModule.forRoot()
], ],
providers: [], providers: [],
schemas: [CUSTOM_ELEMENTS_SCHEMA] schemas: [CUSTOM_ELEMENTS_SCHEMA]
});
beforeEach(async(() => {
fixture = TestBed.createComponent(AttachFileCloudWidgetComponent);
widget = fixture.componentInstance;
element = fixture.nativeElement;
processCloudContentService = TestBed.get(ProcessCloudContentService);
contentCloudNodeSelectorService = TestBed.get(ContentCloudNodeSelectorService);
}));
afterEach(() => {
fixture.destroy();
});
it('should be able to create the widget', () => {
expect(widget).not.toBeNull();
});
it('should show up as simple upload when is configured for only local files', async(() => {
widget.field = new FormFieldModel(new FormModel(), {
type: FormFieldTypes.UPLOAD,
value: []
}); });
widget.field.id = 'simple-upload-button';
widget.field.params = <FormFieldMetadata> onlyLocalParams;
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(element.querySelector('#simple-upload-button')).not.toBeNull();
});
}));
it('should show up as content upload when is configured with content', async(() => {
widget.field = new FormFieldModel(new FormModel(), {
type: FormFieldTypes.UPLOAD,
value: []
});
widget.field.id = 'attach-file-alfresco';
widget.field.params = <FormFieldMetadata> contentSourceParam;
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(element.querySelector('.adf-attach-widget__menu-upload')).not.toBeNull();
});
}));
it('should be able to attach files coming from content selector', async(() => {
spyOn(contentCloudNodeSelectorService, 'openUploadFileDialog').and.returnValue(of([fakeMinimalNode]));
widget.field = new FormFieldModel(new FormModel(), {
type: FormFieldTypes.UPLOAD,
value: []
});
widget.field.id = 'attach-file-alfresco';
widget.field.params = <FormFieldMetadata> contentSourceParam;
fixture.detectChanges();
fixture.whenStable().then(() => {
const attachButton: HTMLButtonElement = element.querySelector('#attach-file-alfresco');
expect(attachButton).not.toBeNull();
attachButton.click();
fixture.whenStable().then(() => {
fixture.detectChanges();
fixture.debugElement.query(By.css('#attach-mock-alf-content')).nativeElement.click();
fixture.detectChanges();
expect(element.querySelector('#file-fake-icon')).not.toBeNull();
});
});
}));
it('should be able to upload files from local source', async(() => {
widget.field = new FormFieldModel(new FormModel(), {
type: FormFieldTypes.UPLOAD,
value: []
});
widget.field.id = 'attach-file-local';
widget.field.params = <FormFieldMetadata> onlyLocalParams;
spyOn(processCloudContentService, 'createTemporaryRawRelatedContent').and.returnValue(of(fakeLocalPngAnswer));
fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
const inputDebugElement = fixture.debugElement.query(By.css('#attach-file-local'));
inputDebugElement.triggerEventHandler('change', { target: { files: [fakeLocalPngAnswer] } });
fixture.detectChanges();
expect(element.querySelector('#file-1155-icon')).not.toBeNull();
});
}));
it('should display file list when field has value', async(() => {
widget.field = new FormFieldModel(new FormModel(), {
type: FormFieldTypes.UPLOAD,
value: [fakePngAnswer]
});
widget.field.id = 'attach-file-attach';
widget.field.params = <FormFieldMetadata> onlyLocalParams;
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(element.querySelector('#file-1155-icon')).not.toBeNull();
});
}));
describe('when a file is uploaded', () => {
beforeEach(async(() => { beforeEach(async(() => {
widget.field = new FormFieldModel(new FormModel(), { fixture = TestBed.createComponent(AttachFileCloudWidgetComponent);
type: FormFieldTypes.UPLOAD, widget = fixture.componentInstance;
value: [] element = fixture.nativeElement;
}); processCloudContentService = TestBed.get(ProcessCloudContentService);
widget.field.id = 'attach-file-attach'; contentCloudNodeSelectorService = TestBed.get(
widget.field.params = <FormFieldMetadata> onlyLocalParams; ContentCloudNodeSelectorService
spyOn(processCloudContentService, 'createTemporaryRawRelatedContent').and.returnValue(of(fakeLocalPngAnswer)); );
fixture.detectChanges(); formService = TestBed.get(FormService);
fixture.whenStable().then(() => {
const inputDebugElement = fixture.debugElement.query(By.css('#attach-file-attach'));
inputDebugElement.triggerEventHandler('change', { target: { files: [fakeLocalPngAnswer] } });
fixture.detectChanges();
expect(element.querySelector('#file-1155-icon')).not.toBeNull();
});
})); }));
it('should remove file when remove is clicked', async(() => { afterEach(() => {
fixture.detectChanges(); fixture.destroy();
const removeOption: HTMLButtonElement = <HTMLButtonElement> fixture.debugElement.query(By.css('#file-1155-remove')).nativeElement; });
removeOption.click();
fixture.detectChanges(); it('should be able to create the widget', () => {
fixture.whenStable().then(() => { expect(widget).not.toBeNull();
expect(element.querySelector('#file-1155-icon')).toBeNull(); });
});
it('should show up as simple upload when is configured for only local files', async(() => {
widget.field = new FormFieldModel(new FormModel(), {
type: FormFieldTypes.UPLOAD,
value: []
});
widget.field.id = 'simple-upload-button';
widget.field.params = <FormFieldMetadata> onlyLocalParams;
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(
element.querySelector('#simple-upload-button')
).not.toBeNull();
});
})); }));
});
it('should show up as content upload when is configured with content', async(() => {
widget.field = new FormFieldModel(new FormModel(), {
type: FormFieldTypes.UPLOAD,
value: []
});
widget.field.id = 'attach-file-alfresco';
widget.field.params = <FormFieldMetadata> contentSourceParam;
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(
element.querySelector('.adf-attach-widget__menu-upload')
).not.toBeNull();
});
}));
it('should be able to attach files coming from content selector', async(() => {
spyOn(
contentCloudNodeSelectorService,
'openUploadFileDialog'
).and.returnValue(of([fakeMinimalNode]));
widget.field = new FormFieldModel(new FormModel(), {
type: FormFieldTypes.UPLOAD,
value: []
});
widget.field.id = 'attach-file-alfresco';
widget.field.params = <FormFieldMetadata> contentSourceParam;
fixture.detectChanges();
fixture.whenStable().then(() => {
const attachButton: HTMLButtonElement = element.querySelector(
'#attach-file-alfresco'
);
expect(attachButton).not.toBeNull();
attachButton.click();
fixture.whenStable().then(() => {
fixture.detectChanges();
fixture.debugElement
.query(By.css('#attach-mock-alf-content'))
.nativeElement.click();
fixture.detectChanges();
expect(element.querySelector('#file-fake-icon')).not.toBeNull();
});
});
}));
it('should be able to upload files from local source', async(() => {
widget.field = new FormFieldModel(new FormModel(), {
type: FormFieldTypes.UPLOAD,
value: []
});
widget.field.id = 'attach-file-local';
widget.field.params = <FormFieldMetadata> onlyLocalParams;
spyOn(
processCloudContentService,
'createTemporaryRawRelatedContent'
).and.returnValue(of(fakeLocalPngAnswer));
fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
const inputDebugElement = fixture.debugElement.query(
By.css('#attach-file-local')
);
inputDebugElement.triggerEventHandler('change', {
target: { files: [fakeLocalPngAnswer] }
});
fixture.detectChanges();
expect(element.querySelector('#file-1155-icon')).not.toBeNull();
});
}));
it('should display file list when field has value', async(() => {
widget.field = new FormFieldModel(new FormModel(), {
type: FormFieldTypes.UPLOAD,
value: [fakePngAnswer]
});
widget.field.id = 'attach-file-attach';
widget.field.params = <FormFieldMetadata> onlyLocalParams;
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(element.querySelector('#file-1155-icon')).not.toBeNull();
});
}));
describe('when a file is uploaded', () => {
beforeEach(async(() => {
widget.field = new FormFieldModel(new FormModel(), {
type: FormFieldTypes.UPLOAD,
value: []
});
widget.field.id = 'attach-file-attach';
widget.field.params = <FormFieldMetadata> onlyLocalParams;
spyOn(
processCloudContentService,
'createTemporaryRawRelatedContent'
).and.returnValue(of(fakeLocalPngAnswer));
fixture.detectChanges();
fixture.whenStable().then(() => {
const inputDebugElement = fixture.debugElement.query(
By.css('#attach-file-attach')
);
inputDebugElement.triggerEventHandler('change', {
target: { files: [fakeLocalPngAnswer] }
});
fixture.detectChanges();
expect(element.querySelector('#file-1155-icon')).not.toBeNull();
});
}));
it('should remove file when remove is clicked', (done) => {
fixture.detectChanges();
const menuButton: HTMLButtonElement = <HTMLButtonElement> (
fixture.debugElement.query(By.css('#file-1155-option-menu'))
.nativeElement
);
menuButton.click();
fixture.detectChanges();
const removeOption: HTMLButtonElement = <HTMLButtonElement> (
fixture.debugElement.query(By.css('#file-1155-remove'))
.nativeElement
);
removeOption.click();
fixture.detectChanges();
fixture.whenRenderingDone().then(() => {
expect(element.querySelector('#file-1155-icon')).toBeNull();
done();
});
});
it('should download file when download is clicked', (done) => {
spyOn(processCloudContentService, 'getRawContentNode').and.returnValue(of(new Blob()));
spyOn(processCloudContentService, 'downloadNodeContent').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();
done();
});
});
it('should preview file when show is clicked', (done) => {
spyOn(processCloudContentService, 'getRawContentNode').and.returnValue(of(new Blob()));
formService.formContentClicked.subscribe(
(fileClicked: any) => {
expect(fileClicked.nodeId).toBe(1155);
done();
}
);
fixture.detectChanges();
const menuButton: HTMLButtonElement = <HTMLButtonElement> (
fixture.debugElement.query(
By.css('#file-1155-option-menu')
).nativeElement
);
menuButton.click();
fixture.detectChanges();
const showOption: HTMLButtonElement = <HTMLButtonElement> (
fixture.debugElement.query(
By.css('#file-1155-show-file')
).nativeElement
);
showOption.click();
});
});
}); });

View File

@@ -22,7 +22,7 @@ import {
FormService, FormService,
LogService, LogService,
ThumbnailService, ThumbnailService,
ProcessContentService ContentLinkModel
} from '@alfresco/adf-core'; } from '@alfresco/adf-core';
import { RelatedContentRepresentation } from '@alfresco/js-api'; import { RelatedContentRepresentation } from '@alfresco/js-api';
import { ContentCloudNodeSelectorService } from '../../services/content-cloud-node-selector.service'; import { ContentCloudNodeSelectorService } from '../../services/content-cloud-node-selector.service';
@@ -30,99 +30,137 @@ import { ProcessCloudContentService } from '../../services/process-cloud-content
import { UploadCloudWidgetComponent } from '../upload-cloud.widget'; import { UploadCloudWidgetComponent } from '../upload-cloud.widget';
@Component({ @Component({
selector: 'adf-cloud-attach-file-cloud-widget', selector: 'adf-cloud-attach-file-cloud-widget',
templateUrl: './attach-file-cloud-widget.component.html', templateUrl: './attach-file-cloud-widget.component.html',
styleUrls: ['./attach-file-cloud-widget.component.scss'], styleUrls: ['./attach-file-cloud-widget.component.scss'],
host: { host: {
'(click)': 'event($event)', '(click)': 'event($event)',
'(blur)': 'event($event)', '(blur)': 'event($event)',
'(change)': 'event($event)', '(change)': 'event($event)',
'(focus)': 'event($event)', '(focus)': 'event($event)',
'(focusin)': 'event($event)', '(focusin)': 'event($event)',
'(focusout)': 'event($event)', '(focusout)': 'event($event)',
'(input)': 'event($event)', '(input)': 'event($event)',
'(invalid)': 'event($event)', '(invalid)': 'event($event)',
'(select)': 'event($event)' '(select)': 'event($event)'
}, },
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
}) })
export class AttachFileCloudWidgetComponent extends UploadCloudWidgetComponent implements OnInit { export class AttachFileCloudWidgetComponent extends UploadCloudWidgetComponent
implements OnInit {
static ACS_SERVICE = 'alfresco-content';
static ACS_SERVICE = 'alfresco-content'; constructor(
public formService: FormService,
constructor( public logger: LogService,
public formService: FormService, public thumbnails: ThumbnailService,
public logger: LogService, public processCloudContentService: ProcessCloudContentService,
public processContentService: ProcessContentService, public contentNodeSelectorService: ContentCloudNodeSelectorService
public thumbnails: ThumbnailService, ) {
public processCloudContentService: ProcessCloudContentService, super(formService, thumbnails, processCloudContentService, logger);
public contentNodeSelectorService: ContentCloudNodeSelectorService) {
super(formService, thumbnails, processCloudContentService, logger);
}
ngOnInit() {
if (this.field &&
this.field.value &&
this.field.value.length > 0) {
this.hasFile = true;
} }
this.getMultipleFileParam();
}
isFileSourceConfigured(): boolean { ngOnInit() {
return !!this.field.params && !!this.field.params.fileSource; if (this.field && this.field.value && this.field.value.length > 0) {
} this.hasFile = true;
}
this.getMultipleFileParam();
}
isMultipleSourceUpload(): boolean { isFileSourceConfigured(): boolean {
return !this.field.readOnly && this.isFileSourceConfigured() && !this.isOnlyLocalSourceSelected(); return !!this.field.params && !!this.field.params.fileSource;
} }
isOnlyLocalSourceSelected(): boolean { isMultipleSourceUpload(): boolean {
return this.field.params && return (
this.field.params.fileSource && !this.field.readOnly &&
this.field.params.fileSource.serviceId === 'local-file'; this.isFileSourceConfigured() &&
} !this.isOnlyLocalSourceSelected()
);
}
isSimpleUploadButton(): boolean { isOnlyLocalSourceSelected(): boolean {
return this.isUploadButtonVisible() && return (
!this.isFileSourceConfigured() || this.field.params &&
this.isOnlyLocalSourceSelected(); this.field.params.fileSource &&
} this.field.params.fileSource.serviceId === 'local-file'
);
}
isUploadButtonVisible(): boolean { isSimpleUploadButton(): boolean {
return (!this.hasFile || this.multipleOption) && !this.field.readOnly; return (
} (this.isUploadButtonVisible() && !this.isFileSourceConfigured()) ||
this.isOnlyLocalSourceSelected()
);
}
onAttachFileChanged(event: any) { isUploadButtonVisible(): boolean {
this.onFileChanged(event); return (!this.hasFile || this.multipleOption) && !this.field.readOnly;
} }
onRemoveAttachFile(file: File | RelatedContentRepresentation) { onAttachFileChanged(event: any) {
this.removeFile(file); this.onFileChanged(event);
} }
uploadFileFromCS() { onRemoveAttachFile(file: File | RelatedContentRepresentation) {
this.openSelectDialog(); this.removeFile(file);
} }
openSelectDialog() { uploadFileFromCS() {
const filesSaved = []; this.openSelectDialog();
this.contentNodeSelectorService.openUploadFileDialog(this.field.form.contentHost).subscribe((selections: any[]) => { }
selections.forEach((node) => node.isExternal = true);
const result = {
nodeId: selections[0].id,
name: selections[0].name,
content: selections[0].content,
createdAt: selections[0].createdAt
};
filesSaved.push(result);
this.fixIncompatibilityFromPreviousAndNewForm(filesSaved);
});
}
isContentSourceSelected(): boolean { openSelectDialog() {
return this.field.params && const filesSaved = [];
this.field.params.fileSource && this.contentNodeSelectorService
this.field.params.fileSource.serviceId === AttachFileCloudWidgetComponent.ACS_SERVICE; .openUploadFileDialog(this.field.form.contentHost)
} .subscribe((selections: any[]) => {
selections.forEach(node => (node.isExternal = true));
const result = {
nodeId: selections[0].id,
name: selections[0].name,
content: selections[0].content,
createdAt: selections[0].createdAt
};
filesSaved.push(result);
this.fixIncompatibilityFromPreviousAndNewForm(filesSaved);
});
}
isContentSourceSelected(): boolean {
return (
this.field.params &&
this.field.params.fileSource &&
this.field.params.fileSource.serviceId ===
AttachFileCloudWidgetComponent.ACS_SERVICE
);
}
downloadContent(file: any): void {
this.processCloudContentService
.getRawContentNode(file.nodeId, this.field.form.contentHost)
.subscribe(
(blob: Blob) => {
this.processCloudContentService.downloadNodeContent(
blob,
file.name
);
},
() => {
this.logger.error(
'Impossible retrieve content for download'
);
}
);
}
onAttachFileClicked(file: ContentLinkModel) {
this.processCloudContentService
.getRawContentNode(file.nodeId, this.field.form.contentHost)
.subscribe(
(blob: Blob) => {
file.contentBlob = blob;
this.fileClicked(file);
});
}
} }

View File

@@ -236,7 +236,7 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges,
await this.getFormByTaskId(appName, taskId); await this.getFormByTaskId(appName, taskId);
const hasUploadWidget = (<any> this.form).hasUpload; const hasUploadWidget = (<any> this.form).hasUpload;
if (hasUploadWidget && !this.readOnly) { if (hasUploadWidget) {
try { try {
const processStorageCloudModel = await this.formCloudService.getProcessStorageFolderTask(appName, taskId, processInstanceId).toPromise(); const processStorageCloudModel = await this.formCloudService.getProcessStorageFolderTask(appName, taskId, processInstanceId).toPromise();
this.form.nodeId = processStorageCloudModel.nodeId; this.form.nodeId = processStorageCloudModel.nodeId;

View File

@@ -20,7 +20,7 @@
import { Component, ElementRef, OnInit, ViewChild, ViewEncapsulation } from '@angular/core'; import { Component, ElementRef, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
import { Observable, from } from 'rxjs'; import { Observable, from } from 'rxjs';
import { mergeMap, map, catchError } from 'rxjs/operators'; import { mergeMap, map, catchError } from 'rxjs/operators';
import { WidgetComponent, baseHost, LogService, FormService, ThumbnailService } from '@alfresco/adf-core'; import { WidgetComponent, baseHost, LogService, FormService, ThumbnailService, ContentLinkModel } from '@alfresco/adf-core';
import { ProcessCloudContentService } from '../services/process-cloud-content.service'; import { ProcessCloudContentService } from '../services/process-cloud-content.service';
@Component({ @Component({
@@ -122,7 +122,8 @@ export class UploadCloudWidgetComponent extends WidgetComponent implements OnIni
} }
private removeElementFromList(file) { private removeElementFromList(file) {
const savedValues = this.field.form.values[this.field.id]; const savedValues = this.field.form.values[this.field.id]
? this.field.form.values[this.field.id] : this.field.value;
const index = savedValues.indexOf(file); const index = savedValues.indexOf(file);
if (index !== -1) { if (index !== -1) {
const filteredValues = savedValues.filter((value: any) => value.nodeId !== file.nodeId); const filteredValues = savedValues.filter((value: any) => value.nodeId !== file.nodeId);
@@ -142,7 +143,7 @@ export class UploadCloudWidgetComponent extends WidgetComponent implements OnIni
} }
} }
fileClicked(nodeId: any): void { fileClicked(file: ContentLinkModel): void {
this.formService.formContentClicked.next(nodeId); this.formService.formContentClicked.next(file);
} }
} }

View File

@@ -18,7 +18,7 @@
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 } from '@alfresco/adf-core'; import { AlfrescoApiService, LogService, ContentService } from '@alfresco/adf-core';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
@@ -27,11 +27,11 @@ export class ProcessCloudContentService {
constructor( constructor(
private apiService: AlfrescoApiService, private apiService: AlfrescoApiService,
private logService: LogService private logService: LogService,
public contentService: ContentService
) { } ) { }
createTemporaryRawRelatedContent(file, nodeId, contentHost): Observable<any> { createTemporaryRawRelatedContent(file, nodeId, contentHost): Observable<any> {
const changedConfig = this.apiService.lastConfig; const changedConfig = this.apiService.lastConfig;
changedConfig.provider = 'ALL'; changedConfig.provider = 'ALL';
changedConfig.hostEcm = contentHost.replace('/alfresco', ''); changedConfig.hostEcm = contentHost.replace('/alfresco', '');
@@ -45,6 +45,18 @@ 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);
return this.contentService.getNodeContent(nodeId);
}
downloadNodeContent(blob: Blob, fileName: string): void {
this.contentService.downloadBlob(blob, fileName);
}
private handleError(error: any) { private handleError(error: any) {
this.logService.error(error); this.logService.error(error);
return throwError(error || 'Server error'); return throwError(error || 'Server error');

View File

@@ -10,7 +10,8 @@
[showSaveButton]="canCompleteTask()" [showSaveButton]="canCompleteTask()"
(formSaved)="onFormSaved($event)" (formSaved)="onFormSaved($event)"
(formCompleted)="onFormCompleted($event)" (formCompleted)="onFormCompleted($event)"
(formError)="onError($event)"> (formError)="onError($event)"
(formContentClicked)="onFormContentClicked($event)">
<adf-cloud-form-custom-outcomes> <adf-cloud-form-custom-outcomes>
<ng-template [ngTemplateOutlet]="taskFormCloudButtons"> <ng-template [ngTemplateOutlet]="taskFormCloudButtons">
</ng-template> </ng-template>

View File

@@ -22,7 +22,7 @@ import {
import { FormCloud } from '../../../form/models/form-cloud.model'; import { FormCloud } from '../../../form/models/form-cloud.model';
import { TaskDetailsCloudModel } from '../../start-task/models/task-details-cloud.model'; import { TaskDetailsCloudModel } from '../../start-task/models/task-details-cloud.model';
import { TaskCloudService } from '../../services/task-cloud.service'; import { TaskCloudService } from '../../services/task-cloud.service';
import { FormRenderingService } from '@alfresco/adf-core'; import { FormRenderingService, ContentLinkModel } from '@alfresco/adf-core';
import { AttachFileCloudWidgetComponent } from '../../../form/components/attach-file-cloud-widget/attach-file-cloud-widget.component'; import { AttachFileCloudWidgetComponent } from '../../../form/components/attach-file-cloud-widget/attach-file-cloud-widget.component';
import { DropdownCloudWidgetComponent } from '../../../form/components/dropdown-cloud/dropdown-cloud.widget'; import { DropdownCloudWidgetComponent } from '../../../form/components/dropdown-cloud/dropdown-cloud.widget';
import { DateCloudWidgetComponent } from '../../../form/components/date-cloud/date-cloud.widget'; import { DateCloudWidgetComponent } from '../../../form/components/date-cloud/date-cloud.widget';
@@ -90,6 +90,9 @@ export class TaskFormCloudComponent implements OnChanges {
@Output() @Output()
error: EventEmitter<any> = new EventEmitter<any>(); error: EventEmitter<any> = new EventEmitter<any>();
@Output()
formContentClicked: EventEmitter<ContentLinkModel> = new EventEmitter();
taskDetails: TaskDetailsCloudModel; taskDetails: TaskDetailsCloudModel;
loading: boolean = false; loading: boolean = false;
@@ -180,4 +183,8 @@ export class TaskFormCloudComponent implements OnChanges {
onError(data: any) { onError(data: any) {
this.error.emit(data); this.error.emit(data);
} }
onFormContentClicked(content: ContentLinkModel) {
this.formContentClicked.emit(content);
}
} }

View File

@@ -23,7 +23,6 @@ import { BrowserActions } from '../../core/utils/browser-actions';
export class DocumentListPage { export class DocumentListPage {
rootElement: ElementFinder; rootElement: ElementFinder;
actionMenu: ElementFinder = element(by.css('div[role="menu"]'));
optionButton: Locator = by.css('button[data-automation-id*="action_menu_"]'); optionButton: Locator = by.css('button[data-automation-id*="action_menu_"]');
tableBody: ElementFinder; tableBody: ElementFinder;
dataTable: DataTableComponentPage; dataTable: DataTableComponentPage;
@@ -66,12 +65,12 @@ export class DocumentListPage {
await BrowserActions.closeMenuAndDialogs(); await BrowserActions.closeMenuAndDialogs();
const row: ElementFinder = this.dataTable.getRow('Display name', content); const row: ElementFinder = this.dataTable.getRow('Display name', content);
await BrowserActions.click(row.element(this.optionButton)); await BrowserActions.click(row.element(this.optionButton));
await BrowserVisibility.waitUntilElementIsVisible(this.actionMenu); await BrowserActions.waitUntilActionMenuIsVisible();
await browser.sleep(500); await browser.sleep(500);
} }
async checkActionMenuIsNotDisplayed(): Promise<void> { async checkActionMenuIsNotDisplayed(): Promise<void> {
await BrowserVisibility.waitUntilElementIsNotVisible(this.actionMenu); await BrowserActions.waitUntilActionMenuIsNotVisible();
} }
dataTablePage(): DataTableComponentPage { dataTablePage(): DataTableComponentPage {

View File

@@ -76,8 +76,12 @@ export class AttachFileWidgetCloud {
async removeFile(fileName: string): Promise<void> { async removeFile(fileName: string): Promise<void> {
const fileId = await this.getFileId(fileName); const fileId = await this.getFileId(fileName);
const deleteButton = this.widget.element(by.css(`button[id='${fileId}-remove']`)); const optionMenu = this.widget.element(by.css(`button[id='${fileId}-option-menu']`));
await BrowserActions.click(optionMenu);
await BrowserActions.waitUntilActionMenuIsVisible();
const deleteButton = element(by.css(`button#${fileId}-remove`));
await BrowserActions.click(deleteButton); await BrowserActions.click(deleteButton);
await BrowserVisibility.waitUntilElementIsNotVisible(deleteButton);
} }
async viewFile(name): Promise<void> { async viewFile(name): Promise<void> {

View File

@@ -26,6 +26,16 @@ export class BrowserActions {
await elementFinder.click(); await elementFinder.click();
} }
static async waitUntilActionMenuIsVisible(): Promise<void> {
const actionMenu = element(by.css('div[role="menu"]'));
await BrowserVisibility.waitUntilElementIsVisible(actionMenu);
}
static async waitUntilActionMenuIsNotVisible(): Promise<void> {
const actionMenu = element(by.css('div[role="menu"]'));
await BrowserVisibility.waitUntilElementIsNotVisible(actionMenu);
}
static async getUrl(url: string): Promise<any> { static async getUrl(url: string): Promise<any> {
return browser.get(url); return browser.get(url);
} }