diff --git a/public/language/en@pirate/error.json b/public/language/en@pirate/error.json index c9751317f1..88c55e7a49 100644 --- a/public/language/en@pirate/error.json +++ b/public/language/en@pirate/error.json @@ -87,5 +87,7 @@ "registration-error": "Registration Error", "parse-error": "Something went wrong while parsing server response", "wrong-login-type-email": "Please use your email to login", - "wrong-login-type-username": "Please use your username to login" -} \ No newline at end of file + "wrong-login-type-username": "Please use your username to login", + + "invite-maximum-met": "You have invited the maximum amount of people (%1 out of %2)." +} diff --git a/public/language/en_GB/error.json b/public/language/en_GB/error.json index 4159d211d8..7842356067 100644 --- a/public/language/en_GB/error.json +++ b/public/language/en_GB/error.json @@ -113,5 +113,7 @@ "registration-error": "Registration Error", "parse-error": "Something went wrong while parsing server response", "wrong-login-type-email": "Please use your email to login", - "wrong-login-type-username": "Please use your username to login" -} \ No newline at end of file + "wrong-login-type-username": "Please use your username to login", + + "invite-maximum-met": "You have invited the maximum amount of people (%1 out of %2)." +} diff --git a/public/language/en_US/error.json b/public/language/en_US/error.json index 2789d31b2d..15c7ee3d72 100644 --- a/public/language/en_US/error.json +++ b/public/language/en_US/error.json @@ -87,5 +87,7 @@ "registration-error": "Registration Error", "parse-error": "Something went wrong while parsing server response", "wrong-login-type-email": "Please use your email to login", - "wrong-login-type-username": "Please use your username to login" -} \ No newline at end of file + "wrong-login-type-username": "Please use your username to login", + + "invite-maximum-met": "You have invited the maximum amount of people (%1 out of %2)." +} diff --git a/public/src/client/register.js b/public/src/client/register.js index ef11e4a4bd..3d0f943da9 100644 --- a/public/src/client/register.js +++ b/public/src/client/register.js @@ -28,7 +28,7 @@ define('forum/register', ['csrf', 'translator'], function(csrf, translator) { var query = utils.params(); if (query.email && query.token) { - email.val(query.email); + email.val(decodeURIComponent(query.email)); $('#token').val(query.token); } @@ -160,7 +160,7 @@ define('forum/register', ['csrf', 'translator'], function(csrf, translator) { socket.emit('user.exists', { username: username }, function(err, exists) { - if(err) { + if (err) { return app.alertError(err.message); } diff --git a/src/controllers/admin/users.js b/src/controllers/admin/users.js index 6c10694ba6..dc5420b401 100644 --- a/src/controllers/admin/users.js +++ b/src/controllers/admin/users.js @@ -1,6 +1,7 @@ "use strict"; -var user = require('../../user'), +var async = require('async'), + user = require('../../user'), meta = require('../../meta'); @@ -31,12 +32,50 @@ usersController.banned = function(req, res, next) { }; usersController.registrationQueue = function(req, res, next) { - user.getRegistrationQueue(0, -1, function(err, data) { + var invitations; + async.parallel({ + users: function(next) { + user.getRegistrationQueue(0, -1, next); + }, + invites: function(next) { + async.waterfall([ + function(next) { + user.getAllInvites(next); + }, + function(_invitations, next) { + invitations = _invitations; + async.map(invitations, function(invites, next) { + user.getUserField(invites.uid, 'username', next); + }, next); + }, + function(usernames, next) { + invitations.forEach(function(invites, index) { + invites.username = usernames[index]; + }); + async.map(invitations, function(invites, next) { + async.map(invites.invitations, user.getUsernameByEmail, next); + }, next); + }, + function(usernames, next) { + invitations.forEach(function(invites, index) { + invites.invitations = invites.invitations.map(function(email, i) { + return { + email: email, + username: usernames[index][i] === '[[global:guest]]' ? '' : usernames[index][i] + }; + }); + }); + next(null, invitations); + } + ], next); + } + }, function(err, data) { if (err) { return next(err); } - res.render('admin/manage/registration', {users: data}); - }) + res.render('admin/manage/registration', data); + }); + }; function getUsers(set, req, res, next) { diff --git a/src/controllers/users.js b/src/controllers/users.js index 0087232255..21d1490717 100644 --- a/src/controllers/users.js +++ b/src/controllers/users.js @@ -150,14 +150,26 @@ usersController.getUsersForSearch = function(req, res, next) { }; function render(req, res, data, next) { - plugins.fireHook('filter:users.build', {req: req, res: res, templateData: data}, function(err, data) { + plugins.fireHook('filter:users.build', { req: req, res: res, templateData: data }, function(err, data) { if (err) { return next(err); } - data.templateData.inviteOnly = meta.config.registrationType === 'invite-only'; - data.templateData['reputation:disabled'] = parseInt(meta.config['reputation:disabled'], 10) === 1; - res.render('users', data.templateData); + if (!req.uid) { + return next(new Error('[[error:no-privileges]]')); + } + + user.getInvitesNumber(req.uid, function(err, num) { + if (err) { + return next(err); + } + + data.templateData.invites = num; + data.templateData.maximumInvites = meta.config.maximumInvites; + data.templateData.inviteOnly = meta.config.registrationType === 'invite-only'; + data.templateData['reputation:disabled'] = parseInt(meta.config['reputation:disabled'], 10) === 1; + res.render('users', data.templateData); + }); }); } diff --git a/src/socket.io/user.js b/src/socket.io/user.js index fcaaa51d30..3f2f72c6f4 100644 --- a/src/socket.io/user.js +++ b/src/socket.io/user.js @@ -267,14 +267,39 @@ SocketUser.loadMore = function(socket, data, callback) { SocketUser.invite = function(socket, email, callback) { if (!email || !socket.uid) { - return callback(new Error('[[error:invald-data]]')); + return callback(new Error('[[error:invalid-data]]')); } if (meta.config.registrationType !== 'invite-only') { return callback(new Error('[[error:forum-not-invite-only]]')); } - user.sendInvitationEmail(socket.uid, email, callback); + var max = meta.config.maximumInvites; + + if (max) { + async.waterfall([ + function(next) { + user.getInvitesNumber(socket.uid, next); + }, + function(invites, next) { + user.isAdministrator(socket.uid, function(err, admin) { + next(err, invites, admin); + }); + }, + function(invites, admin, next) { + console.log(admin, invites, max); + if (!admin && invites > max) { + return next(new Error('[[error:invite-maximum-met, ' + invites + ', ' + max + ']]')); + } + next(); + }, + function(next) { + user.sendInvitationEmail(socket.uid, email, next); + } + ], callback); + } else { + user.sendInvitationEmail(socket.uid, email, callback); + } }; diff --git a/src/user/invite.js b/src/user/invite.js index 0fb8ae979b..ef3146b5f4 100644 --- a/src/user/invite.js +++ b/src/user/invite.js @@ -16,13 +16,68 @@ var async = require('async'), module.exports = function(User) { + User.getInvites = function(uid, callback) { + db.getSetMembers('invitation:uid:' + uid, callback); + }; + + User.getInvitesNumber = function(uid, callback) { + db.setCount('invitation:uid:' + uid, callback); + }; + + User.getInvitingUsers = function(callback) { + db.getSetMembers('invitation:uids', callback); + }; + + User.getAllInvites = function(callback) { + var uids; + async.waterfall([ + User.getInvitingUsers, + function(_uids, next) { + uids = _uids; + async.map(uids, User.getInvites, next); + }, + function(invitations, next) { + invitations = invitations.map(function(invites, index) { + return { + uid: uids[index], + invitations: invites + }; + }); + next(null, invitations); + } + ], callback); + }; + User.sendInvitationEmail = function(uid, email, callback) { callback = callback || function() {}; + var token = utils.generateUUID(); - var registerLink = nconf.get('url') + '/register?token=' + token + '&email=' + email; + var registerLink = nconf.get('url') + '/register?token=' + token + '&email=' + encodeURIComponent(email); var oneDay = 86400000; + async.waterfall([ + function(next) { + User.getUidByEmail(email, next); + }, + function(exists, next) { + if (exists) { + return next(new Error('[[error:email-taken]]')); + } + next(); + }, + function(next) { + async.parallel([ + function(next) { + db.setAdd('invitation:uid:' + uid, email, next); + }, + function(next) { + db.setAdd('invitation:uids', uid, next); + } + ], function(err) { + next(err); + }); + }, function(next) { db.set('invitation:email:' + email, token, next); }, @@ -73,4 +128,4 @@ module.exports = function(User) { db.delete('invitation:email:' + email, callback); }; -}; \ No newline at end of file +}; diff --git a/src/views/admin/manage/registration.tpl b/src/views/admin/manage/registration.tpl index faa31d7535..278ba279b1 100644 --- a/src/views/admin/manage/registration.tpl +++ b/src/views/admin/manage/registration.tpl @@ -1,4 +1,14 @@ -
+
+
+ Queue +
+ +

+ There are no users in the registration queue.
+ To enable this feature, go to Settings -> User -> Authentication and set + Registration Type to "Admin Approval". +

+ @@ -7,11 +17,6 @@ - -

- There are no users in the registration queue. To enable this feature go to Settings -> User -> Authentication and set Registration Type to "Admin Approval". -

-
NameTime
@@ -50,4 +55,31 @@
-
\ No newline at end of file +
+ +
+
+ Invitations +
+

+ Below is a complete list of invitations sent. Use ctrl-f to search through the list by email or username. +

+ The username will be displayed to the right of the emails for users who have redeemed their invitations. +

+ + + + + + + + + + + + + + + +
Inviter UsernameInvitee EmailInvitee Username (if registered)
{invites.username}{invites.invitations.email}{invites.invitations.username}
+
diff --git a/src/views/admin/settings/user.tpl b/src/views/admin/settings/user.tpl index 0b27b24175..6f6b83f78d 100644 --- a/src/views/admin/settings/user.tpl +++ b/src/views/admin/settings/user.tpl @@ -36,6 +36,15 @@ + +
+ + +

+ 0 for no restriction. Admins get infinite invitations
+ Only applicable for "Invite Only" +

+
@@ -267,4 +276,4 @@ - \ No newline at end of file +