modtools: settings for which roles/affiliations may be queried or assigned
This commit is contained in:
parent
e5341d54a9
commit
2af93f4492
@ -18,6 +18,8 @@
|
||||
- #1839: Headline messages are shown in controlbox
|
||||
- Allow ignore bootstrap modules at build using environment variable: BOOTSTRAP_IGNORE_MODULES="Modal,Dropdown".
|
||||
example: export BOOTSTRAP_IGNORE_MODULES="Modal,Dropdown" && make dist
|
||||
- New config option [modtools_disable_query](https://conversejs.org/docs/html/configuration.html#modtools-disable-query)
|
||||
- New config option [modtools_disable_assign](https://conversejs.org/docs/html/configuration.html#modtools-disable-assign)
|
||||
|
||||
## 6.0.0 (2020-01-09)
|
||||
|
||||
|
@ -1039,6 +1039,26 @@ and it's trivial for an attacker to bypass this restriction.
|
||||
|
||||
You should therefore also configure your XMPP server to limit message sizes.
|
||||
|
||||
modtools_disable_assign
|
||||
-----------------------
|
||||
|
||||
* Default: ``false``
|
||||
* Possible Values: ``true``, ``false``, ``['owner', 'admin', 'member', 'outcast', 'none', 'moderator', 'participant', 'visitor']``
|
||||
|
||||
This setting allows you to disable (either completely, or fine-grained) which affiliations and or roles
|
||||
may be assigned in the moderator tools modal.
|
||||
|
||||
|
||||
modtools_disable_query
|
||||
----------------------
|
||||
|
||||
* Default: ``[]``
|
||||
* Possible Values: ``['owner', 'admin', 'member', 'outcast', 'none', 'moderator', 'participant', 'visitor']``
|
||||
|
||||
This setting allows you to disable which affiliations or roles may be queried in the moderator tools modal.
|
||||
If all roles or all affiliations are disabled, then the relevant tab won't be
|
||||
showed at all.
|
||||
|
||||
|
||||
muc_disable_slash_commands
|
||||
--------------------------
|
||||
|
@ -41,7 +41,8 @@
|
||||
_converse.connection.IQ_stanzas = [];
|
||||
tab.click();
|
||||
let select = modal.el.querySelector('.select-affiliation');
|
||||
expect(select.value).toBe('admin');
|
||||
expect(select.value).toBe('owner');
|
||||
select.value = 'admin';
|
||||
let button = modal.el.querySelector('.btn-primary[name="users_with_affiliation"]');
|
||||
button.click();
|
||||
await u.waitUntil(() => !modal.loading_users_with_affiliation);
|
||||
|
@ -40,7 +40,7 @@ const { Strophe, sizzle, $iq, $pres } = converse.env;
|
||||
const u = converse.env.utils;
|
||||
|
||||
const ROLES = ['moderator', 'participant', 'visitor'];
|
||||
const AFFILIATIONS = ['admin', 'member', 'outcast', 'owner'];
|
||||
const AFFILIATIONS = ['owner', 'admin', 'member', 'outcast', 'none'];
|
||||
const OWNER_COMMANDS = ['owner'];
|
||||
const ADMIN_COMMANDS = ['admin', 'ban', 'deop', 'destroy', 'member', 'op', 'revoke'];
|
||||
const MODERATOR_COMMANDS = ['kick', 'mute', 'voice', 'modtools'];
|
||||
@ -101,16 +101,18 @@ converse.plugins.add('converse-muc-views', {
|
||||
'auto_list_rooms': false,
|
||||
'cache_muc_messages': true,
|
||||
'locked_muc_nickname': false,
|
||||
'show_retraction_warning': true,
|
||||
'modtools_disable_query': [],
|
||||
'modtools_disable_assign': false,
|
||||
'muc_disable_slash_commands': false,
|
||||
'muc_show_join_leave': true,
|
||||
'muc_show_join_leave_status': true,
|
||||
'muc_mention_autocomplete_min_chars': 0,
|
||||
'muc_mention_autocomplete_filter': 'contains',
|
||||
'muc_mention_autocomplete_min_chars': 0,
|
||||
'muc_mention_autocomplete_show_avatar': true,
|
||||
'roomconfig_whitelist': [],
|
||||
'muc_roomid_policy': null,
|
||||
'muc_roomid_policy_hint': null,
|
||||
'muc_show_join_leave': true,
|
||||
'muc_show_join_leave_status': true,
|
||||
'roomconfig_whitelist': [],
|
||||
'show_retraction_warning': true,
|
||||
'visible_toolbar_buttons': {
|
||||
'toggle_occupants': true
|
||||
}
|
||||
@ -248,26 +250,17 @@ converse.plugins.add('converse-muc-views', {
|
||||
},
|
||||
|
||||
toHTML () {
|
||||
const allowed_commands = this.chatroomview.getAllowedCommands();
|
||||
const allowed_affiliations = allowed_commands.map(c => COMMAND_TO_AFFILIATION[c]).filter(c => c);
|
||||
const allowed_roles = [...new Set(allowed_commands
|
||||
.filter((value, i, list) => list.indexOf(value) == i)
|
||||
.map(c => COMMAND_TO_ROLE[c])
|
||||
.filter(c => c))];
|
||||
|
||||
allowed_affiliations.sort();
|
||||
allowed_roles.sort();
|
||||
|
||||
const occupant = this.chatroomview.model.occupants.findWhere({'jid': _converse.bare_jid});
|
||||
return tpl_moderator_tools_modal(Object.assign(this.model.toJSON(), {
|
||||
allowed_affiliations,
|
||||
allowed_roles,
|
||||
'affiliations': [...AFFILIATIONS, 'none'],
|
||||
'assignAffiliation': ev => this.assignAffiliation(ev),
|
||||
'assignRole': ev => this.assignRole(ev),
|
||||
'loading_users_with_affiliation': this.loading_users_with_affiliation,
|
||||
'queryAffiliation': ev => this.queryAffiliation(ev),
|
||||
'queryRole': ev => this.queryRole(ev),
|
||||
'roles': ROLES,
|
||||
'queryable_affiliations': AFFILIATIONS.filter(a => !_converse.modtools_disable_query.includes(a)),
|
||||
'queryable_roles': ROLES.filter(a => !_converse.modtools_disable_query.includes(a)),
|
||||
'assignable_affiliations': this.getAssignableAffiliations(occupant),
|
||||
'assignable_roles': this.getAssignableRoles(occupant),
|
||||
'switchTab': ev => this.switchTab(ev),
|
||||
'toggleForm': ev => this.toggleForm(ev),
|
||||
'users_with_affiliation': this.users_with_affiliation,
|
||||
@ -275,6 +268,30 @@ converse.plugins.add('converse-muc-views', {
|
||||
}));
|
||||
},
|
||||
|
||||
getAssignableAffiliations (occupant) {
|
||||
const disabled = _converse.modtools_disable_assign;
|
||||
if (!Array.isArray(disabled)) {
|
||||
return disabled ? [] : AFFILIATIONS;
|
||||
} else if (occupant.get('affiliation') === 'owner') {
|
||||
return AFFILIATIONS.filter(a => !disabled.includes(a));
|
||||
} else if (occupant.get('affiliation') === 'admin') {
|
||||
return AFFILIATIONS.filter(a => !['owner', ...disabled].includes(a));
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
|
||||
getAssignableRoles (occupant) {
|
||||
const disabled = _converse.modtools_disable_assign;
|
||||
if (!Array.isArray(disabled)) {
|
||||
return disabled ? [] : ROLES;
|
||||
} else if (occupant.get('role') === 'moderator') {
|
||||
return ROLES.filter(r => !disabled.includes(r));
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
|
||||
shouldFetchAffiliationsList () {
|
||||
const affiliation = this.model.get('affiliation');
|
||||
if (affiliation === 'none') {
|
||||
|
@ -64,6 +64,31 @@ const affiliation_option = (o) => html`
|
||||
`;
|
||||
|
||||
|
||||
const tpl_set_role_form = (o) => html`
|
||||
<form class="role-form hidden" @submit=${o.assignRole}>
|
||||
<div class="form-group">
|
||||
<input type="hidden" name="jid" value="${o.item.jid}"/>
|
||||
<input type="hidden" name="nick" value="${o.item.nick}"/>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<label><strong>${i18n_new_role}:</strong></label>
|
||||
<select class="custom-select select-role" name="role">
|
||||
${ o.assignable_roles.map(role => html`<option value="${role}" ?selected=${role === o.item.role}>${role}</option>`) }
|
||||
</select>
|
||||
</div>
|
||||
<div class="col">
|
||||
<label><strong>${i18n_reason}:</strong></label>
|
||||
<input class="form-control" type="text" name="reason"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="submit" class="btn btn-primary" value="${i18n_change_role}"/>
|
||||
</div>
|
||||
</form>
|
||||
`;
|
||||
|
||||
|
||||
const role_list_item = (o) => html`
|
||||
<li class="list-group-item">
|
||||
<ul class="list-group">
|
||||
@ -74,34 +99,39 @@ const role_list_item = (o) => html`
|
||||
<div><strong>Nickname:</strong> ${o.item.nick}</div>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<div><strong>Role:</strong> ${o.item.role}<a href="#" data-form="role-form" class="toggle-form right fa fa-wrench" @click=${o.toggleForm}></a></div>
|
||||
<form class="role-form hidden" @submit=${o.assignRole}>
|
||||
<div class="form-group">
|
||||
<input type="hidden" name="jid" value="${o.item.jid}"/>
|
||||
<input type="hidden" name="nick" value="${o.item.nick}"/>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<label><strong>${i18n_new_role}:</strong></label>
|
||||
<select class="custom-select select-role" name="role">
|
||||
${ o.allowed_roles.map(role => html`<option value="${role}" ?selected=${role === o.item.role}>${role}</option>`) }
|
||||
</select>
|
||||
</div>
|
||||
<div class="col">
|
||||
<label><strong>${i18n_reason}:</strong></label>
|
||||
<input class="form-control" type="text" name="reason"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="submit" class="btn btn-primary" value="${i18n_change_role}"/>
|
||||
</div>
|
||||
</form>
|
||||
<div><strong>Role:</strong> ${o.item.role} ${o.assignable_roles.length ? html`<a href="#" data-form="role-form" class="toggle-form right fa fa-wrench" @click=${o.toggleForm}></a>` : ''}</div>
|
||||
${o.assignable_roles.length ? tpl_set_role_form(o) : ''}
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
`;
|
||||
|
||||
|
||||
const tpl_set_affiliation_form = (o) => html`
|
||||
<form class="affiliation-form hidden" @submit=${o.assignAffiliation}>
|
||||
<div class="form-group">
|
||||
<input type="hidden" name="jid" value="${o.item.jid}"/>
|
||||
<input type="hidden" name="nick" value="${o.item.nick}"/>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<label><strong>${i18n_new_affiliation}:</strong></label>
|
||||
<select class="custom-select select-affiliation" name="affiliation">
|
||||
${ o.assignable_affiliations.map(aff => html`<option value="${aff}" ?selected=${aff === o.item.affiliation}>${aff}</option>`) }
|
||||
</select>
|
||||
</div>
|
||||
<div class="col">
|
||||
<label><strong>${i18n_reason}:</strong></label>
|
||||
<input class="form-control" type="text" name="reason"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="submit" class="btn btn-primary" name="change" value="${i18n_change_affiliation}"/>
|
||||
</div>
|
||||
</form>
|
||||
`;
|
||||
|
||||
|
||||
const affiliation_list_item = (o) => html`
|
||||
<li class="list-group-item">
|
||||
<ul class="list-group">
|
||||
@ -112,35 +142,30 @@ const affiliation_list_item = (o) => html`
|
||||
<div><strong>Nickname:</strong> ${o.item.nick}</div>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<div><strong>Affiliation:</strong> ${o.item.affiliation} <a href="#" data-form="affiliation-form" class="toggle-form right fa fa-wrench" @click=${o.toggleForm}></a></div>
|
||||
<form class="affiliation-form hidden" @submit=${o.assignAffiliation}>
|
||||
<div class="form-group">
|
||||
<input type="hidden" name="jid" value="${o.item.jid}"/>
|
||||
<input type="hidden" name="nick" value="${o.item.nick}"/>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<label><strong>${i18n_new_affiliation}:</strong></label>
|
||||
<select class="custom-select select-affiliation" name="affiliation">
|
||||
${ o.allowed_affiliations.map(aff => html`<option value="${aff}" ?selected=${aff === o.item.affiliation}>${aff}</option>`) }
|
||||
</select>
|
||||
</div>
|
||||
<div class="col">
|
||||
<label><strong>${i18n_reason}:</strong></label>
|
||||
<input class="form-control" type="text" name="reason"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="submit" class="btn btn-primary" name="change" value="${i18n_change_affiliation}"/>
|
||||
</div>
|
||||
</form>
|
||||
<div><strong>Affiliation:</strong> ${o.item.affiliation} ${o.assignable_affiliations.length ? html`<a href="#" data-form="affiliation-form" class="toggle-form right fa fa-wrench" @click=${o.toggleForm}></a>` : ''}</div>
|
||||
${o.assignable_affiliations.length ? tpl_set_affiliation_form(o) : ''}
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
`;
|
||||
|
||||
|
||||
export default (o) => html`
|
||||
const tpl_navigation = (o) => html`
|
||||
<ul class="nav nav-pills justify-content-center">
|
||||
<li role="presentation" class="nav-item">
|
||||
<a class="nav-link active" id="affiliations-tab" href="#affiliations-tabpanel" aria-controls="affiliations-tabpanel" role="tab" data-toggle="tab" @click=${o.switchTab}>Affiliations</a>
|
||||
</li>
|
||||
<li role="presentation" class="nav-item">
|
||||
<a class="nav-link" id="roles-tab" href="#roles-tabpanel" aria-controls="roles-tabpanel" role="tab" data-toggle="tab" @click=${o.switchTab}>Roles</a>
|
||||
</li>
|
||||
</ul>
|
||||
`;
|
||||
|
||||
|
||||
export default (o) => {
|
||||
|
||||
const show_both_tabs = o.queryable_roles.length && o.queryable_affiliations.length;
|
||||
return html`
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
@ -150,17 +175,11 @@ export default (o) => html`
|
||||
<div class="modal-body d-flex flex-column">
|
||||
<span class="modal-alert"></span>
|
||||
|
||||
<ul class="nav nav-pills justify-content-center">
|
||||
<li role="presentation" class="nav-item">
|
||||
<a class="nav-link active" id="affiliations-tab" href="#affiliations-tabpanel" aria-controls="affiliations-tabpanel" role="tab" data-toggle="tab" @click=${o.switchTab}>Affiliations</a>
|
||||
</li>
|
||||
<li role="presentation" class="nav-item">
|
||||
<a class="nav-link" id="roles-tab" href="#roles-tabpanel" aria-controls="roles-tabpanel" role="tab" data-toggle="tab" @click=${o.switchTab}>Roles</a>
|
||||
</li>
|
||||
</ul>
|
||||
${ show_both_tabs ? tpl_navigation(o) : '' }
|
||||
|
||||
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane tab-pane--columns active" id="affiliations-tabpanel" role="tabpanel" aria-labelledby="affiliations-tab">
|
||||
<div class="tab-pane tab-pane--columns ${ o.queryable_affiliations.length ? 'active' : ''}" id="affiliations-tabpanel" role="tabpanel" aria-labelledby="affiliations-tab">
|
||||
<form class="converse-form query-affiliation" @submit=${o.queryAffiliation}>
|
||||
<p class="helptext pb-3">${i18n_helptext_affiliation}</p>
|
||||
<div class="form-group">
|
||||
@ -170,7 +189,7 @@ export default (o) => html`
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<select class="custom-select select-affiliation" name="affiliation">
|
||||
${o.affiliations.map(item => affiliation_option(Object.assign({item}, o)))}
|
||||
${o.queryable_affiliations.map(item => affiliation_option(Object.assign({item}, o)))}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col">
|
||||
@ -193,7 +212,7 @@ export default (o) => html`
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane tab-pane--columns" id="roles-tabpanel" role="tabpanel" aria-labelledby="roles-tab">
|
||||
<div class="tab-pane tab-pane--columns ${ !show_both_tabs && o.queryable_roles.length ? 'active' : ''}" id="roles-tabpanel" role="tabpanel" aria-labelledby="roles-tab">
|
||||
<form class="converse-form query-role" @submit=${o.queryRole}>
|
||||
<p class="helptext pb-3">${i18n_helptext_role}</p>
|
||||
<div class="form-group">
|
||||
@ -201,7 +220,7 @@ export default (o) => html`
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<select class="custom-select select-role" name="role">
|
||||
${o.roles.map(item => role_option(Object.assign({item}, o)))}
|
||||
${o.queryable_roles.map(item => role_option(Object.assign({item}, o)))}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col">
|
||||
@ -224,5 +243,5 @@ export default (o) => html`
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
</div>`;
|
||||
}
|
||||
|
@ -23,6 +23,8 @@
|
||||
auto_away: 300,
|
||||
auto_register_muc_nickname: true,
|
||||
loglevel: 'debug',
|
||||
modtools_disable_assign: ['owner', 'moderator', 'participant', 'visitor'],
|
||||
modtools_disable_query: ['moderator', 'participant', 'visitor'],
|
||||
enable_smacks: true,
|
||||
i18n: 'en',
|
||||
message_archiving: 'always',
|
||||
|
Loading…
Reference in New Issue
Block a user