Move various funcitons related to MUC member lists to utils
and out of the MUC views plugin. Refs #1032
This commit is contained in:
parent
06141b3212
commit
ebfd0a8f77
@ -882,7 +882,7 @@
|
|||||||
_converse.api.rooms.open('coven@chat.shakespeare.lit', {'nick': 'some1'});
|
_converse.api.rooms.open('coven@chat.shakespeare.lit', {'nick': 'some1'});
|
||||||
view = _converse.chatboxviews.get('coven@chat.shakespeare.lit');
|
view = _converse.chatboxviews.get('coven@chat.shakespeare.lit');
|
||||||
|
|
||||||
spyOn(view, 'saveAffiliationAndRole').and.callThrough();
|
spyOn(view.model, 'saveAffiliationAndRole').and.callThrough();
|
||||||
|
|
||||||
// We pretend this is a new room, so no disco info is returned.
|
// We pretend this is a new room, so no disco info is returned.
|
||||||
var features_stanza = $iq({
|
var features_stanza = $iq({
|
||||||
@ -913,7 +913,7 @@
|
|||||||
}).up()
|
}).up()
|
||||||
.c('status', {code: '110'});
|
.c('status', {code: '110'});
|
||||||
_converse.connection._dataRecv(test_utils.createRequest(presence));
|
_converse.connection._dataRecv(test_utils.createRequest(presence));
|
||||||
expect(view.saveAffiliationAndRole).toHaveBeenCalled();
|
expect(view.model.saveAffiliationAndRole).toHaveBeenCalled();
|
||||||
expect($(view.el.querySelector('.toggle-chatbox-button')).is(':visible')).toBeTruthy();
|
expect($(view.el.querySelector('.toggle-chatbox-button')).is(':visible')).toBeTruthy();
|
||||||
|
|
||||||
test_utils.waitUntil(function () {
|
test_utils.waitUntil(function () {
|
||||||
@ -1348,7 +1348,7 @@
|
|||||||
// receiving the features for the room.
|
// receiving the features for the room.
|
||||||
view.model.set('open', 'true');
|
view.model.set('open', 'true');
|
||||||
|
|
||||||
spyOn(view, 'directInvite').and.callThrough();
|
spyOn(view.model, 'directInvite').and.callThrough();
|
||||||
var $input;
|
var $input;
|
||||||
$(view.el).find('.chat-area').remove();
|
$(view.el).find('.chat-area').remove();
|
||||||
|
|
||||||
@ -1376,7 +1376,7 @@
|
|||||||
evt.button = 0; // For some reason awesomplete wants this
|
evt.button = 0; // For some reason awesomplete wants this
|
||||||
$hint[0].dispatchEvent(evt);
|
$hint[0].dispatchEvent(evt);
|
||||||
expect(window.prompt).toHaveBeenCalled();
|
expect(window.prompt).toHaveBeenCalled();
|
||||||
expect(view.directInvite).toHaveBeenCalled();
|
expect(view.model.directInvite).toHaveBeenCalled();
|
||||||
expect(sent_stanza.toLocaleString()).toBe(
|
expect(sent_stanza.toLocaleString()).toBe(
|
||||||
"<message from='dummy@localhost/resource' to='felix.amsel@localhost' id='" +
|
"<message from='dummy@localhost/resource' to='felix.amsel@localhost' id='" +
|
||||||
sent_stanza.nodeTree.getAttribute('id') +
|
sent_stanza.nodeTree.getAttribute('id') +
|
||||||
@ -2140,7 +2140,7 @@
|
|||||||
});
|
});
|
||||||
var view = _converse.chatboxviews.get('lounge@localhost');
|
var view = _converse.chatboxviews.get('lounge@localhost');
|
||||||
spyOn(view, 'onMessageSubmitted').and.callThrough();
|
spyOn(view, 'onMessageSubmitted').and.callThrough();
|
||||||
spyOn(view, 'setAffiliation').and.callThrough();
|
spyOn(view.model, 'setAffiliation').and.callThrough();
|
||||||
spyOn(view, 'showStatusNotification').and.callThrough();
|
spyOn(view, 'showStatusNotification').and.callThrough();
|
||||||
spyOn(view, 'validateRoleChangeCommand').and.callThrough();
|
spyOn(view, 'validateRoleChangeCommand').and.callThrough();
|
||||||
var textarea = view.el.querySelector('.chat-textarea')
|
var textarea = view.el.querySelector('.chat-textarea')
|
||||||
@ -2156,7 +2156,7 @@
|
|||||||
"Error: the \"owner\" command takes two arguments, the user's nickname and optionally a reason.",
|
"Error: the \"owner\" command takes two arguments, the user's nickname and optionally a reason.",
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
expect(view.setAffiliation).not.toHaveBeenCalled();
|
expect(view.model.setAffiliation).not.toHaveBeenCalled();
|
||||||
|
|
||||||
// Call now with the correct amount of arguments.
|
// Call now with the correct amount of arguments.
|
||||||
// XXX: Calling onMessageSubmitted directly, trying
|
// XXX: Calling onMessageSubmitted directly, trying
|
||||||
@ -2165,7 +2165,7 @@
|
|||||||
view.onMessageSubmitted('/owner annoyingGuy@localhost You\'re responsible');
|
view.onMessageSubmitted('/owner annoyingGuy@localhost You\'re responsible');
|
||||||
expect(view.validateRoleChangeCommand.calls.count()).toBe(2);
|
expect(view.validateRoleChangeCommand.calls.count()).toBe(2);
|
||||||
expect(view.showStatusNotification.calls.count()).toBe(1);
|
expect(view.showStatusNotification.calls.count()).toBe(1);
|
||||||
expect(view.setAffiliation).toHaveBeenCalled();
|
expect(view.model.setAffiliation).toHaveBeenCalled();
|
||||||
// Check that the member list now gets updated
|
// Check that the member list now gets updated
|
||||||
expect(sent_IQ.toLocaleString()).toBe(
|
expect(sent_IQ.toLocaleString()).toBe(
|
||||||
"<iq to='lounge@localhost' type='set' xmlns='jabber:client' id='"+IQ_id+"'>"+
|
"<iq to='lounge@localhost' type='set' xmlns='jabber:client' id='"+IQ_id+"'>"+
|
||||||
@ -2193,7 +2193,7 @@
|
|||||||
});
|
});
|
||||||
var view = _converse.chatboxviews.get('lounge@localhost');
|
var view = _converse.chatboxviews.get('lounge@localhost');
|
||||||
spyOn(view, 'onMessageSubmitted').and.callThrough();
|
spyOn(view, 'onMessageSubmitted').and.callThrough();
|
||||||
spyOn(view, 'setAffiliation').and.callThrough();
|
spyOn(view.model, 'setAffiliation').and.callThrough();
|
||||||
spyOn(view, 'showStatusNotification').and.callThrough();
|
spyOn(view, 'showStatusNotification').and.callThrough();
|
||||||
spyOn(view, 'validateRoleChangeCommand').and.callThrough();
|
spyOn(view, 'validateRoleChangeCommand').and.callThrough();
|
||||||
var textarea = view.el.querySelector('.chat-textarea')
|
var textarea = view.el.querySelector('.chat-textarea')
|
||||||
@ -2209,7 +2209,7 @@
|
|||||||
"Error: the \"ban\" command takes two arguments, the user's nickname and optionally a reason.",
|
"Error: the \"ban\" command takes two arguments, the user's nickname and optionally a reason.",
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
expect(view.setAffiliation).not.toHaveBeenCalled();
|
expect(view.model.setAffiliation).not.toHaveBeenCalled();
|
||||||
// Call now with the correct amount of arguments.
|
// Call now with the correct amount of arguments.
|
||||||
// XXX: Calling onMessageSubmitted directly, trying
|
// XXX: Calling onMessageSubmitted directly, trying
|
||||||
// again via triggering Event doesn't work for some weird
|
// again via triggering Event doesn't work for some weird
|
||||||
@ -2217,7 +2217,7 @@
|
|||||||
view.onMessageSubmitted('/ban annoyingGuy@localhost You\'re annoying');
|
view.onMessageSubmitted('/ban annoyingGuy@localhost You\'re annoying');
|
||||||
expect(view.validateRoleChangeCommand.calls.count()).toBe(2);
|
expect(view.validateRoleChangeCommand.calls.count()).toBe(2);
|
||||||
expect(view.showStatusNotification.calls.count()).toBe(1);
|
expect(view.showStatusNotification.calls.count()).toBe(1);
|
||||||
expect(view.setAffiliation).toHaveBeenCalled();
|
expect(view.model.setAffiliation).toHaveBeenCalled();
|
||||||
// Check that the member list now gets updated
|
// Check that the member list now gets updated
|
||||||
expect(sent_IQ.toLocaleString()).toBe(
|
expect(sent_IQ.toLocaleString()).toBe(
|
||||||
"<iq to='lounge@localhost' type='set' xmlns='jabber:client' id='"+IQ_id+"'>"+
|
"<iq to='lounge@localhost' type='set' xmlns='jabber:client' id='"+IQ_id+"'>"+
|
||||||
@ -2902,7 +2902,7 @@
|
|||||||
var name = mock.cur_names[0];
|
var name = mock.cur_names[0];
|
||||||
var invitee_jid = name.replace(/ /g,'.').toLowerCase() + '@localhost';
|
var invitee_jid = name.replace(/ /g,'.').toLowerCase() + '@localhost';
|
||||||
var reason = "Please join this chat room";
|
var reason = "Please join this chat room";
|
||||||
view.directInvite(invitee_jid, reason);
|
view.model.directInvite(invitee_jid, reason);
|
||||||
|
|
||||||
// Check in reverse order that we requested all three lists
|
// Check in reverse order that we requested all three lists
|
||||||
// (member, owner and admin).
|
// (member, owner and admin).
|
||||||
@ -3023,26 +3023,26 @@
|
|||||||
var remove_absentees = false;
|
var remove_absentees = false;
|
||||||
var new_list = [];
|
var new_list = [];
|
||||||
var old_list = [];
|
var old_list = [];
|
||||||
var delta = roomview.computeAffiliationsDelta(exclude_existing, remove_absentees, new_list, old_list);
|
var delta = u.computeAffiliationsDelta(exclude_existing, remove_absentees, new_list, old_list);
|
||||||
expect(delta.length).toBe(0);
|
expect(delta.length).toBe(0);
|
||||||
|
|
||||||
new_list = [{'jid': 'wiccarocks@shakespeare.lit', 'affiliation': 'member'}];
|
new_list = [{'jid': 'wiccarocks@shakespeare.lit', 'affiliation': 'member'}];
|
||||||
old_list = [{'jid': 'wiccarocks@shakespeare.lit', 'affiliation': 'member'}];
|
old_list = [{'jid': 'wiccarocks@shakespeare.lit', 'affiliation': 'member'}];
|
||||||
delta = roomview.computeAffiliationsDelta(exclude_existing, remove_absentees, new_list, old_list);
|
delta = u.computeAffiliationsDelta(exclude_existing, remove_absentees, new_list, old_list);
|
||||||
expect(delta.length).toBe(0);
|
expect(delta.length).toBe(0);
|
||||||
|
|
||||||
// When remove_absentees is false, then affiliations in the old
|
// When remove_absentees is false, then affiliations in the old
|
||||||
// list which are not in the new one won't be removed.
|
// list which are not in the new one won't be removed.
|
||||||
old_list = [{'jid': 'oldhag666@shakespeare.lit', 'affiliation': 'owner'},
|
old_list = [{'jid': 'oldhag666@shakespeare.lit', 'affiliation': 'owner'},
|
||||||
{'jid': 'wiccarocks@shakespeare.lit', 'affiliation': 'member'}];
|
{'jid': 'wiccarocks@shakespeare.lit', 'affiliation': 'member'}];
|
||||||
delta = roomview.computeAffiliationsDelta(exclude_existing, remove_absentees, new_list, old_list);
|
delta = u.computeAffiliationsDelta(exclude_existing, remove_absentees, new_list, old_list);
|
||||||
expect(delta.length).toBe(0);
|
expect(delta.length).toBe(0);
|
||||||
|
|
||||||
// With exclude_existing set to false, any changed affiliations
|
// With exclude_existing set to false, any changed affiliations
|
||||||
// will be included in the delta (i.e. existing affiliations
|
// will be included in the delta (i.e. existing affiliations
|
||||||
// are included in the comparison).
|
// are included in the comparison).
|
||||||
old_list = [{'jid': 'wiccarocks@shakespeare.lit', 'affiliation': 'owner'}];
|
old_list = [{'jid': 'wiccarocks@shakespeare.lit', 'affiliation': 'owner'}];
|
||||||
delta = roomview.computeAffiliationsDelta(exclude_existing, remove_absentees, new_list, old_list);
|
delta = u.computeAffiliationsDelta(exclude_existing, remove_absentees, new_list, old_list);
|
||||||
expect(delta.length).toBe(1);
|
expect(delta.length).toBe(1);
|
||||||
expect(delta[0].jid).toBe('wiccarocks@shakespeare.lit');
|
expect(delta[0].jid).toBe('wiccarocks@shakespeare.lit');
|
||||||
expect(delta[0].affiliation).toBe('member');
|
expect(delta[0].affiliation).toBe('member');
|
||||||
@ -3052,12 +3052,12 @@
|
|||||||
remove_absentees = true;
|
remove_absentees = true;
|
||||||
old_list = [{'jid': 'oldhag666@shakespeare.lit', 'affiliation': 'owner'},
|
old_list = [{'jid': 'oldhag666@shakespeare.lit', 'affiliation': 'owner'},
|
||||||
{'jid': 'wiccarocks@shakespeare.lit', 'affiliation': 'member'}];
|
{'jid': 'wiccarocks@shakespeare.lit', 'affiliation': 'member'}];
|
||||||
delta = roomview.computeAffiliationsDelta(exclude_existing, remove_absentees, new_list, old_list);
|
delta = u.computeAffiliationsDelta(exclude_existing, remove_absentees, new_list, old_list);
|
||||||
expect(delta.length).toBe(1);
|
expect(delta.length).toBe(1);
|
||||||
expect(delta[0].jid).toBe('oldhag666@shakespeare.lit');
|
expect(delta[0].jid).toBe('oldhag666@shakespeare.lit');
|
||||||
expect(delta[0].affiliation).toBe('none');
|
expect(delta[0].affiliation).toBe('none');
|
||||||
|
|
||||||
delta = roomview.computeAffiliationsDelta(exclude_existing, remove_absentees, [], old_list);
|
delta = u.computeAffiliationsDelta(exclude_existing, remove_absentees, [], old_list);
|
||||||
expect(delta.length).toBe(2);
|
expect(delta.length).toBe(2);
|
||||||
expect(delta[0].jid).toBe('oldhag666@shakespeare.lit');
|
expect(delta[0].jid).toBe('oldhag666@shakespeare.lit');
|
||||||
expect(delta[0].affiliation).toBe('none');
|
expect(delta[0].affiliation).toBe('none');
|
||||||
@ -3068,7 +3068,7 @@
|
|||||||
// affiliation, we set 'exclude_existing' to true
|
// affiliation, we set 'exclude_existing' to true
|
||||||
exclude_existing = true;
|
exclude_existing = true;
|
||||||
old_list = [{'jid': 'wiccarocks@shakespeare.lit', 'affiliation': 'owner'}];
|
old_list = [{'jid': 'wiccarocks@shakespeare.lit', 'affiliation': 'owner'}];
|
||||||
delta = roomview.computeAffiliationsDelta(exclude_existing, remove_absentees, new_list, old_list);
|
delta = u.computeAffiliationsDelta(exclude_existing, remove_absentees, new_list, old_list);
|
||||||
expect(delta.length).toBe(0);
|
expect(delta.length).toBe(0);
|
||||||
done();
|
done();
|
||||||
}));
|
}));
|
||||||
|
@ -29,7 +29,7 @@ require.config({
|
|||||||
"emojione": "node_modules/emojione/lib/js/emojione",
|
"emojione": "node_modules/emojione/lib/js/emojione",
|
||||||
"es6-promise": "node_modules/es6-promise/dist/es6-promise.auto",
|
"es6-promise": "node_modules/es6-promise/dist/es6-promise.auto",
|
||||||
"eventemitter": "node_modules/otr/build/dep/eventemitter",
|
"eventemitter": "node_modules/otr/build/dep/eventemitter",
|
||||||
"form-utils": "src/form-utils",
|
"form-utils": "src/utils/form",
|
||||||
"i18n": "src/i18n",
|
"i18n": "src/i18n",
|
||||||
"jed": "node_modules/jed/jed",
|
"jed": "node_modules/jed/jed",
|
||||||
"jquery": "src/jquery-stub",
|
"jquery": "src/jquery-stub",
|
||||||
@ -37,19 +37,10 @@ require.config({
|
|||||||
"lodash.converter": "3rdparty/lodash.fp",
|
"lodash.converter": "3rdparty/lodash.fp",
|
||||||
"lodash.fp": "src/lodash.fp",
|
"lodash.fp": "src/lodash.fp",
|
||||||
"lodash.noconflict": "src/lodash.noconflict",
|
"lodash.noconflict": "src/lodash.noconflict",
|
||||||
|
"muc-utils": "src/utils/muc",
|
||||||
"pluggable": "node_modules/pluggable.js/dist/pluggable",
|
"pluggable": "node_modules/pluggable.js/dist/pluggable",
|
||||||
"polyfill": "src/polyfill",
|
"polyfill": "src/polyfill",
|
||||||
"sizzle": "node_modules/sizzle/dist/sizzle",
|
"sizzle": "node_modules/sizzle/dist/sizzle",
|
||||||
"strophe": "node_modules/strophe.js/strophe",
|
|
||||||
"strophe.disco": "node_modules/strophejs-plugin-disco/strophe.disco",
|
|
||||||
"strophe.ping": "node_modules/strophejs-plugin-ping/strophe.ping",
|
|
||||||
"strophe.rsm": "node_modules/strophejs-plugin-rsm/strophe.rsm",
|
|
||||||
"strophe.vcard": "node_modules/strophejs-plugin-vcard/strophe.vcard",
|
|
||||||
"text": "node_modules/text/text",
|
|
||||||
"tpl": "node_modules/lodash-template-loader/loader",
|
|
||||||
"underscore": "src/underscore-shim",
|
|
||||||
"utils": "src/utils",
|
|
||||||
"vdom-parser": "node_modules/vdom-parser/dist",
|
|
||||||
"snabbdom": "node_modules/snabbdom/dist/snabbdom",
|
"snabbdom": "node_modules/snabbdom/dist/snabbdom",
|
||||||
"snabbdom-attributes": "node_modules/snabbdom/dist/snabbdom-attributes",
|
"snabbdom-attributes": "node_modules/snabbdom/dist/snabbdom-attributes",
|
||||||
"snabbdom-class": "node_modules/snabbdom/dist/snabbdom-class",
|
"snabbdom-class": "node_modules/snabbdom/dist/snabbdom-class",
|
||||||
@ -57,7 +48,17 @@ require.config({
|
|||||||
"snabbdom-eventlisteners": "node_modules/snabbdom/dist/snabbdom-eventlisteners",
|
"snabbdom-eventlisteners": "node_modules/snabbdom/dist/snabbdom-eventlisteners",
|
||||||
"snabbdom-props": "node_modules/snabbdom/dist/snabbdom-props",
|
"snabbdom-props": "node_modules/snabbdom/dist/snabbdom-props",
|
||||||
"snabbdom-style": "node_modules/snabbdom/dist/snabbdom-style",
|
"snabbdom-style": "node_modules/snabbdom/dist/snabbdom-style",
|
||||||
|
"strophe": "node_modules/strophe.js/strophe",
|
||||||
|
"strophe.disco": "node_modules/strophejs-plugin-disco/strophe.disco",
|
||||||
|
"strophe.ping": "node_modules/strophejs-plugin-ping/strophe.ping",
|
||||||
|
"strophe.rsm": "node_modules/strophejs-plugin-rsm/strophe.rsm",
|
||||||
|
"strophe.vcard": "node_modules/strophejs-plugin-vcard/strophe.vcard",
|
||||||
|
"text": "node_modules/text/text",
|
||||||
"tovnode": "node_modules/snabbdom/dist/tovnode",
|
"tovnode": "node_modules/snabbdom/dist/tovnode",
|
||||||
|
"tpl": "node_modules/lodash-template-loader/loader",
|
||||||
|
"underscore": "src/underscore-shim",
|
||||||
|
"utils": "src/utils/core",
|
||||||
|
"vdom-parser": "node_modules/vdom-parser/dist",
|
||||||
"xss": "node_modules/xss/dist/xss",
|
"xss": "node_modules/xss/dist/xss",
|
||||||
"xss.noconflict": "src/xss.noconflict",
|
"xss.noconflict": "src/xss.noconflict",
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
(function (root, factory) {
|
(function (root, factory) {
|
||||||
define([
|
define([
|
||||||
"converse-core",
|
"converse-core",
|
||||||
|
"muc-utils",
|
||||||
"emojione",
|
"emojione",
|
||||||
"tpl!add_chatroom_modal",
|
"tpl!add_chatroom_modal",
|
||||||
"tpl!chatarea",
|
"tpl!chatarea",
|
||||||
@ -37,6 +38,7 @@
|
|||||||
], factory);
|
], factory);
|
||||||
}(this, function (
|
}(this, function (
|
||||||
converse,
|
converse,
|
||||||
|
muc_utils,
|
||||||
emojione,
|
emojione,
|
||||||
tpl_add_chatroom_modal,
|
tpl_add_chatroom_modal,
|
||||||
tpl_chatarea,
|
tpl_chatarea,
|
||||||
@ -627,269 +629,6 @@
|
|||||||
this.insertIntoTextArea(ev.target.textContent);
|
this.insertIntoTextArea(ev.target.textContent);
|
||||||
},
|
},
|
||||||
|
|
||||||
requestMemberList (chatroom_jid, affiliation) {
|
|
||||||
/* Send an IQ stanza to the server, asking it for the
|
|
||||||
* member-list of this room.
|
|
||||||
*
|
|
||||||
* See: http://xmpp.org/extensions/xep-0045.html#modifymember
|
|
||||||
*
|
|
||||||
* Parameters:
|
|
||||||
* (String) chatroom_jid: The JID of the chatroom for
|
|
||||||
* which the member-list is being requested
|
|
||||||
* (String) affiliation: The specific member list to
|
|
||||||
* fetch. 'admin', 'owner' or 'member'.
|
|
||||||
*
|
|
||||||
* Returns:
|
|
||||||
* A promise which resolves once the list has been
|
|
||||||
* retrieved.
|
|
||||||
*/
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
affiliation = affiliation || 'member';
|
|
||||||
const iq = $iq({to: chatroom_jid, type: "get"})
|
|
||||||
.c("query", {xmlns: Strophe.NS.MUC_ADMIN})
|
|
||||||
.c("item", {'affiliation': affiliation});
|
|
||||||
_converse.connection.sendIQ(iq, resolve, reject);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
parseMemberListIQ (iq) {
|
|
||||||
/* Given an IQ stanza with a member list, create an array of member
|
|
||||||
* objects.
|
|
||||||
*/
|
|
||||||
return _.map(
|
|
||||||
sizzle(`query[xmlns="${Strophe.NS.MUC_ADMIN}"] item`, iq),
|
|
||||||
(item) => ({
|
|
||||||
'jid': item.getAttribute('jid'),
|
|
||||||
'affiliation': item.getAttribute('affiliation'),
|
|
||||||
})
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
computeAffiliationsDelta (exclude_existing, remove_absentees, new_list, old_list) {
|
|
||||||
/* Given two lists of objects with 'jid', 'affiliation' and
|
|
||||||
* 'reason' properties, return a new list containing
|
|
||||||
* those objects that are new, changed or removed
|
|
||||||
* (depending on the 'remove_absentees' boolean).
|
|
||||||
*
|
|
||||||
* The affiliations for new and changed members stay the
|
|
||||||
* same, for removed members, the affiliation is set to 'none'.
|
|
||||||
*
|
|
||||||
* The 'reason' property is not taken into account when
|
|
||||||
* comparing whether affiliations have been changed.
|
|
||||||
*
|
|
||||||
* Parameters:
|
|
||||||
* (Boolean) exclude_existing: Indicates whether JIDs from
|
|
||||||
* the new list which are also in the old list
|
|
||||||
* (regardless of affiliation) should be excluded
|
|
||||||
* from the delta. One reason to do this
|
|
||||||
* would be when you want to add a JID only if it
|
|
||||||
* doesn't have *any* existing affiliation at all.
|
|
||||||
* (Boolean) remove_absentees: Indicates whether JIDs
|
|
||||||
* from the old list which are not in the new list
|
|
||||||
* should be considered removed and therefore be
|
|
||||||
* included in the delta with affiliation set
|
|
||||||
* to 'none'.
|
|
||||||
* (Array) new_list: Array containing the new affiliations
|
|
||||||
* (Array) old_list: Array containing the old affiliations
|
|
||||||
*/
|
|
||||||
const new_jids = _.map(new_list, 'jid');
|
|
||||||
const old_jids = _.map(old_list, 'jid');
|
|
||||||
|
|
||||||
// Get the new affiliations
|
|
||||||
let delta = _.map(
|
|
||||||
_.difference(new_jids, old_jids),
|
|
||||||
(jid) => new_list[_.indexOf(new_jids, jid)]
|
|
||||||
);
|
|
||||||
if (!exclude_existing) {
|
|
||||||
// Get the changed affiliations
|
|
||||||
delta = delta.concat(_.filter(new_list, function (item) {
|
|
||||||
const idx = _.indexOf(old_jids, item.jid);
|
|
||||||
if (idx >= 0) {
|
|
||||||
return item.affiliation !== old_list[idx].affiliation;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
if (remove_absentees) {
|
|
||||||
// Get the removed affiliations
|
|
||||||
delta = delta.concat(
|
|
||||||
_.map(
|
|
||||||
_.difference(old_jids, new_jids),
|
|
||||||
(jid) => ({'jid': jid, 'affiliation': 'none'})
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return delta;
|
|
||||||
},
|
|
||||||
|
|
||||||
sendAffiliationIQ (chatroom_jid, affiliation, member) {
|
|
||||||
/* Send an IQ stanza specifying an affiliation change.
|
|
||||||
*
|
|
||||||
* Paremeters:
|
|
||||||
* (String) chatroom_jid: JID of the relevant room
|
|
||||||
* (String) affiliation: affiliation (could also be stored
|
|
||||||
* on the member object).
|
|
||||||
* (Object) member: Map containing the member's jid and
|
|
||||||
* optionally a reason and affiliation.
|
|
||||||
*/
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const iq = $iq({to: chatroom_jid, type: "set"})
|
|
||||||
.c("query", {xmlns: Strophe.NS.MUC_ADMIN})
|
|
||||||
.c("item", {
|
|
||||||
'affiliation': member.affiliation || affiliation,
|
|
||||||
'jid': member.jid
|
|
||||||
});
|
|
||||||
if (!_.isUndefined(member.reason)) {
|
|
||||||
iq.c("reason", member.reason);
|
|
||||||
}
|
|
||||||
_converse.connection.sendIQ(iq, resolve, reject);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
setAffiliation (affiliation, members) {
|
|
||||||
/* Send IQ stanzas to the server to set an affiliation for
|
|
||||||
* the provided JIDs.
|
|
||||||
*
|
|
||||||
* See: http://xmpp.org/extensions/xep-0045.html#modifymember
|
|
||||||
*
|
|
||||||
* XXX: Prosody doesn't accept multiple JIDs' affiliations
|
|
||||||
* being set in one IQ stanza, so as a workaround we send
|
|
||||||
* a separate stanza for each JID.
|
|
||||||
* Related ticket: https://prosody.im/issues/issue/795
|
|
||||||
*
|
|
||||||
* Parameters:
|
|
||||||
* (String) affiliation: The affiliation
|
|
||||||
* (Object) members: A map of jids, affiliations and
|
|
||||||
* optionally reasons. Only those entries with the
|
|
||||||
* same affiliation as being currently set will be
|
|
||||||
* considered.
|
|
||||||
*
|
|
||||||
* Returns:
|
|
||||||
* A promise which resolves and fails depending on the
|
|
||||||
* XMPP server response.
|
|
||||||
*/
|
|
||||||
members = _.filter(members, (member) =>
|
|
||||||
// We only want those members who have the right
|
|
||||||
// affiliation (or none, which implies the provided
|
|
||||||
// one).
|
|
||||||
_.isUndefined(member.affiliation) ||
|
|
||||||
member.affiliation === affiliation
|
|
||||||
);
|
|
||||||
const promises = _.map(
|
|
||||||
members,
|
|
||||||
_.partial(this.sendAffiliationIQ, this.model.get('jid'), affiliation)
|
|
||||||
);
|
|
||||||
return Promise.all(promises);
|
|
||||||
},
|
|
||||||
|
|
||||||
setAffiliations (members) {
|
|
||||||
/* Send IQ stanzas to the server to modify the
|
|
||||||
* affiliations in this room.
|
|
||||||
*
|
|
||||||
* See: http://xmpp.org/extensions/xep-0045.html#modifymember
|
|
||||||
*
|
|
||||||
* Parameters:
|
|
||||||
* (Object) members: A map of jids, affiliations and optionally reasons
|
|
||||||
* (Function) onSuccess: callback for a succesful response
|
|
||||||
* (Function) onError: callback for an error response
|
|
||||||
*/
|
|
||||||
const affiliations = _.uniq(_.map(members, 'affiliation'));
|
|
||||||
_.each(affiliations, _.partial(this.setAffiliation.bind(this), _, members));
|
|
||||||
},
|
|
||||||
|
|
||||||
marshallAffiliationIQs () {
|
|
||||||
/* Marshall a list of IQ stanzas into a map of JIDs and
|
|
||||||
* affiliations.
|
|
||||||
*
|
|
||||||
* Parameters:
|
|
||||||
* Any amount of XMLElement objects, representing the IQ
|
|
||||||
* stanzas.
|
|
||||||
*/
|
|
||||||
return _.flatMap(arguments[0], this.parseMemberListIQ);
|
|
||||||
},
|
|
||||||
|
|
||||||
getJidsWithAffiliations (affiliations) {
|
|
||||||
/* Returns a map of JIDs that have the affiliations
|
|
||||||
* as provided.
|
|
||||||
*/
|
|
||||||
if (_.isString(affiliations)) {
|
|
||||||
affiliations = [affiliations];
|
|
||||||
}
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const promises = _.map(
|
|
||||||
affiliations,
|
|
||||||
_.partial(this.requestMemberList, this.model.get('jid'))
|
|
||||||
);
|
|
||||||
|
|
||||||
Promise.all(promises).then(
|
|
||||||
_.flow(this.marshallAffiliationIQs.bind(this), resolve),
|
|
||||||
_.flow(this.marshallAffiliationIQs.bind(this), resolve)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
updateMemberLists (members, affiliations, deltaFunc) {
|
|
||||||
/* Fetch the lists of users with the given affiliations.
|
|
||||||
* Then compute the delta between those users and
|
|
||||||
* the passed in members, and if it exists, send the delta
|
|
||||||
* to the XMPP server to update the member list.
|
|
||||||
*
|
|
||||||
* Parameters:
|
|
||||||
* (Object) members: Map of member jids and affiliations.
|
|
||||||
* (String|Array) affiliation: An array of affiliations or
|
|
||||||
* a string if only one affiliation.
|
|
||||||
* (Function) deltaFunc: The function to compute the delta
|
|
||||||
* between old and new member lists.
|
|
||||||
*
|
|
||||||
* Returns:
|
|
||||||
* A promise which is resolved once the list has been
|
|
||||||
* updated or once it's been established there's no need
|
|
||||||
* to update the list.
|
|
||||||
*/
|
|
||||||
this.getJidsWithAffiliations(affiliations).then((old_members) => {
|
|
||||||
this.setAffiliations(deltaFunc(members, old_members));
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
directInvite (recipient, reason) {
|
|
||||||
/* Send a direct invitation as per XEP-0249
|
|
||||||
*
|
|
||||||
* Parameters:
|
|
||||||
* (String) recipient - JID of the person being invited
|
|
||||||
* (String) reason - Optional reason for the invitation
|
|
||||||
*/
|
|
||||||
if (this.model.get('membersonly')) {
|
|
||||||
// When inviting to a members-only room, we first add
|
|
||||||
// the person to the member list by giving them an
|
|
||||||
// affiliation of 'member' (if they're not affiliated
|
|
||||||
// already), otherwise they won't be able to join.
|
|
||||||
const map = {}; map[recipient] = 'member';
|
|
||||||
const deltaFunc = _.partial(this.computeAffiliationsDelta, true, false);
|
|
||||||
this.updateMemberLists(
|
|
||||||
[{'jid': recipient, 'affiliation': 'member', 'reason': reason}],
|
|
||||||
['member', 'owner', 'admin'],
|
|
||||||
deltaFunc
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const attrs = {
|
|
||||||
'xmlns': 'jabber:x:conference',
|
|
||||||
'jid': this.model.get('jid')
|
|
||||||
};
|
|
||||||
if (reason !== null) { attrs.reason = reason; }
|
|
||||||
if (this.model.get('password')) { attrs.password = this.model.get('password'); }
|
|
||||||
const invitation = $msg({
|
|
||||||
from: _converse.connection.jid,
|
|
||||||
to: recipient,
|
|
||||||
id: _converse.connection.getUniqueId()
|
|
||||||
}).c('x', attrs);
|
|
||||||
_converse.connection.send(invitation);
|
|
||||||
_converse.emit('roomInviteSent', {
|
|
||||||
'room': this,
|
|
||||||
'recipient': recipient,
|
|
||||||
'reason': reason
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
handleChatStateMessage (message) {
|
handleChatStateMessage (message) {
|
||||||
/* Override the method on the ChatBoxView base class to
|
/* Override the method on the ChatBoxView base class to
|
||||||
* ignore <gone/> notifications in groupchats.
|
* ignore <gone/> notifications in groupchats.
|
||||||
@ -1009,14 +748,14 @@
|
|||||||
switch (command) {
|
switch (command) {
|
||||||
case 'admin':
|
case 'admin':
|
||||||
if (!this.validateRoleChangeCommand(command, args)) { break; }
|
if (!this.validateRoleChangeCommand(command, args)) { break; }
|
||||||
this.setAffiliation('admin',
|
this.model.setAffiliation('admin',
|
||||||
[{ 'jid': args[0],
|
[{ 'jid': args[0],
|
||||||
'reason': args[1]
|
'reason': args[1]
|
||||||
}]).then(null, this.onCommandError.bind(this));
|
}]).then(null, this.onCommandError.bind(this));
|
||||||
break;
|
break;
|
||||||
case 'ban':
|
case 'ban':
|
||||||
if (!this.validateRoleChangeCommand(command, args)) { break; }
|
if (!this.validateRoleChangeCommand(command, args)) { break; }
|
||||||
this.setAffiliation('outcast',
|
this.model.setAffiliation('outcast',
|
||||||
[{ 'jid': args[0],
|
[{ 'jid': args[0],
|
||||||
'reason': args[1]
|
'reason': args[1]
|
||||||
}]).then(null, this.onCommandError.bind(this));
|
}]).then(null, this.onCommandError.bind(this));
|
||||||
@ -1064,7 +803,7 @@
|
|||||||
break;
|
break;
|
||||||
case 'member':
|
case 'member':
|
||||||
if (!this.validateRoleChangeCommand(command, args)) { break; }
|
if (!this.validateRoleChangeCommand(command, args)) { break; }
|
||||||
this.setAffiliation('member',
|
this.model.setAffiliation('member',
|
||||||
[{ 'jid': args[0],
|
[{ 'jid': args[0],
|
||||||
'reason': args[1]
|
'reason': args[1]
|
||||||
}]).then(null, this.onCommandError.bind(this));
|
}]).then(null, this.onCommandError.bind(this));
|
||||||
@ -1078,7 +817,7 @@
|
|||||||
break;
|
break;
|
||||||
case 'owner':
|
case 'owner':
|
||||||
if (!this.validateRoleChangeCommand(command, args)) { break; }
|
if (!this.validateRoleChangeCommand(command, args)) { break; }
|
||||||
this.setAffiliation('owner',
|
this.model.setAffiliation('owner',
|
||||||
[{ 'jid': args[0],
|
[{ 'jid': args[0],
|
||||||
'reason': args[1]
|
'reason': args[1]
|
||||||
}]).then(null, this.onCommandError.bind(this));
|
}]).then(null, this.onCommandError.bind(this));
|
||||||
@ -1091,7 +830,7 @@
|
|||||||
break;
|
break;
|
||||||
case 'revoke':
|
case 'revoke':
|
||||||
if (!this.validateRoleChangeCommand(command, args)) { break; }
|
if (!this.validateRoleChangeCommand(command, args)) { break; }
|
||||||
this.setAffiliation('none',
|
this.model.setAffiliation('none',
|
||||||
[{ 'jid': args[0],
|
[{ 'jid': args[0],
|
||||||
'reason': args[1]
|
'reason': args[1]
|
||||||
}]).then(null, this.onCommandError.bind(this));
|
}]).then(null, this.onCommandError.bind(this));
|
||||||
@ -1640,27 +1379,6 @@
|
|||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
|
|
||||||
saveAffiliationAndRole (pres) {
|
|
||||||
/* Parse the presence stanza for the current user's
|
|
||||||
* affiliation.
|
|
||||||
*
|
|
||||||
* Parameters:
|
|
||||||
* (XMLElement) pres: A <presence> stanza.
|
|
||||||
*/
|
|
||||||
const item = sizzle(`x[xmlns="${Strophe.NS.MUC_USER}"] item`, pres).pop();
|
|
||||||
const is_self = pres.querySelector("status[code='110']");
|
|
||||||
if (is_self && !_.isNil(item)) {
|
|
||||||
const affiliation = item.getAttribute('affiliation');
|
|
||||||
const role = item.getAttribute('role');
|
|
||||||
if (affiliation) {
|
|
||||||
this.model.save({'affiliation': affiliation});
|
|
||||||
}
|
|
||||||
if (role) {
|
|
||||||
this.model.save({'role': role});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
parseXUserElement (x, stanza, is_self) {
|
parseXUserElement (x, stanza, is_self) {
|
||||||
/* Parse the passed-in <x xmlns='http://jabber.org/protocol/muc#user'>
|
/* Parse the passed-in <x xmlns='http://jabber.org/protocol/muc#user'>
|
||||||
* element and construct a map containing relevant
|
* element and construct a map containing relevant
|
||||||
@ -1944,7 +1662,7 @@
|
|||||||
* Parameters:
|
* Parameters:
|
||||||
* (XMLElement) pres: The stanza
|
* (XMLElement) pres: The stanza
|
||||||
*/
|
*/
|
||||||
this.saveAffiliationAndRole(pres);
|
this.model.saveAffiliationAndRole(pres);
|
||||||
|
|
||||||
const locked_room = pres.querySelector("status[code='201']");
|
const locked_room = pres.querySelector("status[code='201']");
|
||||||
if (locked_room) {
|
if (locked_room) {
|
||||||
@ -2409,7 +2127,7 @@
|
|||||||
suggestion.text.label, this.model.get('id'))
|
suggestion.text.label, this.model.get('id'))
|
||||||
);
|
);
|
||||||
if (reason !== null) {
|
if (reason !== null) {
|
||||||
this.chatroomview.directInvite(suggestion.text.value, reason);
|
this.chatroomview.model.directInvite(suggestion.text.value, reason);
|
||||||
}
|
}
|
||||||
const form = suggestion.target.form,
|
const form = suggestion.target.form,
|
||||||
error = form.querySelector('.pure-form-message.error');
|
error = form.querySelector('.pure-form-message.error');
|
||||||
|
@ -17,7 +17,8 @@
|
|||||||
"converse-disco",
|
"converse-disco",
|
||||||
"backbone.overview",
|
"backbone.overview",
|
||||||
"backbone.orderedlistview",
|
"backbone.orderedlistview",
|
||||||
"backbone.vdomview"
|
"backbone.vdomview",
|
||||||
|
"muc-utils"
|
||||||
], factory);
|
], factory);
|
||||||
}(this, function (u, converse) {
|
}(this, function (u, converse) {
|
||||||
"use strict";
|
"use strict";
|
||||||
@ -56,6 +57,7 @@
|
|||||||
ENTERED: 5
|
ENTERED: 5
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
converse.plugins.add('converse-muc', {
|
converse.plugins.add('converse-muc', {
|
||||||
/* Optional dependencies are other plugins which might be
|
/* Optional dependencies are other plugins which might be
|
||||||
* overridden or relied upon, and therefore need to be loaded before
|
* overridden or relied upon, and therefore need to be loaded before
|
||||||
@ -273,6 +275,46 @@
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
directInvite (recipient, reason) {
|
||||||
|
/* Send a direct invitation as per XEP-0249
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* (String) recipient - JID of the person being invited
|
||||||
|
* (String) reason - Optional reason for the invitation
|
||||||
|
*/
|
||||||
|
if (this.get('membersonly')) {
|
||||||
|
// When inviting to a members-only room, we first add
|
||||||
|
// the person to the member list by giving them an
|
||||||
|
// affiliation of 'member' (if they're not affiliated
|
||||||
|
// already), otherwise they won't be able to join.
|
||||||
|
const map = {}; map[recipient] = 'member';
|
||||||
|
const deltaFunc = _.partial(u.computeAffiliationsDelta, true, false);
|
||||||
|
this.updateMemberLists(
|
||||||
|
[{'jid': recipient, 'affiliation': 'member', 'reason': reason}],
|
||||||
|
['member', 'owner', 'admin'],
|
||||||
|
deltaFunc
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const attrs = {
|
||||||
|
'xmlns': 'jabber:x:conference',
|
||||||
|
'jid': this.get('jid')
|
||||||
|
};
|
||||||
|
if (reason !== null) { attrs.reason = reason; }
|
||||||
|
if (this.get('password')) { attrs.password = this.get('password'); }
|
||||||
|
const invitation = $msg({
|
||||||
|
from: _converse.connection.jid,
|
||||||
|
to: recipient,
|
||||||
|
id: _converse.connection.getUniqueId()
|
||||||
|
}).c('x', attrs);
|
||||||
|
_converse.connection.send(invitation);
|
||||||
|
_converse.emit('roomInviteSent', {
|
||||||
|
'room': this,
|
||||||
|
'recipient': recipient,
|
||||||
|
'reason': reason
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
sendConfiguration (config, callback, errback) {
|
sendConfiguration (config, callback, errback) {
|
||||||
/* Send an IQ stanza with the room configuration.
|
/* Send an IQ stanza with the room configuration.
|
||||||
*
|
*
|
||||||
@ -335,6 +377,162 @@
|
|||||||
this.save(features);
|
this.save(features);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
requestMemberList (affiliation) {
|
||||||
|
/* Send an IQ stanza to the server, asking it for the
|
||||||
|
* member-list of this room.
|
||||||
|
*
|
||||||
|
* See: http://xmpp.org/extensions/xep-0045.html#modifymember
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* (String) affiliation: The specific member list to
|
||||||
|
* fetch. 'admin', 'owner' or 'member'.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* A promise which resolves once the list has been
|
||||||
|
* retrieved.
|
||||||
|
*/
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
affiliation = affiliation || 'member';
|
||||||
|
const iq = $iq({to: this.get('jid'), type: "get"})
|
||||||
|
.c("query", {xmlns: Strophe.NS.MUC_ADMIN})
|
||||||
|
.c("item", {'affiliation': affiliation});
|
||||||
|
_converse.connection.sendIQ(iq, resolve, reject);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
setAffiliation (affiliation, members) {
|
||||||
|
/* Send IQ stanzas to the server to set an affiliation for
|
||||||
|
* the provided JIDs.
|
||||||
|
*
|
||||||
|
* See: http://xmpp.org/extensions/xep-0045.html#modifymember
|
||||||
|
*
|
||||||
|
* XXX: Prosody doesn't accept multiple JIDs' affiliations
|
||||||
|
* being set in one IQ stanza, so as a workaround we send
|
||||||
|
* a separate stanza for each JID.
|
||||||
|
* Related ticket: https://prosody.im/issues/issue/795
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* (String) affiliation: The affiliation
|
||||||
|
* (Object) members: A map of jids, affiliations and
|
||||||
|
* optionally reasons. Only those entries with the
|
||||||
|
* same affiliation as being currently set will be
|
||||||
|
* considered.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* A promise which resolves and fails depending on the
|
||||||
|
* XMPP server response.
|
||||||
|
*/
|
||||||
|
members = _.filter(members, (member) =>
|
||||||
|
// We only want those members who have the right
|
||||||
|
// affiliation (or none, which implies the provided one).
|
||||||
|
_.isUndefined(member.affiliation) ||
|
||||||
|
member.affiliation === affiliation
|
||||||
|
);
|
||||||
|
const promises = _.map(members, _.bind(this.sendAffiliationIQ, this, affiliation));
|
||||||
|
return Promise.all(promises);
|
||||||
|
},
|
||||||
|
|
||||||
|
saveAffiliationAndRole (pres) {
|
||||||
|
/* Parse the presence stanza for the current user's
|
||||||
|
* affiliation.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* (XMLElement) pres: A <presence> stanza.
|
||||||
|
*/
|
||||||
|
const item = sizzle(`x[xmlns="${Strophe.NS.MUC_USER}"] item`, pres).pop();
|
||||||
|
const is_self = pres.querySelector("status[code='110']");
|
||||||
|
if (is_self && !_.isNil(item)) {
|
||||||
|
const affiliation = item.getAttribute('affiliation');
|
||||||
|
const role = item.getAttribute('role');
|
||||||
|
if (affiliation) {
|
||||||
|
this.save({'affiliation': affiliation});
|
||||||
|
}
|
||||||
|
if (role) {
|
||||||
|
this.save({'role': role});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
sendAffiliationIQ (affiliation, member) {
|
||||||
|
/* Send an IQ stanza specifying an affiliation change.
|
||||||
|
*
|
||||||
|
* Paremeters:
|
||||||
|
* (String) affiliation: affiliation (could also be stored
|
||||||
|
* on the member object).
|
||||||
|
* (Object) member: Map containing the member's jid and
|
||||||
|
* optionally a reason and affiliation.
|
||||||
|
*/
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const iq = $iq({to: this.get('jid'), type: "set"})
|
||||||
|
.c("query", {xmlns: Strophe.NS.MUC_ADMIN})
|
||||||
|
.c("item", {
|
||||||
|
'affiliation': member.affiliation || affiliation,
|
||||||
|
'jid': member.jid
|
||||||
|
});
|
||||||
|
if (!_.isUndefined(member.reason)) {
|
||||||
|
iq.c("reason", member.reason);
|
||||||
|
}
|
||||||
|
_converse.connection.sendIQ(iq, resolve, reject);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
setAffiliations (members) {
|
||||||
|
/* Send IQ stanzas to the server to modify the
|
||||||
|
* affiliations in this room.
|
||||||
|
*
|
||||||
|
* See: http://xmpp.org/extensions/xep-0045.html#modifymember
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* (Object) members: A map of jids, affiliations and optionally reasons
|
||||||
|
* (Function) onSuccess: callback for a succesful response
|
||||||
|
* (Function) onError: callback for an error response
|
||||||
|
*/
|
||||||
|
const affiliations = _.uniq(_.map(members, 'affiliation'));
|
||||||
|
_.each(affiliations, _.partial(this.setAffiliation.bind(this), _, members));
|
||||||
|
},
|
||||||
|
|
||||||
|
getJidsWithAffiliations (affiliations) {
|
||||||
|
/* Returns a map of JIDs that have the affiliations
|
||||||
|
* as provided.
|
||||||
|
*/
|
||||||
|
if (_.isString(affiliations)) {
|
||||||
|
affiliations = [affiliations];
|
||||||
|
}
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const promises = _.map(
|
||||||
|
affiliations,
|
||||||
|
_.partial(this.requestMemberList.bind(this))
|
||||||
|
);
|
||||||
|
Promise.all(promises).then(
|
||||||
|
_.flow(u.marshallAffiliationIQs, resolve),
|
||||||
|
_.flow(u.marshallAffiliationIQs, resolve)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
updateMemberLists (members, affiliations, deltaFunc) {
|
||||||
|
/* Fetch the lists of users with the given affiliations.
|
||||||
|
* Then compute the delta between those users and
|
||||||
|
* the passed in members, and if it exists, send the delta
|
||||||
|
* to the XMPP server to update the member list.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* (Object) members: Map of member jids and affiliations.
|
||||||
|
* (String|Array) affiliation: An array of affiliations or
|
||||||
|
* a string if only one affiliation.
|
||||||
|
* (Function) deltaFunc: The function to compute the delta
|
||||||
|
* between old and new member lists.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* A promise which is resolved once the list has been
|
||||||
|
* updated or once it's been established there's no need
|
||||||
|
* to update the list.
|
||||||
|
*/
|
||||||
|
this.getJidsWithAffiliations(affiliations).then((old_members) => {
|
||||||
|
this.setAffiliations(deltaFunc(members, old_members));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
checkForReservedNick (callback, errback) {
|
checkForReservedNick (callback, errback) {
|
||||||
/* Use service-discovery to ask the XMPP server whether
|
/* Use service-discovery to ask the XMPP server whether
|
||||||
* this user has a reserved nickname for this room.
|
* this user has a reserved nickname for this room.
|
||||||
|
98
src/utils/muc.js
Normal file
98
src/utils/muc.js
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
// Converse.js (A browser based XMPP chat client)
|
||||||
|
// http://conversejs.org
|
||||||
|
//
|
||||||
|
// This is the utilities module.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012-2017, Jan-Carel Brand <jc@opkode.com>
|
||||||
|
// Licensed under the Mozilla Public License (MPLv2)
|
||||||
|
//
|
||||||
|
/*global define, escape, Jed */
|
||||||
|
(function (root, factory) {
|
||||||
|
define(["converse-core", "utils"], factory);
|
||||||
|
}(this, function (converse, u) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const { Strophe, sizzle, _ } = converse.env;
|
||||||
|
|
||||||
|
u.computeAffiliationsDelta = function computeAffiliationsDelta (exclude_existing, remove_absentees, new_list, old_list) {
|
||||||
|
/* Given two lists of objects with 'jid', 'affiliation' and
|
||||||
|
* 'reason' properties, return a new list containing
|
||||||
|
* those objects that are new, changed or removed
|
||||||
|
* (depending on the 'remove_absentees' boolean).
|
||||||
|
*
|
||||||
|
* The affiliations for new and changed members stay the
|
||||||
|
* same, for removed members, the affiliation is set to 'none'.
|
||||||
|
*
|
||||||
|
* The 'reason' property is not taken into account when
|
||||||
|
* comparing whether affiliations have been changed.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* (Boolean) exclude_existing: Indicates whether JIDs from
|
||||||
|
* the new list which are also in the old list
|
||||||
|
* (regardless of affiliation) should be excluded
|
||||||
|
* from the delta. One reason to do this
|
||||||
|
* would be when you want to add a JID only if it
|
||||||
|
* doesn't have *any* existing affiliation at all.
|
||||||
|
* (Boolean) remove_absentees: Indicates whether JIDs
|
||||||
|
* from the old list which are not in the new list
|
||||||
|
* should be considered removed and therefore be
|
||||||
|
* included in the delta with affiliation set
|
||||||
|
* to 'none'.
|
||||||
|
* (Array) new_list: Array containing the new affiliations
|
||||||
|
* (Array) old_list: Array containing the old affiliations
|
||||||
|
*/
|
||||||
|
const new_jids = _.map(new_list, 'jid');
|
||||||
|
const old_jids = _.map(old_list, 'jid');
|
||||||
|
|
||||||
|
// Get the new affiliations
|
||||||
|
let delta = _.map(
|
||||||
|
_.difference(new_jids, old_jids),
|
||||||
|
(jid) => new_list[_.indexOf(new_jids, jid)]
|
||||||
|
);
|
||||||
|
if (!exclude_existing) {
|
||||||
|
// Get the changed affiliations
|
||||||
|
delta = delta.concat(_.filter(new_list, function (item) {
|
||||||
|
const idx = _.indexOf(old_jids, item.jid);
|
||||||
|
if (idx >= 0) {
|
||||||
|
return item.affiliation !== old_list[idx].affiliation;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
if (remove_absentees) {
|
||||||
|
// Get the removed affiliations
|
||||||
|
delta = delta.concat(
|
||||||
|
_.map(
|
||||||
|
_.difference(old_jids, new_jids),
|
||||||
|
(jid) => ({'jid': jid, 'affiliation': 'none'})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return delta;
|
||||||
|
};
|
||||||
|
|
||||||
|
u.parseMemberListIQ = function parseMemberListIQ (iq) {
|
||||||
|
/* Given an IQ stanza with a member list, create an array of member
|
||||||
|
* objects.
|
||||||
|
*/
|
||||||
|
return _.map(
|
||||||
|
sizzle(`query[xmlns="${Strophe.NS.MUC_ADMIN}"] item`, iq),
|
||||||
|
(item) => ({
|
||||||
|
'jid': item.getAttribute('jid'),
|
||||||
|
'affiliation': item.getAttribute('affiliation'),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
u.marshallAffiliationIQs = function marshallAffiliationIQs () {
|
||||||
|
/* Marshall a list of IQ stanzas into a map of JIDs and
|
||||||
|
* affiliations.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* Any amount of XMLElement objects, representing the IQ
|
||||||
|
* stanzas.
|
||||||
|
*/
|
||||||
|
return _.flatMap(arguments[0], u.parseMemberListIQ);
|
||||||
|
}
|
||||||
|
|
||||||
|
}));
|
Loading…
Reference in New Issue
Block a user