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 _flectra_attendee_commands(self, google_event):
        attendee_commands = []
        partner_commands = []
        google_attendees = google_event.attendees or []
        if len(
                google_attendees
        ) == 0 and google_event.organizer and google_event.organizer.get(
                'self', False):
            user = google_event.owner(self.env)
            google_attendees += [{
                'email': user.partner_id.email,
                'status': {
                    'response': 'accepted'
                },
            }]
        emails = [a.get('email') for a in google_attendees]
        existing_attendees = self.env['calendar.attendee']
        if google_event.exists(self.env):
            existing_attendees = self.browse(google_event.flectra_id(
                self.env)).attendee_ids
        attendees_by_emails = {
            tools.email_normalize(a.email): a
            for a in existing_attendees
        }
        for attendee in google_attendees:
            email = attendee.get('email')

            if email in attendees_by_emails:
                # Update existing attendees
                attendee_commands += [(1, attendees_by_emails[email].id, {
                    'state': attendee.get('responseStatus')
                })]
            else:
                # Create new attendees
                partner = self.env.user.partner_id if attendee.get(
                    'self') else self.env['res.partner'].find_or_create(
                        attendee.get('email'))
                attendee_commands += [(0, 0, {
                    'state': attendee.get('responseStatus'),
                    'partner_id': partner.id
                })]
                partner_commands += [(4, partner.id)]
                if attendee.get('displayName') and not partner.name:
                    partner.name = attendee.get('displayName')
        for flectra_attendee in attendees_by_emails.values():
            # Remove old attendees
            if tools.email_normalize(flectra_attendee.email) not in emails:
                attendee_commands += [(2, flectra_attendee.id)]
                partner_commands += [(3, flectra_attendee.partner_id.id)]
        return attendee_commands, partner_commands
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 _parse_partner_name(self, text):
        """ Parse partner name (given by text) in order to find a name and an
        email. Supported syntax:

          * Raoul <*****@*****.**>
          * "Raoul le Grand" <*****@*****.**>
          * Raoul [email protected] (strange fault tolerant support from df40926d2a57c101a3e2d221ecfd08fbb4fea30e)

        Otherwise: default, everything is set as the name. Starting from 13.3
        returned email will be normalized to have a coherent encoding.
         """
        name, email = '', ''
        split_results = tools.email_split_tuples(text)
        if split_results:
            name, email = split_results[0]

        if email and not name:
            fallback_emails = tools.email_split(text.replace(' ', ','))
            if fallback_emails:
                email = fallback_emails[0]
                name = text[:text.index(email)].replace('"', '').replace('<', '').strip()

        if email:
            email = tools.email_normalize(email)
        else:
            name, email = text, ''

        return name, email
Ejemplo n.º 5
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.º 6
0
 def _remove(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.action_archive()
     else:
         record = record.create({'email': email, 'active': False})
     return record
Ejemplo n.º 7
0
    def iap_enrich(self, from_cron=False):
        # Split self in a list of sub-recordsets or 50 records to prevent timeouts
        batches = [self[index:index + 50] for index in range(0, len(self), 50)]
        for leads in batches:
            lead_emails = {}
            with self._cr.savepoint():
                try:
                    self._cr.execute(
                        "SELECT 1 FROM {} WHERE id in %(lead_ids)s FOR UPDATE NOWAIT".format(self._table),
                        {'lead_ids': tuple(leads.ids)}, log_exceptions=False)
                    for lead in leads:
                        # 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.email_from)
                        if not normalized_email:
                            lead.message_post_with_view(
                                'crm_iap_lead_enrich.mail_message_lead_enrich_no_email',
                                subtype_id=self.env.ref('mail.mt_note').id)
                            continue

                        email_domain = normalized_email.split('@')[1]
                        # Discard domains of generic email providers as it won't return relevant information
                        if email_domain in EMAIL_PROVIDERS:
                            lead.write({'iap_enrich_done': True})
                            lead.message_post_with_view(
                                'crm_iap_lead_enrich.mail_message_lead_enrich_notfound',
                                subtype_id=self.env.ref('mail.mt_note').id)
                        else:
                            lead_emails[lead.id] = email_domain

                    if lead_emails:
                        try:
                            iap_response = self.env['iap.enrich.api']._request_enrich(lead_emails)
                        except iap_tools.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'),
                                }
                                leads[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)
                            # Since there are no credits left, there is no point to process the other batches
                            break
                        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)
                except OperationalError:
                    _logger.error('A batch of leads could not be enriched :%s', repr(leads))
                    continue
            # Commit processed batch to avoid complete rollbacks and therefore losing credits.
            if not self.env.registry.in_test_mode():
                self.env.cr.commit()
Ejemplo n.º 8
0
    def res_partner_get_by_email(self, email, name, **kwargs):
        response = {}

        #compute the sender's domain
        normalized_email = tools.email_normalize(email)
        if not normalized_email:
            response['error'] = 'Bad email.'
            return response
        sender_domain = normalized_email.split('@')[1]

        # Search for the partner based on the email.
        # If multiple are found, take the first one.
        partner = request.env['res.partner'].search(
            [('email', 'in', [normalized_email, email])], limit=1)
        if partner:
            response['partner'] = {
                'id': partner.id,
                'name': partner.name,
                'title': partner.function,
                'email': partner.email,
                'image': partner.image_128,
                'phone': partner.phone,
                'mobile': partner.mobile,
                'enrichment_info': None
            }
            # if there is already a company for this partner, just take it without enrichment.
            if partner.parent_id:
                response['partner']['company'] = self._get_company_dict(
                    partner.parent_id)
            else:
                company = self._find_existing_company(sender_domain)
                if not company:  # create and enrich company
                    company, enrichment_info = self._create_company_from_iap(
                        sender_domain)
                    response['enrichment_info'] = enrichment_info
                partner.write({'parent_id': company})
                response['partner']['company'] = self._get_company_dict(
                    company)
        else:  #no partner found
            response['partner'] = {
                'id': -1,
                'name': name,
                'email': email,
                'enrichment_info': None
            }
            company = self._find_existing_company(sender_domain)
            if not company:  # create and enrich company
                company, enrichment_info = self._create_company_from_iap(
                    sender_domain)
                response['enrichment_info'] = enrichment_info
            response['partner']['company'] = self._get_company_dict(company)

        return response
Ejemplo n.º 9
0
    def _validate_char_box(self, answer):
        # Email format validation
        # all the strings of the form "<something>@<anything>.<extension>" will be accepted
        if self.validation_email:
            if not tools.email_normalize(answer):
                return {self.id: _('This answer must be an email address')}

        # Answer validation (if properly defined)
        # Length of the answer must be in a range
        if self.validation_required:
            if not (self.validation_length_min <= len(answer) <=
                    self.validation_length_max):
                return {self.id: self.validation_error_msg}
        return {}
Ejemplo n.º 10
0
    def find_or_create(self, email, assert_valid_email=False):
        """ Override to use the email_normalized field. """
        if not email:
            raise ValueError(
                _('An email is required for find_or_create to work'))

        parsed_name, parsed_email = self._parse_partner_name(email)
        if parsed_email:
            email_normalized = tools.email_normalize(parsed_email)
            if email_normalized:
                partners = self.search(
                    [('email_normalized', '=', email_normalized)], limit=1)
                if partners:
                    return partners

        return super(Partner, self).find_or_create(
            email, assert_valid_email=assert_valid_email)
Ejemplo n.º 11
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.º 12
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.º 13
0
 def write(self, values):
     if 'email' in values:
         values['email'] = tools.email_normalize(values['email'])
     return super(MailBlackList, self).write(values)
Ejemplo n.º 14
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,
                    # if mail_to is void, keep falsy values to allow searching / debugging traces
                    'email': mail_to or mail_values.get('email_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.º 15
0
    def _flectra_attendee_commands_m(self, microsoft_event):
        commands_attendee = []
        commands_partner = []

        microsoft_attendees = microsoft_event.attendees or []
        emails = [a.get('emailAddress').get('address') for a in microsoft_attendees if email_normalize(a.get('emailAddress').get('address'))]
        existing_attendees = self.env['calendar.attendee']
        if microsoft_event.exists(self.env):
            existing_attendees = self.env['calendar.attendee'].search([
                ('event_id', '=', microsoft_event.flectra_id(self.env)),
                ('email', 'in', emails)])
        elif self.env.user.partner_id.email not in emails:
            commands_attendee += [(0, 0, {'state': 'accepted', 'partner_id': self.env.user.partner_id.id})]
            commands_partner += [(4, self.env.user.partner_id.id)]
        partners = self.env['mail.thread']._mail_find_partner_from_emails(emails, records=self, force_create=True)
        attendees_by_emails = {a.email: a for a in existing_attendees}
        for email, partner, m_attendee in zip(emails, partners, microsoft_attendees):
            state = ATTENDEE_CONVERTER_M2O.get(m_attendee.get('status').get('response'))
            if email in attendees_by_emails:
                # Update existing attendees
                commands_attendee += [(1, attendees_by_emails[email].id, {'state': state})]
            else:
                # Create new attendees
                commands_attendee += [(0, 0, {'state': state, 'partner_id': partner.id})]
                commands_partner += [(4, partner.id)]
                if m_attendee.get('emailAddress').get('name') and not partner.name:
                    partner.name = m_attendee.get('emailAddress').get('name')
        for flectra_attendee in attendees_by_emails.values():
            # Remove old attendees
            if flectra_attendee.email not in emails:
                commands_attendee += [(2, flectra_attendee.id)]
                commands_partner += [(3, flectra_attendee.partner_id.id)]
        return commands_attendee, commands_partner
Ejemplo n.º 16
0
 def _compute_email_normalized(self):
     self._assert_primary_email()
     for record in self:
         record.email_normalized = tools.email_normalize(
             record[self._primary_email])