commit
3e2446c6b0
13
CHANGES.rst
13
CHANGES.rst
@ -1,7 +1,6 @@
|
|||||||
Changelog
|
Changelog
|
||||||
=========
|
=========
|
||||||
|
|
||||||
3.0.6 (unreleased)
|
|
||||||
3.0.7 (unreleased)
|
3.0.7 (unreleased)
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
@ -23,25 +22,19 @@ Bug fixes:
|
|||||||
This is a follow up to `issue 476 <https://github.com/plone/plone.app.contenttypes/issues/476>`_.
|
This is a follow up to `issue 476 <https://github.com/plone/plone.app.contenttypes/issues/476>`_.
|
||||||
[iham]
|
[iham]
|
||||||
|
|
||||||
|
- Fix commenting and tests in python 3.
|
||||||
|
[pbauer]
|
||||||
|
|
||||||
3.0.6 (2018-06-18)
|
3.0.6 (2018-06-18)
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
Breaking changes:
|
|
||||||
|
|
||||||
- *add item here*
|
|
||||||
|
|
||||||
New features:
|
|
||||||
|
|
||||||
- *add item here*
|
|
||||||
|
|
||||||
Bug fixes:
|
Bug fixes:
|
||||||
|
|
||||||
- Fix tests to work with merges plone.login.
|
- Fix tests to work with merges plone.login.
|
||||||
[jensens]
|
[jensens]
|
||||||
|
|
||||||
- More Python 2 / 3 compatibility.
|
- More Python 2 / 3 compatibility.
|
||||||
[pbauer]
|
[pbauer, hvelarde]
|
||||||
|
|
||||||
|
|
||||||
3.0.5 (2018-02-04)
|
3.0.5 (2018-02-04)
|
||||||
|
@ -17,6 +17,7 @@ from plone.z3cform import z2
|
|||||||
from plone.z3cform.fieldsets import extensible
|
from plone.z3cform.fieldsets import extensible
|
||||||
from plone.z3cform.interfaces import IWrappedForm
|
from plone.z3cform.interfaces import IWrappedForm
|
||||||
from Products.CMFCore.utils import getToolByName
|
from Products.CMFCore.utils import getToolByName
|
||||||
|
from Products.CMFPlone.utils import safe_unicode
|
||||||
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
|
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
|
||||||
from Products.statusmessages.interfaces import IStatusMessage
|
from Products.statusmessages.interfaces import IStatusMessage
|
||||||
from six.moves.urllib.parse import quote
|
from six.moves.urllib.parse import quote
|
||||||
@ -32,8 +33,6 @@ from zope.i18n import translate
|
|||||||
from zope.i18nmessageid import Message
|
from zope.i18nmessageid import Message
|
||||||
from zope.interface import alsoProvides
|
from zope.interface import alsoProvides
|
||||||
|
|
||||||
import six
|
|
||||||
|
|
||||||
|
|
||||||
COMMENT_DESCRIPTION_PLAIN_TEXT = _(
|
COMMENT_DESCRIPTION_PLAIN_TEXT = _(
|
||||||
u'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
|
# Make sure author_name/ author_email is properly encoded
|
||||||
if 'author_name' in data:
|
if 'author_name' in data:
|
||||||
author_name = data['author_name']
|
author_name = safe_unicode(data['author_name'])
|
||||||
if isinstance(author_name, str):
|
|
||||||
author_name = six.text_type(author_name, 'utf-8')
|
|
||||||
if 'author_email' in data:
|
if 'author_email' in data:
|
||||||
author_email = data['author_email']
|
author_email = safe_unicode(data['author_email'])
|
||||||
if isinstance(author_email, str):
|
|
||||||
author_email = six.text_type(author_email, 'utf-8')
|
|
||||||
|
|
||||||
# Set comment author properties for anonymous users or members
|
# Set comment author properties for anonymous users or members
|
||||||
portal_membership = getToolByName(context, 'portal_membership')
|
portal_membership = getToolByName(context, 'portal_membership')
|
||||||
@ -167,16 +162,13 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
|
|||||||
'Reply to item', context):
|
'Reply to item', context):
|
||||||
# Member
|
# Member
|
||||||
member = portal_membership.getAuthenticatedMember()
|
member = portal_membership.getAuthenticatedMember()
|
||||||
# memberdata is stored as utf-8 encoded strings
|
email = safe_unicode(member.getProperty('email'))
|
||||||
email = member.getProperty('email')
|
|
||||||
fullname = member.getProperty('fullname')
|
fullname = member.getProperty('fullname')
|
||||||
if not fullname or fullname == '':
|
if not fullname or fullname == '':
|
||||||
fullname = member.getUserName()
|
fullname = member.getUserName()
|
||||||
elif isinstance(fullname, str):
|
fullname = safe_unicode(fullname)
|
||||||
fullname = six.text_type(fullname, 'utf-8')
|
|
||||||
author_name = fullname
|
author_name = fullname
|
||||||
if email and isinstance(email, str):
|
email = safe_unicode(email)
|
||||||
email = six.text_type(email, 'utf-8')
|
|
||||||
# XXX: according to IComment interface author_email must not be # noqa T000
|
# XXX: according to IComment interface author_email must not be # noqa T000
|
||||||
# set for logged in users, cite:
|
# set for logged in users, cite:
|
||||||
# 'for anonymous comments only, set to None for logged in comments'
|
# 'for anonymous comments only, set to None for logged in comments'
|
||||||
|
@ -13,6 +13,7 @@ from Products.CMFCore.interfaces import IContentish
|
|||||||
from Products.CMFPlone.utils import safe_unicode
|
from Products.CMFPlone.utils import safe_unicode
|
||||||
from Products.ZCatalog.interfaces import IZCatalog
|
from Products.ZCatalog.interfaces import IZCatalog
|
||||||
|
|
||||||
|
import six
|
||||||
|
|
||||||
MAX_DESCRIPTION = 25
|
MAX_DESCRIPTION = 25
|
||||||
|
|
||||||
@ -70,7 +71,12 @@ def title(object):
|
|||||||
|
|
||||||
@indexer(IComment)
|
@indexer(IComment)
|
||||||
def creator(object):
|
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)
|
@indexer(IComment)
|
||||||
|
@ -148,7 +148,7 @@ class Comment(CatalogAware, WorkflowAware, DynamicType, Traversable,
|
|||||||
text = self.text
|
text = self.text
|
||||||
if text is None:
|
if text is None:
|
||||||
return ''
|
return ''
|
||||||
if isinstance(text, six.text_type):
|
if six.PY2 and isinstance(text, six.text_type):
|
||||||
text = text.encode('utf8')
|
text = text.encode('utf8')
|
||||||
transform = transforms.convertTo(
|
transform = transforms.convertTo(
|
||||||
targetMimetype,
|
targetMimetype,
|
||||||
|
@ -161,7 +161,7 @@ class Conversation(Traversable, Persistent, Explicit):
|
|||||||
|
|
||||||
comment = aq_base(comment)
|
comment = aq_base(comment)
|
||||||
|
|
||||||
id = long(time.time() * 1e6)
|
id = int(time.time() * 1e6)
|
||||||
while id in self._comments:
|
while id in self._comments:
|
||||||
id += 1
|
id += 1
|
||||||
|
|
||||||
@ -206,22 +206,22 @@ class Conversation(Traversable, Persistent, Explicit):
|
|||||||
return len(self._comments)
|
return len(self._comments)
|
||||||
|
|
||||||
def __contains__(self, key):
|
def __contains__(self, key):
|
||||||
return long(key) in self._comments
|
return int(key) in self._comments
|
||||||
|
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key):
|
||||||
"""Get an item by its long key
|
"""Get an item by its int key
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
comment_id = long(key)
|
comment_id = int(key)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return
|
return
|
||||||
return self._comments[comment_id].__of__(self)
|
return self._comments[comment_id].__of__(self)
|
||||||
|
|
||||||
def __delitem__(self, key, suppress_container_modified=False):
|
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)
|
comment = self[key].__of__(self)
|
||||||
commentator = comment.author_username
|
commentator = comment.author_username
|
||||||
@ -260,7 +260,7 @@ class Conversation(Traversable, Persistent, Explicit):
|
|||||||
return iter(self._comments)
|
return iter(self._comments)
|
||||||
|
|
||||||
def get(self, key, default=None):
|
def get(self, key, default=None):
|
||||||
comment = self._comments.get(long(key), default)
|
comment = self._comments.get(int(key), default)
|
||||||
if comment is default:
|
if comment is default:
|
||||||
return default
|
return default
|
||||||
return comment.__of__(self)
|
return comment.__of__(self)
|
||||||
@ -347,20 +347,20 @@ class ConversationReplies(object):
|
|||||||
return len(self.children)
|
return len(self.children)
|
||||||
|
|
||||||
def __contains__(self, key):
|
def __contains__(self, key):
|
||||||
return long(key) in self.children
|
return int(key) in self.children
|
||||||
|
|
||||||
def __getitem__(self, key):
|
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:
|
if key not in self.children:
|
||||||
raise KeyError(key)
|
raise KeyError(key)
|
||||||
return self.conversation[key]
|
return self.conversation[key]
|
||||||
|
|
||||||
def __delitem__(self, 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:
|
if key not in self.children:
|
||||||
raise KeyError(key)
|
raise KeyError(key)
|
||||||
del self.conversation[key]
|
del self.conversation[key]
|
||||||
@ -369,7 +369,7 @@ class ConversationReplies(object):
|
|||||||
return iter(self.children)
|
return iter(self.children)
|
||||||
|
|
||||||
def get(self, key, default=None):
|
def get(self, key, default=None):
|
||||||
key = long(key)
|
key = int(key)
|
||||||
if key not in self.children:
|
if key not in self.children:
|
||||||
return default
|
return default
|
||||||
return self.conversation.get(key)
|
return self.conversation.get(key)
|
||||||
|
@ -470,7 +470,7 @@ Edit the content object.
|
|||||||
>>> from hashlib import sha1 as sha
|
>>> from hashlib import sha1 as sha
|
||||||
>>> ring = _getKeyring('foo')
|
>>> ring = _getKeyring('foo')
|
||||||
>>> secret = ring.random()
|
>>> 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.open("http://nohost/plone/doc1/edit?_authenticator=" + token)
|
||||||
>>> browser.getControl(name='form.widgets.IRichTextBehavior.text').value = "Lorem ipsum"
|
>>> browser.getControl(name='form.widgets.IRichTextBehavior.text').value = "Lorem ipsum"
|
||||||
>>> browser.getControl('Save').click()
|
>>> browser.getControl('Save').click()
|
||||||
|
@ -54,7 +54,7 @@ class CommentTest(unittest.TestCase):
|
|||||||
difference = difference.seconds
|
difference = difference.seconds
|
||||||
# We hope that between comment1 and local_utc happen less than
|
# We hope that between comment1 and local_utc happen less than
|
||||||
# 10 seconds
|
# 10 seconds
|
||||||
self.assertFalse(difference / 10)
|
self.assertFalse(difference // 10)
|
||||||
|
|
||||||
def test_id(self):
|
def test_id(self):
|
||||||
comment1 = createObject('plone.Comment')
|
comment1 = createObject('plone.Comment')
|
||||||
@ -171,9 +171,10 @@ class CommentTest(unittest.TestCase):
|
|||||||
def test_getText_with_non_ascii_characters(self):
|
def test_getText_with_non_ascii_characters(self):
|
||||||
comment1 = createObject('plone.Comment')
|
comment1 = createObject('plone.Comment')
|
||||||
comment1.text = u'Umlaute sind ä, ö und ü.'
|
comment1.text = u'Umlaute sind ä, ö und ü.'
|
||||||
|
out = b'<p>Umlaute sind \xc3\xa4, \xc3\xb6 und \xc3\xbc.</p>'
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
comment1.getText(),
|
comment1.getText(),
|
||||||
'<p>Umlaute sind \xc3\xa4, \xc3\xb6 und \xc3\xbc.</p>',
|
out.decode('utf8')
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_getText_doesnt_link(self):
|
def test_getText_doesnt_link(self):
|
||||||
|
@ -567,7 +567,8 @@ class TestCommentsViewlet(unittest.TestCase):
|
|||||||
replies = self.viewlet.get_replies()
|
replies = self.viewlet.get_replies()
|
||||||
next(replies)
|
next(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):
|
def test_get_replies_on_non_annotatable_object(self):
|
||||||
context = self.portal.MailHost # the mail host is not annotatable
|
context = self.portal.MailHost # the mail host is not annotatable
|
||||||
@ -575,7 +576,8 @@ class TestCommentsViewlet(unittest.TestCase):
|
|||||||
replies = viewlet.get_replies()
|
replies = viewlet.get_replies()
|
||||||
self.assertEqual(len(tuple(replies)), 0)
|
self.assertEqual(len(tuple(replies)), 0)
|
||||||
replies = viewlet.get_replies()
|
replies = viewlet.get_replies()
|
||||||
self.assertRaises(StopIteration, replies.next)
|
with self.assertRaises(StopIteration):
|
||||||
|
next(replies)
|
||||||
|
|
||||||
def test_get_replies_with_workflow_actions(self):
|
def test_get_replies_with_workflow_actions(self):
|
||||||
self.assertFalse(self.viewlet.get_replies(workflow_actions=True))
|
self.assertFalse(self.viewlet.get_replies(workflow_actions=True))
|
||||||
|
@ -54,7 +54,7 @@ class CommentContentRulesTest(unittest.TestCase):
|
|||||||
def testCommentIdStringSubstitution(self):
|
def testCommentIdStringSubstitution(self):
|
||||||
comment_id = getAdapter(self.document, IStringSubstitution,
|
comment_id = getAdapter(self.document, IStringSubstitution,
|
||||||
name=u'comment_id')
|
name=u'comment_id')
|
||||||
self.assertIsInstance(comment_id(), long)
|
self.assertIsInstance(comment_id(), int)
|
||||||
|
|
||||||
def testCommentTextStringSubstitution(self):
|
def testCommentTextStringSubstitution(self):
|
||||||
comment_text = getAdapter(self.document, IStringSubstitution,
|
comment_text = getAdapter(self.document, IStringSubstitution,
|
||||||
@ -114,7 +114,7 @@ class ReplyContentRulesTest(unittest.TestCase):
|
|||||||
IStringSubstitution,
|
IStringSubstitution,
|
||||||
name=u'comment_id',
|
name=u'comment_id',
|
||||||
)
|
)
|
||||||
self.assertIsInstance(reply_id(), long)
|
self.assertIsInstance(reply_id(), int)
|
||||||
|
|
||||||
def testReplyTextStringSubstitution(self):
|
def testReplyTextStringSubstitution(self):
|
||||||
reply_text = getAdapter(
|
reply_text = getAdapter(
|
||||||
|
@ -68,7 +68,7 @@ class ConversationTest(unittest.TestCase):
|
|||||||
new_id = conversation.addComment(comment)
|
new_id = conversation.addComment(comment)
|
||||||
|
|
||||||
# Check that the conversation methods return the correct data
|
# 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.assertTrue(IComment.providedBy(conversation[new_id]))
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
aq_base(conversation[new_id].__parent__),
|
aq_base(conversation[new_id].__parent__),
|
||||||
@ -641,7 +641,7 @@ class ConversationTest(unittest.TestCase):
|
|||||||
|
|
||||||
def test_unconvertible_id(self):
|
def test_unconvertible_id(self):
|
||||||
# make sure the conversation view doesn't break when given comment id
|
# 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 = self.portal.doc1.restrictedTraverse(
|
||||||
'++conversation++default/ThisCantBeRight',
|
'++conversation++default/ThisCantBeRight',
|
||||||
|
3
setup.py
3
setup.py
@ -37,9 +37,12 @@ setup(name='plone.app.discussion',
|
|||||||
'Framework :: Plone',
|
'Framework :: Plone',
|
||||||
'Framework :: Plone :: 5.0',
|
'Framework :: Plone :: 5.0',
|
||||||
'Framework :: Plone :: 5.1',
|
'Framework :: Plone :: 5.1',
|
||||||
|
'Framework :: Plone :: 5.2',
|
||||||
'License :: OSI Approved :: GNU General Public License (GPL)',
|
'License :: OSI Approved :: GNU General Public License (GPL)',
|
||||||
'Programming Language :: Python',
|
'Programming Language :: Python',
|
||||||
'Programming Language :: Python :: 2.7',
|
'Programming Language :: Python :: 2.7',
|
||||||
|
'Programming Language :: Python :: 3.6',
|
||||||
|
'Programming Language :: Python :: 3.7',
|
||||||
],
|
],
|
||||||
keywords='plone discussion',
|
keywords='plone discussion',
|
||||||
author='Timo Stollenwerk - Plone Foundation',
|
author='Timo Stollenwerk - Plone Foundation',
|
||||||
|
Loading…
Reference in New Issue
Block a user