2010-08-25 16:03:29 +02:00
|
|
|
# -*- coding: utf-8 -*-
|
2010-07-13 11:19:20 +02:00
|
|
|
from Acquisition import aq_inner
|
2009-08-04 21:36:33 +02:00
|
|
|
|
2010-09-03 22:40:27 +02:00
|
|
|
from AccessControl import Unauthorized
|
2009-08-04 21:36:33 +02:00
|
|
|
from AccessControl import getSecurityManager
|
2009-08-01 23:47:50 +02:00
|
|
|
|
2009-05-26 09:25:14 +02:00
|
|
|
from datetime import datetime
|
2009-06-16 22:53:45 +02:00
|
|
|
from DateTime import DateTime
|
2009-05-26 09:25:14 +02:00
|
|
|
|
2009-05-28 13:39:36 +02:00
|
|
|
from urllib import quote as url_quote
|
|
|
|
|
2010-11-29 23:42:20 +01:00
|
|
|
from zope.i18n import translate
|
|
|
|
from zope.i18nmessageid import Message
|
|
|
|
|
2010-07-13 11:19:20 +02:00
|
|
|
from zope.component import createObject, queryUtility
|
2009-08-04 21:36:33 +02:00
|
|
|
|
2010-07-13 11:19:20 +02:00
|
|
|
from zope.interface import alsoProvides
|
2009-05-28 08:08:55 +02:00
|
|
|
|
2010-07-13 11:19:20 +02:00
|
|
|
from z3c.form import form, field, button, interfaces
|
2010-02-16 21:25:56 +01:00
|
|
|
from z3c.form.interfaces import IFormLayer
|
2010-10-30 17:02:05 +02:00
|
|
|
from z3c.form.browser.checkbox import SingleCheckBoxFieldWidget
|
2009-05-28 08:35:47 +02:00
|
|
|
|
2009-05-22 19:30:52 +02:00
|
|
|
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
|
2009-05-28 08:08:55 +02:00
|
|
|
from Products.CMFCore.utils import getToolByName
|
2009-07-03 14:06:19 +02:00
|
|
|
from Products.statusmessages.interfaces import IStatusMessage
|
2009-06-21 09:48:33 +02:00
|
|
|
|
2009-06-08 10:00:15 +02:00
|
|
|
from plone.registry.interfaces import IRegistry
|
|
|
|
|
2009-05-28 13:39:36 +02:00
|
|
|
from plone.app.layout.viewlets.common import ViewletBase
|
|
|
|
|
2010-08-05 20:40:22 +02:00
|
|
|
from plone.app.discussion import PloneAppDiscussionMessageFactory as _
|
2010-07-13 11:19:20 +02:00
|
|
|
from plone.app.discussion.interfaces import IConversation
|
|
|
|
from plone.app.discussion.interfaces import IComment
|
|
|
|
from plone.app.discussion.interfaces import IReplies
|
|
|
|
from plone.app.discussion.interfaces import IDiscussionSettings
|
|
|
|
from plone.app.discussion.interfaces import ICaptcha
|
2009-08-13 21:21:52 +02:00
|
|
|
|
2009-08-15 12:21:26 +02:00
|
|
|
from plone.app.discussion.browser.validator import CaptchaValidator
|
2009-05-22 19:30:52 +02:00
|
|
|
|
2010-07-13 11:19:20 +02:00
|
|
|
from plone.z3cform import z2
|
2009-08-01 23:47:50 +02:00
|
|
|
from plone.z3cform.fieldsets import extensible
|
|
|
|
|
2011-04-22 16:59:59 +02:00
|
|
|
|
|
|
|
from plone.z3cform.interfaces import IWrappedForm
|
2009-08-28 15:03:16 +02:00
|
|
|
|
2010-11-29 23:42:20 +01:00
|
|
|
COMMENT_DESCRIPTION_PLAIN_TEXT = _(
|
|
|
|
u"comment_description_plain_text",
|
|
|
|
default=u"You can add a comment by filling out the form below. " +
|
2013-04-18 15:46:28 +02:00
|
|
|
u"Plain text formatting.")
|
2010-11-29 23:42:20 +01:00
|
|
|
|
2011-08-05 12:08:39 +02:00
|
|
|
COMMENT_DESCRIPTION_MARKDOWN = _(
|
|
|
|
u"comment_description_markdown",
|
|
|
|
default=u"You can add a comment by filling out the form below. " +
|
2013-04-18 15:46:28 +02:00
|
|
|
u"Plain text formatting. You can use the Markdown syntax for " +
|
|
|
|
u"links and images.")
|
2011-08-05 12:08:39 +02:00
|
|
|
|
2010-11-29 23:42:20 +01:00
|
|
|
COMMENT_DESCRIPTION_INTELLIGENT_TEXT = _(
|
|
|
|
u"comment_description_intelligent_text",
|
|
|
|
default=u"You can add a comment by filling out the form below. " +
|
2013-04-18 15:46:28 +02:00
|
|
|
u"Plain text formatting. Web and email addresses are " +
|
|
|
|
u"transformed into clickable links.")
|
2010-11-29 23:42:20 +01:00
|
|
|
|
2011-01-07 11:20:24 +01:00
|
|
|
COMMENT_DESCRIPTION_MODERATION_ENABLED = _(
|
|
|
|
u"comment_description_moderation_enabled",
|
|
|
|
default=u"Comments are moderated.")
|
|
|
|
|
2010-09-29 09:56:36 +02:00
|
|
|
|
2009-08-01 23:47:50 +02:00
|
|
|
class CommentForm(extensible.ExtensibleForm, form.Form):
|
2009-08-02 19:32:41 +02:00
|
|
|
|
2012-01-14 07:26:01 +01:00
|
|
|
ignoreContext = True # don't use context to get widget data
|
2010-10-28 12:30:10 +02:00
|
|
|
id = None
|
2009-08-04 21:24:47 +02:00
|
|
|
label = _(u"Add a comment")
|
2009-08-05 11:01:14 +02:00
|
|
|
fields = field.Fields(IComment).omit('portal_type',
|
|
|
|
'__parent__',
|
|
|
|
'__name__',
|
|
|
|
'comment_id',
|
|
|
|
'mime_type',
|
|
|
|
'creator',
|
|
|
|
'creation_date',
|
|
|
|
'modification_date',
|
2010-09-29 09:56:36 +02:00
|
|
|
'author_username',
|
|
|
|
'title')
|
2009-08-04 13:35:05 +02:00
|
|
|
|
2010-02-08 13:02:54 +01:00
|
|
|
def updateFields(self):
|
2010-02-08 19:28:03 +01:00
|
|
|
super(CommentForm, self).updateFields()
|
2010-10-30 17:02:05 +02:00
|
|
|
self.fields['user_notification'].widgetFactory = \
|
|
|
|
SingleCheckBoxFieldWidget
|
2010-02-08 19:28:03 +01:00
|
|
|
|
2009-08-04 17:12:15 +02:00
|
|
|
def updateWidgets(self):
|
|
|
|
super(CommentForm, self).updateWidgets()
|
2010-02-08 19:28:03 +01:00
|
|
|
|
|
|
|
# Widgets
|
2009-08-04 17:12:15 +02:00
|
|
|
self.widgets['in_reply_to'].mode = interfaces.HIDDEN_MODE
|
2010-01-18 20:47:46 +01:00
|
|
|
self.widgets['text'].addClass("autoresize")
|
2010-12-16 00:52:56 +01:00
|
|
|
self.widgets['user_notification'].label = _(u"")
|
|
|
|
|
2012-02-15 17:04:29 +01:00
|
|
|
# Rename the id of the text widgets because there can be css-id
|
|
|
|
# clashes with the text field of documents when using and overlay
|
|
|
|
# with TinyMCE.
|
|
|
|
self.widgets['text'].id = "form-widgets-comment-text"
|
|
|
|
|
2010-02-08 19:28:03 +01:00
|
|
|
# Anonymous / Logged-in
|
2012-03-19 16:32:33 +01:00
|
|
|
mtool = getToolByName(self.context, 'portal_membership')
|
|
|
|
if not mtool.isAnonymousUser():
|
2009-08-05 11:01:14 +02:00
|
|
|
self.widgets['author_name'].mode = interfaces.HIDDEN_MODE
|
|
|
|
self.widgets['author_email'].mode = interfaces.HIDDEN_MODE
|
2010-02-08 19:28:03 +01:00
|
|
|
|
2010-10-30 17:02:05 +02:00
|
|
|
registry = queryUtility(IRegistry)
|
|
|
|
settings = registry.forInterface(IDiscussionSettings, check=False)
|
2013-01-04 19:00:24 +01:00
|
|
|
|
|
|
|
if mtool.isAnonymousUser() and not settings.anonymous_email_enabled:
|
|
|
|
self.widgets['author_email'].mode = interfaces.HIDDEN_MODE
|
|
|
|
|
2011-01-26 09:15:14 +01:00
|
|
|
member = mtool.getAuthenticatedMember()
|
|
|
|
member_email = member.getProperty('email')
|
|
|
|
|
2011-03-17 17:15:40 +01:00
|
|
|
# Hide the user_notification checkbox if user notification is disabled
|
2012-01-14 07:26:01 +01:00
|
|
|
# or the user is not logged in. Also check if the user has a valid
|
|
|
|
# email address
|
2013-04-18 15:46:28 +02:00
|
|
|
member_email_is_empty = member_email == ''
|
|
|
|
user_notification_disabled = not settings.user_notification_enabled
|
|
|
|
anon = mtool.isAnonymousUser()
|
|
|
|
if member_email_is_empty or user_notification_disabled or anon:
|
|
|
|
self.widgets['user_notification'].mode = interfaces.HIDDEN_MODE
|
2010-12-16 00:52:56 +01:00
|
|
|
|
2010-01-27 20:20:25 +01:00
|
|
|
def updateActions(self):
|
2010-12-16 00:52:56 +01:00
|
|
|
super(CommentForm, self).updateActions()
|
2010-01-27 20:20:25 +01:00
|
|
|
self.actions['cancel'].addClass("standalone")
|
2010-12-16 00:52:56 +01:00
|
|
|
self.actions['cancel'].addClass("hide")
|
|
|
|
self.actions['comment'].addClass("context")
|
|
|
|
|
|
|
|
@button.buttonAndHandler(_(u"add_comment_button", default=u"Comment"),
|
2010-08-28 21:51:53 +02:00
|
|
|
name='comment')
|
2009-08-04 22:47:25 +02:00
|
|
|
def handleComment(self, action):
|
2009-10-17 11:17:13 +02:00
|
|
|
context = aq_inner(self.context)
|
2012-01-14 07:26:01 +01:00
|
|
|
|
2011-05-27 15:47:15 +02:00
|
|
|
# Check if conversation is enabled on this content object
|
|
|
|
if not self.__parent__.restrictedTraverse(
|
2013-04-18 15:46:28 +02:00
|
|
|
'@@conversation_view'
|
|
|
|
).enabled():
|
2011-05-27 15:47:15 +02:00
|
|
|
raise Unauthorized("Discussion is not enabled for this content "
|
|
|
|
"object.")
|
2012-01-14 07:26:01 +01:00
|
|
|
|
2011-05-27 15:47:15 +02:00
|
|
|
# Validation form
|
2009-08-01 23:47:50 +02:00
|
|
|
data, errors = self.extractData()
|
2010-06-17 20:56:26 +02:00
|
|
|
if errors:
|
|
|
|
return
|
2012-01-14 07:26:01 +01:00
|
|
|
|
2011-05-27 15:47:15 +02:00
|
|
|
# Validate Captcha
|
2010-07-12 15:47:53 +02:00
|
|
|
registry = queryUtility(IRegistry)
|
2010-10-29 12:43:46 +02:00
|
|
|
settings = registry.forInterface(IDiscussionSettings, check=False)
|
2009-08-15 13:35:54 +02:00
|
|
|
portal_membership = getToolByName(self.context, 'portal_membership')
|
2013-04-18 15:46:28 +02:00
|
|
|
captcha_enabled = settings.captcha != 'disabled'
|
|
|
|
anonymous_comments = settings.anonymous_comments
|
|
|
|
anon = portal_membership.isAnonymousUser()
|
|
|
|
if captcha_enabled and anonymous_comments and anon:
|
2010-08-05 17:36:07 +02:00
|
|
|
if not 'captcha' in data:
|
|
|
|
data['captcha'] = u""
|
2010-12-16 00:52:56 +01:00
|
|
|
captcha = CaptchaValidator(self.context,
|
|
|
|
self.request,
|
|
|
|
None,
|
|
|
|
ICaptcha['captcha'],
|
2010-08-05 17:36:07 +02:00
|
|
|
None)
|
|
|
|
captcha.validate(data['captcha'])
|
2012-01-14 07:26:01 +01:00
|
|
|
|
2011-05-27 15:47:15 +02:00
|
|
|
# some attributes are not always set
|
|
|
|
author_name = u""
|
2012-01-14 07:26:01 +01:00
|
|
|
|
2011-05-27 15:47:15 +02:00
|
|
|
# Create comment
|
|
|
|
comment = createObject('plone.Comment')
|
2012-06-17 12:09:25 +02:00
|
|
|
|
|
|
|
# Set comment mime type to current setting in the discussion registry
|
|
|
|
comment.mime_type = settings.text_transform
|
|
|
|
|
2011-05-27 15:47:15 +02:00
|
|
|
# Set comment attributes (including extended comment form attributes)
|
2011-05-26 23:37:37 +02:00
|
|
|
for attribute in self.fields.keys():
|
|
|
|
setattr(comment, attribute, data[attribute])
|
2011-05-27 15:47:15 +02:00
|
|
|
# Make sure author_name is properly encoded
|
2010-06-17 20:56:26 +02:00
|
|
|
if 'author_name' in data:
|
|
|
|
author_name = data['author_name']
|
2010-11-08 18:09:26 +01:00
|
|
|
if isinstance(author_name, str):
|
|
|
|
author_name = unicode(author_name, 'utf-8')
|
2012-01-14 07:26:01 +01:00
|
|
|
|
2011-05-27 15:47:15 +02:00
|
|
|
# Set comment author properties for anonymous users or members
|
2010-12-09 09:11:41 +01:00
|
|
|
can_reply = getSecurityManager().checkPermission('Reply to item',
|
|
|
|
context)
|
2011-05-27 15:47:15 +02:00
|
|
|
portal_membership = getToolByName(self.context, 'portal_membership')
|
2013-04-18 15:46:28 +02:00
|
|
|
if anon and anonymous_comments:
|
2010-09-29 09:56:36 +02:00
|
|
|
# Anonymous Users
|
2010-06-17 20:56:26 +02:00
|
|
|
comment.author_name = author_name
|
2012-09-17 23:51:15 +02:00
|
|
|
comment.author_email = u""
|
|
|
|
comment.user_notification = None
|
2012-01-14 07:26:01 +01:00
|
|
|
comment.creation_date = datetime.utcnow()
|
|
|
|
comment.modification_date = datetime.utcnow()
|
2010-12-09 09:11:41 +01:00
|
|
|
elif not portal_membership.isAnonymousUser() and can_reply:
|
2010-09-29 09:56:36 +02:00
|
|
|
# Member
|
2010-06-17 20:56:26 +02:00
|
|
|
member = portal_membership.getAuthenticatedMember()
|
2014-02-02 14:55:37 +01:00
|
|
|
memberid = member.getId()
|
2013-11-04 16:30:14 +01:00
|
|
|
user = member.getUser()
|
2010-09-29 09:56:36 +02:00
|
|
|
email = member.getProperty('email')
|
|
|
|
fullname = member.getProperty('fullname')
|
|
|
|
if not fullname or fullname == '':
|
|
|
|
fullname = member.getUserName()
|
2010-10-22 12:14:07 +02:00
|
|
|
# memberdata is stored as utf-8 encoded strings
|
|
|
|
elif isinstance(fullname, str):
|
|
|
|
fullname = unicode(fullname, 'utf-8')
|
|
|
|
if email and isinstance(email, str):
|
|
|
|
email = unicode(email, 'utf-8')
|
2013-11-04 16:30:14 +01:00
|
|
|
comment.changeOwnership(user, recursive=False)
|
2014-02-02 14:55:37 +01:00
|
|
|
comment.manage_setLocalRoles(memberid, ["Owner"])
|
|
|
|
comment.creator = memberid
|
|
|
|
comment.author_username = memberid
|
2010-09-29 09:56:36 +02:00
|
|
|
comment.author_name = fullname
|
|
|
|
comment.author_email = email
|
2012-01-14 07:26:01 +01:00
|
|
|
comment.creation_date = datetime.utcnow()
|
|
|
|
comment.modification_date = datetime.utcnow()
|
|
|
|
else: # pragma: no cover
|
2013-04-18 15:46:28 +02:00
|
|
|
raise Unauthorized(
|
|
|
|
u"Anonymous user tries to post a comment, but anonymous "
|
|
|
|
u"commenting is disabled. Or user does not have the "
|
|
|
|
u"'reply to item' permission."
|
|
|
|
)
|
2010-12-16 00:52:56 +01:00
|
|
|
|
2011-05-27 15:47:15 +02:00
|
|
|
# Add comment to conversation
|
|
|
|
conversation = IConversation(self.__parent__)
|
2010-06-17 20:56:26 +02:00
|
|
|
if data['in_reply_to']:
|
|
|
|
# Add a reply to an existing comment
|
2011-05-27 15:47:15 +02:00
|
|
|
conversation_to_reply_to = conversation.get(data['in_reply_to'])
|
|
|
|
replies = IReplies(conversation_to_reply_to)
|
2010-06-17 20:56:26 +02:00
|
|
|
comment_id = replies.addComment(comment)
|
|
|
|
else:
|
|
|
|
# Add a comment to the conversation
|
|
|
|
comment_id = conversation.addComment(comment)
|
|
|
|
|
2011-05-27 15:47:15 +02:00
|
|
|
# Redirect after form submit:
|
2010-12-16 00:52:56 +01:00
|
|
|
# If a user posts a comment and moderation is enabled, a message is
|
|
|
|
# shown to the user that his/her comment awaits moderation. If the user
|
|
|
|
# has 'review comments' permission, he/she is redirected directly
|
2010-12-01 07:34:05 +01:00
|
|
|
# to the comment.
|
2010-12-16 00:52:56 +01:00
|
|
|
can_review = getSecurityManager().checkPermission('Review comments',
|
2010-08-25 11:19:28 +02:00
|
|
|
context)
|
2011-05-27 15:47:15 +02:00
|
|
|
workflowTool = getToolByName(context, 'portal_workflow')
|
2013-03-27 17:06:14 +01:00
|
|
|
comment_review_state = workflowTool.getInfoFor(
|
2013-04-18 15:46:28 +02:00
|
|
|
comment,
|
|
|
|
'review_state',
|
2013-03-27 17:06:14 +01:00
|
|
|
None
|
|
|
|
)
|
2010-12-01 07:34:05 +01:00
|
|
|
if comment_review_state == 'pending' and not can_review:
|
2010-06-17 20:56:26 +02:00
|
|
|
# Show info message when comment moderation is enabled
|
|
|
|
IStatusMessage(self.context.REQUEST).addStatusMessage(
|
|
|
|
_("Your comment awaits moderator approval."),
|
|
|
|
type="info")
|
2010-08-09 14:25:14 +02:00
|
|
|
self.request.response.redirect(self.action)
|
2010-06-17 20:56:26 +02:00
|
|
|
else:
|
|
|
|
# Redirect to comment (inside a content object page)
|
2010-08-09 14:25:14 +02:00
|
|
|
self.request.response.redirect(self.action + '#' + str(comment_id))
|
2009-08-04 16:51:34 +02:00
|
|
|
|
2009-08-04 21:24:47 +02:00
|
|
|
@button.buttonAndHandler(_(u"Cancel"))
|
|
|
|
def handleCancel(self, action):
|
|
|
|
# This method should never be called, it's only there to show
|
|
|
|
# a cancel button that is handled by a jQuery method.
|
2012-01-14 07:26:01 +01:00
|
|
|
pass # pragma: no cover
|
2009-08-01 23:47:50 +02:00
|
|
|
|
2010-01-18 10:54:27 +01:00
|
|
|
|
2010-02-16 21:25:56 +01:00
|
|
|
class CommentsViewlet(ViewletBase):
|
2009-05-25 20:59:25 +02:00
|
|
|
|
2009-08-01 23:47:50 +02:00
|
|
|
form = CommentForm
|
2009-08-20 04:09:53 +02:00
|
|
|
index = ViewPageTemplateFile('comments.pt')
|
2009-08-01 23:47:50 +02:00
|
|
|
|
2010-02-16 21:25:56 +01:00
|
|
|
def update(self):
|
|
|
|
super(CommentsViewlet, self).update()
|
2013-04-18 15:46:28 +02:00
|
|
|
discussion_allowed = self.is_discussion_allowed()
|
|
|
|
anonymous_allowed_or_can_reply = (
|
|
|
|
self.is_anonymous()
|
|
|
|
and self.anonymous_discussion_allowed()
|
|
|
|
or self.can_reply()
|
|
|
|
)
|
|
|
|
if discussion_allowed and anonymous_allowed_or_can_reply:
|
2011-06-16 12:59:25 +02:00
|
|
|
z2.switch_on(self, request_layer=IFormLayer)
|
|
|
|
self.form = self.form(aq_inner(self.context), self.request)
|
|
|
|
alsoProvides(self.form, IWrappedForm)
|
|
|
|
self.form.update()
|
2009-08-01 23:47:50 +02:00
|
|
|
|
|
|
|
# view methods
|
|
|
|
|
2009-05-28 08:35:47 +02:00
|
|
|
def can_reply(self):
|
2010-10-06 15:55:57 +02:00
|
|
|
"""Returns true if current user has the 'Reply to item' permission.
|
2010-12-09 09:11:41 +01:00
|
|
|
"""
|
2010-12-16 00:52:56 +01:00
|
|
|
return getSecurityManager().checkPermission('Reply to item',
|
2010-08-25 11:19:28 +02:00
|
|
|
aq_inner(self.context))
|
2009-05-28 08:35:47 +02:00
|
|
|
|
2010-11-03 12:26:18 +01:00
|
|
|
def can_manage(self):
|
|
|
|
"""We keep this method for <= 1.0b9 backward compatibility. Since we do
|
|
|
|
not want any API changes in beta releases.
|
|
|
|
"""
|
|
|
|
return self.can_review()
|
2010-12-16 00:52:56 +01:00
|
|
|
|
2010-10-06 15:55:57 +02:00
|
|
|
def can_review(self):
|
|
|
|
"""Returns true if current user has the 'Review comments' permission.
|
|
|
|
"""
|
2010-12-16 00:52:56 +01:00
|
|
|
return getSecurityManager().checkPermission('Review comments',
|
2010-08-25 11:19:28 +02:00
|
|
|
aq_inner(self.context))
|
2010-12-16 00:52:56 +01:00
|
|
|
|
2009-05-28 08:35:47 +02:00
|
|
|
def is_discussion_allowed(self):
|
2009-06-18 22:57:47 +02:00
|
|
|
context = aq_inner(self.context)
|
2011-04-15 06:29:46 +02:00
|
|
|
return context.restrictedTraverse('@@conversation_view').enabled()
|
2009-05-25 20:59:25 +02:00
|
|
|
|
2010-11-29 23:42:20 +01:00
|
|
|
def comment_transform_message(self):
|
|
|
|
"""Returns the description that shows up above the comment text,
|
2011-01-07 11:20:24 +01:00
|
|
|
dependent on the text_transform setting and the comment moderation
|
|
|
|
workflow in the discussion control panel.
|
2010-11-29 23:42:20 +01:00
|
|
|
"""
|
2011-01-07 11:20:24 +01:00
|
|
|
context = aq_inner(self.context)
|
2010-11-29 23:42:20 +01:00
|
|
|
registry = queryUtility(IRegistry)
|
|
|
|
settings = registry.forInterface(IDiscussionSettings, check=False)
|
2010-12-16 00:52:56 +01:00
|
|
|
|
2011-01-07 11:20:24 +01:00
|
|
|
# text transform setting
|
2010-11-29 23:42:20 +01:00
|
|
|
if settings.text_transform == "text/x-web-intelligent":
|
2011-03-17 17:15:40 +01:00
|
|
|
message = translate(Message(COMMENT_DESCRIPTION_INTELLIGENT_TEXT),
|
|
|
|
context=self.request)
|
2011-08-05 12:08:39 +02:00
|
|
|
elif settings.text_transform == "text/x-web-markdown":
|
|
|
|
message = translate(Message(COMMENT_DESCRIPTION_MARKDOWN),
|
|
|
|
context=self.request)
|
2010-11-29 23:42:20 +01:00
|
|
|
else:
|
2011-03-26 06:04:22 +01:00
|
|
|
message = translate(Message(COMMENT_DESCRIPTION_PLAIN_TEXT),
|
|
|
|
context=self.request)
|
2011-03-17 17:15:40 +01:00
|
|
|
|
2011-01-07 11:20:24 +01:00
|
|
|
# comment workflow
|
|
|
|
wftool = getToolByName(context, "portal_workflow", None)
|
2012-03-15 13:06:00 +01:00
|
|
|
workflow_chain = wftool.getChainForPortalType('Discussion Item')
|
|
|
|
if workflow_chain:
|
|
|
|
comment_workflow = workflow_chain[0]
|
|
|
|
comment_workflow = wftool[comment_workflow]
|
|
|
|
# check if the current workflow implements a pending state. If this
|
|
|
|
# is true comments are moderated
|
|
|
|
if 'pending' in comment_workflow.states:
|
|
|
|
message = message + " " + \
|
|
|
|
translate(Message(COMMENT_DESCRIPTION_MODERATION_ENABLED),
|
|
|
|
context=self.request)
|
2011-03-17 17:15:40 +01:00
|
|
|
|
2010-11-29 23:42:20 +01:00
|
|
|
return message
|
|
|
|
|
2009-10-07 22:07:55 +02:00
|
|
|
def has_replies(self, workflow_actions=False):
|
|
|
|
"""Returns true if there are replies.
|
|
|
|
"""
|
2010-08-24 13:25:17 +02:00
|
|
|
if self.get_replies(workflow_actions) is not None:
|
2010-01-22 20:02:41 +01:00
|
|
|
try:
|
|
|
|
self.get_replies(workflow_actions).next()
|
|
|
|
return True
|
2012-01-14 07:26:01 +01:00
|
|
|
except StopIteration: # pragma: no cover
|
2010-10-03 21:41:02 +02:00
|
|
|
pass
|
2010-01-22 20:02:41 +01:00
|
|
|
return False
|
2009-10-07 22:07:55 +02:00
|
|
|
|
2009-06-13 18:46:37 +02:00
|
|
|
def get_replies(self, workflow_actions=False):
|
2009-10-07 22:07:55 +02:00
|
|
|
"""Returns all replies to a content object.
|
|
|
|
|
|
|
|
If workflow_actions is false, only published
|
|
|
|
comments are returned.
|
|
|
|
|
|
|
|
If workflow actions is true, comments are
|
|
|
|
returned with workflow actions.
|
|
|
|
"""
|
2009-06-13 18:46:37 +02:00
|
|
|
context = aq_inner(self.context)
|
2013-08-15 00:01:22 +02:00
|
|
|
conversation = IConversation(context, None)
|
|
|
|
|
|
|
|
if conversation is None:
|
|
|
|
return iter([])
|
2009-06-13 18:46:37 +02:00
|
|
|
|
2009-10-07 22:07:55 +02:00
|
|
|
wf = getToolByName(context, 'portal_workflow')
|
2009-06-13 20:02:59 +02:00
|
|
|
|
2009-10-07 22:07:55 +02:00
|
|
|
# workflow_actions is only true when user
|
|
|
|
# has 'Manage portal' permission
|
|
|
|
|
|
|
|
def replies_with_workflow_actions():
|
|
|
|
# Generator that returns replies dict with workflow actions
|
2009-06-13 20:02:59 +02:00
|
|
|
for r in conversation.getThreads():
|
|
|
|
comment_obj = r['comment']
|
|
|
|
# list all possible workflow actions
|
2013-04-18 15:46:28 +02:00
|
|
|
actions = [
|
|
|
|
a for a in wf.listActionInfos(object=comment_obj)
|
|
|
|
if a['category'] == 'workflow' and a['allowed']
|
|
|
|
]
|
2009-06-13 20:02:59 +02:00
|
|
|
r = r.copy()
|
|
|
|
r['actions'] = actions
|
|
|
|
yield r
|
|
|
|
|
2009-10-07 22:07:55 +02:00
|
|
|
def published_replies():
|
|
|
|
# Generator that returns replies dict with workflow status.
|
|
|
|
for r in conversation.getThreads():
|
|
|
|
comment_obj = r['comment']
|
|
|
|
workflow_status = wf.getInfoFor(comment_obj, 'review_state')
|
|
|
|
if workflow_status == 'published':
|
|
|
|
r = r.copy()
|
|
|
|
r['workflow_status'] = workflow_status
|
|
|
|
yield r
|
2010-12-16 00:52:56 +01:00
|
|
|
|
2009-06-13 18:46:37 +02:00
|
|
|
# Return all direct replies
|
2012-06-13 13:17:22 +02:00
|
|
|
if len(conversation.objectIds()):
|
2009-06-13 18:46:37 +02:00
|
|
|
if workflow_actions:
|
2009-06-13 20:02:59 +02:00
|
|
|
return replies_with_workflow_actions()
|
2009-06-13 18:46:37 +02:00
|
|
|
else:
|
2009-10-07 22:07:55 +02:00
|
|
|
return published_replies()
|
2009-05-25 20:59:25 +02:00
|
|
|
|
2010-01-18 12:38:57 +01:00
|
|
|
def get_commenter_home_url(self, username=None):
|
2009-06-07 22:58:41 +02:00
|
|
|
if username is None:
|
|
|
|
return None
|
|
|
|
else:
|
|
|
|
return "%s/author/%s" % (self.context.portal_url(), username)
|
|
|
|
|
2010-01-18 12:38:57 +01:00
|
|
|
def get_commenter_portrait(self, username=None):
|
2009-06-07 22:58:41 +02:00
|
|
|
|
|
|
|
if username is None:
|
2009-06-14 13:10:45 +02:00
|
|
|
# return the default user image if no username is given
|
2013-11-13 15:53:12 +01:00
|
|
|
return 'defaultUser.png'
|
2009-06-07 22:58:41 +02:00
|
|
|
else:
|
2010-08-25 11:19:28 +02:00
|
|
|
portal_membership = getToolByName(self.context,
|
|
|
|
'portal_membership',
|
|
|
|
None)
|
2013-04-18 15:46:28 +02:00
|
|
|
return portal_membership\
|
|
|
|
.getPersonalPortrait(username)\
|
|
|
|
.absolute_url()
|
2009-06-07 22:58:41 +02:00
|
|
|
|
2009-06-08 10:00:15 +02:00
|
|
|
def anonymous_discussion_allowed(self):
|
|
|
|
# Check if anonymous comments are allowed in the registry
|
2010-07-12 15:47:53 +02:00
|
|
|
registry = queryUtility(IRegistry)
|
2010-10-30 17:02:05 +02:00
|
|
|
settings = registry.forInterface(IDiscussionSettings, check=False)
|
2009-06-08 10:00:15 +02:00
|
|
|
return settings.anonymous_comments
|
|
|
|
|
2009-06-08 10:23:18 +02:00
|
|
|
def show_commenter_image(self):
|
|
|
|
# Check if showing commenter image is enabled in the registry
|
2010-07-12 15:47:53 +02:00
|
|
|
registry = queryUtility(IRegistry)
|
2010-10-29 12:43:46 +02:00
|
|
|
settings = registry.forInterface(IDiscussionSettings, check=False)
|
2009-06-08 10:23:18 +02:00
|
|
|
return settings.show_commenter_image
|
|
|
|
|
2009-05-28 08:35:47 +02:00
|
|
|
def is_anonymous(self):
|
2010-12-16 00:52:56 +01:00
|
|
|
portal_membership = getToolByName(self.context,
|
|
|
|
'portal_membership',
|
2010-08-25 11:19:28 +02:00
|
|
|
None)
|
2010-02-16 21:25:56 +01:00
|
|
|
return portal_membership.isAnonymousUser()
|
2010-12-16 00:52:56 +01:00
|
|
|
|
2009-05-28 13:39:36 +02:00
|
|
|
def login_action(self):
|
2010-08-28 22:19:58 +02:00
|
|
|
return '%s/login_form?came_from=%s' % \
|
|
|
|
(self.navigation_root_url,
|
|
|
|
url_quote(self.request.get('URL', '')),)
|
2009-05-28 08:35:47 +02:00
|
|
|
|
2009-05-26 09:25:14 +02:00
|
|
|
def format_time(self, time):
|
2009-06-16 22:53:45 +02:00
|
|
|
# We have to transform Python datetime into Zope DateTime
|
2009-06-21 22:33:02 +02:00
|
|
|
# before we can call toLocalizedTime.
|
2009-06-16 22:53:45 +02:00
|
|
|
util = getToolByName(self.context, 'translation_service')
|
2010-09-07 14:03:31 +02:00
|
|
|
zope_time = DateTime(time.isoformat())
|
2009-08-20 04:09:53 +02:00
|
|
|
return util.toLocalizedTime(zope_time, long_format=True)
|