Raise an unauthorized error when authenticated users try to post a comment on a content object that has discussion disabled. Thanks to vincentfrentin for reporting this!

svn path=/plone.app.discussion/trunk/; revision=39627
This commit is contained in:
Timo Stollenwerk 2010-09-03 20:40:27 +00:00
parent a410c72333
commit ea13020498
3 changed files with 117 additions and 20 deletions

View File

@ -4,6 +4,11 @@ Changelog
1.0b7 (unreleased) 1.0b7 (unreleased)
------------------ ------------------
* Raise an unauthorized error when authenticated users try to post a comment
on a content object that has discussion disabled. Thanks to vincentfrentin
for reporting this.
[timo]
* Czech translation added. * Czech translation added.
[naro] [naro]

View File

@ -1,6 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from Acquisition import aq_inner from Acquisition import aq_inner
from AccessControl import Unauthorized
from AccessControl import getSecurityManager from AccessControl import getSecurityManager
from datetime import datetime from datetime import datetime
@ -108,11 +109,13 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
author_email = u"" author_email = u""
#author_notification = None #author_notification = None
# Captcha check for anonymous users (if Captcha is enabled) # Captcha check for anonymous users (if Captcha is enabled and
# anonymous commenting is allowed)
registry = queryUtility(IRegistry) registry = queryUtility(IRegistry)
settings = registry.forInterface(IDiscussionSettings) settings = registry.forInterface(IDiscussionSettings)
portal_membership = getToolByName(self.context, 'portal_membership') portal_membership = getToolByName(self.context, 'portal_membership')
if settings.captcha != 'disabled' and \ if settings.captcha != 'disabled' and \
settings.anonymous_comments and \
portal_membership.isAnonymousUser(): portal_membership.isAnonymousUser():
if not 'captcha' in data: if not 'captcha' in data:
data['captcha'] = u"" data['captcha'] = u""
@ -123,13 +126,13 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
None) None)
captcha.validate(data['captcha']) captcha.validate(data['captcha'])
# Fetch data from request
if 'title' in data: if 'title' in data:
title = data['title'] title = data['title']
if 'text' in data: if 'text' in data:
text = data['text'] text = data['text']
if 'author_name' in data: if 'author_name' in data:
author_name = data['author_name'] author_name = data['author_name']
if 'author_email' in data: if 'author_email' in data:
author_email = data['author_email'] author_email = data['author_email']
#if 'author_notification' in data: #if 'author_notification' in data:
@ -138,6 +141,11 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
# The add-comment view is called on the conversation object # The add-comment view is called on the conversation object
conversation = IConversation(self.__parent__) conversation = IConversation(self.__parent__)
# Check if conversation is enabled on this content object
if not conversation.enabled():
raise Unauthorized, "Discussion is not enabled for this content\
object."
if data['in_reply_to']: if data['in_reply_to']:
# Fetch the comment we want to reply to # Fetch the comment we want to reply to
conversation_to_reply_to = conversation.get(data['in_reply_to']) conversation_to_reply_to = conversation.get(data['in_reply_to'])
@ -150,13 +158,14 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
portal_membership = getToolByName(self.context, 'portal_membership') portal_membership = getToolByName(self.context, 'portal_membership')
if portal_membership.isAnonymousUser(): if portal_membership.isAnonymousUser() and \
settings.anonymous_comments:
comment.creator = None comment.creator = None
comment.author_name = author_name comment.author_name = author_name
comment.author_email = author_email comment.author_email = author_email
#comment.author_notification = author_notification #comment.author_notification = author_notification
comment.creation_date = comment.modification_date = datetime.now() comment.creation_date = comment.modification_date = datetime.now()
else: elif not portal_membership.isAnonymousUser():
member = portal_membership.getAuthenticatedMember() member = portal_membership.getAuthenticatedMember()
comment.creator = member.id comment.creator = member.id
comment.author_username = member.getUserName() comment.author_username = member.getUserName()
@ -164,6 +173,9 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
comment.author_email = member.getProperty('email') comment.author_email = member.getProperty('email')
#comment.author_notification = comment.author_notification #comment.author_notification = comment.author_notification
comment.creation_date = comment.modification_date = datetime.now() comment.creation_date = comment.modification_date = datetime.now()
else:
raise Unauthorized, "Anonymous user tries to post a comment, but \
anonymous commenting is disabled."
# Check if the added comment is a reply to an existing comment # Check if the added comment is a reply to an existing comment
# or just a regular reply to the content object. # or just a regular reply to the content object.

View File

@ -2,6 +2,8 @@
import unittest import unittest
from datetime import datetime from datetime import datetime
from AccessControl import Unauthorized
from Acquisition import Implicit from Acquisition import Implicit
from zope.component import createObject, queryUtility from zope.component import createObject, queryUtility
@ -23,10 +25,14 @@ from zope.component import getMultiAdapter
from plone.registry.interfaces import IRegistry from plone.registry.interfaces import IRegistry
from Products.CMFCore.utils import getToolByName from Products.CMFCore.utils import getToolByName
from Products.CMFPlone.tests import dummy from Products.CMFPlone.tests import dummy
from Products.Five.testbrowser import Browser from Products.Five.testbrowser import Browser
from Products.PloneTestCase.ptc import PloneTestCase from Products.PloneTestCase.ptc import PloneTestCase
from Products.PloneTestCase.ptc import FunctionalTestCase from Products.PloneTestCase.ptc import FunctionalTestCase
from Products.PloneTestCase.setup import portal_owner, default_password
from plone.app.discussion.comment import Comment from plone.app.discussion.comment import Comment
from plone.app.discussion.browser.comments import CommentsViewlet from plone.app.discussion.browser.comments import CommentsViewlet
@ -43,16 +49,19 @@ class TestCommentForm(PloneTestCase):
self.loginAsPortalOwner() self.loginAsPortalOwner()
typetool = self.portal.portal_types typetool = self.portal.portal_types
typetool.constructContent('Document', self.portal, 'doc1') typetool.constructContent('Document', self.portal, 'doc1')
self.portal_discussion = getToolByName(self.portal, self.dtool = getToolByName(self.portal,
'portal_discussion', 'portal_discussion',
None) None)
self.membership_tool = getToolByName(self.folder, 'portal_membership') self.dtool.overrideDiscussionFor(self.portal.doc1, False)
self.mtool = getToolByName(self.folder, 'portal_membership', None)
self.memberdata = self.portal.portal_memberdata self.memberdata = self.portal.portal_memberdata
self.request = self.app.REQUEST self.request = self.app.REQUEST
self.context = getattr(self.portal, 'doc1') self.context = getattr(self.portal, 'doc1')
self.viewlet = CommentsViewlet(self.context, self.request, None, None)
def test_add_comment(self): def test_add_comment(self):
# Allow discussion
self.dtool.overrideDiscussionFor(self.portal.doc1, True)
self.viewlet = CommentsViewlet(self.context, self.request, None, None)
def make_request(form={}): def make_request(form={}):
request = TestRequest() request = TestRequest()
@ -66,6 +75,7 @@ class TestCommentForm(PloneTestCase):
factory=CommentForm, factory=CommentForm,
name=u"comment-form") name=u"comment-form")
# The form should return errors if the two required fields are empty
request = make_request(form={}) request = make_request(form={})
commentForm = getMultiAdapter((self.context, request), commentForm = getMultiAdapter((self.context, request),
@ -76,7 +86,7 @@ class TestCommentForm(PloneTestCase):
self.assertEquals(len(errors), 2) self.assertEquals(len(errors), 2)
self.failIf(commentForm.handleComment(commentForm, "foo")) self.failIf(commentForm.handleComment(commentForm, "foo"))
# The form should return an error if the comment text field is empty
request = make_request(form={'form.widgets.text': 'foo'}) request = make_request(form={'form.widgets.text': 'foo'})
commentForm = getMultiAdapter((self.context, request), commentForm = getMultiAdapter((self.context, request),
@ -85,8 +95,10 @@ class TestCommentForm(PloneTestCase):
data, errors = commentForm.extractData() data, errors = commentForm.extractData()
self.assertEquals(len(errors), 1) self.assertEquals(len(errors), 1)
self.failIf(commentForm.handleComment(commentForm, "foo"))
# The form is submitted successfully, if all required fields are
# filled out
request = make_request(form={'form.widgets.title': 'foo', request = make_request(form={'form.widgets.title': 'foo',
'form.widgets.text': 'bar'}) 'form.widgets.text': 'bar'})
@ -99,6 +111,76 @@ class TestCommentForm(PloneTestCase):
self.failIf(commentForm.handleComment(commentForm, "foo")) self.failIf(commentForm.handleComment(commentForm, "foo"))
def test_can_not_add_comments_if_discussion_is_not_allowed(self):
"""Make sure that comments can't be posted if discussion is disabled.
"""
# Discussion is disabled by default
def make_request(form={}):
request = TestRequest()
request.form.update(form)
alsoProvides(request, IFormLayer)
alsoProvides(request, IAttributeAnnotatable)
return request
provideAdapter(adapts=(Interface, IBrowserRequest),
provides=Interface,
factory=CommentForm,
name=u"comment-form")
request = make_request(form={'form.widgets.title': 'foo',
'form.widgets.text': 'bar'})
commentForm = getMultiAdapter((self.context, request),
name=u"comment-form")
commentForm.update()
data, errors = commentForm.extractData()
# No form errors, but raise unauthorized because discussion is not
# allowed
self.assertEquals(len(errors), 0)
self.assertRaises(Unauthorized,
commentForm.handleComment,
commentForm,
"foo")
def test_add_comment_as_anonymous(self):
"""Make sure that anonymous users can't post comments if anonymous
comments are disabled.
"""
# Anonymous comments are disabled by default
self.logout()
def make_request(form={}):
request = TestRequest()
request.form.update(form)
alsoProvides(request, IFormLayer)
alsoProvides(request, IAttributeAnnotatable)
return request
provideAdapter(adapts=(Interface, IBrowserRequest),
provides=Interface,
factory=CommentForm,
name=u"comment-form")
request = make_request(form={'form.widgets.title': 'foo',
'form.widgets.text': 'bar'})
commentForm = getMultiAdapter((self.context, request),
name=u"comment-form")
commentForm.update()
data, errors = commentForm.extractData()
self.assertEquals(len(errors), 0)
self.assertRaises(Unauthorized,
commentForm.handleComment,
commentForm,
"foo")
class TestCommentsViewletIntegration(FunctionalTestCase): class TestCommentsViewletIntegration(FunctionalTestCase):
layer = DiscussionLayer layer = DiscussionLayer
@ -108,8 +190,6 @@ class TestCommentsViewletIntegration(FunctionalTestCase):
portal_url = self.portal.absolute_url() portal_url = self.portal.absolute_url()
browser.handleErrors = False browser.handleErrors = False
from Products.PloneTestCase.setup import portal_owner, default_password
browser.open(portal_url + '/login_form') browser.open(portal_url + '/login_form')
browser.getControl(name='__ac_name').value = portal_owner browser.getControl(name='__ac_name').value = portal_owner
browser.getControl(name='__ac_password').value = default_password browser.getControl(name='__ac_password').value = default_password
@ -151,7 +231,7 @@ class TestCommentsViewlet(PloneTestCase):
self.portal_discussion = getToolByName(self.portal, self.portal_discussion = getToolByName(self.portal,
'portal_discussion', 'portal_discussion',
None) None)
self.membership_tool = getToolByName(self.folder, 'portal_membership') self.mtool = getToolByName(self.folder, 'portal_membership')
self.memberdata = self.portal.portal_memberdata self.memberdata = self.portal.portal_memberdata
request = self.app.REQUEST request = self.app.REQUEST
context = getattr(self.portal, 'doc1') context = getattr(self.portal, 'doc1')
@ -265,7 +345,7 @@ class TestCommentsViewlet(PloneTestCase):
def test_get_commenter_portrait(self): def test_get_commenter_portrait(self):
# Add a user with a member image # Add a user with a member image
self.membership_tool.addMember('jim', 'Jim', ['Member'], []) self.mtool.addMember('jim', 'Jim', ['Member'], [])
self.memberdata._setPortrait(Image(id='jim', self.memberdata._setPortrait(Image(id='jim',
file=dummy.File(), file=dummy.File(),
title=''), 'jim') title=''), 'jim')
@ -298,7 +378,7 @@ class TestCommentsViewlet(PloneTestCase):
def test_get_commenter_portrait_without_userimage(self): def test_get_commenter_portrait_without_userimage(self):
# Create a user without a user image # Create a user without a user image
self.membership_tool.addMember('jim', 'Jim', ['Member'], []) self.mtool.addMember('jim', 'Jim', ['Member'], [])
# Add a conversation with a comment # Add a conversation with a comment
conversation = IConversation(self.portal.doc1) conversation = IConversation(self.portal.doc1)