a few more fix

This commit is contained in:
xtremxpert 2025-01-28 20:55:23 -05:00
parent 4911ef0b9a
commit 55a69d68b1
3 changed files with 121 additions and 43 deletions

View file

@ -1,21 +1,28 @@
"""This module extends ir.attachment model to handle MSG file attachments.
It provides functionality to convert MSG files (Outlook messages) to EML format
and process them as regular email messages in Odoo. This includes extracting
attachments, handling headers, and creating proper mail.message records.
"""
from odoo import models, api, fields
from odoo.tools import html_sanitize
import extract_msg
import base64
import logging
from odoo.tools.translate import _
from email.message import EmailMessage
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
from email.mime.base import MIMEBase
from email import encoders
import lxml.html
import re
from odoo.tools import email_normalize, email_split
from io import BytesIO
import extract_msg
_logger = logging.getLogger(__name__)
_msg_import_logger = logging.getLogger("msg.import")
handler = logging.FileHandler("/var/log/odoo/msg_import.log")
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
@ -23,7 +30,6 @@ handler.setFormatter(formatter)
_msg_import_logger.addHandler(handler)
_msg_import_logger.setLevel(logging.ERROR)
class IrAttachment(models.Model):
_inherit = "ir.attachment"
@ -40,7 +46,7 @@ class IrAttachment(models.Model):
self.ensure_one()
# Check if it's an MSG file
is_msg = self.mimetype == "application/vnd.ms-outlook" or (
is_msg = self.with_context().sudo().mimetype in ['application/vnd.ms-outlook', 'application/x-ole-storage'] or (
self.name and self.name.lower().endswith(".msg")
)
@ -51,15 +57,18 @@ class IrAttachment(models.Model):
max_size = int(
self.env["ir.config_parameter"]
.sudo()
.get_param("msg_attachments.max_file_size", "25")
.get_param("msg_attachments.max_file_size") or 25
)
file_size_mb = (
len(self.datas) * 3 / 4 / 1024 / 1024
) # Convert from base64 to MB
if file_size_mb > max_size:
log_errors = self.env['ir.config_parameter'].sudo().get_param('msg_attachments.log_msg_error', 'False').lower() == 'true'
log_file = self.env['ir.config_parameter'].sudo().get_param('msg_attachments.log_file_path')
error_msg = f"File size error for {self.name}: File size ({file_size_mb:.2f} MB) exceeds maximum allowed size ({max_size} MB)"
_msg_import_logger.error(error_msg)
if log_errors and log_file:
_msg_import_logger.error(error_msg)
self._notify_error(
"File Size Error",
_("The MSG file is too large. Maximum allowed size is %s MB")
@ -164,7 +173,7 @@ class IrAttachment(models.Model):
from_partner = None
elif len(from_partners) > 1:
if current_partner:
# Chercher parmi les contacts liés à la compagnie de l'objet courant
# Search among contacts linked to the current object's company
company = current_partner.commercial_partner_id
company_contacts = from_partners.filtered(
lambda p: p.commercial_partner_id == company
@ -252,6 +261,56 @@ class IrAttachment(models.Model):
if msg_file.body:
body = msg_file.body
# Check if metadata should be included
show_metadata = (
self.env["ir.config_parameter"]
.sudo()
.get_param("msg_attachments.format_metadata", "True")
.lower()
== "true"
)
if show_metadata:
# Format the metadata section
metadata_html = f"""
<div style="background-color: #f8f9fa; padding: 10px; margin-bottom: 15px; border: 1px solid #dee2e6; border-radius: 4px; font-family: Arial, sans-serif; font-size: 13px;">
<div style="font-weight: bold; color: #495057; margin-bottom: 8px;">Original Email Details:</div>
<table style="border-collapse: collapse; width: 100%;">
<tr>
<td style="padding: 2px 8px 2px 0; color: #495057; width: 100px;"><strong>From:</strong></td>
<td style="padding: 2px 0;">{html_sanitize(msg_file.sender or "")}</td>
</tr>
<tr>
<td style="padding: 2px 8px 2px 0; color: #495057;"><strong>Date:</strong></td>
<td style="padding: 2px 0;">{msg_file.date.strftime("%Y-%m-%d %H:%M:%S") if msg_file.date else "Unknown"}</td>
</tr>
<tr>
<td style="padding: 2px 8px 2px 0; color: #495057;"><strong>To:</strong></td>
<td style="padding: 2px 0;">{html_sanitize(msg_file.to or "")}</td>
</tr>
"""
if msg_file.cc:
metadata_html += f"""
<tr>
<td style="padding: 2px 8px 2px 0; color: #495057;"><strong>CC:</strong></td>
<td style="padding: 2px 0;">{html_sanitize(msg_file.cc)}</td>
</tr>
"""
if "message-id" in msg_file.header:
metadata_html += f"""
<tr>
<td style="padding: 2px 8px 2px 0; color: #495057;"><strong>Message-ID:</strong></td>
<td style="padding: 2px 0; word-break: break-all;">{html_sanitize(msg_file.header["message-id"])}</td>
</tr>
"""
metadata_html += """
</table>
</div>
"""
# Check if the body is HTML or contains HTML-like content
is_html = "<html" in body.lower() or any(
tag in body.lower() for tag in ["<div", "<p", "<br", "<table", "<a"]
@ -274,14 +333,15 @@ class IrAttachment(models.Model):
body,
)
# Wrap in HTML tags
body = f"<html><body>{body}</body></html>"
body = f'<div style="font-family: Arial, sans-serif; font-size: 13px;">{body}</div>'
# Always sanitize HTML to ensure proper formatting
if show_metadata:
# Add metadata at the top
body = metadata_html + body
# Always sanitize HTML
body = html_sanitize(
body,
sanitize_tags=False,
sanitize_attributes=False,
sanitize_style=False,
body, sanitize_tags=False, sanitize_attributes=False, sanitize_style=False
)
html_part = MIMEText(body, "html")
@ -371,8 +431,11 @@ class IrAttachment(models.Model):
)
if not self._is_msg_file():
log_errors = self.env['ir.config_parameter'].sudo().get_param('msg_attachments.log_msg_error', 'False').lower() == 'true'
log_file = self.env['ir.config_parameter'].sudo().get_param('msg_attachments.log_file_path')
error_msg = f"Invalid MSG file: {self.name} (model: {self.res_model}, res_id: {self.res_id})"
_msg_import_logger.error(error_msg)
if log_errors and log_file:
_msg_import_logger.error(error_msg)
self._notify_error(
"Invalid File", _("The selected file is not a valid MSG file")
)
@ -425,13 +488,18 @@ class IrAttachment(models.Model):
)
return thread_id
else:
log_errors = self.env['ir.config_parameter'].sudo().get_param('msg_attachments.log_msg_error', 'False').lower() == 'true'
log_file = self.env['ir.config_parameter'].sudo().get_param('msg_attachments.log_file_path')
error_msg = f"Failed to process MSG file: {self.name} - message_process failed to create thread_id"
_msg_import_logger.error(error_msg, exc_info=True)
return False
except Exception as e:
log_errors = self.env['ir.config_parameter'].sudo().get_param('msg_attachments.log_msg_error', 'False').lower() == 'true'
log_file = self.env['ir.config_parameter'].sudo().get_param('msg_attachments.log_file_path')
error_msg = f"Error processing MSG file: {self.name} - {str(e)}"
_msg_import_logger.error(error_msg, exc_info=True)
if log_errors and log_file:
_msg_import_logger.error(error_msg, exc_info=True)
self._notify_error(
"Processing Error", _("Could not process the MSG file: %s") % str(e)
)

View file

@ -30,3 +30,10 @@ class ResConfigSettings(models.TransientModel):
default='.pdf,.doc,.docx,.xls,.xlsx,.jpg,.png',
help='Comma-separated list of allowed file extensions'
)
log_msg_error = fields.Boolean(
string='Log MSG Import Errors',
config_parameter='msg_attachments.log_msg_error',
default=False
)

View file

@ -5,39 +5,42 @@
<field name="model">res.config.settings</field>
<field name="inherit_id" ref="base.res_config_settings_view_form"/>
<field name="arch" type="xml">
<xpath expr="//form" position="inside">
<app data-string="MSG Attachments" string="MSG Attachments" name="msg_attachments" id="msg_attachments">
<block title="MSG File Processing" name="msg_file_processing">
<setting string="Maximum File Size" help="Maximum size for MSG files in megabytes">
<div class="content-group">
<div class="mt16">
<field name="msg_max_file_size" class="o_light_label"/>
<span> MB</span>
</div>
<xpath expr="//block[@name='performance']" position="before">
<block title="MSG File Processing" name="msg_file_processing">
<setting string="Maximum File Size" help="Maximum size for MSG files in megabytes">
<div class="content-group">
<div class="mt16">
<field name="msg_max_file_size" class="o_light_label"/>
<span> MB</span>
</div>
</setting>
</div>
</setting>
<setting string="Show Email Metadata" help="Display sender and date information in processed messages">
<field name="msg_format_metadata"/>
</setting>
<setting string="Show Email Metadata" help="Display sender and date information in processed messages">
<field name="msg_format_metadata"/>
</setting>
<setting string="Attachment Processing" help="Configure how attachments should be processed">
<div class="content-group">
<div class="mt16">
<field name="msg_attachment_mode"/>
</div>
<setting string="Attachment Processing" help="Configure how attachments should be processed">
<div class="content-group">
<div class="mt16">
<field name="msg_attachment_mode"/>
</div>
</setting>
</div>
</setting>
<setting string="Allowed Extensions" help="List of allowed file extensions for attachments (comma-separated)">
<div class="content-group">
<div class="mt16">
<field name="msg_allowed_extensions" class="o_light_label"/>
</div>
<setting string="Allowed Extensions" help="List of allowed file extensions for attachments (comma-separated)" invisible="msg_attachment_mode != 'selective'">
<div class="content-group">
<div class="mt16">
<field name="msg_allowed_extensions" class="o_light_label"/>
</div>
</setting>
</block>
</app>
</div>
</setting>
<setting string="Error Logging" help="Enable logging of MSG import errors">
<field name="log_msg_error"/>
</setting>
</block>
</xpath>
</field>
</record>