This commit is contained in:
Jens W. Klein
2022-05-01 23:14:09 +02:00
parent e72d86b985
commit 34b758f2bd
38 changed files with 2155 additions and 2179 deletions
+128 -116
View File
@@ -44,57 +44,66 @@ import six
COMMENT_TITLE = _(
u'comment_title',
default=u'${author_name} on ${content}',
)
u"comment_title",
default=u"${author_name} on ${content}",
)
MAIL_NOTIFICATION_MESSAGE = _(
u'mail_notification_message',
u"mail_notification_message",
default=u'A comment on "${title}" '
u'has been posted here: ${link}\n\n'
u'---\n'
u'${text}\n'
u'---\n',
)
u"has been posted here: ${link}\n\n"
u"---\n"
u"${text}\n"
u"---\n",
)
MAIL_NOTIFICATION_MESSAGE_MODERATOR = _(
u'mail_notification_message_moderator2',
u"mail_notification_message_moderator2",
default=u'A comment on "${title}" '
u'has been posted by ${commentator}\n'
u'here: ${link}\n\n'
u'---\n\n'
u'${text}\n\n'
u'---\n\n'
u'Log in to moderate.\n\n',
)
u"has been posted by ${commentator}\n"
u"here: ${link}\n\n"
u"---\n\n"
u"${text}\n\n"
u"---\n\n"
u"Log in to moderate.\n\n",
)
logger = logging.getLogger('plone.app.discussion')
logger = logging.getLogger("plone.app.discussion")
@implementer(IComment)
class Comment(CatalogAware, WorkflowAware, DynamicType, Traversable,
RoleManager, Owned, Implicit, Persistent):
class Comment(
CatalogAware,
WorkflowAware,
DynamicType,
Traversable,
RoleManager,
Owned,
Implicit,
Persistent,
):
"""A comment.
This object attempts to be as lightweight as possible. We implement a
number of standard methods instead of subclassing, to have total control
over what goes into the object.
"""
security = ClassSecurityInfo()
meta_type = portal_type = 'Discussion Item'
meta_type = portal_type = "Discussion Item"
# This needs to be kept in sync with types/Discussion_Item.xml title
fti_title = 'Comment'
fti_title = "Comment"
__parent__ = None
comment_id = None # long
in_reply_to = None # long
title = u''
title = u""
mime_type = None
text = u''
text = u""
creator = None
creation_date = None
@@ -113,14 +122,17 @@ class Comment(CatalogAware, WorkflowAware, DynamicType, Traversable,
def __init__(self):
self.creation_date = self.modification_date = datetime.utcnow()
self.mime_type = 'text/plain'
self.mime_type = "text/plain"
user = getSecurityManager().getUser()
if user and user.getId():
aclpath = [x for x in user.getPhysicalPath() if x]
self._owner = (aclpath, user.getId(),)
self._owner = (
aclpath,
user.getId(),
)
self.__ac_local_roles__ = {
user.getId(): ['Owner'],
user.getId(): ["Owner"],
}
@property
@@ -137,32 +149,32 @@ class Comment(CatalogAware, WorkflowAware, DynamicType, Traversable,
def getText(self, targetMimetype=None):
"""The body text of a comment."""
transforms = getToolByName(self, 'portal_transforms')
transforms = getToolByName(self, "portal_transforms")
if targetMimetype is None:
targetMimetype = 'text/x-html-safe'
targetMimetype = "text/x-html-safe"
sourceMimetype = getattr(self, 'mime_type', None)
sourceMimetype = getattr(self, "mime_type", None)
if sourceMimetype is None:
registry = queryUtility(IRegistry)
settings = registry.forInterface(IDiscussionSettings, check=False)
sourceMimetype = settings.text_transform
text = self.text
if text is None:
return ''
return ""
if six.PY2 and isinstance(text, six.text_type):
text = text.encode('utf8')
text = text.encode("utf8")
transform = transforms.convertTo(
targetMimetype,
text,
context=self,
mimetype=sourceMimetype)
targetMimetype, text, context=self, mimetype=sourceMimetype
)
if transform:
return transform.getData()
else:
logger = logging.getLogger('plone.app.discussion')
msg = u'Transform "{0}" => "{1}" not available. Failed to ' \
u'transform comment "{2}".'
logger = logging.getLogger("plone.app.discussion")
msg = (
u'Transform "{0}" => "{1}" not available. Failed to '
u'transform comment "{2}".'
)
logger.error(
msg.format(
sourceMimetype,
@@ -182,8 +194,8 @@ class Comment(CatalogAware, WorkflowAware, DynamicType, Traversable,
author_name = translate(
Message(
_(
u'label_anonymous',
default=u'Anonymous',
u"label_anonymous",
default=u"Anonymous",
),
),
)
@@ -194,9 +206,14 @@ class Comment(CatalogAware, WorkflowAware, DynamicType, Traversable,
# conversation, the parent of the conversation is the content object).
content = aq_base(self.__parent__.__parent__)
title = translate(
Message(COMMENT_TITLE,
mapping={'author_name': safe_unicode(author_name),
'content': safe_unicode(content.Title())}))
Message(
COMMENT_TITLE,
mapping={
"author_name": safe_unicode(author_name),
"content": safe_unicode(content.Title()),
},
)
)
return title
def Creator(self):
@@ -224,25 +241,23 @@ CommentFactory = Factory(Comment)
def notify_workflow(obj, event):
"""Tell the workflow tool when a comment is added
"""
tool = getToolByName(obj, 'portal_workflow', None)
"""Tell the workflow tool when a comment is added"""
tool = getToolByName(obj, "portal_workflow", None)
if tool is not None:
tool.notifyCreated(obj)
def notify_content_object(obj, event):
"""Tell the content object when a comment is added
"""
"""Tell the content object when a comment is added"""
content_obj = aq_parent(aq_parent(obj))
content_obj.reindexObject(idxs=('total_comments',
'last_comment_date',
'commentators'))
content_obj.reindexObject(
idxs=("total_comments", "last_comment_date", "commentators")
)
def notify_content_object_deleted(obj, event):
"""Remove all comments of a content object when the content object has been
deleted.
deleted.
"""
if IAnnotatable.providedBy(obj):
conversation = IConversation(obj)
@@ -251,40 +266,40 @@ def notify_content_object_deleted(obj, event):
def notify_comment_added(obj, event):
""" Notify custom discussion events when a comment is added or replied
"""
"""Notify custom discussion events when a comment is added or replied"""
conversation = aq_parent(obj)
context = aq_parent(conversation)
if getattr(obj, 'in_reply_to', None):
if getattr(obj, "in_reply_to", None):
return notify(ReplyAddedEvent(context, obj))
return notify(CommentAddedEvent(context, obj))
def notify_comment_modified(obj, event):
""" Notify custom discussion events when a comment, or a reply, is modified
"""
"""Notify custom discussion events when a comment, or a reply, is modified"""
conversation = aq_parent(obj)
context = aq_parent(conversation)
if getattr(obj, 'in_reply_to', None):
if getattr(obj, "in_reply_to", None):
return notify(ReplyModifiedEvent(context, obj))
return notify(CommentModifiedEvent(context, obj))
def notify_comment_removed(obj, event):
""" Notify custom discussion events when a comment or reply is removed
"""
"""Notify custom discussion events when a comment or reply is removed"""
conversation = aq_parent(obj)
context = aq_parent(conversation)
if getattr(obj, 'in_reply_to', None):
if getattr(obj, "in_reply_to", None):
return notify(ReplyRemovedEvent(context, obj))
return notify(CommentRemovedEvent(context, obj))
def notify_content_object_moved(obj, event):
"""Update all comments of a content object that has been moved.
"""
if event.oldParent is None or event.newParent is None \
or event.oldName is None or event.newName is None:
"""Update all comments of a content object that has been moved."""
if (
event.oldParent is None
or event.newParent is None
or event.oldName is None
or event.newName is None
):
return
# This method is also called for sublocations of moved objects. We
@@ -293,21 +308,19 @@ def notify_content_object_moved(obj, event):
# in the object hierarchy. The object is already moved at this point. so
# obj.getPhysicalPath retruns the new path get the part of the path that
# was moved.
moved_path = obj.getPhysicalPath()[
len(event.newParent.getPhysicalPath()) + 1:
]
moved_path = obj.getPhysicalPath()[len(event.newParent.getPhysicalPath()) + 1 :]
# Remove comments at the old location from catalog
catalog = getToolByName(obj, 'portal_catalog')
old_path = '/'.join(
event.oldParent.getPhysicalPath() +
(event.oldName,) +
moved_path,
catalog = getToolByName(obj, "portal_catalog")
old_path = "/".join(
event.oldParent.getPhysicalPath() + (event.oldName,) + moved_path,
)
brains = catalog.searchResults(
dict(
path={"query": old_path},
portal_type="Discussion Item",
)
)
brains = catalog.searchResults(dict(
path={'query': old_path},
portal_type='Discussion Item',
))
for brain in brains:
catalog.uncatalog_object(brain.getPath())
# Reindex comment at the new location
@@ -320,11 +333,11 @@ def notify_content_object_moved(obj, event):
def notify_user(obj, event):
"""Tell users when a comment has been added.
This method composes and sends emails to all users that have added a
comment to this conversation and enabled user notification.
This method composes and sends emails to all users that have added a
comment to this conversation and enabled user notification.
This requires the user_notification setting to be enabled in the
discussion control panel.
This requires the user_notification setting to be enabled in the
discussion control panel.
"""
# Check if user notification is enabled
@@ -334,9 +347,9 @@ def notify_user(obj, event):
return
# Get informations that are necessary to send an email
mail_host = getToolByName(obj, 'MailHost')
mail_host = getToolByName(obj, "MailHost")
registry = getUtility(IRegistry)
mail_settings = registry.forInterface(IMailSchema, prefix='plone')
mail_settings = registry.forInterface(IMailSchema, prefix="plone")
sender = mail_settings.email_from_address
# Check if a sender address is available
@@ -360,15 +373,14 @@ def notify_user(obj, event):
if not emails:
return
subject = translate(_(u'A comment has been posted.'),
context=obj.REQUEST)
subject = translate(_(u"A comment has been posted."), context=obj.REQUEST)
message = translate(
Message(
MAIL_NOTIFICATION_MESSAGE,
mapping={
'title': safe_unicode(content_object.title),
'link': content_object.absolute_url() + '/view#' + obj.id,
'text': obj.text,
"title": safe_unicode(content_object.title),
"link": content_object.absolute_url() + "/view#" + obj.id,
"text": obj.text,
},
),
context=obj.REQUEST,
@@ -381,12 +393,11 @@ def notify_user(obj, event):
email,
sender,
subject,
charset='utf-8',
charset="utf-8",
)
except SMTPException:
logger.error(
'SMTP exception while trying to send an ' +
'email from %s to %s',
"SMTP exception while trying to send an " + "email from %s to %s",
sender,
email,
)
@@ -395,15 +406,15 @@ def notify_user(obj, event):
def notify_moderator(obj, event):
"""Tell the moderator when a comment needs attention.
This method sends an email to the moderator if comment moderation a new
comment has been added that needs to be approved.
This method sends an email to the moderator if comment moderation a new
comment has been added that needs to be approved.
The moderator_notification setting has to be enabled in the discussion
control panel.
The moderator_notification setting has to be enabled in the discussion
control panel.
Configure the moderator e-mail address in the discussion control panel.
If no moderator is configured but moderator notifications are turned on,
the site admin email (from the mail control panel) will be used.
Configure the moderator e-mail address in the discussion control panel.
If no moderator is configured but moderator notifications are turned on,
the site admin email (from the mail control panel) will be used.
"""
# Check if moderator notification is enabled
registry = queryUtility(IRegistry)
@@ -412,9 +423,9 @@ def notify_moderator(obj, event):
return
# Get informations that are necessary to send an email
mail_host = getToolByName(obj, 'MailHost')
mail_host = getToolByName(obj, "MailHost")
registry = getUtility(IRegistry)
mail_settings = registry.forInterface(IMailSchema, prefix='plone')
mail_settings = registry.forInterface(IMailSchema, prefix="plone")
sender = mail_settings.email_from_address
if settings.moderator_email:
@@ -430,22 +441,23 @@ def notify_moderator(obj, event):
content_object = aq_parent(conversation)
# Compose email
subject = translate(_(u'A comment has been posted.'), context=obj.REQUEST)
subject = translate(_(u"A comment has been posted."), context=obj.REQUEST)
message = translate(
Message(
MAIL_NOTIFICATION_MESSAGE_MODERATOR,
mapping={
'title': safe_unicode(content_object.title),
'link': content_object.absolute_url() + '/view#' + obj.id,
'text': obj.text,
'commentator': obj.author_email or translate(
Message(
_(
u'label_anonymous',
default=u'Anonymous',
),
"title": safe_unicode(content_object.title),
"link": content_object.absolute_url() + "/view#" + obj.id,
"text": obj.text,
"commentator": obj.author_email
or translate(
Message(
_(
u"label_anonymous",
default=u"Anonymous",
),
)
),
),
},
),
context=obj.REQUEST,
@@ -453,12 +465,12 @@ def notify_moderator(obj, event):
# Send email
try:
mail_host.send(message, mto, sender, subject, charset='utf-8')
mail_host.send(message, mto, sender, subject, charset="utf-8")
except SMTPException as e:
logger.error(
'SMTP exception (%s) while trying to send an ' +
'email notification to the comment moderator ' +
'(from %s to %s, message: %s)',
"SMTP exception (%s) while trying to send an "
+ "email notification to the comment moderator "
+ "(from %s to %s, message: %s)",
e,
sender,
mto,