[ADF-4340] FormCloud - Be able to upload a file from the local source (#4647)

* Add a shiny upload button

* Fix tslint

* Enable the viewer on the content click

* Call the process storage in case the form has an upload

* Fix the lint

* Fix unit tests

* Fix tslint on unit tests

* Fix the lint

* Fix the lint
This commit is contained in:
Maurizio Vitale 2019-04-25 16:23:07 +02:00 committed by Eugenio Romano
parent b87c18bc13
commit be49e72208
15 changed files with 290 additions and 97 deletions

View File

@ -72,6 +72,7 @@ import { AppsCloudDemoComponent } from './components/cloud/apps-cloud-demo.compo
import { TasksCloudDemoComponent } from './components/cloud/tasks-cloud-demo.component'; import { TasksCloudDemoComponent } from './components/cloud/tasks-cloud-demo.component';
import { ProcessesCloudDemoComponent } from './components/cloud/processes-cloud-demo.component'; import { ProcessesCloudDemoComponent } from './components/cloud/processes-cloud-demo.component';
import { TaskDetailsCloudDemoComponent } from './components/cloud/task-details-cloud-demo.component'; import { TaskDetailsCloudDemoComponent } from './components/cloud/task-details-cloud-demo.component';
import { CloudViewerComponent } from './components/cloud/cloud-viewer.component';
import { ProcessDetailsCloudDemoComponent } from './components/cloud/process-details-cloud-demo.component'; import { ProcessDetailsCloudDemoComponent } from './components/cloud/process-details-cloud-demo.component';
import { StartTaskCloudDemoComponent } from './components/cloud/start-task-cloud-demo.component'; import { StartTaskCloudDemoComponent } from './components/cloud/start-task-cloud-demo.component';
import { StartProcessCloudDemoComponent } from './components/cloud/start-process-cloud-demo.component'; import { StartProcessCloudDemoComponent } from './components/cloud/start-process-cloud-demo.component';
@ -139,6 +140,7 @@ import { FormCloudDemoComponent } from './components/app-layout/cloud/form-demo/
TasksCloudDemoComponent, TasksCloudDemoComponent,
ProcessesCloudDemoComponent, ProcessesCloudDemoComponent,
TaskDetailsCloudDemoComponent, TaskDetailsCloudDemoComponent,
CloudViewerComponent,
ProcessDetailsCloudDemoComponent, ProcessDetailsCloudDemoComponent,
StartTaskCloudDemoComponent, StartTaskCloudDemoComponent,
StartProcessCloudDemoComponent, StartProcessCloudDemoComponent,

View File

@ -47,6 +47,7 @@ import { ProcessesCloudDemoComponent } from './components/cloud/processes-cloud-
import { StartTaskCloudDemoComponent } from './components/cloud/start-task-cloud-demo.component'; import { StartTaskCloudDemoComponent } from './components/cloud/start-task-cloud-demo.component';
import { StartProcessCloudDemoComponent } from './components/cloud/start-process-cloud-demo.component'; import { StartProcessCloudDemoComponent } from './components/cloud/start-process-cloud-demo.component';
import { TaskDetailsCloudDemoComponent } from './components/cloud/task-details-cloud-demo.component'; import { TaskDetailsCloudDemoComponent } from './components/cloud/task-details-cloud-demo.component';
import { CloudViewerComponent } from './components/cloud/cloud-viewer.component';
import { ProcessDetailsCloudDemoComponent } from './components/cloud/process-details-cloud-demo.component'; import { ProcessDetailsCloudDemoComponent } from './components/cloud/process-details-cloud-demo.component';
import { TemplateDemoComponent } from './components/template-list/template-demo.component'; import { TemplateDemoComponent } from './components/template-list/template-demo.component';
import { FormCloudDemoComponent } from './components/app-layout/cloud/form-demo/cloud-form-demo.component'; import { FormCloudDemoComponent } from './components/app-layout/cloud/form-demo/cloud-form-demo.component';
@ -152,6 +153,7 @@ export const appRoutes: Routes = [
path: 'cloud', path: 'cloud',
canActivate: [AuthGuardSsoRoleService], canActivate: [AuthGuardSsoRoleService],
data: { roles: ['ACTIVITI_USER'], redirectUrl: '/error/403'}, data: { roles: ['ACTIVITI_USER'], redirectUrl: '/error/403'},
children: [ children: [
{ {
path: '', path: '',
@ -192,6 +194,10 @@ export const appRoutes: Routes = [
path: 'task-details/:taskId', path: 'task-details/:taskId',
component: TaskDetailsCloudDemoComponent component: TaskDetailsCloudDemoComponent
}, },
{
path: 'task-details/:taskId/files/:nodeId/view',
component: CloudViewerComponent
},
{ {
path: 'process-details/:processInstanceId', path: 'process-details/:processInstanceId',
component: ProcessDetailsCloudDemoComponent component: ProcessDetailsCloudDemoComponent

View File

@ -0,0 +1,3 @@
.activiti-form-viewer {
margin: 10px;
}

View File

@ -0,0 +1,4 @@
<adf-viewer
[overlayMode]="true"
[nodeId]="nodeId">
</adf-viewer>

View File

@ -0,0 +1,46 @@
/*!
* @license
* Copyright 2019 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 { Component, OnDestroy, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Subscription } from 'rxjs';
import { Params } from '@angular/router/src/shared';
@Component({
selector: 'app-cloud-viewer',
templateUrl: './cloud-viewer.component.html'
})
export class CloudViewerComponent implements OnInit, OnDestroy {
nodeId: string;
private sub: Subscription;
constructor(private route: ActivatedRoute) {
}
ngOnInit() {
this.sub = this.route.params.subscribe((params: Params) => {
this.nodeId = params['nodeId'];
});
}
ngOnDestroy() {
this.sub.unsubscribe();
}
}

View File

@ -69,6 +69,10 @@ export class TaskDetailsCloudDemoComponent {
this.goBack(); this.goBack();
} }
onFormContentClicked(resourceId) {
this.router.navigate([`/cloud/${this.appName}/task-details/${this.taskId}/files/${resourceId.nodeId}/view`]);
}
onFormSaved() { onFormSaved() {
this.notificationService.openSnackMessage('Task has been saved successfully'); this.notificationService.openSnackMessage('Task has been saved successfully');
} }

View File

@ -374,10 +374,11 @@ export class FormFieldModel extends FormWidgetModel {
} }
break; break;
case FormFieldTypes.UPLOAD: case FormFieldTypes.UPLOAD:
this.form.hasUpload = true;
if (this.value && this.value.length > 0) { if (this.value && this.value.length > 0) {
this.form.values[this.id] = this.value.map((elem) => elem.id).join(','); this.form.values[this.id] = this.value.map((elem) => elem.id).join(',');
} else { } else {
this.form.values[this.id] = null; this.form.values[this.id] = [];
} }
break; break;
case FormFieldTypes.TYPEAHEAD: case FormFieldTypes.TYPEAHEAD:

View File

@ -17,7 +17,7 @@
import { SimpleChange } from '@angular/core'; import { SimpleChange } from '@angular/core';
import { Observable, of, throwError } from 'rxjs'; import { Observable, of, throwError } from 'rxjs';
import { FormFieldModel, FormFieldTypes, FormOutcomeEvent, FormOutcomeModel, LogService, WidgetVisibilityService } from '@alfresco/adf-core'; import { FormFieldModel, FormFieldTypes, FormService, FormOutcomeEvent, FormOutcomeModel, LogService, WidgetVisibilityService } from '@alfresco/adf-core';
import { FormCloudService } from '../services/form-cloud.service'; import { FormCloudService } from '../services/form-cloud.service';
import { FormCloudComponent } from './form-cloud.component'; import { FormCloudComponent } from './form-cloud.component';
import { FormCloud } from '../models/form-cloud.model'; import { FormCloud } from '../models/form-cloud.model';
@ -25,7 +25,8 @@ import { cloudFormMock } from '../mocks/cloud-form.mock';
describe('FormCloudComponent', () => { describe('FormCloudComponent', () => {
let formService: FormCloudService; let formCloudService: FormCloudService;
let formService: FormService;
let formComponent: FormCloudComponent; let formComponent: FormCloudComponent;
let visibilityService: WidgetVisibilityService; let visibilityService: WidgetVisibilityService;
let logService: LogService; let logService: LogService;
@ -34,8 +35,9 @@ describe('FormCloudComponent', () => {
logService = new LogService(null); logService = new LogService(null);
visibilityService = new WidgetVisibilityService(null, logService); visibilityService = new WidgetVisibilityService(null, logService);
spyOn(visibilityService, 'refreshVisibility').and.stub(); spyOn(visibilityService, 'refreshVisibility').and.stub();
formService = new FormCloudService(null, null, logService); formCloudService = new FormCloudService(null, null, logService);
formComponent = new FormCloudComponent(formService, visibilityService); formService = new FormService(null, null, logService);
formComponent = new FormCloudComponent(formCloudService, formService, null, visibilityService);
}); });
it('should check form', () => { it('should check form', () => {
@ -144,15 +146,15 @@ describe('FormCloudComponent', () => {
}); });
it('should get task variables if a task form is rendered', () => { it('should get task variables if a task form is rendered', () => {
spyOn(formService, 'getTaskForm').and.callFake((currentTaskId) => { spyOn(formCloudService, 'getTaskForm').and.callFake((currentTaskId) => {
return new Observable((observer) => { return new Observable((observer) => {
observer.next({ formRepresentation: { taskId: currentTaskId }}); observer.next({ formRepresentation: { taskId: currentTaskId }});
observer.complete(); observer.complete();
}); });
}); });
spyOn(formService, 'getTaskVariables').and.returnValue(of({})); spyOn(formCloudService, 'getTaskVariables').and.returnValue(of({}));
spyOn(formService, 'getTask').and.callFake((currentTaskId) => { spyOn(formCloudService, 'getTask').and.callFake((currentTaskId) => {
return new Observable((observer) => { return new Observable((observer) => {
observer.next({ formRepresentation: { taskId: currentTaskId }}); observer.next({ formRepresentation: { taskId: currentTaskId }});
observer.complete(); observer.complete();
@ -165,25 +167,25 @@ describe('FormCloudComponent', () => {
formComponent.taskId = taskId; formComponent.taskId = taskId;
formComponent.loadForm(); formComponent.loadForm();
expect(formService.getTaskVariables).toHaveBeenCalledWith(appName, taskId); expect(formCloudService.getTaskVariables).toHaveBeenCalledWith(appName, taskId);
}); });
it('should not get task variables and form if task id is not specified', () => { it('should not get task variables and form if task id is not specified', () => {
spyOn(formService, 'getTaskForm').and.callFake((currentTaskId) => { spyOn(formCloudService, 'getTaskForm').and.callFake((currentTaskId) => {
return new Observable((observer) => { return new Observable((observer) => {
observer.next({ taskId: currentTaskId }); observer.next({ taskId: currentTaskId });
observer.complete(); observer.complete();
}); });
}); });
spyOn(formService, 'getTaskVariables').and.returnValue(of({})); spyOn(formCloudService, 'getTaskVariables').and.returnValue(of({}));
formComponent.appName = 'test-app'; formComponent.appName = 'test-app';
formComponent.taskId = null; formComponent.taskId = null;
formComponent.loadForm(); formComponent.loadForm();
expect(formService.getTaskForm).not.toHaveBeenCalled(); expect(formCloudService.getTaskForm).not.toHaveBeenCalled();
expect(formService.getTaskVariables).not.toHaveBeenCalled(); expect(formCloudService.getTaskVariables).not.toHaveBeenCalled();
}); });
it('should get form definition by form id on load', () => { it('should get form definition by form id on load', () => {
@ -200,7 +202,7 @@ describe('FormCloudComponent', () => {
}); });
it('should refresh visibility when the form is loaded', () => { it('should refresh visibility when the form is loaded', () => {
spyOn(formService, 'getForm').and.returnValue(of({formRepresentation: {formDefinition: {}}})); spyOn(formCloudService, 'getForm').and.returnValue(of({formRepresentation: {formDefinition: {}}}));
const formId = '123'; const formId = '123';
const appName = 'test-app'; const appName = 'test-app';
@ -208,7 +210,7 @@ describe('FormCloudComponent', () => {
formComponent.formId = formId; formComponent.formId = formId;
formComponent.loadForm(); formComponent.loadForm();
expect(formService.getForm).toHaveBeenCalledWith(appName, formId); expect(formCloudService.getForm).toHaveBeenCalledWith(appName, formId);
expect(visibilityService.refreshVisibility).toHaveBeenCalled(); expect(visibilityService.refreshVisibility).toHaveBeenCalled();
}); });
@ -376,12 +378,12 @@ describe('FormCloudComponent', () => {
const appName = 'test-app'; const appName = 'test-app';
const taskId = '456'; const taskId = '456';
spyOn(formService, 'getTask').and.returnValue(of({})); spyOn(formCloudService, 'getTask').and.returnValue(of({}));
spyOn(formService, 'getTaskVariables').and.returnValue(of({})); spyOn(formCloudService, 'getTaskVariables').and.returnValue(of({}));
spyOn(formService, 'getTaskForm').and.returnValue(of({formRepresentation: {taskId: taskId, formDefinition: {selectedOutcome: 'custom-outcome'}}})); spyOn(formCloudService, 'getTaskForm').and.returnValue(of({formRepresentation: {taskId: taskId, formDefinition: {selectedOutcome: 'custom-outcome'}}}));
formComponent.formLoaded.subscribe(() => { formComponent.formLoaded.subscribe(() => {
expect(formService.getTaskForm).toHaveBeenCalledWith(appName, taskId); expect(formCloudService.getTaskForm).toHaveBeenCalledWith(appName, taskId);
expect(formComponent.form).toBeDefined(); expect(formComponent.form).toBeDefined();
expect(formComponent.form.taskId).toBe(taskId); expect(formComponent.form.taskId).toBe(taskId);
done(); done();
@ -395,10 +397,10 @@ describe('FormCloudComponent', () => {
it('should handle error when getting form by task id', (done) => { it('should handle error when getting form by task id', (done) => {
const error = 'Some error'; const error = 'Some error';
spyOn(formService, 'getTask').and.returnValue(of({})); spyOn(formCloudService, 'getTask').and.returnValue(of({}));
spyOn(formService, 'getTaskVariables').and.returnValue(of({})); spyOn(formCloudService, 'getTaskVariables').and.returnValue(of({}));
spyOn(formComponent, 'handleError').and.stub(); spyOn(formComponent, 'handleError').and.stub();
spyOn(formService, 'getTaskForm').and.callFake(() => { spyOn(formCloudService, 'getTaskForm').and.callFake(() => {
return throwError(error); return throwError(error);
}); });
@ -409,7 +411,7 @@ describe('FormCloudComponent', () => {
}); });
it('should fetch and parse form definition by id', (done) => { it('should fetch and parse form definition by id', (done) => {
spyOn(formService, 'getForm').and.callFake((currentAppName, currentFormId) => { spyOn(formCloudService, 'getForm').and.callFake((currentAppName, currentFormId) => {
return new Observable((observer) => { return new Observable((observer) => {
observer.next({ formRepresentation: {id: currentFormId, formDefinition: {}}}); observer.next({ formRepresentation: {id: currentFormId, formDefinition: {}}});
observer.complete(); observer.complete();
@ -433,14 +435,14 @@ describe('FormCloudComponent', () => {
const error = 'Some error'; const error = 'Some error';
spyOn(formComponent, 'handleError').and.stub(); spyOn(formComponent, 'handleError').and.stub();
spyOn(formService, 'getForm').and.callFake(() => throwError(error)); spyOn(formCloudService, 'getForm').and.callFake(() => throwError(error));
formComponent.getFormById('test-app', '123'); formComponent.getFormById('test-app', '123');
expect(formComponent.handleError).toHaveBeenCalledWith(error); expect(formComponent.handleError).toHaveBeenCalledWith(error);
}); });
it('should save task form and raise corresponding event', () => { it('should save task form and raise corresponding event', () => {
spyOn(formService, 'saveTaskForm').and.callFake(() => { spyOn(formCloudService, 'saveTaskForm').and.callFake(() => {
return new Observable((observer) => { return new Observable((observer) => {
observer.next(); observer.next();
observer.complete(); observer.complete();
@ -475,14 +477,14 @@ describe('FormCloudComponent', () => {
formComponent.saveTaskForm(); formComponent.saveTaskForm();
expect(formService.saveTaskForm).toHaveBeenCalledWith(appName, formModel.taskId, formModel.id, formModel.values); expect(formCloudService.saveTaskForm).toHaveBeenCalledWith(appName, formModel.taskId, formModel.id, formModel.values);
expect(saved).toBeTruthy(); expect(saved).toBeTruthy();
expect(savedForm).toEqual(formModel); expect(savedForm).toEqual(formModel);
}); });
it('should handle error during form save', () => { it('should handle error during form save', () => {
const error = 'Error'; const error = 'Error';
spyOn(formService, 'saveTaskForm').and.callFake(() => throwError(error)); spyOn(formCloudService, 'saveTaskForm').and.callFake(() => throwError(error));
spyOn(formComponent, 'handleError').and.stub(); spyOn(formComponent, 'handleError').and.stub();
const taskId = '123-223'; const taskId = '123-223';
@ -509,7 +511,7 @@ describe('FormCloudComponent', () => {
}); });
it('should require form with appName and taskId to save', () => { it('should require form with appName and taskId to save', () => {
spyOn(formService, 'saveTaskForm').and.stub(); spyOn(formCloudService, 'saveTaskForm').and.stub();
formComponent.form = null; formComponent.form = null;
formComponent.saveTaskForm(); formComponent.saveTaskForm();
@ -523,11 +525,11 @@ describe('FormCloudComponent', () => {
formComponent.taskId = '123'; formComponent.taskId = '123';
formComponent.saveTaskForm(); formComponent.saveTaskForm();
expect(formService.saveTaskForm).not.toHaveBeenCalled(); expect(formCloudService.saveTaskForm).not.toHaveBeenCalled();
}); });
it('should require form with appName and taskId to complete', () => { it('should require form with appName and taskId to complete', () => {
spyOn(formService, 'completeTaskForm').and.stub(); spyOn(formCloudService, 'completeTaskForm').and.stub();
formComponent.form = null; formComponent.form = null;
formComponent.completeTaskForm('save'); formComponent.completeTaskForm('save');
@ -540,11 +542,11 @@ describe('FormCloudComponent', () => {
formComponent.taskId = '123'; formComponent.taskId = '123';
formComponent.completeTaskForm('complete'); formComponent.completeTaskForm('complete');
expect(formService.completeTaskForm).not.toHaveBeenCalled(); expect(formCloudService.completeTaskForm).not.toHaveBeenCalled();
}); });
it('should complete form and raise corresponding event', () => { it('should complete form and raise corresponding event', () => {
spyOn(formService, 'completeTaskForm').and.callFake(() => { spyOn(formCloudService, 'completeTaskForm').and.callFake(() => {
return new Observable((observer) => { return new Observable((observer) => {
observer.next(); observer.next();
observer.complete(); observer.complete();
@ -575,7 +577,7 @@ describe('FormCloudComponent', () => {
formComponent.appName = appName; formComponent.appName = appName;
formComponent.completeTaskForm(outcome); formComponent.completeTaskForm(outcome);
expect(formService.completeTaskForm).toHaveBeenCalledWith(appName, formModel.taskId, formModel.id, formModel.values, outcome); expect(formCloudService.completeTaskForm).toHaveBeenCalledWith(appName, formModel.taskId, formModel.id, formModel.values, outcome);
expect(completed).toBeTruthy(); expect(completed).toBeTruthy();
}); });
@ -714,7 +716,7 @@ describe('FormCloudComponent', () => {
formComponent.onOutcomeClicked(outcome); formComponent.onOutcomeClicked(outcome);
}); });
it('should refresh form values when data is changed', () => { it('should refresh form values when data is changed', (done) => {
formComponent.form = new FormCloud(JSON.parse(JSON.stringify(cloudFormMock))); formComponent.form = new FormCloud(JSON.parse(JSON.stringify(cloudFormMock)));
let formFields = formComponent.form.getFormFields(); let formFields = formComponent.form.getFormFields();
@ -723,17 +725,23 @@ describe('FormCloudComponent', () => {
expect(labelField.value).toBeNull(); expect(labelField.value).toBeNull();
expect(radioField.value).toBeUndefined(); expect(radioField.value).toBeUndefined();
const formValues: any[] = [{name: 'text1', value: 'test'}, {name: 'number1', value: 23}]; const formValues: any[] = [{name: 'text1', value: 'test'}, {name: 'number1', value: 99}];
const change = new SimpleChange(null, formValues, false); const change = new SimpleChange(null, formValues, false);
formComponent.data = formValues; formComponent.data = formValues;
formComponent.formLoaded.subscribe( (form) => {
formFields = form.getFormFields();
labelField = formFields.find((field) => field.id === 'text1');
radioField = formFields.find((field) => field.id === 'number1');
expect(labelField.value).toBe('test');
expect(radioField.value).toBe(99);
done();
});
formComponent.ngOnChanges({ 'data': change }); formComponent.ngOnChanges({ 'data': change });
formFields = formComponent.form.getFormFields();
labelField = formFields.find((field) => field.id === 'text1');
radioField = formFields.find((field) => field.id === 'number1');
expect(labelField.value).toBe('test');
expect(radioField.value).toBe(23);
}); });
it('should refresh radio buttons value when id is given to data', () => { it('should refresh radio buttons value when id is given to data', () => {

View File

@ -22,7 +22,7 @@ import {
import { Observable, of, forkJoin } from 'rxjs'; import { Observable, of, forkJoin } from 'rxjs';
import { switchMap } from 'rxjs/operators'; import { switchMap } from 'rxjs/operators';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { FormBaseComponent, FormFieldModel, FormOutcomeEvent, FormOutcomeModel, WidgetVisibilityService } from '@alfresco/adf-core'; import { FormBaseComponent, FormFieldModel, FormOutcomeEvent, FormOutcomeModel, WidgetVisibilityService, FormService, NotificationService } from '@alfresco/adf-core';
import { FormCloudService } from '../services/form-cloud.service'; import { FormCloudService } from '../services/form-cloud.service';
import { FormCloud } from '../models/form-cloud.model'; import { FormCloud } from '../models/form-cloud.model';
import { TaskVariableCloud } from '../models/task-variable-cloud.model'; import { TaskVariableCloud } from '../models/task-variable-cloud.model';
@ -81,12 +81,21 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges {
@Output() @Output()
formDataRefreshed: EventEmitter<FormCloud> = new EventEmitter<FormCloud>(); formDataRefreshed: EventEmitter<FormCloud> = new EventEmitter<FormCloud>();
@Output()
formContentClicked: EventEmitter<string> = new EventEmitter<string>();
protected subscriptions: Subscription[] = []; protected subscriptions: Subscription[] = [];
nodeId: string; nodeId: string;
constructor(protected formService: FormCloudService, constructor(protected formCloudService: FormCloudService,
protected formService: FormService,
private notificationService: NotificationService,
protected visibilityService: WidgetVisibilityService) { protected visibilityService: WidgetVisibilityService) {
super(); super();
this.formService.formContentClicked.subscribe((content: any) => {
this.formContentClicked.emit(content);
});
} }
ngOnChanges(changes: SimpleChanges) { ngOnChanges(changes: SimpleChanges) {
@ -136,10 +145,10 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges {
} }
findProcessVariablesByTaskId(appName: string, taskId: string): Observable<any> { findProcessVariablesByTaskId(appName: string, taskId: string): Observable<any> {
return this.formService.getTask(appName, taskId).pipe( return this.formCloudService.getTask(appName, taskId).pipe(
switchMap((task: any) => { switchMap((task: any) => {
if (this.isAProcessTask(task)) { if (this.isAProcessTask(task)) {
return this.formService.getTaskVariables(appName, taskId); return this.formCloudService.getTaskVariables(appName, taskId);
} else { } else {
return of({}); return of({});
} }
@ -153,8 +162,8 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges {
getFormByTaskId(appName, taskId: string): Promise<FormCloud> { getFormByTaskId(appName, taskId: string): Promise<FormCloud> {
return new Promise<FormCloud>((resolve, reject) => { return new Promise<FormCloud>((resolve, reject) => {
forkJoin(this.formService.getTaskForm(appName, taskId), forkJoin(this.formCloudService.getTaskForm(appName, taskId),
this.formService.getTaskVariables(appName, taskId)) this.formCloudService.getTaskVariables(appName, taskId))
.subscribe( .subscribe(
(data) => { (data) => {
this.data = data[1]; this.data = data[1];
@ -163,7 +172,6 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges {
this.visibilityService.refreshVisibility(<any> parsedForm); this.visibilityService.refreshVisibility(<any> parsedForm);
parsedForm.validateForm(); parsedForm.validateForm();
this.form = parsedForm; this.form = parsedForm;
this.form.nodeId = this.nodeId;
this.onFormLoaded(this.form); this.onFormLoaded(this.form);
resolve(this.form); resolve(this.form);
}, },
@ -176,17 +184,8 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges {
}); });
} }
async getFormDefinitionWithFolderTask(appName: string, taskId: string) {
await this.getFolderTask(appName, taskId);
await this.getFormByTaskId(appName, taskId);
}
async getFolderTask(appName: string, taskId: string) {
this.nodeId = await this.formService.getProcessStorageFolderTask(appName, taskId).toPromise();
}
getFormById(appName: string, formId: string) { getFormById(appName: string, formId: string) {
this.formService this.formCloudService
.getForm(appName, formId) .getForm(appName, formId)
.subscribe( .subscribe(
(form) => { (form) => {
@ -195,7 +194,6 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges {
this.visibilityService.refreshVisibility(<any> parsedForm); this.visibilityService.refreshVisibility(<any> parsedForm);
parsedForm.validateForm(); parsedForm.validateForm();
this.form = parsedForm; this.form = parsedForm;
this.form.nodeId = this.nodeId;
this.onFormLoaded(this.form); this.onFormLoaded(this.form);
}, },
(error) => { (error) => {
@ -204,9 +202,37 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges {
); );
} }
getFormDefinitionWithFolderTask(appName: string, taskId: string) {
this.getFormDefinitionWithFolderByTaskId(appName, taskId);
}
async getFormDefinitionWithFolderByTaskId(appName: string, taskId: string) {
try {
await this.getFormByTaskId(appName, taskId);
const hasUploadWidget = (<any> this.form).hasUpload;
if (hasUploadWidget) {
try {
await this.getFolderTask(appName, taskId);
this.form.nodeId = this.nodeId;
} catch (error) {
this.notificationService.openSnackMessage('The content repo is not configured');
}
}
} catch (error) {
this.notificationService.openSnackMessage('Form service an error occour');
}
}
async getFolderTask(appName: string, taskId: string) {
this.nodeId = await this.formCloudService.getProcessStorageFolderTask(appName, taskId).toPromise();
}
saveTaskForm() { saveTaskForm() {
if (this.form && this.appName && this.taskId) { if (this.form && this.appName && this.taskId) {
this.formService this.formCloudService
.saveTaskForm(this.appName, this.taskId, this.form.id, this.form.values) .saveTaskForm(this.appName, this.taskId, this.form.id, this.form.values)
.subscribe( .subscribe(
() => { () => {
@ -219,7 +245,7 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges {
completeTaskForm(outcome?: string) { completeTaskForm(outcome?: string) {
if (this.form && this.appName && this.taskId) { if (this.form && this.appName && this.taskId) {
this.formService this.formCloudService
.completeTaskForm(this.appName, this.taskId, this.form.id, this.form.values, outcome) .completeTaskForm(this.appName, this.taskId, this.form.id, this.form.values, outcome)
.subscribe( .subscribe(
() => { () => {
@ -232,7 +258,7 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges {
parseForm(json: any): FormCloud { parseForm(json: any): FormCloud {
if (json) { if (json) {
const form = new FormCloud(json, this.data, this.readOnly, this.formService); const form = new FormCloud(json, this.data, this.readOnly, this.formCloudService);
if (!json.formRepresentation.formDefinition || !json.formRepresentation.formDefinition.fields) { if (!json.formRepresentation.formDefinition || !json.formRepresentation.formDefinition.fields) {
form.outcomes = this.getFormDefinitionOutcomes(form); form.outcomes = this.getFormDefinitionOutcomes(form);
} }

View File

@ -2,13 +2,13 @@
[class.adf-invalid]="!field.isValid" [class.adf-invalid]="!field.isValid"
[class.adf-readonly]="field.readOnly"> [class.adf-readonly]="field.readOnly">
<label class="adf-label" [attr.for]="field.id">{{field.name}}<span *ngIf="isRequired()">*</span></label> <label class="adf-label" [attr.for]="field.id">{{field.name}}<span *ngIf="isRequired()">*</span></label>
<div class="adf-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 currentFiles">
<img mat-list-icon class="adf-upload-widget__icon" <img mat-list-icon class="adf-upload-widget__icon"
[id]="'file-'+file.id+'-icon'" [id]="'file-'+file.id+'-icon'"
[src]="getIcon(file.mimeType)" [src]="getIcon(file.content.mimeType)"
[alt]="mimeTypeIcon" [alt]="mimeTypeIcon"
(click)="fileClicked(file)" (click)="fileClicked(file)"
(keyup.enter)="fileClicked(file)" (keyup.enter)="fileClicked(file)"

View File

@ -0,0 +1,42 @@
.adf-cloud {
&-upload-widget-container {
margin-bottom: 15px;
input {
cursor: pointer;
height: 100%;
right: 0;
opacity: 0;
position: absolute;
top: 0;
width: 300px;
z-index: 4;
}
}
&-upload-widget {
width: 100%;
word-break: break-all;
padding: 0.4375em 0;
border-top: 0.84375em solid transparent;
}
&-upload-widget__icon {
padding: 6px;
float: left;
cursor: pointer;
}
&-upload-widget__reset {
margin-top: -2px;
}
&-upload-files-row {
.mat-line {
margin-bottom: 0;
}
}
}

View File

@ -19,8 +19,8 @@
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 } 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, ProcessContentService } from '@alfresco/adf-core';
import { FormCloudService } from '../services/form-cloud.service'; import { FormCloudService } from '../services/form-cloud.service';
@Component({ @Component({
@ -37,12 +37,15 @@ export class UploadCloudWidgetComponent extends WidgetComponent implements OnIni
multipleOption: string = ''; multipleOption: string = '';
mimeTypeIcon: string; mimeTypeIcon: string;
currentFiles = [];
@ViewChild('uploadFiles') @ViewChild('uploadFiles')
fileInput: ElementRef; fileInput: ElementRef;
constructor(public formService: FormService, constructor(public formService: FormService,
private thumbnailService: ThumbnailService, private thumbnailService: ThumbnailService,
private formCloudService: FormCloudService, private formCloudService: FormCloudService,
public processContentService: ProcessContentService,
private logService: LogService) { private logService: LogService) {
super(formService); super(formService);
} }
@ -52,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.currentFiles = [...this.field.value];
} }
this.getMultipleFileParam(); this.getMultipleFileParam();
} }
@ -64,26 +68,27 @@ export class UploadCloudWidgetComponent extends WidgetComponent implements OnIni
onFileChanged(event: any) { onFileChanged(event: any) {
const files = event.target.files; const files = event.target.files;
let filesSaved = [];
if (this.field.json.value) {
filesSaved = [...this.field.json.value];
}
if (files && files.length > 0) { if (files && files.length > 0) {
from(files) from(files)
.pipe(mergeMap((file) => this.uploadRawContent(file))) .pipe(mergeMap((file) => this.uploadRawContent(file)))
.subscribe( .subscribe(
(res) => filesSaved.push(res), (res) => {
this.currentFiles.push(res);
},
(error) => this.logService.error(`Error uploading file. See console output for more details. ${error}` ), (error) => this.logService.error(`Error uploading file. See console output for more details. ${error}` ),
() => { () => {
this.field.form.values[this.field.id] = filesSaved; this.fixIncompatibilityFromPreviousAndNewForm(this.currentFiles);
this.hasFile = true; this.hasFile = true;
} }
); );
} }
} }
fixIncompatibilityFromPreviousAndNewForm(filesSaved) {
this.field.form.values[this.field.id] = filesSaved;
}
getIcon(mimeType) { getIcon(mimeType) {
return this.thumbnailService.getMimeTypeIcon(mimeType); return this.thumbnailService.getMimeTypeIcon(mimeType);
} }
@ -93,11 +98,16 @@ export class UploadCloudWidgetComponent extends WidgetComponent implements OnIni
.pipe( .pipe(
map((response: any) => { map((response: any) => {
this.logService.info(response); this.logService.info(response);
return { nodeId : response.id}; 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() {
if (this.field && if (this.field &&
this.field.params && this.field.params &&
@ -107,29 +117,25 @@ export class UploadCloudWidgetComponent extends WidgetComponent implements OnIni
} }
private removeElementFromList(file) { private removeElementFromList(file) {
const index = this.field.value.indexOf(file); const index = this.currentFiles.indexOf(file);
// remove from content too
if (index !== -1) { if (index !== -1) {
this.field.value.splice(index, 1); this.currentFiles.splice(index, 1);
this.field.json.value = this.field.value; this.fixIncompatibilityFromPreviousAndNewForm(this.currentFiles);
this.field.updateForm();
} }
this.hasFile = this.field.value.length > 0; this.hasFile = this.currentFiles.length > 0;
this.resetFormValueWithNoFiles(); this.resetFormValueWithNoFiles();
} }
private resetFormValueWithNoFiles() { private resetFormValueWithNoFiles() {
if (this.field.value.length === 0) { if (this.currentFiles.length === 0) {
this.field.value = []; this.currentFiles = [];
this.field.json.value = [];
} }
} }
fileClicked(contentLinkModel: any): void { fileClicked(nodeId: any): void {
this.formService.formContentClicked.next(nodeId);
} }
} }

View File

@ -81,8 +81,9 @@ export class FormCloud {
this.fields = this.parseRootFields(json); this.fields = this.parseRootFields(json);
if (formData) { if (formData && formData.length > 0) {
this.loadData(formData); this.loadData(formData);
this.fixIncompatibilityFromPreviousAndNewForm(formData);
} }
for (let i = 0; i < this.fields.length; i++) { for (let i = 0; i < this.fields.length; i++) {
@ -123,6 +124,15 @@ export class FormCloud {
this.validateForm(); this.validateForm();
} }
fixIncompatibilityFromPreviousAndNewForm(formData) {
Object.keys(this.values).forEach( (propertyName) => {
const fieldValue = formData.find((value) => { return value.name === propertyName; });
if (fieldValue) {
this.values[propertyName] = fieldValue.value;
}
});
}
hasTabs(): boolean { hasTabs(): boolean {
return this.tabs && this.tabs.length > 0; return this.tabs && this.tabs.length > 0;
} }

View File

@ -94,7 +94,7 @@ describe('Form Cloud service', () => {
expect(result).toBeDefined(); expect(result).toBeDefined();
expect(result.id).toBe(responseBody.entry.id); expect(result.id).toBe(responseBody.entry.id);
expect(result.name).toBe(responseBody.entry.name); expect(result.name).toBe(responseBody.entry.name);
expect(oauth2Auth.callCustomApi.calls.mostRecent().args[0].endsWith(`${appName}/rb/v1/tasks/${taskId}`)).toBeTruthy(); expect(oauth2Auth.callCustomApi.calls.mostRecent().args[0].endsWith(`${appName}/query/v1/tasks/${taskId}`)).toBeTruthy();
expect(oauth2Auth.callCustomApi.calls.mostRecent().args[1]).toBe('GET'); expect(oauth2Auth.callCustomApi.calls.mostRecent().args[1]).toBe('GET');
done(); done();
}); });
@ -102,12 +102,47 @@ describe('Form Cloud service', () => {
}); });
it('should fetch task variables', (done) => { it('should fetch task variables', (done) => {
oauth2Auth.callCustomApi.and.returnValue(Promise.resolve({ content: { name: 'abc' } })); oauth2Auth.callCustomApi.and.returnValue(Promise.resolve({
'list': {
'entries': [
{
'entry': {
'serviceName': 'fake-rb',
'serviceFullName': 'fake-rb',
'serviceVersion': '',
'appName': 'fake',
'appVersion': '',
'serviceType': null,
'id': 25,
'type': 'string',
'name': 'fakeProperty',
'createTime': 1556112661342,
'lastUpdatedTime': 1556112661342,
'executionId': null,
'value': 'fakeValue',
'markedAsDeleted': false,
'processInstanceId': '18e16bc7-6694-11e9-9c1b-0a586460028a',
'taskId': '18e192da-6694-11e9-9c1b-0a586460028a',
'taskVariable': true
}
}
],
'pagination': {
'skipCount': 0,
'maxItems': 100,
'count': 1,
'hasMoreItems': false,
'totalItems': 1
}
}
}));
service.getTaskVariables(appName, taskId).subscribe((result: any) => { service.getTaskVariables(appName, taskId).subscribe((result) => {
expect(result).toBeDefined(); expect(result).toBeDefined();
expect(result.name).toBe('abc'); expect(result.length).toBe(1);
expect(oauth2Auth.callCustomApi.calls.mostRecent().args[0].endsWith(`${appName}/rb/v1/tasks/${taskId}/variables`)).toBeTruthy(); expect(result[0].name).toBe('fakeProperty');
expect(result[0].value).toBe('fakeValue');
expect(oauth2Auth.callCustomApi.calls.mostRecent().args[0].endsWith(`${appName}/query/v1/tasks/${taskId}/variables`)).toBeTruthy();
expect(oauth2Auth.callCustomApi.calls.mostRecent().args[1]).toBe('GET'); expect(oauth2Auth.callCustomApi.calls.mostRecent().args[1]).toBe('GET');
done(); done();
}); });

View File

@ -92,7 +92,7 @@ export class FormCloudService {
.getInstance() .getInstance()
.oauth2Auth.callCustomApi(apiUrl, 'POST', .oauth2Auth.callCustomApi(apiUrl, 'POST',
null, null, null, null, null, null,
{ filedata: file, nodeType: 'cm:content' }, null, { filedata: file, nodeType: 'cm:content', overwrite: true }, null,
['multipart/form-data'], this.accepts, ['multipart/form-data'], this.accepts,
this.returnType, null, null) this.returnType, null, null)
).pipe( ).pipe(
@ -191,7 +191,7 @@ export class FormCloudService {
this.returnType, null, null) this.returnType, null, null)
).pipe( ).pipe(
map((res: any) => { map((res: any) => {
return <TaskVariableCloud[]> res.content; return res.list.entries.map((variable) => new TaskVariableCloud(variable.entry));
}), }),
catchError((err) => this.handleError(err)) catchError((err) => this.handleError(err))
); );
@ -245,7 +245,7 @@ export class FormCloudService {
} }
private buildGetTaskUrl(appName: string, taskId: string): string { private buildGetTaskUrl(appName: string, taskId: string): string {
return `${this.appConfigService.get('bpmHost')}/${appName}/rb/v1/tasks/${taskId}`; return `${this.appConfigService.get('bpmHost')}/${appName}/query/v1/tasks/${taskId}`;
} }
private buildGetFormUrl(appName: string, formId: string): string { private buildGetFormUrl(appName: string, formId: string): string {
@ -265,7 +265,7 @@ export class FormCloudService {
} }
private buildGetTaskVariablesUrl(appName: string, taskId: string): string { private buildGetTaskVariablesUrl(appName: string, taskId: string): string {
return `${this.appConfigService.get('bpmHost')}/${appName}/rb/v1/tasks/${taskId}/variables`; return `${this.appConfigService.get('bpmHost')}/${appName}/query/v1/tasks/${taskId}/variables`;
} }
private buildFolderTask(appName: string, taskId: string): string { private buildFolderTask(appName: string, taskId: string): string {