mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-11-11 16:35:47 +01:00
@@ -317,3 +317,14 @@
|
||||
cursor: move;
|
||||
}
|
||||
}
|
||||
|
||||
.privilege-table {
|
||||
th {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 24px;
|
||||
max-height: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
define('admin/manage/category', [
|
||||
'uploader',
|
||||
'admin/modules/iconSelect',
|
||||
'admin/modules/colorpicker'
|
||||
], function(uploader, iconSelect, colorpicker) {
|
||||
'admin/modules/colorpicker',
|
||||
'autocomplete'
|
||||
], function(uploader, iconSelect, colorpicker, autocomplete) {
|
||||
var Category = {};
|
||||
|
||||
Category.init = function() {
|
||||
@@ -113,7 +114,7 @@ define('admin/manage/category', [
|
||||
|
||||
uploader.open(RELATIVE_PATH + '/api/admin/category/uploadpicture', { cid: cid }, 0, function(imageUrlOnServer) {
|
||||
inputEl.val(imageUrlOnServer);
|
||||
var previewBox = inputEl.parent().siblings('.category-preview');
|
||||
var previewBox = inputEl.parent().parent().siblings('.category-preview');
|
||||
previewBox.css('background', 'url(' + imageUrlOnServer + '?' + new Date().getTime() + ')')
|
||||
.css('background-size', 'cover');
|
||||
modified(inputEl[0]);
|
||||
@@ -125,6 +126,8 @@ define('admin/manage/category', [
|
||||
iconSelect.init($(this).find('i'), modified);
|
||||
});
|
||||
|
||||
Category.setupPrivilegeTable();
|
||||
|
||||
$(function() {
|
||||
|
||||
|
||||
@@ -312,5 +315,68 @@ define('admin/manage/category', [
|
||||
// });
|
||||
// };
|
||||
|
||||
Category.setupPrivilegeTable = function() {
|
||||
var searchEl = $('.privilege-search'),
|
||||
searchObj = autocomplete.user(searchEl);
|
||||
|
||||
// User search + addition to table
|
||||
searchObj.on('autocompleteselect', function(ev, ui) {
|
||||
socket.emit('admin.categories.setPrivilege', {
|
||||
cid: ajaxify.variables.get('cid'),
|
||||
privilege: 'read',
|
||||
set: true,
|
||||
member: ui.item.user.uid
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
|
||||
Category.refreshPrivilegeTable();
|
||||
searchEl.val('');
|
||||
});
|
||||
});
|
||||
|
||||
// Checkbox event capture
|
||||
$('.privilege-table-container').on('change', 'input[type="checkbox"]', function() {
|
||||
var checkboxEl = $(this),
|
||||
privilege = checkboxEl.parent().attr('data-privilege'),
|
||||
state = checkboxEl.prop('checked'),
|
||||
rowEl = checkboxEl.parents('tr'),
|
||||
member = rowEl.attr('data-group-slug') || rowEl.attr('data-uid');
|
||||
|
||||
if (member) {
|
||||
socket.emit('admin.categories.setPrivilege', {
|
||||
cid: ajaxify.variables.get('cid'),
|
||||
privilege: privilege,
|
||||
set: state,
|
||||
member: member
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
|
||||
checkboxEl.replaceWith('<i class="fa fa-spin fa-spinner"></i>');
|
||||
Category.refreshPrivilegeTable();
|
||||
});
|
||||
} else {
|
||||
app.alertError('No member or group was selected');
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
Category.refreshPrivilegeTable = function() {
|
||||
socket.emit('admin.categories.getPrivilegeSettings', 2, function(err, privileges) {
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
|
||||
templates.parse('admin/partials/categories/privileges', {
|
||||
privileges: privileges
|
||||
}, function(html) {
|
||||
$('.privilege-table-container').html(html);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return Category;
|
||||
});
|
||||
@@ -7,7 +7,7 @@ define('autocomplete', function() {
|
||||
var module = {};
|
||||
|
||||
module.user = function (input) {
|
||||
input.autocomplete({
|
||||
return input.autocomplete({
|
||||
delay: 100,
|
||||
source: function(request, response) {
|
||||
socket.emit('user.search', {query: request.term}, function(err, result) {
|
||||
@@ -17,7 +17,15 @@ define('autocomplete', function() {
|
||||
|
||||
if (result && result.users) {
|
||||
var names = result.users.map(function(user) {
|
||||
return user && user.username;
|
||||
return user && {
|
||||
label: user.username,
|
||||
value: user.username,
|
||||
user: {
|
||||
uid: user.uid,
|
||||
name: user.username,
|
||||
slug: user.userslug
|
||||
}
|
||||
};
|
||||
});
|
||||
response(names);
|
||||
}
|
||||
|
||||
@@ -98,6 +98,21 @@
|
||||
}
|
||||
};
|
||||
|
||||
helpers.spawnPrivilegeStates = function(privileges) {
|
||||
var states = [];
|
||||
for(var priv in privileges) {
|
||||
if (privileges.hasOwnProperty(priv)) {
|
||||
states.push({
|
||||
name: priv,
|
||||
state: privileges[priv]
|
||||
});
|
||||
}
|
||||
}
|
||||
return states.map(function(priv) {
|
||||
return '<td class="text-center" data-privilege="' + priv.name + '"><input type="checkbox"' + (priv.state ? ' checked' : '') + ' /></td>';
|
||||
}).join('');
|
||||
};
|
||||
|
||||
exports.register = function() {
|
||||
var templates;
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ var async = require('async'),
|
||||
|
||||
user = require('../user'),
|
||||
categories = require('../categories'),
|
||||
privileges = require('../privileges'),
|
||||
posts = require('../posts'),
|
||||
topics = require('../topics'),
|
||||
meta = require('../meta'),
|
||||
@@ -127,13 +128,17 @@ function getGlobalField(field, callback) {
|
||||
}
|
||||
|
||||
adminController.categories.get = function(req, res, next) {
|
||||
categories.getCategoryData(req.params.category_id, function(err, category) {
|
||||
async.parallel({
|
||||
category: async.apply(categories.getCategoryData, req.params.category_id),
|
||||
privileges: async.apply(privileges.categories.list, req.params.category_id)
|
||||
}, function(err, data) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
res.render('admin/manage/category', {
|
||||
category: category
|
||||
category: data.category,
|
||||
privileges: data.privileges
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@@ -271,7 +271,15 @@ var async = require('async'),
|
||||
};
|
||||
|
||||
Groups.getGroupFields = function(groupName, fields, callback) {
|
||||
db.getObjectFields('group:' + groupName, fields, callback);
|
||||
Groups.getMultipleGroupFields([groupName], fields, function(err, groups) {
|
||||
callback(err, groups ? groups[0] : null);
|
||||
});
|
||||
};
|
||||
|
||||
Groups.getMultipleGroupFields = function(groups, fields, callback) {
|
||||
db.getObjectsFields(groups.map(function(group) {
|
||||
return 'group:' + group;
|
||||
}), fields, callback);
|
||||
};
|
||||
|
||||
Groups.setGroupField = function(groupName, field, value, callback) {
|
||||
@@ -306,6 +314,12 @@ var async = require('async'),
|
||||
db.getSortedSetRevRange('group:' + groupName + ':members', start, end, callback);
|
||||
};
|
||||
|
||||
Groups.getMembersOfGroups = function(groupNames, callback) {
|
||||
db.getSortedSetsMembers(groupNames.map(function(name) {
|
||||
return 'group:' + name + ':members';
|
||||
}), callback);
|
||||
};
|
||||
|
||||
Groups.isMember = function(uid, groupName, callback) {
|
||||
if (!uid || parseInt(uid, 10) <= 0) {
|
||||
return callback(null, false);
|
||||
|
||||
@@ -13,6 +13,108 @@ module.exports = function(privileges) {
|
||||
|
||||
privileges.categories = {};
|
||||
|
||||
privileges.categories.list = function(cid, callback) {
|
||||
// Method used in admin/category controller to show all users with privs in that given cid
|
||||
async.parallel({
|
||||
labels: function(next) {
|
||||
async.parallel({
|
||||
users: async.apply(plugins.fireHook, 'filter:privileges.list_human',
|
||||
['Find category', 'Access & Read', 'Create Topics', 'Reply to Topics', 'Moderator'].map(function(name) {
|
||||
return {
|
||||
name: name
|
||||
};
|
||||
})
|
||||
),
|
||||
groups: async.apply(plugins.fireHook, 'filter:privileges.groups.list_human',
|
||||
['Find category', 'Access & Read', 'Create Topics', 'Reply to Topics'].map(function(name) {
|
||||
return {
|
||||
name: name
|
||||
};
|
||||
})
|
||||
)
|
||||
}, next);
|
||||
},
|
||||
users: function(next) {
|
||||
var privileges;
|
||||
async.waterfall([
|
||||
async.apply(plugins.fireHook, 'filter:privileges.list', [
|
||||
'find', 'read', 'topics:create', 'topics:reply', 'mods'
|
||||
]),
|
||||
function(privs, next) {
|
||||
privileges = privs;
|
||||
groups.getMembersOfGroups(privs.map(function(privilege) {
|
||||
return 'cid:' + cid + ':privileges:' + privilege;
|
||||
}), next);
|
||||
},
|
||||
function(memberSets, next) {
|
||||
// Reduce into a single array
|
||||
var members = memberSets.reduce(function(combined, curMembers) {
|
||||
return combined.concat(curMembers);
|
||||
}).filter(function(member, index, combined) {
|
||||
return combined.indexOf(member) === index;
|
||||
});
|
||||
|
||||
user.getMultipleUserFields(members, ['picture', 'username'], function(err, memberData) {
|
||||
memberData = memberData.map(function(member) {
|
||||
member.privileges = {};
|
||||
for(var x=0,numPrivs=privileges.length;x<numPrivs;x++) {
|
||||
member.privileges[privileges[x]] = memberSets[x].indexOf(member.uid) !== -1
|
||||
}
|
||||
|
||||
return member;
|
||||
});
|
||||
|
||||
next(null, memberData);
|
||||
});
|
||||
}
|
||||
], next);
|
||||
},
|
||||
groups: function(next) {
|
||||
var privileges;
|
||||
async.waterfall([
|
||||
async.apply(plugins.fireHook, 'filter:privileges.groups.list', [
|
||||
'groups:find', 'groups:read', 'groups:topics:create', 'groups:topics:reply'
|
||||
]),
|
||||
function(privs, next) {
|
||||
privileges = privs;
|
||||
groups.getMembersOfGroups(privs.map(function(privilege) {
|
||||
return 'cid:' + cid + ':privileges:' + privilege;
|
||||
}), next);
|
||||
},
|
||||
function(memberSets, next) {
|
||||
// Reduce into a single array
|
||||
var members = memberSets.reduce(function(combined, curMembers) {
|
||||
return combined.concat(curMembers);
|
||||
}).filter(function(member, index, combined) {
|
||||
return combined.indexOf(member) === index;
|
||||
});
|
||||
|
||||
// Special handling for "guests" group
|
||||
members.splice(members.indexOf('guests'), 1);
|
||||
|
||||
groups.getMultipleGroupFields(members, ['name', 'slug', 'memberCount'], function(err, memberData) {
|
||||
memberData.push({
|
||||
name: 'guests',
|
||||
slug: 'guests'
|
||||
});
|
||||
|
||||
memberData = memberData.map(function(member) {
|
||||
member.privileges = {};
|
||||
for(var x=0,numPrivs=privileges.length;x<numPrivs;x++) {
|
||||
member.privileges[privileges[x]] = memberSets[x].indexOf(member.name) !== -1
|
||||
}
|
||||
|
||||
return member;
|
||||
});
|
||||
|
||||
next(null, memberData);
|
||||
});
|
||||
}
|
||||
], next);
|
||||
}
|
||||
}, callback);
|
||||
};
|
||||
|
||||
privileges.categories.get = function(cid, uid, callback) {
|
||||
async.parallel({
|
||||
'topics:create': function(next) {
|
||||
|
||||
@@ -59,83 +59,53 @@ Categories.setPrivilege = function(socket, data, callback) {
|
||||
return callback(new Error('[[error:invalid-data]]'));
|
||||
}
|
||||
|
||||
groups[data.set ? 'join' : 'leave']('cid:' + data.cid + ':privileges:' + data.privilege, data.uid, callback);
|
||||
groups[data.set ? 'join' : 'leave']('cid:' + data.cid + ':privileges:' + data.privilege, data.member, callback);
|
||||
};
|
||||
|
||||
Categories.getPrivilegeSettings = function(socket, cid, callback) {
|
||||
var privileges = ['find', 'read', 'topics:create', 'topics:reply', 'mods'];
|
||||
|
||||
async.reduce(privileges, [], function(members, privilege, next) {
|
||||
groups.get('cid:' + cid + ':privileges:' + privilege, { expand: true }, function(err, groupObj) {
|
||||
if (err || !groupObj) {
|
||||
return next(null, members);
|
||||
}
|
||||
|
||||
members = members.concat(groupObj.members);
|
||||
|
||||
next(null, members);
|
||||
});
|
||||
}, function(err, members) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
// Remove duplicates
|
||||
var present = [],
|
||||
x = members.length,
|
||||
uid;
|
||||
while(x--) {
|
||||
uid = parseInt(members[x].uid, 10);
|
||||
if (present.indexOf(uid) !== -1) {
|
||||
members.splice(x, 1);
|
||||
} else {
|
||||
present.push(uid);
|
||||
}
|
||||
}
|
||||
|
||||
callback(err, members);
|
||||
});
|
||||
privileges.categories.list(cid, callback);
|
||||
};
|
||||
|
||||
Categories.setGroupPrivilege = function(socket, data, callback) {
|
||||
if(!data) {
|
||||
return callback(new Error('[[error:invalid-data]]'));
|
||||
}
|
||||
// Categories.setGroupPrivilege = function(socket, data, callback) {
|
||||
// if(!data) {
|
||||
// return callback(new Error('[[error:invalid-data]]'));
|
||||
// }
|
||||
|
||||
groups[data.set ? 'join' : 'leave']('cid:' + data.cid + ':privileges:' + data.privilege, data.name, function (err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
// groups[data.set ? 'join' : 'leave']('cid:' + data.cid + ':privileges:' + data.privilege, data.name, function (err) {
|
||||
// if (err) {
|
||||
// return callback(err);
|
||||
// }
|
||||
|
||||
groups.hide('cid:' + data.cid + ':privileges:' + data.privilege, callback);
|
||||
});
|
||||
};
|
||||
// groups.hide('cid:' + data.cid + ':privileges:' + data.privilege, callback);
|
||||
// });
|
||||
// };
|
||||
|
||||
Categories.groupsList = function(socket, cid, callback) {
|
||||
groups.list({
|
||||
expand: false,
|
||||
isAdmin: true,
|
||||
showSystemGroups: true
|
||||
}, function(err, data) {
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
// Categories.groupsList = function(socket, cid, callback) {
|
||||
// groups.list({
|
||||
// expand: false,
|
||||
// isAdmin: true,
|
||||
// showSystemGroups: true
|
||||
// }, function(err, data) {
|
||||
// if(err) {
|
||||
// return callback(err);
|
||||
// }
|
||||
|
||||
// Remove privilege groups
|
||||
data = data.filter(function(groupObj) {
|
||||
return groupObj.name.indexOf(':privileges:') === -1;
|
||||
});
|
||||
// // Remove privilege groups
|
||||
// data = data.filter(function(groupObj) {
|
||||
// return groupObj.name.indexOf(':privileges:') === -1;
|
||||
// });
|
||||
|
||||
async.map(data, function(groupObj, next) {
|
||||
privileges.categories.groupPrivileges(cid, groupObj.name, function(err, privileges) {
|
||||
if(err) {
|
||||
return next(err);
|
||||
}
|
||||
// async.map(data, function(groupObj, next) {
|
||||
// privileges.categories.groupPrivileges(cid, groupObj.name, function(err, privileges) {
|
||||
// if(err) {
|
||||
// return next(err);
|
||||
// }
|
||||
|
||||
groupObj.privileges = privileges;
|
||||
next(null, groupObj);
|
||||
});
|
||||
}, callback);
|
||||
});
|
||||
};
|
||||
// groupObj.privileges = privileges;
|
||||
// next(null, groupObj);
|
||||
// });
|
||||
// }, callback);
|
||||
// });
|
||||
// };
|
||||
|
||||
module.exports = Categories;
|
||||
@@ -2,21 +2,8 @@
|
||||
<form role="form" class="category" data-cid="{category.cid}">
|
||||
<div class="col-md-9">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading"><i class="fa fa-folder"></i> Categories</div>
|
||||
<div class="panel-heading"><i class="fa fa-folder"></i> Category Settings</div>
|
||||
<div class="panel-body">
|
||||
<div class="pull-right text-right">
|
||||
<div class="form-group">
|
||||
<div class="dropdown">
|
||||
<button type="button" class="btn btn-default" data-toggle="dropdown"><i class="fa fa-cogs"></i> Options</button>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<li class="permissions"><a href="#"><i class="fa fa-ban"></i> Access Control</a></li>
|
||||
<hr />
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3 data-edit-target="#cid-{category.cid}-name"><span>{category.name}</span> <small><i class="fa fa-edit"></i></small></h3>
|
||||
<input id="cid-{category.cid}-name" type="text" class="form-control hide" placeholder="Category Name" data-name="name" value="{category.name}" />
|
||||
<h4 data-edit-target="#cid-{category.cid}-description"><span>{category.description}</span> <small><i class="fa fa-edit"></i></small></h4>
|
||||
@@ -68,7 +55,7 @@
|
||||
</div>
|
||||
<div class="col-sm-4 col-xs-12">
|
||||
<div class="form-group">
|
||||
<label for="cid-{category.cid}-numRecentReplies"># of Recent Replies Displayed</label>
|
||||
<label for="cid-{category.cid}-numRecentReplies"># of Recent Replies</label>
|
||||
<input id="cid-{category.cid}-numRecentReplies" type="text" class="form-control" placeholder="2" data-name="numRecentReplies" value="{category.numRecentReplies}" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -81,6 +68,24 @@
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading"><i class="fa fa-key"></i> Privileges / Access Control</div>
|
||||
<div class="panel-body">
|
||||
<p>
|
||||
You can configure the access control privileges for this category in this section. Privileges can be granted on a per-user or
|
||||
a per-group basis. You can add a new user or group to this table by searching for them in the form below.
|
||||
</p>
|
||||
<p class="text-warning">
|
||||
<strong>Note</strong>: Privilege settings take effect immediately. It is not necessary to save the category after adjusting
|
||||
these settings.
|
||||
</p>
|
||||
<hr />
|
||||
<input class="form-control privilege-search" type="text" placeholder="Add a user or group to this list..." />
|
||||
<hr />
|
||||
<!-- IMPORT admin/partials/categories/privileges.tpl -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3 options acp-sidebar">
|
||||
@@ -99,11 +104,11 @@
|
||||
</div>
|
||||
<div class="btn-group btn-group-justified">
|
||||
<div class="btn-group">
|
||||
<button type="button" data-cid="{category.cid}" data-name="image" data-value="{category.image}" class="btn btn-default upload-button"><i class="fa fa-upload"></i> Upload Image</button>
|
||||
<button type="button" data-cid="{category.cid}" data-name="image" data-value="{category.image}" class="btn btn-default upload-button"><i class="fa fa-upload"></i> Upload</button>
|
||||
</div>
|
||||
<!-- IF category.image -->
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-warning delete-image"><i data-name="icon" value="fa-times" class="fa fa-times"></i> Remove Image</button>
|
||||
<button class="btn btn-warning delete-image"><i data-name="icon" value="fa-times" class="fa fa-times"></i> Remove</button>
|
||||
</div>
|
||||
<!-- ENDIF category.image -->
|
||||
</div>
|
||||
@@ -113,12 +118,16 @@
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">Categories Control Panel</div>
|
||||
<div class="panel-body">
|
||||
<div class="btn-group btn-group-justified">
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-primary save">Save</button>
|
||||
</div>
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-primary save">Save Changes</button>
|
||||
<button class="btn btn-default revert">Revert</button>
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
<button class="btn btn-danger purge"><i class="fa fa-eraser"></i> Purge Category</button>
|
||||
<button class="btn btn-danger btn-block btn-xs purge"><i class="fa fa-eraser"></i> Purge Category</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -134,3 +143,5 @@
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<input type="hidden" template-variable="cid" value="{category.cid}" />
|
||||
@@ -1,32 +0,0 @@
|
||||
<div id="category-permissions-modal" class="modal permissions-modal fade" tabindex="-1" role="dialog" aria-labelledby="Category Permissions" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3>Category Permissions</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>The following users have access control permissions in this Category</p>
|
||||
<ul class="members"></ul>
|
||||
|
||||
<hr />
|
||||
<form role="form">
|
||||
<div class="form-group">
|
||||
<label for="permission-search">User Search</label>
|
||||
<input class="form-control" type="text" id="permission-search" />
|
||||
</div>
|
||||
</form>
|
||||
<ul class="search-results users"></ul>
|
||||
|
||||
<hr />
|
||||
<form role="form">
|
||||
<div class="form-group">
|
||||
<label for="permission-group-pick">User Groups</label>
|
||||
</div>
|
||||
</form>
|
||||
<ul class="search-results groups"></ul>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user