commit
f0d837294f
@ -15,6 +15,8 @@ Fixes:
|
|||||||
Issue https://github.com/plone/Products.CMFPlone/issues/1332
|
Issue https://github.com/plone/Products.CMFPlone/issues/1332
|
||||||
[staeff, fredvd]
|
[staeff, fredvd]
|
||||||
|
|
||||||
|
- Cleanup code according to our style guide.
|
||||||
|
[gforcada]
|
||||||
|
|
||||||
|
|
||||||
2.4.9 (2015-11-25)
|
2.4.9 (2015-11-25)
|
||||||
|
@ -1,6 +1,2 @@
|
|||||||
# See http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages
|
# -*- coding: utf-8 -*-
|
||||||
try:
|
|
||||||
__import__('pkg_resources').declare_namespace(__name__)
|
__import__('pkg_resources').declare_namespace(__name__)
|
||||||
except ImportError:
|
|
||||||
from pkgutil import extend_path
|
|
||||||
__path__ = extend_path(__path__, __name__)
|
|
||||||
|
@ -1,6 +1,2 @@
|
|||||||
# See http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages
|
# -*- coding: utf-8 -*-
|
||||||
try:
|
|
||||||
__import__('pkg_resources').declare_namespace(__name__)
|
__import__('pkg_resources').declare_namespace(__name__)
|
||||||
except ImportError:
|
|
||||||
from pkgutil import extend_path
|
|
||||||
__path__ = extend_path(__path__, __name__)
|
|
||||||
|
@ -12,7 +12,8 @@ from z3c.form import interfaces
|
|||||||
from z3c.form.field import Fields
|
from z3c.form.field import Fields
|
||||||
from zope import interface
|
from zope import interface
|
||||||
from zope.annotation import factory
|
from zope.annotation import factory
|
||||||
from zope.component import adapts, queryUtility
|
from zope.component import adapts
|
||||||
|
from zope.component import queryUtility
|
||||||
from zope.interface import Interface
|
from zope.interface import Interface
|
||||||
from zope.publisher.interfaces.browser import IDefaultBrowserLayer
|
from zope.publisher.interfaces.browser import IDefaultBrowserLayer
|
||||||
|
|
||||||
@ -51,7 +52,7 @@ class CaptchaExtender(extensible.FormExtender):
|
|||||||
def update(self):
|
def update(self):
|
||||||
if self.captcha != 'disabled' and self.isAnon:
|
if self.captcha != 'disabled' and self.isAnon:
|
||||||
# Add a captcha field if captcha is enabled in the registry
|
# Add a captcha field if captcha is enabled in the registry
|
||||||
self.add(ICaptcha, prefix="")
|
self.add(ICaptcha, prefix='')
|
||||||
if self.captcha == 'captcha':
|
if self.captcha == 'captcha':
|
||||||
from plone.formwidget.captcha import CaptchaFieldWidget
|
from plone.formwidget.captcha import CaptchaFieldWidget
|
||||||
self.form.fields['captcha'].widgetFactory = CaptchaFieldWidget
|
self.form.fields['captcha'].widgetFactory = CaptchaFieldWidget
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
from AccessControl import getSecurityManager
|
from AccessControl import getSecurityManager
|
||||||
from Acquisition import aq_inner
|
from Acquisition import aq_inner
|
||||||
from Acquisition import aq_parent
|
from Acquisition import aq_parent
|
||||||
from zope.component import getUtility
|
|
||||||
from Products.CMFCore.utils import getToolByName
|
|
||||||
from Products.Five.browser import BrowserView
|
|
||||||
from Products.statusmessages.interfaces import IStatusMessage
|
|
||||||
from comments import CommentForm
|
from comments import CommentForm
|
||||||
from plone.app.discussion import _
|
from plone.app.discussion import _
|
||||||
from plone.registry.interfaces import IRegistry
|
from plone.registry.interfaces import IRegistry
|
||||||
from plone.z3cform.layout import wrap_form
|
from plone.z3cform.layout import wrap_form
|
||||||
|
from Products.CMFCore.utils import getToolByName
|
||||||
|
from Products.Five.browser import BrowserView
|
||||||
|
from Products.statusmessages.interfaces import IStatusMessage
|
||||||
from z3c.form import button
|
from z3c.form import button
|
||||||
from zope.component import getMultiAdapter
|
from zope.component import getMultiAdapter
|
||||||
|
from zope.component import getUtility
|
||||||
|
|
||||||
|
|
||||||
class View(BrowserView):
|
class View(BrowserView):
|
||||||
@ -21,9 +22,9 @@ class View(BrowserView):
|
|||||||
has been posted.
|
has been posted.
|
||||||
|
|
||||||
Redirect from the comment object URL
|
Redirect from the comment object URL
|
||||||
"/path/to/object/++conversation++default/123456789" to the content object
|
'/path/to/object/++conversation++default/123456789' to the content object
|
||||||
where the comment has been posted appended by an HTML anchor that points to
|
where the comment has been posted appended by an HTML anchor that points to
|
||||||
the comment "/path/to/object#comment-123456789".
|
the comment '/path/to/object#comment-123456789'.
|
||||||
|
|
||||||
Context is the comment object. The parent of the comment object is the
|
Context is the comment object. The parent of the comment object is the
|
||||||
conversation. The parent of the conversation is the content object where
|
conversation. The parent of the conversation is the content object where
|
||||||
@ -46,15 +47,15 @@ class View(BrowserView):
|
|||||||
will redirect right to the binary object, bypassing comments.
|
will redirect right to the binary object, bypassing comments.
|
||||||
"""
|
"""
|
||||||
if obj.portal_type in view_action_types:
|
if obj.portal_type in view_action_types:
|
||||||
url = "%s/view" % url
|
url = '{0}/view'.format(url)
|
||||||
|
|
||||||
self.request.response.redirect('%s#%s' % (url, context.id))
|
self.request.response.redirect('{0}#{1}'.format(url, context.id))
|
||||||
|
|
||||||
|
|
||||||
class EditCommentForm(CommentForm):
|
class EditCommentForm(CommentForm):
|
||||||
"""Form to edit an existing comment."""
|
"""Form to edit an existing comment."""
|
||||||
ignoreContext = True
|
ignoreContext = True
|
||||||
id = "edit-comment-form"
|
id = 'edit-comment-form'
|
||||||
label = _(u'edit_comment_form_title', default=u'Edit comment')
|
label = _(u'edit_comment_form_title', default=u'Edit comment')
|
||||||
|
|
||||||
def updateWidgets(self):
|
def updateWidgets(self):
|
||||||
@ -71,8 +72,8 @@ class EditCommentForm(CommentForm):
|
|||||||
target = portal_state.portal_url()
|
target = portal_state.portal_url()
|
||||||
self.request.response.redirect(target)
|
self.request.response.redirect(target)
|
||||||
|
|
||||||
@button.buttonAndHandler(_(u"edit_comment_form_button",
|
@button.buttonAndHandler(_(u'edit_comment_form_button',
|
||||||
default=u"Edit comment"), name='comment')
|
default=u'Edit comment'), name='comment')
|
||||||
def handleComment(self, action):
|
def handleComment(self, action):
|
||||||
|
|
||||||
# Validate form
|
# Validate form
|
||||||
@ -93,10 +94,10 @@ class EditCommentForm(CommentForm):
|
|||||||
|
|
||||||
# Redirect to comment
|
# Redirect to comment
|
||||||
IStatusMessage(self.request).add(_(u'comment_edit_notification',
|
IStatusMessage(self.request).add(_(u'comment_edit_notification',
|
||||||
default="Comment was edited"),
|
default='Comment was edited'),
|
||||||
type='info')
|
type='info')
|
||||||
return self._redirect(
|
return self._redirect(
|
||||||
target=self.action.replace("@@edit-comment", "@@view"))
|
target=self.action.replace('@@edit-comment', '@@view'))
|
||||||
|
|
||||||
@button.buttonAndHandler(_(u'cancel_form_button',
|
@button.buttonAndHandler(_(u'cancel_form_button',
|
||||||
default=u'Cancel'), name='cancel')
|
default=u'Cancel'), name='cancel')
|
||||||
|
@ -2,11 +2,8 @@
|
|||||||
from AccessControl import getSecurityManager
|
from AccessControl import getSecurityManager
|
||||||
from AccessControl import Unauthorized
|
from AccessControl import Unauthorized
|
||||||
from Acquisition import aq_inner
|
from Acquisition import aq_inner
|
||||||
from DateTime import DateTime
|
|
||||||
from Products.CMFCore.utils import getToolByName
|
|
||||||
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
|
|
||||||
from Products.statusmessages.interfaces import IStatusMessage
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from DateTime import DateTime
|
||||||
from plone.app.discussion import _
|
from plone.app.discussion import _
|
||||||
from plone.app.discussion.browser.validator import CaptchaValidator
|
from plone.app.discussion.browser.validator import CaptchaValidator
|
||||||
from plone.app.discussion.interfaces import ICaptcha
|
from plone.app.discussion.interfaces import ICaptcha
|
||||||
@ -19,6 +16,9 @@ from plone.registry.interfaces import IRegistry
|
|||||||
from plone.z3cform import z2
|
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.Five.browser.pagetemplatefile import ViewPageTemplateFile
|
||||||
|
from Products.statusmessages.interfaces import IStatusMessage
|
||||||
from urllib import quote as url_quote
|
from urllib import quote as url_quote
|
||||||
from z3c.form import button
|
from z3c.form import button
|
||||||
from z3c.form import field
|
from z3c.form import field
|
||||||
@ -34,32 +34,36 @@ from zope.interface import alsoProvides
|
|||||||
|
|
||||||
|
|
||||||
COMMENT_DESCRIPTION_PLAIN_TEXT = _(
|
COMMENT_DESCRIPTION_PLAIN_TEXT = _(
|
||||||
u"comment_description_plain_text",
|
u'comment_description_plain_text',
|
||||||
default=u"You can add a comment by filling out the form below. " +
|
default=u'You can add a comment by filling out the form below. '
|
||||||
u"Plain text formatting.")
|
u'Plain text formatting.'
|
||||||
|
)
|
||||||
|
|
||||||
COMMENT_DESCRIPTION_MARKDOWN = _(
|
COMMENT_DESCRIPTION_MARKDOWN = _(
|
||||||
u"comment_description_markdown",
|
u'comment_description_markdown',
|
||||||
default=u"You can add a comment by filling out the form below. " +
|
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'Plain text formatting. You can use the Markdown syntax for '
|
||||||
u"links and images.")
|
u'links and images.'
|
||||||
|
)
|
||||||
|
|
||||||
COMMENT_DESCRIPTION_INTELLIGENT_TEXT = _(
|
COMMENT_DESCRIPTION_INTELLIGENT_TEXT = _(
|
||||||
u"comment_description_intelligent_text",
|
u'comment_description_intelligent_text',
|
||||||
default=u"You can add a comment by filling out the form below. " +
|
default=u'You can add a comment by filling out the form below. '
|
||||||
u"Plain text formatting. Web and email addresses are " +
|
u'Plain text formatting. Web and email addresses are '
|
||||||
u"transformed into clickable links.")
|
u'transformed into clickable links.'
|
||||||
|
)
|
||||||
|
|
||||||
COMMENT_DESCRIPTION_MODERATION_ENABLED = _(
|
COMMENT_DESCRIPTION_MODERATION_ENABLED = _(
|
||||||
u"comment_description_moderation_enabled",
|
u'comment_description_moderation_enabled',
|
||||||
default=u"Comments are moderated.")
|
default=u'Comments are moderated.'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class CommentForm(extensible.ExtensibleForm, form.Form):
|
class CommentForm(extensible.ExtensibleForm, form.Form):
|
||||||
|
|
||||||
ignoreContext = True # don't use context to get widget data
|
ignoreContext = True # don't use context to get widget data
|
||||||
id = None
|
id = None
|
||||||
label = _(u"Add a comment")
|
label = _(u'Add a comment')
|
||||||
fields = field.Fields(IComment).omit('portal_type',
|
fields = field.Fields(IComment).omit('portal_type',
|
||||||
'__parent__',
|
'__parent__',
|
||||||
'__name__',
|
'__name__',
|
||||||
@ -81,13 +85,13 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
|
|||||||
|
|
||||||
# Widgets
|
# Widgets
|
||||||
self.widgets['in_reply_to'].mode = interfaces.HIDDEN_MODE
|
self.widgets['in_reply_to'].mode = interfaces.HIDDEN_MODE
|
||||||
self.widgets['text'].addClass("autoresize")
|
self.widgets['text'].addClass('autoresize')
|
||||||
self.widgets['user_notification'].label = _(u"")
|
self.widgets['user_notification'].label = _(u'')
|
||||||
|
|
||||||
# Rename the id of the text widgets because there can be css-id
|
# Rename the id of the text widgets because there can be css-id
|
||||||
# clashes with the text field of documents when using and overlay
|
# clashes with the text field of documents when using and overlay
|
||||||
# with TinyMCE.
|
# with TinyMCE.
|
||||||
self.widgets['text'].id = "form-widgets-comment-text"
|
self.widgets['text'].id = 'form-widgets-comment-text'
|
||||||
|
|
||||||
# Anonymous / Logged-in
|
# Anonymous / Logged-in
|
||||||
mtool = getToolByName(self.context, 'portal_membership')
|
mtool = getToolByName(self.context, 'portal_membership')
|
||||||
@ -99,7 +103,7 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
|
|||||||
if anon:
|
if anon:
|
||||||
if settings.anonymous_email_enabled:
|
if settings.anonymous_email_enabled:
|
||||||
# according to IDiscussionSettings.anonymous_email_enabled:
|
# according to IDiscussionSettings.anonymous_email_enabled:
|
||||||
# "If selected, anonymous user will have to give their email."
|
# 'If selected, anonymous user will have to give their email.'
|
||||||
self.widgets['author_email'].field.required = True
|
self.widgets['author_email'].field.required = True
|
||||||
self.widgets['author_email'].required = True
|
self.widgets['author_email'].required = True
|
||||||
else:
|
else:
|
||||||
@ -121,11 +125,11 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
|
|||||||
|
|
||||||
def updateActions(self):
|
def updateActions(self):
|
||||||
super(CommentForm, self).updateActions()
|
super(CommentForm, self).updateActions()
|
||||||
self.actions['cancel'].addClass("standalone")
|
self.actions['cancel'].addClass('standalone')
|
||||||
self.actions['cancel'].addClass("hide")
|
self.actions['cancel'].addClass('hide')
|
||||||
self.actions['comment'].addClass("context")
|
self.actions['comment'].addClass('context')
|
||||||
|
|
||||||
@button.buttonAndHandler(_(u"add_comment_button", default=u"Comment"),
|
@button.buttonAndHandler(_(u'add_comment_button', default=u'Comment'),
|
||||||
name='comment')
|
name='comment')
|
||||||
def handleComment(self, action):
|
def handleComment(self, action):
|
||||||
context = aq_inner(self.context)
|
context = aq_inner(self.context)
|
||||||
@ -134,8 +138,9 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
|
|||||||
if not self.__parent__.restrictedTraverse(
|
if not self.__parent__.restrictedTraverse(
|
||||||
'@@conversation_view'
|
'@@conversation_view'
|
||||||
).enabled():
|
).enabled():
|
||||||
raise Unauthorized("Discussion is not enabled for this content "
|
raise Unauthorized(
|
||||||
"object.")
|
'Discussion is not enabled for this content object.'
|
||||||
|
)
|
||||||
|
|
||||||
# Validation form
|
# Validation form
|
||||||
data, errors = self.extractData()
|
data, errors = self.extractData()
|
||||||
@ -151,7 +156,7 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
|
|||||||
anon = portal_membership.isAnonymousUser()
|
anon = portal_membership.isAnonymousUser()
|
||||||
if captcha_enabled and anonymous_comments and anon:
|
if captcha_enabled and anonymous_comments and anon:
|
||||||
if 'captcha' not in data:
|
if 'captcha' not in data:
|
||||||
data['captcha'] = u""
|
data['captcha'] = u''
|
||||||
captcha = CaptchaValidator(self.context,
|
captcha = CaptchaValidator(self.context,
|
||||||
self.request,
|
self.request,
|
||||||
None,
|
None,
|
||||||
@ -160,7 +165,7 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
|
|||||||
captcha.validate(data['captcha'])
|
captcha.validate(data['captcha'])
|
||||||
|
|
||||||
# some attributes are not always set
|
# some attributes are not always set
|
||||||
author_name = u""
|
author_name = u''
|
||||||
|
|
||||||
# Create comment
|
# Create comment
|
||||||
comment = createObject('plone.Comment')
|
comment = createObject('plone.Comment')
|
||||||
@ -207,14 +212,14 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
|
|||||||
if email and isinstance(email, str):
|
if email and isinstance(email, str):
|
||||||
email = unicode(email, 'utf-8')
|
email = unicode(email, 'utf-8')
|
||||||
comment.changeOwnership(user, recursive=False)
|
comment.changeOwnership(user, recursive=False)
|
||||||
comment.manage_setLocalRoles(memberid, ["Owner"])
|
comment.manage_setLocalRoles(memberid, ['Owner'])
|
||||||
comment.creator = memberid
|
comment.creator = memberid
|
||||||
comment.author_username = memberid
|
comment.author_username = memberid
|
||||||
comment.author_name = fullname
|
comment.author_name = fullname
|
||||||
|
|
||||||
# XXX: according to IComment interface author_email must not be
|
# XXX: according to IComment interface author_email must not be
|
||||||
# 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'
|
||||||
comment.author_email = email
|
comment.author_email = email
|
||||||
# /XXX
|
# /XXX
|
||||||
|
|
||||||
@ -223,8 +228,8 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
|
|||||||
|
|
||||||
else: # pragma: no cover
|
else: # pragma: no cover
|
||||||
raise Unauthorized(
|
raise Unauthorized(
|
||||||
u"Anonymous user tries to post a comment, but anonymous "
|
u'Anonymous user tries to post a comment, but anonymous '
|
||||||
u"commenting is disabled. Or user does not have the "
|
u'commenting is disabled. Or user does not have the '
|
||||||
u"'reply to item' permission."
|
u"'reply to item' permission."
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -255,14 +260,14 @@ class CommentForm(extensible.ExtensibleForm, form.Form):
|
|||||||
if comment_review_state == 'pending' and not can_review:
|
if comment_review_state == 'pending' and not can_review:
|
||||||
# Show info message when comment moderation is enabled
|
# Show info message when comment moderation is enabled
|
||||||
IStatusMessage(self.context.REQUEST).addStatusMessage(
|
IStatusMessage(self.context.REQUEST).addStatusMessage(
|
||||||
_("Your comment awaits moderator approval."),
|
_('Your comment awaits moderator approval.'),
|
||||||
type="info")
|
type='info')
|
||||||
self.request.response.redirect(self.action)
|
self.request.response.redirect(self.action)
|
||||||
else:
|
else:
|
||||||
# Redirect to comment (inside a content object page)
|
# Redirect to comment (inside a content object page)
|
||||||
self.request.response.redirect(self.action + '#' + str(comment_id))
|
self.request.response.redirect(self.action + '#' + str(comment_id))
|
||||||
|
|
||||||
@button.buttonAndHandler(_(u"Cancel"))
|
@button.buttonAndHandler(_(u'Cancel'))
|
||||||
def handleCancel(self, action):
|
def handleCancel(self, action):
|
||||||
# This method should never be called, it's only there to show
|
# This method should never be called, it's only there to show
|
||||||
# a cancel button that is handled by a jQuery method.
|
# a cancel button that is handled by a jQuery method.
|
||||||
@ -278,9 +283,9 @@ class CommentsViewlet(ViewletBase):
|
|||||||
super(CommentsViewlet, self).update()
|
super(CommentsViewlet, self).update()
|
||||||
discussion_allowed = self.is_discussion_allowed()
|
discussion_allowed = self.is_discussion_allowed()
|
||||||
anonymous_allowed_or_can_reply = (
|
anonymous_allowed_or_can_reply = (
|
||||||
self.is_anonymous()
|
self.is_anonymous() and
|
||||||
and self.anonymous_discussion_allowed()
|
self.anonymous_discussion_allowed() or
|
||||||
or self.can_reply()
|
self.can_reply()
|
||||||
)
|
)
|
||||||
if discussion_allowed and anonymous_allowed_or_can_reply:
|
if discussion_allowed and anonymous_allowed_or_can_reply:
|
||||||
z2.switch_on(self, request_layer=IFormLayer)
|
z2.switch_on(self, request_layer=IFormLayer)
|
||||||
@ -356,10 +361,10 @@ class CommentsViewlet(ViewletBase):
|
|||||||
settings = registry.forInterface(IDiscussionSettings, check=False)
|
settings = registry.forInterface(IDiscussionSettings, check=False)
|
||||||
|
|
||||||
# text transform setting
|
# text transform setting
|
||||||
if settings.text_transform == "text/x-web-intelligent":
|
if settings.text_transform == 'text/x-web-intelligent':
|
||||||
message = translate(Message(COMMENT_DESCRIPTION_INTELLIGENT_TEXT),
|
message = translate(Message(COMMENT_DESCRIPTION_INTELLIGENT_TEXT),
|
||||||
context=self.request)
|
context=self.request)
|
||||||
elif settings.text_transform == "text/x-web-markdown":
|
elif settings.text_transform == 'text/x-web-markdown':
|
||||||
message = translate(Message(COMMENT_DESCRIPTION_MARKDOWN),
|
message = translate(Message(COMMENT_DESCRIPTION_MARKDOWN),
|
||||||
context=self.request)
|
context=self.request)
|
||||||
else:
|
else:
|
||||||
@ -367,7 +372,7 @@ class CommentsViewlet(ViewletBase):
|
|||||||
context=self.request)
|
context=self.request)
|
||||||
|
|
||||||
# comment workflow
|
# comment workflow
|
||||||
wftool = getToolByName(context, "portal_workflow", None)
|
wftool = getToolByName(context, 'portal_workflow', None)
|
||||||
workflow_chain = wftool.getChainForPortalType('Discussion Item')
|
workflow_chain = wftool.getChainForPortalType('Discussion Item')
|
||||||
if workflow_chain:
|
if workflow_chain:
|
||||||
comment_workflow = workflow_chain[0]
|
comment_workflow = workflow_chain[0]
|
||||||
@ -375,7 +380,7 @@ class CommentsViewlet(ViewletBase):
|
|||||||
# check if the current workflow implements a pending state. If this
|
# check if the current workflow implements a pending state. If this
|
||||||
# is true comments are moderated
|
# is true comments are moderated
|
||||||
if 'pending' in comment_workflow.states:
|
if 'pending' in comment_workflow.states:
|
||||||
message = message + " " + \
|
message = message + ' ' + \
|
||||||
translate(Message(COMMENT_DESCRIPTION_MODERATION_ENABLED),
|
translate(Message(COMMENT_DESCRIPTION_MODERATION_ENABLED),
|
||||||
context=self.request)
|
context=self.request)
|
||||||
|
|
||||||
@ -445,7 +450,7 @@ class CommentsViewlet(ViewletBase):
|
|||||||
if username is None:
|
if username is None:
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
return "%s/author/%s" % (self.context.portal_url(), username)
|
return '{0}/author/{1}'.format(self.context.portal_url(), username)
|
||||||
|
|
||||||
def get_commenter_portrait(self, username=None):
|
def get_commenter_portrait(self, username=None):
|
||||||
|
|
||||||
@ -491,9 +496,10 @@ class CommentsViewlet(ViewletBase):
|
|||||||
return portal_membership.isAnonymousUser()
|
return portal_membership.isAnonymousUser()
|
||||||
|
|
||||||
def login_action(self):
|
def login_action(self):
|
||||||
return '%s/login_form?came_from=%s' % \
|
return '{0}/login_form?came_from={1}'.format(
|
||||||
(self.navigation_root_url,
|
self.navigation_root_url,
|
||||||
url_quote(self.request.get('URL', '')),)
|
url_quote(self.request.get('URL', '')),
|
||||||
|
)
|
||||||
|
|
||||||
def format_time(self, time):
|
def format_time(self, time):
|
||||||
# We have to transform Python datetime into Zope DateTime
|
# We have to transform Python datetime into Zope DateTime
|
||||||
|
@ -1,9 +1,4 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from Products.CMFCore.interfaces._content import IDiscussionResponse
|
|
||||||
from Products.CMFCore.utils import getToolByName
|
|
||||||
from Products.CMFPlone.interfaces.controlpanel import IMailSchema
|
|
||||||
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
|
|
||||||
from Products.statusmessages.interfaces import IStatusMessage
|
|
||||||
from plone.app.controlpanel.interfaces import IConfigurationChangedEvent
|
from plone.app.controlpanel.interfaces import IConfigurationChangedEvent
|
||||||
from plone.app.discussion.interfaces import _
|
from plone.app.discussion.interfaces import _
|
||||||
from plone.app.discussion.interfaces import IDiscussionSettings
|
from plone.app.discussion.interfaces import IDiscussionSettings
|
||||||
@ -11,6 +6,11 @@ from plone.app.discussion.upgrades import update_registry
|
|||||||
from plone.app.registry.browser import controlpanel
|
from plone.app.registry.browser import controlpanel
|
||||||
from plone.registry.interfaces import IRecordModifiedEvent
|
from plone.registry.interfaces import IRecordModifiedEvent
|
||||||
from plone.registry.interfaces import IRegistry
|
from plone.registry.interfaces import IRegistry
|
||||||
|
from Products.CMFCore.interfaces._content import IDiscussionResponse
|
||||||
|
from Products.CMFCore.utils import getToolByName
|
||||||
|
from Products.CMFPlone.interfaces.controlpanel import IMailSchema
|
||||||
|
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
|
||||||
|
from Products.statusmessages.interfaces import IStatusMessage
|
||||||
from z3c.form import button
|
from z3c.form import button
|
||||||
from z3c.form.browser.checkbox import SingleCheckBoxFieldWidget
|
from z3c.form.browser.checkbox import SingleCheckBoxFieldWidget
|
||||||
from zope.component import getMultiAdapter
|
from zope.component import getMultiAdapter
|
||||||
@ -23,19 +23,19 @@ class DiscussionSettingsEditForm(controlpanel.RegistryEditForm):
|
|||||||
"""Discussion settings form.
|
"""Discussion settings form.
|
||||||
"""
|
"""
|
||||||
schema = IDiscussionSettings
|
schema = IDiscussionSettings
|
||||||
id = "DiscussionSettingsEditForm"
|
id = 'DiscussionSettingsEditForm'
|
||||||
label = _(u"Discussion settings")
|
label = _(u'Discussion settings')
|
||||||
description = _(
|
description = _(
|
||||||
u"help_discussion_settings_editform",
|
u'help_discussion_settings_editform',
|
||||||
default=u"Some discussion related settings are not "
|
default=u'Some discussion related settings are not '
|
||||||
u"located in the Discussion Control Panel.\n"
|
u'located in the Discussion Control Panel.\n'
|
||||||
u"To enable comments for a specific content type, "
|
u'To enable comments for a specific content type, '
|
||||||
u"go to the Types Control Panel of this type and "
|
u'go to the Types Control Panel of this type and '
|
||||||
u"choose \"Allow comments\".\n"
|
u'choose "Allow comments".\n'
|
||||||
u"To enable the moderation workflow for comments, "
|
u'To enable the moderation workflow for comments, '
|
||||||
u"go to the Types Control Panel, choose "
|
u'go to the Types Control Panel, choose '
|
||||||
u"\"Comment\" and set workflow to "
|
u'"Comment" and set workflow to '
|
||||||
u"\"Comment Review Workflow\"."
|
u'"Comment Review Workflow".'
|
||||||
)
|
)
|
||||||
|
|
||||||
def updateFields(self):
|
def updateFields(self):
|
||||||
@ -65,13 +65,15 @@ class DiscussionSettingsEditForm(controlpanel.RegistryEditForm):
|
|||||||
# provide auto-upgrade
|
# provide auto-upgrade
|
||||||
update_registry(self.context)
|
update_registry(self.context)
|
||||||
super(DiscussionSettingsEditForm, self).updateWidgets()
|
super(DiscussionSettingsEditForm, self).updateWidgets()
|
||||||
self.widgets['globally_enabled'].label = _(u"Enable Comments")
|
self.widgets['globally_enabled'].label = _(u'Enable Comments')
|
||||||
self.widgets['anonymous_comments'].label = _(u"Anonymous Comments")
|
self.widgets['anonymous_comments'].label = _(u'Anonymous Comments')
|
||||||
self.widgets['show_commenter_image'].label = _(u"Commenter Image")
|
self.widgets['show_commenter_image'].label = _(u'Commenter Image')
|
||||||
self.widgets['moderator_notification_enabled'].label = \
|
self.widgets['moderator_notification_enabled'].label = _(
|
||||||
_(u"Moderator Email Notification")
|
u'Moderator Email Notification'
|
||||||
self.widgets['user_notification_enabled'].label = \
|
)
|
||||||
_(u"User Email Notification")
|
self.widgets['user_notification_enabled'].label = _(
|
||||||
|
u'User Email Notification'
|
||||||
|
)
|
||||||
|
|
||||||
@button.buttonAndHandler(_('Save'), name=None)
|
@button.buttonAndHandler(_('Save'), name=None)
|
||||||
def handleSave(self, action):
|
def handleSave(self, action):
|
||||||
@ -80,16 +82,20 @@ class DiscussionSettingsEditForm(controlpanel.RegistryEditForm):
|
|||||||
self.status = self.formErrorsMessage
|
self.status = self.formErrorsMessage
|
||||||
return
|
return
|
||||||
self.applyChanges(data)
|
self.applyChanges(data)
|
||||||
IStatusMessage(self.request).addStatusMessage(_(u"Changes saved"),
|
IStatusMessage(self.request).addStatusMessage(_(u'Changes saved'),
|
||||||
"info")
|
'info')
|
||||||
self.context.REQUEST.RESPONSE.redirect("@@discussion-controlpanel")
|
self.context.REQUEST.RESPONSE.redirect('@@discussion-controlpanel')
|
||||||
|
|
||||||
@button.buttonAndHandler(_('Cancel'), name='cancel')
|
@button.buttonAndHandler(_('Cancel'), name='cancel')
|
||||||
def handleCancel(self, action):
|
def handleCancel(self, action):
|
||||||
IStatusMessage(self.request).addStatusMessage(_(u"Edit cancelled"),
|
IStatusMessage(self.request).addStatusMessage(_(u'Edit cancelled'),
|
||||||
"info")
|
'info')
|
||||||
self.request.response.redirect("%s/%s" % (self.context.absolute_url(),
|
self.request.response.redirect(
|
||||||
self.control_panel_view))
|
'{0}/{1}'.format(
|
||||||
|
self.context.absolute_url(),
|
||||||
|
self.control_panel_view
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class DiscussionSettingsControlPanel(controlpanel.ControlPanelFormWrapper):
|
class DiscussionSettingsControlPanel(controlpanel.ControlPanelFormWrapper):
|
||||||
@ -104,38 +110,38 @@ class DiscussionSettingsControlPanel(controlpanel.ControlPanelFormWrapper):
|
|||||||
"""
|
"""
|
||||||
registry = queryUtility(IRegistry)
|
registry = queryUtility(IRegistry)
|
||||||
settings = registry.forInterface(IDiscussionSettings, check=False)
|
settings = registry.forInterface(IDiscussionSettings, check=False)
|
||||||
wftool = getToolByName(self.context, "portal_workflow", None)
|
wftool = getToolByName(self.context, 'portal_workflow', None)
|
||||||
workflow_chain = wftool.getChainForPortalType('Discussion Item')
|
workflow_chain = wftool.getChainForPortalType('Discussion Item')
|
||||||
output = []
|
output = []
|
||||||
|
|
||||||
# Globally enabled
|
# Globally enabled
|
||||||
if settings.globally_enabled:
|
if settings.globally_enabled:
|
||||||
output.append("globally_enabled")
|
output.append('globally_enabled')
|
||||||
|
|
||||||
# Comment moderation
|
# Comment moderation
|
||||||
one_state_worklow_disabled = 'one_state_workflow' not in workflow_chain
|
one_state_worklow_disabled = 'one_state_workflow' not in workflow_chain
|
||||||
comment_review_workflow_disabled = \
|
comment_review_workflow_disabled = \
|
||||||
'comment_review_workflow' not in workflow_chain
|
'comment_review_workflow' not in workflow_chain
|
||||||
if one_state_worklow_disabled and comment_review_workflow_disabled:
|
if one_state_worklow_disabled and comment_review_workflow_disabled:
|
||||||
output.append("moderation_custom")
|
output.append('moderation_custom')
|
||||||
elif settings.moderation_enabled:
|
elif settings.moderation_enabled:
|
||||||
output.append("moderation_enabled")
|
output.append('moderation_enabled')
|
||||||
|
|
||||||
if settings.edit_comment_enabled:
|
if settings.edit_comment_enabled:
|
||||||
output.append("edit_comment_enabled")
|
output.append('edit_comment_enabled')
|
||||||
|
|
||||||
if settings.delete_own_comment_enabled:
|
if settings.delete_own_comment_enabled:
|
||||||
output.append("delte_own_comment_enabled")
|
output.append('delete_own_comment_enabled')
|
||||||
|
|
||||||
# Anonymous comments
|
# Anonymous comments
|
||||||
if settings.anonymous_comments:
|
if settings.anonymous_comments:
|
||||||
output.append("anonymous_comments")
|
output.append('anonymous_comments')
|
||||||
|
|
||||||
# Invalid mail setting
|
# Invalid mail setting
|
||||||
ctrlOverview = getMultiAdapter((self.context, self.request),
|
ctrlOverview = getMultiAdapter((self.context, self.request),
|
||||||
name='overview-controlpanel')
|
name='overview-controlpanel')
|
||||||
if ctrlOverview.mailhost_warning():
|
if ctrlOverview.mailhost_warning():
|
||||||
output.append("invalid_mail_setup")
|
output.append('invalid_mail_setup')
|
||||||
|
|
||||||
# Workflow
|
# Workflow
|
||||||
wftool = getToolByName(self.context, 'portal_workflow', None)
|
wftool = getToolByName(self.context, 'portal_workflow', None)
|
||||||
@ -161,7 +167,7 @@ class DiscussionSettingsControlPanel(controlpanel.ControlPanelFormWrapper):
|
|||||||
def custom_comment_workflow_warning(self):
|
def custom_comment_workflow_warning(self):
|
||||||
"""Returns a warning string if a custom comment workflow is enabled.
|
"""Returns a warning string if a custom comment workflow is enabled.
|
||||||
"""
|
"""
|
||||||
wftool = getToolByName(self.context, "portal_workflow", None)
|
wftool = getToolByName(self.context, 'portal_workflow', None)
|
||||||
workflow_chain = wftool.getChainForPortalType('Discussion Item')
|
workflow_chain = wftool.getChainForPortalType('Discussion Item')
|
||||||
one_state_workflow_enabled = 'one_state_workflow' in workflow_chain
|
one_state_workflow_enabled = 'one_state_workflow' in workflow_chain
|
||||||
comment_review_workflow_enabled = \
|
comment_review_workflow_enabled = \
|
||||||
|
@ -1,18 +1,20 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
from Acquisition import aq_base
|
from Acquisition import aq_base
|
||||||
from Acquisition import aq_chain
|
from Acquisition import aq_chain
|
||||||
from Acquisition import aq_inner
|
from Acquisition import aq_inner
|
||||||
|
from plone.app.discussion.interfaces import IDiscussionSettings
|
||||||
|
from plone.registry.interfaces import IRegistry
|
||||||
from Products.CMFCore.interfaces import IFolderish
|
from Products.CMFCore.interfaces import IFolderish
|
||||||
from Products.CMFCore.utils import getToolByName
|
from Products.CMFCore.utils import getToolByName
|
||||||
from Products.CMFPlone.interfaces import INonStructuralFolder
|
from Products.CMFPlone.interfaces import INonStructuralFolder
|
||||||
from Products.CMFPlone.interfaces import IPloneSiteRoot
|
from Products.CMFPlone.interfaces import IPloneSiteRoot
|
||||||
from plone.app.discussion.interfaces import IDiscussionSettings
|
|
||||||
from plone.registry.interfaces import IRegistry
|
|
||||||
from zope.component import queryUtility
|
from zope.component import queryUtility
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from plone.dexterity.interfaces import IDexterityContent
|
from plone.dexterity.interfaces import IDexterityContent
|
||||||
DEXTERITY_INSTALLED = True
|
DEXTERITY_INSTALLED = True
|
||||||
except:
|
except ImportError:
|
||||||
DEXTERITY_INSTALLED = False
|
DEXTERITY_INSTALLED = False
|
||||||
|
|
||||||
|
|
||||||
@ -130,7 +132,7 @@ class ConversationView(object):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
# Check if discussion is allowed on the content object
|
# Check if discussion is allowed on the content object
|
||||||
if hasattr(context, "allow_discussion"):
|
if hasattr(context, 'allow_discussion'):
|
||||||
if context.allow_discussion is not None:
|
if context.allow_discussion is not None:
|
||||||
return context.allow_discussion
|
return context.allow_discussion
|
||||||
|
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
from Acquisition import aq_inner
|
from Acquisition import aq_inner
|
||||||
from Acquisition import aq_parent
|
from Acquisition import aq_parent
|
||||||
|
from datetime import datetime
|
||||||
from DateTime import DateTime
|
from DateTime import DateTime
|
||||||
|
from plone.app.discussion.comment import CommentFactory
|
||||||
|
from plone.app.discussion.interfaces import IComment
|
||||||
|
from plone.app.discussion.interfaces import IConversation
|
||||||
|
from plone.app.discussion.interfaces import IReplies
|
||||||
from Products.CMFCore.interfaces._content import IDiscussionResponse
|
from Products.CMFCore.interfaces._content import IDiscussionResponse
|
||||||
from Products.CMFCore.utils import getToolByName
|
from Products.CMFCore.utils import getToolByName
|
||||||
from Products.Five.browser import BrowserView
|
from Products.Five.browser import BrowserView
|
||||||
from datetime import datetime
|
|
||||||
from plone.app.discussion.comment import CommentFactory
|
|
||||||
from plone.app.discussion.interfaces import IConversation, IReplies, IComment
|
|
||||||
from types import TupleType
|
from types import TupleType
|
||||||
|
|
||||||
import transaction
|
import transaction
|
||||||
@ -35,11 +38,11 @@ class View(BrowserView):
|
|||||||
self.total_comments_migrated = 0
|
self.total_comments_migrated = 0
|
||||||
self.total_comments_deleted = 0
|
self.total_comments_deleted = 0
|
||||||
|
|
||||||
dry_run = "dry_run" in self.request
|
dry_run = 'dry_run' in self.request
|
||||||
|
|
||||||
# This is for testing only.
|
# This is for testing only.
|
||||||
# Do not use transactions during a test.
|
# Do not use transactions during a test.
|
||||||
test = "test" in self.request
|
test = 'test' in self.request
|
||||||
|
|
||||||
if not test:
|
if not test:
|
||||||
transaction.begin() # pragma: no cover
|
transaction.begin() # pragma: no cover
|
||||||
@ -71,10 +74,10 @@ class View(BrowserView):
|
|||||||
|
|
||||||
for reply in replies:
|
for reply in replies:
|
||||||
# log
|
# log
|
||||||
indent = " "
|
indent = ' '
|
||||||
for i in range(depth):
|
for i in range(depth):
|
||||||
indent += " "
|
indent += ' '
|
||||||
log("%smigrate_reply: '%s'." % (indent, reply.title))
|
log('{0}migrate_reply: "{1}".'.format(indent, reply.title))
|
||||||
|
|
||||||
should_migrate = True
|
should_migrate = True
|
||||||
if filter_callback and not filter_callback(reply):
|
if filter_callback and not filter_callback(reply):
|
||||||
@ -172,7 +175,7 @@ class View(BrowserView):
|
|||||||
talkback.deleteReply(reply.id)
|
talkback.deleteReply(reply.id)
|
||||||
obj = aq_parent(talkback)
|
obj = aq_parent(talkback)
|
||||||
obj.talkback = None
|
obj.talkback = None
|
||||||
log("%sremove %s" % (indent, reply.id))
|
log('{0}remove {1}'.format(indent, reply.id))
|
||||||
self.total_comments_deleted += 1
|
self.total_comments_deleted += 1
|
||||||
|
|
||||||
# Return True when all comments on a certain level have been
|
# Return True when all comments on a certain level have been
|
||||||
@ -182,7 +185,7 @@ class View(BrowserView):
|
|||||||
# Find content
|
# Find content
|
||||||
brains = catalog.searchResults(
|
brains = catalog.searchResults(
|
||||||
object_provides='Products.CMFCore.interfaces._content.IContentish')
|
object_provides='Products.CMFCore.interfaces._content.IContentish')
|
||||||
log("Found %s content objects." % len(brains))
|
log('Found {0} content objects.'.format(len(brains)))
|
||||||
|
|
||||||
count_discussion_items = len(
|
count_discussion_items = len(
|
||||||
catalog.searchResults(Type='Discussion Item')
|
catalog.searchResults(Type='Discussion Item')
|
||||||
@ -196,12 +199,20 @@ class View(BrowserView):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
log("Found %s Discussion Item objects." % count_discussion_items)
|
log(
|
||||||
log("Found %s old discussion items." % count_comments_old)
|
'Found {0} Discussion Item objects.'.format(
|
||||||
log("Found %s plone.app.discussion comments." % count_comments_pad)
|
count_discussion_items
|
||||||
|
)
|
||||||
|
)
|
||||||
|
log('Found {0} old discussion items.'.format(count_comments_old))
|
||||||
|
log(
|
||||||
|
'Found {0} plone.app.discussion comments.'.format(
|
||||||
|
count_comments_pad
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
log("\n")
|
log('\n')
|
||||||
log("Start comment migration.")
|
log('Start comment migration.')
|
||||||
|
|
||||||
# This loop is necessary to get all contentish objects, but not
|
# This loop is necessary to get all contentish objects, but not
|
||||||
# the Discussion Items. This wouldn't be necessary if the
|
# the Discussion Items. This wouldn't be necessary if the
|
||||||
@ -219,46 +230,54 @@ class View(BrowserView):
|
|||||||
replies = talkback.getReplies()
|
replies = talkback.getReplies()
|
||||||
if replies:
|
if replies:
|
||||||
conversation = IConversation(obj)
|
conversation = IConversation(obj)
|
||||||
log("\n")
|
log('\n')
|
||||||
log("Migrate '%s' (%s)" % (obj.Title(),
|
log(
|
||||||
obj.absolute_url(relative=1)))
|
'Migrate "{0}" ({1})'.format(
|
||||||
|
obj.Title(),
|
||||||
|
obj.absolute_url(relative=1)
|
||||||
|
)
|
||||||
|
)
|
||||||
migrate_replies(context, 0, replies)
|
migrate_replies(context, 0, replies)
|
||||||
obj = aq_parent(talkback)
|
obj = aq_parent(talkback)
|
||||||
obj.talkback = None
|
obj.talkback = None
|
||||||
|
|
||||||
if self.total_comments_deleted != self.total_comments_migrated:
|
if self.total_comments_deleted != self.total_comments_migrated:
|
||||||
log(
|
log(
|
||||||
"Something went wrong during migration. The number of " +
|
'Something went wrong during migration. The number of '
|
||||||
"migrated comments (%s) differs from the number of deleted " +
|
'migrated comments ({0}) differs from the number of deleted '
|
||||||
"comments (%s)." % (
|
'comments ({1}).'.format(
|
||||||
self.total_comments_migrated,
|
self.total_comments_migrated,
|
||||||
self.total_comments_deleted
|
self.total_comments_deleted
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if not test: # pragma: no cover
|
if not test: # pragma: no cover
|
||||||
transaction.abort() # pragma: no cover
|
transaction.abort() # pragma: no cover
|
||||||
log("Abort transaction") # pragma: no cover
|
log('Abort transaction') # pragma: no cover
|
||||||
|
|
||||||
log("\n")
|
log('\n')
|
||||||
log("Comment migration finished.")
|
log('Comment migration finished.')
|
||||||
log("\n")
|
log('\n')
|
||||||
|
|
||||||
log("%s of %s comments migrated."
|
log(
|
||||||
% (self.total_comments_migrated, count_comments_old))
|
'{0} of {1} comments migrated.'.format(
|
||||||
|
self.total_comments_migrated,
|
||||||
|
count_comments_old
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
if self.total_comments_migrated != count_comments_old:
|
if self.total_comments_migrated != count_comments_old:
|
||||||
log(
|
log(
|
||||||
"%s comments could not be migrated." % (
|
'{0} comments could not be migrated.'.format(
|
||||||
count_comments_old - self.total_comments_migrated
|
count_comments_old - self.total_comments_migrated
|
||||||
)
|
)
|
||||||
) # pragma: no cover
|
) # pragma: no cover
|
||||||
log("Please make sure your " +
|
log('Please make sure your ' +
|
||||||
"portal catalog is up-to-date.") # pragma: no cover
|
'portal catalog is up-to-date.') # pragma: no cover
|
||||||
|
|
||||||
if dry_run and not test:
|
if dry_run and not test:
|
||||||
transaction.abort() # pragma: no cover
|
transaction.abort() # pragma: no cover
|
||||||
log("Dry run") # pragma: no cover
|
log('Dry run') # pragma: no cover
|
||||||
log("Abort transaction") # pragma: no cover
|
log('Abort transaction') # pragma: no cover
|
||||||
if not test:
|
if not test:
|
||||||
transaction.commit() # pragma: no cover
|
transaction.commit() # pragma: no cover
|
||||||
return '\n'.join(out)
|
return '\n'.join(out)
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from AccessControl import Unauthorized
|
|
||||||
from AccessControl import getSecurityManager
|
from AccessControl import getSecurityManager
|
||||||
|
from AccessControl import Unauthorized
|
||||||
from Acquisition import aq_inner
|
from Acquisition import aq_inner
|
||||||
from Acquisition import aq_parent
|
from Acquisition import aq_parent
|
||||||
|
from plone.app.discussion.interfaces import _
|
||||||
|
from plone.app.discussion.interfaces import IComment
|
||||||
|
from plone.app.discussion.interfaces import IReplies
|
||||||
from Products.CMFCore.utils import getToolByName
|
from Products.CMFCore.utils import getToolByName
|
||||||
from Products.Five.browser import BrowserView
|
from Products.Five.browser import BrowserView
|
||||||
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 plone.app.discussion.interfaces import _
|
|
||||||
from plone.app.discussion.interfaces import IComment
|
|
||||||
from plone.app.discussion.interfaces import IReplies
|
|
||||||
|
|
||||||
|
|
||||||
class View(BrowserView):
|
class View(BrowserView):
|
||||||
@ -101,8 +101,8 @@ class DeleteComment(BrowserView):
|
|||||||
del conversation[comment.id]
|
del conversation[comment.id]
|
||||||
content_object.reindexObject()
|
content_object.reindexObject()
|
||||||
IStatusMessage(self.context.REQUEST).addStatusMessage(
|
IStatusMessage(self.context.REQUEST).addStatusMessage(
|
||||||
_("Comment deleted."),
|
_('Comment deleted.'),
|
||||||
type="info")
|
type='info')
|
||||||
came_from = self.context.REQUEST.HTTP_REFERER
|
came_from = self.context.REQUEST.HTTP_REFERER
|
||||||
# if the referrer already has a came_from in it, don't redirect back
|
# if the referrer already has a came_from in it, don't redirect back
|
||||||
if len(came_from) == 0 or 'came_from=' in came_from:
|
if len(came_from) == 0 or 'came_from=' in came_from:
|
||||||
@ -133,14 +133,17 @@ class DeleteOwnComment(DeleteComment):
|
|||||||
sm = getSecurityManager()
|
sm = getSecurityManager()
|
||||||
comment = comment or aq_inner(self.context)
|
comment = comment or aq_inner(self.context)
|
||||||
userid = sm.getUser().getId()
|
userid = sm.getUser().getId()
|
||||||
return (sm.checkPermission('Delete own comments',
|
return (
|
||||||
comment)
|
sm.checkPermission('Delete own comments', comment) and
|
||||||
and 'Owner' in comment.get_local_roles_for_userid(userid))
|
'Owner' in comment.get_local_roles_for_userid(userid)
|
||||||
|
)
|
||||||
|
|
||||||
def can_delete(self, comment=None):
|
def can_delete(self, comment=None):
|
||||||
comment = comment or self.context
|
comment = comment or self.context
|
||||||
return (len(IReplies(aq_inner(comment))) == 0
|
return (
|
||||||
and self.could_delete(comment=comment))
|
len(IReplies(aq_inner(comment))) == 0 and
|
||||||
|
self.could_delete(comment=comment)
|
||||||
|
)
|
||||||
|
|
||||||
def __call__(self):
|
def __call__(self):
|
||||||
if self.can_delete():
|
if self.can_delete():
|
||||||
@ -179,8 +182,8 @@ class PublishComment(BrowserView):
|
|||||||
comment.reindexObject()
|
comment.reindexObject()
|
||||||
content_object.reindexObject(idxs=['total_comments'])
|
content_object.reindexObject(idxs=['total_comments'])
|
||||||
IStatusMessage(self.context.REQUEST).addStatusMessage(
|
IStatusMessage(self.context.REQUEST).addStatusMessage(
|
||||||
_("Comment approved."),
|
_('Comment approved.'),
|
||||||
type="info")
|
type='info')
|
||||||
came_from = self.context.REQUEST.HTTP_REFERER
|
came_from = self.context.REQUEST.HTTP_REFERER
|
||||||
# if the referrer already has a came_from in it, don't redirect back
|
# if the referrer already has a came_from in it, don't redirect back
|
||||||
if len(came_from) == 0 or 'came_from=' in came_from:
|
if len(came_from) == 0 or 'came_from=' in came_from:
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
"""Implement the ++comments++ traversal namespace. This should return the
|
"""Implement the ++comments++ traversal namespace. This should return the
|
||||||
IDiscussion container for the context, from which traversal will continue
|
IDiscussion container for the context, from which traversal will continue
|
||||||
into an actual comment object.
|
into an actual comment object.
|
||||||
@ -29,8 +30,8 @@ class ConversationNamespace(object):
|
|||||||
|
|
||||||
def traverse(self, name, ignore):
|
def traverse(self, name, ignore):
|
||||||
|
|
||||||
if name == "default":
|
if name == 'default':
|
||||||
name = u""
|
name = u''
|
||||||
|
|
||||||
conversation = queryAdapter(self.context, IConversation, name=name)
|
conversation = queryAdapter(self.context, IConversation, name=name)
|
||||||
if conversation is None:
|
if conversation is None:
|
||||||
|
@ -1,15 +1,17 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
"""Catalog indexers, using plone.indexer. These will populate standard catalog
|
"""Catalog indexers, using plone.indexer. These will populate standard catalog
|
||||||
indexes with values based on the IComment interface.
|
indexes with values based on the IComment interface.
|
||||||
|
|
||||||
Also provide event handlers to actually catalog the comments.
|
Also provide event handlers to actually catalog the comments.
|
||||||
"""
|
"""
|
||||||
from DateTime import DateTime
|
from DateTime import DateTime
|
||||||
|
from plone.app.discussion.interfaces import IComment
|
||||||
|
from plone.app.discussion.interfaces import IConversation
|
||||||
|
from plone.indexer import indexer
|
||||||
|
from plone.uuid.interfaces import IUUID
|
||||||
from Products.CMFCore.interfaces import IContentish
|
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
|
||||||
from plone.app.discussion.interfaces import IConversation, IComment
|
|
||||||
from plone.indexer import indexer
|
|
||||||
from plone.uuid.interfaces import IUUID
|
|
||||||
from string import join
|
from string import join
|
||||||
|
|
||||||
|
|
||||||
@ -78,7 +80,7 @@ def description(object):
|
|||||||
text = join(object.getText(
|
text = join(object.getText(
|
||||||
targetMimetype='text/plain').split()[:MAX_DESCRIPTION])
|
targetMimetype='text/plain').split()[:MAX_DESCRIPTION])
|
||||||
if len(object.getText().split()) > 25:
|
if len(object.getText().split()) > 25:
|
||||||
text += " [...]"
|
text += ' [...]'
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
|
||||||
|
@ -3,16 +3,9 @@
|
|||||||
"""
|
"""
|
||||||
from AccessControl import ClassSecurityInfo
|
from AccessControl import ClassSecurityInfo
|
||||||
from AccessControl.SecurityManagement import getSecurityManager
|
from AccessControl.SecurityManagement import getSecurityManager
|
||||||
from Acquisition import Implicit
|
|
||||||
from Acquisition import aq_base
|
from Acquisition import aq_base
|
||||||
from Acquisition import aq_parent
|
from Acquisition import aq_parent
|
||||||
from Products.CMFCore import permissions
|
from Acquisition import Implicit
|
||||||
from Products.CMFCore.CMFCatalogAware import CatalogAware
|
|
||||||
from Products.CMFCore.CMFCatalogAware import WorkflowAware
|
|
||||||
from Products.CMFCore.DynamicType import DynamicType
|
|
||||||
from Products.CMFCore.utils import getToolByName
|
|
||||||
from Products.CMFPlone.interfaces.controlpanel import IMailSchema
|
|
||||||
from Products.CMFPlone.utils import safe_unicode
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from OFS.owner import Owned
|
from OFS.owner import Owned
|
||||||
from OFS.role import RoleManager
|
from OFS.role import RoleManager
|
||||||
@ -27,6 +20,13 @@ from plone.app.discussion.interfaces import IComment
|
|||||||
from plone.app.discussion.interfaces import IConversation
|
from plone.app.discussion.interfaces import IConversation
|
||||||
from plone.app.discussion.interfaces import IDiscussionSettings
|
from plone.app.discussion.interfaces import IDiscussionSettings
|
||||||
from plone.registry.interfaces import IRegistry
|
from plone.registry.interfaces import IRegistry
|
||||||
|
from Products.CMFCore import permissions
|
||||||
|
from Products.CMFCore.CMFCatalogAware import CatalogAware
|
||||||
|
from Products.CMFCore.CMFCatalogAware import WorkflowAware
|
||||||
|
from Products.CMFCore.DynamicType import DynamicType
|
||||||
|
from Products.CMFCore.utils import getToolByName
|
||||||
|
from Products.CMFPlone.interfaces.controlpanel import IMailSchema
|
||||||
|
from Products.CMFPlone.utils import safe_unicode
|
||||||
from smtplib import SMTPException
|
from smtplib import SMTPException
|
||||||
from zope.annotation.interfaces import IAnnotatable
|
from zope.annotation.interfaces import IAnnotatable
|
||||||
from zope.component import getUtility
|
from zope.component import getUtility
|
||||||
@ -41,28 +41,28 @@ import logging
|
|||||||
|
|
||||||
|
|
||||||
COMMENT_TITLE = _(
|
COMMENT_TITLE = _(
|
||||||
u"comment_title",
|
u'comment_title',
|
||||||
default=u"${author_name} on ${content}")
|
default=u'${author_name} on ${content}')
|
||||||
|
|
||||||
MAIL_NOTIFICATION_MESSAGE = _(
|
MAIL_NOTIFICATION_MESSAGE = _(
|
||||||
u"mail_notification_message",
|
u'mail_notification_message',
|
||||||
default=u"A comment on '${title}' "
|
default=u'A comment on "${title}" '
|
||||||
u"has been posted here: ${link}\n\n"
|
u'has been posted here: ${link}\n\n'
|
||||||
u"---\n"
|
u'---\n'
|
||||||
u"${text}\n"
|
u'${text}\n'
|
||||||
u"---\n")
|
u'---\n')
|
||||||
|
|
||||||
MAIL_NOTIFICATION_MESSAGE_MODERATOR = _(
|
MAIL_NOTIFICATION_MESSAGE_MODERATOR = _(
|
||||||
u"mail_notification_message_moderator",
|
u'mail_notification_message_moderator',
|
||||||
default=u"A comment on '${title}' "
|
default=u'A comment on "${title}" '
|
||||||
u"has been posted here: ${link}\n\n"
|
u'has been posted here: ${link}\n\n'
|
||||||
u"---\n"
|
u'---\n'
|
||||||
u"${text}\n"
|
u'${text}\n'
|
||||||
u"---\n\n"
|
u'---\n\n'
|
||||||
u"Approve comment:\n${link_approve}\n\n"
|
u'Approve comment:\n${link_approve}\n\n'
|
||||||
u"Delete comment:\n${link_delete}\n")
|
u'Delete comment:\n${link_delete}\n')
|
||||||
|
|
||||||
logger = logging.getLogger("plone.app.discussion")
|
logger = logging.getLogger('plone.app.discussion')
|
||||||
|
|
||||||
|
|
||||||
class Comment(CatalogAware, WorkflowAware, DynamicType, Traversable,
|
class Comment(CatalogAware, WorkflowAware, DynamicType, Traversable,
|
||||||
@ -86,10 +86,10 @@ class Comment(CatalogAware, WorkflowAware, DynamicType, Traversable,
|
|||||||
comment_id = None # long
|
comment_id = None # long
|
||||||
in_reply_to = None # long
|
in_reply_to = None # long
|
||||||
|
|
||||||
title = u""
|
title = u''
|
||||||
|
|
||||||
mime_type = None
|
mime_type = None
|
||||||
text = u""
|
text = u''
|
||||||
|
|
||||||
creator = None
|
creator = None
|
||||||
creation_date = None
|
creation_date = None
|
||||||
@ -157,14 +157,12 @@ class Comment(CatalogAware, WorkflowAware, DynamicType, Traversable,
|
|||||||
if transform:
|
if transform:
|
||||||
return transform.getData()
|
return transform.getData()
|
||||||
else:
|
else:
|
||||||
logger = logging.getLogger("plone.app.discussion")
|
logger = logging.getLogger('plone.app.discussion')
|
||||||
logger.error(_(
|
msg = u'Transform "{0}" => "{1}" not available. Failed to ' \
|
||||||
u"Transform '%s' => '%s' not available." % (
|
u'transform comment "{2}".'
|
||||||
sourceMimetype,
|
logger.error(
|
||||||
targetMimetype
|
msg.format(sourceMimetype, targetMimetype, self.absolute_url())
|
||||||
) +
|
)
|
||||||
u"Failed to transform comment '%s'." % self.absolute_url()
|
|
||||||
))
|
|
||||||
return text
|
return text
|
||||||
|
|
||||||
def Title(self):
|
def Title(self):
|
||||||
@ -177,8 +175,8 @@ class Comment(CatalogAware, WorkflowAware, DynamicType, Traversable,
|
|||||||
if not self.author_name:
|
if not self.author_name:
|
||||||
author_name = translate(
|
author_name = translate(
|
||||||
Message(_(
|
Message(_(
|
||||||
u"label_anonymous",
|
u'label_anonymous',
|
||||||
default=u"Anonymous"
|
default=u'Anonymous'
|
||||||
))
|
))
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
@ -198,8 +196,7 @@ class Comment(CatalogAware, WorkflowAware, DynamicType, Traversable,
|
|||||||
"""
|
"""
|
||||||
return self.creator or self.author_name
|
return self.creator or self.author_name
|
||||||
|
|
||||||
security.declareProtected(permissions.View, 'Type')
|
@security.protected(permissions.View)
|
||||||
|
|
||||||
def Type(self):
|
def Type(self):
|
||||||
"""The Discussion Item content type.
|
"""The Discussion Item content type.
|
||||||
"""
|
"""
|
||||||
@ -292,7 +289,7 @@ def notify_content_object_moved(obj, event):
|
|||||||
)
|
)
|
||||||
brains = catalog.searchResults(dict(
|
brains = catalog.searchResults(dict(
|
||||||
path={'query': old_path},
|
path={'query': old_path},
|
||||||
portal_type="Discussion Item"
|
portal_type='Discussion Item'
|
||||||
))
|
))
|
||||||
for brain in brains:
|
for brain in brains:
|
||||||
catalog.uncatalog_object(brain.getPath())
|
catalog.uncatalog_object(brain.getPath())
|
||||||
@ -346,7 +343,7 @@ def notify_user(obj, event):
|
|||||||
if not emails:
|
if not emails:
|
||||||
return
|
return
|
||||||
|
|
||||||
subject = translate(_(u"A comment has been posted."),
|
subject = translate(_(u'A comment has been posted.'),
|
||||||
context=obj.REQUEST)
|
context=obj.REQUEST)
|
||||||
message = translate(
|
message = translate(
|
||||||
Message(
|
Message(
|
||||||
@ -412,7 +409,7 @@ def notify_moderator(obj, event):
|
|||||||
content_object = aq_parent(conversation)
|
content_object = aq_parent(conversation)
|
||||||
|
|
||||||
# Compose email
|
# Compose email
|
||||||
subject = translate(_(u"A comment has been posted."), context=obj.REQUEST)
|
subject = translate(_(u'A comment has been posted.'), context=obj.REQUEST)
|
||||||
link_approve = obj.absolute_url() + '/@@moderate-publish-comment'
|
link_approve = obj.absolute_url() + '/@@moderate-publish-comment'
|
||||||
link_delete = obj.absolute_url() + '/@@moderate-delete-comment'
|
link_delete = obj.absolute_url() + '/@@moderate-delete-comment'
|
||||||
message = translate(
|
message = translate(
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
""" Content rules handlers
|
""" Content rules handlers
|
||||||
"""
|
"""
|
||||||
from plone.app.discussion import _
|
from plone.app.discussion import _
|
||||||
@ -9,6 +10,7 @@ except ImportError:
|
|||||||
class BaseSubstitution(object):
|
class BaseSubstitution(object):
|
||||||
""" Fallback class if plone.stringinterp is not available
|
""" Fallback class if plone.stringinterp is not available
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, context, **kwargs):
|
def __init__(self, context, **kwargs):
|
||||||
self.context = context
|
self.context = context
|
||||||
|
|
||||||
@ -27,6 +29,7 @@ def execute_comment(event):
|
|||||||
class CommentSubstitution(BaseSubstitution):
|
class CommentSubstitution(BaseSubstitution):
|
||||||
""" Comment string substitution
|
""" Comment string substitution
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, context, **kwargs):
|
def __init__(self, context, **kwargs):
|
||||||
super(CommentSubstitution, self).__init__(context, **kwargs)
|
super(CommentSubstitution, self).__init__(context, **kwargs)
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
"""The conversation and replies adapters
|
"""The conversation and replies adapters
|
||||||
|
|
||||||
The conversation is responsible for storing all comments. It provides a
|
The conversation is responsible for storing all comments. It provides a
|
||||||
@ -20,12 +21,12 @@ from BTrees.OIBTree import OIBTree
|
|||||||
from OFS.event import ObjectWillBeAddedEvent
|
from OFS.event import ObjectWillBeAddedEvent
|
||||||
from OFS.event import ObjectWillBeRemovedEvent
|
from OFS.event import ObjectWillBeRemovedEvent
|
||||||
from OFS.Traversable import Traversable
|
from OFS.Traversable import Traversable
|
||||||
from Products.CMFPlone.interfaces import IHideFromBreadcrumbs
|
|
||||||
from Products.CMFPlone import DISCUSSION_ANNOTATION_KEY as ANNOTATION_KEY
|
|
||||||
from persistent import Persistent
|
from persistent import Persistent
|
||||||
from plone.app.discussion.comment import Comment
|
from plone.app.discussion.comment import Comment
|
||||||
from plone.app.discussion.interfaces import IConversation
|
from plone.app.discussion.interfaces import IConversation
|
||||||
from plone.app.discussion.interfaces import IReplies
|
from plone.app.discussion.interfaces import IReplies
|
||||||
|
from Products.CMFPlone import DISCUSSION_ANNOTATION_KEY as ANNOTATION_KEY
|
||||||
|
from Products.CMFPlone.interfaces import IHideFromBreadcrumbs
|
||||||
from zope.annotation.interfaces import IAnnotatable
|
from zope.annotation.interfaces import IAnnotatable
|
||||||
from zope.annotation.interfaces import IAnnotations
|
from zope.annotation.interfaces import IAnnotations
|
||||||
from zope.component import adapter
|
from zope.component import adapter
|
||||||
@ -38,7 +39,6 @@ from zope.lifecycleevent import ObjectAddedEvent
|
|||||||
from zope.lifecycleevent import ObjectCreatedEvent
|
from zope.lifecycleevent import ObjectCreatedEvent
|
||||||
from zope.lifecycleevent import ObjectRemovedEvent
|
from zope.lifecycleevent import ObjectRemovedEvent
|
||||||
|
|
||||||
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
|
||||||
@ -53,7 +53,7 @@ class Conversation(Traversable, Persistent, Explicit):
|
|||||||
|
|
||||||
__allow_access_to_unprotected_subobjects__ = True
|
__allow_access_to_unprotected_subobjects__ = True
|
||||||
|
|
||||||
def __init__(self, id="++conversation++default"):
|
def __init__(self, id='++conversation++default'):
|
||||||
self.id = id
|
self.id = id
|
||||||
|
|
||||||
# username -> count of comments; key is removed when count reaches 0
|
# username -> count of comments; key is removed when count reaches 0
|
||||||
@ -427,7 +427,7 @@ class CommentReplies(ConversationReplies):
|
|||||||
)
|
)
|
||||||
if self.conversation is None or conversation_has_no_children:
|
if self.conversation is None or conversation_has_no_children:
|
||||||
raise TypeError("This adapter doesn't know what to do with the "
|
raise TypeError("This adapter doesn't know what to do with the "
|
||||||
"parent conversation")
|
'parent conversation')
|
||||||
|
|
||||||
self.comment_id = self.comment.comment_id
|
self.comment_id = self.comment.comment_id
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
""" Custom discussion events
|
""" Custom discussion events
|
||||||
"""
|
"""
|
||||||
from plone.app.discussion.interfaces import ICommentAddedEvent
|
from plone.app.discussion.interfaces import ICommentAddedEvent
|
||||||
|
@ -30,25 +30,24 @@ class IConversation(IIterableMapping):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
total_comments = schema.Int(
|
total_comments = schema.Int(
|
||||||
title=_(u"Total number of public comments on this item"),
|
title=_(u'Total number of public comments on this item'),
|
||||||
min=0,
|
min=0,
|
||||||
readonly=True,
|
readonly=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
last_comment_date = schema.Date(
|
last_comment_date = schema.Date(
|
||||||
title=_(u"Date of the most recent public comment"),
|
title=_(u'Date of the most recent public comment'),
|
||||||
readonly=True,
|
readonly=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
commentators = schema.Set(
|
commentators = schema.Set(
|
||||||
title=_(u"The set of unique commentators (usernames)"),
|
title=_(u'The set of unique commentators (usernames)'),
|
||||||
readonly=True,
|
readonly=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
public_commentators = schema.Set(
|
public_commentators = schema.Set(
|
||||||
title=_(
|
title=_(
|
||||||
u"The set of unique commentators (usernames) of"
|
u'The set of unique commentators (usernames) of published_comments'
|
||||||
u" published_comments"
|
|
||||||
),
|
),
|
||||||
readonly=True,
|
readonly=True,
|
||||||
)
|
)
|
||||||
@ -129,57 +128,57 @@ class IComment(Interface):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
portal_type = schema.ASCIILine(
|
portal_type = schema.ASCIILine(
|
||||||
title=_(u"Portal type"),
|
title=_(u'Portal type'),
|
||||||
default="Discussion Item",
|
default='Discussion Item',
|
||||||
)
|
)
|
||||||
|
|
||||||
__parent__ = schema.Object(
|
__parent__ = schema.Object(
|
||||||
title=_(u"Conversation"), schema=Interface)
|
title=_(u'Conversation'), schema=Interface)
|
||||||
|
|
||||||
__name__ = schema.TextLine(title=_(u"Name"))
|
__name__ = schema.TextLine(title=_(u'Name'))
|
||||||
|
|
||||||
comment_id = schema.Int(
|
comment_id = schema.Int(
|
||||||
title=_(u"A comment id unique to this conversation"))
|
title=_(u'A comment id unique to this conversation'))
|
||||||
|
|
||||||
in_reply_to = schema.Int(
|
in_reply_to = schema.Int(
|
||||||
title=_(u"Id of comment this comment is in reply to"),
|
title=_(u'Id of comment this comment is in reply to'),
|
||||||
required=False,
|
required=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
# for logged in comments - set to None for anonymous
|
# for logged in comments - set to None for anonymous
|
||||||
author_username = schema.TextLine(title=_(u"Name"), required=False)
|
author_username = schema.TextLine(title=_(u'Name'), required=False)
|
||||||
|
|
||||||
# for anonymous comments only, set to None for logged in comments
|
# 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=_(u'Name'), required=False)
|
||||||
author_email = schema.TextLine(title=_(u"Email"), required=False)
|
author_email = schema.TextLine(title=_(u'Email'), required=False)
|
||||||
|
|
||||||
title = schema.TextLine(title=_(u"label_subject",
|
title = schema.TextLine(title=_(u'label_subject',
|
||||||
default=u"Subject"))
|
default=u'Subject'))
|
||||||
|
|
||||||
mime_type = schema.ASCIILine(title=_(u"MIME type"), default="text/plain")
|
mime_type = schema.ASCIILine(title=_(u'MIME type'), default='text/plain')
|
||||||
text = schema.Text(
|
text = schema.Text(
|
||||||
title=_(
|
title=_(
|
||||||
u"label_comment",
|
u'label_comment',
|
||||||
default=u"Comment"
|
default=u'Comment'
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
user_notification = schema.Bool(
|
user_notification = schema.Bool(
|
||||||
title=_(
|
title=_(
|
||||||
u"Notify me of new comments via email."
|
u'Notify me of new comments via email.'
|
||||||
),
|
),
|
||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
creator = schema.TextLine(title=_(u"Username of the commenter"))
|
creator = schema.TextLine(title=_(u'Username of the commenter'))
|
||||||
creation_date = schema.Date(title=_(u"Creation date"))
|
creation_date = schema.Date(title=_(u'Creation date'))
|
||||||
modification_date = schema.Date(title=_(u"Modification date"))
|
modification_date = schema.Date(title=_(u'Modification date'))
|
||||||
|
|
||||||
|
|
||||||
class ICaptcha(Interface):
|
class ICaptcha(Interface):
|
||||||
"""Captcha/ReCaptcha text field to extend the existing comment form.
|
"""Captcha/ReCaptcha text field to extend the existing comment form.
|
||||||
"""
|
"""
|
||||||
captcha = schema.TextLine(title=_(u"Captcha"),
|
captcha = schema.TextLine(title=_(u'Captcha'),
|
||||||
required=False)
|
required=False)
|
||||||
|
|
||||||
|
|
||||||
@ -195,137 +194,137 @@ class IDiscussionSettings(Interface):
|
|||||||
# - Search control panel: Show comments in search results
|
# - Search control panel: Show comments in search results
|
||||||
|
|
||||||
globally_enabled = schema.Bool(
|
globally_enabled = schema.Bool(
|
||||||
title=_(u"label_globally_enabled",
|
title=_(u'label_globally_enabled',
|
||||||
default=u"Globally enable comments"),
|
default=u'Globally enable comments'),
|
||||||
description=_(
|
description=_(
|
||||||
u"help_globally_enabled",
|
u'help_globally_enabled',
|
||||||
default=u"If selected, users are able to post comments on the "
|
default=u'If selected, users are able to post comments on the '
|
||||||
u"site. Though, you have to enable comments for "
|
u'site. Though, you have to enable comments for '
|
||||||
u"specific content types, folders or content objects "
|
u'specific content types, folders or content objects '
|
||||||
u"before users will be able to post comments."
|
u'before users will be able to post comments.'
|
||||||
),
|
),
|
||||||
required=False,
|
required=False,
|
||||||
default=False,
|
default=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
anonymous_comments = schema.Bool(
|
anonymous_comments = schema.Bool(
|
||||||
title=_(u"label_anonymous_comments",
|
title=_(u'label_anonymous_comments',
|
||||||
default="Enable anonymous comments"),
|
default='Enable anonymous comments'),
|
||||||
description=_(
|
description=_(
|
||||||
u"help_anonymous_comments",
|
u'help_anonymous_comments',
|
||||||
default=u"If selected, anonymous users are able to post "
|
default=u'If selected, anonymous users are able to post '
|
||||||
u"comments without loggin in. It is highly "
|
u'comments without loggin in. It is highly '
|
||||||
u"recommended to use a captcha solution to prevent "
|
u'recommended to use a captcha solution to prevent '
|
||||||
u"spam if this setting is enabled."
|
u'spam if this setting is enabled.'
|
||||||
),
|
),
|
||||||
required=False,
|
required=False,
|
||||||
default=False,
|
default=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
anonymous_email_enabled = schema.Bool(
|
anonymous_email_enabled = schema.Bool(
|
||||||
title=_(u"label_anonymous_email_enabled",
|
title=_(u'label_anonymous_email_enabled',
|
||||||
default=u"Enable anonymous email field"),
|
default=u'Enable anonymous email field'),
|
||||||
description=_(
|
description=_(
|
||||||
u"help_anonymous_email_enabled",
|
u'help_anonymous_email_enabled',
|
||||||
default=u"If selected, anonymous user will have to "
|
default=u'If selected, anonymous user will have to '
|
||||||
u"give their email."),
|
u'give their email.'),
|
||||||
required=False,
|
required=False,
|
||||||
default=False
|
default=False
|
||||||
)
|
)
|
||||||
|
|
||||||
moderation_enabled = schema.Bool(
|
moderation_enabled = schema.Bool(
|
||||||
title=_(
|
title=_(
|
||||||
u"label_moderation_enabled",
|
u'label_moderation_enabled',
|
||||||
default="Enable comment moderation"
|
default='Enable comment moderation'
|
||||||
),
|
),
|
||||||
description=_(
|
description=_(
|
||||||
u"help_moderation_enabled",
|
u'help_moderation_enabled',
|
||||||
default=u"If selected, comments will enter a 'Pending' state "
|
default=u'If selected, comments will enter a "Pending" state '
|
||||||
u"in which they are invisible to the public. A user "
|
u'in which they are invisible to the public. A user '
|
||||||
u"with the 'Review comments' permission ('Reviewer' "
|
u'with the "Review comments" permission ("Reviewer" '
|
||||||
u"or 'Manager') can approve comments to make them "
|
u'or "Manager") can approve comments to make them '
|
||||||
u"visible to the public. If you want to enable a "
|
u'visible to the public. If you want to enable a '
|
||||||
u"custom comment workflow, you have to go to the "
|
u'custom comment workflow, you have to go to the '
|
||||||
u"types control panel."
|
u'types control panel.'
|
||||||
),
|
),
|
||||||
required=False,
|
required=False,
|
||||||
default=False,
|
default=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
edit_comment_enabled = schema.Bool(
|
edit_comment_enabled = schema.Bool(
|
||||||
title=_(u"label_edit_comment_enabled",
|
title=_(u'label_edit_comment_enabled',
|
||||||
default="Enable editing of comments"),
|
default='Enable editing of comments'),
|
||||||
description=_(u"help_edit_comment_enabled",
|
description=_(u'help_edit_comment_enabled',
|
||||||
default=u"If selected, supports editing "
|
default=u'If selected, supports editing '
|
||||||
"of comments for users with the 'Edit comments' "
|
'of comments for users with the "Edit comments" '
|
||||||
"permission."),
|
'permission.'),
|
||||||
required=False,
|
required=False,
|
||||||
default=False,
|
default=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
delete_own_comment_enabled = schema.Bool(
|
delete_own_comment_enabled = schema.Bool(
|
||||||
title=_(u"label_delete_own_comment_enabled",
|
title=_(u'label_delete_own_comment_enabled',
|
||||||
default="Enable deleting own comments"),
|
default='Enable deleting own comments'),
|
||||||
description=_(u"help_delete_own_comment_enabled",
|
description=_(u'help_delete_own_comment_enabled',
|
||||||
default=u"If selected, supports deleting "
|
default=u'If selected, supports deleting '
|
||||||
"of own comments for users with the "
|
'of own comments for users with the '
|
||||||
"'Delete own comments' permission."),
|
'"Delete own comments" permission.'),
|
||||||
required=False,
|
required=False,
|
||||||
default=False,
|
default=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
text_transform = schema.Choice(
|
text_transform = schema.Choice(
|
||||||
title=_(u"label_text_transform",
|
title=_(u'label_text_transform',
|
||||||
default="Comment text transform"),
|
default='Comment text transform'),
|
||||||
description=_(
|
description=_(
|
||||||
u"help_text_transform",
|
u'help_text_transform',
|
||||||
default=u"Use this setting to choose if the comment text " +
|
default=u'Use this setting to choose if the comment text '
|
||||||
u"should be transformed in any way. You can choose "
|
u'should be transformed in any way. You can choose '
|
||||||
u"between 'Plain text' and 'Intelligent text'. " +
|
u'between "Plain text" and "Intelligent text". '
|
||||||
u"'Intelligent text' converts plain text into HTML " +
|
u'"Intelligent text" converts plain text into HTML '
|
||||||
u"where line breaks and indentation is preserved, " +
|
u'where line breaks and indentation is preserved, '
|
||||||
u"and web and email addresses are made into " +
|
u'and web and email addresses are made into '
|
||||||
u"clickable links."),
|
u'clickable links.'),
|
||||||
required=True,
|
required=True,
|
||||||
default='text/plain',
|
default='text/plain',
|
||||||
vocabulary='plone.app.discussion.vocabularies.TextTransformVocabulary',
|
vocabulary='plone.app.discussion.vocabularies.TextTransformVocabulary',
|
||||||
)
|
)
|
||||||
|
|
||||||
captcha = schema.Choice(
|
captcha = schema.Choice(
|
||||||
title=_(u"label_captcha",
|
title=_(u'label_captcha',
|
||||||
default="Captcha"),
|
default='Captcha'),
|
||||||
description=_(
|
description=_(
|
||||||
u"help_captcha",
|
u'help_captcha',
|
||||||
default=u"Use this setting to enable or disable Captcha "
|
default=u'Use this setting to enable or disable Captcha '
|
||||||
u"validation for comments. Install "
|
u'validation for comments. Install '
|
||||||
u"plone.formwidget.captcha, "
|
u'plone.formwidget.captcha, '
|
||||||
u"plone.formwidget.recaptcha, collective.akismet, or "
|
u'plone.formwidget.recaptcha, collective.akismet, or '
|
||||||
u"collective.z3cform.norobots if there are no options "
|
u'collective.z3cform.norobots if there are no options '
|
||||||
u"available."),
|
u'available.'),
|
||||||
required=True,
|
required=True,
|
||||||
default='disabled',
|
default='disabled',
|
||||||
vocabulary='plone.app.discussion.vocabularies.CaptchaVocabulary',
|
vocabulary='plone.app.discussion.vocabularies.CaptchaVocabulary',
|
||||||
)
|
)
|
||||||
|
|
||||||
show_commenter_image = schema.Bool(
|
show_commenter_image = schema.Bool(
|
||||||
title=_(u"label_show_commenter_image",
|
title=_(u'label_show_commenter_image',
|
||||||
default=u"Show commenter image"),
|
default=u'Show commenter image'),
|
||||||
description=_(
|
description=_(
|
||||||
u"help_show_commenter_image",
|
u'help_show_commenter_image',
|
||||||
default=u"If selected, an image of the user is shown next to "
|
default=u'If selected, an image of the user is shown next to '
|
||||||
u"the comment."),
|
u'the comment.'),
|
||||||
required=False,
|
required=False,
|
||||||
default=True,
|
default=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
moderator_notification_enabled = schema.Bool(
|
moderator_notification_enabled = schema.Bool(
|
||||||
title=_(u"label_moderator_notification_enabled",
|
title=_(u'label_moderator_notification_enabled',
|
||||||
default=u"Enable moderator email notification"),
|
default=u'Enable moderator email notification'),
|
||||||
description=_(
|
description=_(
|
||||||
u"help_moderator_notification_enabled",
|
u'help_moderator_notification_enabled',
|
||||||
default=u"If selected, the moderator is notified if a comment "
|
default=u'If selected, the moderator is notified if a comment '
|
||||||
u"needs attention. The moderator email address can " +
|
u'needs attention. The moderator email address can '
|
||||||
u"be set below."),
|
u'be set below.'),
|
||||||
required=False,
|
required=False,
|
||||||
default=False,
|
default=False,
|
||||||
)
|
)
|
||||||
@ -337,20 +336,20 @@ class IDiscussionSettings(Interface):
|
|||||||
),
|
),
|
||||||
description=_(
|
description=_(
|
||||||
u'help_moderator_email',
|
u'help_moderator_email',
|
||||||
default=u"Address to which moderator notifications "
|
default=u'Address to which moderator notifications '
|
||||||
u"will be sent."),
|
u'will be sent.'),
|
||||||
required=False,
|
required=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
user_notification_enabled = schema.Bool(
|
user_notification_enabled = schema.Bool(
|
||||||
title=_(
|
title=_(
|
||||||
u"label_user_notification_enabled",
|
u'label_user_notification_enabled',
|
||||||
default=u"Enable user email notification"
|
default=u'Enable user email notification'
|
||||||
),
|
),
|
||||||
description=_(
|
description=_(
|
||||||
u"help_user_notification_enabled",
|
u'help_user_notification_enabled',
|
||||||
default=u"If selected, users can choose to be notified "
|
default=u'If selected, users can choose to be notified '
|
||||||
u"of new comments by email."),
|
u'of new comments by email.'),
|
||||||
required=False,
|
required=False,
|
||||||
default=False
|
default=False
|
||||||
)
|
)
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
from Products.CMFCore.utils import getToolByName
|
from Products.CMFCore.utils import getToolByName
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from Products.CMFCore.utils import getToolByName
|
# -*- coding: utf-8 -*-
|
||||||
from plone.app.contenttypes.testing import PLONE_APP_CONTENTTYPES_FIXTURE
|
from plone.app.contenttypes.testing import PLONE_APP_CONTENTTYPES_FIXTURE
|
||||||
from plone.app.discussion.interfaces import IDiscussionSettings
|
from plone.app.discussion.interfaces import IDiscussionSettings
|
||||||
from plone.app.robotframework.testing import REMOTE_LIBRARY_ROBOT_TESTING
|
from plone.app.robotframework.testing import REMOTE_LIBRARY_ROBOT_TESTING
|
||||||
@ -9,14 +9,16 @@ from plone.app.testing import PloneSandboxLayer
|
|||||||
from plone.app.testing import setRoles
|
from plone.app.testing import setRoles
|
||||||
from plone.app.testing import TEST_USER_ID
|
from plone.app.testing import TEST_USER_ID
|
||||||
from plone.registry.interfaces import IRegistry
|
from plone.registry.interfaces import IRegistry
|
||||||
|
from Products.CMFCore.utils import getToolByName
|
||||||
from zope.component import queryUtility
|
from zope.component import queryUtility
|
||||||
from zope.configuration import xmlconfig
|
from zope.configuration import xmlconfig
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import plone.app.collection # noqa
|
import plone.app.collection # noqa
|
||||||
COLLECTION_TYPE = "Collection"
|
COLLECTION_TYPE = 'Collection'
|
||||||
except:
|
except ImportError:
|
||||||
COLLECTION_TYPE = "Topic"
|
COLLECTION_TYPE = 'Topic'
|
||||||
|
|
||||||
|
|
||||||
class PloneAppDiscussion(PloneSandboxLayer):
|
class PloneAppDiscussion(PloneSandboxLayer):
|
||||||
@ -77,7 +79,7 @@ class PloneAppDiscussion(PloneSandboxLayer):
|
|||||||
gtool.addPrincipalToGroup(self.REVIEWER_NAME, 'Reviewers')
|
gtool.addPrincipalToGroup(self.REVIEWER_NAME, 'Reviewers')
|
||||||
mtool.addMember('jim', 'Jim', ['Member'], [])
|
mtool.addMember('jim', 'Jim', ['Member'], [])
|
||||||
mtool.getMemberById('jim').setMemberProperties(
|
mtool.getMemberById('jim').setMemberProperties(
|
||||||
{"fullname": 'Jim Fult\xc3\xb8rn'})
|
{'fullname': 'Jim Fult\xc3\xb8rn'})
|
||||||
|
|
||||||
acl_users.userFolderAddUser(
|
acl_users.userFolderAddUser(
|
||||||
self.MANAGER_USER_NAME,
|
self.MANAGER_USER_NAME,
|
||||||
@ -107,14 +109,14 @@ PLONE_APP_DISCUSSION_ROBOT_FIXTURE = PloneAppDiscussionRobot()
|
|||||||
PLONE_APP_DISCUSSION_FIXTURE = PloneAppDiscussion()
|
PLONE_APP_DISCUSSION_FIXTURE = PloneAppDiscussion()
|
||||||
PLONE_APP_DISCUSSION_INTEGRATION_TESTING = IntegrationTesting(
|
PLONE_APP_DISCUSSION_INTEGRATION_TESTING = IntegrationTesting(
|
||||||
bases=(PLONE_APP_DISCUSSION_FIXTURE,),
|
bases=(PLONE_APP_DISCUSSION_FIXTURE,),
|
||||||
name="PloneAppDiscussion:Integration")
|
name='PloneAppDiscussion:Integration')
|
||||||
PLONE_APP_DISCUSSION_FUNCTIONAL_TESTING = FunctionalTesting(
|
PLONE_APP_DISCUSSION_FUNCTIONAL_TESTING = FunctionalTesting(
|
||||||
bases=(PLONE_APP_DISCUSSION_FIXTURE,),
|
bases=(PLONE_APP_DISCUSSION_FIXTURE,),
|
||||||
name="PloneAppDiscussion:Functional")
|
name='PloneAppDiscussion:Functional')
|
||||||
PLONE_APP_DISCUSSION_ROBOT_TESTING = FunctionalTesting(
|
PLONE_APP_DISCUSSION_ROBOT_TESTING = FunctionalTesting(
|
||||||
bases=(
|
bases=(
|
||||||
PLONE_APP_DISCUSSION_ROBOT_FIXTURE,
|
PLONE_APP_DISCUSSION_ROBOT_FIXTURE,
|
||||||
REMOTE_LIBRARY_ROBOT_TESTING
|
REMOTE_LIBRARY_ROBOT_TESTING
|
||||||
),
|
),
|
||||||
name="PloneAppDiscussion:Robot"
|
name='PloneAppDiscussion:Robot'
|
||||||
)
|
)
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
"""Test the plone.app.discussion catalog indexes
|
"""Test the plone.app.discussion catalog indexes
|
||||||
"""
|
"""
|
||||||
from Products.CMFCore.utils import getToolByName
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from plone.app.discussion.interfaces import IConversation
|
from plone.app.discussion.interfaces import IConversation
|
||||||
from plone.app.discussion.testing import PLONE_APP_DISCUSSION_INTEGRATION_TESTING # noqa
|
from plone.app.discussion.testing import PLONE_APP_DISCUSSION_INTEGRATION_TESTING # noqa
|
||||||
from plone.app.testing import setRoles
|
from plone.app.testing import setRoles
|
||||||
from plone.app.testing import TEST_USER_ID
|
from plone.app.testing import TEST_USER_ID
|
||||||
|
from Products.CMFCore.utils import getToolByName
|
||||||
from zope.annotation.interfaces import IAnnotations
|
from zope.annotation.interfaces import IAnnotations
|
||||||
from zope.component import createObject
|
from zope.component import createObject
|
||||||
|
|
||||||
@ -74,7 +75,7 @@ class ConversationCatalogTest(unittest.TestCase):
|
|||||||
'query':
|
'query':
|
||||||
'/'.join(self.portal.doc1.getPhysicalPath())
|
'/'.join(self.portal.doc1.getPhysicalPath())
|
||||||
},
|
},
|
||||||
portal_type="Document"
|
portal_type='Document'
|
||||||
))
|
))
|
||||||
self.conversation = conversation
|
self.conversation = conversation
|
||||||
self.brains = brains
|
self.brains = brains
|
||||||
@ -92,14 +93,15 @@ class ConversationCatalogTest(unittest.TestCase):
|
|||||||
new_comment2_id = self.conversation.addComment(comment2)
|
new_comment2_id = self.conversation.addComment(comment2)
|
||||||
|
|
||||||
comment2 = self.portal.doc1.restrictedTraverse(
|
comment2 = self.portal.doc1.restrictedTraverse(
|
||||||
'++conversation++default/%s' % new_comment2_id)
|
'++conversation++default/{0}'.format(new_comment2_id)
|
||||||
|
)
|
||||||
comment2.reindexObject()
|
comment2.reindexObject()
|
||||||
brains = self.catalog.searchResults(dict(
|
brains = self.catalog.searchResults(dict(
|
||||||
path={
|
path={
|
||||||
'query':
|
'query':
|
||||||
'/'.join(self.portal.doc1.getPhysicalPath())
|
'/'.join(self.portal.doc1.getPhysicalPath())
|
||||||
},
|
},
|
||||||
portal_type="Document"
|
portal_type='Document'
|
||||||
))
|
))
|
||||||
doc1_brain = brains[0]
|
doc1_brain = brains[0]
|
||||||
self.assertEqual(doc1_brain.total_comments, 2)
|
self.assertEqual(doc1_brain.total_comments, 2)
|
||||||
@ -120,14 +122,15 @@ class ConversationCatalogTest(unittest.TestCase):
|
|||||||
new_comment2_id = self.conversation.addComment(comment2)
|
new_comment2_id = self.conversation.addComment(comment2)
|
||||||
|
|
||||||
comment2 = self.portal.doc1.restrictedTraverse(
|
comment2 = self.portal.doc1.restrictedTraverse(
|
||||||
'++conversation++default/%s' % new_comment2_id)
|
'++conversation++default/{0}'.format(new_comment2_id)
|
||||||
|
)
|
||||||
comment2.reindexObject()
|
comment2.reindexObject()
|
||||||
brains = self.catalog.searchResults(dict(
|
brains = self.catalog.searchResults(dict(
|
||||||
path={
|
path={
|
||||||
'query':
|
'query':
|
||||||
'/'.join(self.portal.doc1.getPhysicalPath())
|
'/'.join(self.portal.doc1.getPhysicalPath())
|
||||||
},
|
},
|
||||||
portal_type="Document"
|
portal_type='Document'
|
||||||
))
|
))
|
||||||
doc1_brain = brains[0]
|
doc1_brain = brains[0]
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
@ -143,7 +146,7 @@ class ConversationCatalogTest(unittest.TestCase):
|
|||||||
'query':
|
'query':
|
||||||
'/'.join(self.portal.doc1.getPhysicalPath())
|
'/'.join(self.portal.doc1.getPhysicalPath())
|
||||||
},
|
},
|
||||||
portal_type="Document"
|
portal_type='Document'
|
||||||
))
|
))
|
||||||
doc1_brain = brains[0]
|
doc1_brain = brains[0]
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
@ -158,7 +161,7 @@ class ConversationCatalogTest(unittest.TestCase):
|
|||||||
'query':
|
'query':
|
||||||
'/'.join(self.portal.doc1.getPhysicalPath())
|
'/'.join(self.portal.doc1.getPhysicalPath())
|
||||||
},
|
},
|
||||||
portal_type="Document"
|
portal_type='Document'
|
||||||
))
|
))
|
||||||
doc1_brain = brains[0]
|
doc1_brain = brains[0]
|
||||||
self.assertEqual(doc1_brain.last_comment_date, None)
|
self.assertEqual(doc1_brain.last_comment_date, None)
|
||||||
@ -176,7 +179,8 @@ class ConversationCatalogTest(unittest.TestCase):
|
|||||||
new_comment2_id = self.conversation.addComment(comment2)
|
new_comment2_id = self.conversation.addComment(comment2)
|
||||||
|
|
||||||
comment2 = self.portal.doc1.restrictedTraverse(
|
comment2 = self.portal.doc1.restrictedTraverse(
|
||||||
'++conversation++default/%s' % new_comment2_id)
|
'++conversation++default/{0}'.format(new_comment2_id)
|
||||||
|
)
|
||||||
comment2.reindexObject()
|
comment2.reindexObject()
|
||||||
|
|
||||||
brains = self.catalog.searchResults(dict(
|
brains = self.catalog.searchResults(dict(
|
||||||
@ -184,7 +188,7 @@ class ConversationCatalogTest(unittest.TestCase):
|
|||||||
'query':
|
'query':
|
||||||
'/'.join(self.portal.doc1.getPhysicalPath())
|
'/'.join(self.portal.doc1.getPhysicalPath())
|
||||||
},
|
},
|
||||||
portal_type="Document"
|
portal_type='Document'
|
||||||
))
|
))
|
||||||
doc1_brain = brains[0]
|
doc1_brain = brains[0]
|
||||||
|
|
||||||
@ -197,7 +201,7 @@ class ConversationCatalogTest(unittest.TestCase):
|
|||||||
'query':
|
'query':
|
||||||
'/'.join(self.portal.doc1.getPhysicalPath())
|
'/'.join(self.portal.doc1.getPhysicalPath())
|
||||||
},
|
},
|
||||||
portal_type="Document"
|
portal_type='Document'
|
||||||
))
|
))
|
||||||
doc1_brain = brains[0]
|
doc1_brain = brains[0]
|
||||||
self.assertEqual(doc1_brain.commentators, ('Jim',))
|
self.assertEqual(doc1_brain.commentators, ('Jim',))
|
||||||
@ -209,7 +213,7 @@ class ConversationCatalogTest(unittest.TestCase):
|
|||||||
'query':
|
'query':
|
||||||
'/'.join(self.portal.doc1.getPhysicalPath())
|
'/'.join(self.portal.doc1.getPhysicalPath())
|
||||||
},
|
},
|
||||||
portal_type="Document"
|
portal_type='Document'
|
||||||
))
|
))
|
||||||
doc1_brain = brains[0]
|
doc1_brain = brains[0]
|
||||||
self.assertEqual(doc1_brain.commentators, ())
|
self.assertEqual(doc1_brain.commentators, ())
|
||||||
@ -220,7 +224,7 @@ class ConversationCatalogTest(unittest.TestCase):
|
|||||||
'query':
|
'query':
|
||||||
'/'.join(self.portal.doc1.getPhysicalPath())
|
'/'.join(self.portal.doc1.getPhysicalPath())
|
||||||
},
|
},
|
||||||
portal_type="Discussion Item"
|
portal_type='Discussion Item'
|
||||||
))
|
))
|
||||||
comment1_brain = brains[0]
|
comment1_brain = brains[0]
|
||||||
self.assertEqual(comment1_brain.commentators, None)
|
self.assertEqual(comment1_brain.commentators, None)
|
||||||
@ -228,14 +232,14 @@ class ConversationCatalogTest(unittest.TestCase):
|
|||||||
self.assertEqual(comment1_brain.total_comments, None)
|
self.assertEqual(comment1_brain.total_comments, None)
|
||||||
|
|
||||||
def test_dont_index_private_commentators(self):
|
def test_dont_index_private_commentators(self):
|
||||||
self.comment1.manage_permission("View", roles=tuple())
|
self.comment1.manage_permission('View', roles=tuple())
|
||||||
self.portal.doc1.reindexObject()
|
self.portal.doc1.reindexObject()
|
||||||
brains = self.catalog.searchResults(dict(
|
brains = self.catalog.searchResults(dict(
|
||||||
path={
|
path={
|
||||||
'query':
|
'query':
|
||||||
'/'.join(self.portal.doc1.getPhysicalPath())
|
'/'.join(self.portal.doc1.getPhysicalPath())
|
||||||
},
|
},
|
||||||
portal_type="Document"
|
portal_type='Document'
|
||||||
))
|
))
|
||||||
doc1_brain = brains[0]
|
doc1_brain = brains[0]
|
||||||
self.assertEqual(doc1_brain.commentators, ())
|
self.assertEqual(doc1_brain.commentators, ())
|
||||||
@ -262,7 +266,8 @@ class CommentCatalogTest(unittest.TestCase):
|
|||||||
|
|
||||||
# Comment brain
|
# Comment brain
|
||||||
self.comment = self.portal.doc1.restrictedTraverse(
|
self.comment = self.portal.doc1.restrictedTraverse(
|
||||||
'++conversation++default/%s' % new_comment1_id)
|
'++conversation++default/{0}'.format(new_comment1_id)
|
||||||
|
)
|
||||||
brains = self.catalog.searchResults(dict(
|
brains = self.catalog.searchResults(dict(
|
||||||
path={
|
path={
|
||||||
'query':
|
'query':
|
||||||
@ -281,7 +286,8 @@ class CommentCatalogTest(unittest.TestCase):
|
|||||||
|
|
||||||
# Comment brain
|
# Comment brain
|
||||||
comment = self.portal.doc1.restrictedTraverse(
|
comment = self.portal.doc1.restrictedTraverse(
|
||||||
'++conversation++default/%s' % cid)
|
'++conversation++default/{0}'.format(cid)
|
||||||
|
)
|
||||||
brains = self.catalog.searchResults(dict(
|
brains = self.catalog.searchResults(dict(
|
||||||
path={
|
path={
|
||||||
'query':
|
'query':
|
||||||
@ -289,7 +295,7 @@ class CommentCatalogTest(unittest.TestCase):
|
|||||||
}
|
}
|
||||||
))
|
))
|
||||||
comment_brain = brains[0]
|
comment_brain = brains[0]
|
||||||
self.assertEqual(comment_brain.Title, "Anonymous on Document 1")
|
self.assertEqual(comment_brain.Title, 'Anonymous on Document 1')
|
||||||
|
|
||||||
def test_type(self):
|
def test_type(self):
|
||||||
self.assertEqual(self.comment_brain.portal_type, 'Discussion Item')
|
self.assertEqual(self.comment_brain.portal_type, 'Discussion Item')
|
||||||
@ -329,7 +335,7 @@ class CommentCatalogTest(unittest.TestCase):
|
|||||||
"""
|
"""
|
||||||
brains = self.catalog.searchResults({'portal_type': 'Discussion Item'})
|
brains = self.catalog.searchResults({'portal_type': 'Discussion Item'})
|
||||||
self.assertEqual(len(brains), 1)
|
self.assertEqual(len(brains), 1)
|
||||||
self.portal.manage_delObjects(["doc1"])
|
self.portal.manage_delObjects(['doc1'])
|
||||||
brains = self.catalog.searchResults({'portal_type': 'Discussion Item'})
|
brains = self.catalog.searchResults({'portal_type': 'Discussion Item'})
|
||||||
self.assertEqual(len(brains), 0)
|
self.assertEqual(len(brains), 0)
|
||||||
|
|
||||||
@ -362,13 +368,13 @@ class CommentCatalogTest(unittest.TestCase):
|
|||||||
|
|
||||||
# Make sure no old comment brains are
|
# Make sure no old comment brains are
|
||||||
brains = self.catalog.searchResults(dict(
|
brains = self.catalog.searchResults(dict(
|
||||||
portal_type="Discussion Item",
|
portal_type='Discussion Item',
|
||||||
path={'query': '/'.join(self.portal.folder1.getPhysicalPath())}
|
path={'query': '/'.join(self.portal.folder1.getPhysicalPath())}
|
||||||
))
|
))
|
||||||
self.assertEqual(len(brains), 0)
|
self.assertEqual(len(brains), 0)
|
||||||
|
|
||||||
brains = self.catalog.searchResults(dict(
|
brains = self.catalog.searchResults(dict(
|
||||||
portal_type="Discussion Item",
|
portal_type='Discussion Item',
|
||||||
path={
|
path={
|
||||||
'query': '/'.join(self.portal.folder2.getPhysicalPath())
|
'query': '/'.join(self.portal.folder2.getPhysicalPath())
|
||||||
}
|
}
|
||||||
@ -411,14 +417,14 @@ class CommentCatalogTest(unittest.TestCase):
|
|||||||
|
|
||||||
# Make sure no old comment brains are left
|
# Make sure no old comment brains are left
|
||||||
brains = self.catalog.searchResults(dict(
|
brains = self.catalog.searchResults(dict(
|
||||||
portal_type="Discussion Item",
|
portal_type='Discussion Item',
|
||||||
path={'query': '/plone/sourcefolder/moveme'}
|
path={'query': '/plone/sourcefolder/moveme'}
|
||||||
))
|
))
|
||||||
self.assertEqual(len(brains), 0)
|
self.assertEqual(len(brains), 0)
|
||||||
|
|
||||||
# make sure comments are correctly index on the target
|
# make sure comments are correctly index on the target
|
||||||
brains = self.catalog.searchResults(dict(
|
brains = self.catalog.searchResults(dict(
|
||||||
portal_type="Discussion Item",
|
portal_type='Discussion Item',
|
||||||
path={'query': '/plone/targetfolder/moveme'}
|
path={'query': '/plone/targetfolder/moveme'}
|
||||||
))
|
))
|
||||||
self.assertEqual(len(brains), 1)
|
self.assertEqual(len(brains), 1)
|
||||||
@ -432,7 +438,7 @@ class CommentCatalogTest(unittest.TestCase):
|
|||||||
# We need to commit here so that _p_jar isn't None and move will work
|
# We need to commit here so that _p_jar isn't None and move will work
|
||||||
transaction.savepoint(optimistic=True)
|
transaction.savepoint(optimistic=True)
|
||||||
|
|
||||||
self.portal.manage_renameObject("doc1", "doc2")
|
self.portal.manage_renameObject('doc1', 'doc2')
|
||||||
|
|
||||||
brains = self.catalog.searchResults(
|
brains = self.catalog.searchResults(
|
||||||
portal_type='Discussion Item')
|
portal_type='Discussion Item')
|
||||||
@ -535,7 +541,7 @@ class NoConversationCatalogTest(unittest.TestCase):
|
|||||||
'query':
|
'query':
|
||||||
'/'.join(self.portal.doc1.getPhysicalPath())
|
'/'.join(self.portal.doc1.getPhysicalPath())
|
||||||
},
|
},
|
||||||
portal_type="Document"
|
portal_type='Document'
|
||||||
))
|
))
|
||||||
self.conversation = conversation
|
self.conversation = conversation
|
||||||
self.brains = brains
|
self.brains = brains
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from Products.CMFCore.utils import getToolByName
|
|
||||||
from plone.app.discussion.browser.comment import View
|
from plone.app.discussion.browser.comment import View
|
||||||
from plone.app.discussion.interfaces import IComment
|
from plone.app.discussion.interfaces import IComment
|
||||||
from plone.app.discussion.interfaces import IConversation
|
from plone.app.discussion.interfaces import IConversation
|
||||||
@ -7,6 +6,7 @@ from plone.app.discussion.interfaces import IReplies
|
|||||||
from plone.app.discussion.testing import PLONE_APP_DISCUSSION_INTEGRATION_TESTING # noqa
|
from plone.app.discussion.testing import PLONE_APP_DISCUSSION_INTEGRATION_TESTING # noqa
|
||||||
from plone.app.testing import setRoles
|
from plone.app.testing import setRoles
|
||||||
from plone.app.testing import TEST_USER_ID
|
from plone.app.testing import TEST_USER_ID
|
||||||
|
from Products.CMFCore.utils import getToolByName
|
||||||
from zope.component import createObject
|
from zope.component import createObject
|
||||||
from zope.component import getMultiAdapter
|
from zope.component import getMultiAdapter
|
||||||
|
|
||||||
@ -41,9 +41,9 @@ class CommentTest(unittest.TestCase):
|
|||||||
datetime.datetime.now() - datetime.datetime.utcnow()
|
datetime.datetime.now() - datetime.datetime.utcnow()
|
||||||
utc_to_local_diff = abs(utc_to_local_diff.seconds)
|
utc_to_local_diff = abs(utc_to_local_diff.seconds)
|
||||||
if utc_to_local_diff < 60:
|
if utc_to_local_diff < 60:
|
||||||
logger.warning("Your computer is living in a timezone where local "
|
logger.warning('Your computer is living in a timezone where local '
|
||||||
"time equals utc time. Some potential errors can "
|
'time equals utc time. Some potential errors can '
|
||||||
"get hidden by that")
|
'get hidden by that')
|
||||||
comment1 = createObject('plone.Comment')
|
comment1 = createObject('plone.Comment')
|
||||||
local_utc = datetime.datetime.utcnow()
|
local_utc = datetime.datetime.utcnow()
|
||||||
for date in (comment1.creation_date, comment1.modification_date):
|
for date in (comment1.creation_date, comment1.modification_date):
|
||||||
@ -92,15 +92,15 @@ class CommentTest(unittest.TestCase):
|
|||||||
def test_title(self):
|
def test_title(self):
|
||||||
conversation = IConversation(self.portal.doc1)
|
conversation = IConversation(self.portal.doc1)
|
||||||
comment1 = createObject('plone.Comment')
|
comment1 = createObject('plone.Comment')
|
||||||
comment1.author_name = "Jim Fulton"
|
comment1.author_name = 'Jim Fulton'
|
||||||
conversation.addComment(comment1)
|
conversation.addComment(comment1)
|
||||||
self.assertEqual("Jim Fulton on Document 1", comment1.Title())
|
self.assertEqual('Jim Fulton on Document 1', comment1.Title())
|
||||||
|
|
||||||
def test_no_name_title(self):
|
def test_no_name_title(self):
|
||||||
conversation = IConversation(self.portal.doc1)
|
conversation = IConversation(self.portal.doc1)
|
||||||
comment1 = createObject('plone.Comment')
|
comment1 = createObject('plone.Comment')
|
||||||
conversation.addComment(comment1)
|
conversation.addComment(comment1)
|
||||||
self.assertEqual("Anonymous on Document 1", comment1.Title())
|
self.assertEqual('Anonymous on Document 1', comment1.Title())
|
||||||
|
|
||||||
def test_title_special_characters(self):
|
def test_title_special_characters(self):
|
||||||
self.portal.invokeFactory(
|
self.portal.invokeFactory(
|
||||||
@ -110,9 +110,9 @@ class CommentTest(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
conversation = IConversation(self.portal.doc_sp_chars)
|
conversation = IConversation(self.portal.doc_sp_chars)
|
||||||
comment1 = createObject('plone.Comment')
|
comment1 = createObject('plone.Comment')
|
||||||
comment1.author_name = u"Tarek Ziadé"
|
comment1.author_name = u'Tarek Ziadé'
|
||||||
conversation.addComment(comment1)
|
conversation.addComment(comment1)
|
||||||
self.assertEqual(u"Tarek Ziadé on Document äüö", comment1.Title())
|
self.assertEqual(u'Tarek Ziadé on Document äüö', comment1.Title())
|
||||||
|
|
||||||
def test_title_special_characters_utf8(self):
|
def test_title_special_characters_utf8(self):
|
||||||
self.portal.invokeFactory(
|
self.portal.invokeFactory(
|
||||||
@ -122,19 +122,19 @@ class CommentTest(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
conversation = IConversation(self.portal.doc_sp_chars_utf8)
|
conversation = IConversation(self.portal.doc_sp_chars_utf8)
|
||||||
comment1 = createObject('plone.Comment')
|
comment1 = createObject('plone.Comment')
|
||||||
comment1.author_name = "Hüüb Bôûmä"
|
comment1.author_name = 'Hüüb Bôûmä'
|
||||||
conversation.addComment(comment1)
|
conversation.addComment(comment1)
|
||||||
self.assertEqual(u"Hüüb Bôûmä on Document ëïû", comment1.Title())
|
self.assertEqual(u'Hüüb Bôûmä on Document ëïû', comment1.Title())
|
||||||
|
|
||||||
def test_creator(self):
|
def test_creator(self):
|
||||||
comment1 = createObject('plone.Comment')
|
comment1 = createObject('plone.Comment')
|
||||||
comment1.creator = "jim"
|
comment1.creator = 'jim'
|
||||||
self.assertEqual("jim", comment1.Creator())
|
self.assertEqual('jim', comment1.Creator())
|
||||||
|
|
||||||
def test_creator_author_name(self):
|
def test_creator_author_name(self):
|
||||||
comment1 = createObject('plone.Comment')
|
comment1 = createObject('plone.Comment')
|
||||||
comment1.author_name = "joey"
|
comment1.author_name = 'joey'
|
||||||
self.assertEqual("joey", comment1.Creator())
|
self.assertEqual('joey', comment1.Creator())
|
||||||
|
|
||||||
def test_owner(self):
|
def test_owner(self):
|
||||||
comment1 = createObject('plone.Comment')
|
comment1 = createObject('plone.Comment')
|
||||||
@ -151,25 +151,23 @@ class CommentTest(unittest.TestCase):
|
|||||||
|
|
||||||
def test_getText(self):
|
def test_getText(self):
|
||||||
comment1 = createObject('plone.Comment')
|
comment1 = createObject('plone.Comment')
|
||||||
comment1.text = """First paragraph
|
comment1.text = 'First paragraph\n\nSecond paragraph'
|
||||||
|
|
||||||
Second paragraph"""
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
comment1.getText(),
|
comment1.getText(),
|
||||||
"<p>First paragraph<br /><br /> Second paragraph</p>"
|
'<p>First paragraph<br /><br />Second paragraph</p>'
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_getText_escapes_HTML(self):
|
def test_getText_escapes_HTML(self):
|
||||||
comment1 = createObject('plone.Comment')
|
comment1 = createObject('plone.Comment')
|
||||||
comment1.text = """<b>Got HTML?</b>"""
|
comment1.text = '<b>Got HTML?</b>'
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
comment1.getText(),
|
comment1.getText(),
|
||||||
"<p><b>Got HTML?</b></p>"
|
'<p><b>Got HTML?</b></p>'
|
||||||
)
|
)
|
||||||
|
|
||||||
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 ü.'
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
comment1.getText(),
|
comment1.getText(),
|
||||||
'<p>Umlaute sind \xc3\xa4, \xc3\xb6 und \xc3\xbc.</p>'
|
'<p>Umlaute sind \xc3\xa4, \xc3\xb6 und \xc3\xbc.</p>'
|
||||||
@ -177,15 +175,15 @@ class CommentTest(unittest.TestCase):
|
|||||||
|
|
||||||
def test_getText_doesnt_link(self):
|
def test_getText_doesnt_link(self):
|
||||||
comment1 = createObject('plone.Comment')
|
comment1 = createObject('plone.Comment')
|
||||||
comment1.text = "Go to http://www.plone.org"
|
comment1.text = 'Go to http://www.plone.org'
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
comment1.getText(),
|
comment1.getText(),
|
||||||
"<p>Go to http://www.plone.org</p>"
|
'<p>Go to http://www.plone.org</p>'
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_getText_uses_comment_mime_type(self):
|
def test_getText_uses_comment_mime_type(self):
|
||||||
comment1 = createObject('plone.Comment')
|
comment1 = createObject('plone.Comment')
|
||||||
comment1.text = "Go to http://www.plone.org"
|
comment1.text = 'Go to http://www.plone.org'
|
||||||
comment1.mime_type = 'text/x-web-intelligent'
|
comment1.mime_type = 'text/x-web-intelligent'
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
comment1.getText(),
|
comment1.getText(),
|
||||||
@ -229,7 +227,8 @@ class CommentTest(unittest.TestCase):
|
|||||||
new_comment1_id = conversation.addComment(comment1)
|
new_comment1_id = conversation.addComment(comment1)
|
||||||
|
|
||||||
comment = self.portal.doc1.restrictedTraverse(
|
comment = self.portal.doc1.restrictedTraverse(
|
||||||
'++conversation++default/%s' % new_comment1_id)
|
'++conversation++default/{0}'.format(new_comment1_id)
|
||||||
|
)
|
||||||
self.assertTrue(IComment.providedBy(comment))
|
self.assertTrue(IComment.providedBy(comment))
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
@ -260,12 +259,13 @@ class CommentTest(unittest.TestCase):
|
|||||||
comment1.text = 'Comment text'
|
comment1.text = 'Comment text'
|
||||||
new_comment1_id = conversation.addComment(comment1)
|
new_comment1_id = conversation.addComment(comment1)
|
||||||
comment = self.portal.image1.restrictedTraverse(
|
comment = self.portal.image1.restrictedTraverse(
|
||||||
'++conversation++default/%s' % new_comment1_id)
|
'++conversation++default/{0}'.format(new_comment1_id)
|
||||||
|
)
|
||||||
|
|
||||||
view = View(comment, self.request)
|
view = View(comment, self.request)
|
||||||
View.__call__(view)
|
View.__call__(view)
|
||||||
response = self.request.response
|
response = self.request.response
|
||||||
self.assertIn("/view", response.headers['location'])
|
self.assertIn('/view', response.headers['location'])
|
||||||
|
|
||||||
def test_workflow(self):
|
def test_workflow(self):
|
||||||
"""Basic test for the 'comment_review_workflow'
|
"""Basic test for the 'comment_review_workflow'
|
||||||
@ -301,8 +301,8 @@ class CommentTest(unittest.TestCase):
|
|||||||
def test_fti(self):
|
def test_fti(self):
|
||||||
# test that we can look up an FTI for Discussion Item
|
# test that we can look up an FTI for Discussion Item
|
||||||
|
|
||||||
self.assertTrue(
|
self.assertIn(
|
||||||
"Discussion Item" in
|
'Discussion Item',
|
||||||
self.portal.portal_types.objectIds()
|
self.portal.portal_types.objectIds()
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -327,7 +327,8 @@ class CommentTest(unittest.TestCase):
|
|||||||
new_comment1_id = conversation.addComment(comment1)
|
new_comment1_id = conversation.addComment(comment1)
|
||||||
|
|
||||||
comment = self.portal.doc1.restrictedTraverse(
|
comment = self.portal.doc1.restrictedTraverse(
|
||||||
'++conversation++default/%s' % new_comment1_id)
|
'++conversation++default/{0}'.format(new_comment1_id)
|
||||||
|
)
|
||||||
|
|
||||||
# make sure the view is there
|
# make sure the view is there
|
||||||
self.assertTrue(getMultiAdapter((comment, self.request),
|
self.assertTrue(getMultiAdapter((comment, self.request),
|
||||||
@ -364,7 +365,8 @@ class RepliesTest(unittest.TestCase):
|
|||||||
comment.text = 'Comment text'
|
comment.text = 'Comment text'
|
||||||
new_id = replies.addComment(comment)
|
new_id = replies.addComment(comment)
|
||||||
comment = self.portal.doc1.restrictedTraverse(
|
comment = self.portal.doc1.restrictedTraverse(
|
||||||
'++conversation++default/%s' % new_id)
|
'++conversation++default/{0}'.format(new_id)
|
||||||
|
)
|
||||||
|
|
||||||
# Add a reply to the CommentReplies adapter of the first comment
|
# Add a reply to the CommentReplies adapter of the first comment
|
||||||
re_comment = createObject('plone.Comment')
|
re_comment = createObject('plone.Comment')
|
||||||
@ -400,7 +402,8 @@ class RepliesTest(unittest.TestCase):
|
|||||||
comment.text = 'Comment text'
|
comment.text = 'Comment text'
|
||||||
new_id = replies.addComment(comment)
|
new_id = replies.addComment(comment)
|
||||||
comment = self.portal.doc1.restrictedTraverse(
|
comment = self.portal.doc1.restrictedTraverse(
|
||||||
'++conversation++default/%s' % new_id)
|
'++conversation++default/{0}'.format(new_id)
|
||||||
|
)
|
||||||
|
|
||||||
# Add a reply to the CommentReplies adapter of the first comment
|
# Add a reply to the CommentReplies adapter of the first comment
|
||||||
re_comment = createObject('plone.Comment')
|
re_comment = createObject('plone.Comment')
|
||||||
@ -435,7 +438,8 @@ class RepliesTest(unittest.TestCase):
|
|||||||
comment.text = 'Comment text'
|
comment.text = 'Comment text'
|
||||||
new_id = conversation.addComment(comment)
|
new_id = conversation.addComment(comment)
|
||||||
comment = self.portal.doc1.restrictedTraverse(
|
comment = self.portal.doc1.restrictedTraverse(
|
||||||
'++conversation++default/%s' % new_id)
|
'++conversation++default/{0}'.format(new_id)
|
||||||
|
)
|
||||||
|
|
||||||
# Add a reply to the CommentReplies adapter of the first comment
|
# Add a reply to the CommentReplies adapter of the first comment
|
||||||
re_comment = createObject('plone.Comment')
|
re_comment = createObject('plone.Comment')
|
||||||
@ -443,7 +447,7 @@ class RepliesTest(unittest.TestCase):
|
|||||||
replies = IReplies(comment)
|
replies = IReplies(comment)
|
||||||
new_re_id = replies.addComment(re_comment)
|
new_re_id = replies.addComment(re_comment)
|
||||||
re_comment = self.portal.doc1.restrictedTraverse(
|
re_comment = self.portal.doc1.restrictedTraverse(
|
||||||
'++conversation++default/%s' % new_re_id
|
'++conversation++default/{0}'.format(new_re_id)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add a reply to the reply
|
# Add a reply to the reply
|
||||||
@ -452,7 +456,7 @@ class RepliesTest(unittest.TestCase):
|
|||||||
replies = IReplies(re_comment)
|
replies = IReplies(re_comment)
|
||||||
new_re_re_id = replies.addComment(re_re_comment)
|
new_re_re_id = replies.addComment(re_re_comment)
|
||||||
re_re_comment = self.portal.doc1.restrictedTraverse(
|
re_re_comment = self.portal.doc1.restrictedTraverse(
|
||||||
'++conversation++default/%s' % new_re_re_id
|
'++conversation++default/{0}'.format(new_re_re_id)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add a reply to the replies reply
|
# Add a reply to the replies reply
|
||||||
@ -461,7 +465,8 @@ class RepliesTest(unittest.TestCase):
|
|||||||
replies = IReplies(re_re_comment)
|
replies = IReplies(re_re_comment)
|
||||||
new_re_re_re_id = replies.addComment(re_re_re_comment)
|
new_re_re_re_id = replies.addComment(re_re_re_comment)
|
||||||
re_re_re_comment = self.portal.doc1.restrictedTraverse(
|
re_re_re_comment = self.portal.doc1.restrictedTraverse(
|
||||||
'++conversation++default/%s' % new_re_re_re_id)
|
'++conversation++default/{0}'.format(new_re_re_re_id)
|
||||||
|
)
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
('', 'plone', 'doc1', '++conversation++default', str(new_id)),
|
('', 'plone', 'doc1', '++conversation++default', str(new_id)),
|
||||||
|
@ -2,8 +2,6 @@
|
|||||||
from AccessControl import Unauthorized
|
from AccessControl import Unauthorized
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from OFS.Image import Image
|
from OFS.Image import Image
|
||||||
from Products.CMFCore.utils import getToolByName
|
|
||||||
from Products.CMFPlone.tests import dummy
|
|
||||||
from plone.app.discussion import interfaces
|
from plone.app.discussion import interfaces
|
||||||
from plone.app.discussion.browser.comment import EditCommentForm
|
from plone.app.discussion.browser.comment import EditCommentForm
|
||||||
from plone.app.discussion.browser.comments import CommentForm
|
from plone.app.discussion.browser.comments import CommentForm
|
||||||
@ -17,6 +15,8 @@ from plone.app.testing import setRoles
|
|||||||
from plone.app.testing import TEST_USER_ID
|
from plone.app.testing import TEST_USER_ID
|
||||||
from plone.app.testing import TEST_USER_NAME
|
from plone.app.testing import TEST_USER_NAME
|
||||||
from plone.registry.interfaces import IRegistry
|
from plone.registry.interfaces import IRegistry
|
||||||
|
from Products.CMFCore.utils import getToolByName
|
||||||
|
from Products.CMFPlone.tests import dummy
|
||||||
from z3c.form.interfaces import IFormLayer
|
from z3c.form.interfaces import IFormLayer
|
||||||
from zope import interface
|
from zope import interface
|
||||||
from zope.annotation.interfaces import IAttributeAnnotatable
|
from zope.annotation.interfaces import IAttributeAnnotatable
|
||||||
@ -49,7 +49,7 @@ class TestCommentForm(unittest.TestCase):
|
|||||||
interfaces.IDiscussionLayer,
|
interfaces.IDiscussionLayer,
|
||||||
)
|
)
|
||||||
|
|
||||||
wftool = getToolByName(self.portal, "portal_workflow")
|
wftool = getToolByName(self.portal, 'portal_workflow')
|
||||||
wftool.doActionFor(self.portal.doc1, action='publish')
|
wftool.doActionFor(self.portal.doc1, action='publish')
|
||||||
self.portal.doc1.allow_discussion = True
|
self.portal.doc1.allow_discussion = True
|
||||||
self.membershipTool = getToolByName(self.folder, 'portal_membership')
|
self.membershipTool = getToolByName(self.folder, 'portal_membership')
|
||||||
@ -80,7 +80,7 @@ class TestCommentForm(unittest.TestCase):
|
|||||||
adapts=(Interface, IBrowserRequest),
|
adapts=(Interface, IBrowserRequest),
|
||||||
provides=Interface,
|
provides=Interface,
|
||||||
factory=CommentForm,
|
factory=CommentForm,
|
||||||
name=u"comment-form"
|
name=u'comment-form'
|
||||||
)
|
)
|
||||||
|
|
||||||
# The form should return an error if the comment text field is empty
|
# The form should return an error if the comment text field is empty
|
||||||
@ -88,13 +88,13 @@ class TestCommentForm(unittest.TestCase):
|
|||||||
|
|
||||||
commentForm = getMultiAdapter(
|
commentForm = getMultiAdapter(
|
||||||
(self.context, request),
|
(self.context, request),
|
||||||
name=u"comment-form"
|
name=u'comment-form'
|
||||||
)
|
)
|
||||||
commentForm.update()
|
commentForm.update()
|
||||||
data, errors = commentForm.extractData() # pylint: disable-msg=W0612
|
data, errors = commentForm.extractData() # pylint: disable-msg=W0612
|
||||||
|
|
||||||
self.assertEqual(len(errors), 1)
|
self.assertEqual(len(errors), 1)
|
||||||
self.assertFalse(commentForm.handleComment(commentForm, "foo"))
|
self.assertFalse(commentForm.handleComment(commentForm, 'foo'))
|
||||||
|
|
||||||
# The form is submitted successfully, if the required text field is
|
# The form is submitted successfully, if the required text field is
|
||||||
# filled out
|
# filled out
|
||||||
@ -102,22 +102,22 @@ class TestCommentForm(unittest.TestCase):
|
|||||||
|
|
||||||
commentForm = getMultiAdapter(
|
commentForm = getMultiAdapter(
|
||||||
(self.context, request),
|
(self.context, request),
|
||||||
name=u"comment-form"
|
name=u'comment-form'
|
||||||
)
|
)
|
||||||
commentForm.update()
|
commentForm.update()
|
||||||
data, errors = commentForm.extractData() # pylint: disable-msg=W0612
|
data, errors = commentForm.extractData() # pylint: disable-msg=W0612
|
||||||
|
|
||||||
self.assertEqual(len(errors), 0)
|
self.assertEqual(len(errors), 0)
|
||||||
self.assertFalse(commentForm.handleComment(commentForm, "foo"))
|
self.assertFalse(commentForm.handleComment(commentForm, 'foo'))
|
||||||
|
|
||||||
comments = IConversation(commentForm.context).getComments()
|
comments = IConversation(commentForm.context).getComments()
|
||||||
comments = [comment for comment in comments] # consume iterator
|
comments = [comment for comment in comments] # consume iterator
|
||||||
self.assertEqual(len(comments), 1)
|
self.assertEqual(len(comments), 1)
|
||||||
|
|
||||||
for comment in comments:
|
for comment in comments:
|
||||||
self.assertEqual(comment.text, u"bar")
|
self.assertEqual(comment.text, u'bar')
|
||||||
self.assertEqual(comment.creator, "test_user_1_")
|
self.assertEqual(comment.creator, 'test_user_1_')
|
||||||
self.assertEqual(comment.getOwner().getUserName(), "test-user")
|
self.assertEqual(comment.getOwner().getUserName(), 'test-user')
|
||||||
local_roles = comment.get_local_roles()
|
local_roles = comment.get_local_roles()
|
||||||
self.assertEqual(len(local_roles), 1)
|
self.assertEqual(len(local_roles), 1)
|
||||||
userid, roles = local_roles[0]
|
userid, roles = local_roles[0]
|
||||||
@ -144,14 +144,14 @@ class TestCommentForm(unittest.TestCase):
|
|||||||
adapts=(Interface, IBrowserRequest),
|
adapts=(Interface, IBrowserRequest),
|
||||||
provides=Interface,
|
provides=Interface,
|
||||||
factory=CommentForm,
|
factory=CommentForm,
|
||||||
name=u"comment-form"
|
name=u'comment-form'
|
||||||
)
|
)
|
||||||
|
|
||||||
provideAdapter(
|
provideAdapter(
|
||||||
adapts=(Interface, IBrowserRequest),
|
adapts=(Interface, IBrowserRequest),
|
||||||
provides=Interface,
|
provides=Interface,
|
||||||
factory=EditCommentForm,
|
factory=EditCommentForm,
|
||||||
name=u"edit-comment-form"
|
name=u'edit-comment-form'
|
||||||
)
|
)
|
||||||
|
|
||||||
# The form is submitted successfully, if the required text field is
|
# The form is submitted successfully, if the required text field is
|
||||||
@ -160,13 +160,13 @@ class TestCommentForm(unittest.TestCase):
|
|||||||
|
|
||||||
commentForm = getMultiAdapter(
|
commentForm = getMultiAdapter(
|
||||||
(self.context, request),
|
(self.context, request),
|
||||||
name=u"comment-form"
|
name=u'comment-form'
|
||||||
)
|
)
|
||||||
commentForm.update()
|
commentForm.update()
|
||||||
data, errors = commentForm.extractData() # pylint: disable-msg=W0612
|
data, errors = commentForm.extractData() # pylint: disable-msg=W0612
|
||||||
|
|
||||||
self.assertEqual(len(errors), 0)
|
self.assertEqual(len(errors), 0)
|
||||||
self.assertFalse(commentForm.handleComment(commentForm, "foo"))
|
self.assertFalse(commentForm.handleComment(commentForm, 'foo'))
|
||||||
|
|
||||||
# Edit the last comment
|
# Edit the last comment
|
||||||
conversation = IConversation(self.context)
|
conversation = IConversation(self.context)
|
||||||
@ -174,25 +174,25 @@ class TestCommentForm(unittest.TestCase):
|
|||||||
request = make_request(form={'form.widgets.text': u'foobar'})
|
request = make_request(form={'form.widgets.text': u'foobar'})
|
||||||
editForm = getMultiAdapter(
|
editForm = getMultiAdapter(
|
||||||
(comment, request),
|
(comment, request),
|
||||||
name=u"edit-comment-form"
|
name=u'edit-comment-form'
|
||||||
)
|
)
|
||||||
editForm.update()
|
editForm.update()
|
||||||
data, errors = editForm.extractData() # pylint: disable-msg=W0612
|
data, errors = editForm.extractData() # pylint: disable-msg=W0612
|
||||||
|
|
||||||
self.assertEqual(len(errors), 0)
|
self.assertEqual(len(errors), 0)
|
||||||
self.assertFalse(editForm.handleComment(editForm, "foo"))
|
self.assertFalse(editForm.handleComment(editForm, 'foo'))
|
||||||
comment = [x for x in conversation.getComments()][-1]
|
comment = [x for x in conversation.getComments()][-1]
|
||||||
self.assertEquals(comment.text, u"foobar")
|
self.assertEqual(comment.text, u'foobar')
|
||||||
|
|
||||||
comments = IConversation(commentForm.context).getComments()
|
comments = IConversation(commentForm.context).getComments()
|
||||||
comments = [c for c in comments] # consume iterator
|
comments = [c for c in comments] # consume iterator
|
||||||
self.assertEqual(len(comments), 1)
|
self.assertEqual(len(comments), 1)
|
||||||
|
|
||||||
for comment in comments:
|
for comment in comments:
|
||||||
self.assertEqual(comment.text, u"foobar")
|
self.assertEqual(comment.text, u'foobar')
|
||||||
self.assertEqual(comment.creator, "test_user_1_")
|
self.assertEqual(comment.creator, 'test_user_1_')
|
||||||
|
|
||||||
self.assertEqual(comment.getOwner().getUserName(), "test-user")
|
self.assertEqual(comment.getOwner().getUserName(), 'test-user')
|
||||||
local_roles = comment.get_local_roles()
|
local_roles = comment.get_local_roles()
|
||||||
self.assertEqual(len(local_roles), 1)
|
self.assertEqual(len(local_roles), 1)
|
||||||
userid, roles = local_roles[0]
|
userid, roles = local_roles[0]
|
||||||
@ -219,7 +219,7 @@ class TestCommentForm(unittest.TestCase):
|
|||||||
adapts=(Interface, IBrowserRequest),
|
adapts=(Interface, IBrowserRequest),
|
||||||
provides=Interface,
|
provides=Interface,
|
||||||
factory=CommentForm,
|
factory=CommentForm,
|
||||||
name=u"comment-form"
|
name=u'comment-form'
|
||||||
)
|
)
|
||||||
|
|
||||||
# The form is submitted successfully, if the required text field is
|
# The form is submitted successfully, if the required text field is
|
||||||
@ -228,31 +228,31 @@ class TestCommentForm(unittest.TestCase):
|
|||||||
|
|
||||||
commentForm = getMultiAdapter(
|
commentForm = getMultiAdapter(
|
||||||
(self.context, form_request),
|
(self.context, form_request),
|
||||||
name=u"comment-form"
|
name=u'comment-form'
|
||||||
)
|
)
|
||||||
|
|
||||||
commentForm.update()
|
commentForm.update()
|
||||||
data, errors = commentForm.extractData() # pylint: disable-msg=W0612
|
data, errors = commentForm.extractData() # pylint: disable-msg=W0612
|
||||||
self.assertEqual(len(errors), 0)
|
self.assertEqual(len(errors), 0)
|
||||||
self.assertFalse(commentForm.handleComment(commentForm, "foo"))
|
self.assertFalse(commentForm.handleComment(commentForm, 'foo'))
|
||||||
|
|
||||||
# Delete the last comment
|
# Delete the last comment
|
||||||
conversation = IConversation(self.context)
|
conversation = IConversation(self.context)
|
||||||
comment = [x for x in conversation.getComments()][-1]
|
comment = [x for x in conversation.getComments()][-1]
|
||||||
deleteView = getMultiAdapter(
|
deleteView = getMultiAdapter(
|
||||||
(comment, self.request),
|
(comment, self.request),
|
||||||
name=u"moderate-delete-comment"
|
name=u'moderate-delete-comment'
|
||||||
)
|
)
|
||||||
# try to delete last comment without "Delete comments" permission
|
# try to delete last comment without 'Delete comments' permission
|
||||||
setRoles(self.portal, TEST_USER_ID, ['Member'])
|
setRoles(self.portal, TEST_USER_ID, ['Member'])
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
Unauthorized,
|
Unauthorized,
|
||||||
comment.restrictedTraverse,
|
comment.restrictedTraverse,
|
||||||
"@@moderate-delete-comment"
|
'@@moderate-delete-comment'
|
||||||
)
|
)
|
||||||
deleteView()
|
deleteView()
|
||||||
self.assertEqual(1, len([x for x in conversation.getComments()]))
|
self.assertEqual(1, len([x for x in conversation.getComments()]))
|
||||||
# try to delete last comment with "Delete comments" permission
|
# try to delete last comment with 'Delete comments' permission
|
||||||
setRoles(self.portal, TEST_USER_ID, ['Reviewer'])
|
setRoles(self.portal, TEST_USER_ID, ['Reviewer'])
|
||||||
deleteView()
|
deleteView()
|
||||||
self.assertEqual(0, len([x for x in conversation.getComments()]))
|
self.assertEqual(0, len([x for x in conversation.getComments()]))
|
||||||
@ -277,7 +277,7 @@ class TestCommentForm(unittest.TestCase):
|
|||||||
adapts=(Interface, IBrowserRequest),
|
adapts=(Interface, IBrowserRequest),
|
||||||
provides=Interface,
|
provides=Interface,
|
||||||
factory=CommentForm,
|
factory=CommentForm,
|
||||||
name=u"comment-form"
|
name=u'comment-form'
|
||||||
)
|
)
|
||||||
|
|
||||||
# The form is submitted successfully, if the required text field is
|
# The form is submitted successfully, if the required text field is
|
||||||
@ -286,20 +286,20 @@ class TestCommentForm(unittest.TestCase):
|
|||||||
|
|
||||||
commentForm = getMultiAdapter(
|
commentForm = getMultiAdapter(
|
||||||
(self.context, form_request),
|
(self.context, form_request),
|
||||||
name=u"comment-form"
|
name=u'comment-form'
|
||||||
)
|
)
|
||||||
|
|
||||||
commentForm.update()
|
commentForm.update()
|
||||||
data, errors = commentForm.extractData() # pylint: disable-msg=W0612
|
data, errors = commentForm.extractData() # pylint: disable-msg=W0612
|
||||||
self.assertEqual(len(errors), 0)
|
self.assertEqual(len(errors), 0)
|
||||||
self.assertFalse(commentForm.handleComment(commentForm, "foo"))
|
self.assertFalse(commentForm.handleComment(commentForm, 'foo'))
|
||||||
|
|
||||||
# Delete the last comment
|
# Delete the last comment
|
||||||
conversation = IConversation(self.context)
|
conversation = IConversation(self.context)
|
||||||
comment = [x for x in conversation.getComments()][-1]
|
comment = [x for x in conversation.getComments()][-1]
|
||||||
deleteView = getMultiAdapter(
|
deleteView = getMultiAdapter(
|
||||||
(comment, self.request),
|
(comment, self.request),
|
||||||
name=u"delete-own-comment"
|
name=u'delete-own-comment'
|
||||||
)
|
)
|
||||||
# try to delete last comment with johndoe
|
# try to delete last comment with johndoe
|
||||||
setRoles(self.portal, 'johndoe', ['Member'])
|
setRoles(self.portal, 'johndoe', ['Member'])
|
||||||
@ -307,7 +307,7 @@ class TestCommentForm(unittest.TestCase):
|
|||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
Unauthorized,
|
Unauthorized,
|
||||||
comment.restrictedTraverse,
|
comment.restrictedTraverse,
|
||||||
"@@delete-own-comment"
|
'@@delete-own-comment'
|
||||||
)
|
)
|
||||||
self.assertEqual(1, len([x for x in conversation.getComments()]))
|
self.assertEqual(1, len([x for x in conversation.getComments()]))
|
||||||
# try to delete last comment with the same user that created it
|
# try to delete last comment with the same user that created it
|
||||||
@ -338,7 +338,7 @@ class TestCommentForm(unittest.TestCase):
|
|||||||
provideAdapter(adapts=(Interface, IBrowserRequest),
|
provideAdapter(adapts=(Interface, IBrowserRequest),
|
||||||
provides=Interface,
|
provides=Interface,
|
||||||
factory=CommentForm,
|
factory=CommentForm,
|
||||||
name=u"comment-form")
|
name=u'comment-form')
|
||||||
|
|
||||||
# Post an anonymous comment and provide a name
|
# Post an anonymous comment and provide a name
|
||||||
request = make_request(form={
|
request = make_request(form={
|
||||||
@ -348,20 +348,20 @@ class TestCommentForm(unittest.TestCase):
|
|||||||
|
|
||||||
commentForm = getMultiAdapter(
|
commentForm = getMultiAdapter(
|
||||||
(self.context, request),
|
(self.context, request),
|
||||||
name=u"comment-form"
|
name=u'comment-form'
|
||||||
)
|
)
|
||||||
commentForm.update()
|
commentForm.update()
|
||||||
data, errors = commentForm.extractData() # pylint: disable-msg=W0612
|
data, errors = commentForm.extractData() # pylint: disable-msg=W0612
|
||||||
|
|
||||||
self.assertEqual(len(errors), 0)
|
self.assertEqual(len(errors), 0)
|
||||||
self.assertFalse(commentForm.handleComment(commentForm, "action"))
|
self.assertFalse(commentForm.handleComment(commentForm, 'action'))
|
||||||
|
|
||||||
comments = IConversation(commentForm.context).getComments()
|
comments = IConversation(commentForm.context).getComments()
|
||||||
comments = [comment for comment in comments] # consume itertor
|
comments = [comment for comment in comments] # consume itertor
|
||||||
self.assertEqual(len(comments), 1)
|
self.assertEqual(len(comments), 1)
|
||||||
|
|
||||||
for comment in IConversation(commentForm.context).getComments():
|
for comment in IConversation(commentForm.context).getComments():
|
||||||
self.assertEqual(comment.text, u"bar")
|
self.assertEqual(comment.text, u'bar')
|
||||||
self.assertIsNone(comment.creator)
|
self.assertIsNone(comment.creator)
|
||||||
roles = comment.get_local_roles()
|
roles = comment.get_local_roles()
|
||||||
self.assertEqual(len(roles), 0)
|
self.assertEqual(len(roles), 0)
|
||||||
@ -385,13 +385,13 @@ class TestCommentForm(unittest.TestCase):
|
|||||||
provideAdapter(adapts=(Interface, IBrowserRequest),
|
provideAdapter(adapts=(Interface, IBrowserRequest),
|
||||||
provides=Interface,
|
provides=Interface,
|
||||||
factory=CommentForm,
|
factory=CommentForm,
|
||||||
name=u"comment-form")
|
name=u'comment-form')
|
||||||
|
|
||||||
request = make_request(form={'form.widgets.text': u'bar'})
|
request = make_request(form={'form.widgets.text': u'bar'})
|
||||||
|
|
||||||
commentForm = getMultiAdapter(
|
commentForm = getMultiAdapter(
|
||||||
(self.context, request),
|
(self.context, request),
|
||||||
name=u"comment-form"
|
name=u'comment-form'
|
||||||
)
|
)
|
||||||
commentForm.update()
|
commentForm.update()
|
||||||
data, errors = commentForm.extractData() # pylint: disable-msg=W0612
|
data, errors = commentForm.extractData() # pylint: disable-msg=W0612
|
||||||
@ -403,7 +403,7 @@ class TestCommentForm(unittest.TestCase):
|
|||||||
self.assertRaises(Unauthorized,
|
self.assertRaises(Unauthorized,
|
||||||
commentForm.handleComment,
|
commentForm.handleComment,
|
||||||
commentForm,
|
commentForm,
|
||||||
"foo")
|
'foo')
|
||||||
|
|
||||||
def test_anonymous_can_not_add_comments_if_discussion_is_not_allowed(self):
|
def test_anonymous_can_not_add_comments_if_discussion_is_not_allowed(self):
|
||||||
"""Make sure that anonymous users can't post comments if anonymous
|
"""Make sure that anonymous users can't post comments if anonymous
|
||||||
@ -424,12 +424,12 @@ class TestCommentForm(unittest.TestCase):
|
|||||||
provideAdapter(adapts=(Interface, IBrowserRequest),
|
provideAdapter(adapts=(Interface, IBrowserRequest),
|
||||||
provides=Interface,
|
provides=Interface,
|
||||||
factory=CommentForm,
|
factory=CommentForm,
|
||||||
name=u"comment-form")
|
name=u'comment-form')
|
||||||
|
|
||||||
request = make_request(form={'form.widgets.text': u'bar'})
|
request = make_request(form={'form.widgets.text': u'bar'})
|
||||||
|
|
||||||
commentForm = getMultiAdapter((self.context, request),
|
commentForm = getMultiAdapter((self.context, request),
|
||||||
name=u"comment-form")
|
name=u'comment-form')
|
||||||
commentForm.update()
|
commentForm.update()
|
||||||
data, errors = commentForm.extractData() # pylint: disable-msg=W0612
|
data, errors = commentForm.extractData() # pylint: disable-msg=W0612
|
||||||
|
|
||||||
@ -438,7 +438,7 @@ class TestCommentForm(unittest.TestCase):
|
|||||||
Unauthorized,
|
Unauthorized,
|
||||||
commentForm.handleComment,
|
commentForm.handleComment,
|
||||||
commentForm,
|
commentForm,
|
||||||
"foo"
|
'foo'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -519,20 +519,20 @@ class TestCommentsViewlet(unittest.TestCase):
|
|||||||
self.assertTrue(self.viewlet.comment_transform_message())
|
self.assertTrue(self.viewlet.comment_transform_message())
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.viewlet.comment_transform_message(),
|
self.viewlet.comment_transform_message(),
|
||||||
"You can add a comment by filling out the form below. Plain " +
|
'You can add a comment by filling out the form below. Plain ' +
|
||||||
"text formatting.")
|
'text formatting.')
|
||||||
|
|
||||||
# Set text transform to intelligent text
|
# Set text transform to intelligent text
|
||||||
registry = queryUtility(IRegistry)
|
registry = queryUtility(IRegistry)
|
||||||
settings = registry.forInterface(IDiscussionSettings, check=False)
|
settings = registry.forInterface(IDiscussionSettings, check=False)
|
||||||
settings.text_transform = "text/x-web-intelligent"
|
settings.text_transform = 'text/x-web-intelligent'
|
||||||
|
|
||||||
# Make sure the comment description changes accordingly
|
# Make sure the comment description changes accordingly
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.viewlet.comment_transform_message(),
|
self.viewlet.comment_transform_message(),
|
||||||
"You can add a comment by filling out the form below. " +
|
'You can add a comment by filling out the form below. ' +
|
||||||
"Plain text formatting. Web and email addresses are transformed " +
|
'Plain text formatting. Web and email addresses are transformed ' +
|
||||||
"into clickable links."
|
'into clickable links.'
|
||||||
)
|
)
|
||||||
|
|
||||||
# Enable moderation workflow
|
# Enable moderation workflow
|
||||||
@ -543,9 +543,9 @@ class TestCommentsViewlet(unittest.TestCase):
|
|||||||
# Make sure the comment description shows that comments are moderated
|
# Make sure the comment description shows that comments are moderated
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.viewlet.comment_transform_message(),
|
self.viewlet.comment_transform_message(),
|
||||||
"You can add a comment by filling out the form below. " +
|
'You can add a comment by filling out the form below. ' +
|
||||||
"Plain text formatting. Web and email addresses are transformed " +
|
'Plain text formatting. Web and email addresses are transformed ' +
|
||||||
"into clickable links. Comments are moderated.")
|
'into clickable links. Comments are moderated.')
|
||||||
|
|
||||||
def test_has_replies(self):
|
def test_has_replies(self):
|
||||||
self.assertEqual(self.viewlet.has_replies(), False)
|
self.assertEqual(self.viewlet.has_replies(), False)
|
||||||
@ -599,10 +599,11 @@ class TestCommentsViewlet(unittest.TestCase):
|
|||||||
reply['actions'][0]['id'],
|
reply['actions'][0]['id'],
|
||||||
'publish'
|
'publish'
|
||||||
)
|
)
|
||||||
|
expected_url = 'http://nohost/plone/doc1/++conversation++default/{0}' \
|
||||||
|
'/content_status_modify?workflow_action=publish'
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
reply['actions'][0]['url'],
|
reply['actions'][0]['url'],
|
||||||
'http://nohost/plone/doc1/++conversation++default/%s' % int(c1) +
|
expected_url.format(int(c1))
|
||||||
'/content_status_modify?workflow_action=publish'
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_get_commenter_home_url(self):
|
def test_get_commenter_home_url(self):
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from plone.app.discussion.interfaces import ICommentAddedEvent
|
from plone.app.discussion.interfaces import ICommentAddedEvent
|
||||||
from plone.app.discussion.interfaces import ICommentRemovedEvent
|
from plone.app.discussion.interfaces import ICommentRemovedEvent
|
||||||
from plone.app.discussion.interfaces import IConversation, IReplies
|
from plone.app.discussion.interfaces import IConversation
|
||||||
|
from plone.app.discussion.interfaces import IReplies
|
||||||
from plone.app.discussion.interfaces import IReplyAddedEvent
|
from plone.app.discussion.interfaces import IReplyAddedEvent
|
||||||
from plone.app.discussion.interfaces import IReplyRemovedEvent
|
from plone.app.discussion.interfaces import IReplyRemovedEvent
|
||||||
from plone.app.discussion.testing import PLONE_APP_DISCUSSION_INTEGRATION_TESTING # noqa
|
from plone.app.discussion.testing import PLONE_APP_DISCUSSION_INTEGRATION_TESTING # noqa
|
||||||
@ -37,10 +38,10 @@ class CommentContentRulesTest(unittest.TestCase):
|
|||||||
self.document = self.portal['doc1']
|
self.document = self.portal['doc1']
|
||||||
|
|
||||||
comment = createObject('plone.Comment')
|
comment = createObject('plone.Comment')
|
||||||
comment.text = "This is a comment"
|
comment.text = 'This is a comment'
|
||||||
comment.author_username = "jim"
|
comment.author_username = 'jim'
|
||||||
comment.author_name = "Jim"
|
comment.author_name = 'Jim'
|
||||||
comment.author_email = "jim@example.com"
|
comment.author_email = 'jim@example.com'
|
||||||
conversation = IConversation(self.document)
|
conversation = IConversation(self.document)
|
||||||
conversation.addComment(comment)
|
conversation.addComment(comment)
|
||||||
|
|
||||||
@ -52,28 +53,28 @@ 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(), long)
|
||||||
|
|
||||||
def testCommentTextStringSubstitution(self):
|
def testCommentTextStringSubstitution(self):
|
||||||
comment_text = getAdapter(self.document, IStringSubstitution,
|
comment_text = getAdapter(self.document, IStringSubstitution,
|
||||||
name=u"comment_text")
|
name=u'comment_text')
|
||||||
self.assertEqual(comment_text(), u"This is a comment")
|
self.assertEqual(comment_text(), u'This is a comment')
|
||||||
|
|
||||||
def testCommentUserIdStringSubstitution(self):
|
def testCommentUserIdStringSubstitution(self):
|
||||||
comment_user_id = getAdapter(self.document, IStringSubstitution,
|
comment_user_id = getAdapter(self.document, IStringSubstitution,
|
||||||
name=u"comment_user_id")
|
name=u'comment_user_id')
|
||||||
self.assertEqual(comment_user_id(), u"jim")
|
self.assertEqual(comment_user_id(), u'jim')
|
||||||
|
|
||||||
def testCommentUserFullNameStringSubstitution(self):
|
def testCommentUserFullNameStringSubstitution(self):
|
||||||
comment_user_fullname = getAdapter(self.document, IStringSubstitution,
|
comment_user_fullname = getAdapter(self.document, IStringSubstitution,
|
||||||
name=u"comment_user_fullname")
|
name=u'comment_user_fullname')
|
||||||
self.assertEqual(comment_user_fullname(), u"Jim")
|
self.assertEqual(comment_user_fullname(), u'Jim')
|
||||||
|
|
||||||
def testCommentUserEmailStringSubstitution(self):
|
def testCommentUserEmailStringSubstitution(self):
|
||||||
comment_user_email = getAdapter(self.document, IStringSubstitution,
|
comment_user_email = getAdapter(self.document, IStringSubstitution,
|
||||||
name=u"comment_user_email")
|
name=u'comment_user_email')
|
||||||
self.assertEqual(comment_user_email(), u"jim@example.com")
|
self.assertEqual(comment_user_email(), u'jim@example.com')
|
||||||
|
|
||||||
|
|
||||||
class ReplyContentRulesTest(unittest.TestCase):
|
class ReplyContentRulesTest(unittest.TestCase):
|
||||||
@ -95,13 +96,14 @@ class ReplyContentRulesTest(unittest.TestCase):
|
|||||||
comment.text = 'This is a comment'
|
comment.text = 'This is a comment'
|
||||||
new_id = replies.addComment(comment)
|
new_id = replies.addComment(comment)
|
||||||
comment = self.document.restrictedTraverse(
|
comment = self.document.restrictedTraverse(
|
||||||
'++conversation++default/%s' % new_id)
|
'++conversation++default/{0}'.format(new_id)
|
||||||
|
)
|
||||||
|
|
||||||
re_comment = createObject('plone.Comment')
|
re_comment = createObject('plone.Comment')
|
||||||
re_comment.text = 'This is a reply'
|
re_comment.text = 'This is a reply'
|
||||||
re_comment.author_username = "julia"
|
re_comment.author_username = 'julia'
|
||||||
re_comment.author_name = "Juliana"
|
re_comment.author_name = 'Juliana'
|
||||||
re_comment.author_email = "julia@example.com"
|
re_comment.author_email = 'julia@example.com'
|
||||||
|
|
||||||
replies = IReplies(comment)
|
replies = IReplies(comment)
|
||||||
replies.addComment(re_comment)
|
replies.addComment(re_comment)
|
||||||
@ -110,7 +112,7 @@ class ReplyContentRulesTest(unittest.TestCase):
|
|||||||
reply_id = getAdapter(
|
reply_id = getAdapter(
|
||||||
self.document,
|
self.document,
|
||||||
IStringSubstitution,
|
IStringSubstitution,
|
||||||
name=u"comment_id"
|
name=u'comment_id'
|
||||||
)
|
)
|
||||||
self.assertIsInstance(reply_id(), long)
|
self.assertIsInstance(reply_id(), long)
|
||||||
|
|
||||||
@ -118,30 +120,30 @@ class ReplyContentRulesTest(unittest.TestCase):
|
|||||||
reply_text = getAdapter(
|
reply_text = getAdapter(
|
||||||
self.document,
|
self.document,
|
||||||
IStringSubstitution,
|
IStringSubstitution,
|
||||||
name=u"comment_text"
|
name=u'comment_text'
|
||||||
)
|
)
|
||||||
self.assertEqual(reply_text(), u"This is a reply")
|
self.assertEqual(reply_text(), u'This is a reply')
|
||||||
|
|
||||||
def testReplyUserIdStringSubstitution(self):
|
def testReplyUserIdStringSubstitution(self):
|
||||||
reply_user_id = getAdapter(
|
reply_user_id = getAdapter(
|
||||||
self.document,
|
self.document,
|
||||||
IStringSubstitution,
|
IStringSubstitution,
|
||||||
name=u"comment_user_id"
|
name=u'comment_user_id'
|
||||||
)
|
)
|
||||||
self.assertEqual(reply_user_id(), u"julia")
|
self.assertEqual(reply_user_id(), u'julia')
|
||||||
|
|
||||||
def testReplyUserFullNameStringSubstitution(self):
|
def testReplyUserFullNameStringSubstitution(self):
|
||||||
reply_user_fullname = getAdapter(
|
reply_user_fullname = getAdapter(
|
||||||
self.document,
|
self.document,
|
||||||
IStringSubstitution,
|
IStringSubstitution,
|
||||||
name=u"comment_user_fullname"
|
name=u'comment_user_fullname'
|
||||||
)
|
)
|
||||||
self.assertEqual(reply_user_fullname(), u"Juliana")
|
self.assertEqual(reply_user_fullname(), u'Juliana')
|
||||||
|
|
||||||
def testReplyUserEmailStringSubstitution(self):
|
def testReplyUserEmailStringSubstitution(self):
|
||||||
reply_user_email = getAdapter(
|
reply_user_email = getAdapter(
|
||||||
self.document,
|
self.document,
|
||||||
IStringSubstitution,
|
IStringSubstitution,
|
||||||
name=u"comment_user_email"
|
name=u'comment_user_email'
|
||||||
)
|
)
|
||||||
self.assertEqual(reply_user_email(), u"julia@example.com")
|
self.assertEqual(reply_user_email(), u'julia@example.com')
|
||||||
|
@ -29,14 +29,14 @@ class RegistryTest(unittest.TestCase):
|
|||||||
def test_discussion_controlpanel_view(self):
|
def test_discussion_controlpanel_view(self):
|
||||||
view = getMultiAdapter(
|
view = getMultiAdapter(
|
||||||
(self.portal, self.portal.REQUEST),
|
(self.portal, self.portal.REQUEST),
|
||||||
name="discussion-controlpanel"
|
name='discussion-controlpanel'
|
||||||
)
|
)
|
||||||
view = view.__of__(self.portal)
|
view = view.__of__(self.portal)
|
||||||
self.assertTrue(view())
|
self.assertTrue(view())
|
||||||
|
|
||||||
def test_discussion_in_controlpanel(self):
|
def test_discussion_in_controlpanel(self):
|
||||||
# Check if discussion is in the control panel
|
# Check if discussion is in the control panel
|
||||||
self.controlpanel = getToolByName(self.portal, "portal_controlpanel")
|
self.controlpanel = getToolByName(self.portal, 'portal_controlpanel')
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
'discussion' in [
|
'discussion' in [
|
||||||
a.getAction(self)['id']
|
a.getAction(self)['id']
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
from Acquisition import aq_base
|
from Acquisition import aq_base
|
||||||
from Acquisition import aq_parent
|
from Acquisition import aq_parent
|
||||||
from Products.CMFCore.utils import getToolByName
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from plone.app.discussion import interfaces
|
from plone.app.discussion import interfaces
|
||||||
@ -13,6 +13,7 @@ from plone.app.testing import setRoles
|
|||||||
from plone.app.testing import TEST_USER_ID
|
from plone.app.testing import TEST_USER_ID
|
||||||
from plone.app.vocabularies.types import BAD_TYPES
|
from plone.app.vocabularies.types import BAD_TYPES
|
||||||
from plone.registry.interfaces import IRegistry
|
from plone.registry.interfaces import IRegistry
|
||||||
|
from Products.CMFCore.utils import getToolByName
|
||||||
from zope import interface
|
from zope import interface
|
||||||
from zope.annotation.interfaces import IAnnotations
|
from zope.annotation.interfaces import IAnnotations
|
||||||
from zope.component import createObject
|
from zope.component import createObject
|
||||||
@ -24,7 +25,7 @@ import unittest2 as unittest
|
|||||||
try:
|
try:
|
||||||
from plone.dexterity.interfaces import IDexterityContent
|
from plone.dexterity.interfaces import IDexterityContent
|
||||||
DEXTERITY = True
|
DEXTERITY = True
|
||||||
except:
|
except ImportError:
|
||||||
DEXTERITY = False
|
DEXTERITY = False
|
||||||
|
|
||||||
|
|
||||||
@ -82,12 +83,12 @@ class ConversationTest(unittest.TestCase):
|
|||||||
conversation = IConversation(self.portal.doc1)
|
conversation = IConversation(self.portal.doc1)
|
||||||
|
|
||||||
comment = createObject('plone.Comment')
|
comment = createObject('plone.Comment')
|
||||||
comment.author_username = "nobody"
|
comment.author_username = 'nobody'
|
||||||
conversation.addComment(comment)
|
conversation.addComment(comment)
|
||||||
comment.manage_permission("View", roles=tuple())
|
comment.manage_permission('View', roles=tuple())
|
||||||
self.assertEqual(0, conversation.total_comments())
|
self.assertEqual(0, conversation.total_comments())
|
||||||
self.assertEqual(None, conversation.last_comment_date)
|
self.assertEqual(None, conversation.last_comment_date)
|
||||||
self.assertEqual(["nobody"], list(conversation.commentators))
|
self.assertEqual(['nobody'], list(conversation.commentators))
|
||||||
self.assertEqual([], list(conversation.public_commentators))
|
self.assertEqual([], list(conversation.public_commentators))
|
||||||
|
|
||||||
def test_delete_comment(self):
|
def test_delete_comment(self):
|
||||||
@ -434,22 +435,22 @@ class ConversationTest(unittest.TestCase):
|
|||||||
# swapped in
|
# swapped in
|
||||||
comment1 = createObject('plone.Comment')
|
comment1 = createObject('plone.Comment')
|
||||||
comment1.text = 'Comment text'
|
comment1.text = 'Comment text'
|
||||||
comment1.author_username = "Jim"
|
comment1.author_username = 'Jim'
|
||||||
conversation.addComment(comment1)
|
conversation.addComment(comment1)
|
||||||
|
|
||||||
comment2 = createObject('plone.Comment')
|
comment2 = createObject('plone.Comment')
|
||||||
comment2.text = 'Comment text'
|
comment2.text = 'Comment text'
|
||||||
comment2.author_username = "Joe"
|
comment2.author_username = 'Joe'
|
||||||
conversation.addComment(comment2)
|
conversation.addComment(comment2)
|
||||||
|
|
||||||
comment3 = createObject('plone.Comment')
|
comment3 = createObject('plone.Comment')
|
||||||
comment3.text = 'Comment text'
|
comment3.text = 'Comment text'
|
||||||
comment3.author_username = "Jack"
|
comment3.author_username = 'Jack'
|
||||||
new_comment3_id = conversation.addComment(comment3)
|
new_comment3_id = conversation.addComment(comment3)
|
||||||
|
|
||||||
comment4 = createObject('plone.Comment')
|
comment4 = createObject('plone.Comment')
|
||||||
comment4.text = 'Comment text'
|
comment4.text = 'Comment text'
|
||||||
comment4.author_username = "Jack"
|
comment4.author_username = 'Jack'
|
||||||
new_comment4_id = conversation.addComment(comment4)
|
new_comment4_id = conversation.addComment(comment4)
|
||||||
|
|
||||||
# check if all commentators are in the commentators list
|
# check if all commentators are in the commentators list
|
||||||
@ -839,16 +840,19 @@ class RepliesTest(unittest.TestCase):
|
|||||||
# Create the nested comment structure
|
# Create the nested comment structure
|
||||||
new_id_1 = replies.addComment(comment1)
|
new_id_1 = replies.addComment(comment1)
|
||||||
comment1 = self.portal.doc1.restrictedTraverse(
|
comment1 = self.portal.doc1.restrictedTraverse(
|
||||||
'++conversation++default/%s' % new_id_1)
|
'++conversation++default/{0}'.format(new_id_1)
|
||||||
|
)
|
||||||
replies_to_comment1 = IReplies(comment1)
|
replies_to_comment1 = IReplies(comment1)
|
||||||
new_id_2 = replies.addComment(comment2)
|
new_id_2 = replies.addComment(comment2)
|
||||||
comment2 = self.portal.doc1.restrictedTraverse(
|
comment2 = self.portal.doc1.restrictedTraverse(
|
||||||
'++conversation++default/%s' % new_id_2)
|
'++conversation++default/{0}'.format(new_id_2)
|
||||||
|
)
|
||||||
replies_to_comment2 = IReplies(comment2)
|
replies_to_comment2 = IReplies(comment2)
|
||||||
|
|
||||||
new_id_1_1 = replies_to_comment1.addComment(comment1_1)
|
new_id_1_1 = replies_to_comment1.addComment(comment1_1)
|
||||||
comment1_1 = self.portal.doc1.restrictedTraverse(
|
comment1_1 = self.portal.doc1.restrictedTraverse(
|
||||||
'++conversation++default/%s' % new_id_1_1)
|
'++conversation++default/{0}'.format(new_id_1_1)
|
||||||
|
)
|
||||||
replies_to_comment1_1 = IReplies(comment1_1)
|
replies_to_comment1_1 = IReplies(comment1_1)
|
||||||
replies_to_comment1_1.addComment(comment1_1_1)
|
replies_to_comment1_1.addComment(comment1_1_1)
|
||||||
|
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from Zope2.App import zcml
|
|
||||||
from plone.app.discussion.interfaces import IConversation
|
from plone.app.discussion.interfaces import IConversation
|
||||||
from plone.app.discussion.interfaces import IReplies
|
from plone.app.discussion.interfaces import IReplies
|
||||||
from plone.app.discussion.testing import PLONE_APP_DISCUSSION_INTEGRATION_TESTING # noqa
|
from plone.app.discussion.testing import PLONE_APP_DISCUSSION_INTEGRATION_TESTING # noqa
|
||||||
from plone.app.testing import setRoles
|
from plone.app.testing import setRoles
|
||||||
from plone.app.testing import TEST_USER_ID
|
from plone.app.testing import TEST_USER_ID
|
||||||
from zope.component import createObject
|
from zope.component import createObject
|
||||||
|
from Zope2.App import zcml
|
||||||
|
|
||||||
import Products.Five
|
import Products.Five
|
||||||
import unittest2 as unittest
|
import unittest2 as unittest
|
||||||
@ -86,7 +86,7 @@ class CommentEventsTest(unittest.TestCase):
|
|||||||
|
|
||||||
</configure>
|
</configure>
|
||||||
"""
|
"""
|
||||||
zcml.load_config("configure.zcml", Products.Five)
|
zcml.load_config('configure.zcml', Products.Five)
|
||||||
zcml.load_string(configure)
|
zcml.load_string(configure)
|
||||||
|
|
||||||
def test_addEvent(self):
|
def test_addEvent(self):
|
||||||
@ -139,7 +139,7 @@ class RepliesEventsTest(unittest.TestCase):
|
|||||||
|
|
||||||
</configure>
|
</configure>
|
||||||
"""
|
"""
|
||||||
zcml.load_config("configure.zcml", Products.Five)
|
zcml.load_config('configure.zcml', Products.Five)
|
||||||
zcml.load_string(configure)
|
zcml.load_string(configure)
|
||||||
|
|
||||||
def test_addEvent(self):
|
def test_addEvent(self):
|
||||||
@ -152,7 +152,8 @@ class RepliesEventsTest(unittest.TestCase):
|
|||||||
comment.text = 'Comment text'
|
comment.text = 'Comment text'
|
||||||
new_id = replies.addComment(comment)
|
new_id = replies.addComment(comment)
|
||||||
comment = self.document.restrictedTraverse(
|
comment = self.document.restrictedTraverse(
|
||||||
'++conversation++default/%s' % new_id)
|
'++conversation++default/{0}'.format(new_id)
|
||||||
|
)
|
||||||
|
|
||||||
re_comment = createObject('plone.Comment')
|
re_comment = createObject('plone.Comment')
|
||||||
re_comment.text = 'Comment text'
|
re_comment.text = 'Comment text'
|
||||||
@ -172,7 +173,8 @@ class RepliesEventsTest(unittest.TestCase):
|
|||||||
comment.text = 'Comment text'
|
comment.text = 'Comment text'
|
||||||
new_id = replies.addComment(comment)
|
new_id = replies.addComment(comment)
|
||||||
comment = self.portal.doc1.restrictedTraverse(
|
comment = self.portal.doc1.restrictedTraverse(
|
||||||
'++conversation++default/%s' % new_id)
|
'++conversation++default/{0}'.format(new_id)
|
||||||
|
)
|
||||||
|
|
||||||
re_comment = createObject('plone.Comment')
|
re_comment = createObject('plone.Comment')
|
||||||
re_comment.text = 'Comment text'
|
re_comment.text = 'Comment text'
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
"""Test for the plone.app.discussion indexers
|
"""Test for the plone.app.discussion indexers
|
||||||
"""
|
"""
|
||||||
from DateTime import DateTime
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from DateTime import DateTime
|
||||||
from plone.app.discussion import catalog
|
from plone.app.discussion import catalog
|
||||||
from plone.app.discussion.interfaces import IConversation
|
from plone.app.discussion.interfaces import IConversation
|
||||||
from plone.app.discussion.testing import PLONE_APP_DISCUSSION_INTEGRATION_TESTING # noqa
|
from plone.app.discussion.testing import PLONE_APP_DISCUSSION_INTEGRATION_TESTING # noqa
|
||||||
@ -39,24 +40,24 @@ class ConversationIndexersTest(unittest.TestCase):
|
|||||||
|
|
||||||
comment1 = createObject('plone.Comment')
|
comment1 = createObject('plone.Comment')
|
||||||
comment1.text = 'Comment Text'
|
comment1.text = 'Comment Text'
|
||||||
comment1.creator = "jim"
|
comment1.creator = 'jim'
|
||||||
comment1.author_username = "Jim"
|
comment1.author_username = 'Jim'
|
||||||
comment1.creation_date = datetime(2006, 9, 17, 14, 18, 12)
|
comment1.creation_date = datetime(2006, 9, 17, 14, 18, 12)
|
||||||
comment1.modification_date = datetime(2006, 9, 17, 14, 18, 12)
|
comment1.modification_date = datetime(2006, 9, 17, 14, 18, 12)
|
||||||
self.new_id1 = conversation.addComment(comment1)
|
self.new_id1 = conversation.addComment(comment1)
|
||||||
|
|
||||||
comment2 = createObject('plone.Comment')
|
comment2 = createObject('plone.Comment')
|
||||||
comment2.text = 'Comment Text'
|
comment2.text = 'Comment Text'
|
||||||
comment2.creator = "emma"
|
comment2.creator = 'emma'
|
||||||
comment2.author_username = "Emma"
|
comment2.author_username = 'Emma'
|
||||||
comment2.creation_date = datetime(2007, 12, 13, 4, 18, 12)
|
comment2.creation_date = datetime(2007, 12, 13, 4, 18, 12)
|
||||||
comment2.modification_date = datetime(2007, 12, 13, 4, 18, 12)
|
comment2.modification_date = datetime(2007, 12, 13, 4, 18, 12)
|
||||||
self.new_id2 = conversation.addComment(comment2)
|
self.new_id2 = conversation.addComment(comment2)
|
||||||
|
|
||||||
comment3 = createObject('plone.Comment')
|
comment3 = createObject('plone.Comment')
|
||||||
comment3.text = 'Comment Text'
|
comment3.text = 'Comment Text'
|
||||||
comment3.creator = "lukas"
|
comment3.creator = 'lukas'
|
||||||
comment3.author_username = "Lukas"
|
comment3.author_username = 'Lukas'
|
||||||
comment3.creation_date = datetime(2009, 4, 12, 11, 12, 12)
|
comment3.creation_date = datetime(2009, 4, 12, 11, 12, 12)
|
||||||
comment3.modification_date = datetime(2009, 4, 12, 11, 12, 12)
|
comment3.modification_date = datetime(2009, 4, 12, 11, 12, 12)
|
||||||
self.new_id3 = conversation.addComment(comment3)
|
self.new_id3 = conversation.addComment(comment3)
|
||||||
@ -118,8 +119,8 @@ class CommentIndexersTest(unittest.TestCase):
|
|||||||
|
|
||||||
comment = createObject('plone.Comment')
|
comment = createObject('plone.Comment')
|
||||||
comment.text = 'Lorem ipsum dolor sit amet.'
|
comment.text = 'Lorem ipsum dolor sit amet.'
|
||||||
comment.creator = "jim"
|
comment.creator = 'jim'
|
||||||
comment.author_name = "Jim"
|
comment.author_name = 'Jim'
|
||||||
comment.creation_date = datetime(2006, 9, 17, 14, 18, 12)
|
comment.creation_date = datetime(2006, 9, 17, 14, 18, 12)
|
||||||
comment.modification_date = datetime(2008, 3, 12, 7, 32, 52)
|
comment.modification_date = datetime(2008, 3, 12, 7, 32, 52)
|
||||||
|
|
||||||
@ -149,7 +150,7 @@ class CommentIndexersTest(unittest.TestCase):
|
|||||||
self.conversation.addComment(comment_long)
|
self.conversation.addComment(comment_long)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
catalog.description(comment_long)(),
|
catalog.description(comment_long)(),
|
||||||
LONG_TEXT_CUT.replace("\n", " ")
|
LONG_TEXT_CUT.replace('\n', ' ')
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_dates(self):
|
def test_dates(self):
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from Products.CMFCore.utils import getToolByName
|
|
||||||
from plone.app.discussion.browser.moderation import BulkActionsView
|
from plone.app.discussion.browser.moderation import BulkActionsView
|
||||||
from plone.app.discussion.browser.moderation import View
|
from plone.app.discussion.browser.moderation import View
|
||||||
from plone.app.discussion.interfaces import IConversation
|
from plone.app.discussion.interfaces import IConversation
|
||||||
from plone.app.discussion.testing import PLONE_APP_DISCUSSION_INTEGRATION_TESTING # noqa
|
from plone.app.discussion.testing import PLONE_APP_DISCUSSION_INTEGRATION_TESTING # noqa
|
||||||
from plone.app.testing import setRoles
|
from plone.app.testing import setRoles
|
||||||
from plone.app.testing import TEST_USER_ID
|
from plone.app.testing import TEST_USER_ID
|
||||||
|
from Products.CMFCore.utils import getToolByName
|
||||||
from zope.component import createObject
|
from zope.component import createObject
|
||||||
|
|
||||||
import unittest
|
import unittest
|
||||||
@ -75,7 +75,7 @@ class ModerationBulkActionsViewTest(unittest.TestCase):
|
|||||||
comment1.Creator = 'Jim'
|
comment1.Creator = 'Jim'
|
||||||
new_id_1 = conversation.addComment(comment1)
|
new_id_1 = conversation.addComment(comment1)
|
||||||
self.comment1 = self.portal.doc1.restrictedTraverse(
|
self.comment1 = self.portal.doc1.restrictedTraverse(
|
||||||
'++conversation++default/%s' % new_id_1
|
'++conversation++default/{0}'.format(new_id_1)
|
||||||
)
|
)
|
||||||
comment2 = createObject('plone.Comment')
|
comment2 = createObject('plone.Comment')
|
||||||
comment2.title = 'Comment 2'
|
comment2.title = 'Comment 2'
|
||||||
@ -83,7 +83,7 @@ class ModerationBulkActionsViewTest(unittest.TestCase):
|
|||||||
comment2.Creator = 'Joe'
|
comment2.Creator = 'Joe'
|
||||||
new_id_2 = conversation.addComment(comment2)
|
new_id_2 = conversation.addComment(comment2)
|
||||||
self.comment2 = self.portal.doc1.restrictedTraverse(
|
self.comment2 = self.portal.doc1.restrictedTraverse(
|
||||||
'++conversation++default/%s' % new_id_2
|
'++conversation++default/{0}'.format(new_id_2)
|
||||||
)
|
)
|
||||||
comment3 = createObject('plone.Comment')
|
comment3 = createObject('plone.Comment')
|
||||||
comment3.title = 'Comment 3'
|
comment3.title = 'Comment 3'
|
||||||
@ -91,7 +91,7 @@ class ModerationBulkActionsViewTest(unittest.TestCase):
|
|||||||
comment3.Creator = 'Emma'
|
comment3.Creator = 'Emma'
|
||||||
new_id_3 = conversation.addComment(comment3)
|
new_id_3 = conversation.addComment(comment3)
|
||||||
self.comment3 = self.portal.doc1.restrictedTraverse(
|
self.comment3 = self.portal.doc1.restrictedTraverse(
|
||||||
'++conversation++default/%s' % new_id_3
|
'++conversation++default/{0}'.format(new_id_3)
|
||||||
)
|
)
|
||||||
self.conversation = conversation
|
self.conversation = conversation
|
||||||
|
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from Acquisition import aq_base
|
from Acquisition import aq_base
|
||||||
|
from plone.app.discussion.interfaces import IConversation
|
||||||
|
from plone.app.discussion.testing import PLONE_APP_DISCUSSION_INTEGRATION_TESTING # noqa
|
||||||
|
from plone.app.testing import setRoles
|
||||||
|
from plone.app.testing import TEST_USER_ID
|
||||||
|
from plone.registry.interfaces import IRegistry
|
||||||
from Products.CMFPlone.interfaces import IMailSchema
|
from Products.CMFPlone.interfaces import IMailSchema
|
||||||
from Products.CMFPlone.tests.utils import MockMailHost
|
from Products.CMFPlone.tests.utils import MockMailHost
|
||||||
from Products.MailHost.interfaces import IMailHost
|
from Products.MailHost.interfaces import IMailHost
|
||||||
from plone.app.discussion.interfaces import IConversation
|
|
||||||
from plone.app.discussion.testing import PLONE_APP_DISCUSSION_INTEGRATION_TESTING # noqa
|
|
||||||
from plone.app.testing import TEST_USER_ID
|
|
||||||
from plone.app.testing import setRoles
|
|
||||||
from plone.registry.interfaces import IRegistry
|
|
||||||
from zope.component import createObject
|
from zope.component import createObject
|
||||||
from zope.component import getSiteManager
|
from zope.component import getSiteManager
|
||||||
from zope.component import getUtility
|
from zope.component import getUtility
|
||||||
@ -32,7 +32,7 @@ class TestUserNotificationUnit(unittest.TestCase):
|
|||||||
# We need to fake a valid mail setup
|
# We need to fake a valid mail setup
|
||||||
registry = getUtility(IRegistry)
|
registry = getUtility(IRegistry)
|
||||||
mail_settings = registry.forInterface(IMailSchema, prefix='plone')
|
mail_settings = registry.forInterface(IMailSchema, prefix='plone')
|
||||||
mail_settings.email_from_address = "portal@plone.test"
|
mail_settings.email_from_address = 'portal@plone.test'
|
||||||
self.mailhost = self.portal.MailHost
|
self.mailhost = self.portal.MailHost
|
||||||
# Enable user notification setting
|
# Enable user notification setting
|
||||||
registry = queryUtility(IRegistry)
|
registry = queryUtility(IRegistry)
|
||||||
@ -40,7 +40,7 @@ class TestUserNotificationUnit(unittest.TestCase):
|
|||||||
'.user_notification_enabled'] = True
|
'.user_notification_enabled'] = True
|
||||||
# Archetypes content types store data as utf-8 encoded strings
|
# Archetypes content types store data as utf-8 encoded strings
|
||||||
# The missing u in front of a string is therefor not missing
|
# The missing u in front of a string is therefor not missing
|
||||||
self.portal.doc1.title = 'Kölle Alaaf' # What is "Fasching"?
|
self.portal.doc1.title = 'Kölle Alaaf' # What is 'Fasching'?
|
||||||
self.conversation = IConversation(self.portal.doc1)
|
self.conversation = IConversation(self.portal.doc1)
|
||||||
|
|
||||||
def beforeTearDown(self):
|
def beforeTearDown(self):
|
||||||
@ -56,7 +56,7 @@ class TestUserNotificationUnit(unittest.TestCase):
|
|||||||
comment = createObject('plone.Comment')
|
comment = createObject('plone.Comment')
|
||||||
comment.text = 'Comment text'
|
comment.text = 'Comment text'
|
||||||
comment.user_notification = True
|
comment.user_notification = True
|
||||||
comment.author_email = "john@plone.test"
|
comment.author_email = 'john@plone.test'
|
||||||
self.conversation.addComment(comment)
|
self.conversation.addComment(comment)
|
||||||
comment = createObject('plone.Comment')
|
comment = createObject('plone.Comment')
|
||||||
comment.text = 'Comment text'
|
comment.text = 'Comment text'
|
||||||
@ -75,11 +75,10 @@ class TestUserNotificationUnit(unittest.TestCase):
|
|||||||
# The output should be encoded in a reasonable manner
|
# The output should be encoded in a reasonable manner
|
||||||
# (in this case quoted-printable):
|
# (in this case quoted-printable):
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
"A comment on \'K=C3=B6lle Alaaf\' has been posted here:"
|
'A comment on "K=C3=B6lle Alaaf" has been posted here:'
|
||||||
in msg)
|
in msg)
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
"http://nohost/plone/d=\noc1/view#%s"
|
'http://nohost/plone/d=\noc1/view#{0}'.format(comment_id)
|
||||||
% comment_id
|
|
||||||
in msg)
|
in msg)
|
||||||
self.assertTrue('Comment text' in msg)
|
self.assertTrue('Comment text' in msg)
|
||||||
self.assertFalse('Approve comment' in msg)
|
self.assertFalse('Approve comment' in msg)
|
||||||
@ -94,7 +93,7 @@ class TestUserNotificationUnit(unittest.TestCase):
|
|||||||
comment = createObject('plone.Comment')
|
comment = createObject('plone.Comment')
|
||||||
comment.text = 'Comment text'
|
comment.text = 'Comment text'
|
||||||
comment.user_notification = True
|
comment.user_notification = True
|
||||||
comment.author_email = "john@plone.test"
|
comment.author_email = 'john@plone.test'
|
||||||
self.conversation.addComment(comment)
|
self.conversation.addComment(comment)
|
||||||
comment = createObject('plone.Comment')
|
comment = createObject('plone.Comment')
|
||||||
comment.text = 'Comment text'
|
comment.text = 'Comment text'
|
||||||
@ -124,7 +123,7 @@ class TestUserNotificationUnit(unittest.TestCase):
|
|||||||
comment = createObject('plone.Comment')
|
comment = createObject('plone.Comment')
|
||||||
comment.text = 'Comment text'
|
comment.text = 'Comment text'
|
||||||
comment.user_notification = True
|
comment.user_notification = True
|
||||||
comment.author_email = "john@plone.test"
|
comment.author_email = 'john@plone.test'
|
||||||
self.conversation.addComment(comment)
|
self.conversation.addComment(comment)
|
||||||
comment = createObject('plone.Comment')
|
comment = createObject('plone.Comment')
|
||||||
comment.text = 'Comment text'
|
comment.text = 'Comment text'
|
||||||
@ -139,12 +138,12 @@ class TestUserNotificationUnit(unittest.TestCase):
|
|||||||
comment = createObject('plone.Comment')
|
comment = createObject('plone.Comment')
|
||||||
comment.text = 'Comment text'
|
comment.text = 'Comment text'
|
||||||
comment.user_notification = True
|
comment.user_notification = True
|
||||||
comment.author_email = "john@plone.test"
|
comment.author_email = 'john@plone.test'
|
||||||
self.conversation.addComment(comment)
|
self.conversation.addComment(comment)
|
||||||
comment = createObject('plone.Comment')
|
comment = createObject('plone.Comment')
|
||||||
comment.text = 'Comment text'
|
comment.text = 'Comment text'
|
||||||
comment.user_notification = True
|
comment.user_notification = True
|
||||||
comment.author_email = "john@plone.test"
|
comment.author_email = 'john@plone.test'
|
||||||
|
|
||||||
self.conversation.addComment(comment)
|
self.conversation.addComment(comment)
|
||||||
|
|
||||||
@ -171,7 +170,7 @@ class TestModeratorNotificationUnit(unittest.TestCase):
|
|||||||
# We need to fake a valid mail setup
|
# We need to fake a valid mail setup
|
||||||
registry = getUtility(IRegistry)
|
registry = getUtility(IRegistry)
|
||||||
mail_settings = registry.forInterface(IMailSchema, prefix='plone')
|
mail_settings = registry.forInterface(IMailSchema, prefix='plone')
|
||||||
mail_settings.email_from_address = "portal@plone.test"
|
mail_settings.email_from_address = 'portal@plone.test'
|
||||||
self.mailhost = self.portal.MailHost
|
self.mailhost = self.portal.MailHost
|
||||||
# Enable comment moderation
|
# Enable comment moderation
|
||||||
self.portal.portal_types['Document'].allow_discussion = True
|
self.portal.portal_types['Document'].allow_discussion = True
|
||||||
@ -187,7 +186,7 @@ class TestModeratorNotificationUnit(unittest.TestCase):
|
|||||||
] = True
|
] = True
|
||||||
# Archetypes content types store data as utf-8 encoded strings
|
# Archetypes content types store data as utf-8 encoded strings
|
||||||
# The missing u in front of a string is therefor not missing
|
# The missing u in front of a string is therefor not missing
|
||||||
self.portal.doc1.title = 'Kölle Alaaf' # What is "Fasching"?
|
self.portal.doc1.title = 'Kölle Alaaf' # What is 'Fasching'?
|
||||||
self.conversation = IConversation(self.portal.doc1)
|
self.conversation = IConversation(self.portal.doc1)
|
||||||
|
|
||||||
def beforeTearDown(self):
|
def beforeTearDown(self):
|
||||||
@ -216,21 +215,28 @@ class TestModeratorNotificationUnit(unittest.TestCase):
|
|||||||
# The output should be encoded in a reasonable manner
|
# The output should be encoded in a reasonable manner
|
||||||
# (in this case quoted-printable):
|
# (in this case quoted-printable):
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
"A comment on \'K=C3=B6lle Alaaf\' has been posted here:"
|
'A comment on "K=C3=B6lle Alaaf" has been posted here:'
|
||||||
in msg)
|
in msg)
|
||||||
self.assertTrue(
|
self.assertIn(
|
||||||
"http://nohost/plone/d=\noc1/view#%s"
|
'http://nohost/plone/d=\noc1/view#{0}'.format(comment_id),
|
||||||
% comment_id
|
msg
|
||||||
in msg)
|
)
|
||||||
self.assertTrue('Comment text' in msg)
|
self.assertIn(
|
||||||
self.assertTrue(
|
'Comment text',
|
||||||
'Approve comment:\nhttp://nohost/plone/doc1/' +
|
msg
|
||||||
'++conversation++default/%s/@@moderat=\ne-publish-comment'
|
)
|
||||||
% comment_id in msg)
|
text = 'Approve comment:\nhttp://nohost/plone/doc1/' \
|
||||||
self.assertTrue(
|
'++conversation++default/{0}/@@moderat=\ne-publish-comment'
|
||||||
'Delete comment:\nhttp://nohost/plone/doc1/' +
|
self.assertIn(
|
||||||
'++conversation++default/%s/@@moderat=\ne-delete-comment'
|
text.format(comment_id),
|
||||||
% comment_id in msg)
|
msg
|
||||||
|
)
|
||||||
|
text = 'Delete comment:\nhttp://nohost/plone/doc1/' \
|
||||||
|
'++conversation++default/{0}/@@moderat=\ne-delete-comment'
|
||||||
|
self.assertIn(
|
||||||
|
text.format(comment_id),
|
||||||
|
msg
|
||||||
|
)
|
||||||
|
|
||||||
def test_notify_moderator_specific_address(self):
|
def test_notify_moderator_specific_address(self):
|
||||||
# A moderator email address can be specified in the control panel.
|
# A moderator email address can be specified in the control panel.
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
from plone.app.discussion.testing import PLONE_APP_DISCUSSION_ROBOT_TESTING
|
from plone.app.discussion.testing import PLONE_APP_DISCUSSION_ROBOT_TESTING
|
||||||
from plone.app.testing import ROBOT_TEST_LEVEL
|
from plone.app.testing import ROBOT_TEST_LEVEL
|
||||||
from plone.testing import layered
|
from plone.testing import layered
|
||||||
|
@ -2,8 +2,6 @@
|
|||||||
"""Test plone.app.discussion workflow and permissions.
|
"""Test plone.app.discussion workflow and permissions.
|
||||||
"""
|
"""
|
||||||
from AccessControl import Unauthorized
|
from AccessControl import Unauthorized
|
||||||
from Products.CMFCore.permissions import View
|
|
||||||
from Products.CMFCore.utils import _checkPermission as checkPerm
|
|
||||||
from plone.app.discussion.interfaces import IConversation
|
from plone.app.discussion.interfaces import IConversation
|
||||||
from plone.app.discussion.interfaces import IDiscussionLayer
|
from plone.app.discussion.interfaces import IDiscussionLayer
|
||||||
from plone.app.discussion.testing import PLONE_APP_DISCUSSION_INTEGRATION_TESTING # noqa
|
from plone.app.discussion.testing import PLONE_APP_DISCUSSION_INTEGRATION_TESTING # noqa
|
||||||
@ -11,6 +9,8 @@ from plone.app.testing import login
|
|||||||
from plone.app.testing import logout
|
from plone.app.testing import logout
|
||||||
from plone.app.testing import setRoles
|
from plone.app.testing import setRoles
|
||||||
from plone.app.testing import TEST_USER_ID
|
from plone.app.testing import TEST_USER_ID
|
||||||
|
from Products.CMFCore.permissions import View
|
||||||
|
from Products.CMFCore.utils import _checkPermission as checkPerm
|
||||||
from zope.component import createObject
|
from zope.component import createObject
|
||||||
from zope.interface import alsoProvides
|
from zope.interface import alsoProvides
|
||||||
|
|
||||||
@ -87,7 +87,7 @@ class PermissionsSetupTest(unittest.TestCase):
|
|||||||
plone.app.discussion assigns this permission to 'Authenticated' as
|
plone.app.discussion assigns this permission to 'Authenticated' as
|
||||||
well to emulate the behavior of the old commenting system.
|
well to emulate the behavior of the old commenting system.
|
||||||
"""
|
"""
|
||||||
ReplyToItemPerm = "Reply to item"
|
ReplyToItemPerm = 'Reply to item'
|
||||||
# should be allowed as Member
|
# should be allowed as Member
|
||||||
self.assertTrue(self.checkPermission(ReplyToItemPerm, self.portal))
|
self.assertTrue(self.checkPermission(ReplyToItemPerm, self.portal))
|
||||||
# should be allowed as Authenticated
|
# should be allowed as Authenticated
|
||||||
@ -126,7 +126,7 @@ class CommentOneStateWorkflowTest(unittest.TestCase):
|
|||||||
cid = conversation.addComment(comment)
|
cid = conversation.addComment(comment)
|
||||||
|
|
||||||
self.comment = self.folder.doc1.restrictedTraverse(
|
self.comment = self.folder.doc1.restrictedTraverse(
|
||||||
'++conversation++default/%s' % cid
|
'++conversation++default/{0}'.format(cid)
|
||||||
)
|
)
|
||||||
|
|
||||||
self.portal.acl_users._doAddUser('member', 'secret', ['Member'], [])
|
self.portal.acl_users._doAddUser('member', 'secret', ['Member'], [])
|
||||||
@ -192,7 +192,8 @@ class CommentReviewWorkflowTest(unittest.TestCase):
|
|||||||
comment.text = 'Comment text'
|
comment.text = 'Comment text'
|
||||||
comment_id = conversation.addComment(comment)
|
comment_id = conversation.addComment(comment)
|
||||||
comment = self.portal.doc1.restrictedTraverse(
|
comment = self.portal.doc1.restrictedTraverse(
|
||||||
'++conversation++default/%s' % comment_id)
|
'++conversation++default/{0}'.format(comment_id)
|
||||||
|
)
|
||||||
|
|
||||||
self.conversation = conversation
|
self.conversation = conversation
|
||||||
self.comment_id = comment_id
|
self.comment_id = comment_id
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
"""The portal_discussion tool, usually accessed via
|
"""The portal_discussion tool, usually accessed via
|
||||||
queryUtility(ICommentingTool). The default implementation delegates to the
|
queryUtility(ICommentingTool). The default implementation delegates to the
|
||||||
standard portal_catalog for indexing comments.
|
standard portal_catalog for indexing comments.
|
||||||
|
|
||||||
BBB support for the old portal_discussion is provided in the bbb package.
|
BBB support for the old portal_discussion is provided in the bbb package.
|
||||||
"""
|
"""
|
||||||
from OFS.SimpleItem import SimpleItem
|
|
||||||
from Products.CMFCore.utils import UniqueObject
|
|
||||||
from Products.CMFCore.utils import getToolByName
|
|
||||||
from interfaces import IComment
|
from interfaces import IComment
|
||||||
from interfaces import ICommentingTool
|
from interfaces import ICommentingTool
|
||||||
|
from OFS.SimpleItem import SimpleItem
|
||||||
|
from Products.CMFCore.utils import getToolByName
|
||||||
|
from Products.CMFCore.utils import UniqueObject
|
||||||
from zope import interface
|
from zope import interface
|
||||||
from zope.component import queryUtility
|
from zope.component import queryUtility
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
from plone.app.discussion.interfaces import IDiscussionSettings
|
from plone.app.discussion.interfaces import IDiscussionSettings
|
||||||
from plone.registry.interfaces import IRegistry
|
from plone.registry.interfaces import IRegistry
|
||||||
from zope.component import getUtility
|
from zope.component import getUtility
|
||||||
|
Loading…
Reference in New Issue
Block a user