From 29b617f0d23f827d83db3c464c9e39d602fa0dda Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Thu, 18 Jul 2019 18:47:57 +0200 Subject: [PATCH] Reference fields. --- .../Schemas/FieldProperties.cs | 2 ++ .../Apps/Templates/Builders/FieldBuilder.cs | 7 ++++++ .../Guards/FieldPropertiesValidator.cs | 5 ++++ .../Schemas/Models/FieldPropertiesDto.cs | 5 ++++ .../pages/content/content-page.component.ts | 2 +- .../contents/contents-page.component.html | 5 ++-- .../shared/content-item.component.html | 2 +- .../content/shared/content-item.component.ts | 5 +++- .../shared/contents-selector.component.html | 5 ++-- .../shared/references-dropdown.component.ts | 2 +- .../shared/references-editor.component.html | 7 +++--- .../forms/field-form-common.component.ts | 18 ++++++++++++++ .../app/shared/services/schemas.service.ts | 24 ++++++++++++------- .../app/shared/services/schemas.types.ts | 1 + .../Schemas/Guards/GuardSchemaFieldTests.cs | 9 +++++++ .../Schemas/Guards/GuardSchemaTests.cs | 5 +++- 16 files changed, 83 insertions(+), 21 deletions(-) diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldProperties.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldProperties.cs index 59aabc380..b314ce28b 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldProperties.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldProperties.cs @@ -13,6 +13,8 @@ namespace Squidex.Domain.Apps.Core.Schemas public bool IsListField { get; set; } + public bool IsReferenceField { get; set; } + public string Placeholder { get; set; } public string EditorUrl { get; set; } diff --git a/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/FieldBuilder.cs b/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/FieldBuilder.cs index 05a1cf535..29540e8a5 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/FieldBuilder.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/FieldBuilder.cs @@ -66,5 +66,12 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates.Builders return this; } + + public FieldBuilder ShowInReferences() + { + field.Properties.IsReferenceField = true; + + return this; + } } } diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/Guards/FieldPropertiesValidator.cs b/src/Squidex.Domain.Apps.Entities/Schemas/Guards/FieldPropertiesValidator.cs index 7eb970464..aeb29743e 100644 --- a/src/Squidex.Domain.Apps.Entities/Schemas/Guards/FieldPropertiesValidator.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/Guards/FieldPropertiesValidator.cs @@ -28,6 +28,11 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards yield return new ValidationError("UI field cannot be a list field.", nameof(properties.IsListField)); } + if (!properties.IsForApi() && properties.IsReferenceField) + { + yield return new ValidationError("UI field cannot be a reference field.", nameof(properties.IsReferenceField)); + } + foreach (var error in properties.Accept(Instance)) { yield return error; diff --git a/src/Squidex/Areas/Api/Controllers/Schemas/Models/FieldPropertiesDto.cs b/src/Squidex/Areas/Api/Controllers/Schemas/Models/FieldPropertiesDto.cs index b09c7d002..a539a03ad 100644 --- a/src/Squidex/Areas/Api/Controllers/Schemas/Models/FieldPropertiesDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Schemas/Models/FieldPropertiesDto.cs @@ -47,6 +47,11 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models /// public bool IsListField { get; set; } + /// + /// Determines if the field should be displayed in reference lists. + /// + public bool IsReferenceField { get; set; } + /// /// Optional url to the editor. /// diff --git a/src/Squidex/app/features/content/pages/content/content-page.component.ts b/src/Squidex/app/features/content/pages/content/content-page.component.ts index 02d6ada50..08b7c4e56 100644 --- a/src/Squidex/app/features/content/pages/content/content-page.component.ts +++ b/src/Squidex/app/features/content/pages/content/content-page.component.ts @@ -115,7 +115,7 @@ export class ContentPageComponent extends ResourceOwner implements CanComponentD } public canDeactivate(): Observable { - if (!this.contentForm.form.dirty || !this.content) { + if (!this.contentForm.form.dirty) { return of(true); } else { return this.dialogs.confirm('Unsaved changes', 'You have unsaved changes, do you want to close the current content view and discard your changes?'); diff --git a/src/Squidex/app/features/content/pages/contents/contents-page.component.html b/src/Squidex/app/features/content/pages/contents/contents-page.component.html index eae57a6f3..b497183c0 100644 --- a/src/Squidex/app/features/content/pages/contents/contents-page.component.html +++ b/src/Squidex/app/features/content/pages/contents/contents-page.component.html @@ -90,11 +90,12 @@ diff --git a/src/Squidex/app/features/content/shared/content-item.component.html b/src/Squidex/app/features/content/shared/content-item.component.html index e77f981dd..3d390f99d 100644 --- a/src/Squidex/app/features/content/shared/content-item.component.html +++ b/src/Squidex/app/features/content/shared/content-item.component.html @@ -11,7 +11,7 @@ - - diff --git a/src/Squidex/app/features/content/shared/references-dropdown.component.ts b/src/Squidex/app/features/content/shared/references-dropdown.component.ts index e5ca9f971..b4ad8191d 100644 --- a/src/Squidex/app/features/content/shared/references-dropdown.component.ts +++ b/src/Squidex/app/features/content/shared/references-dropdown.component.ts @@ -142,7 +142,7 @@ export class ReferencesDropdownComponent extends StatefulControlComponent { const values: any[] = []; - for (let field of schema.listFields) { + for (let field of schema.referenceFields) { const value = getRawValue(field, content.data, this.languageField); if (!Types.isUndefined(value)) { diff --git a/src/Squidex/app/features/content/shared/references-editor.component.html b/src/Squidex/app/features/content/shared/references-editor.component.html index cd11c82ac..3995986c9 100644 --- a/src/Squidex/app/features/content/shared/references-editor.component.html +++ b/src/Squidex/app/features/content/shared/references-editor.component.html @@ -1,12 +1,12 @@
- +
Click here to link content items.
-
+ diff --git a/src/Squidex/app/features/content/shared/content-item.component.ts b/src/Squidex/app/features/content/shared/content-item.component.ts index 8e038d7b2..650ae0501 100644 --- a/src/Squidex/app/features/content/shared/content-item.component.ts +++ b/src/Squidex/app/features/content/shared/content-item.component.ts @@ -58,6 +58,9 @@ export class ContentItemComponent implements OnChanges { @Input() public schema: SchemaDetailsDto; + @Input() + public schemaFields: RootFieldDto[]; + @Input() public canClone: boolean; @@ -148,7 +151,7 @@ export class ContentItemComponent implements OnChanges { private updateValues() { this.values = []; - for (let field of this.schema.listFields) { + for (let field of this.schemaFields) { const value = this.getRawValue(field); if (Types.isUndefined(value)) { diff --git a/src/Squidex/app/features/content/shared/contents-selector.component.html b/src/Squidex/app/features/content/shared/contents-selector.component.html index e4698264a..b56015d4f 100644 --- a/src/Squidex/app/features/content/shared/contents-selector.component.html +++ b/src/Squidex/app/features/content/shared/contents-selector.component.html @@ -33,7 +33,7 @@ +
@@ -15,7 +15,8 @@ [isReadOnly]="true" [isReference]="true" [isCompact]="isCompact" - [schema]="snapshot.schema" + [schema]="schema" + [schemaFields]="schema.referenceFields" (delete)="remove(content)"> diff --git a/src/Squidex/app/features/schemas/pages/schema/forms/field-form-common.component.ts b/src/Squidex/app/features/schemas/pages/schema/forms/field-form-common.component.ts index 721b99de4..4e813eb43 100644 --- a/src/Squidex/app/features/schemas/pages/schema/forms/field-form-common.component.ts +++ b/src/Squidex/app/features/schemas/pages/schema/forms/field-form-common.component.ts @@ -68,6 +68,21 @@ import { FieldDto } from '@app/shared'; + +
+
+
+ + +
+ + + Reference fields are shown as a column in the content list when referenced by another content.
When no reference field is defined, the first field is used. +
+
+
` }) @@ -88,6 +103,9 @@ export class FieldFormCommonComponent implements OnInit { this.editForm.setControl('isListField', new FormControl(this.field.properties.isListField)); + this.editForm.setControl('isReferenceField', + new FormControl(this.field.properties.isReferenceField)); + this.editForm.setControl('editorUrl', new FormControl(this.field.properties.editorUrl)); diff --git a/src/Squidex/app/shared/services/schemas.service.ts b/src/Squidex/app/shared/services/schemas.service.ts index 79b0e9778..2439b6bd0 100644 --- a/src/Squidex/app/shared/services/schemas.service.ts +++ b/src/Squidex/app/shared/services/schemas.service.ts @@ -82,6 +82,7 @@ export class SchemaDto { export class SchemaDetailsDto extends SchemaDto { public listFields: RootFieldDto[]; public listFieldsEditable: RootFieldDto[]; + public referenceFields: RootFieldDto[]; constructor(links: ResourceLinks, id: string, name: string, category: string, properties: SchemaPropertiesDto, @@ -99,19 +100,24 @@ export class SchemaDetailsDto extends SchemaDto { super(links, id, name, category, properties, isSingleton, isPublished, created, createdBy, lastModified, lastModifiedBy, version); if (fields) { - let listFields = this.fields.filter(x => x.properties.isListField && x.properties.isContentField); + this.listFields = this.getField(x => x.properties.isListField); + this.listFieldsEditable = this.listFields.filter(x => x.isInlineEditable); - if (listFields.length === 0 && this.fields.length > 0) { - listFields = [this.fields[0]]; - } + this.referenceFields = this.getField(x => x.properties.isReferenceField); + } + } - if (listFields.length === 0) { - listFields = NONE_FIELDS; - } + private getField(predicate: (field: RootFieldDto) => boolean) { + let fields = this.fields.filter(x => predicate(x) && x.properties.isContentField); - this.listFields = listFields; - this.listFieldsEditable = listFields.filter(x => x.isInlineEditable); + if (fields.length === 0 && this.fields.length > 0) { + fields = [this.fields[0]]; + } + if (fields.length === 0) { + fields = NONE_FIELDS; } + + return fields; } public export(): any { diff --git a/src/Squidex/app/shared/services/schemas.types.ts b/src/Squidex/app/shared/services/schemas.types.ts index c9df2017c..cb5059939 100644 --- a/src/Squidex/app/shared/services/schemas.types.ts +++ b/src/Squidex/app/shared/services/schemas.types.ts @@ -141,6 +141,7 @@ export abstract class FieldPropertiesDto { public readonly placeholder?: string; public readonly isRequired: boolean = false; public readonly isListField: boolean = false; + public readonly isReferenceField: boolean = false; constructor(public readonly editor: string, props?: Partial diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/GuardSchemaFieldTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/GuardSchemaFieldTests.cs index 8eacb18b6..1458a3c05 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/GuardSchemaFieldTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/GuardSchemaFieldTests.cs @@ -261,6 +261,15 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards new ValidationError("UI field cannot be a list field.", "Properties.IsListField")); } + [Fact] + public void CanUpdate_should_throw_exception_if_marking_a_ui_field_as_reference_field() + { + var command = new UpdateField { FieldId = 4, Properties = new UIFieldProperties { IsReferenceField = true } }; + + ValidationAssert.Throws(() => GuardSchemaField.CanUpdate(schema_0, command), + new ValidationError("UI field cannot be a reference field.", "Properties.IsReferenceField")); + } + [Fact] public void CanUpdate_should_throw_exception_if_properties_null() { diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/GuardSchemaTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/GuardSchemaTests.cs index dae464709..42b65cdd7 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/GuardSchemaTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/GuardSchemaTests.cs @@ -354,7 +354,8 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards Name = "field1", Properties = new UIFieldProperties { - IsListField = true + IsListField = true, + IsReferenceField = true, }, IsHidden = true, IsDisabled = true, @@ -367,6 +368,8 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards await ValidationAssert.ThrowsAsync(() => GuardSchema.CanCreate(command, appProvider), new ValidationError("UI field cannot be a list field.", "Fields[1].Properties.IsListField"), + new ValidationError("UI field cannot be a reference field.", + "Fields[1].Properties.IsReferenceField"), new ValidationError("UI field cannot be hidden.", "Fields[1].IsHidden"), new ValidationError("UI field cannot be disabled.",