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.
|
* This is the primary and only email formatting utility to be used.
|
||||||
* All email formatting should go through here to ensure consistent
|
* All email formatting should go through here to ensure consistent
|
||||||
* handling of text direction and HTML sanitization.
|
* 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';
|
import DOMPurify from 'isomorphic-dompurify';
|
||||||
|
|
||||||
// Configure DOMPurify to preserve direction attributes
|
// Reset any existing hooks to start clean
|
||||||
DOMPurify.addHook('afterSanitizeAttributes', function(node) {
|
DOMPurify.removeAllHooks();
|
||||||
// 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');
|
|
||||||
|
|
||||||
// Configure DOMPurify for English-only content (always LTR)
|
// Configure DOMPurify for English-only content (always LTR)
|
||||||
DOMPurify.addHook('afterSanitizeAttributes', function(node) {
|
DOMPurify.addHook('afterSanitizeAttributes', function(node) {
|
||||||
// Always set direction to LTR for all elements
|
// Force LTR direction on all elements that can have a dir attribute
|
||||||
node.setAttribute('dir', 'ltr');
|
if (node instanceof HTMLElement) {
|
||||||
|
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
|
// Handle style attribute
|
||||||
if (style.includes('text-align: right') || style.includes('text-align:right')) {
|
if (node.hasAttribute('style')) {
|
||||||
style = style.replace(/text-align:\s*right\s*;?/gi, '');
|
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();
|
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
|
// Interface definitions
|
||||||
export interface EmailAddress {
|
export interface EmailAddress {
|
||||||
name: string;
|
name: string;
|
||||||
@ -151,13 +117,14 @@ export function formatEmailDate(date: Date | string | undefined): string {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Sanitize HTML content before processing or displaying
|
* Sanitize HTML content before processing or displaying
|
||||||
|
* This ensures the content is properly formatted for LTR display
|
||||||
* @param content HTML content to sanitize
|
* @param content HTML content to sanitize
|
||||||
* @returns Sanitized HTML
|
* @returns Sanitized HTML with LTR formatting
|
||||||
*/
|
*/
|
||||||
export function sanitizeHtml(content: string): string {
|
export function sanitizeHtml(content: string): string {
|
||||||
if (!content) return '';
|
if (!content) return '';
|
||||||
|
|
||||||
// Sanitize the HTML using our configured DOMPurify
|
// Sanitize the HTML using our configured DOMPurify with LTR enforced
|
||||||
return DOMPurify.sanitize(content);
|
return DOMPurify.sanitize(content);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,7 +147,7 @@ export function formatForwardedEmail(email: EmailMessage): {
|
|||||||
const toString = formatEmailAddresses(email.to || []);
|
const toString = formatEmailAddresses(email.to || []);
|
||||||
const dateString = formatEmailDate(email.date);
|
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 || '');
|
const originalContent = sanitizeHtml(email.content || email.html || email.text || '');
|
||||||
|
|
||||||
// Check if the content already has a forwarded message header
|
// 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 there's already a forwarded message header, don't add another one
|
||||||
if (hasExistingHeader) {
|
if (hasExistingHeader) {
|
||||||
// Just wrap the content in appropriate styling without adding another header
|
// Just wrap the content without additional formatting
|
||||||
const content = `
|
const content = `
|
||||||
<div style="min-height: 20px;"></div>
|
<div style="min-height: 20px;"></div>
|
||||||
<div class="email-original-content">
|
<div class="email-original-content">
|
||||||
@ -256,7 +223,7 @@ export function formatReplyEmail(email: EmailMessage, type: 'reply' | 'reply-all
|
|||||||
// Create quote header
|
// Create quote header
|
||||||
const quoteHeader = `<div style="font-weight: 500;">On ${formattedDate}, ${fromText} wrote:</div>`;
|
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 || '');
|
const quotedContent = sanitizeHtml(email.html || email.content || email.text || '');
|
||||||
|
|
||||||
// Format recipients
|
// 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
|
* COMPATIBILITY LAYER: For backward compatibility with the old email-formatter.ts
|
||||||
* These functions map to our new implementation but preserve the old interface
|
* 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(
|
export function formatEmailForReplyOrForward(
|
||||||
email: EmailMessage,
|
email: EmailMessage,
|
||||||
type: 'reply' | 'reply-all' | 'forward'
|
type: 'reply' | 'reply-all' | 'forward'
|
||||||
@ -329,7 +294,6 @@ export function formatEmailForReplyOrForward(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Decode compose content from MIME format to HTML and text
|
* 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<{
|
export async function decodeComposeContent(content: string): Promise<{
|
||||||
html: string | null;
|
html: string | null;
|
||||||
@ -353,15 +317,17 @@ export async function decodeComposeContent(content: string): Promise<{
|
|||||||
}
|
}
|
||||||
|
|
||||||
const parsed = await response.json();
|
const parsed = await response.json();
|
||||||
|
|
||||||
|
// Apply LTR sanitization to the parsed content
|
||||||
return {
|
return {
|
||||||
html: parsed.html || null,
|
html: parsed.html ? sanitizeHtml(parsed.html) : null,
|
||||||
text: parsed.text || null
|
text: parsed.text || null
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error parsing email content:', error);
|
console.error('Error parsing email content:', error);
|
||||||
// Fallback to basic content handling
|
// Fallback to basic content handling with sanitization
|
||||||
return {
|
return {
|
||||||
html: content,
|
html: sanitizeHtml(content),
|
||||||
text: content
|
text: content
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -369,7 +335,6 @@ export async function decodeComposeContent(content: string): Promise<{
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Encode compose content to MIME format for sending
|
* 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 {
|
export function encodeComposeContent(content: string): string {
|
||||||
if (!content.trim()) {
|
if (!content.trim()) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user