diff --git a/spec/chatroom.js b/spec/chatroom.js index ac94b7574..e726f7d7f 100644 --- a/spec/chatroom.js +++ b/spec/chatroom.js @@ -1403,11 +1403,11 @@ 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) { - var sent_IQ, IQ_id; + var sent_IQs = [], IQ_ids = []; var sendIQ = converse.connection.sendIQ; spyOn(converse.connection, 'sendIQ').andCallFake(function (iq, callback, errback) { - sent_IQ = iq; - IQ_id = sendIQ.bind(this)(iq, callback, errback); + sent_IQs.push(iq); + IQ_ids.push(sendIQ.bind(this)(iq, callback, errback)); }); test_utils.openChatRoom(converse, 'coven', 'chat.shakespeare.lit', 'dummy'); @@ -1415,7 +1415,7 @@ // State that the chat is members-only via the features IQ var features_stanza = $iq({ from: 'coven@chat.shakespeare.lit', - 'id': IQ_id, + 'id': IQ_ids.pop(), 'to': 'dummy@localhost/desktop', 'type': 'result' }) @@ -1450,9 +1450,25 @@ var reason = "Please join this chat room"; view.directInvite(invitee_jid, reason); - // Check that we first requested the member list - expect(sent_IQ.toLocaleString()).toBe( - ""+ + var admin_iq_id = IQ_ids.pop(); + var owner_iq_id = IQ_ids.pop(); + 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( + ""+ + ""+ + ""+ + ""+ + ""); + expect(sent_IQs.pop().toLocaleString()).toBe( + ""+ + ""+ + ""+ + ""+ + ""); + expect(sent_IQs.pop().toLocaleString()).toBe( + ""+ ""+ ""+ ""+ @@ -1474,7 +1490,7 @@ */ var member_list_stanza = $iq({ 'from': 'coven@chat.shakespeare.lit', - 'id': IQ_id, + 'id': member_iq_id, 'to': 'dummy@localhost/resource', 'type': 'result' }).c('query', {'xmlns': Strophe.NS.MUC_ADMIN}) @@ -1486,9 +1502,34 @@ }); 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 - expect(sent_IQ.toLocaleString()).toBe( - ""+ + expect(sent_IQs.pop().toLocaleString()).toBe( + ""+ ""+ ""+ ""+ @@ -1502,6 +1543,5 @@ ); })); }); - }); })); diff --git a/src/converse-muc.js b/src/converse-muc.js index c1fd1f05d..475aea183 100755 --- a/src/converse-muc.js +++ b/src/converse-muc.js @@ -494,32 +494,25 @@ 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 * member-list of this room. * * See: http://xmpp.org/extensions/xep-0045.html#modifymember * * Parameters: - * (Boolean) include_admins: Whether the admin list should - * be included. - * (Boolean) include_owners: Whether the owner list should - * be included. + * (String) affiliation: The specific member list to + * fetch. 'admin', 'owner' or 'member'. * * Returns: * A promise which resolves once the list has been * retrieved. */ var deferred = new $.Deferred(); + affiliation = affiliation || 'member'; var iq = $iq({to: this.model.get('jid'), type: "get"}) .c("query", {xmlns: Strophe.NS.MUC_ADMIN}) - .c("item", {'affiliation': 'member'}).up(); - if (include_admins) { - iq.c("item", {'affiliation': 'admin'}).up(); - } - if (include_owners) { - iq.c("item", {'affiliation': 'owner'}); - } + .c("item", {'affiliation': affiliation}); converse.connection.sendIQ(iq, deferred.resolve, deferred.reject); return deferred.promise(); }, @@ -580,9 +573,9 @@ return list_delta; }, - sendMemberListStanza: function (onSuccess, onError, members) { + setAffiliations: function (members, onSuccess, onError) { /* 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 * @@ -593,8 +586,7 @@ * (Function) onError: callback for an error response */ if (!members.length) { - // Succesfully updated the list with zero new or - // changed members :) + // Succesfully updated with zero affilations :) onSuccess(null); return; } @@ -609,8 +601,7 @@ return converse.connection.sendIQ(iq, onSuccess, onError); }, - updateMemberList: function (members, remove_absentees, - include_admins, include_owners) { + updateMemberLists: function (members, affiliations, remove_absentees) { /* Fetch the member list (optionally including admins and * owners), then compute the delta between that list and * the passed in members, and if it exists, send the delta @@ -619,38 +610,32 @@ * Parameters: * (Array) members: Array of objects with properties 'jid' * and 'affiliation'. + * (String|Array) affiliation: An array of affiliations or + * a string if only one affiliation. * (Boolean) remove_absentees: When computing the list * delta, consider members from the old list that * are not in the new list as removed (therefore their * 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: * 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. */ + var that = this; var deferred = new $.Deferred(); - var computeDelta = _.partial( - this.computeMemberListDelta, members, _, remove_absentees - ); - this.requestMemberList(include_admins, include_owners).then( - _.compose( - _.partial( - this.sendMemberListStanza.bind(this), - deferred.resolve, - deferred.reject - ), - _.compose(computeDelta, this.parseMemberListIQ) - ) - ); + if (typeof affiliations === "string") { + affiliations = [affiliations]; + } + var promises = []; + _.each(affiliations, function (affiliation) { + promises.push(that.requestMemberList(affiliation)); + }); + $.when.apply($, promises).always(function () { + var old_members = _.flatten(_.map(arguments, that.parseMemberListIQ)); + var delta = that.computeMemberListDelta(members, old_members, remove_absentees); + that.setAffiliations(delta, deferred.resolve, deferred.reject); + }); return deferred.promise(); }, @@ -665,10 +650,10 @@ // When inviting to a members-only room, we first add // the person to the member list, otherwise they won't // be able to join. - this.updateMemberList([{ + this.updateMemberLists([{ 'jid': recipient, 'affiliation': 'member' - }]); + }], ['member', 'owner', 'admin'], false); } var attrs = { 'xmlns': 'jabber:x:conference',