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
def _lead_create_contact(self, cr, uid, lead, name, is_company, parent_id=False, context=None): partner = self.pool.get("res.partner") vals = { "name": name, "user_id": lead.user_id.id, "comment": lead.description, "section_id": lead.section_id.id or False, "parent_id": parent_id, "phone": lead.phone, "mobile": lead.mobile, "email": tools.email_split(lead.email_from) and tools.email_split(lead.email_from)[0] or False, "fax": lead.fax, "title": lead.title and lead.title.id or False, "function": lead.function, "street": lead.street, "street2": lead.street2, "zip": lead.zip, "city": lead.city, "country_id": lead.country_id and lead.country_id.id or False, "state_id": lead.state_id and lead.state_id.id or False, "is_company": is_company, "type": "contact", "vat": lead.vat or "", } partner_id = partner.create(cr, uid, vals, context=context) partner.open_trends(cr, uid, [partner_id], context=context) return partner_id
def generate_recipients_batch(self, cr, uid, results, template_id, res_ids, context=None): """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. """ if context is None: context = {} template = self.browse(cr, uid, template_id, context=context) if template.use_default_to or context.get("tpl_force_default_to"): ctx = dict(context, thread_model=template.model) default_recipients = self.pool["mail.thread"].message_get_default_recipients(cr, uid, res_ids, context=ctx) 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 context and 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.pool.get("res.partner").find_or_create(cr, uid, mail, context=context) 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.pool["res.partner"].exists(cr, SUPERUSER_ID, tpl_partner_ids, context=context) results[res_id]["partner_ids"] = partner_ids return results
def _lead_create_contact(self, cr, uid, lead, name, is_company, parent_id=False, context=None): partner = self.pool.get('res.partner') vals = {'name': name, 'user_id': lead.user_id.id, 'comment': lead.description, 'section_id': lead.section_id.id or False, 'parent_id': parent_id, 'phone': lead.phone, 'mobile': lead.mobile, 'email': tools.email_split(lead.email_from) and tools.email_split(lead.email_from)[0] or False, 'fax': lead.fax, 'title': lead.title and lead.title.id or False, 'function': lead.function, 'street': lead.street, 'street2': lead.street2, 'zip': lead.zip, 'city': lead.city, 'country_id': lead.country_id and lead.country_id.id or False, 'state_id': lead.state_id and lead.state_id.id or False, 'is_company': is_company, 'type': 'contact', 'mmh_origin' : lead.mmh_origin and lead.mmh_origin.id, 'mmh_cust_type' : lead.mmh_cust_type and lead.mmh_cust_type.id } partner = partner.create(cr, uid, vals, context=context) return partner
def _lead_create_contact(self, lead, name, is_company, parent_id=False): """ Extends original method to also add house_no and apartment_no fields data """ partner = self.env['res.partner'] vals = {'name': name, 'user_id': lead.user_id.id, 'comment': lead.description, 'section_id': lead.section_id.id or False, 'parent_id': parent_id, 'phone': lead.phone, 'mobile': lead.mobile, 'email': tools.email_split(lead.email_from) and tools.email_split(lead.email_from)[0] or False, 'fax': lead.fax, 'title': lead.title and lead.title.id or False, 'function': lead.function, 'street': lead.street, 'street2': lead.street2, 'zip': lead.zip, 'city': lead.city, 'country_id': lead.country_id and lead.country_id.id or False, 'state_id': lead.state_id and lead.state_id.id or False, 'is_company': is_company, 'type': 'contact', 'house_no': lead.house_no, 'apartment_no': lead.apartment_no, } partner = partner.create(vals) return partner.id
def generate_email_for_composer(self, cr, uid, template_id, res_id, context=None): """ Call email_template.generate_email(), get fields relevant for mail.compose.message, transform email_cc and email_to into partner_ids """ template_values = self.pool.get('email.template').generate_email(cr, uid, template_id, res_id, context=context) # filter template values fields = ['body_html', 'subject', 'email_to', 'email_recipients', 'email_cc', 'attachment_ids', 'attachments'] values = dict((field, template_values[field]) for field in fields if template_values.get(field)) values['body'] = values.pop('body_html', '') # transform email_to, email_cc into partner_ids partner_ids = set() mails = tools.email_split(values.pop('email_to', '')) + tools.email_split(values.pop('email_cc', '')) ctx = dict((k, v) for k, v in (context or {}).items() if not k.startswith('default_')) for mail in mails: partner_id = self.pool.get('res.partner').find_or_create(cr, uid, mail, context=ctx) partner_ids.add(partner_id) email_recipients = values.pop('email_recipients', '') if email_recipients: for partner_id in email_recipients.split(','): if partner_id: # placeholders could generate '', 3, 2 due to some empty field values partner_ids.add(int(partner_id)) # legacy template behavior: void values do not erase existing values and the # related key is removed from the values dict if partner_ids: values['partner_ids'] = list(partner_ids) return values
def _get_or_create_partners_from_values(self, cr, uid, rendered_values, context=None): """ Check for email_to, email_cc, partner_to """ partner_ids = [] mails = tools.email_split(rendered_values.pop('email_to', '')) + tools.email_split(rendered_values.pop('email_cc', '')) for mail in mails: partner_id = self.pool.get('res.partner').find_or_create(cr, uid, mail, context=context) partner_ids.append(partner_id) partner_to = rendered_values.pop('partner_to', '') if partner_to: # placeholders could generate '', 3, 2 due to some empty field values tpl_partner_ids = [pid for pid in partner_to.split(',') if pid] partner_ids += self.pool['res.partner'].exists(cr, SUPERUSER_ID, tpl_partner_ids, context=context) return partner_ids
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)
def send_get_mail_reply_to(self, cr, uid, mail, partner=None, context=None): """ Return a specific ir_email reply_to. :param browse_record mail: mail.mail browse_record :param browse_record partner: specific recipient partner """ if mail.reply_to: return mail.reply_to email_reply_to = False # if model and res_id: try to use ``message_get_reply_to`` that returns the document alias if mail.model and mail.res_id and hasattr(self.pool.get(mail.model), 'message_get_reply_to'): email_reply_to = self.pool.get(mail.model).message_get_reply_to(cr, uid, [mail.res_id], context=context)[0] # no alias reply_to -> reply_to will be the email_from, only the email part if not email_reply_to and mail.email_from: emails = tools.email_split(mail.email_from) if emails: email_reply_to = emails[0] # format 'Document name <email_address>' if email_reply_to and mail.model and mail.res_id: document_name = self.pool.get(mail.model).name_get(cr, SUPERUSER_ID, [mail.res_id], context=context)[0] if document_name: # generate reply to email_reply_to = formataddr((_('Followers of %s') % document_name[1], email_reply_to)) return email_reply_to
def send_get_email_dict(self, cr, uid, mail, partner=None, context=None): """ Return a dictionary for specific email values, depending on a partner, or generic to the whole recipients given by mail.email_to. :param browse_record mail: mail.mail browse_record :param browse_record partner: specific recipient partner """ body = self.send_get_mail_body(cr, uid, mail, partner=partner, context=context) subject = self.send_get_mail_subject(cr, uid, mail, partner=partner, context=context) body_alternative = tools.html2plaintext(body) # generate email_to, heuristic: # 1. if 'partner' is specified and there is a related document: Followers of 'Doc' <email> # 2. if 'partner' is specified, but no related document: Partner Name <email> # 3; fallback on mail.email_to that we split to have an email addresses list if partner and mail.record_name: sanitized_record_name = re.sub(r'[^\w+.]+', '-', mail.record_name) email_to = [_('"Followers of %s" <%s>') % (sanitized_record_name, partner.email)] elif partner: email_to = ['%s <%s>' % (partner.name, partner.email)] else: email_to = tools.email_split(mail.email_to) return { 'body': body, 'body_alternative': body_alternative, 'subject': subject, 'email_to': email_to, }
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: # Convert links in absolute URLs before the application of the shortener self.pool['mail.mass_mailing'].write(cr, uid, [mailing.id], {'body_html': self.pool['mail.template']._replace_local_links(cr, uid, mailing.body_html, context)}, context=context) 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
def _get_reply_to(self, cr, uid, values, context=None): """ Return a specific reply_to: alias of the document through message_get_reply_to or take the email_from """ email_reply_to = None ir_config_parameter = self.pool.get("ir.config_parameter") catchall_domain = ir_config_parameter.get_param(cr, SUPERUSER_ID, "mail.catchall.domain", context=context) # model, res_id, email_from: comes from values OR related message model, res_id, email_from = values.get('model'), values.get('res_id'), values.get('email_from') # if model and res_id: try to use ``message_get_reply_to`` that returns the document alias if not email_reply_to and model and res_id and catchall_domain and hasattr(self.pool[model], 'message_get_reply_to'): email_reply_to = self.pool[model].message_get_reply_to(cr, uid, [res_id], context=context)[0] # no alias reply_to -> catchall alias if not email_reply_to and catchall_domain: catchall_alias = ir_config_parameter.get_param(cr, SUPERUSER_ID, "mail.catchall.alias", context=context) if catchall_alias: email_reply_to = '%s@%s' % (catchall_alias, catchall_domain) # still no reply_to -> reply_to will be the email_from if not email_reply_to and email_from: email_reply_to = email_from # format 'Document name <email_address>' if email_reply_to and model and res_id: emails = tools.email_split(email_reply_to) if emails: email_reply_to = emails[0] document_name = self.pool[model].name_get(cr, SUPERUSER_ID, [res_id], context=context)[0] if document_name: # generate reply to email_reply_to = formataddr((_('Followers of %s') % document_name[1], email_reply_to)) return email_reply_to
def send_mail_test(self): """ Send with Sendgrid if needed. """ self.ensure_one() mailing = self.mass_mailing_id template = mailing.email_template_id if template: # Send with SendGrid (and use E-mail Template) test_emails = tools.email_split(self.email_to) emails = self.env["mail.mail"] for test_mail in test_emails: emails += emails.create( { "email_from": mailing.email_from, "reply_to": mailing.reply_to, "email_to": test_mail, "subject": mailing.name, "body_html": mailing.body_html, "sendgrid_template_id": template.sendgrid_localized_template.id, "notification": True, "mailing_id": mailing.id, "attachment_ids": [(4, attachment.id) for attachment in mailing.attachment_ids], } ) emails.send_sendgrid() mailing.write({"state": "test"}) else: super(TestMassMailing, self).send_mail_test() return True
def send_get_email_dict(self, cr, uid, mail, partner=None, context=None): """ Return a dictionary for specific email values, depending on a partner, or generic to the whole recipients given by mail.email_to. :param browse_record mail: mail.mail browse_record :param browse_record partner: specific recipient partner """ body = self.send_get_mail_body(cr, uid, mail, partner=partner, context=context) subject = self.send_get_mail_subject(cr, uid, mail, partner=partner, context=context) reply_to = self.send_get_mail_reply_to(cr, uid, mail, partner=partner, context=context) body_alternative = tools.html2plaintext(body) # generate email_to, heuristic: # 1. if 'partner' is specified and there is a related document: Followers of 'Doc' <email> # 2. if 'partner' is specified, but no related document: Partner Name <email> # 3; fallback on mail.email_to that we split to have an email addresses list if partner and mail.record_name: email_to = [formataddr((_("Followers of %s") % mail.record_name, partner.email))] elif partner: email_to = [formataddr((partner.name, partner.email))] else: email_to = tools.email_split(mail.email_to) return { "body": body, "body_alternative": body_alternative, "subject": subject, "email_to": email_to, "reply_to": reply_to, }
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.start, tools.DEFAULT_SERVER_DATETIME_FORMAT) + timedelta(hours=event.duration) + timedelta(days=isCreating and 1 or 0), 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") return data
def send_get_email_dict(self, cr, uid, mail, partner=None, context=None): res = super(MailMail, self).send_get_email_dict(cr, uid, mail, partner, context=context) if mail.mailing_id and res.get('body') and res.get('email_to'): email_to = tools.email_split(res.get('email_to')[0]) unsubscribe_url = self._get_unsubscribe_url(cr, uid, mail, email_to, context=context) if unsubscribe_url: res['body'] = tools.append_content_to_html(res['body'], unsubscribe_url, plaintext=False, container_tag='p') return res
def send_get_mail_to(self, cr, uid, mail, partner=None, context=None): """Forge the email_to with the following heuristic: - if 'partner', recipient specific (Partner Name <email>) - else fallback on mail.email_to splitting """ if partner: email_to = [formataddr((partner.name, partner.email))] else: email_to = tools.email_split(mail.email_to) return email_to
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')
def send_get_mail_to(self, cr, uid, mail, partner=None, context=None): """Forge the email_to with the following heuristic: - if 'partner' and mail is a notification on a document: followers (Followers of 'Doc' <email>) - elif 'partner', no notificatoin or no doc: recipient specific (Partner Name <email>) - else fallback on mail.email_to splitting """ if partner: email_to = ['"%s" <%s>' % (partner.name, partner.email)] else: email_to = tools.email_split(mail.email_to) return email_to
def send_get_email_dict(self, cr, uid, mail, partner=None, context=None): res = super(MailMail, self).send_get_email_dict(cr, uid, mail, partner, 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
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
def _get_reply_to(self, cr, uid, values, context=None): """ Return a specific reply_to: alias of the document through message_get_reply_to or take the email_from """ # if value specified: directly return it if values.get('reply_to'): return values.get('reply_to') format_name = True # whether to use a 'Followers of Pigs <*****@*****.**' format email_reply_to = None ir_config_parameter = self.pool.get("ir.config_parameter") catchall_domain = ir_config_parameter.get_param(cr, uid, "mail.catchall.domain", context=context) # model, res_id, email_from: comes from values OR related message model, res_id, email_from = values.get('model'), values.get('res_id'), values.get('email_from') if values.get('mail_message_id'): message = self.pool.get('mail.message').browse(cr, uid, values.get('mail_message_id'), context=context) if message.reply_to: email_reply_to = message.reply_to format_name = False if not model: model = message.model if not res_id: res_id = message.res_id if not email_from: email_from = message.email_from # if model and res_id: try to use ``message_get_reply_to`` that returns the document alias if not email_reply_to and model and res_id and hasattr(self.pool[model], 'message_get_reply_to'): email_reply_to = self.pool[model].message_get_reply_to(cr, uid, [res_id], context=context)[0] # no alias reply_to -> catchall alias if not email_reply_to: catchall_alias = ir_config_parameter.get_param(cr, uid, "mail.catchall.alias", context=context) if catchall_domain and catchall_alias: email_reply_to = '%s@%s' % (catchall_alias, catchall_domain) # still no reply_to -> reply_to will be the email_from if not email_reply_to and email_from: email_reply_to = email_from # format 'Document name <email_address>' if email_reply_to and model and res_id and format_name: emails = tools.email_split(email_reply_to) if emails: email_reply_to = emails[0] document_name = self.pool[model].name_get(cr, SUPERUSER_ID, [res_id], context=context)[0] if document_name: # sanitize document name sanitized_doc_name = re.sub(r'[^\w+.]+', '-', document_name[1]) # generate reply to email_reply_to = _('"Followers of %s" <%s>') % (sanitized_doc_name, email_reply_to) return email_reply_to
def send_get_mail_to(self, cr, uid, mail, partner=None, context=None): """Forge the email_to with the following heuristic: - if 'partner' and mail is a notification on a document: followers (Followers of 'Doc' <email>) - elif 'partner', no notificatoin or no doc: recipient specific (Partner Name <email>) - else fallback on mail.email_to splitting """ if partner and mail.notification and mail.record_name: sanitized_record_name = re.sub(r'[^\w+.]+', '-', mail.record_name) email_to = [_('"Followers of %s" <%s>') % (sanitized_record_name, partner.email)] elif partner: email_to = ['%s <%s>' % (partner.name, partner.email)] else: email_to = tools.email_split(mail.email_to) return email_to
def _get_or_create_partners_from_values(self, cr, uid, rendered_values, context=None): """ Check for email_to, email_cc, partner_to """ partner_ids = [] mails = tools.email_split(rendered_values.pop('email_to', '') + ' ' + rendered_values.pop('email_cc', '')) for mail in mails: partner_id = self.pool.get('res.partner').find_or_create(cr, uid, mail, context=context) partner_ids.append(partner_id) partner_to = rendered_values.pop('partner_to', '') if partner_to: for partner_id in partner_to.split(','): if partner_id: # placeholders could generate '', 3, 2 due to some empty field values partner_ids.append(int(partner_id)) return partner_ids
def _get_or_create_partners_from_values(self, cr, uid, rendered_values, context=None): """ Check for email_to, email_cc, partner_to """ partner_ids = [] mails = tools.email_split(rendered_values.pop( 'email_to', '')) + tools.email_split( rendered_values.pop('email_cc', '')) for mail in mails: partner_id = self.pool.get('res.partner').find_or_create( cr, uid, mail, context=context) partner_ids.append(partner_id) partner_to = rendered_values.pop('partner_to', '') if partner_to: # placeholders could generate '', 3, 2 due to some empty field values tpl_partner_ids = [pid for pid in partner_to.split(',') if pid] partner_ids += self.pool['res.partner'].exists(cr, SUPERUSER_ID, tpl_partner_ids, context=context) return partner_ids
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]
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')
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]
def _lead_create_contact(self, cr, uid, lead, firstname, lastname, is_company, parent_id=False, context=None): partner = self.pool.get('res.partner') vals = {'name': firstname if is_company else '%s %s' % (firstname, lastname), 'user_id': lead.user_id.id, 'comment': lead.description, 'section_id': lead.section_id.id or False, 'parent_id': parent_id, 'phone': lead.phone, 'mobile': lead.mobile, 'email': tools.email_split(lead.email_from) and tools.email_split(lead.email_from)[0] or False, 'fax': lead.fax, 'title': lead.title and lead.title.id or False, 'function': lead.function, 'street': lead.street, 'street2': lead.street2, 'zip': lead.zip, 'city': lead.city, 'country_id': lead.country_id and lead.country_id.id or False, 'state_id': lead.state_id and lead.state_id.id or False, 'is_company': is_company, 'type': 'contact', 'eq_citypart': lead.eq_citypart, 'eq_house_no': lead.eq_house_no, } if not is_company: vals[fistname_column] = firstname vals[surname_column] = lastname vals['use_parent_address']= True partner = partner.create(cr, uid, vals, context=context) partner_pool = self.pool.get('res.partner') partner = partner_pool.browse(cr, uid, partner, context=context) partner.write({'category_id':[(6, 0, [x.id for x in lead.category_ids])], birthday_column:lead.birthdate, #'messaging':lead.messaging, 'website':lead.website, }) return partner.id
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
def send_get_mail_to(self, cr, uid, mail, partner=None, context=None): """Forge the email_to with the following heuristic: - if 'partner' and mail is a notification on a document: followers (Followers of 'Doc' <email>) - elif 'partner', no notificatoin or no doc: recipient specific (Partner Name <email>) - else fallback on mail.email_to splitting """ if partner and mail.notification and mail.record_name: email_to = [ formataddr( (_('Followers of %s') % mail.record_name, partner.email)) ] elif partner: email_to = [formataddr((partner.name, partner.email))] else: email_to = tools.email_split(mail.email_to) return email_to
def _lead_create_contact(self, cr, uid, lead, name, is_company, parent_id=False, context=None): partner = self.pool.get('res.partner') vals = {'name': name, 'user_id': lead.user_id.id, 'comment': lead.description, 'section_id': lead.section_id.id or False, 'parent_id': parent_id, 'phone': lead.phone, 'mobile': lead.mobile, 'email': tools.email_split(lead.email_from) and tools.email_split(lead.email_from)[0] or False, 'fax': lead.fax, 'title': lead.title and lead.title.id or False, 'function': lead.function, 'street': lead.street, 'street2': lead.street2, 'zip': lead.zip, 'city': lead.city, 'country_id': lead.country_id and lead.country_id.id or False, 'state_id': lead.state_id and lead.state_id.id or False, 'is_company': is_company, 'type': 'contact' } partner = partner.create(cr, uid, vals, context=context) return partner
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 base_url.endswith('/'): base_url = base_url.rstrip('/') 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
def _get_duplicated_leads(self, cr, uid, partner_id, email, context=None): """ Search for opportunities that have the same partner and that arent done or cancelled """ lead_obj = self.pool.get('crm.lead') emails = set(email_split(email) + [email]) final_stage_domain = [('stage_id.probability', '<', 100), '|', ('stage_id.probability', '>', 0), ('stage_id.sequence', '<=', 1)] partner_match_domain = [] for email in emails: partner_match_domain.append(('email_from', '=ilike', email)) if partner_id: partner_match_domain.append(('partner_id', '=', partner_id)) partner_match_domain = ['|'] * (len(partner_match_domain) - 1) + partner_match_domain if not partner_match_domain: return [] return lead_obj.search(cr, uid, partner_match_domain + final_stage_domain)
def send_get_mail_to(self, cr, uid, mail, partner=None, context=None): """Forge the email_to with the following heuristic: - if 'partner' and mail is a notification on a document: followers (Followers of 'Doc' <email>) - elif 'partner', no notificatoin or no doc: recipient specific (Partner Name <email>) - else fallback on mail.email_to splitting """ if partner and mail.notification and mail.record_name: sanitized_record_name = re.sub(r'[^\w+.]+', '-', mail.record_name) email_to = [ _('"Followers of %s" <%s>') % (sanitized_record_name, partner.email) ] elif partner: email_to = ['%s <%s>' % (partner.name, partner.email)] else: email_to = tools.email_split(mail.email_to) return email_to
def send_get_email_dict(self, cr, uid, mail, partner=None, context=None): """ Return a dictionary for specific email values, depending on a partner, or generic to the whole recipients given by mail.email_to. :param browse_record mail: mail.mail browse_record :param browse_record partner: specific recipient partner """ body = self.send_get_mail_body(cr, uid, mail, partner=partner, context=context) subject = self.send_get_mail_subject(cr, uid, mail, partner=partner, context=context) reply_to = self.send_get_mail_reply_to(cr, uid, mail, partner=partner, context=context) body_alternative = tools.html2plaintext(body) # generate email_to, heuristic: # 1. if 'partner' is specified and there is a related document: Followers of 'Doc' <email> # 2. if 'partner' is specified, but no related document: Partner Name <email> # 3; fallback on mail.email_to that we split to have an email addresses list if partner and mail.record_name: sanitized_record_name = re.sub(r'[^\w+.]+', '-', mail.record_name) email_to = [ _('"Followers of %s" <%s>') % (sanitized_record_name, partner.email) ] elif partner: email_to = ['%s <%s>' % (partner.name, partner.email)] else: email_to = tools.email_split(mail.email_to) return { 'body': body, 'body_alternative': body_alternative, 'subject': subject, 'email_to': email_to, 'reply_to': reply_to, }
def message_new(self, cr, uid, msg, custom_values=None, context=None): """ Override to updates the document according to the email. """ if custom_values is None: custom_values = {} defaults = { 'name': msg.get('subject'), 'planned_hours': 0.0, 'partner_id': msg.get('author_id', False) } defaults.update(custom_values) res = super(task, self).message_new(cr, uid, msg, custom_values=defaults, context=context) email_list = tools.email_split((msg.get('to') or '') + ',' + (msg.get('cc') or '')) new_task = self.browse(cr, uid, res, context=context) if new_task.project_id and new_task.project_id.alias_name: # check left-part is not already an alias email_list = filter(lambda x: x.split('@')[0] != new_task.project_id.alias_name, email_list) partner_ids = filter(lambda x: x, self._find_partner_from_emails(cr, uid, email_list, check_followers=False)) self.message_subscribe(cr, uid, [res], partner_ids, context=context) return res
def _get_reply_to(self, cr, uid, values, context=None): """ Return a specific reply_to: alias of the document through message_get_reply_to or take the email_from """ if values.get('reply_to'): return values.get('reply_to') email_reply_to = False # model, res_id: comes from values OR related message model = values.get('model') res_id = values.get('res_id') if values.get('mail_message_id') and (not model or not res_id): message = self.pool.get('mail.message').browse( cr, uid, values.get('mail_message_id'), context=context) if not model: model = message.model if not res_id: res_id = message.res_id # if model and res_id: try to use ``message_get_reply_to`` that returns the document alias if model and res_id and hasattr(self.pool.get(model), 'message_get_reply_to'): email_reply_to = self.pool.get(model).message_get_reply_to( cr, uid, [res_id], context=context)[0] # no alias reply_to -> reply_to will be the email_from, only the email part if not email_reply_to and values.get('email_from'): emails = tools.email_split(values.get('email_from')) if emails: email_reply_to = emails[0] # format 'Document name <email_address>' if email_reply_to and model and res_id: document_name = self.pool.get(model).name_get(cr, SUPERUSER_ID, [res_id], context=context)[0] if document_name: # sanitize document name sanitized_doc_name = re.sub(r'[^\w+.]+', '-', document_name[1]) # generate reply to email_reply_to = _('"Followers of %s" <%s>') % ( sanitized_doc_name, email_reply_to) return email_reply_to
def _get_duplicated_leads(self, cr, uid, partner_id, email, include_lost=False, context=None): """ Search for opportunities that have the same partner and that arent done or cancelled """ lead_obj = self.pool.get('crm.lead') emails = set(email_split(email) + [email]) final_stage_domain = [('stage_id.probability', '<', 100), '|', ('stage_id.probability', '>', 0), ('stage_id.sequence', '<=', 1)] partner_match_domain = [] for email in emails: partner_match_domain.append(('email_from', '=ilike', email)) if partner_id: partner_match_domain.append(('partner_id', '=', partner_id)) partner_match_domain = ['|'] * (len(partner_match_domain) - 1) + partner_match_domain if not partner_match_domain: return [] domain = partner_match_domain if not include_lost: domain += final_stage_domain return lead_obj.search(cr, uid, domain)
def send_get_email_dict(self, cr, uid, mail, partner=None, context=None): """ Return a dictionary for specific email values, depending on a partner, or generic to the whole recipients given by mail.email_to. :param browse_record mail: mail.mail browse_record :param browse_record partner: specific recipient partner """ body = self.send_get_mail_body(cr, uid, mail, partner=partner, context=context) subject = self.send_get_mail_subject(cr, uid, mail, partner=partner, context=context) reply_to = self.send_get_mail_reply_to(cr, uid, mail, partner=partner, context=context) body_alternative = tools.html2plaintext(body) email_to = [partner.email] if partner else tools.email_split(mail.email_to) return { 'body': body, 'body_alternative': body_alternative, 'subject': subject, 'email_to': email_to, 'reply_to': reply_to, }
def default_get(self, fields_list): res = super(wizard, self).default_get(fields_list) model_fields = self.fields_get() if model_fields['model']['selection']: res['model'] = model_fields['model']['selection'] and model_fields[ 'model']['selection'][0][0] if 'message_id' in res: message = self.env['mail.message'].browse(res['message_id']) email_from = message.email_from parts = email_split(email_from.replace(' ', ',')) if parts: email = parts[0] name = email_from.find( email ) != -1 and email_from[:email_from.index(email)].replace( '"', '').replace('<', '').strip() or email_from else: name, email = email_from res['message_name_from'] = name res['message_email_from'] = email res['partner_id'] = message.author_id.id if message.author_id and self.env.uid not in [ u.id for u in message.author_id.user_ids ]: res['filter_by_partner'] = True if message.author_id and res.get('model'): res_id = self.env[res['model']].search([], order='id desc', limit=1) if res_id: res['res_id'] = res_id[0].id config_parameters = self.env['ir.config_parameter'] res['move_followers'] = config_parameters.get_param( 'mail_relocation_move_followers') res['uid'] = self.env.uid return res
def send_get_mail_to(self, cr, uid, mail, partner=None, context=None): """Forge the email_to with the following heuristic: - if 'partner', recipient specific (Partner Name <email>) - else fallback on mail.email_to splitting """ res = super(mail_mail, self).send_get_mail_to(cr, uid, mail=mail, partner=partner, context=context) if partner: if context.get('parent_model_name', False) and context.get( 'parent_model_name') == 'sale.order.line.images': if context.get('artwork_image_contact_id', False): artwork_partner_id = self.pool.get('res.partner').browse( cr, uid, context.get('artwork_image_contact_id')) if partner.id == artwork_partner_id.id and artwork_partner_id.order_proof_email: email_to = [ formataddr((artwork_partner_id.name, artwork_partner_id.order_proof_email)) ] else: email_to = [formataddr((partner.name, partner.email))] else: if context.get('artwork_partner_id', False): artwork_partner_id = self.pool.get( 'res.partner').browse( cr, uid, context.get('artwork_partner_id')) if partner.id == artwork_partner_id.id and artwork_partner_id.order_proof_email: email_to = [ formataddr( (artwork_partner_id.name, artwork_partner_id.order_proof_email)) ] else: email_to = [ formataddr((partner.name, partner.email)) ] else: email_to = [formataddr((partner.name, partner.email))] else: email_to = tools.email_split(mail.email_to) return email_to
def send_get_mail_to(self, cr, uid, mail, partner=None, context=None): """Forge the email_to with the following heuristic: - if 'partner', recipient specific (Partner Name <email>) - else fallback on mail.email_to splitting """ if partner: #print('----------------- xxxxxxxxxxxxxxx---------------') #email_to = [formataddr((partner.name, partner.email))] if mail.mailing_id: destinos = partner.get_mass_emails() else: destinos = partner.get_all_emails() email_to = [] for destino in destinos: email_to = email_to + [formataddr((partner.name, destino))] #print(email_to) #print('----------------- xxxxxxxxxxxxxxx---------------') else: email_to = tools.email_split(mail.email_to) #email_to = super(mail_mail_extension, self).send_get_mail_to(self, cr, uid, mail, partner=None, context=None) return email_to
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': mailing.body_html, 'auto_delete': True, 'mailing_id': wizard.mass_mailing_id.id, } mail_ids.append(Mail.create(cr, uid, mail_values, context=context)) Mail.send(cr, uid, mail_ids, context=context) self.pool['mail.mass_mailing'].write(cr, uid, [mailing.id], {'state': 'test'}, context=context) return True
def _get_or_create_partners_from_values(self, cr, uid, rendered_values, context=None): """ Check for email_to, email_cc, partner_to """ partner_ids = [] mails = tools.email_split( rendered_values.pop('email_to', '') + ' ' + rendered_values.pop('email_cc', '')) for mail in mails: partner_id = self.pool.get('res.partner').find_or_create( cr, uid, mail, context=context) partner_ids.append(partner_id) partner_to = rendered_values.pop('partner_to', '') if partner_to: for partner_id in partner_to.split(','): if partner_id: # placeholders could generate '', 3, 2 due to some empty field values partner_ids.append(int(partner_id)) return partner_ids
def generate_email_for_composer(self, cr, uid, template_id, res_id, context=None): """ Call email_template.generate_email(), get fields relevant for mail.compose.message, transform email_cc and email_to into partner_ids """ template_values = self.pool.get('email.template').generate_email( cr, uid, template_id, res_id, context=context) # filter template values fields = [ 'body_html', 'subject', 'email_to', 'email_recipients', 'email_cc', 'attachment_ids', 'attachments' ] values = dict((field, template_values[field]) for field in fields if template_values.get(field)) values['body'] = values.pop('body_html', '') # transform email_to, email_cc into partner_ids partner_ids = set() mails = tools.email_split( values.pop('email_to', '') + ' ' + values.pop('email_cc', '')) ctx = dict((k, v) for k, v in (context or {}).items() if not k.startswith('default_')) for mail in mails: partner_id = self.pool.get('res.partner').find_or_create( cr, uid, mail, context=ctx) partner_ids.add(partner_id) email_recipients = values.pop('email_recipients', '') if email_recipients: for partner_id in email_recipients.split(','): if partner_id: # placeholders could generate '', 3, 2 due to some empty field values partner_ids.add(int(partner_id)) # legacy template behavior: void values do not erase existing values and the # related key is removed from the values dict if partner_ids: values['partner_ids'] = list(partner_ids) return values
def send_get_email_dict(self, cr, uid, mail, partner=None, context=None): res = super(MailMail, self).send_get_email_dict(cr, uid, mail, partner, 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
def send_get_mail_reply_to(self, cr, uid, mail, partner=None, context=None): """ Return a specific ir_email reply_to. :param browse_record mail: mail.mail browse_record :param browse_record partner: specific recipient partner """ if mail.reply_to: return mail.reply_to email_reply_to = False # if model and res_id: try to use ``message_get_reply_to`` that returns the document alias if mail.model and mail.res_id and hasattr(self.pool.get(mail.model), 'message_get_reply_to'): email_reply_to = self.pool.get(mail.model).message_get_reply_to( cr, uid, [mail.res_id], context=context)[0] # no alias reply_to -> reply_to will be the email_from, only the email part if not email_reply_to and mail.email_from: emails = tools.email_split(mail.email_from) if emails: email_reply_to = emails[0] # format 'Document name <email_address>' if email_reply_to and mail.model and mail.res_id: document_name = self.pool.get(mail.model).name_get( cr, SUPERUSER_ID, [mail.res_id], context=context)[0] if document_name: # sanitize document name sanitized_doc_name = re.sub(r'[^\w+.]+', '-', document_name[1]) # generate reply to email_reply_to = _('"Followers of %s" <%s>') % ( sanitized_doc_name, email_reply_to) return email_reply_to
def _get_reply_to(self, cr, uid, values, context=None): """ Return a specific reply_to: alias of the document through message_get_reply_to or take the email_from """ email_reply_to = None ir_config_parameter = self.pool.get("ir.config_parameter") catchall_domain = ir_config_parameter.get_param(cr, uid, "mail.catchall.domain", context=context) # model, res_id, email_from: comes from values OR related message model, res_id, email_from = values.get('model'), values.get('res_id'), values.get('email_from') # if model and res_id: try to use ``message_get_reply_to`` that returns the document alias if not email_reply_to and model and res_id and catchall_domain and hasattr(self.pool[model], 'message_get_reply_to'): email_reply_to = self.pool[model].message_get_reply_to(cr, uid, [res_id], context=context)[0] # no alias reply_to -> catchall alias if not email_reply_to and catchall_domain: catchall_alias = ir_config_parameter.get_param(cr, uid, "mail.catchall.alias", context=context) if catchall_alias: email_reply_to = '%s@%s' % (catchall_alias, catchall_domain) # still no reply_to -> reply_to will be the email_from if not email_reply_to and email_from: email_reply_to = email_from # format 'Document name <email_address>' if email_reply_to and model and res_id: emails = tools.email_split(email_reply_to) if emails: email_reply_to = emails[0] document_name = self.pool[model].name_get(cr, SUPERUSER_ID, [res_id], context=context)[0] if document_name: # sanitize document name sanitized_doc_name = re.sub(r'[^\w+.]+', '-', document_name[1]) # generate reply to email_reply_to = _('"Followers of %s" <%s>') % (sanitized_doc_name, email_reply_to) return email_reply_to
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 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 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
def _lead_create_contact(self, cr, uid, lead, name, is_company, parent_id=False, context=None): partner = self.pool.get('res.partner') vals = {'name': name, 'user_id': lead.user_id.id, 'comment': lead.description, 'section_id': lead.section_id.id or False, 'parent_id': parent_id, 'phone': lead.phone, 'mobile': lead.mobile, 'email': tools.email_split(lead.email_from) and tools.email_split(lead.email_from)[0] or False, 'fax': lead.fax, 'title': lead.title and lead.title.id or False, 'function': lead.function, 'street': lead.street, 'street2': lead.street2, 'zip': lead.zip, 'city': lead.city, 'country_id': lead.country_id and lead.country_id.id or False, 'state_id': lead.state_id and lead.state_id.id or False, 'is_company': is_company, 'type': 'contact', #Student Educational information 'student_id': lead.student_id, 'student_mother_tongue': lead.student_mother_tongue, 'student_first_foreign_language': lead.student_first_foreign_language, 'student_second_foreign_language': lead.student_second_foreign_language, #student personal inforamtion 'student_first_name': lead.student_first_name, 'student_middle_name': lead.student_middle_name, 'student_surname': lead.student_surname, 'student_arabic_full_name': lead.student_arabic_full_name, 'student_birth_date': lead.student_birth_date, 'student_place_of_birth': lead.student_place_of_birth, 'student_gender': lead.student_gender, 'student_nationality': lead.student_nationality.id, 'student_religion': lead.student_religion, 'student_national_id': lead.student_national_id, 'student_birth_certificate_no': lead.student_birth_certificate_no, 'student_issue_date': lead.student_issue_date, #father inforamtion 'father_full_name': lead.father_full_name, 'father_nationality': lead.father_nationality.id, 'father_religion': lead.father_religion, 'father_acadimic': lead.father_acadimic, 'father_mob_no': lead.father_mob_no, 'father_mob_no2': lead.father_mob_no2, 'father_email': lead.father_email, 'father_home_address': lead.father_home_address, 'father_home_tel': lead.father_home_tel, 'father_job': lead.father_job, 'father_employer_address': lead.father_employer_address, 'father_occupation_phone': lead.father_occupation_phone, 'father_status': lead.father_status, 'father_id_type': lead.father_id_type, 'father_id_no': lead.father_id_no, #mother inforamtion 'mother_full_name': lead.mother_full_name, 'mother_nationality': lead.mother_nationality.id, 'mother_religion': lead.mother_religion, 'mother_acadimic': lead.mother_acadimic, 'mother_mob_no': lead.mother_mob_no, 'mother_mob_no2': lead.mother_mob_no2, 'mother_email': lead.mother_email, 'mother_home_address': lead.mother_home_address, 'mother_home_tel': lead.mother_home_tel, 'mother_job': lead.mother_job, 'mother_employer_address': lead.mother_employer_address, 'mother_occupation_phone': lead.mother_occupation_phone, 'mother_status': lead.mother_status, 'mother_id_type': lead.mother_id_type, 'mother_id_no': lead.mother_id_no, #gardian details 'first_guardian': lead.first_guardian, 'fg_full_name': lead.fg_full_name, 'fg_relationship': lead.fg_relationship, 'fg_relative': lead.fg_relative, 'fg_job': lead.fg_job, 'fg_address': lead.fg_address, 'fg_tel': lead.fg_tel, 'fg_mob': lead.fg_mob, 'fg_work_tel': lead.fg_work_tel, 'second_guardian': lead.second_guardian, 'sg_full_name': lead.sg_full_name, 'sg_relationship': lead.sg_relationship, 'sg_relative': lead.sg_relative, 'sg_job': lead.sg_job, 'sg_address': lead.sg_address, 'sg_tel': lead.sg_tel, 'sg_mob': lead.sg_mob, 'sg_work_tel': lead.sg_work_tel, #enrollment data 'applicant_date': lead.applicant_date, 'registeration_date': lead.registeration_date, 'registeration_type': lead.registeration_type, 'grade_level': lead.grade_level, 'student_grade': lead.student_grade.id, 'join_bus': lead.join_bus, 'bus_status': lead.bus_status, 'bus_route': lead.bus_route.id, 'enrollment_date': lead.enrollment_date, 'id_code': lead.id_code, 'left_date': lead.left_date, 'prev_school': lead.prev_school, 'reason_of_transfer': lead.reason_of_transfer, #health info 'health_info': lead.health_info, #make them as lead 'lead' : True, 'customer' : False, 'parent' : context['parent'], 'student' : context['student'] } partner = partner.create(cr, uid, vals, context=context) return partner
def generate_recipients_batch(self, cr, uid, results, template_id, res_ids, context=None): """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. """ if context is None: context = {} template = self.browse(cr, uid, template_id, context=context) if template.use_default_to or context.get('tpl_force_default_to'): ctx = dict(context, thread_model=template.model) default_recipients = self.pool[ 'mail.thread'].message_get_default_recipients(cr, uid, res_ids, context=ctx) 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()) email_to = values.get('email_to', str()) if context and 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.pool.get('res.partner').find_or_create( cr, uid, mail, context=context) 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.pool['res.partner'].exists(cr, SUPERUSER_ID, tpl_partner_ids, context=context) user_ids = template.user_ids if user_ids: for user in user_ids: partner_ids += [int(user.partner_id.id)] email_to += str('%s,' % (user.partner_id.email)) email_to = email_to[:-1] department_ids = template.department_ids if department_ids: for department in department_ids: for hr in department.member_ids: partner_ids += [int(hr.user_id.partner_id.id)] email_to += str('%s,' % (hr.user_id.partner_id.email)) email_to = email_to[:-1] results[res_id]['partner_ids'] = partner_ids results[res_id]['email_to'] = email_to return results
def send(self, cr, uid, ids, auto_commit=False, raise_exception=False, context=None): """ 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 """ context = dict(context or {}) ir_mail_server = self.pool.get('ir.mail_server') ir_attachment = self.pool['ir.attachment'] for mail in self.browse(cr, SUPERUSER_ID, ids, context=context): 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_id = self.pool['ir.model'].search(cr, SUPERUSER_ID, [('model', '=', mail.model)], context=context)[0] model = self.pool['ir.model'].browse(cr, SUPERUSER_ID, model_id, context=context) else: model = None if model: 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. attachment_ids = [a.id for a in mail.attachment_ids] attachments = [(a['datas_fname'], base64.b64decode(a['datas'])) for a in ir_attachment.read(cr, SUPERUSER_ID, attachment_ids, ['datas_fname', 'datas'])] # specific behavior to customize the send email for notified partners email_list = [] if mail.email_to: email_list.append(self.send_get_email_dict(cr, uid, mail, context=context)) for partner in mail.recipient_ids: email_list.append(self.send_get_email_dict(cr, uid, mail, partner=partner, context=context)) # headers headers = {} bounce_alias = self.pool['ir.config_parameter'].get_param(cr, uid, "mail.bounce.alias", context=context) catchall_domain = self.pool['ir.config_parameter'].get_param(cr, uid, "mail.catchall.domain", context=context) if bounce_alias and catchall_domain: headers['Return-Path'] = '%s@%s' % (bounce_alias, catchall_domain) else: headers['Return-Path'] = mail.email_from #=========================================================== # 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'}) mail_sent = False # build an RFC2822 email.message.Message object and send it without queuing res = None for email in email_list: msg = ir_mail_server.build_email( email_from=mail.email_from, email_to=email.get('email_to'), subject=email.get('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) #msg['Return-Path'] = mail.email_from try: res = ir_mail_server.send_email(cr, uid, msg, mail_server_id=mail.mail_server_id.id, context=context) except AssertionError as error: if error.message == ir_mail_server.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.warning("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}) 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) self._postprocess_sent_message(cr, uid, mail, context=context, 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: _logger.exception('failed sending mail.mail %s', mail.id) mail.write({'state': 'exception'}) self._postprocess_sent_message(cr, uid, mail, context=context, 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: cr.commit() return True
def send(self, cr, uid, ids, auto_commit=False, recipient_ids=None, context=None): """ 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 list recipient_ids: specific list of res.partner recipients. If set, one email is sent to each partner. Its is possible to tune the sent email through ``send_get_mail_body`` and ``send_get_mail_subject``. If not specified, one email is sent to mail_mail.email_to. :return: True """ ir_mail_server = self.pool.get('ir.mail_server') for mail in self.browse(cr, uid, ids, context=context): try: # handle attachments attachments = [] for attach in mail.attachment_ids: attachments.append((attach.datas_fname, base64.b64decode(attach.datas))) # specific behavior to customize the send email for notified partners email_list = [] if recipient_ids: for partner in self.pool.get('res.partner').browse(cr, SUPERUSER_ID, recipient_ids, context=context): email_list.append(self.send_get_email_dict(cr, uid, mail, partner=partner, context=context)) else: email_list.append(self.send_get_email_dict(cr, uid, mail, context=context)) # build an RFC2822 email.message.Message object and send it without queuing for email in email_list: msg = ir_mail_server.build_email( email_from = mail.email_from, email_to = email.get('email_to'), subject = email.get('subject'), body = email.get('body'), body_alternative = email.get('body_alternative'), email_cc = tools.email_split(mail.email_cc), reply_to = email.get('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') res = ir_mail_server.send_email(cr, uid, msg, mail_server_id=mail.mail_server_id.id, context=context) if res: mail.write({'state': 'sent', 'message_id': res}) mail_sent = True else: mail.write({'state': 'exception'}) mail_sent = False # /!\ can't use mail.state here, as mail.refresh() will cause an error # see revid:[email protected] in 6.1 if mail_sent: self._postprocess_sent_message(cr, uid, mail, context=context) except Exception: _logger.exception('failed sending mail.mail %s', mail.id) mail.write({'state': 'exception'}) if auto_commit == True: cr.commit() return True
def send(self, cr, uid, ids, auto_commit=False, raise_exception=False, context=None): """ 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 """ ir_mail_server = self.pool.get('ir.mail_server') for mail in self.browse(cr, SUPERUSER_ID, ids, context=context): try: # handle attachments attachments = [] for attach in mail.attachment_ids: attachments.append( (attach.datas_fname, base64.b64decode(attach.datas))) # specific behavior to customize the send email for notified partners email_list = [] if mail.email_to: email_list.append( self.send_get_email_dict(cr, uid, mail, context=context)) for partner in mail.recipient_ids: email_list.append( self.send_get_email_dict(cr, uid, mail, partner=partner, context=context)) # headers headers = {} bounce_alias = self.pool['ir.config_parameter'].get_param( cr, uid, "mail.bounce.alias", context=context) catchall_domain = self.pool['ir.config_parameter'].get_param( cr, uid, "mail.catchall.domain", context=context) 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) # build an RFC2822 email.message.Message object and send it without queuing res = None for email in email_list: msg = ir_mail_server.build_email( email_from=mail.email_from, email_to=email.get('email_to'), subject=email.get('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) res = ir_mail_server.send_email( cr, uid, msg, mail_server_id=mail.mail_server_id.id, context=context) if res: mail.write({'state': 'sent', 'message_id': res}) mail_sent = True else: mail.write({'state': 'exception'}) mail_sent = False # /!\ can't use mail.state here, as mail.refresh() will cause an error # see revid:[email protected] in 6.1 if mail_sent: self._postprocess_sent_message(cr, uid, mail, context=context) except Exception as e: _logger.exception('failed sending mail.mail %s', mail.id) mail.write({'state': 'exception'}) 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 == True: cr.commit() return True