diff --git a/plone/app/discussion/conversation.py b/plone/app/discussion/conversation.py
index ca22c05..ba27c0f 100644
--- a/plone/app/discussion/conversation.py
+++ b/plone/app/discussion/conversation.py
@@ -50,14 +50,8 @@ from plone.app.discussion.comment import Comment
from AccessControl.SpecialUsers import nobody as user_nobody
-from ComputedAttribute import ComputedAttribute
-
ANNOTATION_KEY = 'plone.app.discussion:conversation'
-def computed_attribute_decorator(level=0):
- def computed_attribute_wrapper(func):
- return ComputedAttribute(func, level)
- return computed_attribute_wrapper
class Conversation(Traversable, Persistent, Explicit):
"""A conversation is a container for all comments on a content object.
@@ -93,21 +87,21 @@ class Conversation(Traversable, Persistent, Explicit):
parent = aq_inner(self.__parent__)
return parent.restrictedTraverse('@@conversation_view').enabled()
- @computed_attribute_decorator(level=1)
+ @property
def total_comments(self):
public_comments = [
- x for x in self.values()
+ x for x in self._comments.values()
if user_nobody.has_permission('View', x)
]
return len(public_comments)
- @computed_attribute_decorator(level=1)
+ @property
def last_comment_date(self):
# self._comments is an Instance of a btree. The keys
# are always ordered
comment_keys = self._comments.keys()
for comment_key in reversed(comment_keys):
- comment = self[comment_key]
+ comment = self._comments[comment_key]
if user_nobody.has_permission('View', comment):
return comment.creation_date
return None
@@ -116,10 +110,10 @@ class Conversation(Traversable, Persistent, Explicit):
def commentators(self):
return self._commentators
- @computed_attribute_decorator(level=1)
+ @property
def public_commentators(self):
retval = set()
- for comment in self.values():
+ for comment in self._comments.values():
if not user_nobody.has_permission('View', comment):
continue
retval.add(comment.author_username)
diff --git a/plone/app/discussion/testing.py b/plone/app/discussion/testing.py
index e47026f..e7a397d 100644
--- a/plone/app/discussion/testing.py
+++ b/plone/app/discussion/testing.py
@@ -41,14 +41,10 @@ class PloneAppDiscussion(PloneSandboxLayer):
xmlconfig.file('configure.zcml',
plone.app.discussion,
context=configurationContext)
- xmlconfig.file('configure.zcml',
- plone.app.discussion.tests,
- context=configurationContext)
def setUpPloneSite(self, portal):
# Install into Plone site using portal_setup
applyProfile(portal, 'plone.app.discussion:default')
- applyProfile(portal, 'plone.app.discussion.tests:testing')
# Creates some users
acl_users = getToolByName(portal, 'acl_users')
diff --git a/plone/app/discussion/tests/configure.zcml b/plone/app/discussion/tests/configure.zcml
deleted file mode 100644
index ee79b95..0000000
--- a/plone/app/discussion/tests/configure.zcml
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
-
-
-
diff --git a/plone/app/discussion/tests/profile/types.xml b/plone/app/discussion/tests/profile/types.xml
deleted file mode 100644
index 0aec569..0000000
--- a/plone/app/discussion/tests/profile/types.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
diff --git a/plone/app/discussion/tests/profile/types/sample_content_type.xml b/plone/app/discussion/tests/profile/types/sample_content_type.xml
deleted file mode 100644
index 7869638..0000000
--- a/plone/app/discussion/tests/profile/types/sample_content_type.xml
+++ /dev/null
@@ -1,47 +0,0 @@
-
-
-
-
- sample_content_type
- Sample Content
- document_icon.png
- True
- True
-
-
- True
-
- plone.dexterity.content.Item
-
- cmf.AddPortalContent
-
-
-
-
-
- view
- False
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/plone/app/discussion/tests/profile/workflows.xml b/plone/app/discussion/tests/profile/workflows.xml
deleted file mode 100644
index 500f444..0000000
--- a/plone/app/discussion/tests/profile/workflows.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
diff --git a/plone/app/discussion/tests/profile/workflows/comment_workflow_acquired_view/definition.xml b/plone/app/discussion/tests/profile/workflows/comment_workflow_acquired_view/definition.xml
deleted file mode 100644
index 89a9fb2..0000000
--- a/plone/app/discussion/tests/profile/workflows/comment_workflow_acquired_view/definition.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-
-
- Access contents information
- Change portal events
- Modify portal content
- View
-
- Visible to everyone, editable by the owner.
-
- Anonymous
-
-
- Editor
- Manager
- Owner
- Site Administrator
-
-
- Editor
- Manager
- Owner
- Site Administrator
-
-
-
-
-
- Previous transition
-
- transition/getId|nothing
-
-
-
-
-
- The ID of the user who performed the previous transition
-
- user/getId
-
-
-
-
-
- Comment about the last transition
-
- python:state_change.kwargs.get('comment', '')
-
-
-
-
-
- Provides access to workflow history
-
- state_change/getHistory
-
-
- Request review
- Review portal content
-
-
-
- When the previous transition was performed
-
- state_change/getDateTime
-
-
-
-
-
diff --git a/plone/app/discussion/tests/test_acquisition.py b/plone/app/discussion/tests/test_acquisition.py
deleted file mode 100644
index c6a96db..0000000
--- a/plone/app/discussion/tests/test_acquisition.py
+++ /dev/null
@@ -1,243 +0,0 @@
-# -*- coding: utf-8 -*-
-from AccessControl.User import User # before SpecialUsers
-from AccessControl.SpecialUsers import nobody as user_nobody
-from AccessControl.PermissionRole import rolesForPermissionOn
-from Acquisition import aq_chain, aq_base
-from plone.app.discussion.testing import \
- PLONE_APP_DISCUSSION_INTEGRATION_TESTING
-from plone.app.discussion.interfaces import IConversation
-from plone.app.testing import TEST_USER_ID, setRoles
-from Products.CMFCore.utils import getToolByName
-from zope.component import createObject
-
-import unittest2 as unittest
-
-
-dexterity_type_name = 'sample_content_type'
-dexterity_object_id = 'instance-of-dexterity-type'
-archetypes_object_id = 'instance-of-archetypes-type'
-one_state_workflow = 'one_state_workflow'
-comment_workflow_acquired_view = 'comment_workflow_acquired_view'
-
-class AcquisitionTest(unittest.TestCase):
- """ Define the expected behaviour of wrapped and unwrapped comments. """
-
- layer = PLONE_APP_DISCUSSION_INTEGRATION_TESTING
-
- def setUp(self):
- self.portal = self.layer['portal']
- self.request = self.layer['request']
- setRoles(self.portal, TEST_USER_ID, ['Manager'])
- self.wftool = getToolByName(self.portal, 'portal_workflow')
-
- # Use customized workflow for comments.
- self.wftool.setChainForPortalTypes(
- ['Discussion Item'],
- (comment_workflow_acquired_view,),
- )
-
- # Use one_state_workflow for Document and sample_content_type,
- # so they're always published.
- self.wftool.setChainForPortalTypes(
- ['Document', dexterity_type_name],
- (one_state_workflow,),
- )
-
- # Create a dexterity item and add a comment.
- self.portal.invokeFactory(
- id=dexterity_object_id,
- title='Instance Of Dexterity Type',
- type_name=dexterity_type_name,
- )
-
- self.dexterity_object = self.portal.get(dexterity_object_id)
- dx_conversation = IConversation(self.dexterity_object)
- self.dexterity_conversation = dx_conversation
- dx_comment = createObject('plone.Comment')
- dx_conversation.addComment(dx_comment)
- self.unwrapped_dexterity_comment = dx_comment
- self.wrapped_dexterity_comment = dx_conversation[dx_comment.id]
-
- # Create an Archetypes item and add a comment.
- self.portal.invokeFactory(
- id=archetypes_object_id,
- title='Instance Of Archetypes Type',
- type_name='Document',
- )
-
- self.archetypes_object = self.portal.get(archetypes_object_id)
- at_conversation = IConversation(self.archetypes_object)
- self.archetypes_conversation = at_conversation
- at_comment = createObject('plone.Comment')
- at_conversation.addComment(at_comment)
- self.unwrapped_archetypes_comment = at_comment
- self.wrapped_archetypes_comment = at_conversation[at_comment.id]
-
-
- def test_workflows_installed(self):
- """Check that the new comment workflow has been installed properly.
- (Just a test to check our test setup.)
- """
- workflows = self.wftool.objectIds()
- self.assertTrue('comment_workflow_acquired_view' in workflows)
-
- def test_workflows_applied(self):
- """Check that all objects have the workflow that we expect.
- (Just a test to check our test setup.)"""
- self.assertEqual(
- self.wftool.getChainFor(self.archetypes_object),
- (one_state_workflow,)
- )
- self.assertEqual(
- self.wftool.getChainFor(self.dexterity_object),
- (one_state_workflow,)
- )
- self.assertEqual(
- self.wftool.getChainFor(self.unwrapped_archetypes_comment),
- (comment_workflow_acquired_view,)
- )
- self.assertEqual(
- self.wftool.getChainFor(self.unwrapped_dexterity_comment),
- (comment_workflow_acquired_view,)
- )
-
- def test_comment_acquisition_chain(self):
- """ Test that the acquisition chains for wrapped and unwrapped
- comments are as expected. """
-
- # Unwrapped comments rely on __parent__ attributes to determine
- # parentage. Frustratingly there is no guarantee that __parent__
- # is always set, so the computed acquisition chain may be short.
- # In this case the unwrapped AT and DX objects stored as the
- # conversation parents don't have a __parent__, preventing the portal
- # from being included in the chain.
- self.assertNotIn(self.portal,
- aq_chain(self.unwrapped_archetypes_comment))
- self.assertNotIn(self.portal,
- aq_chain(self.unwrapped_dexterity_comment))
-
- # Wrapped comments however have a complete chain and thus can find the
- # portal object reliably.
- self.assertIn(self.portal,aq_chain(self.wrapped_archetypes_comment))
- self.assertIn(self.portal,aq_chain(self.wrapped_dexterity_comment))
-
-
- def test_acquiring_comment_permissions(self):
- """ Unwrapped comments should not be able to acquire permissions
- controlled by unreachable objects """
-
- # We use the "Allow sendto" permission as by default it is
- # controlled by the portal, which is unreachable via __parent__
- # attributes on the comments.
- permission = "Allow sendto"
-
- # Unwrapped comments can't find the portal so just return manager
- self.assertNotIn("Anonymous",
- rolesForPermissionOn(permission,
- self.unwrapped_archetypes_comment))
- self.assertNotIn("Anonymous",
- rolesForPermissionOn(permission,
- self.unwrapped_dexterity_comment))
-
- # Wrapped objects can find the portal and correctly return the
- # anonymous role.
- self.assertIn("Anonymous",
- rolesForPermissionOn(permission,
- self.wrapped_archetypes_comment))
- self.assertIn("Anonymous",
- rolesForPermissionOn(permission,
- self.wrapped_dexterity_comment))
-
- def test_acquiring_comment_permissions_via_user_nobody(self):
- """ The current implementation uses user_nobody.has_permission to
- check whether anonymous can view comments. This confirms it also
- works. """
-
- # Again we want to use a permission that's not managed by any of our
- # content objects so it must be acquired from the portal.
- permission = "Allow sendto"
-
- self.assertFalse(
- user_nobody.has_permission(permission,
- self.unwrapped_archetypes_comment))
-
- self.assertFalse(
- user_nobody.has_permission(permission,
- self.unwrapped_dexterity_comment))
-
- self.assertTrue(
- user_nobody.has_permission(permission,
- self.wrapped_archetypes_comment))
-
- self.assertTrue(
- user_nobody.has_permission(permission,
- self.wrapped_dexterity_comment))
-
-class AcquiredPermissionTest(unittest.TestCase):
- """ Test methods of a conversation which rely on acquired permissions """
-
- layer = PLONE_APP_DISCUSSION_INTEGRATION_TESTING
-
- def setUp(self):
- self.portal = self.layer['portal']
- self.request = self.layer['request']
- setRoles(self.portal, TEST_USER_ID, ['Manager'])
- self.wftool = getToolByName(self.portal, 'portal_workflow')
-
- # Disable workflow for comments and content.
- self.wftool.setChainForPortalTypes(["Discussion Item"],[])
- self.wftool.setChainForPortalTypes([dexterity_type_name],[])
-
- # Create a dexterity item.
- self.portal.invokeFactory(
- id=dexterity_object_id,
- title='Instance Of Dexterity Type',
- type_name=dexterity_type_name,
- )
-
- self.content = self.portal.get(dexterity_object_id)
-
- # Absolutely make sure that we're replicating the case of an
- # incomplete chain correctly.
- aq_base(self.content).__parent__ = None
-
- self.conversation = IConversation(self.content)
-
- # Add a comment
- comment = createObject('plone.Comment')
- self.conversation.addComment(comment)
- self.comment = comment
-
- def test_view_permission_is_only_available_on_portal(self):
- """ Check that the test setup is correct """
-
- content_roles = rolesForPermissionOn("View",aq_base(self.content))
- self.assertNotIn("Anonymous",content_roles)
-
- comment_roles = rolesForPermissionOn("View",aq_base(self.comment))
- self.assertNotIn("Anonymous",comment_roles)
-
- # This actually acquires view from the app root, but we don't really
- # care, we just need to confirm that something above our content
- # object will give us View.
- portal_roles = rolesForPermissionOn("View",self.portal)
- self.assertIn("Anonymous",portal_roles)
-
- # The following tests fail when the conversation uses unwrapped comment
- # objects to determine whether an anonymous user has the view permission.
-
- def test_total_comments(self):
- self.assertEqual(self.conversation.total_comments,1)
-
- def test_last_comment_date(self):
- self.assertEqual(self.conversation.last_comment_date,
- self.comment.creation_date)
-
- def test_public_commentators(self):
- self.assertEqual(self.conversation.public_commentators,
- (self.comment.author_username,))
-
-
-
-def test_suite():
- return unittest.defaultTestLoader.loadTestsFromName(__name__)