diff options
| author | Thibault Delavallée <tde@openerp.com> | 2015-04-27 11:49:02 +0200 |
|---|---|---|
| committer | Thibault Delavallée <tde@openerp.com> | 2015-04-27 12:52:45 +0200 |
| commit | 4a975f1b7d719ebbf17e0d4194cf3fcc56e2f56f (patch) | |
| tree | 96a29b7f19adabe5f371b023116da887bb0ace03 /addons/mail/mail_mail.py | |
| parent | 4b122ad41d873febe288ab1f8c6cdc078ca2a479 (diff) | |
[MOV] mail: moved files according to the new module organization
Diffstat (limited to 'addons/mail/mail_mail.py')
| -rw-r--r-- | addons/mail/mail_mail.py | 337 |
1 files changed, 0 insertions, 337 deletions
diff --git a/addons/mail/mail_mail.py b/addons/mail/mail_mail.py deleted file mode 100644 index c7576252f3f..00000000000 --- a/addons/mail/mail_mail.py +++ /dev/null @@ -1,337 +0,0 @@ -# -*- coding: utf-8 -*- - -import base64 -import logging -from email.utils import formataddr -from urlparse import urljoin - -from openerp import _, api, fields, models -from openerp import tools -from openerp.addons.base.ir.ir_mail_server import MailDeliveryException -from openerp.tools.safe_eval import safe_eval as eval - -_logger = logging.getLogger(__name__) - - -class MailMail(models.Model): - """ Model holding RFC2822 email messages to send. This model also provides - facilities to queue and send new email messages. """ - _name = 'mail.mail' - _description = 'Outgoing Mails' - _inherits = {'mail.message': 'mail_message_id'} - _order = 'id desc' - _rec_name = 'subject' - - mail_message_id = fields.Many2one('mail.message', 'Message', required=True, ondelete='cascade', auto_join=True) - state = fields.Selection([ - ('outgoing', 'Outgoing'), - ('sent', 'Sent'), - ('received', 'Received'), - ('exception', 'Delivery Failed'), - ('cancel', 'Cancelled'), - ], 'Status', readonly=True, copy=False, default='outgoing') - auto_delete = fields.Boolean('Auto Delete', help="Permanently delete this email after sending it, to save space") - references = fields.Text('References', help='Message references, such as identifiers of previous messages', readonly=1) - email_to = fields.Text('To', help='Message recipients (emails)') - recipient_ids = fields.Many2many('res.partner', string='To (Partners)') - email_cc = fields.Char('Cc', help='Carbon copy message recipients') - body_html = fields.Text('Rich-text Contents', help="Rich-text/HTML message") - headers = fields.Text('Headers', copy=False) - failure_reason = fields.Text('Failure Reason', help="Failure reason. This is usually the exception thrown by the email server, stored to ease the debugging of mailing issues.", readonly=1) - # Auto-detected based on create() - if 'mail_message_id' was passed then this mail is a notification - # and during unlink() we will not cascade delete the parent and its attachments - notification = fields.Boolean('Is Notification', help='Mail has been created to notify people of an existing mail.message') - - @api.model - def create(self, values): - # notification field: if not set, set if mail comes from an existing mail.message - if 'notification' not in values and values.get('mail_message_id'): - values['notification'] = True - return super(MailMail, self).create(values) - - @api.multi - def unlink(self): - # cascade-delete the parent message for all mails that are not created for a notification - to_cascade = self.search([('notification', '=', False), ('id', 'in', self.ids)]).mapped('mail_message_id') - res = super(MailMail, self).unlink() - to_cascade.unlink() - return res - - @api.model - def default_get(self, fields): - # protection for `default_type` values leaking from menu action context (e.g. for invoices) - # To remove when automatic context propagation is removed in web client - if self._context.get('default_type') not in self._all_columns['message_type'].column.selection: - self = self.with_context(dict(self._context, default_type=None)) - return super(MailMail, self).default_get(fields) - - @api.multi - def mark_outgoing(self): - return self.write({'state': 'outgoing'}) - - @api.multi - def cancel(self): - return self.write({'state': 'cancel'}) - - @api.model - def process_email_queue(self, ids=None): - """Send immediately queued messages, committing after each - message is sent - this is not transactional and should - not be called during another transaction! - - :param list ids: optional list of emails ids to send. If passed - no search is performed, and these ids are used - instead. - :param dict context: if a 'filters' key is present in context, - this value will be used as an additional - filter to further restrict the outgoing - messages to send (by default all 'outgoing' - messages are sent). - """ - if not self.ids: - filters = [('state', '=', 'outgoing')] - if 'filters' in self._context: - filters.extend(self._context['filters']) - ids = self.search(filters).ids - res = None - try: - # Force auto-commit - this is meant to be called by - # the scheduler, and we can't allow rolling back the status - # of previously sent emails! - res = self.browse(ids).send(auto_commit=True) - except Exception: - _logger.exception("Failed processing mail queue") - return res - - @api.cr_uid_context - def _postprocess_sent_message(self, cr, uid, mail, context=None, mail_sent=True): - return True - - @api.multi - def _postprocess_sent_message_v9(self, mail_sent=True): - """Perform any post-processing necessary after sending ``mail`` - successfully, including deleting it completely along with its - attachment if the ``auto_delete`` flag of the mail was set. - Overridden by subclasses for extra post-processing behaviors. - - :param browse_record mail: the mail that was just sent - :return: True - """ - # Compat mode until v9 - for mail in self: - self._postprocess_sent_message(mail, mail_sent=mail_sent) - - if mail_sent: - self.sudo().filtered(lambda self: self.auto_delete).unlink() - return True - - # ------------------------------------------------------ - # mail_mail formatting, tools and send mechanism - # ------------------------------------------------------ - - @api.multi - def _get_partner_access_link(self, partner=None): - """Generate URLs for links in mails: partner has access (is user): - link to action_mail_redirect action that will redirect to doc or Inbox """ - self.ensure_one() - if partner and partner.user_ids: - base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url') - mail_model = self.model or 'mail.thread' - if not hasattr(self.env[mail_model], '_get_access_link'): - return None - url = urljoin(base_url, self.env[mail_model]._get_access_link(self, partner)) - return "<span class='oe_mail_footer_access'><small>%(access_msg)s <a style='color:inherit' href='%(portal_link)s'>%(portal_msg)s</a></small></span>" % { - 'access_msg': _('about') if self.record_name else _('access'), - 'portal_link': url, - 'portal_msg': '%s %s' % (self._context.get('model_name', ''), self.record_name) if self.record_name else _('your messages'), - } - else: - return None - - @api.multi - def send_get_mail_subject(self, force=False, partner=None): - """If subject is void, set the subject as 'Re: <Resource>' or - 'Re: <mail.parent_id.subject>' - - :param boolean force: force the subject replacement - """ - self.ensure_one() - if (force or not self.subject) and self.record_name: - return 'Re: %s' % (self.record_name) - elif (force or not self.subject) and self.parent_id and self.parent_id.subject: - return 'Re: %s' % (self.parent_id.subject) - return self.subject - - @api.multi - def send_get_mail_body(self, partner=None): - """Return a specific ir_email body. The main purpose of this method - is to be inherited to add custom content depending on some module.""" - self.ensure_one() - body = self.body_html or '' - - # generate access links for notifications or emails linked to a specific document with auto threading - link = None - if self.notification or (self.model and self.res_id and not self.no_auto_thread): - link = self._get_partner_access_link(partner) - if link: - body = tools.append_content_to_html(body, link, plaintext=False, container_tag='div') - return body - - @api.multi - def send_get_mail_to(self, partner=None): - """Forge the email_to with the following heuristic: - - if 'partner', recipient specific (Partner Name <email>) - - else fallback on mail.email_to splitting """ - self.ensure_one() - if partner: - email_to = [formataddr((partner.name, partner.email))] - else: - email_to = tools.email_split(self.email_to) - return email_to - - @api.multi - def send_get_email_dict(self, partner=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 - """ - self.ensure_one() - body = self.send_get_mail_body(partner=partner) - body_alternative = tools.html2plaintext(body) - res = { - 'body': body, - 'body_alternative': body_alternative, - 'subject': self.send_get_mail_subject(partner=partner), - 'email_to': self.send_get_mail_to(partner=partner), - } - return res - - @api.multi - 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=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) - 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:odo@openerp.com-20120622152536-42b2s28lvdv3odyr 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 |
