Some more design notes/sketchings. Nothing that works yet.
svn path=/plone.app.discussion/trunk/; revision=26918
This commit is contained in:
parent
32f79c7b3c
commit
3b2db86d54
@ -1,5 +1,5 @@
|
||||
"""Implement an IPublishTraverse adapter for discussion items that allows
|
||||
traversal into the 'replies' dictionary, as well as the ++comments++ traversal
|
||||
namespace.
|
||||
"""Implement the ++comments++ traversal namespace. This should return the
|
||||
IDiscussion container for the context, from which traversal will continue
|
||||
into an actual comment object.
|
||||
"""
|
||||
|
||||
|
@ -1,26 +1,15 @@
|
||||
"""Discussion items and replies
|
||||
"""The default comment class and factory.
|
||||
"""
|
||||
|
||||
from zope.interface import implements, alsoProvides
|
||||
|
||||
from BTrees.OOBTree import OOBTree
|
||||
from zope.interface import implements
|
||||
from zope.component.factory import Factory
|
||||
|
||||
from Acquisition import Explicit
|
||||
from OFS.Traversable import Traversable
|
||||
from AccessControl.Role import RoleManager
|
||||
from AccessControl.Owned import Owned
|
||||
|
||||
from plone.app.discussion.interfaces import IReplies, IComment
|
||||
|
||||
def Replies():
|
||||
"""Create a new replies object. Acts like a constructor, but actually
|
||||
returns a BTree marked with an interface. We do this because subclassing
|
||||
an OOBTree does not work properly.
|
||||
"""
|
||||
|
||||
replies = OOBTree()
|
||||
alsoProvides(replies, IReplies)
|
||||
return replies
|
||||
from plone.app.discussion.interfaces import IComment
|
||||
|
||||
class Comment(Explicit, Traversable, RoleManager, Owned):
|
||||
"""A comment.
|
||||
@ -36,11 +25,10 @@ class Comment(Explicit, Traversable, RoleManager, Owned):
|
||||
|
||||
__parent__ = None
|
||||
__name__ = None
|
||||
ancestor = None
|
||||
|
||||
title = u""
|
||||
mime_type = "text/plain"
|
||||
|
||||
mime_type = "text/plain"
|
||||
text = u""
|
||||
|
||||
creator = None
|
||||
@ -52,23 +40,32 @@ class Comment(Explicit, Traversable, RoleManager, Owned):
|
||||
author_name = None
|
||||
author_email = None
|
||||
|
||||
replies = None
|
||||
|
||||
def __init__(self, id, ancestor, parent, **kw):
|
||||
self.__name__ = id
|
||||
self.__parent__ = parent
|
||||
self.ancestor = ancestor
|
||||
def __init__(self, id=None, conversation=None, **kw):
|
||||
self.__name__ = unicode(id)
|
||||
self.__parent__ = conversation
|
||||
|
||||
for k, v in kw:
|
||||
setattr(self, k, v)
|
||||
|
||||
replies = Replies()
|
||||
|
||||
# convenience functions
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return self.__name__
|
||||
return str(self.__name__)
|
||||
|
||||
def getId(self):
|
||||
return self.__name__
|
||||
"""The id of the comment, as a string
|
||||
"""
|
||||
return self.id
|
||||
|
||||
def Title(self):
|
||||
"""The title of the comment
|
||||
"""
|
||||
return self.title
|
||||
|
||||
def Creator(self):
|
||||
"""The name of the person who wrote the comment
|
||||
"""
|
||||
return self.creator
|
||||
|
||||
CommentFactory = Factory(Comment)
|
@ -1,6 +1,12 @@
|
||||
<configure
|
||||
xmlns="http://namespaces.zope.org/zope"
|
||||
i18n_domain="plone.app.discussion">
|
||||
<configure xmlns="http://namespaces.zope.org/zope" i18n_domain="plone.app.discussion">
|
||||
|
||||
<include file="permissions.zcml" />
|
||||
|
||||
<utility object=".comment.CommentFactory" name="Discussion Item" />
|
||||
|
||||
<class class=".comment.Comment">
|
||||
<require interface=".comment.Comment" permission="zope2.View" />
|
||||
<require attributes="Title Creator getId" permission="zope2.View" />
|
||||
</class>
|
||||
|
||||
</configure>
|
||||
|
95
plone/app/discussion/conversation.py
Normal file
95
plone/app/discussion/conversation.py
Normal file
@ -0,0 +1,95 @@
|
||||
"""The conversation and replies adapters
|
||||
|
||||
The conversation is responsible for storing all comments. It provides a
|
||||
dict-like API for accessing comments, where keys are integers and values
|
||||
are IComment objects. It also provides features for finding comments quickly.
|
||||
|
||||
The two IReplies adapters - one for the IConversation and one for IComment -
|
||||
manipulate the same data structures, but provide an API for finding and
|
||||
manipulating the comments directly in reply to a particular comment or at the
|
||||
top level of the conversation.
|
||||
"""
|
||||
|
||||
from zope.interface import implements
|
||||
from zope.component import adapts
|
||||
|
||||
from BTrees.OIBTree import OIBTree
|
||||
from BTrees.IOBTree import IOBTree
|
||||
from BTrees.IIBTree import IIBTree, IISet
|
||||
|
||||
from Acquisition import Explicit
|
||||
|
||||
from plone.app.discussion.interfaces import IConversation, IComment, IReplies
|
||||
|
||||
class Conversation(Explicit):
|
||||
"""A conversation is a container for all comments on a content object.
|
||||
"""
|
||||
|
||||
implements(IConversation)
|
||||
|
||||
def __init__(self, id="++comments++"):
|
||||
self.id = id
|
||||
|
||||
# username -> count of comments; key is removed when count reaches 0
|
||||
self._commentators = OIBTree()
|
||||
self._last_comment_date = None
|
||||
|
||||
# id -> comment - find comment by id
|
||||
self._comments = IOBTree()
|
||||
|
||||
# # id -> IISet (children) - find all children for a given comment. 0 signifies root.
|
||||
self._children = IOBTree()
|
||||
|
||||
# id -> id (parent) - find the parent for a given comment. 0 signifies root
|
||||
self._parents = IIBTree()
|
||||
|
||||
def getId(self):
|
||||
"""
|
||||
"""
|
||||
return self.id
|
||||
|
||||
@property
|
||||
def enabled(self):
|
||||
# TODO
|
||||
return True
|
||||
|
||||
@property
|
||||
def total_comments(self):
|
||||
# TODO
|
||||
return 0
|
||||
|
||||
@property
|
||||
def last_comment_date(self):
|
||||
# TODO
|
||||
return None
|
||||
|
||||
@property
|
||||
def commentators(self):
|
||||
# TODO:
|
||||
return set()
|
||||
|
||||
def getComments(start=0, size=None):
|
||||
# TODO
|
||||
pass
|
||||
|
||||
def getThreads(start=0, size=None, root=None, depth=None):
|
||||
# TODO
|
||||
pass
|
||||
|
||||
# Dict API
|
||||
|
||||
# TODO: Update internal data structures when items added or removed
|
||||
|
||||
class ConversationReplies(object):
|
||||
"""
|
||||
"""
|
||||
|
||||
implements(IReplies)
|
||||
adapts(Conversation)
|
||||
|
||||
class CommentReplies(object):
|
||||
"""
|
||||
"""
|
||||
|
||||
implements(IReplies)
|
||||
adapts(IComment)
|
@ -1,3 +0,0 @@
|
||||
"""Default implementation of the IDiscussable adapter.
|
||||
"""
|
||||
|
@ -6,29 +6,95 @@ from zope.i18nmessageid import MessageFactory
|
||||
|
||||
_ = MessageFactory('plone.app.discussion')
|
||||
|
||||
class IDiscussionSettings(Interface):
|
||||
"""Global discussion settings. This describes records stored in the
|
||||
configuration registry and obtainable via plone.registry.
|
||||
"""
|
||||
|
||||
globally_enabled = schema.Bool(title=_(u"Globally enabled"),
|
||||
description=_(u"Use this setting to enable or disable comments globally"),
|
||||
default=True)
|
||||
|
||||
index_comments = schema.Bool(title=_(u"Index comments"),
|
||||
description=_(u"Enable this option to ensure that comments are searchable. "
|
||||
"Turning this off may improve performance for sites with large "
|
||||
"volumes of comments that do not wish to make them searcahble using "
|
||||
"the standard search tools."),
|
||||
default=True)
|
||||
|
||||
class IConversation(IIterableMapping, IWriteMapping):
|
||||
"""A conversation about a content object.
|
||||
|
||||
This is a persistent object in its own right and manages all comments.
|
||||
|
||||
The dict interface allows access to all comments. They are stored by
|
||||
integer key, in the order they were added.
|
||||
|
||||
To get replies at the top level, adapt the conversation to IReplies.
|
||||
|
||||
The conversation can be traversed to via the ++comments++ namespace.
|
||||
For example, path/to/object/++comments++/123 retrieves comment 123.
|
||||
|
||||
The __parent__ of the conversation (and the acquisition parent during
|
||||
traversal) is the content object. The conversation is the __parent__
|
||||
(and acquisition parent) for all comments, regardless of threading.
|
||||
"""
|
||||
|
||||
enabled = schema.Bool(title=_(u"Is commenting enabled?"))
|
||||
|
||||
total_comments = schema.Int(title=_(u"Total number of comments on this item"), min=0, readonly=True)
|
||||
last_comment_date = schema.Date(title=_(u"Date of the most recent comment"), readonly=True)
|
||||
commentators = schema.Set(title=_(u"The set of unique commentators (usernames)"), readonly=True)
|
||||
|
||||
def getComments(start=0, size=None):
|
||||
"""Return a batch of comment objects for rendering. The 'start'
|
||||
parameter is the id of the comment from which to start the batch.
|
||||
The 'size' parameter is the number of comments to return in the
|
||||
batch.
|
||||
|
||||
The comments are returned in creation date order, in the exact batche
|
||||
size specified.
|
||||
"""
|
||||
|
||||
def getThreads(start=0, size=None, root=None, depth=None):
|
||||
"""Return a batch of comment objects for rendering. The 'start'
|
||||
parameter is the id of the comment from which to start the batch.
|
||||
The 'size' parameter is the number of comments to return in the
|
||||
batch. 'root', if given, is the id of the comment to which reply
|
||||
threads will be found. If not given, all threads are returned.
|
||||
If 'depth' is given, it can be used to limit the depth of threads
|
||||
returned. For example, depth=1 will return only direct replies.
|
||||
|
||||
Comments are returned as a recursive list of '(comment, children)',
|
||||
where 'children' is a similar list of (comment, children), or an empty
|
||||
list of a comment has no direct replies.
|
||||
|
||||
The returned number of comments may be bigger than the batch size,
|
||||
in order to give enough context to show the full lineage of the
|
||||
starting comment.
|
||||
"""
|
||||
|
||||
class IReplies(IIterableMapping, IWriteMapping):
|
||||
"""A set of related comments
|
||||
"""A set of related comments in reply to a given content object or
|
||||
another comment.
|
||||
|
||||
This acts as a mapping (dict) with string keys and values being other
|
||||
discussion items in reply to this discussion item.
|
||||
Adapt a conversation or another comment to this interface to obtain the
|
||||
direct replies.
|
||||
"""
|
||||
|
||||
class IHasReplies(Interface):
|
||||
"""Common interface for objects that have replies.
|
||||
"""
|
||||
class IComment(Interface):
|
||||
"""A comment.
|
||||
|
||||
replies = schema.Object(title=_(u"Replies"), schema=IReplies)
|
||||
|
||||
class IComment(IHasReplies):
|
||||
"""A comment
|
||||
Comments are indexed in the catalog and subject to workflow and security.
|
||||
"""
|
||||
|
||||
portal_type = schema.ASCIILine(title=_(u"Portal type"), default="Discussion Item")
|
||||
|
||||
__parent__ = schema.Object(title=_(u"In reply to"), description=_(u"Another comment or a content item"), schema=Interface)
|
||||
__parent__ = schema.Object(title=_(u"Conversation"), schema=Interface)
|
||||
__name__ = schema.TextLine(title=_(u"Name"))
|
||||
|
||||
ancestor = schema.Object(title=_(u"The original content object the comment is for"), schema=Interface)
|
||||
comment_id = schema.Int(title=_(u"A comment id unique to this conversation"))
|
||||
in_reply_to = schema.Int(title=_(u"Id of comment this comment is in reply to"), required=False)
|
||||
|
||||
title = schema.TextLine(title=_(u"Subject"))
|
||||
|
||||
@ -45,24 +111,3 @@ class IComment(IHasReplies):
|
||||
# for anonymous comments only, set to None for logged in comments
|
||||
author_name = schema.TextLine(title=_(u"Author name"), required=False)
|
||||
author_email = schema.TextLine(title=_(u"Author email address"), required=False)
|
||||
|
||||
class IDiscussable(IHasReplies):
|
||||
"""Adapt a content item to this interface to determine whether discussions
|
||||
are currently enabled, and obtain a list of comments.
|
||||
"""
|
||||
|
||||
enabled = schema.Bool(title=_(u"Is commenting enabled?"))
|
||||
|
||||
total_comments = schema.Int(title=_(u"Total number of comments on this item"), min=0, readonly=True)
|
||||
last_comment_date = schema.Date(title=_(u"Date of the most recent comment"), readonly=True)
|
||||
commentators = schema.Set(title=_(u"The set of unique commentators (usernames)"), readonly=True)
|
||||
|
||||
class IDiscussionSettings(Interface):
|
||||
"""Global discussion settings. This describes records stored in the
|
||||
configuration registry and obtainable via plone.registry.
|
||||
"""
|
||||
|
||||
globally_enabled = schema.Bool(title=_(u"Globally enabled"),
|
||||
description=_(u"Use this setting to enable or disable comments globally"),
|
||||
default=True)
|
||||
|
||||
|
9
plone/app/discussion/permissions.zcml
Normal file
9
plone/app/discussion/permissions.zcml
Normal file
@ -0,0 +1,9 @@
|
||||
<configure
|
||||
xmlns="http://namespaces.zope.org/zope"
|
||||
i18n_domain="plone.app.discussion">
|
||||
|
||||
<include package="collective.autopermission" />
|
||||
|
||||
<!-- custom permissions are defined here -->
|
||||
|
||||
</configure>
|
Loading…
Reference in New Issue
Block a user