mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-10-26 08:36:12 +01:00
refactor: flags detail page
- Show account moderation history
- Ban and delete quick actions
Squashed commit of the following:
commit 0e782e65f4d48ae814708e510ec9d01bcdd914e0
Author: Julian Lam <julian@nodebb.org>
Date: Tue May 26 20:24:53 2020 -0400
fix(deps): use persona 10.1.41/vanilla 11.1.17
commit 369e073d3c3189d8ce181eb3d573489cbe54d4fc
Author: Julian Lam <julian@nodebb.org>
Date: Tue May 26 20:23:24 2020 -0400
fix: allow ban and delete exported methods to have cbs
commit b83a086ea31a77ec82d161306c0b9bc115cb2a3a
Merge: 525aae1ea 256ee45d3
Author: Julian Lam <julian@nodebb.org>
Date: Tue May 26 08:54:25 2020 -0400
Merge remote-tracking branch 'origin/master' into flags-improvements
commit 525aae1ea2e5d0103028a0f0c8dde05f172d088e
Author: Julian Lam <julian@nodebb.org>
Date: Tue May 26 08:53:39 2020 -0400
feat: integrate ban history and username changes to flag history list
commit 3e68ad28ba266f4c8620a676aa7f463f0a9d1df7
Author: Julian Lam <julian@nodebb.org>
Date: Mon May 25 18:22:53 2020 -0400
feat: allow ban and deletion from flag details page
commit a559ea1d8e8883385c2876868d855a0b93516c54
Author: Julian Lam <julian@nodebb.org>
Date: Mon May 25 18:22:00 2020 -0400
feat: export banAccount and deleteAccount methods from accounts module
This commit is contained in:
@@ -89,9 +89,9 @@
|
|||||||
"nodebb-plugin-spam-be-gone": "0.7.0",
|
"nodebb-plugin-spam-be-gone": "0.7.0",
|
||||||
"nodebb-rewards-essentials": "0.1.3",
|
"nodebb-rewards-essentials": "0.1.3",
|
||||||
"nodebb-theme-lavender": "5.0.11",
|
"nodebb-theme-lavender": "5.0.11",
|
||||||
"nodebb-theme-persona": "10.1.40",
|
"nodebb-theme-persona": "10.1.41",
|
||||||
"nodebb-theme-slick": "1.2.29",
|
"nodebb-theme-slick": "1.2.29",
|
||||||
"nodebb-theme-vanilla": "11.1.16",
|
"nodebb-theme-vanilla": "11.1.17",
|
||||||
"nodebb-widget-essentials": "4.1.0",
|
"nodebb-widget-essentials": "4.1.0",
|
||||||
"nodemailer": "^6.4.6",
|
"nodemailer": "^6.4.6",
|
||||||
"passport": "^0.4.1",
|
"passport": "^0.4.1",
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
"filter-cid-all": "All categories",
|
"filter-cid-all": "All categories",
|
||||||
"apply-filters": "Apply Filters",
|
"apply-filters": "Apply Filters",
|
||||||
|
|
||||||
"quick-links": "Quick Links",
|
"quick-actions": "Quick Actions",
|
||||||
"flagged-user": "Flagged User",
|
"flagged-user": "Flagged User",
|
||||||
"view-profile": "View Profile",
|
"view-profile": "View Profile",
|
||||||
"start-new-chat": "Start New Chat",
|
"start-new-chat": "Start New Chat",
|
||||||
@@ -40,7 +40,7 @@
|
|||||||
"add-note": "Add Note",
|
"add-note": "Add Note",
|
||||||
"no-notes": "No shared notes.",
|
"no-notes": "No shared notes.",
|
||||||
|
|
||||||
"history": "Flag History",
|
"history": "Account & Flag History",
|
||||||
"back": "Back to Flags List",
|
"back": "Back to Flags List",
|
||||||
"no-history": "No flag history.",
|
"no-history": "No flag history.",
|
||||||
|
|
||||||
|
|||||||
@@ -161,6 +161,7 @@
|
|||||||
"info.ban-history": "Recent Ban History",
|
"info.ban-history": "Recent Ban History",
|
||||||
"info.no-ban-history": "This user has never been banned",
|
"info.no-ban-history": "This user has never been banned",
|
||||||
"info.banned-until": "Banned until %1",
|
"info.banned-until": "Banned until %1",
|
||||||
|
"info.banned-expiry": "Expiry",
|
||||||
"info.banned-permanently": "Banned permanently",
|
"info.banned-permanently": "Banned permanently",
|
||||||
"info.banned-reason-label": "Reason",
|
"info.banned-reason-label": "Reason",
|
||||||
"info.banned-no-reason": "No reason given.",
|
"info.banned-no-reason": "No reason given.",
|
||||||
|
|||||||
@@ -56,6 +56,10 @@ define('forum/account/header', [
|
|||||||
components.get('account/block').on('click', toggleBlockAccount);
|
components.get('account/block').on('click', toggleBlockAccount);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO: These exported methods are used in forum/flags/detail -- refactor??
|
||||||
|
AccountHeader.banAccount = banAccount;
|
||||||
|
AccountHeader.deleteAccount = deleteAccount;
|
||||||
|
|
||||||
function hidePrivateLinks() {
|
function hidePrivateLinks() {
|
||||||
if (!app.user.uid || app.user.uid !== parseInt(ajaxify.data.theirid, 10)) {
|
if (!app.user.uid || app.user.uid !== parseInt(ajaxify.data.theirid, 10)) {
|
||||||
$('.account-sub-links .plugin-link.private').addClass('hide');
|
$('.account-sub-links .plugin-link.private').addClass('hide');
|
||||||
@@ -117,7 +121,9 @@ define('forum/account/header', [
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function banAccount() {
|
function banAccount(theirid, onSuccess) {
|
||||||
|
theirid = theirid || ajaxify.data.theirid;
|
||||||
|
|
||||||
Benchpress.parse('admin/partials/temporary-ban', {}, function (html) {
|
Benchpress.parse('admin/partials/temporary-ban', {}, function (html) {
|
||||||
bootbox.dialog({
|
bootbox.dialog({
|
||||||
className: 'ban-modal',
|
className: 'ban-modal',
|
||||||
@@ -140,13 +146,18 @@ define('forum/account/header', [
|
|||||||
var until = formData.length > 0 ? (Date.now() + (formData.length * 1000 * 60 * 60 * (parseInt(formData.unit, 10) ? 24 : 1))) : 0;
|
var until = formData.length > 0 ? (Date.now() + (formData.length * 1000 * 60 * 60 * (parseInt(formData.unit, 10) ? 24 : 1))) : 0;
|
||||||
|
|
||||||
socket.emit('user.banUsers', {
|
socket.emit('user.banUsers', {
|
||||||
uids: [ajaxify.data.theirid],
|
uids: [theirid],
|
||||||
until: until,
|
until: until,
|
||||||
reason: formData.reason || '',
|
reason: formData.reason || '',
|
||||||
}, function (err) {
|
}, function (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return app.alertError(err.message);
|
return app.alertError(err.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (typeof onSuccess === 'function') {
|
||||||
|
return onSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
ajaxify.refresh();
|
ajaxify.refresh();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -165,18 +176,25 @@ define('forum/account/header', [
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteAccount() {
|
function deleteAccount(theirid, onSuccess) {
|
||||||
|
theirid = theirid || ajaxify.data.theirid;
|
||||||
|
|
||||||
translator.translate('[[user:delete_this_account_confirm]]', function (translated) {
|
translator.translate('[[user:delete_this_account_confirm]]', function (translated) {
|
||||||
bootbox.confirm(translated, function (confirm) {
|
bootbox.confirm(translated, function (confirm) {
|
||||||
if (!confirm) {
|
if (!confirm) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
socket.emit('admin.user.deleteUsersAndContent', [ajaxify.data.theirid], function (err) {
|
socket.emit('admin.user.deleteUsersAndContent', [theirid], function (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return app.alertError(err.message);
|
return app.alertError(err.message);
|
||||||
}
|
}
|
||||||
app.alertSuccess('[[user:account-deleted]]');
|
app.alertSuccess('[[user:account-deleted]]');
|
||||||
|
|
||||||
|
if (typeof onSuccess === 'function') {
|
||||||
|
return onSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
history.back();
|
history.back();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,15 +1,21 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
define('forum/flags/detail', ['forum/flags/list', 'components', 'translator', 'benchpress'], function (FlagsList, components, translator, Benchpress) {
|
define('forum/flags/detail', ['forum/flags/list', 'components', 'translator', 'benchpress', 'forum/account/header'], function (FlagsList, components, translator, Benchpress, AccountHeader) {
|
||||||
var Flags = {};
|
var Detail = {};
|
||||||
|
|
||||||
Flags.init = function () {
|
Detail.init = function () {
|
||||||
// Update attributes
|
// Update attributes
|
||||||
$('#state').val(ajaxify.data.state).removeAttr('disabled');
|
$('#state').val(ajaxify.data.state).removeAttr('disabled');
|
||||||
$('#assignee').val(ajaxify.data.assignee).removeAttr('disabled');
|
$('#assignee').val(ajaxify.data.assignee).removeAttr('disabled');
|
||||||
|
|
||||||
$('[data-action]').on('click', function () {
|
$('[data-action]').on('click', function () {
|
||||||
var action = this.getAttribute('data-action');
|
var action = this.getAttribute('data-action');
|
||||||
|
var uid;
|
||||||
|
try {
|
||||||
|
uid = $(this).parents('[data-uid]').get(0).getAttribute('data-uid');
|
||||||
|
} catch (e) {
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case 'update':
|
case 'update':
|
||||||
@@ -21,7 +27,7 @@ define('forum/flags/detail', ['forum/flags/list', 'components', 'translator', 'b
|
|||||||
return app.alertError(err.message);
|
return app.alertError(err.message);
|
||||||
}
|
}
|
||||||
app.alertSuccess('[[flags:updated]]');
|
app.alertSuccess('[[flags:updated]]');
|
||||||
Flags.reloadHistory(history);
|
Detail.reloadHistory(history);
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -34,18 +40,29 @@ define('forum/flags/detail', ['forum/flags/list', 'components', 'translator', 'b
|
|||||||
return app.alertError(err.message);
|
return app.alertError(err.message);
|
||||||
}
|
}
|
||||||
app.alertSuccess('[[flags:note-added]]');
|
app.alertSuccess('[[flags:note-added]]');
|
||||||
Flags.reloadNotes(payload.notes);
|
Detail.reloadNotes(payload.notes);
|
||||||
Flags.reloadHistory(payload.history);
|
Detail.reloadHistory(payload.history);
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'chat':
|
||||||
|
app.newChat(uid);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'ban':
|
||||||
|
AccountHeader.banAccount(uid, ajaxify.refresh);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'delete':
|
||||||
|
AccountHeader.deleteAccount(uid, ajaxify.refresh);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
FlagsList.enableFilterForm();
|
FlagsList.enableFilterForm();
|
||||||
FlagsList.enableChatButtons();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Flags.reloadNotes = function (notes) {
|
Detail.reloadNotes = function (notes) {
|
||||||
Benchpress.parse('flags/detail', 'notes', {
|
Benchpress.parse('flags/detail', 'notes', {
|
||||||
notes: notes,
|
notes: notes,
|
||||||
}, function (html) {
|
}, function (html) {
|
||||||
@@ -57,7 +74,7 @@ define('forum/flags/detail', ['forum/flags/list', 'components', 'translator', 'b
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
Flags.reloadHistory = function (history) {
|
Detail.reloadHistory = function (history) {
|
||||||
Benchpress.parse('flags/detail', 'history', {
|
Benchpress.parse('flags/detail', 'history', {
|
||||||
history: history,
|
history: history,
|
||||||
}, function (html) {
|
}, function (html) {
|
||||||
@@ -70,5 +87,5 @@ define('forum/flags/detail', ['forum/flags/list', 'components', 'translator', 'b
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return Flags;
|
return Detail;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ define('forum/flags/list', ['components', 'Chart'], function (components, Chart)
|
|||||||
|
|
||||||
Flags.init = function () {
|
Flags.init = function () {
|
||||||
Flags.enableFilterForm();
|
Flags.enableFilterForm();
|
||||||
Flags.enableChatButtons();
|
|
||||||
Flags.handleGraphs();
|
Flags.handleGraphs();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -27,12 +26,6 @@ define('forum/flags/list', ['components', 'Chart'], function (components, Chart)
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
Flags.enableChatButtons = function () {
|
|
||||||
$('[data-chat]').on('click', function () {
|
|
||||||
app.newChat(this.getAttribute('data-chat'));
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Flags.handleGraphs = function () {
|
Flags.handleGraphs = function () {
|
||||||
var dailyCanvas = document.getElementById('flags:daily');
|
var dailyCanvas = document.getElementById('flags:daily');
|
||||||
var dailyLabels = utils.getDaysArray().map(function (text, idx) {
|
var dailyLabels = utils.getDaysArray().map(function (text, idx) {
|
||||||
|
|||||||
68
src/flags.js
68
src/flags.js
@@ -432,6 +432,8 @@ Flags.update = async function (flagId, uid, changeset) {
|
|||||||
Flags.getHistory = async function (flagId) {
|
Flags.getHistory = async function (flagId) {
|
||||||
const uids = [];
|
const uids = [];
|
||||||
let history = await db.getSortedSetRevRangeWithScores('flag:' + flagId + ':history', 0, -1);
|
let history = await db.getSortedSetRevRangeWithScores('flag:' + flagId + ':history', 0, -1);
|
||||||
|
const flagData = await db.getObjectFields('flag:' + flagId, ['type', 'targetId']);
|
||||||
|
const targetUid = await Flags.getTargetUid(flagData.type, flagData.targetId);
|
||||||
|
|
||||||
history = history.map(function (entry) {
|
history = history.map(function (entry) {
|
||||||
entry.value = JSON.parse(entry.value);
|
entry.value = JSON.parse(entry.value);
|
||||||
@@ -451,8 +453,74 @@ Flags.getHistory = async function (flagId) {
|
|||||||
datetimeISO: utils.toISOString(entry.score),
|
datetimeISO: utils.toISOString(entry.score),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Append ban history and username change data
|
||||||
|
let recentBans = await db.getSortedSetRevRange('uid:' + targetUid + ':bans:timestamp', 0, 19);
|
||||||
|
const usernameChanges = await user.getHistory('user:' + targetUid + ':usernames');
|
||||||
|
const emailChanges = await user.getHistory('user:' + targetUid + ':emails');
|
||||||
|
|
||||||
|
recentBans = await db.getObjects(recentBans);
|
||||||
|
history = history.concat(recentBans.reduce((memo, cur) => {
|
||||||
|
uids.push(cur.fromUid);
|
||||||
|
memo.push({
|
||||||
|
uid: cur.fromUid,
|
||||||
|
meta: [
|
||||||
|
{
|
||||||
|
key: '[[user:banned]]',
|
||||||
|
value: cur.reason,
|
||||||
|
labelClass: 'danger',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '[[user:info.banned-expiry]]',
|
||||||
|
value: new Date(parseInt(cur.expire, 10)).toISOString(),
|
||||||
|
labelClass: 'default',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
datetime: parseInt(cur.timestamp, 10),
|
||||||
|
datetimeISO: utils.toISOString(parseInt(cur.timestamp, 10)),
|
||||||
|
});
|
||||||
|
|
||||||
|
return memo;
|
||||||
|
}, [])).concat(usernameChanges.reduce((memo, changeObj) => {
|
||||||
|
uids.push(targetUid);
|
||||||
|
memo.push({
|
||||||
|
uid: targetUid,
|
||||||
|
meta: [
|
||||||
|
{
|
||||||
|
key: '[[user:change_username]]',
|
||||||
|
value: changeObj.value,
|
||||||
|
labelClass: 'primary',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
datetime: changeObj.timestamp,
|
||||||
|
datetimeISO: changeObj.timestampISO,
|
||||||
|
});
|
||||||
|
|
||||||
|
return memo;
|
||||||
|
}, [])).concat(emailChanges.reduce((memo, changeObj) => {
|
||||||
|
uids.push(targetUid);
|
||||||
|
memo.push({
|
||||||
|
uid: targetUid,
|
||||||
|
meta: [
|
||||||
|
{
|
||||||
|
key: '[[user:change_email]]',
|
||||||
|
value: changeObj.value,
|
||||||
|
labelClass: 'primary',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
datetime: changeObj.timestamp,
|
||||||
|
datetimeISO: changeObj.timestampISO,
|
||||||
|
});
|
||||||
|
|
||||||
|
return memo;
|
||||||
|
}, []));
|
||||||
|
|
||||||
const userData = await user.getUsersFields(uids, ['username', 'userslug', 'picture']);
|
const userData = await user.getUsersFields(uids, ['username', 'userslug', 'picture']);
|
||||||
history.forEach((event, idx) => { event.user = userData[idx]; });
|
history.forEach((event, idx) => { event.user = userData[idx]; });
|
||||||
|
|
||||||
|
// Resort by date
|
||||||
|
history = history.sort((a, b) => b.datetime - a.datetime);
|
||||||
|
|
||||||
return history;
|
return history;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user