def _get_message_id(self, values): if values.get('no_auto_thread', False) is True: message_id = tools.generate_tracking_message_id('reply_to') elif values.get('res_id') and values.get('model'): message_id = tools.generate_tracking_message_id('%(res_id)s-%(model)s' % values) else: message_id = tools.generate_tracking_message_id('private') return message_id
def _get_message_id(self, values): if values.get('reply_to_force_new', False) is True: message_id = tools.generate_tracking_message_id('reply_to') elif self.is_thread_message(values): message_id = tools.generate_tracking_message_id('%(res_id)s-%(model)s' % values) else: message_id = tools.generate_tracking_message_id('private') return message_id
def _get_message_id(self, values): if values.get('no_auto_thread', False) is True: message_id = tools.generate_tracking_message_id('reply_to') elif values.get('res_id') and values.get('model'): message_id = tools.generate_tracking_message_id( '%(res_id)s-%(model)s' % values) else: message_id = tools.generate_tracking_message_id('private') # Replaced application host name with sender host name in message id mail_domain = values['email_from'].split('@')[1] if mail_domain: message_id = message_id.split('@')[0] + '@' + mail_domain return message_id
def setUpClass(cls): super(TestMessagePost, cls).setUpClass() cls._create_portal_user() cls.test_record = cls.env['mail.test.simple'].with_context( cls._test_context).create({ 'name': 'Test', 'email_from': '*****@*****.**' }) cls._reset_mail_context(cls.test_record) cls.test_message = cls.env['mail.message'].create({ 'author_id': cls.partner_employee.id, 'body': '<p>Notify Body <span>Woop Woop</span></p>', 'email_from': cls.partner_employee.email_formatted, 'is_internal': False, 'message_id': tools.generate_tracking_message_id('dummy-generate'), 'message_type': 'comment', 'model': cls.test_record._name, 'record_name': False, 'reply_to': '*****@*****.**', 'subtype_id': cls.env['ir.model.data']._xmlid_to_res_id('mail.mt_comment'), 'subject': 'Notify Test', }) cls.user_admin.write({'notification_type': 'email'})
def gateway_mail_bounce(self, mailing, record, bounce_base_values=None): """ Generate a bounce at mailgateway level. :param mailing: a ``mailing.mailing`` record on which we find a trace to bounce; :param record: record which should bounce; :param bounce_base_values: optional values given to routing; """ trace = mailing.mailing_trace_ids.filtered( lambda t: t.model == record._name and t.res_id == record.id) parsed_bounce_values = { 'email_from': '*****@*****.**', # TDE check: email_from -> trace email ? 'to': '*****@*****.**', # TDE check: bounce alias ? 'message_id': tools.generate_tracking_message_id('MailTest'), 'bounced_partner': self.env['res.partner'].sudo(), 'bounced_message': self.env['mail.message'].sudo() } if bounce_base_values: parsed_bounce_values.update(bounce_base_values) parsed_bounce_values.update({ 'bounced_email': trace.email, 'bounced_msg_id': [trace.message_id], }) self.env['mail.thread']._routing_handle_bounce(False, parsed_bounce_values)
def setUpClass(cls): super(TestMessagePostCommon, cls).setUpClass() # portal user, notably for ACLS / notifications cls.user_portal = cls._create_portal_user() cls.partner_portal = cls.user_portal.partner_id # another standard employee to test follow and notifications between two # users (and not admin / user) cls.user_employee_2 = mail_new_test_user( cls.env, login='******', groups='base.group_user', company_id=cls.company_admin.id, email='*****@*****.**', # check: use a formatted email name='Eglantine Employee2', notification_type='email', signature='--\nEglantine', ) cls.partner_employee_2 = cls.user_employee_2.partner_id cls.test_record = cls.env['mail.test.simple'].with_context(cls._test_context).create({ 'name': 'Test', 'email_from': '*****@*****.**' }) cls._reset_mail_context(cls.test_record) cls.test_message = cls.env['mail.message'].create({ 'author_id': cls.partner_employee.id, 'body': '<p>Notify Body <span>Woop Woop</span></p>', 'email_from': cls.partner_employee.email_formatted, 'is_internal': False, 'message_id': tools.generate_tracking_message_id('dummy-generate'), 'message_type': 'comment', 'model': cls.test_record._name, 'record_name': False, 'reply_to': '*****@*****.**', 'subtype_id': cls.env['ir.model.data']._xmlid_to_res_id('mail.mt_comment'), 'subject': 'Notify Test', }) cls.user_admin.write({'notification_type': 'email'})
def build_email(self, email_from, email_to, subject, body, email_cc=None, email_bcc=None, reply_to=False, attachments=None, message_id=None, references=None, object_id=False, subtype='plain', headers=None, body_alternative=None, subtype_alternative='plain'): """Constructs an RFC2822 email.message.Message object based on the keyword arguments passed, and returns it. :param string email_from: sender email address :param list email_to: list of recipient addresses (to be joined with commas) :param string subject: email subject (no pre-encoding/quoting necessary) :param string body: email body, of the type ``subtype`` (by default, plaintext). If html subtype is used, the message will be automatically converted to plaintext and wrapped in multipart/alternative, unless an explicit ``body_alternative`` version is passed. :param string body_alternative: optional alternative body, of the type specified in ``subtype_alternative`` :param string reply_to: optional value of Reply-To header :param string object_id: optional tracking identifier, to be included in the message-id for recognizing replies. Suggested format for object-id is "res_id-model", e.g. "12345-crm.lead". :param string subtype: optional mime subtype for the text body (usually 'plain' or 'html'), must match the format of the ``body`` parameter. Default is 'plain', making the content part of the mail "text/plain". :param string subtype_alternative: optional mime subtype of ``body_alternative`` (usually 'plain' or 'html'). Default is 'plain'. :param list attachments: list of (filename, filecontents) pairs, where filecontents is a string containing the bytes of the attachment :param list email_cc: optional list of string values for CC header (to be joined with commas) :param list email_bcc: optional list of string values for BCC header (to be joined with commas) :param dict headers: optional map of headers to set on the outgoing mail (may override the other headers, including Subject, Reply-To, Message-Id, etc.) :rtype: email.message.Message (usually MIMEMultipart) :return: the new RFC2822 email message """ email_from = email_from or tools.config.get('email_from') assert email_from, "You must either provide a sender address explicitly or configure "\ "a global sender address in the server configuration or with the "\ "--email-from startup parameter." # Note: we must force all strings to to 8-bit utf-8 when crafting message, # or use encode_header() for headers, which does it automatically. headers = headers or {} # need valid dict later email_cc = email_cc or [] email_bcc = email_bcc or [] body = body or u'' email_body = ustr(body) email_text_part = MIMEText(email_body, _subtype=subtype, _charset='utf-8') msg = MIMEMultipart() if not message_id: if object_id: message_id = tools.generate_tracking_message_id(object_id) else: message_id = make_msgid() msg['Message-Id'] = encode_header(message_id) if references: msg['references'] = encode_header(references) msg['Subject'] = encode_header(subject) msg['From'] = encode_rfc2822_address_header(email_from) del msg['Reply-To'] if reply_to: msg['Reply-To'] = encode_rfc2822_address_header(reply_to) else: msg['Reply-To'] = msg['From'] msg['To'] = encode_rfc2822_address_header(COMMASPACE.join(email_to)) if email_cc: msg['Cc'] = encode_rfc2822_address_header( COMMASPACE.join(email_cc)) if email_bcc: msg['Bcc'] = encode_rfc2822_address_header( COMMASPACE.join(email_bcc)) msg['Date'] = formatdate() # Custom headers may override normal headers or provide additional ones for key, value in headers.items(): msg[pycompat.to_native(ustr(key))] = encode_header(value) if subtype == 'html' and not body_alternative: # Always provide alternative text body ourselves if possible. text = html2text.html2text(email_body) alternative_part = MIMEMultipart(_subtype="alternative") alternative_part.attach( MIMEText(text, _charset='utf-8', _subtype='plain')) alternative_part.attach(email_text_part) msg.attach(alternative_part) elif body_alternative: # Include both alternatives, as specified, within a multipart/alternative part alternative_part = MIMEMultipart(_subtype="alternative") body_alternative_ = ustr(body_alternative) alternative_body_part = MIMEText(body_alternative_, _subtype=subtype_alternative, _charset='utf-8') alternative_part.attach(alternative_body_part) alternative_part.attach(email_text_part) msg.attach(alternative_part) else: msg.attach(email_text_part) if attachments: for (fname, fcontent, mime) in attachments: filename_rfc2047 = encode_header_param(fname) if mime and '/' in mime: maintype, subtype = mime.split('/', 1) part = MIMEBase(maintype, subtype) else: part = MIMEBase('application', "octet-stream") # The default RFC2231 encoding of Message.add_header() works in Thunderbird but not GMail # so we fix it by using RFC2047 encoding for the filename instead. part.set_param('name', filename_rfc2047) part.add_header('Content-Disposition', 'attachment', filename=filename_rfc2047) part.set_payload(fcontent) encoders.encode_base64(part) msg.attach(part) return msg
def build_email(self, email_from, email_to, subject, body, email_cc=None, email_bcc=None, reply_to=False, attachments=None, message_id=None, references=None, object_id=False, subtype='plain', headers=None, body_alternative=None, subtype_alternative='plain'): """ copy-pasted from odoo/addons/base/ir/ir_mail_server.py::build_email """ ftemplate = '__image-%s__' fcounter = 0 attachments = attachments or [] pattern = re.compile(r'"data:image/png;base64,[^"]*"') pos = 0 new_body = '' body = body or '' while True: match = pattern.search(body, pos) if not match: break s = match.start() e = match.end() data = body[s + len('"data:image/png;base64,'):e - 1] new_body += body[pos:s] fname = ftemplate % fcounter fcounter += 1 attachments.append((fname, base64.b64decode(data))) new_body += '"cid:%s"' % fname pos = e new_body += body[pos:] body = new_body email_from = email_from or tools.config.get('email_from') assert email_from, "You must either provide a sender address explicitly or configure "\ "a global sender address in the server configuration or with the "\ "--email-from startup parameter." # Note: we must force all strings to to 8-bit utf-8 when crafting message, # or use encode_header() for headers, which does it automatically. headers = headers or {} # need valid dict later if not email_cc: email_cc = [] if not email_bcc: email_bcc = [] if not body: body = u'' email_body_utf8 = ustr(body).encode('utf-8') email_text_part = MIMEText(email_body_utf8, _subtype=subtype, _charset='utf-8') msg = MIMEMultipart() if not message_id: if object_id: message_id = tools.generate_tracking_message_id(object_id) else: message_id = make_msgid() msg['Message-Id'] = encode_header(message_id) if references: msg['references'] = encode_header(references) msg['Subject'] = encode_header(subject) msg['From'] = encode_rfc2822_address_header(email_from) del msg['Reply-To'] if reply_to: msg['Reply-To'] = encode_rfc2822_address_header(reply_to) else: msg['Reply-To'] = msg['From'] msg['To'] = encode_rfc2822_address_header(COMMASPACE.join(email_to)) if email_cc: msg['Cc'] = encode_rfc2822_address_header(COMMASPACE.join(email_cc)) if email_bcc: msg['Bcc'] = encode_rfc2822_address_header(COMMASPACE.join(email_bcc)) msg['Date'] = formatdate() # Custom headers may override normal headers or provide additional ones for key, value in headers.iteritems(): msg[ustr(key).encode('utf-8')] = encode_header(value) if subtype == 'html' and not body_alternative and html2text: # Always provide alternative text body ourselves if possible. text_utf8 = tools.html2text(email_body_utf8.decode('utf-8')).encode('utf-8') alternative_part = MIMEMultipart(_subtype="alternative") alternative_part.attach(MIMEText(text_utf8, _charset='utf-8', _subtype='plain')) alternative_part.attach(email_text_part) msg.attach(alternative_part) elif body_alternative: # Include both alternatives, as specified, within a multipart/alternative part alternative_part = MIMEMultipart(_subtype="alternative") body_alternative_utf8 = ustr(body_alternative).encode('utf-8') alternative_body_part = MIMEText(body_alternative_utf8, _subtype=subtype_alternative, _charset='utf-8') alternative_part.attach(alternative_body_part) alternative_part.attach(email_text_part) msg.attach(alternative_part) else: msg.attach(email_text_part) if attachments: for (fname, fcontent) in attachments: filename_rfc2047 = encode_header_param(fname) part = MIMEBase('application', "octet-stream") # The default RFC2231 encoding of Message.add_header() works in Thunderbird but not GMail # so we fix it by using RFC2047 encoding for the filename instead. part.set_param('name', filename_rfc2047) part.add_header('Content-Disposition', 'attachment', filename=filename_rfc2047) part.add_header('Content-ID', '<%s>' % filename_rfc2047) # NEW STUFF part.set_payload(fcontent) Encoders.encode_base64(part) msg.attach(part) return msg
def build_email(self, email_from, email_to, subject, body, email_cc=None, email_bcc=None, reply_to=False, attachments=None, message_id=None, references=None, object_id=False, subtype='plain', headers=None, body_alternative=None, subtype_alternative='plain'): """Constructs an RFC2822 email.message.Message object based on the keyword arguments passed, and returns it. :param string email_from: sender email address :param list email_to: list of recipient addresses (to be joined with commas) :param string subject: email subject (no pre-encoding/quoting necessary) :param string body: email body, of the type ``subtype`` (by default, plaintext). If html subtype is used, the message will be automatically converted to plaintext and wrapped in multipart/alternative, unless an explicit ``body_alternative`` version is passed. :param string body_alternative: optional alternative body, of the type specified in ``subtype_alternative`` :param string reply_to: optional value of Reply-To header :param string object_id: optional tracking identifier, to be included in the message-id for recognizing replies. Suggested format for object-id is "res_id-model", e.g. "12345-crm.lead". :param string subtype: optional mime subtype for the text body (usually 'plain' or 'html'), must match the format of the ``body`` parameter. Default is 'plain', making the content part of the mail "text/plain". :param string subtype_alternative: optional mime subtype of ``body_alternative`` (usually 'plain' or 'html'). Default is 'plain'. :param list attachments: list of (filename, filecontents) pairs, where filecontents is a string containing the bytes of the attachment :param list email_cc: optional list of string values for CC header (to be joined with commas) :param list email_bcc: optional list of string values for BCC header (to be joined with commas) :param dict headers: optional map of headers to set on the outgoing mail (may override the other headers, including Subject, Reply-To, Message-Id, etc.) :rtype: email.message.EmailMessage :return: the new RFC2822 email message """ email_from = email_from or self._get_default_from_address() assert email_from, "You must either provide a sender address explicitly or configure "\ "using the combination of `mail.catchall.domain` and `mail.default.from` "\ "ICPs, in the server configuration file or with the "\ "--email-from startup parameter." headers = headers or {} # need valid dict later email_cc = email_cc or [] email_bcc = email_bcc or [] body = body or u'' msg = EmailMessage(policy=email.policy.SMTP) msg.set_charset('utf-8') if not message_id: if object_id: message_id = tools.generate_tracking_message_id(object_id) else: message_id = make_msgid() msg['Message-Id'] = message_id if references: msg['references'] = references msg['Subject'] = subject email_from, return_path = self._get_email_from(email_from) msg['From'] = email_from if return_path: headers.setdefault('Return-Path', return_path) del msg['Reply-To'] msg['Reply-To'] = reply_to or email_from msg['To'] = email_to if email_cc: msg['Cc'] = email_cc if email_bcc: msg['Bcc'] = email_bcc msg['Date'] = datetime.datetime.utcnow() for key, value in headers.items(): msg[pycompat.to_text(ustr(key))] = value email_body = ustr(body) if subtype == 'html' and not body_alternative: msg.add_alternative(html2text.html2text(email_body), subtype='plain', charset='utf-8') msg.add_alternative(email_body, subtype=subtype, charset='utf-8') elif body_alternative: msg.add_alternative(ustr(body_alternative), subtype=subtype_alternative, charset='utf-8') msg.add_alternative(email_body, subtype=subtype, charset='utf-8') else: msg.set_content(email_body, subtype=subtype, charset='utf-8') if attachments: for (fname, fcontent, mime) in attachments: maintype, subtype = mime.split( '/') if mime and '/' in mime else ('application', 'octet-stream') msg.add_attachment(fcontent, maintype, subtype, filename=fname) return msg
def message_post(self, body='', subject=None, email_from=None, author_id=None, **kwargs): """ Custom posting process. This model does not inherit from ``mail.thread`` but uses the mail gateway so few methods should be defined. This custom posting process works as follow * create a ``mail.message`` based on incoming email; * create linked ``mail.group.message`` that encapsulates message in a format used in mail groups; * apply moderation rules; :return message: newly-created mail.message """ self.ensure_one() # First create the <mail.message> Mailthread = self.env['mail.thread'] values = dict((key, val) for key, val in kwargs.items() if key in self.env['mail.message']._fields) author_id, email_from = Mailthread._message_compute_author(author_id, email_from, raise_exception=True) values.update({ 'author_id': author_id, 'body': self._clean_email_body(body), 'email_from': email_from, 'model': self._name, 'partner_ids': [], 'res_id': self.id, 'subject': subject, }) # Force the "reply-to" to make the mail group flow work values['reply_to'] = self.env['mail.message']._get_reply_to(values) # ensure message ID so that replies go to the right thread if not values.get('message_id'): values['message_id'] = generate_tracking_message_id('%s-mail.group' % self.id) attachments = kwargs.get('attachments') or [] attachment_ids = kwargs.get('attachment_ids') or [] attachement_values = Mailthread._message_post_process_attachments(attachments, attachment_ids, values) values.update(attachement_values) mail_message = Mailthread._message_create(values) # Find the <mail.group.message> parent group_message_parent_id = False if mail_message.parent_id: group_message_parent = self.env['mail.group.message'].search( [('mail_message_id', '=', mail_message.parent_id.id)]) group_message_parent_id = group_message_parent.id if group_message_parent else False moderation_status = 'pending_moderation' if self.moderation else 'accepted' # Create the group message associated group_message = self.env['mail.group.message'].create({ 'mail_group_id': self.id, 'mail_message_id': mail_message.id, 'moderation_status': moderation_status, 'group_message_parent_id': group_message_parent_id, }) # Check the moderation rule to determine if we should accept or reject the email email_normalized = email_normalize(email_from) moderation_rule = self.env['mail.group.moderation'].search([ ('mail_group_id', '=', self.id), ('email', '=', email_normalized), ], limit=1) if not self.moderation: self._notify_members(group_message) elif moderation_rule and moderation_rule.status == 'allow': group_message.action_moderate_accept() elif moderation_rule and moderation_rule.status == 'ban': group_message.action_moderate_reject() elif self.moderation_notify: self.env['mail.mail'].sudo().create({ 'author_id': self.env.user.partner_id.id, 'auto_delete': True, 'body_html': group_message.mail_group_id.moderation_notify_msg, 'email_from': self.env.user.company_id.catchall_formatted or self.env.user.company_id.email_formatted, 'email_to': email_from, 'subject': 'Re: %s' % (subject or ''), 'state': 'outgoing' }) return mail_message
def build_email(self, email_from, email_to, subject, body, email_cc=None, email_bcc=None, reply_to=False, attachments=None, message_id=None, references=None, object_id=False, subtype='plain', headers=None, body_alternative=None, subtype_alternative='plain'): """Constructs an RFC2822 email.message.Message object based on the keyword arguments passed, and returns it. :param string email_from: sender email address :param list email_to: list of recipient addresses (to be joined with commas) :param string subject: email subject (no pre-encoding/quoting necessary) :param string body: email body, of the type ``subtype`` (by default, plaintext). If html subtype is used, the message will be automatically converted to plaintext and wrapped in multipart/alternative, unless an explicit ``body_alternative`` version is passed. :param string body_alternative: optional alternative body, of the type specified in ``subtype_alternative`` :param string reply_to: optional value of Reply-To header :param string object_id: optional tracking identifier, to be included in the message-id for recognizing replies. Suggested format for object-id is "res_id-model", e.g. "12345-crm.lead". :param string subtype: optional mime subtype for the text body (usually 'plain' or 'html'), must match the format of the ``body`` parameter. Default is 'plain', making the content part of the mail "text/plain". :param string subtype_alternative: optional mime subtype of ``body_alternative`` (usually 'plain' or 'html'). Default is 'plain'. :param list attachments: list of (filename, filecontents) pairs, where filecontents is a string containing the bytes of the attachment :param list email_cc: optional list of string values for CC header (to be joined with commas) :param list email_bcc: optional list of string values for BCC header (to be joined with commas) :param dict headers: optional map of headers to set on the outgoing mail (may override the other headers, including Subject, Reply-To, Message-Id, etc.) :rtype: email.message.Message (usually MIMEMultipart) :return: the new RFC2822 email message """ email_from = email_from or tools.config.get('email_from') assert email_from, "You must either provide a sender address explicitly or configure "\ "a global sender address in the server configuration or with the "\ "--email-from startup parameter." # Note: we must force all strings to to 8-bit utf-8 when crafting message, # or use encode_header() for headers, which does it automatically. headers = headers or {} # need valid dict later email_cc = email_cc or [] email_bcc = email_bcc or [] body = body or u'' email_body_utf8 = ustr(body).encode('utf-8') email_text_part = MIMEText(email_body_utf8, _subtype=subtype, _charset='utf-8') msg = MIMEMultipart() if not message_id: if object_id: message_id = tools.generate_tracking_message_id(object_id) else: message_id = make_msgid() msg['Message-Id'] = encode_header(message_id) if references: msg['references'] = encode_header(references) msg['Subject'] = encode_header(subject) msg['From'] = encode_rfc2822_address_header(email_from) del msg['Reply-To'] if reply_to: msg['Reply-To'] = encode_rfc2822_address_header(reply_to) else: msg['Reply-To'] = msg['From'] msg['To'] = encode_rfc2822_address_header(COMMASPACE.join(email_to)) if email_cc: msg['Cc'] = encode_rfc2822_address_header(COMMASPACE.join(email_cc)) if email_bcc: msg['Bcc'] = encode_rfc2822_address_header(COMMASPACE.join(email_bcc)) msg['Date'] = formatdate() # Custom headers may override normal headers or provide additional ones for key, value in headers.iteritems(): msg[ustr(key).encode('utf-8')] = encode_header(value) if subtype == 'html' and not body_alternative and html2text: # Always provide alternative text body ourselves if possible. text_utf8 = tools.html2text(email_body_utf8.decode('utf-8')).encode('utf-8') alternative_part = MIMEMultipart(_subtype="alternative") alternative_part.attach(MIMEText(text_utf8, _charset='utf-8', _subtype='plain')) alternative_part.attach(email_text_part) msg.attach(alternative_part) elif body_alternative: # Include both alternatives, as specified, within a multipart/alternative part alternative_part = MIMEMultipart(_subtype="alternative") body_alternative_utf8 = ustr(body_alternative).encode('utf-8') alternative_body_part = MIMEText(body_alternative_utf8, _subtype=subtype_alternative, _charset='utf-8') alternative_part.attach(alternative_body_part) alternative_part.attach(email_text_part) msg.attach(alternative_part) else: msg.attach(email_text_part) if attachments: for (fname, fcontent) in attachments: filename_rfc2047 = encode_header_param(fname) part = MIMEBase('application', "octet-stream") # The default RFC2231 encoding of Message.add_header() works in Thunderbird but not GMail # so we fix it by using RFC2047 encoding for the filename instead. part.set_param('name', filename_rfc2047) part.add_header('Content-Disposition', 'attachment', filename=filename_rfc2047) part.set_payload(fcontent) Encoders.encode_base64(part) msg.attach(part) return msg
def _message_log_batch( self, bodies, author_id=None, email_from=None, subject=False, message_type="notification", # 企业微信字段 start msgtype=None, is_wecom_message=None, message_to_user=None, message_to_party=None, message_to_tag=None, media_id=None, body_html="", body_json="", body_markdown="", safe=None, enable_id_trans=False, enable_duplicate_check=False, duplicate_check_interval=1800, # 企业微信字段 end ): """ 快捷方式允许在一批文档上发布注释。 它实现了与_message_log相同的目的,该目的通过批量完成以加快快速注释日志的速度。 :param bodies: dict {record_id: body} """ author_id, email_from = self._message_compute_author( author_id, email_from, raise_exception=False) base_message_values = { "subject": subject, "author_id": author_id, "email_from": email_from, "message_type": message_type, "model": self._name, "subtype_id": self.env["ir.model.data"].xmlid_to_res_id("mail.mt_note"), "is_internal": True, "record_name": False, "reply_to": self.env["mail.thread"]._notify_get_reply_to(default=email_from, records=None)[False], "message_id": tools.generate_tracking_message_id( "message-notify"), # why? this is all but a notify "msgtype": msgtype, "is_wecom_message": is_wecom_message, "message_to_user": message_to_user, "message_to_party": message_to_party, "message_to_tag": message_to_tag, "media_id": media_id, "body_html": body_html, "body_json": body_json, "body_markdown": body_markdown, "safe": safe, "enable_id_trans": enable_id_trans, "enable_duplicate_check": enable_duplicate_check, "duplicate_check_interval": duplicate_check_interval, } values_list = [ dict(base_message_values, res_id=record.id, body=bodies.get(record.id, "")) for record in self ] return self.sudo()._message_create(values_list)
def _message_log( self, *, body="", author_id=None, email_from=None, subject=False, message_type="notification", # 企业微信字段 start msgtype=None, is_wecom_message=None, message_to_user=None, message_to_party=None, message_to_tag=None, media_id=None, body_html=None, body_json=None, body_markdown=None, safe=None, enable_id_trans=None, enable_duplicate_check=None, duplicate_check_interval=1800, # 企业微信字段 end **kwargs, ): """ 允许在文档上发布注释的快捷方式。 它不执行任何通知,并预先计算一些值以使短代码尽可能优化。 该方法是私有的,因为它不检查访问权限,并且以sudo的身份执行消息创建,以加快日志处理速度。 应该在已经授予访问权限的方法中调用此方法,以避免特权升级。 """ self.ensure_one() author_id, email_from = self._message_compute_author( author_id, email_from, raise_exception=False) message_values = { "subject": subject, "body": body, "author_id": author_id, "email_from": email_from, "message_type": message_type, "model": kwargs.get("model", self._name), "res_id": self.ids[0] if self.ids else False, "subtype_id": self.env["ir.model.data"].xmlid_to_res_id("mail.mt_note"), "is_internal": True, "record_name": False, "reply_to": self.env["mail.thread"]._notify_get_reply_to(default=email_from, records=None)[False], "message_id": tools.generate_tracking_message_id( "message-notify"), # 为什么? 这只是一个通知 "msgtype": msgtype, "is_wecom_message": is_wecom_message, "message_to_user": message_to_user, "message_to_party": message_to_party, "message_to_tag": message_to_tag, "media_id": media_id, "body_html": body_html, "body_json": body_json, "body_markdown": body_markdown, "safe": safe, "enable_id_trans": enable_id_trans, "enable_duplicate_check": enable_duplicate_check, "duplicate_check_interval": duplicate_check_interval, } message_values.update(kwargs) return self.sudo()._message_create(message_values)
def message_notify( self, *, partner_ids=False, parent_id=False, model=False, res_id=False, author_id=None, email_from=None, body="", subject=False, # 企业微信字段 start msgtype=None, is_wecom_message=None, message_to_user=None, message_to_party=None, message_to_tag=None, media_id=None, body_html=None, body_json=None, body_markdown=None, safe=None, enable_id_trans=None, enable_duplicate_check=None, duplicate_check_interval=1800, # 企业微信字段 end **kwargs, ): """ 允许通知合作伙伴有关不应在文档上显示的消息的快捷方式。 像其他通知一样,它会根据用户配置将通知推送到收件箱或通过电子邮件发送。 """ if self: self.ensure_one() # split message additional values from notify additional values msg_kwargs = dict((key, val) for key, val in kwargs.items() if key in self.env["mail.message"]._fields) notif_kwargs = dict( (key, val) for key, val in kwargs.items() if key not in msg_kwargs) author_id, email_from = self._message_compute_author( author_id, email_from, raise_exception=True) if not partner_ids: _logger.warning( "Message notify called without recipient_ids, skipping") return self.env["mail.message"] if not (model and res_id ): # both value should be set or none should be set (record) model = False res_id = False MailThread = self.env["mail.thread"] values = { "parent_id": parent_id, "model": self._name if self else False, "res_id": self.id if self else False, "message_type": "user_notification", "subject": subject, "body": body, "author_id": author_id, "email_from": email_from, "partner_ids": partner_ids, "subtype_id": self.env["ir.model.data"].xmlid_to_res_id("mail.mt_note"), "is_internal": True, "record_name": False, "reply_to": MailThread._notify_get_reply_to(default=email_from, records=None)[False], "message_id": tools.generate_tracking_message_id("message-notify"), "msgtype": msgtype, "is_wecom_message": is_wecom_message, "message_to_user": message_to_user, "message_to_party": message_to_party, "message_to_tag": message_to_tag, "media_id": media_id, "body_html": body_html, "body_json": body_json, "body_markdown": body_markdown, "safe": safe, "enable_id_trans": enable_id_trans, "enable_duplicate_check": enable_duplicate_check, "duplicate_check_interval": duplicate_check_interval, } values.update(msg_kwargs) new_message = MailThread._message_create(values) MailThread._notify_thread(new_message, values, **notif_kwargs) return new_message