Source: data-engine-taglib/src/main/resources/META-INF/resources/data_layout_builder/js/utils/dataConverter.es.js

/**
 * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 2.1 of the License, or (at your option)
 * any later version.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 */

import {FieldSupport} from 'dynamic-data-mapping-form-builder';
import {PagesVisitor} from 'dynamic-data-mapping-form-renderer';

import {getDataDefinitionField as getDataDefinitionFieldUtils} from './dataDefinition.es';
import {
	normalizeDataDefinition,
	normalizeDataLayout,
	normalizeDataLayoutRows,
} from './normalizers.es';

export function getDataDefinitionFieldSet({
	allowInvalidAvailableLocalesForProperty,
	availableLanguageIds,
	defaultLanguageId,
	editingLanguageId,
	fieldSet,
	fieldTypes,
}) {
	const dataLayoutPages = (
		fieldSet.defaultDataLayout || getDefaultDataLayout(fieldSet)
	).dataLayoutPages;

	if (!availableLanguageIds.includes(defaultLanguageId)) {
		availableLanguageIds = [...availableLanguageIds, defaultLanguageId];
	}

	return {
		fieldSet: getFieldSetDDMForm({
			allowInvalidAvailableLocalesForProperty,
			availableLanguageIds,
			editingLanguageId,
			fieldSet,
			fieldTypes,
		}),
		...(fieldSet.id && {
			rows: normalizeDataLayoutRows(dataLayoutPages),
		}),
	};
}

export function getDDMFormField({
	dataDefinition,
	defaultLanguageId = themeDisplay.getDefaultLanguageId(),
	editingLanguageId = defaultLanguageId,
	fieldName,
	fieldTypes,
}) {
	const dataDefinitionField = getDataDefinitionFieldUtils(
		dataDefinition,
		fieldName
	);

	if (dataDefinitionField.fieldType === 'ddm-text-html') {
		dataDefinitionField.fieldType = 'rich_text';
	}

	const settingsContext = getDDMFormFieldSettingsContext({
		dataDefinitionField,
		defaultLanguageId,
		editingLanguageId,
		fieldTypes,
	});

	const ddmFormField = {
		nestedFields: dataDefinitionField.nestedDataDefinitionFields,
		settingsContext,
	};
	const visitor = new PagesVisitor(settingsContext.pages);

	visitor.mapFields((field) => {
		const {fieldName} = field;
		let {value} = field;

		if (fieldName === 'options' && value) {
			value = value[editingLanguageId];
		}
		else if (fieldName === 'name') {
			ddmFormField.fieldName = value;
		}

		ddmFormField[fieldName] = value;
	});

	if (ddmFormField.nestedFields.length > 0) {
		ddmFormField.nestedFields = ddmFormField.nestedFields.map(
			(nestedField) =>
				getDDMFormField({
					dataDefinition,
					defaultLanguageId,
					editingLanguageId,
					fieldName: nestedField.name,
					fieldTypes,
				})
		);
	}

	if (!ddmFormField.instanceId) {
		ddmFormField.instanceId = FieldSupport.generateInstanceId(8);
	}

	return ddmFormField;
}

export function getDDMFormFieldSettingsContext({
	dataDefinitionField,
	defaultLanguageId = themeDisplay.getDefaultLanguageId(),
	editingLanguageId = themeDisplay.getDefaultLanguageId(),
	fieldTypes,
}) {
	const {settingsContext} = fieldTypes.find(({name}) => {
		return name === dataDefinitionField.fieldType;
	});

	const visitor = new PagesVisitor(settingsContext.pages);

	return {
		...settingsContext,
		pages: visitor.mapFields((field) => {
			const {fieldName, localizable} = field;
			const propertyValue = _getDataDefinitionFieldPropertyValue(
				dataDefinitionField,
				_fromDDMFormToDataDefinitionPropertyName(fieldName)
			);

			let value = propertyValue ?? field.value;

			if (localizable && propertyValue && fieldName !== 'label') {
				value =
					propertyValue[editingLanguageId] ||
					propertyValue[defaultLanguageId];
			}

			let localizedValue = {};

			if (localizable) {
				localizedValue = {...propertyValue};
			}

			if (Object.keys(localizedValue).length == 0) {
				localizedValue = {[defaultLanguageId]: ''};
			}

			let multiple = field.multiple;
			let options = field.options;

			if (
				field.type === 'select' &&
				field.fieldName === 'predefinedValue'
			) {
				multiple = dataDefinitionField.customProperties.multiple;
				options =
					dataDefinitionField.customProperties.options[
						editingLanguageId
					];
			}

			return {
				...field,
				defaultLanguageId,
				locale: defaultLanguageId,
				localizedValue,
				multiple,
				options,
				value,
			};
		}),
	};
}

export function getDefaultDataLayout(dataDefinition) {
	const {dataDefinitionFields} = dataDefinition;

	return {
		dataLayoutPages: [
			{
				dataLayoutRows: dataDefinitionFields.map(({name}) => ({
					dataLayoutColumns: [
						{
							columnSize: 12,
							fieldNames: [name],
						},
					],
				})),
			},
		],
	};
}

export function getDataDefinitionAndDataLayout({
	availableLanguageIds = [themeDisplay.getDefaultLanguageId()],
	defaultLanguageId = themeDisplay.getDefaultLanguageId(),
	pages,
	paginationMode,
	rules = [],
}) {
	const fieldDefinitions = [];
	const pagesVisitor = new PagesVisitor(pages);

	const newPages = pagesVisitor.mapFields((field) => {
		fieldDefinitions.push(getDataDefinitionField(field));

		return field.fieldName;
	}, false);

	return {
		definition: {
			availableLanguageIds,
			dataDefinitionFields: fieldDefinitions,
			defaultLanguageId,
		},
		layout: {
			dataLayoutPages: newPages.map((page) => {
				const rows = page.rows.map((row) => {
					const columns = row.columns.map((column) => {
						return {
							columnSize: column.size,
							fieldNames: column.fields,
						};
					});

					return {
						dataLayoutColumns: columns,
					};
				});

				return {
					dataLayoutRows: rows,
					description: page.localizedDescription,
					title: page.localizedTitle,
				};
			}),
			dataRules: rules.map((rule) => {
				if (typeof rule.name === 'string') {
					rule.name = {
						[defaultLanguageId]: rule.name,
					};
				}

				delete rule.ruleEditedIndex;

				return rule;
			}),
			paginationMode,
		},
	};
}

/**
 * Gets a data definition from a field
 *
 * @param {object} field - The field
 * @param {Object[]} field.nestedFields - The array containing all nested fields.
 * 										  It may be undefined
 * @param {object} field.settingsContext - The settings context of a field
 */
export function getDataDefinitionField({nestedFields = [], settingsContext}) {
	const nestedDataDefinitionFields = nestedFields.map((field) =>
		getDataDefinitionField(field)
	);
	const dataDefinition = {
		customProperties: {},
		nestedDataDefinitionFields,
	};
	const settingsContextVisitor = new PagesVisitor(settingsContext.pages);

	settingsContextVisitor.mapFields(
		({dataType, fieldName, localizable, localizedValue, value}) => {
			if (fieldName === 'predefinedValue') {
				fieldName = 'defaultValue';
			}
			else if (fieldName === 'type') {
				fieldName = 'fieldType';
			}

			const updatableHash = _isCustomProperty(fieldName)
				? dataDefinition.customProperties
				: dataDefinition;

			if (localizable) {
				updatableHash[fieldName] = localizedValue ?? {};
			}
			else {
				updatableHash[
					fieldName
				] = _getDataDefinitionFieldFormattedValue(dataType, value);
			}
		},
		false
	);

	return dataDefinition;
}

export function getDataDefinitionFieldByFieldName({
	dataDefinition,
	editingLanguageId,
	fieldName,
	fieldTypes,
}) {
	const dataDefinitionField = dataDefinition.dataDefinitionFields.find(
		(field) => field.name === fieldName
	);

	const settingsContext = getDDMFormFieldSettingsContext({
		dataDefinitionField,
		editingLanguageId,
		fieldTypes,
	});

	return {
		...dataDefinitionField,
		editingLanguageId,
		settingsContext,
	};
}

export function getDDMSettingsContextWithVisualProperties({
	dataDefinitionField,
	editingLanguageId,
	fieldTypes,
}) {
	const {pages} = getDDMFormFieldSettingsContext({
		dataDefinitionField,
		editingLanguageId,
		fieldTypes,
	});
	const visitor = new PagesVisitor(pages);

	const fieldProperties = {};

	visitor.mapFields(
		({fieldName, localizable, localizedValue, value, visualProperty}) => {
			if (visualProperty) {
				fieldProperties[fieldName] = localizable
					? localizedValue
					: value;
			}
		}
	);

	return fieldProperties;
}

/**
 * Converts a FieldSet from data-engine to form-builder data definition
 */
export function getFieldSetDDMForm({
	allowInvalidAvailableLocalesForProperty,
	availableLanguageIds,
	editingLanguageId,
	fieldSet,
	fieldTypes,
}) {
	const {defaultDataLayout, defaultLanguageId} = fieldSet;

	let newDataDefinition = {
		availableLanguageIds,
		...fieldSet,
	};

	if (!allowInvalidAvailableLocalesForProperty) {
		newDataDefinition = normalizeDataDefinition(
			newDataDefinition,
			defaultLanguageId
		);
	}

	const fieldSetDataLayout = normalizeDataLayout(
		defaultDataLayout,
		defaultLanguageId
	);

	return _getDDMForm({
		dataDefinition: newDataDefinition,
		dataLayout: fieldSetDataLayout,
		defaultLanguageId,
		editingLanguageId,
		fieldTypes,
	});
}

export function getFormData({
	availableLanguageIds,
	availableLanguageIdsState,
	defaultLanguageId,
	layoutProvider,
}) {
	const {
		props: {defaultLanguageId: layoutDefaultLanguageId},
		state: {pages: layoutProviderPages, rules},
	} = layoutProvider;

	const pagesVisitor = new PagesVisitor(layoutProviderPages);

	const pages = pagesVisitor.mapFields(
		(field) => {
			const {settingsContext} = field;

			const settingsContextPagesVisitor = new PagesVisitor(
				settingsContext.pages
			);

			const newSettingsContext = {
				...settingsContext,
				pages: settingsContextPagesVisitor.mapFields(
					(settingsField) => {
						if (settingsField.type === 'options') {
							const {value} = settingsField;
							const newValue = {};

							Object.keys(value).forEach((locale) => {
								newValue[locale] = value[locale]?.filter(
									(localizedValue) =>
										localizedValue.value !== '' &&
										localizedValue.label !== ''
								);
							});

							if (!newValue[layoutDefaultLanguageId]) {
								newValue[layoutDefaultLanguageId] = [];
							}

							settingsField = {
								...settingsField,
								value: newValue,
							};
						}

						return settingsField;
					}
				),
			};

			return {
				...field,
				settingsContext: newSettingsContext,
			};
		},
		true,
		true
	);

	return getDataDefinitionAndDataLayout({
		availableLanguageIds: availableLanguageIdsState ?? availableLanguageIds,
		defaultLanguageId,
		pages,
		paginationMode: layoutProvider.getPaginationMode(),
		rules,
	});
}

// private

function _fromDDMFormToDataDefinitionPropertyName(propertyName) {
	const map = {
		fieldName: 'name',
		nestedFields: 'nestedDataDefinitionFields',
		predefinedValue: 'defaultValue',
		type: 'fieldType',
	};

	return map[propertyName] || propertyName;
}

function _getDataDefinitionFieldFormattedValue(dataType, value) {
	if (dataType === 'json' && typeof value !== 'string') {
		return JSON.stringify(value);
	}

	return value;
}

function _getDataDefinitionFieldPropertyValue(
	dataDefinitionField,
	propertyName
) {
	const {customProperties} = dataDefinitionField;

	if (customProperties && _isCustomProperty(propertyName)) {
		return customProperties[propertyName];
	}

	return dataDefinitionField[propertyName];
}

function _getDDMForm({
	dataDefinition,
	dataLayout = getDefaultDataLayout(dataDefinition),
	defaultLanguageId: fieldSetDefaultLanguageId,
	editingLanguageId,
	fieldTypes,
}) {
	const {defaultLanguageId, name} = dataDefinition;

	return {
		description: dataDefinition.description[editingLanguageId],
		id: dataDefinition.id,
		localizedDescription: dataDefinition.description,
		localizedTitle: name,
		pages: dataLayout.dataLayoutPages.map((dataLayoutPage) => ({
			rows: dataLayoutPage.dataLayoutRows.map((dataLayoutRow) => ({
				columns: dataLayoutRow.dataLayoutColumns.map(
					({columnSize, fieldNames}) => ({
						fields: fieldNames.map((fieldName) =>
							getDDMFormField({
								dataDefinition,
								defaultLanguageId: fieldSetDefaultLanguageId,
								editingLanguageId,
								fieldName,
								fieldTypes,
							})
						),
						size: columnSize,
					})
				),
			})),
		})),
		title: name[editingLanguageId] || name[defaultLanguageId],
	};
}

function _isCustomProperty(name) {
	return ![
		'defaultValue',
		'fieldType',
		'indexable',
		'indexType',
		'label',
		'localizable',
		'name',
		'readOnly',
		'repeatable',
		'required',
		'showLabel',
		'tip',
	].includes(name);
}

// For test purpose only

export default {
	_fromDDMFormToDataDefinitionPropertyName,
	_getDataDefinitionFieldFormattedValue,
	_isCustomProperty,
};