================================= 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 = [ ] [ ] SimpleItem = [ ] ISimpleItem [ ] Item [ ] [?] Base = [ ] [ ] Resource = [ ] [ ] CopySource = [ ] ICopySource [ ] Tabs = [ ] [x] Traversable = [ ] ITraversable [ ] Element = [ ] [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.