courrier preview

This commit is contained in:
alma 2025-05-01 15:36:05 +02:00
parent 2b02742bc4
commit 1f6032941a
4 changed files with 104 additions and 29 deletions

View File

@ -91,6 +91,9 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
console.log('Setting initial content in editor', {
length: initialContent.length,
startsWithHtml: initialContent.trim().startsWith('<'),
containsForwardedMessage: initialContent.includes('---------- Forwarded message ----------'),
containsReplyIndicator: initialContent.includes('wrote:'),
hasBlockquote: initialContent.includes('<blockquote')
});
// Detect text direction
@ -99,6 +102,18 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
// Process HTML content using centralized utility
const sanitizedContent = processHtmlContent(initialContent);
// Log sanitized content details for debugging
console.log('Sanitized content details:', {
length: sanitizedContent.length,
isEmpty: sanitizedContent.trim().length === 0,
startsWithDiv: sanitizedContent.trim().startsWith('<div'),
containsForwardedMessage: sanitizedContent.includes('---------- Forwarded message ----------'),
containsQuoteHeader: sanitizedContent.includes('wrote:'),
hasTable: sanitizedContent.includes('<table'),
hasBlockquote: sanitizedContent.includes('<blockquote'),
firstNChars: sanitizedContent.substring(0, 100).replace(/\n/g, '\\n')
});
// Check if sanitized content is valid
if (sanitizedContent.trim().length === 0) {
console.warn('Sanitized content is empty after processing, using fallback approach');
@ -213,7 +228,12 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
try {
console.log('Updating content in editor:', {
contentLength: initialContent.length,
startsWithHtml: initialContent.trim().startsWith('<')
startsWithHtml: initialContent.trim().startsWith('<'),
containsForwardedMessage: initialContent.includes('---------- Forwarded message ----------'),
containsQuoteHeader: initialContent.includes('wrote:'),
hasBlockquote: initialContent.includes('<blockquote'),
hasTable: initialContent.includes('<table'),
firstNChars: initialContent.substring(0, 100).replace(/\n/g, '\\n')
});
// Detect text direction
@ -222,6 +242,18 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
// Process HTML content using centralized utility
const sanitizedContent = processHtmlContent(initialContent);
// Log sanitized content details for debugging
console.log('Sanitized content details:', {
length: sanitizedContent.length,
isEmpty: sanitizedContent.trim().length === 0,
startsWithDiv: sanitizedContent.trim().startsWith('<div'),
containsForwardedMessage: sanitizedContent.includes('---------- Forwarded message ----------'),
containsQuoteHeader: sanitizedContent.includes('wrote:'),
hasTable: sanitizedContent.includes('<table'),
hasBlockquote: sanitizedContent.includes('<blockquote'),
firstNChars: sanitizedContent.substring(0, 100).replace(/\n/g, '\\n')
});
// Check if content is valid HTML
if (sanitizedContent.trim().length === 0) {
console.warn('Sanitized content is empty, using original content');

View File

@ -19,14 +19,16 @@ DOMPurify.setConfig({
'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'
'strong', 'del', 'ins', 'mark', 'small', 'sub', 'sup', 'q', 'abbr',
'font' // Allow legacy font tag often found in emails
],
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-*'
'data-*',
'start', 'type', 'value', 'cite', 'datetime', 'wrap', 'summary'
],
KEEP_CONTENT: true,
WHOLE_DOCUMENT: false,
@ -34,7 +36,8 @@ DOMPurify.setConfig({
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
FORCE_BODY: false,
USE_PROFILES: { html: true } // Use HTML profile for more permissive sanitization for emails
});
/**
@ -47,7 +50,9 @@ export function sanitizeHtml(html: string): string {
try {
// Use DOMPurify with our central configuration
const clean = DOMPurify.sanitize(html);
const clean = DOMPurify.sanitize(html, {
ADD_ATTR: ['style', 'class', 'id', 'align', 'valign', 'colspan', 'rowspan', 'cellspacing', 'cellpadding', 'bgcolor']
});
// Fix common email rendering issues
const fixedHtml = clean

View File

@ -204,10 +204,24 @@ export function processHtmlContent(htmlContent: string, textContent?: string): s
if (!htmlContent) return '';
try {
console.log('processHtmlContent input:', {
length: htmlContent.length,
startsWithHtml: htmlContent.trim().startsWith('<html'),
startsWithDiv: htmlContent.trim().startsWith('<div'),
hasBody: htmlContent.includes('<body'),
containsForwardedMessage: htmlContent.includes('---------- Forwarded message ----------'),
containsQuoteHeader: htmlContent.includes('wrote:'),
hasBlockquote: htmlContent.includes('<blockquote'),
hasTable: htmlContent.includes('<table')
});
// Check for browser environment (DOMParser is browser-only)
const hasHtmlTag = htmlContent.includes('<html');
const hasBodyTag = htmlContent.includes('<body');
// Preserve original HTML for debugging
let originalHtml = htmlContent;
// Extract body content if we have a complete HTML document and in browser environment
if (hasHtmlTag && hasBodyTag && typeof window !== 'undefined' && typeof DOMParser !== 'undefined') {
try {
@ -217,6 +231,7 @@ export function processHtmlContent(htmlContent: string, textContent?: string): s
const bodyContent = doc.body.innerHTML;
if (bodyContent) {
console.log('Extracted body content from HTML document, length:', bodyContent.length);
htmlContent = bodyContent;
}
} catch (error) {
@ -227,6 +242,16 @@ export function processHtmlContent(htmlContent: string, textContent?: string): s
// Use the centralized sanitizeHtml function
let sanitizedContent = sanitizeHtml(htmlContent);
console.log('After sanitizeHtml:', {
originalLength: originalHtml.length,
sanitizedLength: sanitizedContent.length,
difference: originalHtml.length - sanitizedContent.length,
percentRemoved: ((originalHtml.length - sanitizedContent.length) / originalHtml.length * 100).toFixed(2) + '%',
containsForwardedMessage: sanitizedContent.includes('---------- Forwarded message ----------'),
hasTable: sanitizedContent.includes('<table'),
hasBlockquote: sanitizedContent.includes('<blockquote')
});
// Fix URL encoding issues and clean up content
try {
if (typeof window !== 'undefined' && typeof document !== 'undefined') {

View File

@ -456,40 +456,53 @@ export function formatForwardedEmail(originalEmail: EmailMessage | LegacyEmailMe
// Create the forwarded email HTML content
if (htmlContent) {
console.log('Formatting HTML forward, original content length:', htmlContent.length);
htmlContent = `
// Important: First sanitize the content portion only
const sanitizedOriginalContent = sanitizeHtml(htmlContent);
console.log('Sanitized original content length:', sanitizedOriginalContent.length);
// Create the complete forwarded email with header info
const fullForwardedEmail = `
<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>
` : ''}
<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>
</table>
</div>
<div style="padding: 10px 0; border-top: 1px solid #ddd;">
${sanitizeHtml(htmlContent)}
${sanitizedOriginalContent}
</div>
`;
// 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 ----------'));
}
// Format the plain text version