diff --git a/plone/app/discussion/comment.py b/plone/app/discussion/comment.py
index d7c3dc2..bd7d08b 100644
--- a/plone/app/discussion/comment.py
+++ b/plone/app/discussion/comment.py
@@ -42,15 +42,13 @@ class Comment(Explicit, Traversable, RoleManager, Owned):
author_name = None
author_email = None
- def __init__(self, conversation=None, **kw):
- self.comment_id = None # will be set by IConversation.addComment()
-
- self.__parent__ = conversation
+ # Note: we want to use zope.component.createObject() to instantiate
+ # comments as far as possible. comment_id and __parent__ are set via
+ # IConversation.addComment().
+
+ def __init__(self):
self.creation_date = self.modification_date = datetime.now()
- for k, v in kw.items():
- setattr(self, k, v)
-
@property
def __name__(self):
return self.comment_id and unicode(self.comment_id) or None
diff --git a/plone/app/discussion/configure.zcml b/plone/app/discussion/configure.zcml
index 6f3dd5f..bd8d91b 100644
--- a/plone/app/discussion/configure.zcml
+++ b/plone/app/discussion/configure.zcml
@@ -22,9 +22,10 @@
-
+
diff --git a/plone/app/discussion/conversation.py b/plone/app/discussion/conversation.py
index b4dd8a8..5ce861d 100644
--- a/plone/app/discussion/conversation.py
+++ b/plone/app/discussion/conversation.py
@@ -126,7 +126,9 @@ class Conversation(Traversable, Persistent, Explicit):
notify(ObjectWillBeAddedEvent(comment, self, id))
self._comments[id] = comment
- # for logged in users only
+ comment.__parent__ = self
+
+ # Record unique users who've commented (for logged in users only)
commentator = comment.author_username
if commentator:
if not commentator in self._commentators:
@@ -147,6 +149,8 @@ class Conversation(Traversable, Persistent, Explicit):
notify(ObjectAddedEvent(comment.__of__(self), self, id))
notify(ContainerModifiedEvent(self))
+ return id
+
# Dict API
def __len__(self):
@@ -155,12 +159,10 @@ class Conversation(Traversable, Persistent, Explicit):
def __contains__(self, key):
return long(key) in self._comments
- # TODO: Should __getitem__, get, __iter__, values(), items() and iter* return aq-wrapped comments?
-
def __getitem__(self, key):
"""Get an item by its long key
"""
- return self._comments[long(key)]
+ return self._comments[long(key)].__of__(self)
def __delitem__(self, key):
"""Delete an item by its long key
@@ -168,7 +170,7 @@ class Conversation(Traversable, Persistent, Explicit):
key = long(key)
- comment = self[key]
+ comment = self[key].__of__(self)
commentator = comment.author_username
notify(ObjectWillBeRemovedEvent(comment, self, key))
@@ -187,25 +189,30 @@ class Conversation(Traversable, Persistent, Explicit):
return iter(self._comments)
def get(self, key, default=None):
- return self._comments.get(long(key), default)
+ comment = self._comments.get(long(key), default)
+ if comment is default:
+ return default
+ return comment.__of__(self)
def keys(self):
return self._comments.keys()
def items(self):
- return self._comments.items()
+ return [(i[0], i[1].__of__(self),) for i in self._comments.items()]
def values(self):
- return self._comments.values()
+ return [v.__of__(self) for v in self._comments.values()]
def iterkeys(self):
return self._comments.iterkeys()
def itervalues(self):
- return self._comments.itervalues()
+ for v in self._comments.itervalues():
+ yield v.__of__(self)
def iteritems(self):
- return self._comments.iteritems()
+ for k, v in self._comments.iteritems():
+ yield (k, v.__of__(self),)
@implementer(IConversation)
@adapter(IAnnotatable)
diff --git a/plone/app/discussion/tests/test_api.py b/plone/app/discussion/tests/test_api.py
deleted file mode 100644
index 984af14..0000000
--- a/plone/app/discussion/tests/test_api.py
+++ /dev/null
@@ -1,45 +0,0 @@
-import unittest
-from datetime import datetime, timedelta
-
-from Products.PloneTestCase.ptc import PloneTestCase
-from plone.app.discussion.tests.layer import DiscussionLayer
-
-from plone.app.discussion.conversation import Conversation
-from plone.app.discussion.comment import Comment
-from plone.app.discussion.interfaces import ICommentingTool, IConversation
-
-class ConversationTest(PloneTestCase):
-
- layer = DiscussionLayer
-
- def afterSetUp(self):
- # XXX If we make this a layer, it only get run once...
- # First we need to create some content.
- self.loginAsPortalOwner()
- typetool = self.portal.portal_types
- typetool.constructContent('Document', self.portal, 'doc1')
-
- 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)
- # Pretend that we have traversed to the comment by aq wrapping it.
- # XXX implement traversal to commenting and change this:
- conversation = conversation.__of__(self.portal.doc1)
-
- # Add a comment. reply_to=0 means it's not a reply
- comment = Comment(conversation=conversation, reply_to=0)
- comment.title = 'Comment 1'
- comment.text = 'Comment text'
-
- conversation.addComment(comment)
-
- # Check that the conversation methods return the correct data
- self.assert_(isinstance(comment.comment_id, long))
- self.assertEquals(len(conversation.getComments()), 1)
- self.assertEquals(len(conversation.getThreads()), 1)
- self.assertEquals(conversation.total_comments, 1)
- self.assert_(conversation.last_comment_date - datetime.now() < timedelta(seconds=1))
-
-def test_suite():
- return unittest.defaultTestLoader.loadTestsFromName(__name__)
\ No newline at end of file
diff --git a/plone/app/discussion/tests/test_comment.py b/plone/app/discussion/tests/test_comment.py
new file mode 100644
index 0000000..aed3445
--- /dev/null
+++ b/plone/app/discussion/tests/test_comment.py
@@ -0,0 +1,71 @@
+import unittest
+
+from zope.component import createObject
+
+from Products.PloneTestCase.ptc import PloneTestCase
+from plone.app.discussion.tests.layer import DiscussionLayer
+
+from plone.app.discussion.interfaces import IComment, IConversation
+from plone.app.discussion.comment import Comment
+
+class CommentTest(PloneTestCase):
+
+ layer = DiscussionLayer
+
+ def afterSetUp(self):
+ # First we need to create some content.
+ self.loginAsPortalOwner()
+ typetool = self.portal.portal_types
+ typetool.constructContent('Document', self.portal, 'doc1')
+
+ def test_factory(self):
+ # test with createObject()
+ pass
+
+ def test_id(self):
+ # relationship between id, getId(), __name__
+ pass
+
+ def test_title(self):
+ pass
+
+ def test_creator(self):
+ pass
+
+ def test_traversal(self):
+ # make sure comments are traversable, have an id, absolute_url and physical path
+ pass
+
+ def test_workflow(self):
+ # ensure that we can assign a workflow to the comment type and perform
+ # workflow operations
+ pass
+
+ def test_fti(self):
+ # test that we can look up an FTI for Discussion Item
+ pass
+
+class RepliesTest(PloneTestCase):
+
+ # test the IReplies adapter on a comment
+
+ layer = DiscussionLayer
+
+ def afterSetUp(self):
+ # First we need to create some content.
+ self.loginAsPortalOwner()
+ typetool = self.portal.portal_types
+ typetool.constructContent('Document', self.portal, 'doc1')
+
+ def test_add_comment(self):
+ pass
+
+ def test_delete_comment(self):
+ pass
+
+ def test_dict_api(self):
+ # ensure all operations use only top-level comments
+ pass
+
+def test_suite():
+ return unittest.defaultTestLoader.loadTestsFromName(__name__)
\ No newline at end of file
diff --git a/plone/app/discussion/tests/test_conversation.py b/plone/app/discussion/tests/test_conversation.py
new file mode 100644
index 0000000..5be7e53
--- /dev/null
+++ b/plone/app/discussion/tests/test_conversation.py
@@ -0,0 +1,103 @@
+import unittest
+from datetime import datetime, timedelta
+
+from zope.component import createObject
+
+from Acquisition import aq_base
+
+from Products.PloneTestCase.ptc import PloneTestCase
+from plone.app.discussion.tests.layer import DiscussionLayer
+
+from plone.app.discussion.interfaces import IConversation, IComment
+
+class ConversationTest(PloneTestCase):
+
+ layer = DiscussionLayer
+
+ def afterSetUp(self):
+ # First we need to create some content.
+ self.loginAsPortalOwner()
+ typetool = self.portal.portal_types
+ typetool.constructContent('Document', self.portal, 'doc1')
+
+ 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)
+
+ # Pretend that we have traversed to the comment by aq wrapping it.
+ conversation = conversation.__of__(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.title = 'Comment 1'
+ comment.text = 'Comment text'
+
+ new_id = conversation.addComment(comment)
+
+ # Check that the conversation methods return the correct data
+ self.assert_(isinstance(comment.comment_id, long))
+ self.assert_(IComment.providedBy(conversation[new_id]))
+ self.assertEquals(aq_base(conversation[new_id].__parent__), aq_base(conversation))
+ self.assertEquals(new_id, comment.comment_id)
+ self.assertEquals(len(conversation.getComments()), 1)
+ self.assertEquals(len(conversation.getThreads()), 1)
+ self.assertEquals(conversation.total_comments, 1)
+ self.assert_(conversation.last_comment_date - datetime.now() < timedelta(seconds=1))
+
+ def test_delete(self):
+ pass
+
+ def test_dict_operations(self):
+ # test dict operations and acquisition wrapping
+ pass
+
+ def test_total_comments(self):
+ pass
+
+ def test_commentators(self):
+ # add and remove a few comments to make sure the commenetators
+ # property returns a true set
+ pass
+
+ def test_last_comment_date(self):
+ pass
+
+ def test_get_comments_flat(self):
+ pass
+
+ def test_get_comments_batched(self):
+ pass
+
+ def test_get_threads(self):
+ pass
+
+ def test_get_threads_batched(self):
+ pass
+
+class RepliesTest(PloneTestCase):
+
+ # test the IReplies adapter on a conversation
+
+ layer = DiscussionLayer
+
+ def afterSetUp(self):
+ # First we need to create some content.
+ self.loginAsPortalOwner()
+ typetool = self.portal.portal_types
+ typetool.constructContent('Document', self.portal, 'doc1')
+
+ def test_add_comment(self):
+ pass
+
+ def test_delete_comment(self):
+ pass
+
+ def test_dict_api(self):
+ # ensure all operations use only top-level comments
+ pass
+
+def test_suite():
+ return unittest.defaultTestLoader.loadTestsFromName(__name__)
\ No newline at end of file
diff --git a/plone/app/discussion/tests/test_indexers.py b/plone/app/discussion/tests/test_indexers.py
new file mode 100644
index 0000000..d2b9235
--- /dev/null
+++ b/plone/app/discussion/tests/test_indexers.py
@@ -0,0 +1,42 @@
+import unittest
+
+from Products.PloneTestCase.ptc import PloneTestCase
+from plone.app.discussion.tests.layer import DiscussionLayer
+
+class TestIndexers(PloneTestCase):
+
+ layer = DiscussionLayer
+
+ def test_title(self):
+ pass
+
+ def test_description(self):
+ pass
+
+ def test_dates(self):
+ # created, modified, effective etc
+ pass
+
+ def test_searchable_text(self):
+ pass
+
+ def test_creator(self):
+ pass
+
+ def test_title(self):
+ pass
+
+ def test_in_reply_to(self):
+ pass
+
+ def test_path(self):
+ pass
+
+ def test_review_state(self):
+ pass
+
+ def test_object_provides(self):
+ pass
+
+def test_suite():
+ return unittest.defaultTestLoader.loadTestsFromName(__name__)
\ No newline at end of file
diff --git a/plone/app/discussion/tests/test_tool.py b/plone/app/discussion/tests/test_tool.py
index 619a6e8..dab477a 100644
--- a/plone/app/discussion/tests/test_tool.py
+++ b/plone/app/discussion/tests/test_tool.py
@@ -1,11 +1,10 @@
import unittest
-from zope.component import getUtility
+from zope.component import getUtility, createObject
from Products.PloneTestCase.ptc import PloneTestCase
from plone.app.discussion.tests.layer import DiscussionLayer
-from plone.app.discussion.comment import Comment
from plone.app.discussion.interfaces import ICommentingTool, IConversation
class ToolTest(PloneTestCase):
@@ -27,8 +26,8 @@ class ToolTest(PloneTestCase):
# XXX implement traversal to commenting and change this:
conversation = conversation.__of__(self.portal.doc1)
- # Add a comment. reply_to=0 means it's not a reply
- comment = Comment(conversation=conversation, reply_to=0)
+ # Add a comment.
+ comment = createObject('plone.Comment')
comment.title = 'Comment 1'
comment.text = 'Comment text'
@@ -41,5 +40,12 @@ class ToolTest(PloneTestCase):
" %s results in the search" % len(comment))
self.assertEquals(comment[0].Title, 'Comment 1')
+ def test_unindexing(self):
+ pass
+
+ def test_search(self):
+ # search returns only comments
+ pass
+
def test_suite():
return unittest.defaultTestLoader.loadTestsFromName(__name__)
\ No newline at end of file