courrier preview
This commit is contained in:
parent
2b02742bc4
commit
1f6032941a
@ -91,6 +91,9 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
|
|||||||
console.log('Setting initial content in editor', {
|
console.log('Setting initial content in editor', {
|
||||||
length: initialContent.length,
|
length: initialContent.length,
|
||||||
startsWithHtml: initialContent.trim().startsWith('<'),
|
startsWithHtml: initialContent.trim().startsWith('<'),
|
||||||
|
containsForwardedMessage: initialContent.includes('---------- Forwarded message ----------'),
|
||||||
|
containsReplyIndicator: initialContent.includes('wrote:'),
|
||||||
|
hasBlockquote: initialContent.includes('<blockquote')
|
||||||
});
|
});
|
||||||
|
|
||||||
// Detect text direction
|
// Detect text direction
|
||||||
@ -99,6 +102,18 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
|
|||||||
// Process HTML content using centralized utility
|
// Process HTML content using centralized utility
|
||||||
const sanitizedContent = processHtmlContent(initialContent);
|
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
|
// Check if sanitized content is valid
|
||||||
if (sanitizedContent.trim().length === 0) {
|
if (sanitizedContent.trim().length === 0) {
|
||||||
console.warn('Sanitized content is empty after processing, using fallback approach');
|
console.warn('Sanitized content is empty after processing, using fallback approach');
|
||||||
@ -213,7 +228,12 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
|
|||||||
try {
|
try {
|
||||||
console.log('Updating content in editor:', {
|
console.log('Updating content in editor:', {
|
||||||
contentLength: initialContent.length,
|
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
|
// Detect text direction
|
||||||
@ -222,6 +242,18 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
|
|||||||
// Process HTML content using centralized utility
|
// Process HTML content using centralized utility
|
||||||
const sanitizedContent = processHtmlContent(initialContent);
|
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
|
// Check if content is valid HTML
|
||||||
if (sanitizedContent.trim().length === 0) {
|
if (sanitizedContent.trim().length === 0) {
|
||||||
console.warn('Sanitized content is empty, using original content');
|
console.warn('Sanitized content is empty, using original content');
|
||||||
|
|||||||
@ -19,14 +19,16 @@ DOMPurify.setConfig({
|
|||||||
'div', 'span', 'img', 'br', 'hr', 'section', 'article', 'header', 'footer',
|
'div', 'span', 'img', 'br', 'hr', 'section', 'article', 'header', 'footer',
|
||||||
'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'blockquote', 'pre', 'code',
|
'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'blockquote', 'pre', 'code',
|
||||||
'ul', 'ol', 'li', 'dl', 'dt', 'dd', 'a', 'b', 'i', 'u', 'em',
|
'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: [
|
ADD_ATTR: [
|
||||||
'style', 'class', 'id', 'name', 'href', 'src', 'alt', 'title', 'width', 'height',
|
'style', 'class', 'id', 'name', 'href', 'src', 'alt', 'title', 'width', 'height',
|
||||||
'border', 'cellspacing', 'cellpadding', 'bgcolor', 'background', 'color',
|
'border', 'cellspacing', 'cellpadding', 'bgcolor', 'background', 'color',
|
||||||
'align', 'valign', 'dir', 'lang', 'target', 'rel', 'charset', 'media',
|
'align', 'valign', 'dir', 'lang', 'target', 'rel', 'charset', 'media',
|
||||||
'colspan', 'rowspan', 'scope', 'span', 'size', 'face', 'hspace', 'vspace',
|
'colspan', 'rowspan', 'scope', 'span', 'size', 'face', 'hspace', 'vspace',
|
||||||
'data-*'
|
'data-*',
|
||||||
|
'start', 'type', 'value', 'cite', 'datetime', 'wrap', 'summary'
|
||||||
],
|
],
|
||||||
KEEP_CONTENT: true,
|
KEEP_CONTENT: true,
|
||||||
WHOLE_DOCUMENT: false,
|
WHOLE_DOCUMENT: false,
|
||||||
@ -34,7 +36,8 @@ DOMPurify.setConfig({
|
|||||||
ALLOW_UNKNOWN_PROTOCOLS: true, // Needed for some email clients
|
ALLOW_UNKNOWN_PROTOCOLS: true, // Needed for some email clients
|
||||||
FORBID_TAGS: ['script', 'iframe', 'object', 'embed', 'form', 'input', 'button', 'select', 'textarea'],
|
FORBID_TAGS: ['script', 'iframe', 'object', 'embed', 'form', 'input', 'button', 'select', 'textarea'],
|
||||||
FORBID_ATTR: ['onerror', 'onload', 'onclick', 'onmouseover', 'onmouseout'],
|
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 {
|
try {
|
||||||
// Use DOMPurify with our central configuration
|
// 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
|
// Fix common email rendering issues
|
||||||
const fixedHtml = clean
|
const fixedHtml = clean
|
||||||
|
|||||||
@ -204,10 +204,24 @@ export function processHtmlContent(htmlContent: string, textContent?: string): s
|
|||||||
if (!htmlContent) return '';
|
if (!htmlContent) return '';
|
||||||
|
|
||||||
try {
|
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)
|
// Check for browser environment (DOMParser is browser-only)
|
||||||
const hasHtmlTag = htmlContent.includes('<html');
|
const hasHtmlTag = htmlContent.includes('<html');
|
||||||
const hasBodyTag = htmlContent.includes('<body');
|
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
|
// Extract body content if we have a complete HTML document and in browser environment
|
||||||
if (hasHtmlTag && hasBodyTag && typeof window !== 'undefined' && typeof DOMParser !== 'undefined') {
|
if (hasHtmlTag && hasBodyTag && typeof window !== 'undefined' && typeof DOMParser !== 'undefined') {
|
||||||
try {
|
try {
|
||||||
@ -217,6 +231,7 @@ export function processHtmlContent(htmlContent: string, textContent?: string): s
|
|||||||
const bodyContent = doc.body.innerHTML;
|
const bodyContent = doc.body.innerHTML;
|
||||||
|
|
||||||
if (bodyContent) {
|
if (bodyContent) {
|
||||||
|
console.log('Extracted body content from HTML document, length:', bodyContent.length);
|
||||||
htmlContent = bodyContent;
|
htmlContent = bodyContent;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -227,6 +242,16 @@ export function processHtmlContent(htmlContent: string, textContent?: string): s
|
|||||||
// Use the centralized sanitizeHtml function
|
// Use the centralized sanitizeHtml function
|
||||||
let sanitizedContent = sanitizeHtml(htmlContent);
|
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
|
// Fix URL encoding issues and clean up content
|
||||||
try {
|
try {
|
||||||
if (typeof window !== 'undefined' && typeof document !== 'undefined') {
|
if (typeof window !== 'undefined' && typeof document !== 'undefined') {
|
||||||
|
|||||||
@ -456,40 +456,53 @@ export function formatForwardedEmail(originalEmail: EmailMessage | LegacyEmailMe
|
|||||||
// Create the forwarded email HTML content
|
// Create the forwarded email HTML content
|
||||||
if (htmlContent) {
|
if (htmlContent) {
|
||||||
console.log('Formatting HTML forward, original content length:', htmlContent.length);
|
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;">
|
<div style="margin: 20px 0 10px 0; color: #666; font-family: Arial, sans-serif;">
|
||||||
---------- Forwarded message ----------<br>
|
---------- Forwarded message ----------<br>
|
||||||
<table style="margin: 10px 0 15px 0; border-collapse: collapse; font-size: 13px; color: #333;">
|
<table style="margin: 10px 0 15px 0; border-collapse: collapse; font-size: 13px; color: #333;">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td style="padding: 3px 10px 3px 0; font-weight: bold; text-align: right; vertical-align: top;">From:</td>
|
<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>
|
<td style="padding: 3px 0;">${fromStr}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td style="padding: 3px 10px 3px 0; font-weight: bold; text-align: right; vertical-align: top;">Date:</td>
|
<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>
|
<td style="padding: 3px 0;">${date}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td style="padding: 3px 10px 3px 0; font-weight: bold; text-align: right; vertical-align: top;">Subject:</td>
|
<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>
|
<td style="padding: 3px 0;">${email.subject || ''}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td style="padding: 3px 10px 3px 0; font-weight: bold; text-align: right; vertical-align: top;">To:</td>
|
<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>
|
<td style="padding: 3px 0;">${toStr}</td>
|
||||||
</tr>
|
</tr>
|
||||||
${ccStr ? `
|
${ccStr ? `
|
||||||
<tr>
|
<tr>
|
||||||
<td style="padding: 3px 10px 3px 0; font-weight: bold; text-align: right; vertical-align: top;">Cc:</td>
|
<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>
|
<td style="padding: 3px 0;">${ccStr}</td>
|
||||||
</tr>
|
</tr>
|
||||||
` : ''}
|
` : ''}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div style="padding: 10px 0; border-top: 1px solid #ddd;">
|
<div style="padding: 10px 0; border-top: 1px solid #ddd;">
|
||||||
${sanitizeHtml(htmlContent)}
|
${sanitizedOriginalContent}
|
||||||
</div>
|
</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
|
// Format the plain text version
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user