Ejemplo n.º 1
0
 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
Ejemplo n.º 2
0
 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
Ejemplo n.º 3
0
 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
Ejemplo n.º 4
0
    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
Ejemplo n.º 5
0
 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'})
Ejemplo n.º 6
0
    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)
Ejemplo n.º 7
0
    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'})
Ejemplo n.º 8
0
    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
Ejemplo n.º 9
0
    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
Ejemplo n.º 10
0
    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
Ejemplo n.º 11
0
    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
Ejemplo n.º 12
0
    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
Ejemplo n.º 13
0
    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)
Ejemplo n.º 14
0
    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)
Ejemplo n.º 15
0
    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