[FIX] email_template: remove `print` statements
[odoo/odoo.git] / addons / email_template / email_template.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2009  Sharoon Thomas  
6 #    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
7 #
8 #    This program is free software: you can redistribute it and/or modify
9 #    it under the terms of the GNU Affero General Public License as
10 #    published by the Free Software Foundation, either version 3 of the
11 #    License, or (at your option) any later version.
12 #
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 Affero General Public License for more details.
17 #
18 #    You should have received a copy of the GNU Affero General Public License
19 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 #
21 ##############################################################################
22
23 import base64
24 import random
25 import time
26 import types
27 import netsvc
28
29 LOGGER = netsvc.Logger()
30
31 TEMPLATE_ENGINES = []
32
33 from osv import osv, fields
34 from tools.translate import _
35 from mako.template import Template  #For backward combatibility
36 try:
37     from mako.template import Template as MakoTemplate
38     from mako import exceptions
39     TEMPLATE_ENGINES.append(('mako', 'Mako Templates'))
40 except:
41     LOGGER.notifyChannel(
42          _("Email Template"),
43          netsvc.LOG_WARNING,
44          _("Mako templates not installed")
45     )
46 try:
47     from django.template import Context, Template as DjangoTemplate
48     #Workaround for bug:
49     #http://code.google.com/p/django-tagging/issues/detail?id=110
50     from django.conf import settings
51     settings.configure()
52     #Workaround ends
53     TEMPLATE_ENGINES.append(('django', 'Django Template'))
54 except:
55     LOGGER.notifyChannel(
56          _("Email Template"),
57          netsvc.LOG_WARNING,
58          _("Django templates not installed")
59     )
60
61 import email_template_engines
62 import tools
63 import report
64 import pooler
65 import logging
66
67 def get_value(cursor, user, recid, message=None, template=None, context=None):
68     """
69     Evaluates an expression and returns its value
70     @param cursor: Database Cursor
71     @param user: ID of current user
72     @param recid: ID of the target record under evaluation
73     @param message: The expression to be evaluated
74     @param template: BrowseRecord object of the current template
75     @param context: Open ERP Context
76     @return: Computed message (unicode) or u""
77     """
78     pool = pooler.get_pool(cursor.dbname)
79     if message is None:
80         message = {}
81     #Returns the computed expression
82     if message:
83         try:
84             message = tools.ustr(message)
85             object = pool.get(template.model_int_name).browse(cursor, user, recid, context)
86             env = {
87                 'user':pool.get('res.users').browse(cursor, user, user, context),
88                 'db':cursor.dbname
89                    }
90             if template.template_language == 'mako':
91                 templ = MakoTemplate(message, input_encoding='utf-8')
92                 reply = MakoTemplate(message).render_unicode(object=object,
93                                                              peobject=object,
94                                                              env=env,
95                                                              format_exceptions=True)
96             elif template.template_language == 'django':
97                 templ = DjangoTemplate(message)
98                 env['object'] = object
99                 env['peobject'] = object
100                 reply = templ.render(Context(env))
101             return reply or False
102         except Exception:
103             logging.exception("can't render %r", message)
104             return u""
105     else:
106         return message
107
108 class email_template(osv.osv):
109     "Templates for sending Email"
110     
111     _name = "email.template"
112     _description = 'Email Templates for Models'
113
114     def change_model(self, cursor, user, ids, object_name, context=None):
115         if object_name:
116             mod_name = self.pool.get('ir.model').read(
117                                               cursor,
118                                               user,
119                                               object_name,
120                                               ['model'], context)['model']
121         else:
122             mod_name = False
123         return {
124                 'value':{'model_int_name':mod_name}
125                 }
126
127     _columns = {
128         'name' : fields.char('Name', size=100, required=True),
129         'object_name':fields.many2one('ir.model', 'Model'),
130         'model_int_name':fields.char('Model Internal Name', size=200,),
131         'enforce_from_account':fields.many2one(
132                    'email_template.account',
133                    string="Enforce From Account",
134                    help="Emails will be sent only from this account(which are approved)."),
135         'from_email' : fields.related('enforce_from_account', 'email_id',
136                                                 type='char', string='From',
137                                                 help='From Email (select mail account)',
138                                                 readonly=True),        
139         'def_to':fields.char(
140                  'Recipient (To)',
141                  size=250,
142                  help="The default recipient of email." 
143                  "Placeholders can be used here."),
144         'def_cc':fields.char(
145                  'Default CC',
146                  size=250,
147                  help="The default CC for the email."
148                  " Placeholders can be used here."),
149         'def_bcc':fields.char(
150                   'Default BCC',
151                   size=250,
152                   help="The default BCC for the email."
153                   " Placeholders can be used here."),
154         'lang':fields.char(
155                    'Language',
156                    size=250,
157                    help="The default language for the email."
158                    " Placeholders can be used here. "
159                    "eg. ${object.partner_id.lang}"),
160         'def_subject':fields.char(
161                   'Default Subject',
162                   size=200,
163                   help="The default subject of email."
164                   " Placeholders can be used here.",
165                   translate=True),
166         'def_body_text':fields.text(
167                     'Standard Body (Text)',
168                     help="The text version of the mail",
169                     translate=True),
170         'def_body_html':fields.text(
171                     'Body (Text-Web Client Only)',
172                     help="The text version of the mail",
173                     translate=True),
174         'use_sign':fields.boolean(
175                   'Signature',
176                   help="the signature from the User details" 
177                   " will be appended to the mail"),
178         'file_name':fields.char(
179                 'File Name Pattern',
180                 size=200,
181                 help="File name pattern can be specified with placeholders." 
182                 "eg. 2009_SO003.pdf",
183                 translate=True),
184         'report_template':fields.many2one(
185                   'ir.actions.report.xml',
186                   'Report to send'),
187         'ref_ir_act_window':fields.many2one(
188                     'ir.actions.act_window',
189                     'Window Action',
190                     readonly=True),
191         'ref_ir_value':fields.many2one(
192                    'ir.values',
193                    'Wizard Button',
194                    readonly=True),
195         'allowed_groups':fields.many2many(
196                   'res.groups',
197                   'template_group_rel',
198                   'templ_id', 'group_id',
199                   string="Allowed User Groups",
200                   help="Only users from these groups will be"
201                   " allowed to send mails from this Template"),
202         'model_object_field':fields.many2one(
203                  'ir.model.fields',
204                  string="Field",
205                  help="Select the field from the model you want to use."
206                  "\nIf it is a relationship field you will be able to "
207                  "choose the nested values in the box below\n(Note:If "
208                  "there are no values make sure you have selected the"
209                  " correct model)",
210                  store=False),
211         'sub_object':fields.many2one(
212                  'ir.model',
213                  'Sub-model',
214                  help='When a relation field is used this field'
215                  ' will show you the type of field you have selected',
216                  store=False),
217         'sub_model_object_field':fields.many2one(
218                  'ir.model.fields',
219                  'Sub Field',
220                  help="When you choose relationship fields "
221                  "this field will specify the sub value you can use.",
222                  store=False),
223         'null_value':fields.char(
224                  'Null Value',
225                  help="This Value is used if the field is empty",
226                  size=50, store=False),
227         'copyvalue':fields.char(
228                 'Expression',
229                 size=100,
230                 help="Copy and paste the value in the "
231                 "location you want to use a system value.",
232                 store=False),
233         'table_html':fields.text(
234              'HTML code',
235              help="Copy this html code to your HTML message"
236              " body for displaying the info in your mail.",
237              store=False),
238         #Template language(engine eg.Mako) specifics
239         'template_language':fields.selection(
240                 TEMPLATE_ENGINES,
241                 'Templating Language',
242                 required=True
243                 )
244     }
245
246     _defaults = {
247         'template_language' : lambda *a:'mako',
248
249     }
250     
251     _sql_constraints = [
252         ('name', 'unique (name)', _('The template name must be unique !'))
253     ]
254
255     def create_action(self, cr, uid, ids, context):
256         vals = {}
257         template_obj = self.browse(cr, uid, ids)[0]
258         src_obj = template_obj.object_name.model
259         vals['ref_ir_act_window'] = self.pool.get('ir.actions.act_window').create(cr, uid, {
260              'name': template_obj.name,
261              'type': 'ir.actions.act_window',
262              'res_model': 'email_template.send.wizard',
263              'src_model': src_obj,
264              'view_type': 'form',
265              'context': "{'src_model':'%s','template_id':'%d','src_rec_id':active_id,'src_rec_ids':active_ids}" % (src_obj, template_obj.id),
266              'view_mode':'form,tree',
267              'view_id': self.pool.get('ir.ui.view').search(cr, uid, [('name', '=', 'email_template.send.wizard.form')], context=context)[0],
268              'target': 'new',
269              'auto_refresh':1
270         }, context)
271         vals['ref_ir_value'] = self.pool.get('ir.values').create(cr, uid, {
272              'name': _('Send Mail (%s)') % template_obj.name,
273              'model': src_obj,
274              'key2': 'client_action_multi',
275              'value': "ir.actions.act_window," + str(vals['ref_ir_act_window']),
276              'object': True,
277          }, context)
278         self.write(cr, uid, ids, {
279             'ref_ir_act_window': vals['ref_ir_act_window'],
280             'ref_ir_value': vals['ref_ir_value'],
281         }, context)
282         return True
283
284     def unlink_action(self, cr, uid, ids, context):
285         for template in self.browse(cr, uid, ids, context):
286             obj = self.pool.get(template.object_name.model)
287             try:
288                 if template.ref_ir_act_window:
289                     self.pool.get('ir.actions.act_window').unlink(cr, uid, template.ref_ir_act_window.id, context)
290                 if template.ref_ir_value:
291                     self.pool.get('ir.values').unlink(cr, uid, template.ref_ir_value.id, context)
292             except:
293                 raise osv.except_osv(_("Warning"), _("Deletion of Record failed"))
294
295     def delete_action(self, cr, uid, ids, context):
296         self.unlink_action(cr, uid, ids, context)
297         return True
298         
299     def unlink(self, cr, uid, ids, context=None):
300         self.unlink_action(cr, uid, ids, context)
301         return super(email_template, self).unlink(cr, uid, ids, context)
302     
303     def copy(self, cr, uid, id, default=None, context=None):
304         if default is None:
305             default = {}
306         default = default.copy()
307         old = self.read(cr, uid, id, ['name'], context=context)
308         new_name = _("Copy of template ") + old.get('name', 'No Name')
309         check = self.search(cr, uid, [('name', '=', new_name)], context=context)
310         if check:
311             new_name = new_name + '_' + random.choice('abcdefghij') + random.choice('lmnopqrs') + random.choice('tuvwzyz')
312         default.update({'name':new_name})
313         return super(email_template, self).copy(cr, uid, id, default, context)
314     
315     def compute_pl(self,
316                    model_object_field,
317                    sub_model_object_field,
318                    null_value, template_language='mako'):
319         """
320         Returns the expression based on data provided
321         @param model_object_field: First level field
322         @param sub_model_object_field: Second level drilled down field (M2O)
323         @param null_value: What has to be returned if the value is empty
324         @param template_language: The language used for templating
325         @return: computed expression
326         """
327         #Configure for MAKO
328         copy_val = ''
329         if template_language == 'mako':
330             if model_object_field:
331                 copy_val = "${object." + model_object_field
332             if sub_model_object_field:
333                 copy_val += "." + sub_model_object_field
334             if null_value:
335                 copy_val += " or '" + null_value + "'"
336             if model_object_field:
337                 copy_val += "}"
338         elif template_language == 'django':
339             if model_object_field:
340                 copy_val = "{{object." + model_object_field
341             if sub_model_object_field:
342                 copy_val += "." + sub_model_object_field
343             if null_value:
344                 copy_val = copy_val + '|default:"' + null_value + '"'  
345             copy_val = copy_val + "}}"        
346         return copy_val 
347             
348     def onchange_model_object_field(self, cr, uid, ids, model_object_field, template_language, context=None):
349         if not model_object_field:
350             return {}
351         result = {}
352         field_obj = self.pool.get('ir.model.fields').browse(cr, uid, model_object_field, context)
353         #Check if field is relational
354         if field_obj.ttype in ['many2one', 'one2many', 'many2many']:
355             res_ids = self.pool.get('ir.model').search(cr, uid, [('model', '=', field_obj.relation)], context=context)
356             if res_ids:
357                 result['sub_object'] = res_ids[0]
358                 result['copyvalue'] = self.compute_pl(False,
359                                                       False,
360                                                       False,
361                                                       template_language)
362                 result['sub_model_object_field'] = False
363                 result['null_value'] = False
364         else:
365             #Its a simple field... just compute placeholder
366             result['sub_object'] = False
367             result['copyvalue'] = self.compute_pl(field_obj.name,
368                                                   False,
369                                                   False,
370                                                   template_language
371                                                   )
372             result['sub_model_object_field'] = False
373             result['null_value'] = False
374         return {'value':result}
375         
376     def onchange_sub_model_object_field(self, cr, uid, ids, model_object_field, sub_model_object_field, template_language, context=None):
377         if not model_object_field or not sub_model_object_field:
378             return {}
379         result = {}
380         field_obj = self.pool.get('ir.model.fields').browse(cr, uid, model_object_field, context)
381         if field_obj.ttype in ['many2one', 'one2many', 'many2many']:
382             res_ids = self.pool.get('ir.model').search(cr, uid, [('model', '=', field_obj.relation)], context=context)
383             sub_field_obj = self.pool.get('ir.model.fields').browse(cr, uid, sub_model_object_field, context)
384             if res_ids:
385                 result['sub_object'] = res_ids[0]
386                 result['copyvalue'] = self.compute_pl(field_obj.name,
387                                                       sub_field_obj.name,
388                                                       False,
389                                                       template_language
390                                                       )
391                 result['sub_model_object_field'] = sub_model_object_field
392                 result['null_value'] = False
393         else:
394             #Its a simple field... just compute placeholder
395             result['sub_object'] = False
396             result['copyvalue'] = self.compute_pl(field_obj.name,
397                                                   False,
398                                                   False,
399                                                   template_language
400                                                   )
401             result['sub_model_object_field'] = False
402             result['null_value'] = False
403         return {'value':result}
404
405     def onchange_null_value(self, cr, uid, ids, model_object_field, sub_model_object_field, null_value, template_language, context=None):
406         if not model_object_field and not null_value:
407             return {}
408         result = {}
409         field_obj = self.pool.get('ir.model.fields').browse(cr, uid, model_object_field, context)
410         if field_obj.ttype in ['many2one', 'one2many', 'many2many']:
411             res_ids = self.pool.get('ir.model').search(cr, uid, [('model', '=', field_obj.relation)], context=context)
412             sub_field_obj = self.pool.get('ir.model.fields').browse(cr, uid, sub_model_object_field, context)
413             if res_ids:
414                 result['sub_object'] = res_ids[0]
415                 result['copyvalue'] = self.compute_pl(field_obj.name,
416                                                       sub_field_obj.name,
417                                                       null_value,
418                                                       template_language
419                                                       )
420                 result['sub_model_object_field'] = sub_model_object_field
421                 result['null_value'] = null_value
422         else:
423             #Its a simple field... just compute placeholder
424             result['sub_object'] = False
425             result['copyvalue'] = self.compute_pl(field_obj.name,
426                                                   False,
427                                                   null_value,
428                                                   template_language
429                                                   )
430             result['sub_model_object_field'] = False
431             result['null_value'] = null_value
432         return {'value':result}
433                
434     def generate_attach_reports(self,
435                                  cursor,
436                                  user,
437                                  template,
438                                  record_id,
439                                  mail,
440                                  context=None):
441         """
442         Generate report to be attached and attach it
443         to the email
444         
445         @param cursor: Database Cursor
446         @param user: ID of User
447         @param template: Browse record of
448                          template
449         @param record_id: ID of the target model
450                           for which this mail has
451                           to be generated
452         @param mail: Browse record of email object 
453         @return: True 
454         """
455         reportname = 'report.' + \
456             self.pool.get('ir.actions.report.xml').read(
457                                          cursor,
458                                          user,
459                                          template.report_template.id,
460                                          ['report_name'],
461                                          context)['report_name']
462         service = netsvc.LocalService(reportname)
463         data = {}
464         data['model'] = template.model_int_name
465         (result, format) = service.create(cursor,
466                                           user,
467                                           [record_id],
468                                           data,
469                                           context)
470         attachment_obj = self.pool.get('ir.attachment')
471
472         fname = tools.ustr(get_value(cursor, user, record_id,
473                                      template.file_name, template, context)
474                            or 'Report')
475         ext = '.' + format
476         if not fname.endswith(ext):
477             fname += ext
478
479         new_att_vals = {
480             'name':mail.subject + ' (Email Attachment)',
481             'datas':base64.b64encode(result),
482             'datas_fname': fname,
483             'description':mail.subject or "No Description",
484             'res_model':'email_template.mailbox',
485             'res_id':mail.id
486         }
487         attachment_id = attachment_obj.create(cursor,
488                                               user,
489                                               new_att_vals,
490                                               context)
491         if attachment_id:
492             self.pool.get('email_template.mailbox').write(
493                               cursor,
494                               user,
495                               mail.id,
496                               {
497                                'attachments_ids':[
498                                                   [6, 0, [attachment_id]]
499                                                     ],
500                                'mail_type':'multipart/mixed'
501                                },
502                                context)
503         return True
504     
505     def _generate_mailbox_item_from_template(self,
506                                       cursor,
507                                       user,
508                                       template,
509                                       record_id,
510                                       context=None):
511         """
512         Generates an email from the template for
513         record record_id of target object
514         
515         @param cursor: Database Cursor
516         @param user: ID of User
517         @param template: Browse record of
518                          template
519         @param record_id: ID of the target model
520                           for which this mail has
521                           to be generated
522         @return: ID of created object 
523         """
524         if context is None:
525             context = {}
526         #If account to send from is in context select it, else use enforced account
527         if 'account_id' in context.keys():
528             from_account = self.pool.get('email_template.account').read(
529                                                     cursor,
530                                                     user,
531                                                     context.get('account_id'),
532                                                     ['name', 'email_id'],
533                                                     context
534                                                     )
535         else:
536             from_account = {
537                             'id':template.enforce_from_account.id,
538                             'name':template.enforce_from_account.name,
539                             'email_id':template.enforce_from_account.email_id
540                             }
541         lang = get_value(cursor,
542                          user,
543                          record_id,
544                          template.lang,
545                          template,
546                          context)
547         if lang:
548             ctx = context.copy()
549             ctx.update({'lang':lang})
550             template = self.browse(cursor, user, template.id, context=ctx)
551         mailbox_values = {
552             'email_from': tools.ustr(from_account['name']) + \
553                         "<" + tools.ustr(from_account['email_id']) + ">",
554             'email_to':get_value(cursor,
555                                user,
556                                record_id,
557                                template.def_to,
558                                template,
559                                context),
560             'email_cc':get_value(cursor,
561                                user,
562                                record_id,
563                                template.def_cc,
564                                template,
565                                context),
566             'email_bcc':get_value(cursor,
567                                 user,
568                                 record_id,
569                                 template.def_bcc,
570                                 template,
571                                 context),
572             'subject':get_value(cursor,
573                                     user,
574                                     record_id,
575                                     template.def_subject,
576                                     template,
577                                     context),
578             'body_text':get_value(cursor,
579                                       user,
580                                       record_id,
581                                       template.def_body_text,
582                                       template,
583                                       context),
584             'body_html':get_value(cursor,
585                                       user,
586                                       record_id,
587                                       template.def_body_html,
588                                       template,
589                                       context),
590             'account_id' :from_account['id'],
591             #This is a mandatory field when automatic emails are sent
592             'state':'na',
593             'folder':'drafts',
594             'mail_type':'multipart/alternative' 
595         }
596         if not mailbox_values['account_id']:
597             raise Exception("Unable to send the mail. No account linked to the template.")
598         #Use signatures if allowed
599         if template.use_sign:
600             sign = self.pool.get('res.users').read(cursor,
601                                                    user,
602                                                    user,
603                                                    ['signature'],
604                                                    context)['signature']
605             if mailbox_values['body_text']:
606                 mailbox_values['body_text'] += sign
607             if mailbox_values['body_html']:
608                 mailbox_values['body_html'] += sign
609         mailbox_id = self.pool.get('email_template.mailbox').create(
610                                                              cursor,
611                                                              user,
612                                                              mailbox_values,
613                                                              context)
614
615         return mailbox_id
616         
617
618     def generate_mail(self,
619                       cursor,
620                       user,
621                       template_id,
622                       record_ids,
623                       context=None):
624         if context is None:
625             context = {}
626         template = self.browse(cursor, user, template_id, context=context)
627         if not template:
628             raise Exception("The requested template could not be loaded")
629         result = True
630         for record_id in record_ids:
631             mailbox_id = self._generate_mailbox_item_from_template(
632                                                                 cursor,
633                                                                 user,
634                                                                 template,
635                                                                 record_id,
636                                                                 context)
637             mail = self.pool.get('email_template.mailbox').browse(
638                                                         cursor,
639                                                         user,
640                                                         mailbox_id,
641                                                         context=context
642                                                               )
643             if template.report_template:
644                 self.generate_attach_reports(
645                                               cursor,
646                                               user,
647                                               template,
648                                               record_id,
649                                               mail,
650                                               context
651                                               )
652             self.pool.get('email_template.mailbox').write(
653                                                 cursor,
654                                                 user,
655                                                 mailbox_id,
656                                                 {'folder':'outbox'},
657                                                 context=context
658             )
659             # TODO : manage return value of all the records
660             result = self.pool.get('email_template.mailbox').send_this_mail(cursor, user, [mailbox_id], context)
661         return result
662
663 email_template()
664
665 class email_template_preview(osv.osv_memory):
666     _name = "email_template.preview"
667     _description = "Email Template Preview"
668     
669     def _get_model_recs(self, cr, uid, context=None):
670         if context is None:
671             context = {}
672             #Fills up the selection box which allows records from the selected object to be displayed
673         self.context = context
674         if 'template_id' in context.keys():
675             ref_obj_id = self.pool.get('email.template').read(cr, uid, context['template_id'], ['object_name'], context)
676             ref_obj_name = self.pool.get('ir.model').read(cr, uid, ref_obj_id['object_name'][0], ['model'], context)['model']
677             ref_obj_ids = self.pool.get(ref_obj_name).search(cr, uid, [], 0, 20, 'id desc', context=context)
678             ref_obj_recs = self.pool.get(ref_obj_name).name_get(cr, uid, ref_obj_ids, context)
679             return ref_obj_recs    
680         
681     def _default_model(self, cursor, user, context=None):
682         """
683         Returns the default value for model field
684         @param cursor: Database Cursor
685         @param user: ID of current user
686         @param context: Open ERP Context
687         """
688         return self.pool.get('email.template').read(
689                                                    cursor,
690                                                    user,
691                                                    context['template_id'],
692                                                    ['object_name'],
693                                                    context)['object_name']
694         
695     _columns = {
696         'ref_template':fields.many2one(
697                                        'email.template',
698                                        'Template', readonly=True),
699         'rel_model':fields.many2one('ir.model', 'Model', readonly=True),
700         'rel_model_ref':fields.selection(_get_model_recs, 'Referred Document'),
701         'to':fields.char('To', size=250, readonly=True),
702         'cc':fields.char('CC', size=250, readonly=True),
703         'bcc':fields.char('BCC', size=250, readonly=True),
704         'subject':fields.char('Subject', size=200, readonly=True),
705         'body_text':fields.text('Body', readonly=True),
706         'body_html':fields.text('Body', readonly=True),
707         'report':fields.char('Report Name', size=100, readonly=True),
708     }
709     _defaults = {
710         'ref_template': lambda self, cr, uid, ctx:ctx['template_id'],
711         'rel_model': _default_model
712     }
713     def on_change_ref(self, cr, uid, ids, rel_model_ref, context=None):
714         if context is None:
715             context = {}
716         if not rel_model_ref:
717             return {}
718         vals = {}
719         if context == {}:
720             context = self.context
721         template = self.pool.get('email.template').browse(cr, uid, context['template_id'], context)
722         #Search translated template
723         lang = get_value(cr, uid, rel_model_ref, template.lang, template, context)
724         if lang:
725             ctx = context.copy()
726             ctx.update({'lang':lang})
727             template = self.pool.get('email.template').browse(cr, uid, context['template_id'], ctx)
728         vals['to'] = get_value(cr, uid, rel_model_ref, template.def_to, template, context)
729         vals['cc'] = get_value(cr, uid, rel_model_ref, template.def_cc, template, context)
730         vals['bcc'] = get_value(cr, uid, rel_model_ref, template.def_bcc, template, context)
731         vals['subject'] = get_value(cr, uid, rel_model_ref, template.def_subject, template, context)
732         vals['body_text'] = get_value(cr, uid, rel_model_ref, template.def_body_text, template, context)
733         vals['body_html'] = get_value(cr, uid, rel_model_ref, template.def_body_html, template, context)
734         vals['report'] = get_value(cr, uid, rel_model_ref, template.file_name, template, context)
735         return {'value':vals}
736         
737 email_template_preview()
738
739 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: