From 4420e7a9fcf4518e1d7d0357126ec67c46e462a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Tue, 2 Jun 2015 16:20:10 -0400 Subject: [PATCH] closes #3188, closes #3170 --- public/src/client/groups/list.js | 36 ++++++++++++++++++++++--- src/controllers/admin.js | 6 ++++- src/controllers/groups.js | 29 +++++++++++++++----- src/groups.js | 25 ++++++++++++----- src/groups/create.js | 6 +++++ src/groups/delete.js | 3 +++ src/groups/update.js | 28 ++++++++++++++++++- src/socket.io/groups.js | 28 ++++++++++++++++--- src/upgrade.js | 46 +++++++++++++++++++++++++++++++- 9 files changed, 184 insertions(+), 23 deletions(-) diff --git a/public/src/client/groups/list.js b/public/src/client/groups/list.js index 42bdbf88ae..80d82aaa61 100644 --- a/public/src/client/groups/list.js +++ b/public/src/client/groups/list.js @@ -1,7 +1,7 @@ "use strict"; /* globals app, define, ajaxify, socket, bootbox, utils, templates */ -define('forum/groups/list', function() { +define('forum/groups/list', ['forum/infinitescroll'], function(infinitescroll) { var Groups = {}; Groups.init = function() { @@ -13,6 +13,8 @@ define('forum/groups/list', function() { ajaxify.go('groups/' + groupSlug); }); + infinitescroll.init(Groups.loadMoreGroups); + // Group creation $('button[data-action="new"]').on('click', function() { bootbox.prompt('Group Name:', function(name) { @@ -29,13 +31,41 @@ define('forum/groups/list', function() { } }); }); + var params = utils.params(); + $('#search-sort').val(params.sort || 'alpha'); // Group searching $('#search-text').on('keyup', Groups.search); $('#search-button').on('click', Groups.search); - $('#search-sort').on('change', Groups.search); + $('#search-sort').on('change', function() { + ajaxify.go('groups?sort=' + $('#search-sort').val()); + }); }; + Groups.loadMoreGroups = function(direction) { + if (direction < 0) { + return; + } + + infinitescroll.loadMore('groups.loadMore', { + sort: $('#search-sort').val(), + after: $('[component="groups/container"]').attr('data-nextstart') + }, function(data, done) { + console.log(data); + if (data && data.groups.length) { + templates.parse('partials/groups/list', { + groups: data.groups + }, function(html) { + $('#groups-list').append(html); + done(); + }); + } else { + done(); + } + $('[component="groups/container"]').attr('data-nextstart', data.nextStart); + }); + } + Groups.search = function() { var groupsEl = $('#groups-list'), queryEl = $('#search-text'), @@ -44,8 +74,6 @@ define('forum/groups/list', function() { socket.emit('groups.search', { query: queryEl.val(), options: { - expand: true, - truncateUserList: true, sort: sortEl.val() } }, function(err, groups) { diff --git a/src/controllers/admin.js b/src/controllers/admin.js index 59e6680e8b..ba45d8b7d8 100644 --- a/src/controllers/admin.js +++ b/src/controllers/admin.js @@ -397,11 +397,15 @@ adminController.extend.rewards = function(req, res, next) { }; adminController.groups.get = function(req, res, next) { - groups.list(req.uid, 0, -1, function(err, groups) { + groups.getGroupsFromSet('groups:createtime', req.uid, 0, -1, function(err, groups) { if (err) { return next(err); } + groups = groups.filter(function(group) { + return group && group.name.indexOf(':privileges:') === -1 && group.name !== 'registered-users'; + }); + res.render('admin/manage/groups', { groups: groups, yourid: req.user.uid diff --git a/src/controllers/groups.js b/src/controllers/groups.js index 64858ac36b..06ba76ef09 100644 --- a/src/controllers/groups.js +++ b/src/controllers/groups.js @@ -2,25 +2,42 @@ var async = require('async'), nconf = require('nconf'), + db = require('../database'), meta = require('../meta'), groups = require('../groups'), user = require('../user'), helpers = require('./helpers'), + pagination = require('../pagination'), groupsController = {}; groupsController.list = function(req, res, next) { - groups.list(req.uid, 0, -1, function(err, groups) { + var sort = req.query.sort || 'alpha'; + + groupsController.getGroupsFromSet(req.uid, sort, 0, 8, function(err, data) { if (err) { return next(err); } + res.render('groups/list', data); + }); +}; - groups = groups.filter(function(group) { - return group && !group.hidden && !group.system; - }); +groupsController.getGroupsFromSet = function(uid, sort, start, stop, callback) { + var set = 'groups:visible:name'; + if (sort === 'count') { + set = 'groups:visible:memberCount'; + } else if (sort === 'date') { + set = 'groups:visible:createtime'; + } + + groups.getGroupsFromSet(set, uid, start, stop, function(err, groups) { + if (err) { + return callback(err); + } - res.render('groups/list', { + callback(null, { groups: groups, - allowGroupCreation: parseInt(meta.config.allowGroupCreation, 10) === 1 + allowGroupCreation: parseInt(meta.config.allowGroupCreation, 10) === 1, + nextStart: stop + 1 }); }); }; diff --git a/src/groups.js b/src/groups.js index 3505295d09..964c79e549 100644 --- a/src/groups.js +++ b/src/groups.js @@ -57,15 +57,28 @@ var async = require('async'), return ephemeralGroups; }; - Groups.list = function(uid, start, stop, callback) { - db.getSortedSetRevRange('groups:createtime', start, stop, function (err, groupNames) { + Groups.getGroupsFromSet = function(set, uid, start, stop, callback) { + var method; + var args; + if (set === 'groups:visible:name') { + method = db.getSortedSetRangeByLex; + args = [set, '-', '+', start, stop - start + 1, done]; + } else { + method = db.getSortedSetRevRange; + args = [set, start, stop, done]; + } + method.apply(null, args); + + function done(err, groupNames) { if (err) { return callback(err); } - groupNames = groupNames.filter(function(groupName) { - return groupName && groupName.indexOf(':privileges:') === -1 && groupName !== 'registered-users' && groupName !== 'guests'; - }); + if (set === 'groups:visible:name') { + groupNames = groupNames.map(function(name) { + return name.split(':')[1]; + }); + } async.parallel({ groups: function(next) { @@ -89,7 +102,7 @@ var async = require('async'), callback(null, data.groups); }); - }); + } }; Groups.getGroups = function(start, stop, callback) { diff --git a/src/groups/create.js b/src/groups/create.js index d308d1fcb5..5ab45f4f1f 100644 --- a/src/groups/create.js +++ b/src/groups/create.js @@ -49,6 +49,12 @@ module.exports = function(Groups) { groupData.ownerUid = data.ownerUid; } + if (!data.hidden && !system) { + tasks.push(async.apply(db.sortedSetAdd, 'groups:visible:createtime', timestamp, data.name)); + tasks.push(async.apply(db.sortedSetAdd, 'groups:visible:memberCount', memberCount, data.name)); + tasks.push(async.apply(db.sortedSetAdd, 'groups:visible:name', 0, data.name.toLowerCase() + ':' + data.name)); + } + if (!data.hidden) { tasks.push(async.apply(db.setObjectField, 'groupslug:groupname', slug, data.name)); } diff --git a/src/groups/delete.js b/src/groups/delete.js index a8cee2e95d..8e665249a8 100644 --- a/src/groups/delete.js +++ b/src/groups/delete.js @@ -21,6 +21,9 @@ module.exports = function(Groups) { async.parallel([ async.apply(db.delete, 'group:' + groupName), async.apply(db.sortedSetRemove, 'groups:createtime', groupName), + async.apply(db.sortedSetRemove, 'groups:visible:createtime', groupName), + async.apply(db.sortedSetRemove, 'groups:visible:memberCount', groupName), + async.apply(db.sortedSetRemove, 'groups:visible:name', groupName.toLowerCase() + ':' + groupName), async.apply(db.delete, 'group:' + groupName + ':members'), async.apply(db.delete, 'group:' + groupName + ':pending'), async.apply(db.delete, 'group:' + groupName + ':invited'), diff --git a/src/groups/update.js b/src/groups/update.js index b533f63c4e..4c416f156b 100644 --- a/src/groups/update.js +++ b/src/groups/update.js @@ -9,7 +9,7 @@ var async = require('async'), plugins = require('../plugins'), utils = require('../../public/src/utils'), - db = require('./../database'), + db = require('../database'), uploadsController = require('../controllers/uploads'); @@ -46,6 +46,7 @@ module.exports = function(Groups) { async.series([ async.apply(updatePrivacy, groupName, values.private), + async.apply(updateVisibility, groupName, values.hidden), async.apply(db.setObject, 'group:' + groupName, payload), async.apply(renameGroup, groupName, values.name) ], function(err) { @@ -62,6 +63,27 @@ module.exports = function(Groups) { }); }; + function updateVisibility(groupName, hidden, callback) { + if (hidden) { + async.parallel([ + async.apply(db.sortedSetRemove, 'groups:visible:createtime', groupName), + async.apply(db.sortedSetRemove, 'groups:visible:memberCount', groupName), + async.apply(db.sortedSetRemove, 'groups:visible:name', groupName.toLowerCase() + ':' + groupName) + ], callback); + } else { + db.getObjectFields('group:' + groupName, ['createtime', 'memberCount'], function(err, groupData) { + if (err) { + return callback(err); + } + async.parallel([ + async.apply(db.sortedSetAdd, 'groups:visible:createtime', groupData.createtime, groupName), + async.apply(db.sortedSetAdd, 'groups:visible:memberCount', groupData.memberCount, groupName), + async.apply(db.sortedSetAdd, 'groups:visible:name', 0, groupName.toLowerCase() + ':' + groupName) + ], callback); + }); + } + } + Groups.hide = function(groupName, callback) { callback = callback || function() {}; db.setObjectField('group:' + groupName, 'hidden', 1, callback); @@ -195,7 +217,11 @@ module.exports = function(Groups) { async.apply(db.rename, 'group:' + oldName + ':owners', 'group:' + newName + ':owners'), async.apply(db.rename, 'group:' + oldName + ':pending', 'group:' + newName + ':pending'), async.apply(db.rename, 'group:' + oldName + ':invited', 'group:' + newName + ':invited'), + async.apply(renameGroupMember, 'groups:createtime', oldName, newName), + async.apply(renameGroupMember, 'groups:visible:createtime', oldName, newName), + async.apply(renameGroupMember, 'groups:visible:memberCount', oldName, newName), + async.apply(renameGroupMember, 'groups:visible:name', oldName.toLowerCase() + ':' + oldName, newName.toLowerCase() + ':' + newName), function(next) { plugins.fireHook('action:group.rename', { old: oldName, diff --git a/src/socket.io/groups.js b/src/socket.io/groups.js index 44f460d4ed..ed058df7cb 100644 --- a/src/socket.io/groups.js +++ b/src/socket.io/groups.js @@ -1,10 +1,11 @@ "use strict"; -var groups = require('../groups'), +var async = require('async'), + + groups = require('../groups'), meta = require('../meta'), user = require('../user'), - - async = require('async'), + groupsController = require('../controllers/groups'), SocketGroups = {}; @@ -181,7 +182,26 @@ SocketGroups.search = function(socket, data, callback) { return callback(null, []); } - groups.search(data.query || '', data.options || {}, callback); + if (!data.query) { + var groupsPerPage = 9; + groupsController.getGroupsFromSet(socket.uid, data.options.sort, 0, groupsPerPage - 1, function(err, data) { + callback(err, !err ? data.groups : null); + }); + return; + } + + groups.search(data.query, data.options || {}, callback); +}; + +SocketGroups.loadMore = function(socket, data, callback) { + if (!data || !data.sort || !data.after) { + return callback(); + } + + var groupsPerPage = 9; + var start = parseInt(data.after); + var stop = start + groupsPerPage - 1; + groupsController.getGroupsFromSet(socket.uid, data.sort, start, stop, callback); }; SocketGroups.searchMembers = function(socket, data, callback) { diff --git a/src/upgrade.js b/src/upgrade.js index 2abdfe426c..dbeada80fa 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -21,7 +21,7 @@ var db = require('./database'), schemaDate, thisSchemaDate, // IMPORTANT: REMEMBER TO UPDATE VALUE OF latestSchema - latestSchema = Date.UTC(2015, 4, 20); + latestSchema = Date.UTC(2015, 5, 2); Upgrade.check = function(callback) { db.get('schemaDate', function(err, value) { @@ -377,8 +377,52 @@ Upgrade.upgrade = function(callback) { winston.info('[2015/05/20] Adding username:sorted and email:sorted skipped'); next(); } + }, + function(next) { + thisSchemaDate = Date.UTC(2015, 5, 2); + if (schemaDate < thisSchemaDate) { + updatesMade = true; + winston.info('[2015/06/02] Creating group sorted sets'); + + db.getSortedSetRange('groups:createtime', 0, -1, function(err, groupNames) { + if (err) { + return callback(err); + } + + async.eachLimit(groupNames, 500, function(groupName, next) { + if (!groupName) { + return next(); + } + db.getObjectFields('group:' + groupName, ['hidden', 'system', 'createtime', 'memberCount'], function(err, groupData) { + if (err) { + return next(err); + } + + if (parseInt(groupData.hidden, 10) === 1 || parseInt(groupData.system, 10) === 1) { + return next(); + } + async.parallel([ + async.apply(db.sortedSetAdd, 'groups:visible:createtime', groupData.createtime, groupName), + async.apply(db.sortedSetAdd, 'groups:visible:memberCount', groupData.memberCount, groupName), + async.apply(db.sortedSetAdd, 'groups:visible:name', 0, groupName.toLowerCase() + ':' + groupName) + ], next); + }); + }, function(err) { + if (err) { + return next(err); + } + + winston.info('[2015/06/02] Creating group sorted sets done'); + Upgrade.update(thisSchemaDate, next); + }); + }); + } else { + winston.info('[2015/06/02] Creating group sorted sets skipped'); + next(); + } } + // Add new schema updates here // IMPORTANT: REMEMBER TO UPDATE VALUE OF latestSchema IN LINE 24!!! ], function(err) {