Example #1
0
 def _send_build_result(self, short_message):
     "Send build result and error message to branch followers"
     context = {
         'subject':
         short_message,
         'build_url':
         self._get_action_url(
             **{
                 'res_id':
                 self.id,
                 'model':
                 self._name,
                 'view_type':
                 'form',
                 'action_id':
                 self.env.ref('smile_ci.action_repository_branch_build').id,
             }),
     }
     if self.commit_logs:
         context['commit_logs'] = tools.plaintext2html(self.commit_logs)
     if self._context.get('build_error'):
         context['build_error'] = tools.plaintext2html(
             self._context['build_error'])
     template = self.env.ref('smile_ci.mail_template_build_result')
     subject = self.env['mail.template'].with_context(
         context).render_template(template.subject, self._name, self.id)
     body = self.env['mail.template'].with_context(context).render_template(
         template.body_html, self._name, self.id)
     self.message_post(body=body,
                       subject=subject,
                       subtype='mail.mt_comment',
                       partner_ids=self.branch_id.partner_ids)
Example #2
0
 def rating_apply(self, rate, token=None, feedback=None, subtype=None):
     """ Apply a rating given a token. If the current model inherits from
     mail.thread mixing, a message is posted on its chatter.
     :param rate : the rating value to apply
     :type rate : float
     :param token : access token
     :param feedback : additional feedback
     :type feedback : string
     :param subtype : subtype for mail
     :type subtype : string
     :returns rating.rating record
     """
     Rating, rating = self.env['rating.rating'], None
     if token:
         rating = self.env['rating.rating'].search([('access_token', '=', token)], limit=1)
     else:
         rating = Rating.search([('res_model', '=', self._name), ('res_id', '=', self.ids[0])], limit=1)
     if rating:
         rating.write({'rating': rate, 'feedback': feedback, 'consumed': True})
         if hasattr(self, 'message_post'):
             feedback = tools.plaintext2html(feedback or '')
             self.message_post(
                 body="<img src='/rating/static/src/img/rating_%s.png' style='width:20px;height:20px;float:left;margin-right: 5px;'/>%s"
                 % (rate, feedback),
                 subtype=subtype or "mail.mt_comment",
                 author_id=rating.partner_id and rating.partner_id.id or None  # None will set the default author in mail_thread.py
             )
         if hasattr(self, 'stage_id') and self.stage_id and hasattr(self.stage_id, 'auto_validation_kanban_state') and self.stage_id.auto_validation_kanban_state:
             if rating.rating > 5:
                 self.write({'kanban_state': 'done'})
             if rating.rating < 5:
                 self.write({'kanban_state': 'blocked'})
     return rating
def new_message_post(self, cr, uid, thread_id, body='', subject=None, type='notification',
                     subtype=None, parent_id=False, attachments=None, context=None,
                     content_subtype='html', **kwargs):
    ''' Overwritten because it is desired to avoid duplicated messages
        :arguments see the function "message_post" of the file "mail_thread": odoo/addons/mail/mail_thread.py
        :return int: ID of newly created mail.message OR the id of original message which would be duplicated
    '''
    if context is None:
        context = {}

    if not thread_id:
        raise osv.except_osv(_('Invalid thread_id'), _('Thread ID is not set'))
    if isinstance(thread_id, (list, tuple)):
        thread_id = thread_id[0]

    result = False
    call_old_message_post_flag = True

    if attachments is None:
        # As long as there are any attachments new messages will be created
        # Otherwise...
        if context.get('mail_thread_no_duplicate', False):
            mail_message_obj = self.pool.get('mail.message')

            # Handle content subtype: if plaintext, convert into HTML
            if content_subtype == 'plaintext':
                body_handled = tools.plaintext2html(body)
                # We need to modify <br/> to <br> since it is saved in this way in the data base, but handled with <br/> in the record
                body_handled = body_handled.replace("<br/>", "<br>")
            else:
                if "<span>" in body:
                    body_handled = "<div>" + HTMLParser.HTMLParser().unescape(body) + "</div>"
                else:
                    body_handled = HTMLParser.HTMLParser().unescape(body)

            domain = [('res_id', '=', thread_id),
                      ('model', '=', self._name),
                      ('body', '=', body_handled),
                      ]
            if not context['mail_thread_no_duplicate']:
                # If a timedelta is passed, we use it
                date = datetime.now() - context['mail_thread_no_duplicate']
                domain.append(('create_date', '>', date.strftime(DEFAULT_SERVER_DATETIME_FORMAT)))
            mail_message_id = mail_message_obj.search(cr, uid,
                                                      domain,
                                                      context=context, limit=1)
            if mail_message_id:
                # Do nothing
                call_old_message_post_flag = False
                result = mail_message_id[0]

                # Put a message in the logger to show that it was ignored on purpose
                logger.debug("The message: '{0}' has been ignored on purpose since it is duplicated".format(body_handled))

    if call_old_message_post_flag:
        result = old_message_post(self, cr, uid, thread_id, body, subject,
                                  type, subtype, parent_id, attachments,
                                  context, content_subtype, **kwargs)
    return result
Example #4
0
 def test_plaintext2html(self):
     cases = [
         ("First \nSecond \nThird\n \nParagraph\n\r--\nSignature paragraph", 'div',
          "<div><p>First <br/>Second <br/>Third</p><p>Paragraph</p><p>--<br/>Signature paragraph</p></div>"),
         ("First<p>It should be escaped</p>\nSignature", False,
          "<p>First&lt;p&gt;It should be escaped&lt;/p&gt;<br/>Signature</p>")
     ]
     for content, container_tag, expected in cases:
         html = plaintext2html(content, container_tag)
         self.assertEqual(html, expected, 'plaintext2html is broken')
Example #5
0
 def test_plaintext2html(self):
     cases = [
         ("First \nSecond \nThird\n \nParagraph\n\r--\nSignature paragraph", 'div',
          "<div><p>First <br/>Second <br/>Third</p><p>Paragraph</p><p>--<br/>Signature paragraph</p></div>"),
         ("First<p>It should be escaped</p>\nSignature", False,
          "<p>First&lt;p&gt;It should be escaped&lt;/p&gt;<br/>Signature</p>")
     ]
     for content, container_tag, expected in cases:
         html = plaintext2html(content, container_tag)
         self.assertEqual(html, expected, 'plaintext2html is broken')
Example #6
0
 def _load_test_logs(self):
     _logger.info('Importing test logs for build_%s...' % self.id)
     log_obj = self.env['scm.repository.branch.build.log']
     pattern = re.compile(r'([^:]+addons/)(?P<file>[^$]*)')
     csv_input = cStringIO.StringIO(self._get_logs(TESTFILE))
     reader = csv.DictReader(csv_input)
     for vals in reader:
         filepath = vals['file']
         if filepath:
             match = pattern.match(filepath)
             if match:
                 vals['file'] = match.groupdict()['file']
         vals['build_id'] = self.id
         vals['code'] = 'TEST'
         vals['type'] = 'test'
         vals['exception'] = tools.plaintext2html(vals['exception'])
         log_obj.create(vals)
Example #7
0
    def send_transaction(self, values):
        res_transaction_id = False
        if values:
            ### Validate Data
            if not values.get('transaction_id'):
                return {
                    'faultCode': 0,
                    'faultString': 'Transaction is required.'
                }
            if not values.get('contract_id'):
                return {'faultCode': 0, 'faultString': 'Contract is required.'}
            if not values.get('date'):
                return {'faultCode': 0, 'faultString': 'Date is required.'}

            ### Find Data From DB
            contract_id = self.env['res.contract'].search(
                [('name', '=', values['contract_id']),
                 ('is_template', '=', False),
                 ('type_contract', '=', 'package')],
                limit=1)
            if not contract_id:
                return {
                    'faultCode': 0,
                    'faultString': 'contract_id doesn’t exist in Odoo db'
                }

            vals = {
                ### API Fields
                'name': values['transaction_id'],
                'contract_id': contract_id.id,
                'event_ref': values.get('event_ref'),
                'date': str(values['date']).strip(),
                'user': values.get('user'),
            }

            res_transaction_id = self.env['res.transaction'].create(vals)

        if res_transaction_id:
            msg = _("Transaction created \n Id: %s \n Event Ref: %s \n User: %s \n Date: %s") % \
                    (res_transaction_id.name, res_transaction_id.event_ref, res_transaction_id.user, res_transaction_id.date)
            res_transaction_id.contract_id.message_post(
                body=tools.plaintext2html(msg))
            return {'success': res_transaction_id.id}
        else:
            return {'faultCode': 0, 'faultString': 'Something went wrong!'}
Example #8
0
 def rating_apply(self, rate, token=None, feedback=None, subtype=None):
     """ Apply a rating given a token. If the current model inherits from
     mail.thread mixing, a message is posted on its chatter.
     :param rate : the rating value to apply
     :type rate : float
     :param token : access token
     :param feedback : additional feedback
     :type feedback : string
     :param subtype : subtype for mail
     :type subtype : string
     :returns rating.rating record
     """
     Rating, rating = self.env['rating.rating'], None
     if token:
         rating = self.env['rating.rating'].search(
             [('access_token', '=', token)], limit=1)
     else:
         rating = Rating.search([('res_model', '=', self._name),
                                 ('res_id', '=', self.ids[0])],
                                limit=1)
     if rating:
         rating.write({
             'rating': rate,
             'feedback': feedback,
             'consumed': True
         })
         if hasattr(self, 'message_post'):
             feedback = tools.plaintext2html(feedback or '')
             self.message_post(
                 body=
                 "<img src='/rating/static/src/img/rating_%s.png' style='width:20px;height:20px;float:left;margin-right: 5px;'/>%s"
                 % (rate, feedback),
                 subtype=subtype or "mail.mt_comment",
                 author_id=rating.partner_id and rating.partner_id.id or
                 None  # None will set the default author in mail_thread.py
             )
         if hasattr(self, 'stage_id') and self.stage_id and hasattr(
                 self.stage_id, 'auto_validation_kanban_state'
         ) and self.stage_id.auto_validation_kanban_state:
             if rating.rating > 5:
                 self.write({'kanban_state': 'done'})
             if rating.rating < 5:
                 self.write({'kanban_state': 'blocked'})
     return rating
Example #9
0
    def cancel_transaction(self, values):
        transaction_id = False
        if values:
            ### Validate Data
            if not values.get('transaction_id'):
                return {
                    'faultCode': 0,
                    'faultString': 'Transaction is required.'
                }
            if not values.get('motif'):
                return {'faultCode': 0, 'faultString': 'Motif is required.'}
            if not values.get('date_cancel'):
                return {
                    'faultCode': 0,
                    'faultString': 'Cancel Date is required.'
                }

            ### Find Data From DB
            transaction_id = self.env['res.transaction'].search(
                [('name', '=', values['transaction_id'])], limit=1)
            if not transaction_id:
                return {
                    'faultCode': 0,
                    'faultString': 'transaction_id doesn’t exist in Odoo db'
                }

            vals = {
                ### API Fields
                'cancel_date': str(values['date_cancel']).strip(),
                'motif': values['motif'],
            }
            transaction_id.write(vals)
            transaction_id.action_cancel()

        if transaction_id:
            msg = _("Transaction Cancelled \n Id: %s \n Motif: %s \n  Date Cancel: %s") % \
                    (transaction_id.name, transaction_id.motif, transaction_id.cancel_date)
            transaction_id.contract_id.message_post(
                body=tools.plaintext2html(msg))
            return {'success': transaction_id.id}
        else:
            return {'faultCode': 0, 'faultString': 'Something went wrong!'}
Example #10
0
    def message_post(self,
                     cr,
                     uid,
                     thread_id,
                     body='',
                     subject=None,
                     type='notification',
                     subtype=None,
                     parent_id=False,
                     attachments=None,
                     context=None,
                     content_subtype='html',
                     **kwargs):

        if context is None:
            context = {}

        context = dict(context or {})
        #The function is rewritten because of the following functionality
        if context.get('put_this_subtype_instead', False):
            subtype = context.get('put_this_subtype_instead')

        if not context.get('from_composer', False):
            context['mail_post_autofollow'] = False
            context['mail_create_nosubscribe'] = True
        #

        if attachments is None:
            attachments = {}
        mail_message = self.pool.get('mail.message')
        ir_attachment = self.pool.get('ir.attachment')

        assert (not thread_id) or \
                isinstance(thread_id, (int, long)) or \
                (isinstance(thread_id, (list, tuple)) and len(thread_id) == 1), \
                "Invalid thread_id; should be 0, False, an ID or a list with one ID"
        if isinstance(thread_id, (list, tuple)):
            thread_id = thread_id[0]

        #0: Find the message's author, because we need it for private discussion
        author_id = kwargs.get('author_id')
        if author_id is None:  # keep False values
            author_id = self.pool.get('mail.message')._get_default_author(
                cr, uid, context=context)

        user_ids = self.pool.get('res.users').search(
            cr, uid, [('partner_id', '=', author_id)], context=context)
        if user_ids:
            user_obj = self.pool.get('res.users').browse(cr,
                                                         uid,
                                                         user_ids[0],
                                                         context=context)
            if not context.get('lang') and user_obj.lang:
                ctx = context.copy()
                ctx['lang'] = user_obj.lang
                context = ctx

        # if we're processing a message directly coming from the gateway, the destination model was
        # set in the context.
        model = False
        if thread_id:
            model = context.get(
                'thread_model',
                False) if self._name == 'mail.thread' else self._name
            if model and model != self._name and hasattr(
                    self.pool[model], 'message_post'):
                del context['thread_model']
                return self.pool[model].message_post(
                    cr,
                    uid,
                    thread_id,
                    body=body,
                    subject=subject,
                    type=type,
                    subtype=subtype,
                    parent_id=parent_id,
                    attachments=attachments,
                    context=context,
                    content_subtype=content_subtype,
                    **kwargs)

        # 1: Handle content subtype: if plaintext, converto into HTML
        if content_subtype == 'plaintext':
            body = tools.plaintext2html(body)

        # 2: Private message: add recipients (recipients and author of parent message) - current author
        #   + legacy-code management (! we manage only 4 and 6 commands)
        partner_ids = set()
        kwargs_partner_ids = kwargs.pop('partner_ids', [])
        for partner_id in kwargs_partner_ids:
            if isinstance(
                    partner_id,
                (list, tuple)) and partner_id[0] == 4 and len(partner_id) == 2:
                partner_ids.add(partner_id[1])
            if isinstance(
                    partner_id,
                (list, tuple)) and partner_id[0] == 6 and len(partner_id) == 3:
                partner_ids |= set(partner_id[2])
            elif isinstance(partner_id, (int, long)):
                partner_ids.add(partner_id)
            else:
                pass  # we do not manage anything else
        if parent_id and not model:
            parent_message = mail_message.browse(cr,
                                                 uid,
                                                 parent_id,
                                                 context=context)
            private_followers = set(
                [partner.id for partner in parent_message.partner_ids])
            if parent_message.author_id:
                private_followers.add(parent_message.author_id.id)
            private_followers -= set([author_id])
            partner_ids |= private_followers

        # 3. Attachments
        #   - HACK TDE FIXME: Chatter: attachments linked to the document (not done JS-side), load the message
        attachment_ids = self._message_preprocess_attachments(
            cr, uid, attachments, kwargs.pop('attachment_ids', []), model,
            thread_id, context)

        # 4: mail.message.subtype
        subtype_id = False
        if subtype:
            if '.' not in subtype:
                subtype = 'mail.%s' % subtype
            subtype_id = self.pool.get('ir.model.data').xmlid_to_res_id(
                cr, uid, subtype)

        # automatically subscribe recipients if asked to
        if context.get('mail_post_autofollow') and thread_id and partner_ids:
            partner_to_subscribe = partner_ids
            if context.get('mail_post_autofollow_partner_ids'):
                partner_to_subscribe = filter(
                    lambda item: item in context.get(
                        'mail_post_autofollow_partner_ids'), partner_ids)
            self.message_subscribe(cr,
                                   uid, [thread_id],
                                   list(partner_to_subscribe),
                                   context=context)

        # _mail_flat_thread: automatically set free messages to the first posted message
        if self._mail_flat_thread and model and not parent_id and thread_id:
            message_ids = mail_message.search(cr,
                                              uid, [
                                                  '&',
                                                  ('res_id', '=', thread_id),
                                                  ('model', '=', model),
                                                  ('type', '=', 'email')
                                              ],
                                              context=context,
                                              order="id ASC",
                                              limit=1)
            if not message_ids:
                message_ids = message_ids = mail_message.search(
                    cr,
                    uid,
                    ['&', ('res_id', '=', thread_id), ('model', '=', model)],
                    context=context,
                    order="id ASC",
                    limit=1)
            parent_id = message_ids and message_ids[0] or False
        # we want to set a parent: force to set the parent_id to the oldest ancestor, to avoid having more than 1 level of thread
        elif parent_id:
            message_ids = mail_message.search(cr,
                                              SUPERUSER_ID,
                                              [('id', '=', parent_id),
                                               ('parent_id', '!=', False)],
                                              context=context)
            # avoid loops when finding ancestors
            processed_list = []
            if message_ids:
                message = mail_message.browse(cr,
                                              SUPERUSER_ID,
                                              message_ids[0],
                                              context=context)
                while (message.parent_id
                       and message.parent_id.id not in processed_list):
                    processed_list.append(message.parent_id.id)
                    message = message.parent_id
                parent_id = message.id

        values = kwargs

        values.update({
            'author_id': author_id,
            'model': model,
            'res_id': model and thread_id or False,
            'body': body,
            'subject': subject or False,
            'type': type,
            'parent_id': parent_id,
            'attachment_ids': attachment_ids,
            'subtype_id': subtype_id,
            'partner_ids': [(4, pid) for pid in partner_ids],
        })

        # Avoid warnings about non-existing fields
        for x in ('from', 'to', 'cc'):
            values.pop(x, None)

        # Post the message
        msg_id = mail_message.create(cr, uid, values, context=context)

        # Post-process: subscribe author, update message_last_post
        if model and model != 'mail.thread' and thread_id and subtype_id:
            # done with SUPERUSER_ID, because on some models users can post only with read access, not necessarily write access
            self.write(cr,
                       SUPERUSER_ID, [thread_id],
                       {'message_last_post': fields.datetime.now()},
                       context=context)
        message = mail_message.browse(cr, uid, msg_id, context=context)
        if message.author_id and model and thread_id and type != 'notification' and not context.get(
                'mail_create_nosubscribe'):
            self.message_subscribe(cr,
                                   uid, [thread_id], [message.author_id.id],
                                   context=context)
        return msg_id
Example #11
0
    def message_post(self,
                     cr,
                     uid,
                     thread_id,
                     body='',
                     subject=None,
                     type='notification',
                     subtype=None,
                     parent_id=False,
                     attachments=None,
                     context=None,
                     content_subtype='html',
                     **kwargs):
        """ Post a new message in an existing thread, returning the new
            mail.message ID.

            :param int thread_id: thread ID to post into, or list with one ID;
                if False/0, mail.message model will also be set as False
            :param str body: body of the message, usually raw HTML that will
                be sanitized
            :param str type: see mail_message.type field
            :param str content_subtype:: if plaintext: convert body into html
            :param int parent_id: handle reply to a previous message by adding the
                parent partners to the message in case of private discussion
            :param tuple(str,str) attachments or list id: list of attachment tuples in the form
                ``(name,content)``, where content is NOT base64 encoded

            Extra keyword arguments will be used as default column values for the
            new mail.message record. Special cases:
                - attachment_ids: supposed not attached to any document; attach them
                    to the related document. Should only be set by Chatter.
            :return int: ID of newly created mail.message
        """
        if context is None:
            context = {}
        if attachments is None:
            attachments = {}
        mail_message = self.pool.get('mail.message')
        ir_attachment = self.pool.get('ir.attachment')

        assert (not thread_id) or \
                isinstance(thread_id, (int, long)) or \
                (isinstance(thread_id, (list, tuple)) and len(thread_id) == 1), \
                "Invalid thread_id; should be 0, False, an ID or a list with one ID"
        if isinstance(thread_id, (list, tuple)):
            thread_id = thread_id[0]

        # if we're processing a message directly coming from the gateway, the destination model was
        # set in the context.
        model = False
        if thread_id:
            model = context.get(
                'thread_model',
                False) if self._name == 'mail.thread' else self._name
            if model and model != self._name and hasattr(
                    self.pool[model], 'message_post'):
                del context['thread_model']
                return self.pool[model].message_post(
                    cr,
                    uid,
                    thread_id,
                    body=body,
                    subject=subject,
                    type=type,
                    subtype=subtype,
                    parent_id=parent_id,
                    attachments=attachments,
                    context=context,
                    content_subtype=content_subtype,
                    **kwargs)

        #0: Find the message's author, because we need it for private discussion
        author_id = kwargs.get('author_id')
        if author_id is None:  # keep False values
            author_id = self.pool.get('mail.message')._get_default_author(
                cr, uid, context=context)

        # 1: Handle content subtype: if plaintext, converto into HTML
        if content_subtype == 'plaintext':
            body = tools.plaintext2html(body)

        # 2: Private message: add recipients (recipients and author of parent message) - current author
        #   + legacy-code management (! we manage only 4 and 6 commands)
        partner_ids = set()
        kwargs_partner_ids = kwargs.pop('partner_ids', [])
        for partner_id in kwargs_partner_ids:
            if isinstance(
                    partner_id,
                (list, tuple)) and partner_id[0] == 4 and len(partner_id) == 2:
                partner_ids.add(partner_id[1])
            if isinstance(
                    partner_id,
                (list, tuple)) and partner_id[0] == 6 and len(partner_id) == 3:
                partner_ids |= set(partner_id[2])
            elif isinstance(partner_id, (int, long)):
                partner_ids.add(partner_id)
            else:
                pass  # we do not manage anything else
        if parent_id and not model:
            parent_message = mail_message.browse(cr,
                                                 uid,
                                                 parent_id,
                                                 context=context)
            private_followers = set(
                [partner.id for partner in parent_message.partner_ids])
            if parent_message.author_id:
                private_followers.add(parent_message.author_id.id)
            private_followers -= set([author_id])
            partner_ids |= private_followers

        # 3. Attachments
        #   - HACK TDE FIXME: Chatter: attachments linked to the document (not done JS-side), load the message
        attachment_ids = self._message_preprocess_attachments(
            cr, uid, attachments, kwargs.pop('attachment_ids', []), model,
            thread_id, context)

        # 4: mail.message.subtype
        subtype_id = False
        if subtype:
            if '.' not in subtype:
                subtype = 'mail.%s' % subtype
            subtype_id = self.pool.get('ir.model.data').xmlid_to_res_id(
                cr, uid, subtype)

        # automatically subscribe recipients if asked to
        #if context.get('mail_post_autofollow') and thread_id and partner_ids:
        #    partner_to_subscribe = partner_ids
        #    if context.get('mail_post_autofollow_partner_ids'):
        #        partner_to_subscribe = filter(lambda item: item in context.get('mail_post_autofollow_partner_ids'), partner_ids)
        #    self.message_subscribe(cr, uid, [thread_id], list(partner_to_subscribe), context=context)

        # _mail_flat_thread: automatically set free messages to the first posted message
        if self._mail_flat_thread and model and not parent_id and thread_id:
            message_ids = mail_message.search(cr,
                                              uid, [
                                                  '&',
                                                  ('res_id', '=', thread_id),
                                                  ('model', '=', model),
                                                  ('type', '=', 'email')
                                              ],
                                              context=context,
                                              order="id ASC",
                                              limit=1)
            if not message_ids:
                message_ids = message_ids = mail_message.search(
                    cr,
                    uid,
                    ['&', ('res_id', '=', thread_id), ('model', '=', model)],
                    context=context,
                    order="id ASC",
                    limit=1)
            parent_id = message_ids and message_ids[0] or False
        # we want to set a parent: force to set the parent_id to the oldest ancestor, to avoid having more than 1 level of thread
        elif parent_id:
            message_ids = mail_message.search(cr,
                                              SUPERUSER_ID,
                                              [('id', '=', parent_id),
                                               ('parent_id', '!=', False)],
                                              context=context)
            # avoid loops when finding ancestors
            processed_list = []
            if message_ids:
                message = mail_message.browse(cr,
                                              SUPERUSER_ID,
                                              message_ids[0],
                                              context=context)
                while (message.parent_id
                       and message.parent_id.id not in processed_list):
                    processed_list.append(message.parent_id.id)
                    message = message.parent_id
                parent_id = message.id

        values = kwargs
        values.update({
            'author_id': author_id,
            'model': model,
            'res_id': model and thread_id or False,
            'body': body,
            'subject': subject or False,
            'type': type,
            'parent_id': parent_id,
            'attachment_ids': attachment_ids,
            'subtype_id': subtype_id,
            'partner_ids': [(4, pid) for pid in partner_ids],
        })

        # Avoid warnings about non-existing fields
        for x in ('from', 'to', 'cc'):
            values.pop(x, None)

        # Post the message
        msg_id = mail_message.create(cr, uid, values, context=context)

        # Post-process: subscribe author, update message_last_post
        if model and model != 'mail.thread' and thread_id and subtype_id:
            # done with SUPERUSER_ID, because on some models users can post only with read access, not necessarily write access
            self.write(cr,
                       SUPERUSER_ID, [thread_id],
                       {'message_last_post': fields.datetime.now()},
                       context=context)
        message = mail_message.browse(cr, uid, msg_id, context=context)
        if message.author_id and model and thread_id and type != 'notification' and not context.get(
                'mail_create_nosubscribe'):
            self.message_subscribe(cr,
                                   uid, [thread_id], [message.author_id.id],
                                   context=context)
        return msg_id
Example #12
0
    def message_post(self, cr, uid, thread_id, body='', subject=None, type='notification',
                        subtype=None, parent_id=False, attachments=None, context=None,
                        content_subtype='html', **kwargs):
        """ Post a new message in an existing thread, returning the new
            mail.message ID.

            :param int thread_id: thread ID to post into, or list with one ID;
                if False/0, mail.message model will also be set as False
            :param str body: body of the message, usually raw HTML that will
                be sanitized
            :param str type: see mail_message.type field
            :param str content_subtype:: if plaintext: convert body into html
            :param int parent_id: handle reply to a previous message by adding the
                parent partners to the message in case of private discussion
            :param tuple(str,str) attachments or list id: list of attachment tuples in the form
                ``(name,content)``, where content is NOT base64 encoded

            Extra keyword arguments will be used as default column values for the
            new mail.message record. Special cases:
                - attachment_ids: supposed not attached to any document; attach them
                    to the related document. Should only be set by Chatter.
            :return int: ID of newly created mail.message
        """
        if context is None:
            context = {}
        if attachments is None:
            attachments = {}
        mail_message = self.pool.get('mail.message')
        ir_attachment = self.pool.get('ir.attachment')

        assert (not thread_id) or \
                isinstance(thread_id, (int, long)) or \
                (isinstance(thread_id, (list, tuple)) and len(thread_id) == 1), \
                "Invalid thread_id; should be 0, False, an ID or a list with one ID"
        if isinstance(thread_id, (list, tuple)):
            thread_id = thread_id[0]

        # if we're processing a message directly coming from the gateway, the destination model was
        # set in the context.
        model = False
        if thread_id:
            model = context.get('thread_model', self._name) if self._name == 'mail.thread' else self._name
            if model != self._name:
                del context['thread_model']
                return self.pool.get(model).message_post(cr, uid, thread_id, body=body, subject=subject, type=type, subtype=subtype, parent_id=parent_id, attachments=attachments, context=context, content_subtype=content_subtype, **kwargs)

        # 0: Parse email-from, try to find a better author_id based on document's followers for incoming emails
        email_from = kwargs.get('email_from')
        if email_from and thread_id and type == 'email' and kwargs.get('author_id'):
            email_list = tools.email_split(email_from)
            doc = self.browse(cr, uid, thread_id, context=context)
            if email_list and doc:
                author_ids = self.pool.get('res.partner').search(cr, uid, [
                                        ('email', 'ilike', email_list[0]),
                                        ('id', 'in', [f.id for f in doc.message_follower_ids])
                                    ], limit=1, context=context)
                if author_ids:
                    kwargs['author_id'] = author_ids[0]
        author_id = kwargs.get('author_id')
        if author_id is None:  # keep False values
            author_id = self.pool.get('mail.message')._get_default_author(cr, uid, context=context)

        # 1: Handle content subtype: if plaintext, converto into HTML
        if content_subtype == 'plaintext':
            body = tools.plaintext2html(body)

        # 2: Private message: add recipients (recipients and author of parent message) - current author
        #   + legacy-code management (! we manage only 4 and 6 commands)
        partner_ids = set()
        kwargs_partner_ids = kwargs.pop('partner_ids', [])
        for partner_id in kwargs_partner_ids:
            if isinstance(partner_id, (list, tuple)) and partner_id[0] == 4 and len(partner_id) == 2:
                partner_ids.add(partner_id[1])
            if isinstance(partner_id, (list, tuple)) and partner_id[0] == 6 and len(partner_id) == 3:
                partner_ids |= set(partner_id[2])
            elif isinstance(partner_id, (int, long)):
                partner_ids.add(partner_id)
            else:
                pass  # we do not manage anything else
            
        email_cc_ids = set()
        kwargs_email_cc_ids = kwargs.pop('email_cc_ids', [])
        for email_cc_id in kwargs_email_cc_ids:
            if isinstance(partner_id, (list, tuple)) and email_cc_id[0] == 4 and len(email_cc_id) == 2:
                email_cc_ids.add(email_cc_id[1])
            if isinstance(email_cc_id, (list, tuple)) and email_cc_id[0] == 6 and len(email_cc_id) == 3:
                email_cc_ids |= set(email_cc_id[2])
            elif isinstance(email_cc_id, (int, long)):
                email_cc_ids.add(email_cc_id)
            else:
                pass  # we do not manage anything else
        email_bcc_ids = set()
        kwargs_email_bcc_ids = kwargs.pop('email_bcc_ids', [])
        for email_bcc_id in kwargs_email_bcc_ids:
            if isinstance(partner_id, (list, tuple)) and email_bcc_id[0] == 4 and len(email_bcc_id) == 2:
                email_bcc_ids.add(email_bcc_id[1])
            if isinstance(email_bcc_id, (list, tuple)) and email_bcc_id[0] == 6 and len(email_bcc_id) == 3:
                email_bcc_ids |= set(email_bcc_id[2])
            elif isinstance(email_bcc_id, (int, long)):
                email_bcc_ids.add(email_bcc_id)
            else:
                pass  # we do not manage anything else
            
            
        if parent_id and not model:
            parent_message = mail_message.browse(cr, uid, parent_id, context=context)
            private_followers = set([partner.id for partner in parent_message.partner_ids])
            if parent_message.author_id:
                private_followers.add(parent_message.author_id.id)
            private_followers -= set([author_id])
            partner_ids |= private_followers

        # 3. Attachments
        #   - HACK TDE FIXME: Chatter: attachments linked to the document (not done JS-side), load the message
        attachment_ids = kwargs.pop('attachment_ids', []) or []  # because we could receive None (some old code sends None)
        if attachment_ids:
            filtered_attachment_ids = ir_attachment.search(cr, SUPERUSER_ID, [
                ('res_model', '=', 'mail.compose.message'),
                ('create_uid', '=', uid),
                ('id', 'in', attachment_ids)], context=context)
            if filtered_attachment_ids:
                ir_attachment.write(cr, SUPERUSER_ID, filtered_attachment_ids, {'res_model': model, 'res_id': thread_id}, context=context)
        attachment_ids = [(4, id) for id in attachment_ids]
        # Handle attachments parameter, that is a dictionary of attachments
        for name, content in attachments:
            if isinstance(content, unicode):
                content = content.encode('utf-8')
            data_attach = {
                'name': name,
                'datas': base64.b64encode(str(content)),
                'datas_fname': name,
                'description': name,
                'res_model': model,
                'res_id': thread_id,
            }
            attachment_ids.append((0, 0, data_attach))

        # 4: mail.message.subtype
        subtype_id = False
        if subtype:
            if '.' not in subtype:
                subtype = 'mail.%s' % subtype
            ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, *subtype.split('.'))
            subtype_id = ref and ref[1] or False

        # automatically subscribe recipients if asked to
        if context.get('mail_post_autofollow') and thread_id and partner_ids:
            partner_to_subscribe = partner_ids
            if context.get('mail_post_autofollow_partner_ids'):
                partner_to_subscribe = filter(lambda item: item in context.get('mail_post_autofollow_partner_ids'), partner_ids)
            self.message_subscribe(cr, uid, [thread_id], list(partner_to_subscribe), context=context)
        # _mail_flat_thread: automatically set free messages to the first posted message
        if self._mail_flat_thread and not parent_id and thread_id:
            message_ids = mail_message.search(cr, uid, ['&', ('res_id', '=', thread_id), ('model', '=', model)], context=context, order="id ASC", limit=1)
            parent_id = message_ids and message_ids[0] or False
        # we want to set a parent: force to set the parent_id to the oldest ancestor, to avoid having more than 1 level of thread
        elif parent_id:
            message_ids = mail_message.search(cr, SUPERUSER_ID, [('id', '=', parent_id), ('parent_id', '!=', False)], context=context)
            # avoid loops when finding ancestors
            processed_list = []
            if message_ids:
                message = mail_message.browse(cr, SUPERUSER_ID, message_ids[0], context=context)
                while (message.parent_id and message.parent_id.id not in processed_list):
                    processed_list.append(message.parent_id.id)
                    message = message.parent_id
                parent_id = message.id
        
        values = kwargs
        values.update({
            'author_id': author_id,
            'model': model,
            'res_id': thread_id or False,
            'body': body,
            'subject': subject or False,
            'type': type,
            'parent_id': parent_id,
            'attachment_ids': attachment_ids,
            'subtype_id': subtype_id,
            'partner_ids': [(4, pid) for pid in partner_ids],
            'email_cc_ids':[(4, ccid) for ccid in email_cc_ids],
            'email_bcc_ids':[(4, bccid) for bccid in email_bcc_ids],
        })
        # Avoid warnings about non-existing fields
        for x in ('from', 'to', 'cc'):
            values.pop(x, None)
        # Create and auto subscribe the author
        msg_id = mail_message.create(cr, uid, values, context=context)
        message = mail_message.browse(cr, uid, msg_id, context=context)
        if message.author_id and thread_id and type != 'notification' and not context.get('mail_create_nosubscribe'):
            self.message_subscribe(cr, uid, [thread_id], [message.author_id.id], context=context)
        return msg_id
Example #13
0
    def message_custom_post(self, cr, uid, thread_id, body='', subject=None, type='notification',
                        subtype=None, parent_id=False, attachments=None, model=None, res_id=None, context=None,
                        content_subtype='html', **kwargs):
        """ Post a new message in an existing thread, returning the new
            mail.message ID.

            :param int thread_id: thread ID to post into, or list with one ID;
                if False/0, mail.message model will also be set as False
            :param str body: body of the message, usually raw HTML that will
                be sanitized
            :param str type: see mail_message.type field
            :param str content_subtype:: if plaintext: convert body into html
            :param int parent_id: handle reply to a previous message by adding the
                parent partners to the message in case of private discussion
            :param tuple(str,str) attachments or list id: list of attachment tuples in the form
                ``(name,content)``, where content is NOT base64 encoded

            Extra keyword arguments will be used as default column values for the
            new mail.message record. Special cases:
                - attachment_ids: supposed not attached to any document; attach them
                    to the related document. Should only be set by Chatter.
            :return int: ID of newly created mail.message
        """
        if context is None:
            context = {}
        if attachments is None:
            attachments = {}
        mail_message = self.pool.get('mail.message')
        ir_attachment = self.pool.get('ir.attachment')

        assert (not thread_id) or \
                isinstance(thread_id, (int, long)) or \
                (isinstance(thread_id, (list, tuple)) and len(thread_id) == 1), \
                "Invalid thread_id; should be 0, False, an ID or a list with one ID"
        if isinstance(thread_id, (list, tuple)):
            thread_id = thread_id[0]

        # if we're processing a message directly coming from the gateway, the destination model was
        # set in the context.
        if not model:
            model = 'mail.thread'

        #0: Find the message's author, because we need it for private discussion
        author_id = kwargs.get('author_id')
        if author_id is None:  # keep False values
            author_id = self.pool.get('mail.message')._get_default_author(cr, uid, context=context)

        # 1: Handle content subtype: if plaintext, converto into HTML
        if content_subtype == 'plaintext':
            body = tools.plaintext2html(body)

        # 2: Private message: add recipients (recipients and author of parent message) - current author
        #   + legacy-code management (! we manage only 4 and 6 commands)
        partner_ids = set()
        kwargs_partner_ids = kwargs.pop('partner_ids', [])
        for partner_id in kwargs_partner_ids:
            if isinstance(partner_id, (list, tuple)) and partner_id[0] == 4 and len(partner_id) == 2:
                partner_ids.add(partner_id[1])
            if isinstance(partner_id, (list, tuple)) and partner_id[0] == 6 and len(partner_id) == 3:
                partner_ids |= set(partner_id[2])
            elif isinstance(partner_id, (int, long)):
                partner_ids.add(partner_id)
            else:
                pass  # we do not manage anything else
        if parent_id and not model:
            parent_message = mail_message.browse(cr, uid, parent_id, context=context)
            private_followers = set([partner.id for partner in parent_message.partner_ids])
            if parent_message.author_id:
                private_followers.add(parent_message.author_id.id)
            private_followers -= set([author_id])
            partner_ids |= private_followers

        # 3. Attachments
        #   - HACK TDE FIXME: Chatter: attachments linked to the document (not done JS-side), load the message
        attachment_ids = self._message_preprocess_attachments(cr, uid, attachments, kwargs.pop('attachment_ids', []), model, thread_id, context)

        # 4: mail.message.subtype
        subtype_id = False
        if subtype:
            if '.' not in subtype:
                subtype = 'mail.%s' % subtype
            subtype_id = self.pool.get('ir.model.data').xmlid_to_res_id(cr, uid, subtype)

        # automatically subscribe recipients if asked to
        if context.get('mail_post_autofollow') and thread_id and partner_ids:
            partner_to_subscribe = partner_ids
            if context.get('mail_post_autofollow_partner_ids'):
                partner_to_subscribe = filter(lambda item: item in context.get('mail_post_autofollow_partner_ids'), partner_ids)
            self.message_subscribe(cr, uid, [thread_id], list(partner_to_subscribe), context=context)

        # _mail_flat_thread: automatically set free messages to the first posted message
        if self._mail_flat_thread and not parent_id and thread_id:
            message_ids = mail_message.search(cr, uid, ['&', ('res_id', '=', thread_id), ('model', '=', model)], context=context, order="id ASC", limit=1)
            parent_id = message_ids and message_ids[0] or False
        # we want to set a parent: force to set the parent_id to the oldest ancestor, to avoid having more than 1 level of thread
        elif parent_id:
            message_ids = mail_message.search(cr, SUPERUSER_ID, [('id', '=', parent_id), ('parent_id', '!=', False)], context=context)
            # avoid loops when finding ancestors
            processed_list = []
            if message_ids:
                message = mail_message.browse(cr, SUPERUSER_ID, message_ids[0], context=context)
                while (message.parent_id and message.parent_id.id not in processed_list):
                    processed_list.append(message.parent_id.id)
                    message = message.parent_id
                parent_id = message.id

        values = kwargs
        values.update({
            'author_id': author_id,
            'model': model,
            'res_id': res_id or False,
            'body': body,
            'subject': subject or False,
            'type': type,
            'parent_id': parent_id,
            'attachment_ids': attachment_ids,
            'subtype_id': subtype_id,
            'partner_ids': [(4, pid) for pid in partner_ids],
        })

        # Avoid warnings about non-existing fields
        for x in ('from', 'to', 'cc'):
            values.pop(x, None)

        # Post the message
        msg_id = mail_message.create(cr, uid, values, context=context)

        # Post-process: subscribe author, update message_last_post
        if model and model != 'mail.thread' and thread_id and subtype_id:
            # done with SUPERUSER_ID, because on some models users can post only with read access, not necessarily write access
            self.write(cr, SUPERUSER_ID, [thread_id], {'message_last_post': fields.datetime.now()}, context=context)
        message = mail_message.browse(cr, uid, msg_id, context=context)
        if message.author_id and thread_id and type != 'notification' and not context.get('mail_create_nosubscribe'):
            self.message_subscribe(cr, uid, [thread_id], [message.author_id.id], context=context)
        return msg_id
    def message_post(self,
                     cr,
                     uid,
                     thread_id,
                     body='',
                     subject=None,
                     type='notification',
                     subtype=None,
                     parent_id=False,
                     attachments=None,
                     context=None,
                     content_subtype='html',
                     **kwargs):
        """ Post a new message in an existing thread, returning the new
            mail.message ID.

            :param int thread_id: thread ID to post into, or list with one ID;
                if False/0, mail.message model will also be set as False
            :param str body: body of the message, usually raw HTML that will
                be sanitized
            :param str type: see mail_message.type field
            :param str content_subtype:: if plaintext: convert body into html
            :param int parent_id: handle reply to a previous message by adding the
                parent partners to the message in case of private discussion
            :param tuple(str,str) attachments or list id: list of attachment tuples in the form
                ``(name,content)``, where content is NOT base64 encoded

            Extra keyword arguments will be used as default column values for the
            new mail.message record. Special cases:
                - attachment_ids: supposed not attached to any document; attach them
                    to the related document. Should only be set by Chatter.
            :return int: ID of newly created mail.message
        """
        if context is None:
            context = {}
        if attachments is None:
            attachments = {}
        mail_message = self.pool.get('mail.message')
        ir_attachment = self.pool.get('ir.attachment')

        assert (not thread_id) or \
                isinstance(thread_id, (int, long)) or \
                (isinstance(thread_id, (list, tuple)) and len(thread_id) == 1), \
                "Invalid thread_id; should be 0, False, an ID or a list with one ID"
        if isinstance(thread_id, (list, tuple)):
            thread_id = thread_id[0]

        # if we're processing a message directly coming from the gateway, the destination model was
        # set in the context.
        model = False
        if thread_id:
            model = context.get(
                'thread_model',
                self._name) if self._name == 'mail.thread' else self._name
            if model != self._name:
                del context['thread_model']
                return self.pool.get(model).message_post(
                    cr,
                    uid,
                    thread_id,
                    body=body,
                    subject=subject,
                    type=type,
                    subtype=subtype,
                    parent_id=parent_id,
                    attachments=attachments,
                    context=context,
                    content_subtype=content_subtype,
                    **kwargs)

        # 0: Parse email-from, try to find a better author_id based on document's followers for incoming emails
        email_from = kwargs.get('email_from')
        if email_from and thread_id and type == 'email' and kwargs.get(
                'author_id'):
            email_list = tools.email_split(email_from)
            doc = self.browse(cr, uid, thread_id, context=context)
            if email_list and doc:
                author_ids = self.pool.get('res.partner').search(
                    cr,
                    uid,
                    [('email', 'ilike', email_list[0]),
                     ('id', 'in', [f.id for f in doc.message_follower_ids])],
                    limit=1,
                    context=context)
                if author_ids:
                    kwargs['author_id'] = author_ids[0]
        author_id = kwargs.get('author_id')
        if author_id is None:  # keep False values
            author_id = self.pool.get('mail.message')._get_default_author(
                cr, uid, context=context)

        # 1: Handle content subtype: if plaintext, converto into HTML
        if content_subtype == 'plaintext':
            body = tools.plaintext2html(body)

        # 2: Private message: add recipients (recipients and author of parent message) - current author
        #   + legacy-code management (! we manage only 4 and 6 commands)
        partner_ids = set()
        kwargs_partner_ids = kwargs.pop('partner_ids', [])
        for partner_id in kwargs_partner_ids:
            if isinstance(
                    partner_id,
                (list, tuple)) and partner_id[0] == 4 and len(partner_id) == 2:
                partner_ids.add(partner_id[1])
            if isinstance(
                    partner_id,
                (list, tuple)) and partner_id[0] == 6 and len(partner_id) == 3:
                partner_ids |= set(partner_id[2])
            elif isinstance(partner_id, (int, long)):
                partner_ids.add(partner_id)
            else:
                pass  # we do not manage anything else

        email_cc_ids = set()
        kwargs_email_cc_ids = kwargs.pop('email_cc_ids', [])
        for email_cc_id in kwargs_email_cc_ids:
            if isinstance(
                    partner_id,
                (list,
                 tuple)) and email_cc_id[0] == 4 and len(email_cc_id) == 2:
                email_cc_ids.add(email_cc_id[1])
            if isinstance(
                    email_cc_id,
                (list,
                 tuple)) and email_cc_id[0] == 6 and len(email_cc_id) == 3:
                email_cc_ids |= set(email_cc_id[2])
            elif isinstance(email_cc_id, (int, long)):
                email_cc_ids.add(email_cc_id)
            else:
                pass  # we do not manage anything else
        email_bcc_ids = set()
        kwargs_email_bcc_ids = kwargs.pop('email_bcc_ids', [])
        for email_bcc_id in kwargs_email_bcc_ids:
            if isinstance(
                    partner_id,
                (list,
                 tuple)) and email_bcc_id[0] == 4 and len(email_bcc_id) == 2:
                email_bcc_ids.add(email_bcc_id[1])
            if isinstance(
                    email_bcc_id,
                (list,
                 tuple)) and email_bcc_id[0] == 6 and len(email_bcc_id) == 3:
                email_bcc_ids |= set(email_bcc_id[2])
            elif isinstance(email_bcc_id, (int, long)):
                email_bcc_ids.add(email_bcc_id)
            else:
                pass  # we do not manage anything else

        if parent_id and not model:
            parent_message = mail_message.browse(cr,
                                                 uid,
                                                 parent_id,
                                                 context=context)
            private_followers = set(
                [partner.id for partner in parent_message.partner_ids])
            if parent_message.author_id:
                private_followers.add(parent_message.author_id.id)
            private_followers -= set([author_id])
            partner_ids |= private_followers

        # 3. Attachments
        #   - HACK TDE FIXME: Chatter: attachments linked to the document (not done JS-side), load the message
        attachment_ids = kwargs.pop('attachment_ids', []) or [
        ]  # because we could receive None (some old code sends None)
        if attachment_ids:
            filtered_attachment_ids = ir_attachment.search(
                cr,
                SUPERUSER_ID, [('res_model', '=', 'mail.compose.message'),
                               ('create_uid', '=', uid),
                               ('id', 'in', attachment_ids)],
                context=context)
            if filtered_attachment_ids:
                ir_attachment.write(cr,
                                    SUPERUSER_ID,
                                    filtered_attachment_ids, {
                                        'res_model': model,
                                        'res_id': thread_id
                                    },
                                    context=context)
        attachment_ids = [(4, id) for id in attachment_ids]
        # Handle attachments parameter, that is a dictionary of attachments
        for name, content in attachments:
            if isinstance(content, unicode):
                content = content.encode('utf-8')
            data_attach = {
                'name': name,
                'datas': base64.b64encode(str(content)),
                'datas_fname': name,
                'description': name,
                'res_model': model,
                'res_id': thread_id,
            }
            attachment_ids.append((0, 0, data_attach))

        # 4: mail.message.subtype
        subtype_id = False
        if subtype:
            if '.' not in subtype:
                subtype = 'mail.%s' % subtype
            ref = self.pool.get('ir.model.data').get_object_reference(
                cr, uid, *subtype.split('.'))
            subtype_id = ref and ref[1] or False

        # automatically subscribe recipients if asked to
        if context.get('mail_post_autofollow') and thread_id and partner_ids:
            partner_to_subscribe = partner_ids
            if context.get('mail_post_autofollow_partner_ids'):
                partner_to_subscribe = filter(
                    lambda item: item in context.get(
                        'mail_post_autofollow_partner_ids'), partner_ids)
            self.message_subscribe(cr,
                                   uid, [thread_id],
                                   list(partner_to_subscribe),
                                   context=context)
        # _mail_flat_thread: automatically set free messages to the first posted message
        if self._mail_flat_thread and not parent_id and thread_id:
            message_ids = mail_message.search(
                cr,
                uid, ['&', ('res_id', '=', thread_id), ('model', '=', model)],
                context=context,
                order="id ASC",
                limit=1)
            parent_id = message_ids and message_ids[0] or False
        # we want to set a parent: force to set the parent_id to the oldest ancestor, to avoid having more than 1 level of thread
        elif parent_id:
            message_ids = mail_message.search(cr,
                                              SUPERUSER_ID,
                                              [('id', '=', parent_id),
                                               ('parent_id', '!=', False)],
                                              context=context)
            # avoid loops when finding ancestors
            processed_list = []
            if message_ids:
                message = mail_message.browse(cr,
                                              SUPERUSER_ID,
                                              message_ids[0],
                                              context=context)
                while (message.parent_id
                       and message.parent_id.id not in processed_list):
                    processed_list.append(message.parent_id.id)
                    message = message.parent_id
                parent_id = message.id

        values = kwargs
        values.update({
            'author_id': author_id,
            'model': model,
            'res_id': thread_id or False,
            'body': body,
            'subject': subject or False,
            'type': type,
            'parent_id': parent_id,
            'attachment_ids': attachment_ids,
            'subtype_id': subtype_id,
            'partner_ids': [(4, pid) for pid in partner_ids],
            'email_cc_ids': [(4, ccid) for ccid in email_cc_ids],
            'email_bcc_ids': [(4, bccid) for bccid in email_bcc_ids],
        })
        # Avoid warnings about non-existing fields
        for x in ('from', 'to', 'cc'):
            values.pop(x, None)
        # Create and auto subscribe the author
        msg_id = mail_message.create(cr, uid, values, context=context)
        message = mail_message.browse(cr, uid, msg_id, context=context)
        if message.author_id and thread_id and type != 'notification' and not context.get(
                'mail_create_nosubscribe'):
            self.message_subscribe(cr,
                                   uid, [thread_id], [message.author_id.id],
                                   context=context)
        return msg_id
Example #15
0
    def message_custom_post(self,
                            body='',
                            subject=None,
                            message_type='notification',
                            subtype=None,
                            parent_id=False,
                            attachments=None,
                            model=None,
                            res_id=None,
                            content_subtype='html',
                            **kwargs):
        """ Post a new message in an existing thread, returning the new
            mail.message ID.
            :param int thread_id: thread ID to post into, or list with one ID;
                if False/0, mail.message model will also be set as False
            :param str body: body of the message, usually raw HTML that will
                be sanitized
            :param str type: see mail_message.type field
            :param str content_subtype:: if plaintext: convert body into html
            :param int parent_id: handle reply to a previous message by adding the
                parent partners to the message in case of private discussion
            :param tuple(str,str) attachments or list id: list of attachment tuples in the form
                ``(name,content)``, where content is NOT base64 encoded:
            :param str model, model to link the title
            :param str res_id, id for the actual model sent

            Extra keyword arguments will be used as default column values for the
            new mail.message record. Special cases:
                - attachment_ids: supposed not attached to any document; attach them
                    to the related document. Should only be set by Chatter.
            :return int: ID of newly created mail.message
        """
        if attachments is None:
            attachments = {}
        if self.ids and not self.ensure_one():
            raise exceptions.Warning(
                _('Invalid record set: should be called as model (without records) or on single-record recordset'
                  ))

        # 0: Find the message's author, because we need it for private discussion
        author_id = kwargs.get('author_id')
        if author_id is None:  # keep False values
            author_id = self.env['mail.message']._get_default_author().id

        # 1: Handle content subtype: if plaintext, converto into HTML
        if content_subtype == 'plaintext':
            body = tools.plaintext2html(body)

        # 2: Private message: add recipients (recipients and author of parent message) - current author
        #   + legacy-code management (! we manage only 4 and 6 commands)
        partner_ids = set()
        kwargs_partner_ids = kwargs.pop('partner_ids', [])
        for partner_id in kwargs_partner_ids:
            if isinstance(
                    partner_id,
                (list, tuple)) and partner_id[0] == 4 and len(partner_id) == 2:
                partner_ids.add(partner_id[1])
            if isinstance(
                    partner_id,
                (list, tuple)) and partner_id[0] == 6 and len(partner_id) == 3:
                partner_ids |= set(partner_id[2])
            elif isinstance(partner_id, (int, long)):
                partner_ids.add(partner_id)
            else:
                pass  # we do not manage anything else
        if parent_id and not model:
            parent_message = self.env['mail.message'].browse(parent_id)
            private_followers = set(
                [partner.id for partner in parent_message.partner_ids])
            if parent_message.author_id:
                private_followers.add(parent_message.author_id.id)
            private_followers -= set([author_id])
            partner_ids |= private_followers

        # 3. Attachments
        #   - HACK TDE FIXME: Chatter: attachments linked to the document (not done JS-side), load the message
        attachment_ids = self._message_preprocess_attachments(
            attachments, kwargs.pop('attachment_ids', []), model,
            self.ids and self.ids[0] or None)

        # 4: mail.message.subtype
        subtype_id = kwargs.get('subtype_id', False)
        if not subtype_id:
            subtype = subtype or 'mt_note'
            if '.' not in subtype:
                subtype = 'mail.%s' % subtype
            subtype_id = self.env['ir.model.data'].xmlid_to_res_id(subtype)

        # automatically subscribe recipients if asked to
        if self._context.get(
                'mail_post_autofollow') and self.ids and partner_ids:
            partner_to_subscribe = partner_ids
            if self._context.get('mail_post_autofollow_partner_ids'):
                partner_to_subscribe = filter(
                    lambda item: item in self._context.get(
                        'mail_post_autofollow_partner_ids'), partner_ids)
            self.message_subscribe(list(partner_to_subscribe), force=False)

        # _mail_flat_thread: automatically set free messages to the first posted message
        MailMessage = self.env['mail.message']
        if self._mail_flat_thread and model and not parent_id and self.ids:
            messages = MailMessage.search([
                '&', ('res_id', '=', self.ids[0]), ('model', '=', model),
                ('message_type', '=', 'email')
            ],
                                          order="id ASC",
                                          limit=1)
            if not messages:
                messages = MailMessage.search(
                    ['&', ('res_id', '=', self.ids[0]), ('model', '=', model)],
                    order="id ASC",
                    limit=1)
            parent_id = messages and messages[0].id or False
        # we want to set a parent: force to set the parent_id to the oldest ancestor, to avoid having more than 1 level of thread
        elif parent_id:
            messages = MailMessage.sudo().search([('id', '=', parent_id),
                                                  ('parent_id', '!=', False)],
                                                 limit=1)
            # avoid loops when finding ancestors
            processed_list = []
            if messages:
                message = messages[0]
                while (message.parent_id
                       and message.parent_id.id not in processed_list):
                    processed_list.append(message.parent_id.id)
                    message = message.parent_id
                parent_id = message.id

        values = kwargs
        values.update({
            'author_id': author_id,
            'model': model,
            'res_id': res_id or False,
            'body': body,
            'subject': subject or False,
            'message_type': message_type,
            'parent_id': parent_id,
            'attachment_ids': attachment_ids,
            'subtype_id': subtype_id,
            'partner_ids': [(4, pid) for pid in partner_ids],
        })

        # Avoid warnings about non-existing fields
        for x in ('from', 'to', 'cc'):
            values.pop(x, None)

        # Post the message
        new_message = MailMessage.create(values)

        # Post-process: subscribe author, update message_last_post
        # Note: the message_last_post mechanism is no longer used.  This
        # will be removed in a later version.
        if (self._context.get('mail_save_message_last_post') and model
                and model != 'mail.thread' and self.ids and subtype_id):
            subtype_rec = self.env['mail.message.subtype'].sudo().browse(
                subtype_id)
            if not subtype_rec.internal:
                # done with SUPERUSER_ID, because on some models users can post only with read access, not necessarily write access
                self.sudo().write({'message_last_post': fields.Datetime.now()})
        if new_message.author_id and model and self.ids and message_type != 'notification' and not self._context.get(
                'mail_create_nosubscribe'):
            self.message_subscribe([new_message.author_id.id], force=False)
        return new_message