TF-3243 Fix email without content

This commit is contained in:
dab246
2024-10-29 19:30:11 +07:00
committed by Dat H. Pham
parent 2026d9307a
commit 596e95648d
17 changed files with 295 additions and 165 deletions
@@ -1,5 +1,6 @@
import 'package:core/data/network/dio_client.dart'; import 'package:core/data/network/dio_client.dart';
import 'package:core/presentation/utils/html_transformer/base/dom_transformer.dart'; import 'package:core/presentation/utils/html_transformer/base/dom_transformer.dart';
import 'package:core/utils/app_logger.dart';
import 'package:html/dom.dart'; import 'package:html/dom.dart';
class AddLazyLoadingForBackgroundImageTransformer extends DomTransformer { class AddLazyLoadingForBackgroundImageTransformer extends DomTransformer {
@@ -11,16 +12,23 @@ class AddLazyLoadingForBackgroundImageTransformer extends DomTransformer {
required DioClient dioClient, required DioClient dioClient,
Map<String, String>? mapUrlDownloadCID, Map<String, String>? mapUrlDownloadCID,
}) async { }) async {
final elements = document.querySelectorAll('[style*="background-image"]'); try {
await Future.wait(elements.map((element) async { final elements = document.querySelectorAll('[style*="background-image"]');
var exStyle = element.attributes['style'];
final imageUrls = findImageUrlFromStyleTag(exStyle!); if (elements.isEmpty) return;
if (imageUrls != null) {
exStyle = exStyle.replaceFirst(imageUrls.value1, ''); await Future.wait(elements.map((element) async {
element.attributes['style'] = exStyle; var exStyle = element.attributes['style'];
element.attributes['data-src'] = imageUrls.value2; final imageUrls = findImageUrlFromStyleTag(exStyle!);
element.attributes.addAll({'lazy': ''}); if (imageUrls != null) {
} exStyle = exStyle.replaceFirst(imageUrls.value1, '');
})); element.attributes['style'] = exStyle;
element.attributes['data-src'] = imageUrls.value2;
element.attributes.addAll({'lazy': ''});
}
}));
} catch (e) {
logError('$runtimeType::process:Exception = $e');
}
} }
} }
@@ -1,6 +1,7 @@
import 'package:core/data/network/dio_client.dart'; import 'package:core/data/network/dio_client.dart';
import 'package:core/presentation/utils/html_transformer/base/dom_transformer.dart'; import 'package:core/presentation/utils/html_transformer/base/dom_transformer.dart';
import 'package:core/utils/app_logger.dart';
import 'package:html/dom.dart'; import 'package:html/dom.dart';
class BlockCodeTransformer extends DomTransformer { class BlockCodeTransformer extends DomTransformer {
@@ -13,9 +14,13 @@ class BlockCodeTransformer extends DomTransformer {
required DioClient dioClient, required DioClient dioClient,
Map<String, String>? mapUrlDownloadCID, Map<String, String>? mapUrlDownloadCID,
}) async { }) async {
final codeElements = document.getElementsByTagName('pre'); try {
await Future.wait(codeElements.map((element) async { final codeElements = document.getElementsByTagName('pre');
element.attributes['style'] = '''
if (codeElements.isEmpty) return;
await Future.wait(codeElements.map((element) async {
element.attributes['style'] = '''
display: block; display: block;
padding: 10px; padding: 10px;
margin: 0 0 10px; margin: 0 0 10px;
@@ -28,7 +33,10 @@ class BlockCodeTransformer extends DomTransformer {
border: 1px solid #ccc; border: 1px solid #ccc;
border-radius: 4px; border-radius: 4px;
overflow: auto; overflow: auto;
'''; ''';
})); }));
} catch (e) {
logError('$runtimeType::process:Exception = $e');
}
} }
} }
@@ -1,6 +1,7 @@
import 'package:core/data/network/dio_client.dart'; import 'package:core/data/network/dio_client.dart';
import 'package:core/presentation/utils/html_transformer/base/dom_transformer.dart'; import 'package:core/presentation/utils/html_transformer/base/dom_transformer.dart';
import 'package:core/utils/app_logger.dart';
import 'package:html/dom.dart'; import 'package:html/dom.dart';
class BlockQuotedTransformer extends DomTransformer { class BlockQuotedTransformer extends DomTransformer {
@@ -13,15 +14,22 @@ class BlockQuotedTransformer extends DomTransformer {
required DioClient dioClient, required DioClient dioClient,
Map<String, String>? mapUrlDownloadCID, Map<String, String>? mapUrlDownloadCID,
}) async { }) async {
final quotedElements = document.getElementsByTagName('blockquote'); try {
await Future.wait(quotedElements.map((quotedElement) async { final quotedElements = document.getElementsByTagName('blockquote');
quotedElement.attributes['style'] = '''
if (quotedElements.isEmpty) return;
await Future.wait(quotedElements.map((quotedElement) async {
quotedElement.attributes['style'] = '''
margin-left: 4px; margin-left: 4px;
margin-right: 4px; margin-right: 4px;
padding-left: 8px; padding-left: 8px;
padding-right: 8px; padding-right: 8px;
border-left: 2px solid #eee; border-left: 2px solid #eee;
'''; ''';
})); }));
} catch (e) {
logError('$runtimeType::process:Exception = $e');
}
} }
} }
@@ -1,5 +1,6 @@
import 'package:core/data/network/dio_client.dart'; import 'package:core/data/network/dio_client.dart';
import 'package:core/presentation/utils/html_transformer/base/dom_transformer.dart'; import 'package:core/presentation/utils/html_transformer/base/dom_transformer.dart';
import 'package:core/utils/app_logger.dart';
import 'package:html/dom.dart'; import 'package:html/dom.dart';
class HideDraftSignatureTransformer extends DomTransformer { class HideDraftSignatureTransformer extends DomTransformer {
@@ -12,15 +13,19 @@ class HideDraftSignatureTransformer extends DomTransformer {
required DioClient dioClient, required DioClient dioClient,
Map<String, String>? mapUrlDownloadCID Map<String, String>? mapUrlDownloadCID
}) async { }) async {
final signature = document.querySelector('div.tmail-signature'); try {
if (signature == null) return; final signature = document.querySelector('div.tmail-signature');
final currentStyle = signature.attributes['style']?.trim(); if (signature == null) return;
if (currentStyle == null) { final currentStyle = signature.attributes['style']?.trim();
signature.attributes['style'] = 'display: none;'; if (currentStyle == null) {
} else if (currentStyle.endsWith(';')) { signature.attributes['style'] = 'display: none;';
signature.attributes['style'] = '$currentStyle display: none;'; } else if (currentStyle.endsWith(';')) {
} else { signature.attributes['style'] = '$currentStyle display: none;';
signature.attributes['style'] = '$currentStyle; display: none;'; } else {
signature.attributes['style'] = '$currentStyle; display: none;';
}
} catch (e) {
logError('$runtimeType::process:Exception = $e');
} }
} }
} }
@@ -5,6 +5,7 @@ import 'package:core/data/network/dio_client.dart';
import 'package:core/data/utils/compress_file_utils.dart'; import 'package:core/data/utils/compress_file_utils.dart';
import 'package:core/presentation/extensions/html_extension.dart'; import 'package:core/presentation/extensions/html_extension.dart';
import 'package:core/presentation/utils/html_transformer/base/dom_transformer.dart'; import 'package:core/presentation/utils/html_transformer/base/dom_transformer.dart';
import 'package:core/utils/app_logger.dart';
import 'package:core/utils/platform_info.dart'; import 'package:core/utils/platform_info.dart';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
@@ -20,41 +21,48 @@ class ImageTransformer extends DomTransformer {
required DioClient dioClient, required DioClient dioClient,
Map<String, String>? mapUrlDownloadCID, Map<String, String>? mapUrlDownloadCID,
}) async { }) async {
final imageElements = document.querySelectorAll('img'); try {
await Future.wait(imageElements.map((imageElement) async { final imageElements = document.querySelectorAll('img');
var exStyle = imageElement.attributes['style'];
if (exStyle != null) {
if (!exStyle.trim().endsWith(';')) {
exStyle = '$exStyle;';
}
if (!exStyle.contains('display')) {
exStyle = '$exStyle display:inline;';
}
if (!exStyle.contains('max-width')) {
exStyle = '$exStyle max-width:100%;';
}
imageElement.attributes['style'] = exStyle;
} else {
imageElement.attributes['style'] = 'display:inline;max-width:100%;';
}
final src = imageElement.attributes['src'];
if (src == null) return; if (imageElements.isEmpty) return;
if (src.startsWith('cid:') && mapUrlDownloadCID != null) { await Future.wait(imageElements.map((imageElement) async {
final imageBase64 = await _convertCidToBase64Image( var exStyle = imageElement.attributes['style'];
dioClient: dioClient, if (exStyle != null) {
mapUrlDownloadCID: mapUrlDownloadCID, if (!exStyle.trim().endsWith(';')) {
imageSource: src exStyle = '$exStyle;';
); }
imageElement.attributes['src'] = imageBase64 ?? src; if (!exStyle.contains('display')) {
imageElement.attributes['id'] ??= src; exStyle = '$exStyle display:inline;';
} else if (src.startsWith('https://') || src.startsWith('http://')) { }
if (!imageElement.attributes.containsKey('loading')) { if (!exStyle.contains('max-width')) {
imageElement.attributes['loading'] = 'lazy'; exStyle = '$exStyle max-width:100%;';
}
imageElement.attributes['style'] = exStyle;
} else {
imageElement.attributes['style'] = 'display:inline;max-width:100%;';
} }
} final src = imageElement.attributes['src'];
}));
if (src == null) return;
if (src.startsWith('cid:') && mapUrlDownloadCID != null) {
final imageBase64 = await _convertCidToBase64Image(
dioClient: dioClient,
mapUrlDownloadCID: mapUrlDownloadCID,
imageSource: src
);
imageElement.attributes['src'] = imageBase64 ?? src;
imageElement.attributes['id'] ??= src;
} else if (src.startsWith('https://') || src.startsWith('http://')) {
if (!imageElement.attributes.containsKey('loading')) {
imageElement.attributes['loading'] = 'lazy';
}
}
}));
} catch (e) {
logError('$runtimeType::process:Exception = $e');
}
} }
Future<String?> _convertCidToBase64Image({ Future<String?> _convertCidToBase64Image({
@@ -1,6 +1,7 @@
import 'package:core/data/network/dio_client.dart'; import 'package:core/data/network/dio_client.dart';
import 'package:core/presentation/utils/html_transformer/base/dom_transformer.dart'; import 'package:core/presentation/utils/html_transformer/base/dom_transformer.dart';
import 'package:core/utils/app_logger.dart';
import 'package:html/dom.dart'; import 'package:html/dom.dart';
class RemoveCollapsedSignatureButtonTransformer extends DomTransformer { class RemoveCollapsedSignatureButtonTransformer extends DomTransformer {
@@ -13,9 +14,16 @@ class RemoveCollapsedSignatureButtonTransformer extends DomTransformer {
required DioClient dioClient, required DioClient dioClient,
Map<String, String>? mapUrlDownloadCID, Map<String, String>? mapUrlDownloadCID,
}) async { }) async {
final elements = document.querySelectorAll('.tmail-signature-button'); try {
await Future.wait(elements.map((element) async { final elements = document.querySelectorAll('.tmail-signature-button');
element.remove();
})); if (elements.isEmpty) return;
await Future.wait(elements.map((element) async {
element.remove();
}));
} catch (e) {
logError('$runtimeType::process:Exception = $e');
}
} }
} }
@@ -1,5 +1,6 @@
import 'package:core/data/network/dio_client.dart'; import 'package:core/data/network/dio_client.dart';
import 'package:core/presentation/utils/html_transformer/base/dom_transformer.dart'; import 'package:core/presentation/utils/html_transformer/base/dom_transformer.dart';
import 'package:core/utils/app_logger.dart';
import 'package:html/dom.dart'; import 'package:html/dom.dart';
class RemoveLazyLoadingForBackgroundImageTransformer extends DomTransformer { class RemoveLazyLoadingForBackgroundImageTransformer extends DomTransformer {
@@ -11,19 +12,26 @@ class RemoveLazyLoadingForBackgroundImageTransformer extends DomTransformer {
required DioClient dioClient, required DioClient dioClient,
Map<String, String>? mapUrlDownloadCID, Map<String, String>? mapUrlDownloadCID,
}) async { }) async {
final elements = document.querySelectorAll('[lazy]'); try {
await Future.wait(elements.map((element) async { final elements = document.querySelectorAll('[lazy]');
var exStyle = element.attributes['style'];
final dataSrc = element.attributes['data-src']; if (elements.isEmpty) return;
final backgroundImgUrl = 'background-image:url($dataSrc);';
if (exStyle != null) { await Future.wait(elements.map((element) async {
exStyle = '$exStyle;$backgroundImgUrl'; var exStyle = element.attributes['style'];
element.attributes['style'] = exStyle; final dataSrc = element.attributes['data-src'];
} else { final backgroundImgUrl = 'background-image:url($dataSrc);';
element.attributes['style'] = backgroundImgUrl; if (exStyle != null) {
} exStyle = '$exStyle;$backgroundImgUrl';
element.attributes.remove('data-src'); element.attributes['style'] = exStyle;
element.attributes.remove('lazy'); } else {
})); element.attributes['style'] = backgroundImgUrl;
}
element.attributes.remove('data-src');
element.attributes.remove('lazy');
}));
} catch (e) {
logError('$runtimeType::process:Exception = $e');
}
} }
} }
@@ -1,5 +1,6 @@
import 'package:core/data/network/dio_client.dart'; import 'package:core/data/network/dio_client.dart';
import 'package:core/presentation/utils/html_transformer/base/dom_transformer.dart'; import 'package:core/presentation/utils/html_transformer/base/dom_transformer.dart';
import 'package:core/utils/app_logger.dart';
import 'package:html/dom.dart'; import 'package:html/dom.dart';
class RemoveLazyLoadingImageTransformer extends DomTransformer { class RemoveLazyLoadingImageTransformer extends DomTransformer {
@@ -11,9 +12,16 @@ class RemoveLazyLoadingImageTransformer extends DomTransformer {
required DioClient dioClient, required DioClient dioClient,
Map<String, String>? mapUrlDownloadCID, Map<String, String>? mapUrlDownloadCID,
}) async { }) async {
final elements = document.querySelectorAll('img[loading]'); try {
await Future.wait(elements.map((element) async { final elements = document.querySelectorAll('img[loading]');
element.attributes.remove('loading');
})); if (elements.isEmpty) return;
await Future.wait(elements.map((element) async {
element.attributes.remove('loading');
}));
} catch (e) {
logError('$runtimeType::process:Exception = $e');
}
} }
} }
@@ -1,5 +1,6 @@
import 'package:core/data/network/dio_client.dart'; import 'package:core/data/network/dio_client.dart';
import 'package:core/presentation/utils/html_transformer/base/dom_transformer.dart'; import 'package:core/presentation/utils/html_transformer/base/dom_transformer.dart';
import 'package:core/utils/app_logger.dart';
import 'package:html/dom.dart'; import 'package:html/dom.dart';
class RemoveMaxWidthInImageStyleTransformer extends DomTransformer { class RemoveMaxWidthInImageStyleTransformer extends DomTransformer {
@@ -12,13 +13,20 @@ class RemoveMaxWidthInImageStyleTransformer extends DomTransformer {
required DioClient dioClient, required DioClient dioClient,
Map<String, String>? mapUrlDownloadCID, Map<String, String>? mapUrlDownloadCID,
}) async { }) async {
final imageElements = document.querySelectorAll('img[style*="max-width"]'); try {
await Future.wait(imageElements.map((imageElement) async { final imageElements = document.querySelectorAll('img[style*="max-width"]');
var exStyle = imageElement.attributes['style'];
if (exStyle != null) { if (imageElements.isEmpty) return;
exStyle = exStyle.replaceAll('max-width:100%;', '');
imageElement.attributes['style'] = exStyle; await Future.wait(imageElements.map((imageElement) async {
} var exStyle = imageElement.attributes['style'];
})); if (exStyle != null) {
exStyle = exStyle.replaceAll('max-width:100%;', '');
imageElement.attributes['style'] = exStyle;
}
}));
} catch (e) {
logError('$runtimeType::process:Exception = $e');
}
} }
} }
@@ -1,5 +1,6 @@
import 'package:core/data/network/dio_client.dart'; import 'package:core/data/network/dio_client.dart';
import 'package:core/presentation/utils/html_transformer/base/dom_transformer.dart'; import 'package:core/presentation/utils/html_transformer/base/dom_transformer.dart';
import 'package:core/utils/app_logger.dart';
import 'package:html/dom.dart'; import 'package:html/dom.dart';
class RemoveStyleTagOutsideTransformer extends DomTransformer { class RemoveStyleTagOutsideTransformer extends DomTransformer {
@@ -12,9 +13,16 @@ class RemoveStyleTagOutsideTransformer extends DomTransformer {
required DioClient dioClient, required DioClient dioClient,
Map<String, String>? mapUrlDownloadCID, Map<String, String>? mapUrlDownloadCID,
}) async { }) async {
final styleElements = document.querySelectorAll('style'); try {
await Future.wait(styleElements.map((element) async { final styleElements = document.querySelectorAll('style');
element.remove();
})); if (styleElements.isEmpty) return;
await Future.wait(styleElements.map((element) async {
element.remove();
}));
} catch (e) {
logError('$runtimeType::process:Exception = $e');
}
} }
} }
@@ -1,6 +1,7 @@
import 'package:core/data/network/dio_client.dart'; import 'package:core/data/network/dio_client.dart';
import 'package:core/presentation/utils/html_transformer/base/dom_transformer.dart'; import 'package:core/presentation/utils/html_transformer/base/dom_transformer.dart';
import 'package:core/utils/app_logger.dart';
import 'package:core/utils/html/html_template.dart'; import 'package:core/utils/html/html_template.dart';
import 'package:html/dom.dart'; import 'package:html/dom.dart';
@@ -14,20 +15,27 @@ class RemoveTooltipLinkTransformer extends DomTransformer {
required DioClient dioClient, required DioClient dioClient,
Map<String, String>? mapUrlDownloadCID, Map<String, String>? mapUrlDownloadCID,
}) async { }) async {
final linkElements = document.querySelectorAll('a.${HtmlTemplate.nameClassToolTip}'); try {
await Future.wait(linkElements.map((linkElement) async { final linkElements = document.querySelectorAll('a.${HtmlTemplate.nameClassToolTip}');
final classAttribute = linkElement.attributes['class'];
if (classAttribute != null) { if (linkElements.isEmpty) return;
final newClassAttribute = classAttribute.replaceFirst(HtmlTemplate.nameClassToolTip, '');
linkElement.attributes['class'] = newClassAttribute; await Future.wait(linkElements.map((linkElement) async {
} final classAttribute = linkElement.attributes['class'];
final listSpanTag = linkElement.querySelectorAll('span.tooltiptext'); if (classAttribute != null) {
if (listSpanTag.isNotEmpty) { final newClassAttribute = classAttribute.replaceFirst(HtmlTemplate.nameClassToolTip, '');
for (var element in listSpanTag) { linkElement.attributes['class'] = newClassAttribute;
element.remove();
} }
} final listSpanTag = linkElement.querySelectorAll('span.tooltiptext');
})); if (listSpanTag.isNotEmpty) {
for (var element in listSpanTag) {
element.remove();
}
}
}));
} catch (e) {
logError('$runtimeType::process:Exception = $e');
}
} }
} }
@@ -1,6 +1,7 @@
import 'package:core/data/network/dio_client.dart'; import 'package:core/data/network/dio_client.dart';
import 'package:core/presentation/utils/html_transformer/base/dom_transformer.dart'; import 'package:core/presentation/utils/html_transformer/base/dom_transformer.dart';
import 'package:core/presentation/utils/html_transformer/sanitize_url.dart'; import 'package:core/presentation/utils/html_transformer/sanitize_url.dart';
import 'package:core/utils/app_logger.dart';
import 'package:core/utils/html/html_template.dart'; import 'package:core/utils/html/html_template.dart';
import 'package:html/dom.dart'; import 'package:html/dom.dart';
@@ -16,15 +17,22 @@ class SanitizeHyperLinkTagInHtmlTransformer extends DomTransformer {
required DioClient dioClient, required DioClient dioClient,
Map<String, String>? mapUrlDownloadCID, Map<String, String>? mapUrlDownloadCID,
}) async { }) async {
final elements = document.querySelectorAll('a'); try {
await Future.wait(elements.map((element) async { final elements = document.querySelectorAll('a');
_sanitizeUrlResource(element);
if (useTooltip) { if (elements.isEmpty) return;
_addToolTipWhenHoverLink(element);
} await Future.wait(elements.map((element) async {
_addBlankForTargetProperty(element); _sanitizeUrlResource(element);
_addNoReferrerForRelProperty(element); if (useTooltip) {
})); _addToolTipWhenHoverLink(element);
}
_addBlankForTargetProperty(element);
_addNoReferrerForRelProperty(element);
}));
} catch (e) {
logError('$runtimeType::process:Exception = $e');
}
} }
void _sanitizeUrlResource(Element element) { void _sanitizeUrlResource(Element element) {
@@ -1,5 +1,6 @@
import 'package:core/data/network/dio_client.dart'; import 'package:core/data/network/dio_client.dart';
import 'package:core/utils/app_logger.dart';
import 'package:html/dom.dart'; import 'package:html/dom.dart';
import 'package:core/presentation/utils/html_transformer/base/dom_transformer.dart'; import 'package:core/presentation/utils/html_transformer/base/dom_transformer.dart';
@@ -13,9 +14,16 @@ class RemoveScriptTransformer extends DomTransformer {
required DioClient dioClient, required DioClient dioClient,
Map<String, String>? mapUrlDownloadCID, Map<String, String>? mapUrlDownloadCID,
}) async { }) async {
final scriptElements = document.getElementsByTagName('script'); try {
await Future.wait(scriptElements.map((scriptElement) async { final scriptElements = document.getElementsByTagName('script');
scriptElement.remove();
})); if (scriptElements.isEmpty) return;
await Future.wait(scriptElements.map((scriptElement) async {
scriptElement.remove();
}));
} catch (e) {
logError('$runtimeType::process:Exception = $e');
}
} }
} }
@@ -1,6 +1,7 @@
import 'package:core/data/network/dio_client.dart'; import 'package:core/data/network/dio_client.dart';
import 'package:core/presentation/utils/html_transformer/base/dom_transformer.dart'; import 'package:core/presentation/utils/html_transformer/base/dom_transformer.dart';
import 'package:core/utils/app_logger.dart';
import 'package:html/dom.dart'; import 'package:html/dom.dart';
class SignatureTransformer extends DomTransformer { class SignatureTransformer extends DomTransformer {
@@ -13,9 +14,16 @@ class SignatureTransformer extends DomTransformer {
required DioClient dioClient, required DioClient dioClient,
Map<String, String>? mapUrlDownloadCID, Map<String, String>? mapUrlDownloadCID,
}) async { }) async {
final signatureElements = document.querySelectorAll('div.tmail-signature'); try {
await Future.wait(signatureElements.map((element) async { final signatureElements = document.querySelectorAll('div.tmail-signature');
element.attributes['class'] = 'tmail-signature-blocked';
})); if (signatureElements.isEmpty) return;
await Future.wait(signatureElements.map((element) async {
element.attributes['class'] = 'tmail-signature-blocked';
}));
} catch (e) {
logError('$runtimeType::process:Exception = $e');
}
} }
} }
@@ -21,38 +21,43 @@ class SanitizeAutolinkFilter {
SanitizeAutolinkFilter(this.htmlEscape); SanitizeAutolinkFilter(this.htmlEscape);
String process(String inputText) { String process(String inputText) {
if (inputText.isEmpty) { try {
return ''; if (inputText.isEmpty) {
} return '';
final elements = linkify(
inputText,
options: _linkifyOption,
linkifiers: _linkifier
);
log('AutolinkFilter::process:elements: $elements');
final htmlTextBuffer = StringBuffer();
for (var element in elements) {
if (element is TextElement) {
final escapedHtml = htmlEscape.convert(element.text);
htmlTextBuffer.write(escapedHtml);
} else if (element is EmailElement) {
final emailLinkTag = _buildEmailLinkTag(
mailToLink: element.url,
value: element.text
);
htmlTextBuffer.write(emailLinkTag);
} else if (element is UrlElement) {
final urlLinkTag = _buildUrlLinkTag(
urlLink: element.url,
value: element.text
);
htmlTextBuffer.write(urlLinkTag);
} }
}
return htmlTextBuffer.toString(); final elements = linkify(
inputText,
options: _linkifyOption,
linkifiers: _linkifier,
);
log('AutolinkFilter::process:elements: $elements');
final htmlTextBuffer = StringBuffer();
for (var element in elements) {
if (element is TextElement) {
final escapedHtml = htmlEscape.convert(element.text);
htmlTextBuffer.write(escapedHtml);
} else if (element is EmailElement) {
final emailLinkTag = _buildEmailLinkTag(
mailToLink: element.url,
value: element.text
);
htmlTextBuffer.write(emailLinkTag);
} else if (element is UrlElement) {
final urlLinkTag = _buildUrlLinkTag(
urlLink: element.url,
value: element.text
);
htmlTextBuffer.write(urlLinkTag);
}
}
return htmlTextBuffer.toString();
} catch (e) {
logError('$runtimeType::process:Exception = $e');
return inputText;
}
} }
String _buildUrlLinkTag({required String urlLink, required String value}) { String _buildUrlLinkTag({required String urlLink, required String value}) {
@@ -1,3 +1,4 @@
import 'package:core/utils/app_logger.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
class SanitizeUrl { class SanitizeUrl {
@@ -12,17 +13,20 @@ class SanitizeUrl {
); );
String process(String inputText) { String process(String inputText) {
var originalUrl = inputText; try {
log('SanitizeUrl::process:inputText = $inputText');
originalUrl = Uri.decodeFull(originalUrl); var originalUrl = Uri.decodeFull(inputText);
if (GetUtils.isURL(originalUrl)) {
if (GetUtils.isURL(originalUrl)) { originalUrl = !originalUrl.startsWith(_protocolIdentifierRegex)
if (!originalUrl.startsWith(_protocolIdentifierRegex)) { ? (defaultToHttps ? "https://" : "http://") + originalUrl
originalUrl = (defaultToHttps ? "https://" : "http://") + originalUrl; : originalUrl;
} else {
originalUrl = '';
} }
} else { return originalUrl;
originalUrl = ''; } catch (e) {
logError('SanitizeUrl::process:Exception = $e');
return inputText;
} }
return originalUrl;
} }
} }
+12
View File
@@ -108,5 +108,17 @@ void main() {
equals('http://linagora.com') equals('http://linagora.com')
); );
}); });
test('SHOULD returns original input WHEN get an exception', () {
// Arrange
const inputText = "%E0%A4%A";
const expectedOutput = inputText;
// Act
final result = sanitizeUrl.process(inputText);
// Assert
expect(result, expectedOutput);
});
}); });
} }