170 lines
6.7 KiB
Plaintext
170 lines
6.7 KiB
Plaintext
|
=================================
|
||
|
plone.app.discussion design notes
|
||
|
=================================
|
||
|
|
||
|
This document contains design notes for plone.app.discussion.
|
||
|
|
||
|
The Comment class
|
||
|
-----------------
|
||
|
|
||
|
The inheritance tree for DiscussionItem is shown below. Classes we want to
|
||
|
mix in and interface we want to implement in the Comment class are marked
|
||
|
with [x].
|
||
|
|
||
|
[ ] DiscussionItem
|
||
|
[ ] Document
|
||
|
[ ] PortalContent = [ ] IContentish
|
||
|
[ ] DynamicType = [ ] IDynamicType
|
||
|
[ ] CMFCatalogAware = [ ] <no interface>
|
||
|
[ ] SimpleItem = [ ] ISimpleItem
|
||
|
[ ] Item [ ]
|
||
|
[?] Base = [ ] <no interface>
|
||
|
[ ] Resource = [ ] <no interface>
|
||
|
[ ] CopySource = [ ] ICopySource
|
||
|
[ ] Tabs = [ ] <no interface>
|
||
|
[x] Traversable = [ ] ITraversable
|
||
|
[ ] Element = [ ] <no interface>
|
||
|
[x] Owned = [ ] IOwned
|
||
|
[ ] UndoSupport = [ ] IUndoSupport
|
||
|
[ ] Persistent [ ]
|
||
|
[ ] Implicit [ ]
|
||
|
[x] RoleManager = [ ] IRoleManager
|
||
|
[ ] RoleManager = [ ] IPermissionMappingSupport
|
||
|
[ ] DefaultDublinCoreImpl = [ ] IDublinCore
|
||
|
[ ] ICatalogableDublinCore
|
||
|
[ ] IMutableDublinCore
|
||
|
[ ] PropertyManager = [ ] IPropertyManager
|
||
|
|
||
|
Thus, we want:
|
||
|
|
||
|
* Traversable, to get absolute_url() and friends
|
||
|
- this requires a good acquisition chain at all times
|
||
|
* Acquisition.Explicit, to support acquisition
|
||
|
- we do not want implicit acquisition
|
||
|
* Owned, to be able to track ownership
|
||
|
* RoleManager, to support permissions and local roles
|
||
|
|
||
|
We also want to use a number of custom indexers for most of the standard
|
||
|
metadata such as creator, effective date etc.
|
||
|
|
||
|
Finally, we'll need event handlers to perform the actual indexing.
|
||
|
|
||
|
The Discussable class
|
||
|
---------------------
|
||
|
|
||
|
To obtain comments for a content item, we adapt the content to IDiscussable.
|
||
|
This has a 'replies' BTree.
|
||
|
|
||
|
To support __parent__ pointer integrity and stable URLs, we will implement
|
||
|
IDiscussable as a persistent object stored in an annotation. Comments directly
|
||
|
in reply to the content item have a __parent__ pointing to this, which in turn
|
||
|
has a __parent__ pointing at the content item.
|
||
|
|
||
|
The IDiscussable interface also maintains information about the total number
|
||
|
of comments and the unique set of commenters' usernames. These are indexed in
|
||
|
the catalog, allowing queries like "recently commented-upon content items",
|
||
|
"my comments", or "all comments by user X". These values need to be stored and
|
||
|
maintained by event handlers, not calculated on the fly.
|
||
|
|
||
|
See collective.discussionplus for inspiration.
|
||
|
|
||
|
Traversal and acquisition
|
||
|
--------------------------
|
||
|
|
||
|
A comment may have a URL such as:
|
||
|
|
||
|
http://localhost:8080/site/content/++comments++/1/2/3
|
||
|
|
||
|
For this traversal to work, we have:
|
||
|
|
||
|
- a namespace traversal adapter for ++comments++ that looks up an
|
||
|
IDiscussable adapter on the context and returns this
|
||
|
|
||
|
- an IPublishTraverse adapter for IHasReplies (inherited by IDiscussable
|
||
|
and IComment), which looks up values in the 'replies' dictionary and
|
||
|
acquisition-wraps them.
|
||
|
|
||
|
- the IDiscussable adapter needs to have an id of ++comment++
|
||
|
|
||
|
XXX: unrestrictedTraverse() does not take IPublishTraverse adapters into
|
||
|
account. This may mean we need to implement __getitem__ on comments instead
|
||
|
of/in addition to using a custom IPublishTraverse for IHasReplies.
|
||
|
|
||
|
Discussion settings
|
||
|
-------------------
|
||
|
|
||
|
Discussion can be enabled per-type and per-instance, via values in the FTI
|
||
|
(allow_discussion) and on the object. These will remain unchanged. The
|
||
|
IDiscussable object's 'enabled' property should consult these.
|
||
|
|
||
|
Global settings should be managed using plone.registry. A control panel
|
||
|
can be generated from this as well, using the helper class in
|
||
|
plone.app.registry.
|
||
|
|
||
|
Note that some settings, notably those to do with permissions and workflow,
|
||
|
will need to be wired up as custom form fields with custom data mangers
|
||
|
or similar.
|
||
|
|
||
|
Workflow and permissions
|
||
|
------------------------
|
||
|
|
||
|
Where possible, we should use existing permissions:
|
||
|
|
||
|
* View
|
||
|
* Reply to Item
|
||
|
* Modify Portal Content
|
||
|
* Request Review
|
||
|
|
||
|
In addition, we'll need a 'Moderator' role and a moderation permission,
|
||
|
|
||
|
* Moderate comment
|
||
|
* Bypass moderation
|
||
|
|
||
|
To control whether Anonymous can post comments, we manage the 'Reply to Item'
|
||
|
permission. To control whether moderation is required for various roles, we
|
||
|
could manage the 'Bypass moderation' permission.
|
||
|
|
||
|
These could work in a workflow like this:
|
||
|
|
||
|
* --> [posted] -- {publish} --> [published]--> *
|
||
|
| ^
|
||
|
| |
|
||
|
+----- {auto-publish} -----+
|
||
|
| |
|
||
|
+----- {auto-moderate} ----+
|
||
|
|
||
|
The 'posted' state is the initial state. 'published' is the state where the
|
||
|
comment is visible to non-reviewers.
|
||
|
|
||
|
The 'publish' transition would be protected by the 'Moderate comment'
|
||
|
permission. We could have states and transition for 'rejected', etc, but it
|
||
|
is probably just as good to delete comments that are rejected.
|
||
|
|
||
|
The 'auto-publish' transition would be an automatic transition protected by
|
||
|
the 'Bypass moderation' permission.
|
||
|
|
||
|
The 'auto-moderate' transition would be another automatic transition protected
|
||
|
by an expression (e.g. calling a view) that returns True if the user is on
|
||
|
an auto-moderation 'white-list', e.g. by email address or username.
|
||
|
|
||
|
Forms and UI
|
||
|
------------
|
||
|
|
||
|
The basic commenting display/reply form should be placed in a viewlet.
|
||
|
|
||
|
Ideally, the reply form should be inline, perhaps revealed with JavaScript
|
||
|
if enabled. This allows full contextualisation of replies. The current
|
||
|
solution, with a separate form that shows some context, is brittle and
|
||
|
over-complicated.
|
||
|
|
||
|
If we support quoting of comments in replies, we can load the text to quote
|
||
|
using JavaScript as well.
|
||
|
|
||
|
As a fall-back for non-JavaScript enabled browsers, it is probably OK not to
|
||
|
support quoting and/or viewing of context, e.g. the user is taken to a standalone
|
||
|
'comment reply' form.
|
||
|
|
||
|
All actual forms should be handled using z3c.form and plone.z3cform's
|
||
|
ExtensibleForm support. This makes it possible to plug in additional fields
|
||
|
declaratively, e.g. to include spam protection.
|