/* * Squidex Headless CMS * * @license * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ // tslint:disable: max-line-length // tslint:disable: prefer-for-of import { ValidatorFn, Validators } from '@angular/forms'; import { DateTime, Types, ValidatorsEx } from '@app/framework'; import { ContentDto, ContentReferencesValue } from './../services/contents.service'; import { LanguageDto } from './../services/languages.service'; import { FieldDto, RootFieldDto } from './../services/schemas.service'; import { ArrayFieldPropertiesDto, AssetsFieldPropertiesDto, BooleanFieldPropertiesDto, DateTimeFieldPropertiesDto, fieldInvariant, FieldPropertiesVisitor, GeolocationFieldPropertiesDto, JsonFieldPropertiesDto, NumberFieldPropertiesDto, ReferencesFieldPropertiesDto, StringFieldPropertiesDto, TagsFieldPropertiesDto, UIFieldPropertiesDto } from './../services/schemas.types'; export class HtmlValue { constructor( public readonly html: string ) { } } export type FieldValue = string | HtmlValue; export function getContentValue(content: ContentDto, language: LanguageDto, field: RootFieldDto, allowHtml = true): { value: any, formatted: FieldValue } { if (content.referenceData) { const reference = content.referenceData[field.name]; const isAssets = field.properties.fieldType === 'Assets'; if (reference && (!isAssets || allowHtml)) { let fieldValue: ContentReferencesValue; if (field.isLocalizable) { fieldValue = reference[language.iso2Code]; } else { fieldValue = reference[fieldInvariant]; } let value: string | undefined; if (Types.isObject(fieldValue)) { value = fieldValue[language.iso2Code]; } else { value = fieldValue; } let formatted: FieldValue = value!; if (value) { if (isAssets && Types.isArray(value)) { if (value.length === 2) { const previewMode = field.properties['previewMode']; if (previewMode === 'ImageAndFileName') { formatted = new HtmlValue(` ${value[1]}`); } else if (previewMode === 'Image') { formatted = new HtmlValue(``); } else { formatted = value[1]; } } else if (value.length === 1) { formatted = value[0]; } } } else { value = formatted = '-'; } return { value, formatted }; } } const contentField = content.data[field.name]; if (contentField) { let value: any; if (field.isLocalizable) { value = contentField[language.iso2Code]; } else { value = contentField[fieldInvariant]; } let formatted: any; if (Types.isUndefined(value)) { formatted = value || ''; } else { formatted = FieldFormatter.format(field, value, allowHtml); } return { value, formatted }; } return { value: undefined, formatted: '' }; } export class FieldFormatter implements FieldPropertiesVisitor { private constructor( private readonly value: any, private readonly allowHtml: boolean ) { } public static format(field: FieldDto, value: any, allowHtml = true) { if (value === null || value === undefined) { return ''; } return field.properties.accept(new FieldFormatter(value, allowHtml)); } public visitArray(_: ArrayFieldPropertiesDto): string { return this.formatArray('Item', 'Items'); } public visitAssets(_: AssetsFieldPropertiesDto): string { return this.formatArray('Asset', 'Assets'); } public visitBoolean(_: BooleanFieldPropertiesDto): string { return this.value ? 'Yes' : 'No'; } public visitDateTime(properties: DateTimeFieldPropertiesDto): FieldValue { try { const parsed = DateTime.parseISO(this.value); if (properties.editor === 'Date') { return parsed.toStringFormatUTC('P'); } else { return parsed.toStringFormat('Ppp'); } } catch (ex) { return this.value; } } public visitGeolocation(_: GeolocationFieldPropertiesDto): string { return `${this.value.longitude}, ${this.value.latitude}`; } public visitJson(_: JsonFieldPropertiesDto): string { return ''; } public visitNumber(properties: NumberFieldPropertiesDto): FieldValue { if (Types.isNumber(this.value) && properties.editor === 'Stars' && this.allowHtml) { if (this.value <= 0 || this.value > 6) { return new HtmlValue(`★ ${this.value}`); } else { let html = ''; for (let i = 0; i < this.value; i++) { html += '★ '; } return new HtmlValue(html); } } return `${this.value}`; } public visitReferences(_: ReferencesFieldPropertiesDto): string { return this.formatArray('Reference', 'References'); } public visitTags(_: TagsFieldPropertiesDto): string { if (this.value.length) { return this.value.join(', '); } else { return ''; } } public visitString(properties: StringFieldPropertiesDto): any { if (properties.editor === 'StockPhoto' && this.allowHtml && this.value) { const src = thumbnail(this.value, undefined, 50); if (src) { return new HtmlValue(``); } } return this.value; } public visitUI(_: UIFieldPropertiesDto): any { return ''; } private formatArray(singularName: string, pluralName: string) { if (Types.isArray(this.value)) { if (this.value.length > 1) { return `${this.value.length} ${pluralName}`; } else if (this.value.length === 1) { return `1 ${singularName}`; } } return `0 ${pluralName}`; } } export function thumbnail(url: string, width?: number, height?: number) { if (url && url.startsWith('https://images.unsplash.com')) { if (width) { return `${url}&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=${width}&fit=max`; } if (height) { return `${url}&q=80&fm=jpg&crop=entropy&cs=tinysrgb&h=${height}&fit=max`; } } return undefined; } export class FieldsValidators implements FieldPropertiesVisitor> { private constructor( private readonly isOptional: boolean ) { } public static create(field: FieldDto, isOptional: boolean) { const validators = [...field.properties.accept(new FieldsValidators(isOptional))]; if (field.properties.isRequired && !isOptional) { validators.push(Validators.required); } return validators; } public visitArray(properties: ArrayFieldPropertiesDto): ReadonlyArray { const validators: ValidatorFn[] = [ ValidatorsEx.betweenLength(properties.minItems, properties.maxItems) ]; return validators; } public visitAssets(properties: AssetsFieldPropertiesDto): ReadonlyArray { const validators: ValidatorFn[] = [ ValidatorsEx.betweenLength(properties.minItems, properties.maxItems) ]; if (!properties.allowDuplicates) { validators.push(ValidatorsEx.uniqueStrings()); } return validators; } public visitBoolean(_: BooleanFieldPropertiesDto): ReadonlyArray { return []; } public visitDateTime(_: DateTimeFieldPropertiesDto): ReadonlyArray { return []; } public visitGeolocation(_: GeolocationFieldPropertiesDto): ReadonlyArray { return []; } public visitJson(_: JsonFieldPropertiesDto): ReadonlyArray { return []; } public visitNumber(properties: NumberFieldPropertiesDto): ReadonlyArray { const validators: ValidatorFn[] = [ ValidatorsEx.between(properties.minValue, properties.maxValue) ]; if (properties.allowedValues && properties.allowedValues.length > 0) { const values: ReadonlyArray<(number | null)> = properties.allowedValues; if (properties.isRequired && !this.isOptional) { validators.push(ValidatorsEx.validValues(values)); } else { validators.push(ValidatorsEx.validValues(values.concat([null]))); } } return validators; } public visitReferences(properties: ReferencesFieldPropertiesDto): ReadonlyArray { const validators: ValidatorFn[] = [ ValidatorsEx.betweenLength(properties.minItems, properties.maxItems) ]; if (!properties.allowDuplicates) { validators.push(ValidatorsEx.uniqueStrings()); } return validators; } public visitString(properties: StringFieldPropertiesDto): ReadonlyArray { const validators: ValidatorFn[] = [ ValidatorsEx.betweenLength(properties.minLength, properties.maxLength) ]; if (properties.pattern && properties.pattern.length > 0) { validators.push(ValidatorsEx.pattern(properties.pattern, properties.patternMessage)); } if (properties.allowedValues && properties.allowedValues.length > 0) { const values: ReadonlyArray = properties.allowedValues; if (properties.isRequired && !this.isOptional) { validators.push(ValidatorsEx.validValues(values)); } else { validators.push(ValidatorsEx.validValues(values.concat([null]))); } } return validators; } public visitTags(properties: TagsFieldPropertiesDto): ReadonlyArray { const validators: ValidatorFn[] = [ ValidatorsEx.betweenLength(properties.minItems, properties.maxItems) ]; if (properties.allowedValues && properties.allowedValues.length > 0) { const values: ReadonlyArray = properties.allowedValues; validators.push(ValidatorsEx.validArrayValues(values)); } return validators; } public visitUI(_: UIFieldPropertiesDto): ReadonlyArray { return []; } } export class FieldDefaultValue implements FieldPropertiesVisitor { private constructor( private readonly partitionKey: string, private readonly now?: DateTime ) { } public static get(field: FieldDto, partitionKey: string, now?: DateTime) { return field.properties.accept(new FieldDefaultValue(partitionKey, now)); } public visitDateTime(properties: DateTimeFieldPropertiesDto): any { const now = this.now || DateTime.now(); if (properties.calculatedDefaultValue === 'Now') { return `${now.toStringFormatUTC('yyyy-MM-dd\'T\'HH:mm:ss')}Z`; } else if (properties.calculatedDefaultValue === 'Today') { return `${now.toISODate()}T00:00:00Z`; } else { return this.getValue(properties.defaultValue, properties.defaultValues); } } public visitArray(_: ArrayFieldPropertiesDto): any { return null; } public visitAssets(properties: AssetsFieldPropertiesDto): any { return this.getValue(properties.defaultValue, properties.defaultValues); } public visitBoolean(properties: BooleanFieldPropertiesDto): any { return this.getValue(properties.defaultValue, properties.defaultValues); } public visitGeolocation(_: GeolocationFieldPropertiesDto): any { return null; } public visitJson(_: JsonFieldPropertiesDto): any { return null; } public visitNumber(properties: NumberFieldPropertiesDto): any { return this.getValue(properties.defaultValue, properties.defaultValues); } public visitReferences(properties: ReferencesFieldPropertiesDto): any { return this.getValue(properties.defaultValue, properties.defaultValues); } public visitString(properties: StringFieldPropertiesDto): any { return this.getValue(properties.defaultValue, properties.defaultValues); } public visitTags(properties: TagsFieldPropertiesDto): any { return this.getValue(properties.defaultValue, properties.defaultValues); } public visitUI(_: UIFieldPropertiesDto): any { return null; } private getValue(value: any, values?: object) { if (values && values.hasOwnProperty(this.partitionKey)) { return values[this.partitionKey]; } return value; } }