Refactor to send out 3 affiliation request stanzas.

One for each affiliation (member, owner, admin).

Unfortunately Prosody doesn't give you correct results if you query for all
three affiliations in one stanza.

We'll have to see whether setting all three in one stanza actually works.
This commit is contained in:
JC Brand 2016-12-06 18:34:54 +00:00
parent 6c725409c2
commit c6767bcdaa
2 changed files with 77 additions and 52 deletions

View File

@ -1403,11 +1403,11 @@
describe("Someone being invited to a chat room", function () { describe("Someone being invited to a chat room", function () {
it("will first be added to the member list if the chat room is members only", mock.initConverse(function (converse) { it("will first be added to the member list if the chat room is members only", mock.initConverse(function (converse) {
var sent_IQ, IQ_id; var sent_IQs = [], IQ_ids = [];
var sendIQ = converse.connection.sendIQ; var sendIQ = converse.connection.sendIQ;
spyOn(converse.connection, 'sendIQ').andCallFake(function (iq, callback, errback) { spyOn(converse.connection, 'sendIQ').andCallFake(function (iq, callback, errback) {
sent_IQ = iq; sent_IQs.push(iq);
IQ_id = sendIQ.bind(this)(iq, callback, errback); IQ_ids.push(sendIQ.bind(this)(iq, callback, errback));
}); });
test_utils.openChatRoom(converse, 'coven', 'chat.shakespeare.lit', 'dummy'); test_utils.openChatRoom(converse, 'coven', 'chat.shakespeare.lit', 'dummy');
@ -1415,7 +1415,7 @@
// State that the chat is members-only via the features IQ // State that the chat is members-only via the features IQ
var features_stanza = $iq({ var features_stanza = $iq({
from: 'coven@chat.shakespeare.lit', from: 'coven@chat.shakespeare.lit',
'id': IQ_id, 'id': IQ_ids.pop(),
'to': 'dummy@localhost/desktop', 'to': 'dummy@localhost/desktop',
'type': 'result' 'type': 'result'
}) })
@ -1450,9 +1450,25 @@
var reason = "Please join this chat room"; var reason = "Please join this chat room";
view.directInvite(invitee_jid, reason); view.directInvite(invitee_jid, reason);
// Check that we first requested the member list var admin_iq_id = IQ_ids.pop();
expect(sent_IQ.toLocaleString()).toBe( var owner_iq_id = IQ_ids.pop();
"<iq to='coven@chat.shakespeare.lit' type='get' xmlns='jabber:client' id='"+IQ_id+"'>"+ var member_iq_id = IQ_ids.pop();
// Check in reverse order that we requested all three lists
// (member, owner and admin).
expect(sent_IQs.pop().toLocaleString()).toBe(
"<iq to='coven@chat.shakespeare.lit' type='get' xmlns='jabber:client' id='"+admin_iq_id+"'>"+
"<query xmlns='http://jabber.org/protocol/muc#admin'>"+
"<item affiliation='admin'/>"+
"</query>"+
"</iq>");
expect(sent_IQs.pop().toLocaleString()).toBe(
"<iq to='coven@chat.shakespeare.lit' type='get' xmlns='jabber:client' id='"+owner_iq_id+"'>"+
"<query xmlns='http://jabber.org/protocol/muc#admin'>"+
"<item affiliation='owner'/>"+
"</query>"+
"</iq>");
expect(sent_IQs.pop().toLocaleString()).toBe(
"<iq to='coven@chat.shakespeare.lit' type='get' xmlns='jabber:client' id='"+member_iq_id+"'>"+
"<query xmlns='http://jabber.org/protocol/muc#admin'>"+ "<query xmlns='http://jabber.org/protocol/muc#admin'>"+
"<item affiliation='member'/>"+ "<item affiliation='member'/>"+
"</query>"+ "</query>"+
@ -1474,7 +1490,7 @@
*/ */
var member_list_stanza = $iq({ var member_list_stanza = $iq({
'from': 'coven@chat.shakespeare.lit', 'from': 'coven@chat.shakespeare.lit',
'id': IQ_id, 'id': member_iq_id,
'to': 'dummy@localhost/resource', 'to': 'dummy@localhost/resource',
'type': 'result' 'type': 'result'
}).c('query', {'xmlns': Strophe.NS.MUC_ADMIN}) }).c('query', {'xmlns': Strophe.NS.MUC_ADMIN})
@ -1486,9 +1502,34 @@
}); });
converse.connection._dataRecv(test_utils.createRequest(member_list_stanza)); converse.connection._dataRecv(test_utils.createRequest(member_list_stanza));
var admin_list_stanza = $iq({
'from': 'coven@chat.shakespeare.lit',
'id': admin_iq_id,
'to': 'dummy@localhost/resource',
'type': 'result'
}).c('query', {'xmlns': Strophe.NS.MUC_ADMIN})
.c('item', {
'affiliation': 'admin',
'jid': 'wiccarocks@shakespeare.lit',
'nick': 'secondwitch'
});
converse.connection._dataRecv(test_utils.createRequest(admin_list_stanza));
var owner_list_stanza = $iq({
'from': 'coven@chat.shakespeare.lit',
'id': owner_iq_id,
'to': 'dummy@localhost/resource',
'type': 'result'
}).c('query', {'xmlns': Strophe.NS.MUC_ADMIN})
.c('item', {
'affiliation': 'owner',
'jid': 'crone1@shakespeare.lit',
});
converse.connection._dataRecv(test_utils.createRequest(owner_list_stanza));
// Check that the member list now gets updated // Check that the member list now gets updated
expect(sent_IQ.toLocaleString()).toBe( expect(sent_IQs.pop().toLocaleString()).toBe(
"<iq to='coven@chat.shakespeare.lit' type='set' xmlns='jabber:client' id='"+IQ_id+"'>"+ "<iq to='coven@chat.shakespeare.lit' type='set' xmlns='jabber:client' id='"+IQ_ids.pop()+"'>"+
"<query xmlns='http://jabber.org/protocol/muc#admin'>"+ "<query xmlns='http://jabber.org/protocol/muc#admin'>"+
"<item affiliation='member' jid='"+invitee_jid+"'/>"+ "<item affiliation='member' jid='"+invitee_jid+"'/>"+
"</query>"+ "</query>"+
@ -1502,6 +1543,5 @@
); );
})); }));
}); });
}); });
})); }));

View File

@ -494,32 +494,25 @@
this.insertIntoTextArea(ev.target.textContent); this.insertIntoTextArea(ev.target.textContent);
}, },
requestMemberList: function (onSuccess, onError, include_admins, include_owners) { requestMemberList: function (affiliation) {
/* Send an IQ stanza to the server, asking it for the /* Send an IQ stanza to the server, asking it for the
* member-list of this room. * member-list of this room.
* *
* See: http://xmpp.org/extensions/xep-0045.html#modifymember * See: http://xmpp.org/extensions/xep-0045.html#modifymember
* *
* Parameters: * Parameters:
* (Boolean) include_admins: Whether the admin list should * (String) affiliation: The specific member list to
* be included. * fetch. 'admin', 'owner' or 'member'.
* (Boolean) include_owners: Whether the owner list should
* be included.
* *
* Returns: * Returns:
* A promise which resolves once the list has been * A promise which resolves once the list has been
* retrieved. * retrieved.
*/ */
var deferred = new $.Deferred(); var deferred = new $.Deferred();
affiliation = affiliation || 'member';
var iq = $iq({to: this.model.get('jid'), type: "get"}) var iq = $iq({to: this.model.get('jid'), type: "get"})
.c("query", {xmlns: Strophe.NS.MUC_ADMIN}) .c("query", {xmlns: Strophe.NS.MUC_ADMIN})
.c("item", {'affiliation': 'member'}).up(); .c("item", {'affiliation': affiliation});
if (include_admins) {
iq.c("item", {'affiliation': 'admin'}).up();
}
if (include_owners) {
iq.c("item", {'affiliation': 'owner'});
}
converse.connection.sendIQ(iq, deferred.resolve, deferred.reject); converse.connection.sendIQ(iq, deferred.resolve, deferred.reject);
return deferred.promise(); return deferred.promise();
}, },
@ -580,9 +573,9 @@
return list_delta; return list_delta;
}, },
sendMemberListStanza: function (onSuccess, onError, members) { setAffiliations: function (members, onSuccess, onError) {
/* Send an IQ stanza to the server to modify the /* Send an IQ stanza to the server to modify the
* member-list of this room. * affiliations in this room.
* *
* See: http://xmpp.org/extensions/xep-0045.html#modifymember * See: http://xmpp.org/extensions/xep-0045.html#modifymember
* *
@ -593,8 +586,7 @@
* (Function) onError: callback for an error response * (Function) onError: callback for an error response
*/ */
if (!members.length) { if (!members.length) {
// Succesfully updated the list with zero new or // Succesfully updated with zero affilations :)
// changed members :)
onSuccess(null); onSuccess(null);
return; return;
} }
@ -609,8 +601,7 @@
return converse.connection.sendIQ(iq, onSuccess, onError); return converse.connection.sendIQ(iq, onSuccess, onError);
}, },
updateMemberList: function (members, remove_absentees, updateMemberLists: function (members, affiliations, remove_absentees) {
include_admins, include_owners) {
/* Fetch the member list (optionally including admins and /* Fetch the member list (optionally including admins and
* owners), then compute the delta between that list and * owners), then compute the delta between that list and
* the passed in members, and if it exists, send the delta * the passed in members, and if it exists, send the delta
@ -619,38 +610,32 @@
* Parameters: * Parameters:
* (Array) members: Array of objects with properties 'jid' * (Array) members: Array of objects with properties 'jid'
* and 'affiliation'. * and 'affiliation'.
* (String|Array) affiliation: An array of affiliations or
* a string if only one affiliation.
* (Boolean) remove_absentees: When computing the list * (Boolean) remove_absentees: When computing the list
* delta, consider members from the old list that * delta, consider members from the old list that
* are not in the new list as removed (therefore their * are not in the new list as removed (therefore their
* affiliation will get set to 'none'). * affiliation will get set to 'none').
* (Boolean) include_admins: Whether room admins are also
* considered.
* (Boolean) include_owners: Whether room owners are also
* considered.
*
* The include_admins and include_owners options have
* access-control implications. Usually only room owners
* can change those lists.
* *
* Returns: * Returns:
* A promise which is resolved once the list has been * A promise which is resolved once the list has been
* updated or once it's been established there's no need * updated or once it's been established there's no need
* to update the list. * to update the list.
*/ */
var that = this;
var deferred = new $.Deferred(); var deferred = new $.Deferred();
var computeDelta = _.partial( if (typeof affiliations === "string") {
this.computeMemberListDelta, members, _, remove_absentees affiliations = [affiliations];
); }
this.requestMemberList(include_admins, include_owners).then( var promises = [];
_.compose( _.each(affiliations, function (affiliation) {
_.partial( promises.push(that.requestMemberList(affiliation));
this.sendMemberListStanza.bind(this), });
deferred.resolve, $.when.apply($, promises).always(function () {
deferred.reject var old_members = _.flatten(_.map(arguments, that.parseMemberListIQ));
), var delta = that.computeMemberListDelta(members, old_members, remove_absentees);
_.compose(computeDelta, this.parseMemberListIQ) that.setAffiliations(delta, deferred.resolve, deferred.reject);
) });
);
return deferred.promise(); return deferred.promise();
}, },
@ -665,10 +650,10 @@
// When inviting to a members-only room, we first add // When inviting to a members-only room, we first add
// the person to the member list, otherwise they won't // the person to the member list, otherwise they won't
// be able to join. // be able to join.
this.updateMemberList([{ this.updateMemberLists([{
'jid': recipient, 'jid': recipient,
'affiliation': 'member' 'affiliation': 'member'
}]); }], ['member', 'owner', 'admin'], false);
} }
var attrs = { var attrs = {
'xmlns': 'jabber:x:conference', 'xmlns': 'jabber:x:conference',