diff --git a/public/css/admin.less b/public/css/admin.less
index a3b4616261..5f862466e5 100644
--- a/public/css/admin.less
+++ b/public/css/admin.less
@@ -142,6 +142,30 @@
}
}
}
+
+ .groups {
+ li {
+ list-style-type: none;
+ margin-bottom: 1em;
+ padding: 1em;
+ .zebra;
+
+ h2 {
+ margin-top: 0;
+ font-size: 26px;
+ }
+ }
+
+ .members {
+ li {
+ display: inline-block;
+
+ img {
+ width: 32px;
+ }
+ }
+ }
+ }
}
diff --git a/public/src/forum/admin/groups.js b/public/src/forum/admin/groups.js
new file mode 100644
index 0000000000..b5990a4d8d
--- /dev/null
+++ b/public/src/forum/admin/groups.js
@@ -0,0 +1,70 @@
+$(document).ready(function() {
+ var createEl = document.getElementById('create'),
+ createModal = $('#create-modal'),
+ createSubmitBtn = document.getElementById('create-modal-go'),
+ createNameEl = $('#create-group-name'),
+ detailsModal = $('#group-details-modal'),
+ listEl = $('#groups-list');
+
+ createEl.addEventListener('click', function() {
+ createModal.modal('show');
+ setTimeout(function() {
+ createNameEl.focus();
+ }, 250);
+ }, false);
+
+ createSubmitBtn.addEventListener('click', function() {
+ var submitObj = {
+ name: createNameEl.val(),
+ description: $('#create-group-desc').val()
+ },
+ errorEl = $('#create-modal-error'),
+ errorText;
+
+ socket.emit('api:groups.create', submitObj, function(err, data) {
+ if (err) {
+ switch(err) {
+ case 'group-exists':
+ errorText = 'Please choose another name
There seems to be a group with this name already.
';
+ break;
+ case 'name-too-short':
+ errorText = 'Please specify a grou name A group name is required for administrative purposes.
';
+ break;
+ default:
+ errorText = 'Uh-Oh There was a problem creating your group. Please try again later!
';
+ break;
+ }
+
+ errorEl.html(errorText).removeClass('hide');
+ } else {
+ createModal.modal('hide');
+ errorEl.addClass('hide');
+ createNameEl.val('');
+ ajaxify.go('admin/groups');
+ }
+ });
+ });
+
+ listEl.on('click', 'button[data-action]', function() {
+ var action = this.getAttribute('data-action'),
+ gid = $(this).parents('li[data-gid]').attr('data-gid');
+
+ switch(action) {
+ case 'delete':
+ bootbox.confirm('Are you sure you wish to delete this group?', function(confirm) {
+ if (confirm) {
+ socket.emit('api:groups.delete', gid, function(err, data) {
+ if (data === 'OK') ajaxify.go('admin/groups');
+ });
+ }
+ });
+ break;
+ case 'members':
+ socket.emit('api:groups.get', gid, function(err, groupObj) {
+ console.log(groupObj);
+ detailsModal.modal('show');
+ });
+ break;
+ }
+ });
+});
\ No newline at end of file
diff --git a/public/templates/admin/groups.tpl b/public/templates/admin/groups.tpl
new file mode 100644
index 0000000000..5223a0a116
--- /dev/null
+++ b/public/templates/admin/groups.tpl
@@ -0,0 +1,100 @@
+Groups
+
+
+
+
+
+
+
+
+
+
{groups.name}
+
{groups.description}
+
+ Members
+ Delete Group
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ New Group
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/public/templates/admin/header.tpl b/public/templates/admin/header.tpl
index 2ec06fdbb1..5847ae259d 100644
--- a/public/templates/admin/header.tpl
+++ b/public/templates/admin/header.tpl
@@ -74,6 +74,7 @@
Home
Categories
Users
+ Groups
Topics
Themes
Plugins
diff --git a/public/templates/config.json b/public/templates/config.json
index d205e35c6f..0414e4da4c 100644
--- a/public/templates/config.json
+++ b/public/templates/config.json
@@ -13,6 +13,7 @@
"admin/facebook[^]*": "admin/facebook",
"admin/gplus[^]*": "admin/gplus",
"admin/motd/?$": "admin/motd",
+ "admin/groups/?$": "admin/groups",
"install/?$": "install/mail",
"install/mail/?": "install/mail",
"install/social/?": "install/social",
diff --git a/src/groups.js b/src/groups.js
index 0a95912559..0cb4495f35 100644
--- a/src/groups.js
+++ b/src/groups.js
@@ -1,12 +1,19 @@
var async = require('async'),
User = require('./user'),
Groups = {
- list: function(callback) {
+ list: function(options, callback) {
RDB.hvals('group:gid', function(err, gids) {
if (gids.length > 0) {
- async.each(gids, function(gid, next) {
- Groups.get(gid, next);
- }, callback);
+ async.map(gids, function(gid, next) {
+ Groups.get(gid, {
+ expand: options.expand
+ }, next);
+ }, function(err, groups) {
+ callback(err, groups.filter(function(group) {
+ if (group.deleted === '1') return false;
+ else return true;
+ }));
+ });
} else callback(null, []);
});
},
@@ -33,10 +40,10 @@ var async = require('async'),
}, function(err, results) {
if (err) return callback(err);
- results[0].count = results[1].length;
- results[0].users = results[1];
+ results.base.count = results.users.length;
+ results.base.members = results.users;
- callback(err, results[0]);
+ callback(err, results.base);
});
},
getGidFromName: function(name, callback) {
@@ -49,26 +56,28 @@ var async = require('async'),
RDB.hexists('group:gid', name, callback);
},
create: function(name, description, callback) {
+ if (name.length === 0) return callback(new Error('name-too-short'));
+
Groups.exists(name, function(err, exists) {
if (!exists) {
RDB.incr('next_gid', function(err, gid) {
RDB.multi()
.hset('group:gid', name, gid)
- .hset('gid:' + gid, {
+ .hmset('gid:' + gid, {
gid: gid,
name: name,
description: description,
deleted: '0'
})
.exec(function(err) {
- callback(err, gid);
+ Groups.get(gid, {}, callback);
});
});
- }
+ } else callback(new Error('group-exists'))
});
},
destroy: function(gid, callback) {
- RDB.hset('gid:' + gid, deleted, '1', callback);
+ RDB.hset('gid:' + gid, 'deleted', '1', callback);
},
join: function(gid, uid, callback) {
RDB.sadd('gid:' + gid + ':members', uid, callback);
diff --git a/src/routes/admin.js b/src/routes/admin.js
index 524712ea3f..f3b72d4d3d 100644
--- a/src/routes/admin.js
+++ b/src/routes/admin.js
@@ -1,5 +1,6 @@
var user = require('./../user.js'),
+ Groups = require('../groups'),
topics = require('./../topics.js'),
RDB = require('./../redis.js'),
pkg = require('./../../package.json'),
@@ -27,7 +28,7 @@ var user = require('./../user.js'),
(function() {
var routes = [
'categories/active', 'categories/disabled', 'users', 'topics', 'settings', 'themes',
- 'twitter', 'facebook', 'gplus', 'redis', 'motd',
+ 'twitter', 'facebook', 'gplus', 'redis', 'motd', 'groups',
'users/latest', 'users/sort-posts', 'users/sort-reputation',
'users/search', 'plugins'
];
@@ -189,6 +190,15 @@ var user = require('./../user.js'),
res.json(200, {});
});
+ app.get('/api/admin/groups', function(req, res) {
+ Groups.list({
+ expand: true
+ }, function(err, groups) {
+ res.json(200, {
+ groups: groups
+ });
+ });
+ });
};
diff --git a/src/websockets.js b/src/websockets.js
index f75eeda2a0..8f07e6575e 100644
--- a/src/websockets.js
+++ b/src/websockets.js
@@ -3,6 +3,7 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
cookie = require('cookie'),
express = require('express'),
user = require('./user.js'),
+ Groups = require('./groups'),
posts = require('./posts.js'),
favourites = require('./favourites.js'),
utils = require('../public/src/utils.js'),
@@ -817,6 +818,30 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
callback(title, numNotifications);
});
});
+
+ /*
+ GROUPS
+ */
+
+ socket.on('api:groups.create', function(data, callback) {
+ Groups.create(data.name, data.description, function(err, groupObj) {
+ callback(err ? err.message : null, groupObj || undefined);
+ });
+ });
+
+ socket.on('api:groups.delete', function(gid, callback) {
+ Groups.destroy(gid, function(err) {
+ callback(err ? err.message : null, err ? null : 'OK');
+ });
+ });
+
+ socket.on('api:groups.get', function(gid, callback) {
+ Groups.get(gid, {
+ expand: true
+ }, function(err, groupObj) {
+ callback(err ? err.message : null, groupObj || undefined);
+ });
+ });
});
}(SocketIO));