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
This commit is contained in:
Denys Vuika
2017-03-10 18:20:55 +00:00
committed by Mario Romano
parent c13c3aaeeb
commit c290c47229
16 changed files with 103 additions and 55 deletions

View File

@@ -88,10 +88,10 @@
"ng2-alfresco-search": "1.2.0", "ng2-alfresco-search": "1.2.0",
"ng2-alfresco-upload": "1.2.0", "ng2-alfresco-upload": "1.2.0",
"ng2-alfresco-viewer": "1.2.0", "ng2-alfresco-viewer": "1.2.0",
"ng2-activiti-form": "1.2.0", "ng2-activiti-form": "1.2.1",
"ng2-activiti-tasklist": "1.2.0", "ng2-activiti-tasklist": "1.2.1",
"ng2-alfresco-userinfo": "1.2.0", "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-webscript": "1.2.0",
"ng2-alfresco-tag": "1.2.0", "ng2-alfresco-tag": "1.2.0",
"dialog-polyfill": "^0.4.3", "dialog-polyfill": "^0.4.3",

View File

@@ -58,7 +58,7 @@
"ng2-translate": "2.5.0", "ng2-translate": "2.5.0",
"alfresco-js-api": "~1.2.0", "alfresco-js-api": "~1.2.0",
"ng2-alfresco-core": "1.2.0", "ng2-alfresco-core": "1.2.0",
"ng2-activiti-form": "1.2.0" "ng2-activiti-form": "1.2.1"
}, },
"devDependencies": { "devDependencies": {
"@types/jasmine": "^2.2.33", "@types/jasmine": "^2.2.33",

View File

@@ -1,7 +1,7 @@
{ {
"name": "ng2-activiti-form", "name": "ng2-activiti-form",
"description": "Alfresco Activiti Form Component for Angular 2", "description": "Alfresco Activiti Form Component for Angular 2",
"version": "1.2.0", "version": "1.2.1",
"author": "Alfresco Software, Ltd.", "author": "Alfresco Software, Ltd.",
"scripts": { "scripts": {
"clean": "npm install rimraf && npm run clean-build && rimraf dist node_modules typings", "clean": "npm install rimraf && npm run clean-build && rimraf dist node_modules typings",

View File

@@ -14,9 +14,9 @@
<button (click)="openViewer(content)" class="mdl-button mdl-js-button mdl-button--icon"> <button (click)="openViewer(content)" class="mdl-button mdl-js-button mdl-button--icon">
<i class="material-icons">zoom_in</i> <i class="material-icons">zoom_in</i>
</button> </button>
<a (click)="download($event)" [href]="content.contentRawUrl" target="_blank" [download]='content.name' class="mdl-button mdl-js-button mdl-button--icon"> <div (click)="download(content)" class="mdl-button mdl-js-button mdl-button--icon">
<i class="material-icons">file_download</i> <i class="material-icons">file_download</i>
</a> </div>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -78,18 +78,26 @@ export class ActivitiContent implements OnChanges {
} }
loadThumbnailUrl(content: ContentLinkModel) { loadThumbnailUrl(content: ContentLinkModel) {
if (this.content.isTypeImage()) { if (this.content.isThumbnailSupported()) {
this.formService.getFileRawContent(content.id).subscribe( if (this.content.isTypeImage()) {
(response: Blob) => { this.formService.getFileRawContent(content.id).subscribe(
this.content.thumbnailUrl = this.createUrlPreview(response); (response: Blob) => {
}, this.content.thumbnailUrl = this.createUrlPreview(response);
error => { },
this.logService.error(error); error => {
} this.logService.error(error);
); }
} else if (this.content.isThumbnailSupported()) { );
this.content.contentRawUrl = this.formService.getFileRawContentUrl(content.id); } else {
this.content.thumbnailUrl = this.formService.getContentThumbnailUrl(content.id); 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 file opening it in a new window
*/ */
download($event) { download(content) {
$event.stopPropagation(); 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) { private sanitizeUrl(url: string) {
return this.sanitizer.bypassSecurityTrustUrl(url); return this.sanitizer.bypassSecurityTrustResourceUrl(url);
} }
private createUrlPreview(blob: Blob) { private createUrlPreview(blob: Blob) {

View File

@@ -145,7 +145,7 @@ export class DateFieldValidator implements FormFieldValidator {
validate(field: FormFieldModel): boolean { validate(field: FormFieldModel): boolean {
if (this.isSupported(field) && field.value) { if (this.isSupported(field) && field.value) {
if (DateFieldValidator.isValidDate(field.value)) { if (DateFieldValidator.isValidDate(field.value, field.dateDisplayFormat)) {
return true; return true;
} }
field.validationSummary = 'Invalid date format'; field.validationSummary = 'Invalid date format';
@@ -168,7 +168,7 @@ export class MinDateFieldValidator implements FormFieldValidator {
validate(field: FormFieldModel): boolean { validate(field: FormFieldModel): boolean {
if (this.isSupported(field) && field.value) { if (this.isSupported(field) && field.value) {
const dateFormat = 'D-M-YYYY'; const dateFormat = field.dateDisplayFormat;
if (!DateFieldValidator.isValidDate(field.value, dateFormat)) { if (!DateFieldValidator.isValidDate(field.value, dateFormat)) {
field.validationSummary = 'Invalid date format'; field.validationSummary = 'Invalid date format';
@@ -201,7 +201,7 @@ export class MaxDateFieldValidator implements FormFieldValidator {
validate(field: FormFieldModel): boolean { validate(field: FormFieldModel): boolean {
if (this.isSupported(field) && field.value) { if (this.isSupported(field) && field.value) {
const dateFormat = 'D-M-YYYY'; const dateFormat = field.dateDisplayFormat;
if (!DateFieldValidator.isValidDate(field.value, dateFormat)) { if (!DateFieldValidator.isValidDate(field.value, dateFormat)) {
field.validationSummary = 'Invalid date format'; field.validationSummary = 'Invalid date format';

View File

@@ -44,6 +44,8 @@ export class FormFieldModel extends FormWidgetModel {
private _readOnly: boolean = false; private _readOnly: boolean = false;
private _isValid: boolean = true; private _isValid: boolean = true;
readonly defaultDateFormat: string = 'D-M-YYYY';
// model members // model members
fieldType: string; fieldType: string;
id: string; id: string;
@@ -74,6 +76,7 @@ export class FormFieldModel extends FormWidgetModel {
visibilityCondition: WidgetVisibilityModel = null; visibilityCondition: WidgetVisibilityModel = null;
enableFractions: boolean = false; enableFractions: boolean = false;
currency: string = null; currency: string = null;
dateDisplayFormat: string = this.defaultDateFormat;
// container model members // container model members
numberOfColumns: number = 1; numberOfColumns: number = 1;
@@ -155,6 +158,7 @@ export class FormFieldModel extends FormWidgetModel {
this.visibilityCondition = <WidgetVisibilityModel> json.visibilityCondition; this.visibilityCondition = <WidgetVisibilityModel> json.visibilityCondition;
this.enableFractions = <boolean>json.enableFractions; this.enableFractions = <boolean>json.enableFractions;
this.currency = json.currency; this.currency = json.currency;
this.dateDisplayFormat = json.dateDisplayFormat || this.defaultDateFormat;
this._value = this.parseValue(json); this._value = this.parseValue(json);
if (json.placeholder && json.placeholder !== '' && json.placeholder !== 'null') { if (json.placeholder && json.placeholder !== '' && json.placeholder !== 'null') {
@@ -247,7 +251,7 @@ export class FormFieldModel extends FormWidgetModel {
if (value) { if (value) {
let d = moment(value.split('T')[0], 'YYYY-M-D'); let d = moment(value.split('T')[0], 'YYYY-M-D');
if (d.isValid()) { if (d.isValid()) {
value = d.format('D-M-YYYY'); value = d.format(this.dateDisplayFormat);
} }
} }
} }
@@ -303,7 +307,7 @@ export class FormFieldModel extends FormWidgetModel {
} }
break; break;
case FormFieldTypes.DATE: case FormFieldTypes.DATE:
let d = moment(this.value, 'D-M-YYYY'); let d = moment(this.value, this.dateDisplayFormat);
if (d.isValid()) { if (d.isValid()) {
this.form.values[this.id] = `${d.format('YYYY-MM-DD')}T00:00:00.000Z`; this.form.values[this.id] = `${d.format('YYYY-MM-DD')}T00:00:00.000Z`;
} else { } else {

View File

@@ -2,7 +2,7 @@
<div class="mdl-cell mdl-cell--11-col"> <div class="mdl-cell mdl-cell--11-col">
<div class="mdl-textfield mdl-js-textfield date-widget" <div class="mdl-textfield mdl-js-textfield date-widget"
[class.date-widget__invalid]="!field.isValid"> [class.date-widget__invalid]="!field.isValid">
<label [attr.for]="field.id">{{field.name}}</label> <label [attr.for]="field.id">{{field.name}} ({{field.dateDisplayFormat}})</label>
<input class="mdl-textfield__input mdl-date__input" <input class="mdl-textfield__input mdl-date__input"
type="text" type="text"
[attr.id]="field.id" [attr.id]="field.id"

View File

@@ -54,7 +54,7 @@ describe('DateWidget', () => {
}); });
widget.ngOnInit(); widget.ngOnInit();
let expected = moment(minValue, widget.DATE_FORMAT); let expected = moment(minValue, widget.field.dateDisplayFormat);
expect(widget.datePicker._past.isSame(expected)).toBeTruthy(); expect(widget.datePicker._past.isSame(expected)).toBeTruthy();
}); });
@@ -65,7 +65,7 @@ describe('DateWidget', () => {
}); });
widget.ngOnInit(); widget.ngOnInit();
let expected = moment(maxValue, widget.DATE_FORMAT); let expected = moment(maxValue, widget.field.dateDisplayFormat);
expect(widget.datePicker._future.isSame(expected)).toBeTruthy(); expect(widget.datePicker._future.isSame(expected)).toBeTruthy();
}); });
@@ -77,7 +77,7 @@ describe('DateWidget', () => {
}); });
widget.ngOnInit(); widget.ngOnInit();
let expected = moment(dateValue, widget.DATE_FORMAT); let expected = moment(dateValue, widget.field.dateDisplayFormat);
expect(widget.datePicker.time.isSame(expected)).toBeTruthy(); expect(widget.datePicker.time.isSame(expected)).toBeTruthy();
}); });
@@ -115,7 +115,7 @@ describe('DateWidget', () => {
widget.field.value = '31-03-1982'; widget.field.value = '31-03-1982';
widget.onDateChanged(); 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(); expect(widget.datePicker.time.isSame(expected)).toBeTruthy();
}); });
@@ -124,7 +124,7 @@ describe('DateWidget', () => {
widget.ngOnInit(); widget.ngOnInit();
let date = '13-3-1982'; let date = '13-3-1982';
widget.datePicker.time = moment(date, widget.DATE_FORMAT); widget.datePicker.time = moment(date, widget.field.dateDisplayFormat);
widget.onDateSelected(); widget.onDateSelected();
expect(widget.field.value).toBe(date); expect(widget.field.value).toBe(date);
}); });
@@ -157,7 +157,7 @@ describe('DateWidget', () => {
spyOn(w, 'setupMaterialTextField').and.callThrough(); spyOn(w, 'setupMaterialTextField').and.callThrough();
w.field = new FormFieldModel(null, {value: '9-9-9999', type: 'date'}); w.field = new FormFieldModel(null, {value: '9-9-9999', type: 'date'});
w.ngOnInit(); 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) => { w.fieldChanged.subscribe((field) => {
expect(field).toBeDefined(); expect(field).toBeDefined();
expect(field).not.toBeNull(); expect(field).not.toBeNull();
@@ -172,7 +172,7 @@ describe('DateWidget', () => {
spyOn(w, 'setupMaterialTextField').and.callThrough(); spyOn(w, 'setupMaterialTextField').and.callThrough();
w.field = new FormFieldModel(null, {value: '9-9-9999', type: 'date'}); w.field = new FormFieldModel(null, {value: '9-9-9999', type: 'date'});
w.ngOnInit(); 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) => { w.fieldChanged.subscribe((field) => {
expect(field).toBeDefined(); expect(field).toBeDefined();
expect(field).not.toBeNull(); expect(field).not.toBeNull();

View File

@@ -30,8 +30,6 @@ declare var componentHandler: any;
}) })
export class DateWidget extends WidgetComponent implements OnInit, AfterViewChecked { export class DateWidget extends WidgetComponent implements OnInit, AfterViewChecked {
DATE_FORMAT: string = 'D-M-YYYY';
datePicker: any; datePicker: any;
constructor(private elementRef: ElementRef) { constructor(private elementRef: ElementRef) {
@@ -49,15 +47,15 @@ export class DateWidget extends WidgetComponent implements OnInit, AfterViewChec
if (this.field) { if (this.field) {
if (this.field.minValue) { 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) { 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) { 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() { onDateChanged() {
if (this.field.value) { 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()) { if (!value.isValid()) {
value = moment(); value = moment();
} }
@@ -83,7 +81,7 @@ export class DateWidget extends WidgetComponent implements OnInit, AfterViewChec
} }
onDateSelected() { onDateSelected() {
let newValue = this.datePicker.time.format(this.DATE_FORMAT); let newValue = this.datePicker.time.format(this.field.dateDisplayFormat);
this.field.value = newValue; this.field.value = newValue;
this.checkVisibility(this.field); this.checkVisibility(this.field);

View File

@@ -16,6 +16,7 @@
*/ */
import { TestBed } from '@angular/core/testing'; import { TestBed } from '@angular/core/testing';
import { Observable } from 'rxjs/Rx';
import { CoreModule, AlfrescoApiService, LogService, LogServiceMock } from 'ng2-alfresco-core'; import { CoreModule, AlfrescoApiService, LogService, LogServiceMock } from 'ng2-alfresco-core';
import { FormService } from './form.service'; import { FormService } from './form.service';
import { EcmModelService } from './ecm-model.service'; import { EcmModelService } from './ecm-model.service';
@@ -32,6 +33,17 @@ describe('FormService', () => {
let logService: LogService; let logService: LogService;
let bpmCli: any; 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(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [ imports: [
@@ -444,11 +456,18 @@ describe('FormService', () => {
expect(contentRawUrl).toEqual(`${bpmCli.basePath}/api/enterprise/content/${contentId}/raw`); 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 contentId: number = 999;
let contentRawUrl = service.getContentThumbnailUrl(contentId); let blob = createFakeBlob();
expect(contentRawUrl).toEqual(`${bpmCli.basePath}/app/rest/content/${contentId}/rendition/thumbnail`); 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) => { it('should create a Form form a Node', (done) => {

View File

@@ -267,9 +267,9 @@ export class FormService {
return alfrescoApi.activiti.contentApi.getRawContentUrl(contentId); return alfrescoApi.activiti.contentApi.getRawContentUrl(contentId);
} }
getContentThumbnailUrl(contentId: number): string { getContentThumbnailUrl(contentId: number): Observable<any> {
let alfrescoApi = this.apiService.getInstance(); 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<any> { getRestFieldValues(taskId: string, field: string): Observable<any> {

View File

@@ -50,10 +50,10 @@
"md-date-time-picker": "^2.2.0", "md-date-time-picker": "^2.2.0",
"ng2-translate": "2.5.0", "ng2-translate": "2.5.0",
"alfresco-js-api": "~1.2.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-core": "1.2.0",
"ng2-alfresco-datatable": "1.2.0", "ng2-alfresco-datatable": "1.2.0",
"ng2-activiti-processlist": "1.2.0" "ng2-activiti-processlist": "1.2.1"
}, },
"devDependencies": { "devDependencies": {
"@types/jasmine": "^2.2.33", "@types/jasmine": "^2.2.33",

View File

@@ -1,7 +1,7 @@
{ {
"name": "ng2-activiti-processlist", "name": "ng2-activiti-processlist",
"description": "Show active processes from the Activiti Process Services suite", "description": "Show active processes from the Activiti Process Services suite",
"version": "1.2.0", "version": "1.2.1",
"author": "Alfresco Software, Ltd.", "author": "Alfresco Software, Ltd.",
"scripts": { "scripts": {
"clean": "npm install rimraf && npm run clean-build && rimraf dist node_modules typings", "clean": "npm install rimraf && npm run clean-build && rimraf dist node_modules typings",
@@ -63,8 +63,8 @@
"md-date-time-picker": "^2.2.0", "md-date-time-picker": "^2.2.0",
"ng2-translate": "2.5.0", "ng2-translate": "2.5.0",
"alfresco-js-api": "~1.2.0", "alfresco-js-api": "~1.2.0",
"ng2-activiti-form": "1.2.0", "ng2-activiti-form": "1.2.1",
"ng2-activiti-tasklist": "1.2.0", "ng2-activiti-tasklist": "1.2.1",
"ng2-alfresco-core": "1.2.0", "ng2-alfresco-core": "1.2.0",
"ng2-alfresco-datatable": "1.2.0" "ng2-alfresco-datatable": "1.2.0"
}, },

View File

@@ -46,7 +46,7 @@
"alfresco-js-api": "~1.2.0", "alfresco-js-api": "~1.2.0",
"ng2-alfresco-core": "1.2.0", "ng2-alfresco-core": "1.2.0",
"ng2-alfresco-datatable": "1.2.0", "ng2-alfresco-datatable": "1.2.0",
"ng2-activiti-tasklist": "1.2.0" "ng2-activiti-tasklist": "1.2.1"
}, },
"devDependencies": { "devDependencies": {
"@types/jasmine": "^2.2.33", "@types/jasmine": "^2.2.33",

View File

@@ -1,7 +1,7 @@
{ {
"name": "ng2-activiti-tasklist", "name": "ng2-activiti-tasklist",
"description": "Activiti Angular2 Task List Component", "description": "Activiti Angular2 Task List Component",
"version": "1.2.0", "version": "1.2.1",
"author": "Alfresco Software, Ltd.", "author": "Alfresco Software, Ltd.",
"scripts": { "scripts": {
"clean": "npm install rimraf && npm run clean-build && rimraf dist node_modules typings", "clean": "npm install rimraf && npm run clean-build && rimraf dist node_modules typings",
@@ -68,7 +68,7 @@
"md-date-time-picker": "^2.2.0", "md-date-time-picker": "^2.2.0",
"ng2-translate": "2.5.0", "ng2-translate": "2.5.0",
"alfresco-js-api": "~1.2.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-core": "1.2.0",
"ng2-alfresco-datatable": "1.2.0" "ng2-alfresco-datatable": "1.2.0"
}, },