This commit is contained in:
Jens W. Klein
2022-05-01 23:14:09 +02:00
parent e72d86b985
commit 34b758f2bd
38 changed files with 2155 additions and 2179 deletions
+16 -14
View File
@@ -21,9 +21,9 @@ from zope.publisher.interfaces.browser import IDefaultBrowserLayer
@adapter(Comment)
@interface.implementer(ICaptcha)
class Captcha(Persistent):
"""Captcha input field.
"""
captcha = u''
"""Captcha input field."""
captcha = u""
Captcha = factory(Captcha)
@@ -47,22 +47,24 @@ class CaptchaExtender(extensible.FormExtender):
registry = queryUtility(IRegistry)
settings = registry.forInterface(IDiscussionSettings, check=False)
self.captcha = settings.captcha
portal_membership = getToolByName(self.context, 'portal_membership')
portal_membership = getToolByName(self.context, "portal_membership")
self.isAnon = portal_membership.isAnonymousUser()
def update(self):
if self.captcha != 'disabled' and self.isAnon:
if self.captcha != "disabled" and self.isAnon:
# Add a captcha field if captcha is enabled in the registry
self.add(ICaptcha, prefix='')
if self.captcha == 'captcha':
self.add(ICaptcha, prefix="")
if self.captcha == "captcha":
from plone.formwidget.captcha import CaptchaFieldWidget
self.form.fields['captcha'].widgetFactory = CaptchaFieldWidget
elif self.captcha == 'recaptcha':
self.form.fields["captcha"].widgetFactory = CaptchaFieldWidget
elif self.captcha == "recaptcha":
from plone.formwidget.recaptcha import ReCaptchaFieldWidget
self.form.fields['captcha'].widgetFactory = \
ReCaptchaFieldWidget
elif self.captcha == 'norobots':
self.form.fields["captcha"].widgetFactory = ReCaptchaFieldWidget
elif self.captcha == "norobots":
from collective.z3cform.norobots import NorobotsFieldWidget
self.form.fields['captcha'].widgetFactory = NorobotsFieldWidget
self.form.fields["captcha"].widgetFactory = NorobotsFieldWidget
else:
self.form.fields['captcha'].mode = interfaces.HIDDEN_MODE
self.form.fields["captcha"].mode = interfaces.HIDDEN_MODE
+24 -28
View File
@@ -37,8 +37,7 @@ class View(BrowserView):
context = aq_inner(self.context)
registry = getUtility(IRegistry)
view_action_types = registry.get(
'plone.types_use_view_action_in_listings', [])
view_action_types = registry.get("plone.types_use_view_action_in_listings", [])
obj = aq_parent(aq_parent(context))
url = obj.absolute_url()
@@ -49,33 +48,34 @@ class View(BrowserView):
will redirect right to the binary object, bypassing comments.
"""
if obj.portal_type in view_action_types:
url = '{0}/view'.format(url)
url = "{0}/view".format(url)
self.request.response.redirect('{0}#{1}'.format(url, context.id))
self.request.response.redirect("{0}#{1}".format(url, context.id))
class EditCommentForm(CommentForm):
"""Form to edit an existing comment."""
ignoreContext = True
id = 'edit-comment-form'
label = _(u'edit_comment_form_title', default=u'Edit comment')
id = "edit-comment-form"
label = _(u"edit_comment_form_title", default=u"Edit comment")
def updateWidgets(self):
super(EditCommentForm, self).updateWidgets()
self.widgets['text'].value = self.context.text
self.widgets["text"].value = self.context.text
# We have to rename the id, otherwise TinyMCE can't initialize
# because there are two textareas with the same id.
self.widgets['text'].id = 'overlay-comment-text'
self.widgets["text"].id = "overlay-comment-text"
def _redirect(self, target=''):
def _redirect(self, target=""):
if not target:
portal_state = getMultiAdapter((self.context, self.request),
name=u'plone_portal_state')
portal_state = getMultiAdapter(
(self.context, self.request), name=u"plone_portal_state"
)
target = portal_state.portal_url()
self.request.response.redirect(target)
@button.buttonAndHandler(_(u'label_save',
default=u'Save'), name='comment')
@button.buttonAndHandler(_(u"label_save", default=u"Save"), name="comment")
def handleComment(self, action):
# Validate form
@@ -84,32 +84,28 @@ class EditCommentForm(CommentForm):
return
# Check permissions
can_edit = getSecurityManager().checkPermission(
'Edit comments',
self.context)
mtool = getToolByName(self.context, 'portal_membership')
can_edit = getSecurityManager().checkPermission("Edit comments", self.context)
mtool = getToolByName(self.context, "portal_membership")
if mtool.isAnonymousUser() or not can_edit:
return
# Update text
self.context.text = data['text']
self.context.text = data["text"]
# Notify that the object has been modified
notify(ObjectModifiedEvent(self.context))
# Redirect to comment
IStatusMessage(self.request).add(_(u'comment_edit_notification',
default='Comment was edited'),
type='info')
return self._redirect(
target=self.action.replace('@@edit-comment', '@@view'))
IStatusMessage(self.request).add(
_(u"comment_edit_notification", default="Comment was edited"), type="info"
)
return self._redirect(target=self.action.replace("@@edit-comment", "@@view"))
@button.buttonAndHandler(_(u'cancel_form_button',
default=u'Cancel'), name='cancel')
@button.buttonAndHandler(_(u"cancel_form_button", default=u"Cancel"), name="cancel")
def handle_cancel(self, action):
IStatusMessage(self.request).add(
_(u'comment_edit_cancel_notification',
default=u'Edit comment cancelled'),
type='info')
_(u"comment_edit_cancel_notification", default=u"Edit comment cancelled"),
type="info",
)
return self._redirect(target=self.context.absolute_url())
+146 -149
View File
@@ -35,28 +35,28 @@ from zope.interface import alsoProvides
COMMENT_DESCRIPTION_PLAIN_TEXT = _(
u'comment_description_plain_text',
default=u'You can add a comment by filling out the form below. '
u'Plain text formatting.',
u"comment_description_plain_text",
default=u"You can add a comment by filling out the form below. "
u"Plain text formatting.",
)
COMMENT_DESCRIPTION_MARKDOWN = _(
u'comment_description_markdown',
default=u'You can add a comment by filling out the form below. '
u'Plain text formatting. You can use the Markdown syntax for '
u'links and images.',
u"comment_description_markdown",
default=u"You can add a comment by filling out the form below. "
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. '
u'Plain text formatting. Web and email addresses are '
u'transformed into clickable links.',
u"comment_description_intelligent_text",
default=u"You can add a comment by filling out the form below. "
u"Plain text formatting. Web and email addresses are "
u"transformed into clickable links.",
)
COMMENT_DESCRIPTION_MODERATION_ENABLED = _(
u'comment_description_moderation_enabled',
default=u'Comments are moderated.',
u"comment_description_moderation_enabled",
default=u"Comments are moderated.",
)
@@ -64,30 +64,31 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
ignoreContext = True # don't use context to get widget data
id = None
label = _(u'Add a comment')
fields = field.Fields(IComment).omit('portal_type',
'__parent__',
'__name__',
'comment_id',
'mime_type',
'creator',
'creation_date',
'modification_date',
'author_username',
'title')
label = _(u"Add a comment")
fields = field.Fields(IComment).omit(
"portal_type",
"__parent__",
"__name__",
"comment_id",
"mime_type",
"creator",
"creation_date",
"modification_date",
"author_username",
"title",
)
def updateFields(self):
super(CommentForm, self).updateFields()
self.fields['user_notification'].widgetFactory = \
SingleCheckBoxFieldWidget
self.fields["user_notification"].widgetFactory = SingleCheckBoxFieldWidget
def updateWidgets(self):
super(CommentForm, self).updateWidgets()
# Widgets
self.widgets['in_reply_to'].mode = interfaces.HIDDEN_MODE
self.widgets['text'].addClass('autoresize')
self.widgets['user_notification'].label = _(u'')
self.widgets["in_reply_to"].mode = interfaces.HIDDEN_MODE
self.widgets["text"].addClass("autoresize")
self.widgets["user_notification"].label = _(u"")
# Reset widget field settings to their defaults, which may be changed
# further on. Otherwise, the email field might get set to required
# when an anonymous user visits, and then remain required when an
@@ -97,19 +98,19 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
# would have no effect until the instance was restarted. Note that the
# widget is new each time, but the field is the same item in memory as
# the previous time.
self.widgets['author_email'].field.required = False
self.widgets["author_email"].field.required = False
# The widget is new, but its 'required' setting is based on the
# previous value on the field, so we need to reset it here. Changing
# the field in updateFields does not help.
self.widgets['author_email'].required = False
self.widgets["author_email"].required = False
# 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'
self.widgets["text"].id = "form-widgets-comment-text"
# Anonymous / Logged-in
mtool = getToolByName(self.context, 'portal_membership')
mtool = getToolByName(self.context, "portal_membership")
anon = mtool.isAnonymousUser()
registry = queryUtility(IRegistry)
@@ -119,52 +120,51 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
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
self.widgets["author_email"].field.required = True
self.widgets["author_email"].required = True
else:
self.widgets['author_email'].mode = interfaces.HIDDEN_MODE
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
self.widgets["author_name"].mode = interfaces.HIDDEN_MODE
self.widgets["author_email"].mode = interfaces.HIDDEN_MODE
member = mtool.getAuthenticatedMember()
member_email = member.getProperty('email')
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
member_email_is_empty = member_email == ''
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
self.widgets["user_notification"].mode = interfaces.HIDDEN_MODE
def updateActions(self):
super(CommentForm, self).updateActions()
self.actions['cancel'].addClass('btn btn-secondary')
self.actions['cancel'].addClass('hide')
self.actions['comment'].addClass('btn btn-primary')
self.actions["cancel"].addClass("btn btn-secondary")
self.actions["cancel"].addClass("hide")
self.actions["comment"].addClass("btn btn-primary")
def get_author(self, data):
context = aq_inner(self.context)
# some attributes are not always set
author_name = u''
author_name = u""
# Make sure author_name/ author_email is properly encoded
if 'author_name' in data:
author_name = safe_unicode(data['author_name'])
if 'author_email' in data:
author_email = safe_unicode(data['author_email'])
if "author_name" in data:
author_name = safe_unicode(data["author_name"])
if "author_email" in data:
author_email = safe_unicode(data["author_email"])
# Set comment author properties for anonymous users or members
portal_membership = getToolByName(context, 'portal_membership')
portal_membership = getToolByName(context, "portal_membership")
anon = portal_membership.isAnonymousUser()
if not anon and getSecurityManager().checkPermission(
'Reply to item', context):
if not anon and getSecurityManager().checkPermission("Reply to item", context):
# Member
member = portal_membership.getAuthenticatedMember()
email = safe_unicode(member.getProperty('email'))
fullname = member.getProperty('fullname')
if not fullname or fullname == '':
email = safe_unicode(member.getProperty("email"))
fullname = member.getProperty("fullname")
if not fullname or fullname == "":
fullname = member.getUserName()
fullname = safe_unicode(fullname)
author_name = fullname
@@ -179,7 +179,7 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
def create_comment(self, data):
context = aq_inner(self.context)
comment = createObject('plone.Comment')
comment = createObject("plone.Comment")
registry = queryUtility(IRegistry)
settings = registry.forInterface(IDiscussionSettings, check=False)
@@ -200,42 +200,44 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
comment.author_name, comment.author_email = self.get_author(data)
# Set comment author properties for anonymous users or members
portal_membership = getToolByName(context, 'portal_membership')
portal_membership = getToolByName(context, "portal_membership")
anon = portal_membership.isAnonymousUser()
if anon and anonymous_comments:
# Anonymous Users
comment.user_notification = None
elif not anon and getSecurityManager().checkPermission(
'Reply to item', context):
"Reply to item", context
):
# Member
member = portal_membership.getAuthenticatedMember()
memberid = member.getId()
user = member.getUser()
comment.changeOwnership(user, recursive=False)
comment.manage_setLocalRoles(memberid, ['Owner'])
comment.manage_setLocalRoles(memberid, ["Owner"])
comment.creator = memberid
comment.author_username = memberid
else: # pragma: no cover
raise Unauthorized(
u'Anonymous user tries to post a comment, but anonymous '
u'commenting is disabled. Or user does not have the '
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.",
)
return comment
@button.buttonAndHandler(_(u'add_comment_button', default=u'Comment'),
name='comment')
@button.buttonAndHandler(
_(u"add_comment_button", default=u"Comment"), name="comment"
)
def handleComment(self, action):
context = aq_inner(self.context)
# Check if conversation is enabled on this content object
if not self.__parent__.restrictedTraverse(
'@@conversation_view',
"@@conversation_view",
).enabled():
raise Unauthorized(
'Discussion is not enabled for this content object.',
"Discussion is not enabled for this content object.",
)
# Validation form
@@ -246,28 +248,26 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
# Validate Captcha
registry = queryUtility(IRegistry)
settings = registry.forInterface(IDiscussionSettings, check=False)
portal_membership = getToolByName(self.context, 'portal_membership')
captcha_enabled = settings.captcha != 'disabled'
portal_membership = getToolByName(self.context, "portal_membership")
captcha_enabled = settings.captcha != "disabled"
anonymous_comments = settings.anonymous_comments
anon = portal_membership.isAnonymousUser()
if captcha_enabled and anonymous_comments and anon:
if 'captcha' not in data:
data['captcha'] = u''
captcha = CaptchaValidator(self.context,
self.request,
None,
ICaptcha['captcha'],
None)
captcha.validate(data['captcha'])
if "captcha" not in data:
data["captcha"] = u""
captcha = CaptchaValidator(
self.context, self.request, None, ICaptcha["captcha"], None
)
captcha.validate(data["captcha"])
# Create comment
comment = self.create_comment(data)
# Add comment to conversation
conversation = IConversation(self.__parent__)
if data['in_reply_to']:
if data["in_reply_to"]:
# Add a reply to an existing comment
conversation_to_reply_to = conversation.get(data['in_reply_to'])
conversation_to_reply_to = conversation.get(data["in_reply_to"])
replies = IReplies(conversation_to_reply_to)
comment_id = replies.addComment(comment)
else:
@@ -279,25 +279,24 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
# shown to the user that his/her comment awaits moderation. If the user
# has 'review comments' permission, he/she is redirected directly
# to the comment.
can_review = getSecurityManager().checkPermission('Review comments',
context)
workflowTool = getToolByName(context, 'portal_workflow')
can_review = getSecurityManager().checkPermission("Review comments", context)
workflowTool = getToolByName(context, "portal_workflow")
comment_review_state = workflowTool.getInfoFor(
comment,
'review_state',
"review_state",
None,
)
if comment_review_state == 'pending' and not can_review:
if comment_review_state == "pending" and not can_review:
# Show info message when comment moderation is enabled
IStatusMessage(self.context.REQUEST).addStatusMessage(
_('Your comment awaits moderator approval.'),
type='info')
_("Your comment awaits moderator approval."), type="info"
)
self.request.response.redirect(self.action)
else:
# Redirect to comment (inside a content object page)
self.request.response.redirect(self.action + '#' + str(comment_id))
self.request.response.redirect(self.action + "#" + str(comment_id))
@button.buttonAndHandler(_(u'Cancel'))
@button.buttonAndHandler(_(u"Cancel"))
def handleCancel(self, action):
# This method should never be called, it's only there to show
# a cancel button that is handled by a jQuery method.
@@ -307,15 +306,15 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
class CommentsViewlet(ViewletBase):
form = CommentForm
index = ViewPageTemplateFile('comments.pt')
index = ViewPageTemplateFile("comments.pt")
def update(self):
super(CommentsViewlet, self).update()
discussion_allowed = self.is_discussion_allowed()
anonymous_allowed_or_can_reply = (
self.is_anonymous() and
self.anonymous_discussion_allowed() or
self.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)
@@ -326,30 +325,29 @@ class CommentsViewlet(ViewletBase):
# view methods
def can_reply(self):
"""Returns true if current user has the 'Reply to item' permission.
"""
return getSecurityManager().checkPermission('Reply to item',
aq_inner(self.context))
"""Returns true if current user has the 'Reply to item' permission."""
return getSecurityManager().checkPermission(
"Reply to item", aq_inner(self.context)
)
def can_manage(self):
"""We keep this method for <= 1.0b9 backward compatibility. Since we do
not want any API changes in beta releases.
not want any API changes in beta releases.
"""
return self.can_review()
def can_review(self):
"""Returns true if current user has the 'Review comments' permission.
"""
return getSecurityManager().checkPermission('Review comments',
aq_inner(self.context))
"""Returns true if current user has the 'Review comments' permission."""
return getSecurityManager().checkPermission(
"Review comments", aq_inner(self.context)
)
def can_delete_own(self, comment):
"""Returns true if the current user can delete the comment. Only
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
@@ -358,8 +356,7 @@ class CommentsViewlet(ViewletBase):
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
@@ -367,58 +364,63 @@ class CommentsViewlet(ViewletBase):
"""Returns true if current user has the 'Edit comments'
permission.
"""
return getSecurityManager().checkPermission('Edit comments',
aq_inner(reply))
return getSecurityManager().checkPermission("Edit comments", aq_inner(reply))
def can_delete(self, reply):
"""Returns true if current user has the 'Delete comments'
permission.
"""
return getSecurityManager().checkPermission('Delete comments',
aq_inner(reply))
return getSecurityManager().checkPermission("Delete comments", aq_inner(reply))
def is_discussion_allowed(self):
context = aq_inner(self.context)
return context.restrictedTraverse('@@conversation_view').enabled()
return context.restrictedTraverse("@@conversation_view").enabled()
def comment_transform_message(self):
"""Returns the description that shows up above the comment text,
dependent on the text_transform setting and the comment moderation
workflow in the discussion control panel.
dependent on the text_transform setting and the comment moderation
workflow in the discussion control panel.
"""
context = aq_inner(self.context)
registry = queryUtility(IRegistry)
settings = registry.forInterface(IDiscussionSettings, check=False)
# text transform setting
if settings.text_transform == 'text/x-web-intelligent':
message = translate(Message(COMMENT_DESCRIPTION_INTELLIGENT_TEXT),
context=self.request)
elif settings.text_transform == 'text/x-web-markdown':
message = translate(Message(COMMENT_DESCRIPTION_MARKDOWN),
context=self.request)
if settings.text_transform == "text/x-web-intelligent":
message = translate(
Message(COMMENT_DESCRIPTION_INTELLIGENT_TEXT), context=self.request
)
elif settings.text_transform == "text/x-web-markdown":
message = translate(
Message(COMMENT_DESCRIPTION_MARKDOWN), context=self.request
)
else:
message = translate(Message(COMMENT_DESCRIPTION_PLAIN_TEXT),
context=self.request)
message = translate(
Message(COMMENT_DESCRIPTION_PLAIN_TEXT), context=self.request
)
# comment workflow
wftool = getToolByName(context, 'portal_workflow', None)
workflow_chain = wftool.getChainForPortalType('Discussion Item')
wftool = getToolByName(context, "portal_workflow", None)
workflow_chain = wftool.getChainForPortalType("Discussion Item")
if workflow_chain:
comment_workflow = workflow_chain[0]
comment_workflow = wftool[comment_workflow]
# check if the current workflow implements a pending state. If this
# is true comments are moderated
if 'pending' in comment_workflow.states:
message = message + ' ' + \
translate(Message(COMMENT_DESCRIPTION_MODERATION_ENABLED),
context=self.request)
if "pending" in comment_workflow.states:
message = (
message
+ " "
+ translate(
Message(COMMENT_DESCRIPTION_MODERATION_ENABLED),
context=self.request,
)
)
return message
def has_replies(self, workflow_actions=False):
"""Returns true if there are replies.
"""
"""Returns true if there are replies."""
if self.get_replies(workflow_actions) is not None:
try:
next(self.get_replies(workflow_actions))
@@ -442,31 +444,32 @@ class CommentsViewlet(ViewletBase):
if conversation is None:
return iter([])
wf = getToolByName(context, 'portal_workflow')
wf = getToolByName(context, "portal_workflow")
# workflow_actions is only true when user
# has 'Manage portal' permission
def replies_with_workflow_actions():
# Generator that returns replies dict with workflow actions
for r in conversation.getThreads():
comment_obj = r['comment']
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']
a
for a in wf.listActionInfos(object=comment_obj)
if a["category"] == "workflow" and a["allowed"]
]
r = r.copy()
r['actions'] = actions
r["actions"] = actions
yield r
def published_replies():
# Generator that returns replies dict with workflow status.
for r in conversation.getThreads():
comment_obj = r['comment']
workflow_status = wf.getInfoFor(comment_obj, 'review_state')
if workflow_status == 'published':
comment_obj = r["comment"]
workflow_status = wf.getInfoFor(comment_obj, "review_state")
if workflow_status == "published":
r = r.copy()
r['workflow_status'] = workflow_status
r["workflow_status"] = workflow_status
yield r
# Return all direct replies
@@ -480,20 +483,16 @@ class CommentsViewlet(ViewletBase):
if username is None:
return None
else:
return '{0}/author/{1}'.format(self.context.portal_url(), username)
return "{0}/author/{1}".format(self.context.portal_url(), username)
def get_commenter_portrait(self, username=None):
if username is None:
# return the default user image if no username is given
return 'defaultUser.png'
return "defaultUser.png"
else:
portal_membership = getToolByName(self.context,
'portal_membership',
None)
return portal_membership\
.getPersonalPortrait(username)\
.absolute_url()
portal_membership = getToolByName(self.context, "portal_membership", None)
return portal_membership.getPersonalPortrait(username).absolute_url()
def anonymous_discussion_allowed(self):
# Check if anonymous comments are allowed in the registry
@@ -520,20 +519,18 @@ class CommentsViewlet(ViewletBase):
return settings.show_commenter_image
def is_anonymous(self):
portal_membership = getToolByName(self.context,
'portal_membership',
None)
portal_membership = getToolByName(self.context, "portal_membership", None)
return portal_membership.isAnonymousUser()
def login_action(self):
return '{0}/login_form?came_from={1}'.format(
return "{0}/login_form?came_from={1}".format(
self.navigation_root_url,
quote(self.request.get('URL', '')),
quote(self.request.get("URL", "")),
)
def format_time(self, time):
# We have to transform Python datetime into Zope DateTime
# before we can call toLocalizedTime.
util = getToolByName(self.context, 'translation_service')
util = getToolByName(self.context, "translation_service")
zope_time = DateTime(time.isoformat())
return util.toLocalizedTime(zope_time, long_format=True)
+91 -92
View File
@@ -28,42 +28,40 @@ except ImportError:
class DiscussionSettingsEditForm(controlpanel.RegistryEditForm):
"""Discussion settings form.
"""
"""Discussion settings form."""
schema = IDiscussionSettings
id = 'DiscussionSettingsEditForm'
label = _(u'Discussion settings')
id = "DiscussionSettingsEditForm"
label = _(u"Discussion settings")
description = _(
u'help_discussion_settings_editform',
default=u'Some discussion related settings are not '
u'located in the Discussion Control Panel.\n'
u'To enable comments for a specific content type, '
u'go to the Types Control Panel of this type and '
u'choose "Allow comments".\n'
u'To enable the moderation workflow for comments, '
u'go to the Types Control Panel, choose '
u'"Comment" and set workflow to '
u'"Comment Review Workflow".',
u"help_discussion_settings_editform",
default=u"Some discussion related settings are not "
u"located in the Discussion Control Panel.\n"
u"To enable comments for a specific content type, "
u"go to the Types Control Panel of this type and "
u'choose "Allow comments".\n'
u"To enable the moderation workflow for comments, "
u"go to the Types Control Panel, choose "
u'"Comment" and set workflow to '
u'"Comment Review Workflow".',
)
def updateFields(self):
super(DiscussionSettingsEditForm, self).updateFields()
self.fields['globally_enabled'].widgetFactory = \
SingleCheckBoxFieldWidget
self.fields['moderation_enabled'].widgetFactory = \
SingleCheckBoxFieldWidget
self.fields['edit_comment_enabled'].widgetFactory = \
SingleCheckBoxFieldWidget
self.fields['delete_own_comment_enabled'].widgetFactory = \
SingleCheckBoxFieldWidget
self.fields['anonymous_comments'].widgetFactory = \
SingleCheckBoxFieldWidget
self.fields['show_commenter_image'].widgetFactory = \
SingleCheckBoxFieldWidget
self.fields['moderator_notification_enabled'].widgetFactory = \
SingleCheckBoxFieldWidget
self.fields['user_notification_enabled'].widgetFactory = \
SingleCheckBoxFieldWidget
self.fields["globally_enabled"].widgetFactory = SingleCheckBoxFieldWidget
self.fields["moderation_enabled"].widgetFactory = SingleCheckBoxFieldWidget
self.fields["edit_comment_enabled"].widgetFactory = SingleCheckBoxFieldWidget
self.fields[
"delete_own_comment_enabled"
].widgetFactory = SingleCheckBoxFieldWidget
self.fields["anonymous_comments"].widgetFactory = SingleCheckBoxFieldWidget
self.fields["show_commenter_image"].widgetFactory = SingleCheckBoxFieldWidget
self.fields[
"moderator_notification_enabled"
].widgetFactory = SingleCheckBoxFieldWidget
self.fields[
"user_notification_enabled"
].widgetFactory = SingleCheckBoxFieldWidget
def updateWidgets(self):
try:
@@ -73,33 +71,31 @@ class DiscussionSettingsEditForm(controlpanel.RegistryEditForm):
# provide auto-upgrade
update_registry(self.context)
super(DiscussionSettingsEditForm, self).updateWidgets()
self.widgets['globally_enabled'].label = _(u'Enable Comments')
self.widgets['anonymous_comments'].label = _(u'Anonymous Comments')
self.widgets['show_commenter_image'].label = _(u'Commenter Image')
self.widgets['moderator_notification_enabled'].label = _(
u'Moderator Email Notification',
self.widgets["globally_enabled"].label = _(u"Enable Comments")
self.widgets["anonymous_comments"].label = _(u"Anonymous Comments")
self.widgets["show_commenter_image"].label = _(u"Commenter Image")
self.widgets["moderator_notification_enabled"].label = _(
u"Moderator Email Notification",
)
self.widgets['user_notification_enabled'].label = _(
u'User Email Notification',
self.widgets["user_notification_enabled"].label = _(
u"User Email Notification",
)
@button.buttonAndHandler(_('Save'), name=None)
@button.buttonAndHandler(_("Save"), name=None)
def handleSave(self, action):
data, errors = self.extractData()
if errors:
self.status = self.formErrorsMessage
return
self.applyChanges(data)
IStatusMessage(self.request).addStatusMessage(_(u'Changes saved'),
'info')
self.context.REQUEST.RESPONSE.redirect('@@discussion-controlpanel')
IStatusMessage(self.request).addStatusMessage(_(u"Changes saved"), "info")
self.context.REQUEST.RESPONSE.redirect("@@discussion-controlpanel")
@button.buttonAndHandler(_('Cancel'), name='cancel')
@button.buttonAndHandler(_("Cancel"), name="cancel")
def handleCancel(self, action):
IStatusMessage(self.request).addStatusMessage(_(u'Edit cancelled'),
'info')
IStatusMessage(self.request).addStatusMessage(_(u"Edit cancelled"), "info")
self.request.response.redirect(
'{0}/{1}'.format(
"{0}/{1}".format(
self.context.absolute_url(),
self.control_panel_view,
),
@@ -107,10 +103,10 @@ class DiscussionSettingsEditForm(controlpanel.RegistryEditForm):
class DiscussionSettingsControlPanel(controlpanel.ControlPanelFormWrapper):
"""Discussion settings control panel.
"""
"""Discussion settings control panel."""
form = DiscussionSettingsEditForm
index = ViewPageTemplateFile('controlpanel.pt')
index = ViewPageTemplateFile("controlpanel.pt")
def __call__(self):
self.mailhost_warning()
@@ -126,43 +122,44 @@ class DiscussionSettingsControlPanel(controlpanel.ControlPanelFormWrapper):
def settings(self):
"""Compose a string that contains all registry settings that are
needed for the discussion control panel.
needed for the discussion control panel.
"""
registry = queryUtility(IRegistry)
settings = registry.forInterface(IDiscussionSettings, check=False)
wftool = getToolByName(self.context, 'portal_workflow', None)
workflow_chain = wftool.getChainForPortalType('Discussion Item')
wftool = getToolByName(self.context, "portal_workflow", None)
workflow_chain = wftool.getChainForPortalType("Discussion Item")
output = []
# Globally enabled
if settings.globally_enabled:
output.append('globally_enabled')
output.append("globally_enabled")
# Comment moderation
one_state_worklow_disabled = \
'comment_one_state_workflow' not in workflow_chain
comment_review_workflow_disabled = \
'comment_review_workflow' not in workflow_chain
one_state_worklow_disabled = "comment_one_state_workflow" not in workflow_chain
comment_review_workflow_disabled = (
"comment_review_workflow" not in workflow_chain
)
if one_state_worklow_disabled and comment_review_workflow_disabled:
output.append('moderation_custom')
output.append("moderation_custom")
elif settings.moderation_enabled:
output.append('moderation_enabled')
output.append("moderation_enabled")
if settings.edit_comment_enabled:
output.append('edit_comment_enabled')
output.append("edit_comment_enabled")
if settings.delete_own_comment_enabled:
output.append('delete_own_comment_enabled')
output.append("delete_own_comment_enabled")
# Anonymous comments
if settings.anonymous_comments:
output.append('anonymous_comments')
output.append("anonymous_comments")
# Invalid mail setting
ctrlOverview = getMultiAdapter((self.context, self.request),
name='overview-controlpanel')
ctrlOverview = getMultiAdapter(
(self.context, self.request), name="overview-controlpanel"
)
if ctrlOverview.mailhost_warning():
output.append('invalid_mail_setup')
output.append("invalid_mail_setup")
# Workflow
if workflow_chain:
@@ -170,69 +167,71 @@ class DiscussionSettingsControlPanel(controlpanel.ControlPanelFormWrapper):
output.append(discussion_workflow)
# Merge all settings into one string
return ' '.join(output)
return " ".join(output)
def mailhost_warning(self):
"""Returns true if mailhost is not configured properly.
"""
"""Returns true if mailhost is not configured properly."""
# Copied from Products.CMFPlone/controlpanel/browser/overview.py
registry = getUtility(IRegistry)
mail_settings = registry.forInterface(IMailSchema, prefix='plone')
mail_settings = registry.forInterface(IMailSchema, prefix="plone")
mailhost = mail_settings.smtp_host
email = mail_settings.email_from_address
if mailhost and email:
pass
else:
message = _(u'discussion_text_no_mailhost_configured',
default=u'You have not configured a mail host or a site \'From\' address, various features including contact forms, email notification and password reset will not work. Go to the E-Mail Settings to fix this.') # noqa: E501
IStatusMessage(self.request).addStatusMessage(message, 'warning')
message = _(
u"discussion_text_no_mailhost_configured",
default=u"You have not configured a mail host or a site 'From' address, various features including contact forms, email notification and password reset will not work. Go to the E-Mail Settings to fix this.",
) # noqa: E501
IStatusMessage(self.request).addStatusMessage(message, "warning")
def custom_comment_workflow_warning(self):
"""Return True if a custom comment workflow is enabled."""
wftool = getToolByName(self.context, 'portal_workflow', None)
workflow_chain = wftool.getChainForPortalType('Discussion Item')
one_state_workflow_enabled = \
'comment_one_state_workflow' in workflow_chain
comment_review_workflow_enabled = \
'comment_review_workflow' in workflow_chain
wftool = getToolByName(self.context, "portal_workflow", None)
workflow_chain = wftool.getChainForPortalType("Discussion Item")
one_state_workflow_enabled = "comment_one_state_workflow" in workflow_chain
comment_review_workflow_enabled = "comment_review_workflow" in workflow_chain
if one_state_workflow_enabled or comment_review_workflow_enabled:
pass
else:
message = _(u'discussion_text_custom_comment_workflow',
default=u'You have configured a custom workflow for the \'Discussion Item\' content type. You can enable/disable the comment moderation in this control panel only if you use one of the default \'Discussion Item\' workflows. Go to the Types control panel to choose a workflow for the \'Discussion Item\' type.') # noqa: E501
IStatusMessage(self.request).addStatusMessage(message, 'warning')
message = _(
u"discussion_text_custom_comment_workflow",
default=u"You have configured a custom workflow for the 'Discussion Item' content type. You can enable/disable the comment moderation in this control panel only if you use one of the default 'Discussion Item' workflows. Go to the Types control panel to choose a workflow for the 'Discussion Item' type.",
) # noqa: E501
IStatusMessage(self.request).addStatusMessage(message, "warning")
def notify_configuration_changed(event):
"""Event subscriber that is called every time the configuration changed.
"""
"""Event subscriber that is called every time the configuration changed."""
portal = getSite()
wftool = getToolByName(portal, 'portal_workflow', None)
wftool = getToolByName(portal, "portal_workflow", None)
if IRecordModifiedEvent.providedBy(event):
# Discussion control panel setting changed
if event.record.fieldName == 'moderation_enabled':
if event.record.fieldName == "moderation_enabled":
# Moderation enabled has changed
if event.record.value is True:
# Enable moderation workflow
wftool.setChainForPortalTypes(('Discussion Item',),
'comment_review_workflow')
wftool.setChainForPortalTypes(
("Discussion Item",), "comment_review_workflow"
)
else:
# Disable moderation workflow
wftool.setChainForPortalTypes(('Discussion Item',),
'comment_one_state_workflow')
wftool.setChainForPortalTypes(
("Discussion Item",), "comment_one_state_workflow"
)
if IConfigurationChangedEvent.providedBy(event):
# Types control panel setting changed
if 'workflow' in event.data:
if "workflow" in event.data:
registry = queryUtility(IRegistry)
settings = registry.forInterface(IDiscussionSettings, check=False)
workflow_chain = wftool.getChainForPortalType('Discussion Item')
workflow_chain = wftool.getChainForPortalType("Discussion Item")
if workflow_chain:
workflow = workflow_chain[0]
if workflow == 'comment_one_state_workflow':
if workflow == "comment_one_state_workflow":
settings.moderation_enabled = False
elif workflow == 'comment_review_workflow':
elif workflow == "comment_review_workflow":
settings.moderation_enabled = True
else:
# Custom workflow
+12 -12
View File
@@ -14,6 +14,7 @@ from zope.component import queryUtility
try:
from plone.dexterity.interfaces import IDexterityContent
DEXTERITY_INSTALLED = True
except ImportError:
DEXTERITY_INSTALLED = False
@@ -26,15 +27,14 @@ def traverse_parents(context):
if not IPloneSiteRoot.providedBy(obj):
obj_is_folderish = IFolderish.providedBy(obj)
obj_is_stuctural = not INonStructuralFolder.providedBy(obj)
if (obj_is_folderish and obj_is_stuctural):
flag = getattr(obj, 'allow_discussion', None)
if obj_is_folderish and obj_is_stuctural:
flag = getattr(obj, "allow_discussion", None)
if flag is not None:
return flag
return None
class ConversationView(object):
def enabled(self):
if DEXTERITY_INSTALLED and IDexterityContent.providedBy(self.context):
return self._enabled_for_dexterity_types()
@@ -42,7 +42,7 @@ class ConversationView(object):
return self._enabled_for_archetypes()
def _enabled_for_archetypes(self):
""" Returns True if discussion is enabled for this conversation.
"""Returns True if discussion is enabled for this conversation.
This method checks five different settings in order to figure out if
discussion is enabled on a specific content object:
@@ -82,7 +82,7 @@ class ConversationView(object):
return False
# If discussion is disabled for the object, bail out
obj_flag = getattr(aq_base(context), 'allow_discussion', None)
obj_flag = getattr(aq_base(context), "allow_discussion", None)
if obj_flag is False:
return False
@@ -91,16 +91,16 @@ class ConversationView(object):
folder_allow_discussion = traverse_parents(context)
if folder_allow_discussion:
if not getattr(self, 'allow_discussion', None):
if not getattr(self, "allow_discussion", None):
return True
else:
if obj_flag:
return True
# Check if discussion is allowed on the content type
portal_types = getToolByName(self, 'portal_types')
portal_types = getToolByName(self, "portal_types")
document_fti = getattr(portal_types, context.portal_type)
if not document_fti.getProperty('allow_discussion'):
if not document_fti.getProperty("allow_discussion"):
# If discussion is not allowed on the content type,
# check if 'allow discussion' is overridden on the content object.
if not obj_flag:
@@ -109,7 +109,7 @@ class ConversationView(object):
return True
def _enabled_for_dexterity_types(self):
""" Returns True if discussion is enabled for this conversation.
"""Returns True if discussion is enabled for this conversation.
This method checks five different settings in order to figure out if
discussion is enable on a specific content object:
@@ -134,11 +134,11 @@ class ConversationView(object):
return False
# Check if discussion is allowed on the content object
if safe_hasattr(context, 'allow_discussion'):
if safe_hasattr(context, "allow_discussion"):
if context.allow_discussion is not None:
return context.allow_discussion
# Check if discussion is allowed on the content type
portal_types = getToolByName(self, 'portal_types')
portal_types = getToolByName(self, "portal_types")
document_fti = getattr(portal_types, context.portal_type)
return document_fti.getProperty('allow_discussion')
return document_fti.getProperty("allow_discussion")
+82 -80
View File
@@ -18,21 +18,20 @@ from zope.event import notify
# Translations for generated values in buttons
# States
_('comment_pending', default='pending')
_("comment_pending", default="pending")
# _('comment_approved', default='published')
_('comment_published', default='published')
_('comment_rejected', default='rejected')
_('comment_spam', default='marked as spam')
_("comment_published", default="published")
_("comment_rejected", default="rejected")
_("comment_spam", default="marked as spam")
# Transitions
_('Recall')
_('Approve')
_('Reject')
_('Spam')
_("Recall")
_("Approve")
_("Reject")
_("Spam")
PMF = _
class TranslationHelper(BrowserView):
def translate(self, text=""):
return _(text)
@@ -44,22 +43,21 @@ class TranslationHelper(BrowserView):
class View(BrowserView):
"""Show comment moderation view."""
template = ViewPageTemplateFile('moderation.pt')
template = ViewPageTemplateFile("moderation.pt")
try:
template.id = '@@moderate-comments'
template.id = "@@moderate-comments"
except AttributeError:
# id is not writeable in Zope 2.12
pass
def __init__(self, context, request):
super(View, self).__init__(context, request)
self.workflowTool = getToolByName(self.context, 'portal_workflow')
self.workflowTool = getToolByName(self.context, "portal_workflow")
self.transitions = []
def __call__(self):
self.request.set('disable_border', True)
self.request.set('review_state',
self.request.get('review_state', 'pending'))
self.request.set("disable_border", True)
self.request.set("review_state", self.request.get("review_state", "pending"))
return self.template()
def comments(self):
@@ -67,15 +65,19 @@ class View(BrowserView):
review_state is string or list of strings.
"""
catalog = getToolByName(self.context, 'portal_catalog')
if self.request.review_state == 'all':
return catalog(object_provides=IComment.__identifier__,
sort_on='created',
sort_order='reverse')
return catalog(object_provides=IComment.__identifier__,
review_state=self.request.review_state,
sort_on='created',
sort_order='reverse')
catalog = getToolByName(self.context, "portal_catalog")
if self.request.review_state == "all":
return catalog(
object_provides=IComment.__identifier__,
sort_on="created",
sort_order="reverse",
)
return catalog(
object_provides=IComment.__identifier__,
review_state=self.request.review_state,
sort_on="created",
sort_order="reverse",
)
def moderation_enabled(self):
"""Return true if a review workflow is enabled on 'Discussion Item'
@@ -84,11 +86,10 @@ class View(BrowserView):
A 'review workflow' is characterized by implementing a 'pending'
workflow state.
"""
workflows = self.workflowTool.getChainForPortalType(
'Discussion Item')
workflows = self.workflowTool.getChainForPortalType("Discussion Item")
if workflows:
comment_workflow = self.workflowTool[workflows[0]]
if 'pending' in comment_workflow.states:
if "pending" in comment_workflow.states:
return True
return False
@@ -100,11 +101,10 @@ class View(BrowserView):
A 'review multipe state workflow' is characterized by implementing
a 'rejected' workflow state and a 'spam' workflow state.
"""
workflows = self.workflowTool.getChainForPortalType(
'Discussion Item')
workflows = self.workflowTool.getChainForPortalType("Discussion Item")
if workflows:
comment_workflow = self.workflowTool[workflows[0]]
if 'spam' in comment_workflow.states:
if "spam" in comment_workflow.states:
return True
return False
@@ -125,27 +125,26 @@ class View(BrowserView):
"""
if obj:
transitions = [
a for a in self.workflowTool.listActionInfos(object=obj)
if a['category'] == 'workflow' and a['allowed']
]
a
for a in self.workflowTool.listActionInfos(object=obj)
if a["category"] == "workflow" and a["allowed"]
]
return transitions
class ModerateCommentsEnabled(BrowserView):
def __call__(self):
"""Returns true if a 'review workflow' is enabled on 'Discussion Item'
content type. A 'review workflow' is characterized by implementing
a 'pending' workflow state.
content type. A 'review workflow' is characterized by implementing
a 'pending' workflow state.
"""
context = aq_inner(self.context)
workflowTool = getToolByName(context, 'portal_workflow', None)
comment_workflow = workflowTool.getChainForPortalType(
'Discussion Item')
workflowTool = getToolByName(context, "portal_workflow", None)
comment_workflow = workflowTool.getChainForPortalType("Discussion Item")
if comment_workflow:
comment_workflow = comment_workflow[0]
comment_workflow = workflowTool[comment_workflow]
if 'pending' in comment_workflow.states:
if "pending" in comment_workflow.states:
return True
return False
@@ -184,13 +183,15 @@ class DeleteComment(BrowserView):
content_object.reindexObject()
notify(CommentDeletedEvent(self.context, comment))
IStatusMessage(self.context.REQUEST).addStatusMessage(
_('Comment deleted.'),
type='info')
_("Comment deleted."), type="info"
)
came_from = self.context.REQUEST.HTTP_REFERER
# if the referrer already has a came_from in it, don't redirect back
if (len(came_from) == 0 or 'came_from=' in came_from or
not getToolByName(
content_object, 'portal_url').isURLInPortal(came_from)):
if (
len(came_from) == 0
or "came_from=" in came_from
or not getToolByName(content_object, "portal_url").isURLInPortal(came_from)
):
came_from = content_object.absolute_url()
return self.context.REQUEST.RESPONSE.redirect(came_from)
@@ -198,8 +199,7 @@ class DeleteComment(BrowserView):
"""Returns true if current user has the 'Delete comments'
permission.
"""
return getSecurityManager().checkPermission('Delete comments',
aq_inner(reply))
return getSecurityManager().checkPermission("Delete comments", aq_inner(reply))
class DeleteOwnComment(DeleteComment):
@@ -213,21 +213,18 @@ class DeleteOwnComment(DeleteComment):
"""
def could_delete(self, comment=None):
"""Returns true if the comment could be deleted if it had no replies.
"""
"""Returns true if the comment could be deleted if it had no replies."""
sm = getSecurityManager()
comment = comment or aq_inner(self.context)
userid = sm.getUser().getId()
return (
sm.checkPermission('Delete own comments', comment) and
'Owner' in comment.get_local_roles_for_userid(userid)
)
return sm.checkPermission(
"Delete own comments", comment
) and "Owner" in comment.get_local_roles_for_userid(userid)
def can_delete(self, comment=None):
comment = comment or self.context
return (
len(IReplies(aq_inner(comment))) == 0 and
self.could_delete(comment=comment)
return len(IReplies(aq_inner(comment))) == 0 and self.could_delete(
comment=comment
)
def __call__(self):
@@ -262,33 +259,37 @@ class CommentTransition(BrowserView):
"""Call CommentTransition."""
comment = aq_inner(self.context)
content_object = aq_parent(aq_parent(comment))
workflow_action = self.request.form.get('workflow_action', 'publish')
workflowTool = getToolByName(self.context, 'portal_workflow')
workflow_action = self.request.form.get("workflow_action", "publish")
workflowTool = getToolByName(self.context, "portal_workflow")
workflowTool.doActionFor(comment, workflow_action)
comment.reindexObject()
content_object.reindexObject(idxs=['total_comments'])
content_object.reindexObject(idxs=["total_comments"])
notify(CommentPublishedEvent(self.context, comment))
# for complexer workflows:
notify(CommentTransitionEvent(self.context, comment))
comment_state_translated = ''
comment_state_translated = ""
if workflowTool.getWorkflowsFor(comment):
review_state_new = workflowTool.getInfoFor(ob=comment, name='review_state')
review_state_new = workflowTool.getInfoFor(ob=comment, name="review_state")
helper = self.context.restrictedTraverse("translationhelper")
comment_state_translated = helper.translate_comment_review_state(review_state_new)
comment_state_translated = helper.translate_comment_review_state(
review_state_new
)
msgid = _(
"comment_transmitted",
default='Comment ${comment_state_translated}.',
mapping={"comment_state_translated": comment_state_translated})
default="Comment ${comment_state_translated}.",
mapping={"comment_state_translated": comment_state_translated},
)
translated = self.context.translate(msgid)
IStatusMessage(self.request).add(translated, type='info')
IStatusMessage(self.request).add(translated, type="info")
came_from = self.context.REQUEST.HTTP_REFERER
# if the referrer already has a came_from in it, don't redirect back
if (len(came_from) == 0
or 'came_from=' in came_from
or not getToolByName(
content_object, 'portal_url').isURLInPortal(came_from)):
if (
len(came_from) == 0
or "came_from=" in came_from
or not getToolByName(content_object, "portal_url").isURLInPortal(came_from)
):
came_from = content_object.absolute_url()
return self.context.REQUEST.RESPONSE.redirect(came_from)
@@ -318,18 +319,18 @@ class BulkActionsView(BrowserView):
def __init__(self, context, request):
super(BulkActionsView, self).__init__(context, request)
self.workflowTool = getToolByName(context, 'portal_workflow')
self.workflowTool = getToolByName(context, "portal_workflow")
def __call__(self):
"""Call BulkActionsView."""
if 'form.select.BulkAction' in self.request:
bulkaction = self.request.get('form.select.BulkAction')
self.paths = self.request.get('paths')
if "form.select.BulkAction" in self.request:
bulkaction = self.request.get("form.select.BulkAction")
self.paths = self.request.get("paths")
if self.paths:
if bulkaction == '-1':
if bulkaction == "-1":
# no bulk action was selected
pass
elif bulkaction == 'delete':
elif bulkaction == "delete":
self.delete()
else:
self.transmit(bulkaction)
@@ -346,13 +347,14 @@ class BulkActionsView(BrowserView):
comment = context.restrictedTraverse(path)
content_object = aq_parent(aq_parent(comment))
allowed_transitions = [
transition['id'] for transition in self.workflowTool.listActionInfos(object=comment)
if transition['category'] == 'workflow' and transition['allowed']
]
transition["id"]
for transition in self.workflowTool.listActionInfos(object=comment)
if transition["category"] == "workflow" and transition["allowed"]
]
if action in allowed_transitions:
self.workflowTool.doActionFor(comment, action)
comment.reindexObject()
content_object.reindexObject(idxs=['total_comments'])
content_object.reindexObject(idxs=["total_comments"])
notify(CommentPublishedEvent(content_object, comment))
# for complexer workflows:
notify(CommentTransitionEvent(self.context, comment))
@@ -370,5 +372,5 @@ class BulkActionsView(BrowserView):
conversation = aq_parent(comment)
content_object = aq_parent(conversation)
del conversation[comment.id]
content_object.reindexObject(idxs=['total_comments'])
content_object.reindexObject(idxs=["total_comments"])
notify(CommentDeletedEvent(content_object, comment))
+2 -2
View File
@@ -29,8 +29,8 @@ class ConversationNamespace(object):
def traverse(self, name, ignore):
if name == 'default':
name = u''
if name == "default":
name = u""
conversation = queryAdapter(self.context, IConversation, name=name)
if conversation is None:
+6 -6
View File
@@ -44,11 +44,12 @@ class CaptchaValidator(validator.SimpleFieldValidator):
registry = queryUtility(IRegistry)
settings = registry.forInterface(IDiscussionSettings, check=False)
if settings.captcha in ('captcha', 'recaptcha', 'norobots'):
captcha = getMultiAdapter((aq_inner(self.context), self.request),
name=settings.captcha)
if settings.captcha in ("captcha", "recaptcha", "norobots"):
captcha = getMultiAdapter(
(aq_inner(self.context), self.request), name=settings.captcha
)
if not captcha.verify(input=value):
if settings.captcha == 'norobots':
if settings.captcha == "norobots":
raise WrongNorobotsAnswer
else:
raise WrongCaptchaCode
@@ -57,5 +58,4 @@ class CaptchaValidator(validator.SimpleFieldValidator):
# Register Captcha validator for the Captcha field in the ICaptcha Form
validator.WidgetValidatorDiscriminators(CaptchaValidator,
field=ICaptcha['captcha'])
validator.WidgetValidatorDiscriminators(CaptchaValidator, field=ICaptcha["captcha"])