scrivito
Version:
Scrivito is a professional, yet easy to use SaaS Enterprise Content Management Service, built for digital agencies and medium to large businesses. It is completely maintenance-free, cost-effective, and has unprecedented performance and security.
382 lines (324 loc) • 9.53 kB
text/typescript
import escape from 'lodash-es/escape';
import isDate from 'lodash-es/isDate';
import { currentSiteId } from 'scrivito_sdk/app_support/current_page';
import { getDetailsPageAndQuery } from 'scrivito_sdk/app_support/get_details_page_url';
import { hasComponent } from 'scrivito_sdk/app_support/has_component';
import {
InternalError,
assumeString,
convertToFloat,
convertToInteger,
isPresent,
logError,
} from 'scrivito_sdk/common';
import {
DataItem,
DataScope,
DataStack,
applyDataLocator,
} from 'scrivito_sdk/data_integration';
import {
BasicLink,
BasicObj,
BasicTypeInfo,
BasicWidget,
CmsAttributeType,
getContentValueOrConnection,
isContentConnection,
} from 'scrivito_sdk/models';
import { useDataStackOrThrow } from 'scrivito_sdk/react/data_context_container';
import {
AttributeDefinitions,
AttributeValueOf,
Obj,
Schema,
Widget,
unwrapAppClass,
wrapInAppClass,
} from 'scrivito_sdk/realm';
/** @public */
export function useContent<A extends keyof AttributeDefinitions & string>(
content: Obj | Widget,
attributeName: string
): AttributeValueOf<AttributeDefinitions, A> {
return resolveContent(content, attributeName, useDataStackOrThrow());
}
// Exported for test purpose only
export function resolveContent(
content: Obj | Widget,
attributeName: string,
dataStack: DataStack
): ReturnType<typeof useContent> {
const typeInfo = getTypeInfo(content, attributeName);
if (!typeInfo) return null;
return wrapInAppClass(
resolveBasicContent(
unwrapAppClass(content),
attributeName,
typeInfo,
dataStack
)
);
}
function resolveBasicContent<T extends CmsAttributeType>(
basicContent: BasicObj | BasicWidget,
attributeName: string,
typeInfo: BasicTypeInfo<T>,
dataStack: DataStack
) {
const valueOrConnection = getContentValueOrConnection(
basicContent,
attributeName,
typeInfo
);
if (isContentConnection(valueOrConnection)) {
return toBasicValue(
applyDataLocator(dataStack, valueOrConnection[1]),
typeInfo
);
}
return valueOrConnection[1];
}
function toBasicValue<T extends CmsAttributeType>(
dataScope: DataScope,
typeInfo: BasicTypeInfo<T>
) {
const itemAttribute = dataScope.dataItemAttribute();
if (itemAttribute) {
return itemAttributeToBasicValue(
itemAttribute.dataItem(),
itemAttribute.attributeName(),
typeInfo
);
}
const dataItem = dataScope.dataItem();
if (dataItem) return itemToBasicValue(dataItem, typeInfo[0]);
return scopeToBasicValue(dataScope, typeInfo);
}
function scopeToBasicValue<T extends CmsAttributeType>(
dataScope: DataScope,
[attributeType]: BasicTypeInfo<T>
) {
const items = dataScope.take();
if (attributeType === 'linklist') {
return items.map(itemToLink).filter(isPresent);
}
if (attributeType === 'referencelist') {
return items.map(itemToReference).filter(isPresent);
}
return toDefaultValue(attributeType);
}
function itemAttributeToBasicValue<T extends CmsAttributeType>(
dataItem: DataItem,
attributeName: string,
typeInfo: BasicTypeInfo<T>
) {
const obj = dataItem.obj();
if (obj) {
return objAttributeToBasicValue(obj, attributeName, typeInfo);
}
return externalAttributeToBasicValue(dataItem, attributeName, typeInfo);
}
function itemToBasicValue<T extends CmsAttributeType>(
dataItem: DataItem,
attributeType: T
) {
switch (attributeType) {
case 'reference':
return itemToReference(dataItem);
case 'referencelist':
return toList(itemToReference(dataItem));
case 'link':
return itemToLink(dataItem);
case 'linklist':
return toList(itemToLink(dataItem));
default:
return null;
}
}
function itemToReference(dataItem: DataItem) {
return unwrapAppClass(dataItem.obj()) || null;
}
function itemToLink(dataItem: DataItem) {
const obj = dataItem.obj();
let link: BasicLink | null = null;
if (obj && (hasComponent(obj.objClass()) || obj.isBinary())) {
link = new BasicLink({ objId: obj.id() });
}
const detailsPageAndQuery = getDetailsPageAndQuery(dataItem, currentSiteId());
if (detailsPageAndQuery) {
const { detailsPage, queryString } = detailsPageAndQuery;
link = new BasicLink({ objId: detailsPage.id(), query: queryString });
}
return link;
}
function toList<T>(value: T | null): [T] | [] {
return value === null ? [] : [value];
}
function objAttributeToBasicValue<T extends CmsAttributeType>(
obj: Obj,
attributeName: string,
typeInfo: BasicTypeInfo<T>
) {
const basicObj = unwrapAppClass(obj);
if (getTypeInfo(obj, attributeName)?.[0] === 'datalocator') {
logError('Connecting a "datalocator" attribute is not possible');
return null;
}
return basicObj.get(attributeName, typeInfo);
}
function externalAttributeToBasicValue<T extends CmsAttributeType>(
dataItem: DataItem,
attributeName: string,
typeInfo: BasicTypeInfo<T>
) {
const externalAttributeType = dataItem.dataClass().attributeDefinitions()[
attributeName
]?.[0];
const unknownValue = dataItem.get(attributeName);
if (externalAttributeType === 'string') {
return externalStringAttributeToBasicValue(unknownValue, typeInfo);
}
if (externalAttributeType === 'enum') {
return externalEnumAttributeToBasicValue(unknownValue, typeInfo);
}
if (externalAttributeType === 'number') {
return externalNumberAttributeToBasicValue(unknownValue, typeInfo);
}
if (externalAttributeType === 'boolean') {
return externalBooleanAttributeToBasicValue(unknownValue, typeInfo);
}
if (externalAttributeType === 'date') {
return externalDateAttributeToBasicValue(unknownValue, typeInfo);
}
if (externalAttributeType === 'reference') {
return externalReferenceAttributeToBasicValue(unknownValue, typeInfo);
}
return null;
}
function externalStringAttributeToBasicValue<T extends CmsAttributeType>(
unknownValue: unknown,
typeInfo: BasicTypeInfo<T>
) {
const value = assumeString(unknownValue);
const [targetAttributeType, targetTypeInfoConfig] = typeInfo;
switch (targetAttributeType) {
case 'string':
return value;
case 'html':
return escape(value);
case 'enum':
return externalStringAttributeToEnumValue(value, targetTypeInfoConfig);
case 'multienum':
return toList(
externalStringAttributeToEnumValue(value, targetTypeInfoConfig)
);
case 'stringlist':
return toList(value);
default:
return null;
}
}
function externalEnumAttributeToBasicValue<T extends CmsAttributeType>(
unknownValue: unknown,
typeInfo: BasicTypeInfo<T>
) {
const value = assumeStringOrNull(unknownValue);
const [targetAttributeType] = typeInfo;
if (value === null) {
switch (targetAttributeType) {
case 'string':
return '';
case 'multienum':
return [];
default:
return null;
}
}
return externalStringAttributeToBasicValue(unknownValue, typeInfo);
}
function externalNumberAttributeToBasicValue<T extends CmsAttributeType>(
unknownValue: unknown,
typeInfo: BasicTypeInfo<T>
) {
const value = assumeNumber(unknownValue);
const [targetAttributeType] = typeInfo;
switch (targetAttributeType) {
case 'float':
return convertToFloat(value);
case 'integer':
return convertToInteger(value);
default:
return null;
}
}
function externalBooleanAttributeToBasicValue<T extends CmsAttributeType>(
unknownValue: unknown,
typeInfo: BasicTypeInfo<T>
) {
const value = assumeBoolean(unknownValue);
const [targetAttributeType] = typeInfo;
return targetAttributeType === 'boolean' ? value : toDefaultValue('boolean');
}
function externalDateAttributeToBasicValue<T extends CmsAttributeType>(
unknownValue: unknown,
typeInfo: BasicTypeInfo<T>
) {
const value = assumeDateOrNull(unknownValue);
const [targetAttributeType] = typeInfo;
return targetAttributeType === 'date' || targetAttributeType === 'datetime'
? value
: null;
}
function externalReferenceAttributeToBasicValue<T extends CmsAttributeType>(
unknownValue: unknown,
typeInfo: BasicTypeInfo<T>
) {
if (unknownValue instanceof DataItem) {
switch (typeInfo[0]) {
case 'link':
return itemToLink(unknownValue);
case 'linklist':
return toList(itemToLink(unknownValue));
default:
null;
}
}
return null;
}
function externalStringAttributeToEnumValue(
value: string,
typeInfoConfig: BasicTypeInfo<'enum'>[1]
) {
return typeInfoConfig.values.includes(value) ? value : null;
}
function toDefaultValue<T extends CmsAttributeType>(attributeType: T) {
switch (attributeType) {
case 'linklist':
case 'referencelist':
return [];
case 'boolean':
return false;
default:
return null;
}
}
function getTypeInfo(content: Obj | Widget, attributeName: string) {
return Schema.forInstance(content)?.attributes()[attributeName];
}
function assumeStringOrNull(value: unknown): string | null {
if (value === null || typeof value === 'string') return value;
throw new InternalError();
}
function assumeBoolean(value: unknown) {
if (typeof value === 'boolean') return value;
throw new InternalError();
}
function assumeNumber(value: unknown) {
if (typeof value === 'number') return value;
throw new InternalError();
}
function assumeDateOrNull(value: unknown): Date | null {
if (value === null || isDate(value)) return value;
throw new InternalError();
}