add escapeHtml function & custom translate parser

This commit is contained in:
Dan Percic 2023-02-07 23:24:28 +02:00
parent 65da4e2d53
commit 40517f145e
5 changed files with 58 additions and 16 deletions

View File

@ -1,7 +1,5 @@
<section class="dialog">
<div [class.warn]="isDeleteAction" class="dialog-header heading-l">
{{ config.title }}
</div>
<div [class.warn]="isDeleteAction" [innerHTML]="config.title" class="dialog-header heading-l"></div>
<div *ngIf="showToast && config.toastMessage" class="inline-dialog-toast toast-error">
<div [translate]="config.toastMessage"></div>

View File

@ -6,7 +6,6 @@ import { TranslateService } from '@ngx-translate/core';
import { HttpErrorResponse, HttpStatusCode } from '@angular/common/http';
import { filter } from 'rxjs/operators';
import { ErrorMessageService } from './error-message.service';
import { stripHtml } from 'string-strip-html';
import { DomSanitizer } from '@angular/platform-browser';
const enum NotificationType {
@ -80,9 +79,7 @@ export class Toaster {
notificationType = NotificationType.INFO,
options?: Partial<ToasterOptions>,
): ActiveToast<unknown> {
const params = options?.params ? this._sanitizeParams(options.params) : undefined;
const translatedMsg = this._translateService.instant(message, params) as string;
const translatedMsg = this._translateService.instant(message, options?.params) as string;
switch (notificationType) {
case NotificationType.SUCCESS:
@ -94,11 +91,4 @@ export class Toaster {
return this._toastr.info(translatedMsg, options?.title, options);
}
}
private _sanitizeParams(params: Record<string, string | number | undefined>): Record<string, string | null> {
return Object.entries(params).reduce((acc, [key, value]) => {
acc[key] = stripHtml(value?.toString() ?? '').result;
return acc;
}, {} as Record<string, string | null>);
}
}

View File

@ -0,0 +1,12 @@
import { Injectable } from '@angular/core';
import { TranslateDefaultParser } from '@ngx-translate/core';
import { escapeHtml } from '../utils';
@Injectable()
export class IqserTranslateParser extends TranslateDefaultParser {
interpolate(expr: any, params?: Record<string, unknown>) {
const entries = Object.entries(params ?? {});
const escapedParams = entries.reduce((acc, [key, value]) => ({ ...acc, [key]: escapeHtml(value) }), {});
return super.interpolate(expr, escapedParams);
}
}

View File

@ -1,8 +1,9 @@
import { Inject, ModuleWithProviders, NgModule, Optional } from '@angular/core';
import { TranslateCompiler, TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { TranslateCompiler, TranslateLoader, TranslateModule, TranslateParser } from '@ngx-translate/core';
import { TranslateMessageFormatCompiler } from 'ngx-translate-messageformat-compiler';
import { pruningTranslationLoaderFactory } from './http-loader-factory';
import { IqserTranslateModuleOptions } from './iqser-translate-module-options';
import { IqserTranslateParser } from './iqser-translate-parser.service';
const translateLoaderToken = 'translateLoader';
@ -17,6 +18,10 @@ const translateLoaderToken = 'translateLoader';
provide: TranslateCompiler,
useClass: TranslateMessageFormatCompiler,
},
parser: {
provide: TranslateParser,
useClass: IqserTranslateParser,
},
}),
],
exports: [TranslateModule],
@ -30,6 +35,7 @@ export class IqserTranslateModule {
static forRoot(options?: IqserTranslateModuleOptions): ModuleWithProviders<IqserTranslateModule> {
const pathPrefix = options?.pathPrefix?.length ? options.pathPrefix : '/assets/i18n/';
return {
ngModule: IqserTranslateModule,
providers: [

View File

@ -1,4 +1,4 @@
import { Id, ITrackable } from '../listing/models/trackable';
import { Id, ITrackable } from '../listing';
import { UntypedFormGroup } from '@angular/forms';
import { forOwn, has, isEqual, isPlainObject, transform } from 'lodash-es';
import dayjs, { Dayjs } from 'dayjs';
@ -25,6 +25,42 @@ export function humanizeCamelCase(value: string): string {
return value.replace(/([a-z])([A-Z])/g, '$1 $2').toLowerCase();
}
export function escapeHtml<T extends unknown | string>(unsafe: T, options?: { ignoreTags: string[] }) {
if (typeof unsafe !== 'string') {
return unsafe;
}
let _unsafe = unsafe as string;
const ignoredTags = options?.ignoreTags?.reduce(
(acc, tag) => ({
...acc,
[`<${tag}>`]: `???${tag};`,
[`</${tag}>`]: `???/${tag};`,
}),
{} as Record<string, string>,
);
Object.entries(ignoredTags ?? {}).forEach(([key, value]) => {
_unsafe = _unsafe.replaceAll(key, value);
});
let escaped = _unsafe
.replaceAll(/&/g, '&amp;')
.replaceAll(/ /g, '&nbsp;')
.replaceAll(/</g, '&lt;')
.replaceAll(/>/g, '&gt;')
.replaceAll(/"/g, '&quot;');
if (ignoredTags) {
Object.entries(ignoredTags).forEach(([key, value]) => {
escaped = escaped.replaceAll(value, key);
});
}
return escaped;
}
export function _log(value: unknown, message = '') {
console.log(`%c[${dayjs().format('mm:ss.SSS')}] ${message}`, 'color: yellow;', value);
}