diff --git a/CHANGES.rst b/CHANGES.rst
index fa12098..bda1c9b 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -5,6 +5,9 @@ Changelog
2.2.9 (unreleased)
------------------
+- Provide 'delete own comments' as a configurable option
+ [gyst]
+
- Make comments editable.
[pjstevns, gyst]
diff --git a/plone/app/discussion/browser/comments.pt b/plone/app/discussion/browser/comments.pt
index 203e813..0348f33 100644
--- a/plone/app/discussion/browser/comments.pt
+++ b/plone/app/discussion/browser/comments.pt
@@ -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">
-
+
diff --git a/plone/app/discussion/browser/controlpanel.py b/plone/app/discussion/browser/controlpanel.py
index 822ca2b..84ded37 100644
--- a/plone/app/discussion/browser/controlpanel.py
+++ b/plone/app/discussion/browser/controlpanel.py
@@ -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 = \
diff --git a/plone/app/discussion/browser/moderation.py b/plone/app/discussion/browser/moderation.py
index aa7bb91..0324a43 100644
--- a/plone/app/discussion/browser/moderation.py
+++ b/plone/app/discussion/browser/moderation.py
@@ -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.
diff --git a/plone/app/discussion/configure.zcml b/plone/app/discussion/configure.zcml
index ee486c3..536994e 100644
--- a/plone/app/discussion/configure.zcml
+++ b/plone/app/discussion/configure.zcml
@@ -53,6 +53,16 @@
profile="plone.app.discussion:default"
/>
+
+
diff --git a/plone/app/discussion/interfaces.py b/plone/app/discussion/interfaces.py
index cdf662b..8bf188a 100644
--- a/plone/app/discussion/interfaces.py
+++ b/plone/app/discussion/interfaces.py
@@ -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"),
diff --git a/plone/app/discussion/profiles/default/metadata.xml b/plone/app/discussion/profiles/default/metadata.xml
index 7a20473..ce1f445 100644
--- a/plone/app/discussion/profiles/default/metadata.xml
+++ b/plone/app/discussion/profiles/default/metadata.xml
@@ -1,5 +1,5 @@
- 101
+ 102
profile-plone.app.registry:default
diff --git a/plone/app/discussion/tests/functional_test_comments.txt b/plone/app/discussion/tests/functional_test_comments.txt
index 0ab4a61..a31ed48 100644
--- a/plone/app/discussion/tests/functional_test_comments.txt
+++ b/plone/app/discussion/tests/functional_test_comments.txt
@@ -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
---------------------------------------------------