[#7994] automatically merge multiple threads if that occurs
diff --git a/Allura/allura/model/artifact.py b/Allura/allura/model/artifact.py
index 1d4e230..67cbf3b 100644
--- a/Allura/allura/model/artifact.py
+++ b/Allura/allura/model/artifact.py
@@ -346,19 +346,38 @@
"""
from .discuss import Thread
- t = Thread.query.get(ref_id=self.index_id())
- if t is None:
+ threads = Thread.query.find(dict(ref_id=self.index_id())).all()
+ if not threads:
idx = self.index()
t = Thread.new(
app_config_id=self.app_config_id,
discussion_id=self.app_config.discussion_id,
ref_id=idx['id'],
subject='%s discussion' % h.get_first(idx, 'title'))
+ elif len(threads) == 1:
+ t = threads[0]
+ else:
+ # there should not be multiple threads, we'll merge them
+ destination = threads.pop()
+ for thread in threads:
+ for post in thread.posts:
+ post.thread_id = destination._id
+ destination.num_replies += 1
+ destination.last_post_date = max(destination.last_post_date, post.mod_date)
+ session(post).flush(post)
+ session(post).expunge(post) # so thread.posts ref later in the code doesn't use stale posts
+ Thread.query.remove({'_id': thread._id}) # NOT thread.delete() since that would remove its posts too
+ thread.attachment_class().query.update({'thread_id': thread._id},
+ {'$set': {'thread_id': destination._id}},
+ multi=True)
+ t = destination
+
parent_id = None
if data:
in_reply_to = data.get('in_reply_to', [])
if in_reply_to:
parent_id = in_reply_to[0]
+
return t, parent_id
@LazyProperty
diff --git a/Allura/allura/tests/model/test_artifact.py b/Allura/allura/tests/model/test_artifact.py
index adbae4e..b7e9dc0 100644
--- a/Allura/allura/tests/model/test_artifact.py
+++ b/Allura/allura/tests/model/test_artifact.py
@@ -222,3 +222,27 @@
assert_equal(c.project.last_updated, datetime(2014, 1, 1))
finally:
M.artifact_orm_session._get().skip_last_updated = False
+
+
+@with_setup(setUp, tearDown)
+def test_get_discussion_thread_dupe():
+ artif = WM.Page(title='TestSomeArtifact')
+ thr1 = artif.get_discussion_thread()[0]
+ thr1.post('thr1-post1')
+ thr1.post('thr1-post2')
+ thr2 = M.Thread.new(ref_id=thr1.ref_id)
+ thr2.post('thr2-post1')
+ thr2.post('thr2-post2')
+ thr2.post('thr2-post3')
+ thr3 = M.Thread.new(ref_id=thr1.ref_id)
+ thr3.post('thr3-post1')
+ thr4 = M.Thread.new(ref_id=thr1.ref_id)
+
+ thread_q = M.Thread.query.find(dict(ref_id=artif.index_id()))
+ assert_equal(thread_q.count(), 4)
+
+ thread = artif.get_discussion_thread()[0] # force cleanup
+ threads = thread_q.all()
+ assert_equal(len(threads), 1)
+ assert_equal(len(thread.posts), 6)
+ assert not any(p.deleted for p in thread.posts) # normal thread deletion propagates to children, make sure that doesn't happen