From 1af33dd20d8fbcb9abe6f70919ce3c1c15dc049a Mon Sep 17 00:00:00 2001 From: dab246 Date: Wed, 16 Aug 2023 12:02:34 +0700 Subject: [PATCH] TF-1961 Refactor html transformer (cherry picked from commit 0272e45226ddb12827b7ef4820b00632758c353a) --- .../base/dom_transformer.dart | 14 ++--- ...dd_target_blank_in_tag_a_transformers.dart | 4 +- .../dom/add_tooltip_link_transformers.dart | 15 ++--- .../dom/blockcode_transformers.dart | 12 ++-- .../dom/blockquoted_transformers.dart | 12 ++-- .../dom/image_transformers.dart | 63 +++++++++++-------- .../dom/script_transformers.dart | 12 ++-- .../dom/sigature_transformers.dart | 12 ++-- .../html_transformer/html_transform.dart | 35 +++++------ .../message_content_transformer.dart | 42 ++++++------- .../email/data/local/html_analyzer.dart | 28 ++++----- .../identity_data_source_impl.dart | 10 ++- .../identity_interactors_bindings.dart | 2 + .../bindings/network/network_bindings.dart | 3 +- 14 files changed, 134 insertions(+), 130 deletions(-) diff --git a/core/lib/presentation/utils/html_transformer/base/dom_transformer.dart b/core/lib/presentation/utils/html_transformer/base/dom_transformer.dart index 32d0f332c..1deb82264 100644 --- a/core/lib/presentation/utils/html_transformer/base/dom_transformer.dart +++ b/core/lib/presentation/utils/html_transformer/base/dom_transformer.dart @@ -1,5 +1,5 @@ -import 'package:core/core.dart'; +import 'package:core/data/network/dio_client.dart'; import 'package:html/dom.dart'; /// Transforms the HTML DOM. @@ -10,13 +10,11 @@ abstract class DomTransformer { /// Uses the `DOM` [document] to transform the `document`. /// /// All changes will be visible to subsequent transformers. - Future process( - Document document, - { - Map? mapUrlDownloadCID, - DioClient? dioClient, - } - ); + Future process({ + required Document document, + Map? mapUrlDownloadCID, + DioClient? dioClient, + }); /// Adds a HEAD element if necessary void ensureDocumentHeadIsAvailable(Document document) { diff --git a/core/lib/presentation/utils/html_transformer/dom/add_target_blank_in_tag_a_transformers.dart b/core/lib/presentation/utils/html_transformer/dom/add_target_blank_in_tag_a_transformers.dart index 59c46f627..f1e877383 100644 --- a/core/lib/presentation/utils/html_transformer/dom/add_target_blank_in_tag_a_transformers.dart +++ b/core/lib/presentation/utils/html_transformer/dom/add_target_blank_in_tag_a_transformers.dart @@ -6,8 +6,8 @@ class AddTargetBlankInTagATransformer extends DomTransformer { const AddTargetBlankInTagATransformer(); @override - Future process( - Document document, { + Future process({ + required Document document, Map? mapUrlDownloadCID, DioClient? dioClient, }) async { diff --git a/core/lib/presentation/utils/html_transformer/dom/add_tooltip_link_transformers.dart b/core/lib/presentation/utils/html_transformer/dom/add_tooltip_link_transformers.dart index ac579bba6..f51f531b1 100644 --- a/core/lib/presentation/utils/html_transformer/dom/add_tooltip_link_transformers.dart +++ b/core/lib/presentation/utils/html_transformer/dom/add_tooltip_link_transformers.dart @@ -1,22 +1,19 @@ 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/html_template.dart'; import 'package:html/dom.dart'; -import '../html_template.dart'; - class AddTooltipLinkTransformer extends DomTransformer { const AddTooltipLinkTransformer(); @override - Future process( - Document document, - { - Map? mapUrlDownloadCID, - DioClient? dioClient - } - ) async { + Future process({ + required Document document, + Map? mapUrlDownloadCID, + DioClient? dioClient + }) async { final linkElements = document.querySelectorAll('a[href^="http"]'); await Future.wait(linkElements.map((linkElement) async { _addToolTipWhenHoverLink(linkElement); diff --git a/core/lib/presentation/utils/html_transformer/dom/blockcode_transformers.dart b/core/lib/presentation/utils/html_transformer/dom/blockcode_transformers.dart index 8f7e71026..732e3272e 100644 --- a/core/lib/presentation/utils/html_transformer/dom/blockcode_transformers.dart +++ b/core/lib/presentation/utils/html_transformer/dom/blockcode_transformers.dart @@ -8,13 +8,11 @@ class BlockCodeTransformer extends DomTransformer { const BlockCodeTransformer(); @override - Future process( - Document document, - { - Map? mapUrlDownloadCID, - DioClient? dioClient - } - ) async { + Future process({ + required Document document, + Map? mapUrlDownloadCID, + DioClient? dioClient + }) async { final codeElements = document.getElementsByTagName('pre'); await Future.wait(codeElements.map((element) async { element.attributes['style'] = ''' diff --git a/core/lib/presentation/utils/html_transformer/dom/blockquoted_transformers.dart b/core/lib/presentation/utils/html_transformer/dom/blockquoted_transformers.dart index 7c8df307c..7eb40dc0d 100644 --- a/core/lib/presentation/utils/html_transformer/dom/blockquoted_transformers.dart +++ b/core/lib/presentation/utils/html_transformer/dom/blockquoted_transformers.dart @@ -8,13 +8,11 @@ class BlockQuotedTransformer extends DomTransformer { const BlockQuotedTransformer(); @override - Future process( - Document document, - { - Map? mapUrlDownloadCID, - DioClient? dioClient - } - ) async { + Future process({ + required Document document, + Map? mapUrlDownloadCID, + DioClient? dioClient + }) async { final quotedElements = document.getElementsByTagName('blockquote'); await Future.wait(quotedElements.map((quotedElement) async { quotedElement.attributes['style'] = ''' diff --git a/core/lib/presentation/utils/html_transformer/dom/image_transformers.dart b/core/lib/presentation/utils/html_transformer/dom/image_transformers.dart index 44668daa4..c65c2e254 100644 --- a/core/lib/presentation/utils/html_transformer/dom/image_transformers.dart +++ b/core/lib/presentation/utils/html_transformer/dom/image_transformers.dart @@ -5,7 +5,6 @@ import 'package:core/data/network/dio_client.dart'; import 'package:core/data/utils/compress_file_utils.dart'; import 'package:core/presentation/extensions/html_extension.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:dio/dio.dart'; import 'package:flutter/foundation.dart'; @@ -16,16 +15,12 @@ class ImageTransformer extends DomTransformer { const ImageTransformer(); @override - Future process( - Document document, - { - Map? mapUrlDownloadCID, - DioClient? dioClient - } - ) async { - final compressFileUtils = CompressFileUtils(); - final imageElements = document.querySelectorAll('img[src^="cid:"]'); - log('ImageTransformer::process(): imageElements: ${imageElements.length}'); + Future process({ + required Document document, + Map? mapUrlDownloadCID, + DioClient? dioClient + }) async { + final imageElements = document.querySelectorAll('img'); await Future.wait(imageElements.map((imageElement) async { final exStyle = imageElement.attributes['style']; if (exStyle != null) { @@ -34,24 +29,42 @@ class ImageTransformer extends DomTransformer { imageElement.attributes['style'] = 'display: inline;max-width: 100%;'; } final src = imageElement.attributes['src']; - if (src != null) { - log('ImageTransformer::process(): src: $src'); - final cid = src.replaceFirst('cid:', '').trim(); - final urlDownloadCid = mapUrlDownloadCID?[cid]; - log('ImageTransformer::process(): urlDownloadCid: $urlDownloadCid'); - if (urlDownloadCid?.isNotEmpty == true && dioClient != null) { - final imgBase64Uri = await loadAsyncNetworkImageToBase64( - dioClient, - compressFileUtils, - urlDownloadCid!); - if (imgBase64Uri.isNotEmpty) { - imageElement.attributes['src'] = imgBase64Uri; - } - } + + if (src == null) return; + + if (src.startsWith('cid:') && dioClient != null && mapUrlDownloadCID != null) { + final imageBase64 = await _convertCidToBase64Image( + dioClient: dioClient, + mapUrlDownloadCID: mapUrlDownloadCID, + imageSource: src + ); + imageElement.attributes['src'] = imageBase64 ?? src; } })); } + Future _convertCidToBase64Image({ + required DioClient dioClient, + required Map mapUrlDownloadCID, + required String imageSource + }) async { + final cid = imageSource.replaceFirst('cid:', '').trim(); + final urlDownloadCid = mapUrlDownloadCID[cid]; + + if (urlDownloadCid == null || urlDownloadCid.isEmpty) return null; + + final compressFileUtils = CompressFileUtils(); + final imgBase64Uri = await loadAsyncNetworkImageToBase64( + dioClient, + compressFileUtils, + urlDownloadCid + ); + + if (imgBase64Uri.isEmpty) return null; + + return imgBase64Uri; + } + Future loadAsyncNetworkImageToBase64( DioClient dioClient, CompressFileUtils compressFileUtils, diff --git a/core/lib/presentation/utils/html_transformer/dom/script_transformers.dart b/core/lib/presentation/utils/html_transformer/dom/script_transformers.dart index 3176f363e..a1cde09b8 100644 --- a/core/lib/presentation/utils/html_transformer/dom/script_transformers.dart +++ b/core/lib/presentation/utils/html_transformer/dom/script_transformers.dart @@ -8,13 +8,11 @@ class RemoveScriptTransformer extends DomTransformer { const RemoveScriptTransformer(); @override - Future process( - Document document, - { - Map? mapUrlDownloadCID, - DioClient? dioClient - } - ) async { + Future process({ + required Document document, + Map? mapUrlDownloadCID, + DioClient? dioClient + }) async { final scriptElements = document.getElementsByTagName('script'); await Future.wait(scriptElements.map((scriptElement) async { scriptElement.remove(); diff --git a/core/lib/presentation/utils/html_transformer/dom/sigature_transformers.dart b/core/lib/presentation/utils/html_transformer/dom/sigature_transformers.dart index a8466c861..8539a7f59 100644 --- a/core/lib/presentation/utils/html_transformer/dom/sigature_transformers.dart +++ b/core/lib/presentation/utils/html_transformer/dom/sigature_transformers.dart @@ -8,13 +8,11 @@ class SignatureTransformer extends DomTransformer { const SignatureTransformer(); @override - Future process( - Document document, - { - Map? mapUrlDownloadCID, - DioClient? dioClient - } - ) async { + Future process({ + required Document document, + Map? mapUrlDownloadCID, + DioClient? dioClient + }) async { final signatureElements = document.querySelectorAll('div.tmail-signature'); await Future.wait(signatureElements.map((element) async { element.attributes['class'] = 'tmail-signature-blocked'; diff --git a/core/lib/presentation/utils/html_transformer/html_transform.dart b/core/lib/presentation/utils/html_transformer/html_transform.dart index 1dd8f021a..b0ed967b5 100644 --- a/core/lib/presentation/utils/html_transformer/html_transform.dart +++ b/core/lib/presentation/utils/html_transformer/html_transform.dart @@ -3,34 +3,33 @@ import 'package:core/presentation/utils/html_transformer/message_content_transfo class HtmlTransform { - final String _contentHtml; - Map? mapUrlDownloadCID; - DioClient? dioClient; + final DioClient _dioClient; - HtmlTransform( - this._contentHtml, - { - this.mapUrlDownloadCID, - this.dioClient, - } - ); + HtmlTransform(this._dioClient); /// Transforms this message to HTML code. - Future transformToHtml({TransformConfiguration? transformConfiguration}) async { + Future transformToHtml({ + required String contentHtml, + Map? mapUrlDownloadCID, + TransformConfiguration? transformConfiguration, + }) async { transformConfiguration ??= TransformConfiguration.create(); - final transformer = MessageContentTransformer(transformConfiguration); + final transformer = MessageContentTransformer(transformConfiguration, _dioClient); final document = await transformer.toDocument( - _contentHtml, - mapUrlDownloadCID: mapUrlDownloadCID, - dioClient: dioClient); + message: contentHtml, + mapUrlDownloadCID: mapUrlDownloadCID + ); return document.outerHtml; } /// Transforms this message to Text Plain. - String transformToTextPlain({TransformConfiguration? transformConfiguration}) { + String transformToTextPlain({ + required String content, + TransformConfiguration? transformConfiguration + }) { transformConfiguration ??= TransformConfiguration.create(); - final transformer = MessageContentTransformer(transformConfiguration); - final message = transformer.toMessage(_contentHtml); + final transformer = MessageContentTransformer(transformConfiguration, _dioClient); + final message = transformer.toMessage(content); return message; } } \ No newline at end of file diff --git a/core/lib/presentation/utils/html_transformer/message_content_transformer.dart b/core/lib/presentation/utils/html_transformer/message_content_transformer.dart index 761e4f2af..ac31e27f8 100644 --- a/core/lib/presentation/utils/html_transformer/message_content_transformer.dart +++ b/core/lib/presentation/utils/html_transformer/message_content_transformer.dart @@ -1,4 +1,5 @@ -import 'package:core/core.dart'; +import 'package:core/data/network/dio_client.dart'; +import 'package:core/presentation/utils/html_transformer/transform_configuration.dart'; import 'package:html/dom.dart'; import 'package:html/parser.dart' show parse; @@ -6,38 +7,35 @@ import 'package:html/parser.dart' show parse; class MessageContentTransformer { /// The configuration used for the transformation final TransformConfiguration configuration; + final DioClient dioClient; - MessageContentTransformer(this.configuration); + MessageContentTransformer(this.configuration, this.dioClient); - Future _transformDocument( - Document document, - { - Map? mapUrlDownloadCID, - DioClient? dioClient - } - ) async { + Future _transformDocument({ + required Document document, + Map? mapUrlDownloadCID + }) async { await Future.wait([ if (configuration.domTransformers.isNotEmpty) ...configuration.domTransformers.map((domTransformer) async => domTransformer.process( - document, - mapUrlDownloadCID: mapUrlDownloadCID, - dioClient: dioClient)) + document: document, + mapUrlDownloadCID: mapUrlDownloadCID, + dioClient: dioClient + ) + ) ]); } - Future toDocument( - String message, - { - Map? mapUrlDownloadCID, - DioClient? dioClient - } - ) async { + Future toDocument({ + required String message, + Map? mapUrlDownloadCID + }) async { final document = parse(message); await _transformDocument( - document, - mapUrlDownloadCID: mapUrlDownloadCID, - dioClient: dioClient); + document: document, + mapUrlDownloadCID: mapUrlDownloadCID, + ); return document; } diff --git a/lib/features/email/data/local/html_analyzer.dart b/lib/features/email/data/local/html_analyzer.dart index c7127a81a..ebdee846f 100644 --- a/lib/features/email/data/local/html_analyzer.dart +++ b/lib/features/email/data/local/html_analyzer.dart @@ -13,10 +13,10 @@ import 'package:model/email/email_content_type.dart'; class HtmlAnalyzer { - final DioClient _dioClient; + final HtmlTransform _htmlTransform; final HtmlEscape _htmlEscape; - HtmlAnalyzer(this._dioClient, this._htmlEscape); + HtmlAnalyzer(this._htmlTransform, this._htmlEscape); Future transformEmailContent( EmailContent emailContent, @@ -25,13 +25,9 @@ class HtmlAnalyzer { ) async { switch(emailContent.type) { case EmailContentType.textHtml: - final htmlTransform = HtmlTransform( - emailContent.content, - dioClient: _dioClient, - mapUrlDownloadCID: mapUrlDownloadCID - ); - - final htmlContent = await htmlTransform.transformToHtml( + final htmlContent = await _htmlTransform.transformToHtml( + contentHtml: emailContent.content, + mapUrlDownloadCID: mapUrlDownloadCID, transformConfiguration: draftsEmail ? TransformConfiguration.create(customDomTransformers: TransformConfiguration.domTransformersForDraftEmail) : null @@ -39,8 +35,8 @@ class HtmlAnalyzer { return EmailContent(emailContent.type, htmlContent); case EmailContentType.textPlain: - final htmlTransform = HtmlTransform(emailContent.content); - final message = htmlTransform.transformToTextPlain( + final message = _htmlTransform.transformToTextPlain( + content: emailContent.content, transformConfiguration: TransformConfiguration.create( customTextTransformers: [SanitizeAutolinkHtmlTransformers(_htmlEscape)] ) @@ -54,10 +50,12 @@ class HtmlAnalyzer { Future addTooltipWhenHoverOnLink(EmailContent emailContent) async { switch(emailContent.type) { case EmailContentType.textHtml: - final htmlTransform = HtmlTransform(emailContent.content); - final htmlContent = await htmlTransform.transformToHtml( - transformConfiguration: TransformConfiguration.create( - customDomTransformers: [const AddTooltipLinkTransformer()])); + final htmlContent = await _htmlTransform.transformToHtml( + contentHtml: emailContent.content, + transformConfiguration: TransformConfiguration.create( + customDomTransformers: [const AddTooltipLinkTransformer()] + ) + ); return EmailContent(emailContent.type, htmlContent); default: return emailContent; diff --git a/lib/features/manage_account/data/datasource_impl/identity_data_source_impl.dart b/lib/features/manage_account/data/datasource_impl/identity_data_source_impl.dart index 9bdb816f2..e271d5151 100644 --- a/lib/features/manage_account/data/datasource_impl/identity_data_source_impl.dart +++ b/lib/features/manage_account/data/datasource_impl/identity_data_source_impl.dart @@ -20,10 +20,15 @@ import 'package:tmail_ui_user/main/exceptions/exception_thrower.dart'; class IdentityDataSourceImpl extends IdentityDataSource { + final HtmlTransform _htmlTransform; final IdentityAPI _identityAPI; final ExceptionThrower _exceptionThrower; - IdentityDataSourceImpl(this._identityAPI, this._exceptionThrower); + IdentityDataSourceImpl( + this._htmlTransform, + this._identityAPI, + this._exceptionThrower + ); @override Future getAllIdentities(Session session, AccountId accountId, {Properties? properties}) { @@ -56,7 +61,8 @@ class IdentityDataSourceImpl extends IdentityDataSource { @override Future transformHtmlSignature(String signature) { return Future.sync(() async { - final signatureUnescape = await HtmlTransform(signature).transformToHtml( + final signatureUnescape = await _htmlTransform.transformToHtml( + contentHtml: signature, transformConfiguration: TransformConfiguration.create(customDomTransformers: [ const RemoveScriptTransformer(), const BlockQuotedTransformer(), diff --git a/lib/features/manage_account/presentation/profiles/identities/identity_interactors_bindings.dart b/lib/features/manage_account/presentation/profiles/identities/identity_interactors_bindings.dart index d8a165822..495d435b3 100644 --- a/lib/features/manage_account/presentation/profiles/identities/identity_interactors_bindings.dart +++ b/lib/features/manage_account/presentation/profiles/identities/identity_interactors_bindings.dart @@ -1,3 +1,4 @@ +import 'package:core/presentation/utils/html_transformer/html_transform.dart'; import 'package:get/get.dart'; import 'package:tmail_ui_user/features/base/interactors_bindings.dart'; import 'package:tmail_ui_user/features/manage_account/data/datasource/identity_data_source.dart'; @@ -35,6 +36,7 @@ class IdentityInteractorsBindings extends InteractorsBindings { @override void bindingsDataSourceImpl() { Get.lazyPut(() => IdentityDataSourceImpl( + Get.find(), Get.find(), Get.find())); } diff --git a/lib/main/bindings/network/network_bindings.dart b/lib/main/bindings/network/network_bindings.dart index cced0d53c..f8684bd00 100644 --- a/lib/main/bindings/network/network_bindings.dart +++ b/lib/main/bindings/network/network_bindings.dart @@ -110,6 +110,7 @@ class NetworkBindings extends Bindings { void _bindingTransformer() { Get.put(const HtmlEscape()); - Get.put(HtmlAnalyzer(Get.find(), Get.find())); + Get.put(HtmlTransform(Get.find())); + Get.put(HtmlAnalyzer(Get.find(), Get.find())); } } \ No newline at end of file