Follow https://github.com/plone/jenkins.plone.org/blob/master/docs/source/run-qa-on-package.rst
to clean up the code.
This commit is contained in:
Gil Forcada 2016-02-05 01:39:53 +01:00
parent fb71aee6cd
commit cb1bf28c16
33 changed files with 620 additions and 551 deletions

View File

@ -15,6 +15,8 @@ Fixes:
Issue https://github.com/plone/Products.CMFPlone/issues/1332 Issue https://github.com/plone/Products.CMFPlone/issues/1332
[staeff, fredvd] [staeff, fredvd]
- Cleanup code according to our style guide.
[gforcada]
2.4.9 (2015-11-25) 2.4.9 (2015-11-25)

View File

@ -1,6 +1,2 @@
# See http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages # -*- coding: utf-8 -*-
try: __import__('pkg_resources').declare_namespace(__name__)
__import__('pkg_resources').declare_namespace(__name__)
except ImportError:
from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)

View File

@ -1,6 +1,2 @@
# See http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages # -*- coding: utf-8 -*-
try: __import__('pkg_resources').declare_namespace(__name__)
__import__('pkg_resources').declare_namespace(__name__)
except ImportError:
from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)

View File

@ -12,7 +12,8 @@ from z3c.form import interfaces
from z3c.form.field import Fields from z3c.form.field import Fields
from zope import interface from zope import interface
from zope.annotation import factory from zope.annotation import factory
from zope.component import adapts, queryUtility from zope.component import adapts
from zope.component import queryUtility
from zope.interface import Interface from zope.interface import Interface
from zope.publisher.interfaces.browser import IDefaultBrowserLayer from zope.publisher.interfaces.browser import IDefaultBrowserLayer
@ -51,7 +52,7 @@ class CaptchaExtender(extensible.FormExtender):
def update(self): def update(self):
if self.captcha != 'disabled' and self.isAnon: if self.captcha != 'disabled' and self.isAnon:
# Add a captcha field if captcha is enabled in the registry # Add a captcha field if captcha is enabled in the registry
self.add(ICaptcha, prefix="") self.add(ICaptcha, prefix='')
if self.captcha == 'captcha': if self.captcha == 'captcha':
from plone.formwidget.captcha import CaptchaFieldWidget from plone.formwidget.captcha import CaptchaFieldWidget
self.form.fields['captcha'].widgetFactory = CaptchaFieldWidget self.form.fields['captcha'].widgetFactory = CaptchaFieldWidget

View File

@ -1,16 +1,17 @@
# -*- coding: utf-8 -*-
from AccessControl import getSecurityManager from AccessControl import getSecurityManager
from Acquisition import aq_inner from Acquisition import aq_inner
from Acquisition import aq_parent from Acquisition import aq_parent
from zope.component import getUtility
from Products.CMFCore.utils import getToolByName
from Products.Five.browser import BrowserView
from Products.statusmessages.interfaces import IStatusMessage
from comments import CommentForm from comments import CommentForm
from plone.app.discussion import _ from plone.app.discussion import _
from plone.registry.interfaces import IRegistry from plone.registry.interfaces import IRegistry
from plone.z3cform.layout import wrap_form from plone.z3cform.layout import wrap_form
from Products.CMFCore.utils import getToolByName
from Products.Five.browser import BrowserView
from Products.statusmessages.interfaces import IStatusMessage
from z3c.form import button from z3c.form import button
from zope.component import getMultiAdapter from zope.component import getMultiAdapter
from zope.component import getUtility
class View(BrowserView): class View(BrowserView):
@ -21,9 +22,9 @@ class View(BrowserView):
has been posted. has been posted.
Redirect from the comment object URL Redirect from the comment object URL
"/path/to/object/++conversation++default/123456789" to the content object '/path/to/object/++conversation++default/123456789' to the content object
where the comment has been posted appended by an HTML anchor that points to where the comment has been posted appended by an HTML anchor that points to
the comment "/path/to/object#comment-123456789". the comment '/path/to/object#comment-123456789'.
Context is the comment object. The parent of the comment object is the Context is the comment object. The parent of the comment object is the
conversation. The parent of the conversation is the content object where conversation. The parent of the conversation is the content object where
@ -46,15 +47,15 @@ class View(BrowserView):
will redirect right to the binary object, bypassing comments. will redirect right to the binary object, bypassing comments.
""" """
if obj.portal_type in view_action_types: if obj.portal_type in view_action_types:
url = "%s/view" % url url = '{0}/view'.format(url)
self.request.response.redirect('%s#%s' % (url, context.id)) self.request.response.redirect('{0}#{1}'.format(url, context.id))
class EditCommentForm(CommentForm): class EditCommentForm(CommentForm):
"""Form to edit an existing comment.""" """Form to edit an existing comment."""
ignoreContext = True ignoreContext = True
id = "edit-comment-form" id = 'edit-comment-form'
label = _(u'edit_comment_form_title', default=u'Edit comment') label = _(u'edit_comment_form_title', default=u'Edit comment')
def updateWidgets(self): def updateWidgets(self):
@ -71,8 +72,8 @@ class EditCommentForm(CommentForm):
target = portal_state.portal_url() target = portal_state.portal_url()
self.request.response.redirect(target) self.request.response.redirect(target)
@button.buttonAndHandler(_(u"edit_comment_form_button", @button.buttonAndHandler(_(u'edit_comment_form_button',
default=u"Edit comment"), name='comment') default=u'Edit comment'), name='comment')
def handleComment(self, action): def handleComment(self, action):
# Validate form # Validate form
@ -93,19 +94,19 @@ class EditCommentForm(CommentForm):
# Redirect to comment # Redirect to comment
IStatusMessage(self.request).add(_(u'comment_edit_notification', IStatusMessage(self.request).add(_(u'comment_edit_notification',
default="Comment was edited"), default='Comment was edited'),
type='info') type='info')
return self._redirect( return self._redirect(
target=self.action.replace("@@edit-comment", "@@view")) target=self.action.replace('@@edit-comment', '@@view'))
@button.buttonAndHandler(_(u'cancel_form_button', @button.buttonAndHandler(_(u'cancel_form_button',
default=u'Cancel'), name='cancel') default=u'Cancel'), name='cancel')
def handle_cancel(self, action): def handle_cancel(self, action):
IStatusMessage(self.request).add( IStatusMessage(self.request).add(
_(u'comment_edit_cancel_notification', _(u'comment_edit_cancel_notification',
default=u'Edit comment cancelled'), default=u'Edit comment cancelled'),
type='info') type='info')
return self._redirect(target=self.context.absolute_url()) return self._redirect(target=self.context.absolute_url())
EditComment = wrap_form(EditCommentForm) EditComment = wrap_form(EditCommentForm)

View File

@ -2,11 +2,8 @@
from AccessControl import getSecurityManager from AccessControl import getSecurityManager
from AccessControl import Unauthorized from AccessControl import Unauthorized
from Acquisition import aq_inner from Acquisition import aq_inner
from DateTime import DateTime
from Products.CMFCore.utils import getToolByName
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
from Products.statusmessages.interfaces import IStatusMessage
from datetime import datetime from datetime import datetime
from DateTime import DateTime
from plone.app.discussion import _ from plone.app.discussion import _
from plone.app.discussion.browser.validator import CaptchaValidator from plone.app.discussion.browser.validator import CaptchaValidator
from plone.app.discussion.interfaces import ICaptcha from plone.app.discussion.interfaces import ICaptcha
@ -19,6 +16,9 @@ from plone.registry.interfaces import IRegistry
from plone.z3cform import z2 from plone.z3cform import z2
from plone.z3cform.fieldsets import extensible from plone.z3cform.fieldsets import extensible
from plone.z3cform.interfaces import IWrappedForm from plone.z3cform.interfaces import IWrappedForm
from Products.CMFCore.utils import getToolByName
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
from Products.statusmessages.interfaces import IStatusMessage
from urllib import quote as url_quote from urllib import quote as url_quote
from z3c.form import button from z3c.form import button
from z3c.form import field from z3c.form import field
@ -34,32 +34,36 @@ from zope.interface import alsoProvides
COMMENT_DESCRIPTION_PLAIN_TEXT = _( COMMENT_DESCRIPTION_PLAIN_TEXT = _(
u"comment_description_plain_text", u'comment_description_plain_text',
default=u"You can add a comment by filling out the form below. " + default=u'You can add a comment by filling out the form below. '
u"Plain text formatting.") u'Plain text formatting.'
)
COMMENT_DESCRIPTION_MARKDOWN = _( COMMENT_DESCRIPTION_MARKDOWN = _(
u"comment_description_markdown", u'comment_description_markdown',
default=u"You can add a comment by filling out the form below. " + default=u'You can add a comment by filling out the form below. '
u"Plain text formatting. You can use the Markdown syntax for " + u'Plain text formatting. You can use the Markdown syntax for '
u"links and images.") u'links and images.'
)
COMMENT_DESCRIPTION_INTELLIGENT_TEXT = _( COMMENT_DESCRIPTION_INTELLIGENT_TEXT = _(
u"comment_description_intelligent_text", u'comment_description_intelligent_text',
default=u"You can add a comment by filling out the form below. " + default=u'You can add a comment by filling out the form below. '
u"Plain text formatting. Web and email addresses are " + u'Plain text formatting. Web and email addresses are '
u"transformed into clickable links.") u'transformed into clickable links.'
)
COMMENT_DESCRIPTION_MODERATION_ENABLED = _( COMMENT_DESCRIPTION_MODERATION_ENABLED = _(
u"comment_description_moderation_enabled", u'comment_description_moderation_enabled',
default=u"Comments are moderated.") default=u'Comments are moderated.'
)
class CommentForm(extensible.ExtensibleForm, form.Form): class CommentForm(extensible.ExtensibleForm, form.Form):
ignoreContext = True # don't use context to get widget data ignoreContext = True # don't use context to get widget data
id = None id = None
label = _(u"Add a comment") label = _(u'Add a comment')
fields = field.Fields(IComment).omit('portal_type', fields = field.Fields(IComment).omit('portal_type',
'__parent__', '__parent__',
'__name__', '__name__',
@ -81,13 +85,13 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
# Widgets # Widgets
self.widgets['in_reply_to'].mode = interfaces.HIDDEN_MODE self.widgets['in_reply_to'].mode = interfaces.HIDDEN_MODE
self.widgets['text'].addClass("autoresize") self.widgets['text'].addClass('autoresize')
self.widgets['user_notification'].label = _(u"") self.widgets['user_notification'].label = _(u'')
# Rename the id of the text widgets because there can be css-id # Rename the id of the text widgets because there can be css-id
# clashes with the text field of documents when using and overlay # clashes with the text field of documents when using and overlay
# with TinyMCE. # with TinyMCE.
self.widgets['text'].id = "form-widgets-comment-text" self.widgets['text'].id = 'form-widgets-comment-text'
# Anonymous / Logged-in # Anonymous / Logged-in
mtool = getToolByName(self.context, 'portal_membership') mtool = getToolByName(self.context, 'portal_membership')
@ -99,7 +103,7 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
if anon: if anon:
if settings.anonymous_email_enabled: if settings.anonymous_email_enabled:
# according to IDiscussionSettings.anonymous_email_enabled: # according to IDiscussionSettings.anonymous_email_enabled:
# "If selected, anonymous user will have to give their email." # 'If selected, anonymous user will have to give their email.'
self.widgets['author_email'].field.required = True self.widgets['author_email'].field.required = True
self.widgets['author_email'].required = True self.widgets['author_email'].required = True
else: else:
@ -121,11 +125,11 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
def updateActions(self): def updateActions(self):
super(CommentForm, self).updateActions() super(CommentForm, self).updateActions()
self.actions['cancel'].addClass("standalone") self.actions['cancel'].addClass('standalone')
self.actions['cancel'].addClass("hide") self.actions['cancel'].addClass('hide')
self.actions['comment'].addClass("context") self.actions['comment'].addClass('context')
@button.buttonAndHandler(_(u"add_comment_button", default=u"Comment"), @button.buttonAndHandler(_(u'add_comment_button', default=u'Comment'),
name='comment') name='comment')
def handleComment(self, action): def handleComment(self, action):
context = aq_inner(self.context) context = aq_inner(self.context)
@ -134,8 +138,9 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
if not self.__parent__.restrictedTraverse( if not self.__parent__.restrictedTraverse(
'@@conversation_view' '@@conversation_view'
).enabled(): ).enabled():
raise Unauthorized("Discussion is not enabled for this content " raise Unauthorized(
"object.") 'Discussion is not enabled for this content object.'
)
# Validation form # Validation form
data, errors = self.extractData() data, errors = self.extractData()
@ -151,7 +156,7 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
anon = portal_membership.isAnonymousUser() anon = portal_membership.isAnonymousUser()
if captcha_enabled and anonymous_comments and anon: if captcha_enabled and anonymous_comments and anon:
if 'captcha' not in data: if 'captcha' not in data:
data['captcha'] = u"" data['captcha'] = u''
captcha = CaptchaValidator(self.context, captcha = CaptchaValidator(self.context,
self.request, self.request,
None, None,
@ -160,7 +165,7 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
captcha.validate(data['captcha']) captcha.validate(data['captcha'])
# some attributes are not always set # some attributes are not always set
author_name = u"" author_name = u''
# Create comment # Create comment
comment = createObject('plone.Comment') comment = createObject('plone.Comment')
@ -207,14 +212,14 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
if email and isinstance(email, str): if email and isinstance(email, str):
email = unicode(email, 'utf-8') email = unicode(email, 'utf-8')
comment.changeOwnership(user, recursive=False) comment.changeOwnership(user, recursive=False)
comment.manage_setLocalRoles(memberid, ["Owner"]) comment.manage_setLocalRoles(memberid, ['Owner'])
comment.creator = memberid comment.creator = memberid
comment.author_username = memberid comment.author_username = memberid
comment.author_name = fullname comment.author_name = fullname
# XXX: according to IComment interface author_email must not be # XXX: according to IComment interface author_email must not be
# set for logged in users, cite: # set for logged in users, cite:
# "for anonymous comments only, set to None for logged in comments" # 'for anonymous comments only, set to None for logged in comments'
comment.author_email = email comment.author_email = email
# /XXX # /XXX
@ -223,8 +228,8 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
else: # pragma: no cover else: # pragma: no cover
raise Unauthorized( raise Unauthorized(
u"Anonymous user tries to post a comment, but anonymous " u'Anonymous user tries to post a comment, but anonymous '
u"commenting is disabled. Or user does not have the " u'commenting is disabled. Or user does not have the '
u"'reply to item' permission." u"'reply to item' permission."
) )
@ -255,14 +260,14 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
if comment_review_state == 'pending' and not can_review: if comment_review_state == 'pending' and not can_review:
# Show info message when comment moderation is enabled # Show info message when comment moderation is enabled
IStatusMessage(self.context.REQUEST).addStatusMessage( IStatusMessage(self.context.REQUEST).addStatusMessage(
_("Your comment awaits moderator approval."), _('Your comment awaits moderator approval.'),
type="info") type='info')
self.request.response.redirect(self.action) self.request.response.redirect(self.action)
else: else:
# Redirect to comment (inside a content object page) # Redirect to comment (inside a content object page)
self.request.response.redirect(self.action + '#' + str(comment_id)) self.request.response.redirect(self.action + '#' + str(comment_id))
@button.buttonAndHandler(_(u"Cancel")) @button.buttonAndHandler(_(u'Cancel'))
def handleCancel(self, action): def handleCancel(self, action):
# This method should never be called, it's only there to show # This method should never be called, it's only there to show
# a cancel button that is handled by a jQuery method. # a cancel button that is handled by a jQuery method.
@ -278,9 +283,9 @@ class CommentsViewlet(ViewletBase):
super(CommentsViewlet, self).update() super(CommentsViewlet, self).update()
discussion_allowed = self.is_discussion_allowed() discussion_allowed = self.is_discussion_allowed()
anonymous_allowed_or_can_reply = ( anonymous_allowed_or_can_reply = (
self.is_anonymous() self.is_anonymous() and
and self.anonymous_discussion_allowed() self.anonymous_discussion_allowed() or
or self.can_reply() self.can_reply()
) )
if discussion_allowed and anonymous_allowed_or_can_reply: if discussion_allowed and anonymous_allowed_or_can_reply:
z2.switch_on(self, request_layer=IFormLayer) z2.switch_on(self, request_layer=IFormLayer)
@ -356,10 +361,10 @@ class CommentsViewlet(ViewletBase):
settings = registry.forInterface(IDiscussionSettings, check=False) settings = registry.forInterface(IDiscussionSettings, check=False)
# text transform setting # text transform setting
if settings.text_transform == "text/x-web-intelligent": if settings.text_transform == 'text/x-web-intelligent':
message = translate(Message(COMMENT_DESCRIPTION_INTELLIGENT_TEXT), message = translate(Message(COMMENT_DESCRIPTION_INTELLIGENT_TEXT),
context=self.request) context=self.request)
elif settings.text_transform == "text/x-web-markdown": elif settings.text_transform == 'text/x-web-markdown':
message = translate(Message(COMMENT_DESCRIPTION_MARKDOWN), message = translate(Message(COMMENT_DESCRIPTION_MARKDOWN),
context=self.request) context=self.request)
else: else:
@ -367,7 +372,7 @@ class CommentsViewlet(ViewletBase):
context=self.request) context=self.request)
# comment workflow # comment workflow
wftool = getToolByName(context, "portal_workflow", None) wftool = getToolByName(context, 'portal_workflow', None)
workflow_chain = wftool.getChainForPortalType('Discussion Item') workflow_chain = wftool.getChainForPortalType('Discussion Item')
if workflow_chain: if workflow_chain:
comment_workflow = workflow_chain[0] comment_workflow = workflow_chain[0]
@ -375,7 +380,7 @@ class CommentsViewlet(ViewletBase):
# check if the current workflow implements a pending state. If this # check if the current workflow implements a pending state. If this
# is true comments are moderated # is true comments are moderated
if 'pending' in comment_workflow.states: if 'pending' in comment_workflow.states:
message = message + " " + \ message = message + ' ' + \
translate(Message(COMMENT_DESCRIPTION_MODERATION_ENABLED), translate(Message(COMMENT_DESCRIPTION_MODERATION_ENABLED),
context=self.request) context=self.request)
@ -445,7 +450,7 @@ class CommentsViewlet(ViewletBase):
if username is None: if username is None:
return None return None
else: else:
return "%s/author/%s" % (self.context.portal_url(), username) return '{0}/author/{1}'.format(self.context.portal_url(), username)
def get_commenter_portrait(self, username=None): def get_commenter_portrait(self, username=None):
@ -491,9 +496,10 @@ class CommentsViewlet(ViewletBase):
return portal_membership.isAnonymousUser() return portal_membership.isAnonymousUser()
def login_action(self): def login_action(self):
return '%s/login_form?came_from=%s' % \ return '{0}/login_form?came_from={1}'.format(
(self.navigation_root_url, self.navigation_root_url,
url_quote(self.request.get('URL', '')),) url_quote(self.request.get('URL', '')),
)
def format_time(self, time): def format_time(self, time):
# We have to transform Python datetime into Zope DateTime # We have to transform Python datetime into Zope DateTime

View File

@ -1,9 +1,4 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from Products.CMFCore.interfaces._content import IDiscussionResponse
from Products.CMFCore.utils import getToolByName
from Products.CMFPlone.interfaces.controlpanel import IMailSchema
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
from Products.statusmessages.interfaces import IStatusMessage
from plone.app.controlpanel.interfaces import IConfigurationChangedEvent from plone.app.controlpanel.interfaces import IConfigurationChangedEvent
from plone.app.discussion.interfaces import _ from plone.app.discussion.interfaces import _
from plone.app.discussion.interfaces import IDiscussionSettings from plone.app.discussion.interfaces import IDiscussionSettings
@ -11,6 +6,11 @@ from plone.app.discussion.upgrades import update_registry
from plone.app.registry.browser import controlpanel from plone.app.registry.browser import controlpanel
from plone.registry.interfaces import IRecordModifiedEvent from plone.registry.interfaces import IRecordModifiedEvent
from plone.registry.interfaces import IRegistry from plone.registry.interfaces import IRegistry
from Products.CMFCore.interfaces._content import IDiscussionResponse
from Products.CMFCore.utils import getToolByName
from Products.CMFPlone.interfaces.controlpanel import IMailSchema
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
from Products.statusmessages.interfaces import IStatusMessage
from z3c.form import button from z3c.form import button
from z3c.form.browser.checkbox import SingleCheckBoxFieldWidget from z3c.form.browser.checkbox import SingleCheckBoxFieldWidget
from zope.component import getMultiAdapter from zope.component import getMultiAdapter
@ -23,19 +23,19 @@ class DiscussionSettingsEditForm(controlpanel.RegistryEditForm):
"""Discussion settings form. """Discussion settings form.
""" """
schema = IDiscussionSettings schema = IDiscussionSettings
id = "DiscussionSettingsEditForm" id = 'DiscussionSettingsEditForm'
label = _(u"Discussion settings") label = _(u'Discussion settings')
description = _( description = _(
u"help_discussion_settings_editform", u'help_discussion_settings_editform',
default=u"Some discussion related settings are not " default=u'Some discussion related settings are not '
u"located in the Discussion Control Panel.\n" u'located in the Discussion Control Panel.\n'
u"To enable comments for a specific content type, " u'To enable comments for a specific content type, '
u"go to the Types Control Panel of this type and " u'go to the Types Control Panel of this type and '
u"choose \"Allow comments\".\n" u'choose "Allow comments".\n'
u"To enable the moderation workflow for comments, " u'To enable the moderation workflow for comments, '
u"go to the Types Control Panel, choose " u'go to the Types Control Panel, choose '
u"\"Comment\" and set workflow to " u'"Comment" and set workflow to '
u"\"Comment Review Workflow\"." u'"Comment Review Workflow".'
) )
def updateFields(self): def updateFields(self):
@ -65,13 +65,15 @@ class DiscussionSettingsEditForm(controlpanel.RegistryEditForm):
# provide auto-upgrade # provide auto-upgrade
update_registry(self.context) update_registry(self.context)
super(DiscussionSettingsEditForm, self).updateWidgets() super(DiscussionSettingsEditForm, self).updateWidgets()
self.widgets['globally_enabled'].label = _(u"Enable Comments") self.widgets['globally_enabled'].label = _(u'Enable Comments')
self.widgets['anonymous_comments'].label = _(u"Anonymous Comments") self.widgets['anonymous_comments'].label = _(u'Anonymous Comments')
self.widgets['show_commenter_image'].label = _(u"Commenter Image") self.widgets['show_commenter_image'].label = _(u'Commenter Image')
self.widgets['moderator_notification_enabled'].label = \ self.widgets['moderator_notification_enabled'].label = _(
_(u"Moderator Email Notification") u'Moderator Email Notification'
self.widgets['user_notification_enabled'].label = \ )
_(u"User Email Notification") self.widgets['user_notification_enabled'].label = _(
u'User Email Notification'
)
@button.buttonAndHandler(_('Save'), name=None) @button.buttonAndHandler(_('Save'), name=None)
def handleSave(self, action): def handleSave(self, action):
@ -80,16 +82,20 @@ class DiscussionSettingsEditForm(controlpanel.RegistryEditForm):
self.status = self.formErrorsMessage self.status = self.formErrorsMessage
return return
self.applyChanges(data) self.applyChanges(data)
IStatusMessage(self.request).addStatusMessage(_(u"Changes saved"), IStatusMessage(self.request).addStatusMessage(_(u'Changes saved'),
"info") 'info')
self.context.REQUEST.RESPONSE.redirect("@@discussion-controlpanel") self.context.REQUEST.RESPONSE.redirect('@@discussion-controlpanel')
@button.buttonAndHandler(_('Cancel'), name='cancel') @button.buttonAndHandler(_('Cancel'), name='cancel')
def handleCancel(self, action): def handleCancel(self, action):
IStatusMessage(self.request).addStatusMessage(_(u"Edit cancelled"), IStatusMessage(self.request).addStatusMessage(_(u'Edit cancelled'),
"info") 'info')
self.request.response.redirect("%s/%s" % (self.context.absolute_url(), self.request.response.redirect(
self.control_panel_view)) '{0}/{1}'.format(
self.context.absolute_url(),
self.control_panel_view
)
)
class DiscussionSettingsControlPanel(controlpanel.ControlPanelFormWrapper): class DiscussionSettingsControlPanel(controlpanel.ControlPanelFormWrapper):
@ -104,38 +110,38 @@ class DiscussionSettingsControlPanel(controlpanel.ControlPanelFormWrapper):
""" """
registry = queryUtility(IRegistry) registry = queryUtility(IRegistry)
settings = registry.forInterface(IDiscussionSettings, check=False) settings = registry.forInterface(IDiscussionSettings, check=False)
wftool = getToolByName(self.context, "portal_workflow", None) wftool = getToolByName(self.context, 'portal_workflow', None)
workflow_chain = wftool.getChainForPortalType('Discussion Item') workflow_chain = wftool.getChainForPortalType('Discussion Item')
output = [] output = []
# Globally enabled # Globally enabled
if settings.globally_enabled: if settings.globally_enabled:
output.append("globally_enabled") output.append('globally_enabled')
# Comment moderation # Comment moderation
one_state_worklow_disabled = 'one_state_workflow' not in workflow_chain one_state_worklow_disabled = 'one_state_workflow' not in workflow_chain
comment_review_workflow_disabled = \ comment_review_workflow_disabled = \
'comment_review_workflow' not in workflow_chain 'comment_review_workflow' not in workflow_chain
if one_state_worklow_disabled and comment_review_workflow_disabled: if one_state_worklow_disabled and comment_review_workflow_disabled:
output.append("moderation_custom") output.append('moderation_custom')
elif settings.moderation_enabled: elif settings.moderation_enabled:
output.append("moderation_enabled") output.append('moderation_enabled')
if settings.edit_comment_enabled: if settings.edit_comment_enabled:
output.append("edit_comment_enabled") output.append('edit_comment_enabled')
if settings.delete_own_comment_enabled: if settings.delete_own_comment_enabled:
output.append("delte_own_comment_enabled") output.append('delete_own_comment_enabled')
# Anonymous comments # Anonymous comments
if settings.anonymous_comments: if settings.anonymous_comments:
output.append("anonymous_comments") output.append('anonymous_comments')
# Invalid mail setting # Invalid mail setting
ctrlOverview = getMultiAdapter((self.context, self.request), ctrlOverview = getMultiAdapter((self.context, self.request),
name='overview-controlpanel') name='overview-controlpanel')
if ctrlOverview.mailhost_warning(): if ctrlOverview.mailhost_warning():
output.append("invalid_mail_setup") output.append('invalid_mail_setup')
# Workflow # Workflow
wftool = getToolByName(self.context, 'portal_workflow', None) wftool = getToolByName(self.context, 'portal_workflow', None)
@ -161,7 +167,7 @@ class DiscussionSettingsControlPanel(controlpanel.ControlPanelFormWrapper):
def custom_comment_workflow_warning(self): def custom_comment_workflow_warning(self):
"""Returns a warning string if a custom comment workflow is enabled. """Returns a warning string if a custom comment workflow is enabled.
""" """
wftool = getToolByName(self.context, "portal_workflow", None) wftool = getToolByName(self.context, 'portal_workflow', None)
workflow_chain = wftool.getChainForPortalType('Discussion Item') workflow_chain = wftool.getChainForPortalType('Discussion Item')
one_state_workflow_enabled = 'one_state_workflow' in workflow_chain one_state_workflow_enabled = 'one_state_workflow' in workflow_chain
comment_review_workflow_enabled = \ comment_review_workflow_enabled = \

View File

@ -1,18 +1,20 @@
# -*- coding: utf-8 -*-
from Acquisition import aq_base from Acquisition import aq_base
from Acquisition import aq_chain from Acquisition import aq_chain
from Acquisition import aq_inner from Acquisition import aq_inner
from plone.app.discussion.interfaces import IDiscussionSettings
from plone.registry.interfaces import IRegistry
from Products.CMFCore.interfaces import IFolderish from Products.CMFCore.interfaces import IFolderish
from Products.CMFCore.utils import getToolByName from Products.CMFCore.utils import getToolByName
from Products.CMFPlone.interfaces import INonStructuralFolder from Products.CMFPlone.interfaces import INonStructuralFolder
from Products.CMFPlone.interfaces import IPloneSiteRoot from Products.CMFPlone.interfaces import IPloneSiteRoot
from plone.app.discussion.interfaces import IDiscussionSettings
from plone.registry.interfaces import IRegistry
from zope.component import queryUtility from zope.component import queryUtility
try: try:
from plone.dexterity.interfaces import IDexterityContent from plone.dexterity.interfaces import IDexterityContent
DEXTERITY_INSTALLED = True DEXTERITY_INSTALLED = True
except: except ImportError:
DEXTERITY_INSTALLED = False DEXTERITY_INSTALLED = False
@ -130,7 +132,7 @@ class ConversationView(object):
return False return False
# Check if discussion is allowed on the content object # Check if discussion is allowed on the content object
if hasattr(context, "allow_discussion"): if hasattr(context, 'allow_discussion'):
if context.allow_discussion is not None: if context.allow_discussion is not None:
return context.allow_discussion return context.allow_discussion

View File

@ -1,12 +1,15 @@
# -*- coding: utf-8 -*-
from Acquisition import aq_inner from Acquisition import aq_inner
from Acquisition import aq_parent from Acquisition import aq_parent
from datetime import datetime
from DateTime import DateTime from DateTime import DateTime
from plone.app.discussion.comment import CommentFactory
from plone.app.discussion.interfaces import IComment
from plone.app.discussion.interfaces import IConversation
from plone.app.discussion.interfaces import IReplies
from Products.CMFCore.interfaces._content import IDiscussionResponse from Products.CMFCore.interfaces._content import IDiscussionResponse
from Products.CMFCore.utils import getToolByName from Products.CMFCore.utils import getToolByName
from Products.Five.browser import BrowserView from Products.Five.browser import BrowserView
from datetime import datetime
from plone.app.discussion.comment import CommentFactory
from plone.app.discussion.interfaces import IConversation, IReplies, IComment
from types import TupleType from types import TupleType
import transaction import transaction
@ -35,11 +38,11 @@ class View(BrowserView):
self.total_comments_migrated = 0 self.total_comments_migrated = 0
self.total_comments_deleted = 0 self.total_comments_deleted = 0
dry_run = "dry_run" in self.request dry_run = 'dry_run' in self.request
# This is for testing only. # This is for testing only.
# Do not use transactions during a test. # Do not use transactions during a test.
test = "test" in self.request test = 'test' in self.request
if not test: if not test:
transaction.begin() # pragma: no cover transaction.begin() # pragma: no cover
@ -71,10 +74,10 @@ class View(BrowserView):
for reply in replies: for reply in replies:
# log # log
indent = " " indent = ' '
for i in range(depth): for i in range(depth):
indent += " " indent += ' '
log("%smigrate_reply: '%s'." % (indent, reply.title)) log('{0}migrate_reply: "{1}".'.format(indent, reply.title))
should_migrate = True should_migrate = True
if filter_callback and not filter_callback(reply): if filter_callback and not filter_callback(reply):
@ -172,7 +175,7 @@ class View(BrowserView):
talkback.deleteReply(reply.id) talkback.deleteReply(reply.id)
obj = aq_parent(talkback) obj = aq_parent(talkback)
obj.talkback = None obj.talkback = None
log("%sremove %s" % (indent, reply.id)) log('{0}remove {1}'.format(indent, reply.id))
self.total_comments_deleted += 1 self.total_comments_deleted += 1
# Return True when all comments on a certain level have been # Return True when all comments on a certain level have been
@ -182,7 +185,7 @@ class View(BrowserView):
# Find content # Find content
brains = catalog.searchResults( brains = catalog.searchResults(
object_provides='Products.CMFCore.interfaces._content.IContentish') object_provides='Products.CMFCore.interfaces._content.IContentish')
log("Found %s content objects." % len(brains)) log('Found {0} content objects.'.format(len(brains)))
count_discussion_items = len( count_discussion_items = len(
catalog.searchResults(Type='Discussion Item') catalog.searchResults(Type='Discussion Item')
@ -196,12 +199,20 @@ class View(BrowserView):
) )
) )
log("Found %s Discussion Item objects." % count_discussion_items) log(
log("Found %s old discussion items." % count_comments_old) 'Found {0} Discussion Item objects.'.format(
log("Found %s plone.app.discussion comments." % count_comments_pad) count_discussion_items
)
)
log('Found {0} old discussion items.'.format(count_comments_old))
log(
'Found {0} plone.app.discussion comments.'.format(
count_comments_pad
)
)
log("\n") log('\n')
log("Start comment migration.") log('Start comment migration.')
# This loop is necessary to get all contentish objects, but not # This loop is necessary to get all contentish objects, but not
# the Discussion Items. This wouldn't be necessary if the # the Discussion Items. This wouldn't be necessary if the
@ -219,46 +230,54 @@ class View(BrowserView):
replies = talkback.getReplies() replies = talkback.getReplies()
if replies: if replies:
conversation = IConversation(obj) conversation = IConversation(obj)
log("\n") log('\n')
log("Migrate '%s' (%s)" % (obj.Title(), log(
obj.absolute_url(relative=1))) 'Migrate "{0}" ({1})'.format(
obj.Title(),
obj.absolute_url(relative=1)
)
)
migrate_replies(context, 0, replies) migrate_replies(context, 0, replies)
obj = aq_parent(talkback) obj = aq_parent(talkback)
obj.talkback = None obj.talkback = None
if self.total_comments_deleted != self.total_comments_migrated: if self.total_comments_deleted != self.total_comments_migrated:
log( log(
"Something went wrong during migration. The number of " + 'Something went wrong during migration. The number of '
"migrated comments (%s) differs from the number of deleted " + 'migrated comments ({0}) differs from the number of deleted '
"comments (%s)." % ( 'comments ({1}).'.format(
self.total_comments_migrated, self.total_comments_migrated,
self.total_comments_deleted self.total_comments_deleted
) )
) )
if not test: # pragma: no cover if not test: # pragma: no cover
transaction.abort() # pragma: no cover transaction.abort() # pragma: no cover
log("Abort transaction") # pragma: no cover log('Abort transaction') # pragma: no cover
log("\n") log('\n')
log("Comment migration finished.") log('Comment migration finished.')
log("\n") log('\n')
log("%s of %s comments migrated." log(
% (self.total_comments_migrated, count_comments_old)) '{0} of {1} comments migrated.'.format(
self.total_comments_migrated,
count_comments_old
)
)
if self.total_comments_migrated != count_comments_old: if self.total_comments_migrated != count_comments_old:
log( log(
"%s comments could not be migrated." % ( '{0} comments could not be migrated.'.format(
count_comments_old - self.total_comments_migrated count_comments_old - self.total_comments_migrated
) )
) # pragma: no cover ) # pragma: no cover
log("Please make sure your " + log('Please make sure your ' +
"portal catalog is up-to-date.") # pragma: no cover 'portal catalog is up-to-date.') # pragma: no cover
if dry_run and not test: if dry_run and not test:
transaction.abort() # pragma: no cover transaction.abort() # pragma: no cover
log("Dry run") # pragma: no cover log('Dry run') # pragma: no cover
log("Abort transaction") # pragma: no cover log('Abort transaction') # pragma: no cover
if not test: if not test:
transaction.commit() # pragma: no cover transaction.commit() # pragma: no cover
return '\n'.join(out) return '\n'.join(out)

View File

@ -1,15 +1,15 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from AccessControl import Unauthorized
from AccessControl import getSecurityManager from AccessControl import getSecurityManager
from AccessControl import Unauthorized
from Acquisition import aq_inner from Acquisition import aq_inner
from Acquisition import aq_parent from Acquisition import aq_parent
from plone.app.discussion.interfaces import _
from plone.app.discussion.interfaces import IComment
from plone.app.discussion.interfaces import IReplies
from Products.CMFCore.utils import getToolByName from Products.CMFCore.utils import getToolByName
from Products.Five.browser import BrowserView from Products.Five.browser import BrowserView
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
from Products.statusmessages.interfaces import IStatusMessage from Products.statusmessages.interfaces import IStatusMessage
from plone.app.discussion.interfaces import _
from plone.app.discussion.interfaces import IComment
from plone.app.discussion.interfaces import IReplies
class View(BrowserView): class View(BrowserView):
@ -101,8 +101,8 @@ class DeleteComment(BrowserView):
del conversation[comment.id] del conversation[comment.id]
content_object.reindexObject() content_object.reindexObject()
IStatusMessage(self.context.REQUEST).addStatusMessage( IStatusMessage(self.context.REQUEST).addStatusMessage(
_("Comment deleted."), _('Comment deleted.'),
type="info") type='info')
came_from = self.context.REQUEST.HTTP_REFERER came_from = self.context.REQUEST.HTTP_REFERER
# if the referrer already has a came_from in it, don't redirect back # 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: if len(came_from) == 0 or 'came_from=' in came_from:
@ -133,14 +133,17 @@ class DeleteOwnComment(DeleteComment):
sm = getSecurityManager() sm = getSecurityManager()
comment = comment or aq_inner(self.context) comment = comment or aq_inner(self.context)
userid = sm.getUser().getId() userid = sm.getUser().getId()
return (sm.checkPermission('Delete own comments', return (
comment) sm.checkPermission('Delete own comments', comment) and
and 'Owner' in comment.get_local_roles_for_userid(userid)) 'Owner' in comment.get_local_roles_for_userid(userid)
)
def can_delete(self, comment=None): def can_delete(self, comment=None):
comment = comment or self.context comment = comment or self.context
return (len(IReplies(aq_inner(comment))) == 0 return (
and self.could_delete(comment=comment)) len(IReplies(aq_inner(comment))) == 0 and
self.could_delete(comment=comment)
)
def __call__(self): def __call__(self):
if self.can_delete(): if self.can_delete():
@ -179,8 +182,8 @@ class PublishComment(BrowserView):
comment.reindexObject() comment.reindexObject()
content_object.reindexObject(idxs=['total_comments']) content_object.reindexObject(idxs=['total_comments'])
IStatusMessage(self.context.REQUEST).addStatusMessage( IStatusMessage(self.context.REQUEST).addStatusMessage(
_("Comment approved."), _('Comment approved.'),
type="info") type='info')
came_from = self.context.REQUEST.HTTP_REFERER came_from = self.context.REQUEST.HTTP_REFERER
# if the referrer already has a came_from in it, don't redirect back # 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: if len(came_from) == 0 or 'came_from=' in came_from:

View File

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
"""Implement the ++comments++ traversal namespace. This should return the """Implement the ++comments++ traversal namespace. This should return the
IDiscussion container for the context, from which traversal will continue IDiscussion container for the context, from which traversal will continue
into an actual comment object. into an actual comment object.
@ -29,8 +30,8 @@ class ConversationNamespace(object):
def traverse(self, name, ignore): def traverse(self, name, ignore):
if name == "default": if name == 'default':
name = u"" name = u''
conversation = queryAdapter(self.context, IConversation, name=name) conversation = queryAdapter(self.context, IConversation, name=name)
if conversation is None: if conversation is None:

View File

@ -1,15 +1,17 @@
# -*- coding: utf-8 -*-
"""Catalog indexers, using plone.indexer. These will populate standard catalog """Catalog indexers, using plone.indexer. These will populate standard catalog
indexes with values based on the IComment interface. indexes with values based on the IComment interface.
Also provide event handlers to actually catalog the comments. Also provide event handlers to actually catalog the comments.
""" """
from DateTime import DateTime from DateTime import DateTime
from plone.app.discussion.interfaces import IComment
from plone.app.discussion.interfaces import IConversation
from plone.indexer import indexer
from plone.uuid.interfaces import IUUID
from Products.CMFCore.interfaces import IContentish from Products.CMFCore.interfaces import IContentish
from Products.CMFPlone.utils import safe_unicode from Products.CMFPlone.utils import safe_unicode
from Products.ZCatalog.interfaces import IZCatalog from Products.ZCatalog.interfaces import IZCatalog
from plone.app.discussion.interfaces import IConversation, IComment
from plone.indexer import indexer
from plone.uuid.interfaces import IUUID
from string import join from string import join
@ -78,7 +80,7 @@ def description(object):
text = join(object.getText( text = join(object.getText(
targetMimetype='text/plain').split()[:MAX_DESCRIPTION]) targetMimetype='text/plain').split()[:MAX_DESCRIPTION])
if len(object.getText().split()) > 25: if len(object.getText().split()) > 25:
text += " [...]" text += ' [...]'
return text return text

View File

@ -3,16 +3,9 @@
""" """
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
from AccessControl.SecurityManagement import getSecurityManager from AccessControl.SecurityManagement import getSecurityManager
from Acquisition import Implicit
from Acquisition import aq_base from Acquisition import aq_base
from Acquisition import aq_parent from Acquisition import aq_parent
from Products.CMFCore import permissions from Acquisition import Implicit
from Products.CMFCore.CMFCatalogAware import CatalogAware
from Products.CMFCore.CMFCatalogAware import WorkflowAware
from Products.CMFCore.DynamicType import DynamicType
from Products.CMFCore.utils import getToolByName
from Products.CMFPlone.interfaces.controlpanel import IMailSchema
from Products.CMFPlone.utils import safe_unicode
from datetime import datetime from datetime import datetime
from OFS.owner import Owned from OFS.owner import Owned
from OFS.role import RoleManager from OFS.role import RoleManager
@ -27,6 +20,13 @@ from plone.app.discussion.interfaces import IComment
from plone.app.discussion.interfaces import IConversation from plone.app.discussion.interfaces import IConversation
from plone.app.discussion.interfaces import IDiscussionSettings from plone.app.discussion.interfaces import IDiscussionSettings
from plone.registry.interfaces import IRegistry from plone.registry.interfaces import IRegistry
from Products.CMFCore import permissions
from Products.CMFCore.CMFCatalogAware import CatalogAware
from Products.CMFCore.CMFCatalogAware import WorkflowAware
from Products.CMFCore.DynamicType import DynamicType
from Products.CMFCore.utils import getToolByName
from Products.CMFPlone.interfaces.controlpanel import IMailSchema
from Products.CMFPlone.utils import safe_unicode
from smtplib import SMTPException from smtplib import SMTPException
from zope.annotation.interfaces import IAnnotatable from zope.annotation.interfaces import IAnnotatable
from zope.component import getUtility from zope.component import getUtility
@ -41,28 +41,28 @@ import logging
COMMENT_TITLE = _( COMMENT_TITLE = _(
u"comment_title", u'comment_title',
default=u"${author_name} on ${content}") default=u'${author_name} on ${content}')
MAIL_NOTIFICATION_MESSAGE = _( MAIL_NOTIFICATION_MESSAGE = _(
u"mail_notification_message", u'mail_notification_message',
default=u"A comment on '${title}' " default=u'A comment on "${title}" '
u"has been posted here: ${link}\n\n" u'has been posted here: ${link}\n\n'
u"---\n" u'---\n'
u"${text}\n" u'${text}\n'
u"---\n") u'---\n')
MAIL_NOTIFICATION_MESSAGE_MODERATOR = _( MAIL_NOTIFICATION_MESSAGE_MODERATOR = _(
u"mail_notification_message_moderator", u'mail_notification_message_moderator',
default=u"A comment on '${title}' " default=u'A comment on "${title}" '
u"has been posted here: ${link}\n\n" u'has been posted here: ${link}\n\n'
u"---\n" u'---\n'
u"${text}\n" u'${text}\n'
u"---\n\n" u'---\n\n'
u"Approve comment:\n${link_approve}\n\n" u'Approve comment:\n${link_approve}\n\n'
u"Delete comment:\n${link_delete}\n") u'Delete comment:\n${link_delete}\n')
logger = logging.getLogger("plone.app.discussion") logger = logging.getLogger('plone.app.discussion')
class Comment(CatalogAware, WorkflowAware, DynamicType, Traversable, class Comment(CatalogAware, WorkflowAware, DynamicType, Traversable,
@ -86,10 +86,10 @@ class Comment(CatalogAware, WorkflowAware, DynamicType, Traversable,
comment_id = None # long comment_id = None # long
in_reply_to = None # long in_reply_to = None # long
title = u"" title = u''
mime_type = None mime_type = None
text = u"" text = u''
creator = None creator = None
creation_date = None creation_date = None
@ -157,14 +157,12 @@ class Comment(CatalogAware, WorkflowAware, DynamicType, Traversable,
if transform: if transform:
return transform.getData() return transform.getData()
else: else:
logger = logging.getLogger("plone.app.discussion") logger = logging.getLogger('plone.app.discussion')
logger.error(_( msg = u'Transform "{0}" => "{1}" not available. Failed to ' \
u"Transform '%s' => '%s' not available." % ( u'transform comment "{2}".'
sourceMimetype, logger.error(
targetMimetype msg.format(sourceMimetype, targetMimetype, self.absolute_url())
) + )
u"Failed to transform comment '%s'." % self.absolute_url()
))
return text return text
def Title(self): def Title(self):
@ -177,8 +175,8 @@ class Comment(CatalogAware, WorkflowAware, DynamicType, Traversable,
if not self.author_name: if not self.author_name:
author_name = translate( author_name = translate(
Message(_( Message(_(
u"label_anonymous", u'label_anonymous',
default=u"Anonymous" default=u'Anonymous'
)) ))
) )
else: else:
@ -198,8 +196,7 @@ class Comment(CatalogAware, WorkflowAware, DynamicType, Traversable,
""" """
return self.creator or self.author_name return self.creator or self.author_name
security.declareProtected(permissions.View, 'Type') @security.protected(permissions.View)
def Type(self): def Type(self):
"""The Discussion Item content type. """The Discussion Item content type.
""" """
@ -292,7 +289,7 @@ def notify_content_object_moved(obj, event):
) )
brains = catalog.searchResults(dict( brains = catalog.searchResults(dict(
path={'query': old_path}, path={'query': old_path},
portal_type="Discussion Item" portal_type='Discussion Item'
)) ))
for brain in brains: for brain in brains:
catalog.uncatalog_object(brain.getPath()) catalog.uncatalog_object(brain.getPath())
@ -346,7 +343,7 @@ def notify_user(obj, event):
if not emails: if not emails:
return return
subject = translate(_(u"A comment has been posted."), subject = translate(_(u'A comment has been posted.'),
context=obj.REQUEST) context=obj.REQUEST)
message = translate( message = translate(
Message( Message(
@ -412,7 +409,7 @@ def notify_moderator(obj, event):
content_object = aq_parent(conversation) content_object = aq_parent(conversation)
# Compose email # Compose email
subject = translate(_(u"A comment has been posted."), context=obj.REQUEST) subject = translate(_(u'A comment has been posted.'), context=obj.REQUEST)
link_approve = obj.absolute_url() + '/@@moderate-publish-comment' link_approve = obj.absolute_url() + '/@@moderate-publish-comment'
link_delete = obj.absolute_url() + '/@@moderate-delete-comment' link_delete = obj.absolute_url() + '/@@moderate-delete-comment'
message = translate( message = translate(

View File

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
""" Content rules handlers """ Content rules handlers
""" """
from plone.app.discussion import _ from plone.app.discussion import _
@ -9,6 +10,7 @@ except ImportError:
class BaseSubstitution(object): class BaseSubstitution(object):
""" Fallback class if plone.stringinterp is not available """ Fallback class if plone.stringinterp is not available
""" """
def __init__(self, context, **kwargs): def __init__(self, context, **kwargs):
self.context = context self.context = context
@ -27,6 +29,7 @@ def execute_comment(event):
class CommentSubstitution(BaseSubstitution): class CommentSubstitution(BaseSubstitution):
""" Comment string substitution """ Comment string substitution
""" """
def __init__(self, context, **kwargs): def __init__(self, context, **kwargs):
super(CommentSubstitution, self).__init__(context, **kwargs) super(CommentSubstitution, self).__init__(context, **kwargs)

View File

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
"""The conversation and replies adapters """The conversation and replies adapters
The conversation is responsible for storing all comments. It provides a The conversation is responsible for storing all comments. It provides a
@ -20,12 +21,12 @@ from BTrees.OIBTree import OIBTree
from OFS.event import ObjectWillBeAddedEvent from OFS.event import ObjectWillBeAddedEvent
from OFS.event import ObjectWillBeRemovedEvent from OFS.event import ObjectWillBeRemovedEvent
from OFS.Traversable import Traversable from OFS.Traversable import Traversable
from Products.CMFPlone.interfaces import IHideFromBreadcrumbs
from Products.CMFPlone import DISCUSSION_ANNOTATION_KEY as ANNOTATION_KEY
from persistent import Persistent from persistent import Persistent
from plone.app.discussion.comment import Comment from plone.app.discussion.comment import Comment
from plone.app.discussion.interfaces import IConversation from plone.app.discussion.interfaces import IConversation
from plone.app.discussion.interfaces import IReplies from plone.app.discussion.interfaces import IReplies
from Products.CMFPlone import DISCUSSION_ANNOTATION_KEY as ANNOTATION_KEY
from Products.CMFPlone.interfaces import IHideFromBreadcrumbs
from zope.annotation.interfaces import IAnnotatable from zope.annotation.interfaces import IAnnotatable
from zope.annotation.interfaces import IAnnotations from zope.annotation.interfaces import IAnnotations
from zope.component import adapter from zope.component import adapter
@ -38,7 +39,6 @@ from zope.lifecycleevent import ObjectAddedEvent
from zope.lifecycleevent import ObjectCreatedEvent from zope.lifecycleevent import ObjectCreatedEvent
from zope.lifecycleevent import ObjectRemovedEvent from zope.lifecycleevent import ObjectRemovedEvent
import time import time
@ -53,7 +53,7 @@ class Conversation(Traversable, Persistent, Explicit):
__allow_access_to_unprotected_subobjects__ = True __allow_access_to_unprotected_subobjects__ = True
def __init__(self, id="++conversation++default"): def __init__(self, id='++conversation++default'):
self.id = id self.id = id
# username -> count of comments; key is removed when count reaches 0 # username -> count of comments; key is removed when count reaches 0
@ -427,7 +427,7 @@ class CommentReplies(ConversationReplies):
) )
if self.conversation is None or conversation_has_no_children: if self.conversation is None or conversation_has_no_children:
raise TypeError("This adapter doesn't know what to do with the " raise TypeError("This adapter doesn't know what to do with the "
"parent conversation") 'parent conversation')
self.comment_id = self.comment.comment_id self.comment_id = self.comment.comment_id

View File

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
""" Custom discussion events """ Custom discussion events
""" """
from plone.app.discussion.interfaces import ICommentAddedEvent from plone.app.discussion.interfaces import ICommentAddedEvent

View File

@ -30,25 +30,24 @@ class IConversation(IIterableMapping):
""" """
total_comments = schema.Int( total_comments = schema.Int(
title=_(u"Total number of public comments on this item"), title=_(u'Total number of public comments on this item'),
min=0, min=0,
readonly=True, readonly=True,
) )
last_comment_date = schema.Date( last_comment_date = schema.Date(
title=_(u"Date of the most recent public comment"), title=_(u'Date of the most recent public comment'),
readonly=True, readonly=True,
) )
commentators = schema.Set( commentators = schema.Set(
title=_(u"The set of unique commentators (usernames)"), title=_(u'The set of unique commentators (usernames)'),
readonly=True, readonly=True,
) )
public_commentators = schema.Set( public_commentators = schema.Set(
title=_( title=_(
u"The set of unique commentators (usernames) of" u'The set of unique commentators (usernames) of published_comments'
u" published_comments"
), ),
readonly=True, readonly=True,
) )
@ -129,57 +128,57 @@ class IComment(Interface):
""" """
portal_type = schema.ASCIILine( portal_type = schema.ASCIILine(
title=_(u"Portal type"), title=_(u'Portal type'),
default="Discussion Item", default='Discussion Item',
) )
__parent__ = schema.Object( __parent__ = schema.Object(
title=_(u"Conversation"), schema=Interface) title=_(u'Conversation'), schema=Interface)
__name__ = schema.TextLine(title=_(u"Name")) __name__ = schema.TextLine(title=_(u'Name'))
comment_id = schema.Int( comment_id = schema.Int(
title=_(u"A comment id unique to this conversation")) title=_(u'A comment id unique to this conversation'))
in_reply_to = schema.Int( in_reply_to = schema.Int(
title=_(u"Id of comment this comment is in reply to"), title=_(u'Id of comment this comment is in reply to'),
required=False, required=False,
) )
# for logged in comments - set to None for anonymous # for logged in comments - set to None for anonymous
author_username = schema.TextLine(title=_(u"Name"), required=False) author_username = schema.TextLine(title=_(u'Name'), required=False)
# for anonymous comments only, set to None for logged in comments # for anonymous comments only, set to None for logged in comments
author_name = schema.TextLine(title=_(u"Name"), required=False) author_name = schema.TextLine(title=_(u'Name'), required=False)
author_email = schema.TextLine(title=_(u"Email"), required=False) author_email = schema.TextLine(title=_(u'Email'), required=False)
title = schema.TextLine(title=_(u"label_subject", title = schema.TextLine(title=_(u'label_subject',
default=u"Subject")) default=u'Subject'))
mime_type = schema.ASCIILine(title=_(u"MIME type"), default="text/plain") mime_type = schema.ASCIILine(title=_(u'MIME type'), default='text/plain')
text = schema.Text( text = schema.Text(
title=_( title=_(
u"label_comment", u'label_comment',
default=u"Comment" default=u'Comment'
) )
) )
user_notification = schema.Bool( user_notification = schema.Bool(
title=_( title=_(
u"Notify me of new comments via email." u'Notify me of new comments via email.'
), ),
required=False required=False
) )
creator = schema.TextLine(title=_(u"Username of the commenter")) creator = schema.TextLine(title=_(u'Username of the commenter'))
creation_date = schema.Date(title=_(u"Creation date")) creation_date = schema.Date(title=_(u'Creation date'))
modification_date = schema.Date(title=_(u"Modification date")) modification_date = schema.Date(title=_(u'Modification date'))
class ICaptcha(Interface): class ICaptcha(Interface):
"""Captcha/ReCaptcha text field to extend the existing comment form. """Captcha/ReCaptcha text field to extend the existing comment form.
""" """
captcha = schema.TextLine(title=_(u"Captcha"), captcha = schema.TextLine(title=_(u'Captcha'),
required=False) required=False)
@ -195,137 +194,137 @@ class IDiscussionSettings(Interface):
# - Search control panel: Show comments in search results # - Search control panel: Show comments in search results
globally_enabled = schema.Bool( globally_enabled = schema.Bool(
title=_(u"label_globally_enabled", title=_(u'label_globally_enabled',
default=u"Globally enable comments"), default=u'Globally enable comments'),
description=_( description=_(
u"help_globally_enabled", u'help_globally_enabled',
default=u"If selected, users are able to post comments on the " default=u'If selected, users are able to post comments on the '
u"site. Though, you have to enable comments for " u'site. Though, you have to enable comments for '
u"specific content types, folders or content objects " u'specific content types, folders or content objects '
u"before users will be able to post comments." u'before users will be able to post comments.'
), ),
required=False, required=False,
default=False, default=False,
) )
anonymous_comments = schema.Bool( anonymous_comments = schema.Bool(
title=_(u"label_anonymous_comments", title=_(u'label_anonymous_comments',
default="Enable anonymous comments"), default='Enable anonymous comments'),
description=_( description=_(
u"help_anonymous_comments", u'help_anonymous_comments',
default=u"If selected, anonymous users are able to post " default=u'If selected, anonymous users are able to post '
u"comments without loggin in. It is highly " u'comments without loggin in. It is highly '
u"recommended to use a captcha solution to prevent " u'recommended to use a captcha solution to prevent '
u"spam if this setting is enabled." u'spam if this setting is enabled.'
), ),
required=False, required=False,
default=False, default=False,
) )
anonymous_email_enabled = schema.Bool( anonymous_email_enabled = schema.Bool(
title=_(u"label_anonymous_email_enabled", title=_(u'label_anonymous_email_enabled',
default=u"Enable anonymous email field"), default=u'Enable anonymous email field'),
description=_( description=_(
u"help_anonymous_email_enabled", u'help_anonymous_email_enabled',
default=u"If selected, anonymous user will have to " default=u'If selected, anonymous user will have to '
u"give their email."), u'give their email.'),
required=False, required=False,
default=False default=False
) )
moderation_enabled = schema.Bool( moderation_enabled = schema.Bool(
title=_( title=_(
u"label_moderation_enabled", u'label_moderation_enabled',
default="Enable comment moderation" default='Enable comment moderation'
), ),
description=_( description=_(
u"help_moderation_enabled", u'help_moderation_enabled',
default=u"If selected, comments will enter a 'Pending' state " default=u'If selected, comments will enter a "Pending" state '
u"in which they are invisible to the public. A user " u'in which they are invisible to the public. A user '
u"with the 'Review comments' permission ('Reviewer' " u'with the "Review comments" permission ("Reviewer" '
u"or 'Manager') can approve comments to make them " u'or "Manager") can approve comments to make them '
u"visible to the public. If you want to enable a " u'visible to the public. If you want to enable a '
u"custom comment workflow, you have to go to the " u'custom comment workflow, you have to go to the '
u"types control panel." u'types control panel.'
), ),
required=False, required=False,
default=False, default=False,
) )
edit_comment_enabled = schema.Bool( edit_comment_enabled = schema.Bool(
title=_(u"label_edit_comment_enabled", title=_(u'label_edit_comment_enabled',
default="Enable editing of comments"), default='Enable editing of comments'),
description=_(u"help_edit_comment_enabled", description=_(u'help_edit_comment_enabled',
default=u"If selected, supports editing " default=u'If selected, supports editing '
"of comments for users with the 'Edit comments' " 'of comments for users with the "Edit comments" '
"permission."), 'permission.'),
required=False, required=False,
default=False, default=False,
) )
delete_own_comment_enabled = schema.Bool( delete_own_comment_enabled = schema.Bool(
title=_(u"label_delete_own_comment_enabled", title=_(u'label_delete_own_comment_enabled',
default="Enable deleting own comments"), default='Enable deleting own comments'),
description=_(u"help_delete_own_comment_enabled", description=_(u'help_delete_own_comment_enabled',
default=u"If selected, supports deleting " default=u'If selected, supports deleting '
"of own comments for users with the " 'of own comments for users with the '
"'Delete own comments' permission."), '"Delete own comments" permission.'),
required=False, required=False,
default=False, default=False,
) )
text_transform = schema.Choice( text_transform = schema.Choice(
title=_(u"label_text_transform", title=_(u'label_text_transform',
default="Comment text transform"), default='Comment text transform'),
description=_( description=_(
u"help_text_transform", u'help_text_transform',
default=u"Use this setting to choose if the comment text " + default=u'Use this setting to choose if the comment text '
u"should be transformed in any way. You can choose " u'should be transformed in any way. You can choose '
u"between 'Plain text' and 'Intelligent text'. " + u'between "Plain text" and "Intelligent text". '
u"'Intelligent text' converts plain text into HTML " + u'"Intelligent text" converts plain text into HTML '
u"where line breaks and indentation is preserved, " + u'where line breaks and indentation is preserved, '
u"and web and email addresses are made into " + u'and web and email addresses are made into '
u"clickable links."), u'clickable links.'),
required=True, required=True,
default='text/plain', default='text/plain',
vocabulary='plone.app.discussion.vocabularies.TextTransformVocabulary', vocabulary='plone.app.discussion.vocabularies.TextTransformVocabulary',
) )
captcha = schema.Choice( captcha = schema.Choice(
title=_(u"label_captcha", title=_(u'label_captcha',
default="Captcha"), default='Captcha'),
description=_( description=_(
u"help_captcha", u'help_captcha',
default=u"Use this setting to enable or disable Captcha " default=u'Use this setting to enable or disable Captcha '
u"validation for comments. Install " u'validation for comments. Install '
u"plone.formwidget.captcha, " u'plone.formwidget.captcha, '
u"plone.formwidget.recaptcha, collective.akismet, or " u'plone.formwidget.recaptcha, collective.akismet, or '
u"collective.z3cform.norobots if there are no options " u'collective.z3cform.norobots if there are no options '
u"available."), u'available.'),
required=True, required=True,
default='disabled', default='disabled',
vocabulary='plone.app.discussion.vocabularies.CaptchaVocabulary', vocabulary='plone.app.discussion.vocabularies.CaptchaVocabulary',
) )
show_commenter_image = schema.Bool( show_commenter_image = schema.Bool(
title=_(u"label_show_commenter_image", title=_(u'label_show_commenter_image',
default=u"Show commenter image"), default=u'Show commenter image'),
description=_( description=_(
u"help_show_commenter_image", u'help_show_commenter_image',
default=u"If selected, an image of the user is shown next to " default=u'If selected, an image of the user is shown next to '
u"the comment."), u'the comment.'),
required=False, required=False,
default=True, default=True,
) )
moderator_notification_enabled = schema.Bool( moderator_notification_enabled = schema.Bool(
title=_(u"label_moderator_notification_enabled", title=_(u'label_moderator_notification_enabled',
default=u"Enable moderator email notification"), default=u'Enable moderator email notification'),
description=_( description=_(
u"help_moderator_notification_enabled", u'help_moderator_notification_enabled',
default=u"If selected, the moderator is notified if a comment " default=u'If selected, the moderator is notified if a comment '
u"needs attention. The moderator email address can " + u'needs attention. The moderator email address can '
u"be set below."), u'be set below.'),
required=False, required=False,
default=False, default=False,
) )
@ -337,20 +336,20 @@ class IDiscussionSettings(Interface):
), ),
description=_( description=_(
u'help_moderator_email', u'help_moderator_email',
default=u"Address to which moderator notifications " default=u'Address to which moderator notifications '
u"will be sent."), u'will be sent.'),
required=False, required=False,
) )
user_notification_enabled = schema.Bool( user_notification_enabled = schema.Bool(
title=_( title=_(
u"label_user_notification_enabled", u'label_user_notification_enabled',
default=u"Enable user email notification" default=u'Enable user email notification'
), ),
description=_( description=_(
u"help_user_notification_enabled", u'help_user_notification_enabled',
default=u"If selected, users can choose to be notified " default=u'If selected, users can choose to be notified '
u"of new comments by email."), u'of new comments by email.'),
required=False, required=False,
default=False default=False
) )

View File

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from Products.CMFCore.utils import getToolByName from Products.CMFCore.utils import getToolByName

View File

@ -1,4 +1,4 @@
from Products.CMFCore.utils import getToolByName # -*- coding: utf-8 -*-
from plone.app.contenttypes.testing import PLONE_APP_CONTENTTYPES_FIXTURE from plone.app.contenttypes.testing import PLONE_APP_CONTENTTYPES_FIXTURE
from plone.app.discussion.interfaces import IDiscussionSettings from plone.app.discussion.interfaces import IDiscussionSettings
from plone.app.robotframework.testing import REMOTE_LIBRARY_ROBOT_TESTING from plone.app.robotframework.testing import REMOTE_LIBRARY_ROBOT_TESTING
@ -9,14 +9,16 @@ from plone.app.testing import PloneSandboxLayer
from plone.app.testing import setRoles from plone.app.testing import setRoles
from plone.app.testing import TEST_USER_ID from plone.app.testing import TEST_USER_ID
from plone.registry.interfaces import IRegistry from plone.registry.interfaces import IRegistry
from Products.CMFCore.utils import getToolByName
from zope.component import queryUtility from zope.component import queryUtility
from zope.configuration import xmlconfig from zope.configuration import xmlconfig
try: try:
import plone.app.collection # noqa import plone.app.collection # noqa
COLLECTION_TYPE = "Collection" COLLECTION_TYPE = 'Collection'
except: except ImportError:
COLLECTION_TYPE = "Topic" COLLECTION_TYPE = 'Topic'
class PloneAppDiscussion(PloneSandboxLayer): class PloneAppDiscussion(PloneSandboxLayer):
@ -77,7 +79,7 @@ class PloneAppDiscussion(PloneSandboxLayer):
gtool.addPrincipalToGroup(self.REVIEWER_NAME, 'Reviewers') gtool.addPrincipalToGroup(self.REVIEWER_NAME, 'Reviewers')
mtool.addMember('jim', 'Jim', ['Member'], []) mtool.addMember('jim', 'Jim', ['Member'], [])
mtool.getMemberById('jim').setMemberProperties( mtool.getMemberById('jim').setMemberProperties(
{"fullname": 'Jim Fult\xc3\xb8rn'}) {'fullname': 'Jim Fult\xc3\xb8rn'})
acl_users.userFolderAddUser( acl_users.userFolderAddUser(
self.MANAGER_USER_NAME, self.MANAGER_USER_NAME,
@ -107,14 +109,14 @@ PLONE_APP_DISCUSSION_ROBOT_FIXTURE = PloneAppDiscussionRobot()
PLONE_APP_DISCUSSION_FIXTURE = PloneAppDiscussion() PLONE_APP_DISCUSSION_FIXTURE = PloneAppDiscussion()
PLONE_APP_DISCUSSION_INTEGRATION_TESTING = IntegrationTesting( PLONE_APP_DISCUSSION_INTEGRATION_TESTING = IntegrationTesting(
bases=(PLONE_APP_DISCUSSION_FIXTURE,), bases=(PLONE_APP_DISCUSSION_FIXTURE,),
name="PloneAppDiscussion:Integration") name='PloneAppDiscussion:Integration')
PLONE_APP_DISCUSSION_FUNCTIONAL_TESTING = FunctionalTesting( PLONE_APP_DISCUSSION_FUNCTIONAL_TESTING = FunctionalTesting(
bases=(PLONE_APP_DISCUSSION_FIXTURE,), bases=(PLONE_APP_DISCUSSION_FIXTURE,),
name="PloneAppDiscussion:Functional") name='PloneAppDiscussion:Functional')
PLONE_APP_DISCUSSION_ROBOT_TESTING = FunctionalTesting( PLONE_APP_DISCUSSION_ROBOT_TESTING = FunctionalTesting(
bases=( bases=(
PLONE_APP_DISCUSSION_ROBOT_FIXTURE, PLONE_APP_DISCUSSION_ROBOT_FIXTURE,
REMOTE_LIBRARY_ROBOT_TESTING REMOTE_LIBRARY_ROBOT_TESTING
), ),
name="PloneAppDiscussion:Robot" name='PloneAppDiscussion:Robot'
) )

View File

@ -1,11 +1,12 @@
# -*- coding: utf-8 -*-
"""Test the plone.app.discussion catalog indexes """Test the plone.app.discussion catalog indexes
""" """
from Products.CMFCore.utils import getToolByName
from datetime import datetime from datetime import datetime
from plone.app.discussion.interfaces import IConversation from plone.app.discussion.interfaces import IConversation
from plone.app.discussion.testing import PLONE_APP_DISCUSSION_INTEGRATION_TESTING # noqa from plone.app.discussion.testing import PLONE_APP_DISCUSSION_INTEGRATION_TESTING # noqa
from plone.app.testing import setRoles from plone.app.testing import setRoles
from plone.app.testing import TEST_USER_ID from plone.app.testing import TEST_USER_ID
from Products.CMFCore.utils import getToolByName
from zope.annotation.interfaces import IAnnotations from zope.annotation.interfaces import IAnnotations
from zope.component import createObject from zope.component import createObject
@ -74,7 +75,7 @@ class ConversationCatalogTest(unittest.TestCase):
'query': 'query':
'/'.join(self.portal.doc1.getPhysicalPath()) '/'.join(self.portal.doc1.getPhysicalPath())
}, },
portal_type="Document" portal_type='Document'
)) ))
self.conversation = conversation self.conversation = conversation
self.brains = brains self.brains = brains
@ -92,14 +93,15 @@ class ConversationCatalogTest(unittest.TestCase):
new_comment2_id = self.conversation.addComment(comment2) new_comment2_id = self.conversation.addComment(comment2)
comment2 = self.portal.doc1.restrictedTraverse( comment2 = self.portal.doc1.restrictedTraverse(
'++conversation++default/%s' % new_comment2_id) '++conversation++default/{0}'.format(new_comment2_id)
)
comment2.reindexObject() comment2.reindexObject()
brains = self.catalog.searchResults(dict( brains = self.catalog.searchResults(dict(
path={ path={
'query': 'query':
'/'.join(self.portal.doc1.getPhysicalPath()) '/'.join(self.portal.doc1.getPhysicalPath())
}, },
portal_type="Document" portal_type='Document'
)) ))
doc1_brain = brains[0] doc1_brain = brains[0]
self.assertEqual(doc1_brain.total_comments, 2) self.assertEqual(doc1_brain.total_comments, 2)
@ -120,14 +122,15 @@ class ConversationCatalogTest(unittest.TestCase):
new_comment2_id = self.conversation.addComment(comment2) new_comment2_id = self.conversation.addComment(comment2)
comment2 = self.portal.doc1.restrictedTraverse( comment2 = self.portal.doc1.restrictedTraverse(
'++conversation++default/%s' % new_comment2_id) '++conversation++default/{0}'.format(new_comment2_id)
)
comment2.reindexObject() comment2.reindexObject()
brains = self.catalog.searchResults(dict( brains = self.catalog.searchResults(dict(
path={ path={
'query': 'query':
'/'.join(self.portal.doc1.getPhysicalPath()) '/'.join(self.portal.doc1.getPhysicalPath())
}, },
portal_type="Document" portal_type='Document'
)) ))
doc1_brain = brains[0] doc1_brain = brains[0]
self.assertEqual( self.assertEqual(
@ -143,7 +146,7 @@ class ConversationCatalogTest(unittest.TestCase):
'query': 'query':
'/'.join(self.portal.doc1.getPhysicalPath()) '/'.join(self.portal.doc1.getPhysicalPath())
}, },
portal_type="Document" portal_type='Document'
)) ))
doc1_brain = brains[0] doc1_brain = brains[0]
self.assertEqual( self.assertEqual(
@ -158,7 +161,7 @@ class ConversationCatalogTest(unittest.TestCase):
'query': 'query':
'/'.join(self.portal.doc1.getPhysicalPath()) '/'.join(self.portal.doc1.getPhysicalPath())
}, },
portal_type="Document" portal_type='Document'
)) ))
doc1_brain = brains[0] doc1_brain = brains[0]
self.assertEqual(doc1_brain.last_comment_date, None) self.assertEqual(doc1_brain.last_comment_date, None)
@ -176,7 +179,8 @@ class ConversationCatalogTest(unittest.TestCase):
new_comment2_id = self.conversation.addComment(comment2) new_comment2_id = self.conversation.addComment(comment2)
comment2 = self.portal.doc1.restrictedTraverse( comment2 = self.portal.doc1.restrictedTraverse(
'++conversation++default/%s' % new_comment2_id) '++conversation++default/{0}'.format(new_comment2_id)
)
comment2.reindexObject() comment2.reindexObject()
brains = self.catalog.searchResults(dict( brains = self.catalog.searchResults(dict(
@ -184,7 +188,7 @@ class ConversationCatalogTest(unittest.TestCase):
'query': 'query':
'/'.join(self.portal.doc1.getPhysicalPath()) '/'.join(self.portal.doc1.getPhysicalPath())
}, },
portal_type="Document" portal_type='Document'
)) ))
doc1_brain = brains[0] doc1_brain = brains[0]
@ -197,7 +201,7 @@ class ConversationCatalogTest(unittest.TestCase):
'query': 'query':
'/'.join(self.portal.doc1.getPhysicalPath()) '/'.join(self.portal.doc1.getPhysicalPath())
}, },
portal_type="Document" portal_type='Document'
)) ))
doc1_brain = brains[0] doc1_brain = brains[0]
self.assertEqual(doc1_brain.commentators, ('Jim',)) self.assertEqual(doc1_brain.commentators, ('Jim',))
@ -209,7 +213,7 @@ class ConversationCatalogTest(unittest.TestCase):
'query': 'query':
'/'.join(self.portal.doc1.getPhysicalPath()) '/'.join(self.portal.doc1.getPhysicalPath())
}, },
portal_type="Document" portal_type='Document'
)) ))
doc1_brain = brains[0] doc1_brain = brains[0]
self.assertEqual(doc1_brain.commentators, ()) self.assertEqual(doc1_brain.commentators, ())
@ -220,7 +224,7 @@ class ConversationCatalogTest(unittest.TestCase):
'query': 'query':
'/'.join(self.portal.doc1.getPhysicalPath()) '/'.join(self.portal.doc1.getPhysicalPath())
}, },
portal_type="Discussion Item" portal_type='Discussion Item'
)) ))
comment1_brain = brains[0] comment1_brain = brains[0]
self.assertEqual(comment1_brain.commentators, None) self.assertEqual(comment1_brain.commentators, None)
@ -228,14 +232,14 @@ class ConversationCatalogTest(unittest.TestCase):
self.assertEqual(comment1_brain.total_comments, None) self.assertEqual(comment1_brain.total_comments, None)
def test_dont_index_private_commentators(self): def test_dont_index_private_commentators(self):
self.comment1.manage_permission("View", roles=tuple()) self.comment1.manage_permission('View', roles=tuple())
self.portal.doc1.reindexObject() self.portal.doc1.reindexObject()
brains = self.catalog.searchResults(dict( brains = self.catalog.searchResults(dict(
path={ path={
'query': 'query':
'/'.join(self.portal.doc1.getPhysicalPath()) '/'.join(self.portal.doc1.getPhysicalPath())
}, },
portal_type="Document" portal_type='Document'
)) ))
doc1_brain = brains[0] doc1_brain = brains[0]
self.assertEqual(doc1_brain.commentators, ()) self.assertEqual(doc1_brain.commentators, ())
@ -262,7 +266,8 @@ class CommentCatalogTest(unittest.TestCase):
# Comment brain # Comment brain
self.comment = self.portal.doc1.restrictedTraverse( self.comment = self.portal.doc1.restrictedTraverse(
'++conversation++default/%s' % new_comment1_id) '++conversation++default/{0}'.format(new_comment1_id)
)
brains = self.catalog.searchResults(dict( brains = self.catalog.searchResults(dict(
path={ path={
'query': 'query':
@ -281,7 +286,8 @@ class CommentCatalogTest(unittest.TestCase):
# Comment brain # Comment brain
comment = self.portal.doc1.restrictedTraverse( comment = self.portal.doc1.restrictedTraverse(
'++conversation++default/%s' % cid) '++conversation++default/{0}'.format(cid)
)
brains = self.catalog.searchResults(dict( brains = self.catalog.searchResults(dict(
path={ path={
'query': 'query':
@ -289,7 +295,7 @@ class CommentCatalogTest(unittest.TestCase):
} }
)) ))
comment_brain = brains[0] comment_brain = brains[0]
self.assertEqual(comment_brain.Title, "Anonymous on Document 1") self.assertEqual(comment_brain.Title, 'Anonymous on Document 1')
def test_type(self): def test_type(self):
self.assertEqual(self.comment_brain.portal_type, 'Discussion Item') self.assertEqual(self.comment_brain.portal_type, 'Discussion Item')
@ -329,7 +335,7 @@ class CommentCatalogTest(unittest.TestCase):
""" """
brains = self.catalog.searchResults({'portal_type': 'Discussion Item'}) brains = self.catalog.searchResults({'portal_type': 'Discussion Item'})
self.assertEqual(len(brains), 1) self.assertEqual(len(brains), 1)
self.portal.manage_delObjects(["doc1"]) self.portal.manage_delObjects(['doc1'])
brains = self.catalog.searchResults({'portal_type': 'Discussion Item'}) brains = self.catalog.searchResults({'portal_type': 'Discussion Item'})
self.assertEqual(len(brains), 0) self.assertEqual(len(brains), 0)
@ -362,13 +368,13 @@ class CommentCatalogTest(unittest.TestCase):
# Make sure no old comment brains are # Make sure no old comment brains are
brains = self.catalog.searchResults(dict( brains = self.catalog.searchResults(dict(
portal_type="Discussion Item", portal_type='Discussion Item',
path={'query': '/'.join(self.portal.folder1.getPhysicalPath())} path={'query': '/'.join(self.portal.folder1.getPhysicalPath())}
)) ))
self.assertEqual(len(brains), 0) self.assertEqual(len(brains), 0)
brains = self.catalog.searchResults(dict( brains = self.catalog.searchResults(dict(
portal_type="Discussion Item", portal_type='Discussion Item',
path={ path={
'query': '/'.join(self.portal.folder2.getPhysicalPath()) 'query': '/'.join(self.portal.folder2.getPhysicalPath())
} }
@ -411,14 +417,14 @@ class CommentCatalogTest(unittest.TestCase):
# Make sure no old comment brains are left # Make sure no old comment brains are left
brains = self.catalog.searchResults(dict( brains = self.catalog.searchResults(dict(
portal_type="Discussion Item", portal_type='Discussion Item',
path={'query': '/plone/sourcefolder/moveme'} path={'query': '/plone/sourcefolder/moveme'}
)) ))
self.assertEqual(len(brains), 0) self.assertEqual(len(brains), 0)
# make sure comments are correctly index on the target # make sure comments are correctly index on the target
brains = self.catalog.searchResults(dict( brains = self.catalog.searchResults(dict(
portal_type="Discussion Item", portal_type='Discussion Item',
path={'query': '/plone/targetfolder/moveme'} path={'query': '/plone/targetfolder/moveme'}
)) ))
self.assertEqual(len(brains), 1) self.assertEqual(len(brains), 1)
@ -432,7 +438,7 @@ class CommentCatalogTest(unittest.TestCase):
# We need to commit here so that _p_jar isn't None and move will work # We need to commit here so that _p_jar isn't None and move will work
transaction.savepoint(optimistic=True) transaction.savepoint(optimistic=True)
self.portal.manage_renameObject("doc1", "doc2") self.portal.manage_renameObject('doc1', 'doc2')
brains = self.catalog.searchResults( brains = self.catalog.searchResults(
portal_type='Discussion Item') portal_type='Discussion Item')
@ -535,7 +541,7 @@ class NoConversationCatalogTest(unittest.TestCase):
'query': 'query':
'/'.join(self.portal.doc1.getPhysicalPath()) '/'.join(self.portal.doc1.getPhysicalPath())
}, },
portal_type="Document" portal_type='Document'
)) ))
self.conversation = conversation self.conversation = conversation
self.brains = brains self.brains = brains

View File

@ -1,5 +1,4 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from Products.CMFCore.utils import getToolByName
from plone.app.discussion.browser.comment import View from plone.app.discussion.browser.comment import View
from plone.app.discussion.interfaces import IComment from plone.app.discussion.interfaces import IComment
from plone.app.discussion.interfaces import IConversation from plone.app.discussion.interfaces import IConversation
@ -7,6 +6,7 @@ from plone.app.discussion.interfaces import IReplies
from plone.app.discussion.testing import PLONE_APP_DISCUSSION_INTEGRATION_TESTING # noqa from plone.app.discussion.testing import PLONE_APP_DISCUSSION_INTEGRATION_TESTING # noqa
from plone.app.testing import setRoles from plone.app.testing import setRoles
from plone.app.testing import TEST_USER_ID from plone.app.testing import TEST_USER_ID
from Products.CMFCore.utils import getToolByName
from zope.component import createObject from zope.component import createObject
from zope.component import getMultiAdapter from zope.component import getMultiAdapter
@ -41,9 +41,9 @@ class CommentTest(unittest.TestCase):
datetime.datetime.now() - datetime.datetime.utcnow() datetime.datetime.now() - datetime.datetime.utcnow()
utc_to_local_diff = abs(utc_to_local_diff.seconds) utc_to_local_diff = abs(utc_to_local_diff.seconds)
if utc_to_local_diff < 60: if utc_to_local_diff < 60:
logger.warning("Your computer is living in a timezone where local " logger.warning('Your computer is living in a timezone where local '
"time equals utc time. Some potential errors can " 'time equals utc time. Some potential errors can '
"get hidden by that") 'get hidden by that')
comment1 = createObject('plone.Comment') comment1 = createObject('plone.Comment')
local_utc = datetime.datetime.utcnow() local_utc = datetime.datetime.utcnow()
for date in (comment1.creation_date, comment1.modification_date): for date in (comment1.creation_date, comment1.modification_date):
@ -92,15 +92,15 @@ class CommentTest(unittest.TestCase):
def test_title(self): def test_title(self):
conversation = IConversation(self.portal.doc1) conversation = IConversation(self.portal.doc1)
comment1 = createObject('plone.Comment') comment1 = createObject('plone.Comment')
comment1.author_name = "Jim Fulton" comment1.author_name = 'Jim Fulton'
conversation.addComment(comment1) conversation.addComment(comment1)
self.assertEqual("Jim Fulton on Document 1", comment1.Title()) self.assertEqual('Jim Fulton on Document 1', comment1.Title())
def test_no_name_title(self): def test_no_name_title(self):
conversation = IConversation(self.portal.doc1) conversation = IConversation(self.portal.doc1)
comment1 = createObject('plone.Comment') comment1 = createObject('plone.Comment')
conversation.addComment(comment1) conversation.addComment(comment1)
self.assertEqual("Anonymous on Document 1", comment1.Title()) self.assertEqual('Anonymous on Document 1', comment1.Title())
def test_title_special_characters(self): def test_title_special_characters(self):
self.portal.invokeFactory( self.portal.invokeFactory(
@ -110,9 +110,9 @@ class CommentTest(unittest.TestCase):
) )
conversation = IConversation(self.portal.doc_sp_chars) conversation = IConversation(self.portal.doc_sp_chars)
comment1 = createObject('plone.Comment') comment1 = createObject('plone.Comment')
comment1.author_name = u"Tarek Ziadé" comment1.author_name = u'Tarek Ziadé'
conversation.addComment(comment1) conversation.addComment(comment1)
self.assertEqual(u"Tarek Ziadé on Document äüö", comment1.Title()) self.assertEqual(u'Tarek Ziadé on Document äüö', comment1.Title())
def test_title_special_characters_utf8(self): def test_title_special_characters_utf8(self):
self.portal.invokeFactory( self.portal.invokeFactory(
@ -122,19 +122,19 @@ class CommentTest(unittest.TestCase):
) )
conversation = IConversation(self.portal.doc_sp_chars_utf8) conversation = IConversation(self.portal.doc_sp_chars_utf8)
comment1 = createObject('plone.Comment') comment1 = createObject('plone.Comment')
comment1.author_name = "Hüüb Bôûmä" comment1.author_name = 'Hüüb Bôûmä'
conversation.addComment(comment1) conversation.addComment(comment1)
self.assertEqual(u"Hüüb Bôûmä on Document ëïû", comment1.Title()) self.assertEqual(u'Hüüb Bôûmä on Document ëïû', comment1.Title())
def test_creator(self): def test_creator(self):
comment1 = createObject('plone.Comment') comment1 = createObject('plone.Comment')
comment1.creator = "jim" comment1.creator = 'jim'
self.assertEqual("jim", comment1.Creator()) self.assertEqual('jim', comment1.Creator())
def test_creator_author_name(self): def test_creator_author_name(self):
comment1 = createObject('plone.Comment') comment1 = createObject('plone.Comment')
comment1.author_name = "joey" comment1.author_name = 'joey'
self.assertEqual("joey", comment1.Creator()) self.assertEqual('joey', comment1.Creator())
def test_owner(self): def test_owner(self):
comment1 = createObject('plone.Comment') comment1 = createObject('plone.Comment')
@ -151,25 +151,23 @@ class CommentTest(unittest.TestCase):
def test_getText(self): def test_getText(self):
comment1 = createObject('plone.Comment') comment1 = createObject('plone.Comment')
comment1.text = """First paragraph comment1.text = 'First paragraph\n\nSecond paragraph'
Second paragraph"""
self.assertEqual( self.assertEqual(
comment1.getText(), comment1.getText(),
"<p>First paragraph<br /><br /> Second paragraph</p>" '<p>First paragraph<br /><br />Second paragraph</p>'
) )
def test_getText_escapes_HTML(self): def test_getText_escapes_HTML(self):
comment1 = createObject('plone.Comment') comment1 = createObject('plone.Comment')
comment1.text = """<b>Got HTML?</b>""" comment1.text = '<b>Got HTML?</b>'
self.assertEqual( self.assertEqual(
comment1.getText(), comment1.getText(),
"<p>&lt;b&gt;Got HTML?&lt;/b&gt;</p>" '<p>&lt;b&gt;Got HTML?&lt;/b&gt;</p>'
) )
def test_getText_with_non_ascii_characters(self): def test_getText_with_non_ascii_characters(self):
comment1 = createObject('plone.Comment') comment1 = createObject('plone.Comment')
comment1.text = u"""Umlaute sind ä, ö und ü.""" comment1.text = u'Umlaute sind ä, ö und ü.'
self.assertEqual( self.assertEqual(
comment1.getText(), comment1.getText(),
'<p>Umlaute sind \xc3\xa4, \xc3\xb6 und \xc3\xbc.</p>' '<p>Umlaute sind \xc3\xa4, \xc3\xb6 und \xc3\xbc.</p>'
@ -177,15 +175,15 @@ class CommentTest(unittest.TestCase):
def test_getText_doesnt_link(self): def test_getText_doesnt_link(self):
comment1 = createObject('plone.Comment') comment1 = createObject('plone.Comment')
comment1.text = "Go to http://www.plone.org" comment1.text = 'Go to http://www.plone.org'
self.assertEqual( self.assertEqual(
comment1.getText(), comment1.getText(),
"<p>Go to http://www.plone.org</p>" '<p>Go to http://www.plone.org</p>'
) )
def test_getText_uses_comment_mime_type(self): def test_getText_uses_comment_mime_type(self):
comment1 = createObject('plone.Comment') comment1 = createObject('plone.Comment')
comment1.text = "Go to http://www.plone.org" comment1.text = 'Go to http://www.plone.org'
comment1.mime_type = 'text/x-web-intelligent' comment1.mime_type = 'text/x-web-intelligent'
self.assertEqual( self.assertEqual(
comment1.getText(), comment1.getText(),
@ -229,7 +227,8 @@ class CommentTest(unittest.TestCase):
new_comment1_id = conversation.addComment(comment1) new_comment1_id = conversation.addComment(comment1)
comment = self.portal.doc1.restrictedTraverse( comment = self.portal.doc1.restrictedTraverse(
'++conversation++default/%s' % new_comment1_id) '++conversation++default/{0}'.format(new_comment1_id)
)
self.assertTrue(IComment.providedBy(comment)) self.assertTrue(IComment.providedBy(comment))
self.assertEqual( self.assertEqual(
@ -260,12 +259,13 @@ class CommentTest(unittest.TestCase):
comment1.text = 'Comment text' comment1.text = 'Comment text'
new_comment1_id = conversation.addComment(comment1) new_comment1_id = conversation.addComment(comment1)
comment = self.portal.image1.restrictedTraverse( comment = self.portal.image1.restrictedTraverse(
'++conversation++default/%s' % new_comment1_id) '++conversation++default/{0}'.format(new_comment1_id)
)
view = View(comment, self.request) view = View(comment, self.request)
View.__call__(view) View.__call__(view)
response = self.request.response response = self.request.response
self.assertIn("/view", response.headers['location']) self.assertIn('/view', response.headers['location'])
def test_workflow(self): def test_workflow(self):
"""Basic test for the 'comment_review_workflow' """Basic test for the 'comment_review_workflow'
@ -301,8 +301,8 @@ class CommentTest(unittest.TestCase):
def test_fti(self): def test_fti(self):
# test that we can look up an FTI for Discussion Item # test that we can look up an FTI for Discussion Item
self.assertTrue( self.assertIn(
"Discussion Item" in 'Discussion Item',
self.portal.portal_types.objectIds() self.portal.portal_types.objectIds()
) )
@ -327,7 +327,8 @@ class CommentTest(unittest.TestCase):
new_comment1_id = conversation.addComment(comment1) new_comment1_id = conversation.addComment(comment1)
comment = self.portal.doc1.restrictedTraverse( comment = self.portal.doc1.restrictedTraverse(
'++conversation++default/%s' % new_comment1_id) '++conversation++default/{0}'.format(new_comment1_id)
)
# make sure the view is there # make sure the view is there
self.assertTrue(getMultiAdapter((comment, self.request), self.assertTrue(getMultiAdapter((comment, self.request),
@ -364,7 +365,8 @@ class RepliesTest(unittest.TestCase):
comment.text = 'Comment text' comment.text = 'Comment text'
new_id = replies.addComment(comment) new_id = replies.addComment(comment)
comment = self.portal.doc1.restrictedTraverse( comment = self.portal.doc1.restrictedTraverse(
'++conversation++default/%s' % new_id) '++conversation++default/{0}'.format(new_id)
)
# Add a reply to the CommentReplies adapter of the first comment # Add a reply to the CommentReplies adapter of the first comment
re_comment = createObject('plone.Comment') re_comment = createObject('plone.Comment')
@ -400,7 +402,8 @@ class RepliesTest(unittest.TestCase):
comment.text = 'Comment text' comment.text = 'Comment text'
new_id = replies.addComment(comment) new_id = replies.addComment(comment)
comment = self.portal.doc1.restrictedTraverse( comment = self.portal.doc1.restrictedTraverse(
'++conversation++default/%s' % new_id) '++conversation++default/{0}'.format(new_id)
)
# Add a reply to the CommentReplies adapter of the first comment # Add a reply to the CommentReplies adapter of the first comment
re_comment = createObject('plone.Comment') re_comment = createObject('plone.Comment')
@ -435,7 +438,8 @@ class RepliesTest(unittest.TestCase):
comment.text = 'Comment text' comment.text = 'Comment text'
new_id = conversation.addComment(comment) new_id = conversation.addComment(comment)
comment = self.portal.doc1.restrictedTraverse( comment = self.portal.doc1.restrictedTraverse(
'++conversation++default/%s' % new_id) '++conversation++default/{0}'.format(new_id)
)
# Add a reply to the CommentReplies adapter of the first comment # Add a reply to the CommentReplies adapter of the first comment
re_comment = createObject('plone.Comment') re_comment = createObject('plone.Comment')
@ -443,7 +447,7 @@ class RepliesTest(unittest.TestCase):
replies = IReplies(comment) replies = IReplies(comment)
new_re_id = replies.addComment(re_comment) new_re_id = replies.addComment(re_comment)
re_comment = self.portal.doc1.restrictedTraverse( re_comment = self.portal.doc1.restrictedTraverse(
'++conversation++default/%s' % new_re_id '++conversation++default/{0}'.format(new_re_id)
) )
# Add a reply to the reply # Add a reply to the reply
@ -452,7 +456,7 @@ class RepliesTest(unittest.TestCase):
replies = IReplies(re_comment) replies = IReplies(re_comment)
new_re_re_id = replies.addComment(re_re_comment) new_re_re_id = replies.addComment(re_re_comment)
re_re_comment = self.portal.doc1.restrictedTraverse( re_re_comment = self.portal.doc1.restrictedTraverse(
'++conversation++default/%s' % new_re_re_id '++conversation++default/{0}'.format(new_re_re_id)
) )
# Add a reply to the replies reply # Add a reply to the replies reply
@ -461,7 +465,8 @@ class RepliesTest(unittest.TestCase):
replies = IReplies(re_re_comment) replies = IReplies(re_re_comment)
new_re_re_re_id = replies.addComment(re_re_re_comment) new_re_re_re_id = replies.addComment(re_re_re_comment)
re_re_re_comment = self.portal.doc1.restrictedTraverse( re_re_re_comment = self.portal.doc1.restrictedTraverse(
'++conversation++default/%s' % new_re_re_re_id) '++conversation++default/{0}'.format(new_re_re_re_id)
)
self.assertEqual( self.assertEqual(
('', 'plone', 'doc1', '++conversation++default', str(new_id)), ('', 'plone', 'doc1', '++conversation++default', str(new_id)),

View File

@ -2,8 +2,6 @@
from AccessControl import Unauthorized from AccessControl import Unauthorized
from datetime import datetime from datetime import datetime
from OFS.Image import Image from OFS.Image import Image
from Products.CMFCore.utils import getToolByName
from Products.CMFPlone.tests import dummy
from plone.app.discussion import interfaces from plone.app.discussion import interfaces
from plone.app.discussion.browser.comment import EditCommentForm from plone.app.discussion.browser.comment import EditCommentForm
from plone.app.discussion.browser.comments import CommentForm from plone.app.discussion.browser.comments import CommentForm
@ -17,6 +15,8 @@ from plone.app.testing import setRoles
from plone.app.testing import TEST_USER_ID from plone.app.testing import TEST_USER_ID
from plone.app.testing import TEST_USER_NAME from plone.app.testing import TEST_USER_NAME
from plone.registry.interfaces import IRegistry from plone.registry.interfaces import IRegistry
from Products.CMFCore.utils import getToolByName
from Products.CMFPlone.tests import dummy
from z3c.form.interfaces import IFormLayer from z3c.form.interfaces import IFormLayer
from zope import interface from zope import interface
from zope.annotation.interfaces import IAttributeAnnotatable from zope.annotation.interfaces import IAttributeAnnotatable
@ -49,7 +49,7 @@ class TestCommentForm(unittest.TestCase):
interfaces.IDiscussionLayer, interfaces.IDiscussionLayer,
) )
wftool = getToolByName(self.portal, "portal_workflow") wftool = getToolByName(self.portal, 'portal_workflow')
wftool.doActionFor(self.portal.doc1, action='publish') wftool.doActionFor(self.portal.doc1, action='publish')
self.portal.doc1.allow_discussion = True self.portal.doc1.allow_discussion = True
self.membershipTool = getToolByName(self.folder, 'portal_membership') self.membershipTool = getToolByName(self.folder, 'portal_membership')
@ -80,7 +80,7 @@ class TestCommentForm(unittest.TestCase):
adapts=(Interface, IBrowserRequest), adapts=(Interface, IBrowserRequest),
provides=Interface, provides=Interface,
factory=CommentForm, factory=CommentForm,
name=u"comment-form" name=u'comment-form'
) )
# The form should return an error if the comment text field is empty # The form should return an error if the comment text field is empty
@ -88,13 +88,13 @@ class TestCommentForm(unittest.TestCase):
commentForm = getMultiAdapter( commentForm = getMultiAdapter(
(self.context, request), (self.context, request),
name=u"comment-form" name=u'comment-form'
) )
commentForm.update() commentForm.update()
data, errors = commentForm.extractData() # pylint: disable-msg=W0612 data, errors = commentForm.extractData() # pylint: disable-msg=W0612
self.assertEqual(len(errors), 1) self.assertEqual(len(errors), 1)
self.assertFalse(commentForm.handleComment(commentForm, "foo")) self.assertFalse(commentForm.handleComment(commentForm, 'foo'))
# The form is submitted successfully, if the required text field is # The form is submitted successfully, if the required text field is
# filled out # filled out
@ -102,22 +102,22 @@ class TestCommentForm(unittest.TestCase):
commentForm = getMultiAdapter( commentForm = getMultiAdapter(
(self.context, request), (self.context, request),
name=u"comment-form" name=u'comment-form'
) )
commentForm.update() commentForm.update()
data, errors = commentForm.extractData() # pylint: disable-msg=W0612 data, errors = commentForm.extractData() # pylint: disable-msg=W0612
self.assertEqual(len(errors), 0) self.assertEqual(len(errors), 0)
self.assertFalse(commentForm.handleComment(commentForm, "foo")) self.assertFalse(commentForm.handleComment(commentForm, 'foo'))
comments = IConversation(commentForm.context).getComments() comments = IConversation(commentForm.context).getComments()
comments = [comment for comment in comments] # consume iterator comments = [comment for comment in comments] # consume iterator
self.assertEqual(len(comments), 1) self.assertEqual(len(comments), 1)
for comment in comments: for comment in comments:
self.assertEqual(comment.text, u"bar") self.assertEqual(comment.text, u'bar')
self.assertEqual(comment.creator, "test_user_1_") self.assertEqual(comment.creator, 'test_user_1_')
self.assertEqual(comment.getOwner().getUserName(), "test-user") self.assertEqual(comment.getOwner().getUserName(), 'test-user')
local_roles = comment.get_local_roles() local_roles = comment.get_local_roles()
self.assertEqual(len(local_roles), 1) self.assertEqual(len(local_roles), 1)
userid, roles = local_roles[0] userid, roles = local_roles[0]
@ -144,14 +144,14 @@ class TestCommentForm(unittest.TestCase):
adapts=(Interface, IBrowserRequest), adapts=(Interface, IBrowserRequest),
provides=Interface, provides=Interface,
factory=CommentForm, factory=CommentForm,
name=u"comment-form" name=u'comment-form'
) )
provideAdapter( provideAdapter(
adapts=(Interface, IBrowserRequest), adapts=(Interface, IBrowserRequest),
provides=Interface, provides=Interface,
factory=EditCommentForm, factory=EditCommentForm,
name=u"edit-comment-form" name=u'edit-comment-form'
) )
# The form is submitted successfully, if the required text field is # The form is submitted successfully, if the required text field is
@ -160,13 +160,13 @@ class TestCommentForm(unittest.TestCase):
commentForm = getMultiAdapter( commentForm = getMultiAdapter(
(self.context, request), (self.context, request),
name=u"comment-form" name=u'comment-form'
) )
commentForm.update() commentForm.update()
data, errors = commentForm.extractData() # pylint: disable-msg=W0612 data, errors = commentForm.extractData() # pylint: disable-msg=W0612
self.assertEqual(len(errors), 0) self.assertEqual(len(errors), 0)
self.assertFalse(commentForm.handleComment(commentForm, "foo")) self.assertFalse(commentForm.handleComment(commentForm, 'foo'))
# Edit the last comment # Edit the last comment
conversation = IConversation(self.context) conversation = IConversation(self.context)
@ -174,25 +174,25 @@ class TestCommentForm(unittest.TestCase):
request = make_request(form={'form.widgets.text': u'foobar'}) request = make_request(form={'form.widgets.text': u'foobar'})
editForm = getMultiAdapter( editForm = getMultiAdapter(
(comment, request), (comment, request),
name=u"edit-comment-form" name=u'edit-comment-form'
) )
editForm.update() editForm.update()
data, errors = editForm.extractData() # pylint: disable-msg=W0612 data, errors = editForm.extractData() # pylint: disable-msg=W0612
self.assertEqual(len(errors), 0) self.assertEqual(len(errors), 0)
self.assertFalse(editForm.handleComment(editForm, "foo")) self.assertFalse(editForm.handleComment(editForm, 'foo'))
comment = [x for x in conversation.getComments()][-1] comment = [x for x in conversation.getComments()][-1]
self.assertEquals(comment.text, u"foobar") self.assertEqual(comment.text, u'foobar')
comments = IConversation(commentForm.context).getComments() comments = IConversation(commentForm.context).getComments()
comments = [c for c in comments] # consume iterator comments = [c for c in comments] # consume iterator
self.assertEqual(len(comments), 1) self.assertEqual(len(comments), 1)
for comment in comments: for comment in comments:
self.assertEqual(comment.text, u"foobar") self.assertEqual(comment.text, u'foobar')
self.assertEqual(comment.creator, "test_user_1_") self.assertEqual(comment.creator, 'test_user_1_')
self.assertEqual(comment.getOwner().getUserName(), "test-user") self.assertEqual(comment.getOwner().getUserName(), 'test-user')
local_roles = comment.get_local_roles() local_roles = comment.get_local_roles()
self.assertEqual(len(local_roles), 1) self.assertEqual(len(local_roles), 1)
userid, roles = local_roles[0] userid, roles = local_roles[0]
@ -219,7 +219,7 @@ class TestCommentForm(unittest.TestCase):
adapts=(Interface, IBrowserRequest), adapts=(Interface, IBrowserRequest),
provides=Interface, provides=Interface,
factory=CommentForm, factory=CommentForm,
name=u"comment-form" name=u'comment-form'
) )
# The form is submitted successfully, if the required text field is # The form is submitted successfully, if the required text field is
@ -228,31 +228,31 @@ class TestCommentForm(unittest.TestCase):
commentForm = getMultiAdapter( commentForm = getMultiAdapter(
(self.context, form_request), (self.context, form_request),
name=u"comment-form" name=u'comment-form'
) )
commentForm.update() commentForm.update()
data, errors = commentForm.extractData() # pylint: disable-msg=W0612 data, errors = commentForm.extractData() # pylint: disable-msg=W0612
self.assertEqual(len(errors), 0) self.assertEqual(len(errors), 0)
self.assertFalse(commentForm.handleComment(commentForm, "foo")) self.assertFalse(commentForm.handleComment(commentForm, 'foo'))
# Delete the last comment # Delete the last comment
conversation = IConversation(self.context) conversation = IConversation(self.context)
comment = [x for x in conversation.getComments()][-1] comment = [x for x in conversation.getComments()][-1]
deleteView = getMultiAdapter( deleteView = getMultiAdapter(
(comment, self.request), (comment, self.request),
name=u"moderate-delete-comment" name=u'moderate-delete-comment'
) )
# try to delete last comment without "Delete comments" permission # try to delete last comment without 'Delete comments' permission
setRoles(self.portal, TEST_USER_ID, ['Member']) setRoles(self.portal, TEST_USER_ID, ['Member'])
self.assertRaises( self.assertRaises(
Unauthorized, Unauthorized,
comment.restrictedTraverse, comment.restrictedTraverse,
"@@moderate-delete-comment" '@@moderate-delete-comment'
) )
deleteView() deleteView()
self.assertEqual(1, len([x for x in conversation.getComments()])) self.assertEqual(1, len([x for x in conversation.getComments()]))
# try to delete last comment with "Delete comments" permission # try to delete last comment with 'Delete comments' permission
setRoles(self.portal, TEST_USER_ID, ['Reviewer']) setRoles(self.portal, TEST_USER_ID, ['Reviewer'])
deleteView() deleteView()
self.assertEqual(0, len([x for x in conversation.getComments()])) self.assertEqual(0, len([x for x in conversation.getComments()]))
@ -277,7 +277,7 @@ class TestCommentForm(unittest.TestCase):
adapts=(Interface, IBrowserRequest), adapts=(Interface, IBrowserRequest),
provides=Interface, provides=Interface,
factory=CommentForm, factory=CommentForm,
name=u"comment-form" name=u'comment-form'
) )
# The form is submitted successfully, if the required text field is # The form is submitted successfully, if the required text field is
@ -286,20 +286,20 @@ class TestCommentForm(unittest.TestCase):
commentForm = getMultiAdapter( commentForm = getMultiAdapter(
(self.context, form_request), (self.context, form_request),
name=u"comment-form" name=u'comment-form'
) )
commentForm.update() commentForm.update()
data, errors = commentForm.extractData() # pylint: disable-msg=W0612 data, errors = commentForm.extractData() # pylint: disable-msg=W0612
self.assertEqual(len(errors), 0) self.assertEqual(len(errors), 0)
self.assertFalse(commentForm.handleComment(commentForm, "foo")) self.assertFalse(commentForm.handleComment(commentForm, 'foo'))
# Delete the last comment # Delete the last comment
conversation = IConversation(self.context) conversation = IConversation(self.context)
comment = [x for x in conversation.getComments()][-1] comment = [x for x in conversation.getComments()][-1]
deleteView = getMultiAdapter( deleteView = getMultiAdapter(
(comment, self.request), (comment, self.request),
name=u"delete-own-comment" name=u'delete-own-comment'
) )
# try to delete last comment with johndoe # try to delete last comment with johndoe
setRoles(self.portal, 'johndoe', ['Member']) setRoles(self.portal, 'johndoe', ['Member'])
@ -307,7 +307,7 @@ class TestCommentForm(unittest.TestCase):
self.assertRaises( self.assertRaises(
Unauthorized, Unauthorized,
comment.restrictedTraverse, comment.restrictedTraverse,
"@@delete-own-comment" '@@delete-own-comment'
) )
self.assertEqual(1, len([x for x in conversation.getComments()])) self.assertEqual(1, len([x for x in conversation.getComments()]))
# try to delete last comment with the same user that created it # try to delete last comment with the same user that created it
@ -338,7 +338,7 @@ class TestCommentForm(unittest.TestCase):
provideAdapter(adapts=(Interface, IBrowserRequest), provideAdapter(adapts=(Interface, IBrowserRequest),
provides=Interface, provides=Interface,
factory=CommentForm, factory=CommentForm,
name=u"comment-form") name=u'comment-form')
# Post an anonymous comment and provide a name # Post an anonymous comment and provide a name
request = make_request(form={ request = make_request(form={
@ -348,20 +348,20 @@ class TestCommentForm(unittest.TestCase):
commentForm = getMultiAdapter( commentForm = getMultiAdapter(
(self.context, request), (self.context, request),
name=u"comment-form" name=u'comment-form'
) )
commentForm.update() commentForm.update()
data, errors = commentForm.extractData() # pylint: disable-msg=W0612 data, errors = commentForm.extractData() # pylint: disable-msg=W0612
self.assertEqual(len(errors), 0) self.assertEqual(len(errors), 0)
self.assertFalse(commentForm.handleComment(commentForm, "action")) self.assertFalse(commentForm.handleComment(commentForm, 'action'))
comments = IConversation(commentForm.context).getComments() comments = IConversation(commentForm.context).getComments()
comments = [comment for comment in comments] # consume itertor comments = [comment for comment in comments] # consume itertor
self.assertEqual(len(comments), 1) self.assertEqual(len(comments), 1)
for comment in IConversation(commentForm.context).getComments(): for comment in IConversation(commentForm.context).getComments():
self.assertEqual(comment.text, u"bar") self.assertEqual(comment.text, u'bar')
self.assertIsNone(comment.creator) self.assertIsNone(comment.creator)
roles = comment.get_local_roles() roles = comment.get_local_roles()
self.assertEqual(len(roles), 0) self.assertEqual(len(roles), 0)
@ -385,13 +385,13 @@ class TestCommentForm(unittest.TestCase):
provideAdapter(adapts=(Interface, IBrowserRequest), provideAdapter(adapts=(Interface, IBrowserRequest),
provides=Interface, provides=Interface,
factory=CommentForm, factory=CommentForm,
name=u"comment-form") name=u'comment-form')
request = make_request(form={'form.widgets.text': u'bar'}) request = make_request(form={'form.widgets.text': u'bar'})
commentForm = getMultiAdapter( commentForm = getMultiAdapter(
(self.context, request), (self.context, request),
name=u"comment-form" name=u'comment-form'
) )
commentForm.update() commentForm.update()
data, errors = commentForm.extractData() # pylint: disable-msg=W0612 data, errors = commentForm.extractData() # pylint: disable-msg=W0612
@ -403,7 +403,7 @@ class TestCommentForm(unittest.TestCase):
self.assertRaises(Unauthorized, self.assertRaises(Unauthorized,
commentForm.handleComment, commentForm.handleComment,
commentForm, commentForm,
"foo") 'foo')
def test_anonymous_can_not_add_comments_if_discussion_is_not_allowed(self): def test_anonymous_can_not_add_comments_if_discussion_is_not_allowed(self):
"""Make sure that anonymous users can't post comments if anonymous """Make sure that anonymous users can't post comments if anonymous
@ -424,12 +424,12 @@ class TestCommentForm(unittest.TestCase):
provideAdapter(adapts=(Interface, IBrowserRequest), provideAdapter(adapts=(Interface, IBrowserRequest),
provides=Interface, provides=Interface,
factory=CommentForm, factory=CommentForm,
name=u"comment-form") name=u'comment-form')
request = make_request(form={'form.widgets.text': u'bar'}) request = make_request(form={'form.widgets.text': u'bar'})
commentForm = getMultiAdapter((self.context, request), commentForm = getMultiAdapter((self.context, request),
name=u"comment-form") name=u'comment-form')
commentForm.update() commentForm.update()
data, errors = commentForm.extractData() # pylint: disable-msg=W0612 data, errors = commentForm.extractData() # pylint: disable-msg=W0612
@ -438,7 +438,7 @@ class TestCommentForm(unittest.TestCase):
Unauthorized, Unauthorized,
commentForm.handleComment, commentForm.handleComment,
commentForm, commentForm,
"foo" 'foo'
) )
@ -519,20 +519,20 @@ class TestCommentsViewlet(unittest.TestCase):
self.assertTrue(self.viewlet.comment_transform_message()) self.assertTrue(self.viewlet.comment_transform_message())
self.assertEqual( self.assertEqual(
self.viewlet.comment_transform_message(), self.viewlet.comment_transform_message(),
"You can add a comment by filling out the form below. Plain " + 'You can add a comment by filling out the form below. Plain ' +
"text formatting.") 'text formatting.')
# Set text transform to intelligent text # Set text transform to intelligent text
registry = queryUtility(IRegistry) registry = queryUtility(IRegistry)
settings = registry.forInterface(IDiscussionSettings, check=False) settings = registry.forInterface(IDiscussionSettings, check=False)
settings.text_transform = "text/x-web-intelligent" settings.text_transform = 'text/x-web-intelligent'
# Make sure the comment description changes accordingly # Make sure the comment description changes accordingly
self.assertEqual( self.assertEqual(
self.viewlet.comment_transform_message(), self.viewlet.comment_transform_message(),
"You can add a comment by filling out the form below. " + 'You can add a comment by filling out the form below. ' +
"Plain text formatting. Web and email addresses are transformed " + 'Plain text formatting. Web and email addresses are transformed ' +
"into clickable links." 'into clickable links.'
) )
# Enable moderation workflow # Enable moderation workflow
@ -543,9 +543,9 @@ class TestCommentsViewlet(unittest.TestCase):
# Make sure the comment description shows that comments are moderated # Make sure the comment description shows that comments are moderated
self.assertEqual( self.assertEqual(
self.viewlet.comment_transform_message(), self.viewlet.comment_transform_message(),
"You can add a comment by filling out the form below. " + 'You can add a comment by filling out the form below. ' +
"Plain text formatting. Web and email addresses are transformed " + 'Plain text formatting. Web and email addresses are transformed ' +
"into clickable links. Comments are moderated.") 'into clickable links. Comments are moderated.')
def test_has_replies(self): def test_has_replies(self):
self.assertEqual(self.viewlet.has_replies(), False) self.assertEqual(self.viewlet.has_replies(), False)
@ -599,10 +599,11 @@ class TestCommentsViewlet(unittest.TestCase):
reply['actions'][0]['id'], reply['actions'][0]['id'],
'publish' 'publish'
) )
expected_url = 'http://nohost/plone/doc1/++conversation++default/{0}' \
'/content_status_modify?workflow_action=publish'
self.assertEqual( self.assertEqual(
reply['actions'][0]['url'], reply['actions'][0]['url'],
'http://nohost/plone/doc1/++conversation++default/%s' % int(c1) + expected_url.format(int(c1))
'/content_status_modify?workflow_action=publish'
) )
def test_get_commenter_home_url(self): def test_get_commenter_home_url(self):

View File

@ -1,7 +1,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from plone.app.discussion.interfaces import ICommentAddedEvent from plone.app.discussion.interfaces import ICommentAddedEvent
from plone.app.discussion.interfaces import ICommentRemovedEvent from plone.app.discussion.interfaces import ICommentRemovedEvent
from plone.app.discussion.interfaces import IConversation, IReplies from plone.app.discussion.interfaces import IConversation
from plone.app.discussion.interfaces import IReplies
from plone.app.discussion.interfaces import IReplyAddedEvent from plone.app.discussion.interfaces import IReplyAddedEvent
from plone.app.discussion.interfaces import IReplyRemovedEvent from plone.app.discussion.interfaces import IReplyRemovedEvent
from plone.app.discussion.testing import PLONE_APP_DISCUSSION_INTEGRATION_TESTING # noqa from plone.app.discussion.testing import PLONE_APP_DISCUSSION_INTEGRATION_TESTING # noqa
@ -37,10 +38,10 @@ class CommentContentRulesTest(unittest.TestCase):
self.document = self.portal['doc1'] self.document = self.portal['doc1']
comment = createObject('plone.Comment') comment = createObject('plone.Comment')
comment.text = "This is a comment" comment.text = 'This is a comment'
comment.author_username = "jim" comment.author_username = 'jim'
comment.author_name = "Jim" comment.author_name = 'Jim'
comment.author_email = "jim@example.com" comment.author_email = 'jim@example.com'
conversation = IConversation(self.document) conversation = IConversation(self.document)
conversation.addComment(comment) conversation.addComment(comment)
@ -52,28 +53,28 @@ class CommentContentRulesTest(unittest.TestCase):
def testCommentIdStringSubstitution(self): def testCommentIdStringSubstitution(self):
comment_id = getAdapter(self.document, IStringSubstitution, comment_id = getAdapter(self.document, IStringSubstitution,
name=u"comment_id") name=u'comment_id')
self.assertIsInstance(comment_id(), long) self.assertIsInstance(comment_id(), long)
def testCommentTextStringSubstitution(self): def testCommentTextStringSubstitution(self):
comment_text = getAdapter(self.document, IStringSubstitution, comment_text = getAdapter(self.document, IStringSubstitution,
name=u"comment_text") name=u'comment_text')
self.assertEqual(comment_text(), u"This is a comment") self.assertEqual(comment_text(), u'This is a comment')
def testCommentUserIdStringSubstitution(self): def testCommentUserIdStringSubstitution(self):
comment_user_id = getAdapter(self.document, IStringSubstitution, comment_user_id = getAdapter(self.document, IStringSubstitution,
name=u"comment_user_id") name=u'comment_user_id')
self.assertEqual(comment_user_id(), u"jim") self.assertEqual(comment_user_id(), u'jim')
def testCommentUserFullNameStringSubstitution(self): def testCommentUserFullNameStringSubstitution(self):
comment_user_fullname = getAdapter(self.document, IStringSubstitution, comment_user_fullname = getAdapter(self.document, IStringSubstitution,
name=u"comment_user_fullname") name=u'comment_user_fullname')
self.assertEqual(comment_user_fullname(), u"Jim") self.assertEqual(comment_user_fullname(), u'Jim')
def testCommentUserEmailStringSubstitution(self): def testCommentUserEmailStringSubstitution(self):
comment_user_email = getAdapter(self.document, IStringSubstitution, comment_user_email = getAdapter(self.document, IStringSubstitution,
name=u"comment_user_email") name=u'comment_user_email')
self.assertEqual(comment_user_email(), u"jim@example.com") self.assertEqual(comment_user_email(), u'jim@example.com')
class ReplyContentRulesTest(unittest.TestCase): class ReplyContentRulesTest(unittest.TestCase):
@ -95,13 +96,14 @@ class ReplyContentRulesTest(unittest.TestCase):
comment.text = 'This is a comment' comment.text = 'This is a comment'
new_id = replies.addComment(comment) new_id = replies.addComment(comment)
comment = self.document.restrictedTraverse( comment = self.document.restrictedTraverse(
'++conversation++default/%s' % new_id) '++conversation++default/{0}'.format(new_id)
)
re_comment = createObject('plone.Comment') re_comment = createObject('plone.Comment')
re_comment.text = 'This is a reply' re_comment.text = 'This is a reply'
re_comment.author_username = "julia" re_comment.author_username = 'julia'
re_comment.author_name = "Juliana" re_comment.author_name = 'Juliana'
re_comment.author_email = "julia@example.com" re_comment.author_email = 'julia@example.com'
replies = IReplies(comment) replies = IReplies(comment)
replies.addComment(re_comment) replies.addComment(re_comment)
@ -110,7 +112,7 @@ class ReplyContentRulesTest(unittest.TestCase):
reply_id = getAdapter( reply_id = getAdapter(
self.document, self.document,
IStringSubstitution, IStringSubstitution,
name=u"comment_id" name=u'comment_id'
) )
self.assertIsInstance(reply_id(), long) self.assertIsInstance(reply_id(), long)
@ -118,30 +120,30 @@ class ReplyContentRulesTest(unittest.TestCase):
reply_text = getAdapter( reply_text = getAdapter(
self.document, self.document,
IStringSubstitution, IStringSubstitution,
name=u"comment_text" name=u'comment_text'
) )
self.assertEqual(reply_text(), u"This is a reply") self.assertEqual(reply_text(), u'This is a reply')
def testReplyUserIdStringSubstitution(self): def testReplyUserIdStringSubstitution(self):
reply_user_id = getAdapter( reply_user_id = getAdapter(
self.document, self.document,
IStringSubstitution, IStringSubstitution,
name=u"comment_user_id" name=u'comment_user_id'
) )
self.assertEqual(reply_user_id(), u"julia") self.assertEqual(reply_user_id(), u'julia')
def testReplyUserFullNameStringSubstitution(self): def testReplyUserFullNameStringSubstitution(self):
reply_user_fullname = getAdapter( reply_user_fullname = getAdapter(
self.document, self.document,
IStringSubstitution, IStringSubstitution,
name=u"comment_user_fullname" name=u'comment_user_fullname'
) )
self.assertEqual(reply_user_fullname(), u"Juliana") self.assertEqual(reply_user_fullname(), u'Juliana')
def testReplyUserEmailStringSubstitution(self): def testReplyUserEmailStringSubstitution(self):
reply_user_email = getAdapter( reply_user_email = getAdapter(
self.document, self.document,
IStringSubstitution, IStringSubstitution,
name=u"comment_user_email" name=u'comment_user_email'
) )
self.assertEqual(reply_user_email(), u"julia@example.com") self.assertEqual(reply_user_email(), u'julia@example.com')

View File

@ -29,14 +29,14 @@ class RegistryTest(unittest.TestCase):
def test_discussion_controlpanel_view(self): def test_discussion_controlpanel_view(self):
view = getMultiAdapter( view = getMultiAdapter(
(self.portal, self.portal.REQUEST), (self.portal, self.portal.REQUEST),
name="discussion-controlpanel" name='discussion-controlpanel'
) )
view = view.__of__(self.portal) view = view.__of__(self.portal)
self.assertTrue(view()) self.assertTrue(view())
def test_discussion_in_controlpanel(self): def test_discussion_in_controlpanel(self):
# Check if discussion is in the control panel # Check if discussion is in the control panel
self.controlpanel = getToolByName(self.portal, "portal_controlpanel") self.controlpanel = getToolByName(self.portal, 'portal_controlpanel')
self.assertTrue( self.assertTrue(
'discussion' in [ 'discussion' in [
a.getAction(self)['id'] a.getAction(self)['id']

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
from Acquisition import aq_base from Acquisition import aq_base
from Acquisition import aq_parent from Acquisition import aq_parent
from Products.CMFCore.utils import getToolByName
from datetime import datetime from datetime import datetime
from datetime import timedelta from datetime import timedelta
from plone.app.discussion import interfaces from plone.app.discussion import interfaces
@ -13,6 +13,7 @@ from plone.app.testing import setRoles
from plone.app.testing import TEST_USER_ID from plone.app.testing import TEST_USER_ID
from plone.app.vocabularies.types import BAD_TYPES from plone.app.vocabularies.types import BAD_TYPES
from plone.registry.interfaces import IRegistry from plone.registry.interfaces import IRegistry
from Products.CMFCore.utils import getToolByName
from zope import interface from zope import interface
from zope.annotation.interfaces import IAnnotations from zope.annotation.interfaces import IAnnotations
from zope.component import createObject from zope.component import createObject
@ -24,7 +25,7 @@ import unittest2 as unittest
try: try:
from plone.dexterity.interfaces import IDexterityContent from plone.dexterity.interfaces import IDexterityContent
DEXTERITY = True DEXTERITY = True
except: except ImportError:
DEXTERITY = False DEXTERITY = False
@ -82,12 +83,12 @@ class ConversationTest(unittest.TestCase):
conversation = IConversation(self.portal.doc1) conversation = IConversation(self.portal.doc1)
comment = createObject('plone.Comment') comment = createObject('plone.Comment')
comment.author_username = "nobody" comment.author_username = 'nobody'
conversation.addComment(comment) conversation.addComment(comment)
comment.manage_permission("View", roles=tuple()) comment.manage_permission('View', roles=tuple())
self.assertEqual(0, conversation.total_comments()) self.assertEqual(0, conversation.total_comments())
self.assertEqual(None, conversation.last_comment_date) self.assertEqual(None, conversation.last_comment_date)
self.assertEqual(["nobody"], list(conversation.commentators)) self.assertEqual(['nobody'], list(conversation.commentators))
self.assertEqual([], list(conversation.public_commentators)) self.assertEqual([], list(conversation.public_commentators))
def test_delete_comment(self): def test_delete_comment(self):
@ -434,22 +435,22 @@ class ConversationTest(unittest.TestCase):
# swapped in # swapped in
comment1 = createObject('plone.Comment') comment1 = createObject('plone.Comment')
comment1.text = 'Comment text' comment1.text = 'Comment text'
comment1.author_username = "Jim" comment1.author_username = 'Jim'
conversation.addComment(comment1) conversation.addComment(comment1)
comment2 = createObject('plone.Comment') comment2 = createObject('plone.Comment')
comment2.text = 'Comment text' comment2.text = 'Comment text'
comment2.author_username = "Joe" comment2.author_username = 'Joe'
conversation.addComment(comment2) conversation.addComment(comment2)
comment3 = createObject('plone.Comment') comment3 = createObject('plone.Comment')
comment3.text = 'Comment text' comment3.text = 'Comment text'
comment3.author_username = "Jack" comment3.author_username = 'Jack'
new_comment3_id = conversation.addComment(comment3) new_comment3_id = conversation.addComment(comment3)
comment4 = createObject('plone.Comment') comment4 = createObject('plone.Comment')
comment4.text = 'Comment text' comment4.text = 'Comment text'
comment4.author_username = "Jack" comment4.author_username = 'Jack'
new_comment4_id = conversation.addComment(comment4) new_comment4_id = conversation.addComment(comment4)
# check if all commentators are in the commentators list # check if all commentators are in the commentators list
@ -680,10 +681,10 @@ class ConversationEnabledForDexterityTypesTest(unittest.TestCase):
) )
if DEXTERITY: if DEXTERITY:
interface.alsoProvides( interface.alsoProvides(
self.portal.doc1, self.portal.doc1,
IDexterityContent IDexterityContent
) )
def _makeOne(self, *args, **kw): def _makeOne(self, *args, **kw):
return self.portal.doc1.restrictedTraverse('@@conversation_view') return self.portal.doc1.restrictedTraverse('@@conversation_view')
@ -839,16 +840,19 @@ class RepliesTest(unittest.TestCase):
# Create the nested comment structure # Create the nested comment structure
new_id_1 = replies.addComment(comment1) new_id_1 = replies.addComment(comment1)
comment1 = self.portal.doc1.restrictedTraverse( comment1 = self.portal.doc1.restrictedTraverse(
'++conversation++default/%s' % new_id_1) '++conversation++default/{0}'.format(new_id_1)
)
replies_to_comment1 = IReplies(comment1) replies_to_comment1 = IReplies(comment1)
new_id_2 = replies.addComment(comment2) new_id_2 = replies.addComment(comment2)
comment2 = self.portal.doc1.restrictedTraverse( comment2 = self.portal.doc1.restrictedTraverse(
'++conversation++default/%s' % new_id_2) '++conversation++default/{0}'.format(new_id_2)
)
replies_to_comment2 = IReplies(comment2) replies_to_comment2 = IReplies(comment2)
new_id_1_1 = replies_to_comment1.addComment(comment1_1) new_id_1_1 = replies_to_comment1.addComment(comment1_1)
comment1_1 = self.portal.doc1.restrictedTraverse( comment1_1 = self.portal.doc1.restrictedTraverse(
'++conversation++default/%s' % new_id_1_1) '++conversation++default/{0}'.format(new_id_1_1)
)
replies_to_comment1_1 = IReplies(comment1_1) replies_to_comment1_1 = IReplies(comment1_1)
replies_to_comment1_1.addComment(comment1_1_1) replies_to_comment1_1.addComment(comment1_1_1)

View File

@ -1,11 +1,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from Zope2.App import zcml
from plone.app.discussion.interfaces import IConversation from plone.app.discussion.interfaces import IConversation
from plone.app.discussion.interfaces import IReplies from plone.app.discussion.interfaces import IReplies
from plone.app.discussion.testing import PLONE_APP_DISCUSSION_INTEGRATION_TESTING # noqa from plone.app.discussion.testing import PLONE_APP_DISCUSSION_INTEGRATION_TESTING # noqa
from plone.app.testing import setRoles from plone.app.testing import setRoles
from plone.app.testing import TEST_USER_ID from plone.app.testing import TEST_USER_ID
from zope.component import createObject from zope.component import createObject
from Zope2.App import zcml
import Products.Five import Products.Five
import unittest2 as unittest import unittest2 as unittest
@ -86,7 +86,7 @@ class CommentEventsTest(unittest.TestCase):
</configure> </configure>
""" """
zcml.load_config("configure.zcml", Products.Five) zcml.load_config('configure.zcml', Products.Five)
zcml.load_string(configure) zcml.load_string(configure)
def test_addEvent(self): def test_addEvent(self):
@ -139,7 +139,7 @@ class RepliesEventsTest(unittest.TestCase):
</configure> </configure>
""" """
zcml.load_config("configure.zcml", Products.Five) zcml.load_config('configure.zcml', Products.Five)
zcml.load_string(configure) zcml.load_string(configure)
def test_addEvent(self): def test_addEvent(self):
@ -152,7 +152,8 @@ class RepliesEventsTest(unittest.TestCase):
comment.text = 'Comment text' comment.text = 'Comment text'
new_id = replies.addComment(comment) new_id = replies.addComment(comment)
comment = self.document.restrictedTraverse( comment = self.document.restrictedTraverse(
'++conversation++default/%s' % new_id) '++conversation++default/{0}'.format(new_id)
)
re_comment = createObject('plone.Comment') re_comment = createObject('plone.Comment')
re_comment.text = 'Comment text' re_comment.text = 'Comment text'
@ -172,7 +173,8 @@ class RepliesEventsTest(unittest.TestCase):
comment.text = 'Comment text' comment.text = 'Comment text'
new_id = replies.addComment(comment) new_id = replies.addComment(comment)
comment = self.portal.doc1.restrictedTraverse( comment = self.portal.doc1.restrictedTraverse(
'++conversation++default/%s' % new_id) '++conversation++default/{0}'.format(new_id)
)
re_comment = createObject('plone.Comment') re_comment = createObject('plone.Comment')
re_comment.text = 'Comment text' re_comment.text = 'Comment text'

View File

@ -1,7 +1,8 @@
# -*- coding: utf-8 -*-
"""Test for the plone.app.discussion indexers """Test for the plone.app.discussion indexers
""" """
from DateTime import DateTime
from datetime import datetime from datetime import datetime
from DateTime import DateTime
from plone.app.discussion import catalog from plone.app.discussion import catalog
from plone.app.discussion.interfaces import IConversation from plone.app.discussion.interfaces import IConversation
from plone.app.discussion.testing import PLONE_APP_DISCUSSION_INTEGRATION_TESTING # noqa from plone.app.discussion.testing import PLONE_APP_DISCUSSION_INTEGRATION_TESTING # noqa
@ -39,24 +40,24 @@ class ConversationIndexersTest(unittest.TestCase):
comment1 = createObject('plone.Comment') comment1 = createObject('plone.Comment')
comment1.text = 'Comment Text' comment1.text = 'Comment Text'
comment1.creator = "jim" comment1.creator = 'jim'
comment1.author_username = "Jim" comment1.author_username = 'Jim'
comment1.creation_date = datetime(2006, 9, 17, 14, 18, 12) comment1.creation_date = datetime(2006, 9, 17, 14, 18, 12)
comment1.modification_date = datetime(2006, 9, 17, 14, 18, 12) comment1.modification_date = datetime(2006, 9, 17, 14, 18, 12)
self.new_id1 = conversation.addComment(comment1) self.new_id1 = conversation.addComment(comment1)
comment2 = createObject('plone.Comment') comment2 = createObject('plone.Comment')
comment2.text = 'Comment Text' comment2.text = 'Comment Text'
comment2.creator = "emma" comment2.creator = 'emma'
comment2.author_username = "Emma" comment2.author_username = 'Emma'
comment2.creation_date = datetime(2007, 12, 13, 4, 18, 12) comment2.creation_date = datetime(2007, 12, 13, 4, 18, 12)
comment2.modification_date = datetime(2007, 12, 13, 4, 18, 12) comment2.modification_date = datetime(2007, 12, 13, 4, 18, 12)
self.new_id2 = conversation.addComment(comment2) self.new_id2 = conversation.addComment(comment2)
comment3 = createObject('plone.Comment') comment3 = createObject('plone.Comment')
comment3.text = 'Comment Text' comment3.text = 'Comment Text'
comment3.creator = "lukas" comment3.creator = 'lukas'
comment3.author_username = "Lukas" comment3.author_username = 'Lukas'
comment3.creation_date = datetime(2009, 4, 12, 11, 12, 12) comment3.creation_date = datetime(2009, 4, 12, 11, 12, 12)
comment3.modification_date = datetime(2009, 4, 12, 11, 12, 12) comment3.modification_date = datetime(2009, 4, 12, 11, 12, 12)
self.new_id3 = conversation.addComment(comment3) self.new_id3 = conversation.addComment(comment3)
@ -118,8 +119,8 @@ class CommentIndexersTest(unittest.TestCase):
comment = createObject('plone.Comment') comment = createObject('plone.Comment')
comment.text = 'Lorem ipsum dolor sit amet.' comment.text = 'Lorem ipsum dolor sit amet.'
comment.creator = "jim" comment.creator = 'jim'
comment.author_name = "Jim" comment.author_name = 'Jim'
comment.creation_date = datetime(2006, 9, 17, 14, 18, 12) comment.creation_date = datetime(2006, 9, 17, 14, 18, 12)
comment.modification_date = datetime(2008, 3, 12, 7, 32, 52) comment.modification_date = datetime(2008, 3, 12, 7, 32, 52)
@ -149,7 +150,7 @@ class CommentIndexersTest(unittest.TestCase):
self.conversation.addComment(comment_long) self.conversation.addComment(comment_long)
self.assertEqual( self.assertEqual(
catalog.description(comment_long)(), catalog.description(comment_long)(),
LONG_TEXT_CUT.replace("\n", " ") LONG_TEXT_CUT.replace('\n', ' ')
) )
def test_dates(self): def test_dates(self):

View File

@ -1,11 +1,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from Products.CMFCore.utils import getToolByName
from plone.app.discussion.browser.moderation import BulkActionsView from plone.app.discussion.browser.moderation import BulkActionsView
from plone.app.discussion.browser.moderation import View from plone.app.discussion.browser.moderation import View
from plone.app.discussion.interfaces import IConversation from plone.app.discussion.interfaces import IConversation
from plone.app.discussion.testing import PLONE_APP_DISCUSSION_INTEGRATION_TESTING # noqa from plone.app.discussion.testing import PLONE_APP_DISCUSSION_INTEGRATION_TESTING # noqa
from plone.app.testing import setRoles from plone.app.testing import setRoles
from plone.app.testing import TEST_USER_ID from plone.app.testing import TEST_USER_ID
from Products.CMFCore.utils import getToolByName
from zope.component import createObject from zope.component import createObject
import unittest import unittest
@ -75,7 +75,7 @@ class ModerationBulkActionsViewTest(unittest.TestCase):
comment1.Creator = 'Jim' comment1.Creator = 'Jim'
new_id_1 = conversation.addComment(comment1) new_id_1 = conversation.addComment(comment1)
self.comment1 = self.portal.doc1.restrictedTraverse( self.comment1 = self.portal.doc1.restrictedTraverse(
'++conversation++default/%s' % new_id_1 '++conversation++default/{0}'.format(new_id_1)
) )
comment2 = createObject('plone.Comment') comment2 = createObject('plone.Comment')
comment2.title = 'Comment 2' comment2.title = 'Comment 2'
@ -83,7 +83,7 @@ class ModerationBulkActionsViewTest(unittest.TestCase):
comment2.Creator = 'Joe' comment2.Creator = 'Joe'
new_id_2 = conversation.addComment(comment2) new_id_2 = conversation.addComment(comment2)
self.comment2 = self.portal.doc1.restrictedTraverse( self.comment2 = self.portal.doc1.restrictedTraverse(
'++conversation++default/%s' % new_id_2 '++conversation++default/{0}'.format(new_id_2)
) )
comment3 = createObject('plone.Comment') comment3 = createObject('plone.Comment')
comment3.title = 'Comment 3' comment3.title = 'Comment 3'
@ -91,7 +91,7 @@ class ModerationBulkActionsViewTest(unittest.TestCase):
comment3.Creator = 'Emma' comment3.Creator = 'Emma'
new_id_3 = conversation.addComment(comment3) new_id_3 = conversation.addComment(comment3)
self.comment3 = self.portal.doc1.restrictedTraverse( self.comment3 = self.portal.doc1.restrictedTraverse(
'++conversation++default/%s' % new_id_3 '++conversation++default/{0}'.format(new_id_3)
) )
self.conversation = conversation self.conversation = conversation

View File

@ -1,13 +1,13 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from Acquisition import aq_base from Acquisition import aq_base
from plone.app.discussion.interfaces import IConversation
from plone.app.discussion.testing import PLONE_APP_DISCUSSION_INTEGRATION_TESTING # noqa
from plone.app.testing import setRoles
from plone.app.testing import TEST_USER_ID
from plone.registry.interfaces import IRegistry
from Products.CMFPlone.interfaces import IMailSchema from Products.CMFPlone.interfaces import IMailSchema
from Products.CMFPlone.tests.utils import MockMailHost from Products.CMFPlone.tests.utils import MockMailHost
from Products.MailHost.interfaces import IMailHost from Products.MailHost.interfaces import IMailHost
from plone.app.discussion.interfaces import IConversation
from plone.app.discussion.testing import PLONE_APP_DISCUSSION_INTEGRATION_TESTING # noqa
from plone.app.testing import TEST_USER_ID
from plone.app.testing import setRoles
from plone.registry.interfaces import IRegistry
from zope.component import createObject from zope.component import createObject
from zope.component import getSiteManager from zope.component import getSiteManager
from zope.component import getUtility from zope.component import getUtility
@ -32,7 +32,7 @@ class TestUserNotificationUnit(unittest.TestCase):
# We need to fake a valid mail setup # We need to fake a valid mail setup
registry = getUtility(IRegistry) registry = getUtility(IRegistry)
mail_settings = registry.forInterface(IMailSchema, prefix='plone') mail_settings = registry.forInterface(IMailSchema, prefix='plone')
mail_settings.email_from_address = "portal@plone.test" mail_settings.email_from_address = 'portal@plone.test'
self.mailhost = self.portal.MailHost self.mailhost = self.portal.MailHost
# Enable user notification setting # Enable user notification setting
registry = queryUtility(IRegistry) registry = queryUtility(IRegistry)
@ -40,7 +40,7 @@ class TestUserNotificationUnit(unittest.TestCase):
'.user_notification_enabled'] = True '.user_notification_enabled'] = True
# Archetypes content types store data as utf-8 encoded strings # Archetypes content types store data as utf-8 encoded strings
# The missing u in front of a string is therefor not missing # The missing u in front of a string is therefor not missing
self.portal.doc1.title = 'Kölle Alaaf' # What is "Fasching"? self.portal.doc1.title = 'Kölle Alaaf' # What is 'Fasching'?
self.conversation = IConversation(self.portal.doc1) self.conversation = IConversation(self.portal.doc1)
def beforeTearDown(self): def beforeTearDown(self):
@ -56,7 +56,7 @@ class TestUserNotificationUnit(unittest.TestCase):
comment = createObject('plone.Comment') comment = createObject('plone.Comment')
comment.text = 'Comment text' comment.text = 'Comment text'
comment.user_notification = True comment.user_notification = True
comment.author_email = "john@plone.test" comment.author_email = 'john@plone.test'
self.conversation.addComment(comment) self.conversation.addComment(comment)
comment = createObject('plone.Comment') comment = createObject('plone.Comment')
comment.text = 'Comment text' comment.text = 'Comment text'
@ -75,11 +75,10 @@ class TestUserNotificationUnit(unittest.TestCase):
# The output should be encoded in a reasonable manner # The output should be encoded in a reasonable manner
# (in this case quoted-printable): # (in this case quoted-printable):
self.assertTrue( self.assertTrue(
"A comment on \'K=C3=B6lle Alaaf\' has been posted here:" 'A comment on "K=C3=B6lle Alaaf" has been posted here:'
in msg) in msg)
self.assertTrue( self.assertTrue(
"http://nohost/plone/d=\noc1/view#%s" 'http://nohost/plone/d=\noc1/view#{0}'.format(comment_id)
% comment_id
in msg) in msg)
self.assertTrue('Comment text' in msg) self.assertTrue('Comment text' in msg)
self.assertFalse('Approve comment' in msg) self.assertFalse('Approve comment' in msg)
@ -94,7 +93,7 @@ class TestUserNotificationUnit(unittest.TestCase):
comment = createObject('plone.Comment') comment = createObject('plone.Comment')
comment.text = 'Comment text' comment.text = 'Comment text'
comment.user_notification = True comment.user_notification = True
comment.author_email = "john@plone.test" comment.author_email = 'john@plone.test'
self.conversation.addComment(comment) self.conversation.addComment(comment)
comment = createObject('plone.Comment') comment = createObject('plone.Comment')
comment.text = 'Comment text' comment.text = 'Comment text'
@ -124,7 +123,7 @@ class TestUserNotificationUnit(unittest.TestCase):
comment = createObject('plone.Comment') comment = createObject('plone.Comment')
comment.text = 'Comment text' comment.text = 'Comment text'
comment.user_notification = True comment.user_notification = True
comment.author_email = "john@plone.test" comment.author_email = 'john@plone.test'
self.conversation.addComment(comment) self.conversation.addComment(comment)
comment = createObject('plone.Comment') comment = createObject('plone.Comment')
comment.text = 'Comment text' comment.text = 'Comment text'
@ -139,12 +138,12 @@ class TestUserNotificationUnit(unittest.TestCase):
comment = createObject('plone.Comment') comment = createObject('plone.Comment')
comment.text = 'Comment text' comment.text = 'Comment text'
comment.user_notification = True comment.user_notification = True
comment.author_email = "john@plone.test" comment.author_email = 'john@plone.test'
self.conversation.addComment(comment) self.conversation.addComment(comment)
comment = createObject('plone.Comment') comment = createObject('plone.Comment')
comment.text = 'Comment text' comment.text = 'Comment text'
comment.user_notification = True comment.user_notification = True
comment.author_email = "john@plone.test" comment.author_email = 'john@plone.test'
self.conversation.addComment(comment) self.conversation.addComment(comment)
@ -171,7 +170,7 @@ class TestModeratorNotificationUnit(unittest.TestCase):
# We need to fake a valid mail setup # We need to fake a valid mail setup
registry = getUtility(IRegistry) registry = getUtility(IRegistry)
mail_settings = registry.forInterface(IMailSchema, prefix='plone') mail_settings = registry.forInterface(IMailSchema, prefix='plone')
mail_settings.email_from_address = "portal@plone.test" mail_settings.email_from_address = 'portal@plone.test'
self.mailhost = self.portal.MailHost self.mailhost = self.portal.MailHost
# Enable comment moderation # Enable comment moderation
self.portal.portal_types['Document'].allow_discussion = True self.portal.portal_types['Document'].allow_discussion = True
@ -187,7 +186,7 @@ class TestModeratorNotificationUnit(unittest.TestCase):
] = True ] = True
# Archetypes content types store data as utf-8 encoded strings # Archetypes content types store data as utf-8 encoded strings
# The missing u in front of a string is therefor not missing # The missing u in front of a string is therefor not missing
self.portal.doc1.title = 'Kölle Alaaf' # What is "Fasching"? self.portal.doc1.title = 'Kölle Alaaf' # What is 'Fasching'?
self.conversation = IConversation(self.portal.doc1) self.conversation = IConversation(self.portal.doc1)
def beforeTearDown(self): def beforeTearDown(self):
@ -216,21 +215,28 @@ class TestModeratorNotificationUnit(unittest.TestCase):
# The output should be encoded in a reasonable manner # The output should be encoded in a reasonable manner
# (in this case quoted-printable): # (in this case quoted-printable):
self.assertTrue( self.assertTrue(
"A comment on \'K=C3=B6lle Alaaf\' has been posted here:" 'A comment on "K=C3=B6lle Alaaf" has been posted here:'
in msg) in msg)
self.assertTrue( self.assertIn(
"http://nohost/plone/d=\noc1/view#%s" 'http://nohost/plone/d=\noc1/view#{0}'.format(comment_id),
% comment_id msg
in msg) )
self.assertTrue('Comment text' in msg) self.assertIn(
self.assertTrue( 'Comment text',
'Approve comment:\nhttp://nohost/plone/doc1/' + msg
'++conversation++default/%s/@@moderat=\ne-publish-comment' )
% comment_id in msg) text = 'Approve comment:\nhttp://nohost/plone/doc1/' \
self.assertTrue( '++conversation++default/{0}/@@moderat=\ne-publish-comment'
'Delete comment:\nhttp://nohost/plone/doc1/' + self.assertIn(
'++conversation++default/%s/@@moderat=\ne-delete-comment' text.format(comment_id),
% comment_id in msg) msg
)
text = 'Delete comment:\nhttp://nohost/plone/doc1/' \
'++conversation++default/{0}/@@moderat=\ne-delete-comment'
self.assertIn(
text.format(comment_id),
msg
)
def test_notify_moderator_specific_address(self): def test_notify_moderator_specific_address(self):
# A moderator email address can be specified in the control panel. # A moderator email address can be specified in the control panel.

View File

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from plone.app.discussion.testing import PLONE_APP_DISCUSSION_ROBOT_TESTING from plone.app.discussion.testing import PLONE_APP_DISCUSSION_ROBOT_TESTING
from plone.app.testing import ROBOT_TEST_LEVEL from plone.app.testing import ROBOT_TEST_LEVEL
from plone.testing import layered from plone.testing import layered

View File

@ -2,8 +2,6 @@
"""Test plone.app.discussion workflow and permissions. """Test plone.app.discussion workflow and permissions.
""" """
from AccessControl import Unauthorized from AccessControl import Unauthorized
from Products.CMFCore.permissions import View
from Products.CMFCore.utils import _checkPermission as checkPerm
from plone.app.discussion.interfaces import IConversation from plone.app.discussion.interfaces import IConversation
from plone.app.discussion.interfaces import IDiscussionLayer from plone.app.discussion.interfaces import IDiscussionLayer
from plone.app.discussion.testing import PLONE_APP_DISCUSSION_INTEGRATION_TESTING # noqa from plone.app.discussion.testing import PLONE_APP_DISCUSSION_INTEGRATION_TESTING # noqa
@ -11,6 +9,8 @@ from plone.app.testing import login
from plone.app.testing import logout from plone.app.testing import logout
from plone.app.testing import setRoles from plone.app.testing import setRoles
from plone.app.testing import TEST_USER_ID from plone.app.testing import TEST_USER_ID
from Products.CMFCore.permissions import View
from Products.CMFCore.utils import _checkPermission as checkPerm
from zope.component import createObject from zope.component import createObject
from zope.interface import alsoProvides from zope.interface import alsoProvides
@ -87,7 +87,7 @@ class PermissionsSetupTest(unittest.TestCase):
plone.app.discussion assigns this permission to 'Authenticated' as plone.app.discussion assigns this permission to 'Authenticated' as
well to emulate the behavior of the old commenting system. well to emulate the behavior of the old commenting system.
""" """
ReplyToItemPerm = "Reply to item" ReplyToItemPerm = 'Reply to item'
# should be allowed as Member # should be allowed as Member
self.assertTrue(self.checkPermission(ReplyToItemPerm, self.portal)) self.assertTrue(self.checkPermission(ReplyToItemPerm, self.portal))
# should be allowed as Authenticated # should be allowed as Authenticated
@ -126,7 +126,7 @@ class CommentOneStateWorkflowTest(unittest.TestCase):
cid = conversation.addComment(comment) cid = conversation.addComment(comment)
self.comment = self.folder.doc1.restrictedTraverse( self.comment = self.folder.doc1.restrictedTraverse(
'++conversation++default/%s' % cid '++conversation++default/{0}'.format(cid)
) )
self.portal.acl_users._doAddUser('member', 'secret', ['Member'], []) self.portal.acl_users._doAddUser('member', 'secret', ['Member'], [])
@ -192,7 +192,8 @@ class CommentReviewWorkflowTest(unittest.TestCase):
comment.text = 'Comment text' comment.text = 'Comment text'
comment_id = conversation.addComment(comment) comment_id = conversation.addComment(comment)
comment = self.portal.doc1.restrictedTraverse( comment = self.portal.doc1.restrictedTraverse(
'++conversation++default/%s' % comment_id) '++conversation++default/{0}'.format(comment_id)
)
self.conversation = conversation self.conversation = conversation
self.comment_id = comment_id self.comment_id = comment_id

View File

@ -1,14 +1,15 @@
# -*- coding: utf-8 -*-
"""The portal_discussion tool, usually accessed via """The portal_discussion tool, usually accessed via
queryUtility(ICommentingTool). The default implementation delegates to the queryUtility(ICommentingTool). The default implementation delegates to the
standard portal_catalog for indexing comments. standard portal_catalog for indexing comments.
BBB support for the old portal_discussion is provided in the bbb package. BBB support for the old portal_discussion is provided in the bbb package.
""" """
from OFS.SimpleItem import SimpleItem
from Products.CMFCore.utils import UniqueObject
from Products.CMFCore.utils import getToolByName
from interfaces import IComment from interfaces import IComment
from interfaces import ICommentingTool from interfaces import ICommentingTool
from OFS.SimpleItem import SimpleItem
from Products.CMFCore.utils import getToolByName
from Products.CMFCore.utils import UniqueObject
from zope import interface from zope import interface
from zope.component import queryUtility from zope.component import queryUtility

View File

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from plone.app.discussion.interfaces import IDiscussionSettings from plone.app.discussion.interfaces import IDiscussionSettings
from plone.registry.interfaces import IRegistry from plone.registry.interfaces import IRegistry
from zope.component import getUtility from zope.component import getUtility