[IMP] [REF] mail_compose_message: slightly cleaned wizard code to lessen the number...
authorThibault Delavallée <tde@openerp.com>
Mon, 17 Mar 2014 11:53:15 +0000 (12:53 +0100)
committerThibault Delavallée <tde@openerp.com>
Mon, 17 Mar 2014 11:53:15 +0000 (12:53 +0100)
to remove unnecessary code.
post and filter_id fields have been removed as they are not necessary.
Updated tests accordingly to new mass mailing tests: mass mailing create
emails, no message_post anymore using followers.

bzr revid: tde@openerp.com-20140317115315-ws8dvjbiv5rspk43

addons/email_template/tests/test_mail.py
addons/mail/mail_message.py
addons/mail/tests/test_mail_features.py
addons/mail/wizard/mail_compose_message.py

index 580421f..b643ca8 100644 (file)
@@ -74,7 +74,7 @@ class test_message_compose(TestMail):
 
         # 1. Comment on pigs
         compose_id = mail_compose.create(cr, uid,
-            {'subject': 'Forget me subject', 'body': '<p>Dummy body</p>', 'post': True},
+            {'subject': 'Forget me subject', 'body': '<p>Dummy body</p>'},
             {'default_composition_mode': 'comment',
                 'default_model': 'mail.group',
                 'default_res_id': self.group_pigs_id,
@@ -102,7 +102,7 @@ class test_message_compose(TestMail):
             'default_template_id': email_template_id,
             'active_ids': [self.group_pigs_id, self.group_bird_id]
         }
-        compose_id = mail_compose.create(cr, uid, {'subject': 'Forget me subject', 'body': 'Dummy body', 'post': True}, context)
+        compose_id = mail_compose.create(cr, uid, {'subject': 'Forget me subject', 'body': 'Dummy body'}, context)
         compose = mail_compose.browse(cr, uid, compose_id, context)
         onchange_res = compose.onchange_template_id(email_template_id, 'comment', 'mail.group', self.group_pigs_id)['value']
         onchange_res['partner_ids'] = [(4, partner_id) for partner_id in onchange_res.pop('partner_ids', [])]
@@ -146,7 +146,7 @@ class test_message_compose(TestMail):
             'default_partner_ids': [p_a_id],
             'active_ids': [self.group_pigs_id, self.group_bird_id]
         }
-        compose_id = mail_compose.create(cr, uid, {'subject': 'Forget me subject', 'body': 'Dummy body', 'post': True}, context)
+        compose_id = mail_compose.create(cr, uid, {'subject': 'Forget me subject', 'body': 'Dummy body'}, context)
         compose = mail_compose.browse(cr, uid, compose_id, context)
         onchange_res = compose.onchange_template_id(email_template_id, 'mass_mail', 'mail.group', self.group_pigs_id)['value']
         onchange_res['partner_ids'] = [(4, partner_id) for partner_id in onchange_res.pop('partner_ids', [])]
@@ -172,12 +172,12 @@ class test_message_compose(TestMail):
         self.assertIn(_body_html1, message_pigs.body, 'mail.message body on Pigs incorrect')
         self.assertIn(_body_html2, message_bird.body, 'mail.message body on Bird incorrect')
         # Test: partner_ids: p_a_id (default) + 3 newly created partners
-        message_pigs_pids = [partner.id for partner in message_pigs.notified_partner_ids]
-        message_bird_pids = [partner.id for partner in message_bird.notified_partner_ids]
-        partner_ids = self.res_partner.search(cr, uid, [('email', 'in', ['b@b.b', 'c@c.c', 'd@d.d'])])
-        partner_ids.append(p_a_id)
-        self.assertEqual(set(message_pigs_pids), set(partner_ids), 'mail.message on pigs incorrect number of notified_partner_ids')
-        self.assertEqual(set(message_bird_pids), set(partner_ids), 'mail.message on bird notified_partner_ids incorrect')
+        # message_pigs_pids = [partner.id for partner in message_pigs.notified_partner_ids]
+        # message_bird_pids = [partner.id for partner in message_bird.notified_partner_ids]
+        # partner_ids = self.res_partner.search(cr, uid, [('email', 'in', ['b@b.b', 'c@c.c', 'd@d.d'])])
+        # partner_ids.append(p_a_id)
+        # self.assertEqual(set(message_pigs_pids), set(partner_ids), 'mail.message on pigs incorrect number of notified_partner_ids')
+        # self.assertEqual(set(message_bird_pids), set(partner_ids), 'mail.message on bird notified_partner_ids incorrect')
 
         # ----------------------------------------
         # CASE4: test newly introduced partner_to field
index dce5ed1..9557513 100644 (file)
@@ -82,10 +82,10 @@ class mail_message(osv.Model):
             context = dict(context, default_type=None)
         return super(mail_message, self).default_get(cr, uid, fields, context=context)
 
-    def _shorten_name(self, name):
-        if len(name) <= (self._message_record_name_length + 3):
-            return name
-        return name[:self._message_record_name_length] + '...'
+    # def _shorten_name(self, name):
+    #     if len(name) <= (self._message_record_name_length + 3):
+    #         return name
+    #     return name[:self._message_record_name_length] + '...'
 
     def _get_to_read(self, cr, uid, ids, name, arg, context=None):
         """ Compute if the message is unread by the current user. """
@@ -125,15 +125,15 @@ class mail_message(osv.Model):
             inversed because we search unread message on a read column. """
         return ['&', ('notification_ids.partner_id.user_ids', 'in', [uid]), ('notification_ids.starred', '=', domain[0][2])]
 
-    def name_get(self, cr, uid, ids, context=None):
-        # name_get may receive int id instead of an id list
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        res = []
-        for message in self.browse(cr, uid, ids, context=context):
-            name = '%s: %s' % (message.subject or '', strip_tags(message.body or '') or '')
-            res.append((message.id, self._shorten_name(name.lstrip(' :'))))
-        return res
+    # def name_get(self, cr, uid, ids, context=None):
+    #     # name_get may receive int id instead of an id list
+    #     if isinstance(ids, (int, long)):
+    #         ids = [ids]
+    #     res = []
+    #     for message in self.browse(cr, uid, ids, context=context):
+    #         name = '%s: %s' % (message.subject or '', strip_tags(message.body or '') or '')
+    #         res.append((message.id, self._shorten_name(name.lstrip(' :'))))
+    #     return res
 
     _columns = {
         'type': fields.selection([
index 3200169..af77a8c 100644 (file)
@@ -656,7 +656,6 @@ class test_mail(TestMail):
                 'attachment_ids': [(0, 0, _attachments[0]), (0, 0, _attachments[1])]
             }, context={
                 'default_composition_mode': 'reply',
-                'default_model': 'mail.thread',
                 'default_res_id': self.group_pigs_id,
                 'default_parent_id': message.id
             })
@@ -681,11 +680,10 @@ class test_mail(TestMail):
         # --------------------------------------------------
 
         # Do: Compose in mass_mail_mode on pigs and bird
-        compose_id = mail_compose.create(cr, user_raoul.id,
-            {
+        compose_id = mail_compose.create(
+            cr, user_raoul.id, {
                 'subject': _subject,
                 'body': '${object.description}',
-                'post': True,
                 'partner_ids': [(4, p_c_id), (4, p_d_id)],
             }, context={
                 'default_composition_mode': 'mass_mail',
@@ -700,6 +698,13 @@ class test_mail(TestMail):
                         'default_res_id': -1,
                         'active_ids': [self.group_pigs_id, group_bird_id]
                     })
+        # check mail_mail
+        mail_mail_ids = self.mail_mail.search(cr, uid, [('subject', '=', _subject)])
+        for mail_mail in self.mail_mail.browse(cr, uid, mail_mail_ids):
+            self.assertEqual(set([p.id for p in mail_mail.recipient_ids]), set([p_c_id, p_d_id]),
+                             'compose wizard: mail_mail mass mailing: mail.mail in mass mail incorrect recipients')
+
+        # check logged messages
         group_pigs.refresh()
         group_bird.refresh()
         message1 = group_pigs.message_ids[0]
@@ -715,14 +720,14 @@ class test_mail(TestMail):
                         'compose wizard: message_post: mail.message in mass mail subject incorrect')
         self.assertEqual(message1.body, '<p>%s</p>' % group_pigs.description,
                         'compose wizard: message_post: mail.message in mass mail body incorrect')
-        self.assertEqual(set([p.id for p in message1.notified_partner_ids]), set([p_c_id, p_d_id]),
-                        'compose wizard: message_post: mail.message in mass mail incorrect notified partners')
+        # self.assertEqual(set([p.id for p in message1.notified_partner_ids]), set([p_c_id, p_d_id]),
+        #                 'compose wizard: message_post: mail.message in mass mail incorrect notified partners')
         self.assertEqual(message2.subject, _subject,
                         'compose wizard: message_post: mail.message in mass mail subject incorrect')
         self.assertEqual(message2.body, '<p>%s</p>' % group_bird.description,
                         'compose wizard: message_post: mail.message in mass mail body incorrect')
-        self.assertEqual(set([p.id for p in message2.notified_partner_ids]), set([p_c_id, p_d_id]),
-                        'compose wizard: message_post: mail.message in mass mail incorrect notified partners')
+        # self.assertEqual(set([p.id for p in message2.notified_partner_ids]), set([p_c_id, p_d_id]),
+        #                 'compose wizard: message_post: mail.message in mass mail incorrect notified partners')
 
         # Test: mail.group followers: author not added as follower in mass mail mode
         pigs_pids = [p.id for p in group_pigs.message_follower_ids]
index fc331cb..7bdc344 100644 (file)
@@ -38,10 +38,7 @@ class mail_compose_message(osv.TransientModel):
         at model and view levels to provide specific features.
 
         The behavior of the wizard depends on the composition_mode field:
-        - 'reply': reply to a previous message. The wizard is pre-populated
-            via ``get_message_data``.
-        - 'comment': new post on a record. The wizard is pre-populated via
-            ``get_record_data``
+        - 'comment': post on a record. The wizard is pre-populated via ``get_record_data``
         - 'mass_mail': wizard in mass mailing mode where the mail details can
             contain template placeholders that will be merged with actual data
             before being sent to each recipient.
@@ -50,6 +47,7 @@ class mail_compose_message(osv.TransientModel):
     _inherit = 'mail.message'
     _description = 'Email composition wizard'
     _log_access = True
+    _batch_size = 500
 
     def default_get(self, cr, uid, fields, context=None):
         """ Handle composition mode. Some details about context keys:
@@ -82,10 +80,8 @@ class mail_compose_message(osv.TransientModel):
         if 'active_domain' in context:  # not context.get() because we want to keep global [] domains
             vals['use_active_domain'] = True
             vals['active_domain'] = '%s' % context.get('active_domain')
-        if result.get('parent_id'):
-            vals.update(self.get_message_data(cr, uid, result.get('parent_id'), context=context))
-        if result['composition_mode'] == 'comment' and result['model'] and result['res_id']:
-            vals.update(self.get_record_data(cr, uid, result['model'], result['res_id'], context=context))
+        if result['composition_mode'] == 'comment':
+            vals.update(self.get_record_data(cr, uid, result, context=context))
         result['recipients_data'] = self.get_recipients_data(cr, uid, result, context=context)
 
         for field in vals:
@@ -120,14 +116,14 @@ class mail_compose_message(osv.TransientModel):
             help='Helper field used in mass mailing to display a sample of recipients'),
         'use_active_domain': fields.boolean('Use active domain'),
         'active_domain': fields.char('Active domain', readonly=True),
+        'attachment_ids': fields.many2many('ir.attachment',
+            'mail_compose_message_ir_attachments_rel',
+            'wizard_id', 'attachment_id', 'Attachments'),
+        # mass mode options
         'notify': fields.boolean('Notify followers',
             help='Notify followers of the document (mass post only)'),
         'same_thread': fields.boolean('Replies in the document',
             help='Replies to the messages will go into the selected document (mass mail only)'),
-        'attachment_ids': fields.many2many('ir.attachment',
-            'mail_compose_message_ir_attachments_rel',
-            'wizard_id', 'attachment_id', 'Attachments'),
-        'filter_id': fields.many2one('ir.filters', 'Filters'),
     }
     #TODO change same_thread to False in trunk (Require view update)
     _defaults = {
@@ -135,7 +131,6 @@ class mail_compose_message(osv.TransientModel):
         'body': lambda self, cr, uid, ctx={}: '',
         'subject': lambda self, cr, uid, ctx={}: False,
         'partner_ids': lambda self, cr, uid, ctx={}: [],
-        'notify': True,
         'same_thread': True,
     }
 
@@ -175,58 +170,54 @@ class mail_compose_message(osv.TransientModel):
             doc_name = self.pool[model].name_get(cr, uid, [res_id], context=context)
             return doc_name and 'Followers of %s' % doc_name[0][1] or False
         elif composition_mode == 'mass_post' and model:
-            active_ids = context.get('active_ids', list())
-            if not active_ids:
-                return False
-            name_gets = [rec_name[1] for rec_name in self.pool[model].name_get(cr, uid, active_ids[:3], context=context)]
-            return 'Followers of selected documents (' + ', '.join(name_gets) + len(active_ids) > 3 and ', ...' or '' + ')'
+            if 'active_domain' in context:
+                active_ids = self.pool[model].search(cr, uid, eval(context['active_domain']), limit=100, context=context)
+            else:
+                active_ids = context.get('active_ids', list())
+            if active_ids:
+                name_gets = [rec_name[1] for rec_name in self.pool[model].name_get(cr, uid, active_ids[:3], context=context)]
+                return 'Followers of selected documents (' + ', '.join(name_gets) + len(active_ids) > 3 and ', ...' or '' + ')'
         return False
 
-    def get_record_data(self, cr, uid, model, res_id, context=None):
+    def get_record_data(self, cr, uid, values, context=None):
         """ Returns a defaults-like dict with initial values for the composition
             wizard when sending an email related to the document record
             identified by ``model`` and ``res_id``.
 
-            :param str model: model name of the document record this mail is
-                related to.
-            :param int res_id: id of the document record this mail is related to
-        """
-        doc_name_get = self.pool[model].name_get(cr, uid, [res_id], context=context)
-        return {
-            'record_name': doc_name_get and doc_name_get[0][1] or False,
-            'subject': doc_name_get and 'Re: %s' % doc_name_get[0][1] or False,
-        }
-
-    def get_message_data(self, cr, uid, message_id, context=None):
-        """ Returns a defaults-like dict with initial values for the composition
-            wizard when replying to the given message (e.g. including the quote
-            of the initial message, and the correct recipients).
-
-            :param int message_id: id of the mail.message to which the user
-                is replying.
-        """
-        if not message_id:
-            return {}
+            :param values: dictionary of default and already computed values for the mail_compose_message_res_partner_re
+            :type values: dict"""
         if context is None:
             context = {}
-        message_data = self.pool.get('mail.message').browse(cr, uid, message_id, context=context)
+        model, res_id, parent_id = values.get('model'), values.get('res_id'), values.get('parent_id')
+        record_name, subject, partner_ids = values.get('record_name'), values.get('subject'), values.get('partner_ids', list())
+        if parent_id:
+            message_data = self.pool.get('mail.message').browse(cr, uid, parent_id, context=context)
+            record_name = message_data.record_name,
+            subject = tools.ustr(message_data.subject or message_data.record_name or '')
+            if not model:
+                model = message_data.model
+            if not res_id:
+                res_id = message_data.res_id
+            # get partner_ids from original message
+            partner_ids += [partner.id for partner in message_data.partner_ids]
+            if context.get('is_private') and message_data.author_id:  # check message is private then add author also in partner list.
+                partner_ids += [message_data.author_id.id]
+        elif model and res_id:
+            doc_name_get = self.pool[model].name_get(cr, uid, [res_id], context=context)
+            record_name = doc_name_get and doc_name_get[0][1] or ''
+            subject = tools.ustr(record_name)
 
         # create subject
         re_prefix = _('Re:')
-        reply_subject = tools.ustr(message_data.subject or message_data.record_name or '')
-        if not (reply_subject.startswith('Re:') or reply_subject.startswith(re_prefix)):
-            reply_subject = "%s %s" % (re_prefix, reply_subject)
-
-        # get partner_ids from original message
-        partner_ids = [partner.id for partner in message_data.partner_ids] if message_data.partner_ids else []
-        partner_ids += context.get('default_partner_ids', [])
-        if context.get('is_private') and message_data.author_id:  # check message is private then add author also in partner list.
-            partner_ids += [message_data.author_id.id]
-        # update the result
+        if not (subject.startswith('Re:') or subject.startswith(re_prefix)):
+            subject = "%s %s" % (re_prefix, subject)
+
         return {
-            'record_name': message_data.record_name,
-            'subject': reply_subject,
-            'partner_ids': partner_ids,
+            'record_name': record_name,
+            'subject': subject,
+            'partner_ids': list(set(partner_ids)),
+            'model': model,
+            'res_id': res_id,
         }
 
     #------------------------------------------------------
@@ -238,8 +229,8 @@ class mail_compose_message(osv.TransientModel):
             email(s), rendering any template patterns on the fly if needed. """
         if context is None:
             context = {}
-        import datetime
-        print '--> beginning sending email', datetime.datetime.now()
+        # import datetime
+        # print '--> beginning sending email', datetime.datetime.now()
         # clean the context (hint: mass mailing sets some default values that
         # could be wrongly interpreted by mail_mail)
         context.pop('default_email_to', None)
@@ -260,24 +251,26 @@ class mail_compose_message(osv.TransientModel):
             else:
                 res_ids = [wizard.res_id]
 
-            print '----> before computing values', datetime.datetime.now()
-            all_mail_values = self.get_mail_values(cr, uid, wizard, res_ids, context=context)
-            print '----> after computing values', datetime.datetime.now()
-
-            for res_id, mail_values in all_mail_values.iteritems():
-                if wizard.composition_mode == 'mass_mail':
-                    self.pool['mail.mail'].create(cr, uid, mail_values, context=context)
-                else:
-                    subtype = 'mail.mt_comment'
-                    if context.get('mail_compose_log') or (wizard.composition_mode == 'mass_post' and not wizard.notify):  # log a note: subtype is False
-                        subtype = False
-                    if wizard.composition_mode == 'mass_post':
-                        context = dict(context,
-                                       mail_notify_force_send=False,  # do not send emails directly but use the queue instead
-                                       mail_create_nosubscribe=True)  # add context key to avoid subscribing the author
-                    active_model_pool.message_post(cr, uid, [res_id], type='comment', subtype=subtype, context=context, **mail_values)
-
-        print '--> finished sending email', datetime.datetime.now()
+            # print '----> before computing values', datetime.datetime.now()
+            # print '----> after computing values', datetime.datetime.now()
+
+            sliced_res_ids = [res_ids[i:i + self._batch_size] for i in range(0, len(res_ids), self._batch_size)]
+            for res_ids in sliced_res_ids:
+                all_mail_values = self.get_mail_values(cr, uid, wizard, res_ids, context=context)
+                for res_id, mail_values in all_mail_values.iteritems():
+                    if wizard.composition_mode == 'mass_mail':
+                        self.pool['mail.mail'].create(cr, uid, mail_values, context=context)
+                    else:
+                        subtype = 'mail.mt_comment'
+                        if context.get('mail_compose_log') or (wizard.composition_mode == 'mass_post' and not wizard.notify):  # log a note: subtype is False
+                            subtype = False
+                        if wizard.composition_mode == 'mass_post':
+                            context = dict(context,
+                                           mail_notify_force_send=False,  # do not send emails directly but use the queue instead
+                                           mail_create_nosubscribe=True)  # add context key to avoid subscribing the author
+                        active_model_pool.message_post(cr, uid, [res_id], type='comment', subtype=subtype, context=context, **mail_values)
+
+        # print '--> finished sending email', datetime.datetime.now()
         return {'type': 'ir.actions.act_window_close'}
 
     def get_mail_values(self, cr, uid, wizard, res_ids, context=None):
@@ -300,46 +293,36 @@ class mail_compose_message(osv.TransientModel):
                 'attachment_ids': [attach.id for attach in wizard.attachment_ids],
                 'author_id': wizard.author_id.id,
                 'email_from': wizard.email_from,
+                'record_name': wizard.record_name,
             }
             # mass mailing: rendering override wizard static values
             if mass_mail_mode and wizard.model:
+                # always keep a copy, reset record name (avoid browsing records)
+                mail_values.update(notification=True, model=wizard.model, res_id=res_id, record_name=False)
+                # auto deletion of mail_mail
+                if 'mail_auto_delete' in context:
+                    mail_values['auto_delete'] = context.get('mail_auto_delete')
                 # rendered values using template
                 email_dict = rendered_values[res_id]
                 mail_values['partner_ids'] += email_dict.pop('partner_ids', [])
-                # process attachments: should not be encoded before being processed by message_post / mail_mail create
-                attachments = []
-                if email_dict.get('attachments'):
-                    for name, enc_cont in email_dict.pop('attachments'):
-                        attachments.append((name, base64.b64decode(enc_cont)))
-                mail_values['attachments'] = attachments
-                attachment_ids = []
-                for attach_id in mail_values.pop('attachment_ids'):
-                    new_attach_id = self.pool.get('ir.attachment').copy(cr, uid, attach_id, {'res_model': self._name, 'res_id': wizard.id}, context=context)
-                    attachment_ids.append(new_attach_id)
-                mail_values['attachment_ids'] = attachment_ids
-                # replies redirection: mass mailing only
                 if wizard.same_thread:
                     email_dict.pop('reply_to', None)
                 else:
                     mail_values['reply_to'] = email_dict.pop('reply_to', None)
+                # if not mail_values.get('reply_to'):
+                #     mail_values['reply_to'] = mail_values['email_from']
                 mail_values.update(email_dict)
 
-                # value tweaking in mass mailing
-                mail_values['record_name'] = False  # avoid browsing the record for an email
-                if wizard.same_thread:  # same thread: keep a copy of the message in the chatter to enable the reply redirection
-                    mail_values.update(notification=True, model=wizard.model, res_id=res_id)
-                m2m_attachment_ids = self.pool['mail.thread']._message_preprocess_attachments(
+                # process attachments: should not be encoded before being processed by message_post / mail_mail create
+                mail_values['attachments'] = [(name, base64.b64decode(enc_cont)) for name, enc_cont in email_dict.pop('attachments', list())]
+                attachment_ids = []
+                for attach_id in mail_values.pop('attachment_ids'):
+                    new_attach_id = self.pool.get('ir.attachment').copy(cr, uid, attach_id, {'res_model': self._name, 'res_id': wizard.id}, context=context)
+                    attachment_ids.append(new_attach_id)
+                mail_values['attachment_ids'] = self.pool['mail.thread']._message_preprocess_attachments(
                     cr, uid, mail_values.pop('attachments', []),
-                    mail_values.pop('attachment_ids', []),
-                    'mail.message', 0,
-                    context=context)
-                mail_values['attachment_ids'] = m2m_attachment_ids
-                if not mail_values.get('reply_to'):
-                    mail_values['reply_to'] = mail_values['email_from']
-
-                # mail_mail values
-                if 'mail_auto_delete' in context:
-                    mail_values['auto_delete'] = context.get('mail_auto_delete')
+                    attachment_ids, 'mail.message', 0, context=context)
+                # mail_mail values: body -> body_html, partner_ids -> recipient_ids
                 mail_values['body_html'] = mail_values.get('body', '')
                 mail_values['recipient_ids'] = [(4, id) for id in mail_values.pop('partner_ids', [])]