[IMP] email_template: review + many improvements:

- Support for specifying Reply-To and Message-ID headers in templates
- Restored the proper GPLv3 copyright from Sharoon Thomas (mistakenly switched to AGPL)
- Minor refactoring/cleanup/renaming
- Support for multipart/alternative format when sending emails, including wrapped inside multipart/mixed if attachments are present
- Support for plain attachments (existing files) in addition to reports
- Support for specifying "From" email in "Name <email>" format in addition to using the label on the Email Account

bzr revid: odo@openerp.com-20100818152227-i823jbspioqwr1kk
This commit is contained in:
Olivier Dony 2010-08-18 17:22:27 +02:00
parent 195ac98256
commit 84aea26e88
11 changed files with 377 additions and 207 deletions

View File

@ -2,21 +2,21 @@
############################################################################## ##############################################################################
# #
# OpenERP, Open Source Management Solution # OpenERP, Open Source Management Solution
# Copyright (C) 2009 Sharoon Thomas # Copyright (C) 2009 Sharoon Thomas
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>). # Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>)
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as # it under the terms of the GNU General Public License as published by
# published by the Free Software Foundation, either version 3 of the # the Free Software Foundation, either version 3 of the License, or
# License, or (at your option) any later version. # (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>
# #
############################################################################## ##############################################################################

View File

@ -2,21 +2,21 @@
############################################################################## ##############################################################################
# #
# OpenERP, Open Source Management Solution # OpenERP, Open Source Management Solution
# Copyright (C) 2009 Sharoon Thomas # Copyright (C) 2009 Sharoon Thomas
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>). # Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>)
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as # it under the terms of the GNU General Public License as published by
# published by the Free Software Foundation, either version 3 of the # the Free Software Foundation, either version 3 of the License, or
# License, or (at your option) any later version. # (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>
# #
############################################################################## ##############################################################################

View File

@ -2,29 +2,28 @@
############################################################################## ##############################################################################
# #
# OpenERP, Open Source Management Solution # OpenERP, Open Source Management Solution
# Copyright (C) 2009 Sharoon Thomas # Copyright (C) 2009 Sharoon Thomas
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>). # Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>)
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as # it under the terms of the GNU General Public License as published by
# published by the Free Software Foundation, either version 3 of the # the Free Software Foundation, either version 3 of the License, or
# License, or (at your option) any later version. # (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>
# #
############################################################################## ##############################################################################
import base64 import base64
import random import random
import time
import types
import netsvc import netsvc
import re
LOGGER = netsvc.Logger() LOGGER = netsvc.Logger()
@ -58,9 +57,7 @@ except:
_("Django templates not installed") _("Django templates not installed")
) )
import email_template_engines
import tools import tools
import report
import pooler import pooler
import logging import logging
@ -128,29 +125,38 @@ class email_template(osv.osv):
'name' : fields.char('Name', size=100, required=True), 'name' : fields.char('Name', size=100, required=True),
'object_name':fields.many2one('ir.model', 'Model'), 'object_name':fields.many2one('ir.model', 'Model'),
'model_int_name':fields.char('Model Internal Name', size=200,), 'model_int_name':fields.char('Model Internal Name', size=200,),
'enforce_from_account':fields.many2one( 'from_account':fields.many2one(
'email_template.account', 'email_template.account',
string="Enforce From Account", string="Email Account",
help="Emails will be sent only from this account(which are approved)."), help="Emails will be sent from this approved account."),
'from_email' : fields.related('enforce_from_account', 'email_id',
type='char', string='From',
help='From Email (select mail account)',
readonly=True),
'def_to':fields.char( 'def_to':fields.char(
'Recipient (To)', 'Recipient (To)',
size=250, size=250,
help="The default recipient of email." help="The default recipient of email."
"Placeholders can be used here."), "Placeholders can be used here."),
'def_cc':fields.char( 'def_cc':fields.char(
'Default CC', 'CC',
size=250, size=250,
help="The default CC for the email." help="Carbon Copy address(es), comma-separated."
" Placeholders can be used here."), " Placeholders can be used here."),
'def_bcc':fields.char( 'def_bcc':fields.char(
'Default BCC', 'BCC',
size=250, size=250,
help="The default BCC for the email." help="Blind Carbon Copy address(es), comma-separated."
" Placeholders can be used here."), " Placeholders can be used here."),
'reply_to':fields.char('Reply-To',
size=250,
help="The address recipients should reply to,"
" if different from the From address."
" Placeholders can be used here."),
'message_id':fields.char('Message-ID',
size=250,
help="The Message-ID header value, if you need to"
"specify it, for example to automatically recognize the replies later."
" Placeholders can be used here."),
'track_campaign_item':fields.boolean('Track campaign items',
help="Enable this if you want the outgoing e-mails to include a tracking"
" marker that makes it possible to identify the replies an link them back to the campaign item"),
'lang':fields.char( 'lang':fields.char(
'Language', 'Language',
size=250, size=250,
@ -184,6 +190,14 @@ class email_template(osv.osv):
'report_template':fields.many2one( 'report_template':fields.many2one(
'ir.actions.report.xml', 'ir.actions.report.xml',
'Report to send'), 'Report to send'),
'attachment_ids': fields.many2many(
'ir.attachment',
'email_template_attachment_rel',
'email_template_id',
'attachment_id',
'Attached Files',
help="You may attach existing files to this template, "
"so they will be added in all emails created from this template"),
'ref_ir_act_window':fields.many2one( 'ref_ir_act_window':fields.many2one(
'ir.actions.act_window', 'ir.actions.act_window',
'Window Action', 'Window Action',
@ -430,7 +444,37 @@ class email_template(osv.osv):
result['sub_model_object_field'] = False result['sub_model_object_field'] = False
result['null_value'] = null_value result['null_value'] = null_value
return {'value':result} return {'value':result}
def _add_attachment(self, cursor, user, mailbox_id, name, data, filename, context=None):
"""
Add an attachment to a given mailbox entry.
:param data: base64 encoded attachment data to store
"""
attachment_obj = self.pool.get('ir.attachment')
attachment_data = {
'name': (name or '') + _(' (Email Attachment)'),
'datas': data,
'datas_fname': filename,
'description': name or _('No Description'),
'res_model':'email_template.mailbox',
'res_id': mailbox_id,
}
attachment_id = attachment_obj.create(cursor,
user,
attachment_data,
context)
if attachment_id:
self.pool.get('email_template.mailbox').write(
cursor,
user,
mailbox_id,
{
'attachments_ids':[(4, attachment_id)],
'mail_type':'multipart/mixed'
},
context)
def generate_attach_reports(self, def generate_attach_reports(self,
cursor, cursor,
user, user,
@ -440,7 +484,7 @@ class email_template(osv.osv):
context=None): context=None):
""" """
Generate report to be attached and attach it Generate report to be attached and attach it
to the email to the email, and add any directly attached files as well.
@param cursor: Database Cursor @param cursor: Database Cursor
@param user: ID of User @param user: ID of User
@ -452,54 +496,34 @@ class email_template(osv.osv):
@param mail: Browse record of email object @param mail: Browse record of email object
@return: True @return: True
""" """
reportname = 'report.' + \ if template.report_template:
self.pool.get('ir.actions.report.xml').read( reportname = 'report.' + \
cursor, self.pool.get('ir.actions.report.xml').read(
user, cursor,
template.report_template.id, user,
['report_name'], template.report_template.id,
context)['report_name'] ['report_name'],
service = netsvc.LocalService(reportname) context)['report_name']
data = {} service = netsvc.LocalService(reportname)
data['model'] = template.model_int_name data = {}
(result, format) = service.create(cursor, data['model'] = template.model_int_name
user, (result, format) = service.create(cursor,
[record_id],
data,
context)
attachment_obj = self.pool.get('ir.attachment')
fname = tools.ustr(get_value(cursor, user, record_id,
template.file_name, template, context)
or 'Report')
ext = '.' + format
if not fname.endswith(ext):
fname += ext
new_att_vals = {
'name':mail.subject + ' (Email Attachment)',
'datas':base64.b64encode(result),
'datas_fname': fname,
'description':mail.subject or "No Description",
'res_model':'email_template.mailbox',
'res_id':mail.id
}
attachment_id = attachment_obj.create(cursor,
user, user,
new_att_vals, [record_id],
context) data,
if attachment_id: context)
self.pool.get('email_template.mailbox').write( fname = tools.ustr(get_value(cursor, user, record_id,
cursor, template.file_name, template, context)
user, or 'Report')
mail.id, ext = '.' + format
{ if not fname.endswith(ext):
'attachments_ids':[ fname += ext
[6, 0, [attachment_id]] self._add_attachment(cursor, user, mail.id, mail.subject, base64.b64encode(result), fname, context)
],
'mail_type':'multipart/mixed' if template.attachment_ids:
}, for attachment in template.attachment_ids:
context) self._add_attachment(cursor, user, mail.id, attachment.name, attachment.datas, attachment.datas_fname, context)
return True return True
def _generate_mailbox_item_from_template(self, def _generate_mailbox_item_from_template(self,
@ -534,9 +558,9 @@ class email_template(osv.osv):
) )
else: else:
from_account = { from_account = {
'id':template.enforce_from_account.id, 'id':template.from_account.id,
'name':template.enforce_from_account.name, 'name':template.from_account.name,
'email_id':template.enforce_from_account.email_id 'email_id':template.from_account.email_id
} }
lang = get_value(cursor, lang = get_value(cursor,
user, user,
@ -548,9 +572,21 @@ class email_template(osv.osv):
ctx = context.copy() ctx = context.copy()
ctx.update({'lang':lang}) ctx.update({'lang':lang})
template = self.browse(cursor, user, template.id, context=ctx) template = self.browse(cursor, user, template.id, context=ctx)
# determine name of sender, either it is specified in email_id or we
# use the account name
email_id = from_account['email_id'].strip()
email_from = re.findall(r'([^ ,<@]+@[^> ,]+)', email_id)[0]
if email_from != email_id:
# we should keep it all, name is probably specified in the address
email_from = from_account['email_id']
else:
email_from = tools.ustr(from_account['name']) + "<" + tools.ustr('email_id') + ">",
# FIXME: should do this in a loop and rename template fields to the corresponding
# mailbox fields. (makes no sense to have different names I think.
mailbox_values = { mailbox_values = {
'email_from': tools.ustr(from_account['name']) + \ 'email_from': email_from,
"<" + tools.ustr(from_account['email_id']) + ">",
'email_to':get_value(cursor, 'email_to':get_value(cursor,
user, user,
record_id, record_id,
@ -569,6 +605,12 @@ class email_template(osv.osv):
template.def_bcc, template.def_bcc,
template, template,
context), context),
'reply_to':get_value(cursor,
user,
record_id,
template.reply_to,
template,
context),
'subject':get_value(cursor, 'subject':get_value(cursor,
user, user,
record_id, record_id,
@ -591,8 +633,13 @@ class email_template(osv.osv):
#This is a mandatory field when automatic emails are sent #This is a mandatory field when automatic emails are sent
'state':'na', 'state':'na',
'folder':'drafts', 'folder':'drafts',
'mail_type':'multipart/alternative' 'mail_type':'multipart/alternative',
} }
if template['track_campaign_item']:
# get appropriate message-id
mailbox_values.update(message_id=tools.misc.generate_tracking_message_id(record_id))
if not mailbox_values['account_id']: if not mailbox_values['account_id']:
raise Exception("Unable to send the mail. No account linked to the template.") raise Exception("Unable to send the mail. No account linked to the template.")
#Use signatures if allowed #Use signatures if allowed
@ -627,6 +674,7 @@ class email_template(osv.osv):
if not template: if not template:
raise Exception("The requested template could not be loaded") raise Exception("The requested template could not be loaded")
result = True result = True
mailbox_obj = self.pool.get('email_template.mailbox')
for record_id in record_ids: for record_id in record_ids:
mailbox_id = self._generate_mailbox_item_from_template( mailbox_id = self._generate_mailbox_item_from_template(
cursor, cursor,
@ -634,13 +682,13 @@ class email_template(osv.osv):
template, template,
record_id, record_id,
context) context)
mail = self.pool.get('email_template.mailbox').browse( mail = mailbox_obj.browse(
cursor, cursor,
user, user,
mailbox_id, mailbox_id,
context=context context=context
) )
if template.report_template: if template.report_template or template.attachment_ids:
self.generate_attach_reports( self.generate_attach_reports(
cursor, cursor,
user, user,
@ -649,6 +697,7 @@ class email_template(osv.osv):
mail, mail,
context context
) )
self.pool.get('email_template.mailbox').write( self.pool.get('email_template.mailbox').write(
cursor, cursor,
user, user,
@ -662,6 +711,10 @@ class email_template(osv.osv):
email_template() email_template()
## FIXME: this class duplicates a lot of features of the email template send wizard,
## one of the 2 should inherit from the other!
class email_template_preview(osv.osv_memory): class email_template_preview(osv.osv_memory):
_name = "email_template.preview" _name = "email_template.preview"
_description = "Email Template Preview" _description = "Email Template Preview"
@ -674,10 +727,15 @@ class email_template_preview(osv.osv_memory):
if 'template_id' in context.keys(): if 'template_id' in context.keys():
ref_obj_id = self.pool.get('email.template').read(cr, uid, context['template_id'], ['object_name'], context) ref_obj_id = self.pool.get('email.template').read(cr, uid, context['template_id'], ['object_name'], context)
ref_obj_name = self.pool.get('ir.model').read(cr, uid, ref_obj_id['object_name'][0], ['model'], context)['model'] ref_obj_name = self.pool.get('ir.model').read(cr, uid, ref_obj_id['object_name'][0], ['model'], context)['model']
ref_obj_ids = self.pool.get(ref_obj_name).search(cr, uid, [], 0, 20, 'id desc', context=context) model_obj = self.pool.get(ref_obj_name)
ref_obj_recs = self.pool.get(ref_obj_name).name_get(cr, uid, ref_obj_ids, context) ref_obj_ids = model_obj.search(cr, uid, [], 0, 20, 'id desc', context=context)
return ref_obj_recs
# also add the default one if requested, otherwise it won't be available for selection:
default_id = context.get('default_rel_model_ref')
if default_id and default_id not in ref_obj_ids:
ref_obj_ids.insert(0, default_id)
return model_obj.name_get(cr, uid, ref_obj_ids, context)
def _default_model(self, cursor, user, context=None): def _default_model(self, cursor, user, context=None):
""" """
Returns the default value for model field Returns the default value for model field
@ -701,6 +759,16 @@ class email_template_preview(osv.osv_memory):
'to':fields.char('To', size=250, readonly=True), 'to':fields.char('To', size=250, readonly=True),
'cc':fields.char('CC', size=250, readonly=True), 'cc':fields.char('CC', size=250, readonly=True),
'bcc':fields.char('BCC', size=250, readonly=True), 'bcc':fields.char('BCC', size=250, readonly=True),
'reply_to':fields.char('Reply-To',
size=250,
help="The address recipients should reply to,"
" if different from the From address."
" Placeholders can be used here."),
'message_id':fields.char('Message-ID',
size=250,
help="The Message-ID header value, if you need to"
"specify it, for example to automatically recognize the replies later."
" Placeholders can be used here."),
'subject':fields.char('Subject', size=200, readonly=True), 'subject':fields.char('Subject', size=200, readonly=True),
'body_text':fields.text('Body', readonly=True), 'body_text':fields.text('Body', readonly=True),
'body_html':fields.text('Body', readonly=True), 'body_html':fields.text('Body', readonly=True),
@ -728,6 +796,11 @@ class email_template_preview(osv.osv_memory):
vals['to'] = get_value(cr, uid, rel_model_ref, template.def_to, template, context) vals['to'] = get_value(cr, uid, rel_model_ref, template.def_to, template, context)
vals['cc'] = get_value(cr, uid, rel_model_ref, template.def_cc, template, context) vals['cc'] = get_value(cr, uid, rel_model_ref, template.def_cc, template, context)
vals['bcc'] = get_value(cr, uid, rel_model_ref, template.def_bcc, template, context) vals['bcc'] = get_value(cr, uid, rel_model_ref, template.def_bcc, template, context)
vals['reply_to'] = get_value(cr, uid, rel_model_ref, template.reply_to, template, context)
if template.message_id:
vals['message_id'] = get_value(cr, uid, rel_model_ref, template.message_id, template, context)
elif template.track_campaign_item:
vals['message_id'] = tools.misc.generate_tracking_message_id(rel_model_ref)
vals['subject'] = get_value(cr, uid, rel_model_ref, template.def_subject, template, context) vals['subject'] = get_value(cr, uid, rel_model_ref, template.def_subject, template, context)
vals['body_text'] = get_value(cr, uid, rel_model_ref, template.def_body_text, template, context) vals['body_text'] = get_value(cr, uid, rel_model_ref, template.def_body_text, template, context)
vals['body_html'] = get_value(cr, uid, rel_model_ref, template.def_body_html, template, context) vals['body_html'] = get_value(cr, uid, rel_model_ref, template.def_body_html, template, context)

View File

@ -2,26 +2,25 @@
############################################################################## ##############################################################################
# #
# OpenERP, Open Source Management Solution # OpenERP, Open Source Management Solution
# Copyright (C) 2009 Sharoon Thomas # Copyright (C) 2009 Sharoon Thomas
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>). # Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>)
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as # it under the terms of the GNU General Public License as published by
# published by the Free Software Foundation, either version 3 of the # the Free Software Foundation, either version 3 of the License, or
# License, or (at your option) any later version. # (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>
# #
############################################################################## ##############################################################################
from osv import osv, fields from osv import osv, fields
from html2text import html2text
import re import re
import smtplib import smtplib
import base64 import base64
@ -31,12 +30,8 @@ from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText from email.mime.text import MIMEText
from email.header import decode_header, Header from email.header import decode_header, Header
from email.utils import formatdate from email.utils import formatdate
import re
import netsvc import netsvc
import string import datetime
import email
import time, datetime
import email_template_engines
from tools.translate import _ from tools.translate import _
import tools import tools
@ -86,9 +81,10 @@ class email_template_account(osv.osv):
'smtpssl':fields.boolean('SSL/TLS (only in python 2.6)', 'smtpssl':fields.boolean('SSL/TLS (only in python 2.6)',
states={'draft':[('readonly', False)]}, readonly=True), states={'draft':[('readonly', False)]}, readonly=True),
'send_pref':fields.selection([ 'send_pref':fields.selection([
('html', 'HTML otherwise Text'), ('html', 'HTML, otherwise Text'),
('text', 'Text otherwise HTML'), ('text', 'Text, otherwise HTML'),
('both', 'Both HTML & Text') ('alternative', 'Both HTML & Text (Alternative)'),
('mixed', 'Both HTML & Text (Mixed)')
], 'Mail Format', required=True), ], 'Mail Format', required=True),
'company':fields.selection([ 'company':fields.selection([
('yes', 'Yes'), ('yes', 'Yes'),
@ -135,7 +131,10 @@ class email_template_account(osv.osv):
'unique (email_id)', 'unique (email_id)',
'Another setting already exists with this email ID !') 'Another setting already exists with this email ID !')
] ]
def name_get(self, cr, uid, ids, context=None):
return dict([(a["id"], "%s (%s)" % (a['email_id'], a['name'])) for a in self.read(cr, uid, ids, ['name', 'email_id'], context=context)])
def _constraint_unique(self, cursor, user, ids): def _constraint_unique(self, cursor, user, ids):
""" """
This makes sure that you dont give personal This makes sure that you dont give personal
@ -283,7 +282,7 @@ class email_template_account(osv.osv):
TODO: Doc this TODO: Doc this
""" """
result = {'all':[]} result = {'all':[]}
keys = ['To', 'CC', 'BCC'] keys = ['To', 'CC', 'BCC', 'Reply-To']
for each in keys: for each in keys:
ids_as_list = self.split_to_ids(addresses.get(each, u'')) ids_as_list = self.split_to_ids(addresses.get(each, u''))
while u'' in ids_as_list: while u'' in ids_as_list:
@ -292,7 +291,7 @@ class email_template_account(osv.osv):
result['all'].extend(ids_as_list) result['all'].extend(ids_as_list)
return result return result
def send_mail(self, cr, uid, ids, addresses, subject='', body=None, payload=None, context=None): def send_mail(self, cr, uid, ids, addresses, subject='', body=None, payload=None, message_id=None, context=None):
#TODO: Replace all this with a single email object #TODO: Replace all this with a single email object
if body is None: if body is None:
body = {} body = {}
@ -306,18 +305,38 @@ class email_template_account(osv.osv):
serv = self.smtp_connection(cr, uid, id) serv = self.smtp_connection(cr, uid, id)
if serv: if serv:
try: try:
msg = MIMEMultipart() # Prepare multipart containers depending on data
text_subtype = (core_obj.send_pref == 'alternative') and 'alternative' or 'mixed'
# Need a multipart/mixed wrapper for attachments if content is alternative
if payload and text_subtype == 'alternative':
payload_part = MIMEMultipart(_subtype='mixed')
text_part = MIMEMultipart(_subtype=text_subtype)
payload_part.attach(text_part)
else:
# otherwise a single multipart/mixed will do the whole job
payload_part = text_part = MIMEMultipart(_subtype=text_subtype)
if subject: if subject:
msg['Subject'] = subject payload_part['Subject'] = subject
sender_name = Header(core_obj.name, 'utf-8').encode() from_email = core_obj.email_id
msg['From'] = sender_name + " <" + core_obj.email_id + ">" if '<' in from_email:
msg['Organization'] = tools.ustr(core_obj.user.company_id.name) # We have a structured email address, keep it untouched
msg['Date'] = formatdate() payload_part['From'] = Header(core_obj.email_id, 'utf-8').encode()
else:
# Plain email address, construct a structured one based on the name:
sender_name = Header(core_obj.name, 'utf-8').encode()
payload_part['From'] = sender_name + " <" + core_obj.email_id + ">"
payload_part['Organization'] = tools.ustr(core_obj.user.company_id.name)
payload_part['Date'] = formatdate()
addresses_l = self.get_ids_from_dict(addresses) addresses_l = self.get_ids_from_dict(addresses)
if addresses_l['To']: if addresses_l['To']:
msg['To'] = u','.join(addresses_l['To']) payload_part['To'] = u','.join(addresses_l['To'])
if addresses_l['CC']: if addresses_l['CC']:
msg['CC'] = u','.join(addresses_l['CC']) payload_part['CC'] = u','.join(addresses_l['CC'])
if addresses_l['Reply-To']:
payload_part['Reply-To'] = addresses_l['Reply-To'][0]
if message_id:
payload_part['Message-ID'] = message_id
if body.get('text', False): if body.get('text', False):
temp_body_text = body.get('text', '') temp_body_text = body.get('text', '')
l = len(temp_body_text.replace(' ', '').replace('\r', '').replace('\n', '')) l = len(temp_body_text.replace(' ', '').replace('\r', '').replace('\n', ''))
@ -326,28 +345,30 @@ class email_template_account(osv.osv):
# Attach parts into message container. # Attach parts into message container.
# According to RFC 2046, the last part of a multipart message, in this case # According to RFC 2046, the last part of a multipart message, in this case
# the HTML message, is best and preferred. # the HTML message, is best and preferred.
if core_obj.send_pref == 'text' or core_obj.send_pref == 'both': if core_obj.send_pref in ('text', 'mixed', 'alternative'):
body_text = body.get('text', u'No Mail Message') body_text = body.get('text', u'<Empty Message>')
body_text = tools.ustr(body_text) body_text = tools.ustr(body_text)
msg.attach(MIMEText(body_text.encode("utf-8"), _charset='UTF-8')) text_part.attach(MIMEText(body_text.encode("utf-8"), _charset='UTF-8'))
if core_obj.send_pref == 'html' or core_obj.send_pref == 'both': if core_obj.send_pref in ('html', 'mixed', 'alternative'):
html_body = body.get('html', u'') html_body = body.get('html', u'')
if len(html_body) == 0 or html_body == u'': if len(html_body) == 0 or html_body == u'':
html_body = body.get('text', u'<p>No Mail Message</p>').replace('\n', '<br/>').replace('\r', '<br/>') html_body = body.get('text', u'<p>&lt;Empty Message&gt;</p>').replace('\n', '<br/>').replace('\r', '<br/>')
html_body = tools.ustr(html_body) html_body = tools.ustr(html_body)
msg.attach(MIMEText(html_body.encode("utf-8"), _subtype='html', _charset='UTF-8')) text_part.attach(MIMEText(html_body.encode("utf-8"), _subtype='html', _charset='UTF-8'))
#Now add attachments if any
for file in payload.keys(): #Now add attachments if any, wrapping into a container multipart/mixed if needed
part = MIMEBase('application', "octet-stream") if payload:
part.set_payload(base64.decodestring(payload[file])) for file in payload:
part.add_header('Content-Disposition', 'attachment; filename="%s"' % file) part = MIMEBase('application', "octet-stream")
Encoders.encode_base64(part) part.set_payload(base64.decodestring(payload[file]))
msg.attach(part) part.add_header('Content-Disposition', 'attachment; filename="%s"' % file)
Encoders.encode_base64(part)
payload_part.attach(part)
except Exception, error: except Exception, error:
logger.notifyChannel(_("Email Template"), netsvc.LOG_ERROR, _("Mail from Account %s failed. Probable Reason:MIME Error\nDescription: %s") % (id, error)) logger.notifyChannel(_("Email Template"), netsvc.LOG_ERROR, _("Mail from Account %s failed. Probable Reason:MIME Error\nDescription: %s") % (id, error))
return {'error_msg': "Server Send Error\nDescription: %s"%error} return {'error_msg': "Server Send Error\nDescription: %s"%error}
try: try:
serv.sendmail(msg['From'], addresses_l['all'], msg.as_string()) serv.sendmail(payload_part['From'], addresses_l['all'], payload_part.as_string())
except Exception, error: except Exception, error:
logger.notifyChannel(_("Email Template"), netsvc.LOG_ERROR, _("Mail from Account %s failed. Probable Reason:Server Send Error\nDescription: %s") % (id, error)) logger.notifyChannel(_("Email Template"), netsvc.LOG_ERROR, _("Mail from Account %s failed. Probable Reason:Server Send Error\nDescription: %s") % (id, error))
return {'error_msg': "Server Send Error\nDescription: %s"%error} return {'error_msg': "Server Send Error\nDescription: %s"%error}
@ -358,7 +379,7 @@ class email_template_account(osv.osv):
else: else:
logger.notifyChannel(_("Email Template"), netsvc.LOG_ERROR, _("Mail from Account %s failed. Probable Reason:Account not approved") % id) logger.notifyChannel(_("Email Template"), netsvc.LOG_ERROR, _("Mail from Account %s failed. Probable Reason:Account not approved") % id)
return {'error_msg':"Mail from Account %s failed. Probable Reason:Account not approved"% id} return {'error_msg':"Mail from Account %s failed. Probable Reason:Account not approved"% id}
def extracttime(self, time_as_string): def extracttime(self, time_as_string):
""" """
TODO: DOC THis TODO: DOC THis

View File

@ -2,29 +2,25 @@
############################################################################## ##############################################################################
# #
# OpenERP, Open Source Management Solution # OpenERP, Open Source Management Solution
# Copyright (C) 2009 Sharoon Thomas # Copyright (C) 2009 Sharoon Thomas
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>). # Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>)
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as # it under the terms of the GNU General Public License as published by
# published by the Free Software Foundation, either version 3 of the # the Free Software Foundation, either version 3 of the License, or
# License, or (at your option) any later version. # (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>
# #
############################################################################## ##############################################################################
# To change this template, choose Tools | Templates from osv import osv
# and open the template in the editor.
from osv import fields,osv
import pooler
import netsvc
import re import re
class email_template_engines(osv.osv): class email_template_engines(osv.osv):
@ -35,7 +31,7 @@ class email_template_engines(osv.osv):
pass pass
def strip_html(self,text): def strip_html(self,text):
#Removes HTML, Have to check if still relevent #Removes HTML, Have to check if still relevant
if text: if text:
def fixup(m): def fixup(m):
text = m.group(0) text = m.group(0)
@ -69,7 +65,6 @@ class email_template_engines(osv.osv):
#templateid: the template id of the template #templateid: the template id of the template
#context: TODO #context: TODO
if message: if message:
logger = netsvc.Logger()
def merge(match): def merge(match):
template = self.pool.get("email.template").browse(cr,uid,templateid,context) template = self.pool.get("email.template").browse(cr,uid,templateid,context)
obj_pool = self.pool.get(template.object_name.model) obj_pool = self.pool.get(template.object_name.model)

View File

@ -2,27 +2,26 @@
############################################################################## ##############################################################################
# #
# OpenERP, Open Source Management Solution # OpenERP, Open Source Management Solution
# Copyright (C) 2009 Sharoon Thomas # Copyright (C) 2009 Sharoon Thomas
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>). # Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>)
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as # it under the terms of the GNU General Public License as published by
# published by the Free Software Foundation, either version 3 of the # the Free Software Foundation, either version 3 of the License, or
# License, or (at your option) any later version. # (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>
# #
############################################################################## ##############################################################################
from osv import osv, fields from osv import osv, fields
import time import time
import email_template_engines
import netsvc import netsvc
from tools.translate import _ from tools.translate import _
import tools import tools
@ -75,10 +74,15 @@ class email_template_mailbox(osv.osv):
payload[attachment.datas_fname] = attachment.datas payload[attachment.datas_fname] = attachment.datas
result = account_obj.send_mail(cr, uid, result = account_obj.send_mail(cr, uid,
[values['account_id'][0]], [values['account_id'][0]],
{'To':values.get('email_to', u'') or u'', 'CC':values.get('email_cc', u'') or u'', 'BCC':values.get('email_bcc', u'') or u''}, {'To':values.get('email_to') or u'',
'CC':values.get('email_cc') or u'',
'BCC':values.get('email_bcc') or u'',
'Reply-To':values.get('reply_to') or u''},
values['subject'] or u'', values['subject'] or u'',
{'text':values.get('body_text', u'') or u'', 'html':values.get('body_html', u'') or u''}, {'text':values.get('body_text') or u'', 'html':values.get('body_html') or u''},
payload=payload, context=context) payload=payload,
message_id=values['message_id'],
context=context)
if result == True: if result == True:
self.write(cr, uid, id, {'folder':'sent', 'state':'na', 'date_mail':time.strftime("%Y-%m-%d %H:%M:%S")}, context) self.write(cr, uid, id, {'folder':'sent', 'state':'na', 'date_mail':time.strftime("%Y-%m-%d %H:%M:%S")}, context)
self.historise(cr, uid, [id], "Email sent successfully", context) self.historise(cr, uid, [id], "Email sent successfully", context)
@ -88,7 +92,7 @@ class email_template_mailbox(osv.osv):
except Exception, error: except Exception, error:
logger = netsvc.Logger() logger = netsvc.Logger()
logger.notifyChannel(_("Power Email"), netsvc.LOG_ERROR, _("Sending of Mail %s failed. Probable Reason:Could not login to server\nError: %s") % (id, error)) logger.notifyChannel("email-template", netsvc.LOG_ERROR, _("Sending of Mail %s failed. Probable Reason:Could not login to server\nError: %s") % (id, error))
self.historise(cr, uid, [id], error, context) self.historise(cr, uid, [id], error, context)
self.write(cr, uid, id, {'state':'na'}, context) self.write(cr, uid, id, {'state':'na'}, context)
return result return result
@ -103,21 +107,27 @@ class email_template_mailbox(osv.osv):
'From', 'From',
size=64), size=64),
'email_to':fields.char( 'email_to':fields.char(
'Recepient (To)', 'Recipient (To)',
size=250,), size=250,),
'email_cc':fields.char( 'email_cc':fields.char(
' CC', 'CC',
size=250), size=250),
'email_bcc':fields.char( 'email_bcc':fields.char(
' BCC', 'BCC',
size=250),
'reply_to':fields.char(
'Reply-To',
size=250),
'message_id':fields.char(
'Message-ID',
size=250), size=250),
'subject':fields.char( 'subject':fields.char(
' Subject', 'Subject',
size=200,), size=200,),
'body_text':fields.text( 'body_text':fields.text(
'Standard Body (Text)'), 'Standard Body (Text)'),
'body_html':fields.text( 'body_html':fields.text(
'Body (Text-Web Client Only)'), 'Body (Rich Text Clients Only)'),
'attachments_ids':fields.many2many( 'attachments_ids':fields.many2many(
'ir.attachment', 'ir.attachment',
'mail_attachments_rel', 'mail_attachments_rel',

View File

@ -8,14 +8,13 @@
<field name="type">form</field> <field name="type">form</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<form string="Outbox"> <form string="Outbox">
<group col="4" colspan="2"> <group col="4" colspan="4" name="headers">
<field name="email_from" colspan="4" select="1"/> <field name="email_from" select="1"/>
<field name="email_cc" colspan="4" select="1"/> <field name="email_to" required="1" select="1" />
<field name="date_mail" colspan="4" select="2"/> <field name="reply_to" select="2"/>
</group> <field name="email_cc" select="1"/>
<group col="4" colspan="2"> <field name="email_bcc" select="2"/>
<field name="email_to" colspan="4" required="1" select="1" /> <field name="date_mail" select="2"/>
<field name="email_bcc" colspan="4" select="2"/>
<field name="subject" colspan="4" select="1"/> <field name="subject" colspan="4" select="1"/>
</group> </group>
<notebook colspan="4"> <notebook colspan="4">
@ -46,6 +45,7 @@
<field name="server_ref" colspan="2" /> <field name="server_ref" colspan="2" />
<field name="mail_type" colspan="2" /> <field name="mail_type" colspan="2" />
<field name="folder" colspan="2" select="2"/> <field name="folder" colspan="2" select="2"/>
<field name="message_id" select="2"/>
<separator string="History" colspan="4" /> <separator string="History" colspan="4" />
<field name="history" nolabel="1" colspan="4"/> <field name="history" nolabel="1" colspan="4"/>
</group> </group>
@ -83,7 +83,7 @@
<field name="type">search</field> <field name="type">search</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<search string="Mailboxes"> <search string="Mailboxes">
<filter icon="terp-mail-message-new" string="Drafts" name="draft" domain="[('folder','=','drafts']"/> <filter icon="terp-mail-message-new" string="Drafts" name="draft" domain="[('folder','=','drafts')]"/>
<filter icon="terp-mail-" string="Outbox" name="outbox" domain="[('folder','=','outbox')]"/> <filter icon="terp-mail-" string="Outbox" name="outbox" domain="[('folder','=','outbox')]"/>
<separator orientation="vertical"/> <separator orientation="vertical"/>
<filter icon="terp-gtk-jump-to-ltr" string="Sent" domain="[('folder','=','sent')]"/> <filter icon="terp-gtk-jump-to-ltr" string="Sent" domain="[('folder','=','sent')]"/>

View File

@ -16,7 +16,9 @@
<field name="to" /> <field name="to" />
<field name="cc" /> <field name="cc" />
<field name="bcc" /> <field name="bcc" />
<field name="subject" /> <field name="reply_to" />
<field name="message_id" attrs="{'invisible':[('message_id','=',False)]}" groups="base.group_extended"/>
<field name="subject" colspan="8"/>
</group> </group>
<group col="4" colspan="4"> <group col="4" colspan="4">
<separator string= "Body(Text)" colspan="2"/> <separator string= "Body(Text)" colspan="2"/>
@ -58,17 +60,18 @@
<page string="Mail Details"> <page string="Mail Details">
<group col="2" colspan="2"> <group col="2" colspan="2">
<separator string="Addresses" colspan="2"/> <separator string="Addresses" colspan="2"/>
<field name="from_email"/> <field name="from_account" required="1"/>
<field name="def_to" required="1"/> <field name="def_to" required="1"/>
<field name="def_cc"/> <field name="def_cc"/>
<field name="def_bcc"/> <field name="def_bcc"/>
<field name="reply_to"/>
</group> </group>
<group col="2" colspan="2"> <group col="2" colspan="2">
<separator string="Email Data" colspan="2"/> <separator string="Email Data" colspan="2"/>
<field name="enforce_from_account" required= "1"/>
<field name="def_subject" colspan="4" required="1" /> <field name="def_subject" colspan="4" required="1" />
<field name="use_sign" colspan="4" /> <field name="use_sign" colspan="4" />
<field name="lang" colspan="4" /> <field name="lang" colspan="4" />
<field name="track_campaign_item" colspan="4"/>
</group> </group>
<separator colspan="3" string="Standard Body" /> <separator colspan="3" string="Standard Body" />
<separator colspan="1" string="Expression Builder" /> <separator colspan="1" string="Expression Builder" />
@ -116,10 +119,17 @@
<button name="delete_action" string="Delete Action" type="object" colspan="2" attrs="{'invisible':[('ref_ir_act_window','=',False), ('ref_ir_value','=',False)]}"/> <button name="delete_action" string="Delete Action" type="object" colspan="2" attrs="{'invisible':[('ref_ir_act_window','=',False), ('ref_ir_value','=',False)]}"/>
</group> </group>
<group colspan="2" col="2"> <group colspan="2" col="2">
<separator string="Attachments (Report to attach)" colspan="4"/> <separator string="Attachments" colspan="2"/>
<field name="file_name" colspan="2" /> <notebook>
<field name="report_template" colspan="2" <page string="Existing files">
domain="[('model','=',model_int_name)]" /> <field name="attachment_ids" colspan="4" nolabel="1"/>
</page>
<page string="Report">
<field name="file_name" colspan="4" />
<field name="report_template" colspan="4"
domain="[('model','=',model_int_name)]" />
</page>
</notebook>
</group> </group>
</page> </page>
</notebook> </notebook>
@ -141,7 +151,7 @@
<field name="def_subject" colspan="4" select="2" /> <field name="def_subject" colspan="4" select="2" />
<field name="use_sign" colspan="4" select="2" /> <field name="use_sign" colspan="4" select="2" />
<field name="file_name" colspan="4" /> <field name="file_name" colspan="4" />
<field name="enforce_from_account" /> <field name="from_account" />
</tree> </tree>
</field> </field>
</record> </record>

View File

@ -9,7 +9,7 @@ __contributors__ = ["Martin 'Joey' Schulze", "Ricardo Reyes", "Kevin Jay North"]
# Support decoded entities with unifiable. # Support decoded entities with unifiable.
if not hasattr(__builtins__, 'True'): True, False = 1, 0 if not hasattr(__builtins__, 'True'): True, False = 1, 0
import re, sys, urllib, htmlentitydefs, codecs, StringIO, types import re, sys, urllib, htmlentitydefs, codecs
import sgmllib import sgmllib
import urlparse import urlparse
sgmllib.charref = re.compile('&#([xX]?[0-9a-fA-F]+)[^0-9a-fA-F]') sgmllib.charref = re.compile('&#([xX]?[0-9a-fA-F]+)[^0-9a-fA-F]')

View File

@ -1 +1,23 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2009 Sharoon Thomas
# Copyright (C) 2004-2010 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 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
#
##############################################################################
import email_template_send_wizard import email_template_send_wizard

View File

@ -1,13 +1,38 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2009 Sharoon Thomas
# Copyright (C) 2004-2010 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 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
#
##############################################################################
from osv import osv, fields from osv import osv, fields
from mako.template import Template from mako.template import Template
from mako import exceptions from mako import exceptions
import netsvc import netsvc
import base64 import base64
import time
from tools.translate import _ from tools.translate import _
import tools import tools
from email_template.email_template import get_value from email_template.email_template import get_value
## FIXME: this wizard duplicates a lot of features of the email template preview,
## one of the 2 should inherit from the other!
class email_template_send_wizard(osv.osv_memory): class email_template_send_wizard(osv.osv_memory):
_name = 'email_template.send.wizard' _name = 'email_template.send.wizard'
_description = 'This is the wizard for sending mail' _description = 'This is the wizard for sending mail'
@ -23,16 +48,16 @@ class email_template_send_wizard(osv.osv_memory):
logger = netsvc.Logger() logger = netsvc.Logger()
if template.enforce_from_account: if template.from_account:
return [(template.enforce_from_account.id, '%s (%s)' % (template.enforce_from_account.name, template.enforce_from_account.email_id))] return [(template.from_account.id, '%s (%s)' % (template.from_account.name, template.from_account.email_id))]
else: else:
account_id = self.pool.get('email_template.account').search(cr,uid,[('company','=','no'),('user','=',uid)], context=context) account_id = self.pool.get('email_template.account').search(cr,uid,[('company','=','no'),('user','=',uid)], context=context)
if account_id: if account_id:
account = self.pool.get('email_template.account').browse(cr,uid,account_id, context) account = self.pool.get('email_template.account').browse(cr,uid,account_id, context)
return [(r.id,r.name + " (" + r.email_id + ")") for r in account] return [(r.id,r.name + " (" + r.email_id + ")") for r in account]
else: else:
logger.notifyChannel(_("Power Email"), netsvc.LOG_ERROR, _("No personal email accounts are configured for you. \nEither ask admin to enforce an account for this template or get yourself a personal power email account.")) logger.notifyChannel(_("email-template"), netsvc.LOG_ERROR, _("No personal email accounts are configured for you. \nEither ask admin to enforce an account for this template or get yourself a personal email account."))
raise osv.except_osv(_("Power Email"),_("No personal email accounts are configured for you. \nEither ask admin to enforce an account for this template or get yourself a personal power email account.")) raise osv.except_osv(_("Missing mail account"),_("No personal email accounts are configured for you. \nEither ask admin to enforce an account for this template or get yourself a personal email account."))
def get_value(self, cursor, user, template, message, context=None, id=None): def get_value(self, cursor, user, template, message, context=None, id=None):
"""Gets the value of the message parsed with the content of object id (or the first 'src_rec_ids' if id is not given)""" """Gets the value of the message parsed with the content of object id (or the first 'src_rec_ids' if id is not given)"""
@ -91,6 +116,16 @@ class email_template_send_wizard(osv.osv_memory):
'to':fields.char('To',size=250,required=True), 'to':fields.char('To',size=250,required=True),
'cc':fields.char('CC',size=250,), 'cc':fields.char('CC',size=250,),
'bcc':fields.char('BCC',size=250,), 'bcc':fields.char('BCC',size=250,),
'reply_to':fields.char('Reply-To',
size=250,
help="The address recipients should reply to,"
" if different from the From address."
" Placeholders can be used here."),
'message_id':fields.char('Message-ID',
size=250,
help="The Message-ID header value, if you need to"
"specify it, for example to automatically recognize the replies later."
" Placeholders can be used here."),
'subject':fields.char('Subject',size=200), 'subject':fields.char('Subject',size=200),
'body_text':fields.text('Body',), 'body_text':fields.text('Body',),
'body_html':fields.text('Body',), 'body_html':fields.text('Body',),
@ -103,6 +138,7 @@ class email_template_send_wizard(osv.osv_memory):
'attachment_ids': fields.many2many('ir.attachment','send_wizard_attachment_rel', 'wizard_id', 'attachment_id', 'Attachments'), 'attachment_ids': fields.many2many('ir.attachment','send_wizard_attachment_rel', 'wizard_id', 'attachment_id', 'Attachments'),
} }
#FIXME: probably better by overriding default_get directly
_defaults = { _defaults = {
'state': lambda self,cr,uid,ctx: len(ctx['src_rec_ids']) > 1 and 'multi' or 'single', 'state': lambda self,cr,uid,ctx: len(ctx['src_rec_ids']) > 1 and 'multi' or 'single',
'rel_model': lambda self,cr,uid,ctx: self.pool.get('ir.model').search(cr,uid,[('model','=',ctx['src_model'])],context=ctx)[0], 'rel_model': lambda self,cr,uid,ctx: self.pool.get('ir.model').search(cr,uid,[('model','=',ctx['src_model'])],context=ctx)[0],
@ -116,8 +152,11 @@ class email_template_send_wizard(osv.osv_memory):
'report': lambda self,cr,uid,ctx: self._get_template_value(cr, uid, 'file_name', ctx), 'report': lambda self,cr,uid,ctx: self._get_template_value(cr, uid, 'file_name', ctx),
'signature': lambda self,cr,uid,ctx: self._get_template(cr, uid, ctx).use_sign, 'signature': lambda self,cr,uid,ctx: self._get_template(cr, uid, ctx).use_sign,
'ref_template':lambda self,cr,uid,ctx: self._get_template(cr, uid, ctx).id, 'ref_template':lambda self,cr,uid,ctx: self._get_template(cr, uid, ctx).id,
'reply_to': lambda self,cr,uid,ctx: self._get_template_value(cr, uid, 'reply_to', ctx),
'reply_to': lambda self,cr,uid,ctx: self._get_template_value(cr, uid, 'reply_to', ctx),
'requested':lambda self,cr,uid,ctx: len(ctx['src_rec_ids']), 'requested':lambda self,cr,uid,ctx: len(ctx['src_rec_ids']),
'full_success': lambda *a: False 'full_success': False,
'attachment_ids': [],
} }
def fields_get(self, cr, uid, fields=None, context=None, write_access=True): def fields_get(self, cr, uid, fields=None, context=None, write_access=True):
@ -153,13 +192,13 @@ class email_template_send_wizard(osv.osv_memory):
mail_ids = self.save_to_mailbox(cr, uid, ids, context) mail_ids = self.save_to_mailbox(cr, uid, ids, context)
if mail_ids: if mail_ids:
self.pool.get('email_template.mailbox').write(cr, uid, mail_ids, {'folder':'outbox'}, context) self.pool.get('email_template.mailbox').write(cr, uid, mail_ids, {'folder':'outbox'}, context)
logger.notifyChannel(_("Power Email"), netsvc.LOG_INFO, _("Emails for multiple items saved in outbox.")) logger.notifyChannel("email-template", netsvc.LOG_INFO, _("Emails for multiple items saved in outbox."))
self.write(cr, uid, ids, { self.write(cr, uid, ids, {
'generated':len(mail_ids), 'generated':len(mail_ids),
'state':'done' 'state':'done'
}, context) }, context)
else: else:
raise osv.except_osv(_("Power Email"),_("Email sending failed for one or more objects.")) raise osv.except_osv(_("Email Template"),_("Email sending failed for one or more objects."))
return True return True
def save_to_mailbox(self, cr, uid, ids, context=None): def save_to_mailbox(self, cr, uid, ids, context=None):