Merge lp:~openerp-dev/openobject-addons/trunk-mass-mailing-tde into lp:openobject-addons
- trunk-mass-mailing-tde
- Merge into trunk
Proposed by
Thibault Delavallée (OpenERP)
Status: | Merged |
---|---|
Merged at revision: | 8636 |
Proposed branch: | lp:~openerp-dev/openobject-addons/trunk-mass-mailing-tde |
Merge into: | lp:openobject-addons |
Diff against target: |
1086 lines (+286/-154) 26 files modified
account/edi/invoice_action_data.xml (+1/-1) crm/crm_data.xml (+0/-16) crm/crm_lead_data.xml (+13/-1) crm/crm_lead_view.xml (+26/-0) crm/res_partner_view.xml (+12/-0) email_template/email_template.py (+5/-3) email_template/email_template_view.xml (+3/-3) email_template/res_partner_view.xml (+30/-0) email_template/tests/test_mail.py (+4/-3) email_template/wizard/email_template_preview_view.xml (+1/-1) email_template/wizard/mail_compose_message.py (+38/-17) email_template/wizard/mail_compose_message_view.xml (+8/-0) mail/mail_followers.py (+23/-19) mail/mail_mail.py (+19/-35) mail/mail_mail_view.xml (+4/-2) mail/mail_message.py (+10/-6) mail/mail_thread.py (+6/-3) mail/res_partner_view.xml (+3/-0) mail/tests/test_mail_features.py (+2/-2) mail/wizard/invite.py (+12/-11) mail/wizard/mail_compose_message.py (+30/-6) mail/wizard/mail_compose_message_view.xml (+26/-13) portal_sale/portal_sale.py (+6/-8) portal_sale/portal_sale_data.xml (+2/-2) purchase/edi/purchase_order_action_data.xml (+1/-1) sale/edi/sale_order_action_data.xml (+1/-1) |
To merge this branch: | bzr merge lp:~openerp-dev/openobject-addons/trunk-mass-mailing-tde |
Related bugs: | |
Related blueprints: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
OpenERP Core Team | Pending | ||
Review via email: mp+150403@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'account/edi/invoice_action_data.xml' |
2 | --- account/edi/invoice_action_data.xml 2012-12-14 11:31:32 +0000 |
3 | +++ account/edi/invoice_action_data.xml 2013-03-13 13:50:27 +0000 |
4 | @@ -24,7 +24,7 @@ |
5 | <field name="name">Invoice - Send by Email</field> |
6 | <field name="email_from">${object.user_id.email or object.company_id.email or 'noreply@localhost'}</field> |
7 | <field name="subject">${object.company_id.name} Invoice (Ref ${object.number or 'n/a'})</field> |
8 | - <field name="email_recipients">${object.partner_id.id}</field> |
9 | + <field name="partner_to">${object.partner_id.id}</field> |
10 | <field name="model_id" ref="account.model_account_invoice"/> |
11 | <field name="auto_delete" eval="True"/> |
12 | <field name="report_template" ref="account_invoices"/> |
13 | |
14 | === modified file 'crm/crm_data.xml' |
15 | --- crm/crm_data.xml 2012-12-21 16:48:08 +0000 |
16 | +++ crm/crm_data.xml 2013-03-13 13:50:27 +0000 |
17 | @@ -72,20 +72,4 @@ |
18 | </record> |
19 | </data> |
20 | |
21 | - <!-- Mail template is done in a NOUPDATE block |
22 | - so users can freely customize/delete them --> |
23 | - <data noupdate="1"> |
24 | - |
25 | - <!--Definition of an email template with an empty body that will be used in opportunity mailing. Used to give a |
26 | - basis for email recipients, name and to ease the definition of a further elaborated template. --> |
27 | - <record id="email_template_opportunity_mail" model="email.template"> |
28 | - <field name="name">Opportunity - Send Emails</field> |
29 | - <field name="email_from">${object.user_id.email or ''}</field> |
30 | - <field name="subject">Opportunity ${object.name | h})</field> |
31 | - <field name="model_id" ref="crm.model_crm_lead"/> |
32 | - <field name="auto_delete" eval="True"/> |
33 | - <field name="email_recipients">${object.partner_id.id}</field> |
34 | - <field name="body_html"></field> |
35 | - </record> |
36 | - </data> |
37 | </openerp> |
38 | |
39 | === modified file 'crm/crm_lead_data.xml' |
40 | --- crm/crm_lead_data.xml 2013-01-30 09:09:42 +0000 |
41 | +++ crm/crm_lead_data.xml 2013-03-13 13:50:27 +0000 |
42 | @@ -1,6 +1,7 @@ |
43 | <?xml version="1.0"?> |
44 | <openerp> |
45 | - <data noupdate="1"> |
46 | + <!-- <data noupdate="1"> --> |
47 | + <data> |
48 | |
49 | <!-- Crm stages --> |
50 | <record model="crm.case.stage" id="stage_lead1"> |
51 | @@ -221,5 +222,16 @@ |
52 | <field name="relation_field">section_id</field> |
53 | </record> |
54 | |
55 | + <!--Definition of an email template with an empty body that will be used in opportunity mailing. Used to give a |
56 | + basis for email recipients, name and to ease the definition of a further elaborated template. --> |
57 | + <record id="lead_template" model="email.template"> |
58 | + <field name="name">Lead/Opportunity Mass Mail</field> |
59 | + <field name="model_id" ref="crm.model_crm_lead"/> |
60 | + <field name="auto_delete" eval="True"/> |
61 | + <field name="partner_to">${object.partner_id and object.partner_id.id}</field> |
62 | + <field name="email_to">${! object.partner_id and object.email}</field> |
63 | + <field name="body_html"></field> |
64 | + </record> |
65 | + |
66 | </data> |
67 | </openerp> |
68 | |
69 | === modified file 'crm/crm_lead_view.xml' |
70 | --- crm/crm_lead_view.xml 2013-02-21 10:52:47 +0000 |
71 | +++ crm/crm_lead_view.xml 2013-03-13 13:50:27 +0000 |
72 | @@ -339,6 +339,8 @@ |
73 | <filter string="Assigned to My Team(s)" |
74 | domain="[('section_id.member_ids', 'in', [uid])]" context="{'invisible_section': False}" |
75 | help="Leads that are assigned to any sales teams I am member of"/> |
76 | + <filter string="Not opt-out" domain="[('opt_out', '=', False)]" |
77 | + help="Is not opt-out and can be included in mass mailing campaigns"/> |
78 | <separator/> |
79 | <group expand="0" string="Group By..."> |
80 | <filter string="Salesperson" domain="[]" context="{'group_by':'user_id'}"/> |
81 | @@ -577,5 +579,29 @@ |
82 | </field> |
83 | </record> |
84 | |
85 | + <!-- |
86 | + MASS MAILING |
87 | + --> |
88 | + <act_window name="Lead/Opportunity Mass Mail" |
89 | + res_model="mail.compose.message" |
90 | + src_model="crm.lead" |
91 | + view_mode="form" |
92 | + multi="True" |
93 | + target="new" |
94 | + key2="client_action_multi" |
95 | + id="crm.action_lead_mass_mail" |
96 | + context="{ |
97 | + 'default_composition_mode': 'mass_mail', |
98 | + 'default_email_to':'{$object.email or \'\'}', |
99 | + 'default_use_template': True, |
100 | + 'default_template_id': ref('crm.lead_template'), |
101 | + }"/> |
102 | + |
103 | + <!--Definition of an email template with an empty body that will be used in opportunity mailing. Used to give a |
104 | + basis for email recipients, name and to ease the definition of a further elaborated template. --> |
105 | + <record id="lead_template" model="email.template"> |
106 | + <field name="ref_ir_act_window" ref="crm.action_lead_mass_mail"/> |
107 | + </record> |
108 | + |
109 | </data> |
110 | </openerp> |
111 | |
112 | === modified file 'crm/res_partner_view.xml' |
113 | --- crm/res_partner_view.xml 2012-11-29 22:26:45 +0000 |
114 | +++ crm/res_partner_view.xml 2013-03-13 13:50:27 +0000 |
115 | @@ -15,6 +15,18 @@ |
116 | </field> |
117 | </record> |
118 | |
119 | + <record id="view_res_partner_filter_crm" model="ir.ui.view"> |
120 | + <field name="name">res.partner.select.crm</field> |
121 | + <field name="model">res.partner</field> |
122 | + <field name="inherit_id" ref="base.view_res_partner_filter"/> |
123 | + <field name="arch" type="xml"> |
124 | + <filter string="Suppliers" position="after"> |
125 | + <filter string="Not opt-out" domain="[('opt_out', '=', False)]" |
126 | + help="Is not opt-out and can be included in mass mailing campaigns" /> |
127 | + </filter> |
128 | + </field> |
129 | + </record> |
130 | + |
131 | <!-- open meetings related to given partner --> |
132 | <record id="crm_meeting_partner" model="ir.actions.act_window"> |
133 | <field name="name">Meetings</field> |
134 | |
135 | === modified file 'email_template/email_template.py' |
136 | --- email_template/email_template.py 2012-12-17 14:43:06 +0000 |
137 | +++ email_template/email_template.py 2013-03-13 13:50:27 +0000 |
138 | @@ -143,7 +143,9 @@ |
139 | 'subject': fields.char('Subject', translate=True, help="Subject (placeholders may be used here)",), |
140 | 'email_from': fields.char('From', help="Sender address (placeholders may be used here)"), |
141 | 'email_to': fields.char('To (Emails)', help="Comma-separated recipient addresses (placeholders may be used here)"), |
142 | - 'email_recipients': fields.char('To (Partners)', help="Comma-separated ids of recipient partners (placeholders may be used here)"), |
143 | + 'partner_to': fields.char('To (Partners)', |
144 | + help="Comma-separated ids of recipient partners (placeholders may be used here)", |
145 | + oldname='email_recipients'), |
146 | 'email_cc': fields.char('Cc', help="Carbon copy recipients (placeholders may be used here)"), |
147 | 'reply_to': fields.char('Reply-To', help="Preferred response address (placeholders may be used here)"), |
148 | 'mail_server_id': fields.many2one('ir.mail_server', 'Outgoing Mail Server', readonly=False, |
149 | @@ -311,7 +313,7 @@ |
150 | template = self.get_email_template(cr, uid, template_id, res_id, context) |
151 | values = {} |
152 | for field in ['subject', 'body_html', 'email_from', |
153 | - 'email_to', 'email_recipients', 'email_cc', 'reply_to']: |
154 | + 'email_to', 'partner_to', 'email_cc', 'reply_to']: |
155 | values[field] = self.render_template(cr, uid, getattr(template, field), |
156 | template.model, res_id, context=context) \ |
157 | or False |
158 | @@ -371,7 +373,7 @@ |
159 | values = self.generate_email(cr, uid, template_id, res_id, context=context) |
160 | assert 'email_from' in values, 'email_from is missing or empty after template rendering, send_mail() cannot proceed' |
161 | attachments = values.pop('attachments') or {} |
162 | - del values['email_recipients'] # TODO Properly use them. |
163 | + del values['partner_to'] # TODO Properly use them. |
164 | msg_id = mail_mail.create(cr, uid, values, context=context) |
165 | # link attachments |
166 | attachment_ids = [] |
167 | |
168 | === modified file 'email_template/email_template_view.xml' |
169 | --- email_template/email_template_view.xml 2012-11-29 22:26:45 +0000 |
170 | +++ email_template/email_template_view.xml 2013-03-13 13:50:27 +0000 |
171 | @@ -28,9 +28,9 @@ |
172 | <page string="Email Details"> |
173 | <group> |
174 | <group string="Addressing"> |
175 | - <field name="email_from" required="1"/> |
176 | + <field name="email_from"/> |
177 | <field name="email_to"/> |
178 | - <field name="email_recipients"/> |
179 | + <field name="partner_to"/> |
180 | <field name="email_cc"/> |
181 | <field name="reply_to"/> |
182 | <field name="user_signature"/> |
183 | @@ -78,7 +78,7 @@ |
184 | <field name="subject"/> |
185 | <field name="email_from"/> |
186 | <field name="email_to"/> |
187 | - <field name="email_recipients"/> |
188 | + <field name="partner_to"/> |
189 | <field name="report_name"/> |
190 | </tree> |
191 | </field> |
192 | |
193 | === modified file 'email_template/res_partner_view.xml' |
194 | --- email_template/res_partner_view.xml 2012-11-29 22:26:45 +0000 |
195 | +++ email_template/res_partner_view.xml 2013-03-13 13:50:27 +0000 |
196 | @@ -11,5 +11,35 @@ |
197 | </xpath> |
198 | </field> |
199 | </record> |
200 | + |
201 | + <!--Definition of an email template with an empty body that will be used in opportunity mailing. Used to give a |
202 | + basis for email recipients, name and to ease the definition of a further elaborated template. --> |
203 | + <record id="partner_template" model="email.template"> |
204 | + <field name="name">Partner Mass Mail</field> |
205 | + <field name="model_id" ref="base.model_res_partner"/> |
206 | + <field name="auto_delete" eval="True"/> |
207 | + <field name="partner_to">${object.id}</field> |
208 | + </record> |
209 | + |
210 | + <!-- Replace the default mass-mailing wizard in base with the composition wizard --> |
211 | + <act_window name="Partner Mass Mailing" |
212 | + res_model="mail.compose.message" |
213 | + src_model="res.partner" |
214 | + view_mode="form" |
215 | + multi="True" |
216 | + target="new" |
217 | + key2="client_action_multi" |
218 | + id="base.action_partner_mass_mail" |
219 | + context="{ |
220 | + 'default_composition_mode': 'mass_mail', |
221 | + 'default_partner_to': '${object.id or \'\'}', |
222 | + 'default_use_template': True, |
223 | + 'default_template_id': ref('partner_template'), |
224 | + }"/> |
225 | + |
226 | + <record id="partner_template" model="email.template"> |
227 | + <field name="ref_ir_act_window" ref="base.action_partner_mass_mail"/> |
228 | + </record> |
229 | + |
230 | </data> |
231 | </openerp> |
232 | |
233 | === modified file 'email_template/tests/test_mail.py' |
234 | --- email_template/tests/test_mail.py 2013-02-14 11:12:30 +0000 |
235 | +++ email_template/tests/test_mail.py 2013-03-13 13:50:27 +0000 |
236 | @@ -130,6 +130,7 @@ |
237 | # 1. Mass_mail on pigs and bird, with a default_partner_ids set to check he is correctly added |
238 | context = { |
239 | 'default_composition_mode': 'mass_mail', |
240 | + 'default_notify': True, |
241 | 'default_model': 'mail.group', |
242 | 'default_res_id': self.group_pigs_id, |
243 | 'default_template_id': email_template_id, |
244 | @@ -170,20 +171,20 @@ |
245 | self.assertEqual(set(message_bird_pids), set(partner_ids), 'mail.message on bird notified_partner_ids incorrect') |
246 | |
247 | # ---------------------------------------- |
248 | - # CASE4: test newly introduced email_recipients field |
249 | + # CASE4: test newly introduced partner_to field |
250 | # ---------------------------------------- |
251 | |
252 | # get already-created partners back |
253 | p_b_id = self.res_partner.search(cr, uid, [('email', '=', 'b@b.b')])[0] |
254 | p_c_id = self.res_partner.search(cr, uid, [('email', '=', 'c@c.c')])[0] |
255 | p_d_id = self.res_partner.search(cr, uid, [('email', '=', 'd@d.d')])[0] |
256 | - # modify template: use email_recipients, use template and email address in email_to to test all features together |
257 | + # modify template: use partner_to, use template and email address in email_to to test all features together |
258 | user_model_id = self.registry('ir.model').search(cr, uid, [('model', '=', 'res.users')])[0] |
259 | email_template.write(cr, uid, [email_template_id], { |
260 | 'model_id': user_model_id, |
261 | 'body_html': '${object.login}', |
262 | 'email_to': '${object.email} c@c', |
263 | - 'email_recipients': '%i,%i' % (p_b_id, p_c_id), |
264 | + 'partner_to': '%i,%i' % (p_b_id, p_c_id), |
265 | 'email_cc': 'd@d', |
266 | }) |
267 | # patner by email + partner by id (no double) |
268 | |
269 | === modified file 'email_template/wizard/email_template_preview_view.xml' |
270 | --- email_template/wizard/email_template_preview_view.xml 2012-11-29 22:26:45 +0000 |
271 | +++ email_template/wizard/email_template_preview_view.xml 2013-03-13 13:50:27 +0000 |
272 | @@ -17,7 +17,7 @@ |
273 | <group> |
274 | <field name="email_from" readonly="1"/> |
275 | <field name="email_to" readonly="1"/> |
276 | - <field name="email_recipients" readonly="1"/> |
277 | + <field name="partner_to" readonly="1"/> |
278 | <field name="email_cc" readonly="1" attrs="{'invisible':[('email_cc','=',False)]}"/> |
279 | <field name="reply_to" readonly="1" attrs="{'invisible':[('reply_to','=',False)]}"/> |
280 | <field name="subject" readonly="1"/> |
281 | |
282 | === modified file 'email_template/wizard/mail_compose_message.py' |
283 | --- email_template/wizard/mail_compose_message.py 2013-02-13 17:39:40 +0000 |
284 | +++ email_template/wizard/mail_compose_message.py 2013-03-13 13:50:27 +0000 |
285 | @@ -22,6 +22,7 @@ |
286 | from openerp import tools |
287 | from openerp.osv import osv, fields |
288 | |
289 | + |
290 | def _reopen(self, res_id, model): |
291 | return {'type': 'ir.actions.act_window', |
292 | 'view_mode': 'form', |
293 | @@ -36,6 +37,7 @@ |
294 | }, |
295 | } |
296 | |
297 | + |
298 | class mail_compose_message(osv.TransientModel): |
299 | _inherit = 'mail.compose.message' |
300 | |
301 | @@ -59,13 +61,25 @@ |
302 | _columns = { |
303 | # incredible hack of the day: size=-1 means we want an int db column instead of an str one |
304 | 'template_id': fields.selection(_get_templates, 'Template', size=-1), |
305 | + 'partner_to': fields.char('To (Partner IDs)', readonly=True, |
306 | + help="Comma-separated list of recipient partners ids (placeholders may be used here)"), |
307 | + 'email_to': fields.char('To (Emails)', readonly=True, |
308 | + help="Comma-separated recipient addresses (placeholders may be used here)",), |
309 | + 'email_cc': fields.char('Cc (Emails)', readonly=True, |
310 | + help="Carbon copy recipients (placeholders may be used here)"), |
311 | + } |
312 | + |
313 | + _defaults = { |
314 | + 'partner_to': lambda self, cr, uid, ctx={}: '', |
315 | + 'email_to': lambda self, cr, uid, ctx={}: '', |
316 | + 'email_cc': lambda self, cr, uid, ctx={}: '', |
317 | } |
318 | |
319 | def onchange_template_id(self, cr, uid, ids, template_id, composition_mode, model, res_id, context=None): |
320 | """ - mass_mailing: we cannot render, so return the template values |
321 | - normal mode: return rendered values """ |
322 | if template_id and composition_mode == 'mass_mail': |
323 | - values = self.pool.get('email.template').read(cr, uid, template_id, ['subject', 'body_html'], context) |
324 | + values = self.pool.get('email.template').read(cr, uid, template_id, ['subject', 'body_html', 'email_from', 'email_to', 'email_cc', 'partner_to', 'reply_to'], context) |
325 | values.pop('id') |
326 | elif template_id: |
327 | # FIXME odo: change the mail generation to avoid attachment duplication |
328 | @@ -80,11 +94,11 @@ |
329 | 'datas_fname': attach_fname, |
330 | 'res_model': model, |
331 | 'res_id': res_id, |
332 | - 'type': 'binary', # override default_type from context, possibly meant for another model! |
333 | + 'type': 'binary', # override default_type from context, possibly meant for another model! |
334 | } |
335 | values['attachment_ids'].append(ir_attach_obj.create(cr, uid, data_attach, context=context)) |
336 | else: |
337 | - values = self.default_get(cr, uid, ['body', 'subject', 'partner_ids', 'attachment_ids'], context=context) |
338 | + values = self.default_get(cr, uid, ['subject', 'body', 'email_from', 'email_to', 'email_cc', 'partner_to', 'reply_to', 'attachment_ids'], context=context) |
339 | |
340 | if values.get('body_html'): |
341 | values['body'] = values.pop('body_html') |
342 | @@ -117,28 +131,29 @@ |
343 | # Wizard validation and send |
344 | #------------------------------------------------------ |
345 | |
346 | + def _get_or_create_partners_from_values(self, cr, uid, rendered_values, context=None): |
347 | + """ Check for email_to, email_cc, partner_to """ |
348 | + partner_ids = [] |
349 | + mails = tools.email_split(rendered_values.pop('email_to', '') + ' ' + rendered_values.pop('email_cc', '')) |
350 | + for mail in mails: |
351 | + partner_id = self.pool.get('res.partner').find_or_create(cr, uid, mail, context=context) |
352 | + partner_ids.append(partner_id) |
353 | + partner_to = rendered_values.pop('partner_to', '') |
354 | + if partner_to: |
355 | + for partner_id in partner_to.split(','): |
356 | + partner_ids.append(int(partner_id)) |
357 | + return partner_ids |
358 | + |
359 | def generate_email_for_composer(self, cr, uid, template_id, res_id, context=None): |
360 | """ Call email_template.generate_email(), get fields relevant for |
361 | mail.compose.message, transform email_cc and email_to into partner_ids """ |
362 | template_values = self.pool.get('email.template').generate_email(cr, uid, template_id, res_id, context=context) |
363 | # filter template values |
364 | - fields = ['body_html', 'subject', 'email_to', 'email_recipients', 'email_cc', 'attachments'] |
365 | + fields = ['subject', 'body_html', 'email_from', 'email_to', 'partner_to', 'email_cc', 'reply_to', 'attachments'] |
366 | values = dict((field, template_values[field]) for field in fields if template_values.get(field)) |
367 | values['body'] = values.pop('body_html', '') |
368 | # transform email_to, email_cc into partner_ids |
369 | - values['partner_ids'] = [] |
370 | - |
371 | - mails = tools.email_split(values.pop('email_to', '') + ' ' + values.pop('email_cc', '')) |
372 | - for mail in mails: |
373 | - partner_id = self.pool.get('res.partner').find_or_create(cr, uid, mail, context=context) |
374 | - values['partner_ids'].append(partner_id) |
375 | - email_recipients = values.pop('email_recipients', '') |
376 | - if email_recipients: |
377 | - for partner_id in email_recipients.split(','): |
378 | - values['partner_ids'].append(int(partner_id)) |
379 | - |
380 | - values['partner_ids'] = list(set(values['partner_ids'])) |
381 | - |
382 | + values['partner_ids'] = self._get_or_create_partners_from_values(cr, uid, values, context=context) |
383 | return values |
384 | |
385 | def render_message(self, cr, uid, wizard, res_id, context=None): |
386 | @@ -150,6 +165,12 @@ |
387 | values = {} |
388 | # get values to return |
389 | email_dict = super(mail_compose_message, self).render_message(cr, uid, wizard, res_id, context) |
390 | + email_dict['email_to'] = self.render_template(cr, uid, wizard.email_to, wizard.model, res_id, context) |
391 | + email_dict['email_cc'] = self.render_template(cr, uid, wizard.email_cc, wizard.model, res_id, context) |
392 | + email_dict['partner_to'] = self.render_template(cr, uid, wizard.partner_to, wizard.model, res_id, context) |
393 | + # transform email_to, email_cc into partner_ids |
394 | + email_dict['partner_ids'] = self._get_or_create_partners_from_values(cr, uid, email_dict, context=context) |
395 | + # update template values by wizard values |
396 | values.update(email_dict) |
397 | return values |
398 | |
399 | |
400 | === modified file 'email_template/wizard/mail_compose_message_view.xml' |
401 | --- email_template/wizard/mail_compose_message_view.xml 2012-11-29 22:26:45 +0000 |
402 | +++ email_template/wizard/mail_compose_message_view.xml 2013-03-13 13:50:27 +0000 |
403 | @@ -7,6 +7,14 @@ |
404 | <field name="model">mail.compose.message</field> |
405 | <field name="inherit_id" ref="mail.email_compose_message_wizard_form"/> |
406 | <field name="arch" type="xml"> |
407 | + <xpath expr="//field[@name='subject']" position="after"> |
408 | + <field name="partner_to" groups="base.group_no_one" |
409 | + attrs="{'invisible':[('composition_mode', '!=', 'mass_mail')]}"/>/> |
410 | + <field name="email_to" groups="base.group_no_one" |
411 | + attrs="{'invisible':[('composition_mode', '!=', 'mass_mail')]}"/>/> |
412 | + <field name="email_cc" groups="base.group_no_one" |
413 | + attrs="{'invisible':[('composition_mode', '!=', 'mass_mail')]}"/>/> |
414 | + </xpath> |
415 | <xpath expr="//footer" position="inside"> |
416 | <group class="oe_right" col="1"> |
417 | <div>Use template |
418 | |
419 | === modified file 'mail/mail_followers.py' |
420 | --- mail/mail_followers.py 2013-01-30 13:44:47 +0000 |
421 | +++ mail/mail_followers.py 2013-03-13 13:50:27 +0000 |
422 | @@ -75,7 +75,7 @@ |
423 | if not cr.fetchone(): |
424 | cr.execute('CREATE INDEX mail_notification_partner_id_read_starred_message_id ON mail_notification (partner_id, read, starred, message_id)') |
425 | |
426 | - def get_partners_to_notify(self, cr, uid, message, context=None): |
427 | + def get_partners_to_notify(self, cr, uid, message, partners_to_notify=[], context=None): |
428 | """ Return the list of partners to notify, based on their preferences. |
429 | |
430 | :param browse_record message: mail.message to notify |
431 | @@ -85,10 +85,13 @@ |
432 | if notification.read: |
433 | continue |
434 | partner = notification.partner_id |
435 | + # NOtify only required partners |
436 | + if partner.id not in partners_to_notify: |
437 | + continue |
438 | # Do not send to partners without email address defined |
439 | if not partner.email: |
440 | continue |
441 | - # Partner does not want to receive any emails |
442 | + # Partner does not want to receive any emails or is opt-out |
443 | if partner.notification_email_send == 'none': |
444 | continue |
445 | # Partner wants to receive only emails and comments |
446 | @@ -100,17 +103,26 @@ |
447 | notify_pids.append(partner.id) |
448 | return notify_pids |
449 | |
450 | - def _notify(self, cr, uid, msg_id, context=None): |
451 | + def _notify(self, cr, uid, msg_id, partners_to_notify=[], context=None): |
452 | """ Send by email the notification depending on the user preferences """ |
453 | if context is None: |
454 | context = {} |
455 | + if not partners_to_notify: |
456 | + return True |
457 | + mail_message_obj = self.pool.get('mail.message') |
458 | + |
459 | + # update message |
460 | + mail_message_obj.write(cr, uid, msg_id, {'notified_partner_ids': [(4, id) for id in partners_to_notify]}, context=context) |
461 | + |
462 | # mail_notify_noemail (do not send email) or no partner_ids: do not send, return |
463 | if context.get('mail_notify_noemail'): |
464 | return True |
465 | # browse as SUPERUSER_ID because of access to res_partner not necessarily allowed |
466 | msg = self.pool.get('mail.message').browse(cr, SUPERUSER_ID, msg_id, context=context) |
467 | - notify_partner_ids = self.get_partners_to_notify(cr, uid, msg, context=context) |
468 | - if not notify_partner_ids: |
469 | + print 'before partner_to_notify', partners_to_notify |
470 | + partners_to_notify = self.get_partners_to_notify(cr, uid, msg, partners_to_notify=partners_to_notify, context=context) |
471 | + print 'partner_to_notify', partners_to_notify |
472 | + if not partners_to_notify: |
473 | return True |
474 | |
475 | # add the context in the email |
476 | @@ -126,26 +138,18 @@ |
477 | if signature: |
478 | body_html = tools.append_content_to_html(body_html, signature, plaintext=True, container_tag='div') |
479 | |
480 | - # email_from: partner-user alias or partner email or mail.message email_from |
481 | - if msg.author_id and msg.author_id.user_ids and msg.author_id.user_ids[0].alias_domain and msg.author_id.user_ids[0].alias_name: |
482 | - email_from = '%s <%s@%s>' % (msg.author_id.name, msg.author_id.user_ids[0].alias_name, msg.author_id.user_ids[0].alias_domain) |
483 | - elif msg.author_id: |
484 | - email_from = '%s <%s>' % (msg.author_id.name, msg.author_id.email) |
485 | - else: |
486 | - email_from = msg.email_from |
487 | - |
488 | mail_values = { |
489 | 'mail_message_id': msg.id, |
490 | - 'email_to': [], |
491 | 'auto_delete': True, |
492 | 'body_html': body_html, |
493 | - 'email_from': email_from, |
494 | - 'state': 'outgoing', |
495 | + 'recipient_ids': [(4, id) for id in partners_to_notify] |
496 | } |
497 | - mail_values['email_to'] = ', '.join(mail_values['email_to']) |
498 | + if msg.email_from: |
499 | + mail_values['email_from'] = msg.email_from |
500 | + if msg.reply_to: |
501 | + mail_values['reply_to'] = msg.reply_to |
502 | email_notif_id = mail_mail.create(cr, uid, mail_values, context=context) |
503 | try: |
504 | - return mail_mail.send(cr, uid, [email_notif_id], recipient_ids=notify_partner_ids, context=context) |
505 | + return mail_mail.send(cr, uid, [email_notif_id], context=context) |
506 | except Exception: |
507 | return False |
508 | - |
509 | |
510 | === modified file 'mail/mail_mail.py' |
511 | --- mail/mail_mail.py 2013-02-14 12:39:25 +0000 |
512 | +++ mail/mail_mail.py 2013-03-13 13:50:27 +0000 |
513 | @@ -55,10 +55,13 @@ |
514 | help="Permanently delete this email after sending it, to save space"), |
515 | 'references': fields.text('References', help='Message references, such as identifiers of previous messages', readonly=1), |
516 | 'email_from': fields.char('From', help='Message sender, taken from user preferences.'), |
517 | - 'email_to': fields.text('To', help='Message recipients'), |
518 | + 'email_to': fields.text('To', help='Message recipients (emails)'), |
519 | + 'recipient_ids': fields.many2many('res.partner', string='Message recipients (partners)'), |
520 | 'email_cc': fields.char('Cc', help='Carbon copy message recipients'), |
521 | + 'body_html': fields.text('Rich-text Contents', help="Rich-text/HTML message"), |
522 | + |
523 | + # If not set in create values, auto-detected based on create values (res_id, model, email_from) |
524 | 'reply_to': fields.char('Reply-To', help='Preferred response address for the message'), |
525 | - 'body_html': fields.text('Rich-text Contents', help="Rich-text/HTML message"), |
526 | |
527 | # Auto-detected based on create() - if 'mail_message_id' was passed then this mail is a notification |
528 | # and during unlink() we will not cascade delete the parent and its attachments |
529 | @@ -66,11 +69,11 @@ |
530 | } |
531 | |
532 | def _get_default_from(self, cr, uid, context=None): |
533 | - this = self.pool.get('res.users').browse(cr, uid, uid, context=context) |
534 | + this = self.pool.get('res.users').browse(cr, SUPERUSER_ID, uid, context=context) |
535 | if this.alias_domain: |
536 | - return '%s@%s' % (this.alias_name, this.alias_domain) |
537 | + return '%s <%s@%s>' % (this.name, this.alias_name, this.alias_domain) |
538 | elif this.email: |
539 | - return this.email |
540 | + return '%s <%s>' % (this.name, this.email) |
541 | raise osv.except_osv(_('Invalid Action!'), _("Unable to send email, please configure the sender's email address or alias.")) |
542 | |
543 | _defaults = { |
544 | @@ -82,12 +85,16 @@ |
545 | # protection for `default_type` values leaking from menu action context (e.g. for invoices) |
546 | # To remove when automatic context propagation is removed in web client |
547 | if context and context.get('default_type') and context.get('default_type') not in self._all_columns['type'].column.selection: |
548 | - context = dict(context, default_type = None) |
549 | + context = dict(context, default_type=None) |
550 | return super(mail_mail, self).default_get(cr, uid, fields, context=context) |
551 | |
552 | def create(self, cr, uid, values, context=None): |
553 | if 'notification' not in values and values.get('mail_message_id'): |
554 | values['notification'] = True |
555 | + if not values.get('reply_to') and values.get('res_id') and values.get('model') and hasattr(self.pool.get(values.get('model')), 'message_get_reply_to'): |
556 | + values['reply_to'] = self.pool.get(values.get('model')).message_get_reply_to(cr, uid, [values.get('res_id')], context=context)[0] |
557 | + elif not values.get('reply_to'): |
558 | + values['reply_to'] = values.get('email_from', False) |
559 | return super(mail_mail, self).create(cr, uid, values, context=context) |
560 | |
561 | def unlink(self, cr, uid, ids, context=None): |
562 | @@ -190,22 +197,6 @@ |
563 | pass |
564 | return body |
565 | |
566 | - def send_get_mail_reply_to(self, cr, uid, mail, partner=None, context=None): |
567 | - """ Return a specific ir_email body. The main purpose of this method |
568 | - is to be inherited by Portal, to add a link for signing in, in |
569 | - each notification email a partner receives. |
570 | - |
571 | - :param browse_record mail: mail.mail browse_record |
572 | - :param browse_record partner: specific recipient partner |
573 | - """ |
574 | - if mail.reply_to: |
575 | - return mail.reply_to |
576 | - if not mail.model or not mail.res_id: |
577 | - return False |
578 | - if not hasattr(self.pool.get(mail.model), 'message_get_reply_to'): |
579 | - return False |
580 | - return self.pool.get(mail.model).message_get_reply_to(cr, uid, [mail.res_id], context=context)[0] |
581 | - |
582 | def send_get_email_dict(self, cr, uid, mail, partner=None, context=None): |
583 | """ Return a dictionary for specific email values, depending on a |
584 | partner, or generic to the whole recipients given by mail.email_to. |
585 | @@ -215,7 +206,6 @@ |
586 | """ |
587 | body = self.send_get_mail_body(cr, uid, mail, partner=partner, context=context) |
588 | subject = self.send_get_mail_subject(cr, uid, mail, partner=partner, context=context) |
589 | - reply_to = self.send_get_mail_reply_to(cr, uid, mail, partner=partner, context=context) |
590 | body_alternative = tools.html2plaintext(body) |
591 | email_to = [partner.email] if partner else tools.email_split(mail.email_to) |
592 | return { |
593 | @@ -223,10 +213,9 @@ |
594 | 'body_alternative': body_alternative, |
595 | 'subject': subject, |
596 | 'email_to': email_to, |
597 | - 'reply_to': reply_to, |
598 | } |
599 | |
600 | - def send(self, cr, uid, ids, auto_commit=False, recipient_ids=None, context=None): |
601 | + def send(self, cr, uid, ids, auto_commit=False, context=None): |
602 | """ Sends the selected emails immediately, ignoring their current |
603 | state (mails that have already been sent should not be passed |
604 | unless they should actually be re-sent). |
605 | @@ -237,14 +226,10 @@ |
606 | :param bool auto_commit: whether to force a commit of the mail status |
607 | after sending each mail (meant only for scheduler processing); |
608 | should never be True during normal transactions (default: False) |
609 | - :param list recipient_ids: specific list of res.partner recipients. |
610 | - If set, one email is sent to each partner. Its is possible to |
611 | - tune the sent email through ``send_get_mail_body`` and ``send_get_mail_subject``. |
612 | - If not specified, one email is sent to mail_mail.email_to. |
613 | :return: True |
614 | """ |
615 | ir_mail_server = self.pool.get('ir.mail_server') |
616 | - for mail in self.browse(cr, uid, ids, context=context): |
617 | + for mail in self.browse(cr, SUPERUSER_ID, ids, context=context): |
618 | try: |
619 | # handle attachments |
620 | attachments = [] |
621 | @@ -252,11 +237,10 @@ |
622 | attachments.append((attach.datas_fname, base64.b64decode(attach.datas))) |
623 | # specific behavior to customize the send email for notified partners |
624 | email_list = [] |
625 | - if recipient_ids: |
626 | - for partner in self.pool.get('res.partner').browse(cr, SUPERUSER_ID, recipient_ids, context=context): |
627 | - email_list.append(self.send_get_email_dict(cr, uid, mail, partner=partner, context=context)) |
628 | - else: |
629 | + if mail.email_to: |
630 | email_list.append(self.send_get_email_dict(cr, uid, mail, context=context)) |
631 | + for partner in mail.recipient_ids: |
632 | + email_list.append(self.send_get_email_dict(cr, uid, mail, partner=partner, context=context)) |
633 | |
634 | # build an RFC2822 email.message.Message object and send it without queuing |
635 | for email in email_list: |
636 | @@ -267,7 +251,7 @@ |
637 | body = email.get('body'), |
638 | body_alternative = email.get('body_alternative'), |
639 | email_cc = tools.email_split(mail.email_cc), |
640 | - reply_to = email.get('reply_to'), |
641 | + reply_to = mail.reply_to, |
642 | attachments = attachments, |
643 | message_id = mail.message_id, |
644 | references = mail.references, |
645 | |
646 | === modified file 'mail/mail_mail_view.xml' |
647 | --- mail/mail_mail_view.xml 2012-09-04 09:28:20 +0000 |
648 | +++ mail/mail_mail_view.xml 2013-03-13 13:50:27 +0000 |
649 | @@ -25,6 +25,8 @@ |
650 | </group> |
651 | <group> |
652 | <field name="partner_ids" widget="many2many_tags"/> |
653 | + <field name="notified_partner_ids" widget="many2many_tags"/> |
654 | + <field name="recipient_ids" widget="many2many_tags"/> |
655 | </group> |
656 | </group> |
657 | <notebook> |
658 | @@ -67,7 +69,7 @@ |
659 | <field name="subject"/> |
660 | <field name="author_id" string="User"/> |
661 | <field name="message_id" invisible="1"/> |
662 | - <field name="partner_ids" invisible="1"/> |
663 | + <field name="recipient_ids" invisible="1"/> |
664 | <field name="model" invisible="1"/> |
665 | <field name="res_id" invisible="1"/> |
666 | <field name="email_from" invisible="1"/> |
667 | @@ -97,7 +99,7 @@ |
668 | <filter icon="terp-camera_test" name="type_notification" string="Notification" domain="[('type','=','notification')]"/> |
669 | <group expand="0" string="Extended Filters..."> |
670 | <field name="author_id"/> |
671 | - <field name="partner_ids"/> |
672 | + <field name="recipient_ids"/> |
673 | <field name="model"/> |
674 | <field name="res_id"/> |
675 | </group> |
676 | |
677 | === modified file 'mail/mail_message.py' |
678 | --- mail/mail_message.py 2013-02-08 11:52:38 +0000 |
679 | +++ mail/mail_message.py 2013-03-13 13:50:27 +0000 |
680 | @@ -139,6 +139,8 @@ |
681 | "message, comment for other messages such as user replies"), |
682 | 'email_from': fields.char('From', |
683 | help="Email address of the sender. This field is set when no matching partner is found for incoming emails."), |
684 | + 'reply_to': fields.char('Reply-To', |
685 | + help='Reply email address. Setting the reply_to bypasses the automatic thread creation.'), |
686 | 'author_id': fields.many2one('res.partner', 'Author', select=1, |
687 | ondelete='set null', |
688 | help="Author of the message. If not set, email_from may hold an email address that did not match any partner."), |
689 | @@ -185,8 +187,9 @@ |
690 | _defaults = { |
691 | 'type': 'email', |
692 | 'date': lambda *a: fields.datetime.now(), |
693 | - 'author_id': lambda self, cr, uid, ctx={}: self._get_default_author(cr, uid, ctx), |
694 | + 'author_id': lambda self, cr, uid, ctx=None: self._get_default_author(cr, uid, ctx), |
695 | 'body': '', |
696 | + 'email_from': lambda self, cr, uid, ctx=None: self.pool.get('mail.mail')._get_default_from(cr, uid, ctx), |
697 | } |
698 | |
699 | #------------------------------------------------------ |
700 | @@ -731,7 +734,10 @@ |
701 | if context is None: |
702 | context = {} |
703 | default_starred = context.pop('default_starred', False) |
704 | - if not values.get('message_id') and values.get('res_id') and values.get('model'): |
705 | + # generate message_id, to redirect answers to the right discussion thread |
706 | + if not values.get('message_id') and values.get('reply_to'): |
707 | + values['message_id'] = tools.generate_tracking_message_id('reply_to-%(model)s' % values) |
708 | + elif not values.get('message_id') and values.get('res_id') and values.get('model'): |
709 | values['message_id'] = tools.generate_tracking_message_id('%(res_id)s-%(model)s' % values) |
710 | elif not values.get('message_id'): |
711 | values['message_id'] = tools.generate_tracking_message_id('private') |
712 | @@ -861,7 +867,7 @@ |
713 | # message has no subtype_id: pure log message -> no partners, no one notified |
714 | if not message.subtype_id: |
715 | return True |
716 | - |
717 | + |
718 | # all followers of the mail.message document have to be added as partners and notified |
719 | if message.model and message.res_id: |
720 | fol_obj = self.pool.get("mail.followers") |
721 | @@ -883,9 +889,7 @@ |
722 | partners_to_notify |= set(message.partner_ids) |
723 | |
724 | # notify |
725 | - if partners_to_notify: |
726 | - self.write(cr, SUPERUSER_ID, [newid], {'notified_partner_ids': [(4, p.id) for p in partners_to_notify]}, context=context) |
727 | - notification_obj._notify(cr, uid, newid, context=context) |
728 | + notification_obj._notify(cr, uid, newid, partners_to_notify=[p.id for p in partners_to_notify], context=context) |
729 | message.refresh() |
730 | |
731 | # An error appear when a user receive a notification without notifying |
732 | |
733 | === modified file 'mail/mail_thread.py' |
734 | --- mail/mail_thread.py 2013-03-01 07:07:43 +0000 |
735 | +++ mail/mail_thread.py 2013-03-13 13:50:27 +0000 |
736 | @@ -468,6 +468,7 @@ |
737 | # 1. Verify if this is a reply to an existing thread |
738 | thread_references = references or in_reply_to |
739 | ref_match = thread_references and tools.reference_re.search(thread_references) |
740 | + |
741 | if ref_match: |
742 | thread_id = int(ref_match.group(1)) |
743 | model = ref_match.group(2) or model |
744 | @@ -480,7 +481,10 @@ |
745 | |
746 | # Verify whether this is a reply to a private message |
747 | if in_reply_to: |
748 | - message_ids = self.pool.get('mail.message').search(cr, uid, [('message_id', '=', in_reply_to)], limit=1, context=context) |
749 | + message_ids = self.pool.get('mail.message').search(cr, uid, [ |
750 | + ('message_id', '=', in_reply_to), |
751 | + '!', ('message_id', 'ilike', 'reply_to') |
752 | + ], limit=1, context=context) |
753 | if message_ids: |
754 | message = self.pool.get('mail.message').browse(cr, uid, message_ids[0], context=context) |
755 | _logger.debug('Routing mail with Message-Id %s: direct reply to a private message: %s, custom_values: %s, uid: %s', |
756 | @@ -772,8 +776,7 @@ |
757 | author_ids = self._message_find_partners(cr, uid, message, ['From'], context=context) |
758 | if author_ids: |
759 | msg_dict['author_id'] = author_ids[0] |
760 | - else: |
761 | - msg_dict['email_from'] = decode(message.get('from')) |
762 | + msg_dict['email_from'] = decode(message.get('from')) |
763 | partner_ids = self._message_find_partners(cr, uid, message, ['To', 'Cc'], context=context) |
764 | msg_dict['partner_ids'] = [(4, partner_id) for partner_id in partner_ids] |
765 | |
766 | |
767 | === modified file 'mail/res_partner_view.xml' |
768 | --- mail/res_partner_view.xml 2012-11-29 22:26:45 +0000 |
769 | +++ mail/res_partner_view.xml 2013-03-13 13:50:27 +0000 |
770 | @@ -7,6 +7,9 @@ |
771 | <field name="model">res.partner</field> |
772 | <field name="inherit_id" ref="base.view_partner_form"/> |
773 | <field name="arch" type="xml"> |
774 | + <xpath expr="//field[@name='active']" position="after"> |
775 | + <field name='notification_email_send'/> |
776 | + </xpath> |
777 | <xpath expr="//sheet" position="after"> |
778 | <div class="oe_chatter"> |
779 | <field name="message_follower_ids" widget="mail_followers"/> |
780 | |
781 | === modified file 'mail/tests/test_mail_features.py' |
782 | --- mail/tests/test_mail_features.py 2013-02-14 12:39:25 +0000 |
783 | +++ mail/tests/test_mail_features.py 2013-03-13 13:50:27 +0000 |
784 | @@ -154,7 +154,7 @@ |
785 | new_mail = self.mail_message.browse(cr, uid, self.mail_message.search(cr, uid, [('message_id', '=', test_msg_id)])[0]) |
786 | # Test: author_id set, not email_from |
787 | self.assertEqual(new_mail.author_id, user_raoul.partner_id, 'message process wrong author found') |
788 | - self.assertFalse(new_mail.email_from, 'message process should not set the email_from when an author is found') |
789 | + self.assertEqual(new_mail.email_from, user_raoul.email, 'message process wrong email_from') |
790 | |
791 | # Do: post a new message, with a unknown partner |
792 | test_msg_id = '<deadcafe.1337-3@smtp.agrolait.com>' |
793 | @@ -524,7 +524,7 @@ |
794 | # 1. mass_mail on pigs and bird |
795 | compose_id = mail_compose.create(cr, uid, |
796 | {'subject': _subject, 'body': '${object.description}'}, |
797 | - {'default_composition_mode': 'mass_mail', 'default_model': 'mail.group', 'default_res_id': False, |
798 | + {'default_composition_mode': 'mass_mail', 'default_model': 'mail.group', 'default_res_id': False, 'default_notify': True, |
799 | 'active_ids': [self.group_pigs_id, group_bird_id]}) |
800 | compose = mail_compose.browse(cr, uid, compose_id) |
801 | |
802 | |
803 | === modified file 'mail/wizard/invite.py' |
804 | --- mail/wizard/invite.py 2013-02-21 14:22:49 +0000 |
805 | +++ mail/wizard/invite.py 2013-03-13 13:50:27 +0000 |
806 | @@ -66,16 +66,17 @@ |
807 | signature = user_id and user_id["signature"] or '' |
808 | if signature: |
809 | wizard.message = tools.append_content_to_html(wizard.message, signature, plaintext=True, container_tag='div') |
810 | + |
811 | # send mail to new followers |
812 | - for follower_id in new_follower_ids: |
813 | - mail_mail = self.pool.get('mail.mail') |
814 | - # the invite wizard should create a private message not related to any object -> no model, no res_id |
815 | - mail_id = mail_mail.create(cr, uid, { |
816 | - 'model': wizard.res_model, |
817 | - 'res_id': wizard.res_id, |
818 | - 'subject': 'Invitation to follow %s' % document.name_get()[0][1], |
819 | - 'body_html': '%s' % wizard.message, |
820 | - 'auto_delete': True, |
821 | - }, context=context) |
822 | - mail_mail.send(cr, uid, [mail_id], recipient_ids=[follower_id], context=context) |
823 | + # the invite wizard should create a private message not related to any object -> no model, no res_id |
824 | + mail_mail = self.pool.get('mail.mail') |
825 | + mail_id = mail_mail.create(cr, uid, { |
826 | + 'model': wizard.res_model, |
827 | + 'res_id': wizard.res_id, |
828 | + 'subject': 'Invitation to follow %s' % document.name_get()[0][1], |
829 | + 'body_html': '%s' % wizard.message, |
830 | + 'auto_delete': True, |
831 | + 'recipient_ids': [(4, id) for id in new_follower_ids] |
832 | + }, context=context) |
833 | + mail_mail.send(cr, uid, [mail_id], context=context) |
834 | return {'type': 'ir.actions.act_window_close'} |
835 | |
836 | === modified file 'mail/wizard/mail_compose_message.py' |
837 | --- mail/wizard/mail_compose_message.py 2013-01-08 16:13:32 +0000 |
838 | +++ mail/wizard/mail_compose_message.py 2013-03-13 13:50:27 +0000 |
839 | @@ -113,6 +113,12 @@ |
840 | 'partner_ids': fields.many2many('res.partner', |
841 | 'mail_compose_message_res_partner_rel', |
842 | 'wizard_id', 'partner_id', 'Additional contacts'), |
843 | + 'post': fields.boolean('Post a copy in the document', |
844 | + help='Post a copy of the message on the document communication history.'), |
845 | + 'notify': fields.boolean('Notify followers', |
846 | + help='Notify followers of the document'), |
847 | + 'same_thread': fields.boolean('Replies in the document', |
848 | + help='Replies to the messages will go into the selected document.'), |
849 | 'attachment_ids': fields.many2many('ir.attachment', |
850 | 'mail_compose_message_ir_attachments_rel', |
851 | 'wizard_id', 'attachment_id', 'Attachments'), |
852 | @@ -121,9 +127,13 @@ |
853 | |
854 | _defaults = { |
855 | 'composition_mode': 'comment', |
856 | + 'email_from': lambda self, cr, uid, ctx={}: self.pool.get('mail.mail')._get_default_from(cr, uid, context=ctx), |
857 | 'body': lambda self, cr, uid, ctx={}: '', |
858 | 'subject': lambda self, cr, uid, ctx={}: False, |
859 | 'partner_ids': lambda self, cr, uid, ctx={}: [], |
860 | + 'notify': lambda self, cr, uid, ctx={}: False, |
861 | + 'post': lambda self, cr, uid, ctx={}: True, |
862 | + 'same_thread': lambda self, cr, uid, ctx={}: True, |
863 | } |
864 | |
865 | def _notify(self, cr, uid, newid, context=None): |
866 | @@ -215,15 +225,27 @@ |
867 | new_attachments = email_dict.pop('attachments', []) |
868 | post_values['attachments'] += new_attachments |
869 | post_values.update(email_dict) |
870 | + # email_from: mass mailing only can specify another email_from |
871 | + if email_dict.get('email_from'): |
872 | + post_values['email_from'] = email_dict.pop('email_from') |
873 | + # replies redirection: mass mailing only |
874 | + if not wizard.same_thread: |
875 | + post_values['reply_to'] = email_dict.pop('reply_to') |
876 | # automatically subscribe recipients if asked to |
877 | if context.get('mail_post_autofollow') and wizard.model and post_values.get('partner_ids'): |
878 | active_model_pool.message_subscribe(cr, uid, [res_id], [item[1] for item in post_values.get('partner_ids')], context=context) |
879 | # post the message |
880 | - active_model_pool.message_post(cr, uid, [res_id], type='comment', subtype='mt_comment', context=context, **post_values) |
881 | - |
882 | - # post process: update attachments, because id is not necessarily known when adding attachments in Chatter |
883 | - # self.pool.get('ir.attachment').write(cr, uid, [attach.id for attach in wizard.attachment_ids], { |
884 | - # 'res_id': wizard.id, 'res_model': wizard.model or False}, context=context) |
885 | + if mass_mail_mode and not wizard.post: |
886 | + post_values['recipient_ids'] = post_values.pop('partner_ids') |
887 | + self.pool.get('mail.mail').create(cr, uid, post_values, context=context) |
888 | + else: |
889 | + subtype = 'mail.mt_comment' |
890 | + if mass_mail_mode and not wizard.notify: |
891 | + subtype = False |
892 | + msg_id = active_model_pool.message_post(cr, uid, [res_id], type='comment', subtype=subtype, context=context, **post_values) |
893 | + # mass_mailing, post without notify: notify specific partners |
894 | + if mass_mail_mode and not wizard.notify and post_values['partner_ids']: |
895 | + self.pool.get('mail.notification')._notify(cr, uid, msg_id, [item[1] for item in post_values['partner_ids']], context=context) |
896 | |
897 | return {'type': 'ir.actions.act_window_close'} |
898 | |
899 | @@ -234,6 +256,8 @@ |
900 | return { |
901 | 'subject': self.render_template(cr, uid, wizard.subject, wizard.model, res_id, context), |
902 | 'body': self.render_template(cr, uid, wizard.body, wizard.model, res_id, context), |
903 | + 'email_from': self.render_template(cr, uid, wizard.email_from, wizard.model, res_id, context), |
904 | + 'reply_to': self.render_template(cr, uid, wizard.reply_to, wizard.model, res_id, context), |
905 | } |
906 | |
907 | def render_template(self, cr, uid, template, model, res_id, context=None): |
908 | @@ -258,7 +282,7 @@ |
909 | result = eval(exp, { |
910 | 'user': self.pool.get('res.users').browse(cr, uid, uid, context=context), |
911 | 'object': self.pool.get(model).browse(cr, uid, res_id, context=context), |
912 | - 'context': dict(context), # copy context to prevent side-effects of eval |
913 | + 'context': dict(context), # copy context to prevent side-effects of eval |
914 | }) |
915 | return result and tools.ustr(result) or '' |
916 | return template and EXPRESSION_PATTERN.sub(merge, template) |
917 | |
918 | === modified file 'mail/wizard/mail_compose_message_view.xml' |
919 | --- mail/wizard/mail_compose_message_view.xml 2012-12-20 17:20:12 +0000 |
920 | +++ mail/wizard/mail_compose_message_view.xml 2013-03-13 13:50:27 +0000 |
921 | @@ -13,24 +13,33 @@ |
922 | <field name="res_id" invisible="1"/> |
923 | <field name="parent_id" invisible="1"/> |
924 | <!-- visible wizard --> |
925 | - <label for="partner_ids" string="Recipients"/> |
926 | - <div groups="base.group_user"> |
927 | - <span attrs="{'invisible':['|', ('model', '=', False), ('composition_mode', '!=', 'mass_mail')]}"> |
928 | - Followers of selected items and |
929 | - </span> |
930 | - <span attrs="{'invisible':['|', ('model', '=', False), ('composition_mode', '=', 'mass_mail')]}"> |
931 | + <field name="email_from" |
932 | + attrs="{'invisible':[('composition_mode', '!=', 'mass_mail')]}"/> |
933 | + <field name="subject" placeholder="Subject..." required="True"/> |
934 | + <field name="post" groups="base.group_no_one" |
935 | + attrs="{'invisible':[('composition_mode', '!=', 'mass_mail')]}"/> |
936 | + <field name="notify" groups="base.group_no_one" |
937 | + attrs="{'invisible':['|', ('post', '!=', True), ('composition_mode', '!=', 'mass_mail')]}"/> |
938 | + <field name="same_thread" |
939 | + attrs="{'invisible':[('composition_mode', '!=', 'mass_mail')]}"/> |
940 | + <field name="reply_to" placeholder="Email address te redirect replies..." |
941 | + attrs="{'invisible':['|', ('same_thread', '=', True), ('composition_mode', '!=', 'mass_mail')], |
942 | + 'required':[('same_thread', '!=', True)]}"/> |
943 | + <label for="partner_ids" string="Recipients" |
944 | + attrs="{'invisible':[('composition_mode', '=', 'mass_mail')]}"/> |
945 | + <div groups="base.group_user" |
946 | + attrs="{'invisible':[('composition_mode', '=', 'mass_mail')]}"> |
947 | + <span attrs="{'invisible':[('model', '=', False)]}"> |
948 | Followers of |
949 | - <field name="record_name" readonly="1" class="oe_inline" |
950 | - attrs="{'invisible':[('model', '=', False)]}"/> |
951 | - and |
952 | + <field name="record_name" readonly="1" class="oe_inline"/> |
953 | + and |
954 | </span> |
955 | <field name="partner_ids" widget="many2many_tags_email" placeholder="Add contacts to notify..." |
956 | context="{'force_email':True, 'show_email':True}"/> |
957 | </div> |
958 | - <field name="subject" placeholder="Subject..."/> |
959 | </group> |
960 | <field name="body"/> |
961 | - <field name="attachment_ids" widget="many2many_binary"/> |
962 | + <field name="attachment_ids" widget="many2many_binary" string="Attach a file"/> |
963 | <footer> |
964 | <button string="Send" name="send_mail" type="object" class="oe_highlight"/> |
965 | or |
966 | @@ -51,7 +60,7 @@ |
967 | </record> |
968 | |
969 | <!-- Replace the default mass-mailing wizard in base with the composition wizard --> |
970 | - <act_window name="Mass Mailing" |
971 | + <act_window name="Partner Mass Mailing" |
972 | res_model="mail.compose.message" |
973 | src_model="res.partner" |
974 | view_mode="form" |
975 | @@ -59,6 +68,10 @@ |
976 | target="new" |
977 | key2="client_action_multi" |
978 | id="base.action_partner_mass_mail" |
979 | - context="{'default_composition_mode': 'mass_mail'}"/> |
980 | + context="{ |
981 | + 'default_composition_mode': 'mass_mail', |
982 | + 'default_partner_to': '${object.id or \'\'}', |
983 | + }"/> |
984 | + |
985 | </data> |
986 | </openerp> |
987 | |
988 | === modified file 'portal_sale/portal_sale.py' |
989 | --- portal_sale/portal_sale.py 2012-12-21 11:53:15 +0000 |
990 | +++ portal_sale/portal_sale.py 2013-03-13 13:50:27 +0000 |
991 | @@ -59,12 +59,11 @@ |
992 | assert len(ids) == 1 |
993 | document = self.browse(cr, uid, ids[0], context=context) |
994 | partner = document.partner_id |
995 | + # TDE note: this code should be improved: used a real invite wizard instead of an ugly email |
996 | if partner.id not in document.message_follower_ids: |
997 | self.message_subscribe(cr, uid, ids, [partner.id], context=context) |
998 | - user = self.pool.get('res.users').browse(cr, uid, uid, context=context) |
999 | mail_values = { |
1000 | - 'email_from': user.partner_id.email, |
1001 | - 'email_to': partner.email, |
1002 | + 'recipient_ids': [(4, partner.id)], |
1003 | 'subject': 'Invitation to follow %s' % document.name_get()[0][1], |
1004 | 'body_html': 'You have been invited to follow %s' % document.name_get()[0][1], |
1005 | 'auto_delete': True, |
1006 | @@ -72,7 +71,7 @@ |
1007 | } |
1008 | mail_obj = self.pool.get('mail.mail') |
1009 | mail_id = mail_obj.create(cr, uid, mail_values, context=context) |
1010 | - mail_obj.send(cr, uid, [mail_id], recipient_ids=[partner.id], context=context) |
1011 | + mail_obj.send(cr, uid, [mail_id], context=context) |
1012 | return super(sale_order, self).action_button_confirm(cr, uid, ids, context=context) |
1013 | |
1014 | def get_signup_url(self, cr, uid, ids, context=None): |
1015 | @@ -120,12 +119,11 @@ |
1016 | # fetch the partner's id and subscribe the partner to the invoice |
1017 | document = self.browse(cr, uid, ids[0], context=context) |
1018 | partner = document.partner_id |
1019 | + # TDE note: this code should be improved: used a real invite wizard instead of an ugly email |
1020 | if partner.id not in document.message_follower_ids: |
1021 | self.message_subscribe(cr, uid, ids, [partner.id], context=context) |
1022 | - user = self.pool.get('res.users').browse(cr, uid, uid, context=context) |
1023 | mail_values = { |
1024 | - 'email_from': user.partner_id.email, |
1025 | - 'email_to': partner.email, |
1026 | + 'recipient_ids': [(4, partner.id)], |
1027 | 'subject': 'Invitation to follow %s' % document.name_get()[0][1], |
1028 | 'body_html': 'You have been invited to follow %s' % document.name_get()[0][1], |
1029 | 'auto_delete': True, |
1030 | @@ -133,7 +131,7 @@ |
1031 | } |
1032 | mail_obj = self.pool.get('mail.mail') |
1033 | mail_id = mail_obj.create(cr, uid, mail_values, context=context) |
1034 | - mail_obj.send(cr, uid, [mail_id], recipient_ids=[partner.id], context=context) |
1035 | + mail_obj.send(cr, uid, [mail_id], context=context) |
1036 | return super(account_invoice, self).invoice_validate(cr, uid, ids, context=context) |
1037 | |
1038 | def get_signup_url(self, cr, uid, ids, context=None): |
1039 | |
1040 | === modified file 'portal_sale/portal_sale_data.xml' |
1041 | --- portal_sale/portal_sale_data.xml 2012-12-21 16:48:08 +0000 |
1042 | +++ portal_sale/portal_sale_data.xml 2013-03-13 13:50:27 +0000 |
1043 | @@ -8,7 +8,7 @@ |
1044 | <field name="name">Sales Order - Send by Email (Portal)</field> |
1045 | <field name="email_from">${object.user_id.email or ''}</field> |
1046 | <field name="subject">${object.company_id.name} ${object.state in ('draft', 'sent') and 'Quotation' or 'Order'} (Ref ${object.name or 'n/a' })</field> |
1047 | - <field name="email_recipients">${object.partner_invoice_id.id}</field> |
1048 | + <field name="partner_to">${object.partner_invoice_id.id}</field> |
1049 | <field name="model_id" ref="sale.model_sale_order"/> |
1050 | <field name="auto_delete" eval="True"/> |
1051 | <field name="report_template" ref="sale.report_sale_order"/> |
1052 | @@ -97,7 +97,7 @@ |
1053 | <field name="name">Invoice - Send by Email (Portal)</field> |
1054 | <field name="email_from">${object.user_id.email or object.company_id.email or 'noreply@localhost'}</field> |
1055 | <field name="subject">${object.company_id.name} Invoice (Ref ${object.number or 'n/a' })</field> |
1056 | - <field name="email_recipients">${object.partner_id.id}</field> |
1057 | + <field name="partner_to">${object.partner_id.id}</field> |
1058 | <field name="model_id" ref="account.model_account_invoice"/> |
1059 | <field name="auto_delete" eval="True"/> |
1060 | <field name="report_template" ref="account.account_invoices"/> |
1061 | |
1062 | === modified file 'purchase/edi/purchase_order_action_data.xml' |
1063 | --- purchase/edi/purchase_order_action_data.xml 2012-12-14 09:10:57 +0000 |
1064 | +++ purchase/edi/purchase_order_action_data.xml 2013-03-13 13:50:27 +0000 |
1065 | @@ -21,7 +21,7 @@ |
1066 | <field name="name">Purchase Order - Send by mail</field> |
1067 | <field name="email_from">${object.validator.email or ''}</field> |
1068 | <field name="subject">${object.company_id.name} Order (Ref ${object.name or 'n/a' })</field> |
1069 | - <field name="email_recipients">${object.partner_id.id}</field> |
1070 | + <field name="partner_to">${object.partner_id.id}</field> |
1071 | <field name="model_id" ref="purchase.model_purchase_order"/> |
1072 | <field name="auto_delete" eval="True"/> |
1073 | <field name="report_template" ref="report_purchase_quotation"/> |
1074 | |
1075 | === modified file 'sale/edi/sale_order_action_data.xml' |
1076 | --- sale/edi/sale_order_action_data.xml 2012-12-21 16:48:08 +0000 |
1077 | +++ sale/edi/sale_order_action_data.xml 2013-03-13 13:50:27 +0000 |
1078 | @@ -22,7 +22,7 @@ |
1079 | <field name="name">Sales Order - Send by Email</field> |
1080 | <field name="email_from">${object.user_id.email or ''}</field> |
1081 | <field name="subject">${object.company_id.name} ${object.state in ('draft', 'sent') and 'Quotation' or 'Order'} (Ref ${object.name or 'n/a' })</field> |
1082 | - <field name="email_recipients">${object.partner_invoice_id.id}</field> |
1083 | + <field name="partner_to">${object.partner_invoice_id.id}</field> |
1084 | <field name="model_id" ref="sale.model_sale_order"/> |
1085 | <field name="auto_delete" eval="True"/> |
1086 | <field name="report_template" ref="report_sale_order"/> |