[MOV] mail: move method to process attachments
Purpose is to make a section that will cover pre- and post- process methods for posting or mailing records. Task-2710804 (Mail: Clean MailThread API) Part-of: odoo/odoo#106658
This commit is contained in:
+115
-115
@@ -1777,121 +1777,6 @@ class MailThread(models.AbstractModel):
|
||||
# MESSAGE POST MAIN
|
||||
# ------------------------------------------------------------
|
||||
|
||||
def _message_post_process_attachments(self, attachments, attachment_ids, message_values):
|
||||
""" Preprocess attachments for mail_thread.message_post() or mail_mail.create().
|
||||
Purpose is to
|
||||
|
||||
* transfer attachments given by ``attachment_ids`` from the composer to
|
||||
the record (if any);
|
||||
* limit attachments manipulation when being a shared user;
|
||||
* create attachments from ``attachments``. If those are linked to the
|
||||
content (body) through CIDs body is updated accordingly;
|
||||
|
||||
:param list(tuple(str,str), tuple(str,str, dict)) attachments : list of attachment
|
||||
tuples in the form ``(name,content)`` or ``(name,content, info)`` where content
|
||||
is NOT base64 encoded;
|
||||
:param list attachment_ids: list of existing attachments to link to this message;
|
||||
:param message_values: dictionary of values that will be used to create the
|
||||
message. It is used to find back record- or content- context;
|
||||
|
||||
:return dict: new values for message: 'attachment_ids' and optionally 'body'
|
||||
"""
|
||||
return_values = {}
|
||||
body = message_values.get('body')
|
||||
model = message_values['model']
|
||||
res_id = message_values['res_id']
|
||||
|
||||
m2m_attachment_ids = []
|
||||
if attachment_ids:
|
||||
# taking advantage of cache looks better in this case, to check
|
||||
filtered_attachment_ids = self.env['ir.attachment'].sudo().browse(attachment_ids).filtered(
|
||||
lambda a: a.res_model == 'mail.compose.message' and a.create_uid.id == self._uid)
|
||||
# update filtered (pending) attachments to link them to the proper record
|
||||
if filtered_attachment_ids:
|
||||
filtered_attachment_ids.write({'res_model': model, 'res_id': res_id})
|
||||
# prevent public and portal users from using attachments that are not theirs
|
||||
if not self.env.user._is_internal():
|
||||
attachment_ids = filtered_attachment_ids.ids
|
||||
|
||||
m2m_attachment_ids += [Command.link(id) for id in attachment_ids]
|
||||
# Handle attachments parameter, that is a dictionary of attachments
|
||||
|
||||
if attachments: # generate
|
||||
cids_in_body = set()
|
||||
names_in_body = set()
|
||||
cid_list = []
|
||||
name_list = []
|
||||
|
||||
if body:
|
||||
root = lxml.html.fromstring(tools.ustr(body))
|
||||
# first list all attachments that will be needed in body
|
||||
for node in root.iter('img'):
|
||||
if node.get('src', '').startswith('cid:'):
|
||||
cids_in_body.add(node.get('src').split('cid:')[1])
|
||||
elif node.get('data-filename'):
|
||||
names_in_body.add(node.get('data-filename'))
|
||||
attachement_values_list = []
|
||||
|
||||
# generate values
|
||||
for attachment in attachments:
|
||||
cid = False
|
||||
if len(attachment) == 2:
|
||||
name, content = attachment
|
||||
elif len(attachment) == 3:
|
||||
name, content, info = attachment
|
||||
cid = info and info.get('cid')
|
||||
else:
|
||||
continue
|
||||
if isinstance(content, str):
|
||||
content = content.encode('utf-8')
|
||||
elif isinstance(content, EmailMessage):
|
||||
content = content.as_bytes()
|
||||
elif content is None:
|
||||
continue
|
||||
attachement_values = {
|
||||
'name': name,
|
||||
'datas': base64.b64encode(content),
|
||||
'type': 'binary',
|
||||
'description': name,
|
||||
'res_model': model,
|
||||
'res_id': res_id,
|
||||
}
|
||||
if body and (cid and cid in cids_in_body or name in names_in_body):
|
||||
attachement_values['access_token'] = self.env['ir.attachment']._generate_access_token()
|
||||
attachement_values_list.append(attachement_values)
|
||||
# keep cid and name list synced with attachement_values_list length to match ids latter
|
||||
cid_list.append(cid)
|
||||
name_list.append(name)
|
||||
new_attachments = self.env['ir.attachment'].create(attachement_values_list)
|
||||
cid_mapping = {}
|
||||
name_mapping = {}
|
||||
for counter, new_attachment in enumerate(new_attachments):
|
||||
cid = cid_list[counter]
|
||||
if 'access_token' in attachement_values_list[counter]:
|
||||
if cid:
|
||||
cid_mapping[cid] = (new_attachment.id, attachement_values_list[counter]['access_token'])
|
||||
name = name_list[counter]
|
||||
name_mapping[name] = (new_attachment.id, attachement_values_list[counter]['access_token'])
|
||||
m2m_attachment_ids.append((4, new_attachment.id))
|
||||
|
||||
# note: right know we are only taking attachments and ignoring attachment_ids.
|
||||
if (cid_mapping or name_mapping) and body:
|
||||
postprocessed = False
|
||||
for node in root.iter('img'):
|
||||
attachment_data = False
|
||||
if node.get('src', '').startswith('cid:'):
|
||||
cid = node.get('src').split('cid:')[1]
|
||||
attachment_data = cid_mapping.get(cid)
|
||||
if not attachment_data and node.get('data-filename'):
|
||||
attachment_data = name_mapping.get(node.get('data-filename'), False)
|
||||
if attachment_data:
|
||||
node.set('src', '/web/image/%s?access_token=%s' % attachment_data)
|
||||
postprocessed = True
|
||||
if postprocessed:
|
||||
return_values['body'] = lxml.html.tostring(root, pretty_print=False, encoding='UTF-8')
|
||||
return_values['attachment_ids'] = m2m_attachment_ids
|
||||
return return_values
|
||||
|
||||
@api.returns('mail.message', lambda value: value.id)
|
||||
def message_post(self, *,
|
||||
body='', subject=None, message_type='notification',
|
||||
@@ -2021,6 +1906,121 @@ class MailThread(models.AbstractModel):
|
||||
message and computed value are given, to try to lessen query count by
|
||||
using already-computed values instead of having to rebrowse things. """
|
||||
|
||||
def _message_post_process_attachments(self, attachments, attachment_ids, message_values):
|
||||
""" Preprocess attachments for mail_thread.message_post() or mail_mail.create().
|
||||
Purpose is to
|
||||
|
||||
* transfer attachments given by ``attachment_ids`` from the composer to
|
||||
the record (if any);
|
||||
* limit attachments manipulation when being a shared user;
|
||||
* create attachments from ``attachments``. If those are linked to the
|
||||
content (body) through CIDs body is updated accordingly;
|
||||
|
||||
:param list(tuple(str,str), tuple(str,str, dict)) attachments : list of attachment
|
||||
tuples in the form ``(name,content)`` or ``(name,content, info)`` where content
|
||||
is NOT base64 encoded;
|
||||
:param list attachment_ids: list of existing attachments to link to this message;
|
||||
:param message_values: dictionary of values that will be used to create the
|
||||
message. It is used to find back record- or content- context;
|
||||
|
||||
:return dict: new values for message: 'attachment_ids' and optionally 'body'
|
||||
"""
|
||||
return_values = {}
|
||||
body = message_values.get('body')
|
||||
model = message_values['model']
|
||||
res_id = message_values['res_id']
|
||||
|
||||
m2m_attachment_ids = []
|
||||
if attachment_ids:
|
||||
# taking advantage of cache looks better in this case, to check
|
||||
filtered_attachment_ids = self.env['ir.attachment'].sudo().browse(attachment_ids).filtered(
|
||||
lambda a: a.res_model == 'mail.compose.message' and a.create_uid.id == self._uid)
|
||||
# update filtered (pending) attachments to link them to the proper record
|
||||
if filtered_attachment_ids:
|
||||
filtered_attachment_ids.write({'res_model': model, 'res_id': res_id})
|
||||
# prevent public and portal users from using attachments that are not theirs
|
||||
if not self.env.user._is_internal():
|
||||
attachment_ids = filtered_attachment_ids.ids
|
||||
|
||||
m2m_attachment_ids += [Command.link(id) for id in attachment_ids]
|
||||
# Handle attachments parameter, that is a dictionary of attachments
|
||||
|
||||
if attachments: # generate
|
||||
cids_in_body = set()
|
||||
names_in_body = set()
|
||||
cid_list = []
|
||||
name_list = []
|
||||
|
||||
if body:
|
||||
root = lxml.html.fromstring(tools.ustr(body))
|
||||
# first list all attachments that will be needed in body
|
||||
for node in root.iter('img'):
|
||||
if node.get('src', '').startswith('cid:'):
|
||||
cids_in_body.add(node.get('src').split('cid:')[1])
|
||||
elif node.get('data-filename'):
|
||||
names_in_body.add(node.get('data-filename'))
|
||||
attachement_values_list = []
|
||||
|
||||
# generate values
|
||||
for attachment in attachments:
|
||||
cid = False
|
||||
if len(attachment) == 2:
|
||||
name, content = attachment
|
||||
elif len(attachment) == 3:
|
||||
name, content, info = attachment
|
||||
cid = info and info.get('cid')
|
||||
else:
|
||||
continue
|
||||
if isinstance(content, str):
|
||||
content = content.encode('utf-8')
|
||||
elif isinstance(content, EmailMessage):
|
||||
content = content.as_bytes()
|
||||
elif content is None:
|
||||
continue
|
||||
attachement_values = {
|
||||
'name': name,
|
||||
'datas': base64.b64encode(content),
|
||||
'type': 'binary',
|
||||
'description': name,
|
||||
'res_model': model,
|
||||
'res_id': res_id,
|
||||
}
|
||||
if body and (cid and cid in cids_in_body or name in names_in_body):
|
||||
attachement_values['access_token'] = self.env['ir.attachment']._generate_access_token()
|
||||
attachement_values_list.append(attachement_values)
|
||||
# keep cid and name list synced with attachement_values_list length to match ids latter
|
||||
cid_list.append(cid)
|
||||
name_list.append(name)
|
||||
new_attachments = self.env['ir.attachment'].create(attachement_values_list)
|
||||
cid_mapping = {}
|
||||
name_mapping = {}
|
||||
for counter, new_attachment in enumerate(new_attachments):
|
||||
cid = cid_list[counter]
|
||||
if 'access_token' in attachement_values_list[counter]:
|
||||
if cid:
|
||||
cid_mapping[cid] = (new_attachment.id, attachement_values_list[counter]['access_token'])
|
||||
name = name_list[counter]
|
||||
name_mapping[name] = (new_attachment.id, attachement_values_list[counter]['access_token'])
|
||||
m2m_attachment_ids.append((4, new_attachment.id))
|
||||
|
||||
# note: right know we are only taking attachments and ignoring attachment_ids.
|
||||
if (cid_mapping or name_mapping) and body:
|
||||
postprocessed = False
|
||||
for node in root.iter('img'):
|
||||
attachment_data = False
|
||||
if node.get('src', '').startswith('cid:'):
|
||||
cid = node.get('src').split('cid:')[1]
|
||||
attachment_data = cid_mapping.get(cid)
|
||||
if not attachment_data and node.get('data-filename'):
|
||||
attachment_data = name_mapping.get(node.get('data-filename'), False)
|
||||
if attachment_data:
|
||||
node.set('src', '/web/image/%s?access_token=%s' % attachment_data)
|
||||
postprocessed = True
|
||||
if postprocessed:
|
||||
return_values['body'] = lxml.html.tostring(root, pretty_print=False, encoding='UTF-8')
|
||||
return_values['attachment_ids'] = m2m_attachment_ids
|
||||
return return_values
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# MESSAGE POST API / WRAPPERS
|
||||
# ------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user