From 32ca95319ee4e3d29814368d3f399b631c2eacf1 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 20 Jan 2016 16:12:57 +0200 Subject: [PATCH] closes #3436 --- public/less/admin/manage/users.less | 4 +- public/src/admin/manage/users.js | 66 ++--------------- src/controllers/admin/users.js | 108 ++++++++++++++++++++++------ src/routes/admin.js | 5 +- src/socket.io/admin/user.js | 7 +- src/upgrade.js | 36 +++++++++- src/user/create.js | 3 +- src/user/delete.js | 3 +- src/user/email.js | 24 ++++--- src/user/profile.js | 3 + src/views/admin/manage/users.tpl | 24 ++++--- src/views/admin/partials/menu.tpl | 4 +- 12 files changed, 175 insertions(+), 112 deletions(-) diff --git a/public/less/admin/manage/users.less b/public/less/admin/manage/users.less index 91f4e6efad..bed33b77a1 100644 --- a/public/less/admin/manage/users.less +++ b/public/less/admin/manage/users.less @@ -14,8 +14,8 @@ text-overflow: ellipsis; overflow: hidden; height: auto; - max-width: 125px; - min-width: 125px; + max-width: 145px; + min-width: 145px; img, .user-icon { .user-icon-style(80px, 4rem); diff --git a/public/src/admin/manage/users.js b/public/src/admin/manage/users.js index 0baa3e3b96..57be3f327c 100644 --- a/public/src/admin/manage/users.js +++ b/public/src/admin/manage/users.js @@ -1,5 +1,7 @@ "use strict"; -/* global config, socket, define, templates, bootbox, app, ajaxify */ + +/* global socket, define, templates, bootbox, app, ajaxify */ + define('admin/manage/users', ['admin/modules/selectable'], function(selectable) { var Users = {}; @@ -196,7 +198,7 @@ define('admin/manage/users', ['admin/modules/selectable'], function(selectable) passwordAgain = $('#create-user-password-again').val(); - if(password !== passwordAgain) { + if (password !== passwordAgain) { return errorEl.html('Error

Passwords must match!

').removeClass('hide'); } @@ -220,21 +222,9 @@ define('admin/manage/users', ['admin/modules/selectable'], function(selectable) }); } - var timeoutId = 0, - loadingMoreUsers = false; + var timeoutId = 0; - var url = window.location.href, - parts = url.split('/'), - active = parts[parts.length - 1]; - - $('.nav-pills li').removeClass('active'); - $('.nav-pills li a').each(function() { - var $this = $(this); - if ($this.attr('href').match(active)) { - $this.parent().addClass('active'); - return false; - } - }); + $('.nav-pills li').removeClass('active').find('a[href="' + window.location.pathname + '"]').parent().addClass('active'); $('#search-user-name, #search-user-email, #search-user-ip').on('keyup', function() { if (timeoutId !== 0) { @@ -278,50 +268,6 @@ define('admin/manage/users', ['admin/modules/selectable'], function(selectable) handleUserCreate(); - $('#load-more-users-btn').on('click', loadMoreUsers); - - $(window).off('scroll').on('scroll', function() { - var bottom = ($(document).height() - $(window).height()) * 0.9; - - if ($(window).scrollTop() > bottom && !loadingMoreUsers) { - loadMoreUsers(); - } - }); - - - function loadMoreUsers() { - if (active === 'search') { - return; - } - var set = 'users:joindate'; - if (active === 'sort-posts') { - set = 'users:postcount'; - } else if (active === 'sort-reputation') { - set = 'users:reputation'; - } else if (active === 'banned') { - set = 'users:banned'; - } - - loadingMoreUsers = true; - socket.emit('user.loadMore', { - set: set, - after: $('#users-container').children().length - }, function(err, data) { - if (data && data.users.length) { - onUsersLoaded(data.users); - } - loadingMoreUsers = false; - }); - } - - function onUsersLoaded(users) { - templates.parse('admin/manage/users', 'users', {users: users, requireEmailConfirmation: config.requireEmailConfirmation}, function(html) { - $('#users-container').append($(html)); - selectable.enable('#users-container', '.user-selectable'); - }); - } - - }; return Users; diff --git a/src/controllers/admin/users.js b/src/controllers/admin/users.js index dc5420b401..c68fbb8d09 100644 --- a/src/controllers/admin/users.js +++ b/src/controllers/admin/users.js @@ -1,8 +1,10 @@ "use strict"; -var async = require('async'), - user = require('../../user'), - meta = require('../../meta'); +var async = require('async'); +var user = require('../../user'); +var meta = require('../../meta'); +var db = require('../../database'); +var pagination = require('../../pagination'); var usersController = {}; @@ -10,25 +12,68 @@ var usersController = {}; usersController.search = function(req, res, next) { res.render('admin/manage/users', { search_display: '', - loadmore_display: 'hide', users: [] }); }; -usersController.sortByPosts = function(req, res, next) { - getUsers('users:postcount', req, res, next); +usersController.sortByJoinDate = function(req, res, next) { + getUsers('users:joindate', 'latest', req, res, next); }; -usersController.sortByReputation = function(req, res, next) { - getUsers('users:reputation', req, res, next); +usersController.notValidated = function(req, res, next) { + getUsers('users:notvalidated', 'notvalidated', req, res, next); }; -usersController.sortByJoinDate = function(req, res, next) { - getUsers('users:joindate', req, res, next); +usersController.noPosts = function(req, res, next) { + getUsersByScore('users:postcount', 'noposts', 0, 0, req, res, next); +}; + +usersController.inactive = function(req, res, next) { + var timeRange = 1000 * 60 * 60 * 24 * 30 * (parseInt(req.query.months, 10) || 3); + var cutoff = Date.now() - timeRange; + getUsersByScore('users:online', 'inactive', 0, cutoff, req, res, next); }; +function getUsersByScore(set, section, min, max, req, res, callback) { + var page = parseInt(req.query.page, 10) || 1; + var resultsPerPage = 25; + var start = Math.max(0, page - 1) * resultsPerPage; + var count = 0; + + async.waterfall([ + function (next) { + async.parallel({ + count: function (next) { + db.sortedSetCount(set, min, max, next); + }, + uids: function (next) { + db.getSortedSetRevRangeByScore(set, start, resultsPerPage, max, min, next); + } + }, next); + }, + function (results, next) { + count = results.count; + user.getUsers(results.uids, req.uid, next); + } + ], function(err, users) { + if (err) { + return callback(err); + } + users = users.filter(function(user) { + return user && parseInt(user.uid, 10); + }); + var data = { + users: users, + page: page, + pageCount: Math.ceil(count / resultsPerPage) + }; + data[section] = true; + render(req, res, data); + }); +} + usersController.banned = function(req, res, next) { - getUsers('users:banned', req, res, next); + getUsers('users:banned', 'banned', req, res, next); }; usersController.registrationQueue = function(req, res, next) { @@ -75,29 +120,46 @@ usersController.registrationQueue = function(req, res, next) { } res.render('admin/manage/registration', data); }); - }; -function getUsers(set, req, res, next) { - user.getUsersFromSet(set, req.uid, 0, 49, function(err, users) { +function getUsers(set, section, req, res, next) { + var page = parseInt(req.query.page, 10) || 1; + var resultsPerPage = 25; + var start = Math.max(0, page - 1) * resultsPerPage; + var stop = start + resultsPerPage - 1; + + async.parallel({ + count: function(next) { + db.sortedSetCard(set, next); + }, + users: function(next) { + user.getUsersFromSet(set, req.uid, start, stop, next); + } + }, function(err, results) { if (err) { return next(err); } - users = users.filter(function(user) { + results.users = results.users.filter(function(user) { return user && parseInt(user.uid, 10); }); - - res.render('admin/manage/users', { - search_display: 'hidden', - loadmore_display: 'block', - users: users, - yourid: req.uid, - requireEmailConfirmation: parseInt(meta.config.requireEmailConfirmation, 10) === 1 - }); + var data = { + users: results.users, + page: page, + pageCount: Math.ceil(results.count / resultsPerPage) + }; + data[section] = true; + render(req, res, data); }); } +function render(req, res, data) { + data.search_display = 'hidden'; + data.pagination = pagination.create(data.page, data.pageCount, req.query); + data.requireEmailConfirmation = parseInt(meta.config.requireEmailConfirmation, 10) === 1; + res.render('admin/manage/users', data); +} + usersController.getCSV = function(req, res, next) { user.getUsersCSV(function(err, data) { if (err) { diff --git a/src/routes/admin.js b/src/routes/admin.js index 06edc0b805..e5121ac0f9 100644 --- a/src/routes/admin.js +++ b/src/routes/admin.js @@ -58,8 +58,9 @@ function addRoutes(router, middleware, controllers) { router.get('/manage/users', middlewares, controllers.admin.users.sortByJoinDate); router.get('/manage/users/search', middlewares, controllers.admin.users.search); router.get('/manage/users/latest', middlewares, controllers.admin.users.sortByJoinDate); - router.get('/manage/users/sort-posts', middlewares, controllers.admin.users.sortByPosts); - router.get('/manage/users/sort-reputation', middlewares, controllers.admin.users.sortByReputation); + router.get('/manage/users/not-validated', middlewares, controllers.admin.users.notValidated); + router.get('/manage/users/no-posts', middlewares, controllers.admin.users.noPosts); + router.get('/manage/users/inactive', middlewares, controllers.admin.users.inactive); router.get('/manage/users/banned', middlewares, controllers.admin.users.banned); router.get('/manage/registration', middlewares, controllers.admin.users.registrationQueue); diff --git a/src/socket.io/admin/user.js b/src/socket.io/admin/user.js index e1c0489ea8..d8dd551cf0 100644 --- a/src/socket.io/admin/user.js +++ b/src/socket.io/admin/user.js @@ -120,7 +120,12 @@ User.validateEmail = function(socket, uids, callback) { async.each(uids, function(uid, next) { user.setUserField(uid, 'email:confirmed', 1, next); - }, callback); + }, function(err) { + if (err) { + return callback(err); + } + db.sortedSetRemove('users:notvalidated', uids, callback); + }); }; User.sendValidationEmail = function(socket, uids, callback) { diff --git a/src/upgrade.js b/src/upgrade.js index b9dbb45a9a..9cc1d6c2d1 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -10,7 +10,7 @@ var db = require('./database'), schemaDate, thisSchemaDate, // IMPORTANT: REMEMBER TO UPDATE VALUE OF latestSchema - latestSchema = Date.UTC(2016, 0, 14); + latestSchema = Date.UTC(2016, 0, 20); Upgrade.check = function(callback) { db.get('schemaDate', function(err, value) { @@ -335,6 +335,40 @@ Upgrade.upgrade = function(callback) { winston.info('[2016/01/14] Creating user best post sorted sets skipped!'); next(); } + }, + function(next) { + thisSchemaDate = Date.UTC(2016, 0, 20); + + if (schemaDate < thisSchemaDate) { + updatesMade = true; + winston.info('[2016/01/20] Creating users:notvalidated'); + + var batch = require('./batch'); + var now = Date.now(); + batch.processSortedSet('users:joindate', function(ids, next) { + async.eachSeries(ids, function(id, next) { + db.getObjectFields('user:' + id, ['uid', 'email:confirmed'], function(err, userData) { + if (err) { + return next(err); + } + if (!userData || !parseInt(userData.uid, 10) || parseInt(userData['email:confirmed'], 10) === 1) { + return next(); + } + winston.info('processing uid: ' + userData.uid + ' email:confirmed: ' + userData['email:confirmed']); + db.sortedSetAdd('users:notvalidated', now, userData.uid, next); + }); + }, next); + }, {}, function(err) { + if (err) { + return next(err); + } + winston.info('[2016/01/20] Creating users:notvalidated done!'); + Upgrade.update(thisSchemaDate, next); + }); + } else { + winston.info('[2016/01/20] Creating users:notvalidated skipped!'); + next(); + } } // Add new schema updates here // IMPORTANT: REMEMBER TO UPDATE VALUE OF latestSchema IN LINE 24!!! diff --git a/src/user/create.js b/src/user/create.js index c455b61e82..5720f34745 100644 --- a/src/user/create.js +++ b/src/user/create.js @@ -31,6 +31,7 @@ module.exports = function(User) { 'userslug': data.userslug, 'email': data.email, 'joindate': timestamp, + 'lastonline': timestamp, 'picture': '', 'fullname': data.fullname, 'location': '', @@ -89,7 +90,7 @@ module.exports = function(User) { db.sortedSetAdd('userslug:uid', userData.uid, userData.userslug, next); }, function(next) { - db.sortedSetAdd('users:joindate', timestamp, userData.uid, next); + db.sortedSetAdd(['users:joindate', 'users:online', 'users:notvalidated'], timestamp, userData.uid, next); }, function(next) { db.sortedSetsAdd(['users:postcount', 'users:reputation'], 0, userData.uid, next); diff --git a/src/user/delete.js b/src/user/delete.js index 3b60711e72..921bb62d32 100644 --- a/src/user/delete.js +++ b/src/user/delete.js @@ -91,7 +91,8 @@ module.exports = function(User) { 'users:postcount', 'users:reputation', 'users:banned', - 'users:online' + 'users:online', + 'users:notvalidated' ], uid, next); }, function(next) { diff --git a/src/user/email.js b/src/user/email.js index 4f2361c0f7..4b98cc72aa 100644 --- a/src/user/email.js +++ b/src/user/email.js @@ -1,17 +1,16 @@ 'use strict'; -var async = require('async'), - nconf = require('nconf'), - winston = require('winston'), +var async = require('async'); +var nconf = require('nconf'); - user = require('../user'), - utils = require('../../public/src/utils'), - translator = require('../../public/src/modules/translator'), - plugins = require('../plugins'), - db = require('../database'), - meta = require('../meta'), - emailer = require('../emailer'); +var user = require('../user'); +var utils = require('../../public/src/utils'); +var translator = require('../../public/src/modules/translator'); +var plugins = require('../plugins'); +var db = require('../database'); +var meta = require('../meta'); +var emailer = require('../emailer'); (function(UserEmail) { @@ -97,7 +96,10 @@ var async = require('async'), if (confirmObj && confirmObj.uid && confirmObj.email) { async.series([ async.apply(user.setUserField, confirmObj.uid, 'email:confirmed', 1), - async.apply(db.delete, 'confirm:' + code) + async.apply(db.delete, 'confirm:' + code), + function(next) { + db.sortedSetRemove('users:notvalidated', confirmObj.uid, next); + } ], function(err) { callback(err ? new Error('[[error:email-confirm-failed]]') : null); }); diff --git a/src/user/profile.js b/src/user/profile.js index 1c0a1564f1..e157a99671 100644 --- a/src/user/profile.js +++ b/src/user/profile.js @@ -170,6 +170,9 @@ module.exports = function(User) { User.email.sendValidationEmail(uid, newEmail); } User.setUserField(uid, 'email:confirmed', 0, next); + }, + function (next) { + db.sortedSetAdd('users:notvalidated', Date.now(), uid, next); } ], callback); }); diff --git a/src/views/admin/manage/users.tpl b/src/views/admin/manage/users.tpl index 708f9c13fa..cfdc7bb72c 100644 --- a/src/views/admin/manage/users.tpl +++ b/src/views/admin/manage/users.tpl @@ -5,8 +5,9 @@
+ + 3 months + 6 months + 12 months + + + + diff --git a/src/views/admin/partials/menu.tpl b/src/views/admin/partials/menu.tpl index c69d7a3a5f..5e0cc85370 100644 --- a/src/views/admin/partials/menu.tpl +++ b/src/views/admin/partials/menu.tpl @@ -15,7 +15,7 @@