diff --git a/CHANGES.rst b/CHANGES.rst index be4873e..74f80da 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,7 +1,6 @@ Changelog ========= -3.0.6 (unreleased) 3.0.7 (unreleased) ------------------ @@ -23,25 +22,19 @@ Bug fixes: This is a follow up to `issue 476 `_. [iham] +- Fix commenting and tests in python 3. + [pbauer] 3.0.6 (2018-06-18) ------------------ -Breaking changes: - -- *add item here* - -New features: - -- *add item here* - 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) diff --git a/plone/app/discussion/browser/comments.py b/plone/app/discussion/browser/comments.py index b0cfb39..68592d3 100644 --- a/plone/app/discussion/browser/comments.py +++ b/plone/app/discussion/browser/comments.py @@ -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,8 +33,6 @@ 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', @@ -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,16 +162,13 @@ 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') + 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' diff --git a/plone/app/discussion/catalog.py b/plone/app/discussion/catalog.py index 6bbc760..73c5ab2 100644 --- a/plone/app/discussion/catalog.py +++ b/plone/app/discussion/catalog.py @@ -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,7 +71,12 @@ 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) diff --git a/plone/app/discussion/comment.py b/plone/app/discussion/comment.py index 7b96007..788d54d 100644 --- a/plone/app/discussion/comment.py +++ b/plone/app/discussion/comment.py @@ -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, diff --git a/plone/app/discussion/conversation.py b/plone/app/discussion/conversation.py index e616a79..ae13301 100644 --- a/plone/app/discussion/conversation.py +++ b/plone/app/discussion/conversation.py @@ -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) diff --git a/plone/app/discussion/tests/functional_test_comments.txt b/plone/app/discussion/tests/functional_test_comments.txt index d78a7eb..36ad8a1 100644 --- a/plone/app/discussion/tests/functional_test_comments.txt +++ b/plone/app/discussion/tests/functional_test_comments.txt @@ -470,7 +470,7 @@ 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.IRichTextBehavior.text').value = "Lorem ipsum" >>> browser.getControl('Save').click() diff --git a/plone/app/discussion/tests/test_comment.py b/plone/app/discussion/tests/test_comment.py index 8ac4d93..c757db0 100644 --- a/plone/app/discussion/tests/test_comment.py +++ b/plone/app/discussion/tests/test_comment.py @@ -54,7 +54,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') @@ -171,9 +171,10 @@ class CommentTest(unittest.TestCase): def test_getText_with_non_ascii_characters(self): comment1 = createObject('plone.Comment') comment1.text = u'Umlaute sind ä, ö und ü.' + out = b'

Umlaute sind \xc3\xa4, \xc3\xb6 und \xc3\xbc.

' self.assertEqual( comment1.getText(), - '

Umlaute sind \xc3\xa4, \xc3\xb6 und \xc3\xbc.

', + out.decode('utf8') ) def test_getText_doesnt_link(self): diff --git a/plone/app/discussion/tests/test_comments_viewlet.py b/plone/app/discussion/tests/test_comments_viewlet.py index 5a34cb6..942f0d2 100644 --- a/plone/app/discussion/tests/test_comments_viewlet.py +++ b/plone/app/discussion/tests/test_comments_viewlet.py @@ -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)) diff --git a/plone/app/discussion/tests/test_contentrules.py b/plone/app/discussion/tests/test_contentrules.py index 6b9610e..c05aede 100644 --- a/plone/app/discussion/tests/test_contentrules.py +++ b/plone/app/discussion/tests/test_contentrules.py @@ -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, @@ -114,7 +114,7 @@ class ReplyContentRulesTest(unittest.TestCase): IStringSubstitution, name=u'comment_id', ) - self.assertIsInstance(reply_id(), long) + self.assertIsInstance(reply_id(), int) def testReplyTextStringSubstitution(self): reply_text = getAdapter( diff --git a/plone/app/discussion/tests/test_conversation.py b/plone/app/discussion/tests/test_conversation.py index 6f94894..fcf528c 100644 --- a/plone/app/discussion/tests/test_conversation.py +++ b/plone/app/discussion/tests/test_conversation.py @@ -68,7 +68,7 @@ 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__), @@ -641,7 +641,7 @@ class ConversationTest(unittest.TestCase): 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', diff --git a/setup.py b/setup.py index 29d217c..4544405 100644 --- a/setup.py +++ b/setup.py @@ -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',