[IMP] email_template: another pass of cleanup (wip)

bzr revid: odo@openerp.com-20110825122725-mc1zx2f3ck9xr6g9
This commit is contained in:
Olivier Dony 2011-08-25 14:27:25 +02:00
parent b0bed63351
commit 0b0aa95cfe
8 changed files with 268 additions and 345 deletions

View File

@ -19,7 +19,6 @@
# 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 mako_template
import email_template import email_template
import wizard import wizard

View File

@ -3,7 +3,7 @@
# #
# OpenERP, Open Source Management Solution # OpenERP, Open Source Management Solution
# Copyright (C) 2009 Sharoon Thomas # Copyright (C) 2009 Sharoon Thomas
# Copyright (C) 2010-2010 OpenERP SA (<http://www.openerp.com>) # Copyright (C) 2010-Today 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 General Public License as published by # it under the terms of the GNU General Public License as published by
@ -21,26 +21,44 @@
############################################################################## ##############################################################################
{ {
"name" : "Email Template for OpenERP", "name" : "Email Templates",
"version" : "0.7 RC", "version" : "1.1",
"author" : "Openlabs", "author" : "Openlabs",
"website" : "http://openerp.com", "website" : "http://openerp.com",
"category" : "Tools", "category" : "Tools",
'complexity': "expert", 'complexity': "expert",
"depends" : ['mail'], "depends" : ['mail'],
"description": """ "description": """
Email Template is extraction of Power Email basically just to send emails. Email Templating (simplified version of the original Power Email by Openlabs)
========================================================================== =============================================================================
You can define email accounts(server, port, mail format - HTML/Text/Both) Lets you design complete email templates related to any OpenERP document (Sale
and email templates (resource, recipient, subject, body, attachments). Orders, Invoices and so on), including sender, recipient, subject, body (HTML and
Text). You may also automatically attach files to your templates, or print and
attach a report.
For advanced use, the templates may include dynamic attributes of the document
they are related to. For example, you may use the name of a Partner's country
when writing to them, also providing a safe default in case the attribute is
not defined. Each template contains a built-in assistant to help with the
inclusion of these dynamic values.
If you enable the option, a composition assistant will also appear in the sidebar
of the OpenERP documents to which the template applies (e.g. Invoices).
This serves as a quick way to send a new email based on the template, after
reviewing and adapting the contents, if needed.
This composition assistant will also turn into a mass mailing system when called
for multiple documents at once.
These email templates are also at the heart of the marketing campaign system
(see the ``marketing_campaign`` application), if you need to automate larger
campaigns on any OpenERP document.
Technical note: only the templating system of the original Power Email by
Openlabs was kept
For each email template, you can have OpenERP generate a Wizard Action / Button
that will be related to the object. So if you choose to do marketing campaigns
for leads, the action will be added to the right side panel of the Lead form.
""", """,
"init_xml": [], "data": [
"update_xml": [
'wizard/email_template_preview_view.xml', 'wizard/email_template_preview_view.xml',
'email_template_view.xml', 'email_template_view.xml',
'wizard/email_compose_message_view.xml', 'wizard/email_compose_message_view.xml',

View File

@ -20,136 +20,146 @@
# #
############################################################################## ##############################################################################
import base64
import mako_template
import netsvc
from osv import osv from osv import osv
from osv import fields from osv import fields
import base64
import netsvc
from tools.translate import _ from tools.translate import _
class email_template(osv.osv): try:
"Templates for sending Email" from mako.template import Template as MakoTemplate
_inherit = 'email.message.common' except ImportError:
_name = "email.template" logging.getLogger('init').warning("email_template: mako templates not available, templating features will not work!")
_description = 'Email Templates for Models'
def get_template_value(self, cr, uid, message=None, model=None, record_id=None, context=None): class email_template(osv.osv):
import mako_template "Templates for sending email"
return mako_template.get_value(cr, uid, message=message, model=model, record_id=record_id, context=context) _inherit = 'mail.message'
_name = "email.template"
_description = 'Email Templates'
def render_template(self, cr, uid, template, model, res_id, context=None):
"""Render the given template text, replace mako expressions ``${expr}``
with the result of evaluating these expressions with
an evaluation context containing:
* ``user``: browse_record of the current user
* ``object``: browse_record of the document record this mail is
related to
* ``context``: the context passed to the mail composition wizard
:param str template: the template text to render
:param str model: model name of the document record this mail is related to.
:param int res_id: id of the document record this mail is related to.
"""
if not template: return u""
try:
template = tools.ustr(template)
record = self.pool.get(model).browse(cr, uid, res_id, context=context)
user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
result = MakoTemplate(template).render_unicode(object=record,
user=user,
context=context,
format_exceptions=True)
if result == u'False':
result = u''
return result
except Exception:
logging.exception("failed to render mako template value %r", template)
return u""
def get_email_template(self, cr, uid, template_id=False, record_id=None, context=None): def get_email_template(self, cr, uid, template_id=False, record_id=None, context=None):
"Return Template Object"
if context is None: if context is None:
context = {} context = {}
if not template_id: if not template_id:
return False return False
lang = self.render_template(cr, uid, template.lang, template.model, record_id, context)
template = self.browse(cr, uid, int(template_id), context)
lang = self.get_template_value(cr, uid, template.lang, template.model, record_id, context)
if lang: if lang:
# Use translated template if necessary # Use translated template if necessary
ctx = context.copy() ctx = context.copy()
ctx['lang'] = lang ctx['lang'] = lang
template = self.browse(cr, uid, template.id, ctx) template = self.browse(cr, uid, template.id, ctx)
else:
template = self.browse(cr, uid, int(template_id), context)
return template return template
def onchange_model_id(self, cr, uid, ids, model_id, context=None): def onchange_model_id(self, cr, uid, ids, model_id, context=None):
mod_name = False mod_name = False
if model_id: if model_id:
mod_name = self.pool.get('ir.model').browse(cr, uid, model_id, context).model mod_name = self.pool.get('ir.model').browse(cr, uid, model_id, context).model
return {'value':{'model':mod_name}} return {'value':{'model': mod_name}}
def _lang_get(self, cr, uid, context={}):
obj = self.pool.get('res.lang')
ids = obj.search(cr, uid, [], context=context)
res = obj.read(cr, uid, ids, ['code', 'name'], context)
return [(r['code'], r['name']) for r in res] + [('','')]
_columns = { _columns = {
'name': fields.char('Name', size=250), 'name': fields.char('Name', size=250),
'model_id':fields.many2one('ir.model', 'Resource'), 'model_id': fields.many2one('ir.model', 'Related document model'),
'model': fields.related('model_id', 'model', string='Model', type="char", size=128, store=True, readonly=True), 'lang': fields.char('Language code', size=250,
'lang': fields.selection(_lang_get, 'Language', size=5, help="The default language for the email." help="Optional translation language to select when sending out an email. "
" Placeholders can be used here. " "If not set, the english version will be used. "
"eg. ${object.partner_id.lang}"), "This should usually be a placeholder expression "
'subject':fields.char( "that provides the appropriate language code, e.g. "
'Subject', "${object.partner_id.lang.code}."),
size=200, 'user_signature': fields.boolean('Signature',
help="The subject of email." help="If checked, the user's signature will be appended to the text version "
" Placeholders can be used here.", "of the message"),
translate=True), 'report_name': fields.char('Report Filename', size=200, translate=True,
'user_signature':fields.boolean( help="Name to use for the generated report file (may contain placeholders)\n"
'Signature', "The extension can be omitted and will then come from the report type."),
help="the signature from the User details" 'report_template':fields.many2one('ir.actions.report.xml', 'Optional report to print and attach'),
" will be appended to the mail"), 'ref_ir_act_window':fields.many2one('ir.actions.act_window', 'Sidebar action', readonly=True,
'report_name':fields.char( help="Sidebar action to make this template available on records "
'Report Filename', "of the related document model"),
size=200, 'ref_ir_value':fields.many2one('ir.values', 'Sidebar button', readonly=True,
help="Name of the generated report file. Placeholders can be used in the filename. eg: 2009_SO003.pdf", help="Sidebar button to open the sidebar action"),
translate=True),
'report_template':fields.many2one(
'ir.actions.report.xml',
'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(
'ir.actions.act_window',
'Window Action',
help="Action that will open this email template on Resource records",
readonly=True),
'ref_ir_value':fields.many2one(
'ir.values',
'Wizard Button',
help="Button in the side bar of the form view of this Resource that will invoke the Window Action",
readonly=True),
'model_object_field':fields.many2one(
'ir.model.fields',
string="Field",
help="Select the field from the model you want to use."
"\nIf it is a relationship field you will be able to "
"choose the nested values in the box below\n(Note:If "
"there are no values make sure you have selected the"
" correct model)"),
'sub_object':fields.many2one(
'ir.model',
'Sub-model',
help='When a relation field is used this field'
' will show you the type of field you have selected'),
'sub_model_object_field':fields.many2one(
'ir.model.fields',
'Sub Field',
help="When you choose relationship fields "
"this field will specify the sub value you can use."),
'null_value':fields.char(
'Null Value',
help="This Value is used if the field is empty",
size=50),
'copyvalue':fields.char(
'Expression',
size=100,
help="Copy and paste the value in the "
"location you want to use a system value."),
'auto_delete': fields.boolean('Auto Delete', help="Permanently delete emails after sending"), 'auto_delete': fields.boolean('Auto Delete', help="Permanently delete emails after sending"),
'model': fields.related('model_id','model', type='char', size=128, string='Object', help="Placeholders can be used here."), 'track_campaign_item': fields.boolean('Resource Tracking',
'email_from': fields.char('From', size=128, help="Email From. Placeholders can be used here."), help="Enable this is you wish to include a special tracking marker "
'email_to': fields.char('To', size=256, help="Email Recipients. Placeholders can be used here."), "in outgoing emails so you can identify replies and link "
'email_cc': fields.char('Cc', size=256, help="Carbon Copy Email Recipients. Placeholders can be used here."), "them back to the corresponding resource record. "
'email_bcc': fields.char('Bcc', size=256, help="Blind Carbon Copy Email Recipients. Placeholders can be used here."), "This is useful for CRM leads for example"),
'reply_to':fields.char('Reply-To', size=250, help="Placeholders can be used here."),
'body': fields.text('Description', translate=True, help="Placeholders can be used here."), # Overridden mail.message.common fields for technical reasons:
'body_html': fields.text('HTML', help="Contains HTML version of email. Placeholders can be used here."), 'model': fields.related('model_id','model', type='char', string='Related Document model',
size=128, select=True, store=True, readonly=True),
# we need a separate m2m table to avoid ID collisions with the original mail.message entries
'attachment_ids': fields.many2many('ir.attachment', 'email_template_attachment_rel', 'email_template_id',
'attachment_id', 'Files to attach',
help="You may attach files to this template, to be added to all "
"emails created from this template"),
# Overridden mail.message.common fields to make tooltips more appropriate:
'subject':fields.char('Subject', size=512, translate=True, help="Subject (placeholders may be used here)",),
'email_from': fields.char('From', size=128, help="Sender address (placeholders may be used here)"),
'email_to': fields.char('To', size=256, help="Comma-separated recipient addresses (placeholders may be used here)"),
'email_cc': fields.char('Cc', size=256, help="Carbon copy recipients (placeholders may be used here)"),
'email_bcc': fields.char('Bcc', size=256, help="Blind carbon copy recipients (placeholders may be used here)"),
'reply_to': fields.char('Reply-To', size=250, help="Preferred response address (placeholders may be used here)"),
'body_text': fields.text('Text contents', translate=True, help="Plaintext version of the message (placeholders may be used here)"),
'body_html': fields.text('Rich-text contents', help="Rich-text/HTML version of the message (placeholders may be used here)"),
'message_id': fields.char('Message-Id', size=256, help="Message-ID SMTP header to use in outgoing messages based on this template. "
"Please note that this overrides the 'Resource Tracking' option, "
"so if you simply need to track replies to outgoing emails, enable "
"that option instead.\n"
"Placeholders must be used here, as this value always needs to be unique!"),
# Fake fields used to implement the placeholder assistant
'model_object_field': fields.many2one('ir.model.fields', string="Field",
help="Select target field from the related document model.\n"
"If it is a relationship field you will be able to select "
"a target field at the destination of the relationship."),
'sub_object': fields.many2one('ir.model', 'Sub-model', readonly=True,
help="When a relationship field is selected as first field, "
"this field shows the document model the relationship goes to."),
'sub_model_object_field': fields.many2one('ir.model.fields', 'Sub-field',
help="When a relationship field is selected as first field, "
"this field lets you select the target field within the "
"destination document model (sub-model)."),
'null_value': fields.char('Null value', help="Optional value to use if the target field is empty", size=128),
'copyvalue': fields.char('Expression', size=256, help="Final placeholder expression, to be copy-pasted in the desired template field."),
} }
def create_action(self, cr, uid, ids, context=None): def create_action(self, cr, uid, ids, context=None):
vals = {} vals = {}
if context is None:
context = {}
action_obj = self.pool.get('ir.actions.act_window') action_obj = self.pool.get('ir.actions.act_window')
data_obj = self.pool.get('ir.model.data') data_obj = self.pool.get('ir.model.data')
for template in self.browse(cr, uid, ids, context=context): for template in self.browse(cr, uid, ids, context=context):
@ -159,7 +169,7 @@ class email_template(osv.osv):
vals['ref_ir_act_window'] = action_obj.create(cr, uid, { vals['ref_ir_act_window'] = action_obj.create(cr, uid, {
'name': template.name, 'name': template.name,
'type': 'ir.actions.act_window', 'type': 'ir.actions.act_window',
'res_model': 'email.compose.message', 'res_model': 'mail.compose.message',
'src_model': src_obj, 'src_model': src_obj,
'view_type': 'form', 'view_type': 'form',
'context': "{'mass_mail':True}", 'context': "{'mass_mail':True}",
@ -201,16 +211,17 @@ class email_template(osv.osv):
if default is None: if default is None:
default = {} default = {}
default = default.copy() default = default.copy()
default['name'] = template.name or '' + '(copy)' default['name'] = template.name + _('(copy)')
return super(email_template, self).copy(cr, uid, id, default, context) return super(email_template, self).copy(cr, uid, id, default, context)
def build_expression(self, field_name, sub_field_name, null_value): def build_expression(self, field_name, sub_field_name, null_value):
""" """Returns a placeholder expression for use in a template field,
Returns a template expression based on data provided based on the values provided in the placeholder assistant.
@param field_name: field name
@param sub_field_name: sub field name (M2O) :param field_name: main field name
@param null_value: default value if the target value is empty :param sub_field_name: sub field name (M2O)
@return: computed expression :param null_value: default value if the target value is empty
:return: final placeholder expression
""" """
expression = '' expression = ''
if field_name: if field_name:
@ -221,78 +232,6 @@ class email_template(osv.osv):
expression += " or '''%s'''" % null_value expression += " or '''%s'''" % null_value
expression += "}" expression += "}"
return expression return expression
#
# def onchange_model_object_field(self, cr, uid, ids, model_object_field, context=None):
# if not model_object_field:
# return {}
# result = {}
# field_obj = self.pool.get('ir.model.fields').browse(cr, uid, model_object_field, context)
# #Check if field is relational
# if field_obj.ttype in ['many2one', 'one2many', 'many2many']:
# res_ids = self.pool.get('ir.model').search(cr, uid, [('model', '=', field_obj.relation)], context=context)
# if res_ids:
# result['sub_object'] = res_ids[0]
# result['copyvalue'] = self.build_expression(False, False, False)
# result['sub_model_object_field'] = False
# result['null_value'] = False
# else:
# #Its a simple field... just compute placeholder
# result['sub_object'] = False
# result['copyvalue'] = self.build_expression(field_obj.name, False, False)
# result['sub_model_object_field'] = False
# result['null_value'] = False
# return {'value':result}
#
# def onchange_sub_model_object_field(self, cr, uid, ids, model_object_field, sub_model_object_field, context=None):
# if not model_object_field or not sub_model_object_field:
# return {}
# result = {}
# field_obj = self.pool.get('ir.model.fields').browse(cr, uid, model_object_field, context)
# if field_obj.ttype in ['many2one', 'one2many', 'many2many']:
# res_ids = self.pool.get('ir.model').search(cr, uid, [('model', '=', field_obj.relation)], context=context)
# sub_field_obj = self.pool.get('ir.model.fields').browse(cr, uid, sub_model_object_field, context)
# if res_ids:
# result['sub_object'] = res_ids[0]
# result['copyvalue'] = self.build_expression(field_obj.name, sub_field_obj.name, False)
# result['sub_model_object_field'] = sub_model_object_field
# result['null_value'] = False
# else:
# #Its a simple field... just compute placeholder
# result['sub_object'] = False
# result['copyvalue'] = self.build_expression(field_obj.name, False, False)
# result['sub_model_object_field'] = False
# result['null_value'] = False
# return {'value':result}
#
#
# def onchange_null_value(self, cr, uid, ids, model_object_field, sub_model_object_field, null_value, template_language, context=None):
# if not model_object_field and not null_value:
# return {}
# result = {}
# field_obj = self.pool.get('ir.model.fields').browse(cr, uid, model_object_field, context)
# if field_obj.ttype in ['many2one', 'one2many', 'many2many']:
# res_ids = self.pool.get('ir.model').search(cr, uid, [('model', '=', field_obj.relation)], context=context)
# sub_field_obj = self.pool.get('ir.model.fields').browse(cr, uid, sub_model_object_field, context)
# if res_ids:
# result['sub_object'] = res_ids[0]
# result['copyvalue'] = self.build_expression(field_obj.name,
# sub_field_obj.name,
# null_value,
# template_language
# )
# result['sub_model_object_field'] = sub_model_object_field
# result['null_value'] = null_value
# else:
# #Its a simple field... just compute placeholder
# result['sub_object'] = False
# result['copyvalue'] = self.build_expression(field_obj.name,
# False,
# null_value,
# template_language
# )
# result['sub_model_object_field'] = False
# result['null_value'] = null_value
# return {'value':result}
def onchange_sub_model_object_value_field(self, cr, uid, ids, model_object_field, sub_model_object_field=False, null_value=None, context=None): def onchange_sub_model_object_value_field(self, cr, uid, ids, model_object_field, sub_model_object_field=False, null_value=None, context=None):
result = { result = {
@ -324,16 +263,23 @@ class email_template(osv.osv):
return {'value':result} return {'value':result}
def generate_email(self, cr, uid, template_id, record_id, context=None): def generate_email(self, cr, uid, template_id, res_id, context=None):
""" """Generates an email from the template for given (model, res_id) pair.
Generates an email from the template for
record record_id of target object :param template_id: id of the template to render.
:param res_id: id of the record to use for rendering the template (model
is taken from template definition)
:returns: a dict containing all relevant fields for creating a new
mail.message entry, with the addition one additional
special key ``attachments`` containing a list of
""" """
if context is None: if context is None:
context = {} context = {}
values = { values = {
'subject': False, 'subject': False,
'body': False, 'body_text': False,
'body_html': False,
'email_from': False,
'email_to': False, 'email_to': False,
'email_cc': False, 'email_cc': False,
'email_bcc': False, 'email_bcc': False,
@ -341,70 +287,87 @@ class email_template(osv.osv):
'auto_delete': False, 'auto_delete': False,
'model': False, 'model': False,
'res_id': False, 'res_id': False,
'smtp_server_id': False, 'mail_server_id': False,
'attachment': False, 'attachments': False,
'attachment_ids': False, 'attachment_ids': False,
'message_id': False,
'state': 'outgoing',
} }
if not template_id: if not template_id:
return values return values
report_xml_pool = self.pool.get('ir.actions.report.xml') report_xml_pool = self.pool.get('ir.actions.report.xml')
template = self.get_email_template(cr, uid, template_id, record_id, context) template = self.get_email_template(cr, uid, template_id, res_id, context)
def _get_template_value(field):
if context.get('mass_mail', False): # Mass Mail: Gets original template values for multiple email change
return getattr(template, field)
else:
return self.get_template_value(cr, uid, getattr(template, field), template.model, record_id, context=context)
body = _get_template_value('body') for field in ['subject', 'body_text', 'body_html', 'email_from',
'email_to', 'email_cc', 'email_bcc', 'reply_to',
'message_id']:
values[field] = self.render_template(cr, uid, getattr(template, field),
template.model, res_id, context=context) \
or False
#Use signatures if allowed
if template.user_signature: if template.user_signature:
signature = self.pool.get('res.users').browse(cr, uid, uid, context).signature signature = self.pool.get('res.users').browse(cr, uid, uid, context).signature
body += '\n' + signature values['body_text'] += '\n\n' + signature
values = { values.update(mail_server_id = template.mail_server_id.id or False,
'smtp_server_id' : template.smtp_server_id.id, auto_delete = template.auto_delete,
'body' : body, model=template.model,
'email_to' : _get_template_value('email_to') or False, res_id=res_id or False)
'email_cc' : _get_template_value('email_cc') or False,
'email_bcc' : _get_template_value('email_bcc') or False,
'reply_to' : _get_template_value('reply_to') or False,
'subject' : _get_template_value('subject') or False,
'auto_delete': template.auto_delete,
'model' : template.model or False,
'res_id' : record_id or False,
#'body_html': self.get_template_value(cr, uid, template.body_html, model, record_id, context),
}
attachment = {} attachments = {}
# Add report as a Document # Add report as a Document
if template.report_template: if template.report_template:
report_name = template.report_name report_name = template.report_name
reportname = 'report.' + report_xml_pool.browse(cr, uid, template.report_template.id, context).report_name report_service = 'report.' + report_xml_pool.browse(cr, uid, template.report_template.id, context).report_name
data = {}
data['model'] = template.model
# Ensure report is rendered using template's language # Ensure report is rendered using template's language
ctx = context.copy() ctx = context.copy()
if template.lang: if template.lang:
ctx['lang'] = self.get_template_value(cr, uid, template.lang, template.model, record_id, context) ctx['lang'] = self.render_template(cr, uid, template.lang, template.model, res_id, context)
service = netsvc.LocalService(reportname) service = netsvc.LocalService(report_service)
(result, format) = service.create(cr, uid, [record_id], data, ctx) (result, format) = service.create(cr, uid, [res_id], {'model': template.model}, ctx)
result = base64.b64encode(result) result = base64.b64encode(result)
if not report_name: if not report_name:
report_name = reportname report_name = report_service
report_name = report_name + "." + format ext = "." + format
attachment[report_name] = result if not report_name.endswith(ext):
report_name += ext
attachments[report_name] = result
# Add document attachments # Add document attachments
for attach in template.attachment_ids: for attach in template.attachment_ids:
#attach = attahcment_obj.browse(cr, uid, attachment_id, context) # keep the bytes as fetched from the db, base64 encoded
attachment[attach.datas_fname] = base64.decodestring(attach.datas) attachments[attach.datas_fname] = attach.datas
values['attachment'] = attachment
values['attachments'] = attachments
return values return values
email_template() def send_mail(self, cr, uid, template_id, res_id, context=None):
"""Generates a new mail message for the given template and record,
and schedule it for delivery through the ``mail`` module's scheduler.
:param int template_id: id of the template to render
:param int record_id: id of the record to render the template with
(model is taken from the template)
"""
mail_message = self.pool.get('mail.message')
ir_attachment = self.pool.get('ir.attachment')
template = self.browse(cr, uid, template_id, context)
values = self.generate_email(cr, uid, template_id, res_id, context=context)
attachments = values.pop('attachments')
message_id = mail_message.create(values)
# link attachments
attachment_ids = []
for fname, fcontent in values['attachments'].iteritems():
attachment_data = {
'name': fname,
'datas_fname': fname,
'datas': fcontent,
'res_model': mail_message._name,
'res_id': message_id,
}
if context.has_key('default_type'):
del context['default_type']
attachment_ids.append(ir_attachment.create(cr, uid, attachment_data, context))
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -9,14 +9,13 @@
<field name="arch" type="xml"> <field name="arch" type="xml">
<form string="Templates"> <form string="Templates">
<field name="name" required="1"/> <field name="name" required="1"/>
<field name="model_id" required="1" <field name="model_id" required="1" on_change="onchange_model_id(model_id)"/>
on_change="onchange_model_id(model_id)"/>
<field name="model" invisible="1"/> <field name="model" invisible="1"/>
<notebook colspan="4"> <notebook colspan="4">
<page string="Email Details"> <page string="Email Details">
<group col="2" colspan="2"> <group col="2" colspan="2">
<separator string="Addresses" colspan="2"/> <separator string="Addresses" colspan="2"/>
<field name="smtp_server_id"/> <field name="email_from" required="1"/>
<field name="email_to" required="1"/> <field name="email_to" required="1"/>
<field name="email_cc"/> <field name="email_cc"/>
<field name="email_bcc"/> <field name="email_bcc"/>
@ -26,24 +25,24 @@
<separator string="Options" colspan="2"/> <separator string="Options" colspan="2"/>
<field name="lang" colspan="4" /> <field name="lang" colspan="4" />
<field name="user_signature" colspan="4" /> <field name="user_signature" colspan="4" />
<field name="auto_delete"/> <field name="track_campaign_item" colspan="4"/>
</group> </group>
<group col="2" colspan="2"> <group col="2" colspan="2">
<separator colspan="2" string="Email Content"/> <separator colspan="2" string="Email Content"/>
<field name="subject" colspan="4" required="1"/> <field name="subject" colspan="4" required="1"/>
<notebook> <notebook>
<page string="Body"> <page string="Body (Text)">
<field name="body" colspan="4" nolabel="1"/> <field name="body_text" colspan="4" nolabel="1"/>
</page> </page>
<!-- <page string="Body (Raw HTML)"> <page string="Body (Rich/HTML)">
<field name="def_body_html" colspan="4" nolabel="1"/> <field name="body_html" colspan="4" nolabel="1"/>
<label string="Note: This is Raw HTML." colspan="4"/> <label string="Note: This is Raw HTML." colspan="4"/>
</page> --> </page>
</notebook> </notebook>
</group> </group>
<group col="4" colspan="2"> <group col="4" colspan="2">
<notebook> <notebook>
<page string="Insert Simple Field"> <page string="Dynamic Values Builder">
<field name="model_object_field" <field name="model_object_field"
domain="[('model_id','=',model_id),('ttype','!=','one2many'),('ttype','!=','many2many')]" domain="[('model_id','=',model_id),('ttype','!=','one2many'),('ttype','!=','many2many')]"
on_change="onchange_sub_model_object_value_field(model_object_field)" on_change="onchange_sub_model_object_value_field(model_object_field)"
@ -64,26 +63,38 @@
</group> </group>
</page> </page>
<page string="Advanced"> <page string="Advanced">
<field name="mail_server_id"/>
<group colspan="2" col="2"> <group colspan="2" col="2">
<group colspan="2" col="2"> <group colspan="2" col="2">
<separator string="Actions" colspan="2"/> <separator string="Sidebar button" colspan="2"/>
<button name="create_action" string="Create Action" type="object" icon="gtk-execute" colspan="2" attrs="{'invisible':[('ref_ir_act_window','!=',False), ('ref_ir_value','!=',False)]}"/> <button name="create_action" string="Add sidebar button" type="object" icon="gtk-execute"
<field name="ref_ir_act_window"/> colspan="2" attrs="{'invisible':[('ref_ir_act_window','!=',False), ('ref_ir_value','!=',False)]}"
<field name="ref_ir_value"/> help="Display a button in the sidebar of related documents to open a composition wizard with this template"
<button name="unlink_action" string="Delete Action" type="object" icon="gtk-delete" colspan="2" attrs="{'invisible':[('ref_ir_act_window','=',False), ('ref_ir_value','=',False)]}"/> />
<field name="ref_ir_act_window" attrs="{'invisible':[('ref_ir_act_window','!=',False), ('ref_ir_value','!=',False)]}"/>
<field name="ref_ir_value" attrs="{'invisible':[('ref_ir_act_window','!=',False), ('ref_ir_value','!=',False)]}"/>
<button name="unlink_action" string="Remove sidebar button" type="object" icon="gtk-delete"
colspan="2" attrs="{'invisible':[('ref_ir_act_window','=',False), ('ref_ir_value','=',False)]}"
help="Remove the sidebar button currentlu displayed on related documents"
/>
</group>
<group colspan="2" col="2">
<separator string="Advanced Options" colspan="2"/>
<field name="message_id"/>
<field name="auto_delete"/>
</group> </group>
</group> </group>
<group colspan="2" col="2"> <group colspan="2" col="2">
<separator string="Attachments" colspan="2"/> <separator string="Attachments" colspan="2"/>
<notebook> <notebook>
<page string="Existing files"> <page string="Attach Report">
<field name="attachment_ids" colspan="4" nolabel="1" height="350"/>
</page>
<page string="Report">
<field name="report_template" colspan="4" <field name="report_template" colspan="4"
domain="[('model','=',model)]"/> domain="[('model','=',model)]"/>
<field name="report_name" colspan="4" /> <field name="report_name" colspan="4" />
</page> </page>
<page string="Attach existing files">
<field name="attachment_ids" colspan="4" nolabel="1" height="350"/>
</page>
</notebook> </notebook>
</group> </group>
</page> </page>

View File

@ -1,60 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2009 Sharoon Thomas
# Copyright (C) 2010-Today OpenERP SA (<http://www.openerp.com>)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU 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 tools
import pooler
import logging
try:
from mako.template import Template as MakoTemplate
except ImportError:
logging.getLogger('init').warning("module email_template: Mako templates not installed")
def get_value(cr, uid, message=None, model=None, record_id=False, context=None):
"""
returns Messages in Mako Template
"""
pool = pooler.get_pool(cr.dbname)
if message is None:
message = {}
#Returns the computed expression
if message:
try:
message = tools.ustr(message)
record = pool.get(model).browse(cr, uid, record_id, context=context)
env = {
'user': pool.get('res.users').browse(cr, uid, uid, context=context),
'db': cr.dbname
}
templ = MakoTemplate(message, input_encoding='utf-8')
reply = MakoTemplate(message).render_unicode(object=record, peobject=record, env=env, format_exceptions=True)
if reply == 'False':
reply = ''
return reply
except Exception:
logging.exception("can't render %r", message)
return u""
else:
return message
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -22,11 +22,10 @@
from osv import osv from osv import osv
from osv import fields from osv import fields
from tools.translate import _ from tools.translate import _
import binascii import base64
class email_compose_message(osv.osv_memory): class mail_compose_message(osv.osv_memory):
_name = 'email.compose.message' _inherit = 'mail.compose.message'
_inherit = 'email.compose.message'
def _get_templates(self, cr, uid, context=None): def _get_templates(self, cr, uid, context=None):
""" """
@ -38,8 +37,8 @@ class email_compose_message(osv.osv_memory):
email_temp_pool = self.pool.get('email.template') email_temp_pool = self.pool.get('email.template')
model = False model = False
if context.get('message_id'): if context.get('message_id'):
message_pool = self.pool.get('email.message') mail_message = self.pool.get('mail.message')
message_data = message_pool.browse(cr, uid, int(context.get('message_id')), context) message_data = mail_message.browse(cr, uid, int(context.get('message_id')), context)
model = message_data.model model = message_data.model
elif context.get('active_model', False): elif context.get('active_model', False):
model = context.get('active_model') model = context.get('active_model')
@ -52,10 +51,6 @@ class email_compose_message(osv.osv_memory):
'template_id': fields.selection(_get_templates, 'Template'), 'template_id': fields.selection(_get_templates, 'Template'),
} }
def get_template_value(self, cr, uid, message, model, resource_id, context=None):
template_pool = self.pool.get('email.template')
return template_pool.get_template_value(cr, uid, message, model, resource_id, context)
def on_change_template(self, cr, uid, ids, template_id, context=None): def on_change_template(self, cr, uid, ids, template_id, context=None):
if context is None: if context is None:
context = {} context = {}
@ -64,17 +59,17 @@ class email_compose_message(osv.osv_memory):
if template_id: if template_id:
res_id = context.get('active_id', False) res_id = context.get('active_id', False)
values = self.pool.get('email.template').generate_email(cr, uid, template_id, res_id, context=context) values = self.pool.get('email.template').generate_email(cr, uid, template_id, res_id, context=context)
if values['attachment']: if values['attachments']:
attachment = values['attachment'] attachment = values.pop('attachments')
attachment_obj = self.pool.get('ir.attachment') attachment_obj = self.pool.get('ir.attachment')
for fname, fcontent in attachment.items(): for fname, fcontent in attachment.iteritems():
data_attach = { data_attach = {
'name': fname, 'name': fname,
'datas': binascii.b2a_base64(str(fcontent)), 'datas': base64.b64_encode(fcontent),
'datas_fname': fname, 'datas_fname': fname,
'description': _('Mail attachment'), 'description': fname,
'res_model' : self._name, 'res_model' : self._name,
'res_id' : ids and ids[0] or False 'res_id' : ids[0] if ids else False
} }
att_ids.append(attachment_obj.create(cr, uid, data_attach)) att_ids.append(attachment_obj.create(cr, uid, data_attach))
values['attachment_ids'] = att_ids values['attachment_ids'] = att_ids
@ -109,6 +104,5 @@ class email_compose_message(osv.osv_memory):
template_pool.create(cr, uid, values, context=context) template_pool.create(cr, uid, values, context=context)
return {'type': 'ir.actions.act_window_close'} return {'type': 'ir.actions.act_window_close'}
email_compose_message()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -3,8 +3,8 @@
<data> <data>
<record model="ir.ui.view" id="email_compose_message_wizard_inherit_form"> <record model="ir.ui.view" id="email_compose_message_wizard_inherit_form">
<field name="name">email.compose.message.form</field> <field name="name">mail.compose.message.form</field>
<field name="model">email.compose.message</field> <field name="model">mail.compose.message</field>
<field name="type">form</field> <field name="type">form</field>
<field name="inherit_id" ref="mail.email_compose_message_wizard_form"/> <field name="inherit_id" ref="mail.email_compose_message_wizard_form"/>
<field name="arch" type="xml"> <field name="arch" type="xml">

View File

@ -82,19 +82,17 @@ class email_template_preview(osv.osv_memory):
template_id = context.get('template_id', False) template_id = context.get('template_id', False)
template = template_pool.get_email_template(cr, uid, template_id=template_id, record_id=res_id, context=context) template = template_pool.get_email_template(cr, uid, template_id=template_id, record_id=res_id, context=context)
model = template.model model = template.model
vals['email_to'] = self.get_template_value(cr, uid, template.email_to, model, res_id, context) vals['email_to'] = self.render_template(cr, uid, template.email_to, model, res_id, context)
vals['email_cc'] = self.get_template_value(cr, uid, template.email_cc, model, res_id, context) vals['email_cc'] = self.render_template(cr, uid, template.email_cc, model, res_id, context)
vals['email_bcc'] = self.get_template_value(cr, uid, template.email_bcc, model, res_id, context) vals['email_bcc'] = self.render_template(cr, uid, template.email_bcc, model, res_id, context)
vals['reply_to'] = self.get_template_value(cr, uid, template.reply_to, model, res_id, context) vals['reply_to'] = self.render_template(cr, uid, template.reply_to, model, res_id, context)
vals['subject'] = self.get_template_value(cr, uid, template.subject, model, res_id, context) vals['subject'] = self.render_template(cr, uid, template.subject, model, res_id, context)
description = self.get_template_value(cr, uid, template.body, model, res_id, context) or '' description = self.render_template(cr, uid, template.body, model, res_id, context) or ''
if template.user_signature: if template.user_signature:
signature = self.pool.get('res.users').browse(cr, uid, uid, context).signature signature = self.pool.get('res.users').browse(cr, uid, uid, context).signature
description += '\n' + signature description += '\n' + signature
vals['body'] = description vals['body'] = description
vals['report_name'] = self.get_template_value(cr, uid, template.report_name, model, res_id, context) vals['report_name'] = self.render_template(cr, uid, template.report_name, model, res_id, context)
return {'value':vals} return {'value': vals}
email_template_preview()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: