Ejemplo n.º 1
0
 def _mail_cc_sanitized_raw_dict(self, cc_string):
     '''return a dict of sanitize_email:raw_email from a string of cc'''
     if not cc_string:
         return {}
     return {
         tools.email_normalize(email): tools.formataddr(
             (name, tools.email_normalize(email)))
         for (name, email) in tools.email_split_tuples(cc_string)
     }
Ejemplo n.º 2
0
 def _search(self,
             args,
             offset=0,
             limit=None,
             order=None,
             count=False,
             access_rights_uid=None):
     """ Override _search in order to grep search on email field and make it
     lower-case and sanitized """
     if args:
         new_args = []
         for arg in args:
             if isinstance(
                     arg,
                 (list, tuple)) and arg[0] == 'email' and isinstance(
                     arg[2], str):
                 normalized = tools.email_normalize(arg[2])
                 if normalized:
                     new_args.append([arg[0], arg[1], normalized])
                 else:
                     new_args.append(arg)
             else:
                 new_args.append(arg)
     else:
         new_args = args
     return super(MailBlackList,
                  self)._search(new_args,
                                offset=offset,
                                limit=limit,
                                order=order,
                                count=count,
                                access_rights_uid=access_rights_uid)
Ejemplo n.º 3
0
    def action_invite(self):
        """ Process the wizard content and proceed with sending the related
            email(s), rendering any template patterns on the fly if needed """
        self.ensure_one()
        Partner = self.env['res.partner']

        # compute partners and emails, try to find partners for given emails
        valid_partners = self.partner_ids
        valid_emails = []
        for email in emails_split.split(self.emails or ''):
            partner = False
            email_normalized = tools.email_normalize(email)
            if email_normalized:
                limit = None if self.survey_users_login_required else 1
                partner = Partner.search([('email_normalized', '=', email_normalized)], limit=limit)
            if partner:
                valid_partners |= partner
            else:
                email_formatted = tools.email_split_and_format(email)
                if email_formatted:
                    valid_emails.extend(email_formatted)

        if not valid_partners and not valid_emails:
            raise UserError(_("Please enter at least one valid recipient."))

        answers = self._prepare_answers(valid_partners, valid_emails)
        for answer in answers:
            self._send_mail(answer)

        return {'type': 'ir.actions.act_window_close'}
Ejemplo n.º 4
0
 def _add(self, email):
     normalized = tools.email_normalize(email)
     record = self.env["mail.blacklist"].with_context(
         active_test=False).search([('email', '=', normalized)])
     if len(record) > 0:
         record.write({'active': True})
     else:
         record = self.create({'email': email})
     return record
Ejemplo n.º 5
0
 def blacklist_check(self, mailing_id, res_id, email, token):
     if not self._valid_unsubscribe_token(mailing_id, res_id, email, token):
         return 'unauthorized'
     if email:
         record = request.env['mail.blacklist'].sudo().with_context(
             active_test=False).search([('email', '=',
                                         tools.email_normalize(email))])
         if record['active']:
             return True
         return False
     return 'error'
Ejemplo n.º 6
0
    def iap_enrich(self, from_cron=False):
        lead_emails = {}
        for lead in self:
            # If lead is lost, active == False, but is anyway removed from the search in the cron.
            if lead.probability == 100 or lead.iap_enrich_done:
                continue
            normalized_email = tools.email_normalize(
                lead.partner_address_email) or tools.email_normalize(
                    lead.email_from)
            if normalized_email:
                lead_emails[lead.id] = normalized_email.split('@')[1]
            else:
                lead.message_post_with_view(
                    'crm_iap_lead_enrich.mail_message_lead_enrich_no_email',
                    subtype_id=self.env.ref('mail.mt_note').id)

        if lead_emails:
            try:
                iap_response = self.env['iap.enrich.api']._request_enrich(
                    lead_emails)
            except InsufficientCreditError:
                _logger.info(
                    'Sent batch %s enrich requests: failed because of credit',
                    len(lead_emails))
                if not from_cron:
                    data = {
                        'url':
                        self.env['iap.account'].get_credits_url('reveal'),
                    }
                    self[0].message_post_with_view(
                        'crm_iap_lead_enrich.mail_message_lead_enrich_no_credit',
                        values=data,
                        subtype_id=self.env.ref('mail.mt_note').id)
            except Exception as e:
                _logger.info(
                    'Sent batch %s enrich requests: failed with exception %s',
                    len(lead_emails), e)
            else:
                _logger.info('Sent batch %s enrich requests: success',
                             len(lead_emails))
                self._iap_enrich_from_response(iap_response)
Ejemplo n.º 7
0
 def send_feedback(self, mailing_id, res_id, email, feedback, token):
     mailing = request.env['mailing.mailing'].sudo().browse(mailing_id)
     if mailing.exists() and email:
         if not self._valid_unsubscribe_token(mailing_id, res_id, email,
                                              token):
             return 'unauthorized'
         model = request.env[mailing.mailing_model_real]
         records = model.sudo().search([('email_normalized', '=',
                                         tools.email_normalize(email))])
         for record in records:
             record.sudo().message_post(body=_("Feedback from %s: %s" %
                                               (email, feedback)))
         return bool(records)
     return 'error'
Ejemplo n.º 8
0
    def update_opt_out(self, email, list_ids, value):
        if len(list_ids) > 0:
            model = self.env['mailing.contact'].with_context(active_test=False)
            records = model.search([('email_normalized', '=',
                                     tools.email_normalize(email))])
            opt_out_records = self.env['mailing.contact.subscription'].search([
                ('contact_id', 'in', records.ids), ('list_id', 'in', list_ids),
                ('opt_out', '!=', value)
            ])

            opt_out_records.write({'opt_out': value})
            message = _('The recipient <strong>unsubscribed from %s</strong> mailing list(s)') \
                if value else _('The recipient <strong>subscribed to %s</strong> mailing list(s)')
            for record in records:
                # filter the list_id by record
                record_lists = opt_out_records.filtered(
                    lambda rec: rec.contact_id.id == record.id)
                if len(record_lists) > 0:
                    record.sudo().message_post(body=_(message % ', '.join(
                        str(list.name)
                        for list in record_lists.mapped('list_id'))))
Ejemplo n.º 9
0
    def create(self, values):
        # First of all, extract values to ensure emails are really unique (and don't modify values in place)
        new_values = []
        all_emails = []
        for value in values:
            email = tools.email_normalize(value.get('email'))
            if not email:
                raise UserError(_('Invalid email address %r') % value['email'])
            if email in all_emails:
                continue
            all_emails.append(email)
            new_value = dict(value, email=email)
            new_values.append(new_value)
        """ To avoid crash during import due to unique email, return the existing records if any """
        sql = '''SELECT email, id FROM mail_blacklist WHERE email = ANY(%s)'''
        emails = [v['email'] for v in new_values]
        self._cr.execute(sql, (emails, ))
        bl_entries = dict(self._cr.fetchall())
        to_create = [v for v in new_values if v['email'] not in bl_entries]

        # TODO DBE Fixme : reorder ids according to incoming ids.
        results = super(MailBlackList, self).create(to_create)
        return self.env['mail.blacklist'].browse(bl_entries.values()) | results
Ejemplo n.º 10
0
    def test_mail_bounced_auto_blacklist(self):
        mass_mailing_contacts = self.env['mailing.contact']
        mass_mailing = self.env['mailing.mailing']
        mail_blacklist = self.env['mail.blacklist']
        mail_statistics = self.env['mailing.trace']

        # create mailing contact record
        self.mailing_contact_1 = mass_mailing_contacts.create({
            'name':
            'test email 1',
            'email':
            '*****@*****.**'
        })

        base_parsed_values = {
            'email_from': '*****@*****.**',
            'to': '*****@*****.**',
            'message_id': '<*****@*****.**>',
            'bounced_partner': self.env['res.partner'].sudo(),
            'bounced_message': self.env['mail.message'].sudo()
        }

        # create bounced history of 4 statistics
        for idx in range(4):
            mail_statistics.create({
                'model':
                'mailing.contact',
                'res_id':
                self.mailing_contact_1.id,
                'bounced':
                datetime.datetime.now() - datetime.timedelta(weeks=idx + 2),
                'email':
                self.mailing_contact_1.email,
                'message_id':
                '<*****@*****.**>' % idx,
            })
            base_parsed_values.update({
                'bounced_email':
                tools.email_normalize(self.mailing_contact_1.email),
                'bounced_msg_id': ['<*****@*****.**>' % idx]
            })
            self.env['mail.thread']._routing_handle_bounce(
                False, base_parsed_values)

        # create mass mailing record
        self.mass_mailing = mass_mailing.create({
            'name':
            'test',
            'subject':
            'Booooounce!',
            'mailing_domain': [('id', 'in', [self.mailing_contact_1.id])],
            'body_html':
            'This is a bounced mail for auto blacklist demo'
        })
        self.mass_mailing.action_put_in_queue()
        res_ids = self.mass_mailing._get_remaining_recipients()
        composer_values = {
            'body':
            self.mass_mailing.convert_links()[self.mass_mailing.id],
            'subject':
            self.mass_mailing.name,
            'model':
            self.mass_mailing.mailing_model_real,
            'email_from':
            self.mass_mailing.email_from,
            'composition_mode':
            'mass_mail',
            'mass_mailing_id':
            self.mass_mailing.id,
            'mailing_list_ids':
            [(4, l.id) for l in self.mass_mailing.contact_list_ids],
        }
        composer = self.env['mail.compose.message'].with_context(
            active_ids=res_ids,
            mass_mailing_seen_list=self.mass_mailing._get_seen_list()).create(
                composer_values)
        composer.send_mail()

        mail_statistics.create({
            'model': 'mailing.contact',
            'res_id': self.mailing_contact_1.id,
            'bounced': datetime.datetime.now(),
            'email': self.mailing_contact_1.email,
            'message_id': '<*****@*****.**>',
        })
        base_parsed_values.update({
            'bounced_email':
            tools.email_normalize(self.mailing_contact_1.email),
            'bounced_msg_id': ['<*****@*****.**>']
        })
        # call bounced
        self.env['mail.thread']._routing_handle_bounce(False,
                                                       base_parsed_values)

        # check blacklist
        blacklist_record = mail_blacklist.search([
            ('email', '=', self.mailing_contact_1.email)
        ])
        self.assertEqual(
            len(blacklist_record), 1,
            'The email %s must be blacklisted' % self.mailing_contact_1.email)
Ejemplo n.º 11
0
    def get_mail_values(self, res_ids):
        """ Override method that generated the mail content by creating the
        mailing.trace values in the o2m of mail_mail, when doing pure
        email mass mailing. """
        self.ensure_one()
        res = super(MailComposeMessage, self).get_mail_values(res_ids)
        # use only for allowed models in mass mailing
        if self.composition_mode == 'mass_mail' and \
                (self.mass_mailing_name or self.mass_mailing_id) and \
                self.env['ir.model'].sudo().search([('model', '=', self.model), ('is_mail_thread', '=', True)], limit=1):
            mass_mailing = self.mass_mailing_id
            if not mass_mailing:
                reply_to_mode = 'email' if self.no_auto_thread else 'thread'
                reply_to = self.reply_to if self.no_auto_thread else False
                mass_mailing = self.env['mailing.mailing'].create({
                        'campaign_id': self.campaign_id.id,
                        'name': self.mass_mailing_name,
                        'subject': self.subject,
                        'state': 'done',
                        'reply_to_mode': reply_to_mode,
                        'reply_to': reply_to,
                        'sent_date': fields.Datetime.now(),
                        'body_html': self.body,
                        'mailing_model_id': self.env['ir.model']._get(self.model).id,
                        'mailing_domain': self.active_domain,
                })

            # Preprocess res.partners to batch-fetch from db
            # if recipient_ids is present, it means they are partners
            # (the only object to fill get_default_recipient this way)
            recipient_partners_ids = []
            read_partners = {}
            for res_id in res_ids:
                mail_values = res[res_id]
                if mail_values.get('recipient_ids'):
                    # recipient_ids is a list of x2m command tuples at this point
                    recipient_partners_ids.append(mail_values.get('recipient_ids')[0][1])
            read_partners = self.env['res.partner'].browse(recipient_partners_ids)

            partners_email = {p.id: p.email for p in read_partners}

            opt_out_list = self._context.get('mass_mailing_opt_out_list')
            seen_list = self._context.get('mass_mailing_seen_list')
            mass_mail_layout = self.env.ref('mass_mailing.mass_mailing_mail_layout', raise_if_not_found=False)
            for res_id in res_ids:
                mail_values = res[res_id]
                if mail_values.get('email_to'):
                    mail_to = tools.email_normalize(mail_values['email_to'])
                else:
                    partner_id = (mail_values.get('recipient_ids') or [(False, '')])[0][1]
                    mail_to = tools.email_normalize(partners_email.get(partner_id))
                if (opt_out_list and mail_to in opt_out_list) or (seen_list and mail_to in seen_list) \
                        or (not mail_to or not email_re.findall(mail_to)):
                    # prevent sending to blocked addresses that were included by mistake
                    mail_values['state'] = 'cancel'
                elif seen_list is not None:
                    seen_list.add(mail_to)
                trace_vals = {
                    'model': self.model,
                    'res_id': res_id,
                    'mass_mailing_id': mass_mailing.id,
                    'email': mail_to,
                }
                if mail_values.get('body_html') and mass_mail_layout:
                    mail_values['body_html'] = mass_mail_layout.render({'body': mail_values['body_html']}, engine='ir.qweb', minimal_qcontext=True)
                # propagate ignored state to trace when still-born
                if mail_values.get('state') == 'cancel':
                    trace_vals['ignored'] = fields.Datetime.now()
                mail_values.update({
                    'mailing_id': mass_mailing.id,
                    'mailing_trace_ids': [(0, 0, trace_vals)],
                    # email-mode: keep original message for routing
                    'notification': mass_mailing.reply_to_mode == 'thread',
                    'auto_delete': not mass_mailing.keep_archives,
                })
        return res
Ejemplo n.º 12
0
 def write(self, values):
     if 'email' in values:
         values['email'] = tools.email_normalize(values['email'])
     return super(MailBlackList, self).write(values)
Ejemplo n.º 13
0
    def mailing(self, mailing_id, email=None, res_id=None, token="", **post):
        mailing = request.env['mailing.mailing'].sudo().browse(mailing_id)
        if mailing.exists():
            res_id = res_id and int(res_id)
            if not self._valid_unsubscribe_token(mailing_id, res_id, email,
                                                 str(token)):
                raise exceptions.AccessDenied()

            if mailing.mailing_model_real == 'mailing.contact':
                # Unsubscribe directly + Let the user choose his subscriptions
                mailing.update_opt_out(email, mailing.contact_list_ids.ids,
                                       True)

                contacts = request.env['mailing.contact'].sudo().search([
                    ('email_normalized', '=', tools.email_normalize(email))
                ])
                subscription_list_ids = contacts.mapped(
                    'subscription_list_ids')
                # In many user are found : if user is opt_out on the list with contact_id 1 but not with contact_id 2,
                # assume that the user is not opt_out on both
                # TODO DBE Fixme : Optimise the following to get real opt_out and opt_in
                opt_out_list_ids = subscription_list_ids.filtered(
                    lambda rel: rel.opt_out).mapped('list_id')
                opt_in_list_ids = subscription_list_ids.filtered(
                    lambda rel: not rel.opt_out).mapped('list_id')
                opt_out_list_ids = set([
                    list.id for list in opt_out_list_ids
                    if list not in opt_in_list_ids
                ])

                unique_list_ids = set(
                    [list.list_id.id for list in subscription_list_ids])
                list_ids = request.env['mailing.list'].sudo().browse(
                    unique_list_ids)
                unsubscribed_list = ', '.join(
                    str(list.name) for list in mailing.contact_list_ids
                    if list.is_public)
                return request.render(
                    'mass_mailing.page_unsubscribe', {
                        'contacts':
                        contacts,
                        'list_ids':
                        list_ids,
                        'opt_out_list_ids':
                        opt_out_list_ids,
                        'unsubscribed_list':
                        unsubscribed_list,
                        'email':
                        email,
                        'mailing_id':
                        mailing_id,
                        'res_id':
                        res_id,
                        'show_blacklist_button':
                        request.env['ir.config_parameter'].sudo().get_param(
                            'mass_mailing.show_blacklist_buttons'),
                    })
            else:
                opt_in_lists = request.env[
                    'mailing.contact.subscription'].sudo().search([
                        ('contact_id.email_normalized', '=', email),
                        ('opt_out', '=', False)
                    ]).mapped('list_id')
                blacklist_rec = request.env['mail.blacklist'].sudo()._add(
                    email)
                self._log_blacklist_action(
                    blacklist_rec, mailing_id,
                    _("""Requested blacklisting via unsubscribe link."""))
                return request.render(
                    'mass_mailing.page_unsubscribed', {
                        'email':
                        email,
                        'mailing_id':
                        mailing_id,
                        'res_id':
                        res_id,
                        'list_ids':
                        opt_in_lists,
                        'show_blacklist_button':
                        request.env['ir.config_parameter'].sudo().get_param(
                            'mass_mailing.show_blacklist_buttons'),
                    })
        return request.redirect('/web')
Ejemplo n.º 14
0
 def _compute_email_normalized(self):
     self._assert_primary_email()
     for record in self:
         record.email_normalized = tools.email_normalize(record[self._primary_email])