1 # -*- coding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2010-Today OpenERP SA (<http://www.openerp.com>)
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>
20 ##############################################################################
22 from openerp import tools
23 from openerp.osv import osv, fields
26 def _reopen(self, res_id, model):
27 return {'type': 'ir.actions.act_window',
31 'res_model': self._name,
33 # save original model in context, because selecting the list of available
34 # templates requires a model in context
36 'default_model': model,
41 class mail_compose_message(osv.TransientModel):
42 _inherit = 'mail.compose.message'
44 def default_get(self, cr, uid, fields, context=None):
49 res = super(mail_compose_message, self).default_get(cr, uid, fields, context=context)
50 if context.get('default_template_id'):
52 self.onchange_template_id(
53 cr, uid, [], context['default_template_id'], res.get('composition_mode'),
54 res.get('model'), res.get('res_id', context.get('active_id')), context=context
60 'template_id': fields.many2one('email.template', 'Use template', select=True),
61 'partner_to': fields.char('To (Partner IDs)',
62 help="Comma-separated list of recipient partners ids (placeholders may be used here)"),
63 'email_to': fields.char('To (Emails)',
64 help="Comma-separated recipient addresses (placeholders may be used here)",),
65 'email_cc': fields.char('Cc (Emails)',
66 help="Carbon copy recipients (placeholders may be used here)"),
69 def send_mail(self, cr, uid, ids, context=None):
70 """ Override of send_mail to duplicate attachments linked to the email.template.
71 Indeed, basic mail.compose.message wizard duplicates attachments in mass
72 mailing mode. But in 'single post' mode, attachments of an email template
73 also have to be duplicated to avoid changing their ownership. """
76 wizard_context = dict(context)
77 for wizard in self.browse(cr, uid, ids, context=context):
78 if wizard.template_id:
79 wizard_context['mail_notify_user_signature'] = False # template user_signature is added when generating body_html
80 wizard_context['mail_auto_delete'] = wizard.template_id.auto_delete # mass mailing: use template auto_delete value -> note, for emails mass mailing only
81 if not wizard.attachment_ids or wizard.composition_mode == 'mass_mail' or not wizard.template_id:
83 new_attachment_ids = []
84 for attachment in wizard.attachment_ids:
85 if attachment in wizard.template_id.attachment_ids:
86 new_attachment_ids.append(self.pool.get('ir.attachment').copy(cr, uid, attachment.id, {'res_model': 'mail.compose.message', 'res_id': wizard.id}, context=context))
88 new_attachment_ids.append(attachment.id)
89 self.write(cr, uid, wizard.id, {'attachment_ids': [(6, 0, new_attachment_ids)]}, context=context)
90 return super(mail_compose_message, self).send_mail(cr, uid, ids, context=wizard_context)
92 def onchange_template_id(self, cr, uid, ids, template_id, composition_mode, model, res_id, context=None):
93 """ - mass_mailing: we cannot render, so return the template values
94 - normal mode: return rendered values """
95 if template_id and composition_mode == 'mass_mail':
96 fields = ['subject', 'body_html', 'email_from', 'email_to', 'partner_to', 'email_cc', 'reply_to', 'attachment_ids', 'mail_server_id']
97 template_values = self.pool.get('email.template').read(cr, uid, template_id, fields, context)
98 values = dict((field, template_values[field]) for field in fields if template_values.get(field))
100 values = self.generate_email_for_composer_batch(cr, uid, template_id, [res_id], context=context)[res_id]
101 # transform attachments into attachment_ids; not attached to the document because this will
102 # be done further in the posting process, allowing to clean database if email not send
103 values['attachment_ids'] = values.pop('attachment_ids', [])
104 ir_attach_obj = self.pool.get('ir.attachment')
105 for attach_fname, attach_datas in values.pop('attachments', []):
107 'name': attach_fname,
108 'datas': attach_datas,
109 'datas_fname': attach_fname,
110 'res_model': 'mail.compose.message',
112 'type': 'binary', # override default_type from context, possibly meant for another model!
114 values['attachment_ids'].append(ir_attach_obj.create(cr, uid, data_attach, context=context))
116 values = self.default_get(cr, uid, ['subject', 'body', 'email_from', 'email_to', 'email_cc', 'partner_to', 'reply_to', 'attachment_ids', 'mail_server_id'], context=context)
117 if values.get('body_html'):
118 values['body'] = values.pop('body_html')
119 return {'value': values}
121 def save_as_template(self, cr, uid, ids, context=None):
122 """ hit save as template button: current form value will be a new
123 template attached to the current document. """
124 email_template = self.pool.get('email.template')
125 ir_model_pool = self.pool.get('ir.model')
126 for record in self.browse(cr, uid, ids, context=context):
127 model_ids = ir_model_pool.search(cr, uid, [('model', '=', record.model)], context=context)
128 model_id = model_ids and model_ids[0] or False
131 model_name = ir_model_pool.browse(cr, uid, model_id, context=context).name
132 template_name = "%s: %s" % (model_name, tools.ustr(record.subject))
134 'name': template_name,
135 'subject': record.subject or False,
136 'body_html': record.body or False,
137 'model_id': model_id or False,
138 'attachment_ids': [(6, 0, [att.id for att in record.attachment_ids])],
140 template_id = email_template.create(cr, uid, values, context=context)
141 # generate the saved template
142 template_values = record.onchange_template_id(template_id, record.composition_mode, record.model, record.res_id)['value']
143 template_values['template_id'] = template_id
144 record.write(template_values)
145 return _reopen(self, record.id, record.model)
147 #------------------------------------------------------
148 # Wizard validation and send
149 #------------------------------------------------------
151 def _get_or_create_partners_from_values(self, cr, uid, rendered_values, context=None):
152 """ Check for email_to, email_cc, partner_to """
154 mails = tools.email_split(rendered_values.pop('email_to', '') + ' ' + rendered_values.pop('email_cc', ''))
156 partner_id = self.pool.get('res.partner').find_or_create(cr, uid, mail, context=context)
157 partner_ids.append(partner_id)
158 partner_to = rendered_values.pop('partner_to', '')
160 for partner_id in partner_to.split(','):
161 if partner_id: # placeholders could generate '', 3, 2 due to some empty field values
162 partner_ids.append(int(partner_id))
165 def generate_email_for_composer_batch(self, cr, uid, template_id, res_ids, context=None):
166 """ Call email_template.generate_email(), get fields relevant for
167 mail.compose.message, transform email_cc and email_to into partner_ids """
168 # filter template values
169 fields = ['subject', 'body_html', 'email_from', 'email_to', 'partner_to', 'email_cc', 'reply_to', 'attachment_ids', 'attachments', 'mail_server_id']
170 values = dict.fromkeys(res_ids, False)
171 template_values = self.pool.get('email.template').generate_email_batch(cr, uid, template_id, res_ids, context=context)
172 for res_id in res_ids:
173 res_id_values = dict((field, template_values[res_id][field]) for field in fields if template_values[res_id].get(field))
174 res_id_values['body'] = res_id_values.pop('body_html', '')
176 # transform email_to, email_cc into partner_ids
177 ctx = dict((k, v) for k, v in (context or {}).items() if not k.startswith('default_'))
178 partner_ids = self._get_or_create_partners_from_values(cr, uid, res_id_values, context=ctx)
179 # legacy template behavior: void values do not erase existing values and the
180 # related key is removed from the values dict
182 res_id_values['partner_ids'] = list(partner_ids)
184 values[res_id] = res_id_values
187 def render_message_batch(self, cr, uid, wizard, res_ids, context=None):
188 """ Override to handle templates. """
189 # generate template-based values
190 if wizard.template_id:
191 template_values = self.generate_email_for_composer_batch(cr, uid, wizard.template_id.id, res_ids, context=context)
193 template_values = dict.fromkeys(res_ids, dict())
194 # generate composer values
195 composer_values = super(mail_compose_message, self).render_message_batch(cr, uid, wizard, res_ids, context)
197 for res_id in res_ids:
198 # remove attachments from template values as they should not be rendered
199 template_values[res_id].pop('attachment_ids', None)
200 # remove some keys from composer that are readonly
201 composer_values[res_id].pop('email_to', None)
202 composer_values[res_id].pop('email_cc', None)
203 composer_values[res_id].pop('partner_to', None)
204 # update template values by composer values
205 template_values[res_id].update(composer_values[res_id])
206 return template_values
208 def render_template_batch(self, cr, uid, template, model, res_ids, context=None):
209 return self.pool.get('email.template').render_template_batch(cr, uid, template, model, res_ids, context=context)
211 # Compatibility methods
212 def generate_email_for_composer(self, cr, uid, template_id, res_id, context=None):
213 return self.generate_email_for_composer_batch(cr, uid, template_id, [res_id], context)[res_id]
215 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: