diff --git a/public/language/en/topic.json b/public/language/en/topic.json index 9df5829bea..401da12677 100644 --- a/public/language/en/topic.json +++ b/public/language/en/topic.json @@ -12,6 +12,7 @@ "reply": "Reply", "edit": "Edit", "delete": "Delete", + "fork": "Fork", "banned": "banned", "link": "Link", diff --git a/public/src/forum/topic.js b/public/src/forum/topic.js index 531184fcf0..788a0c1718 100644 --- a/public/src/forum/topic.js +++ b/public/src/forum/topic.js @@ -334,13 +334,13 @@ define(['composer'], function(composer) { return false; }); - $('#post-container').delegate('.edit', 'click', function(e) { + $('#post-container').on('click', '.edit', function(e) { var pid = $(this).parents('li').attr('data-pid'); composer.editPost(pid); }); - $('#post-container').delegate('.delete', 'click', function(e) { + $('#post-container').on('click', '.delete', function(e) { var pid = $(this).parents('li').attr('data-pid'), postEl = $(document.querySelector('#post-container li[data-pid="' + pid + '"]')), deleteAction = !postEl.hasClass('deleted') ? true : false, @@ -369,6 +369,21 @@ define(['composer'], function(composer) { } }); + $('#post-container').on('click', '.fork-post', function(e) { + var post = $(this).parents('li'), + pid = post.attr('data-pid'); + + socket.emit('api:topic.createTopicFromPost', {pid:pid}, function(err) { + if(err) { + return app.alertError(err.message); + } + post.fadeOut(500, function() { + post.remove(); + }); + }); + }); + + $('#post-container').on('click', '.chat', function(e) { var username = $(this).parents('li.row').attr('data-username'); var touid = $(this).parents('li.row').attr('data-uid'); diff --git a/public/templates/topic.tpl b/public/templates/topic.tpl index 04fe730113..a38357fafa 100644 --- a/public/templates/topic.tpl +++ b/public/templates/topic.tpl @@ -97,6 +97,9 @@
+ + +
diff --git a/src/database/mongo.js b/src/database/mongo.js index fceb39d00f..28fc911096 100644 --- a/src/database/mongo.js +++ b/src/database/mongo.js @@ -738,6 +738,21 @@ }); } + module.listRemoveAll = function(key, value, callback) { + db.collection('objects').update({_key: key }, { $pull: { array: value } }, function(err, result) { + if(err) { + if(callback) { + return callback(err); + } + return; + } + + if(callback) { + callback(null, result); + } + }); + } + module.getListRange = function(key, start, stop, callback) { if(stop === -1) { diff --git a/src/database/redis.js b/src/database/redis.js index a298980fef..f6291f6fbd 100644 --- a/src/database/redis.js +++ b/src/database/redis.js @@ -402,6 +402,10 @@ redisClient.rpop(key, callback); } + module.listRemoveAll = function(key, value, callback) { + redisClient.lrem(key, 0, value, callback); + } + module.getListRange = function(key, start, stop, callback) { redisClient.lrange(key, start, stop, callback); } diff --git a/src/postTools.js b/src/postTools.js index 37d1ac0a53..48ef0ae221 100644 --- a/src/postTools.js +++ b/src/postTools.js @@ -137,7 +137,7 @@ var winston = require('winston'), events.logPostDelete(uid, pid); posts.getPostFields(pid, ['tid', 'uid'], function(err, postData) { - db.incrObjectFieldBy('topic:' + postData.tid, 'postcount', -1); + topics.decreasePostCount(postData.tid); user.decrementUserFieldBy(postData.uid, 'postcount', 1, function(err, postcount) { db.sortedSetAdd('users:postcount', postcount, postData.uid); @@ -193,7 +193,7 @@ var winston = require('winston'), events.logPostRestore(uid, pid); posts.getPostFields(pid, ['tid', 'uid', 'content'], function(err, postData) { - db.incrObjectFieldBy('topic:' + postData.tid, 'postcount', 1); + topics.increasePostCount(postData.tid); user.incrementUserFieldBy(postData.uid, 'postcount', 1); diff --git a/src/topics.js b/src/topics.js index 751086ae68..bf4dd48979 100644 --- a/src/topics.js +++ b/src/topics.js @@ -23,6 +23,50 @@ var async = require('async'), (function(Topics) { + Topics.create = function(uid, title, cid, callback) { + db.incrObjectField('global', 'nextTid', function(err, tid) { + if(err) { + return callback(err); + } + + db.setAdd('topics:tid', tid); + + var slug = tid + '/' + utils.slugify(title), + timestamp = Date.now(); + + db.setObject('topic:' + tid, { + 'tid': tid, + 'uid': uid, + 'cid': cid, + 'title': title, + 'slug': slug, + 'timestamp': timestamp, + 'lastposttime': 0, + 'postcount': 0, + 'viewcount': 0, + 'locked': 0, + 'deleted': 0, + 'pinned': 0 + }, function(err) { + if(err) { + return callback(err); + } + db.searchIndex('topic', title, tid); + + user.addTopicIdToUser(uid, tid); + + // in future it may be possible to add topics to several categories, so leaving the door open here. + db.sortedSetAdd('categories:' + cid + ':tid', timestamp, tid); + db.incrObjectField('category:' + cid, 'topic_count'); + db.incrObjectField('global', 'topicCount'); + + feed.updateCategory(cid); + + callback(null, tid); + }); + }); + }; + Topics.post = function(uid, title, content, cid, callback) { categoryTools.privileges(cid, uid, function(err, privileges) { @@ -61,41 +105,11 @@ var async = require('async'), return callback(new Error('too-many-posts'), null); } - db.incrObjectField('global', 'nextTid', function(err, tid) { + Topics.create(uid, title, cid, function(err, tid) { if(err) { return callback(err); } - db.setAdd('topics:tid', tid); - - var slug = tid + '/' + utils.slugify(title); - var timestamp = Date.now(); - db.setObject('topic:' + tid, { - 'tid': tid, - 'uid': uid, - 'cid': cid, - 'title': title, - 'slug': slug, - 'timestamp': timestamp, - 'lastposttime': 0, - 'postcount': 0, - 'viewcount': 0, - 'locked': 0, - 'deleted': 0, - 'pinned': 0 - }); - - db.searchIndex('topic', title, tid); - - user.addTopicIdToUser(uid, tid); - - // in future it may be possible to add topics to several categories, so leaving the door open here. - db.sortedSetAdd('categories:' + cid + ':tid', timestamp, tid); - db.incrObjectField('category:' + cid, 'topic_count'); - db.incrObjectField('global', 'topicCount'); - - feed.updateCategory(cid); - Topics.reply(tid, uid, content, function(err, postData) { if(err) { return callback(err, null); @@ -184,6 +198,40 @@ var async = require('async'), }); } + Topics.createTopicFromPost = function(pid, callback) { + posts.getPostData(pid, function(err, postData) { + if(err) { + return callback(err); + } + + posts.getCidByPid(pid, function(err, cid) { + if(err) { + return callback(err); + } + + // TODO : title should be given by client + var title = postData.content.substr(0, 20); + + Topics.create(postData.uid, title, cid, function(err, tid) { + if(err) { + return callback(err); + } + + Topics.removePostFromTopic(postData.tid, postData.pid); + Topics.decreasePostCount(postData.tid); + + posts.setPostField(pid, 'tid', tid); + + Topics.onNewPostMade(tid, postData.pid, postData.timestamp); + + Topics.getTopicData(tid, function(err, topicData) { + callback(err, topicData); + }); + }); + }); + }); + } + Topics.getTopicData = function(tid, callback) { db.getObject('topic:' + tid, function(err, data) { if(err) { @@ -916,6 +964,10 @@ var async = require('async'), db.incrObjectField('topic:' + tid, 'postcount', callback); } + Topics.decreasePostCount = function(tid, callback) { + db.decrObjectField('topic:' + tid, 'postcount', callback); + } + Topics.increaseViewCount = function(tid, callback) { db.incrObjectField('topic:' + tid, 'viewcount', callback); } @@ -944,6 +996,10 @@ var async = require('async'), db.listAppend('tid:' + tid + ':posts', pid); } + Topics.removePostFromTopic = function(tid, pid) { + db.listRemoveAll('tid:' + tid + ':posts', pid); + } + Topics.getPids = function(tid, callback) { db.getListRange('tid:' + tid + ':posts', 0, -1, callback); } diff --git a/src/websockets.js b/src/websockets.js index f6bf315df3..382c87b078 100644 --- a/src/websockets.js +++ b/src/websockets.js @@ -622,6 +622,12 @@ websockets.init = function(io) { }); }); + socket.on('api:topic.createTopicFromPost', function(data, callback) { + topics.createTopicFromPost(data.pid, function(err, data) { + callback(err?{message:err.message}:null, data); + }); + }); + socket.on('api:topic.move', function(data) { threadTools.move(data.tid, data.cid, socket); });