From 9263a61a312c331390b0409c6bc377427801e041 Mon Sep 17 00:00:00 2001 From: Martin Aspeli Date: Sun, 24 May 2009 16:19:21 +0000 Subject: [PATCH] Delete children when deleting a parent comment. svn path=/plone.app.discussion/trunk/; revision=27092 --- plone/app/discussion/conversation.py | 31 +++++++-- .../app/discussion/tests/test_conversation.py | 69 +++++++++++++++++++ 2 files changed, 95 insertions(+), 5 deletions(-) diff --git a/plone/app/discussion/conversation.py b/plone/app/discussion/conversation.py index 3ac1fc2..1a3de77 100644 --- a/plone/app/discussion/conversation.py +++ b/plone/app/discussion/conversation.py @@ -196,7 +196,7 @@ class Conversation(Traversable, Persistent, Explicit): """ return self._comments[long(key)].__of__(self) - def __delitem__(self, key): + def __delitem__(self, key, suppress_container_modified=False): """Delete an item by its long key """ @@ -207,16 +207,37 @@ class Conversation(Traversable, Persistent, Explicit): notify(ObjectWillBeRemovedEvent(comment, self, key)) + # Remove all children + for child_id in self._children.get(key, []): + # avoid sending ContainerModifiedEvent multiple times + self.__delitem__(child_id, suppress_container_modified=True) + + # XXX: During the events sent from the recursive deletion, the + # _children data structure may be in an inconsistent state. We may + # need to delay sending the events until it is fixed up. + + # Remove the comment from _comments self._comments.pop(key) - notify(ObjectRemovedEvent(comment, self, key)) - + + # Remove this comment as a child of its parent + if not suppress_container_modified: + parent = comment.in_reply_to + if parent is not None: + parent_children = self._children.get(parent, None) + if parent_children is not None and key in parent_children: + parent_children.remove(key) + + # Remove commentators if commentator and commentator in self._commentators: if self._commentators[commentator] <= 1: del self._commentators[commentator] else: self._commentators[commentator] -= 1 - - notify(ContainerModifiedEvent(self)) + + notify(ObjectRemovedEvent(comment, self, key)) + + if not suppress_container_modified: + notify(ContainerModifiedEvent(self)) def __iter__(self): return iter(self._comments) diff --git a/plone/app/discussion/tests/test_conversation.py b/plone/app/discussion/tests/test_conversation.py index 4c6aa9f..b3030dc 100644 --- a/plone/app/discussion/tests/test_conversation.py +++ b/plone/app/discussion/tests/test_conversation.py @@ -81,6 +81,75 @@ class ConversationTest(PloneTestCase): # self.assertEquals(len(conversation.getThreads()), 0) self.assertEquals(conversation.total_comments, 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) + + # Pretend that we have traversed to the comment by aq wrapping it. + conversation = conversation.__of__(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.title = 'Comment 1' + comment1.text = 'Comment text' + + comment1_1 = createObject('plone.Comment') + comment1_1.title = 'Re: Comment 1' + comment1_1.text = 'Comment text' + + comment1_1_1 = createObject('plone.Comment') + comment1_1_1.title = 'Re: Re: Comment 1' + comment1_1_1.text = 'Comment text' + + comment1_2 = createObject('plone.Comment') + comment1_2.title = 'Re: Comment 1 (2)' + comment1_2.text = 'Comment text' + + comment2 = createObject('plone.Comment') + comment2.title = 'Comment 2' + comment2.text = 'Comment text' + + comment2_1 = createObject('plone.Comment') + comment2_1.title = 'Re: Comment 2' + 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 + new_id_1_1_1 = conversation.addComment(comment1_1_1) + + comment1_2.in_reply_to = new_id_1 + new_id_1_2 = 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.assertEquals( + [{'comment': comment2, 'depth': 0, 'id': new_id_2}, + {'comment': comment2_1, 'depth': 1, 'id': new_id_2_1}, + ], list(conversation.getThreads())) + + def test_dict_operations(self): # test dict operations and acquisition wrapping