Beispiel #1
0
    def mail_update_message(self, res_id, message, message_id, **post):

        res_model = 'slide.channel'  # keep this mecanism intern to slide
        message_body = plaintext2html(message)
        res_id = int(res_id)
        message_id = int(message_id)
        pid = int(post['pid']) if post.get('pid') else False

        if not _check_special_access(res_model, res_id, token=post.get('token'), _hash=post.get('hash'), pid=pid):
            raise Forbidden()

        # update mail.message
        domain = [
            ('model', '=', res_model),
            ('res_id', '=', res_id),
            ('website_published', '=', True),
            ('author_id', '=', request.env.user.partner_id.id),
            ('id', '=', message_id)
        ]  # restrict to the given message_id
        message = request.env['mail.message'].search(domain, limit=1)
        if not message:
            raise NotFound()
        message.write({
            'body': message_body
        })

        # update rating
        if post.get('rating_value'):
            domain = [('res_model', '=', res_model), ('res_id', '=', res_id), ('website_published', '=', True), ('message_id', '=', message.id)]
            rating = request.env['rating.rating'].search(domain, order='write_date DESC', limit=1)
            rating.write({
                'rating': float(post['rating_value'])
            })

        return werkzeug.utils.redirect(request.httprequest.referrer, 302)
Beispiel #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' alt=':%s/10' style='width:18px;height:18px;float:left;margin-right: 5px;'/>%s"
                 % (rate, 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
Beispiel #3
0
 def portal_chatter_post(self, res_model, res_id, message, **kw):
     url = request.httprequest.referrer
     if message:
         # message is received in plaintext and saved in html
         message = plaintext2html(message)
         _message_post_helper(res_model, int(res_id), message, **kw)
         url = url + "#discussion"
     return request.redirect(url)
Beispiel #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')
Beispiel #5
0
 def mail_chat_post(self, uuid, message_content, **kwargs):
     # find the author from the user session, which can be None
     author_id = False  # message_post accept 'False' author_id, but not 'None'
     if request.session.uid:
         author_id = request.env['res.users'].sudo().browse(request.session.uid).partner_id.id
     # post a message without adding followers to the channel. email_from=False avoid to get author from email data
     mail_channel = request.env["mail.channel"].sudo().search([('uuid', '=', uuid)], limit=1)
     body = tools.plaintext2html(message_content)
     message = mail_channel.sudo().with_context(mail_create_nosubscribe=True).message_post(author_id=author_id, email_from=False, body=body, message_type='comment', subtype='mail.mt_comment')
     return message and message.id or False
Beispiel #6
0
    def mail_update_message(self,
                            res_model,
                            res_id,
                            message,
                            message_id,
                            redirect=None,
                            attachment_ids='',
                            attachment_tokens='',
                            **post):
        # keep this mechanism intern to slide currently (saas 12.5) as it is
        # considered experimental
        if res_model != 'slide.channel':
            raise Forbidden()
        res_id = int(res_id)

        attachment_ids = [
            int(attachment_id) for attachment_id in attachment_ids.split(',')
            if attachment_id
        ]
        attachment_tokens = [
            attachment_token
            for attachment_token in attachment_tokens.split(',')
            if attachment_token
        ]
        self._portal_post_check_attachments(attachment_ids, attachment_tokens)

        pid = int(post['pid']) if post.get('pid') else False
        if not _check_special_access(res_model,
                                     res_id,
                                     token=post.get('token'),
                                     _hash=post.get('hash'),
                                     pid=pid):
            raise Forbidden()

        # fetch and update mail.message
        message_id = int(message_id)
        message_body = plaintext2html(message)
        domain = [('model', '=', res_model), ('res_id', '=', res_id),
                  ('is_internal', '=', False),
                  ('author_id', '=', request.env.user.partner_id.id),
                  ('message_type', '=', 'comment'),
                  ('id', '=', message_id)]  # restrict to the given message_id
        message = request.env['mail.message'].search(domain, limit=1)
        if not message:
            raise NotFound()
        message.sudo().write({
            'body':
            message_body,
            'attachment_ids': [(4, aid) for aid in attachment_ids],
        })

        # update rating
        if post.get('rating_value'):
            domain = [('res_model', '=', res_model), ('res_id', '=', res_id),
                      ('is_internal', '=', False),
                      ('message_id', '=', message.id)]
            rating = request.env['rating.rating'].sudo().search(
                domain, order='write_date DESC', limit=1)
            rating.write({
                'rating': float(post['rating_value']),
                'feedback': html2plaintext(message.body),
            })

        # redirect to specified or referrer or simply channel page as fallback
        redirect_url = redirect or (request.httprequest.referrer
                                    and request.httprequest.referrer +
                                    '#review') or '/slides/%s' % res_id
        return werkzeug.utils.redirect(redirect_url, 302)
Beispiel #7
0
    def message_post(self,
                     body='',
                     subject=None,
                     message_type='notification',
                     subtype=None,
                     parent_id=False,
                     attachments=None,
                     content_subtype='html',
                     **kwargs):
        if attachments is None:
            attachments = {}
        if self.ids and not self.ensure_one():
            raise exceptions.UserError(
                _('Invalid record set: should be called as model (without records) or on single-record recordset'
                  ))
        # inception
        context = self._context.copy()
        if not context.get('from_composer', False):
            context.update({
                'mail_post_autofollow': False,
                'mail_create_nosubscribe': True,
            })
        # /inception

        # if we're processing a message directly coming from the gateway, the destination model was
        # set in the context.
        model = False
        if self.ids:
            self.ensure_one()
            model = self._context.get(
                'thread_model',
                False) if self._name == 'mail.thread' else self._name
            if model and model != self._name and hasattr(
                    self.env[model], 'message_post'):
                RecordModel = self.env[model].with_context(
                    thread_model=None)  # TDE: was removing the key ?
                return RecordModel.browse(self.ids).message_post(
                    body=body,
                    subject=subject,
                    message_type=message_type,
                    subtype=subtype,
                    parent_id=parent_id,
                    attachments=attachments,
                    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.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, pycompat.integer_types):
                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

        # 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)
        # inception
        if context.get('put_this_subtype_instead', False):
            subtype = context.get('put_this_subtype_instead')
            subtype_id = self.env['ir.model.data'].xmlid_to_res_id(subtype)
        # / inception

        # 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 = [
                    p for p in partner_ids if p in self._context.get(
                        'mail_post_autofollow_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': model and self.ids[0] or False,
            'body': body,
            'subject': subject or False,
            'message_type': message_type,
            'parent_id': parent_id,
            'subtype_id': subtype_id,
            'partner_ids': [(4, pid) for pid in partner_ids],
        })

        # 3. Attachments
        #   - HACK TDE FIXME: Chatter: attachments linked to the document (not done JS-side), load the message
        attachment_ids = self._message_post_process_attachments(
            attachments, kwargs.pop('attachment_ids', []), values)
        values['attachment_ids'] = attachment_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 author_id and model and self.ids and message_type != 'notification' and not \
                self._context.get('mail_create_nosubscribe'):
            self.message_subscribe([author_id], force=False)

            self._message_post_after_hook(new_message)

        return new_message
Beispiel #8
0
 def html(self, field_label, field_input):
     return plaintext2html(field_input)
    def message_post(self, body='', subject=None, message_type='notification',
        subtype=None, parent_id=False, attachments=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.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 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'))

        # if we're processing a message directly coming from the gateway,
        # the destination model was set in the context.
        model = False
        if self.ids:
            self.ensure_one()
            _ = self._context.get('thread_model', False)
            model = _ if self._name == 'mail.thread' else self._name
            if model and model != self._name and hasattr(self.env[model],
                                                         'message_post'):
                # TDE: was removing the key ?
                RecordModel = self.env[model].with_context(thread_model=None)
                return RecordModel.browse(self.ids).message_post(
                    body=body, subject=subject, message_type=message_type,
                    subtype=subtype, parent_id=parent_id,
                    attachments=attachments, 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.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, pycompat.integer_types):
                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

        # 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 = [
        #                    p for p in partner_ids if p in
        #                    self._context.get('mail_post_autofollow_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': model and self.ids[0] or False,
            'body': body,
            'subject': subject or False,
            'message_type': message_type,
            'parent_id': parent_id,
            'subtype_id': subtype_id,
            'partner_ids': [(4, pid) for pid in partner_ids],
        })

        # 3. Attachments
        #   - HACK TDE FIXME: Chatter: attachments linked to the document
        # (not done JS-side), load the message
        attachment_ids = self._message_post_process_attachments(
            attachments, kwargs.pop('attachment_ids', []), values)
        values['attachment_ids'] = attachment_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_obj = self.env['mail.message.subtype']
            subtype_rec = subtype_obj.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 author_id and model and self.ids and \
                message_type != 'notification' and \
            not self._context.get('mail_create_nosubscribe'):
            self.message_subscribe([author_id], force=False)

        self._message_post_after_hook(new_message)

        return new_message
Beispiel #10
0
    def event_track_proposal_post(self, event, **post):
        if not event.can_access_from_current_website():
            raise NotFound()

        # Only accept existing tag indices. Use search instead of browse + exists:
        # this prevents users to register colorless tags if not allowed to (ACL).
        input_tag_indices = [
            int(tag_id) for tag_id in post['tags'].split(',') if tag_id
        ]
        valid_tag_indices = request.env['event.track.tag'].search([
            ('id', 'in', input_tag_indices)
        ]).ids

        contact = request.env['res.partner']
        visitor_partner = request.env[
            'website.visitor']._get_visitor_from_request().partner_id
        # Contact name is required. Therefore, empty contacts are not considered here. At least one of contact_phone
        # and contact_email must be filled. Email is verified. If the post tries to create contact with no valid entry,
        # raise exception. If normalized email is the same as logged partner, use its partner_id on track instead.
        # This prevents contact duplication. Otherwise, create new contact with contact additional info of post.
        if post.get('add_contact_information'):
            valid_contact_email = tools.email_normalize(
                post.get('contact_email'))
            # Here, the phone is not formatted. To format it, one needs a country. Based on a country, from geoip for instance.
            # The problem is that one could propose a track in country A with phone number of country B. Validity is therefore
            # quite tricky. We accept any format of contact_phone. Could be improved with select country phone widget.
            if valid_contact_email or post.get('contact_phone'):
                if visitor_partner and valid_contact_email == visitor_partner.email_normalized:
                    contact = visitor_partner
                else:
                    contact = request.env['res.partner'].sudo().create({
                        'email':
                        valid_contact_email,
                        'name':
                        post.get('contact_name'),
                        'phone':
                        post.get('contact_phone'),
                    })
            else:
                raise exceptions.ValidationError(
                    _("Format Error : please enter a valid contact phone or contact email."
                      ))
        # If the speaker email is the same as logged user's, then also uses its partner on track, same as above.
        else:
            valid_speaker_email = tools.email_normalize(post['partner_email'])
            if visitor_partner and valid_speaker_email == visitor_partner.email_normalized:
                contact = visitor_partner

        track = request.env['event.track'].with_context({
            'mail_create_nosubscribe':
            True
        }).sudo().create({
            'name':
            post['track_name'],
            'partner_id':
            contact.id,
            'partner_name':
            post['partner_name'],
            'partner_email':
            post['partner_email'],
            'partner_phone':
            post['partner_phone'],
            'partner_function':
            post['partner_function'],
            'contact_phone':
            contact.phone,
            'contact_email':
            contact.email,
            'event_id':
            event.id,
            'tag_ids': [(6, 0, valid_tag_indices)],
            'description':
            plaintext2html(post['description']),
            'partner_biography':
            plaintext2html(post['partner_biography']),
            'user_id':
            False,
            'image':
            base64.b64encode(post['image'].read())
            if post.get('image') else False,
        })

        if request.env.user != request.website.user_id:
            track.sudo().message_subscribe(
                partner_ids=request.env.user.partner_id.ids)

        return request.redirect('/event/%s/track_proposal/success/%s' %
                                (event.id, track.id))
Beispiel #11
0
    def mail_update_message(self,
                            res_model,
                            res_id,
                            message,
                            message_id,
                            attachment_ids=None,
                            attachment_tokens=None,
                            **post):
        # keep this mechanism intern to slide currently (saas 12.5) as it is
        # considered experimental
        if res_model != 'slide.channel':
            raise Forbidden()
        res_id = int(res_id)

        self._portal_post_check_attachments(attachment_ids, attachment_tokens)

        pid = int(post['pid']) if post.get('pid') else False
        if not _check_special_access(res_model,
                                     res_id,
                                     token=post.get('token'),
                                     _hash=post.get('hash'),
                                     pid=pid):
            raise Forbidden()

        # fetch and update mail.message
        message_id = int(message_id)
        message_body = plaintext2html(message)
        domain = [('model', '=', res_model), ('res_id', '=', res_id),
                  ('is_internal', '=', False),
                  ('author_id', '=', request.env.user.partner_id.id),
                  ('message_type', '=', 'comment'),
                  ('id', '=', message_id)]  # restrict to the given message_id
        message = request.env['mail.message'].search(domain, limit=1)
        if not message:
            raise NotFound()
        message.sudo().write({
            'body':
            message_body,
            'attachment_ids': [(4, aid) for aid in attachment_ids],
        })

        # update rating
        if post.get('rating_value'):
            domain = [('res_model', '=', res_model), ('res_id', '=', res_id),
                      ('is_internal', '=', False),
                      ('message_id', '=', message.id)]
            rating = request.env['rating.rating'].sudo().search(
                domain, order='write_date DESC', limit=1)
            rating.write({
                'rating': float(post['rating_value']),
                'feedback': html2plaintext(message.body),
            })
        channel = request.env[res_model].browse(res_id)
        return {
            'default_message_id':
            message.id,
            'default_message':
            html2plaintext(message.body),
            'default_rating_value':
            message.rating_value,
            'rating_avg':
            channel.rating_avg,
            'rating_count':
            channel.rating_count,
            'default_attachment_ids':
            message.attachment_ids.sudo().read(
                ['id', 'name', 'mimetype', 'file_size', 'access_token']),
            'force_submit_url':
            '/slides/mail/update_comment',
        }
Beispiel #12
0
    def _microsoft_to_odoo_values(self,
                                  microsoft_event,
                                  default_reminders=(),
                                  default_values={}):
        if microsoft_event.is_cancelled():
            return {'active': False}

        sensitivity_o2m = {
            'normal': 'public',
            'private': 'private',
            'confidential': 'confidential',
        }

        commands_attendee, commands_partner = self._odoo_attendee_commands_m(
            microsoft_event)
        timeZone_start = pytz.timezone(microsoft_event.start.get('timeZone'))
        timeZone_stop = pytz.timezone(microsoft_event.end.get('timeZone'))
        start = parse(microsoft_event.start.get('dateTime')).astimezone(
            timeZone_start).replace(tzinfo=None)
        if microsoft_event.isAllDay:
            stop = parse(microsoft_event.end.get('dateTime')).astimezone(
                timeZone_stop).replace(tzinfo=None) - relativedelta(days=1)
        else:
            stop = parse(microsoft_event.end.get('dateTime')).astimezone(
                timeZone_stop).replace(tzinfo=None)
        values = {
            **default_values, 'name':
            microsoft_event.subject or _("(No title)"),
            'description':
            plaintext2html(microsoft_event.bodyPreview),
            'location':
            microsoft_event.location
            and microsoft_event.location.get('displayName') or False,
            'user_id':
            microsoft_event.owner(self.env).id,
            'privacy':
            sensitivity_o2m.get(microsoft_event.sensitivity,
                                self.default_get(['privacy'])['privacy']),
            'attendee_ids':
            commands_attendee,
            'partner_ids':
            commands_partner,
            'allday':
            microsoft_event.isAllDay,
            'start':
            start,
            'stop':
            stop,
            'show_as':
            'free' if microsoft_event.showAs == 'free' else 'busy',
            'recurrency':
            microsoft_event.is_recurrent()
        }

        values['microsoft_id'] = microsoft_event.id
        if microsoft_event.is_recurrent():
            values[
                'microsoft_recurrence_master_id'] = microsoft_event.seriesMasterId

        alarm_commands = self._odoo_reminders_commands_m(microsoft_event)
        if alarm_commands:
            values['alarm_ids'] = alarm_commands

        return values
Beispiel #13
0
    #     for ext in test_mail_examples.MSOFFICE_1_OUT:
    #         self.assertNotIn(ext, new_html)


class TestHtmlTools(BaseCase):
    """ Test some of our generic utility functions about html """

    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')

    def test_append_to_html(self):
        test_samples = [
            ('<!DOCTYPE...><HTML encoding="blah">some <b>content</b></HtMl>', '--\nYours truly', True, True, False,
             '<!DOCTYPE...><html encoding="blah">some <b>content</b>\n<pre>--\nYours truly</pre>\n</html>'),
            ('<!DOCTYPE...><HTML encoding="blah">some <b>content</b></HtMl>', '--\nYours truly', True, False, False,
             '<!DOCTYPE...><html encoding="blah">some <b>content</b>\n<p>--<br/>Yours truly</p>\n</html>'),
            ('<html><body>some <b>content</b></body></html>', '<!DOCTYPE...>\n<html><body>\n<p>--</p>\n<p>Yours truly</p>\n</body>\n</html>', False, False, False,
             '<html><body>some <b>content</b>\n\n\n<p>--</p>\n<p>Yours truly</p>\n\n\n</body></html>'),
        ]
        for html, content, plaintext_flag, preserve_flag, container_tag, expected in test_samples:
            self.assertEqual(append_content_to_html(html, content, plaintext_flag, preserve_flag, container_tag), expected, 'append_content_to_html is broken')

    def test_is_html_empty(self):
Beispiel #14
0
    def _microsoft_to_odoo_values(self,
                                  microsoft_event,
                                  default_reminders=(),
                                  default_values={}):
        if microsoft_event.is_cancelled():
            return {'active': False}

        sensitivity_o2m = {
            'normal': 'public',
            'private': 'private',
            'confidential': 'confidential',
        }

        commands_attendee, commands_partner = self._odoo_attendee_commands_m(
            microsoft_event)
        timeZone_start = pytz.timezone(microsoft_event.start.get('timeZone'))
        timeZone_stop = pytz.timezone(microsoft_event.end.get('timeZone'))
        start = parse(microsoft_event.start.get('dateTime')).astimezone(
            timeZone_start).replace(tzinfo=None)
        if microsoft_event.isAllDay:
            stop = parse(microsoft_event.end.get('dateTime')).astimezone(
                timeZone_stop).replace(tzinfo=None) - relativedelta(days=1)
        else:
            stop = parse(microsoft_event.end.get('dateTime')).astimezone(
                timeZone_stop).replace(tzinfo=None)
        values = {
            **default_values, 'name':
            microsoft_event.subject or _("(No title)"),
            'description':
            plaintext2html(microsoft_event.bodyPreview),
            'location':
            microsoft_event.location
            and microsoft_event.location.get('displayName') or False,
            'user_id':
            microsoft_event.owner(self.env).id,
            'privacy':
            sensitivity_o2m.get(microsoft_event.sensitivity,
                                self.default_get(['privacy'])['privacy']),
            'attendee_ids':
            commands_attendee,
            'allday':
            microsoft_event.isAllDay,
            'start':
            start,
            'stop':
            stop,
            'show_as':
            'free' if microsoft_event.showAs == 'free' else 'busy',
            'recurrency':
            microsoft_event.is_recurrent()
        }
        if commands_partner:
            # Add partner_commands only if set from Microsoft. The write method on calendar_events will
            # override attendee commands if the partner_ids command is set but empty.
            values['partner_ids'] = commands_partner

        if microsoft_event.is_recurrent(
        ) and not microsoft_event.is_recurrence():
            # Propagate the follow_recurrence according to the google result
            values[
                'follow_recurrence'] = not microsoft_event.is_recurrence_outlier(
                )

        values['microsoft_id'] = microsoft_event.id
        if microsoft_event.is_recurrent():
            values[
                'microsoft_recurrence_master_id'] = microsoft_event.seriesMasterId

        alarm_commands = self._odoo_reminders_commands_m(microsoft_event)
        if alarm_commands:
            values['alarm_ids'] = alarm_commands

        return values
Beispiel #15
0
    def _process_step_forward_operator(self, mail_channel):
        """ Special type of step that will add a human operator to the conversation when reached,
        which stops the script and allow the visitor to discuss with a real person.

        In case we don't find any operator (e.g: no-one is available) we don't post any messages.
        The script will continue normally, which allows to add extra steps when it's the case
        (e.g: ask for the visitor's email and create a lead). """

        human_operator = False
        posted_message = False

        if mail_channel.livechat_channel_id:
            human_operator = mail_channel.livechat_channel_id._get_random_operator()

        if human_operator:
            mail_channel.sudo().add_members(
                human_operator.partner_id.ids,
                open_chat_window=True,
                post_joined_message=False)

            if self.message:
                # first post the message of the step (if we have one)
                posted_message = mail_channel._chatbot_post_message(self.chatbot_script_id, plaintext2html(self.message))

            # then post a small custom 'Operator has joined' notification
            mail_channel._chatbot_post_message(
                self.chatbot_script_id,
                Markup('<div class="o_mail_notification">%s</div>') % _('%s has joined', human_operator.partner_id.name))

            mail_channel._broadcast(human_operator.partner_id.ids)
            mail_channel.channel_pin(mail_channel.uuid, pinned=True)

        return posted_message