def test_delete_comment(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) # Add a comment. Note: in real life, we always create comments via the # factory to allow different factories to be swapped in comment = createObject('plone.Comment') comment.text = 'Comment text' new_id = conversation.addComment(comment) # make sure the comment has been added self.assertEqual(len(list(conversation.getComments())), 1) self.assertEqual(len(tuple(conversation.getThreads())), 1) self.assertEqual(conversation.total_comments, 1) # delete the comment we just created del conversation[new_id] # make sure there is no comment left in the conversation self.assertEqual(len(list(conversation.getComments())), 0) self.assertEqual(len(tuple(conversation.getThreads())), 0) self.assertEqual(conversation.total_comments, 0)
def test_delete_comment(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) # Add a comment. Note: in real life, we always create comments via the # factory to allow different factories to be swapped in comment = createObject('plone.Comment') comment.text = 'Comment text' new_id = conversation.addComment(comment) # make sure the comment has been added self.assertEqual(len(list(conversation.getComments())), 1) self.assertEqual(len(tuple(conversation.getThreads())), 1) self.assertEqual(conversation.total_comments, 1) # delete the comment we just created del conversation[new_id] # make sure there is no comment left in the conversation self.assertEqual(len(list(conversation.getComments())), 0) self.assertEqual(len(tuple(conversation.getThreads())), 0) self.assertEqual(conversation.total_comments, 0)
def test_edit_comment(self): """Edit a comment as logged-in user. """ # Allow discussion self.discussionTool.overrideDiscussionFor(self.portal.doc1, True) self.viewlet = CommentsViewlet(self.context, self.request, None, None) def make_request(form={}): request = TestRequest() request.form.update(form) alsoProvides(request, IFormLayer) alsoProvides(request, IAttributeAnnotatable) return request provideAdapter( adapts=(Interface, IBrowserRequest), provides=Interface, factory=CommentForm, name=u"comment-form" ) provideAdapter( adapts=(Interface, IBrowserRequest), provides=Interface, factory=EditCommentForm, name=u"edit-comment-form" ) # The form is submitted successfully, if the required text field is # filled out request = make_request(form={'form.widgets.text': u'bar'}) commentForm = getMultiAdapter( (self.context, request), name=u"comment-form" ) commentForm.update() data, errors = commentForm.extractData() # pylint: disable-msg=W0612 self.assertEqual(len(errors), 0) self.assertFalse(commentForm.handleComment(commentForm, "foo")) # Edit the last comment conversation = IConversation(self.context) comment = [x for x in conversation.getComments()][-1] request = make_request(form={'form.widgets.text': u'foobar'}) editForm = getMultiAdapter( (comment, request), name=u"edit-comment-form" ) editForm.update() data, errors = editForm.extractData() # pylint: disable-msg=W0612 self.assertEqual(len(errors), 0) self.assertFalse(editForm.handleComment(editForm, "foo")) comment = [x for x in conversation.getComments()][-1] self.assertEquals(comment.text, u"foobar")
def test_delete_own_comment(self): """Delete own comment as logged-in user. """ # Allow discussion self.portal.doc1.allow_discussion = True self.viewlet = CommentsViewlet(self.context, self.request, None, None) def make_request(form={}): request = TestRequest() request.form.update(form) alsoProvides(request, IFormLayer) alsoProvides(request, IAttributeAnnotatable) return request provideAdapter( adapts=(Interface, IBrowserRequest), provides=Interface, factory=CommentForm, name=u"comment-form" ) # The form is submitted successfully, if the required text field is # filled out form_request = make_request(form={'form.widgets.text': u'bar'}) commentForm = getMultiAdapter( (self.context, form_request), name=u"comment-form" ) commentForm.update() data, errors = commentForm.extractData() # pylint: disable-msg=W0612 self.assertEqual(len(errors), 0) self.assertFalse(commentForm.handleComment(commentForm, "foo")) # Delete the last comment conversation = IConversation(self.context) comment = [x for x in conversation.getComments()][-1] deleteView = getMultiAdapter( (comment, self.request), name=u"delete-own-comment" ) # try to delete last comment with johndoe setRoles(self.portal, 'johndoe', ['Member']) login(self.portal, 'johndoe') self.assertRaises( Unauthorized, comment.restrictedTraverse, "@@delete-own-comment" ) self.assertEqual(1, len([x for x in conversation.getComments()])) # try to delete last comment with the same user that created it login(self.portal, TEST_USER_NAME) setRoles(self.portal, TEST_USER_ID, ['Member']) deleteView() self.assertEqual(0, len([x for x in conversation.getComments()]))
def test_delete_own_comment(self): """Delete own comment as logged-in user. """ # Allow discussion self.portal.doc1.allow_discussion = True self.viewlet = CommentsViewlet(self.context, self.request, None, None) def make_request(form={}): request = TestRequest() request.form.update(form) alsoProvides(request, IFormLayer) alsoProvides(request, IAttributeAnnotatable) return request provideAdapter( adapts=(Interface, IBrowserRequest), provides=Interface, factory=CommentForm, name=u'comment-form' ) # The form is submitted successfully, if the required text field is # filled out form_request = make_request(form={'form.widgets.text': u'bar'}) commentForm = getMultiAdapter( (self.context, form_request), name=u'comment-form' ) commentForm.update() data, errors = commentForm.extractData() # pylint: disable-msg=W0612 self.assertEqual(len(errors), 0) self.assertFalse(commentForm.handleComment(commentForm, 'foo')) # Delete the last comment conversation = IConversation(self.context) comment = [x for x in conversation.getComments()][-1] deleteView = getMultiAdapter( (comment, self.request), name=u'delete-own-comment' ) # try to delete last comment with johndoe setRoles(self.portal, 'johndoe', ['Member']) login(self.portal, 'johndoe') self.assertRaises( Unauthorized, comment.restrictedTraverse, '@@delete-own-comment' ) self.assertEqual(1, len([x for x in conversation.getComments()])) # try to delete last comment with the same user that created it login(self.portal, TEST_USER_NAME) setRoles(self.portal, TEST_USER_ID, ['Member']) deleteView() self.assertEqual(0, len([x for x in conversation.getComments()]))
def catalog_comments(self, force=False): """Catalog the comments of this object. When force=True, we force this, otherwise we check if the number of items currently in the catalog under this context is the same as the number of actual comments on the context. This check does not work well for folderish items, so they probably always get recataloged, but that is of small concern. """ context = aq_inner(self.context) actual= self.actual_comment_count() if not actual: return in_catalog = self.num_total_comments() if not force and actual == in_catalog: return logger.info("Cataloging %s replies for obj at %s", actual, context.absolute_url()) conversation = None if IConversation is not None: conversation = IConversation(context, None) if conversation is not None: for comment in conversation.getComments(): comment.reindexObject() return portal_discussion = getToolByName(context, 'portal_discussion') talkback = portal_discussion.getDiscussionFor(context) ids = talkback.objectIds() for reply_id in ids: reply = talkback.getReply(reply_id) reply.reindexObject()
def test_like_discussion_item(self): # test discussion-item conversation = IConversation(self.doc1) comment1 = createObject("plone.Comment") conversation.addComment(comment1) comment = [i for i in conversation.getComments()][0] comment_id = IUUID(comment) self.request.form["like_button"] = "like" view = api.content.get_view("toggle_like", self.portal, self.request) view = view.publishTraverse(self.request, comment_id) # Toggle like for comment output = view() self.assertIn("(1)", output) self.assertIn("Unlike", output) user_likes = self.util.get_items_for_user(self.user_id) self.assertTrue(self.util.is_item_liked_by_user(self.user_id, comment_id)) self.assertEqual(len(user_likes), 1) # Toggle like for comment output = view() user_likes = self.util.get_items_for_user(self.user_id) self.assertEqual(len(user_likes), 0) self.assertIn("(0)", output) self.assertIn("Like", output)
def __call__(self): """ """ conversation = IConversation(self.context) comments = conversation.getComments() comments = [comment for comment in comments] tmp_lst = [] for item in comments: tmp_dict = item.__dict__ if not tmp_dict.get("status"): states = list(tmp_dict["workflow_history"].values()) comment_status = states[0][-1]["review_state"] try: del tmp_dict["__parent__"] del tmp_dict["workflow_history"] except Exception: pass tmp_dict["modification_date"] = (DateTime( tmp_dict["modification_date"]).asdatetime().isoformat()) tmp_dict["creation_date"] = (DateTime( tmp_dict["creation_date"]).asdatetime().isoformat()) if not tmp_dict.get("status"): tmp_dict.update({"status": comment_status}) tmp_lst.append(tmp_dict) return {"discussions": tmp_lst}
def notify_content_object_moved(obj, event): """Update all comments of a content object that has been moved. """ if event.oldParent is None or event.newParent is None \ or event.oldName is None or event.newName is None: return # This method is also called for sublocations of moved objects. We # therefore can't assume that event.object == obj and event. # {old,new}{Parent,Name} may refer to the actually moved object further up # in the object hierarchy. The object is already moved at this point. so # obj.getPhysicalPath retruns the new path get the part of the path that # was moved. moved_path = obj.getPhysicalPath()[ len(event.newParent.getPhysicalPath()) + 1: ] # Remove comments at the old location from catalog catalog = getToolByName(obj, 'portal_catalog') old_path = '/'.join( event.oldParent.getPhysicalPath() + (event.oldName,) + moved_path ) brains = catalog.searchResults(dict( path={'query': old_path}, portal_type="Discussion Item" )) for brain in brains: catalog.uncatalog_object(brain.getPath()) # Reindex comment at the new location conversation = IConversation(obj, None) if conversation is not None: for comment in conversation.getComments(): comment.reindexObject()
def test_like_discussion_item(self): # test discussion-item conversation = IConversation(self.doc1) comment1 = createObject('plone.Comment') conversation.addComment(comment1) comment = [i for i in conversation.getComments()][0] comment_id = IUUID(comment) self.request.form['like_button'] = 'like' view = api.content.get_view('toggle_like', self.portal, self.request) view = view.publishTraverse(self.request, comment_id) # Toggle like for comment output = view() self.assertIn('(1)', output) self.assertIn('Unlike', output) user_likes = self.util.get_items_for_user(self.user_id) self.assertTrue( self.util.is_item_liked_by_user(self.user_id, comment_id)) self.assertEqual(len(user_likes), 1) # Toggle like for comment output = view() user_likes = self.util.get_items_for_user(self.user_id) self.assertEqual(len(user_likes), 0) self.assertIn('(0)', output) self.assertIn('Like', output)
def update_comments(obj, path): """Update the comments of this object """ if IConversation is not None: conversation = IConversation(obj, None) if conversation is not None: for comment in conversation.getComments(): comment.reindexObject() return try: talkback = self.portal_discussion.getDiscussionFor(obj) except (TypeError, AttributeError): # Happens for the 'portal_types' object and for # objects in portal_skins/custom. logger.debug("Error getting discussion for obj at %s", path) return except DiscussionNotAllowed: logger.debug("Discussion not allowed for obj at %s", path) if not self.force: return # Try anyway: if not hasattr(aq_base(obj), 'talkback'): return talkback = getattr(obj, 'talkback') logger.info("Discussion not allowed for obj, but forcing " "recatalog anyway at %s", path) ids = talkback.objectIds() if ids: logger.info("%s replies found for obj at %s", len(ids), path) for reply_id in ids: reply = talkback.getReply(reply_id) reply.reindexObject()
def findObjects(origin): """ generator to recursively find and yield all zope objects below the given start point """ traverse = origin.unrestrictedTraverse base = '/'.join(origin.getPhysicalPath()) cut = len(base) + 1 paths = [base] for idx, path in enumerate(paths): obj = traverse(path) yield path[cut:], obj if hasattr(aq_base(obj), 'objectIds'): from zope.component.interfaces import ComponentLookupError try: for id in obj.objectIds(): paths.insert(idx + 1, path + '/' + id) except ComponentLookupError: logger.error( 'Can not list sub-objects of object {0}'.format(path) ) try: conversation = IConversation(obj) except TypeError: continue for comment in conversation.getComments(): comment_path = '/'.join(comment.getPhysicalPath()[-2:]) paths.insert(idx + 1, path + '/' + comment_path)
def notify_content_object_moved(obj, event): """Update all comments of a content object that has been moved. """ if event.oldParent is None or event.newParent is None \ or event.oldName is None or event.newName is None: return # This method is also called for sublocations of moved objects. We # therefore can't assume that event.object == obj and event. # {old,new}{Parent,Name} may refer to the actually moved object further up # in the object hierarchy. The object is already moved at this point. so # obj.getPhysicalPath retruns the new path get the part of the path that # was moved. moved_path = obj.getPhysicalPath()[len(event.newParent.getPhysicalPath()) + 1:] # Remove comments at the old location from catalog catalog = getToolByName(obj, 'portal_catalog') old_path = '/'.join(event.oldParent.getPhysicalPath() + (event.oldName, ) + moved_path) brains = catalog.searchResults( dict(path={'query': old_path}, portal_type="Discussion Item")) for brain in brains: catalog.uncatalog_object(brain.getPath()) # Reindex comment at the new location conversation = IConversation(obj, None) if conversation is not None: for comment in conversation.getComments(): comment.reindexObject()
def test_add_comment(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) # Add a comment. Note: in real life, we always create comments via the # factory to allow different factories to be swapped in comment = createObject('plone.Comment') comment.text = 'Comment text' new_id = conversation.addComment(comment) # Check that the conversation methods return the correct data self.assertTrue(isinstance(comment.comment_id, int)) self.assertTrue(IComment.providedBy(conversation[new_id])) self.assertEqual( aq_base(conversation[new_id].__parent__), aq_base(conversation), ) self.assertEqual(new_id, comment.comment_id) self.assertEqual(len(list(conversation.getComments())), 1) self.assertEqual(len(tuple(conversation.getThreads())), 1) self.assertEqual(conversation.total_comments(), 1) self.assertTrue( conversation.last_comment_date - datetime.utcnow() < timedelta(seconds=1), )
def test_add_comment(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) # Add a comment. Note: in real life, we always create comments via the # factory to allow different factories to be swapped in comment = createObject('plone.Comment') comment.text = 'Comment text' new_id = conversation.addComment(comment) # Check that the conversation methods return the correct data self.assertTrue(isinstance(comment.comment_id, long)) self.assertTrue(IComment.providedBy(conversation[new_id])) self.assertEqual( aq_base(conversation[new_id].__parent__), aq_base(conversation) ) self.assertEqual(new_id, comment.comment_id) self.assertEqual(len(list(conversation.getComments())), 1) self.assertEqual(len(tuple(conversation.getThreads())), 1) self.assertEqual(conversation.total_comments(), 1) self.assertTrue( conversation.last_comment_date - datetime.utcnow() < timedelta(seconds=1) )
def test_migrate_comment_with_creator(self): # Create a comment talkback = self.discussion.getDiscussionFor(self.doc) self.doc.talkback.createReply('My Title', 'My Text', Creator='Jim') reply = talkback.getReplies()[0] reply.setReplyTo(self.doc) reply.creation_date = DateTime(2003, 3, 11, 9, 28, 6, 'GMT') reply.modification_date = DateTime(2009, 7, 12, 19, 38, 7, 'GMT') reply.author_username = '******' reply.email = '*****@*****.**' self._publish(reply) self.assertEqual(reply.Title(), 'My Title') self.assertEqual(reply.EditableBody(), 'My Text') self.assertTrue('Jim' in reply.listCreators()) self.assertEqual(talkback.replyCount(self.doc), 1) self.assertEqual(reply.inReplyTo(), self.doc) self.assertEqual(reply.author_username, 'Jim') self.assertEqual(reply.email, '*****@*****.**') # Call migration script self.view() # Make sure a conversation has been created self.assertTrue( 'plone.app.discussion:conversation' in IAnnotations(self.doc) ) conversation = IConversation(self.doc) # Check migration self.assertEqual(conversation.total_comments, 1) self.assertTrue(conversation.getComments().next()) comment1 = conversation.values()[0] self.assertTrue(IComment.providedBy(comment1)) self.assertEqual(comment1.Title(), 'My Title') self.assertEqual(comment1.text, '<p>My Text</p>\n') self.assertEqual(comment1.mime_type, 'text/html') self.assertEqual(comment1.Creator(), 'Jim') self.assertEqual( comment1.creation_date, datetime(2003, 3, 11, 9, 28, 6) ) self.assertEqual( comment1.modification_date, datetime(2009, 7, 12, 19, 38, 7) ) self.assertEqual([ {'comment': comment1, 'depth': 0, 'id': long(comment1.id)} ], list(conversation.getThreads())) self.assertFalse(self.doc.talkback) # Though this should be Jimmy, but looks like getProperty won't pick # up 'author_username' (reply.author_username is not None), so it's # propagating Creator()..? self.assertEqual(comment1.author_username, 'Jim') self.assertEqual(comment1.author_name, 'Jimmy Jones') self.assertEqual(comment1.author_email, '*****@*****.**')
def test_push_to_tmpstorage(self): push_to_tmpstorage(self.portal.doc1) conversation = IConversation(self.portal) self.assertEqual(len(conversation), 1) self.assertEqual( conversation.getComments().next().text, 'Comment text' )
def test_migrate_comment_with_creator(self): # Create a comment talkback = self.discussion.getDiscussionFor(self.doc) self.doc.talkback.createReply('My Title', 'My Text', Creator='Jim') reply = talkback.getReplies()[0] reply.setReplyTo(self.doc) reply.creation_date = DateTime(2003, 3, 11, 9, 28, 6, 'GMT') reply.modification_date = DateTime(2009, 7, 12, 19, 38, 7, 'GMT') reply.author_username = '******' reply.email = '*****@*****.**' self._publish(reply) self.assertEqual(reply.Title(), 'My Title') self.assertEqual(reply.EditableBody(), 'My Text') self.assertTrue('Jim' in reply.listCreators()) self.assertEqual(talkback.replyCount(self.doc), 1) self.assertEqual(reply.inReplyTo(), self.doc) self.assertEqual(reply.author_username, 'Jim') self.assertEqual(reply.email, '*****@*****.**') # Call migration script self.view() # Make sure a conversation has been created self.assertTrue( 'plone.app.discussion:conversation' in IAnnotations(self.doc)) conversation = IConversation(self.doc) # Check migration self.assertEqual(conversation.total_comments, 1) self.assertTrue(conversation.getComments().next()) comment1 = conversation.values()[0] self.assertTrue(IComment.providedBy(comment1)) self.assertEqual(comment1.Title(), 'My Title') self.assertEqual(comment1.text, '<p>My Text</p>\n') self.assertEqual(comment1.mime_type, 'text/html') self.assertEqual(comment1.Creator(), 'Jim') self.assertEqual(comment1.creation_date, datetime(2003, 3, 11, 9, 28, 6)) self.assertEqual(comment1.modification_date, datetime(2009, 7, 12, 19, 38, 7)) self.assertEqual([{ 'comment': comment1, 'depth': 0, 'id': long(comment1.id) }], list(conversation.getThreads())) self.assertFalse(self.doc.talkback) # Though this should be Jimmy, but looks like getProperty won't pick # up 'author_username' (reply.author_username is not None), so it's # propagating Creator()..? self.assertEqual(comment1.author_username, 'Jim') self.assertEqual(comment1.author_name, 'Jimmy Jones') self.assertEqual(comment1.author_email, '*****@*****.**')
def testDiscussionReply(self): from zope.component import createObject, queryUtility from plone.registry.interfaces import IRegistry from plone.app.discussion.interfaces import IDiscussionSettings from plone.app.discussion.interfaces import IConversation self.folder.invokeFactory('Document', id='doc', title="Document") # Enable discussion registry = queryUtility(IRegistry) settings = registry.forInterface(IDiscussionSettings) settings.globally_enabled = True # Create the conversation object conversation = IConversation(self.folder.doc) # Add a comment comment = createObject('plone.Comment') comment.text = 'Comment text' conversation.addComment(comment) # Test the comment self.assertEqual(len(list(conversation.getComments())), 1) reply = conversation.getComments().next() self.assertEqual(reply.Title(), u'Anonymous on Document') self.assertEqual(reply.text, 'Comment text')
def testDiscussionReply(self): from zope.component import createObject, queryUtility from plone.registry.interfaces import IRegistry from plone.app.discussion.interfaces import IDiscussionSettings from plone.app.discussion.interfaces import IConversation self.folder.invokeFactory('Document', id='doc', title="Document") # Enable discussion registry = queryUtility(IRegistry) settings = registry.forInterface(IDiscussionSettings) settings.globally_enabled = True # Create the conversation object conversation = IConversation(self.folder.doc) # Add a comment comment = createObject('plone.Comment') comment.text = 'Comment text' conversation.addComment(comment) # Test the comment self.assertEquals(len(list(conversation.getComments())), 1) reply = conversation.getComments().next() self.assertEqual(reply.Title(), u'Anonymous on Document') self.assertEquals(reply.text, 'Comment text')
def test_delete_comment_when_content_object_is_deleted(self): # Make sure all comments of a content object are deleted when the # object itself is deleted. conversation = IConversation(self.portal.doc1) comment = createObject('plone.Comment') comment.text = 'Comment text' conversation.addComment(comment) # Delete the content object self.portal.manage_delObjects(['doc1']) # Make sure the comment has been deleted as well self.assertEqual(len(list(conversation.getComments())), 0) self.assertEqual(len(tuple(conversation.getThreads())), 0) self.assertEqual(conversation.total_comments, 0)
def test_delete_comment_when_content_object_is_deleted(self): # Make sure all comments of a content object are deleted when the # object itself is deleted. conversation = IConversation(self.portal.doc1) comment = createObject('plone.Comment') comment.text = 'Comment text' conversation.addComment(comment) # Delete the content object self.portal.manage_delObjects(['doc1']) # Make sure the comment has been deleted as well self.assertEqual(len(list(conversation.getComments())), 0) self.assertEqual(len(tuple(conversation.getThreads())), 0) self.assertEqual(conversation.total_comments, 0)
def test_discussion_normal(self): self._create_commentable_doc() self.browser.open(self.portal_url + '/doc') self.browser.getControl( name='form.widgets.author_name').value = 'Mr. Spammer' self.browser.getControl( name='form.widgets.text').value = 'Spam spam.' self.browser.getControl(name='form.buttons.comment').click() self.assertTrue( 'Required input is missing.' not in self.browser.contents) # The comment is added. conversation = IConversation(self.portal.doc) self.assertEqual(len(list(conversation.getComments())), 1) # No mails are sent. self.assertEqual(len(self.mailhost.messages), 0)
def test_migrate_comment(self): # Create a comment talkback = self.discussion.getDiscussionFor(self.doc) self.doc.talkback.createReply('My Title', 'My Text', Creator='Jim') reply = talkback.getReplies()[0] reply.setReplyTo(self.doc) reply.creation_date = DateTime(2003, 3, 11, 9, 28, 6, 'GMT') reply.modification_date = DateTime(2009, 7, 12, 19, 38, 7, 'GMT') self._publish(reply) self.assertEqual(reply.Title(), 'My Title') self.assertEqual(reply.EditableBody(), 'My Text') self.assertTrue('Jim' in reply.listCreators()) self.assertEqual(talkback.replyCount(self.doc), 1) self.assertEqual(reply.inReplyTo(), self.doc) # Call migration script self.view() # Make sure a conversation has been created self.assertTrue( 'plone.app.discussion:conversation' in IAnnotations(self.doc) ) conversation = IConversation(self.doc) # Check migration self.assertEqual(conversation.total_comments, 1) self.assertTrue(conversation.getComments().next()) comment1 = conversation.values()[0] self.assertTrue(IComment.providedBy(comment1)) self.assertEqual(comment1.Title(), 'My Title') self.assertEqual(comment1.text, '<p>My Text</p>\n') self.assertEqual(comment1.mime_type, 'text/html') self.assertEqual(comment1.Creator(), 'Jim') self.assertEqual( comment1.creation_date, datetime(2003, 3, 11, 9, 28, 6) ) self.assertEqual( comment1.modification_date, datetime(2009, 7, 12, 19, 38, 7) ) self.assertEqual([ {'comment': comment1, 'depth': 0, 'id': long(comment1.id)} ], list(conversation.getThreads())) self.assertFalse(self.doc.talkback)
def _comments(obj): from plone.app.discussion.interfaces import IConversation conversation = IConversation(obj) comments = [] for c in conversation.getComments(): comments.append({ 'author_username': c.author_username, 'text': c.text, 'comment_id': c.comment_id, 'creation_date': api.portal.get_localized_time(c.creation_date, long_format=True) }) return comments
class RedirectionTest(unittest.TestCase): layer = PLONE_APP_DISCUSSION_INTEGRATION_TESTING def setUp(self): # Update settings. self.portal = self.layer['portal'] self.request = self.layer['request'] setRoles(self.portal, TEST_USER_ID, ['Manager']) # applyProfile(self.portal, 'plone.app.discussion:default') registry = queryUtility(IRegistry) settings = registry.forInterface(IDiscussionSettings) settings.globally_enabled = True self.portal.portal_workflow.setChainForPortalTypes( ('Discussion Item',), ('comment_review_workflow',), ) # Create page plus comment. self.portal.invokeFactory( id='page', title='Page 1', type_name='Document', ) self.page = self.portal.page self.conversation = IConversation(self.page) comment = createObject('plone.Comment') comment.text = 'Comment text' self.comment_id = self.conversation.addComment(comment) self.comment = list(self.conversation.getComments())[0] def test_regression(self): page_url = self.page.absolute_url() self.request['HTTP_REFERER'] = page_url for Klass in (DeleteComment, PublishComment): view = Klass(self.comment, self.request) view.__parent__ = self.comment self.assertEqual(page_url, view()) def test_valid_next_url(self): self.request['HTTP_REFERER'] = 'http://attacker.com' for Klass in (DeleteComment, PublishComment): view = Klass(self.comment, self.request) view.__parent__ = self.comment self.assertNotEqual('http://attacker.com', view())
def test_migrate_comment(self): # Create a comment talkback = self.discussion.getDiscussionFor(self.doc) self.doc.talkback.createReply('My Title', 'My Text', Creator='Jim') reply = talkback.getReplies()[0] reply.setReplyTo(self.doc) reply.creation_date = DateTime(2003, 3, 11, 9, 28, 6, 'GMT') reply.modification_date = DateTime(2009, 7, 12, 19, 38, 7, 'GMT') self._publish(reply) self.assertEqual(reply.Title(), 'My Title') self.assertEqual(reply.EditableBody(), 'My Text') self.assertTrue('Jim' in reply.listCreators()) self.assertEqual(talkback.replyCount(self.doc), 1) self.assertEqual(reply.inReplyTo(), self.doc) # Call migration script self.view() # Make sure a conversation has been created self.assertTrue( 'plone.app.discussion:conversation' in IAnnotations(self.doc)) conversation = IConversation(self.doc) # Check migration self.assertEqual(conversation.total_comments, 1) self.assertTrue(conversation.getComments().next()) comment1 = conversation.values()[0] self.assertTrue(IComment.providedBy(comment1)) self.assertEqual(comment1.Title(), 'My Title') self.assertEqual(comment1.text, '<p>My Text</p>\n') self.assertEqual(comment1.mime_type, 'text/html') self.assertEqual(comment1.Creator(), 'Jim') self.assertEqual(comment1.creation_date, datetime(2003, 3, 11, 9, 28, 6)) self.assertEqual(comment1.modification_date, datetime(2009, 7, 12, 19, 38, 7)) self.assertEqual([{ 'comment': comment1, 'depth': 0, 'id': long(comment1.id) }], list(conversation.getThreads())) self.assertFalse(self.doc.talkback)
class RedirectionTest(unittest.TestCase): layer = PLONE_APP_DISCUSSION_INTEGRATION_TESTING def setUp(self): # Update settings. self.portal = self.layer['portal'] self.request = self.layer['request'] setRoles(self.portal, TEST_USER_ID, ['Manager']) # applyProfile(self.portal, 'plone.app.discussion:default') registry = queryUtility(IRegistry) settings = registry.forInterface(IDiscussionSettings) settings.globally_enabled = True self.portal.portal_workflow.setChainForPortalTypes( ('Discussion Item', ), ('comment_review_workflow', ), ) # Create page plus comment. self.portal.invokeFactory( id='page', title='Page 1', type_name='Document', ) self.page = self.portal.page self.conversation = IConversation(self.page) comment = createObject('plone.Comment') comment.text = 'Comment text' self.comment_id = self.conversation.addComment(comment) self.comment = list(self.conversation.getComments())[0] def test_regression(self): page_url = self.page.absolute_url() self.request['HTTP_REFERER'] = page_url for Klass in (DeleteComment, PublishComment): view = Klass(self.comment, self.request) view.__parent__ = self.comment self.assertEqual(page_url, view()) def test_valid_next_url(self): self.request['HTTP_REFERER'] = 'http://attacker.com' for Klass in (DeleteComment, PublishComment): view = Klass(self.comment, self.request) view.__parent__ = self.comment self.assertNotEqual('http://attacker.com', view())
def notify_content_object_moved(obj, event): """Update all comments of a content object that has been moved. """ if ( event.oldParent is None or event.newParent is None or event.oldName is None or event.newName is None or event.oldParent == event.newParent ): return # Remove comments at the old location from catalog catalog = getToolByName(obj, "portal_catalog") for brain in catalog.searchResults(portal_type="Discussion Item"): catalog.uncatalog_object(brain.getPath()) # Reindex comment at the new location conversation = IConversation(obj) for comment in conversation.getComments(): comment.__parent__.__parent__.__parent__ = event.newParent catalog.reindexObject(aq_base(comment))
def move_comments(source_object, target_object): """Move comments by copying the annotation to the target and then removing the comments from the source (not the annotation). """ source_annotations = IAnnotations(source_object) comments = source_annotations.get(DISCUSSION_KEY, None) if comments is not None: target_annotations = IAnnotations(target_object) if target_annotations.get(DISCUSSION_KEY, None) is not None: logger.error('Comments exist on {0}').format( target_object.absolute_url()) target_annotations[DISCUSSION_KEY] = deepcopy(comments) # Delete comments from the portal where wthey were stored temporarily. # Comments on the old objects will be removed with the objects. if IPloneSiteRoot.providedBy(source_object): source_conversation = IConversation(source_object) for comment in source_conversation.getComments(): del source_conversation[comment.comment_id] del source_annotations[DISCUSSION_KEY]
def move_comments(source_object, target_object): """Move comments by copying the annotation to the target and then removing the comments from the source (not the annotation). """ source_annotations = IAnnotations(source_object) comments = source_annotations.get(DISCUSSION_KEY, None) if comments is not None: target_annotations = IAnnotations(target_object) if target_annotations.get(DISCUSSION_KEY, None) is not None: logger.error('Comments exist on {0}').format( target_object.absolute_url()) target_annotations[DISCUSSION_KEY] = deepcopy(comments) # Delete comments from the portal where wthey were stored temporarily. # Comments on the old objects will be removed with the objects. if IPloneSiteRoot.providedBy(source_object): source_conversation = IConversation(source_object) for comment in source_conversation.getComments(): del source_conversation[comment.comment_id] del source_annotations[DISCUSSION_KEY]
def notify_content_object_moved(obj, event): """Update all comments of a content object that has been moved. """ if event.oldParent is None or event.newParent is None \ or event.oldName is None or event.newName is None: return # Remove comments at the old location from catalog catalog = getToolByName(obj, 'portal_catalog') old_path = '/'.join(event.oldParent.getPhysicalPath() + (event.oldName,)) brains = catalog.searchResults(dict( path={'query': old_path}, portal_type="Discussion Item" )) for brain in brains: catalog.uncatalog_object(brain.getPath()) # Reindex comment at the new location conversation = IConversation(obj, None) if conversation is not None: for comment in conversation.getComments(): aq_base(comment).__parent__.__parent__.__parent__ = event.newParent catalog.reindexObject(aq_base(comment))
def notify_content_object_moved(obj, event): """Update all comments of a content object that has been moved. """ if event.oldParent is None or event.newParent is None \ or event.oldName is None or event.newName is None: return # Remove comments at the old location from catalog catalog = getToolByName(obj, 'portal_catalog') old_path = '/'.join(event.oldParent.getPhysicalPath() + (event.oldName,)) brains = catalog.searchResults(dict( path={'query': old_path}, portal_type="Discussion Item" )) for brain in brains: catalog.uncatalog_object(brain.getPath()) # Reindex comment at the new location conversation = IConversation(obj, None) if conversation is not None: for comment in conversation.getComments(): aq_base(comment).__parent__.__parent__.__parent__ = event.newParent catalog.reindexObject(aq_base(comment))
def posts(self): # llista de posts ordenats de més recent a més antic results = [] mtool = api.portal.get_tool(name='portal_membership') brains = self.context.getFolderContents(contentFilter={ 'portal_type': 'genweb.simpleforum.post', 'sort_on': 'modified', 'sort_order': 'reverse'}) for brain in brains: conversation = IConversation(brain.getObject()) total_comments = conversation.total_comments comments = conversation.getComments() last_comment = None for last_comment in comments: pass if last_comment: last_author = last_comment.Creator() else: last_author = brain.Creator # si hi ha comentaris mostrem la data del darrer comentari, si no, la data de modificació del post if total_comments > 0: modification_date = utc_to_local(conversation.last_comment_date) else: modification_date = brain.ModificationDate modification_date = self.context.toLocalizedTime(modification_date, long_format='%B %d, %Y') results.append({ 'title': brain.Title, 'url': brain.getURL(), 'author': mtool.getMemberInfo(last_author)['fullname'], 'modificationDate': modification_date, 'portrait': mtool.getPersonalPortrait(id=brain.listCreators[0]).absolute_url(), 'comments': total_comments }) # ordenem els resultas inversament data creació o d'últim comentari results = sorted(results, key=itemgetter('modificationDate'), reverse=True) # import pdb; pdb.set_trace() return results
def __iter__(self): for item in self.previous: if item['_type'] != 'Discussion Item': # not a comment yield item continue path = item['_path'] pathlist = path.split('/') pathlist.remove('talkback') path = '/'.join(pathlist[:-1]) ob = self.context.unrestrictedTraverse(path.lstrip('/'), None) if ob is None: yield item continue # object not found logger.info("importing Comment on %s", ob) conversation = IConversation(ob) #check to see if comment already exists already_exists = False for comment in conversation.getComments(): if comment.text == item['text']: already_exists = True continue if already_exists: logger.info('comment already exists, skipping') yield item continue comment = createObject('plone.Comment') comment.text = item['text'] comment.author_name = item.get('author_name') comment.author_email = item.get('author_email') comment.creation_date = DateTime( item['creation_date']).asdatetime() comment.modification_date = comment.creation_date in_reply_to = item.get('_in_reply_to', 0) if in_reply_to: comment.in_reply_to = self.comment_map[in_reply_to] id = conversation.addComment(comment) self.comment_map[item['_id']] = id item_tmp = item workflowhistorykey = "_workflow_history" # get back datetime stamp and set the workflow history for workflow in item_tmp[workflowhistorykey]: for k, workflow2 in enumerate( item_tmp[workflowhistorykey][workflow]): # noqa if 'time' in item_tmp[workflowhistorykey][workflow][k]: item_tmp[workflowhistorykey][workflow][k][ 'time'] = DateTime( # noqa item_tmp[workflowhistorykey][workflow][k] ['time']) # noqa #change workflow key/name from 'comment_worklfow' to 'comment_review_workflow' item_tmp[workflowhistorykey]['comment_review_workflow'] = \ item_tmp[workflowhistorykey].pop('comment_workflow') comment.workflow_history.data = item_tmp[workflowhistorykey] # update security workflows = self.wftool.getWorkflowsFor(comment) if workflows: workflows[0].updateRoleMappingsFor(comment) yield item
def test_edit_comment(self): """Edit a comment as logged-in user. """ # Allow discussion self.portal.doc1.allow_discussion = True self.viewlet = CommentsViewlet(self.context, self.request, None, None) def make_request(form={}): request = TestRequest() request.form.update(form) alsoProvides(request, IFormLayer) alsoProvides(request, IAttributeAnnotatable) return request provideAdapter( adapts=(Interface, IBrowserRequest), provides=Interface, factory=CommentForm, name=u"comment-form" ) provideAdapter( adapts=(Interface, IBrowserRequest), provides=Interface, factory=EditCommentForm, name=u"edit-comment-form" ) # The form is submitted successfully, if the required text field is # filled out request = make_request(form={'form.widgets.text': u'bar'}) commentForm = getMultiAdapter( (self.context, request), name=u"comment-form" ) commentForm.update() data, errors = commentForm.extractData() # pylint: disable-msg=W0612 self.assertEqual(len(errors), 0) self.assertFalse(commentForm.handleComment(commentForm, "foo")) # Edit the last comment conversation = IConversation(self.context) comment = [x for x in conversation.getComments()][-1] request = make_request(form={'form.widgets.text': u'foobar'}) editForm = getMultiAdapter( (comment, request), name=u"edit-comment-form" ) editForm.update() data, errors = editForm.extractData() # pylint: disable-msg=W0612 self.assertEqual(len(errors), 0) self.assertFalse(editForm.handleComment(editForm, "foo")) comment = [x for x in conversation.getComments()][-1] self.assertEquals(comment.text, u"foobar") comments = IConversation(commentForm.context).getComments() comments = [comment for comment in comments] # consume itertor self.assertEqual(len(comments), 1) for comment in comments: self.assertEqual(comment.text, u"foobar") self.assertEqual(comment.creator, "test_user_1_") self.assertEqual(comment.getOwner().getUserName(), "test-user") local_roles = comment.get_local_roles() self.assertEqual(len(local_roles), 1) userid, roles = local_roles[0] self.assertEqual(userid, 'test_user_1_') self.assertEqual(len(roles), 1) self.assertEqual(roles[0], 'Owner')