Make comments editable
This commit is contained in:
		
							parent
							
								
									0463b20f78
								
							
						
					
					
						commit
						e6172a219e
					
				@ -1,9 +1,13 @@
 | 
			
		||||
Changelog
 | 
			
		||||
=========
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
2.2.9 (unreleased)
 | 
			
		||||
------------------
 | 
			
		||||
 | 
			
		||||
- Make comments editable.
 | 
			
		||||
  [pjstevns, gyst]
 | 
			
		||||
 | 
			
		||||
- Rename CHANGES.txt to CHANGES.rst.
 | 
			
		||||
  [timo]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,16 @@
 | 
			
		||||
from Acquisition import aq_inner, aq_parent
 | 
			
		||||
from AccessControl import getSecurityManager
 | 
			
		||||
 | 
			
		||||
from zope.component import getMultiAdapter
 | 
			
		||||
from Products.statusmessages.interfaces import IStatusMessage
 | 
			
		||||
from Products.Five.browser import BrowserView
 | 
			
		||||
from Products.CMFCore.utils import getToolByName
 | 
			
		||||
 | 
			
		||||
from plone.app.discussion import PloneAppDiscussionMessageFactory as _
 | 
			
		||||
from comments import CommentForm
 | 
			
		||||
from z3c.form import button
 | 
			
		||||
from plone.z3cform.layout import wrap_form
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class View(BrowserView):
 | 
			
		||||
    """Comment View.
 | 
			
		||||
@ -37,3 +45,64 @@ class View(BrowserView):
 | 
			
		||||
            url = "%s/view" % url
 | 
			
		||||
 | 
			
		||||
        self.request.response.redirect('%s#%s' % (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')
 | 
			
		||||
 | 
			
		||||
    def updateWidgets(self):
 | 
			
		||||
        super(EditCommentForm, self).updateWidgets()
 | 
			
		||||
        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'
 | 
			
		||||
 | 
			
		||||
    def _redirect(self, target=''):
 | 
			
		||||
        if not target:
 | 
			
		||||
            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"edit_comment_form_button",
 | 
			
		||||
                             default=u"Edit comment"), name='comment')
 | 
			
		||||
    def handleComment(self, action):
 | 
			
		||||
 | 
			
		||||
        # Validate form
 | 
			
		||||
        data, errors = self.extractData()
 | 
			
		||||
        if errors:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        # Check permissions
 | 
			
		||||
        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']
 | 
			
		||||
 | 
			
		||||
        # 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"))
 | 
			
		||||
 | 
			
		||||
    @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')
 | 
			
		||||
            return self._redirect(target=self.context.absolute_url())
 | 
			
		||||
 | 
			
		||||
EditComment = wrap_form(EditCommentForm)
 | 
			
		||||
 | 
			
		||||
#EOF
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,7 @@
 | 
			
		||||
<tal:block tal:define="userHasReplyPermission view/can_reply;
 | 
			
		||||
                       isDiscussionAllowed view/is_discussion_allowed;
 | 
			
		||||
                       isAnonymousDiscussionAllowed view/anonymous_discussion_allowed;
 | 
			
		||||
                       isEditCommentAllowed view/edit_comment_allowed;
 | 
			
		||||
                       isAnon view/is_anonymous;
 | 
			
		||||
                       canReview view/can_review;
 | 
			
		||||
                       replies python:view.get_replies(canReview);
 | 
			
		||||
@ -34,7 +35,8 @@
 | 
			
		||||
                             author_home_url python:view.get_commenter_home_url(username=reply.author_username);
 | 
			
		||||
                             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');"
 | 
			
		||||
			     review_state python:wtool.getInfoFor(reply, 'review_state', 'none');
 | 
			
		||||
			     canEdit python:view.can_edit(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'">
 | 
			
		||||
@ -42,14 +44,14 @@
 | 
			
		||||
                <div class="commentImage" tal:condition="showCommenterImage">
 | 
			
		||||
                    <a href="" tal:condition="has_author_link"
 | 
			
		||||
                               tal:attributes="href author_home_url">
 | 
			
		||||
                         <img src="defaultUser.gif"
 | 
			
		||||
                         <img src="defaultUser.png"
 | 
			
		||||
                              alt=""
 | 
			
		||||
                              class="defaultuserimg"
 | 
			
		||||
                              height="32"
 | 
			
		||||
                              tal:attributes="src portrait_url;
 | 
			
		||||
                                              alt reply/author_name" />
 | 
			
		||||
                    </a>
 | 
			
		||||
                    <img src="defaultUser.gif"
 | 
			
		||||
                    <img src="defaultUser.png"
 | 
			
		||||
                         alt=""
 | 
			
		||||
                         class="defaultuserimg"
 | 
			
		||||
                         height="32"
 | 
			
		||||
@ -87,7 +89,7 @@
 | 
			
		||||
                              action=""
 | 
			
		||||
                              method="post"
 | 
			
		||||
                              class="commentactionsform"
 | 
			
		||||
                              tal:condition="canReview"
 | 
			
		||||
                              tal:condition="python:canReview"
 | 
			
		||||
                              tal:attributes="action string:${reply/absolute_url}/@@moderate-delete-comment">
 | 
			
		||||
                            <input name="form.button.DeleteComment"
 | 
			
		||||
                                   class="destructive"
 | 
			
		||||
@ -97,6 +99,21 @@
 | 
			
		||||
                                   />
 | 
			
		||||
                        </form>
 | 
			
		||||
 | 
			
		||||
                        <form name="edit"
 | 
			
		||||
                              action=""
 | 
			
		||||
                              method="get"
 | 
			
		||||
                              class="commentactionsform"
 | 
			
		||||
                              tal:condition="python:isEditCommentAllowed and canEdit"
 | 
			
		||||
                              tal:attributes="action string:${reply/absolute_url}/@@edit-comment">
 | 
			
		||||
                            <input name="form.button.EditComment"
 | 
			
		||||
                                   class="context"
 | 
			
		||||
                                   type="submit"
 | 
			
		||||
                                   value="Edit"
 | 
			
		||||
                                   i18n:attributes="value label_edit;"
 | 
			
		||||
                                   />
 | 
			
		||||
                        </form>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
                        <!-- Workflow actions (e.g. 'publish') -->
 | 
			
		||||
                        <form name=""
 | 
			
		||||
                              action=""
 | 
			
		||||
 | 
			
		||||
@ -298,6 +298,13 @@ class CommentsViewlet(ViewletBase):
 | 
			
		||||
        return getSecurityManager().checkPermission('Review comments',
 | 
			
		||||
                                                    aq_inner(self.context))
 | 
			
		||||
 | 
			
		||||
    def can_edit(self, reply):
 | 
			
		||||
        """Returns true if current user has the 'Delete objects'
 | 
			
		||||
        permission.
 | 
			
		||||
        """
 | 
			
		||||
        return getSecurityManager().checkPermission('Edit comments',
 | 
			
		||||
                                                    aq_inner(reply))
 | 
			
		||||
 | 
			
		||||
    def is_discussion_allowed(self):
 | 
			
		||||
        context = aq_inner(self.context)
 | 
			
		||||
        return context.restrictedTraverse('@@conversation_view').enabled()
 | 
			
		||||
@ -408,7 +415,7 @@ class CommentsViewlet(ViewletBase):
 | 
			
		||||
 | 
			
		||||
        if username is None:
 | 
			
		||||
            # return the default user image if no username is given
 | 
			
		||||
            return 'defaultUser.gif'
 | 
			
		||||
            return 'defaultUser.png'
 | 
			
		||||
        else:
 | 
			
		||||
            portal_membership = getToolByName(self.context,
 | 
			
		||||
                                              'portal_membership',
 | 
			
		||||
@ -423,6 +430,12 @@ class CommentsViewlet(ViewletBase):
 | 
			
		||||
        settings = registry.forInterface(IDiscussionSettings, check=False)
 | 
			
		||||
        return settings.anonymous_comments
 | 
			
		||||
 | 
			
		||||
    def edit_comment_allowed(self):
 | 
			
		||||
        # Check if editing comments is allowed in the registry
 | 
			
		||||
        registry = queryUtility(IRegistry)
 | 
			
		||||
        settings = registry.forInterface(IDiscussionSettings, check=False)
 | 
			
		||||
        return settings.edit_comment_enabled
 | 
			
		||||
 | 
			
		||||
    def show_commenter_image(self):
 | 
			
		||||
        # Check if showing commenter image is enabled in the registry
 | 
			
		||||
        registry = queryUtility(IRegistry)
 | 
			
		||||
 | 
			
		||||
@ -71,6 +71,15 @@
 | 
			
		||||
        permission="zope2.View"
 | 
			
		||||
        />
 | 
			
		||||
 | 
			
		||||
    <!-- Edit comment view -->
 | 
			
		||||
    <browser:page
 | 
			
		||||
        for="plone.app.discussion.interfaces.IComment"
 | 
			
		||||
        name="edit-comment"
 | 
			
		||||
        layer="..interfaces.IDiscussionLayer"
 | 
			
		||||
        class=".comment.EditComment"
 | 
			
		||||
        permission="plone.app.discussion.EditComments"
 | 
			
		||||
        />
 | 
			
		||||
 | 
			
		||||
    <!-- Delete comment view -->
 | 
			
		||||
    <browser:page
 | 
			
		||||
        for="plone.app.discussion.interfaces.IComment"
 | 
			
		||||
 | 
			
		||||
@ -24,6 +24,7 @@ from z3c.form import button
 | 
			
		||||
from z3c.form.browser.checkbox import SingleCheckBoxFieldWidget
 | 
			
		||||
 | 
			
		||||
from plone.app.discussion.interfaces import IDiscussionSettings, _
 | 
			
		||||
from plone.app.discussion.upgrades import update_registry
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DiscussionSettingsEditForm(controlpanel.RegistryEditForm):
 | 
			
		||||
@ -51,6 +52,8 @@ class DiscussionSettingsEditForm(controlpanel.RegistryEditForm):
 | 
			
		||||
            SingleCheckBoxFieldWidget
 | 
			
		||||
        self.fields['moderation_enabled'].widgetFactory = \
 | 
			
		||||
            SingleCheckBoxFieldWidget
 | 
			
		||||
        self.fields['edit_comment_enabled'].widgetFactory = \
 | 
			
		||||
            SingleCheckBoxFieldWidget
 | 
			
		||||
        self.fields['anonymous_comments'].widgetFactory = \
 | 
			
		||||
            SingleCheckBoxFieldWidget
 | 
			
		||||
        self.fields['show_commenter_image'].widgetFactory = \
 | 
			
		||||
@ -61,6 +64,12 @@ class DiscussionSettingsEditForm(controlpanel.RegistryEditForm):
 | 
			
		||||
            SingleCheckBoxFieldWidget
 | 
			
		||||
 | 
			
		||||
    def updateWidgets(self):
 | 
			
		||||
        try:
 | 
			
		||||
            super(DiscussionSettingsEditForm, self).updateWidgets()
 | 
			
		||||
        except KeyError:
 | 
			
		||||
            # upgrade profile not visible in prefs_install_products_form
 | 
			
		||||
            # 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")
 | 
			
		||||
@ -118,6 +127,9 @@ class DiscussionSettingsControlPanel(controlpanel.ControlPanelFormWrapper):
 | 
			
		||||
        elif settings.moderation_enabled:
 | 
			
		||||
            output.append("moderation_enabled")
 | 
			
		||||
 | 
			
		||||
        if settings.edit_comment_enabled:
 | 
			
		||||
            output.append("edit_comment_enabled")
 | 
			
		||||
 | 
			
		||||
        # Anonymous comments
 | 
			
		||||
        if settings.anonymous_comments:
 | 
			
		||||
            output.append("anonymous_comments")
 | 
			
		||||
 | 
			
		||||
@ -165,6 +165,15 @@
 | 
			
		||||
            return false;
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        /**********************************************************************
 | 
			
		||||
         * Edit a comment
 | 
			
		||||
         **********************************************************************/
 | 
			
		||||
	$("form[name='edit']").prepOverlay({
 | 
			
		||||
                cssclass: 'overlay-edit-comment',
 | 
			
		||||
                width: '60%',
 | 
			
		||||
		subtype: 'ajax',
 | 
			
		||||
		filter: '#content>*'
 | 
			
		||||
		})
 | 
			
		||||
 | 
			
		||||
        /**********************************************************************
 | 
			
		||||
         * Delete a comment and its answers.
 | 
			
		||||
 | 
			
		||||
@ -40,6 +40,7 @@
 | 
			
		||||
            $.enableSettings([
 | 
			
		||||
                $('#formfield-form-widgets-anonymous_comments'),
 | 
			
		||||
                $('#formfield-form-widgets-moderation_enabled'),
 | 
			
		||||
                $('#formfield-form-widgets-edit_comment_enabled'),
 | 
			
		||||
                $('#formfield-form-widgets-text_transform'),
 | 
			
		||||
                $('#formfield-form-widgets-captcha'),
 | 
			
		||||
                $('#formfield-form-widgets-show_commenter_image'),
 | 
			
		||||
@ -52,6 +53,7 @@
 | 
			
		||||
            $.disableSettings([
 | 
			
		||||
                $('#formfield-form-widgets-anonymous_comments'),
 | 
			
		||||
                $('#formfield-form-widgets-moderation_enabled'),
 | 
			
		||||
                $('#formfield-form-widgets-edit_comment_enabled'),
 | 
			
		||||
                $('#formfield-form-widgets-text_transform'),
 | 
			
		||||
                $('#formfield-form-widgets-captcha'),
 | 
			
		||||
                $('#formfield-form-widgets-show_commenter_image'),
 | 
			
		||||
 | 
			
		||||
@ -223,3 +223,9 @@
 | 
			
		||||
.row .discussion label {
 | 
			
		||||
    font-weight:bold;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* editing comments */
 | 
			
		||||
 | 
			
		||||
.overlay-edit-comment textarea {
 | 
			
		||||
    height: 10em;
 | 
			
		||||
}
 | 
			
		||||
@ -42,6 +42,7 @@ from Products.CMFCore.CMFCatalogAware import WorkflowAware
 | 
			
		||||
 | 
			
		||||
from OFS.role import RoleManager
 | 
			
		||||
from AccessControl import ClassSecurityInfo
 | 
			
		||||
from AccessControl.SecurityManagement import getSecurityManager
 | 
			
		||||
from Products.CMFCore import permissions
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -115,6 +116,14 @@ class Comment(CatalogAware, WorkflowAware, DynamicType, Traversable,
 | 
			
		||||
        self.creation_date = self.modification_date = datetime.utcnow()
 | 
			
		||||
        self.mime_type = 'text/plain'
 | 
			
		||||
 | 
			
		||||
        user = getSecurityManager().getUser()
 | 
			
		||||
        if user and user.getId():
 | 
			
		||||
            aclpath = [x for x in user.getPhysicalPath() if x]
 | 
			
		||||
            self._owner = (aclpath, user.getId(),)
 | 
			
		||||
            self.__ac_local_roles__ = {
 | 
			
		||||
                user.getId(): ['Owner']
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def __name__(self):
 | 
			
		||||
        return self.comment_id and unicode(self.comment_id) or None
 | 
			
		||||
@ -193,7 +202,7 @@ class Comment(CatalogAware, WorkflowAware, DynamicType, Traversable,
 | 
			
		||||
    def Creator(self):
 | 
			
		||||
        """The name of the person who wrote the comment.
 | 
			
		||||
        """
 | 
			
		||||
        return self.creator
 | 
			
		||||
        return self.creator or self.author_name
 | 
			
		||||
 | 
			
		||||
    security.declareProtected(permissions.View, 'Type')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -43,6 +43,17 @@
 | 
			
		||||
        for="Products.CMFPlone.interfaces.IPloneSiteRoot"
 | 
			
		||||
        />
 | 
			
		||||
 | 
			
		||||
    <genericsetup:upgradeStep
 | 
			
		||||
        title="edit comments"
 | 
			
		||||
        description="reload registry config to enable new field edit_comment_enabled"
 | 
			
		||||
        source="100"
 | 
			
		||||
        destination="101"
 | 
			
		||||
        handler=".upgrades.update_registry"
 | 
			
		||||
        sortkey="1"
 | 
			
		||||
        profile="plone.app.discussion:default"
 | 
			
		||||
    />
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    <!-- Monkey Patches -->
 | 
			
		||||
 | 
			
		||||
    <include package="collective.monkeypatcher" />
 | 
			
		||||
 | 
			
		||||
@ -274,6 +274,17 @@ class IDiscussionSettings(Interface):
 | 
			
		||||
        default=False,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    edit_comment_enabled = schema.Bool(
 | 
			
		||||
        title=_(u"label_edit_comment_enabled",
 | 
			
		||||
                default="Enable editing of comments"),
 | 
			
		||||
        description=_(u"help_edit_comment_enabled",
 | 
			
		||||
                      default=u"If selected, supports editing and deletion "
 | 
			
		||||
                      "of comments for users with the 'Edit comments' "
 | 
			
		||||
                      "permission."),
 | 
			
		||||
        required=False,
 | 
			
		||||
        default=False,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    text_transform = schema.Choice(
 | 
			
		||||
        title=_(u"label_text_transform",
 | 
			
		||||
                default="Comment text transform"),
 | 
			
		||||
 | 
			
		||||
@ -9,4 +9,10 @@
 | 
			
		||||
        title="Review comments"
 | 
			
		||||
        />
 | 
			
		||||
 | 
			
		||||
    <permission
 | 
			
		||||
        id="plone.app.discussion.EditComments"
 | 
			
		||||
        title="Edit comments"
 | 
			
		||||
        />
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
</configure>
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
<metadata>
 | 
			
		||||
 <version>100</version>
 | 
			
		||||
 <version>101</version>
 | 
			
		||||
 <dependencies>
 | 
			
		||||
  <dependency>profile-plone.app.registry:default</dependency>
 | 
			
		||||
 </dependencies>
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,6 @@
 | 
			
		||||
<?xml version="1.0"?>
 | 
			
		||||
<registry>
 | 
			
		||||
 <records interface="plone.app.discussion.interfaces.IDiscussionSettings" />
 | 
			
		||||
	<records interface="plone.app.discussion.interfaces.IDiscussionSettings">
 | 
			
		||||
		<value key="edit_comment_enabled">False</value>
 | 
			
		||||
	</records>
 | 
			
		||||
</registry>
 | 
			
		||||
@ -6,6 +6,12 @@
 | 
			
		||||
	    <role name="Site Administrator"/>
 | 
			
		||||
            <role name="Reviewer"/>
 | 
			
		||||
        </permission>
 | 
			
		||||
        <permission name="Edit comments" acquire="True">
 | 
			
		||||
            <role name="Manager"/>
 | 
			
		||||
	    <role name="Site Administrator"/>
 | 
			
		||||
            <role name="Reviewer"/>
 | 
			
		||||
            <role name="Owner"/>
 | 
			
		||||
        </permission>
 | 
			
		||||
        <permission name="Reply to item" acquire="False">
 | 
			
		||||
            <role name="Authenticated"/>
 | 
			
		||||
        </permission>
 | 
			
		||||
 | 
			
		||||
@ -250,6 +250,77 @@ Check that the reply has been posted properly.
 | 
			
		||||
    True
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Edit an existing comment
 | 
			
		||||
------------------------
 | 
			
		||||
 | 
			
		||||
Log in as admin
 | 
			
		||||
 | 
			
		||||
    >>> browser.getLink('Log out').click()
 | 
			
		||||
    >>> browser.open(portal_url + '/login_form')
 | 
			
		||||
    >>> browser.getControl('Login Name').value = 'admin'
 | 
			
		||||
    >>> browser.getControl('Password').value = 'secret'
 | 
			
		||||
    >>> browser.getControl('Log in').click()
 | 
			
		||||
 | 
			
		||||
Use the Plone control panel to enable comment editing.
 | 
			
		||||
 | 
			
		||||
    >>> browser.open(portal_url + '/plone_control_panel')
 | 
			
		||||
    >>> browser.getLink('Discussion').click()
 | 
			
		||||
    >>> browser.getControl('Enable editing of comments').selected = True
 | 
			
		||||
    >>> browser.getControl(name='form.buttons.save').click()
 | 
			
		||||
 | 
			
		||||
Extract the edit comment url from the first "edit comment" button
 | 
			
		||||
 | 
			
		||||
    >>> browser.open(urldoc1)
 | 
			
		||||
    >>> form = browser.getForm(name='edit', index=0)
 | 
			
		||||
    >>> '@@edit-comment' in form.action
 | 
			
		||||
    True
 | 
			
		||||
 | 
			
		||||
Open the edit comment view
 | 
			
		||||
 | 
			
		||||
    >>> browser.open(form.action)
 | 
			
		||||
    >>> ctrl = browser.getControl('Comment')
 | 
			
		||||
    >>> ctrl.value
 | 
			
		||||
    'Comment from admin'
 | 
			
		||||
 | 
			
		||||
Change and save the comment
 | 
			
		||||
 | 
			
		||||
    >>> ctrl.value = 'Comment from admin / was edited'
 | 
			
		||||
    >>> browser.getControl('Edit comment').click()
 | 
			
		||||
 | 
			
		||||
This used to trigger permissions problems in some portlet configurations.
 | 
			
		||||
Check it ain't so.
 | 
			
		||||
 | 
			
		||||
    >>> 'require_login' in browser.url
 | 
			
		||||
    False
 | 
			
		||||
    >>> browser.url.startswith('http://nohost/plone/doc1')
 | 
			
		||||
    True
 | 
			
		||||
    >>> 'Comment from admin / was edited' in browser.contents
 | 
			
		||||
    True
 | 
			
		||||
 | 
			
		||||
Opening the edit comment view, then cancel, does nothing.
 | 
			
		||||
 | 
			
		||||
    >>> form = browser.getForm(name='edit', index=0)
 | 
			
		||||
    >>> '@@edit-comment' in form.action
 | 
			
		||||
    True
 | 
			
		||||
    >>> browser.open(form.action)
 | 
			
		||||
    >>> browser.getControl('Cancel').click()
 | 
			
		||||
    >>> browser.url.startswith('http://nohost/plone/doc1')
 | 
			
		||||
    True
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Anon cannot edit comments.
 | 
			
		||||
 | 
			
		||||
    >>> unprivileged_browser.open(urldoc1)
 | 
			
		||||
    >>> '@@edit-comments' in browser.contents
 | 
			
		||||
    False
 | 
			
		||||
 | 
			
		||||
But Anon can see the edited comment.
 | 
			
		||||
 | 
			
		||||
    >>> 'Comment from admin / was edited' in unprivileged_browser.contents
 | 
			
		||||
    True
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Post a comment with comment review workflow enabled
 | 
			
		||||
---------------------------------------------------
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -130,6 +130,16 @@ class CommentTest(unittest.TestCase):
 | 
			
		||||
        comment1.creator = "jim"
 | 
			
		||||
        self.assertEqual("jim", comment1.Creator())
 | 
			
		||||
 | 
			
		||||
    def test_creator_author_name(self):
 | 
			
		||||
        comment1 = createObject('plone.Comment')
 | 
			
		||||
        comment1.author_name = "joey"
 | 
			
		||||
        self.assertEqual("joey", comment1.Creator())
 | 
			
		||||
 | 
			
		||||
    def test_owner(self):
 | 
			
		||||
        comment1 = createObject('plone.Comment')
 | 
			
		||||
        self.assertEqual((['plone', 'acl_users'], TEST_USER_ID),
 | 
			
		||||
                         comment1.getOwnerTuple())
 | 
			
		||||
 | 
			
		||||
    def test_type(self):
 | 
			
		||||
        comment1 = createObject('plone.Comment')
 | 
			
		||||
        self.assertEqual(comment1.Type(), 'Comment')
 | 
			
		||||
 | 
			
		||||
@ -33,12 +33,14 @@ from plone.app.testing import login
 | 
			
		||||
 | 
			
		||||
from plone.app.discussion.browser.comments import CommentsViewlet
 | 
			
		||||
from plone.app.discussion.browser.comments import CommentForm
 | 
			
		||||
from plone.app.discussion.browser.comment import EditCommentForm
 | 
			
		||||
from plone.app.discussion import interfaces
 | 
			
		||||
from plone.app.discussion.interfaces import IConversation
 | 
			
		||||
from plone.app.discussion.testing import (
 | 
			
		||||
    PLONE_APP_DISCUSSION_INTEGRATION_TESTING
 | 
			
		||||
)
 | 
			
		||||
from plone.app.discussion.interfaces import IDiscussionSettings
 | 
			
		||||
from plone.app.discussion.interfaces import IConversation
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestCommentForm(unittest.TestCase):
 | 
			
		||||
@ -125,6 +127,65 @@ class TestCommentForm(unittest.TestCase):
 | 
			
		||||
        self.assertEqual(len(errors), 0)
 | 
			
		||||
        self.assertFalse(commentForm.handleComment(commentForm, "foo"))
 | 
			
		||||
 | 
			
		||||
    def test_edit_comment(self):
 | 
			
		||||
        """Edit a comment as logged-in user.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        # Allow discussion
 | 
			
		||||
        self.discussionTool.overrideDiscussionFor(self.portal.doc1, True)
 | 
			
		||||
        self.viewlet = CommentsViewlet(self.context, self.request, None, None)
 | 
			
		||||
 | 
			
		||||
        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"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        provideAdapter(
 | 
			
		||||
            adapts=(Interface, IBrowserRequest),
 | 
			
		||||
            provides=Interface,
 | 
			
		||||
            factory=EditCommentForm,
 | 
			
		||||
            name=u"edit-comment-form"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        # The form is submitted successfully, if the required text field is
 | 
			
		||||
        # filled out
 | 
			
		||||
        request = make_request(form={'form.widgets.text': u'bar'})
 | 
			
		||||
 | 
			
		||||
        commentForm = getMultiAdapter(
 | 
			
		||||
            (self.context, request),
 | 
			
		||||
            name=u"comment-form"
 | 
			
		||||
        )
 | 
			
		||||
        commentForm.update()
 | 
			
		||||
        data, errors = commentForm.extractData()  # pylint: disable-msg=W0612
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(len(errors), 0)
 | 
			
		||||
        self.assertFalse(commentForm.handleComment(commentForm, "foo"))
 | 
			
		||||
 | 
			
		||||
        # Edit the last comment
 | 
			
		||||
        conversation = IConversation(self.context)
 | 
			
		||||
        comment = [x for x in conversation.getComments()][-1]
 | 
			
		||||
        request = make_request(form={'form.widgets.text': u'foobar'})
 | 
			
		||||
        editForm = getMultiAdapter(
 | 
			
		||||
            (comment, request),
 | 
			
		||||
            name=u"edit-comment-form"
 | 
			
		||||
        )
 | 
			
		||||
        editForm.update()
 | 
			
		||||
        data, errors = editForm.extractData()  # pylint: disable-msg=W0612
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(len(errors), 0)
 | 
			
		||||
        self.assertFalse(editForm.handleComment(editForm, "foo"))
 | 
			
		||||
        comment = [x for x in conversation.getComments()][-1]
 | 
			
		||||
        self.assertEquals(comment.text, u"foobar")
 | 
			
		||||
 | 
			
		||||
    def test_add_anonymous_comment(self):
 | 
			
		||||
        self.discussionTool.overrideDiscussionFor(self.portal.doc1, True)
 | 
			
		||||
 | 
			
		||||
@ -459,9 +520,11 @@ class TestCommentsViewlet(unittest.TestCase):
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_get_commenter_portrait_is_none(self):
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            self.viewlet.get_commenter_portrait(),
 | 
			
		||||
            'defaultUser.gif'
 | 
			
		||||
        self.assertTrue(
 | 
			
		||||
            self.viewlet.get_commenter_portrait() in (
 | 
			
		||||
                'defaultUser.png',
 | 
			
		||||
                'defaultUser.gif',
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_get_commenter_portrait_without_userimage(self):
 | 
			
		||||
 | 
			
		||||
@ -81,6 +81,22 @@ class RegistryTest(unittest.TestCase):
 | 
			
		||||
            False
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_edit_comment_enabled(self):
 | 
			
		||||
        # Check edit_comment_enabled record
 | 
			
		||||
        self.assertTrue('edit_comment_enabled' in IDiscussionSettings)
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            self.registry['plone.app.discussion.interfaces.' +
 | 
			
		||||
                          'IDiscussionSettings.edit_comment_enabled'],
 | 
			
		||||
            False)
 | 
			
		||||
 | 
			
		||||
    def test_edit_comment_enabled(self):
 | 
			
		||||
        # Check edit_comment_enabled record
 | 
			
		||||
        self.assertTrue('edit_comment_enabled' in IDiscussionSettings)
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            self.registry['plone.app.discussion.interfaces.' +
 | 
			
		||||
                          'IDiscussionSettings.edit_comment_enabled'],
 | 
			
		||||
            False)
 | 
			
		||||
 | 
			
		||||
    def test_text_transform(self):
 | 
			
		||||
        self.assertTrue('text_transform' in IDiscussionSettings)
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user