Merge lp:~openerp-dev/openobject-addons/trunk-mass-mailing-tde into lp:openobject-addons

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
Reviewer Review Type Date Requested Status
OpenERP Core Team Pending
Review via email: mp+150403@code.launchpad.net
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"/>

Subscribers

People subscribed via source and target branches

to all changes: