From a65d61290742fef5ed76834a73e069336ebe5b6b Mon Sep 17 00:00:00 2001 From: barisusakli Date: Tue, 10 Jun 2014 14:24:50 -0400 Subject: [PATCH] #1281 post purge --- public/language/en_GB/topic.json | 4 +- public/src/forum/topic/events.js | 58 +++++---- public/src/forum/topic/postTools.js | 36 ++++-- src/postTools.js | 10 ++ src/posts/delete.js | 118 +++++++++++++++++ src/socket.io/posts.js | 17 +++ src/topics.js | 1 + src/topics/delete.js | 77 ++++++++++++ src/user/delete.js | 189 ++-------------------------- 9 files changed, 292 insertions(+), 218 deletions(-) create mode 100644 src/topics/delete.js diff --git a/public/language/en_GB/topic.json b/public/language/en_GB/topic.json index 6bbb8ffe3e..2f29c76a7c 100644 --- a/public/language/en_GB/topic.json +++ b/public/language/en_GB/topic.json @@ -17,6 +17,7 @@ "reply": "Reply", "edit": "Edit", "delete": "Delete", + "purge": "Purge", "restore": "Restore", "move": "Move", "fork": "Fork", @@ -68,8 +69,7 @@ "post_delete_confirm": "Are you sure you want to delete this post?", "post_restore_confirm": "Are you sure you want to restore this post?", - "post_delete_error": "Could not delete this post!", - "post_restore_error": "Could not restore this post!", + "post_purge_confirm": "Are you you want to purge this post?", "load_categories": "Loading Categories", "disabled_categories_note": "Disabled Categories are greyed out", diff --git a/public/src/forum/topic/events.js b/public/src/forum/topic/events.js index b5d0a06536..6a4bc06981 100644 --- a/public/src/forum/topic/events.js +++ b/public/src/forum/topic/events.js @@ -25,6 +25,7 @@ define('forum/topic/events', ['forum/topic/browsing', 'forum/topic/postTools', ' 'event:topic_moved': onTopicMoved, 'event:post_edited': onPostEdited, + 'event:post_purged': onPostPurged, 'event:post_deleted': togglePostDeleteState, 'event:post_restored': togglePostDeleteState, @@ -108,41 +109,50 @@ define('forum/topic/events', ['forum/topic/browsing', 'forum/topic/postTools', ' }); } + function onPostPurged(pid) { + $('#post-container li[data-pid="' + pid + '"]').fadeOut(500, function() { + $(this).remove(); + }); + } + function togglePostDeleteState(data) { var postEl = $('#post-container li[data-pid="' + data.pid + '"]'); - if (postEl.length) { - postEl.toggleClass('deleted'); - var isDeleted = postEl.hasClass('deleted'); - postTools.toggle(data.pid, isDeleted); - - if (!app.isAdmin && parseInt(data.uid, 10) !== parseInt(app.uid, 10)) { - if (isDeleted) { - translator.translate('[[topic:post_is_deleted]]', function(translated) { - postEl.find('.post-content').html(translated); - }); - } else { - postEl.find('.post-content').html(data.content); - } - } + if (!postEl.length) { + return; + } - postTools.updatePostCount(); + postEl.toggleClass('deleted'); + var isDeleted = postEl.hasClass('deleted'); + postTools.toggle(data.pid, isDeleted); + + if (!app.isAdmin && parseInt(data.uid, 10) !== parseInt(app.uid, 10)) { + if (isDeleted) { + translator.translate('[[topic:post_is_deleted]]', function(translated) { + postEl.find('.post-content').html(translated); + }); + } else { + postEl.find('.post-content').html(data.content); + } } + + postTools.updatePostCount(); } function togglePostFavourite(data) { - var favBtn = $('li[data-pid="' + data.post.pid + '"] .favourite'); - if (favBtn.length) { - favBtn.addClass('btn-warning') - .attr('data-favourited', data.isFavourited); + if (!favBtn.length) { + return; + } - var icon = favBtn.find('i'); - var className = icon.attr('class'); + favBtn.addClass('btn-warning') + .attr('data-favourited', data.isFavourited); - if (data.isFavourited ? className.indexOf('-o') !== -1 : className.indexOf('-o') === -1) { - icon.attr('class', data.isFavourited ? className.replace('-o', '') : className + '-o'); - } + var icon = favBtn.find('i'); + var className = icon.attr('class'); + + if (data.isFavourited ? className.indexOf('-o') !== -1 : className.indexOf('-o') === -1) { + icon.attr('class', data.isFavourited ? className.replace('-o', '') : className + '-o'); } } diff --git a/public/src/forum/topic/postTools.js b/public/src/forum/topic/postTools.js index 95aa1a7221..9e91c856bf 100644 --- a/public/src/forum/topic/postTools.js +++ b/public/src/forum/topic/postTools.js @@ -21,8 +21,10 @@ define('forum/topic/postTools', ['composer', 'share', 'navigator'], function(com var postEl = $('#post-container li[data-pid="' + pid + '"]'); postEl.find('.quote, .favourite, .post_reply, .chat').toggleClass('none', isDeleted); + postEl.find('.purge').toggleClass('none', !isDeleted); translator.translate(isDeleted ? ' [[topic:restore]]' : ' [[topic:delete]]', function(translated) { + postEl.find('.delete').find('i').toggleClass('fa-trash-o', !isDeleted).toggleClass('fa-comment', isDeleted); postEl.find('.delete').find('span').html(translated); }); }; @@ -88,6 +90,10 @@ define('forum/topic/postTools', ['composer', 'share', 'navigator'], function(com deletePost($(this), tid); }); + postContainer.on('click', '.purge', function(e) { + purgePost($(this), tid); + }) + postContainer.on('click', '.move', function(e) { openMovePostModal($(this)); }); @@ -186,21 +192,31 @@ define('forum/topic/postTools', ['composer', 'share', 'navigator'], function(com function deletePost(button, tid) { var pid = getPid(button), - postEl = $(document.querySelector('#post-container li[data-pid="' + pid + '"]')), + postEl = $('#post-container li[data-pid="' + pid + '"]'), action = !postEl.hasClass('deleted') ? 'delete' : 'restore'; + postAction(action, pid, tid); + } + + function purgePost(button, tid) { + postAction('purge', getPid(button), tid); + } + + function postAction(action, pid, tid) { translator.translate('[[topic:post_' + action + '_confirm]]', function(msg) { bootbox.confirm(msg, function(confirm) { - if (confirm) { - socket.emit('posts.' + action, { - pid: pid, - tid: tid - }, function(err) { - if(err) { - app.alertError('[[topic:post_' + action + '_error]]'); - } - }); + if (!confirm) { + return; } + + socket.emit('posts.' + action, { + pid: pid, + tid: tid + }, function(err) { + if(err) { + app.alertError(err.message); + } + }); }); }); } diff --git a/src/postTools.js b/src/postTools.js index f496e640bf..1a81eee48f 100644 --- a/src/postTools.js +++ b/src/postTools.js @@ -177,6 +177,16 @@ var winston = require('winston'), }); } + PostTools.purge = function(uid, pid, callback) { + privileges.posts.canEdit(pid, uid, function(err, canEdit) { + if (err || !canEdit) { + return callback(err || new Error('[[error:no-privileges]]')); + } + + posts.delete(pid, callback); + }); + }; + function updateTopicTimestamp(tid, callback) { topics.getLatestUndeletedPid(tid, function(err, pid) { if(err || !pid) { diff --git a/src/posts/delete.js b/src/posts/delete.js index 72b420e107..865aac4a8d 100644 --- a/src/posts/delete.js +++ b/src/posts/delete.js @@ -1,9 +1,127 @@ +'use strict'; + +var async = require('async'), + db = require('../database'), + + plugins = require('../plugins'); module.exports = function(Posts) { Posts.delete = function(pid, callback) { + async.parallel([ + function(next) { + deletePostFromTopicAndUser(pid, next); + }, + function(next) { + deletePostFromCategoryRecentPosts(pid, next); + }, + function(next) { + deletePostFromUsersFavourites(pid, next); + }, + function(next) { + deletePostFromUsersVotes(pid, next); + }, + function(next) { + db.sortedSetRemove('posts:pid', pid, next); + } + ], function(err) { + if (err) { + return callback(err); + } + plugins.fireHook('action:post.delete', pid); + db.delete('post:' + pid, callback); + }); }; + function deletePostFromTopicAndUser(pid, callback) { + Posts.getPostFields(pid, ['tid', 'uid', 'deleted'], function(err, postData) { + if (err) { + return callback(err); + } + + async.parallel([ + function(next) { + db.sortedSetRemove('tid:' + postData.tid + ':posts', pid, next); + }, + function(next) { + db.sortedSetRemove('tid:' + postData.tid + ':posts:votes', pid, next); + }, + function(next) { + db.sortedSetRemove('uid:' + postData.uid + ':posts', pid, next); + } + ], function(err) { + if (err) { + return callback(err); + } + + if (parseInt(postData.deleted, 10) === 0) { + db.decrObjectField('global', 'postCount', callback); + } else { + callback(); + } + }); + }); + } + + function deletePostFromCategoryRecentPosts(pid, callback) { + db.getSortedSetRange('categories:cid', 0, -1, function(err, cids) { + if (err) { + return callback(err); + } + + async.each(cids, function(cid, next) { + db.sortedSetRemove('categories:recent_posts:cid:' + cid, pid, next); + }, callback); + }); + } + + function deletePostFromUsersFavourites(pid, callback) { + db.getSetMembers('pid:' + pid + ':users_favourited', function(err, uids) { + if (err) { + return callback(err); + } + + async.each(uids, function(uid, next) { + db.sortedSetRemove('uid:' + uid + ':favourites', pid, next); + }, function(err) { + if (err) { + return callback(err); + } + + db.delete('pid:' + pid + ':users_favourited', callback); + }); + }); + } + + function deletePostFromUsersVotes(pid, callback) { + async.parallel({ + upvoters: function(next) { + db.getSetMembers('pid:' + pid + ':upvote', next); + }, + downvoters: function(next) { + db.getSetMembers('pid:' + pid + ':downvote', next); + } + }, function(err, results) { + if (err) { + return callback(err); + } + + async.parallel([ + function(next) { + async.each(results.upvoters, function(uid, next) { + db.sortedSetRemove('uid:' + uid + ':upvote', pid, next); + }, next); + }, + function(next) { + async.each(results.downvoters, function(uid, next) { + db.sortedSetRemove('uid:' + uid + ':downvote', pid, next); + }, next); + } + ], callback); + }); + } + + }; \ No newline at end of file diff --git a/src/socket.io/posts.js b/src/socket.io/posts.js index 0b10e0d7f0..dfa1ceca9f 100644 --- a/src/socket.io/posts.js +++ b/src/socket.io/posts.js @@ -210,6 +210,23 @@ function deleteOrRestore(command, socket, data, callback) { }); } +SocketPosts.purge = function(socket, data, callback) { + if(!data) { + return callback(new Error('[[error:invalid-data]]')); + } + postTools.purge(socket.uid, data.pid, function(err) { + if(err) { + return callback(err); + } + + module.parent.exports.emitTopicPostStats(); + + websockets.server.sockets.in('topic_' + data.tid).emit('event:post_purged', data.pid); + + callback(); + }); +}; + SocketPosts.getPrivileges = function(socket, pid, callback) { privileges.posts.get(pid, socket.uid, function(err, privileges) { if(err) { diff --git a/src/topics.js b/src/topics.js index 0b225092d4..f2f9e207e2 100644 --- a/src/topics.js +++ b/src/topics.js @@ -15,6 +15,7 @@ var async = require('async'), (function(Topics) { require('./topics/create')(Topics); + require('./topics/delete')(Topics); require('./topics/unread')(Topics); require('./topics/recent')(Topics); require('./topics/fork')(Topics); diff --git a/src/topics/delete.js b/src/topics/delete.js new file mode 100644 index 0000000000..6f3f432a6d --- /dev/null +++ b/src/topics/delete.js @@ -0,0 +1,77 @@ +'use strict'; + +var async = require('async'), + db = require('../database'), + + plugins = require('../plugins'); + + +module.exports = function(Topics) { + Topics.delete = function(tid, callback) { + async.parallel([ + function(next) { + db.delete('tid:' + tid + ':followers', next); + }, + function(next) { + db.delete('tid:' + tid + ':read_by_uid', next); + }, + function(next) { + db.sortedSetRemove('topics:tid', tid, next); + }, + function(next) { + db.sortedSetRemove('topics:recent', tid, next); + }, + function(next) { + db.sortedSetRemove('topics:posts', tid, next); + }, + function(next) { + db.sortedSetRemove('topics:views', tid, next); + }, + function(next) { + deleteTopicFromCategoryAndUser(tid, next); + }, + function(next) { + Topics.deleteTopicTags(tid, next); + } + ], function(err) { + if (err) { + return callback(err); + } + plugins.fireHook('action:topic.delete', tid); + db.delete('topic:' + tid, callback); + }); + }; + + function deleteTopicFromCategoryAndUser(tid, callback) { + Topics.getTopicFields(tid, ['cid', 'uid', 'deleted'], function(err, topicData) { + if (err) { + return callback(err); + } + + async.parallel([ + function(next) { + db.sortedSetRemove('categories:' + topicData.cid + ':tid', tid, next); + }, + function(next) { + db.sortedSetRemove('uid:' + topicData.uid + ':topics', tid, next); + } + ], function(err) { + if (err) { + return callback(err); + } + + db.decrObjectField('category:' + topicData.cid, 'topic_count', function(err) { + if (err) { + return callback(err); + } + + if (parseInt(topicData.deleted, 10) === 0) { + db.decrObjectField('global', 'topicCount', callback); + } else { + callback(); + } + }); + }); + }); + } +}; diff --git a/src/user/delete.js b/src/user/delete.js index 0a378d921d..bd1c509cf8 100644 --- a/src/user/delete.js +++ b/src/user/delete.js @@ -1,13 +1,11 @@ 'use strict'; var async = require('async'), - db = require('./../database'), - posts = require('./../posts'), - user = require('./../user'), - topics = require('./../topics'), - categories = require('./../categories'), - plugins = require('./../plugins'), - groups = require('./../groups'); + db = require('../database'), + posts = require('../posts'), + user = require('../user'), + topics = require('../topics'), + groups = require('../groups'); module.exports = function(User) { @@ -30,123 +28,11 @@ module.exports = function(User) { }; function deletePosts(uid, callback) { - deleteSortedSetElements('uid:' + uid + ':posts', deletePost, callback); - } - - function deletePost(pid, callback) { - async.parallel([ - function(next) { - deletePostFromTopic(pid, next); - }, - function(next) { - deletePostFromCategoryRecentPosts(pid, next); - }, - function(next) { - deletePostFromUsersFavourites(pid, next); - }, - function(next) { - deletePostFromUsersVotes(pid, next); - }, - function(next) { - db.sortedSetRemove('posts:pid', pid, next); - } - ], function(err) { - if (err) { - return callback(err); - } - - plugins.fireHook('action:post.delete', pid); - db.delete('post:' + pid, callback); - }); - } - - function deletePostFromTopic(pid, callback) { - posts.getPostFields(pid, ['tid', 'deleted'], function(err, postData) { - if (err) { - return callback(err); - } - - async.parallel([ - function(next) { - db.sortedSetRemove('tid:' + postData.tid + ':posts', pid, next); - }, - function(next) { - db.sortedSetRemove('tid:' + postData.tid + ':posts:votes', pid, next); - } - ], function(err) { - if (err) { - return callback(err); - } - - if (parseInt(postData.deleted, 10) === 0) { - db.decrObjectField('global', 'postCount', callback); - } else { - callback(); - } - }); - }); - } - - function deletePostFromCategoryRecentPosts(pid, callback) { - db.getSortedSetRange('categories:cid', 0, -1, function(err, cids) { - if (err) { - return callback(err); - } - - async.each(cids, function(cid, next) { - db.sortedSetRemove('categories:recent_posts:cid:' + cid, pid, next); - }, callback); - }); - } - - function deletePostFromUsersFavourites(pid, callback) { - db.getSetMembers('pid:' + pid + ':users_favourited', function(err, uids) { - if (err) { - return callback(err); - } - - async.each(uids, function(uid, next) { - db.sortedSetRemove('uid:' + uid + ':favourites', pid, next); - }, function(err) { - if (err) { - return callback(err); - } - - db.delete('pid:' + pid + ':users_favourited', callback); - }); - }); - } - - function deletePostFromUsersVotes(pid, callback) { - async.parallel({ - upvoters: function(next) { - db.getSetMembers('pid:' + pid + ':upvote', next); - }, - downvoters: function(next) { - db.getSetMembers('pid:' + pid + ':downvote', next); - } - }, function(err, results) { - if (err) { - return callback(err); - } - - async.parallel([ - function(next) { - async.each(results.upvoters, function(uid, next) { - db.sortedSetRemove('uid:' + uid + ':upvote', pid, next); - }, next); - }, - function(next) { - async.each(results.downvoters, function(uid, next) { - db.sortedSetRemove('uid:' + uid + ':downvote', pid, next); - }, next); - } - ], callback); - }); + deleteSortedSetElements('uid:' + uid + ':posts', posts.delete, callback); } function deleteTopics(uid, callback) { - deleteSortedSetElements('uid:' + uid + ':topics', deleteTopic, callback); + deleteSortedSetElements('uid:' + uid + ':topics', topics.delete, callback); } function deleteSortedSetElements(set, deleteMethod, callback) { @@ -159,67 +45,6 @@ module.exports = function(User) { }); } - function deleteTopic(tid, callback) { - async.parallel([ - function(next) { - db.delete('tid:' + tid + ':followers', next); - }, - function(next) { - db.delete('tid:' + tid + ':read_by_uid', next); - }, - function(next) { - db.sortedSetRemove('topics:tid', tid, next); - }, - function(next) { - db.sortedSetRemove('topics:recent', tid, next); - }, - function(next) { - db.sortedSetRemove('topics:posts', tid, next); - }, - function(next) { - db.sortedSetRemove('topics:views', tid, next); - }, - function(next) { - deleteTopicFromCategory(tid, next); - }, - function(next) { - topics.deleteTopicTags(tid, next); - } - ], function(err) { - if (err) { - return callback(err); - } - plugins.fireHook('action:topic.delete', tid); - db.delete('topic:' + tid, callback); - }); - } - - function deleteTopicFromCategory(tid, callback) { - topics.getTopicFields(tid, ['cid', 'deleted'], function(err, topicData) { - if (err) { - return callback(err); - } - - db.sortedSetRemove('categories:' + topicData.cid + ':tid', tid, function(err) { - if (err) { - return callback(err); - } - - db.decrObjectField('category:' + topicData.cid, 'topic_count', function(err) { - if (err) { - return callback(err); - } - - if (parseInt(topicData.deleted, 10) === 0) { - db.decrObjectField('global', 'topicCount', callback); - } else { - callback(); - } - }); - }); - }); - } - function deleteAccount(uid, callback) { user.getUserFields(uid, ['username', 'userslug', 'email'], function(err, userData) { if (err) {