From a3541d887ba019891aebdfc80f50ffecbb3c394f Mon Sep 17 00:00:00 2001 From: Baris Usakli Date: Mon, 12 Aug 2019 14:49:40 -0400 Subject: [PATCH 1/5] feat: async/await controllers/accounts --- src/controllers/accounts/categories.js | 62 +++++-------- src/controllers/accounts/chats.js | 119 ++++++++++--------------- src/controllers/accounts/consent.js | 54 ++++------- src/controllers/accounts/follow.js | 68 ++++++-------- src/controllers/accounts/groups.js | 58 +++++------- src/controllers/accounts/info.js | 103 ++++++++++----------- 6 files changed, 184 insertions(+), 280 deletions(-) diff --git a/src/controllers/accounts/categories.js b/src/controllers/accounts/categories.js index 1371435f49..f5137246d7 100644 --- a/src/controllers/accounts/categories.js +++ b/src/controllers/accounts/categories.js @@ -1,45 +1,29 @@ 'use strict'; -var async = require('async'); +const user = require('../../user'); +const categories = require('../../categories'); +const accountHelpers = require('./helpers'); -var user = require('../../user'); -var categories = require('../../categories'); -var accountHelpers = require('./helpers'); +const categoriesController = module.exports; -var categoriesController = module.exports; +categoriesController.get = async function (req, res, next) { + const userData = await accountHelpers.getUserDataByUserSlug(req.params.userslug, req.uid); + if (!userData) { + return next(); + } + const [states, categoriesData] = await Promise.all([ + user.getCategoryWatchState(userData.uid), + categories.buildForSelect(userData.uid, 'find'), + ]); -categoriesController.get = function (req, res, callback) { - var userData; - async.waterfall([ - function (next) { - accountHelpers.getUserDataByUserSlug(req.params.userslug, req.uid, next); - }, - function (_userData, next) { - userData = _userData; - if (!userData) { - return callback(); - } - - async.parallel({ - states: function (next) { - user.getCategoryWatchState(userData.uid, next); - }, - categories: function (next) { - categories.buildForSelect(userData.uid, 'find', next); - }, - }, next); - }, - function (results) { - results.categories.forEach(function (category) { - if (category) { - category.isIgnored = results.states[category.cid] === categories.watchStates.ignoring; - category.isWatched = results.states[category.cid] === categories.watchStates.watching; - category.isNotWatched = results.states[category.cid] === categories.watchStates.notwatching; - } - }); - userData.categories = results.categories; - userData.title = '[[pages:account/watched_categories, ' + userData.username + ']]'; - res.render('account/categories', userData); - }, - ], callback); + categoriesData.forEach(function (category) { + if (category) { + category.isIgnored = states[category.cid] === categories.watchStates.ignoring; + category.isWatched = states[category.cid] === categories.watchStates.watching; + category.isNotWatched = states[category.cid] === categories.watchStates.notwatching; + } + }); + userData.categories = categoriesData; + userData.title = '[[pages:account/watched_categories, ' + userData.username + ']]'; + res.render('account/categories', userData); }; diff --git a/src/controllers/accounts/chats.js b/src/controllers/accounts/chats.js index 793b43a958..0bfad61b81 100644 --- a/src/controllers/accounts/chats.js +++ b/src/controllers/accounts/chats.js @@ -1,85 +1,62 @@ 'use strict'; -var async = require('async'); +const messaging = require('../../messaging'); +const meta = require('../../meta'); +const user = require('../../user'); +const privileges = require('../../privileges'); +const helpers = require('../helpers'); -var messaging = require('../../messaging'); -var meta = require('../../meta'); -var user = require('../../user'); -var privileges = require('../../privileges'); -var helpers = require('../helpers'); +const chatsController = module.exports; -var chatsController = module.exports; - -chatsController.get = function (req, res, callback) { +chatsController.get = async function (req, res, next) { if (meta.config.disableChat) { - return callback(); + return next(); } - var uid; - var recentChats; + const uid = await user.getUidByUserslug(req.params.userslug); + if (!uid) { + return next(); + } + const canChat = await privileges.global.can('chat', req.uid); + if (!canChat) { + return next(new Error('[[error:no-privileges]]')); + } + const recentChats = await messaging.getRecentChats(req.uid, uid, 0, 19); + if (!recentChats) { + return next(); + } - async.waterfall([ - function (next) { - user.getUidByUserslug(req.params.userslug, next); - }, - function (_uid, next) { - uid = _uid; - if (!uid) { - return callback(); - } - privileges.global.can('chat', req.uid, next); - }, - function (canChat, next) { - if (!canChat) { - return next(new Error('[[error:no-privileges]]')); - } - messaging.getRecentChats(req.uid, uid, 0, 19, next); - }, - function (_recentChats, next) { - recentChats = _recentChats; - if (!recentChats) { - return callback(); - } - if (!req.params.roomid) { - return res.render('chats', { - rooms: recentChats.rooms, - uid: uid, - userslug: req.params.userslug, - nextStart: recentChats.nextStart, - allowed: true, - title: '[[pages:chats]]', - }); - } - messaging.loadRoom(req.uid, { uid: uid, roomId: req.params.roomid }, next); - }, - function (room) { - if (!room) { - return callback(); - } - room.rooms = recentChats.rooms; - room.nextStart = recentChats.nextStart; - room.title = room.roomName || room.usernames || '[[pages:chats]]'; - room.uid = uid; - room.userslug = req.params.userslug; - res.render('chats', room); - }, - ], callback); + if (!req.params.roomid) { + return res.render('chats', { + rooms: recentChats.rooms, + uid: uid, + userslug: req.params.userslug, + nextStart: recentChats.nextStart, + allowed: true, + title: '[[pages:chats]]', + }); + } + const room = await messaging.loadRoom(req.uid, { uid: uid, roomId: req.params.roomid }); + if (!room) { + return next(); + } + + room.rooms = recentChats.rooms; + room.nextStart = recentChats.nextStart; + room.title = room.roomName || room.usernames || '[[pages:chats]]'; + room.uid = uid; + room.userslug = req.params.userslug; + res.render('chats', room); }; -chatsController.redirectToChat = function (req, res, next) { - var roomid = parseInt(req.params.roomid, 10); +chatsController.redirectToChat = async function (req, res, next) { if (!req.loggedIn) { return next(); } - async.waterfall([ - function (next) { - user.getUserField(req.uid, 'userslug', next); - }, - function (userslug, next) { - if (!userslug) { - return next(); - } - helpers.redirect(res, '/user/' + userslug + '/chats' + (roomid ? '/' + roomid : '')); - }, - ], next); + const userslug = await user.getUserField(req.uid, 'userslug'); + if (!userslug) { + return next(); + } + const roomid = parseInt(req.params.roomid, 10); + helpers.redirect(res, '/user/' + userslug + '/chats' + (roomid ? '/' + roomid : '')); }; diff --git a/src/controllers/accounts/consent.js b/src/controllers/accounts/consent.js index aca8b26df0..9038b2905a 100644 --- a/src/controllers/accounts/consent.js +++ b/src/controllers/accounts/consent.js @@ -1,46 +1,30 @@ 'use strict'; -var async = require('async'); +const db = require('../../database'); +const meta = require('../../meta'); +const helpers = require('../helpers'); +const accountHelpers = require('./helpers'); -var db = require('../../database'); -var meta = require('../../meta'); -var helpers = require('../helpers'); -var accountHelpers = require('./helpers'); +const consentController = module.exports; -var consentController = module.exports; - -consentController.get = function (req, res, next) { +consentController.get = async function (req, res, next) { if (!meta.config.gdpr_enabled) { - // GDPR disabled return next(); } - var userData; + const userData = await accountHelpers.getUserDataByUserSlug(req.params.userslug, req.uid); + if (!userData) { + return next(); + } + const consented = await db.getObjectField('user:' + userData.uid, 'gdpr_consent'); + userData.gdpr_consent = parseInt(consented, 10) === 1; + userData.digest = { + frequency: meta.config.dailyDigestFreq, + enabled: meta.config.dailyDigestFreq !== 'off', + }; - async.waterfall([ - function (next) { - accountHelpers.getUserDataByUserSlug(req.params.userslug, req.uid, next); - }, - function (_userData, next) { - userData = _userData; - if (!userData) { - return next(); - } + userData.title = '[[user:consent.title]]'; + userData.breadcrumbs = helpers.buildBreadcrumbs([{ text: userData.username, url: '/user/' + userData.userslug }, { text: '[[user:consent.title]]' }]); - // Direct database call is used here because `gdpr_consent` is a protected user field and is automatically scrubbed from standard user data retrieval calls - db.getObjectField('user:' + userData.uid, 'gdpr_consent', next); - }, - function (consented) { - userData.gdpr_consent = parseInt(consented, 10) === 1; - userData.digest = { - frequency: meta.config.dailyDigestFreq, - enabled: meta.config.dailyDigestFreq !== 'off', - }; - - userData.title = '[[user:consent.title]]'; - userData.breadcrumbs = helpers.buildBreadcrumbs([{ text: userData.username, url: '/user/' + userData.userslug }, { text: '[[user:consent.title]]' }]); - - res.render('account/consent', userData); - }, - ], next); + res.render('account/consent', userData); }; diff --git a/src/controllers/accounts/follow.js b/src/controllers/accounts/follow.js index 3c3e6a3338..65dedbe14f 100644 --- a/src/controllers/accounts/follow.js +++ b/src/controllers/accounts/follow.js @@ -1,51 +1,41 @@ 'use strict'; -var async = require('async'); +const user = require('../../user'); +const helpers = require('../helpers'); +const accountHelpers = require('./helpers'); +const pagination = require('../../pagination'); -var user = require('../../user'); -var helpers = require('../helpers'); -var accountHelpers = require('./helpers'); -var pagination = require('../../pagination'); +const followController = module.exports; -var followController = module.exports; - -followController.getFollowing = function (req, res, next) { - getFollow('account/following', 'following', req, res, next); +followController.getFollowing = async function (req, res, next) { + await getFollow('account/following', 'following', req, res, next); }; -followController.getFollowers = function (req, res, next) { - getFollow('account/followers', 'followers', req, res, next); +followController.getFollowers = async function (req, res, next) { + await getFollow('account/followers', 'followers', req, res, next); }; -function getFollow(tpl, name, req, res, callback) { - var userData; +async function getFollow(tpl, name, req, res, next) { + const userData = await accountHelpers.getUserDataByUserSlug(req.params.userslug, req.uid); + if (!userData) { + return next(); + } - var page = parseInt(req.query.page, 10) || 1; - var resultsPerPage = 50; - var start = Math.max(0, page - 1) * resultsPerPage; - var stop = start + resultsPerPage - 1; + const page = parseInt(req.query.page, 10) || 1; + const resultsPerPage = 50; + const start = Math.max(0, page - 1) * resultsPerPage; + const stop = start + resultsPerPage - 1; - async.waterfall([ - function (next) { - accountHelpers.getUserDataByUserSlug(req.params.userslug, req.uid, next); - }, - function (data, next) { - userData = data; - if (!userData) { - return callback(); - } - var method = name === 'following' ? 'getFollowing' : 'getFollowers'; - user[method](userData.uid, start, stop, next); - }, - function (users) { - userData.users = users; - userData.title = '[[pages:' + tpl + ', ' + userData.username + ']]'; - var count = name === 'following' ? userData.followingCount : userData.followerCount; - var pageCount = Math.ceil(count / resultsPerPage); - userData.pagination = pagination.create(page, pageCount); - userData.breadcrumbs = helpers.buildBreadcrumbs([{ text: userData.username, url: '/user/' + userData.userslug }, { text: '[[user:' + name + ']]' }]); + userData.title = '[[pages:' + tpl + ', ' + userData.username + ']]'; - res.render(tpl, userData); - }, - ], callback); + const method = name === 'following' ? 'getFollowing' : 'getFollowers'; + userData.users = await user[method](userData.uid, start, stop); + + const count = name === 'following' ? userData.followingCount : userData.followerCount; + const pageCount = Math.ceil(count / resultsPerPage); + userData.pagination = pagination.create(page, pageCount); + + userData.breadcrumbs = helpers.buildBreadcrumbs([{ text: userData.username, url: '/user/' + userData.userslug }, { text: '[[user:' + name + ']]' }]); + + res.render(tpl, userData); } diff --git a/src/controllers/accounts/groups.js b/src/controllers/accounts/groups.js index fde4718eb4..fea0d19408 100644 --- a/src/controllers/accounts/groups.js +++ b/src/controllers/accounts/groups.js @@ -1,43 +1,25 @@ 'use strict'; +const groups = require('../../groups'); +const helpers = require('../helpers'); +const accountHelpers = require('./helpers'); -var async = require('async'); +const groupsController = module.exports; -var groups = require('../../groups'); -var helpers = require('../helpers'); -var accountHelpers = require('./helpers'); - -var groupsController = module.exports; - -groupsController.get = function (req, res, callback) { - var userData; - var groupsData; - async.waterfall([ - function (next) { - accountHelpers.getUserDataByUserSlug(req.params.userslug, req.uid, next); - }, - function (_userData, next) { - userData = _userData; - if (!userData) { - return callback(); - } - - groups.getUserGroups([userData.uid], next); - }, - function (_groupsData, next) { - groupsData = _groupsData[0]; - const groupNames = groupsData.filter(Boolean).map(group => group.name); - - groups.getMemberUsers(groupNames, 0, 3, next); - }, - function (members) { - groupsData.forEach(function (group, index) { - group.members = members[index]; - }); - userData.groups = groupsData; - userData.title = '[[pages:account/groups, ' + userData.username + ']]'; - userData.breadcrumbs = helpers.buildBreadcrumbs([{ text: userData.username, url: '/user/' + userData.userslug }, { text: '[[global:header.groups]]' }]); - res.render('account/groups', userData); - }, - ], callback); +groupsController.get = async function (req, res, next) { + const userData = await accountHelpers.getUserDataByUserSlug(req.params.userslug, req.uid); + if (!userData) { + return next(); + } + let groupsData = await groups.getUserGroups([userData.uid]); + groupsData = groupsData[0]; + const groupNames = groupsData.filter(Boolean).map(group => group.name); + const members = await groups.getMemberUsers(groupNames, 0, 3); + groupsData.forEach(function (group, index) { + group.members = members[index]; + }); + userData.groups = groupsData; + userData.title = '[[pages:account/groups, ' + userData.username + ']]'; + userData.breadcrumbs = helpers.buildBreadcrumbs([{ text: userData.username, url: '/user/' + userData.userslug }, { text: '[[global:header.groups]]' }]); + res.render('account/groups', userData); }; diff --git a/src/controllers/accounts/info.js b/src/controllers/accounts/info.js index cb3b6f1abf..00632ed426 100644 --- a/src/controllers/accounts/info.js +++ b/src/controllers/accounts/info.js @@ -1,67 +1,54 @@ 'use strict'; -var async = require('async'); +const db = require('../../database'); +const user = require('../../user'); +const helpers = require('../helpers'); +const accountHelpers = require('./helpers'); +const pagination = require('../../pagination'); -var db = require('../../database'); -var user = require('../../user'); -var helpers = require('../helpers'); -var accountHelpers = require('./helpers'); -var pagination = require('../../pagination'); +const infoController = module.exports; -var infoController = module.exports; +infoController.get = async function (req, res, next) { + const userData = await accountHelpers.getUserDataByUserSlug(req.params.userslug, req.uid); + if (!userData) { + return next(); + } + const page = Math.max(1, req.query.page || 1); + const itemsPerPage = 10; + const start = (page - 1) * itemsPerPage; + const stop = start + itemsPerPage - 1; -infoController.get = function (req, res, callback) { - var userData; - var page = Math.max(1, req.query.page || 1); - var itemsPerPage = 10; + const [history, sessions, usernames, emails, notes] = await Promise.all([ + user.getModerationHistory(userData.uid), + user.auth.getSessions(userData.uid, req.sessionID), + user.getHistory('user:' + userData.uid + ':usernames'), + user.getHistory('user:' + userData.uid + ':emails'), + getNotes(userData, start, stop), + ]); - async.waterfall([ - function (next) { - accountHelpers.getUserDataByUserSlug(req.params.userslug, req.uid, next); - }, - function (_userData, next) { - userData = _userData; - if (!userData) { - return callback(); - } + userData.history = history; + userData.sessions = sessions; + userData.usernames = usernames; + userData.emails = emails; - var start = (page - 1) * itemsPerPage; - var stop = start + itemsPerPage - 1; - async.parallel({ - history: async.apply(user.getModerationHistory, userData.uid), - sessions: async.apply(user.auth.getSessions, userData.uid, req.sessionID), - usernames: async.apply(user.getHistory, 'user:' + userData.uid + ':usernames'), - emails: async.apply(user.getHistory, 'user:' + userData.uid + ':emails'), - notes: function (next) { - if (!userData.isAdminOrGlobalModeratorOrModerator) { - return setImmediate(next); - } - async.parallel({ - notes: function (next) { - user.getModerationNotes(userData.uid, start, stop, next); - }, - count: function (next) { - db.sortedSetCard('uid:' + userData.uid + ':moderation:notes', next); - }, - }, next); - }, - }, next); - }, - function (data) { - userData.history = data.history; - userData.sessions = data.sessions; - userData.usernames = data.usernames; - userData.emails = data.emails; + if (userData.isAdminOrGlobalModeratorOrModerator) { + userData.moderationNotes = notes.notes; + const pageCount = Math.ceil(notes.count / itemsPerPage); + userData.pagination = pagination.create(page, pageCount, req.query); + } + userData.title = '[[pages:account/info]]'; + userData.breadcrumbs = helpers.buildBreadcrumbs([{ text: userData.username, url: '/user/' + userData.userslug }, { text: '[[user:account_info]]' }]); - if (userData.isAdminOrGlobalModeratorOrModerator) { - userData.moderationNotes = data.notes.notes; - var pageCount = Math.ceil(data.notes.count / itemsPerPage); - userData.pagination = pagination.create(page, pageCount, req.query); - } - userData.title = '[[pages:account/info]]'; - userData.breadcrumbs = helpers.buildBreadcrumbs([{ text: userData.username, url: '/user/' + userData.userslug }, { text: '[[user:account_info]]' }]); - - res.render('account/info', userData); - }, - ], callback); + res.render('account/info', userData); }; + +async function getNotes(userData, start, stop) { + if (!userData.isAdminOrGlobalModeratorOrModerator) { + return; + } + const [notes, count] = await Promise.all([ + user.getModerationNotes(userData.uid, start, stop), + db.sortedSetCard('uid:' + userData.uid + ':moderati;on:notes'), + ]); + return { notes: notes, count: count }; +} From 3cc7ec63e827ccdc9a26c0b261e92e978201c6c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 12 Aug 2019 20:58:29 -0400 Subject: [PATCH 2/5] feat: async/await refactor controllers/accounts --- src/controllers/accounts/edit.js | 300 +++++------ src/controllers/accounts/notifications.js | 132 ++--- src/controllers/accounts/profile.js | 153 +++--- src/controllers/accounts/sessions.js | 119 ++--- src/controllers/accounts/settings.js | 576 ++++++++++------------ src/controllers/accounts/uploads.js | 81 ++- src/plugins/hooks.js | 3 + 7 files changed, 583 insertions(+), 781 deletions(-) diff --git a/src/controllers/accounts/edit.js b/src/controllers/accounts/edit.js index c44ee5cce4..39a5f94e25 100644 --- a/src/controllers/accounts/edit.js +++ b/src/controllers/accounts/edit.js @@ -1,212 +1,156 @@ 'use strict'; -var async = require('async'); +const user = require('../../user'); +const meta = require('../../meta'); +const plugins = require('../../plugins'); +const helpers = require('../helpers'); +const groups = require('../../groups'); +const accountHelpers = require('./helpers'); +const privileges = require('../../privileges'); +const file = require('../../file'); -var db = require('../../database'); -var user = require('../../user'); -var meta = require('../../meta'); -var plugins = require('../../plugins'); -var helpers = require('../helpers'); -var groups = require('../../groups'); -var accountHelpers = require('./helpers'); -var privileges = require('../../privileges'); -var file = require('../../file'); +const editController = module.exports; -var editController = module.exports; +editController.get = async function (req, res, next) { + const [userData, canUseSignature] = await Promise.all([ + accountHelpers.getUserDataByUserSlug(req.params.userslug, req.uid), + privileges.global.can('signature', req.uid), + ]); + if (!userData) { + return next(); + } + userData.maximumSignatureLength = meta.config.maximumSignatureLength; + userData.maximumAboutMeLength = meta.config.maximumAboutMeLength; + userData.maximumProfileImageSize = meta.config.maximumProfileImageSize; + userData.allowProfilePicture = !userData.isSelf || !!meta.config['reputation:disabled'] || userData.reputation >= meta.config['min:rep:profile-picture']; + userData.allowCoverPicture = !userData.isSelf || !!meta.config['reputation:disabled'] || userData.reputation >= meta.config['min:rep:cover-picture']; + userData.allowProfileImageUploads = meta.config.allowProfileImageUploads; + userData.allowMultipleBadges = meta.config.allowMultipleBadges === 1; + userData.allowAccountDelete = meta.config.allowAccountDelete === 1; + userData.allowWebsite = !userData.isSelf || !!meta.config['reputation:disabled'] || userData.reputation >= meta.config['min:rep:website']; + userData.allowAboutMe = !userData.isSelf || !!meta.config['reputation:disabled'] || userData.reputation >= meta.config['min:rep:aboutme']; + userData.allowSignature = canUseSignature && (!userData.isSelf || !!meta.config['reputation:disabled'] || userData.reputation >= meta.config['min:rep:signature']); + userData.profileImageDimension = meta.config.profileImageDimension; + userData.defaultAvatar = user.getDefaultAvatar(); -editController.get = function (req, res, callback) { - async.waterfall([ - function (next) { - async.parallel({ - userData: function (next) { - accountHelpers.getUserDataByUserSlug(req.params.userslug, req.uid, next); - }, - canUseSignature: function (next) { - privileges.global.can('signature', req.uid, next); - }, - }, next); + userData.groups = userData.groups.filter(g => g && g.userTitleEnabled && !groups.isPrivilegeGroup(g.name) && g.name !== 'registered-users'); + + if (!userData.allowMultipleBadges) { + userData.groupTitle = userData.groupTitleArray[0]; + } + userData.groups.forEach(function (group) { + group.selected = userData.groupTitleArray.includes(group.name); + }); + + userData.title = '[[pages:account/edit, ' + userData.username + ']]'; + userData.breadcrumbs = helpers.buildBreadcrumbs([ + { + text: userData.username, + url: '/user/' + userData.userslug, }, - function (results, next) { - var userData = results.userData; - if (!userData) { - return callback(); - } - userData.maximumSignatureLength = meta.config.maximumSignatureLength; - userData.maximumAboutMeLength = meta.config.maximumAboutMeLength; - userData.maximumProfileImageSize = meta.config.maximumProfileImageSize; - userData.allowProfilePicture = !userData.isSelf || !!meta.config['reputation:disabled'] || userData.reputation >= meta.config['min:rep:profile-picture']; - userData.allowCoverPicture = !userData.isSelf || !!meta.config['reputation:disabled'] || userData.reputation >= meta.config['min:rep:cover-picture']; - userData.allowProfileImageUploads = meta.config.allowProfileImageUploads; - userData.allowMultipleBadges = meta.config.allowMultipleBadges === 1; - userData.allowAccountDelete = meta.config.allowAccountDelete === 1; - userData.allowWebsite = !userData.isSelf || !!meta.config['reputation:disabled'] || userData.reputation >= meta.config['min:rep:website']; - userData.allowAboutMe = !userData.isSelf || !!meta.config['reputation:disabled'] || userData.reputation >= meta.config['min:rep:aboutme']; - userData.allowSignature = results.canUseSignature && (!userData.isSelf || !!meta.config['reputation:disabled'] || userData.reputation >= meta.config['min:rep:signature']); - userData.profileImageDimension = meta.config.profileImageDimension; - userData.defaultAvatar = user.getDefaultAvatar(); - - userData.groups = userData.groups.filter(function (group) { - return group && group.userTitleEnabled && !groups.isPrivilegeGroup(group.name) && group.name !== 'registered-users'; - }); - - if (!userData.allowMultipleBadges) { - userData.groupTitle = userData.groupTitleArray[0]; - } - userData.groups.forEach(function (group) { - group.selected = userData.groupTitleArray.includes(group.name); - }); - - userData.title = '[[pages:account/edit, ' + userData.username + ']]'; - userData.breadcrumbs = helpers.buildBreadcrumbs([ - { - text: userData.username, - url: '/user/' + userData.userslug, - }, - { - text: '[[user:edit]]', - }, - ]); - userData.editButtons = []; - - plugins.fireHook('filter:user.account.edit', userData, next); + { + text: '[[user:edit]]', }, - function (userData) { - res.render('account/edit', userData); - }, - ], callback); + ]); + userData.editButtons = []; + + const result = await plugins.fireHook('filter:user.account.edit', userData); + res.render('account/edit', result); }; -editController.password = function (req, res, next) { - renderRoute('password', req, res, next); +editController.password = async function (req, res, next) { + await renderRoute('password', req, res, next); }; -editController.username = function (req, res, next) { - renderRoute('username', req, res, next); +editController.username = async function (req, res, next) { + await renderRoute('username', req, res, next); }; -editController.email = function (req, res, next) { - renderRoute('email', req, res, next); +editController.email = async function (req, res, next) { + await renderRoute('email', req, res, next); }; -function renderRoute(name, req, res, next) { - async.waterfall([ - function (next) { - getUserData(req, next, next); +async function renderRoute(name, req, res, next) { + const userData = await getUserData(req, next); + if (!userData) { + return next(); + } + if (meta.config[name + ':disableEdit'] && !userData.isAdmin) { + return helpers.notAllowed(req, res); + } + + if (name === 'password') { + userData.minimumPasswordLength = meta.config.minimumPasswordLength; + userData.minimumPasswordStrength = meta.config.minimumPasswordStrength; + } + + userData.title = '[[pages:account/edit/' + name + ', ' + userData.username + ']]'; + userData.breadcrumbs = helpers.buildBreadcrumbs([ + { + text: userData.username, + url: '/user/' + userData.userslug, }, - function (userData) { - if (!userData) { - return next(); - } - - if (meta.config[name + ':disableEdit'] && !userData.isAdmin) { - return helpers.notAllowed(req, res); - } - - if (name === 'password') { - userData.minimumPasswordLength = meta.config.minimumPasswordLength; - userData.minimumPasswordStrength = meta.config.minimumPasswordStrength; - } - - userData.title = '[[pages:account/edit/' + name + ', ' + userData.username + ']]'; - userData.breadcrumbs = helpers.buildBreadcrumbs([ - { - text: userData.username, - url: '/user/' + userData.userslug, - }, - { - text: '[[user:edit]]', - url: '/user/' + userData.userslug + '/edit', - }, - { - text: '[[user:' + name + ']]', - }, - ]); - - res.render('account/edit/' + name, userData); + { + text: '[[user:edit]]', + url: '/user/' + userData.userslug + '/edit', }, - ], next); + { + text: '[[user:' + name + ']]', + }, + ]); + + res.render('account/edit/' + name, userData); } -function getUserData(req, next, callback) { - var userData; - async.waterfall([ - function (next) { - accountHelpers.getUserDataByUserSlug(req.params.userslug, req.uid, next); - }, - function (data, next) { - userData = data; - if (!userData) { - return callback(null, null); - } - db.getObjectField('user:' + userData.uid, 'password', next); - }, - function (password, next) { - userData.hasPassword = !!password; - next(null, userData); - }, - ], callback); +async function getUserData(req) { + const userData = await accountHelpers.getUserDataByUserSlug(req.params.userslug, req.uid); + if (!userData) { + return null; + } + + userData.hasPassword = await user.hasPassword(userData.uid); + return userData; } -editController.uploadPicture = function (req, res, next) { - var userPhoto = req.files.files[0]; - - var updateUid; - - async.waterfall([ - function (next) { - user.getUidByUserslug(req.params.userslug, next); - }, - function (uid, next) { - updateUid = uid; - - privileges.users.canEdit(req.uid, uid, next); - }, - function (isAllowed, next) { - if (!isAllowed) { - return helpers.notAllowed(req, res); - } - user.checkMinReputation(req.uid, updateUid, 'min:rep:profile-picture', next); - }, - function (next) { - user.uploadCroppedPicture({ - uid: updateUid, - file: userPhoto, - }, next); - }, - ], function (err, image) { - file.delete(userPhoto.path); - if (err) { - return next(err); +editController.uploadPicture = async function (req, res, next) { + const userPhoto = req.files.files[0]; + try { + const updateUid = await user.getUidByUserslug(req.params.userslug); + const isAllowed = await privileges.users.canEdit(req.uid, updateUid); + if (!isAllowed) { + return helpers.notAllowed(req, res); } - + await user.checkMinReputation(req.uid, updateUid, 'min:rep:profile-picture'); + const image = await user.uploadCroppedPicture({ + uid: updateUid, + file: userPhoto, + }); res.json([{ name: userPhoto.name, url: image.url, }]); - }); + } catch (err) { + next(err); + } finally { + file.delete(userPhoto.path); + } }; -editController.uploadCoverPicture = function (req, res, next) { +editController.uploadCoverPicture = async function (req, res, next) { var params = JSON.parse(req.body.params); var coverPhoto = req.files.files[0]; - - async.waterfall([ - function (next) { - user.checkMinReputation(req.uid, params.uid, 'min:rep:cover-picture', next); - }, - function (next) { - user.updateCoverPicture({ - file: coverPhoto, - uid: params.uid, - }, next); - }, - ], function (err, image) { - file.delete(coverPhoto.path); - if (err) { - return next(err); - } + try { + await user.checkMinReputation(req.uid, params.uid, 'min:rep:cover-picture'); + const image = await user.updateCoverPicture({ + file: coverPhoto, + uid: params.uid, + }); res.json([{ url: image.url, }]); - }); + } catch (err) { + next(err); + } finally { + file.delete(coverPhoto.path); + } }; diff --git a/src/controllers/accounts/notifications.js b/src/controllers/accounts/notifications.js index 3ec4708e7a..3ec7c893c8 100644 --- a/src/controllers/accounts/notifications.js +++ b/src/controllers/accounts/notifications.js @@ -1,16 +1,14 @@ 'use strict'; -var async = require('async'); +const user = require('../../user'); +const helpers = require('../helpers'); +const plugins = require('../../plugins'); +const pagination = require('../../pagination'); -var user = require('../../user'); -var helpers = require('../helpers'); -var plugins = require('../../plugins'); -var pagination = require('../../pagination'); +const notificationsController = module.exports; -var notificationsController = module.exports; - -notificationsController.get = function (req, res, next) { - var regularFilters = [ +notificationsController.get = async function (req, res, next) { + const regularFilters = [ { name: '[[notifications:all]]', filter: '' }, { name: '[[global:topics]]', filter: 'new-topic' }, { name: '[[notifications:replies]]', filter: 'new-reply' }, @@ -19,84 +17,56 @@ notificationsController.get = function (req, res, next) { { name: '[[notifications:upvote]]', filter: 'upvote' }, ]; - var moderatorFilters = [ + const moderatorFilters = [ { name: '[[notifications:new-flags]]', filter: 'new-post-flag' }, { name: '[[notifications:my-flags]]', filter: 'my-flags' }, { name: '[[notifications:bans]]', filter: 'ban' }, ]; - var filter = req.query.filter || ''; - var page = Math.max(1, req.query.page || 1); - var itemsPerPage = 20; - var start = (page - 1) * itemsPerPage; - var stop = start + itemsPerPage - 1; - var selectedFilter; - var pageCount = 1; - var allFilters = []; + const filter = req.query.filter || ''; + const page = Math.max(1, req.query.page || 1); + const itemsPerPage = 20; + const start = (page - 1) * itemsPerPage; + const stop = start + itemsPerPage - 1; - async.waterfall([ - function (next) { - async.parallel({ - filters: function (next) { - plugins.fireHook('filter:notifications.addFilters', { - regularFilters: regularFilters, - moderatorFilters: moderatorFilters, - uid: req.uid, - }, next); - }, - isPrivileged: function (next) { - user.isPrivileged(req.uid, next); - }, - }, next); - }, - function (data, _next) { - allFilters = data.filters.regularFilters; + const [filters, isPrivileged] = await Promise.all([ + plugins.fireHook('filter:notifications.addFilters', { + regularFilters: regularFilters, + moderatorFilters: moderatorFilters, + uid: req.uid, + }), + user.isPrivileged(req.uid), + ]); - if (data.isPrivileged) { - allFilters = allFilters.concat([ - { separator: true }, - ]).concat(data.filters.moderatorFilters); - } + let allFilters = filters.regularFilters; + if (isPrivileged) { + allFilters = allFilters.concat([ + { separator: true }, + ]).concat(filters.moderatorFilters); + } + const selectedFilter = allFilters.find(function (filterData) { + filterData.selected = filterData.filter === filter; + return filterData.selected; + }); + if (!selectedFilter) { + return next(); + } + let nids = await user.notifications.getAll(req.uid, selectedFilter.filter); + const pageCount = Math.max(1, Math.ceil(nids.length / itemsPerPage)); + nids = nids.slice(start, stop + 1); - selectedFilter = allFilters.find(function (filterData) { - filterData.selected = filterData.filter === filter; - return filterData.selected; - }); - - if (!selectedFilter) { - return next(); - } - - user.notifications.getAll(req.uid, selectedFilter.filter, _next); - }, - function (nids, next) { - pageCount = Math.max(1, Math.ceil(nids.length / itemsPerPage)); - nids = nids.slice(start, stop + 1); - - user.notifications.getNotifications(nids, req.uid, next); - }, - function (notifications, next) { - plugins.fireHook('filter:notifications.get', { - notifications: notifications, - }, function (err, data) { - if (err) { - return next(err); - } - - next(null, data.notifications); - }); - }, - function (notifications) { - res.render('notifications', { - notifications: notifications, - pagination: pagination.create(page, pageCount, req.query), - filters: allFilters, - regularFilters: regularFilters, - moderatorFilters: moderatorFilters, - selectedFilter: selectedFilter, - title: '[[pages:notifications]]', - breadcrumbs: helpers.buildBreadcrumbs([{ text: '[[pages:notifications]]' }]), - }); - }, - ], next); + const notifications = await user.notifications.getNotifications(nids, req.uid); + const data = await plugins.fireHook('filter:notifications.get', { + notifications: notifications, + }); + res.render('notifications', { + notifications: data.notifications, + pagination: pagination.create(page, pageCount, req.query), + filters: allFilters, + regularFilters: regularFilters, + moderatorFilters: moderatorFilters, + selectedFilter: selectedFilter, + title: '[[pages:notifications]]', + breadcrumbs: helpers.buildBreadcrumbs([{ text: '[[pages:notifications]]' }]), + }); }; diff --git a/src/controllers/accounts/profile.js b/src/controllers/accounts/profile.js index f84d6107f5..55aeb67f26 100644 --- a/src/controllers/accounts/profile.js +++ b/src/controllers/accounts/profile.js @@ -1,23 +1,22 @@ 'use strict'; -var nconf = require('nconf'); -var async = require('async'); +const nconf = require('nconf'); const db = require('../../database'); -var user = require('../../user'); -var posts = require('../../posts'); +const user = require('../../user'); +const posts = require('../../posts'); const categories = require('../../categories'); -var plugins = require('../../plugins'); -var meta = require('../../meta'); -var accountHelpers = require('./helpers'); -var helpers = require('../helpers'); -var messaging = require('../../messaging'); -var utils = require('../../utils'); +const plugins = require('../../plugins'); +const meta = require('../../meta'); +const accountHelpers = require('./helpers'); +const helpers = require('../helpers'); +const messaging = require('../../messaging'); +const utils = require('../../utils'); -var profileController = module.exports; +const profileController = module.exports; -profileController.get = function (req, res, callback) { - var lowercaseSlug = req.params.userslug.toLowerCase(); +profileController.get = async function (req, res, next) { + const lowercaseSlug = req.params.userslug.toLowerCase(); if (req.params.userslug !== lowercaseSlug) { if (res.locals.isAPI) { @@ -27,97 +26,71 @@ profileController.get = function (req, res, callback) { } } - var userData; - async.waterfall([ - function (next) { - accountHelpers.getUserDataByUserSlug(req.params.userslug, req.uid, next); - }, - function (_userData, next) { - if (!_userData) { - return callback(); - } - userData = _userData; + const userData = await accountHelpers.getUserDataByUserSlug(req.params.userslug, req.uid); + if (!userData) { + return next(); + } - if (req.uid >= 0) { - req.session.uids_viewed = req.session.uids_viewed || {}; + await incrementProfileViews(req, userData); - if (req.uid !== userData.uid && (!req.session.uids_viewed[userData.uid] || req.session.uids_viewed[userData.uid] < Date.now() - 3600000)) { - user.incrementUserFieldBy(userData.uid, 'profileviews', 1); - req.session.uids_viewed[userData.uid] = Date.now(); - } - } + const [hasPrivateChat, latestPosts, bestPosts] = await Promise.all([ + messaging.hasPrivateChat(req.uid, userData.uid), + getLatestPosts(req.uid, userData), + getBestPosts(req.uid, userData), + posts.parseSignature(userData, req.uid), + ]); - async.parallel({ - hasPrivateChat: function (next) { - messaging.hasPrivateChat(req.uid, userData.uid, next); - }, - latestPosts: function (next) { - getLatestPosts(req.uid, userData, next); - }, - bestPosts: function (next) { - getBestPosts(req.uid, userData, next); - }, - signature: function (next) { - posts.parseSignature(userData, req.uid, next); - }, - }, next); - }, - function (results, next) { - if (meta.config['reputation:disabled']) { - delete userData.reputation; - } + if (meta.config['reputation:disabled']) { + delete userData.reputation; + } - userData.posts = results.latestPosts; // for backwards compat. - userData.latestPosts = results.latestPosts; - userData.bestPosts = results.bestPosts; - userData.hasPrivateChat = results.hasPrivateChat; - userData.breadcrumbs = helpers.buildBreadcrumbs([{ text: userData.username }]); - userData.title = userData.username; - userData.allowCoverPicture = !userData.isSelf || !!meta.config['reputation:disabled'] || userData.reputation >= meta.config['min:rep:cover-picture']; + userData.posts = latestPosts; // for backwards compat. + userData.latestPosts = latestPosts; + userData.bestPosts = bestPosts; + userData.hasPrivateChat = hasPrivateChat; + userData.breadcrumbs = helpers.buildBreadcrumbs([{ text: userData.username }]); + userData.title = userData.username; + userData.allowCoverPicture = !userData.isSelf || !!meta.config['reputation:disabled'] || userData.reputation >= meta.config['min:rep:cover-picture']; - if (!userData.profileviews) { - userData.profileviews = 1; - } + if (!userData.profileviews) { + userData.profileviews = 1; + } - addMetaTags(res, userData); + addMetaTags(res, userData); - userData.selectedGroup = userData.groups.filter(function (group) { - return group && userData.groupTitleArray.includes(group.name); - }); + userData.selectedGroup = userData.groups.filter(function (group) { + return group && userData.groupTitleArray.includes(group.name); + }); - plugins.fireHook('filter:user.account', { userData: userData, uid: req.uid }, next); - }, - function (results) { - res.render('account/profile', results.userData); - }, - ], callback); + const results = await plugins.fireHook('filter:user.account', { userData: userData, uid: req.uid }); + res.render('account/profile', results.userData); }; -function getLatestPosts(callerUid, userData, callback) { - getPosts(callerUid, userData, 'pids', callback); +async function incrementProfileViews(req, userData) { + if (req.uid >= 0) { + req.session.uids_viewed = req.session.uids_viewed || {}; + + if (req.uid !== userData.uid && (!req.session.uids_viewed[userData.uid] || req.session.uids_viewed[userData.uid] < Date.now() - 3600000)) { + await user.incrementUserFieldBy(userData.uid, 'profileviews', 1); + req.session.uids_viewed[userData.uid] = Date.now(); + } + } } -function getBestPosts(callerUid, userData, callback) { - getPosts(callerUid, userData, 'pids:votes', callback); +async function getLatestPosts(callerUid, userData) { + return await getPosts(callerUid, userData, 'pids'); } -function getPosts(callerUid, userData, setSuffix, callback) { - async.waterfall([ - function (next) { - categories.getCidsByPrivilege('categories:cid', callerUid, 'topics:read', next); - }, - function (cids, next) { - const keys = cids.map(c => 'cid:' + c + ':uid:' + userData.uid + ':' + setSuffix); - db.getSortedSetRevRange(keys, 0, 9, next); - }, - function (pids, next) { - posts.getPostSummaryByPids(pids, callerUid, { stripTags: false }, next); - }, - function (posts, next) { - posts = posts.filter(p => p && !p.deleted); - next(null, posts); - }, - ], callback); +async function getBestPosts(callerUid, userData) { + return await getPosts(callerUid, userData, 'pids:votes'); +} + +async function getPosts(callerUid, userData, setSuffix) { + const cids = await categories.getCidsByPrivilege('categories:cid', callerUid, 'topics:read'); + const keys = cids.map(c => 'cid:' + c + ':uid:' + userData.uid + ':' + setSuffix); + const pids = await db.getSortedSetRevRange(keys, 0, 9); + const postData = await posts.getPostSummaryByPids(pids, callerUid, { stripTags: false }); + return postData.filter(p => p && !p.deleted); } function addMetaTags(res, userData) { diff --git a/src/controllers/accounts/sessions.js b/src/controllers/accounts/sessions.js index 1609fa2383..fbea034afc 100644 --- a/src/controllers/accounts/sessions.js +++ b/src/controllers/accounts/sessions.js @@ -1,80 +1,59 @@ 'use strict'; -var async = require('async'); +const util = require('util'); -var db = require('../../database'); -var user = require('../../user'); -var helpers = require('../helpers'); -var accountHelpers = require('./helpers'); +const db = require('../../database'); +const user = require('../../user'); +const helpers = require('../helpers'); +const accountHelpers = require('./helpers'); -var sessionController = module.exports; +const sessionController = module.exports; -sessionController.get = function (req, res, callback) { - var userData; - - async.waterfall([ - function (next) { - accountHelpers.getUserDataByUserSlug(req.params.userslug, req.uid, next); - }, - function (_userData, next) { - userData = _userData; - if (!userData) { - return callback(); - } - - user.auth.getSessions(userData.uid, req.sessionID, next); - }, - function (sessions) { - userData.sessions = sessions; - - userData.title = '[[pages:account/sessions]]'; - userData.breadcrumbs = helpers.buildBreadcrumbs([{ text: userData.username, url: '/user/' + userData.userslug }, { text: '[[pages:account/sessions]]' }]); - - res.render('account/sessions', userData); - }, - ], callback); -}; - -sessionController.revoke = function (req, res, next) { - if (!req.params.hasOwnProperty('uuid')) { +sessionController.get = async function (req, res, next) { + const userData = await accountHelpers.getUserDataByUserSlug(req.params.userslug, req.uid); + if (!userData) { return next(); } - var _id; - var uid = res.locals.uid; - async.waterfall([ - function (next) { - if (!uid) { - return next(new Error('[[error:no-session-found]]')); - } - db.getSortedSetRange('uid:' + uid + ':sessions', 0, -1, next); - }, - function (sids, done) { - async.eachSeries(sids, function (sid, next) { - db.sessionStore.get(sid, function (err, sessionObj) { - if (err) { - return next(err); - } - if (sessionObj && sessionObj.meta && sessionObj.meta.uuid === req.params.uuid) { - _id = sid; - done(); - } else { - next(); - } - }); - }, next); - }, - function (next) { - if (!_id) { - return next(new Error('[[error:no-session-found]]')); - } + userData.sessions = await user.auth.getSessions(userData.uid, req.sessionID); + userData.title = '[[pages:account/sessions]]'; + userData.breadcrumbs = helpers.buildBreadcrumbs([{ text: userData.username, url: '/user/' + userData.userslug }, { text: '[[pages:account/sessions]]' }]); - user.auth.revokeSession(_id, uid, next); - }, - ], function (err) { - if (err) { - return res.status(500).send(err.message); - } - return res.sendStatus(200); - }); + res.render('account/sessions', userData); +}; + +const getSessionAsync = util.promisify(function (sid, callback) { + db.sessionStore.get(sid, (err, sessionObj) => callback(err, sessionObj || null)); +}); + +sessionController.revoke = async function (req, res, next) { + if (!req.params.hasOwnProperty('uuid')) { + return next(); + } + try { + const uid = await user.getUidByUserslug(req.params.userslug); + if (!uid) { + throw new Error('[[error:no-session-found]]'); + } + const sids = await db.getSortedSetRange('uid:' + uid + ':sessions', 0, -1); + let _id; + for (const sid of sids) { + /* eslint-disable no-await-in-loop */ + const sessionObj = await getSessionAsync(sid); + if (sessionObj && sessionObj.meta && sessionObj.meta.uuid === req.params.uuid) { + _id = sid; + break; + } + } + + if (!_id) { + throw new Error('[[error:no-session-found]]'); + } + + await user.auth.revokeSession(_id, uid); + } catch (err) { + return res.status(500).send(err.message); + } + + res.sendStatus(200); }; diff --git a/src/controllers/accounts/settings.js b/src/controllers/accounts/settings.js index 991b0a8e24..1f0645fd77 100644 --- a/src/controllers/accounts/settings.js +++ b/src/controllers/accounts/settings.js @@ -1,356 +1,306 @@ 'use strict'; -var async = require('async'); -var nconf = require('nconf'); -var winston = require('winston'); -var _ = require('lodash'); -var jwt = require('jsonwebtoken'); +const nconf = require('nconf'); +const winston = require('winston'); +const _ = require('lodash'); +const jwt = require('jsonwebtoken'); +const util = require('util'); -var user = require('../../user'); -var languages = require('../../languages'); -var meta = require('../../meta'); -var plugins = require('../../plugins'); -var privileges = require('../../privileges'); -var categories = require('../../categories'); -var notifications = require('../../notifications'); -var db = require('../../database'); -var helpers = require('../helpers'); -var accountHelpers = require('./helpers'); +const user = require('../../user'); +const languages = require('../../languages'); +const meta = require('../../meta'); +const plugins = require('../../plugins'); +const privileges = require('../../privileges'); +const categories = require('../../categories'); +const notifications = require('../../notifications'); +const db = require('../../database'); +const helpers = require('../helpers'); +const accountHelpers = require('./helpers'); -var settingsController = module.exports; +const settingsController = module.exports; -settingsController.get = function (req, res, callback) { - var userData; - async.waterfall([ - function (next) { - accountHelpers.getUserDataByUserSlug(req.params.userslug, req.uid, next); - }, - function (_userData, next) { - userData = _userData; - if (!userData) { - return callback(); - } - async.parallel({ - settings: function (next) { - user.getSettings(userData.uid, next); - }, - languages: function (next) { - languages.list(next); - }, - soundsMapping: function (next) { - meta.sounds.getUserSoundMap(userData.uid, next); - }, - }, next); - }, - function (results, next) { - userData.settings = results.settings; - userData.languages = results.languages; - if (userData.isAdmin && userData.isSelf) { - userData.acpLanguages = _.cloneDeep(results.languages); - } +settingsController.get = async function (req, res, next) { + const userData = await accountHelpers.getUserDataByUserSlug(req.params.userslug, req.uid); + if (!userData) { + return next(); + } + const [settings, languagesData, soundsMapping] = await Promise.all([ + user.getSettings(userData.uid), + languages.list(), + meta.sounds.getUserSoundMap(userData.uid), - var types = [ - 'notification', - 'chat-incoming', - 'chat-outgoing', - ]; - var aliases = { - notification: 'notificationSound', - 'chat-incoming': 'incomingChatSound', - 'chat-outgoing': 'outgoingChatSound', - }; + ]); - 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 === results.soundsMapping[type], - }; - }); + userData.settings = settings; + userData.languages = languagesData; + if (userData.isAdmin && userData.isSelf) { + userData.acpLanguages = _.cloneDeep(languagesData); + } - return { - name: pack.name, - sounds: sounds, - }; - }); + addSoundSettings(userData, soundsMapping); - userData[type + '-sound'] = soundpacks; - // fallback - userData[aliases[type]] = soundpacks.concat.apply([], soundpacks.map(function (pack) { - return pack.sounds.map(function (sound) { - return { - name: sound.value, - selected: sound.selected, - }; - }); - })); - }); + const data = await plugins.fireHook('filter:user.customSettings', { + settings: settings, + customSettings: [], + uid: req.uid, + }); - plugins.fireHook('filter:user.customSettings', { settings: results.settings, customSettings: [], uid: req.uid }, next); - }, - function (data, next) { - userData.customSettings = data.customSettings; - async.parallel({ - notificationSettings: function (next) { - getNotificationSettings(userData, next); - }, - routes: function (next) { - getHomePageRoutes(userData, next); - }, - }, next); - }, - function (results) { - userData.homePageRoutes = results.routes; - userData.notificationSettings = results.notificationSettings; - userData.disableEmailSubscriptions = meta.config.disableEmailSubscriptions; + const [notificationSettings, routes] = await Promise.all([ + getNotificationSettings(userData), + getHomePageRoutes(userData), + ]); - userData.dailyDigestFreqOptions = [ - { value: 'off', name: '[[user:digest_off]]', selected: userData.settings.dailyDigestFreq === 'off' }, - { value: 'day', name: '[[user:digest_daily]]', selected: userData.settings.dailyDigestFreq === 'day' }, - { value: 'week', name: '[[user:digest_weekly]]', selected: userData.settings.dailyDigestFreq === 'week' }, - { value: 'month', name: '[[user:digest_monthly]]', selected: userData.settings.dailyDigestFreq === 'month' }, - ]; + userData.customSettings = data.customSettings; + userData.homePageRoutes = routes; + userData.notificationSettings = notificationSettings; + userData.disableEmailSubscriptions = meta.config.disableEmailSubscriptions; - userData.bootswatchSkinOptions = [ - { name: 'Default', value: '' }, - { name: 'Cerulean', value: 'cerulean' }, - { name: 'Cosmo', value: 'cosmo' }, - { name: 'Cyborg', value: 'cyborg' }, - { name: 'Darkly', value: 'darkly' }, - { name: 'Flatly', value: 'flatly' }, - { name: 'Journal', value: 'journal' }, - { name: 'Lumen', value: 'lumen' }, - { name: 'Paper', value: 'paper' }, - { name: 'Readable', value: 'readable' }, - { name: 'Sandstone', value: 'sandstone' }, - { name: 'Simplex', value: 'simplex' }, - { name: 'Slate', value: 'slate' }, - { name: 'Spacelab', value: 'spacelab' }, - { name: 'Superhero', value: 'superhero' }, - { name: 'United', value: 'united' }, - { name: 'Yeti', value: 'yeti' }, - ]; + userData.dailyDigestFreqOptions = [ + { value: 'off', name: '[[user:digest_off]]', selected: userData.settings.dailyDigestFreq === 'off' }, + { value: 'day', name: '[[user:digest_daily]]', selected: userData.settings.dailyDigestFreq === 'day' }, + { value: 'week', name: '[[user:digest_weekly]]', selected: userData.settings.dailyDigestFreq === 'week' }, + { value: 'month', name: '[[user:digest_monthly]]', selected: userData.settings.dailyDigestFreq === 'month' }, + ]; - userData.bootswatchSkinOptions.forEach(function (skin) { - skin.selected = skin.value === userData.settings.bootswatchSkin; - }); + userData.bootswatchSkinOptions = [ + { name: 'Default', value: '' }, + { name: 'Cerulean', value: 'cerulean' }, + { name: 'Cosmo', value: 'cosmo' }, + { name: 'Cyborg', value: 'cyborg' }, + { name: 'Darkly', value: 'darkly' }, + { name: 'Flatly', value: 'flatly' }, + { name: 'Journal', value: 'journal' }, + { name: 'Lumen', value: 'lumen' }, + { name: 'Paper', value: 'paper' }, + { name: 'Readable', value: 'readable' }, + { name: 'Sandstone', value: 'sandstone' }, + { name: 'Simplex', value: 'simplex' }, + { name: 'Slate', value: 'slate' }, + { name: 'Spacelab', value: 'spacelab' }, + { name: 'Superhero', value: 'superhero' }, + { name: 'United', value: 'united' }, + { name: 'Yeti', value: 'yeti' }, + ]; - userData.languages.forEach(function (language) { - language.selected = language.code === userData.settings.userLang; - }); + userData.bootswatchSkinOptions.forEach(function (skin) { + skin.selected = skin.value === userData.settings.bootswatchSkin; + }); - if (userData.isAdmin && userData.isSelf) { - userData.acpLanguages.forEach(function (language) { - language.selected = language.code === userData.settings.acpLang; - }); - } + userData.languages.forEach(function (language) { + language.selected = language.code === userData.settings.userLang; + }); - var notifFreqOptions = [ - 'all', - 'first', - 'everyTen', - 'threshold', - 'logarithmic', - 'disabled', - ]; + if (userData.isAdmin && userData.isSelf) { + userData.acpLanguages.forEach(function (language) { + language.selected = language.code === userData.settings.acpLang; + }); + } - userData.upvoteNotifFreq = notifFreqOptions.map(function (name) { + const notifFreqOptions = [ + 'all', + 'first', + 'everyTen', + 'threshold', + 'logarithmic', + 'disabled', + ]; + + userData.upvoteNotifFreq = notifFreqOptions.map(name => ({ name: name, selected: name === userData.settings.upvoteNotifFreq })); + + userData.categoryWatchState = { [userData.settings.categoryWatchState]: true }; + + userData.disableCustomUserSkins = meta.config.disableCustomUserSkins; + + userData.allowUserHomePage = meta.config.allowUserHomePage; + + userData.hideFullname = meta.config.hideFullname; + userData.hideEmail = meta.config.hideEmail; + + userData.inTopicSearchAvailable = plugins.hasListeners('filter:topic.search'); + + userData.maxTopicsPerPage = meta.config.maxTopicsPerPage; + userData.maxPostsPerPage = meta.config.maxPostsPerPage; + + userData.title = '[[pages:account/settings]]'; + userData.breadcrumbs = helpers.buildBreadcrumbs([{ text: userData.username, url: '/user/' + userData.userslug }, { text: '[[user:settings]]' }]); + + res.render('account/settings', userData); +}; + +function addSoundSettings(userData, soundsMapping) { + const types = [ + 'notification', + 'chat-incoming', + 'chat-outgoing', + ]; + const aliases = { + notification: 'notificationSound', + 'chat-incoming': 'incomingChatSound', + 'chat-outgoing': 'outgoingChatSound', + }; + + types.forEach(function (type) { + const soundpacks = plugins.soundpacks.map(function (pack) { + const sounds = Object.keys(pack.sounds).map(function (soundName) { + const value = pack.name + ' | ' + soundName; return { - name: name, - selected: name === userData.settings.upvoteNotifFreq, + name: soundName, + value: value, + selected: value === soundsMapping[type], }; }); - userData.categoryWatchState = { [userData.settings.categoryWatchState]: true }; + return { + name: pack.name, + sounds: sounds, + }; + }); - userData.disableCustomUserSkins = meta.config.disableCustomUserSkins; + userData[type + '-sound'] = soundpacks; + // fallback + userData[aliases[type]] = soundpacks.concat.apply([], soundpacks.map(function (pack) { + return pack.sounds.map(function (sound) { + return { + name: sound.value, + selected: sound.selected, + }; + }); + })); + }); +} - userData.allowUserHomePage = meta.config.allowUserHomePage; +const jwtVerifyAsync = util.promisify(function (token, callback) { + jwt.verify(token, nconf.get('secret'), (err, payload) => callback(err, payload)); +}); - userData.hideFullname = meta.config.hideFullname; - userData.hideEmail = meta.config.hideEmail; - - userData.inTopicSearchAvailable = plugins.hasListeners('filter:topic.search'); - - userData.maxTopicsPerPage = meta.config.maxTopicsPerPage; - userData.maxPostsPerPage = meta.config.maxPostsPerPage; - - userData.title = '[[pages:account/settings]]'; - userData.breadcrumbs = helpers.buildBreadcrumbs([{ text: userData.username, url: '/user/' + userData.userslug }, { text: '[[user:settings]]' }]); - - res.render('account/settings', userData); - }, - ], callback); -}; - -settingsController.unsubscribe = function (req, res) { +settingsController.unsubscribe = async function (req, res) { if (!req.params.token) { return res.sendStatus(404); } - - jwt.verify(req.params.token, nconf.get('secret'), function (err, payload) { - if (err) { - return res.sendStatus(403); + let payload; + try { + payload = await jwtVerifyAsync(req.params.token); + if (!payload || (payload.template !== 'notification' && payload.template !== 'digest')) { + return res.sendStatus(404); } - - switch (payload.template) { - case 'digest': - async.parallel([ - async.apply(user.setSetting, payload.uid, 'dailyDigestFreq', 'off'), - async.apply(user.updateDigestSetting, payload.uid, 'off'), - ], function (err) { - if (err) { - winston.error('[settings/unsubscribe] One-click unsubscribe failed with error: ' + err.message); - return res.sendStatus(500); - } - - return res.sendStatus(200); - }); - break; - case 'notification': - async.waterfall([ - async.apply(db.getObjectField, 'user:' + payload.uid + ':settings', 'notificationType_' + payload.type), - (current, next) => { - user.setSetting(payload.uid, 'notificationType_' + payload.type, (current === 'notificationemail' ? 'notification' : 'none'), next); - }, - ], function (err) { - if (err) { - winston.error('[settings/unsubscribe] One-click unsubscribe failed with error: ' + err.message); - return res.sendStatus(500); - } - - return res.sendStatus(200); - }); - break; - default: - res.sendStatus(404); - break; + } catch (err) { + return res.sendStatus(403); + } + try { + if (payload.template === 'digest') { + await Promise.all([ + user.setSetting(payload.uid, 'dailyDigestFreq', 'off'), + user.updateDigestSetting(payload.uid, 'off'), + ]); + } else if (payload.template === 'notification') { + const current = await db.getObjectField('user:' + payload.uid + ':settings', 'notificationType_' + payload.type); + await user.setSetting(payload.uid, 'notificationType_' + payload.type, (current === 'notificationemail' ? 'notification' : 'none')); } - }); + res.sendStatus(200); + } catch (err) { + winston.error('[settings/unsubscribe] One-click unsubscribe failed with error: ' + err.message); + res.sendStatus(500); + } }; -function getNotificationSettings(userData, callback) { - var privilegedTypes = []; +async function getNotificationSettings(userData) { + const privilegedTypes = []; - async.waterfall([ - function (next) { - user.getPrivileges(userData.uid, next); - }, - function (privileges, next) { - if (privileges.isAdmin) { - privilegedTypes.push('notificationType_new-register'); - } - if (privileges.isAdmin || privileges.isGlobalMod || privileges.isModeratorOfAnyCategory) { - privilegedTypes.push('notificationType_post-queue', 'notificationType_new-post-flag'); - } - if (privileges.isAdmin || privileges.isGlobalMod) { - privilegedTypes.push('notificationType_new-user-flag'); - } - plugins.fireHook('filter:user.notificationTypes', { - types: notifications.baseTypes.slice(), - privilegedTypes: privilegedTypes, - }, next); - }, - function (results, next) { - function modifyType(type) { - var setting = userData.settings[type]; + const privileges = await user.getPrivileges(userData.uid); + if (privileges.isAdmin) { + privilegedTypes.push('notificationType_new-register'); + } + if (privileges.isAdmin || privileges.isGlobalMod || privileges.isModeratorOfAnyCategory) { + privilegedTypes.push('notificationType_post-queue', 'notificationType_new-post-flag'); + } + if (privileges.isAdmin || privileges.isGlobalMod) { + privilegedTypes.push('notificationType_new-user-flag'); + } + const results = await plugins.fireHook('filter:user.notificationTypes', { + types: notifications.baseTypes.slice(), + privilegedTypes: privilegedTypes, + }); - return { - name: type, - label: '[[notifications:' + type + ']]', - none: setting === 'none', - notification: setting === 'notification', - email: setting === 'email', - notificationemail: setting === 'notificationemail', - }; - } + function modifyType(type) { + const setting = userData.settings[type]; + return { + name: type, + label: '[[notifications:' + type + ']]', + none: setting === 'none', + notification: setting === 'notification', + email: setting === 'email', + notificationemail: setting === 'notificationemail', + }; + } - if (meta.config.disableChat) { - results.types = results.types.filter(type => type !== 'notificationType_new-chat'); - } + if (meta.config.disableChat) { + results.types = results.types.filter(type => type !== 'notificationType_new-chat'); + } - var notificationSettings = results.types.map(modifyType).concat(results.privilegedTypes.map(modifyType)); - next(null, notificationSettings); - }, - ], callback); + return results.types.map(modifyType).concat(results.privilegedTypes.map(modifyType)); } -function getHomePageRoutes(userData, callback) { - async.waterfall([ - function (next) { - db.getSortedSetRange('cid:0:children', 0, -1, next); +async function getHomePageRoutes(userData) { + let cids = await categories.getAllCidsFromSet('cid:0:children'); + cids = await privileges.categories.filterCids('find', cids, userData.uid); + let categoryData = await categories.getCategoriesFields(cids, ['name', 'slug']); + + categoryData = categoryData.map(function (category) { + return { + route: 'category/' + category.slug, + name: 'Category: ' + category.name, + }; + }); + + const data = await plugins.fireHook('filter:homepage.get', { routes: [ + { + route: 'categories', + name: 'Categories', }, - function (cids, next) { - privileges.categories.filterCids('find', cids, 0, next); + { + route: 'unread', + name: 'Unread', }, - function (cids, next) { - categories.getCategoriesFields(cids, ['name', 'slug'], next); + { + route: 'recent', + name: 'Recent', }, - function (categoryData, next) { - categoryData = categoryData.map(function (category) { - return { - route: 'category/' + category.slug, - name: 'Category: ' + category.name, - }; - }); - - categoryData = categoryData || []; - - plugins.fireHook('filter:homepage.get', { routes: [ - { - route: 'categories', - name: 'Categories', - }, - { - route: 'unread', - name: 'Unread', - }, - { - route: 'recent', - name: 'Recent', - }, - { - route: 'top', - name: 'Top', - }, - { - route: 'popular', - name: 'Popular', - }, - ].concat(categoryData, [ - { - route: 'custom', - name: 'Custom', - }, - ]) }, next); + { + route: 'top', + name: 'Top', }, - function (data, next) { - // Set selected for each route - var customIdx; - var hasSelected = false; - data.routes = data.routes.map(function (route, idx) { - if (route.route === userData.settings.homePageRoute) { - route.selected = true; - hasSelected = true; - } else { - route.selected = false; - } - - if (route.route === 'custom') { - customIdx = idx; - } - - return route; - }); - - if (!hasSelected && customIdx && userData.settings.homePageRoute !== 'none') { - data.routes[customIdx].selected = true; - } - - next(null, data.routes); + { + route: 'popular', + name: 'Popular', }, - ], callback); + ].concat(categoryData, [ + { + route: 'custom', + name: 'Custom', + }, + ]) }); + + // Set selected for each route + var customIdx; + var hasSelected = false; + data.routes = data.routes.map(function (route, idx) { + if (route.route === userData.settings.homePageRoute) { + route.selected = true; + hasSelected = true; + } else { + route.selected = false; + } + + if (route.route === 'custom') { + customIdx = idx; + } + + return route; + }); + + if (!hasSelected && customIdx && userData.settings.homePageRoute !== 'none') { + data.routes[customIdx].selected = true; + } + + return data.routes; } diff --git a/src/controllers/accounts/uploads.js b/src/controllers/accounts/uploads.js index 899ed866b5..02cc1e9ca7 100644 --- a/src/controllers/accounts/uploads.js +++ b/src/controllers/accounts/uploads.js @@ -1,57 +1,40 @@ 'use strict'; +const nconf = require('nconf'); -var async = require('async'); -var nconf = require('nconf'); +const db = require('../../database'); +const helpers = require('../helpers'); +const meta = require('../../meta'); +const pagination = require('../../pagination'); +const accountHelpers = require('./helpers'); -var db = require('../../database'); -var helpers = require('../helpers'); -var meta = require('../../meta'); -var pagination = require('../../pagination'); -var accountHelpers = require('./helpers'); +const uploadsController = module.exports; -var uploadsController = module.exports; +uploadsController.get = async function (req, res, next) { + const userData = await accountHelpers.getUserDataByUserSlug(req.params.userslug, req.uid); + if (!userData) { + return next(); + } -uploadsController.get = function (req, res, callback) { - var userData; + const page = Math.max(1, parseInt(req.query.page, 10) || 1); + const itemsPerPage = 25; + const start = (page - 1) * itemsPerPage; + const stop = start + itemsPerPage - 1; + const [itemCount, uploadNames] = await Promise.all([ + db.sortedSetCard('uid:' + userData.uid + ':uploads'), + db.getSortedSetRevRange('uid:' + userData.uid + ':uploads', start, stop), + ]); - var page = Math.max(1, parseInt(req.query.page, 10) || 1); - var itemsPerPage = 25; - - async.waterfall([ - function (next) { - accountHelpers.getUserDataByUserSlug(req.params.userslug, req.uid, next); - }, - function (_userData, next) { - userData = _userData; - if (!userData) { - return callback(); - } - - var start = (page - 1) * itemsPerPage; - var stop = start + itemsPerPage - 1; - async.parallel({ - itemCount: function (next) { - db.sortedSetCard('uid:' + userData.uid + ':uploads', next); - }, - uploadNames: function (next) { - db.getSortedSetRevRange('uid:' + userData.uid + ':uploads', start, stop, next); - }, - }, next); - }, - function (results) { - userData.uploads = results.uploadNames.map(function (uploadName) { - return { - name: uploadName, - url: nconf.get('upload_url') + uploadName, - }; - }); - var pageCount = Math.ceil(results.itemCount / itemsPerPage); - userData.pagination = pagination.create(page, pageCount, req.query); - userData.privateUploads = meta.config.privateUploads === 1; - userData.title = '[[pages:account/uploads, ' + userData.username + ']]'; - userData.breadcrumbs = helpers.buildBreadcrumbs([{ text: userData.username, url: '/user/' + userData.userslug }, { text: '[[global:uploads]]' }]); - res.render('account/uploads', userData); - }, - ], callback); + userData.uploads = uploadNames.map(function (uploadName) { + return { + name: uploadName, + url: nconf.get('upload_url') + uploadName, + }; + }); + const pageCount = Math.ceil(itemCount / itemsPerPage); + userData.pagination = pagination.create(page, pageCount, req.query); + userData.privateUploads = meta.config.privateUploads === 1; + userData.title = '[[pages:account/uploads, ' + userData.username + ']]'; + userData.breadcrumbs = helpers.buildBreadcrumbs([{ text: userData.username, url: '/user/' + userData.userslug }, { text: '[[global:uploads]]' }]); + res.render('account/uploads', userData); }; diff --git a/src/plugins/hooks.js b/src/plugins/hooks.js index f5934add23..0adeadb8e9 100644 --- a/src/plugins/hooks.js +++ b/src/plugins/hooks.js @@ -7,6 +7,9 @@ const utils = require('../utils'); module.exports = function (Plugins) { Plugins.deprecatedHooks = { 'filter:controllers.topic.get': 'filter:topic.build', + 'filter:user.account': 'filter:account/profile.build', + 'filter:user.account.edit': 'filter:account/edit.build', + 'filter:notifications.get': 'filter:notifications.build', }; Plugins.internals = { From 7beef91c3f7cbb5b1ddc806ac33ea6f34c61d7fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 12 Aug 2019 21:56:09 -0400 Subject: [PATCH 3/5] feat: async/await --- src/controllers/ping.js | 17 +++++------- src/controllers/posts.js | 58 ++++++++++++++-------------------------- src/posts/topics.js | 4 +-- 3 files changed, 29 insertions(+), 50 deletions(-) diff --git a/src/controllers/ping.js b/src/controllers/ping.js index 6c4dc7f697..f51270d210 100644 --- a/src/controllers/ping.js +++ b/src/controllers/ping.js @@ -1,16 +1,13 @@ 'use strict'; -const async = require('async'); const nconf = require('nconf'); const db = require('../database'); -module.exports.ping = function (req, res, next) { - async.waterfall([ - function (next) { - db.getObject('config', next); - }, - function () { - res.status(200).send(req.path === nconf.get('relative_path') + '/sping' ? 'healthy' : '200'); - }, - ], next); +module.exports.ping = async function (req, res, next) { + try { + await db.getObject('config'); + res.status(200).send(req.path === nconf.get('relative_path') + '/sping' ? 'healthy' : '200'); + } catch (err) { + next(err); + } }; diff --git a/src/controllers/posts.js b/src/controllers/posts.js index 550324c0e0..f8d445d1a4 100644 --- a/src/controllers/posts.js +++ b/src/controllers/posts.js @@ -1,49 +1,31 @@ 'use strict'; -var async = require('async'); +const posts = require('../posts'); +const privileges = require('../privileges'); +const helpers = require('./helpers'); -var posts = require('../posts'); -var privileges = require('../privileges'); -var helpers = require('./helpers'); +const postsController = module.exports; -var postsController = module.exports; - -postsController.redirectToPost = function (req, res, next) { - var pid = parseInt(req.params.pid, 10); +postsController.redirectToPost = async function (req, res, next) { + const pid = parseInt(req.params.pid, 10); if (!pid) { return next(); } - async.waterfall([ - function (next) { - async.parallel({ - canRead: function (next) { - privileges.posts.can('topics:read', pid, req.uid, next); - }, - path: function (next) { - posts.generatePostPath(pid, req.uid, next); - }, - }, next); - }, - function (results, next) { - if (!results.path) { - return next(); - } - if (!results.canRead) { - return helpers.notAllowed(req, res); - } - helpers.redirect(res, results.path); - }, - ], next); + const [canRead, path] = await Promise.all([ + privileges.posts.can('topics:read', pid, req.uid), + posts.generatePostPath(pid, req.uid), + ]); + if (!path) { + return next(); + } + if (!canRead) { + return helpers.notAllowed(req, res); + } + helpers.redirect(res, path); }; -postsController.getRecentPosts = function (req, res, next) { - async.waterfall([ - function (next) { - posts.getRecentPosts(req.uid, 0, 19, req.params.term, next); - }, - function (data) { - res.json(data); - }, - ], next); +postsController.getRecentPosts = async function (req, res) { + const data = await posts.getRecentPosts(req.uid, 0, 19, req.params.term); + res.json(data); }; diff --git a/src/posts/topics.js b/src/posts/topics.js index f5cd8c5a32..973973686a 100644 --- a/src/posts/topics.js +++ b/src/posts/topics.js @@ -38,8 +38,8 @@ module.exports = function (Posts) { ]); const paths = pids.map(function (pid, index) { - var slug = topicData[index] ? topicData[index].slug : null; - var postIndex = utils.isNumber(indices[index]) ? parseInt(indices[index], 10) + 1 : null; + const slug = topicData[index] ? topicData[index].slug : null; + const postIndex = utils.isNumber(indices[index]) ? parseInt(indices[index], 10) + 1 : null; if (slug && postIndex) { return '/topic/' + slug + '/' + postIndex; From 216eba6847ad4867c6381f2cf670b669e1daf979 Mon Sep 17 00:00:00 2001 From: "Misty (Bot)" Date: Tue, 13 Aug 2019 09:28:39 +0000 Subject: [PATCH 4/5] Latest translations and fallbacks --- public/language/pt-BR/admin/extend/plugins.json | 6 +++--- public/language/pt-BR/admin/settings/cookies.json | 2 +- public/language/pt-BR/admin/settings/post.json | 14 +++++++------- public/language/pt-BR/global.json | 4 ++-- public/language/pt-BR/pages.json | 2 +- public/language/pt-BR/topic.json | 8 ++++---- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/public/language/pt-BR/admin/extend/plugins.json b/public/language/pt-BR/admin/extend/plugins.json index e6061b1b71..0a99849fa5 100644 --- a/public/language/pt-BR/admin/extend/plugins.json +++ b/public/language/pt-BR/admin/extend/plugins.json @@ -9,7 +9,7 @@ "plugin-search": "Pesquisar Plugin", "plugin-search-placeholder": "Pesquisar por plugin...", - "submit-anonymous-usage": "Submit anonymous plugin usage data.", + "submit-anonymous-usage": "Enviar anonimamente dados de uso deste plugin", "reorder-plugins": "Re-ordenar Plugins", "order-active": "Ordenar Plugins Ativos", "dev-interested": "Interessado em desenvolver plugins para o NodeBB?", @@ -30,8 +30,8 @@ "plugin-item.more-info": "Para mais informação:", "plugin-item.unknown": "Desconhecido", "plugin-item.unknown-explanation": "O estado deste plugin não pôde ser determinado, possivelmente devido a um erro de configuração.", - "plugin-item.compatible": "This plugin works on NodeBB %1", - "plugin-item.not-compatible": "This plugin has no compatibility data, make sure it works before installing on your production environment.", + "plugin-item.compatible": "Este plugin funciona no NodeBB %1", + "plugin-item.not-compatible": "Este plugin não apresenta compatibilidade, tenha certeza que ele funcione antes de instalar em seu ambiente de produção", "alert.enabled": "Plugin Ativado", "alert.disabled": "Plugin Desativado", diff --git a/public/language/pt-BR/admin/settings/cookies.json b/public/language/pt-BR/admin/settings/cookies.json index 92226ee458..03206154c8 100644 --- a/public/language/pt-BR/admin/settings/cookies.json +++ b/public/language/pt-BR/admin/settings/cookies.json @@ -4,7 +4,7 @@ "consent.message": "Mensagem de notificação", "consent.acceptance": "Mensagem de aprovação", "consent.link-text": "Texto do Link da Política", - "consent.link-url": "Policy Link URL", + "consent.link-url": "Link para Política de Privacidade", "consent.blank-localised-default": "Deixar em branco para utilizar os padrões de localidade do NodeBB", "settings": "Configurações", "cookie-domain": "Domínio da sessão de cookie", diff --git a/public/language/pt-BR/admin/settings/post.json b/public/language/pt-BR/admin/settings/post.json index d2ed9d0495..6ccf2da157 100644 --- a/public/language/pt-BR/admin/settings/post.json +++ b/public/language/pt-BR/admin/settings/post.json @@ -11,15 +11,15 @@ "restrictions-new": "Restrições a Novos Usuários", "restrictions.post-queue": "Ativar enfileiramento de posts", "restrictions-new.post-queue": "Ativar novas restrições de usuários", - "restrictions.post-queue-help": "Enabling post queue will put the posts of new users in a queue for approval", - "restrictions-new.post-queue-help": "Enabling new user restrictions will set restrictions on posts created by new users", - "restrictions.seconds-between": "Number of seconds between posts", + "restrictions.post-queue-help": "Habilitar a fila de postagens colocará as postagens de novos usuários na fila para aprovação", + "restrictions-new.post-queue-help": "Habilitar restrições a novos usuários irá estabelecer restrições em postagens criadas por novos usuários", + "restrictions.seconds-between": "Segundos entre postagens", "restrictions.seconds-between-new": "Tempo em segundos entre postagens para novos usuários", "restrictions.rep-threshold": "Reputação mínima para que essas restrições sejam desativadas", - "restrictions.seconds-before-new": "Seconds before a new user can make their first post", - "restrictions.seconds-edit-after": "Number of seconds a post remains editable (set to 0 to disable)", - "restrictions.seconds-delete-after": "Number of seconds a post remains deletable (set to 0 to disable)", - "restrictions.replies-no-delete": "Number of replies after users are disallowed to delete their own topics (set to 0 to disable)", + "restrictions.seconds-before-new": "Segundos necessários antes de um novo usuário poder realizar sua primeira postagem", + "restrictions.seconds-edit-after": "Tempo, em segundos, que uma postagem permanece editável, após postada (coloque 0 para desabilitar)", + "restrictions.seconds-delete-after": "Tempo, em segundos, que uma postagem pode ser deletada, após postada (coloque 0 para desabilitar)", + "restrictions.replies-no-delete": "Após este número de respostas em uma postagem, o usuário não poderá deletar sua postagem (coloque 0 para desabilitar)", "restrictions.min-title-length": "Tamanho Mínimo dos Títulos", "restrictions.max-title-length": "Tamanho Máximo dos Títulos", "restrictions.min-post-length": "Tamanho Mínimo dos Posts", diff --git a/public/language/pt-BR/global.json b/public/language/pt-BR/global.json index 2ee1c4d81f..aa9af63ca9 100644 --- a/public/language/pt-BR/global.json +++ b/public/language/pt-BR/global.json @@ -59,8 +59,8 @@ "downvoted": "Votou negativamente", "views": "Visualizações", "reputation": "Reputação", - "lastpost": "Last post", - "firstpost": "First post", + "lastpost": "Última postagem", + "firstpost": "Primeira postagem", "read_more": "ler mais", "more": "Mais", "posted_ago_by_guest": "postado %1 por Visitante", diff --git a/public/language/pt-BR/pages.json b/public/language/pt-BR/pages.json index 05db325b39..be34ba5fb2 100644 --- a/public/language/pt-BR/pages.json +++ b/public/language/pt-BR/pages.json @@ -43,7 +43,7 @@ "account/following": "Pessoas que %1 segue", "account/followers": "Pessoas que seguem %1", "account/posts": "Posts feitos por %1", - "account/latest-posts": "Latest posts made by %1", + "account/latest-posts": "Última postagem realizada por %1", "account/topics": "Tópicos criados por %1", "account/groups": "Grupos de %1", "account/watched_categories": "Categorias Acompanhadas por %1", diff --git a/public/language/pt-BR/topic.json b/public/language/pt-BR/topic.json index ce1404c214..f5d866eb10 100644 --- a/public/language/pt-BR/topic.json +++ b/public/language/pt-BR/topic.json @@ -23,7 +23,7 @@ "purge": "Expurgar", "restore": "Restaurar", "move": "Mover", - "change-owner": "Change Owner", + "change-owner": "Trocar proprietário", "fork": "Clonar", "link": "Link", "share": "Compartilhar", @@ -65,7 +65,7 @@ "thread_tools.move": "Mover Tópico", "thread_tools.move-posts": "Mover Posts", "thread_tools.move_all": "Mover Tudo", - "thread_tools.change_owner": "Change Owner", + "thread_tools.change_owner": "Trocar proprietário", "thread_tools.select_category": "Escolha a Categoria", "thread_tools.fork": "Ramificar Tópico", "thread_tools.delete": "Deletar Tópico", @@ -100,7 +100,7 @@ "delete_posts_instruction": "Clique nos posts que você deseja deletar/limpar", "merge_topics_instruction": "Clique nos tópicos que você deseja mesclar", "move_posts_instruction": "Clique nas postagens que você deseja mover", - "change_owner_instruction": "Click the posts you want to assign to another user", + "change_owner_instruction": "Clique na postagem que você quer associar a outro usuário", "composer.title_placeholder": "Digite aqui o título para o seu tópico...", "composer.handle_placeholder": "Nome", "composer.discard": "Descartar", @@ -133,5 +133,5 @@ "diffs.no-revisions-description": "Este post foi revisado %1 vezes.", "diffs.current-revision": "revisão atual", "diffs.original-revision": "revisão original", - "timeago_later": "%1 later" + "timeago_later": "%1 depois" } \ No newline at end of file From 72590b3462d98731e528ed6f34832bb396b846d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Tue, 13 Aug 2019 10:36:48 -0400 Subject: [PATCH 5/5] feat: async/await admin/controllers --- src/controllers/admin/admins-mods.js | 67 ++++++--------- src/controllers/admin/appearance.js | 4 +- src/controllers/admin/blacklist.js | 28 +++---- src/controllers/admin/cache.js | 18 ++-- src/controllers/admin/categories.js | 118 ++++++++++++--------------- 5 files changed, 96 insertions(+), 139 deletions(-) diff --git a/src/controllers/admin/admins-mods.js b/src/controllers/admin/admins-mods.js index 92d4a4bf20..23e013b444 100644 --- a/src/controllers/admin/admins-mods.js +++ b/src/controllers/admin/admins-mods.js @@ -1,52 +1,31 @@ 'use strict'; -var async = require('async'); +const groups = require('../../groups'); +const categories = require('../../categories'); +const privileges = require('../../privileges'); -var groups = require('../../groups'); -var categories = require('../../categories'); -var privileges = require('../../privileges'); +const AdminsMods = module.exports; -var AdminsMods = module.exports; +AdminsMods.get = async function (req, res) { + const [admins, globalMods, categories] = await Promise.all([ + groups.get('administrators', { uid: req.uid }), + groups.get('Global Moderators', { uid: req.uid }), + getModeratorsOfCategories(req.uid), + ]); -AdminsMods.get = function (req, res, next) { - async.waterfall([ - function (next) { - async.parallel({ - admins: function (next) { - groups.get('administrators', { uid: req.uid }, next); - }, - globalMods: function (next) { - groups.get('Global Moderators', { uid: req.uid }, next); - }, - categories: function (next) { - getModeratorsOfCategories(req.uid, next); - }, - }, next); - }, - function (results) { - results.allPrivileges = privileges.userPrivilegeList; - res.render('admin/manage/admins-mods', results); - }, - ], next); + res.render('admin/manage/admins-mods', { + admins: admins, + globalMods: globalMods, + categories: categories, + allPrivileges: privileges.userPrivilegeList, + }); }; -function getModeratorsOfCategories(uid, callback) { - async.waterfall([ - function (next) { - categories.buildForSelect(uid, 'find', next); - }, - function (categoryData, next) { - async.map(categoryData, function (category, next) { - async.waterfall([ - function (next) { - categories.getModerators(category.cid, next); - }, - function (moderators, next) { - category.moderators = moderators; - next(null, category); - }, - ], next); - }, next); - }, - ], callback); +async function getModeratorsOfCategories(uid) { + const categoryData = await categories.buildForSelect(uid, 'find'); + const moderators = await Promise.all(categoryData.map(c => categories.getModerators(c.cid))); + categoryData.forEach((c, index) => { + c.moderators = moderators[index]; + }); + return categoryData; } diff --git a/src/controllers/admin/appearance.js b/src/controllers/admin/appearance.js index 65256f62ad..90b143ceb9 100644 --- a/src/controllers/admin/appearance.js +++ b/src/controllers/admin/appearance.js @@ -1,9 +1,9 @@ 'use strict'; -var appearanceController = module.exports; +const appearanceController = module.exports; appearanceController.get = function (req, res) { - var term = req.params.term ? req.params.term : 'themes'; + const term = req.params.term ? req.params.term : 'themes'; res.render('admin/appearance/' + term, {}); }; diff --git a/src/controllers/admin/blacklist.js b/src/controllers/admin/blacklist.js index 23d66d7c8a..36a446c0aa 100644 --- a/src/controllers/admin/blacklist.js +++ b/src/controllers/admin/blacklist.js @@ -1,22 +1,18 @@ 'use strict'; -var async = require('async'); -var meta = require('../../meta'); -var analytics = require('../../analytics'); +const meta = require('../../meta'); +const analytics = require('../../analytics'); -var blacklistController = module.exports; +const blacklistController = module.exports; -blacklistController.get = function (req, res, next) { - async.parallel({ - rules: async.apply(meta.blacklist.get), - analytics: async.apply(analytics.getBlacklistAnalytics), - }, function (err, data) { - if (err) { - return next(err); - } - - res.render('admin/manage/ip-blacklist', Object.assign(data, { - title: '[[pages:ip-blacklist]]', - })); +blacklistController.get = async function (req, res) { + const [rules, analyticsData] = await Promise.all([ + meta.blacklist.get(), + analytics.getBlacklistAnalytics(), + ]); + res.render('admin/manage/ip-blacklist', { + title: '[[pages:ip-blacklist]]', + rules: rules, + analytics: analyticsData, }); }; diff --git a/src/controllers/admin/cache.js b/src/controllers/admin/cache.js index 2a85e6d07b..5f1a92fcde 100644 --- a/src/controllers/admin/cache.js +++ b/src/controllers/admin/cache.js @@ -1,23 +1,23 @@ 'use strict'; -var cacheController = module.exports; +const cacheController = module.exports; -var utils = require('../../utils'); +const utils = require('../../utils'); cacheController.get = function (req, res) { - var postCache = require('../../posts/cache'); - var groupCache = require('../../groups').cache; - var objectCache = require('../../database').objectCache; - var localCache = require('../../cache'); + const postCache = require('../../posts/cache'); + const groupCache = require('../../groups').cache; + const objectCache = require('../../database').objectCache; + const localCache = require('../../cache'); - var avgPostSize = 0; - var percentFull = 0; + let avgPostSize = 0; + let percentFull = 0; if (postCache.itemCount > 0) { avgPostSize = parseInt((postCache.length / postCache.itemCount), 10); percentFull = ((postCache.length / postCache.max) * 100).toFixed(2); } - var data = { + const data = { postCache: { length: postCache.length, max: postCache.max, diff --git a/src/controllers/admin/categories.js b/src/controllers/admin/categories.js index 73c8eb74db..e06afb24e2 100644 --- a/src/controllers/admin/categories.js +++ b/src/controllers/admin/categories.js @@ -1,84 +1,66 @@ 'use strict'; -var async = require('async'); +const categories = require('../../categories'); +const analytics = require('../../analytics'); +const plugins = require('../../plugins'); +const translator = require('../../translator'); -var categories = require('../../categories'); -var analytics = require('../../analytics'); -var plugins = require('../../plugins'); -var translator = require('../../translator'); +const categoriesController = module.exports; -var categoriesController = module.exports; +categoriesController.get = async function (req, res, next) { + const [categoryData, parent, allCategories] = await Promise.all([ + categories.getCategories([req.params.category_id], req.uid), + categories.getParents([req.params.category_id]), + getAllCategories(req.uid), + ]); -categoriesController.get = function (req, res, callback) { - async.waterfall([ - function (next) { - async.parallel({ - category: async.apply(categories.getCategories, [req.params.category_id], req.uid), - parent: async.apply(categories.getParents, [req.params.category_id]), - allCategories: function (next) { - async.waterfall([ - function (next) { - categories.getAllCidsFromSet('categories:cid', next); - }, - function (cids, next) { - categories.getCategories(cids, req.uid, next); - }, - function (categoryData, next) { - categoryData = categories.getTree(categoryData); - categories.buildForSelectCategories(categoryData, next); - }, - ], next); - }, - }, next); - }, - function (data, next) { - var category = data.category[0]; + const category = categoryData[0]; + if (!category) { + return next(); + } - if (!category) { - return callback(); - } - category.parent = data.parent[0]; - data.allCategories.forEach(function (category) { - if (category) { - category.selected = parseInt(category.cid, 10) === parseInt(req.params.category_id, 10); - } - }); + category.parent = parent[0]; + allCategories.forEach(function (category) { + if (category) { + category.selected = parseInt(category.cid, 10) === parseInt(req.params.category_id, 10); + } + }); - plugins.fireHook('filter:admin.category.get', { - req: req, - res: res, - category: category, - customClasses: [], - allCategories: data.allCategories, - }, next); - }, - function (data) { - data.category.name = translator.escape(String(data.category.name)); + const data = await plugins.fireHook('filter:admin.category.get', { + req: req, + res: res, + category: category, + customClasses: [], + allCategories: allCategories, + }); + data.category.name = translator.escape(String(data.category.name)); - res.render('admin/manage/category', { - category: data.category, - allCategories: data.allCategories, - customClasses: data.customClasses, - }); - }, - ], callback); + res.render('admin/manage/category', { + category: data.category, + allCategories: data.allCategories, + customClasses: data.customClasses, + }); }; +async function getAllCategories(uid) { + const cids = await categories.getAllCidsFromSet('categories:cid'); + const categoryData = await categories.getCategories(cids, uid); + const tree = categories.getTree(categoryData); + return await categories.buildForSelectCategories(tree); +} + categoriesController.getAll = function (req, res) { // Categories list will be rendered on client side with recursion, etc. res.render('admin/manage/categories', {}); }; -categoriesController.getAnalytics = function (req, res, next) { - async.waterfall([ - function (next) { - async.parallel({ - name: async.apply(categories.getCategoryField, req.params.category_id, 'name'), - analytics: async.apply(analytics.getCategoryAnalytics, req.params.category_id), - }, next); - }, - function (data) { - res.render('admin/manage/category-analytics', data); - }, - ], next); +categoriesController.getAnalytics = async function (req, res) { + const [name, analyticsData] = await Promise.all([ + categories.getCategoryField(req.params.category_id, 'name'), + analytics.getCategoryAnalytics(req.params.category_id), + ]); + res.render('admin/manage/category-analytics', { + name: name, + analytics: analyticsData, + }); };