Whitespace.
This commit is contained in:
parent
c5864ca9f7
commit
1f01a71aac
@ -1,7 +1,7 @@
|
|||||||
[buildout]
|
[buildout]
|
||||||
extends =
|
extends =
|
||||||
http://svn.plone.org/svn/collective/buildout/plonetest/plone-4.1.x.cfg
|
http://svn.plone.org/svn/collective/buildout/plonetest/plone-4.1.x.cfg
|
||||||
package-name = plone.app.discussion
|
package-name = plone.app.discussion
|
||||||
package-directory = plone/app/discussion
|
package-directory = plone/app/discussion
|
||||||
parts += instance
|
parts += instance
|
||||||
|
|
||||||
|
4
dev.cfg
4
dev.cfg
@ -2,7 +2,7 @@
|
|||||||
extends =
|
extends =
|
||||||
buildout.cfg
|
buildout.cfg
|
||||||
extensions = buildout.eggtractor mr.developer
|
extensions = buildout.eggtractor mr.developer
|
||||||
tractor-src-directory =
|
tractor-src-directory =
|
||||||
.
|
.
|
||||||
src
|
src
|
||||||
auto-checkout =
|
auto-checkout =
|
||||||
@ -15,7 +15,7 @@ auto-checkout =
|
|||||||
parts +=
|
parts +=
|
||||||
omelette
|
omelette
|
||||||
releaser
|
releaser
|
||||||
pocompile
|
pocompile
|
||||||
zopepy
|
zopepy
|
||||||
sphinxbuilder
|
sphinxbuilder
|
||||||
sphinxupload
|
sphinxupload
|
||||||
|
@ -19,19 +19,19 @@ plone.app.discussion.
|
|||||||
permissions.
|
permissions.
|
||||||
|
|
||||||
**Discussion items are light weight objects**
|
**Discussion items are light weight objects**
|
||||||
Discussion item objects are as light weight as possible. Ideally, a
|
Discussion item objects are as light weight as possible. Ideally, a
|
||||||
discussion item should be as lightweight as a catalog brain. This may mean
|
discussion item should be as lightweight as a catalog brain. This may mean
|
||||||
that we forego convenience base classes and re-implement certain interfaces.
|
that we forego convenience base classes and re-implement certain interfaces.
|
||||||
Comments should not provide the full set of dublin core metadata, though
|
Comments should not provide the full set of dublin core metadata, though
|
||||||
custom indexers can be used to provide values for standard catalog indexes.
|
custom indexers can be used to provide values for standard catalog indexes.
|
||||||
|
|
||||||
**Optimise for retrival speed**
|
**Optimise for retrival speed**
|
||||||
HTML filtering and other processing should happen on save, not on render,
|
HTML filtering and other processing should happen on save, not on render,
|
||||||
to make rendering quick.
|
to make rendering quick.
|
||||||
|
|
||||||
**Settings are stored using plone.registry**
|
**Settings are stored using plone.registry**
|
||||||
Any global setting should be stored in plone.registry records.
|
Any global setting should be stored in plone.registry records.
|
||||||
|
|
||||||
**Forms are constructed using extensible z3c.form forms**
|
**Forms are constructed using extensible z3c.form forms**
|
||||||
This allows plugins (such as spam protection algorithms) to provide
|
This allows plugins (such as spam protection algorithms) to provide
|
||||||
additional validation. It also allows integrators to write add-ons that add
|
additional validation. It also allows integrators to write add-ons that add
|
||||||
@ -45,15 +45,15 @@ plone.app.discussion.
|
|||||||
not stored threaded, the dict interface should act as if they are, i.e.
|
not stored threaded, the dict interface should act as if they are, i.e.
|
||||||
calling items() on a comment should return the replies to that comment
|
calling items() on a comment should return the replies to that comment
|
||||||
(in order).
|
(in order).
|
||||||
|
|
||||||
**Discussion items are retrieved in reverse creation date order**
|
**Discussion items are retrieved in reverse creation date order**
|
||||||
Discussion items do not need to support explicit ordering. They should
|
Discussion items do not need to support explicit ordering. They should
|
||||||
always be retrieved in reverse creation date order (most recent for).
|
always be retrieved in reverse creation date order (most recent for).
|
||||||
They can be stored with keys so that this is always true.
|
They can be stored with keys so that this is always true.
|
||||||
|
|
||||||
**Discussion items do not need readable ids**
|
**Discussion items do not need readable ids**
|
||||||
Ids can be based on the creation date.
|
Ids can be based on the creation date.
|
||||||
|
|
||||||
**Discussion items send events**
|
**Discussion items send events**
|
||||||
The usual zope.lifecycleevent and zope.container events are fired when
|
The usual zope.lifecycleevent and zope.container events are fired when
|
||||||
discussion items are added, removed, or modified.
|
discussion items are added, removed, or modified.
|
||||||
|
@ -71,67 +71,67 @@ logger = logging.getLogger("plone.app.discussion")
|
|||||||
class Comment(CatalogAware, WorkflowAware, DynamicType, Traversable,
|
class Comment(CatalogAware, WorkflowAware, DynamicType, Traversable,
|
||||||
RoleManager, Owned, Implicit, Persistent):
|
RoleManager, Owned, Implicit, Persistent):
|
||||||
"""A comment.
|
"""A comment.
|
||||||
|
|
||||||
This object attempts to be as lightweight as possible. We implement a
|
This object attempts to be as lightweight as possible. We implement a
|
||||||
number of standard methods instead of subclassing, to have total control
|
number of standard methods instead of subclassing, to have total control
|
||||||
over what goes into the object.
|
over what goes into the object.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
implements(IComment)
|
implements(IComment)
|
||||||
|
|
||||||
meta_type = portal_type = 'Discussion Item'
|
meta_type = portal_type = 'Discussion Item'
|
||||||
# This needs to be kept in sync with types/Discussion_Item.xml title
|
# This needs to be kept in sync with types/Discussion_Item.xml title
|
||||||
fti_title = 'Comment'
|
fti_title = 'Comment'
|
||||||
|
|
||||||
__parent__ = None
|
__parent__ = None
|
||||||
|
|
||||||
comment_id = None # long
|
comment_id = None # long
|
||||||
in_reply_to = None # long
|
in_reply_to = None # long
|
||||||
|
|
||||||
title = u""
|
title = u""
|
||||||
|
|
||||||
mime_type = None
|
mime_type = None
|
||||||
text = u""
|
text = u""
|
||||||
|
|
||||||
creator = None
|
creator = None
|
||||||
creation_date = None
|
creation_date = None
|
||||||
modification_date = None
|
modification_date = None
|
||||||
|
|
||||||
author_username = None
|
author_username = None
|
||||||
|
|
||||||
author_name = None
|
author_name = None
|
||||||
author_email = None
|
author_email = None
|
||||||
|
|
||||||
user_notification = None
|
user_notification = None
|
||||||
|
|
||||||
# Note: we want to use zope.component.createObject() to instantiate
|
# Note: we want to use zope.component.createObject() to instantiate
|
||||||
# comments as far as possible. comment_id and __parent__ are set via
|
# comments as far as possible. comment_id and __parent__ are set via
|
||||||
# IConversation.addComment().
|
# IConversation.addComment().
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.creation_date = self.modification_date = datetime.utcnow()
|
self.creation_date = self.modification_date = datetime.utcnow()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def __name__(self):
|
def __name__(self):
|
||||||
return self.comment_id and unicode(self.comment_id) or None
|
return self.comment_id and unicode(self.comment_id) or None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def id(self):
|
def id(self):
|
||||||
return self.comment_id and str(self.comment_id) or None
|
return self.comment_id and str(self.comment_id) or None
|
||||||
|
|
||||||
def getId(self):
|
def getId(self):
|
||||||
"""The id of the comment, as a string.
|
"""The id of the comment, as a string.
|
||||||
"""
|
"""
|
||||||
return self.id
|
return self.id
|
||||||
|
|
||||||
def getText(self, targetMimetype=None):
|
def getText(self, targetMimetype=None):
|
||||||
"""The body text of a comment.
|
"""The body text of a comment.
|
||||||
"""
|
"""
|
||||||
transforms = getToolByName(self, 'portal_transforms')
|
transforms = getToolByName(self, 'portal_transforms')
|
||||||
|
|
||||||
if targetMimetype is None:
|
if targetMimetype is None:
|
||||||
targetMimetype = 'text/x-html-safe'
|
targetMimetype = 'text/x-html-safe'
|
||||||
|
|
||||||
sourceMimetype = getattr(self, 'mime_type', None)
|
sourceMimetype = getattr(self, 'mime_type', None)
|
||||||
if sourceMimetype is None:
|
if sourceMimetype is None:
|
||||||
registry = queryUtility(IRegistry)
|
registry = queryUtility(IRegistry)
|
||||||
@ -146,21 +146,21 @@ class Comment(CatalogAware, WorkflowAware, DynamicType, Traversable,
|
|||||||
text,
|
text,
|
||||||
context=self,
|
context=self,
|
||||||
mimetype=sourceMimetype).getData()
|
mimetype=sourceMimetype).getData()
|
||||||
|
|
||||||
def Title(self):
|
def Title(self):
|
||||||
"""The title of the comment.
|
"""The title of the comment.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if self.title:
|
if self.title:
|
||||||
return self.title
|
return self.title
|
||||||
|
|
||||||
if not self.creator:
|
if not self.creator:
|
||||||
creator = translate(Message(_(u"label_anonymous",
|
creator = translate(Message(_(u"label_anonymous",
|
||||||
default=u"Anonymous")))
|
default=u"Anonymous")))
|
||||||
else:
|
else:
|
||||||
creator = self.creator
|
creator = self.creator
|
||||||
creator = creator
|
creator = creator
|
||||||
|
|
||||||
# Fetch the content object (the parent of the comment is the
|
# Fetch the content object (the parent of the comment is the
|
||||||
# conversation, the parent of the conversation is the content object).
|
# conversation, the parent of the conversation is the content object).
|
||||||
content = aq_base(self.__parent__.__parent__)
|
content = aq_base(self.__parent__.__parent__)
|
||||||
@ -169,26 +169,26 @@ class Comment(CatalogAware, WorkflowAware, DynamicType, Traversable,
|
|||||||
mapping={'creator': creator,
|
mapping={'creator': creator,
|
||||||
'content': safe_unicode(content.Title())}))
|
'content': safe_unicode(content.Title())}))
|
||||||
return title
|
return title
|
||||||
|
|
||||||
def Creator(self):
|
def Creator(self):
|
||||||
"""The name of the person who wrote the comment.
|
"""The name of the person who wrote the comment.
|
||||||
"""
|
"""
|
||||||
return self.creator
|
return self.creator
|
||||||
|
|
||||||
def Type(self):
|
def Type(self):
|
||||||
"""The Discussion Item content type.
|
"""The Discussion Item content type.
|
||||||
"""
|
"""
|
||||||
return self.fti_title
|
return self.fti_title
|
||||||
|
|
||||||
# CMF's event handlers assume any IDynamicType has these :(
|
# CMF's event handlers assume any IDynamicType has these :(
|
||||||
|
|
||||||
def opaqueItems(self): # pragma: no cover
|
def opaqueItems(self): # pragma: no cover
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def opaqueIds(self): # pragma: no cover
|
def opaqueIds(self): # pragma: no cover
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def opaqueValues(self): # pragma: no cover
|
def opaqueValues(self): # pragma: no cover
|
||||||
return []
|
return []
|
||||||
|
|
||||||
CommentFactory = Factory(Comment)
|
CommentFactory = Factory(Comment)
|
||||||
@ -242,24 +242,24 @@ def notify_content_object_moved(obj, event):
|
|||||||
for comment in conversation.getComments():
|
for comment in conversation.getComments():
|
||||||
aq_base(comment).__parent__.__parent__.__parent__ = event.newParent
|
aq_base(comment).__parent__.__parent__.__parent__ = event.newParent
|
||||||
catalog.reindexObject(aq_base(comment))
|
catalog.reindexObject(aq_base(comment))
|
||||||
|
|
||||||
|
|
||||||
def notify_user(obj, event):
|
def notify_user(obj, event):
|
||||||
"""Tell users when a comment has been added.
|
"""Tell users when a comment has been added.
|
||||||
|
|
||||||
This method composes and sends emails to all users that have added a
|
This method composes and sends emails to all users that have added a
|
||||||
comment to this conversation and enabled user notification.
|
comment to this conversation and enabled user notification.
|
||||||
|
|
||||||
This requires the user_notification setting to be enabled in the
|
This requires the user_notification setting to be enabled in the
|
||||||
discussion control panel.
|
discussion control panel.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Check if user notification is enabled
|
# Check if user notification is enabled
|
||||||
registry = queryUtility(IRegistry)
|
registry = queryUtility(IRegistry)
|
||||||
settings = registry.forInterface(IDiscussionSettings, check=False)
|
settings = registry.forInterface(IDiscussionSettings, check=False)
|
||||||
if not settings.user_notification_enabled:
|
if not settings.user_notification_enabled:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Get informations that are necessary to send an email
|
# Get informations that are necessary to send an email
|
||||||
mail_host = getToolByName(obj, 'MailHost')
|
mail_host = getToolByName(obj, 'MailHost')
|
||||||
portal_url = getToolByName(obj, 'portal_url')
|
portal_url = getToolByName(obj, 'portal_url')
|
||||||
@ -282,16 +282,16 @@ def notify_user(obj, event):
|
|||||||
if (obj != comment and
|
if (obj != comment and
|
||||||
comment.user_notification and comment.author_email):
|
comment.user_notification and comment.author_email):
|
||||||
emails.add(comment.author_email)
|
emails.add(comment.author_email)
|
||||||
|
|
||||||
if not emails:
|
if not emails:
|
||||||
return
|
return
|
||||||
|
|
||||||
subject = translate(_(u"A comment has been posted."),
|
subject = translate(_(u"A comment has been posted."),
|
||||||
context=obj.REQUEST)
|
context=obj.REQUEST)
|
||||||
message = translate(Message(
|
message = translate(Message(
|
||||||
MAIL_NOTIFICATION_MESSAGE,
|
MAIL_NOTIFICATION_MESSAGE,
|
||||||
mapping={'title': safe_unicode(content_object.title),
|
mapping={'title': safe_unicode(content_object.title),
|
||||||
'link': content_object.absolute_url() +
|
'link': content_object.absolute_url() +
|
||||||
'/view#' + obj.id,
|
'/view#' + obj.id,
|
||||||
'text': obj.text}),
|
'text': obj.text}),
|
||||||
context=obj.REQUEST)
|
context=obj.REQUEST)
|
||||||
@ -312,13 +312,13 @@ def notify_user(obj, event):
|
|||||||
|
|
||||||
def notify_moderator(obj, event):
|
def notify_moderator(obj, event):
|
||||||
"""Tell the moderator when a comment needs attention.
|
"""Tell the moderator when a comment needs attention.
|
||||||
|
|
||||||
This method sends an email to the moderator if comment moderation a new
|
This method sends an email to the moderator if comment moderation a new
|
||||||
comment has been added that needs to be approved.
|
comment has been added that needs to be approved.
|
||||||
|
|
||||||
The moderator_notification setting has to be enabled in the discussion
|
The moderator_notification setting has to be enabled in the discussion
|
||||||
control panel.
|
control panel.
|
||||||
|
|
||||||
Configure the moderator e-mail address in the discussion control panel.
|
Configure the moderator e-mail address in the discussion control panel.
|
||||||
If no moderator is configured but moderator notifications are turned on,
|
If no moderator is configured but moderator notifications are turned on,
|
||||||
the site admin email (from the mail control panel) will be used.
|
the site admin email (from the mail control panel) will be used.
|
||||||
@ -328,25 +328,25 @@ def notify_moderator(obj, event):
|
|||||||
settings = registry.forInterface(IDiscussionSettings, check=False)
|
settings = registry.forInterface(IDiscussionSettings, check=False)
|
||||||
if not settings.moderator_notification_enabled:
|
if not settings.moderator_notification_enabled:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Get informations that are necessary to send an email
|
# Get informations that are necessary to send an email
|
||||||
mail_host = getToolByName(obj, 'MailHost')
|
mail_host = getToolByName(obj, 'MailHost')
|
||||||
portal_url = getToolByName(obj, 'portal_url')
|
portal_url = getToolByName(obj, 'portal_url')
|
||||||
portal = portal_url.getPortalObject()
|
portal = portal_url.getPortalObject()
|
||||||
sender = portal.getProperty('email_from_address')
|
sender = portal.getProperty('email_from_address')
|
||||||
|
|
||||||
if settings.moderator_email:
|
if settings.moderator_email:
|
||||||
mto = settings.moderator_email
|
mto = settings.moderator_email
|
||||||
else:
|
else:
|
||||||
mto = sender
|
mto = sender
|
||||||
|
|
||||||
# Check if a sender address is available
|
# Check if a sender address is available
|
||||||
if not sender:
|
if not sender:
|
||||||
return
|
return
|
||||||
|
|
||||||
conversation = aq_parent(obj)
|
conversation = aq_parent(obj)
|
||||||
content_object = aq_parent(conversation)
|
content_object = aq_parent(conversation)
|
||||||
|
|
||||||
# Compose email
|
# Compose email
|
||||||
subject = translate(_(u"A comment has been posted."), context=obj.REQUEST)
|
subject = translate(_(u"A comment has been posted."), context=obj.REQUEST)
|
||||||
message = translate(Message(MAIL_NOTIFICATION_MESSAGE_MODERATOR,
|
message = translate(Message(MAIL_NOTIFICATION_MESSAGE_MODERATOR,
|
||||||
@ -358,7 +358,7 @@ def notify_moderator(obj, event):
|
|||||||
'link_delete': obj.absolute_url() + '/@@moderate-delete-comment',
|
'link_delete': obj.absolute_url() + '/@@moderate-delete-comment',
|
||||||
}),
|
}),
|
||||||
context=obj.REQUEST)
|
context=obj.REQUEST)
|
||||||
|
|
||||||
# Send email
|
# Send email
|
||||||
try:
|
try:
|
||||||
mail_host.send(message, mto, sender, subject, charset='utf-8')
|
mail_host.send(message, mto, sender, subject, charset='utf-8')
|
||||||
|
@ -17,7 +17,7 @@ id and allow traversal). Hence, traversing to obj/++conversation++/123 retrieves
|
|||||||
the comment with id 123.
|
the comment with id 123.
|
||||||
|
|
||||||
Comments ids are assigned in order, so a comment with id N was posted before
|
Comments ids are assigned in order, so a comment with id N was posted before
|
||||||
a comment with id N + 1. However, it is not guaranteed that ids will be
|
a comment with id N + 1. However, it is not guaranteed that ids will be
|
||||||
incremental. Ids must be positive integers - 0 or negative numbers are not
|
incremental. Ids must be positive integers - 0 or negative numbers are not
|
||||||
allowed.
|
allowed.
|
||||||
|
|
||||||
@ -42,13 +42,13 @@ Factories
|
|||||||
|
|
||||||
Comments should always be created via the 'Discussion Item' IFactory utility.
|
Comments should always be created via the 'Discussion Item' IFactory utility.
|
||||||
Conversations should always be obtained via the IConversation adapter (even
|
Conversations should always be obtained via the IConversation adapter (even
|
||||||
the ++conversation++ namespace should use this). This makes it possible to
|
the ++conversation++ namespace should use this). This makes it possible to
|
||||||
replace conversations and comments transparently.
|
replace conversations and comments transparently.
|
||||||
|
|
||||||
The Comment class
|
The Comment class
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
The inheritance tree for DiscussionItem is shown below. Classes we want to
|
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
|
mix in and interface we want to implement in the Comment class are marked
|
||||||
with [x].
|
with [x].
|
||||||
|
|
||||||
@ -60,17 +60,17 @@ with [x].
|
|||||||
[ ] DynamicType = [ ] IDynamicType
|
[ ] DynamicType = [ ] IDynamicType
|
||||||
[ ] CMFCatalogAware = [ ] <no interface>
|
[ ] CMFCatalogAware = [ ] <no interface>
|
||||||
[ ] SimpleItem = [ ] ISimpleItem
|
[ ] SimpleItem = [ ] ISimpleItem
|
||||||
[ ] Item [ ]
|
[ ] Item [ ]
|
||||||
[?] Base = [ ] <no interface>
|
[?] Base = [ ] <no interface>
|
||||||
[ ] Resource = [ ] <no interface>
|
[ ] Resource = [ ] <no interface>
|
||||||
[ ] CopySource = [ ] ICopySource
|
[ ] CopySource = [ ] ICopySource
|
||||||
[ ] Tabs = [ ] <no interface>
|
[ ] Tabs = [ ] <no interface>
|
||||||
[x] Traversable = [ ] ITraversable
|
[x] Traversable = [ ] ITraversable
|
||||||
[ ] Element = [ ] <no interface>
|
[ ] Element = [ ] <no interface>
|
||||||
[x] Owned = [ ] IOwned
|
[x] Owned = [ ] IOwned
|
||||||
[ ] UndoSupport = [ ] IUndoSupport
|
[ ] UndoSupport = [ ] IUndoSupport
|
||||||
[ ] Persistent [ ]
|
[ ] Persistent [ ]
|
||||||
[ ] Implicit [ ]
|
[ ] Implicit [ ]
|
||||||
[x] RoleManager = [ ] IRoleManager
|
[x] RoleManager = [ ] IRoleManager
|
||||||
[ ] RoleManager = [ ] IPermissionMappingSupport
|
[ ] RoleManager = [ ] IPermissionMappingSupport
|
||||||
[ ] DefaultDublinCoreImpl = [ ] IDublinCore
|
[ ] DefaultDublinCoreImpl = [ ] IDublinCore
|
||||||
@ -86,7 +86,7 @@ Thus, we want:
|
|||||||
- we do not want implicit acquisition
|
- we do not want implicit acquisition
|
||||||
* Owned, to be able to track ownership
|
* Owned, to be able to track ownership
|
||||||
* RoleManager, to support permissions and local roles
|
* RoleManager, to support permissions and local roles
|
||||||
|
|
||||||
We also want to use a number of custom indexers for most of the standard
|
We also want to use a number of custom indexers for most of the standard
|
||||||
metadata such as creator, effective date etc.
|
metadata such as creator, effective date etc.
|
||||||
|
|
||||||
@ -121,7 +121,7 @@ In addition, we'll need a 'Moderator' role and a moderation permission,
|
|||||||
|
|
||||||
* Moderate comment
|
* Moderate comment
|
||||||
* Bypass moderation
|
* Bypass moderation
|
||||||
|
|
||||||
To control whether Anonymous can post comments, we manage the 'Reply to Item'
|
To control whether Anonymous can post comments, we manage the 'Reply to Item'
|
||||||
permission. To control whether moderation is required for various roles, we
|
permission. To control whether moderation is required for various roles, we
|
||||||
could manage the 'Bypass moderation' permission.
|
could manage the 'Bypass moderation' permission.
|
||||||
@ -136,7 +136,7 @@ These could work in a workflow like this:
|
|||||||
+----- {auto-publish} -----+
|
+----- {auto-publish} -----+
|
||||||
| |
|
| |
|
||||||
+----- {auto-moderate} ----+
|
+----- {auto-moderate} ----+
|
||||||
|
|
||||||
The 'posted' state is the initial state. 'published' is the state where the
|
The 'posted' state is the initial state. 'published' is the state where the
|
||||||
comment is visible to non-reviewers.
|
comment is visible to non-reviewers.
|
||||||
|
|
||||||
@ -150,22 +150,22 @@ the 'Bypass moderation' permission.
|
|||||||
The 'auto-moderate' transition would be another automatic transition protected
|
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
|
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.
|
an auto-moderation 'white-list', e.g. by email address or username.
|
||||||
|
|
||||||
Forms and UI
|
Forms and UI
|
||||||
------------
|
------------
|
||||||
|
|
||||||
The basic commenting display/reply form is placed in a viewlet.
|
The basic commenting display/reply form is placed in a viewlet.
|
||||||
|
|
||||||
The reply form is dynamically created right under the comment when the user hits
|
The reply form is dynamically created right under the comment when the user hits
|
||||||
the reply button. To do so, we copy the standard comment form with a jQuery
|
the reply button. To do so, we copy the standard comment form with a jQuery
|
||||||
function. This function sets the form's hidden in_reply_to field to the id of
|
function. This function sets the form's hidden in_reply_to field to the id of
|
||||||
the comment the user wants to reply to. This also makes it possible to use
|
the comment the user wants to reply to. This also makes it possible to use
|
||||||
z3c.form validation for the reply forms, because we can uniquely identify the
|
z3c.form validation for the reply forms, because we can uniquely identify the
|
||||||
reply form request and return the reply form with validation errors.
|
reply form request and return the reply form with validation errors.
|
||||||
|
|
||||||
Since we rely on JavaScript for the reply form creation, the reply button is
|
Since we rely on JavaScript for the reply form creation, the reply button is
|
||||||
removed for non JavaScript enabled browsers.
|
removed for non JavaScript enabled browsers.
|
||||||
|
|
||||||
The comment form uses z3c.form and plone.z3cform's ExtensibleForm support. This
|
The comment form uses z3c.form and plone.z3cform's ExtensibleForm support. This
|
||||||
makes it possible to plug in additional fields declaratively, e.g. to include
|
makes it possible to plug in additional fields declaratively, e.g. to include
|
||||||
SPAM protection.
|
SPAM protection.
|
||||||
|
@ -112,7 +112,7 @@ class IDiscussionSettings(Interface):
|
|||||||
required=False,
|
required=False,
|
||||||
default=False,
|
default=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
moderator_email = schema.ASCIILine(
|
moderator_email = schema.ASCIILine(
|
||||||
title = _(u'label_moderator_email', default=u'Moderator Email Address'),
|
title = _(u'label_moderator_email', default=u'Moderator Email Address'),
|
||||||
description = _(u'help_moderator_email',
|
description = _(u'help_moderator_email',
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<configure
|
<configure
|
||||||
xmlns="http://namespaces.zope.org/zope"
|
xmlns="http://namespaces.zope.org/zope"
|
||||||
xmlns:genericsetup="http://namespaces.zope.org/genericsetup">
|
xmlns:genericsetup="http://namespaces.zope.org/genericsetup">
|
||||||
|
|
||||||
<genericsetup:upgradeStep
|
<genericsetup:upgradeStep
|
||||||
source="*"
|
source="*"
|
||||||
destination="100"
|
destination="100"
|
||||||
@ -10,5 +10,5 @@
|
|||||||
profile="plone.app.discussion:default"
|
profile="plone.app.discussion:default"
|
||||||
handler=".upgrades.update_registry"
|
handler=".upgrades.update_registry"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</configure>
|
</configure>
|
||||||
|
Loading…
Reference in New Issue
Block a user