Add a new command /modtools
in which you can set user affiliations and roles. Also, let getAffiliationList return an Error instead of `null` if you're not allowed to fetch a particular affiliation list.
This commit is contained in:
parent
a03e722a24
commit
aee6a192d1
|
@ -1,6 +1,11 @@
|
|||
# Changelog
|
||||
|
||||
## 5.0.1 (Unreleased)
|
||||
|
||||
- Add a new GUI for moderator actions. You can trigger it by entering `/modtools` in a MUC.
|
||||
|
||||
## 5.0.0 (2019-08-08)
|
||||
|
||||
- BOSH support has been moved to a plugin.
|
||||
- Support for XEP-0410 to check whether we're still present in a room
|
||||
- Initial support for the [CredentialsContainer](https://developer.mozilla.org/en-US/docs/Web/API/CredentialsContainer) web API
|
||||
|
|
|
@ -259,7 +259,6 @@ body.converse-fullscreen {
|
|||
input[type=text], input[type=password],
|
||||
button {
|
||||
font-size: var(--font-size);
|
||||
padding: 0.25em;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@
|
|||
font-size: var(--font-size);
|
||||
}
|
||||
|
||||
&#converse-register,
|
||||
&#converse-register,
|
||||
&#converse-login {
|
||||
legend {
|
||||
width: 100%;
|
||||
|
@ -95,7 +95,6 @@
|
|||
input[type=submit] {
|
||||
padding-left: 1em;
|
||||
padding-right: 1em;
|
||||
margin: 0.5em 0;
|
||||
border: none;
|
||||
}
|
||||
input.error {
|
||||
|
|
|
@ -1,5 +1,19 @@
|
|||
#conversejs {
|
||||
#converse-modals {
|
||||
|
||||
.modal-body {
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
.scrollable-container {
|
||||
max-height: 50vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.role-form, .affiliation-form {
|
||||
padding: 2em 0 1em 0;
|
||||
}
|
||||
|
||||
.set-xmpp-status {
|
||||
margin: 1em;
|
||||
.custom-control-label {
|
||||
|
@ -43,7 +57,7 @@
|
|||
width: 100%;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
|
||||
.fingerprint-trust {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
|
139
spec/modtools.js
Normal file
139
spec/modtools.js
Normal file
|
@ -0,0 +1,139 @@
|
|||
(function (root, factory) {
|
||||
define(["jasmine", "mock", "test-utils" ], factory);
|
||||
} (this, function (jasmine, mock, test_utils) {
|
||||
const _ = converse.env._;
|
||||
const $iq = converse.env.$iq;
|
||||
const sizzle = converse.env.sizzle;
|
||||
const Strophe = converse.env.Strophe;
|
||||
const u = converse.env.utils;
|
||||
|
||||
describe("The groupchat moderator tool", function () {
|
||||
|
||||
it("allows you to set affiliations and roles",
|
||||
mock.initConverse(
|
||||
null, ['rosterGroupsFetched'], {},
|
||||
async function (done, _converse) {
|
||||
|
||||
spyOn(_converse.ChatRoomView.prototype, 'showModeratorToolsModal').and.callThrough();
|
||||
const muc_jid = 'lounge@montague.lit';
|
||||
|
||||
let members = [
|
||||
{'jid': 'hag66@shakespeare.lit', 'nick': 'witch', 'affiliation': 'member'},
|
||||
{'jid': 'gower@shakespeare.lit', 'nick': 'gower', 'affiliation': 'member'},
|
||||
{'jid': 'wiccarocks@shakespeare.lit', 'nick': 'wiccan', 'affiliation': 'admin'},
|
||||
{'jid': 'crone1@shakespeare.lit', 'nick': 'thirdwitch', 'affiliation': 'owner'},
|
||||
{'jid': 'romeo@montague.lit', 'nick': 'romeo', 'affiliation': 'owner'},
|
||||
];
|
||||
await test_utils.openAndEnterChatRoom(_converse, muc_jid, 'romeo', [], members);
|
||||
const view = _converse.chatboxviews.get(muc_jid);
|
||||
await u.waitUntil(() => (view.model.occupants.length === 5));
|
||||
|
||||
const textarea = view.el.querySelector('.chat-textarea');
|
||||
textarea.value = '/modtools';
|
||||
const enter = { 'target': textarea, 'preventDefault': function preventDefault () {}, 'keyCode': 13 };
|
||||
view.onKeyDown(enter);
|
||||
await u.waitUntil(() => view.showModeratorToolsModal.calls.count());
|
||||
|
||||
const modal = view.modtools_modal;
|
||||
await u.waitUntil(() => u.isVisible(modal.el), 1000);
|
||||
let tab = modal.el.querySelector('#affiliations-tab');
|
||||
// Clear so that we don't match older stanzas
|
||||
_converse.connection.IQ_stanzas = [];
|
||||
tab.click();
|
||||
let select = modal.el.querySelector('.select-affiliation');
|
||||
expect(select.value).toBe('admin');
|
||||
let button = modal.el.querySelector('.btn-primary[name="users_with_affiliation"]');
|
||||
button.click();
|
||||
await u.waitUntil(() => !modal.loading_users_with_affiliation);
|
||||
let user_els = modal.el.querySelectorAll('.list-group--users > li');
|
||||
expect(user_els.length).toBe(1);
|
||||
expect(user_els[0].querySelector('.list-group-item.active').textContent.trim()).toBe('JID: wiccarocks@shakespeare.lit');
|
||||
expect(user_els[0].querySelector('.list-group-item:nth-child(2n)').textContent.trim()).toBe('Nickname: wiccan');
|
||||
expect(user_els[0].querySelector('.list-group-item:nth-child(3n) div').textContent.trim()).toBe('Affiliation: admin');
|
||||
|
||||
_converse.connection.IQ_stanzas = [];
|
||||
select.value = 'owner';
|
||||
button.click();
|
||||
await u.waitUntil(() => !modal.loading_users_with_affiliation);
|
||||
user_els = modal.el.querySelectorAll('.list-group--users > li');
|
||||
expect(user_els.length).toBe(2);
|
||||
expect(user_els[0].querySelector('.list-group-item.active').textContent.trim()).toBe('JID: romeo@montague.lit');
|
||||
expect(user_els[0].querySelector('.list-group-item:nth-child(2n)').textContent.trim()).toBe('Nickname: romeo');
|
||||
expect(user_els[0].querySelector('.list-group-item:nth-child(3n) div').textContent.trim()).toBe('Affiliation: owner');
|
||||
|
||||
expect(user_els[1].querySelector('.list-group-item.active').textContent.trim()).toBe('JID: crone1@shakespeare.lit');
|
||||
expect(user_els[1].querySelector('.list-group-item:nth-child(2n)').textContent.trim()).toBe('Nickname: thirdwitch');
|
||||
expect(user_els[1].querySelector('.list-group-item:nth-child(3n) div').textContent.trim()).toBe('Affiliation: owner');
|
||||
|
||||
const toggle = user_els[1].querySelector('.list-group-item:nth-child(3n) .toggle-form');
|
||||
const form = user_els[1].querySelector('.list-group-item:nth-child(3n) .affiliation-form');
|
||||
expect(u.hasClass('hidden', form)).toBeTruthy();
|
||||
toggle.click();
|
||||
expect(u.hasClass('hidden', form)).toBeFalsy();
|
||||
select = form.querySelector('.select-affiliation');
|
||||
expect(select.value).toBe('owner');
|
||||
select.value = 'admin';
|
||||
const input = form.querySelector('input[name="reason"]');
|
||||
input.value = "You're an admin now";
|
||||
const submit = form.querySelector('.btn-primary');
|
||||
submit.click();
|
||||
|
||||
spyOn(_converse.ChatRoomOccupants.prototype, 'fetchMembers').and.callThrough();
|
||||
const sent_IQ = _converse.connection.IQ_stanzas.pop();
|
||||
expect(Strophe.serialize(sent_IQ)).toBe(
|
||||
`<iq id="${sent_IQ.getAttribute('id')}" to="lounge@montague.lit" type="set" xmlns="jabber:client">`+
|
||||
`<query xmlns="http://jabber.org/protocol/muc#admin">`+
|
||||
`<item affiliation="admin" jid="crone1@shakespeare.lit">`+
|
||||
`<reason>You're an admin now</reason>`+
|
||||
`</item>`+
|
||||
`</query>`+
|
||||
`</iq>`);
|
||||
|
||||
_converse.connection.IQ_stanzas = [];
|
||||
const stanza = $iq({
|
||||
'type': 'result',
|
||||
'id': sent_IQ.getAttribute('id'),
|
||||
'from': view.model.get('jid'),
|
||||
'to': _converse.connection.jid
|
||||
});
|
||||
_converse.connection._dataRecv(test_utils.createRequest(stanza));
|
||||
await u.waitUntil(() => view.model.occupants.fetchMembers.calls.count());
|
||||
|
||||
members = [
|
||||
{'jid': 'hag66@shakespeare.lit', 'nick': 'witch', 'affiliation': 'member'},
|
||||
{'jid': 'gower@shakespeare.lit', 'nick': 'gower', 'affiliation': 'member'},
|
||||
{'jid': 'wiccarocks@shakespeare.lit', 'nick': 'wiccan', 'affiliation': 'admin'},
|
||||
{'jid': 'crone1@shakespeare.lit', 'nick': 'thirdwitch', 'affiliation': 'admin'},
|
||||
{'jid': 'romeo@montague.lit', 'nick': 'romeo', 'affiliation': 'owner'},
|
||||
];
|
||||
await test_utils.returnMemberLists(_converse, muc_jid, members);
|
||||
await u.waitUntil(() => view.model.occupants.pluck('affiliation').filter(o => o === 'owner').length === 1);
|
||||
const alert = modal.el.querySelector('.alert-primary');
|
||||
expect(alert.textContent.trim()).toBe('Affiliation changed');
|
||||
|
||||
user_els = modal.el.querySelectorAll('.list-group--users > li');
|
||||
expect(user_els.length).toBe(1);
|
||||
expect(user_els[0].querySelector('.list-group-item.active').textContent.trim()).toBe('JID: romeo@montague.lit');
|
||||
expect(user_els[0].querySelector('.list-group-item:nth-child(2n)').textContent.trim()).toBe('Nickname: romeo');
|
||||
expect(user_els[0].querySelector('.list-group-item:nth-child(3n) div').textContent.trim()).toBe('Affiliation: owner');
|
||||
|
||||
tab = modal.el.querySelector('#roles-tab');
|
||||
tab.click();
|
||||
select = modal.el.querySelector('.select-role');
|
||||
expect(u.isVisible(select)).toBe(true);
|
||||
expect(select.value).toBe('moderator');
|
||||
button = modal.el.querySelector('.btn-primary[name="users_with_role"]');
|
||||
button.click();
|
||||
|
||||
const roles_panel = modal.el.querySelector('#roles-tabpanel');
|
||||
await u.waitUntil(() => roles_panel.querySelectorAll('.list-group--users > li').length === 1);
|
||||
select.value = 'participant';
|
||||
button.click();
|
||||
await u.waitUntil(() => !modal.loading_users_with_affiliation);
|
||||
user_els = roles_panel.querySelectorAll('.list-group--users > li')
|
||||
expect(user_els.length).toBe(1);
|
||||
expect(user_els[0].textContent.trim()).toBe('No users with that role found.');
|
||||
done();
|
||||
}));
|
||||
});
|
||||
}));
|
20
spec/muc.js
20
spec/muc.js
|
@ -1613,7 +1613,13 @@
|
|||
async function (done, _converse) {
|
||||
|
||||
const muc_jid = 'lounge@montague.lit'
|
||||
await test_utils.openAndEnterChatRoom(_converse, 'lounge@montague.lit', 'romeo', [], ['juliet']);
|
||||
|
||||
const members = [{
|
||||
'nick': 'juliet',
|
||||
'jid': 'juliet@capulet.lit',
|
||||
'affiliation': 'member'
|
||||
}];
|
||||
await test_utils.openAndEnterChatRoom(_converse, muc_jid, 'romeo', [], members);
|
||||
const view = _converse.chatboxviews.get(muc_jid);
|
||||
await u.waitUntil(() => view.model.occupants.length === 2);
|
||||
|
||||
|
@ -2975,7 +2981,7 @@
|
|||
view.onKeyDown(enter);
|
||||
|
||||
let info_messages = Array.prototype.slice.call(view.el.querySelectorAll('.chat-info'), 0);
|
||||
expect(info_messages.length).toBe(19);
|
||||
expect(info_messages.length).toBe(20);
|
||||
expect(info_messages.pop().textContent).toBe('/voice: Allow muted user to post messages');
|
||||
expect(info_messages.pop().textContent).toBe('/topic: Set groupchat subject (alias for /subject)');
|
||||
expect(info_messages.pop().textContent).toBe('/subject: Set groupchat subject');
|
||||
|
@ -2985,6 +2991,7 @@
|
|||
expect(info_messages.pop().textContent).toBe('/op: Grant moderator role to user');
|
||||
expect(info_messages.pop().textContent).toBe('/nick: Change your nickname');
|
||||
expect(info_messages.pop().textContent).toBe('/mute: Remove user\'s ability to post messages');
|
||||
expect(info_messages.pop().textContent).toBe('/modtools: Opens up the moderator tools GUI');
|
||||
expect(info_messages.pop().textContent).toBe('/member: Grant membership to a user');
|
||||
expect(info_messages.pop().textContent).toBe('/me: Write in 3rd person');
|
||||
expect(info_messages.pop().textContent).toBe('/kick: Kick user from groupchat');
|
||||
|
@ -3003,11 +3010,11 @@
|
|||
textarea.value = '/help';
|
||||
view.onKeyDown(enter);
|
||||
info_messages = sizzle('.chat-info', view.el).slice(1);
|
||||
expect(info_messages.length).toBe(17);
|
||||
expect(info_messages.length).toBe(18);
|
||||
let commands = info_messages.map(m => m.textContent.replace(/:.*$/, ''));
|
||||
expect(commands).toEqual([
|
||||
"/admin", "/ban", "/clear", "/deop", "/destroy",
|
||||
"/help", "/kick", "/me", "/member", "/mute", "/nick",
|
||||
"/help", "/kick", "/me", "/member", "/modtools", "/mute", "/nick",
|
||||
"/op", "/register", "/revoke", "/subject", "/topic", "/voice"
|
||||
]);
|
||||
occupant.set('affiliation', 'member');
|
||||
|
@ -3048,7 +3055,7 @@
|
|||
view.onKeyDown(enter);
|
||||
|
||||
const info_messages = Array.prototype.slice.call(view.el.querySelectorAll('.chat-info'), 0);
|
||||
expect(info_messages.length).toBe(17);
|
||||
expect(info_messages.length).toBe(18);
|
||||
expect(info_messages.pop().textContent).toBe('/topic: Set groupchat subject (alias for /subject)');
|
||||
expect(info_messages.pop().textContent).toBe('/subject: Set groupchat subject');
|
||||
expect(info_messages.pop().textContent).toBe('/revoke: Revoke the user\'s current affiliation');
|
||||
|
@ -3056,6 +3063,7 @@
|
|||
expect(info_messages.pop().textContent).toBe('/owner: Grant ownership of this groupchat');
|
||||
expect(info_messages.pop().textContent).toBe('/op: Grant moderator role to user');
|
||||
expect(info_messages.pop().textContent).toBe('/nick: Change your nickname');
|
||||
expect(info_messages.pop().textContent).toBe('/modtools: Opens up the moderator tools GUI');
|
||||
expect(info_messages.pop().textContent).toBe('/member: Grant membership to a user');
|
||||
expect(info_messages.pop().textContent).toBe('/me: Write in 3rd person');
|
||||
expect(info_messages.pop().textContent).toBe('/kick: Kick user from groupchat');
|
||||
|
@ -5366,5 +5374,3 @@
|
|||
});
|
||||
});
|
||||
}));
|
||||
|
||||
|
||||
|
|
|
@ -16,7 +16,6 @@ import BrowserStorage from "backbone.browserStorage";
|
|||
import { Overview } from "backbone.overview";
|
||||
import bootstrap from "bootstrap.native";
|
||||
import converse from "@converse/headless/converse-core";
|
||||
import tpl_alert from "templates/alert.html";
|
||||
import tpl_chatbox from "templates/chatbox.html";
|
||||
import tpl_chatbox_head from "templates/chatbox_head.html";
|
||||
import tpl_chatbox_message_form from "templates/chatbox_message_form.html";
|
||||
|
@ -275,13 +274,7 @@ converse.plugins.add('converse-chatview', {
|
|||
await _converse.api.vcard.update(this.model.contact.vcard, true);
|
||||
} catch (e) {
|
||||
_converse.log(e, Strophe.LogLevel.FATAL);
|
||||
this.el.querySelector('.modal-body').insertAdjacentHTML(
|
||||
'afterBegin',
|
||||
tpl_alert({
|
||||
'type': 'alert-danger',
|
||||
'message': __('Sorry, something went wrong while trying to refresh')
|
||||
})
|
||||
);
|
||||
this.alert(__('Sorry, something went wrong while trying to refresh'), 'danger');
|
||||
}
|
||||
u.removeClass('fa-spin', refresh_icon);
|
||||
},
|
||||
|
|
|
@ -9,9 +9,10 @@
|
|||
import "backbone.vdomview";
|
||||
import bootstrap from "bootstrap.native";
|
||||
import converse from "@converse/headless/converse-core";
|
||||
import tpl_alert from "templates/alert.html";
|
||||
import tpl_alert_modal from "templates/alert_modal.html";
|
||||
|
||||
const { Strophe, Backbone, _ } = converse.env;
|
||||
const { Strophe, Backbone, sizzle, _ } = converse.env;
|
||||
const u = converse.env.utils;
|
||||
|
||||
|
||||
|
@ -22,6 +23,10 @@ converse.plugins.add('converse-modal', {
|
|||
|
||||
_converse.BootstrapModal = Backbone.VDOMView.extend({
|
||||
|
||||
events: {
|
||||
'click .nav-item .nav-link': 'switchTab'
|
||||
},
|
||||
|
||||
initialize () {
|
||||
this.render().insertIntoDOM();
|
||||
this.modal = new bootstrap.Modal(this.el, {
|
||||
|
@ -36,6 +41,33 @@ converse.plugins.add('converse-modal', {
|
|||
container_el.insertAdjacentElement('beforeEnd', this.el);
|
||||
},
|
||||
|
||||
switchTab (ev) {
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
sizzle('.nav-link.active', this.el).forEach(el => {
|
||||
u.removeClass('active', this.el.querySelector(el.getAttribute('href')));
|
||||
u.removeClass('active', el);
|
||||
});
|
||||
u.addClass('active', ev.target);
|
||||
u.addClass('active', this.el.querySelector(ev.target.getAttribute('href')))
|
||||
},
|
||||
|
||||
alert (message, type='primary') {
|
||||
const body = this.el.querySelector('.modal-body');
|
||||
body.insertAdjacentHTML(
|
||||
'afterBegin',
|
||||
tpl_alert({
|
||||
'type': `alert-${type}`,
|
||||
'message': message
|
||||
})
|
||||
);
|
||||
const el = body.firstElementChild;
|
||||
setTimeout(() => {
|
||||
u.addClass('fade-out', el);
|
||||
setTimeout(() => u.removeElement(el), 600);
|
||||
}, 5000);
|
||||
},
|
||||
|
||||
show (ev) {
|
||||
if (ev) {
|
||||
ev.preventDefault();
|
||||
|
|
|
@ -14,6 +14,7 @@ import "backbone.vdomview";
|
|||
import BrowserStorage from "backbone.browserStorage";
|
||||
import { OrderedListView } from "backbone.overview";
|
||||
import _FormData from "formdata-polyfill";
|
||||
import bootstrap from "bootstrap.native";
|
||||
import converse from "@converse/headless/converse-core";
|
||||
import muc_utils from "@converse/headless/utils/muc";
|
||||
import tpl_add_chatroom_modal from "templates/add_chatroom_modal.html";
|
||||
|
@ -32,6 +33,7 @@ import tpl_chatroom_password_form from "templates/chatroom_password_form.html";
|
|||
import tpl_chatroom_sidebar from "templates/chatroom_sidebar.html";
|
||||
import tpl_info from "templates/info.html";
|
||||
import tpl_list_chatrooms_modal from "templates/list_chatrooms_modal.html";
|
||||
import tpl_moderator_tools_modal from "templates/moderator_tools_modal.html";
|
||||
import tpl_occupant from "templates/occupant.html";
|
||||
import tpl_room_description from "templates/room_description.html";
|
||||
import tpl_room_item from "templates/room_item.html";
|
||||
|
@ -43,8 +45,12 @@ import xss from "xss/dist/xss";
|
|||
|
||||
const { Backbone, Promise, Strophe, dayjs, sizzle, _, $iq, $msg, $pres } = converse.env;
|
||||
const u = converse.env.utils;
|
||||
|
||||
const ROLES = ['moderator', 'participant', 'visitor'];
|
||||
const AFFILIATIONS = ['admin', 'member', 'outcast', 'owner'];
|
||||
const AFFILIATION_CHANGE_COMANDS = ['admin', 'ban', 'owner', 'member', 'revoke'];
|
||||
const OWNER_COMMANDS = ['owner'];
|
||||
const ADMIN_COMMANDS = ['admin', 'ban', 'deop', 'destroy', 'member', 'op', 'revoke'];
|
||||
const ADMIN_COMMANDS = ['admin', 'ban', 'deop', 'destroy', 'modtools', 'member', 'op', 'revoke'];
|
||||
const MODERATOR_COMMANDS = ['kick', 'mute', 'voice'];
|
||||
const VISITOR_COMMANDS = ['nick'];
|
||||
|
||||
|
@ -202,6 +208,167 @@ converse.plugins.add('converse-muc-views', {
|
|||
}
|
||||
|
||||
|
||||
_converse.ModeratorToolsModal = _converse.BootstrapModal.extend({
|
||||
|
||||
events: {
|
||||
'submit .affiliation-form': 'assignAffiliation',
|
||||
'submit .role-form': 'assignRole',
|
||||
'submit .query-affiliation': 'queryAffiliation',
|
||||
'submit .query-role': 'queryRole',
|
||||
'click .nav-item .nav-link': 'switchTab',
|
||||
'click .toggle-form': 'toggleForm',
|
||||
},
|
||||
|
||||
initialize (attrs) {
|
||||
this.chatroomview = attrs.chatroomview;
|
||||
_converse.BootstrapModal.prototype.initialize.apply(this, arguments);
|
||||
|
||||
this.model.on('change:role', () => {
|
||||
this.users_with_role = this.getUsersWithRole();
|
||||
this.render();
|
||||
});
|
||||
this.model.on('change:affiliation', async () => {
|
||||
this.loading_users_with_affiliation = true;
|
||||
this.users_with_affiliation = null;
|
||||
this.render();
|
||||
const affiliation = this.model.get('affiliation');
|
||||
if (!_converse.muc_fetch_members || affiliation === 'outcast') {
|
||||
this.users_with_affiliation = await this.chatroomview.model.getAffiliationList(affiliation);
|
||||
} else {
|
||||
this.users_with_affiliation = this.getUsersWithAffiliation();
|
||||
}
|
||||
this.loading_users_with_affiliation = false;
|
||||
this.render();
|
||||
});
|
||||
},
|
||||
|
||||
toHTML () {
|
||||
const allowed_commands = this.chatroomview.getAllowedCommands();
|
||||
const allowed_affiliations = allowed_commands.map(c => COMMAND_TO_AFFILIATION[c]).filter(c => c);
|
||||
const allowed_roles = _.uniq(allowed_commands
|
||||
.map(c => COMMAND_TO_ROLE[c])
|
||||
.filter(c => c));
|
||||
|
||||
allowed_affiliations.sort();
|
||||
allowed_roles.sort();
|
||||
|
||||
return tpl_moderator_tools_modal(Object.assign(this.model.toJSON(), {
|
||||
'__': __,
|
||||
'affiliations': AFFILIATIONS,
|
||||
'allowed_affiliations': allowed_affiliations,
|
||||
'allowed_roles': allowed_roles,
|
||||
'loading_users_with_affiliation': this.loading_users_with_affiliation,
|
||||
'roles': ROLES,
|
||||
'users_with_affiliation': this.users_with_affiliation,
|
||||
'users_with_role': this.users_with_role
|
||||
}));
|
||||
},
|
||||
|
||||
toggleForm (ev) {
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
const form_class = ev.target.getAttribute('data-form');
|
||||
const form = u.ancestor(ev.target, '.list-group-item').querySelector(`.${form_class}`);
|
||||
if (u.hasClass('hidden', form)) {
|
||||
u.removeClass('hidden', form);
|
||||
} else {
|
||||
u.addClass('hidden', form);
|
||||
}
|
||||
},
|
||||
|
||||
getUsersWithAffiliation () {
|
||||
return this.chatroomview.model.occupants
|
||||
.where({'affiliation': this.model.get('affiliation')})
|
||||
.map(item => {
|
||||
return {
|
||||
'jid': item.get('jid'),
|
||||
'nick': item.get('nick'),
|
||||
'affiliation': item.get('affiliation')
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
getUsersWithRole () {
|
||||
return this.chatroomview.model.occupants
|
||||
.where({'role': this.model.get('role')})
|
||||
.map(item => {
|
||||
return {
|
||||
'jid': item.get('jid'),
|
||||
'nick': item.get('nick'),
|
||||
'role': item.get('role')
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
queryRole (ev) {
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
const data = new FormData(ev.target);
|
||||
const role = data.get('role');
|
||||
this.model.set({'role': null}, {'silent': true});
|
||||
this.model.set({'role': role});
|
||||
},
|
||||
|
||||
queryAffiliation (ev) {
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
const data = new FormData(ev.target);
|
||||
const affiliation = data.get('affiliation');
|
||||
this.model.set({'affiliation': null}, {'silent': true});
|
||||
this.model.set({'affiliation': affiliation});
|
||||
},
|
||||
|
||||
assignAffiliation (ev) {
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
const data = new FormData(ev.target);
|
||||
const affiliation = data.get('affiliation');
|
||||
const attrs = {
|
||||
'jid': data.get('jid'),
|
||||
'reason': data.get('reason')
|
||||
}
|
||||
const current_affiliation = this.model.get('affiliation');
|
||||
this.chatroomview.model.setAffiliation(affiliation, [attrs])
|
||||
.then(async () => {
|
||||
this.alert(__('Affiliation changed'), 'primary');
|
||||
await this.chatroomview.model.occupants.fetchMembers()
|
||||
this.model.set({'affiliation': null}, {'silent': true});
|
||||
this.model.set({'affiliation': current_affiliation});
|
||||
})
|
||||
.catch(err => {
|
||||
this.alert(__('Sorry, something went wrong while trying to set the affiliation'), 'danger');
|
||||
_converse.log(err, Strophe.LogLevel.ERROR);
|
||||
});
|
||||
},
|
||||
|
||||
assignRole (ev) {
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
const data = new FormData(ev.target);
|
||||
const jid = data.get('jid');
|
||||
const occupant = this.chatroomview.model.getOccupant(jid);
|
||||
const role = data.get('role');
|
||||
const reason = data.get('reason');
|
||||
const current_role = this.model.get('role');
|
||||
this.chatroomview.model.setRole(occupant, role, reason,
|
||||
() => {
|
||||
this.alert(__('Role changed'), 'primary');
|
||||
this.model.set({'role': null}, {'silent': true});
|
||||
this.model.set({'role': current_role});
|
||||
},
|
||||
(e) => {
|
||||
if (sizzle(`not-allowed[xmlns="${Strophe.NS.STANZAS}"]`, e).length) {
|
||||
this.alert(__('You\'re not allowed to make that change'), 'danger');
|
||||
} else {
|
||||
this.alert(__('Sorry, something went wrong while trying to set the role'), 'danger');
|
||||
}
|
||||
_converse.log(e, Strophe.LogLevel.ERROR);
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
_converse.ListChatRoomsModal = _converse.BootstrapModal.extend({
|
||||
|
||||
events: {
|
||||
|
@ -432,7 +599,6 @@ converse.plugins.add('converse-muc-views', {
|
|||
|
||||
/**
|
||||
* The View of an open/ongoing groupchat conversation
|
||||
*
|
||||
* @class
|
||||
* @namespace _converse.ChatRoomView
|
||||
* @memberOf _converse
|
||||
|
@ -594,6 +760,16 @@ converse.plugins.add('converse-muc-views', {
|
|||
return _converse.ChatBoxView.prototype.onKeyUp.call(this, ev);
|
||||
},
|
||||
|
||||
showModeratorToolsModal (affiliation) {
|
||||
if (_.isUndefined(this.model.modtools_modal)) {
|
||||
const model = new Backbone.Model({'affiliation': affiliation});
|
||||
this.modtools_modal = new _converse.ModeratorToolsModal({'model': model, 'chatroomview': this});
|
||||
} else {
|
||||
this.modtools_modal.set('affiliation', affiliation);
|
||||
}
|
||||
this.modtools_modal.show();
|
||||
},
|
||||
|
||||
showRoomDetailsModal (ev) {
|
||||
ev.preventDefault();
|
||||
if (this.model.room_details_modal === undefined) {
|
||||
|
@ -927,6 +1103,26 @@ converse.plugins.add('converse-muc-views', {
|
|||
this.showErrorMessage(__("Sorry, an error happened while running the command. Check your browser's developer console for details."));
|
||||
},
|
||||
|
||||
getAllowedCommands () {
|
||||
// FIXME: The availability of some of these commands
|
||||
// depend on the MUCs configuration (e.g. whether it's
|
||||
// moderated or not). We need to take that into
|
||||
// consideration.
|
||||
let allowed_commands = ['clear', 'help', 'me', 'nick', 'subject', 'topic', 'register'];
|
||||
const occupant = this.model.occupants.findWhere({'jid': _converse.bare_jid});
|
||||
if (this.verifyAffiliations(['owner'], occupant, false)) {
|
||||
allowed_commands = allowed_commands.concat(OWNER_COMMANDS).concat(ADMIN_COMMANDS);
|
||||
} else if (this.verifyAffiliations(['admin'], occupant, false)) {
|
||||
allowed_commands = allowed_commands.concat(ADMIN_COMMANDS);
|
||||
}
|
||||
if (this.verifyRoles(['moderator'], occupant, false)) {
|
||||
allowed_commands = allowed_commands.concat(MODERATOR_COMMANDS).concat(VISITOR_COMMANDS);
|
||||
} else if (!this.verifyRoles(['visitor', 'participant', 'moderator'], occupant, false)) {
|
||||
allowed_commands = allowed_commands.concat(VISITOR_COMMANDS);
|
||||
}
|
||||
return allowed_commands;
|
||||
},
|
||||
|
||||
parseMessageForCommands (text) {
|
||||
if (_converse.muc_disable_slash_commands && !Array.isArray(_converse.muc_disable_slash_commands)) {
|
||||
return _converse.ChatBoxView.prototype.parseMessageForCommands.apply(this, arguments);
|
||||
|
@ -936,7 +1132,7 @@ converse.plugins.add('converse-muc-views', {
|
|||
if (!command) {
|
||||
return false;
|
||||
}
|
||||
const args = text.slice(('/'+command).length+1);
|
||||
const args = text.slice(('/'+command).length+1).trim();
|
||||
|
||||
let disabled_commands = [];
|
||||
if (Array.isArray(_converse.muc_disable_slash_commands)) {
|
||||
|
@ -955,6 +1151,10 @@ converse.plugins.add('converse-muc-views', {
|
|||
this.setAffiliation(command, args, ['admin', 'owner']);
|
||||
break;
|
||||
}
|
||||
case 'modtools': {
|
||||
this.showModeratorToolsModal(args);
|
||||
break;
|
||||
}
|
||||
case 'deop': {
|
||||
// FIXME: /deop only applies to setting a moderators
|
||||
// role to "participant" (which only admin/owner can
|
||||
|
@ -975,22 +1175,7 @@ converse.plugins.add('converse-muc-views', {
|
|||
break;
|
||||
}
|
||||
case 'help': {
|
||||
// FIXME: The availability of some of these commands
|
||||
// depend on the MUCs configuration (e.g. whether it's
|
||||
// moderated or not). We need to take that into
|
||||
// consideration.
|
||||
let allowed_commands = ['clear', 'help', 'me', 'nick', 'subject', 'topic', 'register'];
|
||||
const occupant = this.model.occupants.findWhere({'jid': _converse.bare_jid});
|
||||
if (this.verifyAffiliations(['owner'], occupant, false)) {
|
||||
allowed_commands = allowed_commands.concat(OWNER_COMMANDS).concat(ADMIN_COMMANDS);
|
||||
} else if (this.verifyAffiliations(['admin'], occupant, false)) {
|
||||
allowed_commands = allowed_commands.concat(ADMIN_COMMANDS);
|
||||
}
|
||||
if (this.verifyRoles(['moderator'], occupant, false)) {
|
||||
allowed_commands = allowed_commands.concat(MODERATOR_COMMANDS).concat(VISITOR_COMMANDS);
|
||||
} else if (!this.verifyRoles(['visitor', 'participant', 'moderator'], occupant, false)) {
|
||||
allowed_commands = allowed_commands.concat(VISITOR_COMMANDS);
|
||||
}
|
||||
const allowed_commands = this.getAllowedCommands();
|
||||
this.showHelpMessages([`<strong>${__("You can run the following commands")}</strong>`]);
|
||||
this.showHelpMessages([
|
||||
`<strong>/admin</strong>: ${__("Change user's affiliation to admin")}`,
|
||||
|
@ -1002,6 +1187,7 @@ converse.plugins.add('converse-muc-views', {
|
|||
`<strong>/kick</strong>: ${__('Kick user from groupchat')}`,
|
||||
`<strong>/me</strong>: ${__('Write in 3rd person')}`,
|
||||
`<strong>/member</strong>: ${__('Grant membership to a user')}`,
|
||||
`<strong>/modtools</strong>: ${__('Opens up the moderator tools GUI')}`,
|
||||
`<strong>/mute</strong>: ${__("Remove user's ability to post messages")}`,
|
||||
`<strong>/nick</strong>: ${__('Change your nickname')}`,
|
||||
`<strong>/op</strong>: ${__('Grant moderator role to user')}`,
|
||||
|
|
|
@ -1112,10 +1112,11 @@ converse.plugins.add('converse-muc', {
|
|||
.c("item", {'affiliation': affiliation});
|
||||
const result = await _converse.api.sendIQ(iq, null, false);
|
||||
if (result.getAttribute('type') === 'error') {
|
||||
const err_msg = `Not allowed to fetch ${affiliation} list for MUC ${this.get('jid')}`;
|
||||
const err_msg = `Error: not allowed to fetch ${affiliation} list for MUC ${this.get('jid')}`;
|
||||
const err = new Error(err_msg);
|
||||
_converse.log(err_msg, Strophe.LogLevel.WARN);
|
||||
_converse.log(result, Strophe.LogLevel.WARN);
|
||||
return null;
|
||||
return err;
|
||||
}
|
||||
return u.parseMemberListIQ(result).filter(p => p);
|
||||
},
|
||||
|
@ -1136,8 +1137,8 @@ converse.plugins.add('converse-muc', {
|
|||
async updateMemberLists (members) {
|
||||
const all_affiliations = ['member', 'admin', 'owner'];
|
||||
const aff_lists = await Promise.all(all_affiliations.map(a => this.getAffiliationList(a)));
|
||||
const known_affiliations = all_affiliations.filter(a => aff_lists[all_affiliations.indexOf(a)] !== null);
|
||||
const old_members = aff_lists.reduce((acc, val) => (val !== null ? [...val, ...acc] : acc), []);
|
||||
const known_affiliations = all_affiliations.filter(a => !u.isErrorObject(aff_lists[all_affiliations.indexOf(a)]));
|
||||
const old_members = aff_lists.reduce((acc, val) => (u.isErrorObject(val) ? acc: [...val, ...acc]), []);
|
||||
await this.setAffiliations(u.computeAffiliationsDelta(true, false, members, old_members));
|
||||
if (_converse.muc_fetch_members) {
|
||||
return this.occupants.fetchMembers();
|
||||
|
@ -1911,8 +1912,8 @@ converse.plugins.add('converse-muc', {
|
|||
async fetchMembers () {
|
||||
const all_affiliations = ['member', 'admin', 'owner'];
|
||||
const aff_lists = await Promise.all(all_affiliations.map(a => this.chatroom.getAffiliationList(a)));
|
||||
const new_members = aff_lists.reduce((acc, val) => (val !== null ? [...val, ...acc] : acc), []);
|
||||
const known_affiliations = all_affiliations.filter(a => aff_lists[all_affiliations.indexOf(a)] !== null);
|
||||
const new_members = aff_lists.reduce((acc, val) => (u.isErrorObject(val) ? acc : [...val, ...acc]), []);
|
||||
const known_affiliations = all_affiliations.filter(a => !u.isErrorObject(aff_lists[all_affiliations.indexOf(a)]));
|
||||
const new_jids = new_members.map(m => m.jid).filter(m => m !== undefined);
|
||||
const new_nicks = new_members.map(m => !m.jid && m.nick || undefined).filter(m => m !== undefined);
|
||||
const removed_members = this.filter(m => {
|
||||
|
|
|
@ -158,6 +158,10 @@ u.isHeadlineMessage = function (_converse, message) {
|
|||
return false;
|
||||
};
|
||||
|
||||
u.isErrorObject = function (o) {
|
||||
return o instanceof Error;
|
||||
}
|
||||
|
||||
|
||||
u.isForbiddenError = function (stanza) {
|
||||
if (!_.isElement(stanza)) {
|
||||
|
|
168
src/templates/moderator_tools_modal.html
Normal file
168
src/templates/moderator_tools_modal.html
Normal file
|
@ -0,0 +1,168 @@
|
|||
<div class="modal" id="list-chatrooms-modal" tabindex="-1" role="dialog" aria-labelledby="list-chatrooms-modal-label" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"
|
||||
id="list-chatrooms-modal-label">{{{o.__('Moderator Tools')}}}</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body d-flex flex-column">
|
||||
<ul class="nav nav-pills justify-content-center">
|
||||
<li role="presentation" class="nav-item">
|
||||
<a class="nav-link active" id="roles-tab" href="#roles-tabpanel" aria-controls="roles-tabpanel" role="tab" data-toggle="tab">Roles</a>
|
||||
</li>
|
||||
<li role="presentation" class="nav-item">
|
||||
<a class="nav-link" id="affiliations-tab" href="#affiliations-tabpanel" aria-controls="affiliations-tabpanel" role="tab" data-toggle="tab">Affiliations</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane active" id="roles-tabpanel" role="tabpanel" aria-labelledby="roles-tab">
|
||||
<form class="converse-form query-role">
|
||||
<div class="form-group">
|
||||
<label for="role">
|
||||
<strong>{{{o.__('Role')}}}:</strong>
|
||||
</label>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<select class="custom-select select-role" name="role">
|
||||
{[ o.roles.forEach(function (role) { ]}
|
||||
<option value="{{{role}}}" {[ if (role === o.role) { ]} selected="selected" {[ } ]}>{{{role}}}</option>
|
||||
{[ }); ]}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col">
|
||||
<input type="submit" class="btn btn-primary" name="users_with_role" value="{{{o.__('Show users')}}}"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div class="scrollable-container">
|
||||
<ul class="list-group list-group--users">
|
||||
{[ if (o.loading_users_with_role) { ]}
|
||||
<li class="list-group-item"> <span class="spinner fa fa-spinner centered"/> </li>
|
||||
{[ } ]}
|
||||
{[ if (o.users_with_role && o.users_with_role.length === 0) { ]}
|
||||
<li class="list-group-item">{{{o.__('No users with that role found.')}}}</li>
|
||||
{[ } ]}
|
||||
{[ (o.users_with_role || []).forEach(function (item) { ]}
|
||||
<li class="list-group-item">
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item active">
|
||||
<div><strong>JID:</strong> {{{item.jid}}}</div>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<div><strong>Nickname:</strong> {{{item.nick}}}</div>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<div><strong>Role:</strong> {{{item.role}}}<a href="#" data-form="role-form" class="toggle-form right fa fa-wrench"></a></div>
|
||||
<form class="role-form hidden">
|
||||
<div class="form-group">
|
||||
<input type="hidden" name="jid" value="{{{item.jid}}}"/>
|
||||
<input type="hidden" name="nick" value="{{{item.nick}}}"/>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<label><strong>{{{o.__('New Role')}}}:</strong></label>
|
||||
<select class="custom-select select-role" name="role">
|
||||
{[ o.allowed_roles.forEach(function (role) { ]}
|
||||
<option value="{{{role}}}" {[ if (role === item.role) { ]} selected="selected" {[ } ]}>{{{role}}}</option>
|
||||
{[ }); ]}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col">
|
||||
<label><strong>{{{o.__('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="{{{o.__('Change role')}}}"/>
|
||||
</div>
|
||||
</form>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
{[ }); ]}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="tab-pane" id="affiliations-tabpanel" role="tabpanel" aria-labelledby="affiliations-tab">
|
||||
<form class="converse-form query-affiliation">
|
||||
<div class="form-group">
|
||||
<label for="affiliation">
|
||||
<strong>{{{o.__('Affiliation')}}}:</strong>
|
||||
</label>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<select class="custom-select select-affiliation" name="affiliation">
|
||||
{[ o.affiliations.forEach(function (aff) { ]}
|
||||
<option value="{{{aff}}}" {[ if (aff === o.affiliation) { ]} selected="selected" {[ } ]}>{{{aff}}}</option>
|
||||
{[ }); ]}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col">
|
||||
<input type="submit" class="btn btn-primary" name="users_with_affiliation" value="{{{o.__('Show users')}}}"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<ul class="list-group list-group--users">
|
||||
{[ if (o.loading_users_with_affiliation) { ]}
|
||||
<li class="list-group-item"> <span class="spinner fa fa-spinner centered"/> </li>
|
||||
{[ } else { ]}
|
||||
{[ if (o.users_with_affiliation && o.users_with_affiliation.length === 0) { ]}
|
||||
<li class="list-group-item">{{{o.__('No users with that affiliation found.')}}}</li>
|
||||
{[ } ]}
|
||||
{[ if (o.users_with_affiliation instanceof Error) { ]}
|
||||
<li class="list-group-item">{{{o.users_with_affiliation.message}}}</li>
|
||||
{[ } ]}
|
||||
{[ (o.users_with_affiliation || []).forEach(function (item) { ]}
|
||||
<li class="list-group-item">
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item active">
|
||||
<div><strong>JID:</strong> {{{item.jid}}}</div>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<div><strong>Nickname:</strong> {{{item.nick}}}</div>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<div><strong>Affiliation:</strong> {{{item.affiliation}}} <a href="#" data-form="affiliation-form" class="toggle-form right fa fa-wrench"></a></div>
|
||||
<form class="affiliation-form hidden">
|
||||
<div class="form-group">
|
||||
<input type="hidden" name="jid" value="{{{item.jid}}}"/>
|
||||
<input type="hidden" name="nick" value="{{{item.nick}}}"/>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<label><strong>{{{o.__('New affiliation')}}}:</strong></label>
|
||||
<select class="custom-select select-affiliation" name="affiliation">
|
||||
{[ o.allowed_affiliations.forEach(function (aff) { ]}
|
||||
<option value="{{{aff}}}" {[ if (aff === item.affiliation) { ]} selected="selected" {[ } ]}>{{{aff}}}</option>
|
||||
{[ }); ]}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col">
|
||||
<label><strong>{{{o.__('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="{{{o.__('Change affiliation')}}}"/>
|
||||
</div>
|
||||
</form>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
{[ }); ]}
|
||||
{[ } ]}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -55,6 +55,7 @@ var specs = [
|
|||
"spec/user-details-modal",
|
||||
"spec/messages",
|
||||
"spec/muc",
|
||||
"spec/modtools",
|
||||
"spec/room_registration",
|
||||
"spec/autocomplete",
|
||||
"spec/minchats",
|
||||
|
|
102
tests/utils.js
102
tests/utils.js
|
@ -213,51 +213,71 @@
|
|||
};
|
||||
|
||||
|
||||
utils.returnMemberLists = async function (_converse, muc_jid, members=[]) {
|
||||
utils.returnMemberLists = async function (_converse, muc_jid, members=[], affiliations=['member', 'owner', 'admin']) {
|
||||
const stanzas = _converse.connection.IQ_stanzas;
|
||||
const member_IQ = await u.waitUntil(() => _.filter(
|
||||
stanzas,
|
||||
s => sizzle(`iq[to="${muc_jid}"] query[xmlns="${Strophe.NS.MUC_ADMIN}"] item[affiliation="member"]`, s).length
|
||||
).pop());
|
||||
const member_list_stanza = $iq({
|
||||
'from': 'coven@chat.shakespeare.lit',
|
||||
'id': member_IQ.getAttribute('id'),
|
||||
'to': 'romeo@montague.lit/orchard',
|
||||
'type': 'result'
|
||||
}).c('query', {'xmlns': Strophe.NS.MUC_ADMIN});
|
||||
members.forEach(member => {
|
||||
member_list_stanza.c('item', {
|
||||
'affiliation': 'member',
|
||||
'jid': 'hag66@shakespeare.lit',
|
||||
'nick': member,
|
||||
'role': 'participant'
|
||||
|
||||
if (affiliations.includes('member')) {
|
||||
const member_IQ = await u.waitUntil(() => _.filter(
|
||||
stanzas,
|
||||
s => sizzle(`iq[to="${muc_jid}"] query[xmlns="${Strophe.NS.MUC_ADMIN}"] item[affiliation="member"]`, s).length
|
||||
).pop());
|
||||
const member_list_stanza = $iq({
|
||||
'from': 'coven@chat.shakespeare.lit',
|
||||
'id': member_IQ.getAttribute('id'),
|
||||
'to': 'romeo@montague.lit/orchard',
|
||||
'type': 'result'
|
||||
}).c('query', {'xmlns': Strophe.NS.MUC_ADMIN});
|
||||
members.filter(m => m.affiliation === 'member').forEach(m => {
|
||||
member_list_stanza.c('item', {
|
||||
'affiliation': m.affiliation,
|
||||
'jid': m.jid,
|
||||
'nick': m.nick
|
||||
});
|
||||
});
|
||||
});
|
||||
_converse.connection._dataRecv(utils.createRequest(member_list_stanza));
|
||||
_converse.connection._dataRecv(utils.createRequest(member_list_stanza));
|
||||
}
|
||||
|
||||
const admin_IQ = await u.waitUntil(() => _.filter(
|
||||
stanzas,
|
||||
s => sizzle(`iq[to="${muc_jid}"] query[xmlns="${Strophe.NS.MUC_ADMIN}"] item[affiliation="admin"]`, s).length
|
||||
).pop());
|
||||
const admin_list_stanza = $iq({
|
||||
'from': 'coven@chat.shakespeare.lit',
|
||||
'id': admin_IQ.getAttribute('id'),
|
||||
'to': 'romeo@montague.lit/orchard',
|
||||
'type': 'result'
|
||||
}).c('query', {'xmlns': Strophe.NS.MUC_ADMIN});
|
||||
_converse.connection._dataRecv(utils.createRequest(admin_list_stanza));
|
||||
if (affiliations.includes('admin')) {
|
||||
const admin_IQ = await u.waitUntil(() => _.filter(
|
||||
stanzas,
|
||||
s => sizzle(`iq[to="${muc_jid}"] query[xmlns="${Strophe.NS.MUC_ADMIN}"] item[affiliation="admin"]`, s).length
|
||||
).pop());
|
||||
const admin_list_stanza = $iq({
|
||||
'from': 'coven@chat.shakespeare.lit',
|
||||
'id': admin_IQ.getAttribute('id'),
|
||||
'to': 'romeo@montague.lit/orchard',
|
||||
'type': 'result'
|
||||
}).c('query', {'xmlns': Strophe.NS.MUC_ADMIN});
|
||||
members.filter(m => m.affiliation === 'admin').forEach(m => {
|
||||
admin_list_stanza.c('item', {
|
||||
'affiliation': m.affiliation,
|
||||
'jid': m.jid,
|
||||
'nick': m.nick
|
||||
});
|
||||
});
|
||||
_converse.connection._dataRecv(utils.createRequest(admin_list_stanza));
|
||||
}
|
||||
|
||||
const owner_IQ = await u.waitUntil(() => _.filter(
|
||||
stanzas,
|
||||
s => sizzle(`iq[to="${muc_jid}"] query[xmlns="${Strophe.NS.MUC_ADMIN}"] item[affiliation="owner"]`, s).length
|
||||
).pop());
|
||||
const owner_list_stanza = $iq({
|
||||
'from': 'coven@chat.shakespeare.lit',
|
||||
'id': owner_IQ.getAttribute('id'),
|
||||
'to': 'romeo@montague.lit/orchard',
|
||||
'type': 'result'
|
||||
}).c('query', {'xmlns': Strophe.NS.MUC_ADMIN});
|
||||
_converse.connection._dataRecv(utils.createRequest(owner_list_stanza));
|
||||
if (affiliations.includes('owner')) {
|
||||
const owner_IQ = await u.waitUntil(() => _.filter(
|
||||
stanzas,
|
||||
s => sizzle(`iq[to="${muc_jid}"] query[xmlns="${Strophe.NS.MUC_ADMIN}"] item[affiliation="owner"]`, s).length
|
||||
).pop());
|
||||
const owner_list_stanza = $iq({
|
||||
'from': 'coven@chat.shakespeare.lit',
|
||||
'id': owner_IQ.getAttribute('id'),
|
||||
'to': 'romeo@montague.lit/orchard',
|
||||
'type': 'result'
|
||||
}).c('query', {'xmlns': Strophe.NS.MUC_ADMIN});
|
||||
members.filter(m => m.affiliation === 'owner').forEach(m => {
|
||||
owner_list_stanza.c('item', {
|
||||
'affiliation': m.affiliation,
|
||||
'jid': m.jid,
|
||||
'nick': m.nick
|
||||
});
|
||||
});
|
||||
_converse.connection._dataRecv(utils.createRequest(owner_list_stanza));
|
||||
}
|
||||
};
|
||||
|
||||
utils.receiveOwnMUCPresence = function (_converse, muc_jid, nick) {
|
||||
|
|
Loading…
Reference in New Issue
Block a user