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):
"""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.
"""
adapts(Interface, IDefaultBrowserLayer, CommentForm) # context, request, form
@ -72,4 +72,4 @@ class CaptchaExtender(extensible.FormExtender):
self.form.fields['captcha'].widgetFactory = NorobotsFieldWidget
else:
self.form.fields['captcha'].mode = interfaces.HIDDEN_MODE

View File

@ -1,15 +1,15 @@
<configure
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"
i18n_domain="plone.app.discussion">
<!--
<!--
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
installed.
installed.
-->
<configure zcml:condition="installed plone.formwidget.captcha">
<meta:provides feature="plone.app.discussion-captcha" />
@ -21,11 +21,11 @@
<!-- Captcha comment form extender -->
<configure zcml:condition="have plone.app.discussion-captcha">
<!--
<!--
Register the Captcha form extender and validator only if there are
plugins installed that declare to implement a Captcha solution for
plone.app.discussion (e.g. plone.formwidget.captcha and
plone.formwidget.recaptcha).
plone.app.discussion (e.g. plone.formwidget.captcha and
plone.formwidget.recaptcha).
-->
<adapter
factory=".captcha.Captcha"
@ -44,7 +44,7 @@
<adapter
factory="collective.akismet.validator.AkismetValidator"
provides="z3c.form.interfaces.IValidator"
/>
/>
</configure>
</configure>
</configure>

View File

@ -5,15 +5,15 @@ from Products.Five.browser import BrowserView
class View(BrowserView):
"""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
has been posted.
Redirect from the comment object URL
"/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".
Context is the comment object. The parent of the comment object is the
conversation. The parent of the conversation is the content object where
the comment has been posted.
@ -24,4 +24,4 @@ class View(BrowserView):
self.request.response.redirect(
aq_parent(aq_parent(context)).absolute_url() +
'#' + str(context.id)
)
)

View File

@ -58,7 +58,7 @@
tal:attributes="src portrait_url;
alt reply/Creator" />
</div>
<div class="documentByLine" i18n:domain="plone.app.discussion">
<tal:name>
<a href=""
@ -72,16 +72,16 @@
<span tal:condition="not: reply/Creator">Anonymous</span>
</tal:name>
<tal:posted i18n:translate="label_says">says:</tal:posted>
<div class="commentDate"
<div class="commentDate"
tal:content="python:view.format_time(reply.modification_date)">
8/23/2001 12:40:44 PM
</div>
</div>
<div class="commentBody">
<span tal:replace="structure python:view.cook(reply.getText())" />
<div class="commentActions">
<form name="delete"
action=""
@ -96,7 +96,7 @@
i18n:attributes="value label_delete;"
/>
</form>
<!-- Workflow actions (e.g. 'publish') -->
<form name=""
action=""
@ -115,7 +115,7 @@
/>
</form>
</div>
</div>
<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.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:
from plone.z3cform.interfaces import IWrappedForm
HAS_WRAPPED_FORM = True
from plone.z3cform.interfaces import IWrappedForm
HAS_WRAPPED_FORM = True
except ImportError: # pragma: no cover
HAS_WRAPPED_FORM = False
@ -87,48 +87,48 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
# Widgets
self.widgets['in_reply_to'].mode = interfaces.HIDDEN_MODE
self.widgets['text'].addClass("autoresize")
self.widgets['user_notification'].label = _(u"")
self.widgets['user_notification'].label = _(u"")
# Anonymous / Logged-in
portal_membership = getToolByName(self.context, 'portal_membership')
if not portal_membership.isAnonymousUser():
self.widgets['author_name'].mode = interfaces.HIDDEN_MODE
self.widgets['author_email'].mode = interfaces.HIDDEN_MODE
# 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
# integrators or later use.
# 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
# integrators or later use.
self.widgets['author_email'].mode = interfaces.HIDDEN_MODE
registry = queryUtility(IRegistry)
settings = registry.forInterface(IDiscussionSettings, check=False)
portal_membership = getToolByName(self.context, 'portal_membership')
if not settings.user_notification_enabled or portal_membership.isAnonymousUser():
self.widgets['user_notification'].mode = interfaces.HIDDEN_MODE
def updateActions(self):
super(CommentForm, self).updateActions()
super(CommentForm, self).updateActions()
self.actions['cancel'].addClass("standalone")
self.actions['cancel'].addClass("hide")
self.actions['comment'].addClass("context")
@button.buttonAndHandler(_(u"add_comment_button", default=u"Comment"),
self.actions['cancel'].addClass("hide")
self.actions['comment'].addClass("context")
@button.buttonAndHandler(_(u"add_comment_button", default=u"Comment"),
name='comment')
def handleComment(self, action):
context = aq_inner(self.context)
wf = getToolByName(context, 'portal_workflow')
data, errors = self.extractData()
if errors:
return
text = u""
author_name = u""
author_email = u""
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)
registry = queryUtility(IRegistry)
settings = registry.forInterface(IDiscussionSettings, check=False)
@ -138,10 +138,10 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
portal_membership.isAnonymousUser():
if not 'captcha' in data:
data['captcha'] = u""
captcha = CaptchaValidator(self.context,
self.request,
None,
ICaptcha['captcha'],
captcha = CaptchaValidator(self.context,
self.request,
None,
ICaptcha['captcha'],
None)
captcha.validate(data['captcha'])
@ -156,10 +156,10 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
author_email = data['author_email']
if 'user_notification' in data:
user_notification = data['user_notification']
# The add-comment view is called on the conversation object
conversation = IConversation(self.__parent__)
# Check if conversation is enabled on this content object
if not conversation.enabled():
raise Unauthorized, "Discussion is not enabled for this content\
@ -175,10 +175,10 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
comment.text = text
portal_membership = getToolByName(self.context, 'portal_membership')
can_reply = getSecurityManager().checkPermission('Reply to item',
context)
if portal_membership.isAnonymousUser() and \
settings.anonymous_comments:
# Anonymous Users
@ -211,7 +211,7 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
"""Anonymous user tries to post a comment, but
anonymous commenting is disabled. Or user
does not have the 'reply to item' permission.""" # pragma: no cover
# Check if the added comment is a reply to an existing comment
# or just a regular reply to the content object.
if data['in_reply_to']:
@ -221,11 +221,11 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
# Add a comment to the conversation
comment_id = conversation.addComment(comment)
# 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
# has 'review comments' permission, he/she is redirected directly
# 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
# has 'review comments' permission, he/she is redirected directly
# to the comment.
can_review = getSecurityManager().checkPermission('Review comments',
can_review = getSecurityManager().checkPermission('Review comments',
context)
comment_review_state = wf.getInfoFor(comment, 'review_state')
if comment_review_state == 'pending' and not can_review:
@ -254,8 +254,8 @@ class CommentsViewlet(ViewletBase):
super(CommentsViewlet, self).update()
z2.switch_on(self, request_layer=IFormLayer)
self.form = self.form(aq_inner(self.context), self.request)
if HAS_WRAPPED_FORM:
alsoProvides(self.form, IWrappedForm)
if HAS_WRAPPED_FORM:
alsoProvides(self.form, IWrappedForm)
self.form.update()
# view methods
@ -265,16 +265,16 @@ class CommentsViewlet(ViewletBase):
targetMimetype = 'text/html'
registry = queryUtility(IRegistry)
settings = registry.forInterface(IDiscussionSettings, check=False)
mimetype = settings.text_transform
return transforms.convertTo(targetMimetype,
text,
context=self,
mimetype = settings.text_transform
return transforms.convertTo(targetMimetype,
text,
context=self,
mimetype=mimetype).getData()
def can_reply(self):
"""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))
def can_manage(self):
@ -282,13 +282,13 @@ class CommentsViewlet(ViewletBase):
not want any API changes in beta releases.
"""
return self.can_review()
def can_review(self):
"""Returns true if current user has the 'Review comments' permission.
"""
return getSecurityManager().checkPermission('Review comments',
return getSecurityManager().checkPermission('Review comments',
aq_inner(self.context))
def is_discussion_allowed(self):
context = aq_inner(self.context)
conversation = IConversation(context)
@ -301,7 +301,7 @@ class CommentsViewlet(ViewletBase):
"""
registry = queryUtility(IRegistry)
settings = registry.forInterface(IDiscussionSettings, check=False)
if settings.text_transform == "text/x-web-intelligent":
message = translate(Message(COMMENT_DESCRIPTION_INTELLIGENT_TEXT))
else:
@ -356,7 +356,7 @@ class CommentsViewlet(ViewletBase):
r = r.copy()
r['workflow_status'] = workflow_status
yield r
# Return all direct replies
if conversation.total_comments > 0:
if workflow_actions:
@ -395,11 +395,11 @@ class CommentsViewlet(ViewletBase):
return settings.show_commenter_image
def is_anonymous(self):
portal_membership = getToolByName(self.context,
'portal_membership',
portal_membership = getToolByName(self.context,
'portal_membership',
None)
return portal_membership.isAnonymousUser()
def login_action(self):
return '%s/login_form?came_from=%s' % \
(self.navigation_root_url,

View File

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

View File

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

View File

@ -21,7 +21,7 @@ from zope.component import getMultiAdapter, queryUtility
from z3c.form import button
from z3c.form.browser.checkbox import SingleCheckBoxFieldWidget
from plone.app.discussion.interfaces import IDiscussionSettings, _
@ -33,7 +33,7 @@ class DiscussionSettingsEditForm(controlpanel.RegistryEditForm):
description = _(u"help_discussion_settings_editform",
default=u"Some discussion related settings are not located "
"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 "
"choose \"Allow comments\".\n"
"To enable the moderation workflow for comments, "
@ -73,18 +73,18 @@ class DiscussionSettingsEditForm(controlpanel.RegistryEditForm):
self.status = self.formErrorsMessage
return
changes = self.applyChanges(data)
IStatusMessage(self.request).addStatusMessage(_(u"Changes saved"),
IStatusMessage(self.request).addStatusMessage(_(u"Changes saved"),
"info")
self.context.REQUEST.RESPONSE.redirect("@@discussion-settings")
@button.buttonAndHandler(_('Cancel'), name='cancel')
def handleCancel(self, action):
IStatusMessage(self.request).addStatusMessage(_(u"Edit cancelled"),
IStatusMessage(self.request).addStatusMessage(_(u"Edit cancelled"),
"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))
class DiscussionSettingsControlPanel(controlpanel.ControlPanelFormWrapper):
"""Discussion settings control panel.
"""
@ -100,22 +100,22 @@ class DiscussionSettingsControlPanel(controlpanel.ControlPanelFormWrapper):
wftool = getToolByName(self.context, "portal_workflow", None)
wf = wftool.getChainForPortalType('Discussion Item')
output = []
# Globally enabled
if settings.globally_enabled:
output.append("globally_enabled")
# Comment moderation
if 'one_state_workflow' not in wf and \
'comment_review_workflow' not in wf:
output.append("moderation_custom")
output.append("moderation_custom")
elif settings.moderation_enabled:
output.append("moderation_enabled")
# Anonymous comments
if settings.anonymous_comments:
output.append("anonymous_comments")
# Invalid mail setting
ctrlOverview = getMultiAdapter((self.context, self.request),
name='overview-controlpanel')
@ -127,7 +127,7 @@ class DiscussionSettingsControlPanel(controlpanel.ControlPanelFormWrapper):
discussion_workflow = wftool.getChainForPortalType('Discussion Item')[0]
if discussion_workflow:
output.append(discussion_workflow)
# Merge all settings into one string
return ' '.join(output)
@ -152,27 +152,27 @@ class DiscussionSettingsControlPanel(controlpanel.ControlPanelFormWrapper):
if 'one_state_workflow' in wf or 'comment_review_workflow' in wf:
return
return True
def notify_configuration_changed(event):
"""Event subscriber that is called every time the configuration changed.
"""
portal = getSite()
wftool = getToolByName(portal, 'portal_workflow', None)
if IRecordModifiedEvent.providedBy(event):
# Discussion control panel setting changed
if event.record.fieldName == 'moderation_enabled':
# Moderation enabled has changed
if event.record.value == True:
# Enable moderation workflow
wftool.setChainForPortalTypes(('Discussion Item',),
'comment_review_workflow')
wftool.setChainForPortalTypes(('Discussion Item',),
'comment_review_workflow')
else:
# Disable moderation workflow
wftool.setChainForPortalTypes(('Discussion Item',),
wftool.setChainForPortalTypes(('Discussion Item',),
'one_state_workflow')
if IConfigurationChangedEvent.providedBy(event):
# Types control panel setting changed
if 'workflow' in event.data:

View File

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

View File

@ -33,11 +33,11 @@
<a i18n:name="enable_comment_workflow"
i18n:translate="message_enable_comment_workflow" href=""
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.
</dd>
</dl>
<form tal:condition="not:items">
<fieldset id="fieldset-moderate-comments" class="formPanel">
<p id="no-comments-message" i18n:translate="message_nothing_to_moderate">
@ -114,7 +114,7 @@
<a href=""
tal:attributes="href string:${item/getURL}/getText"
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>
</td>
<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 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.
# This error occured on Plone 3.3.x only!
#
# Source:
# Source:
# http://athenageek.wordpress.com/2008/01/08/
# contentproviderlookuperror-plonehtmlhead/
#
#
# 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):
self = self.aq_parent
return self
ZopeTwoPageTemplateFile._getContext = _getContext # pragma: no cover
# End ugly hack.
@ -61,7 +61,7 @@ class View(BrowserView):
return text
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
a 'pending' workflow state.
"""
@ -78,7 +78,7 @@ class View(BrowserView):
class ModerateCommentsEnabled(BrowserView):
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
a 'pending' workflow state.
"""
@ -91,26 +91,26 @@ class ModerateCommentsEnabled(BrowserView):
else:
return
class DeleteComment(BrowserView):
"""Delete a comment from a conversation.
This view is always called directly on the comment object:
http://nohost/front-page/++conversation++default/1286289644723317/\
@@moderate-delete-comment
Each table row (comment) in the moderation view contains a hidden input
field with the absolute URL of the content object:
<input type="hidden"
<input type="hidden"
value="http://nohost/front-page/++conversation++default/\
1286289644723317"
1286289644723317"
name="selected_obj_paths:list">
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
details.
details.
"""
def __call__(self):
@ -132,23 +132,23 @@ class DeleteComment(BrowserView):
class PublishComment(BrowserView):
"""Publish a comment.
This view is always called directly on the comment object:
http://nohost/front-page/++conversation++default/1286289644723317/\
@@moderate-publish-comment
Each table row (comment) in the moderation view contains a hidden input
field with the absolute URL of the content object:
<input type="hidden"
<input type="hidden"
value="http://nohost/front-page/++conversation++default/\
1286289644723317"
1286289644723317"
name="selected_obj_paths:list">
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
details.
details.
"""
def __call__(self):
@ -171,24 +171,24 @@ class PublishComment(BrowserView):
class BulkActionsView(BrowserView):
"""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:
<input type="checkbox"
name="paths:list"
value="/plone/front-page/++conversation++default/\
1286289644723317"
1286289644723317"
id="cb_1286289644723317" />
If checked, the comment path will occur in the 'paths' variable of
the request when the bulk actions view is called. The bulk action
(delete, publish, etc.) will be applied to all comments that are
If checked, the comment path will occur in the 'paths' variable of
the request when the bulk actions view is called. The bulk action
(delete, publish, etc.) will be applied to all comments that are
included.
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):
"""Publishes all comments in the paths variable.
Expects a list of absolute paths (without host and port):
/Plone/startseite/++conversation++default/1286200010610352
"""
context = aq_inner(self.context)
for path in self.paths:
@ -240,12 +240,12 @@ class BulkActionsView(BrowserView):
def delete(self):
"""Deletes all comments in the paths variable.
Expects a list of absolute paths (without host and port):
/Plone/startseite/++conversation++default/1286200010610352
"""
"""
context = aq_inner(self.context)
for path in self.paths:
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
does not allow traversal to namespaces with an empty string name.
"""
implements(ITraversable)
adapts(Interface, IBrowserRequest)
def __init__(self, context, request=None):
self.context = context
self.request = request
def traverse(self, name, ignore):
if name == "default":
name = u""
conversation = queryAdapter(self.context, IConversation, name=name)
if conversation is None:
raise TraversalError(name) # pragma: no cover
return conversation

View File

@ -49,7 +49,7 @@ class CaptchaValidator(validator.SimpleFieldValidator):
settings = registry.forInterface(IDiscussionSettings, check=False)
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)
if not captcha.verify(input=value):
if settings.captcha == 'norobots':
@ -61,6 +61,6 @@ class CaptchaValidator(validator.SimpleFieldValidator):
# Register Captcha validator for the Captcha field in the ICaptcha Form
validator.WidgetValidatorDiscriminators(CaptchaValidator,
validator.WidgetValidatorDiscriminators(CaptchaValidator,
field=ICaptcha['captcha'])

View File

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

View File

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

View File

@ -484,6 +484,6 @@ class CommentReplies(ConversationReplies):
comment.in_reply_to = self.comment_id
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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -23,12 +23,12 @@ class PloneAppDiscussion(PloneSandboxLayer):
USER_WITH_FULLNAME_PASSWORD = 'secret'
MANAGER_USER_NAME = 'manager'
MANAGER_USER_PASSWORD = 'secret'
def setUpZope(self, app, configurationContext):
# Load ZCML
import plone.app.discussion
xmlconfig.file('configure.zcml',
plone.app.discussion,
xmlconfig.file('configure.zcml',
plone.app.discussion,
context=configurationContext)
def setUpPloneSite(self, portal):
@ -55,8 +55,8 @@ class PloneAppDiscussion(PloneSandboxLayer):
['Member'],
[],
)
mtool = getToolByName(portal, 'portal_membership', None)
mtool.addMember('jim', 'Jim', ['Member'], [])
mtool = getToolByName(portal, 'portal_membership', None)
mtool.addMember('jim', 'Jim', ['Member'], [])
mtool.getMemberById('jim').setMemberProperties({"fullname": 'Jim Fult\xc3\xb8rn'})
acl_users.userFolderAddUser(
@ -68,8 +68,8 @@ class PloneAppDiscussion(PloneSandboxLayer):
PLONE_APP_DISCUSSION_FIXTURE = PloneAppDiscussion()
PLONE_APP_DISCUSSION_INTEGRATION_TESTING = IntegrationTesting(
bases=(PLONE_APP_DISCUSSION_FIXTURE,),
bases=(PLONE_APP_DISCUSSION_FIXTURE,),
name="PloneAppDiscussion:Integration")
PLONE_APP_DISCUSSION_FUNCTIONAL_TESTING = FunctionalTesting(
bases=(PLONE_APP_DISCUSSION_FIXTURE,),
bases=(PLONE_APP_DISCUSSION_FIXTURE,),
name="PloneAppDiscussion:Functional")

View File

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

View File

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

View File

@ -4,7 +4,7 @@ import time
from datetime import datetime
from AccessControl import Unauthorized
from zope.component import createObject, queryUtility
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 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.interfaces import IDiscussionSettings
@ -44,8 +44,8 @@ class TestCommentForm(PloneTestCase):
self.loginAsPortalOwner()
typetool = self.portal.portal_types
typetool.constructContent('Document', self.portal, 'doc1')
self.dtool = getToolByName(self.portal,
'portal_discussion',
self.dtool = getToolByName(self.portal,
'portal_discussion',
None)
self.dtool.overrideDiscussionFor(self.portal.doc1, False)
self.mtool = getToolByName(self.folder, 'portal_membership', None)
@ -57,11 +57,11 @@ class TestCommentForm(PloneTestCase):
registry = queryUtility(IRegistry)
settings = registry.forInterface(IDiscussionSettings)
settings.globally_enabled = True
def test_add_comment(self):
"""Post a comment as logged-in user.
"""
# Allow discussion
self.dtool.overrideDiscussionFor(self.portal.doc1, True)
self.viewlet = CommentsViewlet(self.context, self.request, None, None)
@ -72,99 +72,99 @@ class TestCommentForm(PloneTestCase):
alsoProvides(request, IFormLayer)
alsoProvides(request, IAttributeAnnotatable)
return request
provideAdapter(adapts=(Interface, IBrowserRequest),
provides=Interface,
factory=CommentForm,
name=u"comment-form")
# The form should return an error if the comment text field is empty
request = make_request(form={})
commentForm = getMultiAdapter((self.context, request),
commentForm = getMultiAdapter((self.context, request),
name=u"comment-form")
commentForm.update()
data, errors = commentForm.extractData() # pylint: disable-msg=W0612
data, errors = commentForm.extractData() # pylint: disable-msg=W0612
self.assertEquals(len(errors), 1)
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
request = make_request(form={'form.widgets.text': u'bar'})
commentForm = getMultiAdapter((self.context, request),
commentForm = getMultiAdapter((self.context, request),
name=u"comment-form")
commentForm.update()
data, errors = commentForm.extractData() # pylint: disable-msg=W0612
data, errors = commentForm.extractData() # pylint: disable-msg=W0612
self.assertEquals(len(errors), 0)
self.failIf(commentForm.handleComment(commentForm, "foo"))
def test_add_anonymous_comment(self):
"""Add a comment as anonymous.
"""
# Allow discussion
self.dtool.overrideDiscussionFor(self.portal.doc1, True)
self.viewlet = CommentsViewlet(self.context, self.request, None, None)
registry = queryUtility(IRegistry)
settings = registry.forInterface(IDiscussionSettings, check=False)
settings.anonymous_comments = True
# Logout
self.logout()
def make_request(form={}):
request = TestRequest()
request.form.update(form)
alsoProvides(request, IFormLayer)
alsoProvides(request, IAttributeAnnotatable)
return request
provideAdapter(adapts=(Interface, IBrowserRequest),
provides=Interface,
factory=CommentForm,
name=u"comment-form")
# Post an anonymous comment and provide a name
request = make_request(form={'form.widgets.name': u'john doe',
'form.widgets.text': u'bar'})
commentForm = getMultiAdapter((self.context, request),
commentForm = getMultiAdapter((self.context, request),
name=u"comment-form")
commentForm.update()
data, errors = commentForm.extractData() # pylint: disable-msg=W0612
data, errors = commentForm.extractData() # pylint: disable-msg=W0612
self.assertEquals(len(errors), 0)
self.failIf(commentForm.handleComment(commentForm, "action"))
def test_can_not_add_comments_if_discussion_is_not_allowed(self):
"""Make sure that comments can't be posted if discussion is disabled.
"""
# Discussion is disabled by default
def make_request(form={}):
request = TestRequest()
request.form.update(form)
alsoProvides(request, IFormLayer)
alsoProvides(request, IAttributeAnnotatable)
return request
provideAdapter(adapts=(Interface, IBrowserRequest),
provides=Interface,
factory=CommentForm,
name=u"comment-form")
request = make_request(form={'form.widgets.text': u'bar'})
commentForm = getMultiAdapter((self.context, request),
commentForm = getMultiAdapter((self.context, request),
name=u"comment-form")
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
# allowed
self.assertEquals(len(errors), 0)
@ -172,52 +172,52 @@ class TestCommentForm(PloneTestCase):
commentForm.handleComment,
commentForm,
"foo")
def test_anonymous_can_not_add_comments_if_discussion_is_not_allowed(self):
"""Make sure that anonymous users can't post comments if anonymous
comments are disabled.
"""
# Anonymous comments are disabled by default
self.logout()
def make_request(form={}):
request = TestRequest()
request.form.update(form)
alsoProvides(request, IFormLayer)
alsoProvides(request, IAttributeAnnotatable)
return request
provideAdapter(adapts=(Interface, IBrowserRequest),
provides=Interface,
factory=CommentForm,
name=u"comment-form")
request = make_request(form={'form.widgets.text': u'bar'})
commentForm = getMultiAdapter((self.context, request),
commentForm = getMultiAdapter((self.context, request),
name=u"comment-form")
commentForm.update()
data, errors = commentForm.extractData() # pylint: disable-msg=W0612
data, errors = commentForm.extractData() # pylint: disable-msg=W0612
self.assertEquals(len(errors), 0)
self.assertRaises(Unauthorized,
commentForm.handleComment,
commentForm,
"foo")
class TestCommentsViewlet(PloneTestCase):
layer = DiscussionLayer
def afterSetUp(self):
self.loginAsPortalOwner()
typetool = self.portal.portal_types
typetool.constructContent('Document', self.portal, 'doc1')
self.portal_discussion = getToolByName(self.portal,
'portal_discussion',
self.portal_discussion = getToolByName(self.portal,
'portal_discussion',
None)
self.mtool = getToolByName(self.folder, 'portal_membership')
self.memberdata = self.portal.portal_memberdata
@ -229,29 +229,29 @@ class TestCommentsViewlet(PloneTestCase):
registry = queryUtility(IRegistry)
settings = registry.forInterface(IDiscussionSettings)
settings.globally_enabled = True
def test_cook(self):
text = """First paragraph
Second paragraph"""
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):
text = """<b>Got HTML?</b>"""
self.assertEquals(self.viewlet.cook(text),
"<p>&lt;b&gt;Got HTML?&lt;/b&gt;</p>")
def test_cook_with_no_ascii_characters(self):
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>")
def test_cook_links(self):
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>")
def test_can_reply(self):
# Portal owner can reply
self.failUnless(self.viewlet.can_reply())
@ -268,7 +268,7 @@ class TestCommentsViewlet(PloneTestCase):
# The reviewer role has the 'Review comments' permission
self.portal.acl_users._doAddUser('reviewer', 'secret', ['Reviewer'], [])
self.login('reviewer')
self.failUnless(self.viewlet.can_review())
self.failUnless(self.viewlet.can_review())
def test_can_manage(self):
"""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
self.portal.acl_users._doAddUser('reviewer', 'secret', ['Reviewer'], [])
self.login('reviewer')
self.failUnless(self.viewlet.can_manage())
self.failUnless(self.viewlet.can_manage())
def test_is_discussion_allowed(self):
# By default, discussion is disabled
self.failIf(self.viewlet.is_discussion_allowed())
@ -309,12 +309,11 @@ class TestCommentsViewlet(PloneTestCase):
# Make sure the comment description is changes accordingly
self.assertEquals(
self.viewlet.comment_transform_message(),
self.viewlet.comment_transform_message(),
"You can add a comment by filling out the form below. " +
"Plain text formatting. Web and email addresses are transformed " +
"into clickable links.")
def test_has_replies(self):
self.assertEquals(self.viewlet.has_replies(), False)
comment = createObject('plone.Comment')
@ -331,7 +330,7 @@ class TestCommentsViewlet(PloneTestCase):
conversation.addComment(comment)
conversation.addComment(comment)
replies = self.viewlet.get_replies()
self.assertEquals(len(tuple(replies)), 2)
self.assertEquals(len(tuple(replies)), 2)
replies = self.viewlet.get_replies()
replies.next()
replies.next()
@ -344,7 +343,7 @@ class TestCommentsViewlet(PloneTestCase):
conversation = IConversation(self.portal.doc1)
c1 = conversation.addComment(comment)
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
self.portal.portal_workflow.setChainForPortalTypes(
('Discussion Item',),
@ -356,8 +355,8 @@ class TestCommentsViewlet(PloneTestCase):
'publish')
self.assertEquals(reply['actions'][0]['url'],
'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):
comment = createObject('plone.Comment')
comment.text = 'Comment text'
@ -369,17 +368,17 @@ class TestCommentsViewlet(PloneTestCase):
def test_get_commenter_home_url_is_none(self):
self.failIf(self.viewlet.get_commenter_home_url())
def test_get_commenter_portrait(self):
# Add a user with a member image
self.mtool.addMember('jim', 'Jim', ['Member'], [])
self.memberdata._setPortrait(Image(id='jim',
self.memberdata._setPortrait(Image(id='jim',
file=dummy.File(),
title=''), 'jim')
self.assertEqual(self.memberdata._getPortrait('jim').getId(),
self.assertEqual(self.memberdata._getPortrait('jim').getId(),
'jim')
self.assertEqual(self.memberdata._getPortrait('jim').meta_type,
self.assertEqual(self.memberdata._getPortrait('jim').meta_type,
'Image')
# Add a conversation with a comment
@ -395,13 +394,13 @@ class TestCommentsViewlet(PloneTestCase):
portrait_url = self.viewlet.get_commenter_portrait('jim')
# Check if the correct member image URL is returned
self.assertEquals(portrait_url,
self.assertEquals(portrait_url,
'http://nohost/plone/portal_memberdata/portraits/jim')
def test_get_commenter_portrait_is_none(self):
self.assertEquals(self.viewlet.get_commenter_portrait(),
self.assertEquals(self.viewlet.get_commenter_portrait(),
'defaultUser.gif')
def test_get_commenter_portrait_without_userimage(self):
# Create a user without a user image
@ -431,14 +430,14 @@ class TestCommentsViewlet(PloneTestCase):
'anonymous_comments'] = True
# Test if anonymous discussion is allowed for the viewlet
self.failUnless(self.viewlet.anonymous_discussion_allowed())
def test_show_commenter_image(self):
self.failUnless(self.viewlet.show_commenter_image())
registry = queryUtility(IRegistry)
registry['plone.app.discussion.interfaces.IDiscussionSettings.' +
'show_commenter_image'] = False
'show_commenter_image'] = False
self.failIf(self.viewlet.show_commenter_image())
def test_is_anonymous(self):
self.failIf(self.viewlet.is_anonymous())
self.logout()
@ -447,8 +446,8 @@ class TestCommentsViewlet(PloneTestCase):
def test_login_action(self):
self.viewlet.update()
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):
python_time = datetime(2009, 02, 01, 23, 32, 03, 57)
# 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)
self.assertEquals(localized_time, "Feb 01, 2009 11:32 PM")
def test_suite():
return unittest.defaultTestLoader.loadTestsFromName(__name__)

View File

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

View File

@ -29,20 +29,20 @@ class ConversationTest(PloneTestCase):
typetool = self.portal.portal_types
typetool.constructContent('Document', self.portal, 'doc1')
self.typetool = typetool
self.portal_discussion = getToolByName(self.portal,
'portal_discussion',
self.portal_discussion = getToolByName(self.portal,
'portal_discussion',
None)
# Allow discussion
registry = queryUtility(IRegistry)
settings = registry.forInterface(IDiscussionSettings)
settings.globally_enabled = True
def test_add_comment(self):
# Create a conversation. In this case we doesn't assign it to an
# object, as we just want to check the Conversation object API.
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
comment = createObject('plone.Comment')
@ -59,7 +59,7 @@ class ConversationTest(PloneTestCase):
self.assertEquals(len(list(conversation.getComments())), 1)
self.assertEquals(len(tuple(conversation.getThreads())), 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))
def test_delete_comment(self):
@ -67,7 +67,7 @@ class ConversationTest(PloneTestCase):
# object, as we just want to check the Conversation object API.
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
comment = createObject('plone.Comment')
@ -148,7 +148,7 @@ class ConversationTest(PloneTestCase):
], list(conversation.getThreads()))
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.
conversation = IConversation(self.portal.doc1)
comment = createObject('plone.Comment')
@ -157,11 +157,11 @@ class ConversationTest(PloneTestCase):
# Delete the content object
self.portal.manage_delObjects(['doc1'])
# Make sure the comment has been deleted as well
self.assertEquals(len(list(conversation.getComments())), 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):
# 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')
self.assertEquals(portal_discussion.isDiscussionAllowedFor(
self.portal.doc1), False)
self.assertEquals(self.portal.doc1.getTypeInfo().allowDiscussion(),
self.assertEquals(self.portal.doc1.getTypeInfo().allowDiscussion(),
False)
# The allow discussion flag is None by default
self.failIf(getattr(self.portal.doc1, 'allow_discussion', None))
# But isDiscussionAllowedFor, also checks if discussion is allowed on
# the content type. So we allow discussion on the Document content
# But isDiscussionAllowedFor, also checks if discussion is allowed on
# the content type. So we allow discussion on the Document content
# type and check if the Document object allows discussion now.
document_fti = getattr(portal_types, 'Document')
document_fti.manage_changeProperties(allow_discussion = True)
self.assertEquals(portal_discussion.isDiscussionAllowedFor(
self.portal.doc1), True)
self.assertEquals(self.portal.doc1.getTypeInfo().allowDiscussion(),
self.assertEquals(self.portal.doc1.getTypeInfo().allowDiscussion(),
True)
# We can also override the allow_discussion locally
@ -209,16 +209,16 @@ class ConversationTest(PloneTestCase):
# Check if the Document discussion is disabled
self.assertEquals(portal_discussion.isDiscussionAllowedFor(
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
self.assertEquals(getattr(self.portal.doc1, 'allow_discussion', None),
self.assertEquals(getattr(self.portal.doc1, 'allow_discussion', None),
False)
# Disallow discussion on the Document content type again
document_fti.manage_changeProperties(allow_discussion = False)
self.assertEquals(portal_discussion.isDiscussionAllowedFor(
self.portal.doc1), False)
self.assertEquals(self.portal.doc1.getTypeInfo().allowDiscussion(),
self.assertEquals(self.portal.doc1.getTypeInfo().allowDiscussion(),
False)
# 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.assertEquals(portal_discussion.isDiscussionAllowedFor(
self.portal.doc1), True)
self.assertEquals(getattr(self.portal.doc1, 'allow_discussion', None),
self.assertEquals(getattr(self.portal.doc1, 'allow_discussion', None),
True)
def test_comments_enabled_on_doc_in_subfolder(self):
typetool = self.portal.portal_types
typetool.constructContent('Folder', self.portal, 'folder1')
typetool.constructContent('Document', self.portal.folder1, 'doc2')
folder = self.portal.folder1
folder.allowDiscussion(False)
self.assertFalse(hasattr(aq_base(folder), 'allow_discussion'))
@ -241,11 +241,11 @@ class ConversationTest(PloneTestCase):
self.assertTrue(aq_base(folder).allow_discussion)
folder.allowDiscussion(False)
self.assertFalse(aq_base(folder).allow_discussion)
doc = self.portal.folder1.doc2
conversation = IConversation(doc)
self.assertEquals(conversation.enabled(), False)
# We have to allow discussion on Document content type, since
# otherwise allow_discussion will always return False
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.
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
comment1 = createObject('plone.Comment')
@ -571,9 +571,9 @@ class ConversationTest(PloneTestCase):
new_comment3_id = conversation.addComment(comment3)
# 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))
self.assert_(conversation.last_comment_date >
self.assert_(conversation.last_comment_date >
datetime.utcnow() - timedelta(days=1, seconds=1))
# remove the latest comment
@ -581,9 +581,9 @@ class ConversationTest(PloneTestCase):
# check if the latest comment has been updated
# 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))
self.assert_(conversation.last_comment_date > datetime.utcnow() -
self.assert_(conversation.last_comment_date > datetime.utcnow() -
timedelta(days=2, seconds=1))
# remove the latest comment again
@ -591,9 +591,9 @@ class ConversationTest(PloneTestCase):
# check if the latest comment has been updated
# 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))
self.assert_(conversation.last_comment_date > datetime.utcnow() -
self.assert_(conversation.last_comment_date > datetime.utcnow() -
timedelta(days=4, seconds=2))
def test_get_comments_full(self):
@ -678,15 +678,15 @@ class ConversationTest(PloneTestCase):
'++conversation++default')
self.assert_(IConversation.providedBy(conversation))
self.assertEquals(('', 'plone', 'doc1', '++conversation++default'),
self.assertEquals(('', 'plone', 'doc1', '++conversation++default'),
conversation.getPhysicalPath())
# XXX: conversation.absolute_url() returns different values dependent
# on the Plone version used.
# Plone 3.3:
#self.assertEquals('plone/doc1/%2B%2Bconversation%2B%2Bdefault',
#self.assertEquals('plone/doc1/%2B%2Bconversation%2B%2Bdefault',
#conversation.absolute_url())
# Plone 4:
#self.assertEquals('http://nohost/plone/doc1/++conversation++default',
#self.assertEquals('http://nohost/plone/doc1/++conversation++default',
#conversation.absolute_url())
def test_parent(self):

View File

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

View File

@ -17,14 +17,14 @@ from plone.indexer.delegate import DelegatingIndexerFactory
from plone.app.discussion import catalog
LONG_TEXT = """Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed
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.
Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit
LONG_TEXT = """Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed
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.
Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit
amet."""
LONG_TEXT_CUT = """Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed
diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat,
LONG_TEXT_CUT = """Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed
diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat,
sed diam voluptua. At [...]"""
@ -37,8 +37,8 @@ class ConversationIndexersTest(PloneTestCase):
def afterSetUp(self):
# First we need to create some content.
self.loginAsPortalOwner()
self.portal.invokeFactory(id='doc1',
title='Document 1',
self.portal.invokeFactory(id='doc1',
title='Document 1',
type_name='Document')
# Create a conversation.
@ -71,7 +71,7 @@ class ConversationIndexersTest(PloneTestCase):
self.conversation = conversation
def test_conversation_total_comments(self):
self.assert_(isinstance(catalog.total_comments,
self.assert_(isinstance(catalog.total_comments,
DelegatingIndexerFactory))
self.assertEquals(catalog.total_comments(self.portal.doc1)(), 3)
del self.conversation[self.new_id1]
@ -81,12 +81,12 @@ class ConversationIndexersTest(PloneTestCase):
self.assertEquals(catalog.total_comments(self.portal.doc1)(), 0)
def test_conversation_last_comment_date(self):
self.assert_(isinstance(catalog.last_comment_date,
self.assert_(isinstance(catalog.last_comment_date,
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))
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))
del self.conversation[self.new_id2]
del self.conversation[self.new_id1]
@ -94,11 +94,12 @@ class ConversationIndexersTest(PloneTestCase):
def test_conversation_commentators(self):
pass
#self.assertEquals(catalog.commentators(self.portal.doc1)(),
#self.assertEquals(catalog.commentators(self.portal.doc1)(),
# ('Jim', 'Emma', 'Lukas'))
#self.assert_(isinstance(catalog.commentators,
#self.assert_(isinstance(catalog.commentators,
# DelegatingIndexerFactory))
class CommentIndexersTest(PloneTestCase):
layer = DiscussionLayer
@ -106,15 +107,15 @@ class CommentIndexersTest(PloneTestCase):
def afterSetUp(self):
# First we need to create some content.
self.loginAsPortalOwner()
self.portal.invokeFactory(id='doc1',
title='Document 1',
type_name='Document')
self.portal.invokeFactory(id='doc1',
title='Document 1',
type_name='Document')
# Create a conversation. In this case we doesn't assign it to an
# object, as we just want to check the Conversation object API.
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
comment = createObject('plone.Comment')
@ -132,7 +133,7 @@ class CommentIndexersTest(PloneTestCase):
self.assert_(isinstance(catalog.title, DelegatingIndexerFactory))
def test_description(self):
self.assertEquals(catalog.description(self.comment)(),
self.assertEquals(catalog.description(self.comment)(),
'Lorem ipsum dolor sit amet.')
self.assert_(isinstance(catalog.description, DelegatingIndexerFactory))
@ -144,23 +145,23 @@ class CommentIndexersTest(PloneTestCase):
comment_long.text = LONG_TEXT
self.conversation.addComment(comment_long)
self.assertEquals(catalog.description(comment_long)(),
LONG_TEXT_CUT.replace("\n", ""))
self.assertEquals(catalog.description(comment_long)(),
LONG_TEXT_CUT.replace("\n", " "))
def test_dates(self):
# 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))
self.assertEquals(catalog.effective(self.comment)(),
self.assertEquals(catalog.effective(self.comment)(),
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))
def test_searchable_text(self):
# 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.'))
self.assert_(isinstance(catalog.searchable_text,
self.assert_(isinstance(catalog.searchable_text,
DelegatingIndexerFactory))
def test_creator(self):
@ -171,5 +172,6 @@ class CommentIndexersTest(PloneTestCase):
# object the comment was added to
self.assertEquals(catalog.in_response_to(self.comment)(), 'Document 1')
def test_suite():
return unittest.defaultTestLoader.loadTestsFromName(__name__)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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