diff --git a/public/language/en/topic.json b/public/language/en/topic.json index 401da12677..51d1563fcf 100644 --- a/public/language/en/topic.json +++ b/public/language/en/topic.json @@ -20,11 +20,13 @@ "thread_tools.pin": "Pin Thread", "thread_tools.lock": "Lock Thread", "thread_tools.move": "Move Thread", + "thread_tools.fork": "Fork Thread", "thread_tools.delete": "Delete Thread", "load_categories": "Loading Categories", "disabled_categories_note": "Disabled Categories are greyed out", "confirm_move": "Move", + "confirm_fork": "Fork", "favourite": "Favourite", "favourites": "Favorites", @@ -36,7 +38,11 @@ "loading": "Loading", "more_posts": "More Posts", "move_topic": "Move Topic", + "fork_topic": "Fork Topic", "topic_will_be_moved_to": "This topic will be moved to the category", + "fork_topic_instruction": "Click the posts you want to fork", + "fork_no_pids": "No posts selected!", + "fork_success": "Succesfully forked topic!", "reputation": "Reputation", "posts": "Posts" diff --git a/public/src/forum/topic.js b/public/src/forum/topic.js index 6ad6120977..15eba4d0d4 100644 --- a/public/src/forum/topic.js +++ b/public/src/forum/topic.js @@ -174,6 +174,96 @@ define(['composer'], function(composer) { }); } }); + + $('.fork_thread').on('click', function() { + var pids = []; + var forkModal = $('#fork-thread-modal'), + forkCommit = forkModal.find('#fork_thread_commit'); + forkModal.removeClass('hide'); + forkModal.css("position", "fixed") + .css("left", Math.max(0, (($(window).width() - $(forkModal).outerWidth()) / 2) + $(window).scrollLeft()) + "px") + .css("top", "0px") + .css("z-index", "2000"); + + showNoPostsSelected(); + + forkModal.find('.close,#fork_thread_cancel').on('click', closeForkModal); + forkModal.find('#fork-title').on('change', checkForkButtonEnable); + $('#post-container').on('click', 'li', togglePostSelection); + forkCommit.on('click', createTopicFromPosts); + + function createTopicFromPosts() { + socket.emit('api:topic.createTopicFromPosts', { + title: forkModal.find('#fork-title').val(), + pids: pids + }, function(err) { + if(err) { + return app.alertError(err.message); + } + + translator.get('topic:fork_success', function(translated) { + app.alertSuccess(translated); + }); + + for(var i=0; i<pids.length; ++i) { + $('#post-container li[data-pid="' + pids[i] + '"]').fadeOut(500, function() { + $(this).remove(); + }); + } + closeForkModal(); + }); + } + + function togglePostSelection() { + + var newPid = $(this).attr('data-pid'); + + if($(this).attr('data-index') === '0') { + return; + } + + if(newPid) { + var index = pids.indexOf(newPid); + if(index === -1) { + pids.push(newPid); + $(this).css('opacity', '0.5'); + } else { + pids.splice(index, 1); + $(this).css('opacity', '1.0'); + } + + if(pids.length) { + pids.sort(); + forkModal.find('#fork-pids').html(pids.toString()); + } else { + showNoPostsSelected(); + } + checkForkButtonEnable(); + } + } + + function closeForkModal() { + for(var i=0; i<pids.length; ++i) { + $('#post-container li[data-pid="' + pids[i] + '"]').css('opacity', 1.0); + } + forkModal.addClass('hide'); + $('#post-container').off('click', 'li'); + } + + function checkForkButtonEnable() { + if(forkModal.find('#fork-title').length && pids.length) { + forkCommit.removeAttr('disabled'); + } else { + forkCommit.attr('disabled', true); + } + } + + function showNoPostsSelected() { + translator.get('topic:fork_no_pids', function(translated) { + forkModal.find('#fork-pids').html(translated); + }); + } + }); } fixDeleteStateForPosts(); @@ -375,21 +465,6 @@ 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/src/modules/chat.js b/public/src/modules/chat.js index ce8bb19ebe..990b86b8fc 100644 --- a/public/src/modules/chat.js +++ b/public/src/modules/chat.js @@ -4,11 +4,11 @@ define(['taskbar', 'string'], function(taskbar, S) { module.bringModalToTop = function(chatModal) { var topZ = 0; - $('.modal').each(function() { - var thisZ = parseInt($(this).css('zIndex'), 10); - if (thisZ > topZ) { - topZ = thisZ; - } + $('.chat-modal').each(function() { + var thisZ = parseInt($(this).css('zIndex'), 10); + if (thisZ > topZ) { + topZ = thisZ; + } }); chatModal.css('zIndex', topZ + 1); } diff --git a/public/templates/topic.tpl b/public/templates/topic.tpl index 26bdee338e..64ee27d852 100644 --- a/public/templates/topic.tpl +++ b/public/templates/topic.tpl @@ -97,9 +97,6 @@ <div class="btn-group post-tools"> <button class="btn btn-sm btn-default edit" type="button" title="[[topic:edit]]"><i class="fa fa-pencil"></i></button> <button class="btn btn-sm btn-default delete" type="button" title="[[topic:delete]]"><i class="fa fa-trash-o"></i></button> - <!-- IF !@first --> - <button class="btn btn-sm btn-default fork-post" type="button" title="[[topic:fork]]"><i class="fa fa-code-fork"></i></button> - <!-- ENDIF !@first --> </div> <!-- ENDIF posts.display_moderator_tools --> </div> @@ -151,6 +148,7 @@ <li><a href="#" class="lock_thread"><i class="fa fa-lock"></i> [[topic:thread_tools.lock]]</a></li> <li class="divider"></li> <li><a href="#" class="move_thread"><i class="fa fa-arrows"></i> [[topic:thread_tools.move]]</a></li> + <li><a href="#" class="fork_thread"><i class="fa fa-code-fork"></i> [[topic:thread_tools.fork]]</a></li> <li class="divider"></li> <li><a href="#" class="delete_thread"><span class="text-error"><i class="fa fa-trash-o"></i> [[topic:thread_tools.delete]]</span></a></li> </ul> @@ -175,6 +173,7 @@ <li><a href="#" class="lock_thread"><i class="fa fa-lock"></i> [[topic:thread_tools.lock]]</a></li> <li class="divider"></li> <li><a href="#" class="move_thread"><i class="fa fa-arrows"></i> [[topic:thread_tools.move]]</a></li> + <li><a href="#" class="fork_thread"><i class="fa fa-code-fork"></i> [[topic:thread_tools.fork]]</a></li> <li class="divider"></li> <li><a href="#" class="delete_thread"><span class="text-error"><i class="fa fa-trash-o"></i> [[topic:thread_tools.delete]]</span></a></li> </ul> @@ -209,4 +208,29 @@ </div> </div> + + <div id="fork-thread-modal" class="hide" tabindex="-1" role="dialog" aria-labelledby="Chat" aria-hidden="true" data-backdrop="none"> + <div class="modal-dialog"> + <div class="modal-content"> + <div class="modal-header"> + <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> + <h4>[[topic:fork_topic]]</h4> + </div> + <div class="modal-body"> + <div class="form-group"> + <label for="title">Title</label> + <input id="fork-title" type="text" class="form-control" placeholder="Enter new thread title"><br/> + <label>[[topic:fork_topic_instruction]]</label> <br/> + <span id="fork-pids"></span> + </div> + </div> + + <div class="modal-footer"> + <button type="button" class="btn btn-default" data-dismiss="modal" id="fork_thread_cancel">[[global:buttons.close]]</button> + <button type="button" class="btn btn-primary" id="fork_thread_commit" disabled>[[topic:confirm_fork]]</button> + </div> + </div> + </div> +</div> + </div> \ No newline at end of file diff --git a/src/topics.js b/src/topics.js index bf4dd48979..1ba193e9ba 100644 --- a/src/topics.js +++ b/src/topics.js @@ -198,40 +198,70 @@ var async = require('async'), }); } - Topics.createTopicFromPost = function(pid, callback) { - posts.getPostData(pid, function(err, postData) { + Topics.createTopicFromPosts = function(title, pids, callback) { + if(title) { + title = title.trim(); + } + + if(!title) { + return callback(new Error('invalid-title')); + } + + if(!pids || !pids.length) { + return callback(new Error('invalid-pids')); + } + + pids.sort(); + var mainPid = pids[0]; + + posts.getPostData(mainPid, function(err, postData) { if(err) { return callback(err); } - posts.getCidByPid(pid, function(err, cid) { + posts.getCidByPid(mainPid, 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); + async.eachSeries(pids, move, function(err) { + if(err) { + return callback(err); + } - posts.setPostField(pid, 'tid', tid); + Topics.getTopicData(tid, function(err, topicData) { + callback(err, topicData); + }); + }); - Topics.onNewPostMade(tid, postData.pid, postData.timestamp); + function move(pid, next) { + posts.getPostField(pid, 'timestamp', function(err, timestamp) { + if(err) { + return next(err); + } - Topics.getTopicData(tid, function(err, topicData) { - callback(err, topicData); - }); + Topics.movePostToTopic(pid, postData.tid, tid, timestamp, next); + }); + } }); }); }); } + Topics.movePostToTopic = function(pid, oldTid, newTid, timestamp, callback) { + Topics.removePostFromTopic(oldTid, pid); + Topics.decreasePostCount(oldTid); + + posts.setPostField(pid, 'tid', newTid); + + Topics.onNewPostMade(newTid, pid, timestamp, callback); + } + Topics.getTopicData = function(tid, callback) { db.getObject('topic:' + tid, function(err, data) { if(err) { @@ -986,14 +1016,14 @@ var async = require('async'), Topics.setTopicField(tid, 'lastposttime', timestamp); } - Topics.onNewPostMade = function(tid, pid, timestamp) { - Topics.addPostToTopic(tid, pid); + Topics.onNewPostMade = function(tid, pid, timestamp, callback) { Topics.increasePostCount(tid); Topics.updateTimestamp(tid, timestamp); + Topics.addPostToTopic(tid, pid, callback); } - Topics.addPostToTopic = function(tid, pid) { - db.listAppend('tid:' + tid + ':posts', pid); + Topics.addPostToTopic = function(tid, pid, callback) { + db.listAppend('tid:' + tid + ':posts', pid, callback); } Topics.removePostFromTopic = function(tid, pid) { diff --git a/src/websockets.js b/src/websockets.js index 382c87b078..fddd4beaa3 100644 --- a/src/websockets.js +++ b/src/websockets.js @@ -622,8 +622,8 @@ websockets.init = function(io) { }); }); - socket.on('api:topic.createTopicFromPost', function(data, callback) { - topics.createTopicFromPost(data.pid, function(err, data) { + socket.on('api:topic.createTopicFromPosts', function(data, callback) { + topics.createTopicFromPosts(data.title, data.pids, function(err, data) { callback(err?{message:err.message}:null, data); }); });