Exemple #1
0
    def generate_recipients(self, results, res_ids):
        """Generates the recipients of the template. Default values can ben generated
        instead of the template values if requested by template or context.
        Emails (email_to, email_cc) can be transformed into partners if requested
        in the context. """
        self.ensure_one()

        if self.use_default_to or self._context.get('tpl_force_default_to'):
            default_recipients = self.env[
                'mail.thread'].message_get_default_recipients(
                    res_model=self.model, res_ids=res_ids)
            for res_id, recipients in default_recipients.iteritems():
                results[res_id].pop('partner_to', None)
                results[res_id].update(recipients)

        for res_id, values in results.iteritems():
            partner_ids = values.get('partner_ids', list())
            if self._context.get('tpl_partners_only'):
                mails = tools.email_split(values.pop(
                    'email_to', '')) + tools.email_split(
                        values.pop('email_cc', ''))
                for mail in mails:
                    partner_id = self.env['res.partner'].find_or_create(mail)
                    partner_ids.append(partner_id)
            partner_to = values.pop('partner_to', '')
            if partner_to:
                # placeholders could generate '', 3, 2 due to some empty field values
                tpl_partner_ids = [
                    int(pid) for pid in partner_to.split(',') if pid
                ]
                partner_ids += self.env['res.partner'].sudo().browse(
                    tpl_partner_ids).exists().ids
            results[res_id]['partner_ids'] = partner_ids
        return results
Exemple #2
0
 def message_new(self, msg, custom_values=None):
     """ Overrides mail_thread message_new that is called by the mailgateway
         through message_process.
         This override updates the document according to the email.
     """
     if custom_values is None:
         custom_values = {}
     email = tools.email_split(msg.get('from')) and tools.email_split(msg.get('from'))[0] or False
     user = self.env['res.users'].search([('login', '=', email)], limit=1)
     if user:
         employee = self.env['hr.employee'].search([('user_id', '=', user.id)], limit=1)
         if employee:
             custom_values['employee_id'] = employee and employee[0].id
     return super(HrEquipmentRequest, self).message_new(msg, custom_values=custom_values)
Exemple #3
0
 def email_split(self, cr, uid, ids, msg, context=None):
     email_list = tools.email_split((msg.get('to') or '') + ',' +
                                    (msg.get('cc') or ''))
     # check left-part is not already an alias
     issue_ids = self.browse(cr, uid, ids, context=context)
     aliases = [
         issue.project_id.alias_name for issue in issue_ids
         if issue.project_id
     ]
     return filter(lambda x: x.split('@')[0] not in aliases, email_list)
Exemple #4
0
 def test_email_split(self):
     cases = [
         ("John <*****@*****.**>", ['*****@*****.**']),  # regular form
         ("d@x; 1@2", ['d@x', '1@2']),  # semi-colon + extra space
         ("'(ss)' <*****@*****.**>, 'foo' <foo@bar>", ['*****@*****.**', 'foo@bar']),  # comma + single-quoting
         ('"*****@*****.**"<*****@*****.**>', ['*****@*****.**']),  # double-quoting
         ('"<jg>" <*****@*****.**>', ['*****@*****.**']),  # double-quoting with brackets
     ]
     for text, expected in cases:
         self.assertEqual(email_split(text), expected, 'email_split is broken')
Exemple #5
0
 def _parse_partner_name(self, text, context=None):
     """ Supported syntax:
         - 'Raoul <*****@*****.**>': will find name and email address
         - otherwise: default, everything is set as the name """
     emails = tools.email_split(text.replace(' ',','))
     if emails:
         email = emails[0]
         name = text[:text.index(email)].replace('"', '').replace('<', '').strip()
     else:
         name, email = text, ''
     return name, email
Exemple #6
0
 def send_get_email_dict(self, cr, uid, ids, partner=None, context=None):
     # TDE: temporary addition (mail was parameter) due to semi-new-API
     res = super(MailMail, self).send_get_email_dict(cr, uid, ids, partner, context=context)
     mail = self.browse(cr, uid, ids[0], context=context)
     base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url')
     if mail.mailing_id and res.get('body') and res.get('email_to'):
         emails = tools.email_split(res.get('email_to')[0])
         email_to = emails and emails[0] or False
         unsubscribe_url= self._get_unsubscribe_url(cr, uid, mail, email_to, context=context)
         link_to_replace =  base_url+'/unsubscribe_from_list'
         if link_to_replace in res['body']:
             res['body'] = res['body'].replace(link_to_replace, unsubscribe_url if unsubscribe_url else '#')
     return res
Exemple #7
0
 def _parse_partner_name(self, text, context=None):
     """ Supported syntax:
         - 'Raoul <*****@*****.**>': will find name and email address
         - otherwise: default, everything is set as the name """
     emails = tools.email_split(text.replace(' ', ','))
     if emails:
         email = emails[0]
         name = text[:text.index(email)].replace('"',
                                                 '').replace('<',
                                                             '').strip()
     else:
         name, email = text, ''
     return name, email
Exemple #8
0
    def find_or_create(self, cr, uid, email, context=None):
        """ Find a partner with the given ``email`` or use :py:method:`~.name_create`
            to create one

            :param str email: email-like string, which should contain at least one email,
                e.g. ``"Raoul Grosbedon <*****@*****.**>"``"""
        assert email, 'an email is required for find_or_create to work'
        emails = tools.email_split(email)
        if emails:
            email = emails[0]
        ids = self.search(cr, uid, [('email','=ilike',email)], context=context)
        if not ids:
            return self.name_create(cr, uid, email, context=context)[0]
        return ids[0]
Exemple #9
0
    def find_or_create(self, cr, uid, email, context=None):
        """ Find a partner with the given ``email`` or use :py:method:`~.name_create`
            to create one

            :param str email: email-like string, which should contain at least one email,
                e.g. ``"Raoul Grosbedon <*****@*****.**>"``"""
        assert email, 'an email is required for find_or_create to work'
        emails = tools.email_split(email)
        if emails:
            email = emails[0]
        ids = self.search(cr,
                          uid, [('email', '=ilike', email)],
                          context=context)
        if not ids:
            return self.name_create(cr, uid, email, context=context)[0]
        return ids[0]
Exemple #10
0
 def send_mail_test(self, cr, uid, ids, context=None):
     Mail = self.pool['mail.mail']
     for wizard in self.browse(cr, uid, ids, context=context):
         mailing = wizard.mass_mailing_id
         test_emails = tools.email_split(wizard.email_to)
         mail_ids = []
         for test_mail in test_emails:
             mail_values = {
                 'email_from': mailing.email_from,
                 'reply_to': mailing.reply_to,
                 'email_to': test_mail,
                 'subject': mailing.name,
                 'body_html': '',
                 'notification': True,
                 'mailing_id': mailing.id,
                 'attachment_ids': [(4, attachment.id) for attachment in mailing.attachment_ids],
             }
             mail_mail_obj = Mail.browse(cr, uid, Mail.create(cr, uid, mail_values, context=context), context=context)
             unsubscribe_url = Mail._get_unsubscribe_url(cr, uid, mail_mail_obj, test_mail, context=context)
             body = tools.append_content_to_html(mailing.body_html, unsubscribe_url, plaintext=False, container_tag='p')
             Mail.write(cr, uid, mail_mail_obj.id, {'body_html': mailing.body_html}, context=context)
             mail_ids.append(mail_mail_obj.id)
         Mail.send(cr, uid, mail_ids, context=context)
     return True
Exemple #11
0
 def send_get_email_dict(self, cr, uid, ids, partner=None, context=None):
     # TDE: temporary addition (mail was parameter) due to semi-new-API
     res = super(MailMail, self).send_get_email_dict(cr,
                                                     uid,
                                                     ids,
                                                     partner,
                                                     context=context)
     mail = self.browse(cr, uid, ids[0], context=context)
     base_url = self.pool.get('ir.config_parameter').get_param(
         cr, uid, 'web.base.url')
     if mail.mailing_id and res.get('body') and res.get('email_to'):
         emails = tools.email_split(res.get('email_to')[0])
         email_to = emails and emails[0] or False
         unsubscribe_url = self._get_unsubscribe_url(cr,
                                                     uid,
                                                     mail,
                                                     email_to,
                                                     context=context)
         link_to_replace = base_url + '/unsubscribe_from_list'
         if link_to_replace in res['body']:
             res['body'] = res['body'].replace(
                 link_to_replace,
                 unsubscribe_url if unsubscribe_url else '#')
     return res
Exemple #12
0
def extract_email(email):
    """ extract the email address from a user-friendly email address """
    addresses = email_split(email)
    return addresses[0] if addresses else ''
 def email_split(self, cr, uid, ids, msg, context=None):
     email_list = tools.email_split((msg.get('to') or '') + ',' + (msg.get('cc') or ''))
     # check left-part is not already an alias
     issue_ids = self.browse(cr, uid, ids, context=context)
     aliases = [issue.project_id.alias_name for issue in issue_ids if issue.project_id]
     return filter(lambda x: x.split('@')[0] not in aliases, email_list)
    def generate_data(self, cr, uid, event, isCreating=False, context=None):
        if not context:
            context = {}
        if event.allday:
            start_date = fields.datetime.context_timestamp(cr, uid, datetime.strptime(event.start, tools.DEFAULT_SERVER_DATETIME_FORMAT), context=context).isoformat('T').split('T')[0]
            final_date = fields.datetime.context_timestamp(cr, uid, datetime.strptime(event.stop, tools.DEFAULT_SERVER_DATETIME_FORMAT) + timedelta(days=1), context=context).isoformat('T').split('T')[0]
            type = 'date'
            vstype = 'dateTime'
        else:
            start_date = fields.datetime.context_timestamp(cr, uid, datetime.strptime(event.start, tools.DEFAULT_SERVER_DATETIME_FORMAT), context=context).isoformat('T')
            final_date = fields.datetime.context_timestamp(cr, uid, datetime.strptime(event.stop, tools.DEFAULT_SERVER_DATETIME_FORMAT), context=context).isoformat('T')
            type = 'dateTime'
            vstype = 'date'
        attendee_list = []
        for attendee in event.attendee_ids:
            email = tools.email_split(attendee.email)
            email = email[0] if email else '*****@*****.**'
            attendee_list.append({
                'email': email,
                'displayName': attendee.partner_id.name,
                'responseStatus': attendee.state or 'needsAction',
            })

        reminders = []
        for alarm in event.alarm_ids:
            reminders.append({
                "method": "email" if alarm.type == "email" else "popup",
                "minutes": alarm.duration_minutes
            })
        data = {
            "summary": event.name or '',
            "description": event.description or '',
            "start": {
                type: start_date,
                vstype: None,
                'timeZone': context.get('tz', 'UTC'),
            },
            "end": {
                type: final_date,
                vstype: None,
                'timeZone': context.get('tz', 'UTC'),
            },
            "attendees": attendee_list,
            "reminders": {
                "overrides": reminders,
                "useDefault": "false"
            },
            "location": event.location or '',
            "visibility": event['class'] or 'public',
        }
        if event.recurrency and event.rrule:
            data["recurrence"] = ["RRULE:" + event.rrule]

        if not event.active:
            data["state"] = "cancelled"

        if not self.get_need_synchro_attendee(cr, uid, context=context):
            data.pop("attendees")
        if isCreating:
            other_google_ids = [other_att.google_internal_event_id for other_att in event.attendee_ids if other_att.google_internal_event_id]
            if other_google_ids:
                data["id"] = other_google_ids[0]
        return data
Exemple #15
0
    def send(self, auto_commit=False, raise_exception=False):
        """ Sends the selected emails immediately, ignoring their current
            state (mails that have already been sent should not be passed
            unless they should actually be re-sent).
            Emails successfully delivered are marked as 'sent', and those
            that fail to be deliver are marked as 'exception', and the
            corresponding error mail is output in the server logs.

            :param bool auto_commit: whether to force a commit of the mail status
                after sending each mail (meant only for scheduler processing);
                should never be True during normal transactions (default: False)
            :param bool raise_exception: whether to raise an exception if the
                email sending process has failed
            :return: True
        """
        IrMailServer = self.env['ir.mail_server']

        for mail in self:
            try:
                # TDE note: remove me when model_id field is present on mail.message - done here to avoid doing it multiple times in the sub method
                if mail.model:
                    model = self.env['ir.model'].sudo().search([('model', '=', mail.model)])[0]
                else:
                    model = None
                if model:
                    mail = mail.with_context(model_name=model.name)

                # load attachment binary data with a separate read(), as prefetching all
                # `datas` (binary field) could bloat the browse cache, triggerring
                # soft/hard mem limits with temporary data.
                attachments = [(a['datas_fname'], base64.b64decode(a['datas']))
                               for a in mail.attachment_ids.sudo().read(['datas_fname', 'datas'])]

                # specific behavior to customize the send email for notified partners
                email_list = []
                if mail.email_to:
                    email_list.append(mail.send_get_email_dict())
                for partner in mail.recipient_ids:
                    email_list.append(mail.send_get_email_dict(partner=partner))

                # headers
                headers = {}
                bounce_alias = self.env['ir.config_parameter'].get_param("mail.bounce.alias")
                catchall_domain = self.env['ir.config_parameter'].get_param("mail.catchall.domain")
                if bounce_alias and catchall_domain:
                    if mail.model and mail.res_id:
                        headers['Return-Path'] = '%s-%d-%s-%d@%s' % (bounce_alias, mail.id, mail.model, mail.res_id, catchall_domain)
                    else:
                        headers['Return-Path'] = '%s-%d@%s' % (bounce_alias, mail.id, catchall_domain)
                if mail.headers:
                    try:
                        headers.update(eval(mail.headers))
                    except Exception:
                        pass

                # Writing on the mail object may fail (e.g. lock on user) which
                # would trigger a rollback *after* actually sending the email.
                # To avoid sending twice the same email, provoke the failure earlier
                mail.write({
                    'state': 'exception',
                    'failure_reason': _('Error without exception. Probably due do sending an email without computed recipients.'),
                })
                mail_sent = False

                # build an RFC2822 email.message.Message object and send it without queuing
                res = None
                for email in email_list:
                    msg = IrMailServer.build_email(
                        email_from=mail.email_from,
                        email_to=email.get('email_to'),
                        subject=mail.subject,
                        body=email.get('body'),
                        body_alternative=email.get('body_alternative'),
                        email_cc=tools.email_split(mail.email_cc),
                        reply_to=mail.reply_to,
                        attachments=attachments,
                        message_id=mail.message_id,
                        references=mail.references,
                        object_id=mail.res_id and ('%s-%s' % (mail.res_id, mail.model)),
                        subtype='html',
                        subtype_alternative='plain',
                        headers=headers)
                    try:
                        res = IrMailServer.send_email(msg, mail_server_id=mail.mail_server_id.id)
                    except AssertionError as error:
                        if error.message == IrMailServer.NO_VALID_RECIPIENT:
                            # No valid recipient found for this particular
                            # mail item -> ignore error to avoid blocking
                            # delivery to next recipients, if any. If this is
                            # the only recipient, the mail will show as failed.
                            _logger.info("Ignoring invalid recipients for mail.mail %s: %s",
                                         mail.message_id, email.get('email_to'))
                        else:
                            raise
                if res:
                    mail.write({'state': 'sent', 'message_id': res, 'failure_reason': False})
                    mail_sent = True

                # /!\ can't use mail.state here, as mail.refresh() will cause an error
                # see revid:[email protected] in 6.1
                if mail_sent:
                    _logger.info('Mail with ID %r and Message-Id %r successfully sent', mail.id, mail.message_id)
                mail._postprocess_sent_message_v9(mail_sent=mail_sent)
            except MemoryError:
                # prevent catching transient MemoryErrors, bubble up to notify user or abort cron job
                # instead of marking the mail as failed
                _logger.exception(
                    'MemoryError while processing mail with ID %r and Msg-Id %r. Consider raising the --limit-memory-hard startup option',
                    mail.id, mail.message_id)
                raise
            except psycopg2.Error:
                # If an error with the database occurs, chances are that the cursor is unusable.
                # This will lead to an `psycopg2.InternalError` being raised when trying to write
                # `state`, shadowing the original exception and forbid a retry on concurrent
                # update. Let's bubble it.
                raise
            except Exception as e:
                failure_reason = tools.ustr(e)
                _logger.exception('failed sending mail (id: %s) due to %s', mail.id, failure_reason)
                mail.write({'state': 'exception', 'failure_reason': failure_reason})
                mail._postprocess_sent_message_v9(mail_sent=False)
                if raise_exception:
                    if isinstance(e, AssertionError):
                        # get the args of the original error, wrap into a value and throw a MailDeliveryException
                        # that is an except_orm, with name and value as arguments
                        value = '. '.join(e.args)
                        raise MailDeliveryException(_("Mail Delivery Failed"), value)
                    raise

            if auto_commit is True:
                self._cr.commit()
        return True
Exemple #16
0
    def send(self, auto_commit=False, raise_exception=False):
        """ Sends the selected emails immediately, ignoring their current
            state (mails that have already been sent should not be passed
            unless they should actually be re-sent).
            Emails successfully delivered are marked as 'sent', and those
            that fail to be deliver are marked as 'exception', and the
            corresponding error mail is output in the server logs.

            :param bool auto_commit: whether to force a commit of the mail status
                after sending each mail (meant only for scheduler processing);
                should never be True during normal transactions (default: False)
            :param bool raise_exception: whether to raise an exception if the
                email sending process has failed
            :return: True
        """
        IrMailServer = self.env['ir.mail_server']

        for mail in self:
            try:
                # TDE note: remove me when model_id field is present on mail.message - done here to avoid doing it multiple times in the sub method
                if mail.model:
                    model = self.env['ir.model'].sudo().search([
                        ('model', '=', mail.model)
                    ])[0]
                else:
                    model = None
                if model:
                    mail = mail.with_context(model_name=model.name)

                # load attachment binary data with a separate read(), as prefetching all
                # `datas` (binary field) could bloat the browse cache, triggerring
                # soft/hard mem limits with temporary data.
                attachments = [(a['datas_fname'], base64.b64decode(a['datas']))
                               for a in mail.attachment_ids.sudo().read(
                                   ['datas_fname', 'datas'])]

                # specific behavior to customize the send email for notified partners
                email_list = []
                if mail.email_to:
                    email_list.append(mail.send_get_email_dict())
                for partner in mail.recipient_ids:
                    email_list.append(
                        mail.send_get_email_dict(partner=partner))

                # headers
                headers = {}
                bounce_alias = self.env['ir.config_parameter'].get_param(
                    "mail.bounce.alias")
                catchall_domain = self.env['ir.config_parameter'].get_param(
                    "mail.catchall.domain")
                if bounce_alias and catchall_domain:
                    if mail.model and mail.res_id:
                        headers['Return-Path'] = '%s-%d-%s-%d@%s' % (
                            bounce_alias, mail.id, mail.model, mail.res_id,
                            catchall_domain)
                    else:
                        headers['Return-Path'] = '%s-%d@%s' % (
                            bounce_alias, mail.id, catchall_domain)
                if mail.headers:
                    try:
                        headers.update(eval(mail.headers))
                    except Exception:
                        pass

                # Writing on the mail object may fail (e.g. lock on user) which
                # would trigger a rollback *after* actually sending the email.
                # To avoid sending twice the same email, provoke the failure earlier
                mail.write({
                    'state':
                    'exception',
                    'failure_reason':
                    _('Error without exception. Probably due do sending an email without computed recipients.'
                      ),
                })
                mail_sent = False

                # build an RFC2822 email.message.Message object and send it without queuing
                res = None
                for email in email_list:
                    msg = IrMailServer.build_email(
                        email_from=mail.email_from,
                        email_to=email.get('email_to'),
                        subject=mail.subject,
                        body=email.get('body'),
                        body_alternative=email.get('body_alternative'),
                        email_cc=tools.email_split(mail.email_cc),
                        reply_to=mail.reply_to,
                        attachments=attachments,
                        message_id=mail.message_id,
                        references=mail.references,
                        object_id=mail.res_id
                        and ('%s-%s' % (mail.res_id, mail.model)),
                        subtype='html',
                        subtype_alternative='plain',
                        headers=headers)
                    try:
                        res = IrMailServer.send_email(
                            msg, mail_server_id=mail.mail_server_id.id)
                    except AssertionError as error:
                        if error.message == IrMailServer.NO_VALID_RECIPIENT:
                            # No valid recipient found for this particular
                            # mail item -> ignore error to avoid blocking
                            # delivery to next recipients, if any. If this is
                            # the only recipient, the mail will show as failed.
                            _logger.info(
                                "Ignoring invalid recipients for mail.mail %s: %s",
                                mail.message_id, email.get('email_to'))
                        else:
                            raise
                if res:
                    mail.write({
                        'state': 'sent',
                        'message_id': res,
                        'failure_reason': False
                    })
                    mail_sent = True

                # /!\ can't use mail.state here, as mail.refresh() will cause an error
                # see revid:[email protected] in 6.1
                if mail_sent:
                    _logger.info(
                        'Mail with ID %r and Message-Id %r successfully sent',
                        mail.id, mail.message_id)
                mail._postprocess_sent_message_v9(mail_sent=mail_sent)
            except MemoryError:
                # prevent catching transient MemoryErrors, bubble up to notify user or abort cron job
                # instead of marking the mail as failed
                _logger.exception(
                    'MemoryError while processing mail with ID %r and Msg-Id %r. Consider raising the --limit-memory-hard startup option',
                    mail.id, mail.message_id)
                raise
            except psycopg2.Error:
                # If an error with the database occurs, chances are that the cursor is unusable.
                # This will lead to an `psycopg2.InternalError` being raised when trying to write
                # `state`, shadowing the original exception and forbid a retry on concurrent
                # update. Let's bubble it.
                raise
            except Exception as e:
                failure_reason = tools.ustr(e)
                _logger.exception('failed sending mail (id: %s) due to %s',
                                  mail.id, failure_reason)
                mail.write({
                    'state': 'exception',
                    'failure_reason': failure_reason
                })
                mail._postprocess_sent_message_v9(mail_sent=False)
                if raise_exception:
                    if isinstance(e, AssertionError):
                        # get the args of the original error, wrap into a value and throw a MailDeliveryException
                        # that is an except_orm, with name and value as arguments
                        value = '. '.join(e.args)
                        raise MailDeliveryException(_("Mail Delivery Failed"),
                                                    value)
                    raise

            if auto_commit is True:
                self._cr.commit()
        return True