2012-08-22 07:45:45 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
##############################################################################
|
|
|
|
#
|
|
|
|
# OpenERP, Open Source Management Solution
|
|
|
|
# Copyright (C) 2010-today OpenERP SA (<http://www.openerp.com>)
|
|
|
|
#
|
|
|
|
# This program is free software: you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU Affero General Public License as
|
|
|
|
# published by the Free Software Foundation, either version 3 of the
|
|
|
|
# License, or (at your option) any later version
|
|
|
|
#
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU Affero General Public License for more details
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
|
|
#
|
|
|
|
##############################################################################
|
|
|
|
|
2012-08-24 16:09:36 +00:00
|
|
|
# import ast
|
2012-08-15 18:44:03 +00:00
|
|
|
import base64
|
|
|
|
import logging
|
2012-08-22 07:45:45 +00:00
|
|
|
import tools
|
2012-08-15 18:44:03 +00:00
|
|
|
|
|
|
|
from osv import osv
|
|
|
|
from osv import fields
|
2012-08-29 15:00:02 +00:00
|
|
|
from tools.translate import _
|
2012-08-15 18:44:03 +00:00
|
|
|
|
2012-08-22 01:09:47 +00:00
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
|
2012-08-15 17:08:22 +00:00
|
|
|
class mail_mail(osv.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'
|
2012-08-16 09:26:16 +00:00
|
|
|
_inherits = {'mail.message': 'mail_message_id'}
|
2012-08-30 08:51:16 +00:00
|
|
|
_order = 'id desc'
|
|
|
|
|
2012-08-15 17:08:22 +00:00
|
|
|
_columns = {
|
2012-08-16 09:26:16 +00:00
|
|
|
'mail_message_id': fields.many2one('mail.message', 'Message', required=True, ondelete='cascade'),
|
2012-08-15 17:08:22 +00:00
|
|
|
'mail_server_id': fields.many2one('ir.mail_server', 'Outgoing mail server', readonly=1),
|
|
|
|
'state': fields.selection([
|
2012-08-15 19:34:06 +00:00
|
|
|
('outgoing', 'Outgoing'),
|
|
|
|
('sent', 'Sent'),
|
|
|
|
('received', 'Received'),
|
|
|
|
('exception', 'Delivery Failed'),
|
|
|
|
('cancel', 'Cancelled'),
|
|
|
|
], 'Status', readonly=True),
|
2012-08-15 17:08:22 +00:00
|
|
|
'auto_delete': fields.boolean('Auto Delete',
|
|
|
|
help="Permanently delete this email after sending it, to save space"),
|
2012-08-16 09:26:16 +00:00
|
|
|
'references': fields.text('References', help='Message references, such as identifiers of previous messages', readonly=1),
|
2012-08-30 17:44:52 +00:00
|
|
|
'email_from': fields.char('From', help='Message sender, taken from user preferences.'),
|
2012-08-15 17:08:22 +00:00
|
|
|
'email_to': fields.text('To', help='Message recipients'),
|
2012-08-30 17:44:52 +00:00
|
|
|
'email_cc': fields.char('Cc', help='Carbon copy message recipients'),
|
|
|
|
'reply_to':fields.char('Reply-To', help='Preferred response address for the message'),
|
2012-08-30 08:51:16 +00:00
|
|
|
'body_html': fields.text('Rich-text Contents', help="Rich-text/HTML message"),
|
2012-08-15 17:08:22 +00:00
|
|
|
}
|
|
|
|
|
2012-08-29 15:00:02 +00:00
|
|
|
def _get_default_from(self, cr, uid, context=None):
|
2012-08-20 16:51:10 +00:00
|
|
|
cur = self.pool.get('res.users').browse(cr, uid, uid, context=context)
|
|
|
|
if not cur.alias_domain:
|
2012-08-29 15:00:02 +00:00
|
|
|
# TDE temp: impossible to install social module, because mail.group tries to send emails ...
|
|
|
|
# raise osv.except_osv(_('Invalid Action!'), _('Unable to send email, set an alias domain in your server settings.'))
|
|
|
|
pass
|
2012-08-20 16:51:10 +00:00
|
|
|
return cur.alias_name + '@' + cur.alias_domain
|
|
|
|
|
2012-08-15 17:08:22 +00:00
|
|
|
_defaults = {
|
2012-08-15 18:44:03 +00:00
|
|
|
'state': 'outgoing',
|
2012-08-29 15:00:02 +00:00
|
|
|
'email_from': lambda self, cr, uid, ctx=None: self._get_default_from(cr, uid, ctx),
|
2012-08-15 17:08:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
def mark_outgoing(self, cr, uid, ids, context=None):
|
|
|
|
return self.write(cr, uid, ids, {'state':'outgoing'}, context=context)
|
|
|
|
|
|
|
|
def cancel(self, cr, uid, ids, context=None):
|
|
|
|
return self.write(cr, uid, ids, {'state':'cancel'}, context=context)
|
|
|
|
|
|
|
|
def process_email_queue(self, cr, uid, ids=None, context=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 context is None:
|
|
|
|
context = {}
|
|
|
|
if not ids:
|
|
|
|
filters = ['&', ('state', '=', 'outgoing'), ('type', '=', 'email')]
|
|
|
|
if 'filters' in context:
|
|
|
|
filters.extend(context['filters'])
|
|
|
|
ids = self.search(cr, uid, filters, context=context)
|
|
|
|
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.send(cr, uid, ids, auto_commit=True, context=context)
|
|
|
|
except Exception:
|
|
|
|
_logger.exception("Failed processing mail queue")
|
|
|
|
return res
|
|
|
|
|
|
|
|
def _postprocess_sent_message(self, cr, uid, message, context=None):
|
|
|
|
"""Perform any post-processing necessary after sending ``message``
|
|
|
|
successfully, including deleting it completely along with its
|
|
|
|
attachment if the ``auto_delete`` flag of the message was set.
|
|
|
|
Overridden by subclasses for extra post-processing behaviors.
|
|
|
|
|
|
|
|
:param browse_record message: the message that was just sent
|
|
|
|
:return: True
|
|
|
|
"""
|
|
|
|
if message.auto_delete:
|
|
|
|
self.pool.get('ir.attachment').unlink(cr, uid,
|
2012-08-15 18:44:03 +00:00
|
|
|
[x.id for x in message.attachment_ids],
|
2012-08-15 17:08:22 +00:00
|
|
|
context=context)
|
|
|
|
message.unlink()
|
|
|
|
return True
|
|
|
|
|
|
|
|
def send(self, cr, uid, ids, auto_commit=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 message is output in the server logs.
|
|
|
|
|
|
|
|
:param bool auto_commit: whether to force a commit of the message
|
|
|
|
status after sending each message (meant
|
|
|
|
only for processing by the scheduler),
|
|
|
|
should never be True during normal
|
|
|
|
transactions (default: False)
|
|
|
|
:return: True
|
|
|
|
"""
|
|
|
|
ir_mail_server = self.pool.get('ir.mail_server')
|
|
|
|
for message in self.browse(cr, uid, ids, context=context):
|
|
|
|
try:
|
2012-08-30 08:51:16 +00:00
|
|
|
body = message.body_html
|
2012-08-29 15:00:02 +00:00
|
|
|
|
|
|
|
# handle attachments
|
2012-08-15 17:08:22 +00:00
|
|
|
attachments = []
|
|
|
|
for attach in message.attachment_ids:
|
|
|
|
attachments.append((attach.datas_fname, base64.b64decode(attach.datas)))
|
|
|
|
|
2012-08-29 15:00:02 +00:00
|
|
|
# no subject, res_id, model: '<Author> posted on <Resource>'
|
|
|
|
if not message.subject and message.model and message.res_id:
|
|
|
|
subject = '%s posted on %s' % (message.author_id.name, message.record_name)
|
|
|
|
else:
|
|
|
|
subject = message.subject
|
|
|
|
|
|
|
|
# use only sanitized html and set its plaintexted version as alternative
|
|
|
|
body_alternative = tools.html2plaintext(body)
|
|
|
|
content_subtype_alternative = 'plain'
|
2012-08-15 17:08:22 +00:00
|
|
|
|
|
|
|
# build an RFC2822 email.message.Message object and send it
|
|
|
|
# without queuing
|
|
|
|
msg = ir_mail_server.build_email(
|
2012-08-30 08:51:16 +00:00
|
|
|
email_from = message.email_from,
|
|
|
|
email_to = tools.email_split(message.email_to),
|
2012-08-29 15:00:02 +00:00
|
|
|
subject = subject,
|
|
|
|
body = body,
|
|
|
|
body_alternative = body_alternative,
|
|
|
|
email_cc = tools.email_split(message.email_cc),
|
|
|
|
reply_to = message.reply_to,
|
|
|
|
attachments = attachments,
|
|
|
|
message_id = message.message_id,
|
2012-08-15 17:08:22 +00:00
|
|
|
references = message.references,
|
2012-08-29 15:00:02 +00:00
|
|
|
object_id = message.res_id and ('%s-%s' % (message.res_id,message.model)),
|
|
|
|
subtype = 'html',
|
|
|
|
subtype_alternative = content_subtype_alternative)
|
2012-08-15 17:08:22 +00:00
|
|
|
res = ir_mail_server.send_email(cr, uid, msg,
|
2012-08-29 15:00:02 +00:00
|
|
|
mail_server_id=message.mail_server_id.id, context=context)
|
2012-08-15 17:08:22 +00:00
|
|
|
if res:
|
2012-08-29 15:00:02 +00:00
|
|
|
message.write({'state':'sent', 'message_id': res})
|
2012-08-15 17:08:22 +00:00
|
|
|
else:
|
2012-08-29 15:00:02 +00:00
|
|
|
message.write({'state':'exception'})
|
2012-08-15 17:08:22 +00:00
|
|
|
message.refresh()
|
|
|
|
if message.state == 'sent':
|
|
|
|
self._postprocess_sent_message(cr, uid, message, context=context)
|
|
|
|
except Exception:
|
2012-08-21 21:24:19 +00:00
|
|
|
_logger.exception('failed sending mail.mail %s', message.id)
|
2012-08-15 17:08:22 +00:00
|
|
|
message.write({'state':'exception'})
|
|
|
|
|
|
|
|
if auto_commit == True:
|
|
|
|
cr.commit()
|
|
|
|
return True
|