From c290c472298a3aba34046a79505298b69dbf6db8 Mon Sep 17 00:00:00 2001 From: Denys Vuika Date: Fri, 10 Mar 2017 18:20:55 +0000 Subject: [PATCH] Date widget: support for `display date format` (#1710) * Date widget: support for `display date format` * unit tests update * pin js-api version * Fix Thumbnail preview (#1689) * Fix Thumbnail preview * Fix thumbnail unit test * Remove the fix prexif * Rollback the pin js api version * Update package.json * 1.2.1 tasklist,processlist,form --- demo-shell-ng2/package.json | 6 +- .../ng2-activiti-form/demo/package.json | 2 +- ng2-components/ng2-activiti-form/package.json | 2 +- .../activiti-content.component.html | 4 +- .../components/activiti-content.component.ts | 57 ++++++++++++++----- .../widgets/core/form-field-validator.ts | 6 +- .../widgets/core/form-field.model.ts | 8 ++- .../components/widgets/date/date.widget.html | 2 +- .../widgets/date/date.widget.spec.ts | 14 ++--- .../components/widgets/date/date.widget.ts | 12 ++-- .../src/services/form.service.spec.ts | 25 +++++++- .../src/services/form.service.ts | 4 +- .../demo/package.json | 4 +- .../ng2-activiti-processlist/package.json | 6 +- .../ng2-activiti-tasklist/demo/package.json | 2 +- .../ng2-activiti-tasklist/package.json | 4 +- 16 files changed, 103 insertions(+), 55 deletions(-) diff --git a/demo-shell-ng2/package.json b/demo-shell-ng2/package.json index ab63233e52..5daedb6d34 100644 --- a/demo-shell-ng2/package.json +++ b/demo-shell-ng2/package.json @@ -88,10 +88,10 @@ "ng2-alfresco-search": "1.2.0", "ng2-alfresco-upload": "1.2.0", "ng2-alfresco-viewer": "1.2.0", - "ng2-activiti-form": "1.2.0", - "ng2-activiti-tasklist": "1.2.0", + "ng2-activiti-form": "1.2.1", + "ng2-activiti-tasklist": "1.2.1", "ng2-alfresco-userinfo": "1.2.0", - "ng2-activiti-processlist": "1.2.0", + "ng2-activiti-processlist": "1.2.1", "ng2-alfresco-webscript": "1.2.0", "ng2-alfresco-tag": "1.2.0", "dialog-polyfill": "^0.4.3", diff --git a/ng2-components/ng2-activiti-form/demo/package.json b/ng2-components/ng2-activiti-form/demo/package.json index 20993bd4e6..d867bcaefc 100644 --- a/ng2-components/ng2-activiti-form/demo/package.json +++ b/ng2-components/ng2-activiti-form/demo/package.json @@ -58,7 +58,7 @@ "ng2-translate": "2.5.0", "alfresco-js-api": "~1.2.0", "ng2-alfresco-core": "1.2.0", - "ng2-activiti-form": "1.2.0" + "ng2-activiti-form": "1.2.1" }, "devDependencies": { "@types/jasmine": "^2.2.33", diff --git a/ng2-components/ng2-activiti-form/package.json b/ng2-components/ng2-activiti-form/package.json index 90eab8b153..3979b7cfb3 100644 --- a/ng2-components/ng2-activiti-form/package.json +++ b/ng2-components/ng2-activiti-form/package.json @@ -1,7 +1,7 @@ { "name": "ng2-activiti-form", "description": "Alfresco Activiti Form Component for Angular 2", - "version": "1.2.0", + "version": "1.2.1", "author": "Alfresco Software, Ltd.", "scripts": { "clean": "npm install rimraf && npm run clean-build && rimraf dist node_modules typings", diff --git a/ng2-components/ng2-activiti-form/src/components/activiti-content.component.html b/ng2-components/ng2-activiti-form/src/components/activiti-content.component.html index b47019dd21..72435d7454 100644 --- a/ng2-components/ng2-activiti-form/src/components/activiti-content.component.html +++ b/ng2-components/ng2-activiti-form/src/components/activiti-content.component.html @@ -14,9 +14,9 @@ - +
file_download - +
diff --git a/ng2-components/ng2-activiti-form/src/components/activiti-content.component.ts b/ng2-components/ng2-activiti-form/src/components/activiti-content.component.ts index 6055fcc37c..577ba69763 100644 --- a/ng2-components/ng2-activiti-form/src/components/activiti-content.component.ts +++ b/ng2-components/ng2-activiti-form/src/components/activiti-content.component.ts @@ -78,18 +78,26 @@ export class ActivitiContent implements OnChanges { } loadThumbnailUrl(content: ContentLinkModel) { - if (this.content.isTypeImage()) { - this.formService.getFileRawContent(content.id).subscribe( - (response: Blob) => { - this.content.thumbnailUrl = this.createUrlPreview(response); - }, - error => { - this.logService.error(error); - } - ); - } else if (this.content.isThumbnailSupported()) { - this.content.contentRawUrl = this.formService.getFileRawContentUrl(content.id); - this.content.thumbnailUrl = this.formService.getContentThumbnailUrl(content.id); + if (this.content.isThumbnailSupported()) { + if (this.content.isTypeImage()) { + this.formService.getFileRawContent(content.id).subscribe( + (response: Blob) => { + this.content.thumbnailUrl = this.createUrlPreview(response); + }, + error => { + this.logService.error(error); + } + ); + } else { + this.formService.getContentThumbnailUrl(content.id).subscribe( + (response: Blob) => { + this.content.thumbnailUrl = this.createUrlPreview(response); + }, + error => { + this.logService.error(error); + } + ); + } } } @@ -101,12 +109,31 @@ export class ActivitiContent implements OnChanges { /** * Download file opening it in a new window */ - download($event) { - $event.stopPropagation(); + download(content) { + this.formService.getFileRawContent(content.id).subscribe( + (response: Blob) => { + let thumbnailUrl = this.createUrlPreview(response); + this.createDownloadElement(thumbnailUrl, content.name); + }, + error => { + this.logService.error(error); + } + ); + } + + createDownloadElement(url: string, name: string) { + let downloadElement = window.document.createElement('a'); + downloadElement.setAttribute('id', 'export-download'); + downloadElement.setAttribute('href', url); + downloadElement.setAttribute('download', name); + downloadElement.setAttribute('target', '_blank'); + window.document.body.appendChild(downloadElement); + downloadElement.click(); + window.document.body.removeChild(downloadElement); } private sanitizeUrl(url: string) { - return this.sanitizer.bypassSecurityTrustUrl(url); + return this.sanitizer.bypassSecurityTrustResourceUrl(url); } private createUrlPreview(blob: Blob) { diff --git a/ng2-components/ng2-activiti-form/src/components/widgets/core/form-field-validator.ts b/ng2-components/ng2-activiti-form/src/components/widgets/core/form-field-validator.ts index 12f84889b4..561b171b3c 100644 --- a/ng2-components/ng2-activiti-form/src/components/widgets/core/form-field-validator.ts +++ b/ng2-components/ng2-activiti-form/src/components/widgets/core/form-field-validator.ts @@ -145,7 +145,7 @@ export class DateFieldValidator implements FormFieldValidator { validate(field: FormFieldModel): boolean { if (this.isSupported(field) && field.value) { - if (DateFieldValidator.isValidDate(field.value)) { + if (DateFieldValidator.isValidDate(field.value, field.dateDisplayFormat)) { return true; } field.validationSummary = 'Invalid date format'; @@ -168,7 +168,7 @@ export class MinDateFieldValidator implements FormFieldValidator { validate(field: FormFieldModel): boolean { if (this.isSupported(field) && field.value) { - const dateFormat = 'D-M-YYYY'; + const dateFormat = field.dateDisplayFormat; if (!DateFieldValidator.isValidDate(field.value, dateFormat)) { field.validationSummary = 'Invalid date format'; @@ -201,7 +201,7 @@ export class MaxDateFieldValidator implements FormFieldValidator { validate(field: FormFieldModel): boolean { if (this.isSupported(field) && field.value) { - const dateFormat = 'D-M-YYYY'; + const dateFormat = field.dateDisplayFormat; if (!DateFieldValidator.isValidDate(field.value, dateFormat)) { field.validationSummary = 'Invalid date format'; diff --git a/ng2-components/ng2-activiti-form/src/components/widgets/core/form-field.model.ts b/ng2-components/ng2-activiti-form/src/components/widgets/core/form-field.model.ts index dcaed466dc..85ba75b16a 100644 --- a/ng2-components/ng2-activiti-form/src/components/widgets/core/form-field.model.ts +++ b/ng2-components/ng2-activiti-form/src/components/widgets/core/form-field.model.ts @@ -44,6 +44,8 @@ export class FormFieldModel extends FormWidgetModel { private _readOnly: boolean = false; private _isValid: boolean = true; + readonly defaultDateFormat: string = 'D-M-YYYY'; + // model members fieldType: string; id: string; @@ -74,6 +76,7 @@ export class FormFieldModel extends FormWidgetModel { visibilityCondition: WidgetVisibilityModel = null; enableFractions: boolean = false; currency: string = null; + dateDisplayFormat: string = this.defaultDateFormat; // container model members numberOfColumns: number = 1; @@ -155,6 +158,7 @@ export class FormFieldModel extends FormWidgetModel { this.visibilityCondition = json.visibilityCondition; this.enableFractions = json.enableFractions; this.currency = json.currency; + this.dateDisplayFormat = json.dateDisplayFormat || this.defaultDateFormat; this._value = this.parseValue(json); if (json.placeholder && json.placeholder !== '' && json.placeholder !== 'null') { @@ -247,7 +251,7 @@ export class FormFieldModel extends FormWidgetModel { if (value) { let d = moment(value.split('T')[0], 'YYYY-M-D'); if (d.isValid()) { - value = d.format('D-M-YYYY'); + value = d.format(this.dateDisplayFormat); } } } @@ -303,7 +307,7 @@ export class FormFieldModel extends FormWidgetModel { } break; case FormFieldTypes.DATE: - let d = moment(this.value, 'D-M-YYYY'); + let d = moment(this.value, this.dateDisplayFormat); if (d.isValid()) { this.form.values[this.id] = `${d.format('YYYY-MM-DD')}T00:00:00.000Z`; } else { diff --git a/ng2-components/ng2-activiti-form/src/components/widgets/date/date.widget.html b/ng2-components/ng2-activiti-form/src/components/widgets/date/date.widget.html index 047eaef114..ef2151b0ee 100644 --- a/ng2-components/ng2-activiti-form/src/components/widgets/date/date.widget.html +++ b/ng2-components/ng2-activiti-form/src/components/widgets/date/date.widget.html @@ -2,7 +2,7 @@
- + { }); widget.ngOnInit(); - let expected = moment(minValue, widget.DATE_FORMAT); + let expected = moment(minValue, widget.field.dateDisplayFormat); expect(widget.datePicker._past.isSame(expected)).toBeTruthy(); }); @@ -65,7 +65,7 @@ describe('DateWidget', () => { }); widget.ngOnInit(); - let expected = moment(maxValue, widget.DATE_FORMAT); + let expected = moment(maxValue, widget.field.dateDisplayFormat); expect(widget.datePicker._future.isSame(expected)).toBeTruthy(); }); @@ -77,7 +77,7 @@ describe('DateWidget', () => { }); widget.ngOnInit(); - let expected = moment(dateValue, widget.DATE_FORMAT); + let expected = moment(dateValue, widget.field.dateDisplayFormat); expect(widget.datePicker.time.isSame(expected)).toBeTruthy(); }); @@ -115,7 +115,7 @@ describe('DateWidget', () => { widget.field.value = '31-03-1982'; widget.onDateChanged(); - let expected = moment('31-03-1982', widget.DATE_FORMAT); + let expected = moment('31-03-1982', widget.field.dateDisplayFormat); expect(widget.datePicker.time.isSame(expected)).toBeTruthy(); }); @@ -124,7 +124,7 @@ describe('DateWidget', () => { widget.ngOnInit(); let date = '13-3-1982'; - widget.datePicker.time = moment(date, widget.DATE_FORMAT); + widget.datePicker.time = moment(date, widget.field.dateDisplayFormat); widget.onDateSelected(); expect(widget.field.value).toBe(date); }); @@ -157,7 +157,7 @@ describe('DateWidget', () => { spyOn(w, 'setupMaterialTextField').and.callThrough(); w.field = new FormFieldModel(null, {value: '9-9-9999', type: 'date'}); w.ngOnInit(); - w.datePicker.time = moment('9-9-9999', w.DATE_FORMAT); + w.datePicker.time = moment('9-9-9999', w.field.dateDisplayFormat); w.fieldChanged.subscribe((field) => { expect(field).toBeDefined(); expect(field).not.toBeNull(); @@ -172,7 +172,7 @@ describe('DateWidget', () => { spyOn(w, 'setupMaterialTextField').and.callThrough(); w.field = new FormFieldModel(null, {value: '9-9-9999', type: 'date'}); w.ngOnInit(); - w.datePicker.time = moment('9-9-9999', w.DATE_FORMAT); + w.datePicker.time = moment('9-9-9999', w.field.dateDisplayFormat); w.fieldChanged.subscribe((field) => { expect(field).toBeDefined(); expect(field).not.toBeNull(); diff --git a/ng2-components/ng2-activiti-form/src/components/widgets/date/date.widget.ts b/ng2-components/ng2-activiti-form/src/components/widgets/date/date.widget.ts index 7a36b067b6..4f1b739c47 100644 --- a/ng2-components/ng2-activiti-form/src/components/widgets/date/date.widget.ts +++ b/ng2-components/ng2-activiti-form/src/components/widgets/date/date.widget.ts @@ -30,8 +30,6 @@ declare var componentHandler: any; }) export class DateWidget extends WidgetComponent implements OnInit, AfterViewChecked { - DATE_FORMAT: string = 'D-M-YYYY'; - datePicker: any; constructor(private elementRef: ElementRef) { @@ -49,15 +47,15 @@ export class DateWidget extends WidgetComponent implements OnInit, AfterViewChec if (this.field) { if (this.field.minValue) { - settings.past = moment(this.field.minValue, this.DATE_FORMAT); + settings.past = moment(this.field.minValue, this.field.dateDisplayFormat); } if (this.field.maxValue) { - settings.future = moment(this.field.maxValue, this.DATE_FORMAT); + settings.future = moment(this.field.maxValue, this.field.dateDisplayFormat); } if (this.field.value) { - settings.init = moment(this.field.value, this.DATE_FORMAT); + settings.init = moment(this.field.value, this.field.dateDisplayFormat); } } @@ -73,7 +71,7 @@ export class DateWidget extends WidgetComponent implements OnInit, AfterViewChec onDateChanged() { if (this.field.value) { - let value = moment(this.field.value, this.DATE_FORMAT); + let value = moment(this.field.value, this.field.dateDisplayFormat); if (!value.isValid()) { value = moment(); } @@ -83,7 +81,7 @@ export class DateWidget extends WidgetComponent implements OnInit, AfterViewChec } onDateSelected() { - let newValue = this.datePicker.time.format(this.DATE_FORMAT); + let newValue = this.datePicker.time.format(this.field.dateDisplayFormat); this.field.value = newValue; this.checkVisibility(this.field); diff --git a/ng2-components/ng2-activiti-form/src/services/form.service.spec.ts b/ng2-components/ng2-activiti-form/src/services/form.service.spec.ts index 1e042a453f..805ab8ea35 100644 --- a/ng2-components/ng2-activiti-form/src/services/form.service.spec.ts +++ b/ng2-components/ng2-activiti-form/src/services/form.service.spec.ts @@ -16,6 +16,7 @@ */ import { TestBed } from '@angular/core/testing'; +import { Observable } from 'rxjs/Rx'; import { CoreModule, AlfrescoApiService, LogService, LogServiceMock } from 'ng2-alfresco-core'; import { FormService } from './form.service'; import { EcmModelService } from './ecm-model.service'; @@ -32,6 +33,17 @@ describe('FormService', () => { let logService: LogService; let bpmCli: any; + function createFakeBlob() { + let data = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=='; + + let bytes = new Uint8Array(data.length / 2); + + for (let i = 0; i < data.length; i += 2) { + bytes[i / 2] = parseInt(data.substring(i, i + 2), /* base = */ 16); + } + return new Blob([bytes], {type: 'image/png'}); + } + beforeEach(() => { TestBed.configureTestingModule({ imports: [ @@ -444,11 +456,18 @@ describe('FormService', () => { expect(contentRawUrl).toEqual(`${bpmCli.basePath}/api/enterprise/content/${contentId}/raw`); }); - it('should return the thumbnail URL', () => { + it('should return a Blob as thumbnail', (done) => { let contentId: number = 999; - let contentRawUrl = service.getContentThumbnailUrl(contentId); - expect(contentRawUrl).toEqual(`${bpmCli.basePath}/app/rest/content/${contentId}/rendition/thumbnail`); + let blob = createFakeBlob(); + spyOn(service, 'getContentThumbnailUrl').and.returnValue(Observable.of(blob)); + + service.getContentThumbnailUrl(contentId).subscribe(result => { + expect(result).toEqual(jasmine.any(Blob)); + expect(result.size).toEqual(48); + expect(result.type).toEqual('image/png'); + done(); + }); }); it('should create a Form form a Node', (done) => { diff --git a/ng2-components/ng2-activiti-form/src/services/form.service.ts b/ng2-components/ng2-activiti-form/src/services/form.service.ts index a142f492ad..fb2a4a14db 100644 --- a/ng2-components/ng2-activiti-form/src/services/form.service.ts +++ b/ng2-components/ng2-activiti-form/src/services/form.service.ts @@ -267,9 +267,9 @@ export class FormService { return alfrescoApi.activiti.contentApi.getRawContentUrl(contentId); } - getContentThumbnailUrl(contentId: number): string { + getContentThumbnailUrl(contentId: number): Observable { let alfrescoApi = this.apiService.getInstance(); - return alfrescoApi.activiti.contentApi.getContentThumbnailUrl(contentId); + return Observable.fromPromise(alfrescoApi.activiti.contentApi.getContentThumbnailUrl(contentId)); } getRestFieldValues(taskId: string, field: string): Observable { diff --git a/ng2-components/ng2-activiti-processlist/demo/package.json b/ng2-components/ng2-activiti-processlist/demo/package.json index 5b20cb04c2..3f8f8eaf84 100644 --- a/ng2-components/ng2-activiti-processlist/demo/package.json +++ b/ng2-components/ng2-activiti-processlist/demo/package.json @@ -50,10 +50,10 @@ "md-date-time-picker": "^2.2.0", "ng2-translate": "2.5.0", "alfresco-js-api": "~1.2.0", - "ng2-activiti-tasklist": "1.2.0", + "ng2-activiti-tasklist": "1.2.1", "ng2-alfresco-core": "1.2.0", "ng2-alfresco-datatable": "1.2.0", - "ng2-activiti-processlist": "1.2.0" + "ng2-activiti-processlist": "1.2.1" }, "devDependencies": { "@types/jasmine": "^2.2.33", diff --git a/ng2-components/ng2-activiti-processlist/package.json b/ng2-components/ng2-activiti-processlist/package.json index 54452b9292..cb82b9571e 100644 --- a/ng2-components/ng2-activiti-processlist/package.json +++ b/ng2-components/ng2-activiti-processlist/package.json @@ -1,7 +1,7 @@ { "name": "ng2-activiti-processlist", "description": "Show active processes from the Activiti Process Services suite", - "version": "1.2.0", + "version": "1.2.1", "author": "Alfresco Software, Ltd.", "scripts": { "clean": "npm install rimraf && npm run clean-build && rimraf dist node_modules typings", @@ -63,8 +63,8 @@ "md-date-time-picker": "^2.2.0", "ng2-translate": "2.5.0", "alfresco-js-api": "~1.2.0", - "ng2-activiti-form": "1.2.0", - "ng2-activiti-tasklist": "1.2.0", + "ng2-activiti-form": "1.2.1", + "ng2-activiti-tasklist": "1.2.1", "ng2-alfresco-core": "1.2.0", "ng2-alfresco-datatable": "1.2.0" }, diff --git a/ng2-components/ng2-activiti-tasklist/demo/package.json b/ng2-components/ng2-activiti-tasklist/demo/package.json index ce3a735022..912bcc9528 100644 --- a/ng2-components/ng2-activiti-tasklist/demo/package.json +++ b/ng2-components/ng2-activiti-tasklist/demo/package.json @@ -46,7 +46,7 @@ "alfresco-js-api": "~1.2.0", "ng2-alfresco-core": "1.2.0", "ng2-alfresco-datatable": "1.2.0", - "ng2-activiti-tasklist": "1.2.0" + "ng2-activiti-tasklist": "1.2.1" }, "devDependencies": { "@types/jasmine": "^2.2.33", diff --git a/ng2-components/ng2-activiti-tasklist/package.json b/ng2-components/ng2-activiti-tasklist/package.json index daa25209d3..de9d14e536 100644 --- a/ng2-components/ng2-activiti-tasklist/package.json +++ b/ng2-components/ng2-activiti-tasklist/package.json @@ -1,7 +1,7 @@ { "name": "ng2-activiti-tasklist", "description": "Activiti Angular2 Task List Component", - "version": "1.2.0", + "version": "1.2.1", "author": "Alfresco Software, Ltd.", "scripts": { "clean": "npm install rimraf && npm run clean-build && rimraf dist node_modules typings", @@ -68,7 +68,7 @@ "md-date-time-picker": "^2.2.0", "ng2-translate": "2.5.0", "alfresco-js-api": "~1.2.0", - "ng2-activiti-form": "1.2.0", + "ng2-activiti-form": "1.2.1", "ng2-alfresco-core": "1.2.0", "ng2-alfresco-datatable": "1.2.0" },