courrier preview
This commit is contained in:
parent
5160967c75
commit
ddcb428233
@ -35,16 +35,38 @@ function cleanupTableStructures(htmlContent: string): string {
|
||||
htmlContent.includes('forwarded message') ||
|
||||
(htmlContent.includes('From:') && htmlContent.includes('Date:') && htmlContent.includes('Subject:'));
|
||||
|
||||
const isReplyEmail =
|
||||
htmlContent.includes('wrote:') ||
|
||||
htmlContent.includes('<blockquote') ||
|
||||
htmlContent.includes('gmail_quote');
|
||||
|
||||
// Check if content has complex tables that might cause issues
|
||||
const hasComplexTables = tables.length > 0 &&
|
||||
(isForwardedEmail || htmlContent.includes('gmail_quote') ||
|
||||
htmlContent.includes('blockquote') || htmlContent.includes('wrote:'));
|
||||
(isForwardedEmail || isReplyEmail);
|
||||
|
||||
if (hasComplexTables) {
|
||||
console.log(`Found ${tables.length} tables in complex email content`);
|
||||
|
||||
let convertedCount = 0;
|
||||
tables.forEach(table => {
|
||||
// Special handling for tables inside blockquotes (quoted content)
|
||||
if (table.closest('blockquote') ||
|
||||
(isReplyEmail && table.innerHTML.includes('wrote:'))) {
|
||||
console.log('Preserving table inside quoted content');
|
||||
// Apply minimal styling to ensure it renders correctly
|
||||
table.setAttribute('style', 'border-collapse: collapse; width: 100%; max-width: 100%; margin: 8px 0;');
|
||||
// Make sure all cells have some basic styling
|
||||
const cells = table.querySelectorAll('td, th');
|
||||
cells.forEach(cell => {
|
||||
if (cell instanceof HTMLTableCellElement) {
|
||||
cell.style.padding = '4px';
|
||||
cell.style.textAlign = 'left';
|
||||
cell.style.verticalAlign = 'top';
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Preserve the main forwarded email header table
|
||||
if (isForwardedEmail &&
|
||||
table.innerHTML.includes('From:') &&
|
||||
@ -180,8 +202,18 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
|
||||
hasBlockquote: initialContent.includes('<blockquote')
|
||||
});
|
||||
|
||||
// Process HTML content using centralized utility
|
||||
const processed = processHtmlContent(initialContent);
|
||||
// Check if content is reply or forward to use special handling
|
||||
const isReplyOrForward =
|
||||
initialContent.includes('wrote:') ||
|
||||
initialContent.includes('<blockquote') ||
|
||||
initialContent.includes('Forwarded message') ||
|
||||
initialContent.includes('---------- Forwarded message ----------');
|
||||
|
||||
// Process HTML content using centralized utility with special settings for replies/forwards
|
||||
const processed = processHtmlContent(initialContent, {
|
||||
sanitize: true,
|
||||
preserveReplyFormat: isReplyOrForward
|
||||
});
|
||||
const sanitizedContent = processed.sanitizedContent;
|
||||
const direction = processed.direction; // Use direction from processed result
|
||||
|
||||
@ -194,6 +226,7 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
|
||||
containsQuoteHeader: sanitizedContent.includes('wrote:'),
|
||||
hasTable: sanitizedContent.includes('<table'),
|
||||
hasBlockquote: sanitizedContent.includes('<blockquote'),
|
||||
isReplyOrForward: isReplyOrForward,
|
||||
firstNChars: sanitizedContent.substring(0, 100).replace(/\n/g, '\\n')
|
||||
});
|
||||
|
||||
@ -213,8 +246,19 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
|
||||
quillRef.current.setText('Error loading content');
|
||||
}
|
||||
} else {
|
||||
// Use direct innerHTML setting for the initial content
|
||||
quillRef.current.root.innerHTML = sanitizedContent;
|
||||
// Special handling for reply or forwarded content
|
||||
if (isReplyOrForward) {
|
||||
console.log('Using special handling for reply/forward content');
|
||||
|
||||
// Clean up any problematic table structures, with special care for quoted content
|
||||
const cleanedContent = cleanupTableStructures(sanitizedContent);
|
||||
|
||||
// Use direct innerHTML setting with minimal processing for reply/forward content
|
||||
quillRef.current.root.innerHTML = cleanedContent;
|
||||
} else {
|
||||
// Use direct innerHTML setting for regular content
|
||||
quillRef.current.root.innerHTML = sanitizedContent;
|
||||
}
|
||||
|
||||
// Set the direction for the content
|
||||
if (quillRef.current && quillRef.current.format) {
|
||||
@ -319,8 +363,18 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
|
||||
firstNChars: initialContent.substring(0, 100).replace(/\n/g, '\\n')
|
||||
});
|
||||
|
||||
// Check if content is reply or forward to use special handling
|
||||
const isReplyOrForward =
|
||||
initialContent.includes('wrote:') ||
|
||||
initialContent.includes('<blockquote') ||
|
||||
initialContent.includes('Forwarded message') ||
|
||||
initialContent.includes('---------- Forwarded message ----------');
|
||||
|
||||
// Process HTML content using centralized utility
|
||||
const processed = processHtmlContent(initialContent);
|
||||
const processed = processHtmlContent(initialContent, {
|
||||
sanitize: true,
|
||||
preserveReplyFormat: isReplyOrForward
|
||||
});
|
||||
const sanitizedContent = processed.sanitizedContent;
|
||||
const direction = processed.direction; // Use direction from processed result
|
||||
|
||||
@ -333,6 +387,7 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
|
||||
containsQuoteHeader: sanitizedContent.includes('wrote:'),
|
||||
hasTable: sanitizedContent.includes('<table'),
|
||||
hasBlockquote: sanitizedContent.includes('<blockquote'),
|
||||
isReplyOrForward: isReplyOrForward,
|
||||
firstNChars: sanitizedContent.substring(0, 100).replace(/\n/g, '\\n')
|
||||
});
|
||||
|
||||
@ -351,11 +406,17 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
|
||||
} else {
|
||||
// SIMPLIFIED: Set content directly to the root element rather than using clipboard
|
||||
if (quillRef.current && quillRef.current.root) {
|
||||
// Clean up any problematic table structures first
|
||||
const cleanedContent = cleanupTableStructures(sanitizedContent);
|
||||
// Special handling for reply or forward content
|
||||
let contentToSet = sanitizedContent;
|
||||
|
||||
if (isReplyOrForward) {
|
||||
console.log('Using special handling for reply/forward content update');
|
||||
// Clean up tables with special care for quoted content
|
||||
contentToSet = cleanupTableStructures(sanitizedContent);
|
||||
}
|
||||
|
||||
// First set the content
|
||||
quillRef.current.root.innerHTML = cleanedContent;
|
||||
quillRef.current.root.innerHTML = contentToSet;
|
||||
|
||||
// Then safely apply formatting only if quillRef is valid
|
||||
try {
|
||||
|
||||
@ -84,14 +84,23 @@ export const purify = configureDOMPurify();
|
||||
/**
|
||||
* Sanitize HTML content using our email-specific configuration
|
||||
*/
|
||||
export function sanitizeHtml(content: string): string {
|
||||
export function sanitizeHtml(content: string, options?: { preserveReplyFormat?: boolean }): string {
|
||||
if (!content) return '';
|
||||
|
||||
try {
|
||||
// Sanitize with our configured instance
|
||||
// Special handling for reply/forward emails to be less aggressive with sanitization
|
||||
const extraTags = options?.preserveReplyFormat
|
||||
? ['style', 'blockquote', 'table', 'thead', 'tbody', 'tr', 'td', 'th']
|
||||
: ['style'];
|
||||
|
||||
const extraAttrs = options?.preserveReplyFormat
|
||||
? ['style', 'class', 'align', 'valign', 'bgcolor', 'colspan', 'rowspan', 'width', 'height', 'border']
|
||||
: ['style', 'class'];
|
||||
|
||||
// Sanitize with our configured instance and options
|
||||
return purify.sanitize(content, {
|
||||
ADD_TAGS: ['style'], // Allow internal styles temporarily for cleaning
|
||||
ADD_ATTR: ['style', 'class'] // Allow style and class attributes
|
||||
ADD_TAGS: extraTags,
|
||||
ADD_ATTR: extraAttrs
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Failed to sanitize HTML content:', error);
|
||||
|
||||
@ -226,6 +226,7 @@ export function processHtmlContent(
|
||||
options?: {
|
||||
sanitize?: boolean;
|
||||
blockExternalContent?: boolean;
|
||||
preserveReplyFormat?: boolean;
|
||||
attachments?: Array<{
|
||||
filename?: string;
|
||||
name?: string;
|
||||
@ -252,6 +253,7 @@ export function processHtmlContent(
|
||||
containsForwardedMessage: htmlContent?.includes('---------- Forwarded message ----------'),
|
||||
containsQuoteHeader: htmlContent?.includes('<div class="gmail_quote"'),
|
||||
sanitize: options?.sanitize,
|
||||
preserveReplyFormat: options?.preserveReplyFormat,
|
||||
blockExternalContent: options?.blockExternalContent,
|
||||
hasAttachments: options?.attachments?.length || 0
|
||||
});
|
||||
@ -275,8 +277,13 @@ export function processHtmlContent(
|
||||
}
|
||||
|
||||
try {
|
||||
// Special handling for reply/forwarded content with less aggressive sanitization
|
||||
const isReplyOrForward = options?.preserveReplyFormat === true;
|
||||
|
||||
// Apply sanitization by default unless explicitly turned off
|
||||
let sanitizedContent = (options?.sanitize !== false) ? sanitizeHtml(htmlContent) : htmlContent;
|
||||
let sanitizedContent = (options?.sanitize !== false)
|
||||
? sanitizeHtml(htmlContent, { preserveReplyFormat: isReplyOrForward })
|
||||
: htmlContent;
|
||||
|
||||
// Log content changes from sanitization
|
||||
console.log('HTML sanitization results:', {
|
||||
@ -284,10 +291,11 @@ export function processHtmlContent(
|
||||
sanitizedLength: sanitizedContent.length,
|
||||
difference: originalContent.length - sanitizedContent.length,
|
||||
percentRemoved: ((originalContent.length - sanitizedContent.length) / originalContent.length * 100).toFixed(2) + '%',
|
||||
isEmpty: !sanitizedContent || sanitizedContent.trim().length === 0
|
||||
isEmpty: !sanitizedContent || sanitizedContent.trim().length === 0,
|
||||
isReplyOrForward: isReplyOrForward
|
||||
});
|
||||
|
||||
// Check if content is a forwarded message to ensure special handling for tables
|
||||
// Detect if content is a forwarded message to ensure special handling for tables
|
||||
const isForwardedEmail =
|
||||
sanitizedContent.includes('---------- Forwarded message ----------') ||
|
||||
sanitizedContent.includes('Forwarded message') ||
|
||||
@ -295,8 +303,8 @@ export function processHtmlContent(
|
||||
sanitizedContent.includes('Subject:') && sanitizedContent.includes('To:'));
|
||||
|
||||
// Special processing for forwarded email styling
|
||||
if (isForwardedEmail) {
|
||||
console.log('Detected forwarded email content, preserving table structure');
|
||||
if (isForwardedEmail || isReplyOrForward) {
|
||||
console.log('Detected forwarded email or reply content, enhancing structure');
|
||||
// Make sure we're not removing important table structures
|
||||
sanitizedContent = sanitizedContent
|
||||
// Preserve table styling for email headers
|
||||
|
||||
@ -324,14 +324,26 @@ export function formatReplyEmail(originalEmail: EmailMessage | LegacyEmailMessag
|
||||
|
||||
// Create the quoted reply content
|
||||
if (htmlContent) {
|
||||
// Format HTML reply
|
||||
// Format HTML reply with better styling for quoted content
|
||||
console.log('Formatting HTML reply, quoted content length:', htmlContent.length);
|
||||
|
||||
// Apply minimal sanitization to the original content - preserve more structure
|
||||
// We'll do a more comprehensive sanitization later in the flow
|
||||
const sanitizedOriginal = sanitizeHtml(htmlContent, { preserveReplyFormat: true });
|
||||
|
||||
// Check if original content already contains blockquotes or is reply/forward
|
||||
const containsExistingQuote =
|
||||
sanitizedOriginal.includes('<blockquote') ||
|
||||
sanitizedOriginal.includes('wrote:') ||
|
||||
sanitizedOriginal.includes('---------- Forwarded message ----------');
|
||||
|
||||
// Preserve existing quotes and add outer quote
|
||||
htmlContent = `
|
||||
<div style="margin: 20px 0 10px 0; color: #666; border-bottom: 1px solid #ddd; padding-bottom: 5px;">
|
||||
On ${date}, ${sender} wrote:
|
||||
</div>
|
||||
<blockquote style="margin: 0; padding-left: 10px; border-left: 3px solid #ddd; color: #505050; background-color: #f9f9f9; padding: 10px;">
|
||||
${sanitizeHtml(htmlContent)}
|
||||
${sanitizedOriginal}
|
||||
</blockquote>
|
||||
`;
|
||||
}
|
||||
@ -477,7 +489,7 @@ export function processCidReferences(htmlContent: string, attachments?: Array<{
|
||||
* Format email for forwarding
|
||||
*/
|
||||
export function formatForwardedEmail(originalEmail: EmailMessage | LegacyEmailMessage | null): FormattedEmail {
|
||||
console.log('formatForwardedEmail called:', { emailId: originalEmail?.id });
|
||||
console.log('formatForwardedEmail called, emailId:', originalEmail?.id);
|
||||
|
||||
if (!originalEmail) {
|
||||
console.warn('formatForwardedEmail: No original email provided');
|
||||
@ -496,14 +508,6 @@ export function formatForwardedEmail(originalEmail: EmailMessage | LegacyEmailMe
|
||||
(email.subject.toLowerCase().startsWith('fwd:') ? email.subject : `Fwd: ${email.subject}`) :
|
||||
'Fwd: ';
|
||||
|
||||
// Get original email info for headers
|
||||
const { fromStr, toStr, ccStr, dateStr } = getFormattedHeaderInfo(email);
|
||||
|
||||
console.log('Forward header info:', { fromStr, toStr, dateStr, subject });
|
||||
|
||||
// Original sent date
|
||||
const date = dateStr;
|
||||
|
||||
// Get email content
|
||||
const originalContent = email.content;
|
||||
|
||||
@ -544,75 +548,66 @@ export function formatForwardedEmail(originalEmail: EmailMessage | LegacyEmailMe
|
||||
htmlContent = formatPlainTextToHtml(textContent);
|
||||
}
|
||||
}
|
||||
|
||||
// Process embedded images with CID references
|
||||
if (htmlContent && email.attachments && email.attachments.length > 0) {
|
||||
console.log('Processing CID references before sanitization');
|
||||
htmlContent = processCidReferences(htmlContent, email.attachments);
|
||||
}
|
||||
|
||||
// Create the forwarded email HTML content
|
||||
|
||||
// Get header info for the forwarded message
|
||||
const headerInfo = getFormattedHeaderInfo(email);
|
||||
|
||||
// Create the forwarded content
|
||||
if (htmlContent) {
|
||||
console.log('Formatting HTML forward, original content length:', htmlContent.length);
|
||||
console.log('Formatting HTML forward, content length:', htmlContent.length);
|
||||
|
||||
// Important: First sanitize the content portion only
|
||||
const sanitizedOriginalContent = sanitizeHtml(htmlContent);
|
||||
console.log('Sanitized original content length:', sanitizedOriginalContent.length);
|
||||
// Apply minimal sanitization to the original content - preserve more structure
|
||||
// We'll do a more comprehensive sanitization later in the flow
|
||||
const sanitizedOriginal = sanitizeHtml(htmlContent, { preserveReplyFormat: true });
|
||||
|
||||
// Create the complete forwarded email with header info
|
||||
const fullForwardedEmail = `
|
||||
// Create forwarded message with header info
|
||||
htmlContent = `
|
||||
<div style="margin: 20px 0 10px 0; color: #666; font-family: Arial, sans-serif;">
|
||||
---------- Forwarded message ----------<br>
|
||||
<table style="margin: 10px 0 15px 0; border-collapse: collapse; font-size: 13px; color: #333;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="padding: 3px 10px 3px 0; font-weight: bold; text-align: right; vertical-align: top;">From:</td>
|
||||
<td style="padding: 3px 0;">${fromStr}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 3px 10px 3px 0; font-weight: bold; text-align: right; vertical-align: top;">Date:</td>
|
||||
<td style="padding: 3px 0;">${date}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 3px 10px 3px 0; font-weight: bold; text-align: right; vertical-align: top;">Subject:</td>
|
||||
<td style="padding: 3px 0;">${email.subject || ''}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 3px 10px 3px 0; font-weight: bold; text-align: right; vertical-align: top;">To:</td>
|
||||
<td style="padding: 3px 0;">${toStr}</td>
|
||||
</tr>
|
||||
${ccStr ? `
|
||||
<tr>
|
||||
<td style="padding: 3px 10px 3px 0; font-weight: bold; text-align: right; vertical-align: top;">Cc:</td>
|
||||
<td style="padding: 3px 0;">${ccStr}</td>
|
||||
</tr>
|
||||
` : ''}
|
||||
</tbody>
|
||||
<div style="border-bottom: 1px solid #ccc; margin-bottom: 10px; padding-bottom: 5px;">
|
||||
<div>---------------------------- Forwarded Message ----------------------------</div>
|
||||
</div>
|
||||
<table style="margin-bottom: 10px; font-size: 14px; border-collapse: collapse;">
|
||||
<tr>
|
||||
<td style="padding: 3px 10px 3px 0; font-weight: bold; text-align: right; vertical-align: top;">From:</td>
|
||||
<td style="padding: 3px 0;">${headerInfo.fromStr}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 3px 10px 3px 0; font-weight: bold; text-align: right; vertical-align: top;">Date:</td>
|
||||
<td style="padding: 3px 0;">${headerInfo.dateStr}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 3px 10px 3px 0; font-weight: bold; text-align: right; vertical-align: top;">Subject:</td>
|
||||
<td style="padding: 3px 0;">${headerInfo.subject}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 3px 10px 3px 0; font-weight: bold; text-align: right; vertical-align: top;">To:</td>
|
||||
<td style="padding: 3px 0;">${headerInfo.toStr}</td>
|
||||
</tr>
|
||||
${headerInfo.ccStr ? `
|
||||
<tr>
|
||||
<td style="padding: 3px 10px 3px 0; font-weight: bold; text-align: right; vertical-align: top;">Cc:</td>
|
||||
<td style="padding: 3px 0;">${headerInfo.ccStr}</td>
|
||||
</tr>` : ''}
|
||||
</table>
|
||||
<div style="border-bottom: 1px solid #ccc; margin-top: 5px; margin-bottom: 15px; padding-bottom: 5px;">
|
||||
<div>----------------------------------------------------------------------</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="forwarded-content" style="margin: 0; color: #333;">
|
||||
${sanitizedOriginal}
|
||||
</div>
|
||||
<blockquote style="margin: 0; padding-left: 10px; border-left: 3px solid #ddd; color: #505050; background-color: #f9f9f9; padding: 10px;">
|
||||
${sanitizedOriginalContent}
|
||||
</blockquote>
|
||||
`;
|
||||
|
||||
// Now we have the full forwarded email structure without sanitizing it again
|
||||
htmlContent = fullForwardedEmail;
|
||||
|
||||
console.log('Final forward HTML content length:', htmlContent.length,
|
||||
'contains table:', htmlContent.includes('<table'),
|
||||
'contains forwarded message:', htmlContent.includes('---------- Forwarded message ----------'),
|
||||
'contains sanitized content:', htmlContent.includes(sanitizedOriginalContent.substring(0, 30)));
|
||||
}
|
||||
|
||||
// Format the plain text version
|
||||
if (textContent) {
|
||||
// Format plain text forward
|
||||
textContent = `
|
||||
---------- Forwarded message ----------
|
||||
From: ${fromStr}
|
||||
Date: ${date}
|
||||
Subject: ${email.subject || ''}
|
||||
To: ${toStr}
|
||||
${ccStr ? `Cc: ${ccStr}\n` : ''}
|
||||
From: ${headerInfo.fromStr}
|
||||
Date: ${headerInfo.dateStr}
|
||||
Subject: ${headerInfo.subject}
|
||||
To: ${headerInfo.toStr}
|
||||
${headerInfo.ccStr ? `Cc: ${headerInfo.ccStr}` : ''}
|
||||
|
||||
${textContent}
|
||||
`.trim();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user