1 # -*- coding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2009 Sharoon Thomas
6 # Copyright (C) 2010-Today OpenERP SA (<http://www.openerp.com>)
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program. If not, see <http://www.gnu.org/licenses/>
21 ##############################################################################
24 from osv import fields
30 from tools.translate import _
34 class email_template(osv.osv):
35 "Templates for sending Email"
36 _inherit = 'email.message.common'
37 _name = "email.template"
38 _description = 'Email Templates for Models'
40 def get_template_value(self, cr, uid, message=None, model=None, record_id=None, context=None):
42 return mako_template.get_value(cr, uid, message=message, model=model, record_id=record_id, context=context)
44 def get_email_template(self, cr, uid, template_id=False, record_id=None, context=None):
45 "Return Template Object"
49 template_id = context.get('template_id', False)
53 template = self.browse(cr, uid, int(template_id), context)
54 lang = self.get_template_value(cr, uid, template.lang, template.model, record_id, context)
56 # Use translated template if necessary
59 template = self.browse(cr, uid, template.id, ctx)
62 def onchange_model_id(self, cr, uid, ids, model_id, context=None):
65 mod_name = self.pool.get('ir.model').browse(cr, uid, model_id, context).model
66 return {'value':{'model':mod_name}}
69 'name': fields.char('Name', size=250),
70 'model_id':fields.many2one('ir.model', 'Resource'),
71 'model': fields.related('model_id', 'model', string='Model', type="char", size=128, store=True, readonly=True),
72 'track_campaign_item':fields.boolean('Resource Tracking',
73 help="Enable this is you wish to include a special \
74 tracking marker in outgoing emails so you can identify replies and link \
75 them back to the corresponding resource record. \
76 This is useful for CRM leads for example"),
80 help="The default language for the email."
81 " Placeholders can be used here. "
82 "eg. ${object.partner_id.lang}"),
83 'subject':fields.char(
86 help="The subject of email."
87 " Placeholders can be used here.",
89 # 'description':fields.text(
90 # 'Standard Body (Text)',
91 # help="The text version of the mail",
93 # 'body_html':fields.text(
94 # 'Body (Text-Web Client Only)',
95 # help="The text version of the mail",
97 'user_signature':fields.boolean(
99 help="the signature from the User details"
100 " will be appended to the mail"),
101 'report_name':fields.char(
104 help="Name of the generated report file. Placeholders can be used in the filename. eg: 2009_SO003.pdf",
106 'report_template':fields.many2one(
107 'ir.actions.report.xml',
109 'attachment_ids': fields.many2many(
111 'email_template_attachment_rel',
115 help="You may attach existing files to this template, "
116 "so they will be added in all emails created from this template"),
117 'ref_ir_act_window':fields.many2one(
118 'ir.actions.act_window',
120 help="Action that will open this email template on Resource records",
122 'ref_ir_value':fields.many2one(
125 help="Button in the side bar of the form view of this Resource that will invoke the Window Action",
127 'model_object_field':fields.many2one(
130 help="Select the field from the model you want to use."
131 "\nIf it is a relationship field you will be able to "
132 "choose the nested values in the box below\n(Note:If "
133 "there are no values make sure you have selected the"
135 'sub_object':fields.many2one(
138 help='When a relation field is used this field'
139 ' will show you the type of field you have selected'),
140 'sub_model_object_field':fields.many2one(
143 help="When you choose relationship fields "
144 "this field will specify the sub value you can use."),
145 'null_value':fields.char(
147 help="This Value is used if the field is empty",
149 'copyvalue':fields.char(
152 help="Copy and paste the value in the "
153 "location you want to use a system value."),
154 'auto_delete': fields.boolean('Auto Delete', help="Permanently delete emails after sending"),
155 'model': fields.related('model_id','model', type='char', size=128, string='Object'),
159 ('name', 'unique (name)','The template name must be unique !')
162 def create_action(self, cr, uid, ids, context=None):
166 action_obj = self.pool.get('ir.actions.act_window')
167 data_obj = self.pool.get('ir.model.data')
168 for template in self.browse(cr, uid, ids, context=context):
169 src_obj = template.model_id.model
170 model_data_id = data_obj._get_id(cr, uid, 'email', 'email_compose_message_wizard_form')
171 res_id = data_obj.browse(cr, uid, model_data_id, context=context).res_id
172 vals['ref_ir_act_window'] = action_obj.create(cr, uid, {
173 'name': template.name,
174 'type': 'ir.actions.act_window',
175 'res_model': 'email.compose.message',
176 'src_model': src_obj,
178 'context': "{'email_model':'%s', 'email_res_id': active_id,'template_id':'%d','src_rec_id':active_id,'src_rec_ids':active_ids}" % (src_obj, template.id),
179 'view_mode':'form,tree',
184 vals['ref_ir_value'] = self.pool.get('ir.values').create(cr, uid, {
185 'name': _('Send Mail (%s)') % template.name,
187 'key2': 'client_action_multi',
188 'value': "ir.actions.act_window," + str(vals['ref_ir_act_window']),
191 self.write(cr, uid, ids, {
192 'ref_ir_act_window': vals.get('ref_ir_act_window',False),
193 'ref_ir_value': vals.get('ref_ir_value',False),
197 def unlink_action(self, cr, uid, ids, context=None):
198 for template in self.browse(cr, uid, ids, context=context):
200 if template.ref_ir_act_window:
201 self.pool.get('ir.actions.act_window').unlink(cr, uid, template.ref_ir_act_window.id, context)
202 if template.ref_ir_value:
203 self.pool.get('ir.values').unlink(cr, uid, template.ref_ir_value.id, context)
205 raise osv.except_osv(_("Warning"), _("Deletion of Record failed"))
207 def delete_action(self, cr, uid, ids, context=None):
208 self.unlink_action(cr, uid, ids, context=context)
211 def unlink(self, cr, uid, ids, context=None):
212 self.unlink_action(cr, uid, ids, context=context)
213 return super(email_template, self).unlink(cr, uid, ids, context=context)
215 def copy(self, cr, uid, id, default=None, context=None):
218 default = default.copy()
219 old = self.read(cr, uid, id, ['name'], context=context)
220 new_name = _("Copy of template ") + old.get('name', 'No Name')
221 check = self.search(cr, uid, [('name', '=', new_name)], context=context)
223 new_name = new_name + '_' + random.choice('abcdefghij') + random.choice('lmnopqrs') + random.choice('tuvwzyz')
224 default.update({'name':new_name})
225 return super(email_template, self).copy(cr, uid, id, default, context)
227 def build_expression(self, field_name, sub_field_name, null_value):
229 Returns a template expression based on data provided
230 @param field_name: field name
231 @param sub_field_name: sub field name (M2O)
232 @param null_value: default value if the target value is empty
233 @return: computed expression
237 expression = "${object." + field_name
239 expression += "." + sub_field_name
241 expression += " or '''%s'''" % null_value
245 # def onchange_model_object_field(self, cr, uid, ids, model_object_field, context=None):
246 # if not model_object_field:
249 # field_obj = self.pool.get('ir.model.fields').browse(cr, uid, model_object_field, context)
250 # #Check if field is relational
251 # if field_obj.ttype in ['many2one', 'one2many', 'many2many']:
252 # res_ids = self.pool.get('ir.model').search(cr, uid, [('model', '=', field_obj.relation)], context=context)
254 # result['sub_object'] = res_ids[0]
255 # result['copyvalue'] = self.build_expression(False, False, False)
256 # result['sub_model_object_field'] = False
257 # result['null_value'] = False
259 # #Its a simple field... just compute placeholder
260 # result['sub_object'] = False
261 # result['copyvalue'] = self.build_expression(field_obj.name, False, False)
262 # result['sub_model_object_field'] = False
263 # result['null_value'] = False
264 # return {'value':result}
266 # def onchange_sub_model_object_field(self, cr, uid, ids, model_object_field, sub_model_object_field, context=None):
267 # if not model_object_field or not sub_model_object_field:
270 # field_obj = self.pool.get('ir.model.fields').browse(cr, uid, model_object_field, context)
271 # if field_obj.ttype in ['many2one', 'one2many', 'many2many']:
272 # res_ids = self.pool.get('ir.model').search(cr, uid, [('model', '=', field_obj.relation)], context=context)
273 # sub_field_obj = self.pool.get('ir.model.fields').browse(cr, uid, sub_model_object_field, context)
275 # result['sub_object'] = res_ids[0]
276 # result['copyvalue'] = self.build_expression(field_obj.name, sub_field_obj.name, False)
277 # result['sub_model_object_field'] = sub_model_object_field
278 # result['null_value'] = False
280 # #Its a simple field... just compute placeholder
281 # result['sub_object'] = False
282 # result['copyvalue'] = self.build_expression(field_obj.name, False, False)
283 # result['sub_model_object_field'] = False
284 # result['null_value'] = False
285 # return {'value':result}
288 # def onchange_null_value(self, cr, uid, ids, model_object_field, sub_model_object_field, null_value, template_language, context=None):
289 # if not model_object_field and not null_value:
292 # field_obj = self.pool.get('ir.model.fields').browse(cr, uid, model_object_field, context)
293 # if field_obj.ttype in ['many2one', 'one2many', 'many2many']:
294 # res_ids = self.pool.get('ir.model').search(cr, uid, [('model', '=', field_obj.relation)], context=context)
295 # sub_field_obj = self.pool.get('ir.model.fields').browse(cr, uid, sub_model_object_field, context)
297 # result['sub_object'] = res_ids[0]
298 # result['copyvalue'] = self.build_expression(field_obj.name,
299 # sub_field_obj.name,
303 # result['sub_model_object_field'] = sub_model_object_field
304 # result['null_value'] = null_value
306 # #Its a simple field... just compute placeholder
307 # result['sub_object'] = False
308 # result['copyvalue'] = self.build_expression(field_obj.name,
313 # result['sub_model_object_field'] = False
314 # result['null_value'] = null_value
315 # return {'value':result}
317 def onchange_sub_model_object_value_field(self, cr, uid, ids, model_object_field, sub_model_object_field=False, null_value=None, context=None):
321 'sub_model_object_field': False,
324 if model_object_field:
325 fields_obj = self.pool.get('ir.model.fields')
326 field_value = fields_obj.browse(cr, uid, model_object_field, context)
327 if field_value.ttype in ['many2one', 'one2many', 'many2many']:
328 res_ids = self.pool.get('ir.model').search(cr, uid, [('model', '=', field_value.relation)], context=context)
329 sub_field_value = False
330 if sub_model_object_field:
331 sub_field_value = fields_obj.browse(cr, uid, sub_model_object_field, context)
334 'sub_object': res_ids[0],
335 'copyvalue': self.build_expression(field_value.name, sub_field_value and sub_field_value.name or False, null_value or False),
336 'sub_model_object_field': sub_model_object_field or False,
337 'null_value': null_value or False
341 'copyvalue': self.build_expression(field_value.name, False, null_value or False),
342 'null_value': null_value or False
344 return {'value':result}
347 def _generate_email(self, cr, uid, template_id, record_id, context=None):
349 Generates an email from the template for
350 record record_id of target object
354 smtp_pool = self.pool.get('email.smtp_server')
355 email_message_pool = self.pool.get('email.message')
356 report_xml_pool = self.pool.get('ir.actions.report.xml')
357 template = self.get_email_template(cr, uid, template_id, record_id, context)
358 smtp_server_id = context.get('smtp_server_id', False)
359 if not smtp_server_id and template.smtp_server_id:
360 smtp_server_id = template.smtp_server_id.id
362 smtp_ids = smtp_pool.search(cr, uid, [('default','=',True)])
363 smtp_server_id = smtp_ids and smtp_ids[0]
364 smtp_server = smtp_pool.browse(cr, uid, smtp_server_id, context=context)
365 # determine name of sender, either it is specified in email_id
367 email_id = smtp_server.email_id.strip()
368 email_from = re.findall(r'([^ ,<@]+@[^> ,]+)', email_id)[0]
369 if email_from != email_id:
370 email_from = smtp_server.email_id
372 email_from = tools.ustr(smtp_server.name) + "<" + tools.ustr(email_id) + ">"
374 model = template.model_id.model
376 'email_from': email_from,
377 'email_to': self.get_template_value(cr, uid, template.email_to, model, record_id, context),
378 'email_cc': self.get_template_value(cr, uid, template.email_cc, model, record_id, context),
379 'email_bcc': self.get_template_value(cr, uid, template.email_bcc, model, record_id, context),
380 'reply_to': self.get_template_value(cr, uid, template.reply_to, model, record_id, context),
381 'name': self.get_template_value(cr, uid, template.subject, model, record_id, context),
382 'description': self.get_template_value(cr, uid, template.description, model, record_id, context),
383 #'body_html': self.get_template_value(cr, uid, template.body_html, model, record_id, context),
386 if template.message_id:
387 # use provided message_id with placeholders
388 values.update({'message_id': self.get_template_value(cr, uid, template.message_id, model, record_id, context)})
390 elif template['track_campaign_item']:
391 # get appropriate message-id
392 values.update({'message_id': tools.misc.generate_tracking_message_id(record_id)})
394 #Use signatures if allowed
395 if template.user_signature:
396 sign = self.pool.get('res.users').read(cr, uid, uid, ['signature'], context)['signature']
397 if values['description']:
398 values['description'] += '\n\n' + sign
399 #if values['body_html']:
400 # values['body_html'] += sign
404 # Add report as a Document
405 if template.report_template:
406 report_name = template.report_name
407 reportname = 'report.' + report_xml_pool.browse(cr, uid, template.report_template.id, context).report_name
409 data['model'] = template.model
411 # Ensure report is rendered using template's language
414 ctx['lang'] = self.get_template_value(cr, uid, template.lang, template.model, record_id, context)
415 service = netsvc.LocalService(reportname)
416 (result, format) = service.create(cr, uid, [record_id], data, ctx)
417 result = base64.b64encode(result)
419 report_name = reportname
420 report_name = report_name + "." + format
421 attachment.append((report_name, result))
424 # Add document attachments
425 for attach in template.attachment_ids:
426 #attach = attahcment_obj.browse(cr, uid, attachment_id, context)
427 attachment.append((attach.datas_fname, attach.datas))
430 context.update({'notemplate':True})
431 email_id = email_message_pool.email_send(cr, uid, values.get('email_from'), values.get('email_to'), values.get('name'), values.get('description'),
432 model=model, email_cc=values.get('email_cc'), email_bcc=values.get('email_bcc'), reply_to=values.get('reply_to'),
433 attach=attachment, message_id=values.get('message_id'), openobject_id=record_id, debug=True, subtype='plain', x_headers={}, priority='3', smtp_server_id=smtp_server.id, context=context)
434 email_message_pool.write(cr, uid, email_id, {'template_id': context.get('template_id',template.id)})
439 def generate_email(self, cr, uid, template_id, record_id, context=None):
442 email_id = self._generate_email(cr, uid, template_id, record_id, context)
448 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: