merge master in filippo_moderation_js

This commit is contained in:
Filippo Campi 2018-09-25 16:31:09 +02:00
commit 3648c3345f
25 changed files with 539 additions and 443 deletions

View File

@ -1,7 +1,7 @@
Changelog
=========
3.0.6 (unreleased)
3.0.7 (unreleased)
------------------
Breaking changes:
@ -14,11 +14,27 @@ New features:
Bug fixes:
- Fix location of controlpanel events.
[jensens]
- Fixed tests when IRichText behavior is used.
IRichText -> IRichTextBehavior
This is a follow up to `issue 476 <https://github.com/plone/plone.app.contenttypes/issues/476>`_.
[iham]
- Fix commenting and tests in python 3.
[pbauer]
3.0.6 (2018-06-18)
------------------
Bug fixes:
- Fix tests to work with merges plone.login.
[jensens]
- More Python 2 / 3 compatibility.
[pbauer]
[pbauer, hvelarde]
3.0.5 (2018-02-04)

View File

@ -14,6 +14,7 @@ from zope.component import getMultiAdapter
from zope.component import getUtility
from zope.event import notify
from zope.lifecycleevent import ObjectModifiedEvent
from .comments import CommentForm
class View(BrowserView):

View File

@ -17,6 +17,7 @@ from plone.z3cform import z2
from plone.z3cform.fieldsets import extensible
from plone.z3cform.interfaces import IWrappedForm
from Products.CMFCore.utils import getToolByName
from Products.CMFPlone.utils import safe_unicode
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
from Products.statusmessages.interfaces import IStatusMessage
from six.moves.urllib.parse import quote
@ -32,32 +33,30 @@ from zope.i18n import translate
from zope.i18nmessageid import Message
from zope.interface import alsoProvides
import six
COMMENT_DESCRIPTION_PLAIN_TEXT = _(
u'comment_description_plain_text',
default=u'You can add a comment by filling out the form below. '
u'Plain text formatting.'
u'Plain text formatting.',
)
COMMENT_DESCRIPTION_MARKDOWN = _(
u'comment_description_markdown',
default=u'You can add a comment by filling out the form below. '
u'Plain text formatting. You can use the Markdown syntax for '
u'links and images.'
u'links and images.',
)
COMMENT_DESCRIPTION_INTELLIGENT_TEXT = _(
u'comment_description_intelligent_text',
default=u'You can add a comment by filling out the form below. '
u'Plain text formatting. Web and email addresses are '
u'transformed into clickable links.'
u'transformed into clickable links.',
)
COMMENT_DESCRIPTION_MODERATION_ENABLED = _(
u'comment_description_moderation_enabled',
default=u'Comments are moderated.'
default=u'Comments are moderated.',
)
@ -152,13 +151,9 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
# Make sure author_name/ author_email is properly encoded
if 'author_name' in data:
author_name = data['author_name']
if isinstance(author_name, str):
author_name = six.text_type(author_name, 'utf-8')
author_name = safe_unicode(data['author_name'])
if 'author_email' in data:
author_email = data['author_email']
if isinstance(author_email, str):
author_email = six.text_type(author_email, 'utf-8')
author_email = safe_unicode(data['author_email'])
# Set comment author properties for anonymous users or members
portal_membership = getToolByName(context, 'portal_membership')
@ -167,21 +162,18 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
'Reply to item', context):
# Member
member = portal_membership.getAuthenticatedMember()
# memberdata is stored as utf-8 encoded strings
email = member.getProperty('email')
email = safe_unicode(member.getProperty('email'))
fullname = member.getProperty('fullname')
if not fullname or fullname == '':
fullname = member.getUserName()
elif isinstance(fullname, str):
fullname = six.text_type(fullname, 'utf-8')
fullname = safe_unicode(fullname)
author_name = fullname
if email and isinstance(email, str):
email = six.text_type(email, 'utf-8')
# XXX: according to IComment interface author_email must not be
email = safe_unicode(email)
# XXX: according to IComment interface author_email must not be # noqa T000
# set for logged in users, cite:
# 'for anonymous comments only, set to None for logged in comments'
author_email = email
# /XXX
# /XXX # noqa T000
return author_name, author_email
@ -228,7 +220,7 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
raise Unauthorized(
u'Anonymous user tries to post a comment, but anonymous '
u'commenting is disabled. Or user does not have the '
u"'reply to item' permission."
u"'reply to item' permission.",
)
return comment
@ -240,10 +232,10 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
# Check if conversation is enabled on this content object
if not self.__parent__.restrictedTraverse(
'@@conversation_view'
'@@conversation_view',
).enabled():
raise Unauthorized(
'Discussion is not enabled for this content object.'
'Discussion is not enabled for this content object.',
)
# Validation form
@ -293,7 +285,7 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
comment_review_state = workflowTool.getInfoFor(
comment,
'review_state',
None
None,
)
if comment_review_state == 'pending' and not can_review:
# Show info message when comment moderation is enabled

View File

@ -1,5 +1,4 @@
# -*- coding: utf-8 -*-
from plone.app.controlpanel.interfaces import IConfigurationChangedEvent
from plone.app.discussion.interfaces import _
from plone.app.discussion.interfaces import IDiscussionSettings
from plone.app.discussion.upgrades import update_registry
@ -7,6 +6,7 @@ from plone.app.registry.browser import controlpanel
from plone.registry.interfaces import IRecordModifiedEvent
from plone.registry.interfaces import IRegistry
from Products.CMFCore.utils import getToolByName
from Products.CMFPlone.interfaces.controlpanel import IConfigurationChangedEvent # noqa: E501
from Products.CMFPlone.interfaces.controlpanel import IMailSchema
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
from Products.statusmessages.interfaces import IStatusMessage
@ -34,7 +34,7 @@ class DiscussionSettingsEditForm(controlpanel.RegistryEditForm):
u'To enable the moderation workflow for comments, '
u'go to the Types Control Panel, choose '
u'"Comment" and set workflow to '
u'"Comment Review Workflow".'
u'"Comment Review Workflow".',
)
def updateFields(self):
@ -68,10 +68,10 @@ class DiscussionSettingsEditForm(controlpanel.RegistryEditForm):
self.widgets['anonymous_comments'].label = _(u'Anonymous Comments')
self.widgets['show_commenter_image'].label = _(u'Commenter Image')
self.widgets['moderator_notification_enabled'].label = _(
u'Moderator Email Notification'
u'Moderator Email Notification',
)
self.widgets['user_notification_enabled'].label = _(
u'User Email Notification'
u'User Email Notification',
)
@button.buttonAndHandler(_('Save'), name=None)
@ -92,8 +92,8 @@ class DiscussionSettingsEditForm(controlpanel.RegistryEditForm):
self.request.response.redirect(
'{0}/{1}'.format(
self.context.absolute_url(),
self.control_panel_view
)
self.control_panel_view,
),
)

View File

@ -13,6 +13,7 @@ from Products.CMFCore.interfaces import IContentish
from Products.CMFPlone.utils import safe_unicode
from Products.ZCatalog.interfaces import IZCatalog
import six
MAX_DESCRIPTION = 25
@ -70,13 +71,20 @@ def title(object):
@indexer(IComment)
def creator(object):
return object.creator and safe_unicode(object.creator).encode('utf-8')
if not object.creator:
return
value = safe_unicode(object.creator)
if six.PY2:
return value.encode('utf8')
return value
@indexer(IComment)
def description(object):
# Return the first 25 words of the comment text and append ' [...]'
text = ' '.join(object.getText(targetMimetype='text/plain').split()[:MAX_DESCRIPTION])
text = ' '.join(
object.getText(targetMimetype='text/plain').split()[:MAX_DESCRIPTION],
)
if len(object.getText().split()) > 25:
text += ' [...]'
return text

View File

@ -118,7 +118,7 @@ class Comment(CatalogAware, WorkflowAware, DynamicType, Traversable,
aclpath = [x for x in user.getPhysicalPath() if x]
self._owner = (aclpath, user.getId(),)
self.__ac_local_roles__ = {
user.getId(): ['Owner']
user.getId(): ['Owner'],
}
@property
@ -148,7 +148,7 @@ class Comment(CatalogAware, WorkflowAware, DynamicType, Traversable,
text = self.text
if text is None:
return ''
if isinstance(text, six.text_type):
if six.PY2 and isinstance(text, six.text_type):
text = text.encode('utf8')
transform = transforms.convertTo(
targetMimetype,
@ -162,7 +162,11 @@ class Comment(CatalogAware, WorkflowAware, DynamicType, Traversable,
msg = u'Transform "{0}" => "{1}" not available. Failed to ' \
u'transform comment "{2}".'
logger.error(
msg.format(sourceMimetype, targetMimetype, self.absolute_url())
msg.format(
sourceMimetype,
targetMimetype,
self.absolute_url(),
),
)
return text
@ -174,10 +178,12 @@ class Comment(CatalogAware, WorkflowAware, DynamicType, Traversable,
if not self.author_name:
author_name = translate(
Message(_(
Message(
_(
u'label_anonymous',
default=u'Anonymous'
))
default=u'Anonymous',
),
),
)
else:
author_name = self.author_name
@ -284,11 +290,11 @@ def notify_content_object_moved(obj, event):
old_path = '/'.join(
event.oldParent.getPhysicalPath() +
(event.oldName,) +
moved_path
moved_path,
)
brains = catalog.searchResults(dict(
path={'query': old_path},
portal_type='Discussion Item'
portal_type='Discussion Item',
))
for brain in brains:
catalog.uncatalog_object(brain.getPath())
@ -350,24 +356,28 @@ def notify_user(obj, event):
mapping={
'title': safe_unicode(content_object.title),
'link': content_object.absolute_url() + '/view#' + obj.id,
'text': obj.text
}
'text': obj.text,
},
),
context=obj.REQUEST
context=obj.REQUEST,
)
for email in emails:
# Send email
try:
mail_host.send(message,
mail_host.send(
message,
email,
sender,
subject,
charset='utf-8')
charset='utf-8',
)
except SMTPException:
logger.error('SMTP exception while trying to send an ' +
logger.error(
'SMTP exception while trying to send an ' +
'email from %s to %s',
sender,
email)
email,
)
def notify_moderator(obj, event):
@ -420,19 +430,21 @@ def notify_moderator(obj, event):
'text': obj.text,
'link_approve': link_approve,
'link_delete': link_delete,
}
},
),
context=obj.REQUEST
context=obj.REQUEST,
)
# Send email
try:
mail_host.send(message, mto, sender, subject, charset='utf-8')
except SMTPException as e:
logger.error('SMTP exception (%s) while trying to send an ' +
logger.error(
'SMTP exception (%s) while trying to send an ' +
'email notification to the comment moderator ' +
'(from %s to %s, message: %s)',
e,
sender,
mto,
message)
message,
)

View File

@ -161,7 +161,7 @@ class Conversation(Traversable, Persistent, Explicit):
comment = aq_base(comment)
id = long(time.time() * 1e6)
id = int(time.time() * 1e6)
while id in self._comments:
id += 1
@ -206,22 +206,22 @@ class Conversation(Traversable, Persistent, Explicit):
return len(self._comments)
def __contains__(self, key):
return long(key) in self._comments
return int(key) in self._comments
def __getitem__(self, key):
"""Get an item by its long key
"""Get an item by its int key
"""
try:
comment_id = long(key)
comment_id = int(key)
except ValueError:
return
return self._comments[comment_id].__of__(self)
def __delitem__(self, key, suppress_container_modified=False):
"""Delete an item by its long key
"""Delete an item by its int key
"""
key = long(key)
key = int(key)
comment = self[key].__of__(self)
commentator = comment.author_username
@ -260,7 +260,7 @@ class Conversation(Traversable, Persistent, Explicit):
return iter(self._comments)
def get(self, key, default=None):
comment = self._comments.get(long(key), default)
comment = self._comments.get(int(key), default)
if comment is default:
return default
return comment.__of__(self)
@ -347,20 +347,20 @@ class ConversationReplies(object):
return len(self.children)
def __contains__(self, key):
return long(key) in self.children
return int(key) in self.children
def __getitem__(self, key):
"""Get an item by its long key
"""Get an item by its int key
"""
key = long(key)
key = int(key)
if key not in self.children:
raise KeyError(key)
return self.conversation[key]
def __delitem__(self, key):
"""Delete an item by its long key
"""Delete an item by its int key
"""
key = long(key)
key = int(key)
if key not in self.children:
raise KeyError(key)
del self.conversation[key]
@ -369,7 +369,7 @@ class ConversationReplies(object):
return iter(self.children)
def get(self, key, default=None):
key = long(key)
key = int(key)
if key not in self.children:
return default
return self.conversation.get(key)
@ -418,7 +418,7 @@ class CommentReplies(ConversationReplies):
self.conversation = aq_parent(self.comment)
conversation_has_no_children = not hasattr(
self.conversation,
'_children'
'_children',
)
if self.conversation is None or conversation_has_no_children:
raise TypeError("This adapter doesn't know what to do with the "

View File

@ -59,7 +59,8 @@ class IConversation(IIterableMapping):
public_commentators = schema.Set(
title=_(
u'The set of unique commentators (usernames) of published_comments'
u'The set of unique commentators (usernames) '
u'of published_comments',
),
readonly=True,
)
@ -174,15 +175,15 @@ class IComment(Interface):
text = schema.Text(
title=_(
u'label_comment',
default=u'Comment'
)
default=u'Comment',
),
)
user_notification = schema.Bool(
title=_(
u'Notify me of new comments via email.'
u'Notify me of new comments via email.',
),
required=False
required=False,
)
creator = schema.TextLine(title=_(u'Username of the commenter'))
@ -216,7 +217,7 @@ class IDiscussionSettings(Interface):
default=u'If selected, users are able to post comments on the '
u'site. However, you will still need to enable comments '
u'for specific content types, folders or content '
u'objects before users will be able to post comments.'
u'objects before users will be able to post comments.',
),
required=False,
default=False,
@ -230,7 +231,7 @@ class IDiscussionSettings(Interface):
default=u'If selected, anonymous users are able to post '
u'comments without logging in. It is highly '
u'recommended to use a captcha solution to prevent '
u'spam if this setting is enabled.'
u'spam if this setting is enabled.',
),
required=False,
default=False,
@ -242,15 +243,16 @@ class IDiscussionSettings(Interface):
description=_(
u'help_anonymous_email_enabled',
default=u'If selected, anonymous user will have to '
u'give their email.'),
u'give their email.',
),
required=False,
default=False
default=False,
)
moderation_enabled = schema.Bool(
title=_(
u'label_moderation_enabled',
default='Enable comment moderation'
default='Enable comment moderation',
),
description=_(
u'help_moderation_enabled',
@ -260,7 +262,7 @@ class IDiscussionSettings(Interface):
u'or "Manager") can approve comments to make them '
u'visible to the public. If you want to enable a '
u'custom comment workflow, you have to go to the '
u'types control panel.'
u'types control panel.',
),
required=False,
default=False,
@ -347,7 +349,7 @@ class IDiscussionSettings(Interface):
moderator_email = schema.ASCIILine(
title=_(
u'label_moderator_email',
default=u'Moderator Email Address'
default=u'Moderator Email Address',
),
description=_(
u'help_moderator_email',
@ -359,14 +361,14 @@ class IDiscussionSettings(Interface):
user_notification_enabled = schema.Bool(
title=_(
u'label_user_notification_enabled',
default=u'Enable user email notification'
default=u'Enable user email notification',
),
description=_(
u'help_user_notification_enabled',
default=u'If selected, users can choose to be notified '
u'of new comments by email.'),
required=False,
default=False
default=False,
)

View File

@ -66,7 +66,7 @@
<!-- Control panel event subscribers -->
<subscriber
for="plone.app.controlpanel.interfaces.IConfigurationChangedEvent"
for="Products.CMFPlone.interfaces.events.IConfigurationChangedEvent"
handler=".browser.controlpanel.notify_configuration_changed"
/>

View File

@ -92,7 +92,7 @@ class PloneAppDiscussion(PloneSandboxLayer):
portal.invokeFactory(
id='doc1',
title='Document 1',
type_name='Document'
type_name='Document',
)
@ -119,5 +119,5 @@ PLONE_APP_DISCUSSION_FUNCTIONAL_TESTING = FunctionalTesting(
name='PloneAppDiscussion:Functional')
PLONE_APP_DISCUSSION_ROBOT_TESTING = FunctionalTesting(
bases=(PLONE_APP_DISCUSSION_ROBOT_FIXTURE,),
name='PloneAppDiscussion:Robot'
name='PloneAppDiscussion:Robot',
)

View File

@ -470,9 +470,9 @@ Edit the content object.
>>> from hashlib import sha1 as sha
>>> ring = _getKeyring('foo')
>>> secret = ring.random()
>>> token = hmac.new(secret, 'admin', sha).hexdigest()
>>> token = hmac.new(secret.encode('utf8'), b'admin', sha).hexdigest()
>>> browser.open("http://nohost/plone/doc1/edit?_authenticator=" + token)
>>> browser.getControl(name='form.widgets.IRichText.text').value = "Lorem ipsum"
>>> browser.getControl(name='form.widgets.IRichTextBehavior.text').value = "Lorem ipsum"
>>> browser.getControl('Save').click()
Make sure the edit was successful.

View File

@ -26,19 +26,19 @@ class CatalogSetupTest(unittest.TestCase):
def test_catalog_installed(self):
self.assertTrue(
'total_comments' in
self.portal.portal_catalog.indexes()
self.portal.portal_catalog.indexes(),
)
self.assertTrue(
'commentators' in
self.portal.portal_catalog.indexes()
self.portal.portal_catalog.indexes(),
)
self.assertTrue(
'total_comments' in
self.portal.portal_catalog.schema()
self.portal.portal_catalog.schema(),
)
self.assertTrue(
'in_response_to' in
self.portal.portal_catalog.schema()
self.portal.portal_catalog.schema(),
)
def test_collection_criteria_installed(self):
@ -76,13 +76,14 @@ class ConversationCatalogTest(unittest.TestCase):
new_comment1_id = conversation.addComment(comment1)
self.comment_id = new_comment1_id
brains = self.catalog.searchResults(dict(
brains = self.catalog.searchResults(
dict(
path={
'query':
'/'.join(self.portal.doc1.getPhysicalPath())
'query': '/'.join(self.portal.doc1.getPhysicalPath()),
},
portal_type='Document'
))
portal_type='Document',
),
)
self.conversation = conversation
self.brains = brains
self.doc1_brain = brains[0]
@ -99,16 +100,17 @@ class ConversationCatalogTest(unittest.TestCase):
new_comment2_id = self.conversation.addComment(comment2)
comment2 = self.portal.doc1.restrictedTraverse(
'++conversation++default/{0}'.format(new_comment2_id)
'++conversation++default/{0}'.format(new_comment2_id),
)
comment2.reindexObject()
brains = self.catalog.searchResults(dict(
brains = self.catalog.searchResults(
dict(
path={
'query':
'/'.join(self.portal.doc1.getPhysicalPath())
'query': '/'.join(self.portal.doc1.getPhysicalPath()),
},
portal_type='Document'
))
portal_type='Document',
),
)
doc1_brain = brains[0]
self.assertEqual(doc1_brain.total_comments, 2)
@ -116,7 +118,7 @@ class ConversationCatalogTest(unittest.TestCase):
self.assertTrue('last_comment_date' in self.doc1_brain)
self.assertEqual(
self.doc1_brain.last_comment_date,
datetime(2006, 9, 17, 14, 18, 12)
datetime(2006, 9, 17, 14, 18, 12),
)
# Add another comment and check if last comment date is updated.
@ -128,47 +130,50 @@ class ConversationCatalogTest(unittest.TestCase):
new_comment2_id = self.conversation.addComment(comment2)
comment2 = self.portal.doc1.restrictedTraverse(
'++conversation++default/{0}'.format(new_comment2_id)
'++conversation++default/{0}'.format(new_comment2_id),
)
comment2.reindexObject()
brains = self.catalog.searchResults(dict(
brains = self.catalog.searchResults(
dict(
path={
'query':
'/'.join(self.portal.doc1.getPhysicalPath())
'query': '/'.join(self.portal.doc1.getPhysicalPath()),
},
portal_type='Document'
))
portal_type='Document',
),
)
doc1_brain = brains[0]
self.assertEqual(
doc1_brain.last_comment_date,
datetime(2009, 9, 17, 14, 18, 12)
datetime(2009, 9, 17, 14, 18, 12),
)
# Remove the comment again
del self.conversation[new_comment2_id]
brains = self.catalog.searchResults(dict(
brains = self.catalog.searchResults(
dict(
path={
'query':
'/'.join(self.portal.doc1.getPhysicalPath())
'query': '/'.join(self.portal.doc1.getPhysicalPath()),
},
portal_type='Document'
))
portal_type='Document',
),
)
doc1_brain = brains[0]
self.assertEqual(
doc1_brain.last_comment_date,
datetime(2006, 9, 17, 14, 18, 12)
datetime(2006, 9, 17, 14, 18, 12),
)
# remove all comments
del self.conversation[self.new_comment1_id]
brains = self.catalog.searchResults(dict(
brains = self.catalog.searchResults(
dict(
path={
'query':
'/'.join(self.portal.doc1.getPhysicalPath())
'query': '/'.join(self.portal.doc1.getPhysicalPath()),
},
portal_type='Document'
))
portal_type='Document',
),
)
doc1_brain = brains[0]
self.assertEqual(doc1_brain.last_comment_date, None)
@ -185,53 +190,57 @@ class ConversationCatalogTest(unittest.TestCase):
new_comment2_id = self.conversation.addComment(comment2)
comment2 = self.portal.doc1.restrictedTraverse(
'++conversation++default/{0}'.format(new_comment2_id)
'++conversation++default/{0}'.format(new_comment2_id),
)
comment2.reindexObject()
brains = self.catalog.searchResults(dict(
brains = self.catalog.searchResults(
dict(
path={
'query':
'/'.join(self.portal.doc1.getPhysicalPath())
'query': '/'.join(self.portal.doc1.getPhysicalPath()),
},
portal_type='Document'
))
portal_type='Document',
),
)
doc1_brain = brains[0]
self.assertEqual(doc1_brain.commentators, ('Jim', 'Emma'))
# remove one comments
del self.conversation[new_comment2_id]
brains = self.catalog.searchResults(dict(
brains = self.catalog.searchResults(
dict(
path={
'query':
'/'.join(self.portal.doc1.getPhysicalPath())
'query': '/'.join(self.portal.doc1.getPhysicalPath()),
},
portal_type='Document'
))
portal_type='Document',
),
)
doc1_brain = brains[0]
self.assertEqual(doc1_brain.commentators, ('Jim',))
# remove all comments
del self.conversation[self.new_comment1_id]
brains = self.catalog.searchResults(dict(
brains = self.catalog.searchResults(
dict(
path={
'query':
'/'.join(self.portal.doc1.getPhysicalPath())
'query': '/'.join(self.portal.doc1.getPhysicalPath()),
},
portal_type='Document'
))
portal_type='Document',
),
)
doc1_brain = brains[0]
self.assertEqual(doc1_brain.commentators, ())
def test_conversation_indexes_not_in_comments(self):
brains = self.catalog.searchResults(dict(
brains = self.catalog.searchResults(
dict(
path={
'query':
'/'.join(self.portal.doc1.getPhysicalPath())
'query': '/'.join(self.portal.doc1.getPhysicalPath()),
},
portal_type='Discussion Item'
))
portal_type='Discussion Item',
),
)
comment1_brain = brains[0]
self.assertEqual(comment1_brain.commentators, None)
self.assertEqual(comment1_brain.last_comment_date, None)
@ -240,13 +249,14 @@ class ConversationCatalogTest(unittest.TestCase):
def test_dont_index_private_commentators(self):
self.comment1.manage_permission('View', roles=tuple())
self.portal.doc1.reindexObject()
brains = self.catalog.searchResults(dict(
brains = self.catalog.searchResults(
dict(
path={
'query':
'/'.join(self.portal.doc1.getPhysicalPath())
'query': '/'.join(self.portal.doc1.getPhysicalPath()),
},
portal_type='Document'
))
portal_type='Document',
),
)
doc1_brain = brains[0]
self.assertEqual(doc1_brain.commentators, ())
@ -272,14 +282,15 @@ class CommentCatalogTest(unittest.TestCase):
# Comment brain
self.comment = self.portal.doc1.restrictedTraverse(
'++conversation++default/{0}'.format(new_comment1_id)
'++conversation++default/{0}'.format(new_comment1_id),
)
brains = self.catalog.searchResults(dict(
brains = self.catalog.searchResults(
dict(
path={
'query':
'/'.join(self.comment.getPhysicalPath())
}
))
'query': '/'.join(self.comment.getPhysicalPath()),
},
),
)
self.comment_brain = brains[0]
def test_title(self):
@ -292,14 +303,15 @@ class CommentCatalogTest(unittest.TestCase):
# Comment brain
comment = self.portal.doc1.restrictedTraverse(
'++conversation++default/{0}'.format(cid)
'++conversation++default/{0}'.format(cid),
)
brains = self.catalog.searchResults(dict(
brains = self.catalog.searchResults(
dict(
path={
'query':
'/'.join(comment.getPhysicalPath())
}
))
'query': '/'.join(comment.getPhysicalPath()),
},
),
)
comment_brain = brains[0]
self.assertEqual(comment_brain.Title, 'Anonymous on Document 1')
@ -327,12 +339,13 @@ class CommentCatalogTest(unittest.TestCase):
# 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(dict(
brains = self.catalog.searchResults(
dict(
path={
'query':
'/'.join(self.comment.getPhysicalPath())
}
))
'query': '/'.join(self.comment.getPhysicalPath()),
},
),
)
self.assertEqual(len(brains), 0)
def test_reindex_comment(self):
@ -357,17 +370,17 @@ class CommentCatalogTest(unittest.TestCase):
self.portal.invokeFactory(
id='folder1',
title='Folder 1',
type_name='Folder'
type_name='Folder',
)
self.portal.invokeFactory(
id='folder2',
title='Folder 2',
type_name='Folder'
type_name='Folder',
)
self.portal.folder1.invokeFactory(
id='moveme',
title='Move Me',
type_name='Document'
type_name='Document',
)
conversation = IConversation(self.portal.folder1.moveme)
comment = createObject('plone.Comment')
@ -380,43 +393,57 @@ class CommentCatalogTest(unittest.TestCase):
self.portal.folder2.manage_pasteObjects(cp)
# Make sure no old comment brains are
brains = self.catalog.searchResults(dict(
portal_type='Discussion Item',
path={'query': '/'.join(self.portal.folder1.getPhysicalPath())}
))
self.assertEqual(len(brains), 0)
brains = self.catalog.searchResults(dict(
brains = self.catalog.searchResults(
dict(
portal_type='Discussion Item',
path={
'query': '/'.join(self.portal.folder2.getPhysicalPath())
}
))
'query': '/'.join(self.portal.folder1.getPhysicalPath()),
},
),
)
self.assertEqual(len(brains), 0)
brains = self.catalog.searchResults(
dict(
portal_type='Discussion Item',
path={
'query': '/'.join(self.portal.folder2.getPhysicalPath()),
},
),
)
self.assertEqual(len(brains), 1)
self.assertEqual(
brains[0].getPath(),
'/plone/folder2/moveme/++conversation++default/' +
str(comment_id)
str(comment_id),
)
def test_move_upper_level_folder(self):
# create a folder with a nested structure
self.portal.invokeFactory(id='sourcefolder',
self.portal.invokeFactory(
id='sourcefolder',
title='Source Folder',
type_name='Folder')
self.portal.sourcefolder.invokeFactory(id='moveme',
type_name='Folder',
)
self.portal.sourcefolder.invokeFactory(
id='moveme',
title='Move Me',
type_name='Folder')
self.portal.sourcefolder.moveme.invokeFactory(id='mydocument',
type_name='Folder',
)
self.portal.sourcefolder.moveme.invokeFactory(
id='mydocument',
title='My Document',
type_name='Folder')
self.portal.invokeFactory(id='targetfolder',
type_name='Folder',
)
self.portal.invokeFactory(
id='targetfolder',
title='Target Folder',
type_name='Folder')
type_name='Folder',
)
# create comment on my-document
conversation = IConversation(
self.portal.sourcefolder.moveme.mydocument
self.portal.sourcefolder.moveme.mydocument,
)
comment = createObject('plone.Comment')
comment_id = conversation.addComment(comment)
@ -429,22 +456,26 @@ class CommentCatalogTest(unittest.TestCase):
self.portal.targetfolder.manage_pasteObjects(cp)
# Make sure no old comment brains are left
brains = self.catalog.searchResults(dict(
brains = self.catalog.searchResults(
dict(
portal_type='Discussion Item',
path={'query': '/plone/sourcefolder/moveme'}
))
path={'query': '/plone/sourcefolder/moveme'},
),
)
self.assertEqual(len(brains), 0)
# make sure comments are correctly index on the target
brains = self.catalog.searchResults(dict(
brains = self.catalog.searchResults(
dict(
portal_type='Discussion Item',
path={'query': '/plone/targetfolder/moveme'}
))
path={'query': '/plone/targetfolder/moveme'},
),
)
self.assertEqual(len(brains), 1)
self.assertEqual(
brains[0].getPath(),
'/plone/targetfolder/moveme/mydocument/++conversation++default/' +
str(comment_id)
str(comment_id),
)
def test_update_comments_when_content_object_is_renamed(self):
@ -454,12 +485,13 @@ class CommentCatalogTest(unittest.TestCase):
self.portal.manage_renameObject('doc1', 'doc2')
brains = self.catalog.searchResults(
portal_type='Discussion Item')
portal_type='Discussion Item',
)
self.assertEqual(len(brains), 1)
self.assertEqual(
brains[0].getPath(),
'/plone/doc2/++conversation++default/' +
str(self.comment_id)
str(self.comment_id),
)
def test_clear_and_rebuild_catalog(self):
@ -477,7 +509,7 @@ class CommentCatalogTest(unittest.TestCase):
self.assertEqual(
comment_brain.getPath(),
'/plone/doc1/++conversation++default/' +
str(self.comment_id)
str(self.comment_id),
)
def test_clear_and_rebuild_catalog_for_nested_comments(self):
@ -549,13 +581,14 @@ class NoConversationCatalogTest(unittest.TestCase):
conversation = IConversation(self.portal.doc1)
brains = self.catalog.searchResults(dict(
brains = self.catalog.searchResults(
dict(
path={
'query':
'/'.join(self.portal.doc1.getPhysicalPath())
'query': '/'.join(self.portal.doc1.getPhysicalPath()),
},
portal_type='Document'
))
portal_type='Document',
),
)
self.conversation = conversation
self.brains = brains
self.doc1_brain = brains[0]
@ -567,5 +600,5 @@ class NoConversationCatalogTest(unittest.TestCase):
# Make sure no conversation has been created
self.assertTrue(
'plone.app.discussion:conversation' not in
IAnnotations(self.portal.doc1)
IAnnotations(self.portal.doc1),
)

View File

@ -12,6 +12,7 @@ from zope.component import getMultiAdapter
import datetime
import logging
import six
import unittest
@ -54,7 +55,7 @@ class CommentTest(unittest.TestCase):
difference = difference.seconds
# We hope that between comment1 and local_utc happen less than
# 10 seconds
self.assertFalse(difference / 10)
self.assertFalse(difference // 10)
def test_id(self):
comment1 = createObject('plone.Comment')
@ -68,7 +69,7 @@ class CommentTest(unittest.TestCase):
comment1 = createObject('plone.Comment')
conversation.addComment(comment1)
comment_brain = self.catalog.searchResults(
portal_type='Discussion Item'
portal_type='Discussion Item',
)[0]
self.assertTrue(comment_brain.UID)
@ -79,7 +80,7 @@ class CommentTest(unittest.TestCase):
comment2 = createObject('plone.Comment')
conversation.addComment(comment2)
brains = self.catalog.searchResults(
portal_type='Discussion Item'
portal_type='Discussion Item',
)
self.assertNotEqual(brains[0].UID, brains[1].UID)
@ -88,7 +89,7 @@ class CommentTest(unittest.TestCase):
comment1 = createObject('plone.Comment')
conversation.addComment(comment1)
comment_brain = self.catalog.searchResults(
portal_type='Discussion Item'
portal_type='Discussion Item',
)[0]
self.assertNotEqual(self.document_brain.UID, comment_brain.UID)
@ -109,7 +110,7 @@ class CommentTest(unittest.TestCase):
self.portal.invokeFactory(
id='doc_sp_chars',
title=u'Document äüö',
type_name='Document'
type_name='Document',
)
conversation = IConversation(self.portal.doc_sp_chars)
comment1 = createObject('plone.Comment')
@ -121,7 +122,7 @@ class CommentTest(unittest.TestCase):
self.portal.invokeFactory(
id='doc_sp_chars_utf8',
title='Document ëïû',
type_name='Document'
type_name='Document',
)
conversation = IConversation(self.portal.doc_sp_chars_utf8)
comment1 = createObject('plone.Comment')
@ -157,7 +158,7 @@ class CommentTest(unittest.TestCase):
comment1.text = 'First paragraph\n\nSecond_paragraph'
self.assertEqual(
''.join(comment1.getText().split()),
'<p>Firstparagraph<br/><br/>Second_paragraph</p>'
'<p>Firstparagraph<br/><br/>Second_paragraph</p>',
)
def test_getText_escapes_HTML(self):
@ -165,23 +166,28 @@ class CommentTest(unittest.TestCase):
comment1.text = '<b>Got HTML?</b>'
self.assertEqual(
comment1.getText(),
'<p>&lt;b&gt;Got HTML?&lt;/b&gt;</p>'
'<p>&lt;b&gt;Got HTML?&lt;/b&gt;</p>',
)
def test_getText_with_non_ascii_characters(self):
comment1 = createObject('plone.Comment')
comment1.text = u'Umlaute sind ä, ö und ü.'
out = b'<p>Umlaute sind \xc3\xa4, \xc3\xb6 und \xc3\xbc.</p>'
if six.PY2:
self.assertEqual(
comment1.getText(),
'<p>Umlaute sind \xc3\xa4, \xc3\xb6 und \xc3\xbc.</p>'
)
out)
else:
self.assertEqual(
comment1.getText(),
out.decode('utf8'))
def test_getText_doesnt_link(self):
comment1 = createObject('plone.Comment')
comment1.text = 'Go to http://www.plone.org'
self.assertEqual(
comment1.getText(),
'<p>Go to http://www.plone.org</p>'
'<p>Go to http://www.plone.org</p>',
)
def test_getText_uses_comment_mime_type(self):
@ -191,7 +197,7 @@ class CommentTest(unittest.TestCase):
self.assertEqual(
comment1.getText(),
'Go to <a href="http://www.plone.org" ' +
'rel="nofollow">http://www.plone.org</a>'
'rel="nofollow">http://www.plone.org</a>',
)
def test_getText_uses_comment_mime_type_html(self):
@ -200,7 +206,7 @@ class CommentTest(unittest.TestCase):
comment1.mime_type = 'text/html'
self.assertEqual(
comment1.getText(),
'Go to <a href="http://www.plone.org">plone.org</a>'
'Go to <a href="http://www.plone.org">plone.org</a>',
)
def test_getText_w_custom_targetMimetype(self):
@ -230,20 +236,20 @@ class CommentTest(unittest.TestCase):
new_comment1_id = conversation.addComment(comment1)
comment = self.portal.doc1.restrictedTraverse(
'++conversation++default/{0}'.format(new_comment1_id)
'++conversation++default/{0}'.format(new_comment1_id),
)
self.assertTrue(IComment.providedBy(comment))
self.assertEqual(
(
'', 'plone', 'doc1', '++conversation++default',
str(new_comment1_id)
str(new_comment1_id),
),
comment.getPhysicalPath()
comment.getPhysicalPath(),
)
self.assertEqual(
'http://nohost/plone/doc1/++conversation++default/' +
str(new_comment1_id), comment.absolute_url()
str(new_comment1_id), comment.absolute_url(),
)
def test_view_blob_types(self):
@ -254,7 +260,7 @@ class CommentTest(unittest.TestCase):
self.portal.invokeFactory(
id='image1',
title='Image',
type_name='Image'
type_name='Image',
)
conversation = IConversation(self.portal.image1)
@ -262,7 +268,7 @@ class CommentTest(unittest.TestCase):
comment1.text = 'Comment text'
new_comment1_id = conversation.addComment(comment1)
comment = self.portal.image1.restrictedTraverse(
'++conversation++default/{0}'.format(new_comment1_id)
'++conversation++default/{0}'.format(new_comment1_id),
)
view = View(comment, self.request)
@ -275,7 +281,8 @@ class CommentTest(unittest.TestCase):
"""
self.portal.portal_workflow.setChainForPortalTypes(
('Discussion Item',),
('comment_review_workflow,'))
('comment_review_workflow,'),
)
conversation = IConversation(self.portal.doc1)
comment1 = createObject('plone.Comment')
@ -290,15 +297,15 @@ class CommentTest(unittest.TestCase):
# Ensure the initial state was entered and recorded
self.assertEqual(
1,
len(comment.workflow_history['comment_review_workflow'])
len(comment.workflow_history['comment_review_workflow']),
)
self.assertEqual(
None,
comment.workflow_history['comment_review_workflow'][0]['action']
comment.workflow_history['comment_review_workflow'][0]['action'],
)
self.assertEqual(
'pending',
self.portal.portal_workflow.getInfoFor(comment, 'review_state')
self.portal.portal_workflow.getInfoFor(comment, 'review_state'),
)
def test_fti(self):
@ -306,7 +313,7 @@ class CommentTest(unittest.TestCase):
self.assertIn(
'Discussion Item',
self.portal.portal_types.objectIds()
self.portal.portal_types.objectIds(),
)
comment1 = createObject('plone.Comment')
@ -330,12 +337,16 @@ class CommentTest(unittest.TestCase):
new_comment1_id = conversation.addComment(comment1)
comment = self.portal.doc1.restrictedTraverse(
'++conversation++default/{0}'.format(new_comment1_id)
'++conversation++default/{0}'.format(new_comment1_id),
)
# make sure the view is there
self.assertTrue(getMultiAdapter((comment, self.request),
name='view'))
self.assertTrue(
getMultiAdapter(
(comment, self.request),
name='view',
),
)
# make sure the HTTP redirect (status code 302) works when a comment
# is called directly
@ -371,7 +382,7 @@ class RepliesTest(unittest.TestCase):
comment.text = 'Comment text'
new_id = replies.addComment(comment)
comment = self.portal.doc1.restrictedTraverse(
'++conversation++default/{0}'.format(new_id)
'++conversation++default/{0}'.format(new_id),
)
# Add a reply to the CommentReplies adapter of the first comment
@ -408,7 +419,7 @@ class RepliesTest(unittest.TestCase):
comment.text = 'Comment text'
new_id = replies.addComment(comment)
comment = self.portal.doc1.restrictedTraverse(
'++conversation++default/{0}'.format(new_id)
'++conversation++default/{0}'.format(new_id),
)
# Add a reply to the CommentReplies adapter of the first comment
@ -444,7 +455,7 @@ class RepliesTest(unittest.TestCase):
comment.text = 'Comment text'
new_id = conversation.addComment(comment)
comment = self.portal.doc1.restrictedTraverse(
'++conversation++default/{0}'.format(new_id)
'++conversation++default/{0}'.format(new_id),
)
# Add a reply to the CommentReplies adapter of the first comment
@ -453,7 +464,7 @@ class RepliesTest(unittest.TestCase):
replies = IReplies(comment)
new_re_id = replies.addComment(re_comment)
re_comment = self.portal.doc1.restrictedTraverse(
'++conversation++default/{0}'.format(new_re_id)
'++conversation++default/{0}'.format(new_re_id),
)
# Add a reply to the reply
@ -462,7 +473,7 @@ class RepliesTest(unittest.TestCase):
replies = IReplies(re_comment)
new_re_re_id = replies.addComment(re_re_comment)
re_re_comment = self.portal.doc1.restrictedTraverse(
'++conversation++default/{0}'.format(new_re_re_id)
'++conversation++default/{0}'.format(new_re_re_id),
)
# Add a reply to the replies reply
@ -471,47 +482,47 @@ class RepliesTest(unittest.TestCase):
replies = IReplies(re_re_comment)
new_re_re_re_id = replies.addComment(re_re_re_comment)
re_re_re_comment = self.portal.doc1.restrictedTraverse(
'++conversation++default/{0}'.format(new_re_re_re_id)
'++conversation++default/{0}'.format(new_re_re_re_id),
)
self.assertEqual(
('', 'plone', 'doc1', '++conversation++default', str(new_id)),
comment.getPhysicalPath()
comment.getPhysicalPath(),
)
self.assertEqual(
'http://nohost/plone/doc1/++conversation++default/' +
str(new_id), comment.absolute_url()
str(new_id), comment.absolute_url(),
)
self.assertEqual(
('', 'plone', 'doc1', '++conversation++default', str(new_re_id)),
re_comment.getPhysicalPath()
re_comment.getPhysicalPath(),
)
self.assertEqual(
'http://nohost/plone/doc1/++conversation++default/' +
str(new_re_id),
re_comment.absolute_url()
re_comment.absolute_url(),
)
self.assertEqual(
(
'', 'plone', 'doc1', '++conversation++default',
str(new_re_re_id)
str(new_re_re_id),
),
re_re_comment.getPhysicalPath()
re_re_comment.getPhysicalPath(),
)
self.assertEqual(
'http://nohost/plone/doc1/++conversation++default/' +
str(new_re_re_id),
re_re_comment.absolute_url()
re_re_comment.absolute_url(),
)
self.assertEqual(
(
'', 'plone', 'doc1', '++conversation++default',
str(new_re_re_re_id)
str(new_re_re_re_id),
),
re_re_re_comment.getPhysicalPath()
re_re_re_comment.getPhysicalPath(),
)
self.assertEqual(
'http://nohost/plone/doc1/++conversation++default/' +
str(new_re_re_re_id),
re_re_re_comment.absolute_url()
re_re_re_comment.absolute_url(),
)

View File

@ -80,7 +80,7 @@ class TestCommentForm(unittest.TestCase):
adapts=(Interface, IBrowserRequest),
provides=Interface,
factory=CommentForm,
name=u'comment-form'
name=u'comment-form',
)
# The form should return an error if the comment text field is empty
@ -88,7 +88,7 @@ class TestCommentForm(unittest.TestCase):
commentForm = getMultiAdapter(
(self.context, request),
name=u'comment-form'
name=u'comment-form',
)
commentForm.update()
data, errors = commentForm.extractData() # pylint: disable-msg=W0612
@ -102,7 +102,7 @@ class TestCommentForm(unittest.TestCase):
commentForm = getMultiAdapter(
(self.context, request),
name=u'comment-form'
name=u'comment-form',
)
commentForm.update()
data, errors = commentForm.extractData() # pylint: disable-msg=W0612
@ -144,14 +144,14 @@ class TestCommentForm(unittest.TestCase):
adapts=(Interface, IBrowserRequest),
provides=Interface,
factory=CommentForm,
name=u'comment-form'
name=u'comment-form',
)
provideAdapter(
adapts=(Interface, IBrowserRequest),
provides=Interface,
factory=EditCommentForm,
name=u'edit-comment-form'
name=u'edit-comment-form',
)
# The form is submitted successfully, if the required text field is
@ -160,7 +160,7 @@ class TestCommentForm(unittest.TestCase):
commentForm = getMultiAdapter(
(self.context, request),
name=u'comment-form'
name=u'comment-form',
)
commentForm.update()
data, errors = commentForm.extractData() # pylint: disable-msg=W0612
@ -174,7 +174,7 @@ class TestCommentForm(unittest.TestCase):
request = make_request(form={'form.widgets.text': u'foobar'})
editForm = getMultiAdapter(
(comment, request),
name=u'edit-comment-form'
name=u'edit-comment-form',
)
editForm.update()
data, errors = editForm.extractData() # pylint: disable-msg=W0612
@ -219,7 +219,7 @@ class TestCommentForm(unittest.TestCase):
adapts=(Interface, IBrowserRequest),
provides=Interface,
factory=CommentForm,
name=u'comment-form'
name=u'comment-form',
)
# The form is submitted successfully, if the required text field is
@ -228,7 +228,7 @@ class TestCommentForm(unittest.TestCase):
commentForm = getMultiAdapter(
(self.context, form_request),
name=u'comment-form'
name=u'comment-form',
)
commentForm.update()
@ -241,14 +241,14 @@ class TestCommentForm(unittest.TestCase):
comment = [x for x in conversation.getComments()][-1]
deleteView = getMultiAdapter(
(comment, self.request),
name=u'moderate-delete-comment'
name=u'moderate-delete-comment',
)
# try to delete last comment without 'Delete comments' permission
setRoles(self.portal, TEST_USER_ID, ['Member'])
self.assertRaises(
Unauthorized,
comment.restrictedTraverse,
'@@moderate-delete-comment'
'@@moderate-delete-comment',
)
deleteView()
self.assertEqual(1, len([x for x in conversation.getComments()]))
@ -277,7 +277,7 @@ class TestCommentForm(unittest.TestCase):
adapts=(Interface, IBrowserRequest),
provides=Interface,
factory=CommentForm,
name=u'comment-form'
name=u'comment-form',
)
# The form is submitted successfully, if the required text field is
@ -286,7 +286,7 @@ class TestCommentForm(unittest.TestCase):
commentForm = getMultiAdapter(
(self.context, form_request),
name=u'comment-form'
name=u'comment-form',
)
commentForm.update()
@ -299,7 +299,7 @@ class TestCommentForm(unittest.TestCase):
comment = [x for x in conversation.getComments()][-1]
deleteView = getMultiAdapter(
(comment, self.request),
name=u'delete-own-comment'
name=u'delete-own-comment',
)
# try to delete last comment with johndoe
setRoles(self.portal, 'johndoe', ['Member'])
@ -307,7 +307,7 @@ class TestCommentForm(unittest.TestCase):
self.assertRaises(
Unauthorized,
comment.restrictedTraverse,
'@@delete-own-comment'
'@@delete-own-comment',
)
self.assertEqual(1, len([x for x in conversation.getComments()]))
# try to delete last comment with the same user that created it
@ -343,12 +343,12 @@ class TestCommentForm(unittest.TestCase):
# Post an anonymous comment and provide a name
request = make_request(form={
'form.widgets.name': u'john doe',
'form.widgets.text': u'bar'
'form.widgets.text': u'bar',
})
commentForm = getMultiAdapter(
(self.context, request),
name=u'comment-form'
name=u'comment-form',
)
commentForm.update()
data, errors = commentForm.extractData() # pylint: disable-msg=W0612
@ -391,7 +391,7 @@ class TestCommentForm(unittest.TestCase):
commentForm = getMultiAdapter(
(self.context, request),
name=u'comment-form'
name=u'comment-form',
)
commentForm.update()
data, errors = commentForm.extractData() # pylint: disable-msg=W0612
@ -438,7 +438,7 @@ class TestCommentForm(unittest.TestCase):
Unauthorized,
commentForm.handleComment,
commentForm,
'foo'
'foo',
)
@ -454,7 +454,7 @@ class TestCommentsViewlet(unittest.TestCase):
self.folder = self.portal['test-folder']
interface.alsoProvides(
self.request,
interfaces.IDiscussionLayer
interfaces.IDiscussionLayer,
)
self.workflowTool = getToolByName(self.portal, 'portal_workflow')
@ -532,7 +532,7 @@ class TestCommentsViewlet(unittest.TestCase):
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.'
'into clickable links.',
)
# Enable moderation workflow
@ -567,7 +567,8 @@ class TestCommentsViewlet(unittest.TestCase):
replies = self.viewlet.get_replies()
next(replies)
next(replies)
self.assertRaises(StopIteration, replies.next)
with self.assertRaises(StopIteration):
next(replies)
def test_get_replies_on_non_annotatable_object(self):
context = self.portal.MailHost # the mail host is not annotatable
@ -575,7 +576,8 @@ class TestCommentsViewlet(unittest.TestCase):
replies = viewlet.get_replies()
self.assertEqual(len(tuple(replies)), 0)
replies = viewlet.get_replies()
self.assertRaises(StopIteration, replies.next)
with self.assertRaises(StopIteration):
next(replies)
def test_get_replies_with_workflow_actions(self):
self.assertFalse(self.viewlet.get_replies(workflow_actions=True))
@ -585,25 +587,25 @@ class TestCommentsViewlet(unittest.TestCase):
c1 = conversation.addComment(comment)
self.assertEqual(
len(tuple(self.viewlet.get_replies(workflow_actions=True))),
1
1,
)
# Enable moderation workflow
self.workflowTool.setChainForPortalTypes(
('Discussion Item',),
('comment_review_workflow,')
('comment_review_workflow,'),
)
# Check if workflow actions are available
reply = next(self.viewlet.get_replies(workflow_actions=True))
self.assertTrue('actions' in reply)
self.assertEqual(
reply['actions'][0]['id'],
'publish'
'publish',
)
expected_url = 'http://nohost/plone/doc1/++conversation++default/{0}' \
'/content_status_modify?workflow_action=publish'
self.assertEqual(
reply['actions'][0]['url'],
expected_url.format(int(c1))
expected_url.format(int(c1)),
)
def test_get_commenter_home_url(self):
@ -614,7 +616,7 @@ class TestCommentsViewlet(unittest.TestCase):
m = portal_membership.getAuthenticatedMember()
self.assertEqual(
self.viewlet.get_commenter_home_url(m.getUserName()),
'http://nohost/plone/author/test-user'
'http://nohost/plone/author/test-user',
)
def test_get_commenter_home_url_is_none(self):
@ -627,15 +629,15 @@ class TestCommentsViewlet(unittest.TestCase):
self.memberdata._setPortrait(Image(
id='jim',
file=dummy.File(),
title=''
title='',
), 'jim')
self.assertEqual(
self.memberdata._getPortrait('jim').getId(),
'jim'
'jim',
)
self.assertEqual(
self.memberdata._getPortrait('jim').meta_type,
'Image'
'Image',
)
# Add a conversation with a comment
@ -653,7 +655,7 @@ class TestCommentsViewlet(unittest.TestCase):
# Check if the correct member image URL is returned
self.assertEqual(
portrait_url,
'http://nohost/plone/portal_memberdata/portraits/jim'
'http://nohost/plone/portal_memberdata/portraits/jim',
)
def test_get_commenter_portrait_is_none(self):
@ -662,8 +664,7 @@ class TestCommentsViewlet(unittest.TestCase):
self.viewlet.get_commenter_portrait() in (
'defaultUser.png',
'defaultUser.gif',
)
),
)
def test_get_commenter_portrait_without_userimage(self):
@ -689,8 +690,8 @@ class TestCommentsViewlet(unittest.TestCase):
self.assertTrue(
portrait_url in (
'http://nohost/plone/defaultUser.png',
'http://nohost/plone/defaultUser.gif'
)
'http://nohost/plone/defaultUser.gif',
),
)
def test_anonymous_discussion_allowed(self):
@ -723,7 +724,7 @@ class TestCommentsViewlet(unittest.TestCase):
self.viewlet.update()
self.assertEqual(
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):
@ -737,8 +738,8 @@ class TestCommentsViewlet(unittest.TestCase):
# time of the local time given above. That way, the time for the
# example below is correct within each time zone, independent of DST
python_time = datetime(
*time.gmtime(time.mktime(python_time.timetuple()))[:7]
)
*time.gmtime(time.mktime(python_time.timetuple()))[:7])
localized_time = self.viewlet.format_time(python_time)
self.assertTrue(
localized_time in ['Feb 01, 2009 11:32 PM', '2009-02-01 23:32'])
localized_time in ['Feb 01, 2009 11:32 PM', '2009-02-01 23:32'],
)

View File

@ -30,7 +30,7 @@ class CommentContentRulesTest(unittest.TestCase):
member = self.portal.portal_membership.getMemberById(TEST_USER_ID)
member.setMemberProperties({
'fullname': 'X Manager',
'email': 'xmanager@example.com'
'email': 'xmanager@example.com',
})
setRoles(self.portal, TEST_USER_ID, ['Manager'])
@ -54,7 +54,7 @@ class CommentContentRulesTest(unittest.TestCase):
def testCommentIdStringSubstitution(self):
comment_id = getAdapter(self.document, IStringSubstitution,
name=u'comment_id')
self.assertIsInstance(comment_id(), long)
self.assertIsInstance(comment_id(), int)
def testCommentTextStringSubstitution(self):
comment_text = getAdapter(self.document, IStringSubstitution,
@ -96,7 +96,7 @@ class ReplyContentRulesTest(unittest.TestCase):
comment.text = 'This is a comment'
new_id = replies.addComment(comment)
comment = self.document.restrictedTraverse(
'++conversation++default/{0}'.format(new_id)
'++conversation++default/{0}'.format(new_id),
)
re_comment = createObject('plone.Comment')
@ -112,15 +112,15 @@ class ReplyContentRulesTest(unittest.TestCase):
reply_id = getAdapter(
self.document,
IStringSubstitution,
name=u'comment_id'
name=u'comment_id',
)
self.assertIsInstance(reply_id(), long)
self.assertIsInstance(reply_id(), int)
def testReplyTextStringSubstitution(self):
reply_text = getAdapter(
self.document,
IStringSubstitution,
name=u'comment_text'
name=u'comment_text',
)
self.assertEqual(reply_text(), u'This is a reply')
@ -128,7 +128,7 @@ class ReplyContentRulesTest(unittest.TestCase):
reply_user_id = getAdapter(
self.document,
IStringSubstitution,
name=u'comment_user_id'
name=u'comment_user_id',
)
self.assertEqual(reply_user_id(), u'julia')
@ -136,7 +136,7 @@ class ReplyContentRulesTest(unittest.TestCase):
reply_user_fullname = getAdapter(
self.document,
IStringSubstitution,
name=u'comment_user_fullname'
name=u'comment_user_fullname',
)
self.assertEqual(reply_user_fullname(), u'Juliana')
@ -144,6 +144,6 @@ class ReplyContentRulesTest(unittest.TestCase):
reply_user_email = getAdapter(
self.document,
IStringSubstitution,
name=u'comment_user_email'
name=u'comment_user_email',
)
self.assertEqual(reply_user_email(), u'julia@example.com')

View File

@ -29,7 +29,7 @@ class RegistryTest(unittest.TestCase):
def test_discussion_controlpanel_view(self):
view = getMultiAdapter(
(self.portal, self.portal.REQUEST),
name='discussion-controlpanel'
name='discussion-controlpanel',
)
self.assertTrue(view())
@ -40,7 +40,7 @@ class RegistryTest(unittest.TestCase):
'discussion' in [
a.getAction(self)['id']
for a in self.controlpanel.listActions()
]
],
)
def test_globally_enabled(self):
@ -51,7 +51,7 @@ class RegistryTest(unittest.TestCase):
'plone.app.discussion.interfaces.' +
'IDiscussionSettings.globally_enabled'
],
False
False,
)
def test_anonymous_comments(self):
@ -62,7 +62,7 @@ class RegistryTest(unittest.TestCase):
'plone.app.discussion.interfaces.' +
'IDiscussionSettings.anonymous_comments'
],
False
False,
)
def test_moderation_enabled(self):
@ -73,7 +73,7 @@ class RegistryTest(unittest.TestCase):
'plone.app.discussion.interfaces.' +
'IDiscussionSettings.moderation_enabled'
],
False
False,
)
def test_edit_comment_enabled(self):
@ -82,7 +82,8 @@ class RegistryTest(unittest.TestCase):
self.assertEqual(
self.registry['plone.app.discussion.interfaces.' +
'IDiscussionSettings.edit_comment_enabled'],
False)
False,
)
def test_delete_own_comment_enabled(self):
# Check delete_own_comment_enabled record
@ -90,7 +91,8 @@ class RegistryTest(unittest.TestCase):
self.assertEqual(
self.registry['plone.app.discussion.interfaces.' +
'IDiscussionSettings.delete_own_comment_enabled'],
False)
False,
)
def test_text_transform(self):
self.assertTrue('text_transform' in IDiscussionSettings)
@ -99,7 +101,7 @@ class RegistryTest(unittest.TestCase):
'plone.app.discussion.interfaces.' +
'IDiscussionSettings.text_transform'
],
'text/plain'
'text/plain',
)
def test_captcha(self):
@ -110,7 +112,7 @@ class RegistryTest(unittest.TestCase):
'plone.app.discussion.interfaces.' +
'IDiscussionSettings.captcha'
],
'disabled'
'disabled',
)
def test_show_commenter_image(self):
@ -121,20 +123,20 @@ class RegistryTest(unittest.TestCase):
'plone.app.discussion.interfaces.' +
'IDiscussionSettings.show_commenter_image'
],
True
True,
)
def test_moderator_notification_enabled(self):
# Check show_commenter_image record
self.assertTrue(
'moderator_notification_enabled' in IDiscussionSettings
'moderator_notification_enabled' in IDiscussionSettings,
)
self.assertEqual(
self.registry[
'plone.app.discussion.interfaces.' +
'IDiscussionSettings.moderator_notification_enabled'
],
False
False,
)
# def test_user_notification_enabled(self):
@ -167,8 +169,8 @@ class ConfigurationChangedSubscriberTest(unittest.TestCase):
self.assertEqual(
('comment_one_state_workflow',),
self.portal.portal_workflow.getChainForPortalType(
'Discussion Item'
)
'Discussion Item',
),
)
# Enable moderation in the discussion control panel
@ -179,16 +181,16 @@ class ConfigurationChangedSubscriberTest(unittest.TestCase):
self.assertEqual(
('comment_review_workflow',),
self.portal.portal_workflow.getChainForPortalType(
'Discussion Item'
)
'Discussion Item',
),
)
# And back
self.settings.moderation_enabled = False
self.assertEqual(
('comment_one_state_workflow',),
self.portal.portal_workflow.getChainForPortalType(
'Discussion Item'
)
'Discussion Item',
),
)
def test_change_workflow_in_types_control_panel(self):
@ -202,7 +204,7 @@ class ConfigurationChangedSubscriberTest(unittest.TestCase):
# Enable the 'comment_review_workflow' with moderation enabled
self.portal.portal_workflow.setChainForPortalTypes(
('Discussion Item',),
('comment_review_workflow',)
('comment_review_workflow',),
)
# Make sure the moderation_enabled settings has changed
@ -211,14 +213,14 @@ class ConfigurationChangedSubscriberTest(unittest.TestCase):
# Enable the 'comment_review_workflow' with moderation enabled
self.portal.portal_workflow.setChainForPortalTypes(
('Discussion Item',),
('comment_one_state_workflow',)
('comment_one_state_workflow',),
)
self.settings.moderation_enabled = True
# Enable a 'custom' discussion workflow
self.portal.portal_workflow.setChainForPortalTypes(
('Discussion Item',),
('intranet_workflow',)
('intranet_workflow',),
)
# Setting has not changed. A Custom workflow disables the

View File

@ -68,11 +68,11 @@ class ConversationTest(unittest.TestCase):
new_id = conversation.addComment(comment)
# Check that the conversation methods return the correct data
self.assertTrue(isinstance(comment.comment_id, long))
self.assertTrue(isinstance(comment.comment_id, int))
self.assertTrue(IComment.providedBy(conversation[new_id]))
self.assertEqual(
aq_base(conversation[new_id].__parent__),
aq_base(conversation)
aq_base(conversation),
)
self.assertEqual(new_id, comment.comment_id)
self.assertEqual(len(list(conversation.getComments())), 1)
@ -80,7 +80,7 @@ class ConversationTest(unittest.TestCase):
self.assertEqual(conversation.total_comments(), 1)
self.assertTrue(
conversation.last_comment_date - datetime.utcnow() <
timedelta(seconds=1)
timedelta(seconds=1),
)
def test_private_comment(self):
@ -278,7 +278,7 @@ class ConversationTest(unittest.TestCase):
# Create a conversation.
conversation = self.portal.doc1.restrictedTraverse(
'@@conversation_view'
'@@conversation_view',
)
# The Document content type is disabled by default
@ -326,7 +326,7 @@ class ConversationTest(unittest.TestCase):
# Create a conversation.
conversation = self.portal.doc1.restrictedTraverse(
'@@conversation_view'
'@@conversation_view',
)
# Discussion is disallowed by default
@ -396,7 +396,7 @@ class ConversationTest(unittest.TestCase):
self.assertTrue((new_id1, comment1) in six.iteritems(conversation))
self.assertTrue((new_id2, comment2) in six.iteritems(conversation))
# TODO test acquisition wrapping
# TODO test acquisition wrapping # noqa T000
# self.assertTrue(aq_base(aq_parent(comment1)) is conversation)
def test_total_comments(self):
@ -512,11 +512,11 @@ class ConversationTest(unittest.TestCase):
# check if the latest comment is exactly one day old
self.assertTrue(
conversation.last_comment_date < datetime.utcnow() -
timedelta(hours=23, minutes=59, seconds=59)
timedelta(hours=23, minutes=59, seconds=59),
)
self.assertTrue(
conversation.last_comment_date >
datetime.utcnow() - timedelta(days=1, seconds=1)
datetime.utcnow() - timedelta(days=1, seconds=1),
)
# remove the latest comment
@ -526,11 +526,11 @@ class ConversationTest(unittest.TestCase):
# the latest comment should be exactly two days old
self.assertTrue(
conversation.last_comment_date < datetime.utcnow() -
timedelta(days=1, hours=23, minutes=59, seconds=59)
timedelta(days=1, hours=23, minutes=59, seconds=59),
)
self.assertTrue(
conversation.last_comment_date > datetime.utcnow() -
timedelta(days=2, seconds=1)
timedelta(days=2, seconds=1),
)
# remove the latest comment again
@ -540,11 +540,11 @@ class ConversationTest(unittest.TestCase):
# the latest comment should be exactly four days old
self.assertTrue(
conversation.last_comment_date < datetime.utcnow() -
timedelta(days=3, hours=23, minutes=59, seconds=59)
timedelta(days=3, hours=23, minutes=59, seconds=59),
)
self.assertTrue(
conversation.last_comment_date > datetime.utcnow() -
timedelta(days=4, seconds=2)
timedelta(days=4, seconds=2),
)
def test_get_comments_full(self):
@ -618,7 +618,7 @@ class ConversationTest(unittest.TestCase):
], list(conversation.getThreads()))
def test_get_threads_batched(self):
# TODO: test start, size, root and depth arguments to getThreads()
# TODO: test start, size, root and depth arguments to getThreads() # noqa T000
# - may want to split this into multiple tests
pass
@ -626,25 +626,25 @@ class ConversationTest(unittest.TestCase):
# make sure we can traverse to conversations and get a URL and path
conversation = self.portal.doc1.restrictedTraverse(
'++conversation++default'
'++conversation++default',
)
self.assertTrue(IConversation.providedBy(conversation))
self.assertEqual(
('', 'plone', 'doc1', '++conversation++default'),
conversation.getPhysicalPath()
conversation.getPhysicalPath(),
)
self.assertEqual(
'http://nohost/plone/doc1/++conversation++default',
conversation.absolute_url()
conversation.absolute_url(),
)
def test_unconvertible_id(self):
# make sure the conversation view doesn't break when given comment id
# can't be converted to long
# can't be converted to int
conversation = self.portal.doc1.restrictedTraverse(
'++conversation++default/ThisCantBeRight'
'++conversation++default/ThisCantBeRight',
)
self.assertEqual(conversation, None)
@ -668,7 +668,7 @@ class ConversationTest(unittest.TestCase):
# Make sure no conversation has been created
self.assertTrue(
'plone.app.discussion:conversation' not in
IAnnotations(self.portal.doc1)
IAnnotations(self.portal.doc1),
)
@ -681,13 +681,13 @@ class ConversationEnabledForDexterityTypesTest(unittest.TestCase):
setRoles(self.portal, TEST_USER_ID, ['Manager'])
interface.alsoProvides(
self.portal.REQUEST,
interfaces.IDiscussionLayer
interfaces.IDiscussionLayer,
)
if DEXTERITY:
interface.alsoProvides(
self.portal.doc1,
IDexterityContent
IDexterityContent,
)
def _makeOne(self, *args, **kw):
@ -847,18 +847,18 @@ class RepliesTest(unittest.TestCase):
# Create the nested comment structure
new_id_1 = replies.addComment(comment1)
comment1 = self.portal.doc1.restrictedTraverse(
'++conversation++default/{0}'.format(new_id_1)
'++conversation++default/{0}'.format(new_id_1),
)
replies_to_comment1 = IReplies(comment1)
new_id_2 = replies.addComment(comment2)
comment2 = self.portal.doc1.restrictedTraverse(
'++conversation++default/{0}'.format(new_id_2)
'++conversation++default/{0}'.format(new_id_2),
)
replies_to_comment2 = IReplies(comment2)
new_id_1_1 = replies_to_comment1.addComment(comment1_1)
comment1_1 = self.portal.doc1.restrictedTraverse(
'++conversation++default/{0}'.format(new_id_1_1)
'++conversation++default/{0}'.format(new_id_1_1),
)
replies_to_comment1_1 = IReplies(comment1_1)
replies_to_comment1_1.addComment(comment1_1_1)

View File

@ -152,7 +152,7 @@ class RepliesEventsTest(unittest.TestCase):
comment.text = 'Comment text'
new_id = replies.addComment(comment)
comment = self.document.restrictedTraverse(
'++conversation++default/{0}'.format(new_id)
'++conversation++default/{0}'.format(new_id),
)
re_comment = createObject('plone.Comment')
@ -173,7 +173,7 @@ class RepliesEventsTest(unittest.TestCase):
comment.text = 'Comment text'
new_id = replies.addComment(comment)
comment = self.portal.doc1.restrictedTraverse(
'++conversation++default/{0}'.format(new_id)
'++conversation++default/{0}'.format(new_id),
)
re_comment = createObject('plone.Comment')

View File

@ -25,11 +25,16 @@ normal_testfiles = [
def test_suite():
suite = unittest.TestSuite()
suite.addTests([
layered(doctest.DocFileSuite(test,
layered(
doctest.DocFileSuite(
test,
optionflags=optionflags,
globs={'pprint': pprint.pprint,
globs={
'pprint': pprint.pprint,
}
),
layer=PLONE_APP_DISCUSSION_FUNCTIONAL_TESTING)
for test in normal_testfiles])
layer=PLONE_APP_DISCUSSION_FUNCTIONAL_TESTING,
)
for test in normal_testfiles
])
return suite

View File

@ -70,7 +70,7 @@ class ConversationIndexersTest(unittest.TestCase):
def test_conversation_total_comments(self):
self.assertTrue(isinstance(
catalog.total_comments,
DelegatingIndexerFactory
DelegatingIndexerFactory,
))
self.assertEqual(catalog.total_comments(self.portal.doc1)(), 3)
del self.conversation[self.new_id1]
@ -82,16 +82,16 @@ class ConversationIndexersTest(unittest.TestCase):
def test_conversation_last_comment_date(self):
self.assertTrue(isinstance(
catalog.last_comment_date,
DelegatingIndexerFactory
DelegatingIndexerFactory,
))
self.assertEqual(
catalog.last_comment_date(self.portal.doc1)(),
datetime(2009, 4, 12, 11, 12, 12)
datetime(2009, 4, 12, 11, 12, 12),
)
del self.conversation[self.new_id3]
self.assertEqual(
catalog.last_comment_date(self.portal.doc1)(),
datetime(2007, 12, 13, 4, 18, 12)
datetime(2007, 12, 13, 4, 18, 12),
)
del self.conversation[self.new_id2]
del self.conversation[self.new_id1]
@ -138,7 +138,7 @@ class CommentIndexersTest(unittest.TestCase):
def test_description(self):
self.assertEqual(
catalog.description(self.comment)(),
'Lorem ipsum dolor sit amet.'
'Lorem ipsum dolor sit amet.',
)
self.assertTrue(
isinstance(catalog.description, DelegatingIndexerFactory))
@ -153,33 +153,33 @@ class CommentIndexersTest(unittest.TestCase):
self.conversation.addComment(comment_long)
self.assertEqual(
catalog.description(comment_long)(),
LONG_TEXT_CUT.replace('\n', ' ')
LONG_TEXT_CUT.replace('\n', ' '),
)
def test_dates(self):
# Test if created, modified, effective etc. are set correctly
self.assertEqual(
catalog.created(self.comment)(),
DateTime(2006, 9, 17, 14, 18, 12, 'GMT')
DateTime(2006, 9, 17, 14, 18, 12, 'GMT'),
)
self.assertEqual(
catalog.effective(self.comment)(),
DateTime(2006, 9, 17, 14, 18, 12, 'GMT')
DateTime(2006, 9, 17, 14, 18, 12, 'GMT'),
)
self.assertEqual(
catalog.modified(self.comment)(),
DateTime(2008, 3, 12, 7, 32, 52, 'GMT')
DateTime(2008, 3, 12, 7, 32, 52, 'GMT'),
)
def test_searchable_text(self):
# Test if searchable text is a concatenation of title and comment text
self.assertEqual(
catalog.searchable_text(self.comment)(),
('Lorem ipsum dolor sit amet.')
('Lorem ipsum dolor sit amet.'),
)
self.assertTrue(isinstance(
catalog.searchable_text,
DelegatingIndexerFactory
DelegatingIndexerFactory,
))
def test_creator(self):

View File

@ -69,7 +69,9 @@ class ModerationBulkActionsViewTest(unittest.TestCase):
None)
self.context = self.portal
self.portal.portal_workflow.setChainForPortalTypes(
('Discussion Item',), 'comment_review_workflow')
('Discussion Item',),
'comment_review_workflow',
)
self.wf_tool = self.portal.portal_workflow
# Add a conversation with three comments
conversation = IConversation(self.portal.doc1)
@ -79,7 +81,7 @@ class ModerationBulkActionsViewTest(unittest.TestCase):
comment1.Creator = 'Jim'
new_id_1 = conversation.addComment(comment1)
self.comment1 = self.portal.doc1.restrictedTraverse(
'++conversation++default/{0}'.format(new_id_1)
'++conversation++default/{0}'.format(new_id_1),
)
comment2 = createObject('plone.Comment')
comment2.title = 'Comment 2'
@ -87,7 +89,7 @@ class ModerationBulkActionsViewTest(unittest.TestCase):
comment2.Creator = 'Joe'
new_id_2 = conversation.addComment(comment2)
self.comment2 = self.portal.doc1.restrictedTraverse(
'++conversation++default/{0}'.format(new_id_2)
'++conversation++default/{0}'.format(new_id_2),
)
comment3 = createObject('plone.Comment')
comment3.title = 'Comment 3'
@ -95,7 +97,7 @@ class ModerationBulkActionsViewTest(unittest.TestCase):
comment3.Creator = 'Emma'
new_id_3 = conversation.addComment(comment3)
self.comment3 = self.portal.doc1.restrictedTraverse(
'++conversation++default/{0}'.format(new_id_3)
'++conversation++default/{0}'.format(new_id_3),
)
self.conversation = conversation
@ -114,8 +116,7 @@ class ModerationBulkActionsViewTest(unittest.TestCase):
view = BulkActionsView(self.portal, self.request)
self.assertRaises(NotImplementedError,
view)
self.assertRaises(NotImplementedError, view)
def test_publish(self):
self.request.set('form.select.BulkAction', 'publish')
@ -176,12 +177,13 @@ class RedirectionTest(unittest.TestCase):
settings.globally_enabled = True
self.portal.portal_workflow.setChainForPortalTypes(
('Discussion Item',),
('comment_review_workflow',))
('comment_review_workflow',),
)
# Create page plus comment.
self.portal.invokeFactory(
id='page',
title='Page 1',
type_name='Document'
type_name='Document',
)
self.page = self.portal.page
self.conversation = IConversation(self.page)

View File

@ -176,7 +176,7 @@ class TestModeratorNotificationUnit(unittest.TestCase):
self.portal.portal_types['Document'].allow_discussion = True
self.portal.portal_workflow.setChainForPortalTypes(
('Discussion Item',),
('comment_review_workflow',)
('comment_review_workflow',),
)
# Enable moderator notification setting
registry = queryUtility(IRegistry)
@ -219,23 +219,23 @@ class TestModeratorNotificationUnit(unittest.TestCase):
in msg)
self.assertIn(
'http://nohost/plone/d=\noc1/view#{0}'.format(comment_id),
msg
msg,
)
self.assertIn(
'Comment text',
msg
msg,
)
text = 'Approve comment:\nhttp://nohost/plone/doc1/' \
'++conversation++default/{0}/@@moderat=\ne-publish-comment'
self.assertIn(
text.format(comment_id),
msg
msg,
)
text = 'Delete comment:\nhttp://nohost/plone/doc1/' \
'++conversation++default/{0}/@@moderat=\ne-delete-comment'
self.assertIn(
text.format(comment_id),
msg
msg,
)
def test_notify_moderator_specific_address(self):

View File

@ -23,7 +23,7 @@ def test_suite():
suite.addTests([
layered(
robottestsuite,
layer=PLONE_APP_DISCUSSION_ROBOT_TESTING
layer=PLONE_APP_DISCUSSION_ROBOT_TESTING,
),
])
return suite

View File

@ -47,8 +47,8 @@ class WorkflowSetupTest(unittest.TestCase):
self.assertEqual(
('comment_one_state_workflow',),
self.portal.portal_workflow.getChainForPortalType(
'Discussion Item'
)
'Discussion Item',
),
)
def test_review_comments_permission(self):
@ -61,9 +61,9 @@ class WorkflowSetupTest(unittest.TestCase):
self.assertFalse(
self.portal.portal_membership.checkPermission(
'Review comments',
self.folder
self.folder,
),
self.folder
self.folder,
)
def test_reply_to_item_permission(self):
@ -125,7 +125,7 @@ class CommentOneStateWorkflowTest(unittest.TestCase):
cid = conversation.addComment(comment)
self.comment = self.folder.doc1.restrictedTraverse(
'++conversation++default/{0}'.format(cid)
'++conversation++default/{0}'.format(cid),
)
self.portal.acl_users._doAddUser('member', 'secret', ['Member'], [])
@ -138,8 +138,10 @@ class CommentOneStateWorkflowTest(unittest.TestCase):
def test_initial_workflow_state(self):
"""Make sure the initial workflow state of a comment is 'private'.
"""
self.assertEqual(self.workflow.getInfoFor(self.doc, 'review_state'),
'private')
self.assertEqual(
self.workflow.getInfoFor(self.doc, 'review_state'),
'private',
)
def test_view_comments(self):
"""Make sure published comments can be viewed by everyone.
@ -184,7 +186,8 @@ class CommentOneStateWorkflowTest(unittest.TestCase):
# The workflow chain is still what we want.
self.assertEqual(
self.portal.portal_workflow.getChainFor('Discussion Item'),
('comment_one_state_workflow',))
('comment_one_state_workflow',),
)
# A Manager can still see the comment.
self.assertTrue(checkPerm(View, self.comment))
# Anonymous cannot see the comment.
@ -209,7 +212,8 @@ class CommentReviewWorkflowTest(unittest.TestCase):
# Set workflow for Discussion item to review workflow
self.portal.portal_workflow.setChainForPortalTypes(
('Discussion Item',),
('comment_review_workflow',))
('comment_review_workflow',),
)
# Create a conversation for this Document
conversation = IConversation(self.portal.doc1)
@ -219,7 +223,7 @@ class CommentReviewWorkflowTest(unittest.TestCase):
comment.text = 'Comment text'
comment_id = conversation.addComment(comment)
comment = self.portal.doc1.restrictedTraverse(
'++conversation++default/{0}'.format(comment_id)
'++conversation++default/{0}'.format(comment_id),
)
self.conversation = conversation
@ -239,9 +243,11 @@ class CommentReviewWorkflowTest(unittest.TestCase):
# Make sure that anonymous users can not delete comments
logout()
self.portal.REQUEST.form['comment_id'] = self.comment_id
self.assertRaises(Unauthorized,
self.assertRaises(
Unauthorized,
self.comment.restrictedTraverse,
'@@moderate-delete-comment')
'@@moderate-delete-comment',
)
self.assertTrue(self.comment_id in self.conversation.objectIds())
def test_delete_as_user(self):
@ -249,9 +255,11 @@ class CommentReviewWorkflowTest(unittest.TestCase):
logout()
setRoles(self.portal, TEST_USER_ID, ['Member'])
self.portal.REQUEST.form['comment_id'] = self.comment_id
self.assertRaises(Unauthorized,
self.assertRaises(
Unauthorized,
self.comment.restrictedTraverse,
'@@moderate-delete-comment')
'@@moderate-delete-comment',
)
self.assertTrue(self.comment_id in self.conversation.objectIds())
def test_publish(self):
@ -261,8 +269,8 @@ class CommentReviewWorkflowTest(unittest.TestCase):
'pending',
self.portal.portal_workflow.getInfoFor(
self.comment,
'review_state'
)
'review_state',
),
)
view = self.comment.restrictedTraverse('@@moderate-publish-comment')
view()
@ -270,8 +278,8 @@ class CommentReviewWorkflowTest(unittest.TestCase):
'published',
self.portal.portal_workflow.getInfoFor(
self.comment,
'review_state'
)
'review_state',
),
)
def test_publish_as_anonymous(self):
@ -281,20 +289,20 @@ class CommentReviewWorkflowTest(unittest.TestCase):
self.assertEqual(
'pending', self.portal.portal_workflow.getInfoFor(
self.comment,
'review_state'
)
'review_state',
),
)
self.assertRaises(
Unauthorized,
self.comment.restrictedTraverse,
'@@moderate-publish-comment'
'@@moderate-publish-comment',
)
self.assertEqual(
'pending',
self.portal.portal_workflow.getInfoFor(
self.comment,
'review_state'
)
'review_state',
),
)
def test_publish_comment_on_private_content_not_visible_to_world(self):

View File

@ -4,7 +4,7 @@ from setuptools import find_packages
from setuptools import setup
version = '3.0.6.dev0'
version = '3.0.7.dev0'
install_requires = [
'setuptools',
@ -37,9 +37,12 @@ setup(name='plone.app.discussion',
'Framework :: Plone',
'Framework :: Plone :: 5.0',
'Framework :: Plone :: 5.1',
'Framework :: Plone :: 5.2',
'License :: OSI Approved :: GNU General Public License (GPL)',
'Programming Language :: Python',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
],
keywords='plone discussion',
author='Timo Stollenwerk - Plone Foundation',