courrier clean 2$
This commit is contained in:
parent
e40df0ad87
commit
1685946c07
@ -4,84 +4,50 @@
|
||||
* This is the primary and only email formatting utility to be used.
|
||||
* All email formatting should go through here to ensure consistent
|
||||
* handling of text direction and HTML sanitization.
|
||||
*
|
||||
* IMPORTANT: This formatter is configured for English-only content
|
||||
* and enforces left-to-right text direction.
|
||||
*/
|
||||
|
||||
import DOMPurify from 'isomorphic-dompurify';
|
||||
|
||||
// Configure DOMPurify to preserve direction attributes
|
||||
DOMPurify.addHook('afterSanitizeAttributes', function(node) {
|
||||
// Preserve direction attributes
|
||||
if (node.hasAttribute('dir')) {
|
||||
node.setAttribute('dir', node.getAttribute('dir') || 'ltr');
|
||||
}
|
||||
// Preserve text-align in styles
|
||||
if (node.hasAttribute('style')) {
|
||||
const style = node.getAttribute('style') || '';
|
||||
if (style.includes('text-align')) {
|
||||
// Keep existing alignment
|
||||
} else if (node.hasAttribute('dir') && node.getAttribute('dir') === 'rtl') {
|
||||
node.setAttribute('style', style + '; text-align: right;');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Configure DOMPurify to enforce LTR for English-only content
|
||||
DOMPurify.addHook('afterSanitizeAttributes', function(node) {
|
||||
// Always set direction to LTR for all elements
|
||||
if (node.hasAttribute('dir')) {
|
||||
node.setAttribute('dir', 'ltr');
|
||||
}
|
||||
|
||||
// Ensure text alignment is left-aligned for all elements
|
||||
if (node.hasAttribute('style')) {
|
||||
let style = node.getAttribute('style') || '';
|
||||
|
||||
// Remove any right-to-left text alignment
|
||||
if (style.includes('text-align: right') || style.includes('text-align:right')) {
|
||||
style = style.replace(/text-align:\s*right\s*;?/gi, '');
|
||||
style = style.trim();
|
||||
// Add semicolon if needed
|
||||
if (style && !style.endsWith(';')) {
|
||||
style += ';';
|
||||
}
|
||||
}
|
||||
|
||||
// Add left alignment if not already specified
|
||||
if (!style.includes('text-align:')) {
|
||||
style += (style ? ' ' : '') + 'text-align: left;';
|
||||
}
|
||||
|
||||
node.setAttribute('style', style);
|
||||
}
|
||||
});
|
||||
|
||||
// Clear existing hooks first
|
||||
DOMPurify.removeHook('afterSanitizeAttributes');
|
||||
// Reset any existing hooks to start clean
|
||||
DOMPurify.removeAllHooks();
|
||||
|
||||
// Configure DOMPurify for English-only content (always LTR)
|
||||
DOMPurify.addHook('afterSanitizeAttributes', function(node) {
|
||||
// Always set direction to LTR for all elements
|
||||
node.setAttribute('dir', 'ltr');
|
||||
|
||||
// Ensure text alignment is left-aligned for all elements
|
||||
if (node.hasAttribute('style')) {
|
||||
let style = node.getAttribute('style') || '';
|
||||
// Force LTR direction on all elements that can have a dir attribute
|
||||
if (node instanceof HTMLElement) {
|
||||
node.setAttribute('dir', 'ltr');
|
||||
|
||||
// Remove any right-to-left text alignment
|
||||
if (style.includes('text-align: right') || style.includes('text-align:right')) {
|
||||
style = style.replace(/text-align:\s*right\s*;?/gi, '');
|
||||
// Handle style attribute
|
||||
if (node.hasAttribute('style')) {
|
||||
let style = node.getAttribute('style') || '';
|
||||
|
||||
// Remove any RTL-related styles
|
||||
style = style.replace(/direction\s*:\s*rtl\s*;?/gi, '');
|
||||
style = style.replace(/text-align\s*:\s*right\s*;?/gi, '');
|
||||
style = style.replace(/unicode-bidi\s*:[^;]*;?/gi, '');
|
||||
|
||||
// Add explicit LTR styles
|
||||
style = style.trim();
|
||||
if (style && !style.endsWith(';')) style += ';';
|
||||
style += ' direction: ltr; text-align: left;';
|
||||
|
||||
node.setAttribute('style', style);
|
||||
} else {
|
||||
// If no style exists, add default LTR styles
|
||||
node.setAttribute('style', 'direction: ltr; text-align: left;');
|
||||
}
|
||||
|
||||
// Add left alignment
|
||||
style = (style ? style + '; ' : '') + 'text-align: left;';
|
||||
node.setAttribute('style', style);
|
||||
} else {
|
||||
// If no style exists, add default left alignment
|
||||
node.setAttribute('style', 'text-align: left;');
|
||||
}
|
||||
});
|
||||
|
||||
// Configure DOMPurify to add certain attributes and forbid others
|
||||
DOMPurify.setConfig({
|
||||
ADD_ATTR: ['dir'],
|
||||
FORBID_ATTR: ['lang', 'bidi']
|
||||
});
|
||||
|
||||
// Interface definitions
|
||||
export interface EmailAddress {
|
||||
name: string;
|
||||
@ -151,13 +117,14 @@ export function formatEmailDate(date: Date | string | undefined): string {
|
||||
|
||||
/**
|
||||
* Sanitize HTML content before processing or displaying
|
||||
* This ensures the content is properly formatted for LTR display
|
||||
* @param content HTML content to sanitize
|
||||
* @returns Sanitized HTML
|
||||
* @returns Sanitized HTML with LTR formatting
|
||||
*/
|
||||
export function sanitizeHtml(content: string): string {
|
||||
if (!content) return '';
|
||||
|
||||
// Sanitize the HTML using our configured DOMPurify
|
||||
// Sanitize the HTML using our configured DOMPurify with LTR enforced
|
||||
return DOMPurify.sanitize(content);
|
||||
}
|
||||
|
||||
@ -180,7 +147,7 @@ export function formatForwardedEmail(email: EmailMessage): {
|
||||
const toString = formatEmailAddresses(email.to || []);
|
||||
const dateString = formatEmailDate(email.date);
|
||||
|
||||
// Get and sanitize original content
|
||||
// Get and sanitize original content (sanitization enforces LTR)
|
||||
const originalContent = sanitizeHtml(email.content || email.html || email.text || '');
|
||||
|
||||
// Check if the content already has a forwarded message header
|
||||
@ -188,7 +155,7 @@ export function formatForwardedEmail(email: EmailMessage): {
|
||||
|
||||
// If there's already a forwarded message header, don't add another one
|
||||
if (hasExistingHeader) {
|
||||
// Just wrap the content in appropriate styling without adding another header
|
||||
// Just wrap the content without additional formatting
|
||||
const content = `
|
||||
<div style="min-height: 20px;"></div>
|
||||
<div class="email-original-content">
|
||||
@ -256,7 +223,7 @@ export function formatReplyEmail(email: EmailMessage, type: 'reply' | 'reply-all
|
||||
// Create quote header
|
||||
const quoteHeader = `<div style="font-weight: 500;">On ${formattedDate}, ${fromText} wrote:</div>`;
|
||||
|
||||
// Get and sanitize original content
|
||||
// Get and sanitize original content (sanitization enforces LTR)
|
||||
const quotedContent = sanitizeHtml(email.html || email.content || email.text || '');
|
||||
|
||||
// Format recipients
|
||||
@ -298,8 +265,6 @@ export function formatReplyEmail(email: EmailMessage, type: 'reply' | 'reply-all
|
||||
* COMPATIBILITY LAYER: For backward compatibility with the old email-formatter.ts
|
||||
* These functions map to our new implementation but preserve the old interface
|
||||
*/
|
||||
|
||||
// For compatibility with old code that might be using the other email-formatter.ts
|
||||
export function formatEmailForReplyOrForward(
|
||||
email: EmailMessage,
|
||||
type: 'reply' | 'reply-all' | 'forward'
|
||||
@ -329,7 +294,6 @@ export function formatEmailForReplyOrForward(
|
||||
|
||||
/**
|
||||
* Decode compose content from MIME format to HTML and text
|
||||
* This replaces the functionality previously in lib/compose-mime-decoder.ts
|
||||
*/
|
||||
export async function decodeComposeContent(content: string): Promise<{
|
||||
html: string | null;
|
||||
@ -353,15 +317,17 @@ export async function decodeComposeContent(content: string): Promise<{
|
||||
}
|
||||
|
||||
const parsed = await response.json();
|
||||
|
||||
// Apply LTR sanitization to the parsed content
|
||||
return {
|
||||
html: parsed.html || null,
|
||||
html: parsed.html ? sanitizeHtml(parsed.html) : null,
|
||||
text: parsed.text || null
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error parsing email content:', error);
|
||||
// Fallback to basic content handling
|
||||
// Fallback to basic content handling with sanitization
|
||||
return {
|
||||
html: content,
|
||||
html: sanitizeHtml(content),
|
||||
text: content
|
||||
};
|
||||
}
|
||||
@ -369,7 +335,6 @@ export async function decodeComposeContent(content: string): Promise<{
|
||||
|
||||
/**
|
||||
* Encode compose content to MIME format for sending
|
||||
* This replaces the functionality previously in lib/compose-mime-decoder.ts
|
||||
*/
|
||||
export function encodeComposeContent(content: string): string {
|
||||
if (!content.trim()) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user