provide "delete own comments" as a configurable option
This commit is contained in:
parent
e6172a219e
commit
82a473c138
@ -5,6 +5,9 @@ Changelog
|
||||
2.2.9 (unreleased)
|
||||
------------------
|
||||
|
||||
- Provide 'delete own comments' as a configurable option
|
||||
[gyst]
|
||||
|
||||
- Make comments editable.
|
||||
[pjstevns, gyst]
|
||||
|
||||
|
@ -36,7 +36,8 @@
|
||||
has_author_link python:author_home_url and not isAnon;
|
||||
portrait_url python:view.get_commenter_portrait(reply.author_username);
|
||||
review_state python:wtool.getInfoFor(reply, 'review_state', 'none');
|
||||
canEdit python:view.can_edit(reply)"
|
||||
canEdit python:view.can_edit(reply);
|
||||
canDelete python:view.can_delete(reply)"
|
||||
tal:attributes="class python:'comment replyTreeLevel'+str(depth)+' state-'+str(review_state);
|
||||
id string:${reply/getId}"
|
||||
tal:condition="python:canReview or review_state == 'published'">
|
||||
@ -89,7 +90,7 @@
|
||||
action=""
|
||||
method="post"
|
||||
class="commentactionsform"
|
||||
tal:condition="python:canReview"
|
||||
tal:condition="python:canDelete"
|
||||
tal:attributes="action string:${reply/absolute_url}/@@moderate-delete-comment">
|
||||
<input name="form.button.DeleteComment"
|
||||
class="destructive"
|
||||
|
@ -299,12 +299,28 @@ class CommentsViewlet(ViewletBase):
|
||||
aq_inner(self.context))
|
||||
|
||||
def can_edit(self, reply):
|
||||
"""Returns true if current user has the 'Delete objects'
|
||||
"""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'.
|
||||
"""
|
||||
if self.is_delete_own_comment_allowed():
|
||||
permission = 'Edit comments'
|
||||
else:
|
||||
permission = 'Review comments'
|
||||
return getSecurityManager().checkPermission(permission,
|
||||
aq_inner(reply))
|
||||
|
||||
def is_delete_own_comment_allowed(self):
|
||||
registry = queryUtility(IRegistry)
|
||||
settings = registry.forInterface(IDiscussionSettings, check=False)
|
||||
return settings.delete_own_comment_enabled
|
||||
|
||||
def is_discussion_allowed(self):
|
||||
context = aq_inner(self.context)
|
||||
return context.restrictedTraverse('@@conversation_view').enabled()
|
||||
|
@ -80,13 +80,15 @@
|
||||
permission="plone.app.discussion.EditComments"
|
||||
/>
|
||||
|
||||
<!-- Delete comment view -->
|
||||
<!-- Delete comment view
|
||||
has conditional security dependent on controlpanel settings.
|
||||
-->
|
||||
<browser:page
|
||||
for="plone.app.discussion.interfaces.IComment"
|
||||
name="moderate-delete-comment"
|
||||
layer="..interfaces.IDiscussionLayer"
|
||||
class=".moderation.DeleteComment"
|
||||
permission="plone.app.discussion.ReviewComments"
|
||||
permission="zope2.DeleteObjects"
|
||||
/>
|
||||
|
||||
<!-- Publish comment view -->
|
||||
|
@ -54,6 +54,8 @@ class DiscussionSettingsEditForm(controlpanel.RegistryEditForm):
|
||||
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 = \
|
||||
|
@ -1,5 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from Acquisition import aq_inner, aq_parent
|
||||
from AccessControl import getSecurityManager
|
||||
from zope.component import queryUtility
|
||||
|
||||
from Products.Five.browser import BrowserView
|
||||
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
|
||||
@ -8,6 +10,8 @@ from Products.CMFCore.utils import getToolByName
|
||||
|
||||
from Products.statusmessages.interfaces import IStatusMessage
|
||||
|
||||
from plone.registry.interfaces import IRegistry
|
||||
from plone.app.discussion.interfaces import IDiscussionSettings
|
||||
from plone.app.discussion.interfaces import _
|
||||
from plone.app.discussion.interfaces import IComment
|
||||
|
||||
@ -94,17 +98,37 @@ class DeleteComment(BrowserView):
|
||||
comment = aq_inner(self.context)
|
||||
conversation = aq_parent(comment)
|
||||
content_object = aq_parent(conversation)
|
||||
del conversation[comment.id]
|
||||
content_object.reindexObject()
|
||||
IStatusMessage(self.context.REQUEST).addStatusMessage(
|
||||
_("Comment deleted."),
|
||||
type="info")
|
||||
# conditional security
|
||||
# base ZCML condition zope2.deleteObject allows 'delete own object'
|
||||
# modify this for 'delete_own_comment_allowed' controlpanel setting
|
||||
if self.can_delete(comment):
|
||||
del conversation[comment.id]
|
||||
content_object.reindexObject()
|
||||
IStatusMessage(self.context.REQUEST).addStatusMessage(
|
||||
_("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:
|
||||
came_from = content_object.absolute_url()
|
||||
return self.context.REQUEST.RESPONSE.redirect(came_from)
|
||||
|
||||
def can_delete(self, reply):
|
||||
"""By default requires 'Review comments'.
|
||||
If 'delete own comments' is enabled, requires 'Edit comments'.
|
||||
"""
|
||||
if self.is_delete_own_comment_allowed():
|
||||
permission = 'Edit comments'
|
||||
else:
|
||||
permission = 'Review comments'
|
||||
return getSecurityManager().checkPermission(permission,
|
||||
aq_inner(reply))
|
||||
|
||||
def is_delete_own_comment_allowed(self):
|
||||
registry = queryUtility(IRegistry)
|
||||
settings = registry.forInterface(IDiscussionSettings, check=False)
|
||||
return settings.delete_own_comment_enabled
|
||||
|
||||
|
||||
class PublishComment(BrowserView):
|
||||
"""Publish a comment.
|
||||
|
@ -53,6 +53,16 @@
|
||||
profile="plone.app.discussion:default"
|
||||
/>
|
||||
|
||||
<genericsetup:upgradeStep
|
||||
title="delete own comments"
|
||||
description="reload registry config to enable new field delete_own_comment_enabled"
|
||||
source="101"
|
||||
destination="102"
|
||||
handler=".upgrades.update_registry"
|
||||
sortkey="1"
|
||||
profile="plone.app.discussion:default"
|
||||
/>
|
||||
|
||||
|
||||
<!-- Monkey Patches -->
|
||||
|
||||
|
@ -285,6 +285,17 @@ class IDiscussionSettings(Interface):
|
||||
default=False,
|
||||
)
|
||||
|
||||
delete_own_comment_enabled = schema.Bool(
|
||||
title=_(u"label_delete_own_comment_enabled",
|
||||
default="Allow users to delete their own comment threads"),
|
||||
description=_(u"help_edit_comment_enabled",
|
||||
default=u"If selected, users may delete their own "
|
||||
"comments -> AND the whole reply thread below that "
|
||||
"comment!"),
|
||||
required=False,
|
||||
default=False,
|
||||
)
|
||||
|
||||
text_transform = schema.Choice(
|
||||
title=_(u"label_text_transform",
|
||||
default="Comment text transform"),
|
||||
|
@ -1,5 +1,5 @@
|
||||
<metadata>
|
||||
<version>101</version>
|
||||
<version>102</version>
|
||||
<dependencies>
|
||||
<dependency>profile-plone.app.registry:default</dependency>
|
||||
</dependencies>
|
||||
|
@ -320,6 +320,146 @@ But Anon can see the edited comment.
|
||||
True
|
||||
|
||||
|
||||
Deleting existing comments | 'delete own comments' disabled
|
||||
-----------------------------------------------------------
|
||||
|
||||
Anonymous cannot delete comments
|
||||
|
||||
>>> unprivileged_browser.open(urldoc1)
|
||||
>>> 'form.button.Delete' in unprivileged_browser.contents
|
||||
False
|
||||
|
||||
A member cannot delete his own comments, unless this is explicitly enabled (see later)
|
||||
|
||||
>>> browser_member.open(urldoc1)
|
||||
>>> 'form.button.Delete' in browser_member.contents
|
||||
False
|
||||
|
||||
Admin can delete comments
|
||||
|
||||
>>> browser.open(urldoc1)
|
||||
>>> 'form.button.Delete' in browser.contents
|
||||
True
|
||||
|
||||
Extract the delete comment url from the first "delete comment" button
|
||||
|
||||
>>> browser.open(urldoc1)
|
||||
>>> form = browser.getForm(name='delete', index=0)
|
||||
>>> delete_url = form.action
|
||||
>>> '@@moderate-delete-comment' in delete_url
|
||||
True
|
||||
>>> comment_id = delete_url.split('/')[-2]
|
||||
|
||||
Anonymous cannot delete a comment by hitting the delete url directly.
|
||||
|
||||
>>> unprivileged_browser.open(delete_url)
|
||||
|
||||
The comment is still there
|
||||
|
||||
>>> unprivileged_browser.open(urldoc1)
|
||||
>>> comment_id in unprivileged_browser.contents
|
||||
True
|
||||
|
||||
|
||||
A Member cannot delete even his own comment by hitting the delete url directly.
|
||||
|
||||
Extract the member comment id from the admin browser
|
||||
|
||||
>>> form = browser.getForm(name='delete', index=2)
|
||||
>>> delete_url = form.action
|
||||
>>> '@@moderate-delete-comment' in delete_url
|
||||
True
|
||||
>>> comment_id = delete_url.split('/')[-2]
|
||||
|
||||
Now try to hit that url as the member owning that comment.
|
||||
Work around some possible testbrowser breakage and check the result later.
|
||||
|
||||
>>> try:
|
||||
... browser_member.open(delete_url)
|
||||
... except:
|
||||
... pass
|
||||
|
||||
The comment is still there
|
||||
|
||||
>>> browser_member.open(urldoc1)
|
||||
>>> comment_id in browser_member.contents
|
||||
True
|
||||
>>> 'Comment from Jim' in browser_member.contents
|
||||
True
|
||||
|
||||
Admin, who hase 'review comments' permission, can delete comments
|
||||
|
||||
>>> browser.open(urldoc1)
|
||||
>>> form = browser.getForm(name='delete', index=0)
|
||||
>>> '@@moderate-delete-comment' in form.action
|
||||
True
|
||||
|
||||
>>> comment_id = form.action.split('/')[-2]
|
||||
|
||||
Submitting the form runs into a testbrowser notFoundException.
|
||||
We'll just catch that and check the result later.
|
||||
|
||||
>>> try:
|
||||
... form.submit()
|
||||
... except:
|
||||
... pass
|
||||
|
||||
Returning to the document we find the deleted comment is indeed gone
|
||||
|
||||
>>> browser.open(urldoc1)
|
||||
>>> comment_id in browser.contents
|
||||
False
|
||||
|
||||
|
||||
Deleting existing comments | 'delete own comments' ENABLED
|
||||
----------------------------------------------------------
|
||||
|
||||
Enable deletion of own comments
|
||||
|
||||
>>> from zope.component import queryUtility
|
||||
>>> from plone.registry.interfaces import IRegistry
|
||||
>>> from plone.app.discussion.interfaces import IDiscussionSettings
|
||||
>>> registry = queryUtility(IRegistry)
|
||||
>>> settings = registry.forInterface(IDiscussionSettings)
|
||||
>>> settings.delete_own_comment_enabled = True
|
||||
|
||||
>>> import transaction
|
||||
>>> transaction.commit()
|
||||
|
||||
Anonymous still cannot delete comments
|
||||
|
||||
>>> unprivileged_browser.open(urldoc1)
|
||||
>>> 'form.button.Delete' in unprivileged_browser.contents
|
||||
False
|
||||
|
||||
A member can now delete his own comments
|
||||
|
||||
>>> browser_member.open(urldoc1)
|
||||
>>> 'form.button.Delete' in browser_member.contents
|
||||
True
|
||||
|
||||
>>> form = browser_member.getForm(name='delete', index=0)
|
||||
>>> '@@moderate-delete-comment' in form.action
|
||||
True
|
||||
|
||||
>>> comment_id = form.action.split('/')[-2]
|
||||
|
||||
Submitting the form runs into a testbrowser notFoundException.
|
||||
We'll just catch that and check the result later.
|
||||
|
||||
>>> try:
|
||||
... form.submit()
|
||||
... except:
|
||||
... pass
|
||||
|
||||
Returning to the document we find the deleted comment is indeed gone
|
||||
|
||||
>>> browser_member.open(urldoc1)
|
||||
>>> comment_id in browser_member.contents
|
||||
False
|
||||
>>> 'Comment from Jim' in browser_member.contents
|
||||
False
|
||||
|
||||
|
||||
Post a comment with comment review workflow enabled
|
||||
---------------------------------------------------
|
||||
|
Loading…
Reference in New Issue
Block a user