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:
		
							parent
							
								
									a410c72333
								
							
						
					
					
						commit
						ea13020498
					
				@ -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]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -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.
 | 
				
			||||||
 | 
				
			|||||||
@ -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)
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user