from plone.app.discussion.browser.comment import View from plone.app.discussion.interfaces import IComment from plone.app.discussion.interfaces import IConversation from plone.app.discussion.interfaces import IReplies from plone.app.discussion.testing import PLONE_APP_DISCUSSION_INTEGRATION_TESTING from plone.app.testing import setRoles from plone.app.testing import TEST_USER_ID from Products.CMFCore.utils import getToolByName from zope.component import createObject from zope.component import getMultiAdapter import datetime import logging import unittest logger = logging.getLogger("plone.app.discussion.tests") logger.addHandler(logging.StreamHandler()) def normalize(value): # Strip all white spaces of every line, then join on one line. # But try to avoid getting 'Go tohttp://www.plone.org', ) def test_getText_uses_comment_mime_type_html(self): comment1 = createObject("plone.Comment") comment1.text = 'Go to plone.org' comment1.mime_type = "text/html" self.assertEqual( normalize(comment1.getText()), 'Go to plone.org', ) def test_getText_w_custom_targetMimetype(self): comment1 = createObject("plone.Comment") comment1.text = "para" self.assertEqual(comment1.getText(targetMimetype="text/plain"), "para") def test_getText_invalid_transformation_raises_error(self): conversation = IConversation(self.portal.doc1) comment1 = createObject("plone.Comment") comment1.mime_type = "text/x-html-safe" comment1.text = "para" conversation.addComment(comment1) self.assertEqual(comment1.getText(targetMimetype="text/html"), "para") def test_traversal(self): # 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" new_comment1_id = conversation.addComment(comment1) comment = self.portal.doc1.restrictedTraverse( f"++conversation++default/{new_comment1_id}", ) self.assertTrue(IComment.providedBy(comment)) self.assertEqual( ( "", "plone", "doc1", "++conversation++default", str(new_comment1_id), ), comment.getPhysicalPath(), ) self.assertEqual( "http://nohost/plone/doc1/++conversation++default/" + str(new_comment1_id), comment.absolute_url(), ) def test_view_blob_types(self): """ Make sure that traversal to images/files redirects to the version of the url with a /view in it. """ self.portal.invokeFactory( id="image1", title="Image", type_name="Image", ) conversation = IConversation(self.portal.image1) comment1 = createObject("plone.Comment") comment1.text = "Comment text" new_comment1_id = conversation.addComment(comment1) comment = self.portal.image1.restrictedTraverse( f"++conversation++default/{new_comment1_id}", ) view = View(comment, self.request) View.__call__(view) response = self.request.response self.assertIn("/view", response.headers["location"]) def test_workflow(self): """Basic test for the 'comment_review_workflow'""" self.portal.portal_workflow.setChainForPortalTypes( ("Discussion Item",), ("comment_review_workflow,"), ) conversation = IConversation(self.portal.doc1) comment1 = createObject("plone.Comment") new_comment1_id = conversation.addComment(comment1) comment = conversation[new_comment1_id] # Make sure comments use the 'comment_review_workflow' chain = self.portal.portal_workflow.getChainFor(comment) self.assertEqual(("comment_review_workflow",), chain) # Ensure the initial state was entered and recorded self.assertEqual( 1, len(comment.workflow_history["comment_review_workflow"]), ) self.assertEqual( None, comment.workflow_history["comment_review_workflow"][0]["action"], ) self.assertEqual( "pending", self.portal.portal_workflow.getInfoFor(comment, "review_state"), ) def test_fti(self): # test that we can look up an FTI for Discussion Item self.assertIn( "Discussion Item", self.portal.portal_types.objectIds(), ) comment1 = createObject("plone.Comment") fti = self.portal.portal_types.getTypeInfo(comment1) self.assertEqual("Discussion Item", fti.getTypeInfo(comment1).getId()) def test_view(self): # make sure that the comment view is there and redirects to the right # URL # 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) # Create a comment comment1 = createObject("plone.Comment") comment1.text = "Comment text" # Add comment to the conversation new_comment1_id = conversation.addComment(comment1) comment = self.portal.doc1.restrictedTraverse( f"++conversation++default/{new_comment1_id}", ) # make sure the view is there self.assertTrue( getMultiAdapter( (comment, self.request), name="view", ), ) # make sure the HTTP redirect (status code 302) works when a comment # is called directly view = View(comment, self.request) View.__call__(view) self.assertEqual(self.request.response.status, 302) class RepliesTest(unittest.TestCase): # test the IReplies adapter on a comment layer = PLONE_APP_DISCUSSION_INTEGRATION_TESTING def setUp(self): self.portal = self.layer["portal"] setRoles(self.portal, TEST_USER_ID, ["Manager"]) workflow = self.portal.portal_workflow workflow.doActionFor(self.portal.doc1, "publish") 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( f"++conversation++default/{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 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( f"++conversation++default/{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) 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( f"++conversation++default/{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( f"++conversation++default/{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( f"++conversation++default/{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( f"++conversation++default/{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(), )