find -name "*.py" -exec pyupgrade --py3-only --py37-plus {} +

This commit is contained in:
Jens W. Klein 2022-05-01 23:14:41 +02:00
parent 34b758f2bd
commit 75c6a5dcc1
38 changed files with 315 additions and 361 deletions

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
#
# plone.app.discussion documentation build configuration file, created by
# sphinx-quickstart on Thu Mar 18 10:17:15 2010.
@ -47,8 +46,8 @@ source_suffix = ".txt"
master_doc = "index"
# General information about the project.
project = u"plone.app.discussion"
copyright = u"2010, Timo Stollenwerk - Plone Foundation"
project = "plone.app.discussion"
copyright = "2010, Timo Stollenwerk - Plone Foundation"
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
@ -185,8 +184,8 @@ latex_documents = [
(
"index",
"ploneappdiscussion.tex",
u"plone.app.discussion Documentation",
u"Timo Stollenwerk",
"plone.app.discussion Documentation",
"Timo Stollenwerk",
"manual",
),
]

View File

@ -1,2 +1 @@
# -*- coding: utf-8 -*-
__import__("pkg_resources").declare_namespace(__name__)

View File

@ -1,2 +1 @@
# -*- coding: utf-8 -*-
__import__("pkg_resources").declare_namespace(__name__)

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from zope.i18nmessageid import MessageFactory

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Captcha validator, see captcha.txt for design notes.
from persistent import Persistent
from plone.app.discussion.browser.comments import CommentForm
@ -23,7 +22,7 @@ from zope.publisher.interfaces.browser import IDefaultBrowserLayer
class Captcha(Persistent):
"""Captcha input field."""
captcha = u""
captcha = ""
Captcha = factory(Captcha)

View File

@ -1,4 +1,3 @@
# coding: utf-8
from .comments import CommentForm
from AccessControl import getSecurityManager
from Acquisition import aq_inner
@ -48,9 +47,9 @@ class View(BrowserView):
will redirect right to the binary object, bypassing comments.
"""
if obj.portal_type in view_action_types:
url = "{0}/view".format(url)
url = f"{url}/view"
self.request.response.redirect("{0}#{1}".format(url, context.id))
self.request.response.redirect(f"{url}#{context.id}")
class EditCommentForm(CommentForm):
@ -58,10 +57,10 @@ class EditCommentForm(CommentForm):
ignoreContext = True
id = "edit-comment-form"
label = _(u"edit_comment_form_title", default=u"Edit comment")
label = _("edit_comment_form_title", default="Edit comment")
def updateWidgets(self):
super(EditCommentForm, self).updateWidgets()
super().updateWidgets()
self.widgets["text"].value = self.context.text
# We have to rename the id, otherwise TinyMCE can't initialize
# because there are two textareas with the same id.
@ -70,12 +69,12 @@ class EditCommentForm(CommentForm):
def _redirect(self, target=""):
if not target:
portal_state = getMultiAdapter(
(self.context, self.request), name=u"plone_portal_state"
(self.context, self.request), name="plone_portal_state"
)
target = portal_state.portal_url()
self.request.response.redirect(target)
@button.buttonAndHandler(_(u"label_save", default=u"Save"), name="comment")
@button.buttonAndHandler(_("label_save", default="Save"), name="comment")
def handleComment(self, action):
# Validate form
@ -96,14 +95,14 @@ class EditCommentForm(CommentForm):
# Redirect to comment
IStatusMessage(self.request).add(
_(u"comment_edit_notification", default="Comment was edited"), type="info"
_("comment_edit_notification", default="Comment was edited"), type="info"
)
return self._redirect(target=self.action.replace("@@edit-comment", "@@view"))
@button.buttonAndHandler(_(u"cancel_form_button", default=u"Cancel"), name="cancel")
@button.buttonAndHandler(_("cancel_form_button", default="Cancel"), name="cancel")
def handle_cancel(self, action):
IStatusMessage(self.request).add(
_(u"comment_edit_cancel_notification", default=u"Edit comment cancelled"),
_("comment_edit_cancel_notification", default="Edit comment cancelled"),
type="info",
)
return self._redirect(target=self.context.absolute_url())

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from AccessControl import getSecurityManager
from AccessControl import Unauthorized
from Acquisition import aq_inner
@ -35,28 +34,28 @@ from zope.interface import alsoProvides
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.",
"comment_description_plain_text",
default="You can add a comment by filling out the form below. "
"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.",
"comment_description_markdown",
default="You can add a comment by filling out the form below. "
"Plain text formatting. You can use the Markdown syntax for "
"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.",
"comment_description_intelligent_text",
default="You can add a comment by filling out the form below. "
"Plain text formatting. Web and email addresses are "
"transformed into clickable links.",
)
COMMENT_DESCRIPTION_MODERATION_ENABLED = _(
u"comment_description_moderation_enabled",
default=u"Comments are moderated.",
"comment_description_moderation_enabled",
default="Comments are moderated.",
)
@ -64,7 +63,7 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
ignoreContext = True # don't use context to get widget data
id = None
label = _(u"Add a comment")
label = _("Add a comment")
fields = field.Fields(IComment).omit(
"portal_type",
"__parent__",
@ -79,16 +78,16 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
)
def updateFields(self):
super(CommentForm, self).updateFields()
super().updateFields()
self.fields["user_notification"].widgetFactory = SingleCheckBoxFieldWidget
def updateWidgets(self):
super(CommentForm, self).updateWidgets()
super().updateWidgets()
# Widgets
self.widgets["in_reply_to"].mode = interfaces.HIDDEN_MODE
self.widgets["text"].addClass("autoresize")
self.widgets["user_notification"].label = _(u"")
self.widgets["user_notification"].label = _("")
# Reset widget field settings to their defaults, which may be changed
# further on. Otherwise, the email field might get set to required
# when an anonymous user visits, and then remain required when an
@ -140,7 +139,7 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
self.widgets["user_notification"].mode = interfaces.HIDDEN_MODE
def updateActions(self):
super(CommentForm, self).updateActions()
super().updateActions()
self.actions["cancel"].addClass("btn btn-secondary")
self.actions["cancel"].addClass("hide")
self.actions["comment"].addClass("btn btn-primary")
@ -148,7 +147,7 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
def get_author(self, data):
context = aq_inner(self.context)
# some attributes are not always set
author_name = u""
author_name = ""
# Make sure author_name/ author_email is properly encoded
if "author_name" in data:
@ -219,16 +218,14 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
else: # pragma: no cover
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.",
"Anonymous user tries to post a comment, but anonymous "
"commenting is disabled. Or user does not have the "
"'reply to item' permission.",
)
return comment
@button.buttonAndHandler(
_(u"add_comment_button", default=u"Comment"), name="comment"
)
@button.buttonAndHandler(_("add_comment_button", default="Comment"), name="comment")
def handleComment(self, action):
context = aq_inner(self.context)
@ -254,7 +251,7 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
anon = portal_membership.isAnonymousUser()
if captcha_enabled and anonymous_comments and anon:
if "captcha" not in data:
data["captcha"] = u""
data["captcha"] = ""
captcha = CaptchaValidator(
self.context, self.request, None, ICaptcha["captcha"], None
)
@ -296,7 +293,7 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
# Redirect to comment (inside a content object page)
self.request.response.redirect(self.action + "#" + str(comment_id))
@button.buttonAndHandler(_(u"Cancel"))
@button.buttonAndHandler(_("Cancel"))
def handleCancel(self, action):
# This method should never be called, it's only there to show
# a cancel button that is handled by a jQuery method.
@ -309,7 +306,7 @@ class CommentsViewlet(ViewletBase):
index = ViewPageTemplateFile("comments.pt")
def update(self):
super(CommentsViewlet, self).update()
super().update()
discussion_allowed = self.is_discussion_allowed()
anonymous_allowed_or_can_reply = (
self.is_anonymous()
@ -483,7 +480,7 @@ class CommentsViewlet(ViewletBase):
if username is None:
return None
else:
return "{0}/author/{1}".format(self.context.portal_url(), username)
return f"{self.context.portal_url()}/author/{username}"
def get_commenter_portrait(self, username=None):
@ -523,7 +520,7 @@ class CommentsViewlet(ViewletBase):
return portal_membership.isAnonymousUser()
def login_action(self):
return "{0}/login_form?came_from={1}".format(
return "{}/login_form?came_from={}".format(
self.navigation_root_url,
quote(self.request.get("URL", "")),
)

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from plone.app.discussion.interfaces import _
from plone.app.discussion.interfaces import IDiscussionSettings
from plone.app.discussion.upgrades import update_registry
@ -32,22 +31,22 @@ class DiscussionSettingsEditForm(controlpanel.RegistryEditForm):
schema = IDiscussionSettings
id = "DiscussionSettingsEditForm"
label = _(u"Discussion settings")
label = _("Discussion settings")
description = _(
u"help_discussion_settings_editform",
default=u"Some discussion related settings are not "
u"located in the Discussion Control Panel.\n"
u"To enable comments for a specific content type, "
u"go to the Types Control Panel of this type and "
u'choose "Allow comments".\n'
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".',
"help_discussion_settings_editform",
default="Some discussion related settings are not "
"located in the Discussion Control Panel.\n"
"To enable comments for a specific content type, "
"go to the Types Control Panel of this type and "
'choose "Allow comments".\n'
"To enable the moderation workflow for comments, "
"go to the Types Control Panel, choose "
'"Comment" and set workflow to '
'"Comment Review Workflow".',
)
def updateFields(self):
super(DiscussionSettingsEditForm, self).updateFields()
super().updateFields()
self.fields["globally_enabled"].widgetFactory = SingleCheckBoxFieldWidget
self.fields["moderation_enabled"].widgetFactory = SingleCheckBoxFieldWidget
self.fields["edit_comment_enabled"].widgetFactory = SingleCheckBoxFieldWidget
@ -65,20 +64,20 @@ class DiscussionSettingsEditForm(controlpanel.RegistryEditForm):
def updateWidgets(self):
try:
super(DiscussionSettingsEditForm, self).updateWidgets()
super().updateWidgets()
except KeyError:
# upgrade profile not visible in prefs_install_products_form
# provide auto-upgrade
update_registry(self.context)
super(DiscussionSettingsEditForm, self).updateWidgets()
self.widgets["globally_enabled"].label = _(u"Enable Comments")
self.widgets["anonymous_comments"].label = _(u"Anonymous Comments")
self.widgets["show_commenter_image"].label = _(u"Commenter Image")
super().updateWidgets()
self.widgets["globally_enabled"].label = _("Enable Comments")
self.widgets["anonymous_comments"].label = _("Anonymous Comments")
self.widgets["show_commenter_image"].label = _("Commenter Image")
self.widgets["moderator_notification_enabled"].label = _(
u"Moderator Email Notification",
"Moderator Email Notification",
)
self.widgets["user_notification_enabled"].label = _(
u"User Email Notification",
"User Email Notification",
)
@button.buttonAndHandler(_("Save"), name=None)
@ -88,14 +87,14 @@ class DiscussionSettingsEditForm(controlpanel.RegistryEditForm):
self.status = self.formErrorsMessage
return
self.applyChanges(data)
IStatusMessage(self.request).addStatusMessage(_(u"Changes saved"), "info")
IStatusMessage(self.request).addStatusMessage(_("Changes saved"), "info")
self.context.REQUEST.RESPONSE.redirect("@@discussion-controlpanel")
@button.buttonAndHandler(_("Cancel"), name="cancel")
def handleCancel(self, action):
IStatusMessage(self.request).addStatusMessage(_(u"Edit cancelled"), "info")
IStatusMessage(self.request).addStatusMessage(_("Edit cancelled"), "info")
self.request.response.redirect(
"{0}/{1}".format(
"{}/{}".format(
self.context.absolute_url(),
self.control_panel_view,
),
@ -111,7 +110,7 @@ class DiscussionSettingsControlPanel(controlpanel.ControlPanelFormWrapper):
def __call__(self):
self.mailhost_warning()
self.custom_comment_workflow_warning()
return super(DiscussionSettingsControlPanel, self).__call__()
return super().__call__()
@property
def site_url(self):
@ -180,8 +179,8 @@ class DiscussionSettingsControlPanel(controlpanel.ControlPanelFormWrapper):
pass
else:
message = _(
u"discussion_text_no_mailhost_configured",
default=u"You have not configured a mail host or a site 'From' address, various features including contact forms, email notification and password reset will not work. Go to the E-Mail Settings to fix this.",
"discussion_text_no_mailhost_configured",
default="You have not configured a mail host or a site 'From' address, various features including contact forms, email notification and password reset will not work. Go to the E-Mail Settings to fix this.",
) # noqa: E501
IStatusMessage(self.request).addStatusMessage(message, "warning")
@ -195,8 +194,8 @@ class DiscussionSettingsControlPanel(controlpanel.ControlPanelFormWrapper):
pass
else:
message = _(
u"discussion_text_custom_comment_workflow",
default=u"You have configured a custom workflow for the 'Discussion Item' content type. You can enable/disable the comment moderation in this control panel only if you use one of the default 'Discussion Item' workflows. Go to the Types control panel to choose a workflow for the 'Discussion Item' type.",
"discussion_text_custom_comment_workflow",
default="You have configured a custom workflow for the 'Discussion Item' content type. You can enable/disable the comment moderation in this control panel only if you use one of the default 'Discussion Item' workflows. Go to the Types control panel to choose a workflow for the 'Discussion Item' type.",
) # noqa: E501
IStatusMessage(self.request).addStatusMessage(message, "warning")

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from Acquisition import aq_base
from Acquisition import aq_chain
from Acquisition import aq_inner
@ -34,7 +33,7 @@ def traverse_parents(context):
return None
class ConversationView(object):
class ConversationView:
def enabled(self):
if DEXTERITY_INSTALLED and IDexterityContent.providedBy(self.context):
return self._enabled_for_dexterity_types()

View File

@ -1,4 +1,3 @@
# coding: utf-8
from AccessControl import getSecurityManager
from AccessControl import Unauthorized
from Acquisition import aq_inner
@ -51,7 +50,7 @@ class View(BrowserView):
pass
def __init__(self, context, request):
super(View, self).__init__(context, request)
super().__init__(context, request)
self.workflowTool = getToolByName(self.context, "portal_workflow")
self.transitions = []
@ -229,7 +228,7 @@ class DeleteOwnComment(DeleteComment):
def __call__(self):
if self.can_delete():
super(DeleteOwnComment, self).__call__()
super().__call__()
else:
raise Unauthorized("You're not allowed to delete this comment.")
@ -318,7 +317,7 @@ class BulkActionsView(BrowserView):
"""
def __init__(self, context, request):
super(BulkActionsView, self).__init__(context, request)
super().__init__(context, request)
self.workflowTool = getToolByName(context, "portal_workflow")
def __call__(self):

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""Implement the ++comments++ traversal namespace. This should return the
IDiscussion container for the context, from which traversal will continue
into an actual comment object.
@ -15,7 +14,7 @@ from zope.traversing.interfaces import TraversalError
@implementer(ITraversable)
@adapter(Interface, IBrowserRequest)
class ConversationNamespace(object):
class ConversationNamespace:
"""Allow traversal into a conversation via a ++conversation++name
namespace. The name is the name of an adapter from context to
IConversation. The special name 'default' will be taken as the default
@ -30,7 +29,7 @@ class ConversationNamespace(object):
def traverse(self, name, ignore):
if name == "default":
name = u""
name = ""
conversation = queryAdapter(self.context, IConversation, name=name)
if conversation is None:

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""Captcha validator, see captcha.txt for design notes.
"""
from Acquisition import aq_inner
@ -39,7 +38,7 @@ class CaptchaValidator(validator.SimpleFieldValidator):
# We adapt the CaptchaValidator class to all form fields (IField)
def validate(self, value):
super(CaptchaValidator, self).validate(value)
super().validate(value)
registry = queryUtility(IRegistry)
settings = registry.forInterface(IDiscussionSettings, check=False)

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""Catalog indexers, using plone.indexer. These will populate standard catalog
indexes with values based on the IComment interface.

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""The default comment class and factory.
"""
from AccessControl import ClassSecurityInfo
@ -44,28 +43,28 @@ import six
COMMENT_TITLE = _(
u"comment_title",
default=u"${author_name} on ${content}",
"comment_title",
default="${author_name} on ${content}",
)
MAIL_NOTIFICATION_MESSAGE = _(
u"mail_notification_message",
default=u'A comment on "${title}" '
u"has been posted here: ${link}\n\n"
u"---\n"
u"${text}\n"
u"---\n",
"mail_notification_message",
default='A comment on "${title}" '
"has been posted here: ${link}\n\n"
"---\n"
"${text}\n"
"---\n",
)
MAIL_NOTIFICATION_MESSAGE_MODERATOR = _(
u"mail_notification_message_moderator2",
default=u'A comment on "${title}" '
u"has been posted by ${commentator}\n"
u"here: ${link}\n\n"
u"---\n\n"
u"${text}\n\n"
u"---\n\n"
u"Log in to moderate.\n\n",
"mail_notification_message_moderator2",
default='A comment on "${title}" '
"has been posted by ${commentator}\n"
"here: ${link}\n\n"
"---\n\n"
"${text}\n\n"
"---\n\n"
"Log in to moderate.\n\n",
)
logger = logging.getLogger("plone.app.discussion")
@ -100,10 +99,10 @@ class Comment(
comment_id = None # long
in_reply_to = None # long
title = u""
title = ""
mime_type = None
text = u""
text = ""
creator = None
creation_date = None
@ -137,7 +136,7 @@ class Comment(
@property
def __name__(self):
return self.comment_id and six.text_type(self.comment_id) or None
return self.comment_id and str(self.comment_id) or None
@property
def id(self):
@ -162,7 +161,7 @@ class Comment(
text = self.text
if text is None:
return ""
if six.PY2 and isinstance(text, six.text_type):
if six.PY2 and isinstance(text, str):
text = text.encode("utf8")
transform = transforms.convertTo(
targetMimetype, text, context=self, mimetype=sourceMimetype
@ -172,8 +171,8 @@ class Comment(
else:
logger = logging.getLogger("plone.app.discussion")
msg = (
u'Transform "{0}" => "{1}" not available. Failed to '
u'transform comment "{2}".'
'Transform "{0}" => "{1}" not available. Failed to '
'transform comment "{2}".'
)
logger.error(
msg.format(
@ -194,8 +193,8 @@ class Comment(
author_name = translate(
Message(
_(
u"label_anonymous",
default=u"Anonymous",
"label_anonymous",
default="Anonymous",
),
),
)
@ -373,7 +372,7 @@ def notify_user(obj, event):
if not emails:
return
subject = translate(_(u"A comment has been posted."), context=obj.REQUEST)
subject = translate(_("A comment has been posted."), context=obj.REQUEST)
message = translate(
Message(
MAIL_NOTIFICATION_MESSAGE,
@ -441,7 +440,7 @@ def notify_moderator(obj, event):
content_object = aq_parent(conversation)
# Compose email
subject = translate(_(u"A comment has been posted."), context=obj.REQUEST)
subject = translate(_("A comment has been posted."), context=obj.REQUEST)
message = translate(
Message(
MAIL_NOTIFICATION_MESSAGE_MODERATOR,
@ -453,8 +452,8 @@ def notify_moderator(obj, event):
or translate(
Message(
_(
u"label_anonymous",
default=u"Anonymous",
"label_anonymous",
default="Anonymous",
),
),
),

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" Content rules handlers
"""
from plone.app.discussion import _
@ -8,7 +7,7 @@ try:
from plone.stringinterp.adapters import BaseSubstitution
except ImportError:
class BaseSubstitution(object):
class BaseSubstitution:
"""Fallback class if plone.stringinterp is not available"""
def __init__(self, context, **kwargs):
@ -32,7 +31,7 @@ class CommentSubstitution(BaseSubstitution):
"""Comment string substitution"""
def __init__(self, context, **kwargs):
super(CommentSubstitution, self).__init__(context, **kwargs)
super().__init__(context, **kwargs)
@property
def event(self):
@ -48,53 +47,53 @@ class CommentSubstitution(BaseSubstitution):
class Id(CommentSubstitution):
"""Comment id string substitution"""
category = _(u"Comments")
description = _(u"Comment id")
category = _("Comments")
description = _("Comment id")
def safe_call(self):
"""Safe call"""
return getattr(self.comment, "comment_id", u"")
return getattr(self.comment, "comment_id", "")
class Text(CommentSubstitution):
"""Comment text"""
category = _(u"Comments")
description = _(u"Comment text")
category = _("Comments")
description = _("Comment text")
def safe_call(self):
"""Safe call"""
return getattr(self.comment, "text", u"")
return getattr(self.comment, "text", "")
class AuthorUserName(CommentSubstitution):
"""Comment author user name string substitution"""
category = _(u"Comments")
description = _(u"Comment author user name")
category = _("Comments")
description = _("Comment author user name")
def safe_call(self):
"""Safe call"""
return getattr(self.comment, "author_username", u"")
return getattr(self.comment, "author_username", "")
class AuthorFullName(CommentSubstitution):
"""Comment author full name string substitution"""
category = _(u"Comments")
description = _(u"Comment author full name")
category = _("Comments")
description = _("Comment author full name")
def safe_call(self):
"""Safe call"""
return getattr(self.comment, "author_name", u"")
return getattr(self.comment, "author_name", "")
class AuthorEmail(CommentSubstitution):
"""Comment author email string substitution"""
category = _(u"Comments")
description = _(u"Comment author email")
category = _("Comments")
description = _("Comment author email")
def safe_call(self):
"""Safe call"""
return getattr(self.comment, "author_email", u"")
return getattr(self.comment, "author_email", "")

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""The conversation and replies adapters
The conversation is responsible for storing all comments. It provides a
@ -130,8 +129,7 @@ class Conversation(Traversable, Persistent, Explicit):
children = self._children.get(comment_id, None)
if children is not None:
for child_id in children:
for value in recurse(child_id, d + 1):
yield value
yield from recurse(child_id, d + 1)
# Find top level threads
comments = self._children.get(root, None)
@ -145,8 +143,7 @@ class Conversation(Traversable, Persistent, Explicit):
return
# Let the closure recurse
for value in recurse(comment_id):
yield value
yield from recurse(comment_id)
def addComment(self, comment):
"""Add a new comment. The parent id should have been set already. The
@ -276,14 +273,14 @@ class Conversation(Traversable, Persistent, Explicit):
return [v.__of__(self) for v in self._comments.values()]
def iterkeys(self):
return six.iterkeys(self._comments)
return self._comments.keys()
def itervalues(self):
for v in six.itervalues(self._comments):
for v in self._comments.values():
yield v.__of__(self)
def iteritems(self):
for k, v in six.iteritems(self._comments):
for k, v in self._comments.items():
yield (
k,
v.__of__(self),
@ -332,7 +329,7 @@ else:
@implementer(IReplies)
@adapter(Conversation) # relies on implementation details
class ConversationReplies(object):
class ConversationReplies:
"""An IReplies adapter for conversations.
This makes it easy to work with top-level comments.

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
""" Custom discussion events
"""
from plone.app.discussion.interfaces import ICommentAddedEvent
@ -15,7 +14,7 @@ from zope.interface import implementer
@implementer(IDiscussionEvent)
class DiscussionEvent(object):
class DiscussionEvent:
"""Custom event"""
def __init__(self, context, comment, **kwargs):

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""Interfaces for plone.app.discussion
"""
from plone.app.discussion import _
@ -42,24 +41,24 @@ class IConversation(IIterableMapping):
"""
total_comments = schema.Int(
title=_(u"Total number of public comments on this item"),
title=_("Total number of public comments on this item"),
min=0,
readonly=True,
)
last_comment_date = schema.Date(
title=_(u"Date of the most recent public comment"),
title=_("Date of the most recent public comment"),
readonly=True,
)
commentators = schema.Set(
title=_(u"The set of unique commentators (usernames)"),
title=_("The set of unique commentators (usernames)"),
readonly=True,
)
public_commentators = schema.Set(
title=_(
u"The set of unique commentators (usernames) " u"of published_comments",
"The set of unique commentators (usernames) " "of published_comments",
),
readonly=True,
)
@ -138,58 +137,58 @@ class IComment(Interface):
"""
portal_type = schema.ASCIILine(
title=_(u"Portal type"),
title=_("Portal type"),
default="Discussion Item",
)
__parent__ = schema.Object(title=_(u"Conversation"), schema=Interface)
__parent__ = schema.Object(title=_("Conversation"), schema=Interface)
__name__ = schema.TextLine(title=_(u"Name"))
__name__ = schema.TextLine(title=_("Name"))
comment_id = schema.Int(title=_(u"A comment id unique to this conversation"))
comment_id = schema.Int(title=_("A comment id unique to this conversation"))
in_reply_to = schema.Int(
title=_(u"Id of comment this comment is in reply to"),
title=_("Id of comment this comment is in reply to"),
required=False,
)
# for logged in comments - set to None for anonymous
author_username = schema.TextLine(title=_(u"Name"), required=False)
author_username = schema.TextLine(title=_("Name"), required=False)
# for anonymous comments only, set to None for logged in comments
author_name = schema.TextLine(title=_(u"Name"), required=False)
author_name = schema.TextLine(title=_("Name"), required=False)
author_email = schema.TextLine(
title=_(u"Email"),
title=_("Email"),
required=False,
constraint=isEmail,
)
title = schema.TextLine(title=_(u"label_subject", default=u"Subject"))
title = schema.TextLine(title=_("label_subject", default="Subject"))
mime_type = schema.ASCIILine(title=_(u"MIME type"), default="text/plain")
mime_type = schema.ASCIILine(title=_("MIME type"), default="text/plain")
text = schema.Text(
title=_(
u"label_comment",
default=u"Comment",
"label_comment",
default="Comment",
),
)
user_notification = schema.Bool(
title=_(
u"Notify me of new comments via email.",
"Notify me of new comments via email.",
),
required=False,
)
creator = schema.TextLine(title=_(u"Username of the commenter"))
creation_date = schema.Date(title=_(u"Creation date"))
modification_date = schema.Date(title=_(u"Modification date"))
creator = schema.TextLine(title=_("Username of the commenter"))
creation_date = schema.Date(title=_("Creation date"))
modification_date = schema.Date(title=_("Modification date"))
class ICaptcha(Interface):
"""Captcha/ReCaptcha text field to extend the existing comment form."""
captcha = schema.TextLine(title=_(u"Captcha"), required=False)
captcha = schema.TextLine(title=_("Captcha"), required=False)
class IDiscussionSettings(Interface):
@ -204,26 +203,26 @@ class IDiscussionSettings(Interface):
# - Search control panel: Show comments in search results
globally_enabled = schema.Bool(
title=_(u"label_globally_enabled", default=u"Globally enable comments"),
title=_("label_globally_enabled", default="Globally enable comments"),
description=_(
u"help_globally_enabled",
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.",
"help_globally_enabled",
default="If selected, users are able to post comments on the "
"site. However, you will still need to enable comments "
"for specific content types, folders or content "
"objects before users will be able to post comments.",
),
required=False,
default=False,
)
anonymous_comments = schema.Bool(
title=_(u"label_anonymous_comments", default="Enable anonymous comments"),
title=_("label_anonymous_comments", default="Enable anonymous comments"),
description=_(
u"help_anonymous_comments",
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.",
"help_anonymous_comments",
default="If selected, anonymous users are able to post "
"comments without logging in. It is highly "
"recommended to use a captcha solution to prevent "
"spam if this setting is enabled.",
),
required=False,
default=False,
@ -231,11 +230,11 @@ class IDiscussionSettings(Interface):
anonymous_email_enabled = schema.Bool(
title=_(
u"label_anonymous_email_enabled", default=u"Enable anonymous email field"
"label_anonymous_email_enabled", default="Enable anonymous email field"
),
description=_(
u"help_anonymous_email_enabled",
default=u"If selected, anonymous user will have to " u"give their email.",
"help_anonymous_email_enabled",
default="If selected, anonymous user will have to " "give their email.",
),
required=False,
default=False,
@ -243,28 +242,28 @@ class IDiscussionSettings(Interface):
moderation_enabled = schema.Bool(
title=_(
u"label_moderation_enabled",
"label_moderation_enabled",
default="Enable comment moderation",
),
description=_(
u"help_moderation_enabled",
default=u'If selected, comments will enter a "Pending" state '
u"in which they are invisible to the public. A user "
u'with the "Review comments" permission ("Reviewer" '
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.",
"help_moderation_enabled",
default='If selected, comments will enter a "Pending" state '
"in which they are invisible to the public. A user "
'with the "Review comments" permission ("Reviewer" '
'or "Manager") can approve comments to make them '
"visible to the public. If you want to enable a "
"custom comment workflow, you have to go to the "
"types control panel.",
),
required=False,
default=False,
)
edit_comment_enabled = schema.Bool(
title=_(u"label_edit_comment_enabled", default="Enable editing of comments"),
title=_("label_edit_comment_enabled", default="Enable editing of comments"),
description=_(
u"help_edit_comment_enabled",
default=u"If selected, supports editing "
"help_edit_comment_enabled",
default="If selected, supports editing "
'of comments for users with the "Edit comments" '
"permission.",
),
@ -274,11 +273,11 @@ class IDiscussionSettings(Interface):
delete_own_comment_enabled = schema.Bool(
title=_(
u"label_delete_own_comment_enabled", default="Enable deleting own comments"
"label_delete_own_comment_enabled", default="Enable deleting own comments"
),
description=_(
u"help_delete_own_comment_enabled",
default=u"If selected, supports deleting "
"help_delete_own_comment_enabled",
default="If selected, supports deleting "
"of own comments for users with the "
'"Delete own comments" permission.',
),
@ -287,16 +286,16 @@ class IDiscussionSettings(Interface):
)
text_transform = schema.Choice(
title=_(u"label_text_transform", default="Comment text transform"),
title=_("label_text_transform", default="Comment text transform"),
description=_(
u"help_text_transform",
default=u"Use this setting to choose if the comment text "
u"should be transformed in any way. You can choose "
u'between "Plain text" and "Intelligent text". '
u'"Intelligent text" converts plain text into HTML '
u"where line breaks and indentation is preserved, "
u"and web and email addresses are made into "
u"clickable links.",
"help_text_transform",
default="Use this setting to choose if the comment text "
"should be transformed in any way. You can choose "
'between "Plain text" and "Intelligent text". '
'"Intelligent text" converts plain text into HTML '
"where line breaks and indentation is preserved, "
"and web and email addresses are made into "
"clickable links.",
),
required=True,
default="text/plain",
@ -304,15 +303,15 @@ class IDiscussionSettings(Interface):
)
captcha = schema.Choice(
title=_(u"label_captcha", default="Captcha"),
title=_("label_captcha", default="Captcha"),
description=_(
u"help_captcha",
default=u"Use this setting to enable or disable Captcha "
u"validation for comments. Install "
u"plone.formwidget.captcha, "
u"plone.formwidget.recaptcha, collective.akismet, or "
u"collective.z3cform.norobots if there are no options "
u"available.",
"help_captcha",
default="Use this setting to enable or disable Captcha "
"validation for comments. Install "
"plone.formwidget.captcha, "
"plone.formwidget.recaptcha, collective.akismet, or "
"collective.z3cform.norobots if there are no options "
"available.",
),
required=True,
default="disabled",
@ -320,11 +319,11 @@ class IDiscussionSettings(Interface):
)
show_commenter_image = schema.Bool(
title=_(u"label_show_commenter_image", default=u"Show commenter image"),
title=_("label_show_commenter_image", default="Show commenter image"),
description=_(
u"help_show_commenter_image",
default=u"If selected, an image of the user is shown next to "
u"the comment.",
"help_show_commenter_image",
default="If selected, an image of the user is shown next to "
"the comment.",
),
required=False,
default=True,
@ -332,14 +331,14 @@ class IDiscussionSettings(Interface):
moderator_notification_enabled = schema.Bool(
title=_(
u"label_moderator_notification_enabled",
default=u"Enable moderator email notification",
"label_moderator_notification_enabled",
default="Enable moderator email notification",
),
description=_(
u"help_moderator_notification_enabled",
default=u"If selected, the moderator is notified if a comment "
u"needs attention. The moderator email address can "
u"be set below.",
"help_moderator_notification_enabled",
default="If selected, the moderator is notified if a comment "
"needs attention. The moderator email address can "
"be set below.",
),
required=False,
default=False,
@ -347,25 +346,25 @@ class IDiscussionSettings(Interface):
moderator_email = schema.ASCIILine(
title=_(
u"label_moderator_email",
default=u"Moderator Email Address",
"label_moderator_email",
default="Moderator Email Address",
),
description=_(
u"help_moderator_email",
default=u"Address to which moderator notifications " u"will be sent.",
"help_moderator_email",
default="Address to which moderator notifications " "will be sent.",
),
required=False,
)
user_notification_enabled = schema.Bool(
title=_(
u"label_user_notification_enabled",
default=u"Enable user email notification",
"label_user_notification_enabled",
default="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.",
"help_user_notification_enabled",
default="If selected, users can choose to be notified "
"of new comments by email.",
),
required=False,
default=False,

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from Products.CMFCore.utils import getToolByName

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from plone.app.contenttypes.testing import PLONE_APP_CONTENTTYPES_FIXTURE
from plone.app.discussion.interfaces import IDiscussionSettings
from plone.app.robotframework.testing import REMOTE_LIBRARY_ROBOT_TESTING

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""Test the plone.app.discussion catalog indexes
"""
from datetime import datetime
@ -98,7 +97,7 @@ class ConversationCatalogTest(unittest.TestCase):
new_comment2_id = self.conversation.addComment(comment2)
comment2 = self.portal.doc1.restrictedTraverse(
"++conversation++default/{0}".format(new_comment2_id),
f"++conversation++default/{new_comment2_id}",
)
comment2.reindexObject()
brains = self.catalog.searchResults(
@ -128,7 +127,7 @@ class ConversationCatalogTest(unittest.TestCase):
new_comment2_id = self.conversation.addComment(comment2)
comment2 = self.portal.doc1.restrictedTraverse(
"++conversation++default/{0}".format(new_comment2_id),
f"++conversation++default/{new_comment2_id}",
)
comment2.reindexObject()
brains = self.catalog.searchResults(
@ -188,7 +187,7 @@ class ConversationCatalogTest(unittest.TestCase):
new_comment2_id = self.conversation.addComment(comment2)
comment2 = self.portal.doc1.restrictedTraverse(
"++conversation++default/{0}".format(new_comment2_id),
f"++conversation++default/{new_comment2_id}",
)
comment2.reindexObject()
@ -283,7 +282,7 @@ class CommentCatalogTest(unittest.TestCase):
# Comment brain
self.comment = self.portal.doc1.restrictedTraverse(
"++conversation++default/{0}".format(new_comment1_id),
f"++conversation++default/{new_comment1_id}",
)
brains = self.catalog.searchResults(
dict(
@ -304,7 +303,7 @@ class CommentCatalogTest(unittest.TestCase):
# Comment brain
comment = self.portal.doc1.restrictedTraverse(
"++conversation++default/{0}".format(cid),
f"++conversation++default/{cid}",
)
brains = self.catalog.searchResults(
dict(
@ -503,7 +502,7 @@ class CommentCatalogTest(unittest.TestCase):
brains = self.catalog.searchResults({"portal_type": "Discussion Item"})
self.assertTrue(brains)
comment_brain = brains[0]
self.assertEqual(comment_brain.Title, u"Jim on Document 1")
self.assertEqual(comment_brain.Title, "Jim on Document 1")
self.assertEqual(
comment_brain.getPath(),
"/plone/doc1/++conversation++default/" + str(self.comment_id),

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from plone.app.discussion.browser.comment import View
from plone.app.discussion.interfaces import IComment
from plone.app.discussion.interfaces import IConversation
@ -64,7 +63,7 @@ class CommentTest(unittest.TestCase):
comment1.comment_id = 123
self.assertEqual("123", comment1.id)
self.assertEqual("123", comment1.getId())
self.assertEqual(u"123", comment1.__name__)
self.assertEqual("123", comment1.__name__)
def test_uid(self):
conversation = IConversation(self.portal.doc1)
@ -111,14 +110,14 @@ class CommentTest(unittest.TestCase):
def test_title_special_characters(self):
self.portal.invokeFactory(
id="doc_sp_chars",
title=u"Document äüö",
title="Document äüö",
type_name="Document",
)
conversation = IConversation(self.portal.doc_sp_chars)
comment1 = createObject("plone.Comment")
comment1.author_name = u"Tarek Ziadé"
comment1.author_name = "Tarek Ziadé"
conversation.addComment(comment1)
self.assertEqual(u"Tarek Ziadé on Document äüö", comment1.Title())
self.assertEqual("Tarek Ziadé on Document äüö", comment1.Title())
def test_title_special_characters_utf8(self):
self.portal.invokeFactory(
@ -130,7 +129,7 @@ class CommentTest(unittest.TestCase):
comment1 = createObject("plone.Comment")
comment1.author_name = "Hüüb Bôûmä"
conversation.addComment(comment1)
self.assertEqual(u"Hüüb Bôûmä on Document ëïû", comment1.Title())
self.assertEqual("Hüüb Bôûmä on Document ëïû", comment1.Title())
def test_creator(self):
comment1 = createObject("plone.Comment")
@ -174,11 +173,8 @@ class CommentTest(unittest.TestCase):
def test_getText_with_non_ascii_characters(self):
comment1 = createObject("plone.Comment")
comment1.text = u"Umlaute sind ä, ö und ü."
comment1.text = "Umlaute sind ä, ö und ü."
out = b"<p>Umlaute sind \xc3\xa4, \xc3\xb6 und \xc3\xbc.</p>"
if six.PY2:
self.assertEqual(comment1.getText(), out)
else:
self.assertEqual(comment1.getText(), out.decode("utf8"))
def test_getText_doesnt_link(self):
@ -233,7 +229,7 @@ class CommentTest(unittest.TestCase):
new_comment1_id = conversation.addComment(comment1)
comment = self.portal.doc1.restrictedTraverse(
"++conversation++default/{0}".format(new_comment1_id),
f"++conversation++default/{new_comment1_id}",
)
self.assertTrue(IComment.providedBy(comment))
@ -268,7 +264,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),
f"++conversation++default/{new_comment1_id}",
)
view = View(comment, self.request)
@ -336,7 +332,7 @@ class CommentTest(unittest.TestCase):
new_comment1_id = conversation.addComment(comment1)
comment = self.portal.doc1.restrictedTraverse(
"++conversation++default/{0}".format(new_comment1_id),
f"++conversation++default/{new_comment1_id}",
)
# make sure the view is there
@ -381,7 +377,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),
f"++conversation++default/{new_id}",
)
# Add a reply to the CommentReplies adapter of the first comment
@ -418,7 +414,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),
f"++conversation++default/{new_id}",
)
# Add a reply to the CommentReplies adapter of the first comment
@ -454,7 +450,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),
f"++conversation++default/{new_id}",
)
# Add a reply to the CommentReplies adapter of the first comment
@ -463,7 +459,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),
f"++conversation++default/{new_re_id}",
)
# Add a reply to the reply
@ -472,7 +468,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),
f"++conversation++default/{new_re_re_id}",
)
# Add a reply to the replies reply
@ -481,7 +477,7 @@ 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),
f"++conversation++default/{new_re_re_re_id}",
)
self.assertEqual(

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from AccessControl import Unauthorized
from datetime import datetime
from OFS.Image import Image
@ -81,7 +80,7 @@ class TestCommentForm(unittest.TestCase):
adapts=(Interface, IBrowserRequest),
provides=Interface,
factory=CommentForm,
name=u"comment-form",
name="comment-form",
)
# The form should return an error if the comment text field is empty
@ -89,7 +88,7 @@ class TestCommentForm(unittest.TestCase):
commentForm = getMultiAdapter(
(self.context, request),
name=u"comment-form",
name="comment-form",
)
commentForm.update()
data, errors = commentForm.extractData() # pylint: disable-msg=W0612
@ -99,11 +98,11 @@ class TestCommentForm(unittest.TestCase):
# The form is submitted successfully, if the required text field is
# filled out
request = make_request(form={"form.widgets.text": u"bar"})
request = make_request(form={"form.widgets.text": "bar"})
commentForm = getMultiAdapter(
(self.context, request),
name=u"comment-form",
name="comment-form",
)
commentForm.update()
data, errors = commentForm.extractData() # pylint: disable-msg=W0612
@ -116,7 +115,7 @@ class TestCommentForm(unittest.TestCase):
self.assertEqual(len(comments), 1)
for comment in comments:
self.assertEqual(comment.text, u"bar")
self.assertEqual(comment.text, "bar")
self.assertEqual(comment.creator, "test_user_1_")
self.assertEqual(comment.getOwner().getUserName(), "test-user")
local_roles = comment.get_local_roles()
@ -144,23 +143,23 @@ class TestCommentForm(unittest.TestCase):
adapts=(Interface, IBrowserRequest),
provides=Interface,
factory=CommentForm,
name=u"comment-form",
name="comment-form",
)
provideAdapter(
adapts=(Interface, IBrowserRequest),
provides=Interface,
factory=EditCommentForm,
name=u"edit-comment-form",
name="edit-comment-form",
)
# The form is submitted successfully, if the required text field is
# filled out
request = make_request(form={"form.widgets.text": u"bar"})
request = make_request(form={"form.widgets.text": "bar"})
commentForm = getMultiAdapter(
(self.context, request),
name=u"comment-form",
name="comment-form",
)
commentForm.update()
data, errors = commentForm.extractData() # pylint: disable-msg=W0612
@ -171,10 +170,10 @@ class TestCommentForm(unittest.TestCase):
# Edit the last comment
conversation = IConversation(self.context)
comment = [x for x in conversation.getComments()][-1]
request = make_request(form={"form.widgets.text": u"foobar"})
request = make_request(form={"form.widgets.text": "foobar"})
editForm = getMultiAdapter(
(comment, request),
name=u"edit-comment-form",
name="edit-comment-form",
)
editForm.update()
data, errors = editForm.extractData() # pylint: disable-msg=W0612
@ -182,14 +181,14 @@ class TestCommentForm(unittest.TestCase):
self.assertEqual(len(errors), 0)
self.assertFalse(editForm.handleComment(editForm, "foo"))
comment = [x for x in conversation.getComments()][-1]
self.assertEqual(comment.text, u"foobar")
self.assertEqual(comment.text, "foobar")
comments = IConversation(commentForm.context).getComments()
comments = [c for c in comments] # consume iterator
self.assertEqual(len(comments), 1)
for comment in comments:
self.assertEqual(comment.text, u"foobar")
self.assertEqual(comment.text, "foobar")
self.assertEqual(comment.creator, "test_user_1_")
self.assertEqual(comment.getOwner().getUserName(), "test-user")
@ -218,16 +217,16 @@ class TestCommentForm(unittest.TestCase):
adapts=(Interface, IBrowserRequest),
provides=Interface,
factory=CommentForm,
name=u"comment-form",
name="comment-form",
)
# The form is submitted successfully, if the required text field is
# filled out
form_request = make_request(form={"form.widgets.text": u"bar"})
form_request = make_request(form={"form.widgets.text": "bar"})
commentForm = getMultiAdapter(
(self.context, form_request),
name=u"comment-form",
name="comment-form",
)
commentForm.update()
@ -240,7 +239,7 @@ class TestCommentForm(unittest.TestCase):
comment = [x for x in conversation.getComments()][-1]
deleteView = getMultiAdapter(
(comment, self.request),
name=u"moderate-delete-comment",
name="moderate-delete-comment",
)
# try to delete last comment without 'Delete comments' permission
setRoles(self.portal, TEST_USER_ID, ["Member"])
@ -275,16 +274,16 @@ class TestCommentForm(unittest.TestCase):
adapts=(Interface, IBrowserRequest),
provides=Interface,
factory=CommentForm,
name=u"comment-form",
name="comment-form",
)
# The form is submitted successfully, if the required text field is
# filled out
form_request = make_request(form={"form.widgets.text": u"bar"})
form_request = make_request(form={"form.widgets.text": "bar"})
commentForm = getMultiAdapter(
(self.context, form_request),
name=u"comment-form",
name="comment-form",
)
commentForm.update()
@ -297,7 +296,7 @@ class TestCommentForm(unittest.TestCase):
comment = [x for x in conversation.getComments()][-1]
deleteView = getMultiAdapter(
(comment, self.request),
name=u"delete-own-comment",
name="delete-own-comment",
)
# try to delete last comment with johndoe
setRoles(self.portal, "johndoe", ["Member"])
@ -337,20 +336,20 @@ class TestCommentForm(unittest.TestCase):
adapts=(Interface, IBrowserRequest),
provides=Interface,
factory=CommentForm,
name=u"comment-form",
name="comment-form",
)
# Post an anonymous comment and provide a name
request = make_request(
form={
"form.widgets.name": u"john doe",
"form.widgets.text": u"bar",
"form.widgets.name": "john doe",
"form.widgets.text": "bar",
}
)
commentForm = getMultiAdapter(
(self.context, request),
name=u"comment-form",
name="comment-form",
)
commentForm.update()
data, errors = commentForm.extractData() # pylint: disable-msg=W0612
@ -363,7 +362,7 @@ class TestCommentForm(unittest.TestCase):
self.assertEqual(len(comments), 1)
for comment in IConversation(commentForm.context).getComments():
self.assertEqual(comment.text, u"bar")
self.assertEqual(comment.text, "bar")
self.assertIsNone(comment.creator)
roles = comment.get_local_roles()
self.assertEqual(len(roles), 0)
@ -387,14 +386,14 @@ class TestCommentForm(unittest.TestCase):
adapts=(Interface, IBrowserRequest),
provides=Interface,
factory=CommentForm,
name=u"comment-form",
name="comment-form",
)
request = make_request(form={"form.widgets.text": u"bar"})
request = make_request(form={"form.widgets.text": "bar"})
commentForm = getMultiAdapter(
(self.context, request),
name=u"comment-form",
name="comment-form",
)
commentForm.update()
data, errors = commentForm.extractData() # pylint: disable-msg=W0612
@ -425,12 +424,12 @@ class TestCommentForm(unittest.TestCase):
adapts=(Interface, IBrowserRequest),
provides=Interface,
factory=CommentForm,
name=u"comment-form",
name="comment-form",
)
request = make_request(form={"form.widgets.text": u"bar"})
request = make_request(form={"form.widgets.text": "bar"})
commentForm = getMultiAdapter((self.context, request), name=u"comment-form")
commentForm = getMultiAdapter((self.context, request), name="comment-form")
commentForm.update()
data, errors = commentForm.extractData() # pylint: disable-msg=W0612

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from plone.app.discussion.interfaces import ICommentAddedEvent
from plone.app.discussion.interfaces import ICommentRemovedEvent
from plone.app.discussion.interfaces import IConversation
@ -56,32 +55,32 @@ class CommentContentRulesTest(unittest.TestCase):
self.assertTrue(IRuleEventType.providedBy(IReplyRemovedEvent))
def testCommentIdStringSubstitution(self):
comment_id = getAdapter(self.document, IStringSubstitution, name=u"comment_id")
comment_id = getAdapter(self.document, IStringSubstitution, name="comment_id")
self.assertIsInstance(comment_id(), int)
def testCommentTextStringSubstitution(self):
comment_text = getAdapter(
self.document, IStringSubstitution, name=u"comment_text"
self.document, IStringSubstitution, name="comment_text"
)
self.assertEqual(comment_text(), u"This is a comment")
self.assertEqual(comment_text(), "This is a comment")
def testCommentUserIdStringSubstitution(self):
comment_user_id = getAdapter(
self.document, IStringSubstitution, name=u"comment_user_id"
self.document, IStringSubstitution, name="comment_user_id"
)
self.assertEqual(comment_user_id(), u"jim")
self.assertEqual(comment_user_id(), "jim")
def testCommentUserFullNameStringSubstitution(self):
comment_user_fullname = getAdapter(
self.document, IStringSubstitution, name=u"comment_user_fullname"
self.document, IStringSubstitution, name="comment_user_fullname"
)
self.assertEqual(comment_user_fullname(), u"Jim")
self.assertEqual(comment_user_fullname(), "Jim")
def testCommentUserEmailStringSubstitution(self):
comment_user_email = getAdapter(
self.document, IStringSubstitution, name=u"comment_user_email"
self.document, IStringSubstitution, name="comment_user_email"
)
self.assertEqual(comment_user_email(), u"jim@example.com")
self.assertEqual(comment_user_email(), "jim@example.com")
class ReplyContentRulesTest(unittest.TestCase):
@ -103,7 +102,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),
f"++conversation++default/{new_id}",
)
re_comment = createObject("plone.Comment")
@ -119,7 +118,7 @@ class ReplyContentRulesTest(unittest.TestCase):
reply_id = getAdapter(
self.document,
IStringSubstitution,
name=u"comment_id",
name="comment_id",
)
self.assertIsInstance(reply_id(), int)
@ -127,30 +126,30 @@ class ReplyContentRulesTest(unittest.TestCase):
reply_text = getAdapter(
self.document,
IStringSubstitution,
name=u"comment_text",
name="comment_text",
)
self.assertEqual(reply_text(), u"This is a reply")
self.assertEqual(reply_text(), "This is a reply")
def testReplyUserIdStringSubstitution(self):
reply_user_id = getAdapter(
self.document,
IStringSubstitution,
name=u"comment_user_id",
name="comment_user_id",
)
self.assertEqual(reply_user_id(), u"julia")
self.assertEqual(reply_user_id(), "julia")
def testReplyUserFullNameStringSubstitution(self):
reply_user_fullname = getAdapter(
self.document,
IStringSubstitution,
name=u"comment_user_fullname",
name="comment_user_fullname",
)
self.assertEqual(reply_user_fullname(), u"Juliana")
self.assertEqual(reply_user_fullname(), "Juliana")
def testReplyUserEmailStringSubstitution(self):
reply_user_email = getAdapter(
self.document,
IStringSubstitution,
name=u"comment_user_email",
name="comment_user_email",
)
self.assertEqual(reply_user_email(), u"julia@example.com")
self.assertEqual(reply_user_email(), "julia@example.com")

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from plone.app.discussion.interfaces import IDiscussionSettings
from plone.app.discussion.testing import ( # noqa
PLONE_APP_DISCUSSION_INTEGRATION_TESTING,

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from Acquisition import aq_base
from Acquisition import aq_parent
from datetime import datetime
@ -387,17 +386,17 @@ class ConversationTest(unittest.TestCase):
self.assertTrue(comment2 in conversation.values())
# check if comment ids are in iterkeys
self.assertTrue(new_id1 in six.iterkeys(conversation))
self.assertTrue(new_id2 in six.iterkeys(conversation))
self.assertFalse(123 in six.iterkeys(conversation))
self.assertTrue(new_id1 in conversation.keys())
self.assertTrue(new_id2 in conversation.keys())
self.assertFalse(123 in conversation.keys())
# check if comment objects are in itervalues
self.assertTrue(comment1 in six.itervalues(conversation))
self.assertTrue(comment2 in six.itervalues(conversation))
self.assertTrue(comment1 in conversation.values())
self.assertTrue(comment2 in conversation.values())
# check if iteritems returns (key, comment object) pairs
self.assertTrue((new_id1, comment1) in six.iteritems(conversation))
self.assertTrue((new_id2, comment2) in six.iteritems(conversation))
self.assertTrue((new_id1, comment1) in conversation.items())
self.assertTrue((new_id2, comment2) in conversation.items())
# TODO test acquisition wrapping # noqa T000
# self.assertTrue(aq_base(aq_parent(comment1)) is conversation)
@ -852,18 +851,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),
f"++conversation++default/{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),
f"++conversation++default/{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),
f"++conversation++default/{new_id_1_1}",
)
replies_to_comment1_1 = IReplies(comment1_1)
replies_to_comment1_1.addComment(comment1_1_1)

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from plone.app.discussion.interfaces import IConversation
from plone.app.discussion.interfaces import IReplies
from plone.app.discussion.testing import ( # noqa
@ -20,7 +19,7 @@ import unittest
#
class EventsRegistry(object):
class EventsRegistry:
"""Fake registry to be used while testing discussion events"""
commentAdded = False
@ -123,7 +122,7 @@ class CommentEventsTest(unittest.TestCase):
conversation = IConversation(self.document)
new_id = conversation.addComment(comment)
comment = self.document.restrictedTraverse(
"++conversation++default/{0}".format(new_id),
f"++conversation++default/{new_id}",
)
comment.text = "foo"
notify(ObjectModifiedEvent(comment))
@ -191,7 +190,7 @@ class RepliesEventsTest(unittest.TestCase):
comment.text = "Comment text"
new_id = replies.addComment(comment)
comment = self.document.restrictedTraverse(
"++conversation++default/{0}".format(new_id),
f"++conversation++default/{new_id}",
)
re_comment = createObject("plone.Comment")
@ -211,7 +210,7 @@ class RepliesEventsTest(unittest.TestCase):
comment.text = "Comment text"
comment_id = replies.addComment(comment)
comment = self.document.restrictedTraverse(
"++conversation++default/{0}".format(comment_id),
f"++conversation++default/{comment_id}",
)
re_comment = createObject("plone.Comment")
re_comment.text = "Comment text"
@ -232,7 +231,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),
f"++conversation++default/{new_id}",
)
re_comment = createObject("plone.Comment")

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""Functional Doctests for plone.app.discussion.
These test are only triggered when Plone 4 (and plone.testing) is installed.

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""Test for the plone.app.discussion indexers
"""
from datetime import datetime

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from plone.app.discussion.browser.moderation import BulkActionsView
from plone.app.discussion.browser.moderation import CommentTransition
from plone.app.discussion.browser.moderation import DeleteComment
@ -42,7 +41,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),
f"++conversation++default/{new_id_1}",
)
comment2 = createObject("plone.Comment")
comment2.title = "Comment 2"
@ -50,7 +49,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),
f"++conversation++default/{new_id_2}",
)
comment3 = createObject("plone.Comment")
comment3.title = "Comment 3"
@ -58,7 +57,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),
f"++conversation++default/{new_id_3}",
)
self.conversation = conversation

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from plone.app.discussion.browser.moderation import BulkActionsView
from plone.app.discussion.browser.moderation import CommentTransition
from plone.app.discussion.browser.moderation import DeleteComment
@ -81,7 +80,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),
f"++conversation++default/{new_id_1}",
)
comment2 = createObject("plone.Comment")
comment2.title = "Comment 2"
@ -89,7 +88,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),
f"++conversation++default/{new_id_2}",
)
comment3 = createObject("plone.Comment")
comment3.title = "Comment 3"
@ -97,7 +96,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),
f"++conversation++default/{new_id_3}",
)
self.conversation = conversation

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from Acquisition import aq_base
from plone.app.discussion.interfaces import IConversation
from plone.app.discussion.testing import ( # noqa
@ -80,7 +79,7 @@ class TestUserNotificationUnit(unittest.TestCase):
# you may get lines separated by '\n' or '\r\n' in here.
msg = msg.replace("\r\n", "\n")
self.assertIn('A comment on "K=C3=B6lle Alaaf" has been posted here:', msg)
self.assertIn("http://nohost/plone/d=\noc1/view#{0}".format(comment_id), msg)
self.assertIn(f"http://nohost/plone/d=\noc1/view#{comment_id}", msg)
self.assertIn("Comment text", msg)
self.assertNotIn("Approve comment", msg)
self.assertNotIn("Delete comment", msg)
@ -215,7 +214,7 @@ class TestModeratorNotificationUnit(unittest.TestCase):
# The output should be encoded in a reasonable manner
# (in this case quoted-printable):
self.assertTrue('A comment on "K=C3=B6lle Alaaf" has been posted' in msg)
self.assertIn("http://nohost/plone/doc1/view#{0}".format(comment_id), msg)
self.assertIn(f"http://nohost/plone/doc1/view#{comment_id}", msg)
self.assertIn(comment.author_email, msg)
self.assertIn(comment.text, msg)

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from plone.app.discussion.testing import PLONE_APP_DISCUSSION_ROBOT_TESTING
from plone.app.testing import ROBOT_TEST_LEVEL
from plone.testing import layered

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""Test plone.app.discussion workflow and permissions.
"""
from AccessControl import Unauthorized
@ -128,7 +127,7 @@ class CommentOneStateWorkflowTest(unittest.TestCase):
cid = conversation.addComment(comment)
self.comment = self.folder.doc1.restrictedTraverse(
"++conversation++default/{0}".format(cid),
f"++conversation++default/{cid}",
)
self.portal.acl_users._doAddUser("member", "secret", ["Member"], [])
@ -223,7 +222,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),
f"++conversation++default/{comment_id}",
)
self.conversation = conversation

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""The portal_discussion tool, usually accessed via
queryUtility(ICommentingTool). The default implementation delegates to the
standard portal_catalog for indexing comments.

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from plone.app.discussion.interfaces import IDiscussionSettings
from plone.registry.interfaces import IRegistry
from Products.CMFCore.utils import getToolByName
@ -63,7 +62,7 @@ def upgrade_comment_workflows_apply_rolemapping(context):
wf.updateRoleMappingsFor(comment)
comment.reindexObjectSecurity()
except (AttributeError, KeyError):
logger.info("Could not reindex comment {0}".format(brain.getURL()))
logger.info(f"Could not reindex comment {brain.getURL()}")
def upgrade_comment_workflows(context):

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from plone.app.discussion.interfaces import _
from zope.schema.vocabulary import SimpleTerm
from zope.schema.vocabulary import SimpleVocabulary
@ -40,7 +39,7 @@ except ImportError:
def captcha_vocabulary(context):
"""Vocabulary with all available captcha implementations."""
terms = []
terms.append(SimpleTerm(value="disabled", token="disabled", title=_(u"Disabled")))
terms.append(SimpleTerm(value="disabled", token="disabled", title=_("Disabled")))
if HAS_CAPTCHA: # pragma: no cover
terms.append(SimpleTerm(value="captcha", token="captcha", title="Captcha"))

View File

@ -1,5 +1,3 @@
# encoding: utf-8
from setuptools import find_packages
from setuptools import setup