courrier clean 2$

This commit is contained in:
alma 2025-04-26 21:05:25 +02:00
parent e40df0ad87
commit 1685946c07

View File

@ -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()) {