+
diff --git a/lib/utils/email-content.ts b/lib/utils/email-content.ts
index 3667bcad..0c30aa81 100644
--- a/lib/utils/email-content.ts
+++ b/lib/utils/email-content.ts
@@ -1,5 +1,10 @@
import DOMPurify from 'dompurify';
+/**
+ * Format and standardize email content for display following email industry standards.
+ * This function handles various email content formats and ensures proper display
+ * including support for HTML emails, plain text emails, RTL languages, and email client quirks.
+ */
export function formatEmailContent(email: any): string {
if (!email) {
console.log('formatEmailContent: No email provided');
@@ -7,47 +12,91 @@ export function formatEmailContent(email: any): string {
}
try {
- console.log('formatEmailContent: Raw email content:', {
- content: email.content,
- html: email.html,
- text: email.text,
- formattedContent: email.formattedContent
- });
-
- // Get the content in order of preference
+ // Get the content in order of preference with proper fallbacks
let content = '';
+ let isHtml = false;
+ let textContent = '';
+ // Extract content based on standardized property hierarchy
if (email.content && typeof email.content === 'object') {
- console.log('formatEmailContent: Using object content:', email.content);
- content = email.content.html || email.content.text || '';
+ isHtml = !!email.content.html;
+ content = email.content.html || '';
+ textContent = email.content.text || '';
} else if (typeof email.content === 'string') {
- console.log('formatEmailContent: Using direct string content');
+ // Check if the string content is HTML
+ isHtml = email.content.trim().startsWith('<') &&
+ (email.content.includes(''));
content = email.content;
+ textContent = email.content;
} else if (email.html) {
- console.log('formatEmailContent: Using html content');
+ isHtml = true;
content = email.html;
+ textContent = email.text || '';
} else if (email.text) {
- console.log('formatEmailContent: Using text content');
- content = email.text;
+ isHtml = false;
+ content = '';
+ textContent = email.text;
} else if (email.formattedContent) {
- console.log('formatEmailContent: Using formattedContent');
+ // Assume formattedContent is already HTML
+ isHtml = true;
content = email.formattedContent;
+ textContent = '';
}
- console.log('formatEmailContent: Content before sanitization:', content);
+ // If we have HTML content, sanitize and standardize it
+ if (isHtml && content) {
+ // Sanitize with industry-standard email tags and attributes
+ const sanitizedContent = DOMPurify.sanitize(content, {
+ ADD_TAGS: [
+ 'style', 'table', 'thead', 'tbody', 'tfoot', 'tr', 'td', 'th',
+ 'caption', 'col', 'colgroup', 'div', 'span', 'img', 'br', 'hr',
+ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'blockquote', 'pre',
+ 'ul', 'ol', 'li', 'dl', 'dt', 'dd', 'a', 'b', 'i', 'u', 'em',
+ 'strong', 'del', 'ins', 'sub', 'sup', 'small', 'mark', 'q'
+ ],
+ ADD_ATTR: [
+ 'class', 'style', 'id', 'href', 'src', 'alt', 'title', 'width', 'height',
+ 'border', 'cellspacing', 'cellpadding', 'bgcolor', 'color', 'dir', 'lang',
+ 'align', 'valign', 'span', 'colspan', 'rowspan', 'target', 'rel',
+ 'background', 'data-*'
+ ],
+ ALLOW_DATA_ATTR: true,
+ WHOLE_DOCUMENT: false,
+ RETURN_DOM: false,
+ FORBID_TAGS: ['script', 'iframe', 'object', 'embed', 'form', 'input', 'textarea', 'select', 'button'],
+ FORBID_ATTR: ['onerror', 'onload', 'onclick', 'onmouseover', 'onmouseout']
+ });
+
+ // Wrap the content in standard email container with responsive styling
+ return `
+
+ ${sanitizedContent}
+
+ `;
+ }
+ // If we only have text content, format it properly
+ else if (textContent) {
+ // Format plain text with proper line breaks and paragraphs
+ const formattedText = textContent
+ .replace(/\r\n|\r|\n/g, '
') // Convert all newlines to
+ .replace(/((?:
){2,})/g, '
') // Convert multiple newlines to paragraphs
+ .replace(/
<\/p>/g, '
') // Fix any
combinations
+ .replace(/
/g, '
'); // Fix any
combinations
+
+ return `
+
+ `;
+ }
- // Sanitize the content for display while preserving formatting
- const sanitizedContent = DOMPurify.sanitize(content, {
- ADD_TAGS: ['style', 'table', 'thead', 'tbody', 'tr', 'td', 'th'],
- ADD_ATTR: ['class', 'style', 'dir', 'colspan', 'rowspan'],
- ALLOW_DATA_ATTR: false
- });
-
- console.log('formatEmailContent: Final sanitized content:', sanitizedContent);
-
- return sanitizedContent;
+ // Default case: empty or unrecognized content
+ return '
No content available
';
} catch (error) {
console.error('formatEmailContent: Error formatting email content:', error);
- return '';
+ return '
Error displaying email content
';
}
}
\ No newline at end of file
diff --git a/lib/utils/email-formatter.ts b/lib/utils/email-formatter.ts
index 29180535..58e716e1 100644
--- a/lib/utils/email-formatter.ts
+++ b/lib/utils/email-formatter.ts
@@ -135,34 +135,54 @@ export function formatEmailDate(date: Date | string | undefined): string {
/**
* Sanitize HTML content before processing or displaying
- * This ensures the content is properly sanitized while preserving text direction
+ * Implements email industry standards for proper, consistent, and secure rendering
+ *
* @param html HTML content to sanitize
- * @returns Sanitized HTML with preserved text direction
+ * @returns Sanitized HTML with preserved styling and structure
*/
export function sanitizeHtml(html: string): string {
if (!html) return '';
try {
- // Use DOMPurify but ensure we keep all elements and attributes that might be in emails
+ // Use DOMPurify with comprehensive email HTML standards
const clean = DOMPurify.sanitize(html, {
- ADD_TAGS: ['button', 'style', 'img', 'iframe', 'meta', 'table', 'thead', 'tbody', 'tr', 'td', 'th'],
- ADD_ATTR: ['target', 'rel', 'style', 'class', 'id', 'href', 'src', 'alt', 'title', 'width', 'height', 'onclick', 'colspan', 'rowspan'],
+ ADD_TAGS: [
+ 'html', 'head', 'body', 'style', 'link', 'meta', 'title',
+ 'table', 'caption', 'col', 'colgroup', 'thead', 'tbody', 'tfoot', 'tr', 'td', 'th',
+ 'div', 'span', 'img', 'br', 'hr', 'section', 'article', 'header', 'footer',
+ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'blockquote', 'pre', 'code',
+ 'ul', 'ol', 'li', 'dl', 'dt', 'dd', 'a', 'b', 'i', 'u', 'em',
+ 'strong', 'del', 'ins', 'mark', 'small', 'sub', 'sup', 'q', 'abbr'
+ ],
+ ADD_ATTR: [
+ 'style', 'class', 'id', 'name', 'href', 'src', 'alt', 'title', 'width', 'height',
+ 'border', 'cellspacing', 'cellpadding', 'bgcolor', 'background', 'color',
+ 'align', 'valign', 'dir', 'lang', 'target', 'rel', 'charset', 'media',
+ 'colspan', 'rowspan', 'scope', 'span', 'size', 'face', 'hspace', 'vspace',
+ 'data-*'
+ ],
KEEP_CONTENT: true,
WHOLE_DOCUMENT: false,
ALLOW_DATA_ATTR: true,
- ALLOW_UNKNOWN_PROTOCOLS: true,
- FORCE_BODY: false,
- RETURN_DOM: false,
- RETURN_DOM_FRAGMENT: false,
+ ALLOW_UNKNOWN_PROTOCOLS: true, // Needed for some email clients
+ FORBID_TAGS: ['script', 'iframe', 'object', 'embed', 'form', 'input', 'button', 'select', 'textarea'],
+ FORBID_ATTR: ['onerror', 'onload', 'onclick', 'onmouseover', 'onmouseout'],
+ FORCE_BODY: false
});
- return clean;
+ // Fix common email rendering issues
+ return clean
+ // Fix for Outlook WebVML content
+ .replace(/