diff --git a/docs/source/conf.py b/docs/source/conf.py
index d2a393e..71374d4 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -17,7 +17,7 @@ import os
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
-#sys.path.append(os.path.abspath('.'))
+# sys.path.append(os.path.abspath('.'))
# -- General configuration ----------------------------------------------------
@@ -95,7 +95,7 @@ pygments_style = 'sphinx'
#modindex_common_prefix = []
-# -- Options for HTML output ---------------------------------------------------
+# -- Options for HTML output ---------------------------------------------
# The theme to use for HTML and HTML Help pages. Major themes that come with
# Sphinx are currently 'default' and 'sphinxdoc'.
@@ -169,7 +169,7 @@ html_static_path = ['_static']
htmlhelp_basename = 'ploneappdiscussiondoc'
-# -- Options for LaTeX output --------------------------------------------------
+# -- Options for LaTeX output --------------------------------------------
# The paper size ('letter' or 'a4').
#latex_paper_size = 'letter'
@@ -180,8 +180,8 @@ htmlhelp_basename = 'ploneappdiscussiondoc'
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
- ('index', 'ploneappdiscussion.tex', u'plone.app.discussion Documentation',
- u'Timo Stollenwerk', 'manual'),
+ ('index', 'ploneappdiscussion.tex', u'plone.app.discussion Documentation',
+ u'Timo Stollenwerk', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
diff --git a/plone/app/discussion/browser/captcha.py b/plone/app/discussion/browser/captcha.py
index ffbda2f..090fce9 100644
--- a/plone/app/discussion/browser/captcha.py
+++ b/plone/app/discussion/browser/captcha.py
@@ -12,29 +12,30 @@ from z3c.form import interfaces
from z3c.form.field import Fields
from zope import interface
from zope.annotation import factory
-from zope.component import adapts
+from zope.component import adapter
from zope.component import queryUtility
from zope.interface import Interface
from zope.publisher.interfaces.browser import IDefaultBrowserLayer
+@adapter(Comment)
@interface.implementer(ICaptcha)
class Captcha(Persistent):
"""Captcha input field.
"""
- adapts(Comment)
- captcha = u""
+ captcha = u''
+
Captcha = factory(Captcha)
+# context, request, form
+@adapter(Interface, IDefaultBrowserLayer, CommentForm)
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
"plone.app.discussion-captcha" feature.
"""
- # context, request, form
- adapts(Interface, IDefaultBrowserLayer, CommentForm)
fields = Fields(ICaptcha)
@@ -65,5 +66,3 @@ class CaptchaExtender(extensible.FormExtender):
self.form.fields['captcha'].widgetFactory = NorobotsFieldWidget
else:
self.form.fields['captcha'].mode = interfaces.HIDDEN_MODE
-
-
diff --git a/plone/app/discussion/browser/comment.py b/plone/app/discussion/browser/comment.py
index 6b9b5d3..ab5f32d 100644
--- a/plone/app/discussion/browser/comment.py
+++ b/plone/app/discussion/browser/comment.py
@@ -1,4 +1,4 @@
-# -*- coding: utf-8 -*-
+# coding: utf-8
from AccessControl import getSecurityManager
from Acquisition import aq_inner
from Acquisition import aq_parent
@@ -112,6 +112,5 @@ class EditCommentForm(CommentForm):
type='info')
return self._redirect(target=self.context.absolute_url())
-EditComment = wrap_form(EditCommentForm)
-# EOF
+EditComment = wrap_form(EditCommentForm)
diff --git a/plone/app/discussion/browser/moderation.pt b/plone/app/discussion/browser/moderation.pt
index 24b2fe6..aca24bd 100644
--- a/plone/app/discussion/browser/moderation.pt
+++ b/plone/app/discussion/browser/moderation.pt
@@ -106,11 +106,11 @@
Name
-
Email
|
- |
25:
text += ' [...]'
return text
@@ -99,37 +101,43 @@ def in_response_to(object):
@indexer(IComment)
def effective(object):
# the catalog index needs Zope DateTime instead of Python datetime
- return DateTime(object.creation_date.year,
- object.creation_date.month,
- object.creation_date.day,
- object.creation_date.hour,
- object.creation_date.minute,
- object.creation_date.second,
- 'GMT')
+ return DateTime(
+ object.creation_date.year,
+ object.creation_date.month,
+ object.creation_date.day,
+ object.creation_date.hour,
+ object.creation_date.minute,
+ object.creation_date.second,
+ 'GMT',
+ )
@indexer(IComment)
def created(object):
# the catalog index needs Zope DateTime instead of Python datetime
- return DateTime(object.creation_date.year,
- object.creation_date.month,
- object.creation_date.day,
- object.creation_date.hour,
- object.creation_date.minute,
- object.creation_date.second,
- 'GMT')
+ return DateTime(
+ object.creation_date.year,
+ object.creation_date.month,
+ object.creation_date.day,
+ object.creation_date.hour,
+ object.creation_date.minute,
+ object.creation_date.second,
+ 'GMT',
+ )
@indexer(IComment)
def modified(object):
# the catalog index needs Zope DateTime instead of Python datetime
- return DateTime(object.modification_date.year,
- object.modification_date.month,
- object.modification_date.day,
- object.modification_date.hour,
- object.modification_date.minute,
- object.modification_date.second,
- 'GMT')
+ return DateTime(
+ object.modification_date.year,
+ object.modification_date.month,
+ object.modification_date.day,
+ object.modification_date.hour,
+ object.modification_date.minute,
+ object.modification_date.second,
+ 'GMT',
+ )
# Override the conversation indexers for comments
diff --git a/plone/app/discussion/comment.py b/plone/app/discussion/comment.py
index 92c1c50..5a7f689 100644
--- a/plone/app/discussion/comment.py
+++ b/plone/app/discussion/comment.py
@@ -42,7 +42,8 @@ import logging
COMMENT_TITLE = _(
u'comment_title',
- default=u'${author_name} on ${content}')
+ default=u'${author_name} on ${content}',
+ )
MAIL_NOTIFICATION_MESSAGE = _(
u'mail_notification_message',
@@ -50,7 +51,8 @@ MAIL_NOTIFICATION_MESSAGE = _(
u'has been posted here: ${link}\n\n'
u'---\n'
u'${text}\n'
- u'---\n')
+ u'---\n',
+ )
MAIL_NOTIFICATION_MESSAGE_MODERATOR = _(
u'mail_notification_message_moderator',
@@ -60,7 +62,8 @@ MAIL_NOTIFICATION_MESSAGE_MODERATOR = _(
u'${text}\n'
u'---\n\n'
u'Approve comment:\n${link_approve}\n\n'
- u'Delete comment:\n${link_delete}\n')
+ u'Delete comment:\n${link_delete}\n',
+ )
logger = logging.getLogger('plone.app.discussion')
diff --git a/plone/app/discussion/conversation.py b/plone/app/discussion/conversation.py
index 525a64d..989f31e 100644
--- a/plone/app/discussion/conversation.py
+++ b/plone/app/discussion/conversation.py
@@ -30,7 +30,6 @@ from Products.CMFPlone.interfaces import IHideFromBreadcrumbs
from zope.annotation.interfaces import IAnnotatable
from zope.annotation.interfaces import IAnnotations
from zope.component import adapter
-from zope.component import adapts
from zope.container.contained import ContainerModifiedEvent
from zope.event import notify
from zope.interface import implementer
@@ -325,13 +324,13 @@ else:
return conversationAdapterFactory(content)
+@adapter(Conversation) # relies on implementation details
@implementer(IReplies)
class ConversationReplies(object):
"""An IReplies adapter for conversations.
This makes it easy to work with top-level comments.
"""
- adapts(Conversation) # relies on implementation details
def __init__(self, context):
self.conversation = context
@@ -401,6 +400,7 @@ class ConversationReplies(object):
return self.conversation._children.get(self.comment_id, LLSet())
+@adapter(Comment)
@implementer(IReplies)
class CommentReplies(ConversationReplies):
"""An IReplies adapter for comments.
@@ -412,8 +412,6 @@ class CommentReplies(ConversationReplies):
# most likely, anyone writing a different type of Conversation will also
# have a different type of Comment
- adapts(Comment)
-
def __init__(self, context):
self.comment = context
self.conversation = aq_parent(self.comment)
diff --git a/plone/app/discussion/interfaces.py b/plone/app/discussion/interfaces.py
index 5799602..549eaf9 100644
--- a/plone/app/discussion/interfaces.py
+++ b/plone/app/discussion/interfaces.py
@@ -11,13 +11,15 @@ from zope.interface import Interface
from zope.interface import Invalid
from zope.interface.common.mapping import IIterableMapping
+
def isEmail(value):
portal = getUtility(ISiteRoot)
reg_tool = getToolByName(portal, 'portal_registration')
if not (value and reg_tool.isValidEmail(value)):
- raise Invalid(_("Invalid email address."))
+ raise Invalid(_('Invalid email address.'))
return True
+
class IConversation(IIterableMapping):
"""A conversation about a content object.
@@ -160,7 +162,10 @@ class IComment(Interface):
# for anonymous comments only, set to None for logged in comments
author_name = schema.TextLine(title=_(u'Name'), required=False)
- author_email = schema.TextLine(title=_(u'Email'), required=False, constraint=isEmail)
+ author_email = schema.TextLine(title=_(u'Email'),
+ required=False,
+ constraint=isEmail,
+ )
title = schema.TextLine(title=_(u'label_subject',
default=u'Subject'))
diff --git a/plone/app/discussion/testing.py b/plone/app/discussion/testing.py
index 1612edd..4280a6e 100644
--- a/plone/app/discussion/testing.py
+++ b/plone/app/discussion/testing.py
@@ -11,7 +11,6 @@ from plone.app.testing import TEST_USER_ID
from plone.registry.interfaces import IRegistry
from Products.CMFCore.utils import getToolByName
from zope.component import queryUtility
-from zope.configuration import xmlconfig
try:
@@ -40,9 +39,9 @@ class PloneAppDiscussion(PloneSandboxLayer):
def setUpZope(self, app, configurationContext):
# Load ZCML
import plone.app.discussion
- xmlconfig.file('configure.zcml',
- plone.app.discussion,
- context=configurationContext)
+ self.loadZCML(package=plone.app.discussion,
+ context=configurationContext,
+ )
def setUpPloneSite(self, portal):
# Install into Plone site using portal_setup
diff --git a/plone/app/discussion/tests/functional_test_comment_review_workflow.txt b/plone/app/discussion/tests/functional_test_comment_review_workflow.txt
index 7cc743c..cd654d9 100644
--- a/plone/app/discussion/tests/functional_test_comment_review_workflow.txt
+++ b/plone/app/discussion/tests/functional_test_comment_review_workflow.txt
@@ -182,7 +182,7 @@ flaw? Though, the comment is published properly.
>>> browser.handleErrors = False
>>> browser.raiseHttpErrors = True
-Make sure anonyous users see the approved comment, but not the unapproved ones.
+Make sure anonymous users see the approved comment, but not the unapproved ones.
>>> unprivileged_browser.open(urldoc)
>>> 'First anonymous comment' in unprivileged_browser.contents
@@ -230,3 +230,53 @@ Make sure the catalog has been updated properly.
>>> portal.portal_catalog.searchResults(id='doc', total_comments=0)
[>> browser.open(portal_url + '/logout')
+ >>> browser.open(portal_url + '/login_form')
+ >>> browser.getControl(name='__ac_name').value = 'admin'
+ >>> browser.getControl(name='__ac_password').value = 'secret'
+ >>> browser.getControl(name='submit').click()
+ >>> browser.open(portal_url+'/@@discussion-controlpanel')
+ >>> browser.getControl(name='form.widgets.anonymous_comments:list').value = 'selected'
+ >>> browser.getControl(name='form.widgets.anonymous_email_enabled:list').value = 'selected'
+ >>> browser.getControl(name='form.buttons.save').click()
+ >>> browser.open(portal_url + '/logout')
+
+Now we can post an anonymous comment.
+
+ >>> unprivileged_browser.open(urldoc)
+ >>> unprivileged_browser.getControl(name='form.widgets.text').value = "This is an anonymous comment"
+ >>> unprivileged_browser.getControl(name='form.widgets.author_name').value = u'John'
+ >>> unprivileged_browser.getControl(name='form.widgets.author_email').value = 'john@acme.com'
+ >>> unprivileged_browser.getControl(name='form.buttons.comment').click()
+
+
+Check that the form has been properly submitted.
+
+ >>> unprivileged_browser.url
+ 'http://nohost/plone/doc/document_view'
+
+ >>> 'Your comment awaits moderator approval.' in unprivileged_browser.contents
+ True
+
+Change to Moderation view.
+
+ >>> browser.open(urldoc)
+ >>> browser.getLink("Moderate comments").click()
+
+The new comment is shown in moderation view with authors name and email.
+
+ >>> browser.url
+ 'http://nohost/plone/@@moderate-comments'
+
+ >>> 'John' in browser.contents
+ True
+
+ >>> 'john@acme.com' in browser.contents
+ True
diff --git a/plone/app/discussion/tests/test_conversation.py b/plone/app/discussion/tests/test_conversation.py
index 95256d1..c2d30d0 100644
--- a/plone/app/discussion/tests/test_conversation.py
+++ b/plone/app/discussion/tests/test_conversation.py
@@ -175,8 +175,8 @@ class ConversationTest(unittest.TestCase):
del conversation[new_id_1]
self.assertEqual([
- {'comment': comment2, 'depth': 0, 'id': new_id_2},
- {'comment': comment2_1, 'depth': 1, 'id': new_id_2_1},
+ {'comment': comment2, 'depth': 0, 'id': new_id_2},
+ {'comment': comment2_1, 'depth': 1, 'id': new_id_2_1},
], list(conversation.getThreads()))
def test_delete_comment_when_content_object_is_deleted(self):
@@ -608,12 +608,12 @@ class ConversationTest(unittest.TestCase):
# Get threads
self.assertEqual([
- {'comment': comment1, 'depth': 0, 'id': new_id_1},
- {'comment': comment1_1, 'depth': 1, 'id': new_id_1_1},
+ {'comment': comment1, 'depth': 0, 'id': new_id_1},
+ {'comment': comment1_1, 'depth': 1, 'id': new_id_1_1},
{'comment': comment1_1_1, 'depth': 2, 'id': new_id_1_1_1},
- {'comment': comment1_2, 'depth': 1, 'id': new_id_1_2},
- {'comment': comment2, 'depth': 0, 'id': new_id_2},
- {'comment': comment2_1, 'depth': 1, 'id': new_id_2_1},
+ {'comment': comment1_2, 'depth': 1, 'id': new_id_1_2},
+ {'comment': comment2, 'depth': 0, 'id': new_id_2},
+ {'comment': comment2_1, 'depth': 1, 'id': new_id_2_1},
], list(conversation.getThreads()))
def test_get_threads_batched(self):
diff --git a/plone/app/discussion/tests/test_events.py b/plone/app/discussion/tests/test_events.py
index 892ee55..7ca7976 100644
--- a/plone/app/discussion/tests/test_events.py
+++ b/plone/app/discussion/tests/test_events.py
@@ -4,8 +4,8 @@ from plone.app.discussion.interfaces import IReplies
from plone.app.discussion.testing import PLONE_APP_DISCUSSION_INTEGRATION_TESTING # noqa
from plone.app.testing import setRoles
from plone.app.testing import TEST_USER_ID
-from zope.component import createObject
from Zope2.App import zcml
+from zope.component import createObject
import Products.Five
import unittest
diff --git a/plone/app/discussion/upgrades.py b/plone/app/discussion/upgrades.py
index 8d4083e..d9114f4 100644
--- a/plone/app/discussion/upgrades.py
+++ b/plone/app/discussion/upgrades.py
@@ -60,4 +60,4 @@ def upgrade_comment_workflows(context):
wf.updateRoleMappingsFor(comment)
comment.reindexObjectSecurity()
except (AttributeError, KeyError):
- logger.info('Could not reindex comment %s' % brain.getURL())
+ logger.info('Could not reindex comment {0}'.format(brain.getURL()))
diff --git a/setup.py b/setup.py
index 85f100b..7c8755b 100644
--- a/setup.py
+++ b/setup.py
@@ -1,6 +1,9 @@
+# encoding: utf-8
+
from setuptools import find_packages
from setuptools import setup
+
version = '3.0.3.dev0'
install_requires = [
@@ -26,15 +29,15 @@ install_requires = [
setup(name='plone.app.discussion',
version=version,
- description="Enhanced discussion support for Plone",
- long_description=open("README.rst").read() + "\n" +
- open("CHANGES.rst").read(),
+ description='Enhanced discussion support for Plone',
+ long_description=open('README.rst').read() + '\n' +
+ open('CHANGES.rst').read(),
classifiers=[
- "Framework :: Plone",
- "Framework :: Plone :: 5.0",
- "Framework :: Plone :: 5.1",
- "Programming Language :: Python",
- "Programming Language :: Python :: 2.7",
+ 'Framework :: Plone',
+ 'Framework :: Plone :: 5.0',
+ 'Framework :: Plone :: 5.1',
+ 'Programming Language :: Python',
+ 'Programming Language :: Python :: 2.7',
],
keywords='plone discussion',
author='Timo Stollenwerk - Plone Foundation',
@@ -54,11 +57,10 @@ setup(name='plone.app.discussion',
'plone.app.contentrules',
'plone.app.contenttypes[test]',
'plone.app.robotframework',
- ]
+ ],
},
entry_points="""
[z3c.autoinclude.plugin]
target = plone
""",
)
-
|