This commit is contained in:
Carlos Villaronga 2025-08-13 10:24:41 +02:00 committed by GitHub
commit 5d7bec029f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 126 additions and 1 deletions

View file

@ -31,6 +31,7 @@ import {FormatOptions, Formatter} from './formatter.model';
import {CurrencyFormatter} from './currency/currency-formatter.service';
import {DateFormatter} from './datetime/date-formatter.service';
import {PhoneFormatter} from './phone/phone-formatter.service';
import {TextFormatter} from "./text/text-formatter.service";
export interface TypeFormatterMap {
[key: string]: Formatter;
@ -49,6 +50,7 @@ export class DataTypeFormatter {
protected dateFormatter: DateFormatter,
protected datetimeFormatter: DatetimeFormatter,
protected phoneFormatter: PhoneFormatter,
protected text: TextFormatter,
) {
this.map.int = numberFormatter;
this.map.float = numberFormatter;
@ -56,6 +58,7 @@ export class DataTypeFormatter {
this.map.datetime = datetimeFormatter;
this.map.currency = currencyFormatter;
this.map.phone = phoneFormatter;
this.map.text = text;
}
toUserFormat(dataType: string, value: string, options?: FormatOptions): string {

View file

@ -30,11 +30,13 @@ import {numberFormatterMock} from './number/number-formatter.spec.mock';
import {datetimeFormatterMock} from './datetime/datetime-formatter.service.spec.mock';
import {phoneFormatterMock} from './phone/phone-formatter.spec.mock';
import {dateFormatterMock} from './datetime/date-formatter.service.spec.mock';
import {textFormatterMock} from "./text/text-formatter.service.spec.mock";
export const dataTypeFormatterMock = new DataTypeFormatter(
currencyFormatterMock,
numberFormatterMock,
dateFormatterMock,
datetimeFormatterMock,
phoneFormatterMock
phoneFormatterMock,
textFormatterMock
);

View file

@ -0,0 +1,31 @@
/**
* SuiteCRM is a customer relationship management program developed by SalesAgility Ltd.
* Copyright (C) 2021 SalesAgility Ltd.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by the
* Free Software Foundation with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SALESAGILITY, SALESAGILITY DISCLAIMS THE
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program 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 Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* In accordance with Section 7(b) of the GNU Affero General Public License
* version 3, these Appropriate Legal Notices must retain the display of the
* "Supercharged by SuiteCRM" logo. If the display of the logos is not reasonably
* feasible for technical reasons, the Appropriate Legal Notices must display
* the words "Supercharged by SuiteCRM".
*/
import { TextFormatter } from './text-formatter.service';
import {FormControlUtils} from '../../../services/record/field/form-control.utils';
import {userPreferenceStoreMock} from '../../../store/user-preference/user-preference.store.spec.mock';
export const textFormatterMock = new TextFormatter(userPreferenceStoreMock,new FormControlUtils(),'en_US');

View file

@ -0,0 +1,85 @@
import {Inject, Injectable, LOCALE_ID} from '@angular/core';
import {FormatOptions, Formatter} from '../formatter.model';
import { FormControlUtils } from '../../record/field/form-control.utils';
import {UserPreferenceStore} from "../../../store/user-preference/user-preference.store";
@Injectable({
providedIn: 'root'
})
export class TextFormatter implements Formatter {
private defaultMap: { [key: string]: string } = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#39;',
'à': '&agrave;', 'á': '&aacute;', 'â': '&acirc;', 'ã': '&atilde;', 'ä': '&auml;',
'è': '&egrave;', 'é': '&eacute;', 'ê': '&ecirc;', 'ë': '&euml;',
'ì': '&igrave;', 'í': '&iacute;', 'î': '&icirc;', 'ï': '&iuml;',
'ò': '&ograve;', 'ó': '&oacute;', 'ô': '&ocirc;', 'õ': '&otilde;', 'ö': '&ouml;',
'ù': '&ugrave;', 'ú': '&uacute;', 'û': '&ucirc;', 'ü': '&uuml;',
'ñ': '&ntilde;', 'Ñ': '&Ntilde;',
'ç': '&ccedil;', 'Ç': '&Ccedil;',
'€': '&euro;',
'¥': '&yen;',
'£': '&pound;',
'¢': '&cent;',
'©': '&copy;',
'®': '&reg;',
'™': '&trade;',
'§': '&sect;',
'¶': '&para;',
'•': '&bull;',
'…': '&hellip;',
'': '&ndash;',
'—': '&mdash;',
// ... add more characters as needed
};
constructor(
protected preferences: UserPreferenceStore,
protected formUtils: FormControlUtils,
@Inject(LOCALE_ID) public locale: string
) {}
/**
* Converts the value to a format suitable for display to the user.
* Escapes HTML special characters.
*/
toUserFormat(value: string, options?: FormatOptions): string {
// Reverse the map for decoding
const decodeMap: { [key: string]: string } = Object.entries(this.defaultMap).reduce((acc, [char, entity]) => {
acc[entity] = char;
return acc;
}, {});
// Create a regex to match all entities in the text
const entityRegex = new RegExp(Object.keys(decodeMap).join('|'), 'g');
// Recursively decode until no more encoded entities remain
let decodedValue = value;
let previousValue;
do {
previousValue = decodedValue;
decodedValue = decodedValue?.replace(entityRegex, (match) => decodeMap[match] || match);
} while (decodedValue !== previousValue); // Repeat until no changes
return decodedValue;
}
/**
* Converts the user input back to the internal format.
* Decodes HTML special characters.
*/
toInternalFormat(value): string {
const formmatedValue = value?.replace(/[<>&"'\u00A0-\uFFFF]/g, (match) => {
const encoded = this.defaultMap[match];
return encoded !== undefined ? encoded : match;
});
return formmatedValue;
}
}

View file

@ -204,6 +204,10 @@ export class FieldBuilder {
if (defaultValue) {
field.default = defaultValue;
}
// issue #542 formmattedValue applied to value (only for text fields?)
if(field.type == 'text'){
field.value = formattedValue;
}
field.defaultValueModes = viewField?.defaultValueModes ?? definition?.defaultValueModes ?? [];