Nuke trailing white space

svn path=/plone.app.discussion/trunk/; revision=46366
This commit is contained in:
Maurits van Rees 2010-12-15 23:52:56 +00:00
parent aff8a3709c
commit a2a17085a3
38 changed files with 507 additions and 505 deletions

View File

@ -39,7 +39,7 @@ Captcha = factory(Captcha)
class CaptchaExtender(extensible.FormExtender): class CaptchaExtender(extensible.FormExtender):
"""Extends the comment form with a Captcha. This Captcha extender is only """Extends the comment form with a Captcha. This Captcha extender is only
registered when a plugin is installed that provides the registered when a plugin is installed that provides the
"plone.app.discussion-captcha" feature. "plone.app.discussion-captcha" feature.
""" """
adapts(Interface, IDefaultBrowserLayer, CommentForm) # context, request, form adapts(Interface, IDefaultBrowserLayer, CommentForm) # context, request, form
@ -72,4 +72,4 @@ class CaptchaExtender(extensible.FormExtender):
self.form.fields['captcha'].widgetFactory = NorobotsFieldWidget self.form.fields['captcha'].widgetFactory = NorobotsFieldWidget
else: else:
self.form.fields['captcha'].mode = interfaces.HIDDEN_MODE self.form.fields['captcha'].mode = interfaces.HIDDEN_MODE

View File

@ -1,15 +1,15 @@
<configure <configure
xmlns="http://namespaces.zope.org/zope" xmlns="http://namespaces.zope.org/zope"
xmlns:meta="http://namespaces.zope.org/meta" xmlns:meta="http://namespaces.zope.org/meta"
xmlns:zcml="http://namespaces.zope.org/zcml" xmlns:zcml="http://namespaces.zope.org/zcml"
i18n_domain="plone.app.discussion"> i18n_domain="plone.app.discussion">
<!-- <!--
Plone 3 fixes Plone 3 fixes
Plone 3 / Zope 2.10 does not recognize the meta:provides feature. Plone 3 / Zope 2.10 does not recognize the meta:provides feature.
Therefore we claim to provide this feature when a suitable package is Therefore we claim to provide this feature when a suitable package is
installed. installed.
--> -->
<configure zcml:condition="installed plone.formwidget.captcha"> <configure zcml:condition="installed plone.formwidget.captcha">
<meta:provides feature="plone.app.discussion-captcha" /> <meta:provides feature="plone.app.discussion-captcha" />
@ -21,11 +21,11 @@
<!-- Captcha comment form extender --> <!-- Captcha comment form extender -->
<configure zcml:condition="have plone.app.discussion-captcha"> <configure zcml:condition="have plone.app.discussion-captcha">
<!-- <!--
Register the Captcha form extender and validator only if there are Register the Captcha form extender and validator only if there are
plugins installed that declare to implement a Captcha solution for plugins installed that declare to implement a Captcha solution for
plone.app.discussion (e.g. plone.formwidget.captcha and plone.app.discussion (e.g. plone.formwidget.captcha and
plone.formwidget.recaptcha). plone.formwidget.recaptcha).
--> -->
<adapter <adapter
factory=".captcha.Captcha" factory=".captcha.Captcha"
@ -44,7 +44,7 @@
<adapter <adapter
factory="collective.akismet.validator.AkismetValidator" factory="collective.akismet.validator.AkismetValidator"
provides="z3c.form.interfaces.IValidator" provides="z3c.form.interfaces.IValidator"
/> />
</configure> </configure>
</configure> </configure>

View File

@ -5,15 +5,15 @@ from Products.Five.browser import BrowserView
class View(BrowserView): class View(BrowserView):
"""Comment View. """Comment View.
When the view of a comment object is called directly, redirect to the When the view of a comment object is called directly, redirect to the
the page (content object) and the location (HTML-anchor) where the comment the page (content object) and the location (HTML-anchor) where the comment
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
the comment has been posted. the comment has been posted.
@ -24,4 +24,4 @@ class View(BrowserView):
self.request.response.redirect( self.request.response.redirect(
aq_parent(aq_parent(context)).absolute_url() + aq_parent(aq_parent(context)).absolute_url() +
'#' + str(context.id) '#' + str(context.id)
) )

View File

@ -58,7 +58,7 @@
tal:attributes="src portrait_url; tal:attributes="src portrait_url;
alt reply/Creator" /> alt reply/Creator" />
</div> </div>
<div class="documentByLine" i18n:domain="plone.app.discussion"> <div class="documentByLine" i18n:domain="plone.app.discussion">
<tal:name> <tal:name>
<a href="" <a href=""
@ -72,16 +72,16 @@
<span tal:condition="not: reply/Creator">Anonymous</span> <span tal:condition="not: reply/Creator">Anonymous</span>
</tal:name> </tal:name>
<tal:posted i18n:translate="label_says">says:</tal:posted> <tal:posted i18n:translate="label_says">says:</tal:posted>
<div class="commentDate" <div class="commentDate"
tal:content="python:view.format_time(reply.modification_date)"> tal:content="python:view.format_time(reply.modification_date)">
8/23/2001 12:40:44 PM 8/23/2001 12:40:44 PM
</div> </div>
</div> </div>
<div class="commentBody"> <div class="commentBody">
<span tal:replace="structure python:view.cook(reply.getText())" /> <span tal:replace="structure python:view.cook(reply.getText())" />
<div class="commentActions"> <div class="commentActions">
<form name="delete" <form name="delete"
action="" action=""
@ -96,7 +96,7 @@
i18n:attributes="value label_delete;" i18n:attributes="value label_delete;"
/> />
</form> </form>
<!-- Workflow actions (e.g. 'publish') --> <!-- Workflow actions (e.g. 'publish') -->
<form name="" <form name=""
action="" action=""
@ -115,7 +115,7 @@
/> />
</form> </form>
</div> </div>
</div> </div>
<button class="context reply-to-comment-button hide allowMultiSubmit" <button class="context reply-to-comment-button hide allowMultiSubmit"

View File

@ -41,10 +41,10 @@ from plone.z3cform import z2
from plone.z3cform.widget import SingleCheckBoxWidget from plone.z3cform.widget import SingleCheckBoxWidget
from plone.z3cform.fieldsets import extensible from plone.z3cform.fieldsets import extensible
# starting from 0.6.0 version plone.z3cform has IWrappedForm interface # starting from 0.6.0 version plone.z3cform has IWrappedForm interface
try: try:
from plone.z3cform.interfaces import IWrappedForm from plone.z3cform.interfaces import IWrappedForm
HAS_WRAPPED_FORM = True HAS_WRAPPED_FORM = True
except ImportError: # pragma: no cover except ImportError: # pragma: no cover
HAS_WRAPPED_FORM = False HAS_WRAPPED_FORM = False
@ -87,48 +87,48 @@ 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"")
# Anonymous / Logged-in # Anonymous / Logged-in
portal_membership = getToolByName(self.context, 'portal_membership') portal_membership = getToolByName(self.context, 'portal_membership')
if not portal_membership.isAnonymousUser(): if not portal_membership.isAnonymousUser():
self.widgets['author_name'].mode = interfaces.HIDDEN_MODE self.widgets['author_name'].mode = interfaces.HIDDEN_MODE
self.widgets['author_email'].mode = interfaces.HIDDEN_MODE self.widgets['author_email'].mode = interfaces.HIDDEN_MODE
# Todo: Since we are not using the author_email field in the # Todo: Since we are not using the author_email field in the
# current state, we hide it by default. But we keep the field for # current state, we hide it by default. But we keep the field for
# integrators or later use. # integrators or later use.
self.widgets['author_email'].mode = interfaces.HIDDEN_MODE self.widgets['author_email'].mode = interfaces.HIDDEN_MODE
registry = queryUtility(IRegistry) registry = queryUtility(IRegistry)
settings = registry.forInterface(IDiscussionSettings, check=False) settings = registry.forInterface(IDiscussionSettings, check=False)
portal_membership = getToolByName(self.context, 'portal_membership') portal_membership = getToolByName(self.context, 'portal_membership')
if not settings.user_notification_enabled or portal_membership.isAnonymousUser(): if not settings.user_notification_enabled or portal_membership.isAnonymousUser():
self.widgets['user_notification'].mode = interfaces.HIDDEN_MODE self.widgets['user_notification'].mode = interfaces.HIDDEN_MODE
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)
wf = getToolByName(context, 'portal_workflow') wf = getToolByName(context, 'portal_workflow')
data, errors = self.extractData() data, errors = self.extractData()
if errors: if errors:
return return
text = u"" text = u""
author_name = u"" author_name = u""
author_email = u"" author_email = u""
user_notification = None user_notification = None
# Captcha check for anonymous users (if Captcha is enabled and # Captcha check for anonymous users (if Captcha is enabled and
# anonymous commenting is allowed) # anonymous commenting is allowed)
registry = queryUtility(IRegistry) registry = queryUtility(IRegistry)
settings = registry.forInterface(IDiscussionSettings, check=False) settings = registry.forInterface(IDiscussionSettings, check=False)
@ -138,10 +138,10 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
portal_membership.isAnonymousUser(): portal_membership.isAnonymousUser():
if not 'captcha' in data: if not 'captcha' in data:
data['captcha'] = u"" data['captcha'] = u""
captcha = CaptchaValidator(self.context, captcha = CaptchaValidator(self.context,
self.request, self.request,
None, None,
ICaptcha['captcha'], ICaptcha['captcha'],
None) None)
captcha.validate(data['captcha']) captcha.validate(data['captcha'])
@ -156,10 +156,10 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
author_email = data['author_email'] author_email = data['author_email']
if 'user_notification' in data: if 'user_notification' in data:
user_notification = data['user_notification'] user_notification = data['user_notification']
# The add-comment view is called on the conversation object # The add-comment view is called on the conversation object
conversation = IConversation(self.__parent__) conversation = IConversation(self.__parent__)
# Check if conversation is enabled on this content object # Check if conversation is enabled on this content object
if not conversation.enabled(): if not conversation.enabled():
raise Unauthorized, "Discussion is not enabled for this content\ raise Unauthorized, "Discussion is not enabled for this content\
@ -175,10 +175,10 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
comment.text = text comment.text = text
portal_membership = getToolByName(self.context, 'portal_membership') portal_membership = getToolByName(self.context, 'portal_membership')
can_reply = getSecurityManager().checkPermission('Reply to item', can_reply = getSecurityManager().checkPermission('Reply to item',
context) context)
if portal_membership.isAnonymousUser() and \ if portal_membership.isAnonymousUser() and \
settings.anonymous_comments: settings.anonymous_comments:
# Anonymous Users # Anonymous Users
@ -211,7 +211,7 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
"""Anonymous user tries to post a comment, but """Anonymous user tries to post a comment, but
anonymous commenting is disabled. Or user anonymous commenting is disabled. Or user
does not have the 'reply to item' permission.""" # pragma: no cover does not have the 'reply to item' permission.""" # pragma: no cover
# Check if the added comment is a reply to an existing comment # Check if the added comment is a reply to an existing comment
# or just a regular reply to the content object. # or just a regular reply to the content object.
if data['in_reply_to']: if data['in_reply_to']:
@ -221,11 +221,11 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
# Add a comment to the conversation # Add a comment to the conversation
comment_id = conversation.addComment(comment) comment_id = conversation.addComment(comment)
# If a user posts a comment and moderation is enabled, a message is # If a user posts a comment and moderation is enabled, a message is
# shown to the user that his/her comment awaits moderation. If the user # shown to the user that his/her comment awaits moderation. If the user
# has 'review comments' permission, he/she is redirected directly # has 'review comments' permission, he/she is redirected directly
# to the comment. # to the comment.
can_review = getSecurityManager().checkPermission('Review comments', can_review = getSecurityManager().checkPermission('Review comments',
context) context)
comment_review_state = wf.getInfoFor(comment, 'review_state') comment_review_state = wf.getInfoFor(comment, 'review_state')
if comment_review_state == 'pending' and not can_review: if comment_review_state == 'pending' and not can_review:
@ -254,8 +254,8 @@ class CommentsViewlet(ViewletBase):
super(CommentsViewlet, self).update() super(CommentsViewlet, self).update()
z2.switch_on(self, request_layer=IFormLayer) z2.switch_on(self, request_layer=IFormLayer)
self.form = self.form(aq_inner(self.context), self.request) self.form = self.form(aq_inner(self.context), self.request)
if HAS_WRAPPED_FORM: if HAS_WRAPPED_FORM:
alsoProvides(self.form, IWrappedForm) alsoProvides(self.form, IWrappedForm)
self.form.update() self.form.update()
# view methods # view methods
@ -265,16 +265,16 @@ class CommentsViewlet(ViewletBase):
targetMimetype = 'text/html' targetMimetype = 'text/html'
registry = queryUtility(IRegistry) registry = queryUtility(IRegistry)
settings = registry.forInterface(IDiscussionSettings, check=False) settings = registry.forInterface(IDiscussionSettings, check=False)
mimetype = settings.text_transform mimetype = settings.text_transform
return transforms.convertTo(targetMimetype, return transforms.convertTo(targetMimetype,
text, text,
context=self, context=self,
mimetype=mimetype).getData() mimetype=mimetype).getData()
def can_reply(self): def can_reply(self):
"""Returns true if current user has the 'Reply to item' permission. """Returns true if current user has the 'Reply to item' permission.
""" """
return getSecurityManager().checkPermission('Reply to item', return getSecurityManager().checkPermission('Reply to item',
aq_inner(self.context)) aq_inner(self.context))
def can_manage(self): def can_manage(self):
@ -282,13 +282,13 @@ class CommentsViewlet(ViewletBase):
not want any API changes in beta releases. not want any API changes in beta releases.
""" """
return self.can_review() return self.can_review()
def can_review(self): def can_review(self):
"""Returns true if current user has the 'Review comments' permission. """Returns true if current user has the 'Review comments' permission.
""" """
return getSecurityManager().checkPermission('Review comments', return getSecurityManager().checkPermission('Review comments',
aq_inner(self.context)) aq_inner(self.context))
def is_discussion_allowed(self): def is_discussion_allowed(self):
context = aq_inner(self.context) context = aq_inner(self.context)
conversation = IConversation(context) conversation = IConversation(context)
@ -301,7 +301,7 @@ class CommentsViewlet(ViewletBase):
""" """
registry = queryUtility(IRegistry) registry = queryUtility(IRegistry)
settings = registry.forInterface(IDiscussionSettings, check=False) settings = registry.forInterface(IDiscussionSettings, check=False)
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))
else: else:
@ -356,7 +356,7 @@ class CommentsViewlet(ViewletBase):
r = r.copy() r = r.copy()
r['workflow_status'] = workflow_status r['workflow_status'] = workflow_status
yield r yield r
# Return all direct replies # Return all direct replies
if conversation.total_comments > 0: if conversation.total_comments > 0:
if workflow_actions: if workflow_actions:
@ -395,11 +395,11 @@ class CommentsViewlet(ViewletBase):
return settings.show_commenter_image return settings.show_commenter_image
def is_anonymous(self): def is_anonymous(self):
portal_membership = getToolByName(self.context, portal_membership = getToolByName(self.context,
'portal_membership', 'portal_membership',
None) None)
return portal_membership.isAnonymousUser() return portal_membership.isAnonymousUser()
def login_action(self): def login_action(self):
return '%s/login_form?came_from=%s' % \ return '%s/login_form?came_from=%s' % \
(self.navigation_root_url, (self.navigation_root_url,

View File

@ -70,7 +70,7 @@
class=".moderation.ModerateCommentsEnabled" class=".moderation.ModerateCommentsEnabled"
permission="zope2.View" permission="zope2.View"
/> />
<!-- Delete comment view --> <!-- Delete comment view -->
<browser:page <browser:page
for="plone.app.discussion.interfaces.IComment" for="plone.app.discussion.interfaces.IComment"
@ -131,4 +131,4 @@
permission="cmf.ManagePortal" permission="cmf.ManagePortal"
/> />
</configure> </configure>

View File

@ -5,7 +5,7 @@
lang="en" lang="en"
metal:use-macro="here/prefs_main_template/macros/master" metal:use-macro="here/prefs_main_template/macros/master"
i18n:domain="plone"> i18n:domain="plone">
<metal:block fill-slot="top_slot" <metal:block fill-slot="top_slot"
tal:define="dummy python:request.set('disable_border',1)" /> tal:define="dummy python:request.set('disable_border',1)" />
@ -17,14 +17,14 @@
<body> <body>
<div id="content" <div id="content"
tal:attributes="class view/settings" tal:attributes="class view/settings"
metal:fill-slot="prefs_configlet_content"> metal:fill-slot="prefs_configlet_content">
<script type="text/javascript" <script type="text/javascript"
tal:attributes="src string:${context/portal_url}/++resource++plone.app.discussion.javascripts/controlpanel.js"> tal:attributes="src string:${context/portal_url}/++resource++plone.app.discussion.javascripts/controlpanel.js">
</script> </script>
<dl class="portalMessage warning" <dl class="portalMessage warning"
tal:condition="view/mailhost_warning"> tal:condition="view/mailhost_warning">
<dt i18n:translate=""> <dt i18n:translate="">
@ -63,24 +63,24 @@
to choose a workflow for the 'Discussion Item' type. to choose a workflow for the 'Discussion Item' type.
</dd> </dd>
</dl> </dl>
<div metal:use-macro="context/global_statusmessage/macros/portal_message"> <div metal:use-macro="context/global_statusmessage/macros/portal_message">
Portal status message Portal status message
</div> </div>
<a href="" <a href=""
id="setup-link" id="setup-link"
tal:attributes="href string:$portal_url/plone_control_panel" tal:attributes="href string:$portal_url/plone_control_panel"
i18n:translate=""> i18n:translate="">
Site Setup Site Setup
</a> &rsaquo; </a> &rsaquo;
<h1 class="documentFirstHeading" tal:content="view/label">View Title</h1> <h1 class="documentFirstHeading" tal:content="view/label">View Title</h1>
<div id="layout-contents"> <div id="layout-contents">
<span tal:replace="structure view/contents" /> <span tal:replace="structure view/contents" />
</div> </div>
</div> </div>
</body> </body>
</html> </html>

View File

@ -21,7 +21,7 @@ from zope.component import getMultiAdapter, queryUtility
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 plone.app.discussion.interfaces import IDiscussionSettings, _ from plone.app.discussion.interfaces import IDiscussionSettings, _
@ -33,7 +33,7 @@ class DiscussionSettingsEditForm(controlpanel.RegistryEditForm):
description = _(u"help_discussion_settings_editform", description = _(u"help_discussion_settings_editform",
default=u"Some discussion related settings are not located " default=u"Some discussion related settings are not located "
"in the Discussion Control Panel.\n" "in the Discussion Control Panel.\n"
"To enable comments for a specific content type, " "To enable comments for a specific content type, "
"go to the Types Control Panel of this type and " "go to the Types Control Panel of this type and "
"choose \"Allow comments\".\n" "choose \"Allow comments\".\n"
"To enable the moderation workflow for comments, " "To enable the moderation workflow for comments, "
@ -73,18 +73,18 @@ class DiscussionSettingsEditForm(controlpanel.RegistryEditForm):
self.status = self.formErrorsMessage self.status = self.formErrorsMessage
return return
changes = self.applyChanges(data) changes = 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-settings") self.context.REQUEST.RESPONSE.redirect("@@discussion-settings")
@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("%s/%s" % (self.context.absolute_url(),
self.control_panel_view)) self.control_panel_view))
class DiscussionSettingsControlPanel(controlpanel.ControlPanelFormWrapper): class DiscussionSettingsControlPanel(controlpanel.ControlPanelFormWrapper):
"""Discussion settings control panel. """Discussion settings control panel.
""" """
@ -100,22 +100,22 @@ class DiscussionSettingsControlPanel(controlpanel.ControlPanelFormWrapper):
wftool = getToolByName(self.context, "portal_workflow", None) wftool = getToolByName(self.context, "portal_workflow", None)
wf = wftool.getChainForPortalType('Discussion Item') wf = 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
if 'one_state_workflow' not in wf and \ if 'one_state_workflow' not in wf and \
'comment_review_workflow' not in wf: 'comment_review_workflow' not in wf:
output.append("moderation_custom") output.append("moderation_custom")
elif settings.moderation_enabled: elif settings.moderation_enabled:
output.append("moderation_enabled") output.append("moderation_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')
@ -127,7 +127,7 @@ class DiscussionSettingsControlPanel(controlpanel.ControlPanelFormWrapper):
discussion_workflow = wftool.getChainForPortalType('Discussion Item')[0] discussion_workflow = wftool.getChainForPortalType('Discussion Item')[0]
if discussion_workflow: if discussion_workflow:
output.append(discussion_workflow) output.append(discussion_workflow)
# Merge all settings into one string # Merge all settings into one string
return ' '.join(output) return ' '.join(output)
@ -152,27 +152,27 @@ class DiscussionSettingsControlPanel(controlpanel.ControlPanelFormWrapper):
if 'one_state_workflow' in wf or 'comment_review_workflow' in wf: if 'one_state_workflow' in wf or 'comment_review_workflow' in wf:
return return
return True return True
def notify_configuration_changed(event): def notify_configuration_changed(event):
"""Event subscriber that is called every time the configuration changed. """Event subscriber that is called every time the configuration changed.
""" """
portal = getSite() portal = getSite()
wftool = getToolByName(portal, 'portal_workflow', None) wftool = getToolByName(portal, 'portal_workflow', None)
if IRecordModifiedEvent.providedBy(event): if IRecordModifiedEvent.providedBy(event):
# Discussion control panel setting changed # Discussion control panel setting changed
if event.record.fieldName == 'moderation_enabled': if event.record.fieldName == 'moderation_enabled':
# Moderation enabled has changed # Moderation enabled has changed
if event.record.value == True: if event.record.value == True:
# Enable moderation workflow # Enable moderation workflow
wftool.setChainForPortalTypes(('Discussion Item',), wftool.setChainForPortalTypes(('Discussion Item',),
'comment_review_workflow') 'comment_review_workflow')
else: else:
# Disable moderation workflow # Disable moderation workflow
wftool.setChainForPortalTypes(('Discussion Item',), wftool.setChainForPortalTypes(('Discussion Item',),
'one_state_workflow') 'one_state_workflow')
if IConfigurationChangedEvent.providedBy(event): if IConfigurationChangedEvent.providedBy(event):
# Types control panel setting changed # Types control panel setting changed
if 'workflow' in event.data: if 'workflow' in event.data:

View File

@ -25,22 +25,22 @@ class View(BrowserView):
out = [] out = []
self.total_comments_migrated = 0 self.total_comments_migrated = 0
self.total_comments_deleted = 0 self.total_comments_deleted = 0
dry_run = self.request.has_key("dry_run") dry_run = self.request.has_key("dry_run")
# 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 = self.request.has_key("test") test = self.request.has_key("test")
if not test: if not test:
transaction.begin() # pragma: no cover transaction.begin() # pragma: no cover
catalog = getToolByName(context, 'portal_catalog') catalog = getToolByName(context, 'portal_catalog')
def log(msg): def log(msg):
# encode string before sending it to external world # encode string before sending it to external world
if isinstance(msg, unicode): if isinstance(msg, unicode):
msg = msg.encode('utf-8') # pragma: no cover msg = msg.encode('utf-8') # pragma: no cover
context.plone_log(msg) context.plone_log(msg)
out.append(msg) out.append(msg)
@ -85,9 +85,9 @@ class View(BrowserView):
# migrate all talkbacks of the reply # migrate all talkbacks of the reply
talkback = getattr( reply, 'talkback', None ) talkback = getattr( reply, 'talkback', None )
no_replies_left = migrate_replies(context, no_replies_left = migrate_replies(context,
new_in_reply_to, new_in_reply_to,
talkback.getReplies(), talkback.getReplies(),
depth=depth+1) depth=depth+1)
if no_replies_left: if no_replies_left:
# remove reply and talkback # remove reply and talkback
@ -97,7 +97,7 @@ class View(BrowserView):
log("%sremove %s" % (indent, reply.id)) log("%sremove %s" % (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
# migrated. # migrated.
return True return True
@ -113,14 +113,14 @@ class View(BrowserView):
count_comments_old = len(catalog.searchResults( count_comments_old = len(catalog.searchResults(
object_provides=IDiscussionResponse.\ object_provides=IDiscussionResponse.\
__identifier__)) __identifier__))
log("Found %s Discussion Item objects." % count_discussion_items) log("Found %s Discussion Item objects." % count_discussion_items)
log("Found %s old discussion items." % count_comments_old) log("Found %s old discussion items." % count_comments_old)
log("Found %s plone.app.discussion comments." % count_comments_pad) log("Found %s plone.app.discussion comments." % 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
# zcatalog would support NOT expressions. # zcatalog would support NOT expressions.
@ -138,7 +138,7 @@ class View(BrowserView):
if replies: if replies:
conversation = IConversation(obj) conversation = IConversation(obj)
log("\n") log("\n")
log("Migrate '%s' (%s)" % (obj.Title(), log("Migrate '%s' (%s)" % (obj.Title(),
obj.absolute_url(relative=1))) obj.absolute_url(relative=1)))
migrate_replies(context, 0, replies) migrate_replies(context, 0, replies)
obj = aq_parent(talkback) obj = aq_parent(talkback)
@ -152,23 +152,23 @@ class View(BrowserView):
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("%s of %s comments migrated."
% (self.total_comments_migrated, count_comments_old)) % (self.total_comments_migrated, count_comments_old))
if self.total_comments_migrated != count_comments_old: if self.total_comments_migrated != count_comments_old:
log("%s comments could not be migrated." log("%s comments could not be migrated."
% (count_comments_old - self.total_comments_migrated)) # pragma: no cover % (count_comments_old - self.total_comments_migrated)) # pragma: no cover
log("Please make sure your portal catalog is up-to-date.") # pragma: no cover log("Please make sure your 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

@ -33,11 +33,11 @@
<a i18n:name="enable_comment_workflow" <a i18n:name="enable_comment_workflow"
i18n:translate="message_enable_comment_workflow" href="" i18n:translate="message_enable_comment_workflow" href=""
tal:attributes="href string:${context/portal_url}/@@types-controlpanel?type_id=Discussion Item"> tal:attributes="href string:${context/portal_url}/@@types-controlpanel?type_id=Discussion Item">
enable the 'Comment Review Workflow' for the Comment content enable the 'Comment Review Workflow' for the Comment content
type</a> before you can moderate comments here. type</a> before you can moderate comments here.
</dd> </dd>
</dl> </dl>
<form tal:condition="not:items"> <form tal:condition="not:items">
<fieldset id="fieldset-moderate-comments" class="formPanel"> <fieldset id="fieldset-moderate-comments" class="formPanel">
<p id="no-comments-message" i18n:translate="message_nothing_to_moderate"> <p id="no-comments-message" i18n:translate="message_nothing_to_moderate">
@ -114,7 +114,7 @@
<a href="" <a href=""
tal:attributes="href string:${item/getURL}/getText" tal:attributes="href string:${item/getURL}/getText"
tal:condition="python:item.Description.endswith('[...]')" tal:condition="python:item.Description.endswith('[...]')"
i18n:translate="label_show_full_comment_text" i18n:translate="label_show_full_comment_text"
class="show-full-comment-text">show full comment text</a> class="show-full-comment-text">show full comment text</a>
</td> </td>
<td class="actions"> <td class="actions">

View File

@ -11,14 +11,14 @@ from Products.statusmessages.interfaces import IStatusMessage
from plone.app.discussion.interfaces import _ from plone.app.discussion.interfaces import _
from plone.app.discussion.interfaces import IComment from plone.app.discussion.interfaces import IComment
# Begin ugly hack. It works around a ContentProviderLookupError: # Begin ugly hack. It works around a ContentProviderLookupError:
# plone.htmlhead error caused by Zope 2 permissions. # plone.htmlhead error caused by Zope 2 permissions.
# This error occured on Plone 3.3.x only! # This error occured on Plone 3.3.x only!
# #
# Source: # Source:
# http://athenageek.wordpress.com/2008/01/08/ # http://athenageek.wordpress.com/2008/01/08/
# contentproviderlookuperror-plonehtmlhead/ # contentproviderlookuperror-plonehtmlhead/
# #
# Bug report: https://bugs.launchpad.net/zope2/+bug/176566 # Bug report: https://bugs.launchpad.net/zope2/+bug/176566
# #
@ -27,7 +27,7 @@ def _getContext(self): # pragma: no cover
while getattr(self, '_is_wrapperish', None): while getattr(self, '_is_wrapperish', None):
self = self.aq_parent self = self.aq_parent
return self return self
ZopeTwoPageTemplateFile._getContext = _getContext # pragma: no cover ZopeTwoPageTemplateFile._getContext = _getContext # pragma: no cover
# End ugly hack. # End ugly hack.
@ -61,7 +61,7 @@ class View(BrowserView):
return text return text
def moderation_enabled(self): def moderation_enabled(self):
"""Returns true if a 'review workflow' is enabled on 'Discussion Item' """Returns true if a 'review workflow' is enabled on 'Discussion Item'
content type. A 'review workflow' is characterized by implementing content type. A 'review workflow' is characterized by implementing
a 'pending' workflow state. a 'pending' workflow state.
""" """
@ -78,7 +78,7 @@ class View(BrowserView):
class ModerateCommentsEnabled(BrowserView): class ModerateCommentsEnabled(BrowserView):
def __call__(self): def __call__(self):
"""Returns true if a 'review workflow' is enabled on 'Discussion Item' """Returns true if a 'review workflow' is enabled on 'Discussion Item'
content type. A 'review workflow' is characterized by implementing content type. A 'review workflow' is characterized by implementing
a 'pending' workflow state. a 'pending' workflow state.
""" """
@ -91,26 +91,26 @@ class ModerateCommentsEnabled(BrowserView):
else: else:
return return
class DeleteComment(BrowserView): class DeleteComment(BrowserView):
"""Delete a comment from a conversation. """Delete a comment from a conversation.
This view is always called directly on the comment object: This view is always called directly on the comment object:
http://nohost/front-page/++conversation++default/1286289644723317/\ http://nohost/front-page/++conversation++default/1286289644723317/\
@@moderate-delete-comment @@moderate-delete-comment
Each table row (comment) in the moderation view contains a hidden input Each table row (comment) in the moderation view contains a hidden input
field with the absolute URL of the content object: field with the absolute URL of the content object:
<input type="hidden" <input type="hidden"
value="http://nohost/front-page/++conversation++default/\ value="http://nohost/front-page/++conversation++default/\
1286289644723317" 1286289644723317"
name="selected_obj_paths:list"> name="selected_obj_paths:list">
This absolute URL is called from a jQuery method that is bind to the This absolute URL is called from a jQuery method that is bind to the
'delete' button of the table row. See javascripts/moderation.js for more 'delete' button of the table row. See javascripts/moderation.js for more
details. details.
""" """
def __call__(self): def __call__(self):
@ -132,23 +132,23 @@ class DeleteComment(BrowserView):
class PublishComment(BrowserView): class PublishComment(BrowserView):
"""Publish a comment. """Publish a comment.
This view is always called directly on the comment object: This view is always called directly on the comment object:
http://nohost/front-page/++conversation++default/1286289644723317/\ http://nohost/front-page/++conversation++default/1286289644723317/\
@@moderate-publish-comment @@moderate-publish-comment
Each table row (comment) in the moderation view contains a hidden input Each table row (comment) in the moderation view contains a hidden input
field with the absolute URL of the content object: field with the absolute URL of the content object:
<input type="hidden" <input type="hidden"
value="http://nohost/front-page/++conversation++default/\ value="http://nohost/front-page/++conversation++default/\
1286289644723317" 1286289644723317"
name="selected_obj_paths:list"> name="selected_obj_paths:list">
This absolute URL is called from a jQuery method that is bind to the This absolute URL is called from a jQuery method that is bind to the
'delete' button of the table row. See javascripts/moderation.js for more 'delete' button of the table row. See javascripts/moderation.js for more
details. details.
""" """
def __call__(self): def __call__(self):
@ -171,24 +171,24 @@ class PublishComment(BrowserView):
class BulkActionsView(BrowserView): class BulkActionsView(BrowserView):
"""Bulk actions (unapprove, approve, delete, mark as spam). """Bulk actions (unapprove, approve, delete, mark as spam).
Each table row of the moderation view has a checkbox with the absolute Each table row of the moderation view has a checkbox with the absolute
path (without host and port) of the comment objects: path (without host and port) of the comment objects:
<input type="checkbox" <input type="checkbox"
name="paths:list" name="paths:list"
value="/plone/front-page/++conversation++default/\ value="/plone/front-page/++conversation++default/\
1286289644723317" 1286289644723317"
id="cb_1286289644723317" /> id="cb_1286289644723317" />
If checked, the comment path will occur in the 'paths' variable of If checked, the comment path will occur in the 'paths' variable of
the request when the bulk actions view is called. The bulk action the request when the bulk actions view is called. The bulk action
(delete, publish, etc.) will be applied to all comments that are (delete, publish, etc.) will be applied to all comments that are
included. included.
The paths have to be 'traversable': The paths have to be 'traversable':
/plone/front-page/++conversation++default/1286289644723317 /plone/front-page/++conversation++default/1286289644723317
""" """
@ -219,11 +219,11 @@ class BulkActionsView(BrowserView):
def publish(self): def publish(self):
"""Publishes all comments in the paths variable. """Publishes all comments in the paths variable.
Expects a list of absolute paths (without host and port): Expects a list of absolute paths (without host and port):
/Plone/startseite/++conversation++default/1286200010610352 /Plone/startseite/++conversation++default/1286200010610352
""" """
context = aq_inner(self.context) context = aq_inner(self.context)
for path in self.paths: for path in self.paths:
@ -240,12 +240,12 @@ class BulkActionsView(BrowserView):
def delete(self): def delete(self):
"""Deletes all comments in the paths variable. """Deletes all comments in the paths variable.
Expects a list of absolute paths (without host and port): Expects a list of absolute paths (without host and port):
/Plone/startseite/++conversation++default/1286200010610352 /Plone/startseite/++conversation++default/1286200010610352
""" """
context = aq_inner(self.context) context = aq_inner(self.context)
for path in self.paths: for path in self.paths:
comment = context.restrictedTraverse(path) comment = context.restrictedTraverse(path)

View File

@ -18,21 +18,21 @@ class ConversationNamespace(object):
(unnamed) adapter. This is to work around a bug in OFS.Traversable which (unnamed) adapter. This is to work around a bug in OFS.Traversable which
does not allow traversal to namespaces with an empty string name. does not allow traversal to namespaces with an empty string name.
""" """
implements(ITraversable) implements(ITraversable)
adapts(Interface, IBrowserRequest) adapts(Interface, IBrowserRequest)
def __init__(self, context, request=None): def __init__(self, context, request=None):
self.context = context self.context = context
self.request = request self.request = request
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:
raise TraversalError(name) # pragma: no cover raise TraversalError(name) # pragma: no cover
return conversation return conversation

View File

@ -49,7 +49,7 @@ class CaptchaValidator(validator.SimpleFieldValidator):
settings = registry.forInterface(IDiscussionSettings, check=False) settings = registry.forInterface(IDiscussionSettings, check=False)
if settings.captcha in ('captcha', 'recaptcha', 'norobots'): if settings.captcha in ('captcha', 'recaptcha', 'norobots'):
captcha = getMultiAdapter((aq_inner(self.context), self.request), captcha = getMultiAdapter((aq_inner(self.context), self.request),
name=settings.captcha) name=settings.captcha)
if not captcha.verify(input=value): if not captcha.verify(input=value):
if settings.captcha == 'norobots': if settings.captcha == 'norobots':
@ -61,6 +61,6 @@ class CaptchaValidator(validator.SimpleFieldValidator):
# Register Captcha validator for the Captcha field in the ICaptcha Form # Register Captcha validator for the Captcha field in the ICaptcha Form
validator.WidgetValidatorDiscriminators(CaptchaValidator, validator.WidgetValidatorDiscriminators(CaptchaValidator,
field=ICaptcha['captcha']) field=ICaptcha['captcha'])

View File

@ -54,7 +54,7 @@ except: # pragma: no cover
COMMENT_TITLE = _(u"comment_title", COMMENT_TITLE = _(u"comment_title",
default=u"${creator} on ${content}") default=u"${creator} on ${content}")
MAIL_NOTIFICATION_MESSAGE = _(u"mail_notification_message", MAIL_NOTIFICATION_MESSAGE = _(u"mail_notification_message",
default=u"A comment on '${title}' " default=u"A comment on '${title}' "
"has been posted here: ${link}") "has been posted here: ${link}")
@ -95,7 +95,7 @@ class Comment(CatalogAware, WorkflowAware, DynamicType, Traversable,
author_name = None author_name = None
author_email = None author_email = None
user_notification = None user_notification = None
# Note: we want to use zope.component.createObject() to instantiate # Note: we want to use zope.component.createObject() to instantiate
@ -127,13 +127,13 @@ class Comment(CatalogAware, WorkflowAware, DynamicType, Traversable,
"""The title of the comment. """The title of the comment.
""" """
if not self.creator: if not self.creator:
creator = translate(Message(_(u"label_anonymous", creator = translate(Message(_(u"label_anonymous",
default=u"Anonymous"))) default=u"Anonymous")))
else: else:
creator = self.creator creator = self.creator
creator = creator creator = creator
# Fetch the content object (the parent of the comment is the # Fetch the content object (the parent of the comment is the
# conversation, the parent of the conversation is the content object). # conversation, the parent of the conversation is the content object).
content = aq_base(self.__parent__.__parent__) content = aq_base(self.__parent__.__parent__)
title = translate( title = translate(
@ -141,7 +141,7 @@ class Comment(CatalogAware, WorkflowAware, DynamicType, Traversable,
mapping={'creator': creator, mapping={'creator': creator,
'content': safe_unicode(content.Title())})) 'content': safe_unicode(content.Title())}))
return title return title
def Creator(self): def Creator(self):
"""The name of the person who wrote the comment. """The name of the person who wrote the comment.
""" """
@ -176,8 +176,8 @@ def notify_content_object(obj, event):
"""Tell the content object when a comment is added """Tell the content object when a comment is added
""" """
content_obj = aq_parent(aq_parent(obj)) content_obj = aq_parent(aq_parent(obj))
content_obj.reindexObject(idxs=('total_comments', content_obj.reindexObject(idxs=('total_comments',
'last_comment_date', 'last_comment_date',
'commentators',)) 'commentators',))
def notify_content_object_deleted(obj, event): def notify_content_object_deleted(obj, event):
@ -189,14 +189,14 @@ def notify_content_object_deleted(obj, event):
for comment in conversation.getComments(): for comment in conversation.getComments():
del conversation[comment.id] del conversation[comment.id]
def notify_user(obj, event): def notify_user(obj, event):
"""Tell users when a comment has been added. """Tell users when a comment has been added.
This method composes and sends emails to all users that have added a This method composes and sends emails to all users that have added a
comment to this conversation and enabled user notification. comment to this conversation and enabled user notification.
This requires the user_notification setting to be enabled in the This requires the user_notification setting to be enabled in the
discussion control panel. discussion control panel.
""" """
# Check if user notification is enabled # Check if user notification is enabled
@ -268,36 +268,36 @@ def notify_user(obj, event):
def notify_moderator(obj, event): def notify_moderator(obj, event):
"""Tell the moderator when a comment needs attention. """Tell the moderator when a comment needs attention.
This method sends an email to the site admin (mail control panel setting) This method sends an email to the site admin (mail control panel setting)
if comment moderation is enabled and a new comment has been added that if comment moderation is enabled and a new comment has been added that
needs to be approved. needs to be approved.
This requires the moderator_notification to be enabled in the discussion This requires the moderator_notification to be enabled in the discussion
control panel and the comment_review_workflow enabled for the comment control panel and the comment_review_workflow enabled for the comment
content type. content type.
""" """
# Check if moderator notification is enabled # Check if moderator notification is enabled
registry = queryUtility(IRegistry) registry = queryUtility(IRegistry)
settings = registry.forInterface(IDiscussionSettings, check=False) settings = registry.forInterface(IDiscussionSettings, check=False)
if not settings.moderator_notification_enabled: if not settings.moderator_notification_enabled:
return return
# Check if the current workflow implements a pending state. # Check if the current workflow implements a pending state.
wf_tool = getToolByName(obj, 'portal_workflow') wf_tool = getToolByName(obj, 'portal_workflow')
comment_workflow = wf_tool.getChainForPortalType('Discussion Item')[0] comment_workflow = wf_tool.getChainForPortalType('Discussion Item')[0]
comment_workflow = wf_tool[comment_workflow] comment_workflow = wf_tool[comment_workflow]
if 'pending' not in comment_workflow.states: if 'pending' not in comment_workflow.states:
return return
# Get informations that are necessary to send an email # Get informations that are necessary to send an email
mail_host = getToolByName(obj, 'MailHost') mail_host = getToolByName(obj, 'MailHost')
portal_url = getToolByName(obj, 'portal_url') portal_url = getToolByName(obj, 'portal_url')
portal = portal_url.getPortalObject() portal = portal_url.getPortalObject()
sender = portal.getProperty('email_from_address') sender = portal.getProperty('email_from_address')
mto = portal.getProperty('email_from_address') mto = portal.getProperty('email_from_address')
# Check if a sender address is available # Check if a sender address is available
if not sender: if not sender:
return return
@ -305,7 +305,7 @@ def notify_moderator(obj, event):
conversation = aq_parent(obj) conversation = aq_parent(obj)
content_object = aq_parent(conversation) content_object = aq_parent(conversation)
# Compose email # Compose email
#comment = conversation.getComments().next() #comment = conversation.getComments().next()
subject = translate(_(u"A comment has been posted."), context=obj.REQUEST) subject = translate(_(u"A comment has been posted."), context=obj.REQUEST)
message = translate(Message(MAIL_NOTIFICATION_MESSAGE, message = translate(Message(MAIL_NOTIFICATION_MESSAGE,
@ -327,10 +327,10 @@ def notify_moderator(obj, event):
message) message)
else: # pragma: no cover else: # pragma: no cover
try: try:
mail_host.secureSend(message, mail_host.secureSend(message,
mto, mto,
sender, sender,
subject=subject, subject=subject,
charset='utf-8') charset='utf-8')
except SMTPException, e: except SMTPException, e:
logger.error('SMTP exception (%s) while trying to send an ' + logger.error('SMTP exception (%s) while trying to send an ' +

View File

@ -10,11 +10,11 @@
<five:registerPackage package="." /> <five:registerPackage package="." />
<include package="plone.indexer" /> <include package="plone.indexer" />
<!-- XXX: Excluding plone.app.uuid for Plone 3 is only a temporary 'fix'. <!-- XXX: Excluding plone.app.uuid for Plone 3 is only a temporary 'fix'.
plone.app.uuid causes the IObjectAddedEvent to be fired twice on plone.app.uuid causes the IObjectAddedEvent to be fired twice on
Plone 3. This causes the email notifications to be send twice. Plone 3. This causes the email notifications to be send twice.
This means that https://dev.plone.org/plone/ticket/10652 is fixed only This means that https://dev.plone.org/plone/ticket/10652 is fixed only
for Plone 4. for Plone 4.
--> -->
@ -29,7 +29,7 @@
<include package=".browser" /> <include package=".browser" />
<i18n:registerTranslations directory="locales" /> <i18n:registerTranslations directory="locales" />
<!-- Register the installation GenericSetup extension profile --> <!-- Register the installation GenericSetup extension profile -->
<genericsetup:registerProfile <genericsetup:registerProfile
name="default" name="default"
@ -59,7 +59,7 @@
<require attributes="Title Creator getId getText" permission="zope2.View" /> <require attributes="Title Creator getId getText" permission="zope2.View" />
</class> </class>
<!-- XXX: Excluding plone.uuid for Plone 3 is only a temporary 'fix'. <!-- XXX: Excluding plone.uuid for Plone 3 is only a temporary 'fix'.
See the comment above --> See the comment above -->
<configure zcml:condition="have plone-4"> <configure zcml:condition="have plone-4">

View File

@ -484,6 +484,6 @@ class CommentReplies(ConversationReplies):
comment.in_reply_to = self.comment_id comment.in_reply_to = self.comment_id
return self.conversation.addComment(comment) return self.conversation.addComment(comment)
# Dict API is inherited, written in terms of self.conversation and # Dict API is inherited, written in terms of self.conversation and
# self.children # self.children

View File

@ -31,7 +31,7 @@ class IDiscussionSettings(Interface):
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"),
@ -43,7 +43,7 @@ class IDiscussionSettings(Interface):
required=False, required=False,
default=False, default=False,
) )
moderation_enabled = schema.Bool( moderation_enabled = schema.Bool(
title=_(u"label_moderation_enabled", title=_(u"label_moderation_enabled",
default="Enable comment moderation"), default="Enable comment moderation"),
@ -70,7 +70,7 @@ class IDiscussionSettings(Interface):
"'Intelligent text' converts plain text into HTML " + "'Intelligent text' converts plain text into HTML " +
"where line breaks and indentation is preserved, " + "where line breaks and indentation is preserved, " +
"and web and email addresses are made into " + "and web and email addresses are made into " +
"clickable links."), "clickable links."),
required=True, required=True,
default='text/plain', default='text/plain',
vocabulary='plone.app.discussion.vocabularies.TextTransformVocabulary', vocabulary='plone.app.discussion.vocabularies.TextTransformVocabulary',
@ -100,7 +100,7 @@ class IDiscussionSettings(Interface):
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"),

View File

@ -12,18 +12,18 @@
zope.lifecycleevent.interfaces.IObjectAddedEvent" zope.lifecycleevent.interfaces.IObjectAddedEvent"
handler=".comment.notify_user" handler=".comment.notify_user"
/> />
<subscriber <subscriber
for="plone.app.discussion.interfaces.IComment for="plone.app.discussion.interfaces.IComment
zope.lifecycleevent.interfaces.IObjectAddedEvent" zope.lifecycleevent.interfaces.IObjectAddedEvent"
handler=".comment.notify_moderator" handler=".comment.notify_moderator"
/> />
</configure> </configure>
<!-- Plone 4 Event Subscribers --> <!-- Plone 4 Event Subscribers -->
<configure zcml:condition="installed zope.app.container"> <configure zcml:condition="installed zope.app.container">
<subscriber <subscriber

View File

@ -15,7 +15,7 @@ def patchedClearFindAndRebuild(self):
with an indexObject method), and reindexes them. with an indexObject method), and reindexes them.
This may take a long time. This may take a long time.
""" """
def indexObject(obj, path): def indexObject(obj, path):
if (base_hasattr(obj, 'indexObject') and if (base_hasattr(obj, 'indexObject') and

View File

@ -1,7 +1,7 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<componentregistry> <componentregistry>
<utilities> <utilities>
<utility <utility
interface="plone.app.discussion.interfaces.ICommentingTool" interface="plone.app.discussion.interfaces.ICommentingTool"
object="portal_discussion" object="portal_discussion"
/> />

View File

@ -79,7 +79,7 @@
<guard> <guard>
</guard> </guard>
</variable> </variable>
<variable variable_id="actor" for_catalog="False" <variable variable_id="actor" for_catalog="False"
for_status="True" update_always="True"> for_status="True" update_always="True">
<description i18n:translate=""> <description i18n:translate="">
@ -91,7 +91,7 @@
<guard> <guard>
</guard> </guard>
</variable> </variable>
<variable variable_id="comments" for_catalog="False" <variable variable_id="comments" for_catalog="False"
for_status="True" update_always="True"> for_status="True" update_always="True">
<description i18n:translate=""> <description i18n:translate="">
@ -103,7 +103,7 @@
<guard> <guard>
</guard> </guard>
</variable> </variable>
<variable variable_id="review_history" for_catalog="False" <variable variable_id="review_history" for_catalog="False"
for_status="False" update_always="False"> for_status="False" update_always="False">
<description i18n:translate=""> <description i18n:translate="">
@ -117,7 +117,7 @@
<guard-permission>Review portal content</guard-permission> <guard-permission>Review portal content</guard-permission>
</guard> </guard>
</variable> </variable>
<variable variable_id="time" for_catalog="False" <variable variable_id="time" for_catalog="False"
for_status="True" update_always="True"> for_status="True" update_always="True">
<description i18n:translate=""> <description i18n:translate="">

View File

@ -52,7 +52,7 @@
<guard> <guard>
</guard> </guard>
</variable> </variable>
<variable variable_id="actor" for_catalog="False" <variable variable_id="actor" for_catalog="False"
for_status="True" update_always="True"> for_status="True" update_always="True">
<description i18n:translate=""> <description i18n:translate="">
@ -64,7 +64,7 @@
<guard> <guard>
</guard> </guard>
</variable> </variable>
<variable variable_id="comments" for_catalog="False" <variable variable_id="comments" for_catalog="False"
for_status="True" update_always="True"> for_status="True" update_always="True">
<description i18n:translate=""> <description i18n:translate="">
@ -76,7 +76,7 @@
<guard> <guard>
</guard> </guard>
</variable> </variable>
<variable variable_id="review_history" for_catalog="False" <variable variable_id="review_history" for_catalog="False"
for_status="False" update_always="False"> for_status="False" update_always="False">
<description i18n:translate=""> <description i18n:translate="">
@ -90,7 +90,7 @@
<guard-permission>Review portal content</guard-permission> <guard-permission>Review portal content</guard-permission>
</guard> </guard>
</variable> </variable>
<variable variable_id="time" for_catalog="False" <variable variable_id="time" for_catalog="False"
for_status="True" update_always="True"> for_status="True" update_always="True">
<description i18n:translate=""> <description i18n:translate="">

View File

@ -42,12 +42,12 @@
zope.lifecycleevent.interfaces.IObjectRemovedEvent" zope.lifecycleevent.interfaces.IObjectRemovedEvent"
handler=".comment.notify_content_object_deleted" handler=".comment.notify_content_object_deleted"
/> />
</configure> </configure>
<!-- Plone 4 Event Subscribers --> <!-- Plone 4 Event Subscribers -->
<configure zcml:condition="installed zope.app.container"> <configure zcml:condition="installed zope.app.container">
<subscriber <subscriber
@ -88,7 +88,7 @@
</configure> </configure>
<!-- Control panel event subscribers --> <!-- Control panel event subscribers -->
<subscriber <subscriber
for="plone.app.controlpanel.interfaces.IConfigurationChangedEvent" for="plone.app.controlpanel.interfaces.IConfigurationChangedEvent"
handler=".browser.controlpanel.notify_configuration_changed" handler=".browser.controlpanel.notify_configuration_changed"
@ -97,6 +97,6 @@
<subscriber <subscriber
for="plone.registry.interfaces.IRecordModifiedEvent" for="plone.registry.interfaces.IRecordModifiedEvent"
handler=".browser.controlpanel.notify_configuration_changed" handler=".browser.controlpanel.notify_configuration_changed"
/> />
</configure> </configure>

View File

@ -23,12 +23,12 @@ class PloneAppDiscussion(PloneSandboxLayer):
USER_WITH_FULLNAME_PASSWORD = 'secret' USER_WITH_FULLNAME_PASSWORD = 'secret'
MANAGER_USER_NAME = 'manager' MANAGER_USER_NAME = 'manager'
MANAGER_USER_PASSWORD = 'secret' MANAGER_USER_PASSWORD = 'secret'
def setUpZope(self, app, configurationContext): def setUpZope(self, app, configurationContext):
# Load ZCML # Load ZCML
import plone.app.discussion import plone.app.discussion
xmlconfig.file('configure.zcml', xmlconfig.file('configure.zcml',
plone.app.discussion, plone.app.discussion,
context=configurationContext) context=configurationContext)
def setUpPloneSite(self, portal): def setUpPloneSite(self, portal):
@ -55,8 +55,8 @@ class PloneAppDiscussion(PloneSandboxLayer):
['Member'], ['Member'],
[], [],
) )
mtool = getToolByName(portal, 'portal_membership', None) mtool = getToolByName(portal, 'portal_membership', None)
mtool.addMember('jim', 'Jim', ['Member'], []) mtool.addMember('jim', 'Jim', ['Member'], [])
mtool.getMemberById('jim').setMemberProperties({"fullname": 'Jim Fult\xc3\xb8rn'}) mtool.getMemberById('jim').setMemberProperties({"fullname": 'Jim Fult\xc3\xb8rn'})
acl_users.userFolderAddUser( acl_users.userFolderAddUser(
@ -68,8 +68,8 @@ class PloneAppDiscussion(PloneSandboxLayer):
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")

View File

@ -19,13 +19,13 @@ class CatalogSetupTest(PloneTestCase):
layer = DiscussionLayer layer = DiscussionLayer
def test_catalog_installed(self): def test_catalog_installed(self):
self.failUnless('total_comments' in self.failUnless('total_comments' in
self.portal.portal_catalog.indexes()) self.portal.portal_catalog.indexes())
self.failUnless('commentators' in self.failUnless('commentators' in
self.portal.portal_catalog.indexes()) self.portal.portal_catalog.indexes())
self.failUnless('total_comments' in self.failUnless('total_comments' in
self.portal.portal_catalog.schema()) self.portal.portal_catalog.schema())
self.failUnless('in_response_to' in self.failUnless('in_response_to' in
self.portal.portal_catalog.schema()) self.portal.portal_catalog.schema())
def test_collection_criteria_installed(self): def test_collection_criteria_installed(self):
@ -44,8 +44,8 @@ class ConversationCatalogTest(PloneTestCase):
def afterSetUp(self): def afterSetUp(self):
# First we need to create some content. # First we need to create some content.
self.loginAsPortalOwner() self.loginAsPortalOwner()
self.portal.invokeFactory(id='doc1', self.portal.invokeFactory(id='doc1',
Title='Document 1', Title='Document 1',
type_name='Document') type_name='Document')
self.catalog = getToolByName(self.portal, 'portal_catalog') self.catalog = getToolByName(self.portal, 'portal_catalog')
@ -64,7 +64,7 @@ class ConversationCatalogTest(PloneTestCase):
self.comment_id = new_comment1_id self.comment_id = new_comment1_id
brains = self.catalog.searchResults( brains = self.catalog.searchResults(
path = {'query' : path = {'query' :
'/'.join(self.portal.doc1.getPhysicalPath()) }, '/'.join(self.portal.doc1.getPhysicalPath()) },
portal_type = "Document" portal_type = "Document"
) )
@ -88,7 +88,7 @@ class ConversationCatalogTest(PloneTestCase):
'++conversation++default/%s' % new_comment2_id) '++conversation++default/%s' % new_comment2_id)
comment2.reindexObject() comment2.reindexObject()
brains = self.catalog.searchResults( brains = self.catalog.searchResults(
path = {'query' : path = {'query' :
'/'.join(self.portal.doc1.getPhysicalPath()) }, '/'.join(self.portal.doc1.getPhysicalPath()) },
portal_type = "Document" portal_type = "Document"
) )
@ -97,7 +97,7 @@ class ConversationCatalogTest(PloneTestCase):
def test_last_comment_date(self): def test_last_comment_date(self):
self.failUnless(self.doc1_brain.has_key('last_comment_date')) self.failUnless(self.doc1_brain.has_key('last_comment_date'))
self.assertEquals(self.doc1_brain.last_comment_date, self.assertEquals(self.doc1_brain.last_comment_date,
datetime(2006, 9, 17, 14, 18, 12)) datetime(2006, 9, 17, 14, 18, 12))
# Add another comment and check if last comment date is updated. # Add another comment and check if last comment date is updated.
@ -113,31 +113,31 @@ class ConversationCatalogTest(PloneTestCase):
'++conversation++default/%s' % new_comment2_id) '++conversation++default/%s' % new_comment2_id)
comment2.reindexObject() comment2.reindexObject()
brains = self.catalog.searchResults( brains = self.catalog.searchResults(
path = {'query' : path = {'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.assertEquals(doc1_brain.last_comment_date, self.assertEquals(doc1_brain.last_comment_date,
datetime(2009, 9, 17, 14, 18, 12)) datetime(2009, 9, 17, 14, 18, 12))
# Remove the comment again # Remove the comment again
del self.conversation[new_comment2_id] del self.conversation[new_comment2_id]
brains = self.catalog.searchResults( brains = self.catalog.searchResults(
path = {'query' : path = {'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.assertEquals(doc1_brain.last_comment_date, self.assertEquals(doc1_brain.last_comment_date,
datetime(2006, 9, 17, 14, 18, 12)) datetime(2006, 9, 17, 14, 18, 12))
# remove all comments # remove all comments
del self.conversation[self.new_comment1_id] del self.conversation[self.new_comment1_id]
brains = self.catalog.searchResults( brains = self.catalog.searchResults(
path = {'query' : path = {'query' :
'/'.join(self.portal.doc1.getPhysicalPath()) }, '/'.join(self.portal.doc1.getPhysicalPath()) },
portal_type = "Document" portal_type = "Document"
) )
@ -161,7 +161,7 @@ class ConversationCatalogTest(PloneTestCase):
comment2.reindexObject() comment2.reindexObject()
brains = self.catalog.searchResults( brains = self.catalog.searchResults(
path = {'query' : path = {'query' :
'/'.join(self.portal.doc1.getPhysicalPath()) }, '/'.join(self.portal.doc1.getPhysicalPath()) },
portal_type = "Document" portal_type = "Document"
) )
@ -172,7 +172,7 @@ class ConversationCatalogTest(PloneTestCase):
# remove one comments # remove one comments
del self.conversation[new_comment2_id] del self.conversation[new_comment2_id]
brains = self.catalog.searchResults( brains = self.catalog.searchResults(
path = {'query' : path = {'query' :
'/'.join(self.portal.doc1.getPhysicalPath()) }, '/'.join(self.portal.doc1.getPhysicalPath()) },
portal_type = "Document" portal_type = "Document"
) )
@ -182,7 +182,7 @@ class ConversationCatalogTest(PloneTestCase):
# remove all comments # remove all comments
del self.conversation[self.new_comment1_id] del self.conversation[self.new_comment1_id]
brains = self.catalog.searchResults( brains = self.catalog.searchResults(
path = {'query' : path = {'query' :
'/'.join(self.portal.doc1.getPhysicalPath()) }, '/'.join(self.portal.doc1.getPhysicalPath()) },
portal_type = "Document" portal_type = "Document"
) )
@ -191,7 +191,7 @@ class ConversationCatalogTest(PloneTestCase):
def test_conversation_indexes_not_in_comments(self): def test_conversation_indexes_not_in_comments(self):
brains = self.catalog.searchResults( brains = self.catalog.searchResults(
path = {'query' : path = {'query' :
'/'.join(self.portal.doc1.getPhysicalPath()) }, '/'.join(self.portal.doc1.getPhysicalPath()) },
portal_type = "Discussion Item" portal_type = "Discussion Item"
) )
@ -208,8 +208,8 @@ class CommentCatalogTest(PloneTestCase):
"""Create a document with a comment. """Create a document with a comment.
""" """
self.loginAsPortalOwner() self.loginAsPortalOwner()
self.portal.invokeFactory(id='doc1', self.portal.invokeFactory(id='doc1',
title='Document 1', title='Document 1',
type_name='Document') type_name='Document')
self.catalog = getToolByName(self.portal, 'portal_catalog') self.catalog = getToolByName(self.portal, 'portal_catalog')
@ -226,13 +226,13 @@ class CommentCatalogTest(PloneTestCase):
self.comment = self.portal.doc1.restrictedTraverse( self.comment = self.portal.doc1.restrictedTraverse(
'++conversation++default/%s' % new_comment1_id) '++conversation++default/%s' % new_comment1_id)
brains = self.catalog.searchResults( brains = self.catalog.searchResults(
path = {'query' : path = {'query' :
'/'.join(self.comment.getPhysicalPath()) }) '/'.join(self.comment.getPhysicalPath()) })
self.comment_brain = brains[0] self.comment_brain = brains[0]
def test_title(self): def test_title(self):
self.assertEquals(self.comment_brain.Title, 'Jim on Document 1') self.assertEquals(self.comment_brain.Title, 'Jim on Document 1')
def test_no_name_title(self): def test_no_name_title(self):
comment = createObject('plone.Comment') comment = createObject('plone.Comment')
comment.text = 'Comment text' comment.text = 'Comment text'
@ -242,7 +242,7 @@ class CommentCatalogTest(PloneTestCase):
comment = self.portal.doc1.restrictedTraverse( comment = self.portal.doc1.restrictedTraverse(
'++conversation++default/%s' % cid) '++conversation++default/%s' % cid)
brains = self.catalog.searchResults( brains = self.catalog.searchResults(
path = {'query' : path = {'query' :
'/'.join(comment.getPhysicalPath()) }) '/'.join(comment.getPhysicalPath()) })
comment_brain = brains[0] comment_brain = brains[0]
self.assertEquals(comment_brain.Title, "Anonymous on Document 1") self.assertEquals(comment_brain.Title, "Anonymous on Document 1")
@ -266,13 +266,13 @@ class CommentCatalogTest(PloneTestCase):
def test_add_comment(self): def test_add_comment(self):
self.failUnless(self.comment_brain) self.failUnless(self.comment_brain)
def test_delete_comment(self): def test_delete_comment(self):
# Make sure a comment is removed from the catalog as well when it is # Make sure a comment is removed from the catalog as well when it is
# deleted. # deleted.
del self.conversation[self.comment_id] del self.conversation[self.comment_id]
brains = self.catalog.searchResults( brains = self.catalog.searchResults(
path = {'query' : path = {'query' :
'/'.join(self.comment.getPhysicalPath()) }) '/'.join(self.comment.getPhysicalPath()) })
self.assertEquals(len(brains), 0) self.assertEquals(len(brains), 0)
@ -285,7 +285,7 @@ class CommentCatalogTest(PloneTestCase):
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.assertEquals(len(brains), 0) self.assertEquals(len(brains), 0)
def test_clear_and_rebuild_catalog(self): def test_clear_and_rebuild_catalog(self):
# Clear and rebuild catalog # Clear and rebuild catalog
self.catalog.clearFindAndRebuild() self.catalog.clearFindAndRebuild()
@ -353,7 +353,7 @@ class CommentCatalogTest(PloneTestCase):
self.assertEquals(len(brains), 6) self.assertEquals(len(brains), 6)
def test_collection(self): def test_collection(self):
self.portal.invokeFactory(id='topic', type_name='Topic') self.portal.invokeFactory(id='topic', type_name='Topic')
topic = self.portal.topic topic = self.portal.topic
crit = topic.addCriterion('Type', 'ATSimpleStringCriterion') crit = topic.addCriterion('Type', 'ATSimpleStringCriterion')
crit.setValue('Comment') crit.setValue('Comment')

View File

@ -37,7 +37,7 @@ class CommentTest(PloneTestCase):
self.catalog = getToolByName(self.portal, 'portal_catalog') self.catalog = getToolByName(self.portal, 'portal_catalog')
self.document_brain = self.catalog.searchResults( self.document_brain = self.catalog.searchResults(
portal_type = 'Document')[0] portal_type = 'Document')[0]
def test_factory(self): def test_factory(self):
comment1 = createObject('plone.Comment') comment1 = createObject('plone.Comment')
self.assert_(IComment.providedBy(comment1)) self.assert_(IComment.providedBy(comment1))
@ -71,7 +71,7 @@ class CommentTest(PloneTestCase):
conversation.addComment(comment1) conversation.addComment(comment1)
comment_brain = self.catalog.searchResults( comment_brain = self.catalog.searchResults(
portal_type = 'Discussion Item')[0] portal_type = 'Discussion Item')[0]
# comment should only have a UID if plone.uuid is present # comment should only have a UID if plone.uuid is present
try: try:
from plone.uuid.interfaces import IUUID from plone.uuid.interfaces import IUUID
@ -80,7 +80,7 @@ class CommentTest(PloneTestCase):
self.failIf(comment_brain.UID) self.failIf(comment_brain.UID)
else: else:
self.failUnless(comment_brain.UID) self.failUnless(comment_brain.UID)
def test_uid_is_unique(self): def test_uid_is_unique(self):
conversation = IConversation(self.portal.doc1) conversation = IConversation(self.portal.doc1)
comment1 = createObject('plone.Comment') comment1 = createObject('plone.Comment')
@ -89,12 +89,12 @@ class CommentTest(PloneTestCase):
conversation.addComment(comment2) conversation.addComment(comment2)
brains = self.catalog.searchResults( brains = self.catalog.searchResults(
portal_type = 'Discussion Item') portal_type = 'Discussion Item')
# make sure uids are either both None (i.e. without plone.uuid), # make sure uids are either both None (i.e. without plone.uuid),
# or not equal # or not equal
if brains[0].UID != None or brains[1].UID != None: if brains[0].UID != None or brains[1].UID != None:
self.assertNotEquals(brains[0].UID, brains[1].UID) self.assertNotEquals(brains[0].UID, brains[1].UID)
def test_comment_uid_differs_from_content_uid(self): def test_comment_uid_differs_from_content_uid(self):
conversation = IConversation(self.portal.doc1) conversation = IConversation(self.portal.doc1)
comment1 = createObject('plone.Comment') comment1 = createObject('plone.Comment')

View File

@ -4,7 +4,7 @@ import time
from datetime import datetime from datetime import datetime
from AccessControl import Unauthorized from AccessControl import Unauthorized
from zope.component import createObject, queryUtility from zope.component import createObject, queryUtility
from OFS.Image import Image from OFS.Image import Image
@ -31,7 +31,7 @@ from Products.PloneTestCase.ptc import PloneTestCase
from plone.app.discussion.browser.comments import CommentsViewlet from plone.app.discussion.browser.comments import CommentsViewlet
from plone.app.discussion.browser.comments import CommentForm from plone.app.discussion.browser.comments import CommentForm
from plone.app.discussion.interfaces import IConversation from plone.app.discussion.interfaces import IConversation
from plone.app.discussion.tests.layer import DiscussionLayer from plone.app.discussion.tests.layer import DiscussionLayer
from plone.app.discussion.interfaces import IDiscussionSettings from plone.app.discussion.interfaces import IDiscussionSettings
@ -44,8 +44,8 @@ class TestCommentForm(PloneTestCase):
self.loginAsPortalOwner() self.loginAsPortalOwner()
typetool = self.portal.portal_types typetool = self.portal.portal_types
typetool.constructContent('Document', self.portal, 'doc1') typetool.constructContent('Document', self.portal, 'doc1')
self.dtool = getToolByName(self.portal, self.dtool = getToolByName(self.portal,
'portal_discussion', 'portal_discussion',
None) None)
self.dtool.overrideDiscussionFor(self.portal.doc1, False) self.dtool.overrideDiscussionFor(self.portal.doc1, False)
self.mtool = getToolByName(self.folder, 'portal_membership', None) self.mtool = getToolByName(self.folder, 'portal_membership', None)
@ -57,11 +57,11 @@ class TestCommentForm(PloneTestCase):
registry = queryUtility(IRegistry) registry = queryUtility(IRegistry)
settings = registry.forInterface(IDiscussionSettings) settings = registry.forInterface(IDiscussionSettings)
settings.globally_enabled = True settings.globally_enabled = True
def test_add_comment(self): def test_add_comment(self):
"""Post a comment as logged-in user. """Post a comment as logged-in user.
""" """
# Allow discussion # Allow discussion
self.dtool.overrideDiscussionFor(self.portal.doc1, True) self.dtool.overrideDiscussionFor(self.portal.doc1, True)
self.viewlet = CommentsViewlet(self.context, self.request, None, None) self.viewlet = CommentsViewlet(self.context, self.request, None, None)
@ -72,99 +72,99 @@ class TestCommentForm(PloneTestCase):
alsoProvides(request, IFormLayer) alsoProvides(request, IFormLayer)
alsoProvides(request, IAttributeAnnotatable) alsoProvides(request, IAttributeAnnotatable)
return request return request
provideAdapter(adapts=(Interface, IBrowserRequest), provideAdapter(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
request = make_request(form={}) request = make_request(form={})
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
self.assertEquals(len(errors), 1) self.assertEquals(len(errors), 1)
self.failIf(commentForm.handleComment(commentForm, "foo")) self.failIf(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
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
self.assertEquals(len(errors), 0) self.assertEquals(len(errors), 0)
self.failIf(commentForm.handleComment(commentForm, "foo")) self.failIf(commentForm.handleComment(commentForm, "foo"))
def test_add_anonymous_comment(self): def test_add_anonymous_comment(self):
"""Add a comment as anonymous. """Add a comment as anonymous.
""" """
# Allow discussion # Allow discussion
self.dtool.overrideDiscussionFor(self.portal.doc1, True) self.dtool.overrideDiscussionFor(self.portal.doc1, True)
self.viewlet = CommentsViewlet(self.context, self.request, None, None) self.viewlet = CommentsViewlet(self.context, self.request, None, None)
registry = queryUtility(IRegistry) registry = queryUtility(IRegistry)
settings = registry.forInterface(IDiscussionSettings, check=False) settings = registry.forInterface(IDiscussionSettings, check=False)
settings.anonymous_comments = True settings.anonymous_comments = True
# Logout # Logout
self.logout() self.logout()
def make_request(form={}): def make_request(form={}):
request = TestRequest() request = TestRequest()
request.form.update(form) request.form.update(form)
alsoProvides(request, IFormLayer) alsoProvides(request, IFormLayer)
alsoProvides(request, IAttributeAnnotatable) alsoProvides(request, IAttributeAnnotatable)
return request return request
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={'form.widgets.name': u'john doe', request = make_request(form={'form.widgets.name': u'john doe',
'form.widgets.text': u'bar'}) '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
self.assertEquals(len(errors), 0) self.assertEquals(len(errors), 0)
self.failIf(commentForm.handleComment(commentForm, "action")) self.failIf(commentForm.handleComment(commentForm, "action"))
def test_can_not_add_comments_if_discussion_is_not_allowed(self): def test_can_not_add_comments_if_discussion_is_not_allowed(self):
"""Make sure that comments can't be posted if discussion is disabled. """Make sure that comments can't be posted if discussion is disabled.
""" """
# Discussion is disabled by default # Discussion is disabled by default
def make_request(form={}): def make_request(form={}):
request = TestRequest() request = TestRequest()
request.form.update(form) request.form.update(form)
alsoProvides(request, IFormLayer) alsoProvides(request, IFormLayer)
alsoProvides(request, IAttributeAnnotatable) alsoProvides(request, IAttributeAnnotatable)
return request return request
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
# No form errors, but raise unauthorized because discussion is not # No form errors, but raise unauthorized because discussion is not
# allowed # allowed
self.assertEquals(len(errors), 0) self.assertEquals(len(errors), 0)
@ -172,52 +172,52 @@ class TestCommentForm(PloneTestCase):
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
comments are disabled. comments are disabled.
""" """
# Anonymous comments are disabled by default # Anonymous comments are disabled by default
self.logout() self.logout()
def make_request(form={}): def make_request(form={}):
request = TestRequest() request = TestRequest()
request.form.update(form) request.form.update(form)
alsoProvides(request, IFormLayer) alsoProvides(request, IFormLayer)
alsoProvides(request, IAttributeAnnotatable) alsoProvides(request, IAttributeAnnotatable)
return request return request
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
self.assertEquals(len(errors), 0) self.assertEquals(len(errors), 0)
self.assertRaises(Unauthorized, self.assertRaises(Unauthorized,
commentForm.handleComment, commentForm.handleComment,
commentForm, commentForm,
"foo") "foo")
class TestCommentsViewlet(PloneTestCase): class TestCommentsViewlet(PloneTestCase):
layer = DiscussionLayer layer = DiscussionLayer
def afterSetUp(self): def afterSetUp(self):
self.loginAsPortalOwner() self.loginAsPortalOwner()
typetool = self.portal.portal_types typetool = self.portal.portal_types
typetool.constructContent('Document', self.portal, 'doc1') typetool.constructContent('Document', self.portal, 'doc1')
self.portal_discussion = getToolByName(self.portal, self.portal_discussion = getToolByName(self.portal,
'portal_discussion', 'portal_discussion',
None) None)
self.mtool = getToolByName(self.folder, 'portal_membership') self.mtool = getToolByName(self.folder, 'portal_membership')
self.memberdata = self.portal.portal_memberdata self.memberdata = self.portal.portal_memberdata
@ -229,29 +229,29 @@ class TestCommentsViewlet(PloneTestCase):
registry = queryUtility(IRegistry) registry = queryUtility(IRegistry)
settings = registry.forInterface(IDiscussionSettings) settings = registry.forInterface(IDiscussionSettings)
settings.globally_enabled = True settings.globally_enabled = True
def test_cook(self): def test_cook(self):
text = """First paragraph text = """First paragraph
Second paragraph""" Second paragraph"""
self.assertEquals(self.viewlet.cook(text), self.assertEquals(self.viewlet.cook(text),
"<p>First paragraph<br /> <br /> Second paragraph</p>") "<p>First paragraph<br /><br /> Second paragraph</p>")
def test_cook_no_html(self): def test_cook_no_html(self):
text = """<b>Got HTML?</b>""" text = """<b>Got HTML?</b>"""
self.assertEquals(self.viewlet.cook(text), self.assertEquals(self.viewlet.cook(text),
"<p>&lt;b&gt;Got HTML?&lt;/b&gt;</p>") "<p>&lt;b&gt;Got HTML?&lt;/b&gt;</p>")
def test_cook_with_no_ascii_characters(self): def test_cook_with_no_ascii_characters(self):
text = """Umlaute sind ä, ö und ü.""" text = """Umlaute sind ä, ö und ü."""
self.assertEquals(self.viewlet.cook(text), self.assertEquals(self.viewlet.cook(text),
"<p>Umlaute sind \xc3\xa4, \xc3\xb6 und \xc3\xbc.</p>") "<p>Umlaute sind \xc3\xa4, \xc3\xb6 und \xc3\xbc.</p>")
def test_cook_links(self): def test_cook_links(self):
text = "Go to http://www.plone.org" text = "Go to http://www.plone.org"
self.assertEquals(self.viewlet.cook(text), self.assertEquals(self.viewlet.cook(text),
"<p>Go to http://www.plone.org</p>") "<p>Go to http://www.plone.org</p>")
def test_can_reply(self): def test_can_reply(self):
# Portal owner can reply # Portal owner can reply
self.failUnless(self.viewlet.can_reply()) self.failUnless(self.viewlet.can_reply())
@ -268,7 +268,7 @@ class TestCommentsViewlet(PloneTestCase):
# The reviewer role has the 'Review comments' permission # The reviewer role has the 'Review comments' permission
self.portal.acl_users._doAddUser('reviewer', 'secret', ['Reviewer'], []) self.portal.acl_users._doAddUser('reviewer', 'secret', ['Reviewer'], [])
self.login('reviewer') self.login('reviewer')
self.failUnless(self.viewlet.can_review()) self.failUnless(self.viewlet.can_review())
def test_can_manage(self): def test_can_manage(self):
"""We keep this method for backward compatibility. This method has been """We keep this method for backward compatibility. This method has been
@ -283,8 +283,8 @@ class TestCommentsViewlet(PloneTestCase):
# The reviewer role has the 'Review comments' permission # The reviewer role has the 'Review comments' permission
self.portal.acl_users._doAddUser('reviewer', 'secret', ['Reviewer'], []) self.portal.acl_users._doAddUser('reviewer', 'secret', ['Reviewer'], [])
self.login('reviewer') self.login('reviewer')
self.failUnless(self.viewlet.can_manage()) self.failUnless(self.viewlet.can_manage())
def test_is_discussion_allowed(self): def test_is_discussion_allowed(self):
# By default, discussion is disabled # By default, discussion is disabled
self.failIf(self.viewlet.is_discussion_allowed()) self.failIf(self.viewlet.is_discussion_allowed())
@ -309,12 +309,11 @@ class TestCommentsViewlet(PloneTestCase):
# Make sure the comment description is changes accordingly # Make sure the comment description is changes accordingly
self.assertEquals( self.assertEquals(
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.")
def test_has_replies(self): def test_has_replies(self):
self.assertEquals(self.viewlet.has_replies(), False) self.assertEquals(self.viewlet.has_replies(), False)
comment = createObject('plone.Comment') comment = createObject('plone.Comment')
@ -331,7 +330,7 @@ class TestCommentsViewlet(PloneTestCase):
conversation.addComment(comment) conversation.addComment(comment)
conversation.addComment(comment) conversation.addComment(comment)
replies = self.viewlet.get_replies() replies = self.viewlet.get_replies()
self.assertEquals(len(tuple(replies)), 2) self.assertEquals(len(tuple(replies)), 2)
replies = self.viewlet.get_replies() replies = self.viewlet.get_replies()
replies.next() replies.next()
replies.next() replies.next()
@ -344,7 +343,7 @@ class TestCommentsViewlet(PloneTestCase):
conversation = IConversation(self.portal.doc1) conversation = IConversation(self.portal.doc1)
c1 = conversation.addComment(comment) c1 = conversation.addComment(comment)
self.assertEquals( self.assertEquals(
len(tuple(self.viewlet.get_replies(workflow_actions=True))), 1) len(tuple(self.viewlet.get_replies(workflow_actions=True))), 1)
# Enable moderation workflow # Enable moderation workflow
self.portal.portal_workflow.setChainForPortalTypes( self.portal.portal_workflow.setChainForPortalTypes(
('Discussion Item',), ('Discussion Item',),
@ -356,8 +355,8 @@ class TestCommentsViewlet(PloneTestCase):
'publish') 'publish')
self.assertEquals(reply['actions'][0]['url'], self.assertEquals(reply['actions'][0]['url'],
'http://nohost/plone/doc1/++conversation++default/%s' % int(c1) + 'http://nohost/plone/doc1/++conversation++default/%s' % int(c1) +
'/content_status_modify?workflow_action=publish') '/content_status_modify?workflow_action=publish')
def test_get_commenter_home_url(self): def test_get_commenter_home_url(self):
comment = createObject('plone.Comment') comment = createObject('plone.Comment')
comment.text = 'Comment text' comment.text = 'Comment text'
@ -369,17 +368,17 @@ class TestCommentsViewlet(PloneTestCase):
def test_get_commenter_home_url_is_none(self): def test_get_commenter_home_url_is_none(self):
self.failIf(self.viewlet.get_commenter_home_url()) self.failIf(self.viewlet.get_commenter_home_url())
def test_get_commenter_portrait(self): def test_get_commenter_portrait(self):
# Add a user with a member image # Add a user with a member image
self.mtool.addMember('jim', 'Jim', ['Member'], []) self.mtool.addMember('jim', 'Jim', ['Member'], [])
self.memberdata._setPortrait(Image(id='jim', self.memberdata._setPortrait(Image(id='jim',
file=dummy.File(), file=dummy.File(),
title=''), 'jim') title=''), 'jim')
self.assertEqual(self.memberdata._getPortrait('jim').getId(), self.assertEqual(self.memberdata._getPortrait('jim').getId(),
'jim') 'jim')
self.assertEqual(self.memberdata._getPortrait('jim').meta_type, self.assertEqual(self.memberdata._getPortrait('jim').meta_type,
'Image') 'Image')
# Add a conversation with a comment # Add a conversation with a comment
@ -395,13 +394,13 @@ class TestCommentsViewlet(PloneTestCase):
portrait_url = self.viewlet.get_commenter_portrait('jim') portrait_url = self.viewlet.get_commenter_portrait('jim')
# Check if the correct member image URL is returned # Check if the correct member image URL is returned
self.assertEquals(portrait_url, self.assertEquals(portrait_url,
'http://nohost/plone/portal_memberdata/portraits/jim') 'http://nohost/plone/portal_memberdata/portraits/jim')
def test_get_commenter_portrait_is_none(self): def test_get_commenter_portrait_is_none(self):
self.assertEquals(self.viewlet.get_commenter_portrait(), self.assertEquals(self.viewlet.get_commenter_portrait(),
'defaultUser.gif') 'defaultUser.gif')
def test_get_commenter_portrait_without_userimage(self): def test_get_commenter_portrait_without_userimage(self):
# Create a user without a user image # Create a user without a user image
@ -431,14 +430,14 @@ class TestCommentsViewlet(PloneTestCase):
'anonymous_comments'] = True 'anonymous_comments'] = True
# Test if anonymous discussion is allowed for the viewlet # Test if anonymous discussion is allowed for the viewlet
self.failUnless(self.viewlet.anonymous_discussion_allowed()) self.failUnless(self.viewlet.anonymous_discussion_allowed())
def test_show_commenter_image(self): def test_show_commenter_image(self):
self.failUnless(self.viewlet.show_commenter_image()) self.failUnless(self.viewlet.show_commenter_image())
registry = queryUtility(IRegistry) registry = queryUtility(IRegistry)
registry['plone.app.discussion.interfaces.IDiscussionSettings.' + registry['plone.app.discussion.interfaces.IDiscussionSettings.' +
'show_commenter_image'] = False 'show_commenter_image'] = False
self.failIf(self.viewlet.show_commenter_image()) self.failIf(self.viewlet.show_commenter_image())
def test_is_anonymous(self): def test_is_anonymous(self):
self.failIf(self.viewlet.is_anonymous()) self.failIf(self.viewlet.is_anonymous())
self.logout() self.logout()
@ -447,8 +446,8 @@ class TestCommentsViewlet(PloneTestCase):
def test_login_action(self): def test_login_action(self):
self.viewlet.update() self.viewlet.update()
self.assertEquals(self.viewlet.login_action(), self.assertEquals(self.viewlet.login_action(),
'http://nohost/plone/login_form?came_from=http%3A//nohost') 'http://nohost/plone/login_form?came_from=http%3A//nohost')
def test_format_time(self): def test_format_time(self):
python_time = datetime(2009, 02, 01, 23, 32, 03, 57) python_time = datetime(2009, 02, 01, 23, 32, 03, 57)
# Python Time must be utc time. There seems to be no too simple way # Python Time must be utc time. There seems to be no too simple way
@ -464,5 +463,6 @@ class TestCommentsViewlet(PloneTestCase):
localized_time = self.viewlet.format_time(python_time) localized_time = self.viewlet.format_time(python_time)
self.assertEquals(localized_time, "Feb 01, 2009 11:32 PM") self.assertEquals(localized_time, "Feb 01, 2009 11:32 PM")
def test_suite(): def test_suite():
return unittest.defaultTestLoader.loadTestsFromName(__name__) return unittest.defaultTestLoader.loadTestsFromName(__name__)

View File

@ -27,9 +27,9 @@ class RegistryTest(PloneTestCase):
def test_registry_registered(self): def test_registry_registered(self):
registry = queryUtility(IRegistry) registry = queryUtility(IRegistry)
self.failUnless(registry.forInterface(IDiscussionSettings)) self.failUnless(registry.forInterface(IDiscussionSettings))
def test_discussion_controlpanel_view(self): def test_discussion_controlpanel_view(self):
view = getMultiAdapter((self.portal, self.portal.REQUEST), view = getMultiAdapter((self.portal, self.portal.REQUEST),
name="discussion-settings") name="discussion-settings")
view = view.__of__(self.portal) view = view.__of__(self.portal)
self.failUnless(view()) self.failUnless(view())
@ -45,7 +45,7 @@ class RegistryTest(PloneTestCase):
self.failUnless('globally_enabled' in IDiscussionSettings) self.failUnless('globally_enabled' in IDiscussionSettings)
self.assertEquals( self.assertEquals(
self.registry['plone.app.discussion.interfaces.' + self.registry['plone.app.discussion.interfaces.' +
'IDiscussionSettings.globally_enabled'], 'IDiscussionSettings.globally_enabled'],
False) False)
def test_anonymous_comments(self): def test_anonymous_comments(self):
@ -59,16 +59,16 @@ class RegistryTest(PloneTestCase):
self.failUnless('moderation_enabled' in IDiscussionSettings) self.failUnless('moderation_enabled' in IDiscussionSettings)
self.assertEquals( self.assertEquals(
self.registry['plone.app.discussion.interfaces.' + self.registry['plone.app.discussion.interfaces.' +
'IDiscussionSettings.moderation_enabled'], 'IDiscussionSettings.moderation_enabled'],
False) False)
def test_text_transform(self): def test_text_transform(self):
self.failUnless('text_transform' in IDiscussionSettings) self.failUnless('text_transform' in IDiscussionSettings)
self.assertEquals( self.assertEquals(
self.registry['plone.app.discussion.interfaces.' + self.registry['plone.app.discussion.interfaces.' +
'IDiscussionSettings.text_transform'], 'IDiscussionSettings.text_transform'],
'text/plain') 'text/plain')
def test_captcha(self): def test_captcha(self):
# Check globally_enabled record # Check globally_enabled record
self.failUnless('captcha' in IDiscussionSettings) self.failUnless('captcha' in IDiscussionSettings)
@ -79,12 +79,12 @@ class RegistryTest(PloneTestCase):
def test_show_commenter_image(self): def test_show_commenter_image(self):
# Check show_commenter_image record # Check show_commenter_image record
self.failUnless('show_commenter_image' in IDiscussionSettings) self.failUnless('show_commenter_image' in IDiscussionSettings)
self.assertEquals(self.registry['plone.app.discussion.interfaces.' + self.assertEquals(self.registry['plone.app.discussion.interfaces.' +
'IDiscussionSettings.show_commenter_image'], True) 'IDiscussionSettings.show_commenter_image'], True)
def test_moderator_notification_enabled(self): def test_moderator_notification_enabled(self):
# Check show_commenter_image record # Check show_commenter_image record
self.failUnless('moderator_notification_enabled' in self.failUnless('moderator_notification_enabled' in
IDiscussionSettings) IDiscussionSettings)
self.assertEquals(self.registry['plone.app.discussion.interfaces.' + self.assertEquals(self.registry['plone.app.discussion.interfaces.' +
'IDiscussionSettings.moderator_notification_enabled'], False) 'IDiscussionSettings.moderator_notification_enabled'], False)
@ -108,39 +108,39 @@ class ConfigurationChangedSubscriberTest(PloneTestCase):
# Set up the registry # Set up the registry
registry = queryUtility(IRegistry) registry = queryUtility(IRegistry)
self.settings = registry.forInterface(IDiscussionSettings, check=False) self.settings = registry.forInterface(IDiscussionSettings, check=False)
def test_moderation_enabled_in_discussion_control_panel_changed(self): def test_moderation_enabled_in_discussion_control_panel_changed(self):
"""Make sure the 'Discussion Item' workflow is changed properly, when """Make sure the 'Discussion Item' workflow is changed properly, when
the 'comment_moderation' setting in the discussion control panel the 'comment_moderation' setting in the discussion control panel
changes. changes.
""" """
# By default the one_state_workflow without moderation is enabled # By default the one_state_workflow without moderation is enabled
self.assertEquals(('one_state_workflow',), self.assertEquals(('one_state_workflow',),
self.portal.portal_workflow.getChainForPortalType( self.portal.portal_workflow.getChainForPortalType(
'Discussion Item')) 'Discussion Item'))
# Enable moderation in the discussion control panel # Enable moderation in the discussion control panel
self.settings.moderation_enabled = True self.settings.moderation_enabled = True
# Make sure the comment_review_workflow with moderation enabled is # Make sure the comment_review_workflow with moderation enabled is
# enabled # enabled
self.assertEquals(('comment_review_workflow',), self.assertEquals(('comment_review_workflow',),
self.portal.portal_workflow.getChainForPortalType( self.portal.portal_workflow.getChainForPortalType(
'Discussion Item')) 'Discussion Item'))
# And back # And back
self.settings.moderation_enabled = False self.settings.moderation_enabled = False
self.assertEquals(('one_state_workflow',), self.assertEquals(('one_state_workflow',),
self.portal.portal_workflow.getChainForPortalType( self.portal.portal_workflow.getChainForPortalType(
'Discussion Item')) 'Discussion Item'))
def test_change_workflow_in_types_control_panel(self): def test_change_workflow_in_types_control_panel(self):
"""Make sure the setting in the discussion control panel is changed """Make sure the setting in the discussion control panel is changed
accordingly, when the workflow for the 'Discussion Item' changed in accordingly, when the workflow for the 'Discussion Item' changed in
the types control panel. the types control panel.
""" """
# By default, moderation is disabled # By default, moderation is disabled
self.settings.moderation_enabled = False self.settings.moderation_enabled = False
# Enable the 'comment_review_workflow' with moderation enabled # Enable the 'comment_review_workflow' with moderation enabled
self.portal.portal_workflow.setChainForPortalTypes( self.portal.portal_workflow.setChainForPortalTypes(
('Discussion Item',), ('Discussion Item',),
@ -159,11 +159,11 @@ class ConfigurationChangedSubscriberTest(PloneTestCase):
self.portal.portal_workflow.setChainForPortalTypes( self.portal.portal_workflow.setChainForPortalTypes(
('Discussion Item',), ('Discussion Item',),
('intranet_workflow',)) ('intranet_workflow',))
# Setting has not changed. A Custom workflow disables the # Setting has not changed. A Custom workflow disables the
# enable_moderation checkbox in the discussion control panel. The # enable_moderation checkbox in the discussion control panel. The
# setting itself remains unchanged. # setting itself remains unchanged.
self.settings.moderation_enabled = True self.settings.moderation_enabled = True
def test_suite(): def test_suite():
return unittest.defaultTestLoader.loadTestsFromName(__name__) return unittest.defaultTestLoader.loadTestsFromName(__name__)

View File

@ -29,20 +29,20 @@ class ConversationTest(PloneTestCase):
typetool = self.portal.portal_types typetool = self.portal.portal_types
typetool.constructContent('Document', self.portal, 'doc1') typetool.constructContent('Document', self.portal, 'doc1')
self.typetool = typetool self.typetool = typetool
self.portal_discussion = getToolByName(self.portal, self.portal_discussion = getToolByName(self.portal,
'portal_discussion', 'portal_discussion',
None) None)
# Allow discussion # Allow discussion
registry = queryUtility(IRegistry) registry = queryUtility(IRegistry)
settings = registry.forInterface(IDiscussionSettings) settings = registry.forInterface(IDiscussionSettings)
settings.globally_enabled = True settings.globally_enabled = True
def test_add_comment(self): def test_add_comment(self):
# Create a conversation. In this case we doesn't assign it to an # Create a conversation. In this case we doesn't assign it to an
# object, as we just want to check the Conversation object API. # object, as we just want to check the Conversation object API.
conversation = IConversation(self.portal.doc1) conversation = IConversation(self.portal.doc1)
# Add a comment. Note: in real life, we always create comments via the # Add a comment. Note: in real life, we always create comments via the
# factory to allow different factories to be swapped in # factory to allow different factories to be swapped in
comment = createObject('plone.Comment') comment = createObject('plone.Comment')
@ -59,7 +59,7 @@ class ConversationTest(PloneTestCase):
self.assertEquals(len(list(conversation.getComments())), 1) self.assertEquals(len(list(conversation.getComments())), 1)
self.assertEquals(len(tuple(conversation.getThreads())), 1) self.assertEquals(len(tuple(conversation.getThreads())), 1)
self.assertEquals(conversation.total_comments, 1) self.assertEquals(conversation.total_comments, 1)
self.assert_(conversation.last_comment_date - datetime.utcnow() < self.assert_(conversation.last_comment_date - datetime.utcnow() <
timedelta(seconds=1)) timedelta(seconds=1))
def test_delete_comment(self): def test_delete_comment(self):
@ -67,7 +67,7 @@ class ConversationTest(PloneTestCase):
# object, as we just want to check the Conversation object API. # object, as we just want to check the Conversation object API.
conversation = IConversation(self.portal.doc1) conversation = IConversation(self.portal.doc1)
# Add a comment. Note: in real life, we always create comments via the # Add a comment. Note: in real life, we always create comments via the
# factory to allow different factories to be swapped in # factory to allow different factories to be swapped in
comment = createObject('plone.Comment') comment = createObject('plone.Comment')
@ -148,7 +148,7 @@ class ConversationTest(PloneTestCase):
], list(conversation.getThreads())) ], list(conversation.getThreads()))
def test_delete_comment_when_content_object_is_deleted(self): def test_delete_comment_when_content_object_is_deleted(self):
# Make sure all comments of a content object are deleted when the # Make sure all comments of a content object are deleted when the
# object itself is deleted. # object itself is deleted.
conversation = IConversation(self.portal.doc1) conversation = IConversation(self.portal.doc1)
comment = createObject('plone.Comment') comment = createObject('plone.Comment')
@ -157,11 +157,11 @@ class ConversationTest(PloneTestCase):
# Delete the content object # Delete the content object
self.portal.manage_delObjects(['doc1']) self.portal.manage_delObjects(['doc1'])
# Make sure the comment has been deleted as well # Make sure the comment has been deleted as well
self.assertEquals(len(list(conversation.getComments())), 0) self.assertEquals(len(list(conversation.getComments())), 0)
self.assertEquals(len(tuple(conversation.getThreads())), 0) self.assertEquals(len(tuple(conversation.getThreads())), 0)
self.assertEquals(conversation.total_comments, 0) self.assertEquals(conversation.total_comments, 0)
def test_allow_discussion(self): def test_allow_discussion(self):
# This is not a real test! It's only there to understand the # This is not a real test! It's only there to understand the
@ -188,20 +188,20 @@ class ConversationTest(PloneTestCase):
portal_discussion = getToolByName(self.portal, 'portal_discussion') portal_discussion = getToolByName(self.portal, 'portal_discussion')
self.assertEquals(portal_discussion.isDiscussionAllowedFor( self.assertEquals(portal_discussion.isDiscussionAllowedFor(
self.portal.doc1), False) self.portal.doc1), False)
self.assertEquals(self.portal.doc1.getTypeInfo().allowDiscussion(), self.assertEquals(self.portal.doc1.getTypeInfo().allowDiscussion(),
False) False)
# The allow discussion flag is None by default # The allow discussion flag is None by default
self.failIf(getattr(self.portal.doc1, 'allow_discussion', None)) self.failIf(getattr(self.portal.doc1, 'allow_discussion', None))
# But isDiscussionAllowedFor, also checks if discussion is allowed on # But isDiscussionAllowedFor, also checks if discussion is allowed on
# the content type. So we allow discussion on the Document content # the content type. So we allow discussion on the Document content
# type and check if the Document object allows discussion now. # type and check if the Document object allows discussion now.
document_fti = getattr(portal_types, 'Document') document_fti = getattr(portal_types, 'Document')
document_fti.manage_changeProperties(allow_discussion = True) document_fti.manage_changeProperties(allow_discussion = True)
self.assertEquals(portal_discussion.isDiscussionAllowedFor( self.assertEquals(portal_discussion.isDiscussionAllowedFor(
self.portal.doc1), True) self.portal.doc1), True)
self.assertEquals(self.portal.doc1.getTypeInfo().allowDiscussion(), self.assertEquals(self.portal.doc1.getTypeInfo().allowDiscussion(),
True) True)
# We can also override the allow_discussion locally # We can also override the allow_discussion locally
@ -209,16 +209,16 @@ class ConversationTest(PloneTestCase):
# Check if the Document discussion is disabled # Check if the Document discussion is disabled
self.assertEquals(portal_discussion.isDiscussionAllowedFor( self.assertEquals(portal_discussion.isDiscussionAllowedFor(
self.portal.doc1), False) self.portal.doc1), False)
# Check that the local allow_discussion flag is now explicitly set to # Check that the local allow_discussion flag is now explicitly set to
# False # False
self.assertEquals(getattr(self.portal.doc1, 'allow_discussion', None), self.assertEquals(getattr(self.portal.doc1, 'allow_discussion', None),
False) False)
# Disallow discussion on the Document content type again # Disallow discussion on the Document content type again
document_fti.manage_changeProperties(allow_discussion = False) document_fti.manage_changeProperties(allow_discussion = False)
self.assertEquals(portal_discussion.isDiscussionAllowedFor( self.assertEquals(portal_discussion.isDiscussionAllowedFor(
self.portal.doc1), False) self.portal.doc1), False)
self.assertEquals(self.portal.doc1.getTypeInfo().allowDiscussion(), self.assertEquals(self.portal.doc1.getTypeInfo().allowDiscussion(),
False) False)
# Now we override allow_discussion again (True) for the Document # Now we override allow_discussion again (True) for the Document
@ -226,14 +226,14 @@ class ConversationTest(PloneTestCase):
self.portal_discussion.overrideDiscussionFor(self.portal.doc1, True) self.portal_discussion.overrideDiscussionFor(self.portal.doc1, True)
self.assertEquals(portal_discussion.isDiscussionAllowedFor( self.assertEquals(portal_discussion.isDiscussionAllowedFor(
self.portal.doc1), True) self.portal.doc1), True)
self.assertEquals(getattr(self.portal.doc1, 'allow_discussion', None), self.assertEquals(getattr(self.portal.doc1, 'allow_discussion', None),
True) True)
def test_comments_enabled_on_doc_in_subfolder(self): def test_comments_enabled_on_doc_in_subfolder(self):
typetool = self.portal.portal_types typetool = self.portal.portal_types
typetool.constructContent('Folder', self.portal, 'folder1') typetool.constructContent('Folder', self.portal, 'folder1')
typetool.constructContent('Document', self.portal.folder1, 'doc2') typetool.constructContent('Document', self.portal.folder1, 'doc2')
folder = self.portal.folder1 folder = self.portal.folder1
folder.allowDiscussion(False) folder.allowDiscussion(False)
self.assertFalse(hasattr(aq_base(folder), 'allow_discussion')) self.assertFalse(hasattr(aq_base(folder), 'allow_discussion'))
@ -241,11 +241,11 @@ class ConversationTest(PloneTestCase):
self.assertTrue(aq_base(folder).allow_discussion) self.assertTrue(aq_base(folder).allow_discussion)
folder.allowDiscussion(False) folder.allowDiscussion(False)
self.assertFalse(aq_base(folder).allow_discussion) self.assertFalse(aq_base(folder).allow_discussion)
doc = self.portal.folder1.doc2 doc = self.portal.folder1.doc2
conversation = IConversation(doc) conversation = IConversation(doc)
self.assertEquals(conversation.enabled(), False) self.assertEquals(conversation.enabled(), False)
# We have to allow discussion on Document content type, since # We have to allow discussion on Document content type, since
# otherwise allow_discussion will always return False # otherwise allow_discussion will always return False
portal_types = getToolByName(self.portal, 'portal_types') portal_types = getToolByName(self.portal, 'portal_types')
@ -409,7 +409,7 @@ class ConversationTest(PloneTestCase):
# object, as we just want to check the Conversation object API. # object, as we just want to check the Conversation object API.
conversation = IConversation(self.portal.doc1) conversation = IConversation(self.portal.doc1)
# Add a comment. Note: in real life, we always create comments via the # Add a comment. Note: in real life, we always create comments via the
# factory to allow different factories to be swapped in # factory to allow different factories to be swapped in
comment1 = createObject('plone.Comment') comment1 = createObject('plone.Comment')
@ -571,9 +571,9 @@ class ConversationTest(PloneTestCase):
new_comment3_id = conversation.addComment(comment3) new_comment3_id = conversation.addComment(comment3)
# check if the latest comment is exactly one day old # check if the latest comment is exactly one day old
self.assert_(conversation.last_comment_date < datetime.utcnow() - self.assert_(conversation.last_comment_date < datetime.utcnow() -
timedelta(hours=23, minutes=59, seconds=59)) timedelta(hours=23, minutes=59, seconds=59))
self.assert_(conversation.last_comment_date > self.assert_(conversation.last_comment_date >
datetime.utcnow() - timedelta(days=1, seconds=1)) datetime.utcnow() - timedelta(days=1, seconds=1))
# remove the latest comment # remove the latest comment
@ -581,9 +581,9 @@ class ConversationTest(PloneTestCase):
# check if the latest comment has been updated # check if the latest comment has been updated
# the latest comment should be exactly two days old # the latest comment should be exactly two days old
self.assert_(conversation.last_comment_date < datetime.utcnow() - self.assert_(conversation.last_comment_date < datetime.utcnow() -
timedelta(days=1, hours=23, minutes=59, seconds=59)) timedelta(days=1, hours=23, minutes=59, seconds=59))
self.assert_(conversation.last_comment_date > datetime.utcnow() - self.assert_(conversation.last_comment_date > datetime.utcnow() -
timedelta(days=2, seconds=1)) timedelta(days=2, seconds=1))
# remove the latest comment again # remove the latest comment again
@ -591,9 +591,9 @@ class ConversationTest(PloneTestCase):
# check if the latest comment has been updated # check if the latest comment has been updated
# the latest comment should be exactly four days old # the latest comment should be exactly four days old
self.assert_(conversation.last_comment_date < datetime.utcnow() - self.assert_(conversation.last_comment_date < datetime.utcnow() -
timedelta(days=3, hours=23, minutes=59, seconds=59)) timedelta(days=3, hours=23, minutes=59, seconds=59))
self.assert_(conversation.last_comment_date > datetime.utcnow() - self.assert_(conversation.last_comment_date > datetime.utcnow() -
timedelta(days=4, seconds=2)) timedelta(days=4, seconds=2))
def test_get_comments_full(self): def test_get_comments_full(self):
@ -678,15 +678,15 @@ class ConversationTest(PloneTestCase):
'++conversation++default') '++conversation++default')
self.assert_(IConversation.providedBy(conversation)) self.assert_(IConversation.providedBy(conversation))
self.assertEquals(('', 'plone', 'doc1', '++conversation++default'), self.assertEquals(('', 'plone', 'doc1', '++conversation++default'),
conversation.getPhysicalPath()) conversation.getPhysicalPath())
# XXX: conversation.absolute_url() returns different values dependent # XXX: conversation.absolute_url() returns different values dependent
# on the Plone version used. # on the Plone version used.
# Plone 3.3: # Plone 3.3:
#self.assertEquals('plone/doc1/%2B%2Bconversation%2B%2Bdefault', #self.assertEquals('plone/doc1/%2B%2Bconversation%2B%2Bdefault',
#conversation.absolute_url()) #conversation.absolute_url())
# Plone 4: # Plone 4:
#self.assertEquals('http://nohost/plone/doc1/++conversation++default', #self.assertEquals('http://nohost/plone/doc1/++conversation++default',
#conversation.absolute_url()) #conversation.absolute_url())
def test_parent(self): def test_parent(self):

View File

@ -9,9 +9,9 @@ try:
import unittest2 as unittest import unittest2 as unittest
import pprint import pprint
import interlude import interlude
from plone.testing import layered from plone.testing import layered
from plone.app.discussion.testing import \ from plone.app.discussion.testing import \
PLONE_APP_DISCUSSION_FUNCTIONAL_TESTING PLONE_APP_DISCUSSION_FUNCTIONAL_TESTING
PLONE4 = True PLONE4 = True
@ -26,7 +26,7 @@ normal_testfiles = [
] ]
if PLONE4: if PLONE4:
def test_suite(): def test_suite():
suite = unittest.TestSuite() suite = unittest.TestSuite()
suite.addTests([ suite.addTests([
@ -41,9 +41,9 @@ if PLONE4:
return suite return suite
else: else:
def test_suite(): def test_suite():
return unittest.TestSuite([]) return unittest.TestSuite([])
if __name__ == '__main__': if __name__ == '__main__':
unittest.main(defaultTest='test_suite') unittest.main(defaultTest='test_suite')

View File

@ -17,14 +17,14 @@ from plone.indexer.delegate import DelegatingIndexerFactory
from plone.app.discussion import catalog from plone.app.discussion import catalog
LONG_TEXT = """Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed LONG_TEXT = """Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed
diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat,
sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum.
Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit
amet.""" amet."""
LONG_TEXT_CUT = """Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed LONG_TEXT_CUT = """Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed
diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat,
sed diam voluptua. At [...]""" sed diam voluptua. At [...]"""
@ -37,8 +37,8 @@ class ConversationIndexersTest(PloneTestCase):
def afterSetUp(self): def afterSetUp(self):
# First we need to create some content. # First we need to create some content.
self.loginAsPortalOwner() self.loginAsPortalOwner()
self.portal.invokeFactory(id='doc1', self.portal.invokeFactory(id='doc1',
title='Document 1', title='Document 1',
type_name='Document') type_name='Document')
# Create a conversation. # Create a conversation.
@ -71,7 +71,7 @@ class ConversationIndexersTest(PloneTestCase):
self.conversation = conversation self.conversation = conversation
def test_conversation_total_comments(self): def test_conversation_total_comments(self):
self.assert_(isinstance(catalog.total_comments, self.assert_(isinstance(catalog.total_comments,
DelegatingIndexerFactory)) DelegatingIndexerFactory))
self.assertEquals(catalog.total_comments(self.portal.doc1)(), 3) self.assertEquals(catalog.total_comments(self.portal.doc1)(), 3)
del self.conversation[self.new_id1] del self.conversation[self.new_id1]
@ -81,12 +81,12 @@ class ConversationIndexersTest(PloneTestCase):
self.assertEquals(catalog.total_comments(self.portal.doc1)(), 0) self.assertEquals(catalog.total_comments(self.portal.doc1)(), 0)
def test_conversation_last_comment_date(self): def test_conversation_last_comment_date(self):
self.assert_(isinstance(catalog.last_comment_date, self.assert_(isinstance(catalog.last_comment_date,
DelegatingIndexerFactory)) DelegatingIndexerFactory))
self.assertEquals(catalog.last_comment_date(self.portal.doc1)(), self.assertEquals(catalog.last_comment_date(self.portal.doc1)(),
datetime(2009, 4, 12, 11, 12, 12)) datetime(2009, 4, 12, 11, 12, 12))
del self.conversation[self.new_id3] del self.conversation[self.new_id3]
self.assertEquals(catalog.last_comment_date(self.portal.doc1)(), self.assertEquals(catalog.last_comment_date(self.portal.doc1)(),
datetime(2007, 12, 13, 4, 18, 12)) datetime(2007, 12, 13, 4, 18, 12))
del self.conversation[self.new_id2] del self.conversation[self.new_id2]
del self.conversation[self.new_id1] del self.conversation[self.new_id1]
@ -94,11 +94,12 @@ class ConversationIndexersTest(PloneTestCase):
def test_conversation_commentators(self): def test_conversation_commentators(self):
pass pass
#self.assertEquals(catalog.commentators(self.portal.doc1)(), #self.assertEquals(catalog.commentators(self.portal.doc1)(),
# ('Jim', 'Emma', 'Lukas')) # ('Jim', 'Emma', 'Lukas'))
#self.assert_(isinstance(catalog.commentators, #self.assert_(isinstance(catalog.commentators,
# DelegatingIndexerFactory)) # DelegatingIndexerFactory))
class CommentIndexersTest(PloneTestCase): class CommentIndexersTest(PloneTestCase):
layer = DiscussionLayer layer = DiscussionLayer
@ -106,15 +107,15 @@ class CommentIndexersTest(PloneTestCase):
def afterSetUp(self): def afterSetUp(self):
# First we need to create some content. # First we need to create some content.
self.loginAsPortalOwner() self.loginAsPortalOwner()
self.portal.invokeFactory(id='doc1', self.portal.invokeFactory(id='doc1',
title='Document 1', title='Document 1',
type_name='Document') type_name='Document')
# Create a conversation. In this case we doesn't assign it to an # Create a conversation. In this case we doesn't assign it to an
# object, as we just want to check the Conversation object API. # object, as we just want to check the Conversation object API.
conversation = IConversation(self.portal.doc1) conversation = IConversation(self.portal.doc1)
# Add a comment. Note: in real life, we always create comments via the # Add a comment. Note: in real life, we always create comments via the
# factory to allow different factories to be swapped in # factory to allow different factories to be swapped in
comment = createObject('plone.Comment') comment = createObject('plone.Comment')
@ -132,7 +133,7 @@ class CommentIndexersTest(PloneTestCase):
self.assert_(isinstance(catalog.title, DelegatingIndexerFactory)) self.assert_(isinstance(catalog.title, DelegatingIndexerFactory))
def test_description(self): def test_description(self):
self.assertEquals(catalog.description(self.comment)(), self.assertEquals(catalog.description(self.comment)(),
'Lorem ipsum dolor sit amet.') 'Lorem ipsum dolor sit amet.')
self.assert_(isinstance(catalog.description, DelegatingIndexerFactory)) self.assert_(isinstance(catalog.description, DelegatingIndexerFactory))
@ -144,23 +145,23 @@ class CommentIndexersTest(PloneTestCase):
comment_long.text = LONG_TEXT comment_long.text = LONG_TEXT
self.conversation.addComment(comment_long) self.conversation.addComment(comment_long)
self.assertEquals(catalog.description(comment_long)(), self.assertEquals(catalog.description(comment_long)(),
LONG_TEXT_CUT.replace("\n", "")) LONG_TEXT_CUT.replace("\n", " "))
def test_dates(self): def test_dates(self):
# Test if created, modified, effective etc. are set correctly # Test if created, modified, effective etc. are set correctly
self.assertEquals(catalog.created(self.comment)(), self.assertEquals(catalog.created(self.comment)(),
DateTime(2006, 9, 17, 14, 18, 12)) DateTime(2006, 9, 17, 14, 18, 12))
self.assertEquals(catalog.effective(self.comment)(), self.assertEquals(catalog.effective(self.comment)(),
DateTime(2006, 9, 17, 14, 18, 12)) DateTime(2006, 9, 17, 14, 18, 12))
self.assertEquals(catalog.modified(self.comment)(), self.assertEquals(catalog.modified(self.comment)(),
DateTime(2008, 3, 12, 7, 32, 52)) DateTime(2008, 3, 12, 7, 32, 52))
def test_searchable_text(self): def test_searchable_text(self):
# Test if searchable text is a concatenation of title and comment text # Test if searchable text is a concatenation of title and comment text
self.assertEquals(catalog.searchable_text(self.comment)(), self.assertEquals(catalog.searchable_text(self.comment)(),
('Lorem ipsum dolor sit amet.')) ('Lorem ipsum dolor sit amet.'))
self.assert_(isinstance(catalog.searchable_text, self.assert_(isinstance(catalog.searchable_text,
DelegatingIndexerFactory)) DelegatingIndexerFactory))
def test_creator(self): def test_creator(self):
@ -171,5 +172,6 @@ class CommentIndexersTest(PloneTestCase):
# object the comment was added to # object the comment was added to
self.assertEquals(catalog.in_response_to(self.comment)(), 'Document 1') self.assertEquals(catalog.in_response_to(self.comment)(), 'Document 1')
def test_suite(): def test_suite():
return unittest.defaultTestLoader.loadTestsFromName(__name__) return unittest.defaultTestLoader.loadTestsFromName(__name__)

View File

@ -18,11 +18,11 @@ from plone.app.discussion.interfaces import IConversation, IComment
class MigrationTest(PloneTestCase): class MigrationTest(PloneTestCase):
layer = DiscussionLayer layer = DiscussionLayer
def afterSetUp(self): def afterSetUp(self):
self.loginAsPortalOwner() self.loginAsPortalOwner()
self.portal.invokeFactory(id='doc', self.portal.invokeFactory(id='doc',
title='Document 1', title='Document 1',
type_name='Document') type_name='Document')
# Create a document # Create a document
self.discussion = getToolByName(self.portal, 'portal_discussion', None) self.discussion = getToolByName(self.portal, 'portal_discussion', None)
@ -35,7 +35,7 @@ class MigrationTest(PloneTestCase):
request.set("test", True) request.set("test", True)
context = getattr(self.portal, 'doc') context = getattr(self.portal, 'doc')
self.view = View(context, request) self.view = View(context, request)
self.workflow.setChainForPortalTypes(('Discussion Item',), self.workflow.setChainForPortalTypes(('Discussion Item',),
'comment_review_workflow') 'comment_review_workflow')
self.doc = self.portal.doc self.doc = self.portal.doc
@ -59,7 +59,7 @@ class MigrationTest(PloneTestCase):
self.view() self.view()
# Make sure a conversation has been created # Make sure a conversation has been created
self.failUnless('plone.app.discussion:conversation' in self.failUnless('plone.app.discussion:conversation' in
IAnnotations(self.doc)) IAnnotations(self.doc))
conversation = IConversation(self.doc) conversation = IConversation(self.doc)
@ -71,9 +71,9 @@ class MigrationTest(PloneTestCase):
self.assertEquals(comment1.Title(), 'Jim on Document 1') self.assertEquals(comment1.Title(), 'Jim on Document 1')
self.assertEquals(comment1.text, 'My Text') self.assertEquals(comment1.text, 'My Text')
self.assertEquals(comment1.Creator(), 'Jim') self.assertEquals(comment1.Creator(), 'Jim')
self.assertEquals(comment1.creation_date, self.assertEquals(comment1.creation_date,
datetime(2003, 3, 11, 9, 28, 6)) datetime(2003, 3, 11, 9, 28, 6))
self.assertEquals(comment1.modification_date, self.assertEquals(comment1.modification_date,
datetime(2009, 7, 12, 19, 38, 7)) datetime(2009, 7, 12, 19, 38, 7))
self.assertEquals( self.assertEquals(
[{'comment': comment1, 'depth': 0, 'id': long(comment1.id)},] [{'comment': comment1, 'depth': 0, 'id': long(comment1.id)},]

View File

@ -22,7 +22,7 @@ class ModerationViewTest(PloneTestCase):
self.loginAsPortalOwner() self.loginAsPortalOwner()
typetool = self.portal.portal_types typetool = self.portal.portal_types
typetool.constructContent('Document', self.portal, 'doc1') typetool.constructContent('Document', self.portal, 'doc1')
self.portal_discussion = getToolByName(self.portal, self.portal_discussion = getToolByName(self.portal,
'portal_discussion', 'portal_discussion',
None) None)
@ -66,7 +66,7 @@ class ModerationViewTest(PloneTestCase):
'++conversation++default/%s' % new_id_3) '++conversation++default/%s' % new_id_3)
def test_moderation_enabled(self): def test_moderation_enabled(self):
"""Make sure that moderation_enabled returns true if the comment """Make sure that moderation_enabled returns true if the comment
workflow implements a 'pending' state. workflow implements a 'pending' state.
""" """
# The one_state_workflow does not have a 'pending' state # The one_state_workflow does not have a 'pending' state
@ -81,10 +81,10 @@ class ModerationViewTest(PloneTestCase):
def test_old_comments_not_shown_in_moderation_view(self): def test_old_comments_not_shown_in_moderation_view(self):
# Create an old comment and make sure it is not shown # Create an old comment and make sure it is not shown
# in the moderation view. # in the moderation view.
# Create old comment # Create old comment
discussion = getToolByName(self.portal, 'portal_discussion', None) discussion = getToolByName(self.portal, 'portal_discussion', None)
discussion.overrideDiscussionFor(self.portal.doc1, 1) discussion.overrideDiscussionFor(self.portal.doc1, 1)
talkback = discussion.getDiscussionFor(self.portal.doc1) talkback = discussion.getDiscussionFor(self.portal.doc1)
self.portal.doc1.talkback.createReply('My Title', 'My Text', Creator='Jim') self.portal.doc1.talkback.createReply('My Title', 'My Text', Creator='Jim')
reply = talkback.getReplies()[0] reply = talkback.getReplies()[0]
@ -96,7 +96,7 @@ class ModerationViewTest(PloneTestCase):
self.failUnless('Jim' in reply.listCreators()) self.failUnless('Jim' in reply.listCreators())
self.assertEquals(talkback.replyCount(self.portal.doc1), 1) self.assertEquals(talkback.replyCount(self.portal.doc1), 1)
self.assertEquals(reply.inReplyTo(), self.portal.doc1) self.assertEquals(reply.inReplyTo(), self.portal.doc1)
# Make sure only the two new comments are shown # Make sure only the two new comments are shown
self.view() self.view()
self.assertEquals(len(self.view.comments), 3) self.assertEquals(len(self.view.comments), 3)
@ -110,10 +110,10 @@ class ModerationBulkActionsViewTest(PloneTestCase):
self.loginAsPortalOwner() self.loginAsPortalOwner()
typetool = self.portal.portal_types typetool = self.portal.portal_types
typetool.constructContent('Document', self.portal, 'doc1') typetool.constructContent('Document', self.portal, 'doc1')
self.wf = getToolByName(self.portal, self.wf = getToolByName(self.portal,
'portal_workflow', 'portal_workflow',
None) None)
self.request = self.app.REQUEST self.request = self.app.REQUEST
self.context = self.portal self.context = self.portal
self.portal.portal_workflow.setChainForPortalTypes( self.portal.portal_workflow.setChainForPortalTypes(
@ -149,7 +149,7 @@ class ModerationBulkActionsViewTest(PloneTestCase):
'++conversation++default/%s' % new_id_3) '++conversation++default/%s' % new_id_3)
self.conversation = conversation self.conversation = conversation
def test_default_bulkaction(self): def test_default_bulkaction(self):
# Make sure no error is raised when no bulk actions has been supplied # Make sure no error is raised when no bulk actions has been supplied
self.request = self.app.REQUEST self.request = self.app.REQUEST
@ -158,14 +158,14 @@ class ModerationBulkActionsViewTest(PloneTestCase):
self.request.set('paths', ['/'.join(self.comment1.getPhysicalPath())]) self.request.set('paths', ['/'.join(self.comment1.getPhysicalPath())])
view = BulkActionsView(self.context, self.request) view = BulkActionsView(self.context, self.request)
self.failIf(view()) self.failIf(view())
def test_retract(self): def test_retract(self):
self.request = self.app.REQUEST self.request = self.app.REQUEST
self.context = self.portal self.context = self.portal
self.request.set('form.select.BulkAction', 'retract') self.request.set('form.select.BulkAction', 'retract')
self.request.set('paths', ['/'.join(self.comment1.getPhysicalPath())]) self.request.set('paths', ['/'.join(self.comment1.getPhysicalPath())])
view = BulkActionsView(self.context, self.request) view = BulkActionsView(self.context, self.request)
self.assertRaises(NotImplementedError, self.assertRaises(NotImplementedError,
view) view)
@ -173,10 +173,10 @@ class ModerationBulkActionsViewTest(PloneTestCase):
self.request = self.app.REQUEST self.request = self.app.REQUEST
self.context = self.portal self.context = self.portal
self.request.set('form.select.BulkAction', 'publish') self.request.set('form.select.BulkAction', 'publish')
self.request.set('paths', ['/'.join(self.comment1.getPhysicalPath())]) self.request.set('paths', ['/'.join(self.comment1.getPhysicalPath())])
view = BulkActionsView(self.context, self.request) view = BulkActionsView(self.context, self.request)
view() view()
# Count published comments # Count published comments
published_comments = 0 published_comments = 0
for r in self.conversation.getThreads(): for r in self.conversation.getThreads():
@ -184,17 +184,17 @@ class ModerationBulkActionsViewTest(PloneTestCase):
workflow_status = self.wf.getInfoFor(comment_obj, 'review_state') workflow_status = self.wf.getInfoFor(comment_obj, 'review_state')
if workflow_status == 'published': if workflow_status == 'published':
published_comments += 1 published_comments += 1
# Make sure the comment has been published # Make sure the comment has been published
self.assertEquals(published_comments, 1) self.assertEquals(published_comments, 1)
def test_mark_as_spam(self): def test_mark_as_spam(self):
self.request = self.app.REQUEST self.request = self.app.REQUEST
self.context = self.portal self.context = self.portal
self.request.set('form.select.BulkAction', 'mark_as_spam') self.request.set('form.select.BulkAction', 'mark_as_spam')
self.request.set('paths', ['/'.join(self.comment1.getPhysicalPath())]) self.request.set('paths', ['/'.join(self.comment1.getPhysicalPath())])
view = BulkActionsView(self.context, self.request) view = BulkActionsView(self.context, self.request)
self.assertRaises(NotImplementedError, self.assertRaises(NotImplementedError,
view) view)
@ -204,14 +204,14 @@ class ModerationBulkActionsViewTest(PloneTestCase):
# Initially we have three comments # Initially we have three comments
self.assertEquals(self.conversation.total_comments, 3) self.assertEquals(self.conversation.total_comments, 3)
# Delete two comments with bulk actions # Delete two comments with bulk actions
self.request.set('form.select.BulkAction', 'delete') self.request.set('form.select.BulkAction', 'delete')
self.request.set('paths', ['/'.join(self.comment1.getPhysicalPath()), self.request.set('paths', ['/'.join(self.comment1.getPhysicalPath()),
'/'.join(self.comment3.getPhysicalPath())]) '/'.join(self.comment3.getPhysicalPath())])
view = BulkActionsView(self.context, self.request) view = BulkActionsView(self.context, self.request)
view() view()
# Make sure that the two comments have been deleted # Make sure that the two comments have been deleted
self.assertEquals(self.conversation.total_comments, 1) self.assertEquals(self.conversation.total_comments, 1)
comment = self.conversation.getComments().next() comment = self.conversation.getComments().next()

View File

@ -50,9 +50,9 @@ class TestUserNotificationUnit(PloneTestCase):
self.portal.MailHost = self.portal._original_MailHost self.portal.MailHost = self.portal._original_MailHost
sm = getSiteManager(context=self.portal) sm = getSiteManager(context=self.portal)
sm.unregisterUtility(provided=IMailHost) sm.unregisterUtility(provided=IMailHost)
sm.registerUtility(aq_base(self.portal._original_MailHost), sm.registerUtility(aq_base(self.portal._original_MailHost),
provided=IMailHost) provided=IMailHost)
def test_notify_user(self): def test_notify_user(self):
# Add a comment with user notification enabled. Add another comment # Add a comment with user notification enabled. Add another comment
# and make sure an email is send to the user of the first comment. # and make sure an email is send to the user of the first comment.
@ -74,7 +74,7 @@ class TestUserNotificationUnit(PloneTestCase):
# We expect the headers to be properly header encoded (7-bit): # We expect the headers to be properly header encoded (7-bit):
#>>> 'Subject: =?utf-8?q?Some_t=C3=A4st_subject=2E?=' in msg #>>> 'Subject: =?utf-8?q?Some_t=C3=A4st_subject=2E?=' in msg
#True #True
# # 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):
#>>> msg #>>> msg
#'...Another t=C3=A4st message...You are receiving this mail \ #'...Another t=C3=A4st message...You are receiving this mail \
@ -97,9 +97,9 @@ class TestUserNotificationUnit(PloneTestCase):
comment = createObject('plone.Comment') comment = createObject('plone.Comment')
comment.text = 'Comment text' comment.text = 'Comment text'
self.conversation.addComment(comment) self.conversation.addComment(comment)
self.assertEquals(len(self.mailhost.messages), 0) self.assertEquals(len(self.mailhost.messages), 0)
def test_do_not_notify_user_when_email_address_is_given(self): def test_do_not_notify_user_when_email_address_is_given(self):
comment = createObject('plone.Comment') comment = createObject('plone.Comment')
comment.text = 'Comment text' comment.text = 'Comment text'
@ -109,7 +109,7 @@ class TestUserNotificationUnit(PloneTestCase):
comment = createObject('plone.Comment') comment = createObject('plone.Comment')
comment.text = 'Comment text' comment.text = 'Comment text'
self.conversation.addComment(comment) self.conversation.addComment(comment)
self.assertEquals(len(self.mailhost.messages), 0) self.assertEquals(len(self.mailhost.messages), 0)
def test_do_not_notify_user_when_no_sender_is_available(self): def test_do_not_notify_user_when_no_sender_is_available(self):
@ -126,7 +126,7 @@ class TestUserNotificationUnit(PloneTestCase):
comment = createObject('plone.Comment') comment = createObject('plone.Comment')
comment.text = 'Comment text' comment.text = 'Comment text'
self.conversation.addComment(comment) self.conversation.addComment(comment)
self.assertEquals(len(self.mailhost.messages), 0) self.assertEquals(len(self.mailhost.messages), 0)
def test_notify_only_once(self): def test_notify_only_once(self):
@ -179,13 +179,13 @@ class TestModeratorNotificationUnit(PloneTestCase):
# We need to fake a valid mail setup # We need to fake a valid mail setup
self.portal.email_from_address = "portal@plone.test" self.portal.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
self.portal.portal_workflow.setChainForPortalTypes( self.portal.portal_workflow.setChainForPortalTypes(
('Discussion Item',), ('Discussion Item',),
('comment_review_workflow',)) ('comment_review_workflow',))
# Enable moderator notification setting # Enable moderator notification setting
registry = queryUtility(IRegistry) registry = queryUtility(IRegistry)
registry['plone.app.discussion.interfaces.IDiscussionSettings.' + registry['plone.app.discussion.interfaces.IDiscussionSettings.' +
@ -201,9 +201,9 @@ class TestModeratorNotificationUnit(PloneTestCase):
self.portal.MailHost = self.portal._original_MailHost self.portal.MailHost = self.portal._original_MailHost
sm = getSiteManager(context=self.portal) sm = getSiteManager(context=self.portal)
sm.unregisterUtility(provided=IMailHost) sm.unregisterUtility(provided=IMailHost)
sm.registerUtility(aq_base(self.portal._original_MailHost), sm.registerUtility(aq_base(self.portal._original_MailHost),
provided=IMailHost) provided=IMailHost)
def test_notify_moderator(self): def test_notify_moderator(self):
# Add a comment and make sure an email is send to the moderator. # Add a comment and make sure an email is send to the moderator.
comment = createObject('plone.Comment') comment = createObject('plone.Comment')
@ -213,7 +213,7 @@ class TestModeratorNotificationUnit(PloneTestCase):
self.assertEquals(len(self.mailhost.messages), 1) self.assertEquals(len(self.mailhost.messages), 1)
self.failUnless(self.mailhost.messages[0]) self.failUnless(self.mailhost.messages[0])
msg = self.mailhost.messages[0] msg = self.mailhost.messages[0]
if not isinstance(msg, str): if not isinstance(msg, str):
# Plone 3 # Plone 3
self.failUnless('portal@plone.test' in msg.mfrom) self.failUnless('portal@plone.test' in msg.mfrom)
@ -221,17 +221,17 @@ class TestModeratorNotificationUnit(PloneTestCase):
else: else:
#Plone 4 #Plone 4
self.failUnless('To: portal@plone.test' in msg) self.failUnless('To: portal@plone.test' in msg)
self.failUnless('From: portal@plone.test' in msg) self.failUnless('From: portal@plone.test' in msg)
#We expect the headers to be properly header encoded (7-bit): #We expect the headers to be properly header encoded (7-bit):
#>>> 'Subject: =?utf-8?q?Some_t=C3=A4st_subject=2E?=' in msg #>>> 'Subject: =?utf-8?q?Some_t=C3=A4st_subject=2E?=' in msg
#True #True
#The output should be encoded in a reasonable manner (in this case #The output should be encoded in a reasonable manner (in this case
# quoted-printable): # quoted-printable):
#>>> msg #>>> msg
#'...Another t=C3=A4st message...You are receiving this mail because #'...Another t=C3=A4st message...You are receiving this mail because
# T=C3=A4st user\ntest@plone.test...is sending feedback about the site # T=C3=A4st user\ntest@plone.test...is sending feedback about the site
# you administer at... # you administer at...
def test_do_not_notify_moderator_when_no_sender_is_available(self): def test_do_not_notify_moderator_when_no_sender_is_available(self):
@ -243,9 +243,9 @@ class TestModeratorNotificationUnit(PloneTestCase):
comment.text = 'Comment text' comment.text = 'Comment text'
self.conversation.addComment(comment) self.conversation.addComment(comment)
self.assertEquals(len(self.mailhost.messages), 0) self.assertEquals(len(self.mailhost.messages), 0)
def test_do_not_notify_moderator_when_notification_is_disabled(self): def test_do_not_notify_moderator_when_notification_is_disabled(self):
# Disable moderator notification setting and make sure no email is send # Disable moderator notification setting and make sure no email is send
# to the moderator. # to the moderator.
registry = queryUtility(IRegistry) registry = queryUtility(IRegistry)
registry['plone.app.discussion.interfaces.IDiscussionSettings.' + registry['plone.app.discussion.interfaces.IDiscussionSettings.' +
@ -263,7 +263,7 @@ class TestModeratorNotificationUnit(PloneTestCase):
self.portal.portal_workflow.setChainForPortalTypes( self.portal.portal_workflow.setChainForPortalTypes(
('Discussion Item',), ('Discussion Item',),
('one_state_workflow',)) ('one_state_workflow',))
comment = createObject('plone.Comment') comment = createObject('plone.Comment')
comment.text = 'Comment text' comment.text = 'Comment text'
self.conversation.addComment(comment) self.conversation.addComment(comment)

View File

@ -14,8 +14,8 @@ class ToolTest(PloneTestCase):
def afterSetUp(self): def afterSetUp(self):
# First we need to create some content. # First we need to create some content.
self.loginAsPortalOwner() self.loginAsPortalOwner()
self.portal.invokeFactory(id='doc1', self.portal.invokeFactory(id='doc1',
title='Document 1', title='Document 1',
type_name='Document') type_name='Document')
def test_tool_indexing(self): def test_tool_indexing(self):

View File

@ -23,7 +23,7 @@ class WorkflowSetupTest(PloneTestCase):
""" """
layer = DiscussionLayer layer = DiscussionLayer
def afterSetUp(self): def afterSetUp(self):
"""Create a document and allow discussion. """Create a document and allow discussion.
""" """
@ -31,13 +31,13 @@ class WorkflowSetupTest(PloneTestCase):
self.portal_discussion = self.portal.portal_discussion self.portal_discussion = self.portal.portal_discussion
self.folder.invokeFactory('Document', 'doc1') self.folder.invokeFactory('Document', 'doc1')
self.doc = self.folder.doc1 self.doc = self.folder.doc1
def test_workflows_installed(self): def test_workflows_installed(self):
"""Make sure both comment workflows have been installed properly. """Make sure both comment workflows have been installed properly.
""" """
self.failUnless('one_state_workflow' in self.failUnless('one_state_workflow' in
self.portal.portal_workflow.objectIds()) self.portal.portal_workflow.objectIds())
self.failUnless('comment_review_workflow' in self.failUnless('comment_review_workflow' in
self.portal.portal_workflow.objectIds()) self.portal.portal_workflow.objectIds())
def test_default_workflow(self): def test_default_workflow(self):
@ -46,7 +46,7 @@ class WorkflowSetupTest(PloneTestCase):
self.assertEquals(('one_state_workflow',), self.assertEquals(('one_state_workflow',),
self.portal.portal_workflow.getChainForPortalType( self.portal.portal_workflow.getChainForPortalType(
'Discussion Item')) 'Discussion Item'))
def test_review_comments_permission(self): def test_review_comments_permission(self):
#'Review comments' in self.portal.permissionsOfRole('Admin') #'Review comments' in self.portal.permissionsOfRole('Admin')
@ -65,12 +65,12 @@ class PermissionsSetupTest(PloneTestCase):
""" """
layer = DiscussionLayer layer = DiscussionLayer
def afterSetUp(self): def afterSetUp(self):
portal = self.portal portal = self.portal
mtool = self.portal.portal_membership mtool = self.portal.portal_membership
self.checkPermission = mtool.checkPermission self.checkPermission = mtool.checkPermission
def test_reply_to_item_permission_assigned(self): def test_reply_to_item_permission_assigned(self):
"""Make sure the 'Reply to item' permission is properly assigned. """Make sure the 'Reply to item' permission is properly assigned.
By default this permission is assigned to 'Member' and 'Manager'. By default this permission is assigned to 'Member' and 'Manager'.
@ -96,7 +96,7 @@ class CommentOneStateWorkflowTest(PloneTestCase):
""" """
layer = DiscussionLayer layer = DiscussionLayer
def afterSetUp(self): def afterSetUp(self):
"""Create a document with comments and enable the one. """Create a document with comments and enable the one.
""" """
@ -106,31 +106,31 @@ class CommentOneStateWorkflowTest(PloneTestCase):
'one_state_workflow') 'one_state_workflow')
self.folder.invokeFactory('Document', 'doc1') self.folder.invokeFactory('Document', 'doc1')
self.doc = self.folder.doc1 self.doc = self.folder.doc1
# Add a comment # Add a comment
conversation = IConversation(self.folder.doc1) conversation = IConversation(self.folder.doc1)
comment = createObject('plone.Comment') comment = createObject('plone.Comment')
comment.text = 'Comment text' comment.text = 'Comment text'
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/%s' % cid)
self.portal.acl_users._doAddUser('member', 'secret', ['Member'], []) self.portal.acl_users._doAddUser('member', 'secret', ['Member'], [])
self.portal.acl_users._doAddUser('reviewer', 'secret', ['Reviewer'], []) self.portal.acl_users._doAddUser('reviewer', 'secret', ['Reviewer'], [])
self.portal.acl_users._doAddUser('manager', 'secret', ['Manager'], []) self.portal.acl_users._doAddUser('manager', 'secret', ['Manager'], [])
self.portal.acl_users._doAddUser('editor' , ' secret', ['Editor'],[]) self.portal.acl_users._doAddUser('editor' , ' secret', ['Editor'],[])
self.portal.acl_users._doAddUser('reader', 'secret', ['Reader'], []) self.portal.acl_users._doAddUser('reader', 'secret', ['Reader'], [])
def test_initial_workflow_state(self): def test_initial_workflow_state(self):
"""Make sure the initial workflow state of a comment is 'published'. """Make sure the initial workflow state of a comment is 'published'.
""" """
self.assertEqual(self.workflow.getInfoFor(self.doc, 'review_state'), self.assertEqual(self.workflow.getInfoFor(self.doc, 'review_state'),
'published') 'published')
def test_view_comments(self): def test_view_comments(self):
"""Make sure published comments can be viewed by everyone. """Make sure published comments can be viewed by everyone.
""" """
# Owner is allowed # Owner is allowed
#self.login(default_user) #self.login(default_user)
#self.failUnless(checkPerm(View, self.doc)) #self.failUnless(checkPerm(View, self.doc))
@ -149,14 +149,14 @@ class CommentOneStateWorkflowTest(PloneTestCase):
# Reader is allowed # Reader is allowed
self.login('reader') self.login('reader')
self.failUnless(checkPerm(View, self.comment)) self.failUnless(checkPerm(View, self.comment))
class CommentReviewWorkflowTest(PloneTestCase): class CommentReviewWorkflowTest(PloneTestCase):
"""Test the comment_review_workflow that ships with plone.app.discussion. """Test the comment_review_workflow that ships with plone.app.discussion.
""" """
layer = DiscussionLayer layer = DiscussionLayer
def afterSetUp(self): def afterSetUp(self):
# Allow discussion and # Allow discussion and
self.loginAsPortalOwner() self.loginAsPortalOwner()
@ -217,7 +217,7 @@ class CommentReviewWorkflowTest(PloneTestCase):
def test_publish(self): def test_publish(self):
self.portal.REQUEST.form['comment_id'] = self.comment_id self.portal.REQUEST.form['comment_id'] = self.comment_id
self.portal.REQUEST.form['workflow_action'] = 'publish' self.portal.REQUEST.form['workflow_action'] = 'publish'
self.assertEquals('pending', self.assertEquals('pending',
self.portal.portal_workflow.getInfoFor( self.portal.portal_workflow.getInfoFor(
self.comment, 'review_state')) self.comment, 'review_state'))
view = self.comment.restrictedTraverse('@@moderate-publish-comment') view = self.comment.restrictedTraverse('@@moderate-publish-comment')

View File

@ -15,18 +15,18 @@ from OFS.SimpleItem import SimpleItem
class CommentingTool(UniqueObject, SimpleItem): class CommentingTool(UniqueObject, SimpleItem):
interface.implements(ICommentingTool) interface.implements(ICommentingTool)
meta_type = 'plone.app.discussion tool' meta_type = 'plone.app.discussion tool'
id = 'portal_discussion' id = 'portal_discussion'
def reindexObject(self, object): def reindexObject(self, object):
"""Remove from catalog. """Remove from catalog.
""" """
catalog = getToolByName(self, 'portal_catalog') catalog = getToolByName(self, 'portal_catalog')
return catalog.reindexObject(object) return catalog.reindexObject(object)
indexObject = reindexObject indexObject = reindexObject
def unindexObject(self, object): def unindexObject(self, object):
@ -34,7 +34,7 @@ class CommentingTool(UniqueObject, SimpleItem):
""" """
catalog = getToolByName(self, 'portal_catalog') catalog = getToolByName(self, 'portal_catalog')
return catalog.unindexObject(object) return catalog.unindexObject(object)
def uniqueValuesFor(self, name): def uniqueValuesFor(self, name):
""" return unique values for FieldIndex name """ """ return unique values for FieldIndex name """
catalog = getToolByName(self, 'portal_catalog') catalog = getToolByName(self, 'portal_catalog')
@ -47,14 +47,14 @@ class CommentingTool(UniqueObject, SimpleItem):
""" """
catalog = getToolByName(self, 'portal_catalog') catalog = getToolByName(self, 'portal_catalog')
object_provides = [IComment.__identifier__] object_provides = [IComment.__identifier__]
if 'object_provides' in kw: if 'object_provides' in kw:
kw_provides = kw['object_provides'] kw_provides = kw['object_provides']
if isinstance(str, kw_provides): if isinstance(str, kw_provides):
object_provides.append(kw_provides) object_provides.append(kw_provides)
else: else:
object_provides.extend(kw_provides) object_provides.extend(kw_provides)
if REQUEST is not None and 'object_provides' in REQUEST.form: if REQUEST is not None and 'object_provides' in REQUEST.form:
rq_provides = REQUEST.form['object_provides'] rq_provides = REQUEST.form['object_provides']
del REQUEST.form['object_provides'] del REQUEST.form['object_provides']
@ -62,7 +62,7 @@ class CommentingTool(UniqueObject, SimpleItem):
object_provides.append(rq_provides) object_provides.append(rq_provides)
else: else:
object_provides.extend(rq_provides) object_provides.extend(rq_provides)
kw['object_provides'] = object_provides kw['object_provides'] = object_provides
return catalog.searchResults(REQUEST, **kw) return catalog.searchResults(REQUEST, **kw)
@ -72,7 +72,7 @@ def index_object(obj, event):
tool = queryUtility(ICommentingTool) tool = queryUtility(ICommentingTool)
if tool is not None: if tool is not None:
tool.indexObject(obj) tool.indexObject(obj)
def unindex_object(obj, event): def unindex_object(obj, event):
"""Unindex the object when removed """Unindex the object when removed
""" """

View File

@ -2,7 +2,7 @@
from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm
from plone.app.discussion.interfaces import _ from plone.app.discussion.interfaces import _
HAS_CAPTCHA = False HAS_CAPTCHA = False
try: try:
@ -55,17 +55,17 @@ def captcha_vocabulary(context):
SimpleTerm( SimpleTerm(
value='recaptcha', value='recaptcha',
token='recaptcha', token='recaptcha',
title='ReCaptcha')) title='ReCaptcha'))
if HAS_AKISMET: # pragma: no cover if HAS_AKISMET: # pragma: no cover
terms.append( terms.append(
SimpleTerm( SimpleTerm(
value='akismet', value='akismet',
token='akismet', token='akismet',
title='Akismet')) title='Akismet'))
if HAS_NOROBOTS: # pragma: no cover if HAS_NOROBOTS: # pragma: no cover
terms.append( terms.append(
SimpleTerm( SimpleTerm(
value='norobots', value='norobots',
token='norobots', token='norobots',