a huge frickin' number of changes for #2887. This is part of #2463

v1.18.x
Julian Lam 10 years ago
parent 815ea88fa5
commit 9b84a887d3

@ -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);
});
};
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.hide('cid:' + data.cid + ':privileges:' + data.privilege, callback);
});
privileges.categories.list(cid, callback);
};
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;
});
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);
});
};
// 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.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);
// }
// // 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);
// }
// 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">
<button class="btn btn-primary save">Save Changes</button>
<button class="btn btn-default revert">Revert</button>
<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-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>
@ -133,4 +142,6 @@
</div>
</div>
</form>
</div>
</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">&times;</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>
Loading…
Cancel
Save