diff --git a/install/package.json b/install/package.json
index 0b92fa2ea9..f3d3b431cc 100644
--- a/install/package.json
+++ b/install/package.json
@@ -75,9 +75,9 @@
"nodebb-plugin-spam-be-gone": "0.5.4",
"nodebb-rewards-essentials": "0.0.11",
"nodebb-theme-lavender": "5.0.5",
- "nodebb-theme-persona": "9.0.17",
- "nodebb-theme-slick": "1.2.5",
- "nodebb-theme-vanilla": "10.0.15",
+ "nodebb-theme-persona": "9.0.18",
+ "nodebb-theme-slick": "1.2.6",
+ "nodebb-theme-vanilla": "10.0.16",
"nodebb-widget-essentials": "4.0.7",
"nodemailer": "^4.6.5",
"passport": "^0.4.0",
diff --git a/public/language/en-GB/groups.json b/public/language/en-GB/groups.json
index 08c8d4d1f5..c18335bb5e 100644
--- a/public/language/en-GB/groups.json
+++ b/public/language/en-GB/groups.json
@@ -33,7 +33,7 @@
"details.grant": "Grant/Rescind Ownership",
"details.kick": "Kick",
"details.kick_confirm": "Are you sure you want to remove this member from the group?",
-
+ "details.add-member": "Add Member",
"details.owner_options": "Group Administration",
"details.group_name": "Group Name",
"details.member_count": "Member Count",
diff --git a/public/src/admin/manage/group.js b/public/src/admin/manage/group.js
index 95e1751ff6..92ef056630 100644
--- a/public/src/admin/manage/group.js
+++ b/public/src/admin/manage/group.js
@@ -5,20 +5,14 @@ define('admin/manage/group', [
'forum/groups/memberlist',
'iconSelect',
'admin/modules/colorpicker',
- 'translator',
- 'benchpress',
-], function (memberList, iconSelect, colorpicker, translator, Benchpress) {
+], function (memberList, iconSelect, colorpicker) {
var Groups = {};
Groups.init = function () {
- var groupDetailsSearch = $('#group-details-search');
- var groupDetailsSearchResults = $('#group-details-search-results');
var groupIcon = $('#group-icon');
var changeGroupUserTitle = $('#change-group-user-title');
var changeGroupLabelColor = $('#change-group-label-color');
var groupLabelPreview = $('#group-label-preview');
- var searchDelay;
-
var groupName = ajaxify.data.group.name;
@@ -36,87 +30,6 @@ define('admin/manage/group', [
groupLabelPreview.css('background', changeGroupLabelColor.val() || '#000000');
});
- groupDetailsSearch.on('keyup', function () {
- if (searchDelay) {
- clearTimeout(searchDelay);
- }
-
- searchDelay = setTimeout(function () {
- var searchText = groupDetailsSearch.val();
- var foundUser;
-
- socket.emit('admin.user.search', {
- query: searchText,
- }, function (err, results) {
- if (!err && results && results.users.length > 0) {
- var numResults = results.users.length;
- var x;
- if (numResults > 20) {
- numResults = 20;
- }
-
- groupDetailsSearchResults.empty();
-
- for (x = 0; x < numResults; x += 1) {
- foundUser = $('
');
- foundUser
- .attr({
- title: results.users[x].username,
- 'data-uid': results.users[x].uid,
- 'data-username': results.users[x].username,
- 'data-userslug': results.users[x].userslug,
- 'data-picture': results.users[x].picture,
- 'data-usericon-bgColor': results.users[x]['icon:bgColor'],
- 'data-usericon-text': results.users[x]['icon:text'],
- })
- .append(results.users[x].picture ?
- $('
').addClass('avatar avatar-sm').attr('src', results.users[x].picture) :
- $('').addClass('avatar avatar-sm').css('background-color', results.users[x]['icon:bgColor']).html(results.users[x]['icon:text']))
- .append($('').html(results.users[x].username));
-
- groupDetailsSearchResults.append(foundUser);
- }
- } else {
- groupDetailsSearchResults.translateHtml('[[admin/manage/groups:edit.no-users-found]]');
- }
- });
- }, 200);
- });
-
- groupDetailsSearchResults.on('click', 'li[data-uid]', function () {
- var userLabel = $(this);
- var uid = parseInt(userLabel.attr('data-uid'), 10);
-
- socket.emit('admin.groups.join', {
- groupName: groupName,
- uid: uid,
- }, function (err) {
- if (err) {
- return app.alertError(err.message);
- }
-
- var member = {
- uid: userLabel.attr('data-uid'),
- username: userLabel.attr('data-username'),
- userslug: userLabel.attr('data-userslug'),
- picture: userLabel.attr('data-picture'),
- 'icon:bgColor': userLabel.attr('data-usericon-bgColor'),
- 'icon:text': userLabel.attr('data-usericon-text'),
- };
-
- Benchpress.parse('admin/partials/groups/memberlist', 'group.members', {
- group: {
- isOwner: ajaxify.data.group.isOwner,
- members: [member],
- },
- }, function (html) {
- translator.translate(html, function (html) {
- $('[component="groups/members"] tbody').prepend(html);
- });
- });
- });
- });
-
$('[component="groups/members"]').on('click', '[data-action]', function () {
var btnEl = $(this);
var userRow = btnEl.parents('[data-uid]');
diff --git a/public/src/client/groups/memberlist.js b/public/src/client/groups/memberlist.js
index cc3b36ce3c..82e2cdc9a7 100644
--- a/public/src/client/groups/memberlist.js
+++ b/public/src/client/groups/memberlist.js
@@ -1,7 +1,7 @@
'use strict';
-define('forum/groups/memberlist', ['components', 'forum/infinitescroll'], function () {
+define('forum/groups/memberlist', ['autocomplete'], function (autocomplete) {
var MemberList = {};
var searchInterval;
var groupName;
@@ -11,10 +11,40 @@ define('forum/groups/memberlist', ['components', 'forum/infinitescroll'], functi
templateName = _templateName || 'groups/details';
groupName = ajaxify.data.group.name;
+ handleMemberAdd();
handleMemberSearch();
handleMemberInfiniteScroll();
};
+ function handleMemberAdd() {
+ $('[component="groups/members/add"]').on('click', function () {
+ var modal = bootbox.dialog({
+ title: '[[groups:details.add-member]]',
+ message: '',
+ });
+ autocomplete.user(modal.find('input'), function (ev, ui) {
+ var user = ui.item.user;
+ if (user) {
+ addUserToGroup(user, function () {
+ modal.modal('hide');
+ });
+ }
+ });
+ });
+ }
+
+ function addUserToGroup(user, callback) {
+ socket.emit('groups.addMember', { groupName: groupName, uid: user.uid }, function (err) {
+ if (err) {
+ return app.alertError(err);
+ }
+ parseAndTranslate([user], function (html) {
+ $('[component="groups/members"] tbody').prepend(html);
+ });
+ callback();
+ });
+ }
+
function handleMemberSearch() {
$('[component="groups/members/search"]').on('keyup', function () {
var query = $(this).val();
diff --git a/src/socket.io/groups.js b/src/socket.io/groups.js
index e98b8451c6..033a4e7a4b 100644
--- a/src/socket.io/groups.js
+++ b/src/socket.io/groups.js
@@ -71,14 +71,27 @@ SocketGroups.leave = function (socket, data, callback) {
groups.leave(data.groupName, socket.uid, callback);
};
+SocketGroups.addMember = isOwner(function (socket, data, callback) {
+ if (data.groupName === 'administrators' || groups.isPrivilegeGroup(data.groupName)) {
+ return callback(new Error('[[error:not-allowed]]'));
+ }
+ groups.join(data.groupName, data.uid, callback);
+});
+
function isOwner(next) {
return function (socket, data, callback) {
async.parallel({
isAdmin: async.apply(user.isAdministrator, socket.uid),
+ isGlobalModerator: async.apply(user.isGlobalModerator, socket.uid),
isOwner: async.apply(groups.ownership.isOwner, socket.uid, data.groupName),
+ group: async.apply(groups.getGroupData, data.groupName),
}, function (err, results) {
- if (err || (!results.isOwner && !results.isAdmin)) {
- return callback(err || new Error('[[error:no-privileges]]'));
+ if (err) {
+ return callback(err);
+ }
+ var isOwner = results.isOwner || results.isAdmin || (results.isGlobalModerator && !results.group.system);
+ if (!isOwner) {
+ return callback(new Error('[[error:no-privileges]]'));
}
next(socket, data, callback);
});
diff --git a/src/views/admin/manage/group.tpl b/src/views/admin/manage/group.tpl
index b9170df21a..51301f8e56 100644
--- a/src/views/admin/manage/group.tpl
+++ b/src/views/admin/manage/group.tpl
@@ -74,13 +74,6 @@
-
-