用户权限管理

This commit is contained in:
undefined committed 2020-04-28 18:16:43 +08:00
1 parent c2f9bf8c53
commit 4ec2a46877
11 files changed
+172 -75

No files matched your search

+25 -16
View File
@@ -1,13 +1,11 @@
const system = require('../model/system');
const user = require('../model/user'); const user = require('../model/user');
const builtin = require('../model/builtin');
const { Route, Handler } = require('../service/server'); const { Route, Handler } = require('../service/server');
const { PERM_ADMIN } = require('../permission'); const { PERM_MANAGE } = require('../permission');
const { RoleAlreadyExistError, ValidationError } = require('../error'); const { RoleAlreadyExistError, ValidationError } = require('../error');
class ManageHandler extends Handler { class ManageHandler extends Handler {
async prepare() { async prepare() {
this.checkPerm(PERM_ADMIN); this.checkPerm(PERM_MANAGE);
this.system = await user.getById(0); this.system = await user.getById(0);
} }
} }
@@ -43,20 +41,22 @@ class ManageEditHandler extends ManageHandler {
class ManageUserHandler extends ManageHandler { class ManageUserHandler extends ManageHandler {
async get() { async get() {
const uids = []; const uids = [];
const [rudocs, udocs, roles] = await Promise.all([ const rudocs = {};
system.get(), const [udocs, roles, system] = await Promise.all([
user.getMulti({ role: { $neq: 'default' } }).toArray(), user.getMulti({ role: { $ne: 'default' } }).toArray(),
user.getRoles(), user.getRoles(),
user.getById(0),
]); ]);
for (const role of roles) rudocs[role._id] = [];
for (const udoc of udocs) { for (const udoc of udocs) {
uids.push(udoc._id); uids.push(udoc._id);
rudocs[udoc.role].push(udoc); rudocs[udoc.role].push(udoc);
} }
const roles_with_text = roles.map((role) => [role, role]); const rolesSelect = roles.map((role) => [role._id, role._id]);
const udict = await user.getList(uids); const udict = await user.getList(uids);
this.response.template = 'domain_manage_user.html'; this.response.template = 'domain_manage_user.html';
this.response.body = { this.response.body = {
roles, roles_with_text, rudocs, udict, roles, rolesSelect, rudocs, udict, system,
}; };
} }
@@ -68,9 +68,12 @@ class ManageUserHandler extends ManageHandler {
class ManagePermissionHandler extends ManageHandler { class ManagePermissionHandler extends ManageHandler {
async get() { async get() {
const roles = await user.getRoles(); const [roles, system] = await Promise.all([
user.getRoles(),
user.getById(0),
]);
this.response.template = 'domain_manage_permission.html'; this.response.template = 'domain_manage_permission.html';
this.response.body = { roles }; this.response.body = { roles, system };
} }
async post({ roles }) { async post({ roles }) {
@@ -88,21 +91,27 @@ class ManagePermissionHandler extends ManageHandler {
class ManageRoleHandler extends ManageHandler { class ManageRoleHandler extends ManageHandler {
async get() { async get() {
const roles = await user.getRoles(); const [roles, system] = await Promise.all([
user.getRoles(),
user.getById(0),
]);
this.response.template = 'domain_manage_role.html'; this.response.template = 'domain_manage_role.html';
this.response.body = { roles }; this.response.body = { roles, system };
} }
async postAdd({ role }) { async postAdd({ role }) {
const r = await user.getRole(role); const [r, u] = await Promise.all([
user.getRole(role),
user.getRole('default'),
]);
if (r) throw new RoleAlreadyExistError(role); if (r) throw new RoleAlreadyExistError(role);
await user.addRole(role, builtin.PERM_DEFAULT); await user.addRole(role, u.perm);
this.back(); this.back();
} }
async postDelete({ roles }) { async postDelete({ roles }) {
for (const role of roles) { for (const role of roles) {
if (['admin', 'default', 'guest'].includes(role)) { if (['root', 'default', 'guest'].includes(role)) {
throw new ValidationError('role'); throw new ValidationError('role');
} }
} }
+10 -3
View File
@@ -30,6 +30,7 @@ class UserLoginHandler extends Handler {
this.response.redirect = referer.endsWith('/login') ? '/' : referer; this.response.redirect = referer.endsWith('/login') ? '/' : referer;
} }
} }
class UserLogoutHandler extends Handler { class UserLogoutHandler extends Handler {
async prepare() { async prepare() {
this.checkPerm(PERM_LOGGEDIN); this.checkPerm(PERM_LOGGEDIN);
@@ -44,6 +45,7 @@ class UserLogoutHandler extends Handler {
this.response.body = {}; this.response.body = {};
} }
} }
class UserRegisterHandler extends Handler { class UserRegisterHandler extends Handler {
async prepare() { async prepare() {
this.checkPerm(PERM_REGISTER_USER); this.checkPerm(PERM_REGISTER_USER);
@@ -71,6 +73,7 @@ class UserRegisterHandler extends Handler {
} }
} }
} }
class UserRegisterWithCodeHandler extends Handler { class UserRegisterWithCodeHandler extends Handler {
async prepare() { async prepare() {
this.checkPerm(PERM_REGISTER_USER); this.checkPerm(PERM_REGISTER_USER);
@@ -99,6 +102,7 @@ class UserRegisterWithCodeHandler extends Handler {
this.response.redirect = '/'; this.response.redirect = '/';
} }
} }
class UserLostPassHandler extends Handler { class UserLostPassHandler extends Handler {
constructor(ctx) { constructor(ctx) {
if (!options.smtp.user) throw new SystemError('Cannot send mail'); if (!options.smtp.user) throw new SystemError('Cannot send mail');
@@ -123,6 +127,7 @@ class UserLostPassHandler extends Handler {
this.response.template = 'user_lostpass_mail_sent.html'; this.response.template = 'user_lostpass_mail_sent.html';
} }
} }
class UserLostPassWithCodeHandler extends Handler { class UserLostPassWithCodeHandler extends Handler {
async get({ code }) { async get({ code }) {
const tdoc = await token.get(code, token.TYPE_LOSTPASS); const tdoc = await token.get(code, token.TYPE_LOSTPASS);
@@ -140,6 +145,7 @@ class UserLostPassWithCodeHandler extends Handler {
this.response.redirect = '/'; this.response.redirect = '/';
} }
} }
class UserDetailHandler extends Handler { class UserDetailHandler extends Handler {
constructor(ctx) { constructor(ctx) {
super(ctx); super(ctx);
@@ -154,6 +160,7 @@ class UserDetailHandler extends Handler {
this.response.body = { isSelfProfile, udoc, sdoc }; this.response.body = { isSelfProfile, udoc, sdoc };
} }
} }
class UserSearchHandler extends Handler { class UserSearchHandler extends Handler {
async get({ q, exactMatch = false }) { async get({ q, exactMatch = false }) {
let udocs; let udocs;
@@ -161,7 +168,7 @@ class UserSearchHandler extends Handler {
else udocs = await user.getPrefixList(q, 20); else udocs = await user.getPrefixList(q, 20);
try { try {
const udoc = await user.getById(parseInt(q)); const udoc = await user.getById(parseInt(q));
if (udoc) udocs.insert(0, udoc); if (udoc) udocs.push(udoc);
} catch (e) { } catch (e) {
/* Ignore */ /* Ignore */
} }
@@ -170,7 +177,7 @@ class UserSearchHandler extends Handler {
udocs[i].gravatar_url = misc.gravatar_url[udocs[i].gravatar]; udocs[i].gravatar_url = misc.gravatar_url[udocs[i].gravatar];
} }
} }
this.response.body = { udocs }; this.response.body = udocs;
} }
} }
@@ -180,5 +187,5 @@ Route('/register/:code', UserRegisterWithCodeHandler);
Route('/logout', UserLogoutHandler); Route('/logout', UserLogoutHandler);
Route('/lostpass', UserLostPassHandler); Route('/lostpass', UserLostPassHandler);
Route('/lostpass/:code', UserLostPassWithCodeHandler); Route('/lostpass/:code', UserLostPassWithCodeHandler);
Route('/user/:uid', UserDetailHandler);
Route('/user/search', UserSearchHandler); Route('/user/search', UserSearchHandler);
Route('/user/:uid', UserDetailHandler);
+1 -1
View File
@@ -44,7 +44,7 @@ async function run() {
hash: pwhash.hash('rootroot', salt), hash: pwhash.hash('rootroot', salt),
salt, salt,
gravatar: 'root@hydro', gravatar: 'root@hydro',
role: 'admin', role: 'root',
}, udoc), }, udoc),
]); ]);
} }
+1 -1
View File
@@ -3,7 +3,7 @@ const perm = require('../permission');
const BUILTIN_ROLES = [ const BUILTIN_ROLES = [
{ _id: 'guest', perm: perm.PERM_BASIC }, { _id: 'guest', perm: perm.PERM_BASIC },
{ _id: 'default', perm: perm.PERM_DEFAULT, count: 0 }, { _id: 'default', perm: perm.PERM_DEFAULT, count: 0 },
{ _id: 'admin', perm: perm.PERM_ADMIN, count: 0 }, { _id: 'root', perm: perm.PERM_ALL, count: 0 },
]; ];
const CATEGORIES = { const CATEGORIES = {
动态规划: [ 动态规划: [
+21 -4
View File
@@ -1,5 +1,6 @@
const system = require('./system'); const system = require('./system');
const { UserNotFoundError, UserAlreadyExistError } = require('../error'); const { UserNotFoundError, UserAlreadyExistError } = require('../error');
const perm = require('../permission');
const pwhash = require('../lib/pwhash'); const pwhash = require('../lib/pwhash');
const db = require('../service/db'); const db = require('../service/db');
@@ -25,7 +26,7 @@ class USER {
} }
hasPerm(perm) { hasPerm(perm) {
return this.perm === '-' || (this.perm || '').includes(perm); return this.perm.includes(perm);
} }
checkPassword(password) { checkPassword(password) {
@@ -122,7 +123,7 @@ async function create({
role, role,
gravatar: mail, gravatar: mail,
}), }),
collRole.updateOne({ _id: role }, { $inc: { count: 1 } }) collRole.updateOne({ _id: role }, { $inc: { count: 1 } }),
]); ]);
} catch (e) { } catch (e) {
throw new UserAlreadyExistError([uid, uname, mail]); throw new UserAlreadyExistError([uid, uname, mail]);
@@ -133,6 +134,13 @@ function getMulti(params) {
return coll.find(params); return coll.find(params);
} }
async function getPrefixList(prefix, limit = 50) {
prefix = prefix.toLowerCase();
const $regex = new RegExp(`\\A\\Q${prefix.replace(/\\E/gmi, /\\E\\E\\Q/gmi)}\\E`, 'gmi');
const udocs = await coll.find({ unameLower: { $regex } }).limit(limit).toArray();
return udocs;
}
async function setRole(uid, role) { async function setRole(uid, role) {
const udoc = await getById(uid); const udoc = await getById(uid);
return await Promise.all([ return await Promise.all([
@@ -143,7 +151,7 @@ async function setRole(uid, role) {
} }
function getRoles() { function getRoles() {
return collRole.find().toArray(); return collRole.find().sort('_id', 1).toArray();
} }
function getRole(name) { function getRole(name) {
@@ -155,7 +163,14 @@ function addRole(name, perm) {
} }
function deleteRoles(roles) { function deleteRoles(roles) {
return collRole.deleteMany({ _id: { $in: roles } }); return Promise.all([
coll.updateMany({ role: { $in: roles } }, { $set: { role: 'default' } }),
collRole.deleteMany({ _id: { $in: roles } }),
]);
}
function init() {
return collRole.update({ _id: 'root' }, { $set: { perm: perm.PERM_ALL } });
} }
module.exports = { module.exports = {
@@ -169,10 +184,12 @@ module.exports = {
setById, setById,
setEmail, setEmail,
setPassword, setPassword,
getPrefixList,
setRole, setRole,
getRole, getRole,
getList, getList,
getRoles, getRoles,
addRole, addRole,
deleteRoles, deleteRoles,
init,
}; };
+80 -33
View File
@@ -1,12 +1,8 @@
const permissions = { const perm = {
PERM_NONE: '0', PERM_MANAGE: '#',
PERM_ALL: '-', PERM_LOGGEDIN: '0',
PERM_LOGGEDIN: '~', PERM_REGISTER_USER: '1',
PERM_REGISTER_USER: '+',
PERM_VIEW: '1',
PERM_EDIT_PERM: '2',
PERM_MOD_BADGE: '3', PERM_MOD_BADGE: '3',
PERM_EDIT_DESCRIPTION: '4',
PERM_CREATE_PROBLEM: '5', PERM_CREATE_PROBLEM: '5',
PERM_EDIT_PROBLEM: '6', PERM_EDIT_PROBLEM: '6',
PERM_VIEW_PROBLEM: '7', PERM_VIEW_PROBLEM: '7',
@@ -43,28 +39,79 @@ const permissions = {
PERM_EDIT_TRAINING: 'c', PERM_EDIT_TRAINING: 'c',
PERM_JUDGE: 'd', PERM_JUDGE: 'd',
}; };
permissions.PERM_BASIC = permissions.PERM_VIEW
+ permissions.PERM_VIEW_PROBLEM let PERM_ALL = '';
+ permissions.PERM_VIEW_PROBLEM_SOLUTION for (const p in perm) PERM_ALL += perm[p];
+ permissions.PERM_VIEW_DISCUSSION perm.PERM_ALL = PERM_ALL;
+ permissions.PERM_VIEW_CONTEST
+ permissions.PERM_VIEW_CONTEST_SCOREBOARD const Permission = (family, key, desc) => ({ family, key, desc });
+ permissions.PERM_REGISTER_USER;
permissions.PERM_DEFAULT = permissions.PERM_VIEW const PERMS = [
+ permissions.PERM_VIEW_PROBLEM Permission('perm_general', perm.PERM_REGISTER_USER, 'Register new account'),
+ permissions.PERM_SUBMIT_PROBLEM Permission('perm_general', perm.PERM_MANAGE, 'Manage the system'),
+ permissions.PERM_VIEW_PROBLEM_SOLUTION Permission('perm_general', perm.PERM_MOD_BADGE, 'Show MOD badge'),
+ permissions.PERM_CREATE_PROBLEM_SOLUTION Permission('perm_problem', perm.PERM_CREATE_PROBLEM, 'Create problems'),
+ permissions.PERM_VOTE_PROBLEM_SOLUTION Permission('perm_problem', perm.PERM_EDIT_PROBLEM, 'Edit problems'),
+ permissions.PERM_REPLY_PROBLEM_SOLUTION Permission('perm_problem', perm.PERM_VIEW_PROBLEM, 'View problems'),
+ permissions.PERM_VIEW_DISCUSSION Permission('perm_problem', perm.PERM_VIEW_PROBLEM_HIDDEN, 'View hidden problems'),
+ permissions.PERM_CREATE_DISCUSSION Permission('perm_problem', perm.PERM_SUBMIT_PROBLEM, 'Submit problem'),
+ permissions.PERM_REPLY_DISCUSSION Permission('perm_problem', perm.PERM_READ_PROBLEM_DATA, 'Read data of problem'),
+ permissions.PERM_VIEW_CONTEST Permission('perm_record', perm.PERM_READ_RECORD_CODE, 'Read record codes'),
+ permissions.PERM_VIEW_CONTEST_SCOREBOARD Permission('perm_record', perm.PERM_REJUDGE_PROBLEM, 'Rejudge problems'),
+ permissions.PERM_ATTEND_CONTEST Permission('perm_record', perm.PERM_REJUDGE, 'Rejudge records'),
+ permissions.PERM_VIEW_TRAINING Permission('perm_problem_solution', perm.PERM_VIEW_PROBLEM_SOLUTION, 'View problem solutions'),
+ permissions.PERM_CREATE_TRAINING Permission('perm_problem_solution', perm.PERM_CREATE_PROBLEM_SOLUTION, 'Create problem solutions'),
+ permissions.PERM_LOGGEDIN; Permission('perm_problem_solution', perm.PERM_VOTE_PROBLEM_SOLUTION, 'Vote problem solutions'),
permissions.PERM_ADMIN = permissions.PERM_ALL; Permission('perm_problem_solution', perm.PERM_EDIT_PROBLEM_SOLUTION, 'Edit problem solutions'),
module.exports = permissions; Permission('perm_problem_solution', perm.PERM_DELETE_PROBLEM_SOLUTION, 'Delete problem solutions'),
Permission('perm_problem_solution', perm.PERM_REPLY_PROBLEM_SOLUTION, 'Reply problem solutions'),
Permission('perm_problem_solution', perm.PERM_EDIT_PROBLEM_SOLUTION_REPLY, 'Edit problem solution replies'),
Permission('perm_problem_solution', perm.PERM_DELETE_PROBLEM_SOLUTION_REPLY, 'Delete problem solution replies'),
Permission('perm_discussion', perm.PERM_VIEW_DISCUSSION, 'View discussions'),
Permission('perm_discussion', perm.PERM_CREATE_DISCUSSION, 'Create discussions'),
Permission('perm_discussion', perm.PERM_HIGHLIGHT_DISCUSSION, 'Highlight discussions'),
Permission('perm_discussion', perm.PERM_EDIT_DISCUSSION, 'Edit discussions'),
Permission('perm_discussion', perm.PERM_DELETE_DISCUSSION, 'Delete discussions'),
Permission('perm_discussion', perm.PERM_REPLY_DISCUSSION, 'Reply discussions'),
Permission('perm_discussion', perm.PERM_EDIT_DISCUSSION_REPLY, 'Edit discussion replies'),
Permission('perm_discussion', perm.PERM_DELETE_DISCUSSION_REPLY, 'Delete discussion replies'),
Permission('perm_contest', perm.PERM_VIEW_CONTEST, 'View contests'),
Permission('perm_contest', perm.PERM_VIEW_CONTEST_SCOREBOARD, 'View contest scoreboard'),
Permission('perm_contest', perm.PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD, 'View hidden contest submission status and scoreboard'),
Permission('perm_contest', perm.PERM_CREATE_CONTEST, 'Create contests'),
Permission('perm_contest', perm.PERM_ATTEND_CONTEST, 'Attend contests'),
Permission('perm_contest', perm.PERM_EDIT_CONTEST, 'Edit any contests'),
Permission('perm_training', perm.PERM_VIEW_TRAINING, 'View training plans'),
Permission('perm_training', perm.PERM_CREATE_TRAINING, 'Create training plans'),
Permission('perm_training', perm.PERM_EDIT_TRAINING, 'Edit training plans'),
];
const PERMS_BY_FAMILY = {};
for (const p of PERMS) {
if (!PERMS_BY_FAMILY[p.family]) PERMS_BY_FAMILY[p.family] = [p];
else PERMS_BY_FAMILY[p.family].push(p);
}
perm.PERMS_BY_FAMILY = PERMS_BY_FAMILY;
perm.PERM_BASIC = perm.PERM_VIEW_PROBLEM
+ perm.PERM_VIEW_PROBLEM_SOLUTION
+ perm.PERM_VIEW_DISCUSSION
+ perm.PERM_VIEW_TRAINING
+ perm.PERM_VIEW_CONTEST
+ perm.PERM_VIEW_CONTEST_SCOREBOARD
+ perm.PERM_REGISTER_USER;
perm.PERM_DEFAULT = perm.PERM_BASIC
+ perm.PERM_SUBMIT_PROBLEM
+ perm.PERM_CREATE_PROBLEM_SOLUTION
+ perm.PERM_VOTE_PROBLEM_SOLUTION
+ perm.PERM_REPLY_PROBLEM_SOLUTION
+ perm.PERM_CREATE_DISCUSSION
+ perm.PERM_REPLY_DISCUSSION
+ perm.PERM_ATTEND_CONTEST
+ perm.PERM_CREATE_TRAINING
+ perm.PERM_LOGGEDIN;
perm.PERM_ADMIN = perm.PERM_ALL;
module.exports = perm;
+5 -4
View File
@@ -1,9 +1,10 @@
{% set page_name = "domain_manage_permission" %}
{% extends "manage_base.html" %} {% extends "manage_base.html" %}
{% block domain_content %} {% block domain_content %}
<form method="post"> <form method="post">
<div class="section"> <div class="section">
<div class="section__header"> <div class="section__header">
<h1 class="section__title">{{ _('{0}: Permissions').format(handler.domain['name']) }}</h1> <h1 class="section__title">{{ _('{0}: Permissions').format(system.uname) }}</h1>
</div> </div>
<div class="section__body no-padding domain-users"> <div class="section__body no-padding domain-users">
<table class="data-table"> <table class="data-table">
@@ -17,11 +18,11 @@
<tr> <tr>
<th class="col--description">{{ _('Permissions') }}</th> <th class="col--description">{{ _('Permissions') }}</th>
{% for role in roles %} {% for role in roles %}
<th class="col--p"><span data-tooltip="{{ role }}">{{ role }}</span></th> <th class="col--p"><span data-tooltip="{{ role }}">{{ role._id }}</span></th>
{% endfor %} {% endfor %}
</tr> </tr>
</thead> </thead>
{% for family, perms in perm.PERMS_BY_FAMILY.items() %} {% for family, perms in perm.PERMS_BY_FAMILY %}
<tbody> <tbody>
<tr> <tr>
<td class="col--family" colspan="{{ (roles|length)+1 }}"> <td class="col--family" colspan="{{ (roles|length)+1 }}">
@@ -36,7 +37,7 @@
{% for role in roles %} {% for role in roles %}
<td class="col--p"> <td class="col--p">
<label class="compact checkbox"> <label class="compact checkbox">
<input name="{{ role }}" data-checkbox-group="{{ role }}" data-checkbox-range value="{{ perm.key }}" type="checkbox"{% if roles[role].includes(perm.key) or roles[role] == '-' %} checked{% endif %}> <input name="{{ role._id }}" data-checkbox-group="{{ role._id }}" data-checkbox-range value="{{ perm.key }}" type="checkbox"{% if role.perm.includes(perm.key) %} checked{% endif %}{% if role._id == 'root' %} disabled{% endif %}>
</label> </label>
</td> </td>
{% endfor %} {% endfor %}
+2 -2
View File
@@ -21,7 +21,7 @@
</div> </div>
<div class="section"> <div class="section">
<div class="section__header"> <div class="section__header">
<h1 class="section__title">{{ _('{0}: Roles').format(handler.domain['name']) }}</h1> <h1 class="section__title">{{ _('{0}: Roles').format(system.uname) }}</h1>
<div class="section__tools"> <div class="section__tools">
<button class="primary rounded button" name="create_role">{{ _('Create Role') }}</button> <button class="primary rounded button" name="create_role">{{ _('Create Role') }}</button>
</div> </div>
@@ -48,7 +48,7 @@
</thead> </thead>
<tbody> <tbody>
{% for role in roles %} {% for role in roles %}
{% if role._id not in ['default', 'guest', 'admin'] %} {% if role._id not in ['default', 'guest', 'root'] %}
<tr data-role="{{ role._id }}"> <tr data-role="{{ role._id }}">
<td class="col--checkbox"> <td class="col--checkbox">
<label class="compact checkbox"> <label class="compact checkbox">
+25 -9
View File
@@ -1,3 +1,4 @@
{% set page_name = "domain_manage_user" %}
{% extends "manage_base.html" %} {% extends "manage_base.html" %}
{% block domain_content %} {% block domain_content %}
<div style="display: none" class="dialog__body--add-user"> <div style="display: none" class="dialog__body--add-user">
@@ -15,7 +16,12 @@
<div class="medium-6 columns"> <div class="medium-6 columns">
<label> <label>
{{ _('Role') }} {{ _('Role') }}
{{ form.select([('', _('Select a role'))] + roles_with_text, 'role') }} {{ form.select({
label:_('Select a role'),
options:rolesSelect,
name:'role',
value:'default'
}) }}
</label> </label>
</div> </div>
</div> </div>
@@ -30,7 +36,11 @@
<div class="medium-12 columns"> <div class="medium-12 columns">
<label> <label>
{{ _('Set Roles for selected users') }} {{ _('Set Roles for selected users') }}
{{ form.select([('', _('Select a role'))] + roles_with_text, 'role') }} {{ form.select({
label:_('Select a role'),
options:rolesSelect,
name:'role'
}) }}
</label> </label>
</div> </div>
</div> </div>
@@ -38,7 +48,7 @@
</div> </div>
<div class="section"> <div class="section">
<div class="section__header"> <div class="section__header">
<h1 class="section__title">{{ _('{0}: Users').format(handler.domain['name']) }}</h1> <h1 class="section__title">{{ _('{0}: Users').format(system.uname) }}</h1>
<div class="section__tools"> <div class="section__tools">
<button class="primary rounded button" name="add_user">{{ _('Add User to Domain') }}</button> <button class="primary rounded button" name="add_user">{{ _('Add User to Domain') }}</button>
</div> </div>
@@ -68,24 +78,30 @@
<tbody> <tbody>
{% for role in roles %} {% for role in roles %}
{% for rudoc in rudocs[role] %} {% for rudoc in rudocs[role] %}
{% set is_disabled=(rudoc['uid'] == handler.domain['owner']) %} {% set is_disabled=(rudoc._id == handler.user._id) %}
<tr data-uid="{{ rudoc['uid'] }}"> <tr data-uid="{{ rudoc._id }}">
<td class="col--checkbox"> <td class="col--checkbox">
<label class="compact checkbox"> <label class="compact checkbox">
<input type="checkbox" data-checkbox-group="user" {% if is_disabled %}disabled{% else %}data-checkbox-range{% endif %}> <input type="checkbox" data-checkbox-group="user" {% if is_disabled %}disabled{% else %}data-checkbox-range{% endif %}>
</label> </label>
</td> </td>
<td class="col--uid"> <td class="col--uid">
{{ rudoc['uid'] }} {{ rudoc._id }}
</td> </td>
<td class="col--user"> <td class="col--user">
{{ user.render_inline(udict[rudoc['uid']], badge=false) }} {{ user.render_inline(udict[rudoc._id], badge=false) }}
</td> </td>
<td class="col--name-alias"> <td class="col--name-alias">
{{ rudoc['display_name']|default('') }} {{ rudoc.displayName|default('') }}
</td> </td>
<td class="col--role"> <td class="col--role">
{{ form.select(roles_with_text, 'role', value=rudoc['role'], extra_class='compact', disabled=is_disabled) }} {{ form.select({
options:rolesSelect,
name:'role',
value:rudoc['role'],
extra_class:'compact',
disabled:is_disabled
}) }}
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}
+1 -1
View File
@@ -23,7 +23,7 @@
{% if handler.hasPerm(perm.PERM_VIEW_CONTEST) %} {% if handler.hasPerm(perm.PERM_VIEW_CONTEST) %}
{{ nav_item('/c', 'contest_main', 'contest') }} {{ nav_item('/c', 'contest_main', 'contest') }}
{% endif %} {% endif %}
{% if handler.hasPerm(perm.PERM_EDIT_DESCRIPTION) or handler.hasPerm(perm.PERM_EDIT_PERM) %} {% if handler.hasPerm(perm.PERM_MANAGE) %}
{{ nav_item('/manage', 'domain_manage', 'domain_manage') }} {{ nav_item('/manage', 'domain_manage', 'domain_manage') }}
{% endif %} {% endif %}
</ol> </ol>
@@ -16,7 +16,7 @@ function renderItem(user) {
return tpl` return tpl`
<div class="media"> <div class="media">
<div class="media__left medium"> <div class="media__left medium">
<img class="small user-profile-avatar" src="${user.gravatar_url}" width="30" height="30"> <img class="small user-profile-avatar" src="${user.gravatar}" width="30" height="30">
</div> </div>
<div class="media__body medium"> <div class="media__body medium">
<div class="user-select__uname">${user.uname}</div> <div class="user-select__uname">${user.uname}</div>