refactor: async/await admin/controllers

v1.18.x
Barış Soner Uşaklı 6 years ago
parent 592d9c82c5
commit 6f375482a0

@ -7,10 +7,10 @@ define('admin/manage/users', ['translator', 'benchpress'], function (translator,
Users.init = function () {
var navPills = $('.nav-pills li');
var pathname = window.location.pathname;
if (!navPills.find('a[href="' + pathname + '"]').length) {
if (!navPills.find('a[href^="' + pathname + '"]').length) {
pathname = config.relative_path + '/admin/manage/users/latest';
}
navPills.removeClass('active').find('a[href="' + pathname + '"]').parent().addClass('active');
navPills.removeClass('active').find('a[href^="' + pathname + '"]').parent().addClass('active');
$('#results-per-page').val(ajaxify.data.resultsPerPage).on('change', function () {
var query = utils.params();

@ -1,19 +1,10 @@
'use strict';
var async = require('async');
const admin = require('../../rewards/admin');
var rewardsController = module.exports;
const rewardsController = module.exports;
rewardsController.get = function (req, res, next) {
async.waterfall([
function (next) {
require('../../rewards/admin').get(next);
},
function (data) {
res.render('admin/extend/rewards', data);
},
], next);
rewardsController.get = async function (req, res) {
const data = await admin.get();
res.render('admin/extend/rewards', data);
};
module.exports = rewardsController;

@ -1,64 +1,47 @@
'use strict';
var async = require('async');
const meta = require('../../meta');
const emailer = require('../../emailer');
const notifications = require('../../notifications');
var meta = require('../../meta');
var emailer = require('../../emailer');
var notifications = require('../../notifications');
const settingsController = module.exports;
var settingsController = module.exports;
settingsController.get = async function (req, res, next) {
const term = req.params.term ? req.params.term : 'general';
settingsController.get = function (req, res, next) {
var term = req.params.term ? req.params.term : 'general';
switch (req.params.term) {
case 'email':
renderEmail(req, res, next);
break;
case 'user':
renderUser(req, res, next);
break;
default:
if (term === 'email') {
await renderEmail(req, res, next);
} else if (term === 'user') {
await renderUser(req, res, next);
} else {
res.render('admin/settings/' + term);
}
};
function renderEmail(req, res, next) {
async.waterfall([
function (next) {
async.parallel({
emails: async.apply(emailer.getTemplates, meta.config),
services: emailer.listServices,
}, next);
},
function (results) {
res.render('admin/settings/email', {
emails: results.emails,
sendable: results.emails.filter(function (email) {
return !email.path.includes('_plaintext') && !email.path.includes('partials');
}),
services: results.services,
});
},
], next);
async function renderEmail(req, res) {
const [emails, services] = await Promise.all([
emailer.getTemplates(meta.config),
emailer.listServices(),
]);
res.render('admin/settings/email', {
emails: emails,
sendable: emails.filter(function (email) {
return !email.path.includes('_plaintext') && !email.path.includes('partials');
}),
services: services,
});
}
function renderUser(req, res, next) {
async.waterfall([
function (next) {
notifications.getAllNotificationTypes(next);
},
function (notificationTypes) {
var notificationSettings = notificationTypes.map(function (type) {
return {
name: type,
label: '[[notifications:' + type + ']]',
};
});
res.render('admin/settings/user', {
notificationSettings: notificationSettings,
});
},
], next);
async function renderUser(req, res) {
const notificationTypes = await notifications.getAllNotificationTypes();
const notificationSettings = notificationTypes.map(function (type) {
return {
name: type,
label: '[[notifications:' + type + ']]',
};
});
res.render('admin/settings/user', {
notificationSettings: notificationSettings,
});
}

@ -1,20 +1,12 @@
'use strict';
var async = require('async');
const social = require('../../social');
var social = require('../../social');
const socialController = module.exports;
var socialController = module.exports;
socialController.get = function (req, res, next) {
async.waterfall([
function (next) {
social.getPostSharing(next);
},
function (posts) {
res.render('admin/general/social', {
posts: posts,
});
},
], next);
socialController.get = async function (req, res) {
const posts = await social.getPostSharing();
res.render('admin/general/social', {
posts: posts,
});
};

@ -1,48 +1,38 @@
'use strict';
var async = require('async');
const plugins = require('../../plugins');
const meta = require('../../meta');
var plugins = require('../../plugins');
var meta = require('../../meta');
const soundsController = module.exports;
var soundsController = module.exports;
soundsController.get = function (req, res, next) {
var types = [
soundsController.get = async function (req, res) {
const types = [
'notification',
'chat-incoming',
'chat-outgoing',
];
async.waterfall([
function (next) {
meta.configs.getFields(types, next);
},
function (settings) {
settings = settings || {};
var output = {};
types.forEach(function (type) {
var soundpacks = plugins.soundpacks.map(function (pack) {
var sounds = Object.keys(pack.sounds).map(function (soundName) {
var value = pack.name + ' | ' + soundName;
return {
name: soundName,
value: value,
selected: value === settings[type],
};
});
const settings = await meta.configs.getFields(types) || {};
var output = {};
types.forEach(function (type) {
var soundpacks = plugins.soundpacks.map(function (pack) {
var sounds = Object.keys(pack.sounds).map(function (soundName) {
var value = pack.name + ' | ' + soundName;
return {
name: soundName,
value: value,
selected: value === settings[type],
};
});
return {
name: pack.name,
sounds: sounds,
};
});
return {
name: pack.name,
sounds: sounds,
};
});
output[type + '-sound'] = soundpacks;
});
output[type + '-sound'] = soundpacks;
});
res.render('admin/general/sounds', output);
},
], next);
res.render('admin/general/sounds', output);
};

@ -1,18 +1,10 @@
'use strict';
var async = require('async');
const topics = require('../../topics');
var topics = require('../../topics');
const tagsController = module.exports;
var tagsController = module.exports;
tagsController.get = function (req, res, next) {
async.waterfall([
function (next) {
topics.getTags(0, 199, next);
},
function (tags) {
res.render('admin/manage/tags', { tags: tags });
},
], next);
tagsController.get = async function (req, res) {
const tags = await topics.getTags(0, 199);
res.render('admin/manage/tags', { tags: tags });
};

@ -1,48 +1,32 @@
'use strict';
var path = require('path');
var fs = require('fs');
var async = require('async');
var file = require('../../file');
var themesController = module.exports;
var defaultScreenshotPath = path.join(__dirname, '../../../public/images/themes/default.png');
themesController.get = function (req, res, next) {
var themeDir = path.join(__dirname, '../../../node_modules', req.params.theme);
var themeConfigPath = path.join(themeDir, 'theme.json');
var screenshotPath;
async.waterfall([
function (next) {
fs.readFile(themeConfigPath, 'utf8', function (err, config) {
if (err) {
if (err.code === 'ENOENT') {
return next(Error('invalid-data'));
}
return next(err);
}
return next(null, config);
});
},
function (themeConfig, next) {
try {
themeConfig = JSON.parse(themeConfig);
} catch (e) {
return next(e);
}
next(null, themeConfig.screenshot ? path.join(themeDir, themeConfig.screenshot) : defaultScreenshotPath);
},
function (_screenshotPath, next) {
screenshotPath = _screenshotPath;
file.exists(screenshotPath, next);
},
function (exists) {
res.sendFile(exists ? screenshotPath : defaultScreenshotPath);
},
], next);
const path = require('path');
const fs = require('fs');
const util = require('util');
const readFileAsync = util.promisify(fs.readFile);
const file = require('../../file');
const themesController = module.exports;
const defaultScreenshotPath = path.join(__dirname, '../../../public/images/themes/default.png');
themesController.get = async function (req, res, next) {
const themeDir = path.join(__dirname, '../../../node_modules', req.params.theme);
const themeConfigPath = path.join(themeDir, 'theme.json');
let themeConfig;
try {
themeConfig = await readFileAsync(themeConfigPath, 'utf8');
themeConfig = JSON.parse(themeConfig);
} catch (err) {
if (err.code === 'ENOENT') {
return next(Error('invalid-data'));
}
return next(err);
}
const screenshotPath = themeConfig.screenshot ? path.join(themeDir, themeConfig.screenshot) : defaultScreenshotPath;
const exists = await file.exists(screenshotPath);
res.sendFile(exists ? screenshotPath : defaultScreenshotPath);
};

@ -1,83 +1,71 @@
'use strict';
var path = require('path');
var async = require('async');
var nconf = require('nconf');
var mime = require('mime');
var fs = require('fs');
var meta = require('../../meta');
var posts = require('../../posts');
var file = require('../../file');
var image = require('../../image');
var plugins = require('../../plugins');
var pagination = require('../../pagination');
var allowedImageTypes = ['image/png', 'image/jpeg', 'image/pjpeg', 'image/jpg', 'image/gif', 'image/svg+xml'];
var uploadsController = module.exports;
uploadsController.get = function (req, res, next) {
var currentFolder = path.join(nconf.get('upload_path'), req.query.dir || '');
const path = require('path');
const nconf = require('nconf');
const mime = require('mime');
const fs = require('fs');
const util = require('util');
const readdirAsync = util.promisify(fs.readdir);
const statAsync = util.promisify(fs.stat);
const meta = require('../../meta');
const posts = require('../../posts');
const file = require('../../file');
const image = require('../../image');
const plugins = require('../../plugins');
const pagination = require('../../pagination');
const allowedImageTypes = ['image/png', 'image/jpeg', 'image/pjpeg', 'image/jpg', 'image/gif', 'image/svg+xml'];
const uploadsController = module.exports;
uploadsController.get = async function (req, res, next) {
const currentFolder = path.join(nconf.get('upload_path'), req.query.dir || '');
if (!currentFolder.startsWith(nconf.get('upload_path'))) {
return next(new Error('[[error:invalid-path]]'));
}
var itemsPerPage = 20;
var itemCount = 0;
var page = parseInt(req.query.page, 10) || 1;
async.waterfall([
function (next) {
fs.readdir(currentFolder, next);
},
function (files, next) {
files = files.filter(function (filename) {
return filename !== '.gitignore';
});
itemCount = files.length;
var start = Math.max(0, (page - 1) * itemsPerPage);
var stop = start + itemsPerPage;
files = files.slice(start, stop);
filesToData(currentFolder, files, next);
},
function (files, next) {
// Float directories to the top
files.sort(function (a, b) {
if (a.isDirectory && !b.isDirectory) {
return -1;
} else if (!a.isDirectory && b.isDirectory) {
return 1;
} else if (!a.isDirectory && !b.isDirectory) {
return a.mtime < b.mtime ? -1 : 1;
}
return 0;
});
const itemsPerPage = 20;
const page = parseInt(req.query.page, 10) || 1;
try {
let files = await readdirAsync(currentFolder);
files = files.filter(filename => filename !== '.gitignore');
const itemCount = files.length;
var start = Math.max(0, (page - 1) * itemsPerPage);
var stop = start + itemsPerPage;
files = files.slice(start, stop);
files = await filesToData(currentFolder, files);
// Float directories to the top
files.sort(function (a, b) {
if (a.isDirectory && !b.isDirectory) {
return -1;
} else if (!a.isDirectory && b.isDirectory) {
return 1;
} else if (!a.isDirectory && !b.isDirectory) {
return a.mtime < b.mtime ? -1 : 1;
}
// Add post usage info if in /files
if (req.query.dir === '/files') {
posts.uploads.getUsage(files, function (err, usage) {
files.forEach(function (file, idx) {
file.inPids = usage[idx].map(pid => parseInt(pid, 10));
});
return 0;
});
next(err, files);
});
} else {
setImmediate(next, null, files);
}
},
function (files) {
res.render('admin/manage/uploads', {
currentFolder: currentFolder.replace(nconf.get('upload_path'), ''),
showPids: files.length && files[0].hasOwnProperty('inPids'),
files: files,
breadcrumbs: buildBreadcrumbs(currentFolder),
pagination: pagination.create(page, Math.ceil(itemCount / itemsPerPage), req.query),
// Add post usage info if in /files
if (req.query.dir === '/files') {
const usage = await posts.uploads.getUsage(files);
files.forEach(function (file, idx) {
file.inPids = usage[idx].map(pid => parseInt(pid, 10));
});
},
], next);
}
res.render('admin/manage/uploads', {
currentFolder: currentFolder.replace(nconf.get('upload_path'), ''),
showPids: files.length && files[0].hasOwnProperty('inPids'),
files: files,
breadcrumbs: buildBreadcrumbs(currentFolder),
pagination: pagination.create(page, Math.ceil(itemCount / itemsPerPage), req.query),
});
} catch (err) {
next(err);
}
};
function buildBreadcrumbs(currentFolder) {
@ -98,42 +86,33 @@ function buildBreadcrumbs(currentFolder) {
return crumbs;
}
function filesToData(currentDir, files, callback) {
async.map(files, function (file, next) {
var stat;
async.waterfall([
function (next) {
fs.stat(path.join(currentDir, file), next);
},
function (_stat, next) {
stat = _stat;
if (stat.isDirectory()) {
fs.readdir(path.join(currentDir, file), next);
} else {
next(null, []);
}
},
function (filesInDir, next) {
var url = nconf.get('upload_url') + currentDir.replace(nconf.get('upload_path'), '') + '/' + file;
next(null, {
name: file,
path: path.join(currentDir, file).replace(nconf.get('upload_path'), ''),
url: url,
fileCount: Math.max(0, filesInDir.length - 1), // ignore .gitignore
size: stat.size,
sizeHumanReadable: (stat.size / 1024).toFixed(1) + 'KiB',
isDirectory: stat.isDirectory(),
isFile: stat.isFile(),
mtime: stat.mtimeMs,
});
},
], next);
}, callback);
async function filesToData(currentDir, files) {
return await Promise.all(files.map(file => getFileData(currentDir, file)));
}
uploadsController.uploadCategoryPicture = function (req, res, next) {
var uploadedFile = req.files.files[0];
var params = null;
async function getFileData(currentDir, file) {
const stat = await statAsync(path.join(currentDir, file));
let filesInDir = [];
if (stat.isDirectory()) {
filesInDir = await readdirAsync(path.join(currentDir, file));
}
const url = nconf.get('upload_url') + currentDir.replace(nconf.get('upload_path'), '') + '/' + file;
return {
name: file,
path: path.join(currentDir, file).replace(nconf.get('upload_path'), ''),
url: url,
fileCount: Math.max(0, filesInDir.length - 1), // ignore .gitignore
size: stat.size,
sizeHumanReadable: (stat.size / 1024).toFixed(1) + 'KiB',
isDirectory: stat.isDirectory(),
isFile: stat.isFile(),
mtime: stat.mtimeMs,
};
}
uploadsController.uploadCategoryPicture = async function (req, res, next) {
const uploadedFile = req.files.files[0];
let params = null;
try {
params = JSON.parse(req.body.params);
@ -142,91 +121,80 @@ uploadsController.uploadCategoryPicture = function (req, res, next) {
return next(new Error('[[error:invalid-json]]'));
}
if (validateUpload(req, res, next, uploadedFile, allowedImageTypes)) {
var filename = 'category-' + params.cid + path.extname(uploadedFile.name);
uploadImage(filename, 'category', uploadedFile, req, res, next);
if (validateUpload(res, uploadedFile, allowedImageTypes)) {
const filename = 'category-' + params.cid + path.extname(uploadedFile.name);
await uploadImage(filename, 'category', uploadedFile, req, res, next);
}
};
uploadsController.uploadFavicon = function (req, res, next) {
var uploadedFile = req.files.files[0];
var allowedTypes = ['image/x-icon', 'image/vnd.microsoft.icon'];
if (validateUpload(req, res, next, uploadedFile, allowedTypes)) {
file.saveFileToLocal('favicon.ico', 'system', uploadedFile.path, function (err, image) {
uploadsController.uploadFavicon = async function (req, res, next) {
const uploadedFile = req.files.files[0];
const allowedTypes = ['image/x-icon', 'image/vnd.microsoft.icon'];
if (validateUpload(res, uploadedFile, allowedTypes)) {
try {
const imageObj = await file.saveFileToLocal('favicon.ico', 'system', uploadedFile.path);
res.json([{ name: uploadedFile.name, url: imageObj.url }]);
} catch (err) {
next(err);
} finally {
file.delete(uploadedFile.path);
if (err) {
return next(err);
}
res.json([{ name: uploadedFile.name, url: image.url }]);
});
}
}
};
uploadsController.uploadTouchIcon = function (req, res, next) {
var uploadedFile = req.files.files[0];
var allowedTypes = ['image/png'];
var sizes = [36, 48, 72, 96, 144, 192];
if (validateUpload(req, res, next, uploadedFile, allowedTypes)) {
file.saveFileToLocal('touchicon-orig.png', 'system', uploadedFile.path, function (err, imageObj) {
if (err) {
return next(err);
}
uploadsController.uploadTouchIcon = async function (req, res, next) {
const uploadedFile = req.files.files[0];
const allowedTypes = ['image/png'];
const sizes = [36, 48, 72, 96, 144, 192];
if (validateUpload(res, uploadedFile, allowedTypes)) {
try {
const imageObj = await file.saveFileToLocal('touchicon-orig.png', 'system', uploadedFile.path);
// Resize the image into squares for use as touch icons at various DPIs
async.eachSeries(sizes, function (size, next) {
image.resizeImage({
for (const size of sizes) {
/* eslint-disable no-await-in-loop */
await image.resizeImage({
path: uploadedFile.path,
target: path.join(nconf.get('upload_path'), 'system', 'touchicon-' + size + '.png'),
width: size,
height: size,
}, next);
}, function (err) {
file.delete(uploadedFile.path);
if (err) {
return next(err);
}
res.json([{ name: uploadedFile.name, url: imageObj.url }]);
});
});
});
}
res.json([{ name: uploadedFile.name, url: imageObj.url }]);
} catch (err) {
next(err);
} finally {
file.delete(uploadedFile.path);
}
}
};
uploadsController.uploadLogo = function (req, res, next) {
upload('site-logo', req, res, next);
uploadsController.uploadLogo = async function (req, res, next) {
await upload('site-logo', req, res, next);
};
uploadsController.uploadSound = function (req, res, next) {
var uploadedFile = req.files.files[0];
uploadsController.uploadSound = async function (req, res, next) {
const uploadedFile = req.files.files[0];
var mimeType = mime.getType(uploadedFile.name);
const mimeType = mime.getType(uploadedFile.name);
if (!/^audio\//.test(mimeType)) {
return next(Error('[[error:invalid-data]]'));
}
async.waterfall([
function (next) {
file.saveFileToLocal(uploadedFile.name, 'sounds', uploadedFile.path, next);
},
function (uploadedSound, next) {
meta.sounds.build(next);
},
], function (err) {
file.delete(uploadedFile.path);
if (err) {
return next(err);
}
try {
await file.saveFileToLocal(uploadedFile.name, 'sounds', uploadedFile.path);
await meta.sounds.build();
res.json([{}]);
});
} catch (err) {
next(err);
} finally {
file.delete(uploadedFile.path);
}
};
uploadsController.uploadFile = function (req, res, next) {
var uploadedFile = req.files.files[0];
var params;
uploadsController.uploadFile = async function (req, res, next) {
const uploadedFile = req.files.files[0];
let params;
try {
params = JSON.parse(req.body.params);
} catch (e) {
@ -234,33 +202,34 @@ uploadsController.uploadFile = function (req, res, next) {
return next(new Error('[[error:invalid-json]]'));
}
file.saveFileToLocal(uploadedFile.name, params.folder, uploadedFile.path, function (err, data) {
file.delete(uploadedFile.path);
if (err) {
return next(err);
}
try {
const data = await file.saveFileToLocal(uploadedFile.name, params.folder, uploadedFile.path);
res.json([{ url: data.url }]);
});
} catch (err) {
next(err);
} finally {
file.delete(uploadedFile.path);
}
};
uploadsController.uploadDefaultAvatar = function (req, res, next) {
upload('avatar-default', req, res, next);
uploadsController.uploadDefaultAvatar = async function (req, res, next) {
await upload('avatar-default', req, res, next);
};
uploadsController.uploadOgImage = function (req, res, next) {
upload('og:image', req, res, next);
uploadsController.uploadOgImage = async function (req, res, next) {
await upload('og:image', req, res, next);
};
function upload(name, req, res, next) {
var uploadedFile = req.files.files[0];
async function upload(name, req, res, next) {
const uploadedFile = req.files.files[0];
if (validateUpload(req, res, next, uploadedFile, allowedImageTypes)) {
var filename = name + path.extname(uploadedFile.name);
uploadImage(filename, 'system', uploadedFile, req, res, next);
if (validateUpload(res, uploadedFile, allowedImageTypes)) {
const filename = name + path.extname(uploadedFile.name);
await uploadImage(filename, 'system', uploadedFile, req, res, next);
}
}
function validateUpload(req, res, next, uploadedFile, allowedTypes) {
function validateUpload(res, uploadedFile, allowedTypes) {
if (!allowedTypes.includes(uploadedFile.type)) {
file.delete(uploadedFile.path);
res.json({ error: '[[error:invalid-image-type, ' + allowedTypes.join('&#44; ') + ']]' });
@ -270,64 +239,39 @@ function validateUpload(req, res, next, uploadedFile, allowedTypes) {
return true;
}
function uploadImage(filename, folder, uploadedFile, req, res, next) {
async.waterfall([
function (next) {
if (plugins.hasListeners('filter:uploadImage')) {
plugins.fireHook('filter:uploadImage', { image: uploadedFile, uid: req.uid }, next);
} else {
file.saveFileToLocal(filename, folder, uploadedFile.path, next);
}
},
function (imageData, next) {
// Post-processing for site-logo
if (path.basename(filename, path.extname(filename)) === 'site-logo' && folder === 'system') {
var uploadPath = path.join(nconf.get('upload_path'), folder, 'site-logo-x50.png');
async.series([
async.apply(image.resizeImage, {
path: uploadedFile.path,
target: uploadPath,
height: 50,
}),
async.apply(meta.configs.set, 'brand:emailLogo', path.join(nconf.get('upload_url'), 'system/site-logo-x50.png')),
function (next) {
image.size(uploadedFile.path, function (err, size) {
if (err) {
return next(err);
}
meta.configs.setMultiple({
'brand:logo:width': size.width,
'brand:logo:height': size.height,
}, function (err) {
next(err);
});
});
},
], function (err) {
next(err, imageData);
});
} else if (path.basename(filename, path.extname(filename)) === 'og:image' && folder === 'system') {
image.size(uploadedFile.path, function (err, size) {
if (err) {
next(err);
}
meta.configs.setMultiple({
'og:image:width': size.width,
'og:image:height': size.height,
}, function (err) {
next(err, imageData);
});
});
} else {
setImmediate(next, null, imageData);
}
},
], function (err, image) {
file.delete(uploadedFile.path);
if (err) {
return next(err);
async function uploadImage(filename, folder, uploadedFile, req, res, next) {
let imageData;
try {
if (plugins.hasListeners('filter:uploadImage')) {
imageData = await plugins.fireHook('filter:uploadImage', { image: uploadedFile, uid: req.uid });
} else {
imageData = await file.saveFileToLocal(filename, folder, uploadedFile.path);
}
res.json([{ name: uploadedFile.name, url: image.url.startsWith('http') ? image.url : nconf.get('relative_path') + image.url }]);
});
if (path.basename(filename, path.extname(filename)) === 'site-logo' && folder === 'system') {
const uploadPath = path.join(nconf.get('upload_path'), folder, 'site-logo-x50.png');
await image.resizeImage({
path: uploadedFile.path,
target: uploadPath,
height: 50,
});
await meta.configs.set('brand:emailLogo', path.join(nconf.get('upload_url'), 'system/site-logo-x50.png'));
const size = await image.size(uploadedFile.path);
await meta.configs.setMultiple({
'brand:logo:width': size.width,
'brand:logo:height': size.height,
});
} else if (path.basename(filename, path.extname(filename)) === 'og:image' && folder === 'system') {
const size = await image.size(uploadedFile.path);
await meta.configs.setMultiple({
'og:image:width': size.width,
'og:image:height': size.height,
});
}
res.json([{ name: uploadedFile.name, url: imageData.url.startsWith('http') ? imageData.url : nconf.get('relative_path') + imageData.url }]);
} catch (err) {
next(err);
} finally {
file.delete(uploadedFile.path);
}
}

@ -1,18 +1,18 @@
'use strict';
var async = require('async');
var nconf = require('nconf');
const nconf = require('nconf');
var user = require('../../user');
var meta = require('../../meta');
var db = require('../../database');
var pagination = require('../../pagination');
var events = require('../../events');
var plugins = require('../../plugins');
const user = require('../../user');
const meta = require('../../meta');
const db = require('../../database');
const pagination = require('../../pagination');
const events = require('../../events');
const plugins = require('../../plugins');
const utils = require('../../utils');
var usersController = module.exports;
const usersController = module.exports;
var userFields = ['uid', 'username', 'userslug', 'email', 'postcount', 'joindate', 'banned',
const userFields = ['uid', 'username', 'userslug', 'email', 'postcount', 'joindate', 'banned',
'reputation', 'picture', 'flags', 'lastonline', 'email:confirmed'];
usersController.search = function (req, res) {
@ -22,154 +22,129 @@ usersController.search = function (req, res) {
});
};
usersController.sortByJoinDate = function (req, res, next) {
getUsers('users:joindate', 'latest', undefined, undefined, req, res, next);
usersController.sortByJoinDate = async function (req, res) {
await getUsers('users:joindate', 'latest', undefined, undefined, req, res);
};
usersController.notValidated = function (req, res, next) {
getUsers('users:notvalidated', 'notvalidated', undefined, undefined, req, res, next);
usersController.notValidated = async function (req, res) {
await getUsers('users:notvalidated', 'notvalidated', undefined, undefined, req, res);
};
usersController.noPosts = function (req, res, next) {
getUsers('users:postcount', 'noposts', '-inf', 0, req, res, next);
usersController.noPosts = async function (req, res) {
await getUsers('users:postcount', 'noposts', '-inf', 0, req, res);
};
usersController.topPosters = function (req, res, next) {
getUsers('users:postcount', 'topposts', 0, '+inf', req, res, next);
usersController.topPosters = async function (req, res) {
await getUsers('users:postcount', 'topposts', 0, '+inf', req, res);
};
usersController.mostReputaion = function (req, res, next) {
getUsers('users:reputation', 'mostreputation', 0, '+inf', req, res, next);
usersController.mostReputaion = async function (req, res) {
await getUsers('users:reputation', 'mostreputation', 0, '+inf', req, res);
};
usersController.flagged = function (req, res, next) {
getUsers('users:flags', 'mostflags', 1, '+inf', req, res, next);
usersController.flagged = async function (req, res) {
await getUsers('users:flags', 'mostflags', 1, '+inf', req, res);
};
usersController.inactive = function (req, res, next) {
var timeRange = 1000 * 60 * 60 * 24 * 30 * (parseInt(req.query.months, 10) || 3);
var cutoff = Date.now() - timeRange;
getUsers('users:online', 'inactive', '-inf', cutoff, req, res, next);
usersController.inactive = async function (req, res) {
const timeRange = 1000 * 60 * 60 * 24 * 30 * (parseInt(req.query.months, 10) || 3);
const cutoff = Date.now() - timeRange;
await getUsers('users:online', 'inactive', '-inf', cutoff, req, res);
};
usersController.banned = function (req, res, next) {
getUsers('users:banned', 'banned', undefined, undefined, req, res, next);
usersController.banned = async function (req, res) {
await getUsers('users:banned', 'banned', undefined, undefined, req, res);
};
usersController.registrationQueue = function (req, res, next) {
var page = parseInt(req.query.page, 10) || 1;
var itemsPerPage = 20;
var start = (page - 1) * 20;
var stop = start + itemsPerPage - 1;
var invitations;
async.waterfall([
function (next) {
async.parallel({
registrationQueueCount: function (next) {
db.sortedSetCard('registration:queue', next);
},
users: function (next) {
user.getRegistrationQueue(start, stop, next);
},
customHeaders: function (next) {
plugins.fireHook('filter:admin.registrationQueue.customHeaders', { headers: [] }, next);
},
invites: function (next) {
async.waterfall([
function (next) {
user.getAllInvites(next);
},
function (_invitations, next) {
invitations = _invitations;
async.map(invitations, function (invites, next) {
user.getUserField(invites.uid, 'username', next);
}, next);
},
function (usernames, next) {
invitations.forEach(function (invites, index) {
invites.username = usernames[index];
});
async.map(invitations, function (invites, next) {
async.map(invites.invitations, user.getUsernameByEmail, next);
}, next);
},
function (usernames, next) {
invitations.forEach(function (invites, index) {
invites.invitations = invites.invitations.map(function (email, i) {
return {
email: email,
username: usernames[index][i] === '[[global:guest]]' ? '' : usernames[index][i],
};
});
});
next(null, invitations);
},
], next);
},
}, next);
},
function (data) {
var pageCount = Math.max(1, Math.ceil(data.registrationQueueCount / itemsPerPage));
data.pagination = pagination.create(page, pageCount);
data.customHeaders = data.customHeaders.headers;
res.render('admin/manage/registration', data);
},
], next);
usersController.registrationQueue = async function (req, res) {
const page = parseInt(req.query.page, 10) || 1;
const itemsPerPage = 20;
const start = (page - 1) * 20;
const stop = start + itemsPerPage - 1;
const data = await utils.promiseParallel({
registrationQueueCount: db.sortedSetCard('registration:queue'),
users: user.getRegistrationQueue(start, stop),
customHeaders: plugins.fireHook('filter:admin.registrationQueue.customHeaders', { headers: [] }),
invites: getInvites(),
});
var pageCount = Math.max(1, Math.ceil(data.registrationQueueCount / itemsPerPage));
data.pagination = pagination.create(page, pageCount);
data.customHeaders = data.customHeaders.headers;
res.render('admin/manage/registration', data);
};
function getUsers(set, section, min, max, req, res, next) {
var page = parseInt(req.query.page, 10) || 1;
var resultsPerPage = parseInt(req.query.resultsPerPage, 10) || 50;
async function getInvites() {
const invitations = await user.getAllInvites();
const uids = invitations.map(invite => invite.uid);
let usernames = await user.getUsersFields(uids, ['username']);
usernames = usernames.map(user => user.username);
invitations.forEach(function (invites, index) {
invites.username = usernames[index];
});
async function getUsernamesByEmails(emails) {
const uids = await db.sortedSetScore('email:uid', emails.map(email => String(email).toLowerCase()));
const usernames = await user.getUsersFields(uids, ['username']);
return usernames.map(user => user.username);
}
usernames = await Promise.all(invitations.map(invites => getUsernamesByEmails(invites.invitations)));
invitations.forEach(function (invites, index) {
invites.invitations = invites.invitations.map(function (email, i) {
return {
email: email,
username: usernames[index][i] === '[[global:guest]]' ? '' : usernames[index][i],
};
});
});
return invitations;
}
async function getUsers(set, section, min, max, req, res) {
const page = parseInt(req.query.page, 10) || 1;
let resultsPerPage = parseInt(req.query.resultsPerPage, 10) || 50;
if (![50, 100, 250, 500].includes(resultsPerPage)) {
resultsPerPage = 50;
}
var start = Math.max(0, page - 1) * resultsPerPage;
var stop = start + resultsPerPage - 1;
var byScore = min !== undefined && max !== undefined;
async.waterfall([
function (next) {
async.parallel({
count: function (next) {
if (byScore) {
db.sortedSetCount(set, min, max, next);
} else if (set === 'users:banned' || set === 'users:notvalidated') {
db.sortedSetCard(set, next);
} else {
db.getObjectField('global', 'userCount', next);
}
},
users: function (next) {
async.waterfall([
function (next) {
if (byScore) {
db.getSortedSetRevRangeByScore(set, start, resultsPerPage, max, min, next);
} else {
user.getUidsFromSet(set, start, stop, next);
}
},
function (uids, next) {
user.getUsersWithFields(uids, userFields, req.uid, next);
},
], next);
},
}, next);
},
function (results) {
results.users = results.users.filter(function (user) {
return user && parseInt(user.uid, 10);
});
var data = {
users: results.users,
page: page,
pageCount: Math.max(1, Math.ceil(results.count / resultsPerPage)),
resultsPerPage: resultsPerPage,
};
data[section] = true;
render(req, res, data);
},
], next);
const start = Math.max(0, page - 1) * resultsPerPage;
const stop = start + resultsPerPage - 1;
const byScore = min !== undefined && max !== undefined;
async function getCount() {
if (byScore) {
return await db.sortedSetCount(set, min, max);
} else if (set === 'users:banned' || set === 'users:notvalidated') {
return await db.sortedSetCard(set);
}
return await db.getObjectField('global', 'userCount');
}
async function getUsersWithFields() {
let uids;
if (byScore) {
uids = await db.getSortedSetRevRangeByScore(set, start, resultsPerPage, max, min);
} else {
uids = await user.getUidsFromSet(set, start, stop);
}
return await user.getUsersWithFields(uids, userFields, req.uid);
}
const [count, users] = await Promise.all([
getCount(),
getUsersWithFields(),
]);
const data = {
users: users.filter(user => user && parseInt(user.uid, 10)),
page: page,
pageCount: Math.max(1, Math.ceil(count / resultsPerPage)),
resultsPerPage: resultsPerPage,
};
data[section] = true;
render(req, res, data);
}
function render(req, res, data) {
@ -185,7 +160,7 @@ function render(req, res, data) {
res.render('admin/manage/users', data);
}
usersController.getCSV = function (req, res, next) {
usersController.getCSV = async function (req, res) {
var referer = req.headers.referer;
if (!referer || !referer.replace(nconf.get('url'), '').startsWith('/admin/manage/users')) {
@ -196,14 +171,8 @@ usersController.getCSV = function (req, res, next) {
uid: req.uid,
ip: req.ip,
});
async.waterfall([
function (next) {
user.getUsersCSV(next);
},
function (data) {
res.attachment('users.csv');
res.setHeader('Content-Type', 'text/csv');
res.end(data);
},
], next);
const data = await user.getUsersCSV();
res.attachment('users.csv');
res.setHeader('Content-Type', 'text/csv');
res.end(data);
};

@ -1,16 +1,9 @@
'use strict';
var async = require('async');
const widgetsController = module.exports;
const admin = require('../../widgets/admin');
var widgetsController = module.exports;
widgetsController.get = function (req, res, next) {
async.waterfall([
function (next) {
require('../../widgets/admin').get(next);
},
function (data) {
res.render('admin/extend/widgets', data);
},
], next);
widgetsController.get = async function (req, res) {
const data = await admin.get();
res.render('admin/extend/widgets', data);
};

@ -139,3 +139,5 @@ function getActiveRewards(callback) {
});
});
}
require('../promisify')(rewards);

Loading…
Cancel
Save