Added basic infrastructure for discussion controlpanel.
svn path=/plone.app.discussion/trunk/; revision=27269
This commit is contained in:
parent
47911fa24a
commit
f27156e916
@ -3,6 +3,8 @@
|
|||||||
xmlns:browser="http://namespaces.zope.org/browser"
|
xmlns:browser="http://namespaces.zope.org/browser"
|
||||||
i18n_domain="plone.app.discussion">
|
i18n_domain="plone.app.discussion">
|
||||||
|
|
||||||
|
<include package="plone.app.registry" />
|
||||||
|
|
||||||
<!-- Traversal adapter -->
|
<!-- Traversal adapter -->
|
||||||
<adapter factory=".traversal.ConversationNamespace" name="conversation" />
|
<adapter factory=".traversal.ConversationNamespace" name="conversation" />
|
||||||
|
|
||||||
@ -66,4 +68,20 @@
|
|||||||
layer="..interfaces.IDiscussionLayer"
|
layer="..interfaces.IDiscussionLayer"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<browser:page
|
||||||
|
name="discussion-settings"
|
||||||
|
for="Products.CMFPlone.interfaces.IPloneSiteRoot"
|
||||||
|
class=".controlpanel.DiscussionSettingsControlPanel"
|
||||||
|
permission="cmf.ManagePortal"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- Utility view - use in portal_css or similar as portal/@@xdv-check/enabled" -->
|
||||||
|
<browser:page
|
||||||
|
name="discussion-check"
|
||||||
|
for="Products.CMFPlone.interfaces.IPloneSiteRoot"
|
||||||
|
class=".controlpanel.Utility"
|
||||||
|
permission="zope.Public"
|
||||||
|
allowed_attributes="globally_enabled"
|
||||||
|
/>
|
||||||
|
|
||||||
</configure>
|
</configure>
|
||||||
|
@ -0,0 +1,57 @@
|
|||||||
|
from Products.Five.browser import BrowserView
|
||||||
|
|
||||||
|
from zope.component import queryUtility
|
||||||
|
from plone.registry.interfaces import IRegistry
|
||||||
|
|
||||||
|
from plone.app.registry.browser import controlpanel
|
||||||
|
|
||||||
|
from plone.app.discussion.interfaces import IDiscussionSettings, _
|
||||||
|
|
||||||
|
try:
|
||||||
|
# only in z3c.form 2.0
|
||||||
|
from z3c.form.browser.textlines import TextLinesFieldWidget
|
||||||
|
from z3c.form.browser.widget import SingleCheckBoxWidget
|
||||||
|
except ImportError:
|
||||||
|
from plone.z3cform.textlines import TextLinesFieldWidget
|
||||||
|
from plone.z3cform.widget import SingleCheckBoxWidget
|
||||||
|
|
||||||
|
class DiscussionSettingsEditForm(controlpanel.RegistryEditForm):
|
||||||
|
|
||||||
|
schema = IDiscussionSettings
|
||||||
|
label = _(u"Discussion settings")
|
||||||
|
description = _(u"Please enter the options specified")
|
||||||
|
|
||||||
|
def updateFields(self):
|
||||||
|
super(DiscussionSettingsEditForm, self).updateFields()
|
||||||
|
#self.fields['globally_enabled'].widgetFactory = SingleCheckBoxWidget
|
||||||
|
|
||||||
|
def updateWidgets(self):
|
||||||
|
super(DiscussionSettingsEditForm, self).updateWidgets()
|
||||||
|
|
||||||
|
class DiscussionSettingsControlPanel(controlpanel.ControlPanelFormWrapper):
|
||||||
|
form = DiscussionSettingsEditForm
|
||||||
|
|
||||||
|
class Utility(BrowserView):
|
||||||
|
"""Utility view to determine if the site is currently styled with xdv
|
||||||
|
"""
|
||||||
|
|
||||||
|
def globally_enabled(self):
|
||||||
|
"""Determine if the utility is enabled and we are in an enabled domain
|
||||||
|
"""
|
||||||
|
|
||||||
|
registry = queryUtility(IRegistry)
|
||||||
|
if registry is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
settings = None
|
||||||
|
try:
|
||||||
|
settings = registry.for_interface(IDiscussionSettings)
|
||||||
|
except KeyError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not settings.globally_enabled:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
@ -11,80 +11,80 @@ class IDiscussionSettings(Interface):
|
|||||||
configuration registry and obtainable via plone.registry.
|
configuration registry and obtainable via plone.registry.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
globally_enabled = schema.Bool(title=_(u"Globally enabled"),
|
#globally_enabled = schema.Bool(title=_(u"Globally enabled"),
|
||||||
description=_(u"Use this setting to enable or disable comments globally"),
|
# description=_(u"Use this setting to enable or disable comments globally"),
|
||||||
default=True)
|
# default=True)
|
||||||
|
|
||||||
class IConversation(IIterableMapping):
|
class IConversation(IIterableMapping):
|
||||||
"""A conversation about a content object.
|
"""A conversation about a content object.
|
||||||
|
|
||||||
This is a persistent object in its own right and manages all comments.
|
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
|
The dict interface allows access to all comments. They are stored by
|
||||||
long integer key, in the order they were added.
|
long integer key, in the order they were added.
|
||||||
|
|
||||||
Note that __setitem__() is not supported - use addComment() instead.
|
Note that __setitem__() is not supported - use addComment() instead.
|
||||||
However, comments can be deleted using __delitem__().
|
However, comments can be deleted using __delitem__().
|
||||||
|
|
||||||
To get replies at the top level, adapt the conversation to IReplies.
|
To get replies at the top level, adapt the conversation to IReplies.
|
||||||
|
|
||||||
The conversation can be traversed to via the ++comments++ namespace.
|
The conversation can be traversed to via the ++comments++ namespace.
|
||||||
For example, path/to/object/++comments++/123 retrieves comment 123.
|
For example, path/to/object/++comments++/123 retrieves comment 123.
|
||||||
|
|
||||||
The __parent__ of the conversation (and the acquisition parent during
|
The __parent__ of the conversation (and the acquisition parent during
|
||||||
traversal) is the content object. The conversation is the __parent__
|
traversal) is the content object. The conversation is the __parent__
|
||||||
(and acquisition parent) for all comments, regardless of threading.
|
(and acquisition parent) for all comments, regardless of threading.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
enabled = schema.Bool(title=_(u"Is commenting enabled?"))
|
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)
|
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)
|
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)
|
commentators = schema.Set(title=_(u"The set of unique commentators (usernames)"), readonly=True)
|
||||||
|
|
||||||
def addComment(comment):
|
def addComment(comment):
|
||||||
"""Adds a new comment to the list of comments, and returns the
|
"""Adds a new comment to the list of comments, and returns the
|
||||||
comment id that was assigned. The comment_id property on the comment
|
comment id that was assigned. The comment_id property on the comment
|
||||||
will be set accordingly.
|
will be set accordingly.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __delitem__(key):
|
def __delitem__(key):
|
||||||
"""Delete the comment with the given key. The key is a long id.
|
"""Delete the comment with the given key. The key is a long id.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def getComments(start=0, size=None):
|
def getComments(start=0, size=None):
|
||||||
"""Return an iterator of comment objects for rendering.
|
"""Return an iterator of comment objects for rendering.
|
||||||
|
|
||||||
The 'start' parameter is the id of the comment from which to start the
|
The 'start' parameter is the id of the comment from which to start the
|
||||||
batch. If no such comment exists, the next higher id will be used.
|
batch. If no such comment exists, the next higher id will be used.
|
||||||
This means that you can use max key from a previous batch + 1 safely.
|
This means that you can use max key from a previous batch + 1 safely.
|
||||||
|
|
||||||
The 'size' parameter is the number of comments to return in the
|
The 'size' parameter is the number of comments to return in the
|
||||||
batch.
|
batch.
|
||||||
|
|
||||||
The comments are returned in creation date order, in the exact batch
|
The comments are returned in creation date order, in the exact batch
|
||||||
size specified.
|
size specified.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def getThreads(start=0, size=None, root=0, depth=None):
|
def getThreads(start=0, size=None, root=0, depth=None):
|
||||||
"""Return a batch of comment objects for rendering.
|
"""Return a batch of comment objects for rendering.
|
||||||
|
|
||||||
The 'start' parameter is the id of the comment from which to start
|
The 'start' parameter is the id of the comment from which to start
|
||||||
the batch. If no such comment exists, the next higher id will be used.
|
the batch. If no such comment exists, the next higher id will be used.
|
||||||
This means that you can use max key from a previous batch + 1 safely.
|
This means that you can use max key from a previous batch + 1 safely.
|
||||||
This should be a root level comment.
|
This should be a root level comment.
|
||||||
|
|
||||||
The 'size' parameter is the number of threads to return in the
|
The 'size' parameter is the number of threads to return in the
|
||||||
batch. Full threads are always returned (although you can stop
|
batch. Full threads are always returned (although you can stop
|
||||||
consuming the iterator if you want to abort).
|
consuming the iterator if you want to abort).
|
||||||
|
|
||||||
'root', if given, is the id of the comment to which reply
|
'root', if given, is the id of the comment to which reply
|
||||||
threads will be found. 'root' itself is not included. If not given,
|
threads will be found. 'root' itself is not included. If not given,
|
||||||
all threads are returned.
|
all threads are returned.
|
||||||
|
|
||||||
If 'depth' is given, it can be used to limit the depth of threads
|
If 'depth' is given, it can be used to limit the depth of threads
|
||||||
returned. For example, depth=1 will return only direct replies.
|
returned. For example, depth=1 will return only direct replies.
|
||||||
|
|
||||||
Comments are returned as an iterator of dicts with keys 'comment',
|
Comments are returned as an iterator of dicts with keys 'comment',
|
||||||
the comment, 'id', the comment id, and 'depth', which is 0 for
|
the comment, 'id', the comment id, and 'depth', which is 0 for
|
||||||
top-level comments, 1 for their replies, and so on. The list is
|
top-level comments, 1 for their replies, and so on. The list is
|
||||||
@ -94,78 +94,78 @@ class IConversation(IIterableMapping):
|
|||||||
class IReplies(IIterableMapping):
|
class IReplies(IIterableMapping):
|
||||||
"""A set of related comments in reply to a given content object or
|
"""A set of related comments in reply to a given content object or
|
||||||
another comment.
|
another comment.
|
||||||
|
|
||||||
Adapt a conversation or another comment to this interface to obtain the
|
Adapt a conversation or another comment to this interface to obtain the
|
||||||
direct replies.
|
direct replies.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def addComment(comment):
|
def addComment(comment):
|
||||||
"""Adds a new comment as a child of this comment, and returns the
|
"""Adds a new comment as a child of this comment, and returns the
|
||||||
comment id that was assigned. The comment_id property on the comment
|
comment id that was assigned. The comment_id property on the comment
|
||||||
will be set accordingly.
|
will be set accordingly.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __delitem__(key):
|
def __delitem__(key):
|
||||||
"""Delete the comment with the given key. The key is a long id.
|
"""Delete the comment with the given key. The key is a long id.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class IComment(Interface):
|
class IComment(Interface):
|
||||||
"""A comment.
|
"""A comment.
|
||||||
|
|
||||||
Comments are indexed in the catalog and subject to workflow and security.
|
Comments are indexed in the catalog and subject to workflow and security.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
portal_type = schema.ASCIILine(title=_(u"Portal type"), default="Discussion Item")
|
portal_type = schema.ASCIILine(title=_(u"Portal type"), default="Discussion Item")
|
||||||
|
|
||||||
__parent__ = schema.Object(title=_(u"Conversation"), schema=Interface)
|
__parent__ = schema.Object(title=_(u"Conversation"), schema=Interface)
|
||||||
__name__ = schema.TextLine(title=_(u"Name"))
|
__name__ = schema.TextLine(title=_(u"Name"))
|
||||||
|
|
||||||
comment_id = schema.Int(title=_(u"A comment id unique to this conversation"))
|
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)
|
in_reply_to = schema.Int(title=_(u"Id of comment this comment is in reply to"), required=False)
|
||||||
|
|
||||||
title = schema.TextLine(title=_(u"Subject"))
|
title = schema.TextLine(title=_(u"Subject"))
|
||||||
|
|
||||||
mime_type = schema.ASCIILine(title=_(u"MIME type"), default="text/plain")
|
mime_type = schema.ASCIILine(title=_(u"MIME type"), default="text/plain")
|
||||||
text = schema.Text(title=_(u"Comment text"))
|
text = schema.Text(title=_(u"Comment text"))
|
||||||
|
|
||||||
creator = schema.TextLine(title=_(u"Author name (for display)"))
|
creator = schema.TextLine(title=_(u"Author name (for display)"))
|
||||||
creation_date = schema.Date(title=_(u"Creation date"))
|
creation_date = schema.Date(title=_(u"Creation date"))
|
||||||
modification_date = schema.Date(title=_(u"Modification date"))
|
modification_date = schema.Date(title=_(u"Modification date"))
|
||||||
|
|
||||||
# for logged in comments - set to None for anonymous
|
# for logged in comments - set to None for anonymous
|
||||||
author_username = schema.TextLine(title=_(u"Author username"), required=False)
|
author_username = schema.TextLine(title=_(u"Author username"), required=False)
|
||||||
|
|
||||||
# for anonymous comments only, set to None for logged in comments
|
# for anonymous comments only, set to None for logged in comments
|
||||||
author_name = schema.TextLine(title=_(u"Author name"), required=False)
|
author_name = schema.TextLine(title=_(u"Author name"), required=False)
|
||||||
author_email = schema.TextLine(title=_(u"Author email address"), required=False)
|
author_email = schema.TextLine(title=_(u"Author email address"), required=False)
|
||||||
|
|
||||||
class ICommentingTool(Interface):
|
class ICommentingTool(Interface):
|
||||||
"""A tool that indexes all comments for usage by the management interface.
|
"""A tool that indexes all comments for usage by the management interface.
|
||||||
|
|
||||||
This means the management interface can still work even though we don't
|
This means the management interface can still work even though we don't
|
||||||
index the comments in portal_catalog.
|
index the comments in portal_catalog.
|
||||||
|
|
||||||
The default implementation of this interface simply defers to
|
The default implementation of this interface simply defers to
|
||||||
portal_catalog, but a custom version of the tool can be used to provide
|
portal_catalog, but a custom version of the tool can be used to provide
|
||||||
an alternate indexing mechanism.
|
an alternate indexing mechanism.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def indexObject(comment):
|
def indexObject(comment):
|
||||||
"""Indexes a comment
|
"""Indexes a comment
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def reindexObject(comment):
|
def reindexObject(comment):
|
||||||
"""Reindex a comment
|
"""Reindex a comment
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def unindexObject(comment):
|
def unindexObject(comment):
|
||||||
"""Removes a comment from the indexes
|
"""Removes a comment from the indexes
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def uniqueValuesFor(name):
|
def uniqueValuesFor(name):
|
||||||
"""Get unique values for FieldIndex name
|
"""Get unique values for FieldIndex name
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def searchResults(REQUEST=None, **kw):
|
def searchResults(REQUEST=None, **kw):
|
||||||
"""Perform a search over all indexed comments.
|
"""Perform a search over all indexed comments.
|
||||||
"""
|
"""
|
||||||
|
20
plone/app/discussion/profiles/default/controlpanel.xml
Normal file
20
plone/app/discussion/profiles/default/controlpanel.xml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<object
|
||||||
|
name="portal_controlpanel"
|
||||||
|
xmlns:i18n="http://xml.zope.org/namespaces/i18n"
|
||||||
|
i18n:domain="plone.app.discussion"
|
||||||
|
purge="False">
|
||||||
|
|
||||||
|
<configlet
|
||||||
|
title="Discussion"
|
||||||
|
action_id="discussion"
|
||||||
|
appId="plone.app.discussion"
|
||||||
|
category="Products"
|
||||||
|
condition_expr=""
|
||||||
|
url_expr="string:${portal_url}/@@discussion-settings"
|
||||||
|
visible="True"
|
||||||
|
i18n:attributes="title">
|
||||||
|
<permission>Manage portal</permission>
|
||||||
|
</configlet>
|
||||||
|
|
||||||
|
</object>
|
4
plone/app/discussion/profiles/default/registry.xml
Normal file
4
plone/app/discussion/profiles/default/registry.xml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<registry>
|
||||||
|
<records interface="plone.app.discussion.interfaces.IDiscussionSettings" />
|
||||||
|
</registry>
|
4
setup.py
4
setup.py
@ -27,14 +27,16 @@ setup(name='plone.app.discussion',
|
|||||||
'setuptools',
|
'setuptools',
|
||||||
'collective.autopermission',
|
'collective.autopermission',
|
||||||
'collective.testcaselayer',
|
'collective.testcaselayer',
|
||||||
|
'plone.app.registry',
|
||||||
'plone.indexer',
|
'plone.indexer',
|
||||||
|
'plone.registry',
|
||||||
|
'plone.z3cform',
|
||||||
'ZODB3',
|
'ZODB3',
|
||||||
'zope.interface',
|
'zope.interface',
|
||||||
'zope.component',
|
'zope.component',
|
||||||
'zope.annotation',
|
'zope.annotation',
|
||||||
'zope.event',
|
'zope.event',
|
||||||
'zope.app.container', # XXX: eventually should change to zope.container
|
'zope.app.container', # XXX: eventually should change to zope.container
|
||||||
'plone.indexer',
|
|
||||||
],
|
],
|
||||||
entry_points="""
|
entry_points="""
|
||||||
[z3c.autoinclude.plugin]
|
[z3c.autoinclude.plugin]
|
||||||
|
Loading…
Reference in New Issue
Block a user