[Testbot] Plone 5.0 - Python 2.7 - Build # 2478 - Regression! - 2 failure(s)
jenkins at plone.org
jenkins at plone.org
Mon May 12 21:39:57 UTC 2014
-------------------------------------------------------------------------------
Plone 5.0 - Python 2.7 - Build # 2478 - Failure!
-------------------------------------------------------------------------------
http://jenkins.plone.org/job/plone-5.0-python-2.7/2478/
-------------------------------------------------------------------------------
CHANGES
-------------------------------------------------------------------------------
Repository: plone.app.discussion
Branch: refs/heads/master
Date: 2013-11-21T17:22:12Z
Author: Kees Hink (khink) <keeshink at gmail.com>
Commit: https://github.com/plone/plone.app.discussion/commit/5fb6968fcad4e40c25f6ae65a331f015bbd30ce0
Add a test for Acquisition in comments.
Files changed:
A plone/app/discussion/tests/configure.zcml
A plone/app/discussion/tests/profile/types.xml
A plone/app/discussion/tests/profile/types/sample_content_type.xml
A plone/app/discussion/tests/profile/workflows.xml
A plone/app/discussion/tests/profile/workflows/comment_workflow_acquired_view/definition.xml
A plone/app/discussion/tests/test_acquisition.py
M plone/app/discussion/testing.py
diff --git a/plone/app/discussion/testing.py b/plone/app/discussion/testing.py
index cf1066c..02b8fa3 100644
--- a/plone/app/discussion/testing.py
+++ b/plone/app/discussion/testing.py
@@ -35,10 +35,14 @@ def setUpZope(self, app, configurationContext):
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
new file mode 100644
index 0000000..ee79b95
--- /dev/null
+++ b/plone/app/discussion/tests/configure.zcml
@@ -0,0 +1,16 @@
+<configure
+ xmlns="http://namespaces.zope.org/zope"
+ xmlns:genericsetup="http://namespaces.zope.org/genericsetup"
+ i18n_domain="freitag.discussion">
+
+ <include package="plone.app.dexterity" />
+
+ <genericsetup:registerProfile
+ name="testing"
+ title="plone.app.discussion.testing"
+ directory="profile"
+ description="Testing profile for plone.app.discussion"
+ provides="Products.GenericSetup.interfaces.EXTENSION"
+ />
+
+</configure>
diff --git a/plone/app/discussion/tests/profile/types.xml b/plone/app/discussion/tests/profile/types.xml
new file mode 100644
index 0000000..0aec569
--- /dev/null
+++ b/plone/app/discussion/tests/profile/types.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0"?>
+<object name="portal_types" meta_type="Plone Types Tool">
+ <object name="sample_content_type" meta_type="Dexterity FTI" />
+</object>
diff --git a/plone/app/discussion/tests/profile/types/sample_content_type.xml b/plone/app/discussion/tests/profile/types/sample_content_type.xml
new file mode 100644
index 0000000..7869638
--- /dev/null
+++ b/plone/app/discussion/tests/profile/types/sample_content_type.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0"?>
+<object name="sample_content_type"
+ meta_type="Dexterity FTI"
+ i18n:domain="freitag.discussion" xmlns:i18n="http://xml.zope.org/namespaces/i18n">
+
+ <!-- Basic metadata -->
+ <property name="title" i18n:translate="">sample_content_type</property>
+ <property name="description"
+ i18n:translate="">Sample Content</property>
+ <property name="content_icon">document_icon.png</property>
+ <property name="global_allow">True</property>
+ <property name="filter_content_types">True</property>
+ <property name="allowed_content_types">
+ </property>
+ <property name="allow_discussion">True</property>
+
+ <property name="klass">plone.dexterity.content.Item</property>
+
+ <property name="add_permission">cmf.AddPortalContent</property>
+ <property name="behaviors">
+ <element value="plone.app.content.interfaces.INameFromTitle" />
+ </property>
+
+ <!-- View information -->
+ <property name="default_view">view</property>
+ <property name="default_view_fallback">False</property>
+ <property name="view_methods">
+ <element value="view" />
+ </property>
+
+ <!-- Method aliases -->
+ <alias from="(Default)" to="(selected layout)" />
+ <alias from="edit" to="@@edit" />
+ <alias from="sharing" to="@@sharing" />
+ <alias from="view" to="@@view" />
+
+ <!-- Actions -->
+ <action title="View" action_id="view" category="object" condition_expr=""
+ url_expr="string:${object_url}/" visible="True">
+ <permission value="View" />
+ </action>
+
+ <action title="Edit" action_id="edit" category="object" condition_expr=""
+ url_expr="string:${object_url}/edit" visible="True">
+ <permission value="Modify portal content" />
+ </action>
+</object>
diff --git a/plone/app/discussion/tests/profile/workflows.xml b/plone/app/discussion/tests/profile/workflows.xml
new file mode 100644
index 0000000..500f444
--- /dev/null
+++ b/plone/app/discussion/tests/profile/workflows.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0"?>
+<object name="portal_workflow" meta_type="CMF Workflow Tool">
+ <object name="comment_workflow_acquired_view" meta_type="Workflow"/>
+</object>
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
new file mode 100644
index 0000000..89a9fb2
--- /dev/null
+++ b/plone/app/discussion/tests/profile/workflows/comment_workflow_acquired_view/definition.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0"?>
+<dc-workflow
+ workflow_id="comment_workflow_acquired_view"
+ title="Single State Workflow"
+ description="- Essentially a workflow with no transitions, but has a Published state, so portlets and applications that expect that state will continue to work."
+ state_variable="review_state"
+ initial_state="published"
+ manager_bypass="False">
+ <permission>Access contents information</permission>
+ <permission>Change portal events</permission>
+ <permission>Modify portal content</permission>
+ <permission>View</permission>
+ <state state_id="published" title="Published">
+ <description>Visible to everyone, editable by the owner.</description>
+ <permission-map name="Access contents information" acquired="False">
+ <permission-role>Anonymous</permission-role>
+ </permission-map>
+ <permission-map name="Change portal events" acquired="False">
+ <permission-role>Editor</permission-role>
+ <permission-role>Manager</permission-role>
+ <permission-role>Owner</permission-role>
+ <permission-role>Site Administrator</permission-role>
+ </permission-map>
+ <permission-map name="Modify portal content" acquired="False">
+ <permission-role>Editor</permission-role>
+ <permission-role>Manager</permission-role>
+ <permission-role>Owner</permission-role>
+ <permission-role>Site Administrator</permission-role>
+ </permission-map>
+ <permission-map name="View" acquired="True">
+ </permission-map>
+ </state>
+ <variable variable_id="action" for_catalog="False" for_status="True" update_always="True">
+ <description>Previous transition</description>
+ <default>
+ <expression>transition/getId|nothing</expression>
+ </default>
+ <guard>
+ </guard>
+ </variable>
+ <variable variable_id="actor" for_catalog="False" for_status="True" update_always="True">
+ <description>The ID of the user who performed the previous transition</description>
+ <default>
+ <expression>user/getId</expression>
+ </default>
+ <guard>
+ </guard>
+ </variable>
+ <variable variable_id="comments" for_catalog="False" for_status="True" update_always="True">
+ <description>Comment about the last transition</description>
+ <default>
+ <expression>python:state_change.kwargs.get('comment', '')</expression>
+ </default>
+ <guard>
+ </guard>
+ </variable>
+ <variable variable_id="review_history" for_catalog="False" for_status="False" update_always="False">
+ <description>Provides access to workflow history</description>
+ <default>
+ <expression>state_change/getHistory</expression>
+ </default>
+ <guard>
+ <guard-permission>Request review</guard-permission>
+ <guard-permission>Review portal content</guard-permission>
+ </guard>
+ </variable>
+ <variable variable_id="time" for_catalog="False" for_status="True" update_always="True">
+ <description>When the previous transition was performed</description>
+ <default>
+ <expression>state_change/getDateTime</expression>
+ </default>
+ <guard>
+ </guard>
+ </variable>
+</dc-workflow>
diff --git a/plone/app/discussion/tests/test_acquisition.py b/plone/app/discussion/tests/test_acquisition.py
new file mode 100644
index 0000000..31d5f81
--- /dev/null
+++ b/plone/app/discussion/tests/test_acquisition.py
@@ -0,0 +1,145 @@
+# -*- coding: utf-8 -*-
+from Acquisition import aq_chain
+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'
+
+
+def _anonymousCanView(obj):
+ """Use rolesOfPermission() to sees if Anonymous has View permission on an
+ object"""
+ roles_of_view_permission = obj.rolesOfPermission("View")
+ # rolesOfPermission returns a list of dictionaries that have the key
+ # 'name' for role.
+ anon_views = [r for r in roles_of_view_permission
+ if r['name'] == 'Anonymous']
+ # only one entry per role should be present
+ anon_view = anon_views[0]
+ # if this role has the permission, 'selected' is set to 'SELECTED'
+ return anon_view['selected'] == 'SELECTED'
+
+
+class DexterityAcquisitionTest(unittest.TestCase):
+ """See test_view_permission."""
+
+ 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
+ comment1 = createObject('plone.Comment')
+ dx_conversation.addComment(comment1)
+ self.dexterity_comment = comment1
+
+ # 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
+ comment2 = createObject('plone.Comment')
+ at_conversation.addComment(comment2)
+ self.archetypes_comment = comment2
+
+ 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.archetypes_comment),
+ (comment_workflow_acquired_view,)
+ )
+ self.assertEqual(
+ self.wftool.getChainFor(self.dexterity_comment),
+ (comment_workflow_acquired_view,)
+ )
+
+ def test_view_permission(self):
+ """Test that if the View permission on Discussion Items is acquired,
+ Anonymous can view comments on published items."""
+
+ # Anonymous has View permission on commented objects.
+ self.assertTrue(_anonymousCanView(self.archetypes_object))
+ self.assertTrue(_anonymousCanView(self.dexterity_object))
+
+ # Fails: Anonymous should therefore have View permission on the
+ # comments.
+ self.assertTrue(_anonymousCanView(self.archetypes_comment))
+ self.assertTrue(_anonymousCanView(self.dexterity_comment))
+
+ def test_acquisition_chain(self):
+ """The acquisition chain for the comment should contain the same items
+ as that of the conversation.
+
+ Note that the list returned by aq_inner has the innermost object
+ first."""
+
+ # Fails: list index out of range
+ at_comment_chain = aq_chain(self.archetypes_comment)
+ at_conversation_chain = aq_chain(self.archetypes_conversation)
+ for (index, item) in enumerate(at_conversation_chain):
+ self.assertEqual(item, at_comment_chain[index + 1])
+
+ # Fails: list index out of range
+ dx_comment_chain = aq_chain(self.dexterity_comment)
+ dx_conversation_chain = aq_chain(self.dexterity_conversation)
+ for (index, item) in enumerate(dx_conversation_chain):
+ self.assertEqual(item, dx_comment_chain[index + 1])
+
+
+def test_suite():
+ return unittest.defaultTestLoader.loadTestsFromName(__name__)
Repository: plone.app.discussion
Branch: refs/heads/master
Date: 2013-11-21T17:23:36Z
Author: vmaksymiv (vmaksymiv) <vmaksymiv at quintagroup.com>
Commit: https://github.com/plone/plone.app.discussion/commit/8e56b99638f48170725a29425312ae6f3966aec8
added test acquisition chain for unwrapped object
Conflicts:
plone/app/discussion/tests/test_acquisition.py
Files changed:
M plone/app/discussion/tests/test_acquisition.py
diff --git a/plone/app/discussion/tests/test_acquisition.py b/plone/app/discussion/tests/test_acquisition.py
index 31d5f81..4f4d01b 100644
--- a/plone/app/discussion/tests/test_acquisition.py
+++ b/plone/app/discussion/tests/test_acquisition.py
@@ -1,5 +1,8 @@
# -*- coding: utf-8 -*-
-from Acquisition import aq_chain
+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
@@ -140,6 +143,25 @@ def test_acquisition_chain(self):
for (index, item) in enumerate(dx_conversation_chain):
self.assertEqual(item, dx_comment_chain[index + 1])
+ def test_acquisition_base_object_chain(self):
+ """ The acquisition chain for the object without wrappers should return
+ list which contains only the object.
+ """
+
+ at_object_base_chain = aq_chain(aq_base(self.archetypes_object))
+ dx_object_base_chain = aq_chain(aq_base(self.dexterity_object))
+
+ # Fails: acquisition chain has more than one object
+ self.assertTrue(len(at_object_base_chain) == 1)
+ self.assertTrue(len(dx_object_base_chain) == 1)
+
+ at_comment_base_chain = aq_chain(aq_base(self.archetypes_comment))
+ dx_comment_base_chain = aq_chain(aq_base(self.dexterity_comment))
+
+ # Fails: acquisition chain has more than one object
+ self.assertTrue(len(at_comment_base_chain) == 1)
+ self.assertTrue(len(dx_comment_base_chain) == 1)
+
def test_suite():
return unittest.defaultTestLoader.loadTestsFromName(__name__)
Repository: plone.app.discussion
Branch: refs/heads/master
Date: 2013-11-21T18:13:31Z
Author: Alan Hoey (evilbungle) <alan.hoey at gmail.com>
Commit: https://github.com/plone/plone.app.discussion/commit/4be4c03c49f478e7339d59b008fc21ba52576e9e
Fix total comments index
Files changed:
M plone/app/discussion/conversation.py
diff --git a/plone/app/discussion/conversation.py b/plone/app/discussion/conversation.py
index ba27c0f..16a3cec 100644
--- a/plone/app/discussion/conversation.py
+++ b/plone/app/discussion/conversation.py
@@ -50,8 +50,14 @@
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.
@@ -87,10 +93,10 @@ def enabled(self):
parent = aq_inner(self.__parent__)
return parent.restrictedTraverse('@@conversation_view').enabled()
- @property
+ @computed_attribute_decorator(level=1)
def total_comments(self):
public_comments = [
- x for x in self._comments.values()
+ x for x in self.values()
if user_nobody.has_permission('View', x)
]
return len(public_comments)
Repository: plone.app.discussion
Branch: refs/heads/master
Date: 2013-11-22T11:51:47Z
Author: Jess Henderson (jessnorwood) <jess at delib.net>
Commit: https://github.com/plone/plone.app.discussion/commit/7269dfca6568e9d5c8554aaac5228c957efc1c9a
Add dev.delib-da.1 to version pin
Files changed:
M setup.py
diff --git a/setup.py b/setup.py
index af74549..7a91fc2 100644
--- a/setup.py
+++ b/setup.py
@@ -1,6 +1,6 @@
from setuptools import setup, find_packages
-version = '2.3.0dev'
+version = '2.3.0dev.delib-da.1'
install_requires = [
'setuptools',
Repository: plone.app.discussion
Branch: refs/heads/master
Date: 2013-11-25T12:06:05Z
Author: Alan Hoey (evilbungle) <alan.hoey at gmail.com>
Commit: https://github.com/plone/plone.app.discussion/commit/4804868ab25a5c5c38f73fed25065ab6c849e6c3
Refactor acquisition tests to define the expected behaviour when dealing with wrapped and unwrapped comments.
Files changed:
M plone/app/discussion/tests/test_acquisition.py
diff --git a/plone/app/discussion/tests/test_acquisition.py b/plone/app/discussion/tests/test_acquisition.py
index 4f4d01b..72d68fe 100644
--- a/plone/app/discussion/tests/test_acquisition.py
+++ b/plone/app/discussion/tests/test_acquisition.py
@@ -2,7 +2,7 @@
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 Acquisition import aq_chain
from plone.app.discussion.testing import \
PLONE_APP_DISCUSSION_INTEGRATION_TESTING
from plone.app.discussion.interfaces import IConversation
@@ -19,23 +19,8 @@
one_state_workflow = 'one_state_workflow'
comment_workflow_acquired_view = 'comment_workflow_acquired_view'
-
-def _anonymousCanView(obj):
- """Use rolesOfPermission() to sees if Anonymous has View permission on an
- object"""
- roles_of_view_permission = obj.rolesOfPermission("View")
- # rolesOfPermission returns a list of dictionaries that have the key
- # 'name' for role.
- anon_views = [r for r in roles_of_view_permission
- if r['name'] == 'Anonymous']
- # only one entry per role should be present
- anon_view = anon_views[0]
- # if this role has the permission, 'selected' is set to 'SELECTED'
- return anon_view['selected'] == 'SELECTED'
-
-
-class DexterityAcquisitionTest(unittest.TestCase):
- """See test_view_permission."""
+class AcquisitionTest(unittest.TestCase):
+ """ Define the expected behaviour of wrapped and unwrapped comments. """
layer = PLONE_APP_DISCUSSION_INTEGRATION_TESTING
@@ -64,12 +49,14 @@ def setUp(self):
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
- comment1 = createObject('plone.Comment')
- dx_conversation.addComment(comment1)
- self.dexterity_comment = comment1
+ 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(
@@ -77,12 +64,15 @@ def setUp(self):
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
- comment2 = createObject('plone.Comment')
- at_conversation.addComment(comment2)
- self.archetypes_comment = comment2
+ 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.
@@ -103,64 +93,85 @@ def test_workflows_applied(self):
(one_state_workflow,)
)
self.assertEqual(
- self.wftool.getChainFor(self.archetypes_comment),
+ self.wftool.getChainFor(self.unwrapped_archetypes_comment),
(comment_workflow_acquired_view,)
)
self.assertEqual(
- self.wftool.getChainFor(self.dexterity_comment),
+ self.wftool.getChainFor(self.unwrapped_dexterity_comment),
(comment_workflow_acquired_view,)
)
- def test_view_permission(self):
- """Test that if the View permission on Discussion Items is acquired,
- Anonymous can view comments on published items."""
-
- # Anonymous has View permission on commented objects.
- self.assertTrue(_anonymousCanView(self.archetypes_object))
- self.assertTrue(_anonymousCanView(self.dexterity_object))
-
- # Fails: Anonymous should therefore have View permission on the
- # comments.
- self.assertTrue(_anonymousCanView(self.archetypes_comment))
- self.assertTrue(_anonymousCanView(self.dexterity_comment))
-
- def test_acquisition_chain(self):
- """The acquisition chain for the comment should contain the same items
- as that of the conversation.
-
- Note that the list returned by aq_inner has the innermost object
- first."""
-
- # Fails: list index out of range
- at_comment_chain = aq_chain(self.archetypes_comment)
- at_conversation_chain = aq_chain(self.archetypes_conversation)
- for (index, item) in enumerate(at_conversation_chain):
- self.assertEqual(item, at_comment_chain[index + 1])
-
- # Fails: list index out of range
- dx_comment_chain = aq_chain(self.dexterity_comment)
- dx_conversation_chain = aq_chain(self.dexterity_conversation)
- for (index, item) in enumerate(dx_conversation_chain):
- self.assertEqual(item, dx_comment_chain[index + 1])
-
- def test_acquisition_base_object_chain(self):
- """ The acquisition chain for the object without wrappers should return
- list which contains only the object.
- """
-
- at_object_base_chain = aq_chain(aq_base(self.archetypes_object))
- dx_object_base_chain = aq_chain(aq_base(self.dexterity_object))
-
- # Fails: acquisition chain has more than one object
- self.assertTrue(len(at_object_base_chain) == 1)
- self.assertTrue(len(dx_object_base_chain) == 1)
-
- at_comment_base_chain = aq_chain(aq_base(self.archetypes_comment))
- dx_comment_base_chain = aq_chain(aq_base(self.dexterity_comment))
-
- # Fails: acquisition chain has more than one object
- self.assertTrue(len(at_comment_base_chain) == 1)
- self.assertTrue(len(dx_comment_base_chain) == 1)
+ 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))
def test_suite():
Repository: plone.app.discussion
Branch: refs/heads/master
Date: 2013-11-25T12:52:31Z
Author: Alan Hoey (evilbungle) <alan.hoey at gmail.com>
Commit: https://github.com/plone/plone.app.discussion/commit/e6ec6ebe164b68f465fe87ea3dda8e7136411053
Add tests to confirm that some methods of a conversation incorrectly use an unwrapped comment to determine whether anonymous users can view.
Files changed:
M plone/app/discussion/tests/test_acquisition.py
diff --git a/plone/app/discussion/tests/test_acquisition.py b/plone/app/discussion/tests/test_acquisition.py
index 72d68fe..c6a96db 100644
--- a/plone/app/discussion/tests/test_acquisition.py
+++ b/plone/app/discussion/tests/test_acquisition.py
@@ -2,7 +2,7 @@
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
+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
@@ -173,6 +173,71 @@ def test_acquiring_comment_permissions_via_user_nobody(self):
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__)
Repository: plone.app.discussion
Branch: refs/heads/master
Date: 2013-11-25T12:54:49Z
Author: Alan Hoey (evilbungle) <alan.hoey at gmail.com>
Commit: https://github.com/plone/plone.app.discussion/commit/8161dc14dae8cd8b7c9d796066d302b8a4a153c9
Fixing test failures.
Files changed:
M plone/app/discussion/conversation.py
diff --git a/plone/app/discussion/conversation.py b/plone/app/discussion/conversation.py
index 16a3cec..ca22c05 100644
--- a/plone/app/discussion/conversation.py
+++ b/plone/app/discussion/conversation.py
@@ -101,13 +101,13 @@ def total_comments(self):
]
return len(public_comments)
- @property
+ @computed_attribute_decorator(level=1)
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._comments[comment_key]
+ comment = self[comment_key]
if user_nobody.has_permission('View', comment):
return comment.creation_date
return None
@@ -116,10 +116,10 @@ def last_comment_date(self):
def commentators(self):
return self._commentators
- @property
+ @computed_attribute_decorator(level=1)
def public_commentators(self):
retval = set()
- for comment in self._comments.values():
+ for comment in self.values():
if not user_nobody.has_permission('View', comment):
continue
retval.add(comment.author_username)
Repository: plone.app.discussion
Branch: refs/heads/master
Date: 2013-11-25T13:04:24Z
Author: Alan Hoey (evilbungle) <alan.hoey at gmail.com>
Commit: https://github.com/plone/plone.app.discussion/commit/1bf4dacc1d116e8c33691d01dbc1083fd004514a
Back out internal version number
Files changed:
M setup.py
diff --git a/setup.py b/setup.py
index 7a91fc2..af74549 100644
--- a/setup.py
+++ b/setup.py
@@ -1,6 +1,6 @@
from setuptools import setup, find_packages
-version = '2.3.0dev.delib-da.1'
+version = '2.3.0dev'
install_requires = [
'setuptools',
Repository: plone.app.discussion
Branch: refs/heads/master
Date: 2014-05-12T22:53:34+02:00
Author: Timo Stollenwerk (tisto) <tisto at plone.org>
Commit: https://github.com/plone/plone.app.discussion/commit/e18598e316e824795e752e7bb1c90ee37dc1cc6f
Merge pull request #38 from delib/evilbungle-comment-acquisition
Fixing incorrect behaviour with acquired permissions
Files changed:
A plone/app/discussion/tests/configure.zcml
A plone/app/discussion/tests/profile/types.xml
A plone/app/discussion/tests/profile/types/sample_content_type.xml
A plone/app/discussion/tests/profile/workflows.xml
A plone/app/discussion/tests/profile/workflows/comment_workflow_acquired_view/definition.xml
A plone/app/discussion/tests/test_acquisition.py
M plone/app/discussion/conversation.py
M plone/app/discussion/testing.py
diff --git a/plone/app/discussion/conversation.py b/plone/app/discussion/conversation.py
index ba27c0f..ca22c05 100644
--- a/plone/app/discussion/conversation.py
+++ b/plone/app/discussion/conversation.py
@@ -50,8 +50,14 @@
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.
@@ -87,21 +93,21 @@ def enabled(self):
parent = aq_inner(self.__parent__)
return parent.restrictedTraverse('@@conversation_view').enabled()
- @property
+ @computed_attribute_decorator(level=1)
def total_comments(self):
public_comments = [
- x for x in self._comments.values()
+ x for x in self.values()
if user_nobody.has_permission('View', x)
]
return len(public_comments)
- @property
+ @computed_attribute_decorator(level=1)
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._comments[comment_key]
+ comment = self[comment_key]
if user_nobody.has_permission('View', comment):
return comment.creation_date
return None
@@ -110,10 +116,10 @@ def last_comment_date(self):
def commentators(self):
return self._commentators
- @property
+ @computed_attribute_decorator(level=1)
def public_commentators(self):
retval = set()
- for comment in self._comments.values():
+ for comment in self.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 e7a397d..e47026f 100644
--- a/plone/app/discussion/testing.py
+++ b/plone/app/discussion/testing.py
@@ -41,10 +41,14 @@ def setUpZope(self, app, configurationContext):
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
new file mode 100644
index 0000000..ee79b95
--- /dev/null
+++ b/plone/app/discussion/tests/configure.zcml
@@ -0,0 +1,16 @@
+<configure
+ xmlns="http://namespaces.zope.org/zope"
+ xmlns:genericsetup="http://namespaces.zope.org/genericsetup"
+ i18n_domain="freitag.discussion">
+
+ <include package="plone.app.dexterity" />
+
+ <genericsetup:registerProfile
+ name="testing"
+ title="plone.app.discussion.testing"
+ directory="profile"
+ description="Testing profile for plone.app.discussion"
+ provides="Products.GenericSetup.interfaces.EXTENSION"
+ />
+
+</configure>
diff --git a/plone/app/discussion/tests/profile/types.xml b/plone/app/discussion/tests/profile/types.xml
new file mode 100644
index 0000000..0aec569
--- /dev/null
+++ b/plone/app/discussion/tests/profile/types.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0"?>
+<object name="portal_types" meta_type="Plone Types Tool">
+ <object name="sample_content_type" meta_type="Dexterity FTI" />
+</object>
diff --git a/plone/app/discussion/tests/profile/types/sample_content_type.xml b/plone/app/discussion/tests/profile/types/sample_content_type.xml
new file mode 100644
index 0000000..7869638
--- /dev/null
+++ b/plone/app/discussion/tests/profile/types/sample_content_type.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0"?>
+<object name="sample_content_type"
+ meta_type="Dexterity FTI"
+ i18n:domain="freitag.discussion" xmlns:i18n="http://xml.zope.org/namespaces/i18n">
+
+ <!-- Basic metadata -->
+ <property name="title" i18n:translate="">sample_content_type</property>
+ <property name="description"
+ i18n:translate="">Sample Content</property>
+ <property name="content_icon">document_icon.png</property>
+ <property name="global_allow">True</property>
+ <property name="filter_content_types">True</property>
+ <property name="allowed_content_types">
+ </property>
+ <property name="allow_discussion">True</property>
+
+ <property name="klass">plone.dexterity.content.Item</property>
+
+ <property name="add_permission">cmf.AddPortalContent</property>
+ <property name="behaviors">
+ <element value="plone.app.content.interfaces.INameFromTitle" />
+ </property>
+
+ <!-- View information -->
+ <property name="default_view">view</property>
+ <property name="default_view_fallback">False</property>
+ <property name="view_methods">
+ <element value="view" />
+ </property>
+
+ <!-- Method aliases -->
+ <alias from="(Default)" to="(selected layout)" />
+ <alias from="edit" to="@@edit" />
+ <alias from="sharing" to="@@sharing" />
+ <alias from="view" to="@@view" />
+
+ <!-- Actions -->
+ <action title="View" action_id="view" category="object" condition_expr=""
+ url_expr="string:${object_url}/" visible="True">
+ <permission value="View" />
+ </action>
+
+ <action title="Edit" action_id="edit" category="object" condition_expr=""
+ url_expr="string:${object_url}/edit" visible="True">
+ <permission value="Modify portal content" />
+ </action>
+</object>
diff --git a/plone/app/discussion/tests/profile/workflows.xml b/plone/app/discussion/tests/profile/workflows.xml
new file mode 100644
index 0000000..500f444
--- /dev/null
+++ b/plone/app/discussion/tests/profile/workflows.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0"?>
+<object name="portal_workflow" meta_type="CMF Workflow Tool">
+ <object name="comment_workflow_acquired_view" meta_type="Workflow"/>
+</object>
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
new file mode 100644
index 0000000..89a9fb2
--- /dev/null
+++ b/plone/app/discussion/tests/profile/workflows/comment_workflow_acquired_view/definition.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0"?>
+<dc-workflow
+ workflow_id="comment_workflow_acquired_view"
+ title="Single State Workflow"
+ description="- Essentially a workflow with no transitions, but has a Published state, so portlets and applications that expect that state will continue to work."
+ state_variable="review_state"
+ initial_state="published"
+ manager_bypass="False">
+ <permission>Access contents information</permission>
+ <permission>Change portal events</permission>
+ <permission>Modify portal content</permission>
+ <permission>View</permission>
+ <state state_id="published" title="Published">
+ <description>Visible to everyone, editable by the owner.</description>
+ <permission-map name="Access contents information" acquired="False">
+ <permission-role>Anonymous</permission-role>
+ </permission-map>
+ <permission-map name="Change portal events" acquired="False">
+ <permission-role>Editor</permission-role>
+ <permission-role>Manager</permission-role>
+ <permission-role>Owner</permission-role>
+ <permission-role>Site Administrator</permission-role>
+ </permission-map>
+ <permission-map name="Modify portal content" acquired="False">
+ <permission-role>Editor</permission-role>
+ <permission-role>Manager</permission-role>
+ <permission-role>Owner</permission-role>
+ <permission-role>Site Administrator</permission-role>
+ </permission-map>
+ <permission-map name="View" acquired="True">
+ </permission-map>
+ </state>
+ <variable variable_id="action" for_catalog="False" for_status="True" update_always="True">
+ <description>Previous transition</description>
+ <default>
+ <expression>transition/getId|nothing</expression>
+ </default>
+ <guard>
+ </guard>
+ </variable>
+ <variable variable_id="actor" for_catalog="False" for_status="True" update_always="True">
+ <description>The ID of the user who performed the previous transition</description>
+ <default>
+ <expression>user/getId</expression>
+ </default>
+ <guard>
+ </guard>
+ </variable>
+ <variable variable_id="comments" for_catalog="False" for_status="True" update_always="True">
+ <description>Comment about the last transition</description>
+ <default>
+ <expression>python:state_change.kwargs.get('comment', '')</expression>
+ </default>
+ <guard>
+ </guard>
+ </variable>
+ <variable variable_id="review_history" for_catalog="False" for_status="False" update_always="False">
+ <description>Provides access to workflow history</description>
+ <default>
+ <expression>state_change/getHistory</expression>
+ </default>
+ <guard>
+ <guard-permission>Request review</guard-permission>
+ <guard-permission>Review portal content</guard-permission>
+ </guard>
+ </variable>
+ <variable variable_id="time" for_catalog="False" for_status="True" update_always="True">
+ <description>When the previous transition was performed</description>
+ <default>
+ <expression>state_change/getDateTime</expression>
+ </default>
+ <guard>
+ </guard>
+ </variable>
+</dc-workflow>
diff --git a/plone/app/discussion/tests/test_acquisition.py b/plone/app/discussion/tests/test_acquisition.py
new file mode 100644
index 0000000..c6a96db
--- /dev/null
+++ b/plone/app/discussion/tests/test_acquisition.py
@@ -0,0 +1,243 @@
+# -*- 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__)
-------------------------------------------------------------------------------
-------------- next part --------------
A non-text attachment was scrubbed...
Name: CHANGES.log
Type: application/octet-stream
Size: 56055 bytes
Desc: not available
URL: <http://lists.plone.org/pipermail/plone-testbot/attachments/20140512/4f1715d7/attachment-0002.obj>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: build.log
Type: application/octet-stream
Size: 86229 bytes
Desc: not available
URL: <http://lists.plone.org/pipermail/plone-testbot/attachments/20140512/4f1715d7/attachment-0003.obj>
More information about the Testbot
mailing list