From 376c50fbc810d7110e95bdff316bd3c29f0357af Mon Sep 17 00:00:00 2001 From: JC Brand Date: Mon, 5 Dec 2016 10:50:01 +0000 Subject: [PATCH] When inviting to a members-only room, first add to user to the member-list --- docs/CHANGES.md | 2 + spec/chatroom.js | 94 ++++++++++++++++++++++++++++++++++++++------ src/converse-core.js | 1 + src/converse-muc.js | 36 ++++++++++++++++- 4 files changed, 118 insertions(+), 15 deletions(-) diff --git a/docs/CHANGES.md b/docs/CHANGES.md index a48be5c73..7d0baad4f 100755 --- a/docs/CHANGES.md +++ b/docs/CHANGES.md @@ -10,6 +10,8 @@ disconnection or reconnection events. [jcbrand] - Optimize fetching of MAM messages (in some cases happened on each page load). [jcbrand] - Fix empty controlbox toggle after disconnect. [jcbrand] +- When inviting someone to a members-only room, first add them to the member + list. [jcbrand] ## 2.0.3 (2016-11-30) - #735 Room configuration button not visible. [jcbrand] diff --git a/spec/chatroom.js b/spec/chatroom.js index fdd81e485..e1c26b652 100644 --- a/spec/chatroom.js +++ b/spec/chatroom.js @@ -907,7 +907,7 @@ expect($occupants.children().first(0).text()).toBe("newnick"); })); - if("queries for the room information before attempting to join the user", mock.initConverse(function (converse) { + it("queries for the room information before attempting to join the user", mock.initConverse(function (converse) { var sent_IQ, IQ_id; var sendIQ = converse.connection.sendIQ; spyOn(converse.connection, 'sendIQ').andCallFake(function (iq, callback, errback) { @@ -955,21 +955,21 @@ 'type': 'text' }).up() .c('feature', {'var': 'http://jabber.org/protocol/muc'}).up() - .c('feature', {'var': 'passwordprotected'}).up() - .c('feature', {'var': 'hidden'}).up() - .c('feature', {'var': 'temporary'}).up() - .c('feature', {'var': 'open'}).up() - .c('feature', {'var': 'unmoderated'}).up() - .c('feature', {'var': 'nonanonymous'}); + .c('feature', {'var': 'muc_passwordprotected'}).up() + .c('feature', {'var': 'muc_hidden'}).up() + .c('feature', {'var': 'muc_temporary'}).up() + .c('feature', {'var': 'muc_open'}).up() + .c('feature', {'var': 'muc_unmoderated'}).up() + .c('feature', {'var': 'muc_nonanonymous'}); converse.connection._dataRecv(test_utils.createRequest(features_stanza)); var view = converse.chatboxviews.get('coven@chat.shakespeare.lit'); - expect(view.model.get('passwordprotected')).toBe('true'); - expect(view.model.get('hidden')).toBe('true'); - expect(view.model.get('temporary')).toBe('true'); - expect(view.model.get('open')).toBe('true'); - expect(view.model.get('unmoderated')).toBe('true'); - expect(view.model.get('nonanonymous')).toBe('true'); + expect(view.model.get('passwordprotected')).toBe(true); + expect(view.model.get('hidden')).toBe(true); + expect(view.model.get('temporary')).toBe(true); + expect(view.model.get('open')).toBe(true); + expect(view.model.get('unmoderated')).toBe(true); + expect(view.model.get('nonanonymous')).toBe(true); })); it("indicates when a room is no longer anonymous", mock.initConverse(function (converse) { @@ -1398,5 +1398,73 @@ expect(view.$el.find('.chatroom-body p:last').text()).toBe("This room has reached its maximum number of occupants"); })); }); + + 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 sendIQ = converse.connection.sendIQ; + spyOn(converse.connection, 'sendIQ').andCallFake(function (iq, callback, errback) { + sent_IQ = iq; + IQ_id = sendIQ.bind(this)(iq, callback, errback); + }); + + test_utils.openChatRoom(converse, 'coven', 'chat.shakespeare.lit', 'dummy'); + + // State that the chat is members-only via the features IQ + var features_stanza = $iq({ + from: 'coven@chat.shakespeare.lit', + 'id': IQ_id, + 'to': 'dummy@localhost/desktop', + 'type': 'result' + }) + .c('query', { 'xmlns': 'http://jabber.org/protocol/disco#info'}) + .c('identity', { + 'category': 'conference', + 'name': 'A Dark Cave', + 'type': 'text' + }).up() + .c('feature', {'var': 'http://jabber.org/protocol/muc'}).up() + .c('feature', {'var': 'muc_hidden'}).up() + .c('feature', {'var': 'muc_temporary'}).up() + .c('feature', {'var': 'muc_membersonly'}).up(); + converse.connection._dataRecv(test_utils.createRequest(features_stanza)); + + var view = converse.chatboxviews.get('coven@chat.shakespeare.lit'); + expect(view.model.get('membersonly')).toBeTruthy(); + + spyOn(view, 'setMemberList').andCallThrough(); + + test_utils.createContacts(converse, 'current'); + + var sent_stanza, + sent_id; + spyOn(converse.connection, 'send').andCallFake(function (stanza) { + if (stanza.nodeTree && stanza.nodeTree.nodeName === 'message') { + sent_id = stanza.nodeTree.getAttribute('id'); + sent_stanza = stanza; + } + }); + + var name = mock.cur_names[0]; + var invitee_jid = name.replace(/ /g,'.').toLowerCase() + '@localhost'; + var reason = "Please join this chat room"; + view.directInvite(invitee_jid, reason); + + expect(sent_IQ.toLocaleString()).toBe( + ""+ + ""+ + ""+ + ""+ + ""); + + expect(sent_stanza.toLocaleString()).toBe( // Strophe adds the xmlns attr (although not in spec) + ""+ + ""+ + "" + ); + })); + }); + }); })); diff --git a/src/converse-core.js b/src/converse-core.js index efdd8860a..10c81af08 100755 --- a/src/converse-core.js +++ b/src/converse-core.js @@ -1182,6 +1182,7 @@ chat_status = $presence.find('show').text() || 'online', status_message = $presence.find('status'), contact = this.get(bare_jid); + if (this.isSelf(bare_jid)) { if ((converse.connection.jid !== jid) && (presence_type !== 'unavailable') && diff --git a/src/converse-muc.js b/src/converse-muc.js index 286aaa26f..9e17e002a 100755 --- a/src/converse-muc.js +++ b/src/converse-muc.js @@ -494,6 +494,29 @@ this.insertIntoTextArea(ev.target.textContent); }, + setMemberList: function (members, onSuccess, onError) { + /* Send an IQ stanza to the server to modify the + * member-list of this room. + * + * See: http://xmpp.org/extensions/xep-0045.html#modifymember + * + * Parameters: + * (Array) members: An array of member objects, containing + * the JID and affiliation of each. + * (Function) onSuccess: callback for a succesful response + * (Function) onError: callback for an error response + */ + var iq = $iq({to: this.model.get('jid'), type: "set"}) + .c("query", {xmlns: Strophe.NS.MUC_ADMIN}); + _.each(members, function (member) { + iq.c("item", { + 'affiliation': member.affiliation, + 'jid': member.jid + }); + }); + return converse.connection.sendIQ(iq, onSuccess, onError); + }, + directInvite: function (recipient, reason) { /* Send a direct invitation as per XEP-0249 * @@ -501,6 +524,15 @@ * (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, otherwise they won't + // be able to join. + this.setMemberList([{ + 'jid': recipient, + 'affiliation': 'member' + }]); + } var attrs = { 'xmlns': 'jabber:x:conference', 'jid': this.model.get('jid') @@ -1082,7 +1114,7 @@ var that = this; converse.connection.disco.info(this.model.get('jid'), null, function (iq) { - /* + /* * See http://xmpp.org/extensions/xep-0045.html#disco-roominfo * *