diff --git a/public/language/en-GB/admin/manage/users.json b/public/language/en-GB/admin/manage/users.json
index ae225d5b6f..93add0d7a4 100644
--- a/public/language/en-GB/admin/manage/users.json
+++ b/public/language/en-GB/admin/manage/users.json
@@ -12,8 +12,9 @@
"unban": "Unban User(s)",
"reset-lockout": "Reset Lockout",
"reset-flags": "Reset Flags",
- "delete": "Delete User(s)",
- "purge": "Delete User(s) and Content",
+ "delete": "Delete User(s)",
+ "delete-content": "Delete User(s) Content",
+ "purge": "Delete User(s) and Content",
"download-csv": "Download CSV",
"manage-groups": "Manage Groups",
"add-group": "Add Group",
@@ -93,9 +94,11 @@
"alerts.validate-email-success": "Emails validated",
"alerts.validate-force-password-reset-success": "User(s) passwords have been reset and their existing sessions have been revoked.",
"alerts.password-reset-confirm": "Do you want to send password reset email(s) to these user(s)?",
- "alerts.confirm-delete": "Warning!
Do you really want to delete user(s)?
This action is not reversable! Only the user account will be deleted, their posts and topics will remain.",
+ "alerts.confirm-delete": "Warning!
Do you really want to delete user(s)?
This action is not reversible! Only the user account will be deleted, their posts and topics will remain.
",
"alerts.delete-success": "User(s) Deleted!",
- "alerts.confirm-purge": "Warning!
Do you really want to delete user(s) and their content?
This action is not reversable! All user data and content will be erased!",
+ "alerts.confirm-delete-content": "Warning!Do you really want to delete these user(s) content?
This action is not reversible! The users' accounts will remain, but their posts and topics will be deleted.
",
+ "alerts.delete-content-success": "User(s) Content Deleted!",
+ "alerts.confirm-purge": "Warning!Do you really want to delete user(s) and their content?
This action is not reversible! All user data and content will be erased!
",
"alerts.create": "Create User",
"alerts.button-create": "Create",
"alerts.button-cancel": "Cancel",
diff --git a/public/language/en-GB/user.json b/public/language/en-GB/user.json
index 54d0fcf272..aa43c69e4c 100644
--- a/public/language/en-GB/user.json
+++ b/public/language/en-GB/user.json
@@ -13,9 +13,12 @@
"ban_account_confirm": "Do you really want to ban this user?",
"unban_account": "Unban Account",
"delete_account": "Delete Account",
+ "delete_content": "Delete Account Content Only",
"delete_account_confirm": "Are you sure you want to delete your account?
This action is irreversible and you will not be able to recover any of your data
Enter your password to confirm that you wish to destroy this account.",
"delete_this_account_confirm": "Are you sure you want to delete this account?
This action is irreversible and you will not be able to recover any data
",
+ "delete_account_content_confirm": "Are you sure you want to delete this account's content (posts/topics/uploads)?
This action is irreversible and you will not be able to recover any data
",
"account-deleted": "Account deleted",
+ "account-content-deleted": "Account content deleted",
"fullname": "Full Name",
"website": "Website",
diff --git a/public/src/admin/manage/users.js b/public/src/admin/manage/users.js
index ca4e63dc41..033613ee88 100644
--- a/public/src/admin/manage/users.js
+++ b/public/src/admin/manage/users.js
@@ -262,6 +262,25 @@ define('admin/manage/users', ['translator', 'benchpress', 'autocomplete'], funct
});
});
+ $('.delete-user-content').on('click', function () {
+ var uids = getSelectedUids();
+ if (!uids.length) {
+ return;
+ }
+
+ bootbox.confirm('[[admin/manage/users:alerts.confirm-delete-content]]', function (confirm) {
+ if (confirm) {
+ socket.emit('admin.user.deleteUsersContent', uids, function (err) {
+ if (err) {
+ return app.alertError(err.message);
+ }
+
+ app.alertSuccess('[[admin/manage/users:alerts.delete-content-success]]');
+ });
+ }
+ });
+ });
+
$('.delete-user-and-content').on('click', function () {
var uids = getSelectedUids();
if (!uids.length) {
diff --git a/public/src/client/account/header.js b/public/src/client/account/header.js
index 7ac50ef653..a8620ed98e 100644
--- a/public/src/client/account/header.js
+++ b/public/src/client/account/header.js
@@ -59,6 +59,7 @@ define('forum/account/header', [
// TODO: These exported methods are used in forum/flags/detail -- refactor??
AccountHeader.banAccount = banAccount;
AccountHeader.deleteAccount = deleteAccount;
+ AccountHeader.deleteContent = deleteContent;
function hidePrivateLinks() {
if (!app.user.uid || app.user.uid !== parseInt(ajaxify.data.theirid, 10)) {
@@ -201,6 +202,31 @@ define('forum/account/header', [
});
}
+ function deleteContent(theirid, onSuccess) {
+ theirid = theirid || ajaxify.data.theirid;
+
+ translator.translate('[[user:delete_account_content_confirm]]', function (translated) {
+ bootbox.confirm(translated, function (confirm) {
+ if (!confirm) {
+ return;
+ }
+
+ socket.emit('admin.user.deleteUsersContent', [theirid], function (err) {
+ if (err) {
+ return app.alertError(err.message);
+ }
+ app.alertSuccess('[[user:account-content-deleted]]');
+
+ if (typeof onSuccess === 'function') {
+ return onSuccess();
+ }
+
+ history.back();
+ });
+ });
+ });
+ }
+
function flagAccount() {
require(['flags'], function (flags) {
flags.showFlagModal({
diff --git a/public/src/client/flags/detail.js b/public/src/client/flags/detail.js
index 353a09b66b..1f8ecbb1e2 100644
--- a/public/src/client/flags/detail.js
+++ b/public/src/client/flags/detail.js
@@ -52,6 +52,10 @@ define('forum/flags/detail', ['forum/flags/list', 'components', 'translator', 'b
AccountHeader.deleteAccount(uid, ajaxify.refresh);
break;
+ case 'delete-content':
+ AccountHeader.deleteContent(uid, ajaxify.refresh);
+ break;
+
case 'delete-post':
postAction('delete', ajaxify.data.target.pid, ajaxify.data.target.tid);
break;
diff --git a/src/socket.io/admin/user.js b/src/socket.io/admin/user.js
index f0192cf6f9..05a7e8cc27 100644
--- a/src/socket.io/admin/user.js
+++ b/src/socket.io/admin/user.js
@@ -126,6 +126,20 @@ User.deleteUsers = async function (socket, uids) {
});
};
+User.deleteUsersContent = async function (socket, uids) {
+ if (!Array.isArray(uids)) {
+ throw new Error('[[error:invalid-data]]');
+ }
+ const isMembers = await groups.isMembers(uids, 'administrators');
+ if (isMembers.includes(true)) {
+ throw new Error('[[error:cant-delete-other-admins]]');
+ }
+
+ await Promise.all(uids.map(async (uid) => {
+ await user.deleteContent(socket.uid, uid);
+ }));
+};
+
User.deleteUsersAndContent = async function (socket, uids) {
deleteUsers(socket, uids, async function (uid) {
await user.delete(socket.uid, uid);
diff --git a/src/user/delete.js b/src/user/delete.js
index 1560005aeb..57ed4adeb7 100644
--- a/src/user/delete.js
+++ b/src/user/delete.js
@@ -17,7 +17,13 @@ const file = require('../file');
module.exports = function (User) {
const deletesInProgress = {};
- User.delete = async function (callerUid, uid) {
+ User.delete = async (callerUid, uid) => {
+ await User.deleteContent(callerUid, uid);
+ await removeFromSortedSets(uid);
+ return await User.deleteAccount(uid);
+ };
+
+ User.deleteContent = async function (callerUid, uid) {
if (parseInt(uid, 10) <= 0) {
throw new Error('[[error:invalid-uid]]');
}
@@ -25,13 +31,10 @@ module.exports = function (User) {
throw new Error('[[error:already-deleting]]');
}
deletesInProgress[uid] = 'user.delete';
- await removeFromSortedSets(uid);
await deletePosts(callerUid, uid);
await deleteTopics(callerUid, uid);
await deleteUploads(uid);
await deleteQueued(uid);
- const userData = await User.deleteAccount(uid);
- return userData;
};
async function deletePosts(callerUid, uid) {
diff --git a/src/views/admin/manage/users.tpl b/src/views/admin/manage/users.tpl
index fb9061a700..07ab3a399c 100644
--- a/src/views/admin/manage/users.tpl
+++ b/src/views/admin/manage/users.tpl
@@ -22,6 +22,7 @@
[[admin/manage/users:reset-lockout]]
[[admin/manage/users:delete]]
+ [[admin/manage/users:delete-content]]
[[admin/manage/users:purge]]