enabled property code added to conversation.
svn path=/plone.app.discussion/trunk/; revision=27380
This commit is contained in:
parent
0f7d05566a
commit
e465d73c95
@ -14,14 +14,18 @@ import time
|
||||
|
||||
from persistent import Persistent
|
||||
|
||||
from plone.registry.interfaces import IRegistry
|
||||
|
||||
from zope.app.component.hooks import getSite
|
||||
|
||||
from zope.interface import implements, implementer
|
||||
from zope.component import adapts, adapter
|
||||
from zope.component import adapts, adapter, queryUtility
|
||||
|
||||
from zope.annotation.interfaces import IAnnotations, IAnnotatable
|
||||
|
||||
from zope.event import notify
|
||||
|
||||
from Acquisition import aq_base
|
||||
from Acquisition import aq_base, aq_inner
|
||||
from Acquisition import Explicit
|
||||
|
||||
from OFS.Traversable import Traversable
|
||||
@ -29,6 +33,8 @@ from OFS.Traversable import Traversable
|
||||
from OFS.event import ObjectWillBeAddedEvent
|
||||
from OFS.event import ObjectWillBeRemovedEvent
|
||||
|
||||
from Products.CMFCore.utils import getToolByName
|
||||
|
||||
from zope.app.container.contained import ContainerModifiedEvent
|
||||
|
||||
from zope.app.container.contained import ObjectAddedEvent
|
||||
@ -44,7 +50,7 @@ except ImportError:
|
||||
from BTrees.OOBTree import OOBTree as LOBTree
|
||||
from BTrees.OOBTree import OOSet as LLSet
|
||||
|
||||
from plone.app.discussion.interfaces import IConversation, IReplies
|
||||
from plone.app.discussion.interfaces import IConversation, IReplies, IDiscussionSettings
|
||||
from plone.app.discussion.comment import Comment
|
||||
|
||||
ANNOTATION_KEY = 'plone.app.discussion:conversation'
|
||||
@ -80,7 +86,29 @@ class Conversation(Traversable, Persistent, Explicit):
|
||||
|
||||
@property
|
||||
def enabled(self):
|
||||
# TODO - check __parent__'s settings + global settings
|
||||
# Returns True if discussion is allowed
|
||||
|
||||
# Fetch discussion registry
|
||||
registry = queryUtility(IRegistry)
|
||||
settings = registry.for_interface(IDiscussionSettings)
|
||||
|
||||
# Check if discussion is allowed globally
|
||||
if not settings.globally_enabled:
|
||||
return False
|
||||
|
||||
# Check if discussion is allowed on the content type
|
||||
site = getSite()
|
||||
portal_types = getToolByName(site, 'portal_types')
|
||||
portal_type = self.__parent__.portal_type
|
||||
document_fti = getattr(portal_types, 'Document')
|
||||
if not document_fti.getProperty('allow_discussion'):
|
||||
# If discussion is not allowed on the content type,
|
||||
# check if 'allow discussion' is overridden on the content object.
|
||||
#if hasattr( aq_base(self.__parent__), 'allow_discussion' ):
|
||||
portal_discussion = getToolByName(site, 'portal_discussion')
|
||||
if not portal_discussion.isDiscussionAllowedFor(aq_inner(self.__parent__)):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
@property
|
||||
@ -104,7 +132,7 @@ class Conversation(Traversable, Persistent, Explicit):
|
||||
count = 0l
|
||||
for comment in self._comments.values(min=start):
|
||||
yield comment
|
||||
|
||||
|
||||
count += 1
|
||||
if size and count > size:
|
||||
return
|
||||
@ -112,11 +140,11 @@ class Conversation(Traversable, Persistent, Explicit):
|
||||
def getThreads(self, start=0, size=None, root=0, depth=None):
|
||||
"""Get threaded comments
|
||||
"""
|
||||
|
||||
|
||||
def recurse(comment_id, d=0):
|
||||
# Yield the current comment before we look for its children
|
||||
yield {'id': comment_id, 'comment': self._comments[comment_id], 'depth': d}
|
||||
|
||||
|
||||
# Recurse if there are children and we are not out of our depth
|
||||
if depth is None or d + 1 < depth:
|
||||
children = self._children.get(comment_id, None)
|
||||
@ -124,18 +152,18 @@ class Conversation(Traversable, Persistent, Explicit):
|
||||
for child_id in children:
|
||||
for value in recurse(child_id, d+1):
|
||||
yield value
|
||||
|
||||
|
||||
# Find top level threads
|
||||
comments = self._children.get(root, None)
|
||||
if comments is not None:
|
||||
count = 0l
|
||||
for comment_id in comments.keys(min=start):
|
||||
|
||||
|
||||
# Abort if we have found all the threads we want
|
||||
count += 1
|
||||
if size and count > size:
|
||||
return
|
||||
|
||||
|
||||
# Let the closure recurse
|
||||
for value in recurse(comment_id):
|
||||
yield value
|
||||
@ -211,14 +239,14 @@ class Conversation(Traversable, Persistent, Explicit):
|
||||
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)
|
||||
|
||||
|
||||
# Remove this comment as a child of its parent
|
||||
if not suppress_container_modified:
|
||||
parent = comment.in_reply_to
|
||||
@ -226,16 +254,16 @@ class Conversation(Traversable, Persistent, Explicit):
|
||||
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(ObjectRemovedEvent(comment, self, key))
|
||||
|
||||
|
||||
if not suppress_container_modified:
|
||||
notify(ContainerModifiedEvent(self))
|
||||
|
||||
@ -279,6 +307,7 @@ def conversationAdapterFactory(content):
|
||||
conversation = Conversation()
|
||||
annotions[ANNOTATION_KEY] = conversation
|
||||
conversation = annotions[ANNOTATION_KEY]
|
||||
conversation.__parent__ = aq_base(content)
|
||||
return conversation
|
||||
|
||||
class ConversationReplies(object):
|
||||
@ -350,13 +379,13 @@ class ConversationReplies(object):
|
||||
def iteritems(self):
|
||||
for key in self.children:
|
||||
yield (key, self.conversation[key],)
|
||||
|
||||
|
||||
@property
|
||||
def children(self):
|
||||
# we need to look this up every time, because we may not have a
|
||||
# dict yet when the adapter is first created
|
||||
return self.conversation._children.get(self.comment_id, LLSet())
|
||||
|
||||
|
||||
class CommentReplies(ConversationReplies):
|
||||
"""An IReplies adapter for comments.
|
||||
|
||||
|
@ -1,14 +1,14 @@
|
||||
import unittest
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from plone.registry import Registry
|
||||
|
||||
from zope.component import createObject
|
||||
from zope.component import createObject, queryUtility
|
||||
|
||||
from Acquisition import aq_base, aq_parent, aq_inner
|
||||
|
||||
from plone.app.vocabularies.types import BAD_TYPES
|
||||
|
||||
from plone.registry.interfaces import IRegistry
|
||||
|
||||
from Products.CMFCore.utils import getToolByName
|
||||
from Products.PloneTestCase.ptc import PloneTestCase
|
||||
from plone.app.discussion.tests.layer import DiscussionLayer
|
||||
@ -155,6 +155,82 @@ class ConversationTest(PloneTestCase):
|
||||
{'comment': comment2_1, 'depth': 1, 'id': new_id_2_1},
|
||||
], list(conversation.getThreads()))
|
||||
|
||||
def test_disable_commenting_globally(self):
|
||||
|
||||
# Create a conversation.
|
||||
conversation = IConversation(self.portal.doc1)
|
||||
|
||||
# We have to allow discussion on Document content type, since
|
||||
# otherwise allow_discussion will always return False
|
||||
portal_types = getToolByName(self.portal, 'portal_types')
|
||||
document_fti = getattr(portal_types, 'Document')
|
||||
document_fti.manage_changeProperties(allow_discussion = True)
|
||||
|
||||
# Check if conversation is enabled now
|
||||
self.assertEquals(conversation.enabled, True)
|
||||
|
||||
# Disable commenting in the registry
|
||||
registry = queryUtility(IRegistry)
|
||||
settings = registry.for_interface(IDiscussionSettings)
|
||||
settings.globally_enabled = False
|
||||
|
||||
# Check if commenting is disabled on the conversation
|
||||
self.assertEquals(conversation.enabled, False)
|
||||
|
||||
# Enable discussion again
|
||||
settings.globally_enabled = True
|
||||
self.assertEquals(conversation.enabled, True)
|
||||
|
||||
|
||||
def test_disable_commenting_for_content_type(self):
|
||||
|
||||
# Create a conversation.
|
||||
conversation = IConversation(self.portal.doc1)
|
||||
|
||||
# The Document content type is disabled by default
|
||||
self.assertEquals(conversation.enabled, False)
|
||||
|
||||
# Allow discussion on Document content type
|
||||
portal_types = getToolByName(self.portal, 'portal_types')
|
||||
document_fti = getattr(portal_types, 'Document')
|
||||
document_fti.manage_changeProperties(allow_discussion = True)
|
||||
|
||||
# Check if conversation is enabled now
|
||||
self.assertEquals(conversation.enabled, True)
|
||||
|
||||
# Disallow discussion on Document content type
|
||||
portal_types = getToolByName(self.portal, 'portal_types')
|
||||
document_fti = getattr(portal_types, 'Document')
|
||||
document_fti.manage_changeProperties(allow_discussion = False)
|
||||
|
||||
# Check if conversation is enabled now
|
||||
self.assertEquals(conversation.enabled, False)
|
||||
|
||||
def test_is_discussion_allowed_for_folder(self):
|
||||
# Create a folder with two content objects. Change allow_discussion
|
||||
# and check if the content objects inside the folder are commentable.
|
||||
pass
|
||||
|
||||
def test_is_discussion_allowed_on_content_object(self):
|
||||
# Allow discussion on a single content object
|
||||
|
||||
registry = queryUtility(IRegistry)
|
||||
settings = registry.for_interface(IDiscussionSettings)
|
||||
|
||||
# Create a conversation.
|
||||
conversation = IConversation(self.portal.doc1)
|
||||
|
||||
# Discussion is disallowed by default
|
||||
self.assertEquals(conversation.enabled, False)
|
||||
|
||||
# Allow discussion on content object
|
||||
self.portal_discussion.overrideDiscussionFor(self.portal.doc1, True)
|
||||
|
||||
# Check if discussion is now allowed on the content object
|
||||
self.assertEquals(conversation.enabled, True)
|
||||
|
||||
self.portal_discussion.overrideDiscussionFor(self.portal.doc1, False)
|
||||
self.assertEquals(conversation.enabled, False)
|
||||
|
||||
def test_dict_operations(self):
|
||||
# test dict operations and acquisition wrapping
|
||||
@ -459,6 +535,16 @@ class ConversationTest(PloneTestCase):
|
||||
self.assertEquals(('', 'plone', 'doc1', '++conversation++default'), conversation.getPhysicalPath())
|
||||
self.assertEquals('plone/doc1/%2B%2Bconversation%2B%2Bdefault', conversation.absolute_url())
|
||||
|
||||
def test_parent(self):
|
||||
# Check that conversation has a content object as parent
|
||||
|
||||
# Create a conversation.
|
||||
conversation = IConversation(self.portal.doc1)
|
||||
|
||||
# Check the parent
|
||||
self.failUnless(conversation.__parent__)
|
||||
self.assertEquals(conversation.__parent__.getId(), 'doc1')
|
||||
|
||||
def test_discussion_item_not_in_bad_types(self):
|
||||
self.failIf('Discussion Item' in BAD_TYPES)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user