merged master
This commit is contained in:
@@ -38,7 +38,6 @@ from plone.app.discussion.interfaces import ICaptcha
|
||||
from plone.app.discussion.browser.validator import CaptchaValidator
|
||||
|
||||
from plone.z3cform import z2
|
||||
from plone.z3cform.widget import SingleCheckBoxWidget
|
||||
from plone.z3cform.fieldsets import extensible
|
||||
|
||||
|
||||
@@ -47,19 +46,19 @@ from plone.z3cform.interfaces import IWrappedForm
|
||||
COMMENT_DESCRIPTION_PLAIN_TEXT = _(
|
||||
u"comment_description_plain_text",
|
||||
default=u"You can add a comment by filling out the form below. " +
|
||||
"Plain text formatting.")
|
||||
u"Plain text formatting.")
|
||||
|
||||
COMMENT_DESCRIPTION_MARKDOWN = _(
|
||||
u"comment_description_markdown",
|
||||
default=u"You can add a comment by filling out the form below. " +
|
||||
"Plain text formatting. You can use the Markdown syntax for " +
|
||||
"links and images.")
|
||||
u"Plain text formatting. You can use the Markdown syntax for " +
|
||||
u"links and images.")
|
||||
|
||||
COMMENT_DESCRIPTION_INTELLIGENT_TEXT = _(
|
||||
u"comment_description_intelligent_text",
|
||||
default=u"You can add a comment by filling out the form below. " +
|
||||
"Plain text formatting. Web and email addresses are " +
|
||||
"transformed into clickable links.")
|
||||
u"Plain text formatting. Web and email addresses are " +
|
||||
u"transformed into clickable links.")
|
||||
|
||||
COMMENT_DESCRIPTION_MODERATION_ENABLED = _(
|
||||
u"comment_description_moderation_enabled",
|
||||
@@ -95,29 +94,40 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
|
||||
self.widgets['text'].addClass("autoresize")
|
||||
self.widgets['user_notification'].label = _(u"")
|
||||
|
||||
# Rename the id of the text widgets because there can be css-id
|
||||
# clashes with the text field of documents when using and overlay
|
||||
# with TinyMCE.
|
||||
self.widgets['text'].id = "form-widgets-comment-text"
|
||||
|
||||
# Anonymous / Logged-in
|
||||
mtool = getToolByName(self.context, 'portal_membership')
|
||||
if not mtool.isAnonymousUser():
|
||||
self.widgets['author_name'].mode = interfaces.HIDDEN_MODE
|
||||
self.widgets['author_email'].mode = interfaces.HIDDEN_MODE
|
||||
|
||||
# Todo: Since we are not using the author_email field in the
|
||||
# current state, we hide it by default. But we keep the field for
|
||||
# integrators or later use.
|
||||
self.widgets['author_email'].mode = interfaces.HIDDEN_MODE
|
||||
anon = mtool.isAnonymousUser()
|
||||
|
||||
registry = queryUtility(IRegistry)
|
||||
settings = registry.forInterface(IDiscussionSettings, check=False)
|
||||
|
||||
if anon:
|
||||
if settings.anonymous_email_enabled:
|
||||
# according to IDiscussionSettings.anonymous_email_enabled:
|
||||
# "If selected, anonymous user will have to give their email."
|
||||
self.widgets['author_email'].field.required = True
|
||||
self.widgets['author_email'].required = True
|
||||
else:
|
||||
self.widgets['author_email'].mode = interfaces.HIDDEN_MODE
|
||||
else:
|
||||
self.widgets['author_name'].mode = interfaces.HIDDEN_MODE
|
||||
self.widgets['author_email'].mode = interfaces.HIDDEN_MODE
|
||||
|
||||
member = mtool.getAuthenticatedMember()
|
||||
member_email = member.getProperty('email')
|
||||
|
||||
# Hide the user_notification checkbox if user notification is disabled
|
||||
# or the user is not logged in. Also check if the user has a valid
|
||||
# email address
|
||||
if member_email == '' or \
|
||||
not settings.user_notification_enabled or \
|
||||
mtool.isAnonymousUser():
|
||||
self.widgets['user_notification'].mode = interfaces.HIDDEN_MODE
|
||||
member_email_is_empty = member_email == ''
|
||||
user_notification_disabled = not settings.user_notification_enabled
|
||||
if member_email_is_empty or user_notification_disabled or anon:
|
||||
self.widgets['user_notification'].mode = interfaces.HIDDEN_MODE
|
||||
|
||||
def updateActions(self):
|
||||
super(CommentForm, self).updateActions()
|
||||
@@ -132,7 +142,8 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
|
||||
|
||||
# Check if conversation is enabled on this content object
|
||||
if not self.__parent__.restrictedTraverse(
|
||||
'@@conversation_view').enabled():
|
||||
'@@conversation_view'
|
||||
).enabled():
|
||||
raise Unauthorized("Discussion is not enabled for this content "
|
||||
"object.")
|
||||
|
||||
@@ -145,9 +156,10 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
|
||||
registry = queryUtility(IRegistry)
|
||||
settings = registry.forInterface(IDiscussionSettings, check=False)
|
||||
portal_membership = getToolByName(self.context, 'portal_membership')
|
||||
if settings.captcha != 'disabled' and \
|
||||
settings.anonymous_comments and \
|
||||
portal_membership.isAnonymousUser():
|
||||
captcha_enabled = settings.captcha != 'disabled'
|
||||
anonymous_comments = settings.anonymous_comments
|
||||
anon = portal_membership.isAnonymousUser()
|
||||
if captcha_enabled and anonymous_comments and anon:
|
||||
if not 'captcha' in data:
|
||||
data['captcha'] = u""
|
||||
captcha = CaptchaValidator(self.context,
|
||||
@@ -159,38 +171,42 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
|
||||
|
||||
# some attributes are not always set
|
||||
author_name = u""
|
||||
author_email = u""
|
||||
user_notification = None
|
||||
|
||||
# Create comment
|
||||
comment = createObject('plone.Comment')
|
||||
|
||||
# Set comment mime type to current setting in the discussion registry
|
||||
comment.mime_type = settings.text_transform
|
||||
|
||||
# Set comment attributes (including extended comment form attributes)
|
||||
for attribute in self.fields.keys():
|
||||
setattr(comment, attribute, data[attribute])
|
||||
# Make sure author_name is properly encoded
|
||||
# Make sure author_name/ author_email is properly encoded
|
||||
if 'author_name' in data:
|
||||
author_name = data['author_name']
|
||||
if isinstance(author_name, str):
|
||||
author_name = unicode(author_name, 'utf-8')
|
||||
if 'author_email' in data:
|
||||
author_email = data['author_email']
|
||||
if isinstance(author_email, str):
|
||||
author_email = unicode(author_email, 'utf-8')
|
||||
|
||||
# Set comment author properties for anonymous users or members
|
||||
can_reply = getSecurityManager().checkPermission('Reply to item',
|
||||
context)
|
||||
portal_membership = getToolByName(self.context, 'portal_membership')
|
||||
if portal_membership.isAnonymousUser() and \
|
||||
settings.anonymous_comments:
|
||||
if anon and anonymous_comments:
|
||||
# Anonymous Users
|
||||
comment.creator = author_name
|
||||
comment.author_name = author_name
|
||||
comment.author_email = author_email
|
||||
comment.user_notification = user_notification
|
||||
comment.user_notification = None
|
||||
comment.creation_date = datetime.utcnow()
|
||||
comment.modification_date = datetime.utcnow()
|
||||
elif not portal_membership.isAnonymousUser() and can_reply:
|
||||
# Member
|
||||
member = portal_membership.getAuthenticatedMember()
|
||||
username = member.getUserName()
|
||||
userid = member.getUserId()
|
||||
memberid = member.getId()
|
||||
user = member.getUser()
|
||||
email = member.getProperty('email')
|
||||
fullname = member.getProperty('fullname')
|
||||
if not fullname or fullname == '':
|
||||
@@ -200,21 +216,27 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
|
||||
fullname = unicode(fullname, 'utf-8')
|
||||
if email and isinstance(email, str):
|
||||
email = unicode(email, 'utf-8')
|
||||
comment.creator = fullname
|
||||
comment.author_username = username
|
||||
comment.changeOwnership(user, recursive=False)
|
||||
comment.manage_setLocalRoles(memberid, ["Owner"])
|
||||
comment.creator = memberid
|
||||
comment.author_username = memberid
|
||||
comment.author_name = fullname
|
||||
|
||||
# XXX: according to IComment interface author_email must not be
|
||||
# set for logged in users, cite:
|
||||
# "for anonymous comments only, set to None for logged in comments"
|
||||
comment.author_email = email
|
||||
comment.user_notification = user_notification
|
||||
# /XXX
|
||||
|
||||
comment.creation_date = datetime.utcnow()
|
||||
comment.modification_date = datetime.utcnow()
|
||||
|
||||
# add local "Owner" role for current user
|
||||
comment.manage_setLocalRoles(userid, ['Owner'])
|
||||
|
||||
else: # pragma: no cover
|
||||
raise Unauthorized("Anonymous user tries to post a comment, but "
|
||||
"anonymous commenting is disabled. Or user does not have the "
|
||||
"'reply to item' permission.")
|
||||
raise Unauthorized(
|
||||
u"Anonymous user tries to post a comment, but anonymous "
|
||||
u"commenting is disabled. Or user does not have the "
|
||||
u"'reply to item' permission."
|
||||
)
|
||||
|
||||
# Add comment to conversation
|
||||
conversation = IConversation(self.__parent__)
|
||||
@@ -235,7 +257,11 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
|
||||
can_review = getSecurityManager().checkPermission('Review comments',
|
||||
context)
|
||||
workflowTool = getToolByName(context, 'portal_workflow')
|
||||
comment_review_state = workflowTool.getInfoFor(comment, 'review_state')
|
||||
comment_review_state = workflowTool.getInfoFor(
|
||||
comment,
|
||||
'review_state',
|
||||
None
|
||||
)
|
||||
if comment_review_state == 'pending' and not can_review:
|
||||
# Show info message when comment moderation is enabled
|
||||
IStatusMessage(self.context.REQUEST).addStatusMessage(
|
||||
@@ -260,9 +286,13 @@ class CommentsViewlet(ViewletBase):
|
||||
|
||||
def update(self):
|
||||
super(CommentsViewlet, self).update()
|
||||
if self.is_discussion_allowed() and \
|
||||
(self.is_anonymous() and self.anonymous_discussion_allowed() \
|
||||
or self.can_reply()):
|
||||
discussion_allowed = self.is_discussion_allowed()
|
||||
anonymous_allowed_or_can_reply = (
|
||||
self.is_anonymous()
|
||||
and self.anonymous_discussion_allowed()
|
||||
or self.can_reply()
|
||||
)
|
||||
if discussion_allowed and anonymous_allowed_or_can_reply:
|
||||
z2.switch_on(self, request_layer=IFormLayer)
|
||||
self.form = self.form(aq_inner(self.context), self.request)
|
||||
alsoProvides(self.form, IWrappedForm)
|
||||
@@ -293,19 +323,35 @@ class CommentsViewlet(ViewletBase):
|
||||
comments without replies can be deleted.
|
||||
"""
|
||||
try:
|
||||
return comment.restrictedTraverse('@@delete-own-comment').can_delete()
|
||||
return comment.restrictedTraverse(
|
||||
'@@delete-own-comment').can_delete()
|
||||
except Unauthorized:
|
||||
return False
|
||||
|
||||
def could_delete_own(self, comment):
|
||||
"""Returns true if the current user could delete the comment if it had no
|
||||
replies. This is used to prepare hidden form buttons for JS.
|
||||
"""Returns true if the current user could delete the comment if it had
|
||||
no replies. This is used to prepare hidden form buttons for JS.
|
||||
"""
|
||||
try:
|
||||
return comment.restrictedTraverse('@@delete-own-comment').could_delete()
|
||||
return comment.restrictedTraverse(
|
||||
'@@delete-own-comment').could_delete()
|
||||
except Unauthorized:
|
||||
return False
|
||||
|
||||
def can_edit(self, reply):
|
||||
"""Returns true if current user has the 'Edit comments'
|
||||
permission.
|
||||
"""
|
||||
return getSecurityManager().checkPermission('Edit comments',
|
||||
aq_inner(reply))
|
||||
|
||||
def can_delete(self, reply):
|
||||
"""By default requires 'Review comments'.
|
||||
If 'delete own comments' is enabled, requires 'Edit comments'.
|
||||
"""
|
||||
return getSecurityManager().checkPermission('Delete comments',
|
||||
aq_inner(reply))
|
||||
|
||||
def is_discussion_allowed(self):
|
||||
context = aq_inner(self.context)
|
||||
return context.restrictedTraverse('@@conversation_view').enabled()
|
||||
@@ -366,7 +412,10 @@ class CommentsViewlet(ViewletBase):
|
||||
returned with workflow actions.
|
||||
"""
|
||||
context = aq_inner(self.context)
|
||||
conversation = IConversation(context)
|
||||
conversation = IConversation(context, None)
|
||||
|
||||
if conversation is None:
|
||||
return iter([])
|
||||
|
||||
wf = getToolByName(context, 'portal_workflow')
|
||||
# workflow_actions is only true when user
|
||||
@@ -377,8 +426,10 @@ class CommentsViewlet(ViewletBase):
|
||||
for r in conversation.getThreads():
|
||||
comment_obj = r['comment']
|
||||
# list all possible workflow actions
|
||||
actions = [a for a in wf.listActionInfos(object=comment_obj)
|
||||
if a['category'] == 'workflow' and a['allowed']]
|
||||
actions = [
|
||||
a for a in wf.listActionInfos(object=comment_obj)
|
||||
if a['category'] == 'workflow' and a['allowed']
|
||||
]
|
||||
r = r.copy()
|
||||
r['actions'] = actions
|
||||
yield r
|
||||
@@ -394,7 +445,7 @@ class CommentsViewlet(ViewletBase):
|
||||
yield r
|
||||
|
||||
# Return all direct replies
|
||||
if conversation.total_comments > 0:
|
||||
if len(conversation.objectIds()):
|
||||
if workflow_actions:
|
||||
return replies_with_workflow_actions()
|
||||
else:
|
||||
@@ -410,13 +461,14 @@ class CommentsViewlet(ViewletBase):
|
||||
|
||||
if username is None:
|
||||
# return the default user image if no username is given
|
||||
return 'defaultUser.gif'
|
||||
return 'defaultUser.png'
|
||||
else:
|
||||
portal_membership = getToolByName(self.context,
|
||||
'portal_membership',
|
||||
None)
|
||||
return portal_membership.getPersonalPortrait(username)\
|
||||
.absolute_url()
|
||||
return portal_membership\
|
||||
.getPersonalPortrait(username)\
|
||||
.absolute_url()
|
||||
|
||||
def anonymous_discussion_allowed(self):
|
||||
# Check if anonymous comments are allowed in the registry
|
||||
@@ -424,6 +476,12 @@ class CommentsViewlet(ViewletBase):
|
||||
settings = registry.forInterface(IDiscussionSettings, check=False)
|
||||
return settings.anonymous_comments
|
||||
|
||||
def edit_comment_allowed(self):
|
||||
# Check if editing comments is allowed in the registry
|
||||
registry = queryUtility(IRegistry)
|
||||
settings = registry.forInterface(IDiscussionSettings, check=False)
|
||||
return settings.edit_comment_enabled
|
||||
|
||||
def show_commenter_image(self):
|
||||
# Check if showing commenter image is enabled in the registry
|
||||
registry = queryUtility(IRegistry)
|
||||
|
||||
Reference in New Issue
Block a user