def test_delete_comment(self):
        # Add and remove a comment to a CommentReplies adapter

        # Create a conversation. In this case we doesn't assign it to an
        # object, as we just want to check the Conversation object API.
        conversation = IConversation(self.portal.doc1)

        # Add a comment to the conversation
        replies = IReplies(conversation)

        comment = createObject('plone.Comment')
        comment.text = 'Comment text'
        new_id = replies.addComment(comment)
        comment = self.portal.doc1.restrictedTraverse(
            '++conversation++default/{0}'.format(new_id), )

        # Add a reply to the CommentReplies adapter of the first comment
        re_comment = createObject('plone.Comment')
        re_comment.text = 'Comment text'

        replies = IReplies(comment)

        new_re_id = replies.addComment(re_comment)

        # Remove the reply to the CommentReplies adapter
        del replies[new_re_id]

        # Make sure there is no comment left in CommentReplies
        self.assertEqual(len(replies), 0)

        # Make sure the first comment is still in the conversation
        self.assertEqual(conversation.total_comments(), 1)
Beispiel #2
0
    def setUp(self):
        # Setup sandbox
        self.portal = self.layer['portal']
        self.request = self.layer['request']
        setRoles(self.portal, TEST_USER_ID, ['Manager'])

        self.document = self.portal['doc1']
        conversation = IConversation(self.document)
        replies = IReplies(conversation)

        comment = createObject('plone.Comment')
        comment.text = 'This is a comment'
        new_id = replies.addComment(comment)
        comment = self.document.restrictedTraverse(
            '++conversation++default/{0}'.format(new_id)
        )

        re_comment = createObject('plone.Comment')
        re_comment.text = 'This is a reply'
        re_comment.author_username = '******'
        re_comment.author_name = 'Juliana'
        re_comment.author_email = '*****@*****.**'

        replies = IReplies(comment)
        replies.addComment(re_comment)
    def test_add_comment(self):
        # Add comments to a CommentReplies adapter

        # Create a conversation. In this case we doesn't assign it to an
        # object, as we just want to check the Conversation object API.
        conversation = IConversation(self.portal.doc1)

        # Add a comment to the conversation
        replies = IReplies(conversation)

        comment = createObject('plone.Comment')
        comment.text = 'Comment text'
        new_id = replies.addComment(comment)
        comment = self.portal.doc1.restrictedTraverse(
            '++conversation++default/{0}'.format(new_id), )

        # Add a reply to the CommentReplies adapter of the first comment
        re_comment = createObject('plone.Comment')
        re_comment.text = 'Comment text'

        replies = IReplies(comment)

        new_re_id = replies.addComment(re_comment)

        # check that replies provides the IReplies interface
        self.assertTrue(IReplies.providedBy(replies))

        # Make sure our comment was added
        self.assertTrue(new_re_id in replies)

        # Make sure it is also reflected in the conversation
        self.assertTrue(new_re_id in conversation)

        # Make sure the conversation has the correct comment id
        self.assertEqual(conversation[new_re_id].comment_id, new_re_id)
    def setUp(self):
        # Setup sandbox
        self.portal = self.layer['portal']
        self.request = self.layer['request']
        setRoles(self.portal, TEST_USER_ID, ['Manager'])
        name = self.portal.invokeFactory(id='doc1',
                                         title='Document 1',
                                         type_name='Document')

        self.document = self.portal[name]
        conversation = IConversation(self.document)
        replies = IReplies(conversation)

        comment = createObject('plone.Comment')
        comment.text = 'This is a comment'
        new_id = replies.addComment(comment)
        comment = self.document.restrictedTraverse(
            '++conversation++default/%s' % new_id)

        re_comment = createObject('plone.Comment')
        re_comment.text = 'This is a reply'
        re_comment.author_username = "******"
        re_comment.author_name = "Juliana"
        re_comment.author_email = "*****@*****.**"

        replies = IReplies(comment)
        new_re_id = replies.addComment(re_comment)
Beispiel #5
0
    def handleComment(self, action):
        context = aq_inner(self.context)

        # Check if conversation is enabled on this content object
        if not self.__parent__.restrictedTraverse(
                '@@conversation_view').enabled():
            raise Unauthorized(
                'Discussion is not enabled for this content object.')

        # Validation form
        data, errors = self.extractData()
        if errors:
            return

        # Validate Captcha
        registry = queryUtility(IRegistry)
        settings = registry.forInterface(IDiscussionSettings, check=False)
        portal_membership = getToolByName(self.context, 'portal_membership')
        captcha_enabled = settings.captcha != 'disabled'
        anonymous_comments = settings.anonymous_comments
        anon = portal_membership.isAnonymousUser()
        if captcha_enabled and anonymous_comments and anon:
            if 'captcha' not in data:
                data['captcha'] = u''
            captcha = CaptchaValidator(self.context, self.request, None,
                                       ICaptcha['captcha'], None)
            captcha.validate(data['captcha'])

        # Create comment
        comment = self.create_comment(data)

        # Add comment to conversation
        conversation = IConversation(self.__parent__)
        if data['in_reply_to']:
            # Add a reply to an existing comment
            conversation_to_reply_to = conversation.get(data['in_reply_to'])
            replies = IReplies(conversation_to_reply_to)
            comment_id = replies.addComment(comment)
        else:
            # Add a comment to the conversation
            comment_id = conversation.addComment(comment)

        # Redirect after form submit:
        # If a user posts a comment and moderation is enabled, a message is
        # shown to the user that his/her comment awaits moderation. If the user
        # has 'review comments' permission, he/she is redirected directly
        # to the comment.
        can_review = getSecurityManager().checkPermission(
            'Review comments', context)
        workflowTool = getToolByName(context, 'portal_workflow')
        comment_review_state = workflowTool.getInfoFor(comment, 'review_state',
                                                       None)
        if comment_review_state == 'pending' and not can_review:
            # Show info message when comment moderation is enabled
            IStatusMessage(self.context.REQUEST).addStatusMessage(
                _('Your comment awaits moderator approval.'), type='info')
            self.request.response.redirect(self.action)
        else:
            # Redirect to comment (inside a content object page)
            self.request.response.redirect(self.action + '#' + str(comment_id))
Beispiel #6
0
    def setUp(self):
        self.app = self.layer["app"]
        self.portal = self.layer["portal"]
        self.request = self.layer["request"]
        self.portal_url = self.portal.absolute_url()

        # Allow discussion
        registry = getUtility(IRegistry)
        settings = registry.forInterface(IDiscussionSettings, check=False)
        settings.globally_enabled = True
        settings.edit_comment_enabled = True
        settings.delete_own_comment_enabled = True

        # doc with comments
        self.doc = api.content.create(
            container=self.portal,
            type="Document",
            id="doc_with_comments",
            title="Document with comments",
            allow_discussion=True,
        )
        self.conversation = IConversation(self.doc)
        self.replies = IReplies(self.conversation)
        comment = createObject("plone.Comment")
        comment.text = "Comment"
        self.comment = self.replies[self.replies.addComment(comment)]

        comment = createObject("plone.Comment")
        comment.text = "Comment 2"
        self.replies.addComment(comment)
Beispiel #7
0
    def test_delete_recursive(self):
        # Create a conversation. In this case we doesn't assign it to an
        # object, as we just want to check the Conversation object API.
        conversation = IConversation(self.portal.doc1)

        IReplies(conversation)

        # Create a nested comment structure:
        #
        # Conversation
        # +- Comment 1
        #    +- Comment 1_1
        #    |  +- Comment 1_1_1
        #    +- Comment 1_2
        # +- Comment 2
        #    +- Comment 2_1

        # Create all comments
        comment1 = createObject('plone.Comment')
        comment1.text = 'Comment text'

        comment1_1 = createObject('plone.Comment')
        comment1_1.text = 'Comment text'

        comment1_1_1 = createObject('plone.Comment')
        comment1_1_1.text = 'Comment text'

        comment1_2 = createObject('plone.Comment')
        comment1_2.text = 'Comment text'

        comment2 = createObject('plone.Comment')
        comment2.text = 'Comment text'

        comment2_1 = createObject('plone.Comment')
        comment2_1.text = 'Comment text'

        # Create the nested comment structure
        new_id_1 = conversation.addComment(comment1)
        new_id_2 = conversation.addComment(comment2)

        comment1_1.in_reply_to = new_id_1
        new_id_1_1 = conversation.addComment(comment1_1)

        comment1_1_1.in_reply_to = new_id_1_1
        conversation.addComment(comment1_1_1)

        comment1_2.in_reply_to = new_id_1
        conversation.addComment(comment1_2)

        comment2_1.in_reply_to = new_id_2
        new_id_2_1 = conversation.addComment(comment2_1)

        del conversation[new_id_1]

        self.assertEqual(
            [{'comment': comment2,     'depth': 0, 'id': new_id_2},
             {'comment': comment2_1,   'depth': 1, 'id': new_id_2_1},
            ], list(conversation.getThreads()))
    def test_removedEvent(self):
        self.assertFalse(self.registry.replyRemoved)

        conversation = IConversation(self.portal.doc1)
        replies = IReplies(conversation)

        comment = createObject('plone.Comment')
        comment.text = 'Comment text'
        new_id = replies.addComment(comment)
        comment = self.portal.doc1.restrictedTraverse(
            '++conversation++default/%s' % new_id)

        re_comment = createObject('plone.Comment')
        re_comment.text = 'Comment text'
        replies = IReplies(comment)
        new_re_id = replies.addComment(re_comment)

        del replies[new_re_id]
        self.assertTrue(self.registry.replyRemoved)
Beispiel #9
0
    def create_comments(self, document):
        document.allow_discussion = True

        conversation = IConversation(document)
        replies = IReplies(conversation)
        comments = []
        for x in range(1, 2):
            comment = createObject("plone.Comment")
            comment.text = "Comment %d" % x
            comment = replies[replies.addComment(comment)]

            comment_replies = IReplies(comment)
            for y in range(1, 2):
                comment = createObject("plone.Comment")
                comment.text = "Comment %d.%d" % (x, y)
                comment_replies.addComment(comment)
                comments.append(comment)

        return comments
    def test_addEvent(self):
        self.assertFalse(self.registry.replyAdded)

        conversation = IConversation(self.document)
        replies = IReplies(conversation)

        comment = createObject('plone.Comment')
        comment.text = 'Comment text'
        new_id = replies.addComment(comment)
        comment = self.document.restrictedTraverse(
            '++conversation++default/%s' % new_id)

        re_comment = createObject('plone.Comment')
        re_comment.text = 'Comment text'

        replies = IReplies(comment)
        replies.addComment(re_comment)

        self.assertTrue(self.registry.replyAdded)
Beispiel #11
0
    def test_comment_with_no_author_image(self):
        setRoles(self.portal, TEST_USER_ID, ["Manager"])
        self.conversation = IConversation(self.doc)
        self.replies = IReplies(self.conversation)
        comment = createObject("plone.Comment")
        comment.text = "Hey ho, let's go!"
        comment.author_username = TEST_USER_ID
        self.comment = self.replies[self.replies.addComment(comment)]

        serializer = getMultiAdapter((self.comment, self.request), ISerializeToJson)
        self.assertEqual(
            None,
            serializer().get("author_image"),
        )
Beispiel #12
0
    def test_comment_with_mimetype_text_plain(self):
        self.conversation = IConversation(self.doc)
        self.replies = IReplies(self.conversation)
        comment = createObject("plone.Comment")
        comment.text = "Hey, I am plain text!"
        comment.mime_type = "text/plain"
        self.comment = self.replies[self.replies.addComment(comment)]

        serializer = getMultiAdapter((self.comment, self.request), ISerializeToJson)

        # serializer should return HTML with a clickable link
        self.assertEqual(
            "Hey, I am plain text!",
            serializer()["text"]["data"],
        )
        # serializer should return mimetype = text/x-web-intelligent
        self.assertEqual("text/plain", serializer()["text"]["mime-type"])
Beispiel #13
0
    def test_comment_with_author_image(self):
        setRoles(self.portal, TEST_USER_ID, ["Manager"])
        # set member portrait
        membertool = getToolByName(self.portal, "portal_memberdata")
        membertool._setPortrait(
            Image(id=TEST_USER_ID, file=dummy.File(), title=""), TEST_USER_ID)
        self.conversation = IConversation(self.doc)
        self.replies = IReplies(self.conversation)
        comment = createObject("plone.Comment")
        comment.text = "Hey ho, let's go!"
        comment.author_username = TEST_USER_ID
        self.comment = self.replies[self.replies.addComment(comment)]

        serializer = getMultiAdapter((self.comment, self.request),
                                     ISerializeToJson)
        self.assertEqual(
            f"{self.portal_url}/portal_memberdata/portraits/test_user_1_",
            serializer().get("author_image"),
        )
Beispiel #14
0
    def test_comment_with_mimetype_html(self):
        # Set text transform to text/html
        registry = queryUtility(IRegistry)
        settings = registry.forInterface(IDiscussionSettings, check=False)
        settings.text_transform = "text/html"

        self.conversation = IConversation(self.doc)
        self.replies = IReplies(self.conversation)
        comment = createObject("plone.Comment")
        comment.text = "Go to <a href='https://www.plone.org'>Plone</a>"
        comment.mime_type = "text/html"
        self.comment = self.replies[self.replies.addComment(comment)]

        serializer = getMultiAdapter((self.comment, self.request), ISerializeToJson)

        # serializer should return HTML
        self.assertEqual(
            'Go to <a href="https://www.plone.org">Plone</a>',
            serializer()["text"]["data"],
        )
        # serializer should return mimetype = text/html
        self.assertEqual("text/html", serializer()["text"]["mime-type"])
Beispiel #15
0
    def test_delete_comment(self):
        # Create and remove a comment and check if the replies adapter
        # has been updated accordingly

        # Create a conversation. In this case we doesn't assign it to an
        # object, as we just want to check the Conversation object API.
        conversation = IConversation(self.portal.doc1)

        replies = IReplies(conversation)

        # Add a comment.
        comment = createObject('plone.Comment')
        comment.text = 'Comment text'

        new_id = replies.addComment(comment)

        # make sure the comment has been added
        self.assertEqual(len(replies), 1)

        # delete the comment we just created
        del replies[new_id]

        # make sure there is no comment left in the conversation
        self.assertEqual(len(replies), 0)
Beispiel #16
0
    def test_add_comment(self):
        # Add comments to a ConversationReplies adapter

        # Create a conversation. In this case we doesn't assign it to an
        # object, as we just want to check the Conversation object API.
        conversation = IConversation(self.portal.doc1)

        replies = IReplies(conversation)

        comment = createObject('plone.Comment')
        comment.text = 'Comment text'

        new_id = replies.addComment(comment)

        # check that replies provides the IReplies interface
        self.assertTrue(IReplies.providedBy(replies))

        # Make sure our comment was added
        self.assertTrue(new_id in replies)

        # Make sure it is also reflected in the conversation
        self.assertTrue(new_id in conversation)

        self.assertEqual(conversation[new_id].comment_id, new_id)
Beispiel #17
0
    def test_comment_with_mimetype_intelligent_text(self):
        # Set text transform to intelligent text
        registry = queryUtility(IRegistry)
        settings = registry.forInterface(IDiscussionSettings, check=False)
        settings.text_transform = "text/x-web-intelligent"

        self.conversation = IConversation(self.doc)
        self.replies = IReplies(self.conversation)
        comment = createObject("plone.Comment")
        comment.text = "Go to https://www.plone.org"
        comment.mime_type = "text/x-web-intelligent"
        self.comment = self.replies[self.replies.addComment(comment)]

        serializer = getMultiAdapter((self.comment, self.request),
                                     ISerializeToJson)

        # serializer should return HTML with a clickable link
        self.assertEqual(
            'Go to <a href="https://www.plone.org" ' +
            'rel="nofollow">https://www.plone.org</a>',
            normalize_html(serializer()["text"]["data"]),
        )
        # serializer should return mimetype = text/html
        self.assertEqual("text/html", serializer()["text"]["mime-type"])
Beispiel #18
0
    def migrate_replies(context, in_reply_to, replies, depth=0, just_delete=0):
        # Recursive function to migrate all direct replies
        # of a comment. Returns True if there are no replies to
        # this comment left, and therefore the comment can be removed.
        if len(replies) == 0:
            return True

        for reply in replies:

            # log
            indent = "  "
            for i in range(depth):
                indent += "  "
            log("%smigrate_reply: '%s'." % (indent, reply.title))

            should_migrate = True
            if filter_callback and not filter_callback(reply):
                should_migrate = False
            if just_delete:
                should_migrate = False

            new_in_reply_to = None
            if should_migrate:
                # create a reply object
                comment = CommentFactory()
                comment.title = reply.Title()
                comment.text = reply.cooked_text
                comment.mime_type = 'text/html'
                comment.creator = reply.Creator()

                email = reply.getProperty('email', None)
                if email:
                    comment.author_email = email

                comment.creation_date = DT2dt(reply.creation_date)
                comment.modification_date = DT2dt(reply.modification_date)

                comment.reply_to = in_reply_to

                if in_reply_to == 0:
                    # Direct reply to a content object
                    new_in_reply_to = conversation.addComment(comment)
                else:
                    # Reply to another comment
                    comment_to_reply_to = conversation.get(in_reply_to)
                    replies = IReplies(comment_to_reply_to)
                    try:
                        comment.text.encode("utf-8")
                        new_in_reply_to = replies.addComment(comment)
                    except UnicodeDecodeError, e:
                        log("Fixing UnicodeDecodeError %s" % e)
                        comment.text = comment.text.decode("utf-8")
                    new_in_reply_to = replies.addComment(comment)

            total_comments_migrated[0] += 1

            # migrate all talkbacks of the reply
            talkback = getattr( reply, 'talkback', None)
            no_replies_left = migrate_replies(context,
                                              new_in_reply_to,
                                              talkback.getReplies(),
                                              depth=depth+1,
                                              just_delete=not should_migrate)

            if no_replies_left:
                # remove reply and talkback
                talkback.deleteReply(reply.id)
                obj = aq_parent(talkback)
                obj.talkback = None
                log("%sremove %s" % (indent, reply.id))
                total_comments_deleted[0] += 1
Beispiel #19
0
    def test_dict_api(self):
        # This test is for the ConversationReplies as well as the
        # CommentReplies adapter.
        #
        # Ensure all operations use only top-level comments. Add some
        # deeper children and ensure that these are not exposed through the
        # IReplies dict.

        # Create a conversation. In this case we doesn't assign it to an
        # object, as we just want to check the Conversation object API.
        conversation = IConversation(self.portal.doc1)

        replies = IReplies(conversation)

        # Create a nested comment structure:
        #
        # Conversation
        # +- Comment 1
        #    +- Comment 1_1
        #    |  +- Comment 1_1_1
        #    +- Comment 1_2
        # +- Comment 2
        #    +- Comment 2_1

        # Create all comments
        comment1 = createObject('plone.Comment')
        comment1.text = 'Comment text'

        comment1_1 = createObject('plone.Comment')
        comment1_1.text = 'Comment text'

        comment1_1_1 = createObject('plone.Comment')
        comment1_1_1.text = 'Comment text'

        comment1_2 = createObject('plone.Comment')
        comment1_2.text = 'Comment text'

        comment2 = createObject('plone.Comment')
        comment2.text = 'Comment text'

        comment2_1 = createObject('plone.Comment')
        comment2_1.text = 'Comment text'

        # Create the nested comment structure
        new_id_1 = replies.addComment(comment1)
        comment1 = self.portal.doc1.restrictedTraverse(
            '++conversation++default/%s' % new_id_1)
        replies_to_comment1 = IReplies(comment1)
        new_id_2 = replies.addComment(comment2)
        comment2 = self.portal.doc1.restrictedTraverse(
            '++conversation++default/%s' % new_id_2)
        replies_to_comment2 = IReplies(comment2)

        new_id_1_1 = replies_to_comment1.addComment(comment1_1)
        comment1_1 = self.portal.doc1.restrictedTraverse(
            '++conversation++default/%s' % new_id_1_1)
        replies_to_comment1_1 = IReplies(comment1_1)
        replies_to_comment1_1.addComment(comment1_1_1)

        replies_to_comment1.addComment(comment1_2)

        replies_to_comment2.addComment(comment2_1)

        # check that replies only contain the direct comments
        # and no comments deeper than 1
        self.assertEqual(conversation.total_comments, 6)
        self.assertEqual(len(replies), 2)
        self.assertEqual(len(replies_to_comment1), 2)
        self.assertEqual(len(replies_to_comment1_1), 1)
        self.assertEqual(len(replies_to_comment2), 1)
Beispiel #20
0
        def migrate_replies(context,
                            in_reply_to,
                            replies,
                            depth=0,
                            just_delete=0):
            # Recursive function to migrate all direct replies
            # of a comment. Returns True if there are no replies to
            # this comment left, and therefore the comment can be removed.
            if len(replies) == 0:
                return True

            workflow = context.portal_workflow
            oldchain = workflow.getChainForPortalType('Discussion Item')
            new_workflow = workflow.comment_review_workflow

            if type(oldchain) == TupleType and len(oldchain) > 0:
                oldchain = oldchain[0]

            for reply in replies:
                # log
                indent = "  "
                for i in range(depth):
                    indent += "  "
                log("%smigrate_reply: '%s'." % (indent, reply.title))

                should_migrate = True
                if filter_callback and not filter_callback(reply):
                    should_migrate = False
                if just_delete:
                    should_migrate = False

                new_in_reply_to = None
                if should_migrate:

                    # create a reply object
                    comment = CommentFactory()
                    comment.title = reply.Title()
                    comment.text = reply.cooked_text
                    comment.mime_type = 'text/html'
                    comment.creator = reply.Creator()

                    email = reply.getProperty('email', None)
                    if email:
                        comment.author_email = email

                    comment.creation_date = DT2dt(reply.creation_date)
                    comment.modification_date = DT2dt(reply.modification_date)

                    comment.reply_to = in_reply_to

                    if in_reply_to == 0:
                        # Direct reply to a content object
                        new_in_reply_to = conversation.addComment(comment)
                    else:
                        # Reply to another comment
                        comment_to_reply_to = conversation.get(in_reply_to)
                        replies = IReplies(comment_to_reply_to)
                        new_in_reply_to = replies.addComment(comment)

                    # migrate the review state
                    old_status = workflow.getStatusOf(oldchain, reply)
                    new_status = {
                        'action':
                        None,
                        'actor':
                        None,
                        'comment':
                        'Migrated workflow state',
                        'review_state':
                        old_status and old_status.get(
                            'review_state', new_workflow.initial_state)
                        or 'published',
                        'time':
                        DateTime()
                    }
                    workflow.setStatusOf('comment_review_workflow', comment,
                                         new_status)

                    auto_transition = new_workflow._findAutomaticTransition(
                        comment, new_workflow._getWorkflowStateOf(comment))
                    if auto_transition is not None:
                        new_workflow._changeStateOf(comment, auto_transition)
                    else:
                        new_workflow.updateRoleMappingsFor(comment)
                    comment.reindexObject(
                        idxs=['allowedRolesAndUsers', 'review_state'])

                self.total_comments_migrated += 1

                # migrate all talkbacks of the reply
                talkback = getattr(reply, 'talkback', None)
                no_replies_left = migrate_replies(
                    context,
                    new_in_reply_to,
                    talkback.getReplies(),
                    depth=depth + 1,
                    just_delete=not should_migrate)

                if no_replies_left:
                    # remove reply and talkback
                    talkback.deleteReply(reply.id)
                    obj = aq_parent(talkback)
                    obj.talkback = None
                    log("%sremove %s" % (indent, reply.id))
                    self.total_comments_deleted += 1

            # Return True when all comments on a certain level have been
            # migrated.
            return True
    def handleComment(self, action):
        context = aq_inner(self.context)

        # Check if conversation is enabled on this content object
        if not self.__parent__.restrictedTraverse(
                '@@conversation_view').enabled():
            raise Unauthorized("Discussion is not enabled for this content "
                               "object.")

        # Validation form
        data, errors = self.extractData()
        if errors:
            return

        # Validate Captcha
        registry = queryUtility(IRegistry)
        settings = registry.forInterface(IDiscussionSettings, check=False)
        portal_membership = getToolByName(self.context, 'portal_membership')
        if settings.captcha != 'disabled' and \
        settings.anonymous_comments and \
        portal_membership.isAnonymousUser():
            if not 'captcha' in data:
                data['captcha'] = u""
            captcha = CaptchaValidator(self.context, self.request, None,
                                       ICaptcha['captcha'], None)
            captcha.validate(data['captcha'])

        # some attributes are not always set
        author_name = u""

        # Create comment
        comment = createObject('plone.Comment')

        # Set comment mime type to current setting in the discussion registry
        comment.mime_type = settings.text_transform

        # Set comment attributes (including extended comment form attributes)
        for attribute in self.fields.keys():
            setattr(comment, attribute, data[attribute])
        # Make sure author_name is properly encoded
        if 'author_name' in data:
            author_name = data['author_name']
            if isinstance(author_name, str):
                author_name = unicode(author_name, 'utf-8')

        # Set comment author properties for anonymous users or members
        can_reply = getSecurityManager().checkPermission(
            'Reply to item', context)
        portal_membership = getToolByName(self.context, 'portal_membership')
        if portal_membership.isAnonymousUser() and \
           settings.anonymous_comments:
            # Anonymous Users
            comment.author_name = author_name
            comment.author_email = u""
            comment.user_notification = None
            comment.creation_date = datetime.utcnow()
            comment.modification_date = datetime.utcnow()
        elif not portal_membership.isAnonymousUser() and can_reply:
            # Member
            member = portal_membership.getAuthenticatedMember()
            username = member.getUserName()
            email = member.getProperty('email')
            fullname = member.getProperty('fullname')
            if not fullname or fullname == '':
                fullname = member.getUserName()
            # memberdata is stored as utf-8 encoded strings
            elif isinstance(fullname, str):
                fullname = unicode(fullname, 'utf-8')
            if email and isinstance(email, str):
                email = unicode(email, 'utf-8')
            comment.creator = username
            comment.author_username = username
            comment.author_name = fullname
            comment.author_email = email
            comment.creation_date = datetime.utcnow()
            comment.modification_date = datetime.utcnow()
        else:  # pragma: no cover
            raise Unauthorized(
                "Anonymous user tries to post a comment, but "
                "anonymous commenting is disabled. Or user does not have the "
                "'reply to item' permission.")

        # Add comment to conversation
        conversation = IConversation(self.__parent__)
        if data['in_reply_to']:
            # Add a reply to an existing comment
            conversation_to_reply_to = conversation.get(data['in_reply_to'])
            replies = IReplies(conversation_to_reply_to)
            comment_id = replies.addComment(comment)
        else:
            # Add a comment to the conversation
            comment_id = conversation.addComment(comment)

        # Redirect after form submit:
        # If a user posts a comment and moderation is enabled, a message is
        # shown to the user that his/her comment awaits moderation. If the user
        # has 'review comments' permission, he/she is redirected directly
        # to the comment.
        can_review = getSecurityManager().checkPermission(
            'Review comments', context)
        workflowTool = getToolByName(context, 'portal_workflow')
        comment_review_state = workflowTool.getInfoFor(comment, 'review_state')
        if comment_review_state == 'pending' and not can_review:
            # Show info message when comment moderation is enabled
            IStatusMessage(self.context.REQUEST).addStatusMessage(
                _("Your comment awaits moderator approval."), type="info")
            self.request.response.redirect(self.action)
        else:
            # Redirect to comment (inside a content object page)
            self.request.response.redirect(self.action + '#' + str(comment_id))
 def can_delete(self, comment=None):
     comment = comment or self.context
     return (len(IReplies(aq_inner(comment))) == 0
             and self.could_delete(comment=comment))
Beispiel #23
0
    def migrate_replies(context, in_reply_to, replies, depth=0, just_delete=0):
        """migrate_replies"""
        # Recursive function to migrate all direct replies
        # of a comment. Returns True if there are no replies to
        # this comment left, and therefore the comment can be removed.
        if not replies:
            return True

        for reply in replies:

            # log
            indent = "  " * depth + 1
            log("%smigrate_reply: '%s'." % (indent, reply.title))

            should_migrate = True
            if filter_callback and not filter_callback(reply):
                should_migrate = False
            if just_delete:
                should_migrate = False

            new_in_reply_to = None
            if should_migrate:
                # create a reply object
                comment = CommentFactory()
                comment.title = reply.Title()
                comment.text = reply.cooked_text
                comment.mime_type = 'text/html'
                comment.creator = reply.Creator()

                email = reply.getProperty('email', None)
                if email:
                    comment.author_email = email

                comment.creation_date = DT2dt(reply.creation_date)
                comment.modification_date = DT2dt(reply.modification_date)

                comment.in_reply_to = in_reply_to

                if in_reply_to == 0:
                    # Direct reply to a content object
                    new_in_reply_to = conversation.addComment(comment)
                else:
                    # Reply to another comment
                    comment_to_reply_to = conversation.get(in_reply_to)
                    replies = IReplies(comment_to_reply_to)
                    new_in_reply_to = replies.addComment(comment)

            self.total_comments_migrated += 1

            # migrate all talkbacks of the reply
            talkback = getattr(reply, 'talkback', None)
            no_replies_left = migrate_replies(context,
                                              new_in_reply_to,
                                              talkback.getReplies(),
                                              depth=depth+1,
                                              just_delete=not should_migrate)

            if no_replies_left:
                # remove reply and talkback
                talkback.deleteReply(reply.id)
                obj = aq_parent(talkback)
                obj.talkback = None
                log("%sremove %s" % (indent, reply.id))
                self.total_comments_deleted += 1

        # Return True when all comments on a certain level have been
        # migrated.
        return True
    def test_traversal(self):
        # Create a nested structure of comment replies and check the traversal

        # make sure comments are traversable, have an id, absolute_url and
        # physical path
        conversation = IConversation(self.portal.doc1)

        comment1 = createObject('plone.Comment')
        comment1.text = 'Comment text'

        conversation.addComment(comment1)

        comment = createObject('plone.Comment')
        comment.text = 'Comment text'
        new_id = conversation.addComment(comment)
        comment = self.portal.doc1.restrictedTraverse(
            '++conversation++default/{0}'.format(new_id), )

        # Add a reply to the CommentReplies adapter of the first comment
        re_comment = createObject('plone.Comment')
        re_comment.text = 'Comment text'
        replies = IReplies(comment)
        new_re_id = replies.addComment(re_comment)
        re_comment = self.portal.doc1.restrictedTraverse(
            '++conversation++default/{0}'.format(new_re_id), )

        # Add a reply to the reply
        re_re_comment = createObject('plone.Comment')
        re_re_comment.text = 'Comment text'
        replies = IReplies(re_comment)
        new_re_re_id = replies.addComment(re_re_comment)
        re_re_comment = self.portal.doc1.restrictedTraverse(
            '++conversation++default/{0}'.format(new_re_re_id), )

        # Add a reply to the replies reply
        re_re_re_comment = createObject('plone.Comment')
        re_re_re_comment.text = 'Comment text'
        replies = IReplies(re_re_comment)
        new_re_re_re_id = replies.addComment(re_re_re_comment)
        re_re_re_comment = self.portal.doc1.restrictedTraverse(
            '++conversation++default/{0}'.format(new_re_re_re_id), )

        self.assertEqual(
            ('', 'plone', 'doc1', '++conversation++default', str(new_id)),
            comment.getPhysicalPath(),
        )
        self.assertEqual(
            'http://nohost/plone/doc1/++conversation++default/' + str(new_id),
            comment.absolute_url(),
        )
        self.assertEqual(
            ('', 'plone', 'doc1', '++conversation++default', str(new_re_id)),
            re_comment.getPhysicalPath(),
        )
        self.assertEqual(
            'http://nohost/plone/doc1/++conversation++default/' +
            str(new_re_id),
            re_comment.absolute_url(),
        )
        self.assertEqual(
            (
                '',
                'plone',
                'doc1',
                '++conversation++default',
                str(new_re_re_id),
            ),
            re_re_comment.getPhysicalPath(),
        )
        self.assertEqual(
            'http://nohost/plone/doc1/++conversation++default/' +
            str(new_re_re_id),
            re_re_comment.absolute_url(),
        )
        self.assertEqual(
            (
                '',
                'plone',
                'doc1',
                '++conversation++default',
                str(new_re_re_re_id),
            ),
            re_re_re_comment.getPhysicalPath(),
        )
        self.assertEqual(
            'http://nohost/plone/doc1/++conversation++default/' +
            str(new_re_re_re_id),
            re_re_re_comment.absolute_url(),
        )