[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:
parent
195ac98256
commit
84aea26e88
|
@ -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/>
|
||||||
#
|
#
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
|
|
|
@ -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/>
|
||||||
#
|
#
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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><Empty Message></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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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')]"/>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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]')
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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):
|
||||||
|
|
Loading…
Reference in New Issue