From 015bd3b3a2d988a384c99ba608a6d3463c8a6417 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Sat, 5 May 2018 12:24:21 +0200 Subject: [PATCH] Editor SDK. --- .../Schemas/FieldProperties.cs | 2 + .../Schemas/Models/FieldPropertiesDto.cs | 5 + .../content/content-field.component.html | 194 +++++++++--------- .../schema/types/boolean-ui.component.html | 11 + .../schema/types/boolean-ui.component.ts | 5 - .../schema/types/date-time-ui.component.html | 11 + .../schema/types/date-time-ui.component.ts | 5 - .../types/geolocation-ui.component.html | 11 + .../schema/types/number-ui.component.html | 11 + .../pages/schema/types/number-ui.component.ts | 5 - .../schema/types/string-ui.component.html | 11 + .../pages/schema/types/string-ui.component.ts | 5 - .../forms/iframe-editor.component.html | 1 + .../forms/iframe-editor.component.scss | 8 + .../angular/forms/iframe-editor.component.ts | 111 ++++++++++ src/Squidex/app/framework/declarations.ts | 1 + src/Squidex/app/framework/module.ts | 3 + .../shared/services/schemas.fields.spec.ts | 48 ++--- .../app/shared/services/schemas.service.ts | 55 ++--- src/Squidex/app/shared/state/schemas.state.ts | 6 + .../Migrations/PopulateGrainIndexes.cs | 36 ++-- 21 files changed, 360 insertions(+), 185 deletions(-) create mode 100644 src/Squidex/app/framework/angular/forms/iframe-editor.component.html create mode 100644 src/Squidex/app/framework/angular/forms/iframe-editor.component.scss create mode 100644 src/Squidex/app/framework/angular/forms/iframe-editor.component.ts diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldProperties.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldProperties.cs index 3820bd6c5..606f90b3a 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldProperties.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldProperties.cs @@ -15,6 +15,8 @@ namespace Squidex.Domain.Apps.Core.Schemas public string Placeholder { get; set; } + public string EditorUrl { get; set; } + public abstract T Accept(IFieldPropertiesVisitor visitor); public abstract Field CreateField(long id, string name, Partitioning partitioning); diff --git a/src/Squidex/Areas/Api/Controllers/Schemas/Models/FieldPropertiesDto.cs b/src/Squidex/Areas/Api/Controllers/Schemas/Models/FieldPropertiesDto.cs index afd3e8094..f250e1352 100644 --- a/src/Squidex/Areas/Api/Controllers/Schemas/Models/FieldPropertiesDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Schemas/Models/FieldPropertiesDto.cs @@ -46,6 +46,11 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models /// public bool IsListField { get; set; } + /// + /// Optional url to the editor. + /// + public string EditorUrl { get; set; } + /// /// Gets the partitioning of the language, e.g. invariant or language. /// diff --git a/src/Squidex/app/features/content/pages/content/content-field.component.html b/src/Squidex/app/features/content/pages/content/content-field.component.html index 672986d8a..20f661428 100644 --- a/src/Squidex/app/features/content/pages/content/content-field.component.html +++ b/src/Squidex/app/features/content/pages/content/content-field.component.html @@ -21,99 +21,107 @@ -
-
-
-
- -
-
- -
-
- -
-
-
- - -
-
-
-
-
-
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
-
- - -
-
-
-
-
-
-
- -
-
-
- -
-
-
-
-
- -
-
- -
-
- -
-
- -
-
- -
-
- - -
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/src/Squidex/app/features/schemas/pages/schema/types/boolean-ui.component.html b/src/Squidex/app/features/schemas/pages/schema/types/boolean-ui.component.html index f74a1c026..6c11e3d6b 100644 --- a/src/Squidex/app/features/schemas/pages/schema/types/boolean-ui.component.html +++ b/src/Squidex/app/features/schemas/pages/schema/types/boolean-ui.component.html @@ -1,4 +1,15 @@
+
+ + +
+ + + + Url to your plugin if you use a custom editor. + +
+
diff --git a/src/Squidex/app/features/schemas/pages/schema/types/boolean-ui.component.ts b/src/Squidex/app/features/schemas/pages/schema/types/boolean-ui.component.ts index 6fa536fdf..130698031 100644 --- a/src/Squidex/app/features/schemas/pages/schema/types/boolean-ui.component.ts +++ b/src/Squidex/app/features/schemas/pages/schema/types/boolean-ui.component.ts @@ -31,11 +31,6 @@ export class BooleanUIComponent implements OnInit { Validators.required ])); - this.editForm.setControl('placeholder', - new FormControl(this.properties.placeholder, [ - Validators.maxLength(100) - ])); - this.editForm.setControl('inlineEditable', new FormControl(this.properties.inlineEditable)); } diff --git a/src/Squidex/app/features/schemas/pages/schema/types/date-time-ui.component.html b/src/Squidex/app/features/schemas/pages/schema/types/date-time-ui.component.html index b888ce094..7802e154a 100644 --- a/src/Squidex/app/features/schemas/pages/schema/types/date-time-ui.component.html +++ b/src/Squidex/app/features/schemas/pages/schema/types/date-time-ui.component.html @@ -1,4 +1,15 @@
+
+ + +
+ + + + Url to your plugin if you use a custom editor. + +
+
diff --git a/src/Squidex/app/features/schemas/pages/schema/types/date-time-ui.component.ts b/src/Squidex/app/features/schemas/pages/schema/types/date-time-ui.component.ts index eabd30c6e..b145acbed 100644 --- a/src/Squidex/app/features/schemas/pages/schema/types/date-time-ui.component.ts +++ b/src/Squidex/app/features/schemas/pages/schema/types/date-time-ui.component.ts @@ -35,10 +35,5 @@ export class DateTimeUIComponent implements OnInit { new FormControl(this.properties.editor, [ Validators.required ])); - - this.editForm.setControl('placeholder', - new FormControl(this.properties.placeholder, [ - Validators.maxLength(100) - ])); } } \ No newline at end of file diff --git a/src/Squidex/app/features/schemas/pages/schema/types/geolocation-ui.component.html b/src/Squidex/app/features/schemas/pages/schema/types/geolocation-ui.component.html index 6bfee94c9..d5dd76e57 100644 --- a/src/Squidex/app/features/schemas/pages/schema/types/geolocation-ui.component.html +++ b/src/Squidex/app/features/schemas/pages/schema/types/geolocation-ui.component.html @@ -1,4 +1,15 @@
+
+ + +
+ + + + Url to your plugin if you use a custom editor. + +
+
diff --git a/src/Squidex/app/features/schemas/pages/schema/types/number-ui.component.html b/src/Squidex/app/features/schemas/pages/schema/types/number-ui.component.html index 357cb7826..75c821ab4 100644 --- a/src/Squidex/app/features/schemas/pages/schema/types/number-ui.component.html +++ b/src/Squidex/app/features/schemas/pages/schema/types/number-ui.component.html @@ -1,4 +1,15 @@
+
+ + +
+ + + + Url to your plugin if you use a custom editor. + +
+
diff --git a/src/Squidex/app/features/schemas/pages/schema/types/number-ui.component.ts b/src/Squidex/app/features/schemas/pages/schema/types/number-ui.component.ts index 0c34229b1..94b6a6e7d 100644 --- a/src/Squidex/app/features/schemas/pages/schema/types/number-ui.component.ts +++ b/src/Squidex/app/features/schemas/pages/schema/types/number-ui.component.ts @@ -45,11 +45,6 @@ export class NumberUIComponent implements OnDestroy, OnInit { Validators.required ])); - this.editForm.setControl('placeholder', - new FormControl(this.properties.placeholder, [ - Validators.maxLength(100) - ])); - this.editForm.setControl('allowedValues', new FormControl(this.properties.allowedValues, [])); diff --git a/src/Squidex/app/features/schemas/pages/schema/types/string-ui.component.html b/src/Squidex/app/features/schemas/pages/schema/types/string-ui.component.html index d25ff9340..eebeb9636 100644 --- a/src/Squidex/app/features/schemas/pages/schema/types/string-ui.component.html +++ b/src/Squidex/app/features/schemas/pages/schema/types/string-ui.component.html @@ -1,4 +1,15 @@
+
+ + +
+ + + + Url to your plugin if you use a custom editor. + +
+
diff --git a/src/Squidex/app/features/schemas/pages/schema/types/string-ui.component.ts b/src/Squidex/app/features/schemas/pages/schema/types/string-ui.component.ts index 7907eff8c..06991f00d 100644 --- a/src/Squidex/app/features/schemas/pages/schema/types/string-ui.component.ts +++ b/src/Squidex/app/features/schemas/pages/schema/types/string-ui.component.ts @@ -43,11 +43,6 @@ export class StringUIComponent implements OnDestroy, OnInit { Validators.required ])); - this.editForm.setControl('placeholder', - new FormControl(this.properties.placeholder, [ - Validators.maxLength(100) - ])); - this.editForm.setControl('allowedValues', new FormControl(this.properties.allowedValues)); diff --git a/src/Squidex/app/framework/angular/forms/iframe-editor.component.html b/src/Squidex/app/framework/angular/forms/iframe-editor.component.html new file mode 100644 index 000000000..44f811b58 --- /dev/null +++ b/src/Squidex/app/framework/angular/forms/iframe-editor.component.html @@ -0,0 +1 @@ + diff --git a/src/Squidex/app/framework/angular/forms/iframe-editor.component.scss b/src/Squidex/app/framework/angular/forms/iframe-editor.component.scss new file mode 100644 index 000000000..a095b5177 --- /dev/null +++ b/src/Squidex/app/framework/angular/forms/iframe-editor.component.scss @@ -0,0 +1,8 @@ +@import '_mixins'; +@import '_vars'; + +iframe { + border: 0; + background: 0; + overflow: hidden; +} \ No newline at end of file diff --git a/src/Squidex/app/framework/angular/forms/iframe-editor.component.ts b/src/Squidex/app/framework/angular/forms/iframe-editor.component.ts new file mode 100644 index 000000000..26dde9d2f --- /dev/null +++ b/src/Squidex/app/framework/angular/forms/iframe-editor.component.ts @@ -0,0 +1,111 @@ +/* + * Squidex Headless CMS + * + * @license + * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. + */ + +import { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, forwardRef, Input, OnDestroy, OnInit, Renderer, ViewChild } from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { DomSanitizer } from '@angular/platform-browser'; + +export const SQX_IFRAME_EDITOR_CONTROL_VALUE_ACCESSOR: any = { + provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => IFrameEditorComponent), multi: true +}; + +@Component({ + selector: 'sqx-iframe-editor', + styleUrls: ['./iframe-editor.component.scss'], + templateUrl: './iframe-editor.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, + providers: [SQX_IFRAME_EDITOR_CONTROL_VALUE_ACCESSOR] +}) +export class IFrameEditorComponent implements ControlValueAccessor, AfterViewInit, OnInit, OnDestroy { + private windowMessageListener: Function; + private callChange = (v: any) => { /* NOOP */ }; + private callTouched = () => { /* NOOP */ }; + private value: string; + private isDisabled = false; + private isInitialized = false; + private plugin: HTMLIFrameElement; + + @ViewChild('iframe') + public iframe: ElementRef; + + @Input() + public url: string; + + constructor( + private readonly sanitizer: DomSanitizer, + private readonly renderer: Renderer + ) { + } + + public ngOnDestroy() { + this.windowMessageListener(); + } + + public ngAfterViewInit() { + this.plugin = this.iframe.nativeElement; + } + + public ngOnInit(): void { + this.windowMessageListener = + this.renderer.listenGlobal('window', 'message', (event: MessageEvent) => { + if (event.source === this.plugin.contentWindow) { + const { type } = event.data; + + if (type === 'started') { + this.isInitialized = true; + + if (this.plugin.contentWindow) { + this.plugin.contentWindow.postMessage({ type: 'disabled', disabled: this.isDisabled }, '*'); + this.plugin.contentWindow.postMessage({ type: 'valueChanged', value: this.value }, '*'); + } + } else if (type === 'resize') { + const { height } = event.data; + + this.plugin.height = height + 'px'; + } else if (type === 'valueChanged') { + const { value } = event.data; + + if (this.value !== value) { + this.value = value; + + this.callChange(value); + } + } else if (type === 'touched') { + this.callTouched(); + } + } + }); + } + + public sanitizedUrl() { + return this.sanitizer.bypassSecurityTrustResourceUrl(this.url); + } + + public writeValue(value: string) { + this.value = value; + + if (this.isInitialized && this.plugin.contentWindow) { + this.plugin.contentWindow.postMessage({ type: 'valueChanged', value: this.value }, '*'); + } + } + + public setDisabledState(isDisabled: boolean): void { + this.isDisabled = isDisabled; + + if (this.isInitialized && this.plugin.contentWindow) { + this.plugin.contentWindow.postMessage({ type: 'disabled', disabled: this.isDisabled }, '*'); + } + } + + public registerOnChange(fn: any) { + this.callChange = fn; + } + + public registerOnTouched(fn: any) { + this.callTouched = fn; + } +} \ No newline at end of file diff --git a/src/Squidex/app/framework/declarations.ts b/src/Squidex/app/framework/declarations.ts index f285a2076..659283d6c 100644 --- a/src/Squidex/app/framework/declarations.ts +++ b/src/Squidex/app/framework/declarations.ts @@ -14,6 +14,7 @@ export * from './angular/forms/dropdown.component'; export * from './angular/forms/file-drop.directive'; export * from './angular/forms/focus-on-init.directive'; export * from './angular/forms/form-error.component'; +export * from './angular/forms/iframe-editor.component'; export * from './angular/forms/indeterminate-value.directive'; export * from './angular/forms/jscript-editor.component'; export * from './angular/forms/json-editor.component'; diff --git a/src/Squidex/app/framework/module.ts b/src/Squidex/app/framework/module.ts index db3753c92..43480accb 100644 --- a/src/Squidex/app/framework/module.ts +++ b/src/Squidex/app/framework/module.ts @@ -32,6 +32,7 @@ import { FormErrorComponent, FromNowPipe, FullDateTimePipe, + IFrameEditorComponent, IgnoreScrollbarDirective, ImageSourceDirective, IndeterminateValueDirective, @@ -101,6 +102,7 @@ import { FormErrorComponent, FromNowPipe, FullDateTimePipe, + IFrameEditorComponent, IgnoreScrollbarDirective, ImageSourceDirective, IndeterminateValueDirective, @@ -159,6 +161,7 @@ import { FormsModule, FromNowPipe, FullDateTimePipe, + IFrameEditorComponent, IgnoreScrollbarDirective, ImageSourceDirective, IndeterminateValueDirective, diff --git a/src/Squidex/app/shared/services/schemas.fields.spec.ts b/src/Squidex/app/shared/services/schemas.fields.spec.ts index 2dda6b093..765989151 100644 --- a/src/Squidex/app/shared/services/schemas.fields.spec.ts +++ b/src/Squidex/app/shared/services/schemas.fields.spec.ts @@ -43,9 +43,9 @@ describe('SchemaDetailsDto', () => { }); it('should return configured fields as list fields if no schema field are declared', () => { - const field1 = createField(new AssetsFieldPropertiesDto(null, null, null, false, true), 1); - const field2 = createField(new AssetsFieldPropertiesDto(null, null, null, false, false), 2); - const field3 = createField(new AssetsFieldPropertiesDto(null, null, null, false, true), 3); + const field1 = createField(new AssetsFieldPropertiesDto(null, null, null, null, false, true), 1); + const field2 = createField(new AssetsFieldPropertiesDto(null, null, null, null, false, false), 2); + const field3 = createField(new AssetsFieldPropertiesDto(null, null, null, null, false, true), 3); const schema = createSchema(new SchemaPropertiesDto(''), 1, [field1, field2, field3]); @@ -53,9 +53,9 @@ describe('SchemaDetailsDto', () => { }); it('should return first fields as list fields if no schema field is declared', () => { - const field1 = createField(new AssetsFieldPropertiesDto(null, null, null, false, false), 1); - const field2 = createField(new AssetsFieldPropertiesDto(null, null, null, false, false), 2); - const field3 = createField(new AssetsFieldPropertiesDto(null, null, null, false, false), 3); + const field1 = createField(new AssetsFieldPropertiesDto(null, null, null, null, false, false), 1); + const field2 = createField(new AssetsFieldPropertiesDto(null, null, null, null, false, false), 2); + const field3 = createField(new AssetsFieldPropertiesDto(null, null, null, null, false, false), 3); const schema = createSchema(new SchemaPropertiesDto(''), 1, [field1, field2, field3]); @@ -71,50 +71,50 @@ describe('SchemaDetailsDto', () => { describe('FieldDto', () => { it('should return label as display name', () => { - const field = createField(new AssetsFieldPropertiesDto('Label', null, null, true, false), 1); + const field = createField(new AssetsFieldPropertiesDto('Label', null, null, null, true, false), 1); expect(field.displayName).toBe('Label'); }); it('should return name as display name if label is null', () => { - const field = createField(new AssetsFieldPropertiesDto(null, null, null, true, false), 1); + const field = createField(new AssetsFieldPropertiesDto(null, null, null, null, true, false), 1); expect(field.displayName).toBe('field1'); }); it('should return name as display name label is empty', () => { - const field = createField(new AssetsFieldPropertiesDto('', null, null, true, false), 1); + const field = createField(new AssetsFieldPropertiesDto('', null, null, null, true, false), 1); expect(field.displayName).toBe('field1'); }); it('should return placeholder as display placeholder', () => { - const field = createField(new AssetsFieldPropertiesDto(null, null, 'Placeholder', true, false), 1); + const field = createField(new AssetsFieldPropertiesDto(null, null, 'Placeholder', null, true, false), 1); expect(field.displayPlaceholder).toBe('Placeholder'); }); it('should return empty as display placeholder if placeholder is null', () => { - const field = createField(new AssetsFieldPropertiesDto(null, null, null, true, false)); + const field = createField(new AssetsFieldPropertiesDto(null, null, null, null, true, false)); expect(field.displayPlaceholder).toBe(''); }); it('should return localizable if partitioning is language', () => { - const field = createField(new AssetsFieldPropertiesDto(null, null, null, true, false), 1, 'language'); + const field = createField(new AssetsFieldPropertiesDto(null, null, null, null, true, false), 1, 'language'); expect(field.isLocalizable).toBeTruthy(); }); it('should not return localizable if partitioning is invarient', () => { - const field = createField(new AssetsFieldPropertiesDto(null, null, null, true, false), 1, 'invariant'); + const field = createField(new AssetsFieldPropertiesDto(null, null, null, null, true, false), 1, 'invariant'); expect(field.isLocalizable).toBeFalsy(); }); }); describe('AssetsField', () => { - const field = createField(new AssetsFieldPropertiesDto(null, null, null, true, false, 1, 1)); + const field = createField(new AssetsFieldPropertiesDto(null, null, null, null, true, false, 1, 1)); it('should create validators', () => { expect(field.createValidators(false).length).toBe(3); @@ -138,7 +138,7 @@ describe('AssetsField', () => { }); describe('TagsField', () => { - const field = createField(new TagsFieldPropertiesDto(null, null, null, true, false, 1, 1)); + const field = createField(new TagsFieldPropertiesDto(null, null, null, null, true, false, 1, 1)); it('should create validators', () => { expect(field.createValidators(false).length).toBe(3); @@ -162,7 +162,7 @@ describe('TagsField', () => { }); describe('BooleanField', () => { - const field = createField(new BooleanFieldPropertiesDto(null, null, null, true, false, false, 'Checkbox')); + const field = createField(new BooleanFieldPropertiesDto(null, null, null, null, true, false, false, 'Checkbox')); it('should create validators', () => { expect(field.createValidators(false).length).toBe(1); @@ -189,7 +189,7 @@ describe('BooleanField', () => { describe('DateTimeField', () => { const now = DateTime.parseISO_UTC('2017-10-12T16:30:10Z'); - const field = createField(new DateTimeFieldPropertiesDto(null, null, null, true, false, 'Date')); + const field = createField(new DateTimeFieldPropertiesDto(null, null, null, null, true, false, 'Date')); it('should create validators', () => { expect(field.createValidators(false).length).toBe(1); @@ -204,13 +204,13 @@ describe('DateTimeField', () => { }); it('should format to date', () => { - const dateField = createField(new DateTimeFieldPropertiesDto(null, null, null, true, false, 'Date')); + const dateField = createField(new DateTimeFieldPropertiesDto(null, null, null, null, true, false, 'Date')); expect(dateField.formatValue('2017-12-12T16:00:00Z')).toBe('2017-12-12'); }); it('should format to date time', () => { - const dateTimeField = createField(new DateTimeFieldPropertiesDto(null, null, null, true, false, 'DateTime')); + const dateTimeField = createField(new DateTimeFieldPropertiesDto(null, null, null, null, true, false, 'DateTime')); expect(dateTimeField.formatValue('2017-12-12T16:00:00Z')).toBe('2017-12-12 16:00:00'); }); @@ -235,7 +235,7 @@ describe('DateTimeField', () => { }); describe('GeolocationField', () => { - const field = createField(new GeolocationFieldPropertiesDto(null, null, null, true, false, 'Default')); + const field = createField(new GeolocationFieldPropertiesDto(null, null, null, null, true, false, 'Default')); it('should create validators', () => { expect(field.createValidators(false).length).toBe(1); @@ -255,7 +255,7 @@ describe('GeolocationField', () => { }); describe('JsonField', () => { - const field = createField(new JsonFieldPropertiesDto(null, null, null, true, false)); + const field = createField(new JsonFieldPropertiesDto(null, null, null, null, true, false)); it('should create validators', () => { expect(field.createValidators(false).length).toBe(1); @@ -275,7 +275,7 @@ describe('JsonField', () => { }); describe('NumberField', () => { - const field = createField(new NumberFieldPropertiesDto(null, null, null, true, false, false, 'Input', undefined, 3, 1, [1, 2, 3])); + const field = createField(new NumberFieldPropertiesDto(null, null, null, null, true, false, false, 'Input', undefined, 3, 1, [1, 2, 3])); it('should create validators', () => { expect(field.createValidators(false).length).toBe(4); @@ -297,7 +297,7 @@ describe('NumberField', () => { }); describe('ReferencesField', () => { - const field = createField(new ReferencesFieldPropertiesDto(null, null, null, true, false, 1, 1)); + const field = createField(new ReferencesFieldPropertiesDto(null, null, null, null, true, false, 1, 1)); it('should create validators', () => { expect(field.createValidators(false).length).toBe(3); @@ -321,7 +321,7 @@ describe('ReferencesField', () => { }); describe('StringField', () => { - const field = createField(new StringFieldPropertiesDto(null, null, null, true, false, false, 'Input', undefined, 'pattern', undefined, 3, 1, ['1', '2'])); + const field = createField(new StringFieldPropertiesDto(null, null, null, null, true, false, false, 'Input', undefined, 'pattern', undefined, 3, 1, ['1', '2'])); it('should create validators', () => { expect(field.createValidators(false).length).toBe(5); diff --git a/src/Squidex/app/shared/services/schemas.service.ts b/src/Squidex/app/shared/services/schemas.service.ts index 492318838..0e154710c 100644 --- a/src/Squidex/app/shared/services/schemas.service.ts +++ b/src/Squidex/app/shared/services/schemas.service.ts @@ -61,31 +61,31 @@ export function createProperties(fieldType: string, values: Object | null = null switch (fieldType) { case 'Number': - properties = new NumberFieldPropertiesDto(null, null, null, false, false, false, 'Input'); + properties = new NumberFieldPropertiesDto(null, null, null, null, false, false, false, 'Input'); break; case 'String': - properties = new StringFieldPropertiesDto(null, null, null, false, false, false, 'Input'); + properties = new StringFieldPropertiesDto(null, null, null, null, false, false, false, 'Input'); break; case 'Boolean': - properties = new BooleanFieldPropertiesDto(null, null, null, false, false, false, 'Checkbox'); + properties = new BooleanFieldPropertiesDto(null, null, null, null, false, false, false, 'Checkbox'); break; case 'DateTime': - properties = new DateTimeFieldPropertiesDto(null, null, null, false, false, 'DateTime'); + properties = new DateTimeFieldPropertiesDto(null, null, null, null, false, false, 'DateTime'); break; case 'Geolocation': - properties = new GeolocationFieldPropertiesDto(null, null, null, false, false, 'Map'); + properties = new GeolocationFieldPropertiesDto(null, null, null, null, false, false, 'Map'); break; case 'Json': - properties = new JsonFieldPropertiesDto(null, null, null, false, false); + properties = new JsonFieldPropertiesDto(null, null, null, null, false, false); break; case 'References': - properties = new ReferencesFieldPropertiesDto(null, null, null, false, false); + properties = new ReferencesFieldPropertiesDto(null, null, null, null, false, false); break; case 'Assets': - properties = new AssetsFieldPropertiesDto(null, null, null, false, false); + properties = new AssetsFieldPropertiesDto(null, null, null, null, false, false); break; case 'Tags': - properties = new TagsFieldPropertiesDto(null, null, null, false, false); + properties = new TagsFieldPropertiesDto(null, null, null, null, false, false); break; default: throw 'Invalid properties type'; @@ -176,6 +176,7 @@ export abstract class FieldPropertiesDto { public readonly label: string | null, public readonly hints: string | null, public readonly placeholder: string | null, + public readonly editorUrl: string | null, public readonly isRequired: boolean, public readonly isListField: boolean ) { @@ -191,7 +192,7 @@ export abstract class FieldPropertiesDto { } export class StringFieldPropertiesDto extends FieldPropertiesDto { - constructor(label: string | null, hints: string | null, placeholder: string | null, + constructor(label: string | null, hints: string | null, placeholder: string | null, editorUrl: string | null, isRequired: boolean, isListField: boolean, public readonly inlineEditable: boolean, @@ -203,7 +204,7 @@ export class StringFieldPropertiesDto extends FieldPropertiesDto { public readonly maxLength?: number, public readonly allowedValues?: string[] ) { - super('String', label, hints, placeholder, isRequired, isListField); + super('String', label, hints, placeholder, editorUrl, isRequired, isListField); } public formatValue(value: any): string { @@ -252,7 +253,7 @@ export class StringFieldPropertiesDto extends FieldPropertiesDto { } export class NumberFieldPropertiesDto extends FieldPropertiesDto { - constructor(label: string | null, hints: string | null, placeholder: string | null, + constructor(label: string | null, hints: string | null, placeholder: string | null, editorUrl: string | null, isRequired: boolean, isListField: boolean, public readonly inlineEditable: boolean, @@ -262,7 +263,7 @@ export class NumberFieldPropertiesDto extends FieldPropertiesDto { public readonly minValue?: number, public readonly allowedValues?: number[] ) { - super('Number', label, hints, placeholder, isRequired, isListField); + super('Number', label, hints, placeholder, editorUrl, isRequired, isListField); } public formatValue(value: any): string { @@ -307,7 +308,7 @@ export class NumberFieldPropertiesDto extends FieldPropertiesDto { } export class DateTimeFieldPropertiesDto extends FieldPropertiesDto { - constructor(label: string | null, hints: string | null, placeholder: string | null, + constructor(label: string | null, hints: string | null, placeholder: string | null, editorUrl: string | null, isRequired: boolean, isListField: boolean, public readonly editor: string, @@ -316,7 +317,7 @@ export class DateTimeFieldPropertiesDto extends FieldPropertiesDto { public readonly minValue?: string, public readonly calculatedDefaultValue?: string ) { - super('DateTime', label, hints, placeholder, isRequired, isListField); + super('DateTime', label, hints, placeholder, editorUrl, isRequired, isListField); } public formatValue(value: any): string { @@ -361,14 +362,14 @@ export class DateTimeFieldPropertiesDto extends FieldPropertiesDto { } export class BooleanFieldPropertiesDto extends FieldPropertiesDto { - constructor(label: string | null, hints: string | null, placeholder: string | null, + constructor(label: string | null, hints: string | null, placeholder: string | null, editorUrl: string | null, isRequired: boolean, isListField: boolean, public readonly inlineEditable: boolean, public readonly editor: string, public readonly defaultValue?: boolean ) { - super('Boolean', label, hints, placeholder, isRequired, isListField); + super('Boolean', label, hints, placeholder, editorUrl, isRequired, isListField); } public formatValue(value: any): string { @@ -395,12 +396,12 @@ export class BooleanFieldPropertiesDto extends FieldPropertiesDto { } export class GeolocationFieldPropertiesDto extends FieldPropertiesDto { - constructor(label: string | null, hints: string | null, placeholder: string | null, + constructor(label: string | null, hints: string | null, placeholder: string | null, editorUrl: string | null, isRequired: boolean, isListField: boolean, public readonly editor: string ) { - super('Geolocation', label, hints, placeholder, isRequired, isListField); + super('Geolocation', label, hints, placeholder, editorUrl, isRequired, isListField); } public formatValue(value: any): string { @@ -423,14 +424,14 @@ export class GeolocationFieldPropertiesDto extends FieldPropertiesDto { } export class ReferencesFieldPropertiesDto extends FieldPropertiesDto { - constructor(label: string | null, hints: string | null, placeholder: string | null, + constructor(label: string | null, hints: string | null, placeholder: string | null, editorUrl: string | null, isRequired: boolean, isListField: boolean, public readonly minItems?: number, public readonly maxItems?: number, public readonly schemaId?: string ) { - super('References', label, hints, placeholder, isRequired, isListField); + super('References', label, hints, placeholder, editorUrl, isRequired, isListField); } public formatValue(value: any): string { @@ -465,7 +466,7 @@ export class ReferencesFieldPropertiesDto extends FieldPropertiesDto { } export class AssetsFieldPropertiesDto extends FieldPropertiesDto { - constructor(label: string | null, hints: string | null, placeholder: string | null, + constructor(label: string | null, hints: string | null, placeholder: string | null, editorUrl: string | null, isRequired: boolean, isListField: boolean, public readonly minItems?: number, @@ -481,7 +482,7 @@ export class AssetsFieldPropertiesDto extends FieldPropertiesDto { public readonly aspectWidth?: number, public readonly aspectHeight?: number ) { - super('Assets', label, hints, placeholder, isRequired, isListField); + super('Assets', label, hints, placeholder, editorUrl, isRequired, isListField); } public formatValue(value: any): string { @@ -516,13 +517,13 @@ export class AssetsFieldPropertiesDto extends FieldPropertiesDto { } export class TagsFieldPropertiesDto extends FieldPropertiesDto { - constructor(label: string | null, hints: string | null, placeholder: string | null, + constructor(label: string | null, hints: string | null, placeholder: string | null, editorUrl: string | null, isRequired: boolean, isListField: boolean, public readonly minItems?: number, public readonly maxItems?: number ) { - super('Tags', label, hints, placeholder, isRequired, isListField); + super('Tags', label, hints, placeholder, editorUrl, isRequired, isListField); } public formatValue(value: any): string { @@ -557,11 +558,11 @@ export class TagsFieldPropertiesDto extends FieldPropertiesDto { } export class JsonFieldPropertiesDto extends FieldPropertiesDto { - constructor(label: string | null, hints: string | null, placeholder: string | null, + constructor(label: string | null, hints: string | null, placeholder: string | null, editorUrl: string | null, isRequired: boolean, isListField: boolean ) { - super('Json', label, hints, placeholder, isRequired, isListField); + super('Json', label, hints, placeholder, editorUrl, isRequired, isListField); } public formatValue(value: any): string { diff --git a/src/Squidex/app/shared/state/schemas.state.ts b/src/Squidex/app/shared/state/schemas.state.ts index e397b0d15..963f2d1a0 100644 --- a/src/Squidex/app/shared/state/schemas.state.ts +++ b/src/Squidex/app/shared/state/schemas.state.ts @@ -85,6 +85,12 @@ export class EditFieldForm extends Form { Validators.maxLength(1000) ] ], + placeholder: ['', + [ + Validators.maxLength(1000) + ] + ], + editorUrl: null, isRequired: false, isListField: false })); diff --git a/tools/Migrate_01/Migrations/PopulateGrainIndexes.cs b/tools/Migrate_01/Migrations/PopulateGrainIndexes.cs index ce21e9060..40828a5fa 100644 --- a/tools/Migrate_01/Migrations/PopulateGrainIndexes.cs +++ b/tools/Migrate_01/Migrations/PopulateGrainIndexes.cs @@ -7,7 +7,6 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Threading.Tasks; using Orleans; using Squidex.Domain.Apps.Entities.Apps; @@ -71,36 +70,32 @@ namespace Migrate_01.Migrations return TaskHelper.Done; }); - var tasks = - appsByUser.Select(x => - grainFactory.GetGrain(x.Key).RebuildAsync(x.Value)) - .Union(new[] - { - grainFactory.GetGrain(SingleGrain.Id).RebuildAsync(appsByName) - }); + await grainFactory.GetGrain(SingleGrain.Id).RebuildAsync(appsByName); - await Task.WhenAll(tasks); + foreach (var kvp in appsByUser) + { + await grainFactory.GetGrain(kvp.Key).RebuildAsync(kvp.Value); + } } private async Task RebuildRuleIndexes() { - var schemasByApp = new Dictionary>(); + var rulesByApp = new Dictionary>(); await statesForRules.ReadAllAsync((schema, version) => { if (!schema.IsDeleted) { - schemasByApp.GetOrAddNew(schema.AppId.Id).Add(schema.Id); + rulesByApp.GetOrAddNew(schema.AppId.Id).Add(schema.Id); } return TaskHelper.Done; }); - var tasks = - schemasByApp.Select(x => - grainFactory.GetGrain(x.Key).RebuildAsync(x.Value)); - - await Task.WhenAll(tasks); + foreach (var kvp in rulesByApp) + { + await grainFactory.GetGrain(kvp.Key).RebuildAsync(kvp.Value); + } } private async Task RebuildSchemaIndexes() @@ -117,11 +112,10 @@ namespace Migrate_01.Migrations return TaskHelper.Done; }); - var tasks = - schemasByApp.Select(x => - grainFactory.GetGrain(x.Key).RebuildAsync(x.Value)); - - await Task.WhenAll(tasks); + foreach (var kvp in schemasByApp) + { + await grainFactory.GetGrain(kvp.Key).RebuildAsync(kvp.Value); + } } } }