plone.app.discussion/plone/app/discussion/browser/migration.py

175 lines
6.6 KiB
Python

from datetime import datetime
from Acquisition import aq_inner, aq_parent
from Products.Five.browser import BrowserView
from Products.CMFCore.utils import getToolByName
from Products.CMFCore.interfaces._content import IDiscussionResponse
import transaction
from plone.app.discussion.comment import CommentFactory
from plone.app.discussion.interfaces import IConversation, IReplies, IComment
class View(BrowserView):
"""Migration View
"""
def __call__(self):
context = aq_inner(self.context)
out = []
self.total_comments_migrated = 0
self.total_comments_deleted = 0
dry_run = self.request.has_key("dry_run")
# This is for testing only.
# Do not use transactions during a test.
test = self.request.has_key("test")
if not test:
transaction.begin() # pragma: no cover
catalog = getToolByName(context, 'portal_catalog')
def log(msg):
# encode string before sending it to external world
if isinstance(msg, unicode):
msg = msg.encode('utf-8') # pragma: no cover
context.plone_log(msg)
out.append(msg)
def migrate_replies(context, in_reply_to, replies, depth=0):
# Recursive function to migrate all direct replies
# of a comment. Returns True if there are no replies to
# this comment left, and therefore the comment can be removed.
if len(replies) == 0:
return True
for reply in replies:
# log
indent = " "
for i in range(depth):
indent += " "
log("%smigrate_reply: '%s'." % (indent, reply.title))
# create a reply object
comment = CommentFactory()
comment.title = reply.Title()
comment.text = reply.text
comment.creator = reply.Creator()
comment.creation_date = datetime.fromtimestamp(
reply.creation_date)
comment.modification_date = datetime.fromtimestamp(
reply.modification_date)
comment.reply_to = in_reply_to
if in_reply_to == 0:
# Direct reply to a content object
new_in_reply_to = conversation.addComment(comment)
else:
# Reply to another comment
comment_to_reply_to = conversation.get(in_reply_to)
replies = IReplies(comment_to_reply_to)
new_in_reply_to = replies.addComment(comment)
self.total_comments_migrated += 1
# migrate all talkbacks of the reply
talkback = getattr( reply, 'talkback', None )
no_replies_left = migrate_replies(context,
new_in_reply_to,
talkback.getReplies(),
depth=depth+1)
if no_replies_left:
# remove reply and talkback
talkback.deleteReply(reply.id)
obj = aq_parent(talkback)
obj.talkback = None
log("%sremove %s" % (indent, reply.id))
self.total_comments_deleted += 1
# Return True when all comments on a certain level have been
# migrated.
return True
# Find content
brains = catalog.searchResults(
object_provides='Products.CMFCore.interfaces._content.IContentish')
log("Found %s content objects." % len(brains))
count_discussion_items = len(catalog.searchResults(
Type='Discussion Item'))
count_comments_pad = len(catalog.searchResults(
object_provides=IComment.__identifier__))
count_comments_old = len(catalog.searchResults(
object_provides=IDiscussionResponse.\
__identifier__))
log("Found %s Discussion Item objects." % count_discussion_items)
log("Found %s old discussion items." % count_comments_old)
log("Found %s plone.app.discussion comments." % count_comments_pad)
log("\n")
log("Start comment migration.")
# This loop is necessary to get all contentish objects, but not
# the Discussion Items. This wouldn't be necessary if the
# zcatalog would support NOT expressions.
new_brains = []
for brain in brains:
if brain.portal_type != 'Discussion Item':
new_brains.append(brain)
# Recursively run through the comment tree and migrate all comments.
for brain in new_brains:
obj = brain.getObject()
talkback = getattr( obj, 'talkback', None )
if talkback:
replies = talkback.getReplies()
if replies:
conversation = IConversation(obj)
log("\n")
log("Migrate '%s' (%s)" % (obj.Title(),
obj.absolute_url(relative=1)))
migrate_replies(context, 0, replies)
obj = aq_parent(talkback)
obj.talkback = None
if self.total_comments_deleted != self.total_comments_migrated:
log("Something went wrong during migration. The number of \
migrated comments (%s) differs from the number of deleted \
comments (%s)." # pragma: no cover
% (self.total_comments_migrated, self.total_comments_deleted))
if not test: # pragma: no cover
transaction.abort() # pragma: no cover
log("Abort transaction") # pragma: no cover
log("\n")
log("Comment migration finished.")
log("\n")
log("%s of %s comments migrated."
% (self.total_comments_migrated, count_comments_old))
if self.total_comments_migrated != count_comments_old:
log("%s comments could not be migrated."
% (count_comments_old - self.total_comments_migrated)) # pragma: no cover
log("Please make sure your portal catalog is up-to-date.") # pragma: no cover
if dry_run and not test:
transaction.abort() # pragma: no cover
log("Dry run") # pragma: no cover
log("Abort transaction") # pragma: no cover
if not test:
transaction.commit() # pragma: no cover
return '\n'.join(out)