Browse Source

Add new config option `muc_nickname_from_jid`

which if set to `true` will let converse.js automatically take the node part of
a user's JID as their nickname when entering a room.

If there is a nickname clash, then the nickname will be disambiguated by adding
integers to it.

For example, john will become john-1, then john-2 and so forth.
JC Brand 9 years ago
parent
commit
462a43b891
4 changed files with 142 additions and 46 deletions
  1. 4 0
      docs/CHANGES.md
  2. 15 0
      docs/source/configuration.rst
  3. 78 41
      spec/chatroom.js
  4. 45 5
      src/converse-muc.js

+ 4 - 0
docs/CHANGES.md

@@ -1,5 +1,9 @@
 # Changelog
 
+## 1.0.6 (Unreleased)
+- #674 Polish translation updated to the current master. [ser]
+- New config option [muc_nickname_from_jid](https://conversejs.org/docs/html/configuration.html#muc_nickname_from_jid) [jcbrand]
+
 ## 1.0.5 (2016-07-28)
 - In case of nickname conflict when joining a room, allow the user to choose a new one.
   [jcbrand]

+ 15 - 0
docs/source/configuration.rst

@@ -639,6 +639,21 @@ different approach.
 If you're using MAM for archiving chat room messages, you might want to set
 this option to zero.
 
+muc_nickname_from_jid
+---------------------
+
+* Default: ``false``
+
+When set to ``true``, then users will not be prompted to provide nicknames for
+chat rooms. Instead, the node part of a user's JID (i.e. JID = node@domain/resource)
+will be used. If the user's nickname is already taken by another user in the
+chat room, then an integer will be added to make it unique.
+
+So, for example, if john@example.com joins a chatroom, his nickname will
+automatically be "john". If now john@differentdomain.com tries to join the
+room, his nickname will be "john-2", and if john@somethingelse.com joins, then
+his nickname will be "john-3", and so forth.
+
 notify_all_room_messages
 ------------------------
 

+ 78 - 41
spec/chatroom.js

@@ -630,7 +630,7 @@
                     .c('status').attrs({code:'307'}).nodeTree;
 
                 var view = this.chatboxviews.get('lounge@localhost');
-                view.onChatRoomPresence(presence, {nick: 'dummy', name: 'lounge@localhost'});
+                view.onChatRoomPresence(presence);
                 expect(view.$('.chat-area').is(':visible')).toBeFalsy();
                 expect(view.$('.occupants').is(':visible')).toBeFalsy();
                 var $chat_body = view.$('.chatroom-body');
@@ -796,7 +796,7 @@
                 var view = this.chatboxviews.get('problematic@muc.localhost');
                 spyOn(view, 'renderPasswordForm').andCallThrough();
                 runs(function () {
-                    view.onChatRoomPresence(presence, {'nick': 'dummy'});
+                    view.onChatRoomPresence(presence);
                 });
                 waits(250);
                 runs(function () {
@@ -816,11 +816,11 @@
                 .c('x').attrs({xmlns:'http://jabber.org/protocol/muc'}).up()
                 .c('error').attrs({by:'lounge@localhost', type:'auth'})
                     .c('registration-required').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
-                var view = this.chatboxviews.get('problematic@muc.localhost');
+                var view = converse.chatboxviews.get('problematic@muc.localhost');
                 spyOn(view, 'showErrorMessage').andCallThrough();
-                view.onChatRoomPresence(presence, {'nick': 'dummy'});
+                view.onChatRoomPresence(presence);
                 expect(view.$el.find('.chatroom-body p:last').text()).toBe('You are not on the member list of this room');
-            }.bind(converse));
+            });
 
             it("will show an error message if the user has been banned", function () {
                 var presence = $pres().attrs({
@@ -831,26 +831,79 @@
                 .c('x').attrs({xmlns:'http://jabber.org/protocol/muc'}).up()
                 .c('error').attrs({by:'lounge@localhost', type:'auth'})
                     .c('forbidden').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
-                var view = this.chatboxviews.get('problematic@muc.localhost');
+                var view = converse.chatboxviews.get('problematic@muc.localhost');
                 spyOn(view, 'showErrorMessage').andCallThrough();
-                view.onChatRoomPresence(presence, {'nick': 'dummy'});
+                view.onChatRoomPresence(presence);
                 expect(view.$el.find('.chatroom-body p:last').text()).toBe('You have been banned from this room');
-            }.bind(converse));
+            });
 
-            it("will show an error message if no nickname was specified for the user", function () {
+            it("will render a nickname form if a nickname conflict happens and muc_nickname_from_jid=false", function () {
                 var presence = $pres().attrs({
                     from:'lounge@localhost/thirdwitch',
                         id:'n13mt3l',
                         to:'dummy@localhost/pda',
                         type:'error'})
                 .c('x').attrs({xmlns:'http://jabber.org/protocol/muc'}).up()
-                .c('error').attrs({by:'lounge@localhost', type:'modify'})
-                    .c('jid-malformed').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
-                var view = this.chatboxviews.get('problematic@muc.localhost');
+                .c('error').attrs({by:'lounge@localhost', type:'cancel'})
+                    .c('conflict').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
+                var view = converse.chatboxviews.get('problematic@muc.localhost');
                 spyOn(view, 'showErrorMessage').andCallThrough();
-                view.onChatRoomPresence(presence, {'nick': 'dummy'});
-                expect(view.$el.find('.chatroom-body p:last').text()).toBe('No nickname was specified');
-            }.bind(converse));
+                view.onChatRoomPresence(presence);
+                expect(view.$el.find('.chatroom-body form.chatroom-form label:first').text()).toBe('Please choose your nickname');
+            });
+
+            it("will automatically choose a new nickname if a nickname conflict happens and muc_nickname_from_jid=true", function () {
+                /*
+                    <presence
+                        from='coven@chat.shakespeare.lit/thirdwitch'
+                        id='n13mt3l'
+                        to='hag66@shakespeare.lit/pda'
+                        type='error'>
+                    <x xmlns='http://jabber.org/protocol/muc'/>
+                    <error by='coven@chat.shakespeare.lit' type='cancel'>
+                        <conflict xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
+                    </error>
+                    </presence>
+                */
+                converse.muc_nickname_from_jid = true;
+
+                var attrs = {
+                    from:'lounge@localhost/dummy',
+                    id:'n13mt3l',
+                    to:'dummy@localhost/pda',
+                    type:'error'
+                };
+                var presence = $pres().attrs(attrs)
+                    .c('x').attrs({xmlns:'http://jabber.org/protocol/muc'}).up()
+                    .c('error').attrs({by:'lounge@localhost', type:'cancel'})
+                        .c('conflict').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
+
+                var view = converse.chatboxviews.get('problematic@muc.localhost');
+                spyOn(view, 'showErrorMessage').andCallThrough();
+                spyOn(view, 'join').andCallThrough();
+
+                // Simulate repeatedly that there's already someone in the room
+                // with that nickname
+                view.onChatRoomPresence(presence);
+                expect(view.join).toHaveBeenCalledWith('dummy-2');
+
+                attrs.from = 'lounge@localhost/dummy-2';
+                presence = $pres().attrs(attrs)
+                    .c('x').attrs({xmlns:'http://jabber.org/protocol/muc'}).up()
+                    .c('error').attrs({by:'lounge@localhost', type:'cancel'})
+                        .c('conflict').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
+                view.onChatRoomPresence(presence);
+
+                expect(view.join).toHaveBeenCalledWith('dummy-3');
+
+                attrs.from = 'lounge@localhost/dummy-3';
+                presence = $pres().attrs(attrs)
+                    .c('x').attrs({xmlns:'http://jabber.org/protocol/muc'}).up()
+                    .c('error').attrs({by:'lounge@localhost', type:'cancel'})
+                        .c('conflict').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
+                view.onChatRoomPresence(presence);
+                expect(view.join).toHaveBeenCalledWith('dummy-4');
+            });
 
             it("will show an error message if the user is not allowed to have created the room", function () {
                 var presence = $pres().attrs({
@@ -863,7 +916,7 @@
                     .c('not-allowed').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
                 var view = this.chatboxviews.get('problematic@muc.localhost');
                 spyOn(view, 'showErrorMessage').andCallThrough();
-                view.onChatRoomPresence(presence, {'nick': 'dummy'});
+                view.onChatRoomPresence(presence);
                 expect(view.$el.find('.chatroom-body p:last').text()).toBe('You are not allowed to create new rooms');
             }.bind(converse));
 
@@ -876,27 +929,11 @@
                 .c('x').attrs({xmlns:'http://jabber.org/protocol/muc'}).up()
                 .c('error').attrs({by:'lounge@localhost', type:'cancel'})
                     .c('not-acceptable').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
-                var view = this.chatboxviews.get('problematic@muc.localhost');
+                var view = converse.chatboxviews.get('problematic@muc.localhost');
                 spyOn(view, 'showErrorMessage').andCallThrough();
-                view.onChatRoomPresence(presence, {'nick': 'dummy'});
+                view.onChatRoomPresence(presence);
                 expect(view.$el.find('.chatroom-body p:last').text()).toBe("Your nickname doesn't conform to this room's policies");
-            }.bind(converse));
-
-            it("will show an error message if the user's nickname is already taken", function () {
-                var presence = $pres().attrs({
-                    from:'lounge@localhost/thirdwitch',
-                        id:'n13mt3l',
-                        to:'dummy@localhost/pda',
-                        type:'error'})
-                .c('x').attrs({xmlns:'http://jabber.org/protocol/muc'}).up()
-                .c('error').attrs({by:'lounge@localhost', type:'cancel'})
-                    .c('conflict').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
-                var view = this.chatboxviews.get('problematic@muc.localhost');
-                spyOn(view, 'showErrorMessage').andCallThrough();
-                view.onChatRoomPresence(presence, {'nick': 'dummy'});
-                expect(view.$el.find('.chatroom-body p:last').text()).toBe(
-                        "The nickname you chose is reserved or currently in use, please choose a different one.");
-            }.bind(converse));
+            });
 
             it("will show an error message if the room doesn't yet exist", function () {
                 var presence = $pres().attrs({
@@ -907,11 +944,11 @@
                 .c('x').attrs({xmlns:'http://jabber.org/protocol/muc'}).up()
                 .c('error').attrs({by:'lounge@localhost', type:'cancel'})
                     .c('item-not-found').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
-                var view = this.chatboxviews.get('problematic@muc.localhost');
+                var view = converse.chatboxviews.get('problematic@muc.localhost');
                 spyOn(view, 'showErrorMessage').andCallThrough();
-                view.onChatRoomPresence(presence, {'nick': 'dummy'});
+                view.onChatRoomPresence(presence);
                 expect(view.$el.find('.chatroom-body p:last').text()).toBe("This room does not (yet) exist");
-            }.bind(converse));
+            });
 
             it("will show an error message if the room has reached its maximum number of occupants", function () {
                 var presence = $pres().attrs({
@@ -922,11 +959,11 @@
                 .c('x').attrs({xmlns:'http://jabber.org/protocol/muc'}).up()
                 .c('error').attrs({by:'lounge@localhost', type:'cancel'})
                     .c('service-unavailable').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
-                var view = this.chatboxviews.get('problematic@muc.localhost');
+                var view = converse.chatboxviews.get('problematic@muc.localhost');
                 spyOn(view, 'showErrorMessage').andCallThrough();
-                view.onChatRoomPresence(presence, {'nick': 'dummy'});
+                view.onChatRoomPresence(presence);
                 expect(view.$el.find('.chatroom-body p:last').text()).toBe("This room has reached its maximum number of occupants");
-            }.bind(converse));
+            });
         }.bind(converse));
     }.bind(converse, mock, test_utils));
 }));

+ 45 - 5
src/converse-muc.js

@@ -159,6 +159,7 @@
             this.updateSettings({
                 allow_muc_invitations: true,
                 allow_muc: true,
+                muc_nickname_from_jid: false, // Use the node part of the user's JID as room nickname
                 auto_join_on_invite: false,  // Auto-join chatroom on invite
                 auto_join_rooms: [], // List of maps {'jid': 'room@example.org', 'nick': 'WizardKing69' },
                                      // providing room jids and nicks or simply a list JIDs.
@@ -694,7 +695,7 @@
                             'node': 'x-roomuser-item'
                         }),
                         this.onNickNameFound.bind(this),
-                        this.renderNicknameForm.bind(this)
+                        this.onNickNameNotFound.bind(this)
                     );
                 },
 
@@ -708,13 +709,51 @@
                         .find('query[node="x-roomuser-item"] identity')
                         .attr('name');
                     if (!nick) {
-                        this.renderNicknameForm();
+                        this.onNickNameNotFound();
                     } else {
                         this.join(nick);
                     }
                 },
 
+                onNickNameNotFound: function (message) {
+                    if (converse.muc_nickname_from_jid) {
+                        // We try to enter the room with the node part of
+                        // the user's JID.
+                        this.join(Strophe.unescapeNode(Strophe.getNodeFromJid(converse.bare_jid)));
+                    } else {
+                        this.renderNicknameForm(message);
+                    }
+                },
+
+                onNicknameClash: function (presence) {
+                    /* When the nickname is already taken, we either render a
+                     * form for the user to choose a new nickname, or we
+                     * try to make the nickname unique by adding an integer to
+                     * it. So john will become john-2, and then john-3 and so on.
+                     *
+                     * Which option is take depends on the value of
+                     * muc_nickname_from_jid.
+                     */
+                    if (converse.muc_nickname_from_jid) {
+                        var nick = presence.getAttribute('from').split('/')[1];
+                        if (nick === Strophe.unescapeNode(Strophe.getNodeFromJid(converse.bare_jid))) {
+                            this.join(nick + '-2');
+                        } else {
+                            var del= nick.lastIndexOf("-");
+                            var num = nick.substring(del+1, nick.length);
+                            this.join(nick.substring(0, del+1) + String(Number(num)+1));
+                        }
+                    } else {
+                        this.renderNicknameForm(
+                            __("The nickname you chose is reserved or currently in use, please choose a different one.")
+                        );
+                    }
+                },
+
                 renderNicknameForm: function (message) {
+                    /* Render a form which allows the user to choose their
+                     * nickname.
+                     */
                     this.$('.chatroom-body').children().addClass('hidden');
                     this.$('span.centered.spinner').remove();
                     if (typeof message !== "string") {
@@ -883,9 +922,10 @@
                     return el;
                 },
 
-                showErrorMessage: function ($error) {
+                showErrorMessage: function (presence) {
                     // We didn't enter the room, so we must remove it from the MUC
                     // add-on
+                    var $error = $(presence).find('error');
                     if ($error.attr('type') === 'auth') {
                         if ($error.find('not-authorized').length) {
                             this.renderPasswordForm();
@@ -904,7 +944,7 @@
                         } else if ($error.find('not-acceptable').length) {
                             this.showDisconnectMessage(__("Your nickname doesn't conform to this room's policies"));
                         } else if ($error.find('conflict').length) {
-                            this.renderNicknameForm(__("The nickname you chose is reserved or currently in use, please choose a different one."));
+                            this.onNicknameClash(presence);
                         } else if ($error.find('item-not-found').length) {
                             this.showDisconnectMessage(__("This room does not (yet) exist"));
                         } else if ($error.find('service-unavailable').length) {
@@ -940,7 +980,7 @@
                     var nick = this.model.get('nick');
                     if ($presence.attr('type') === 'error') {
                         this.model.set('connection_status', Strophe.Status.DISCONNECTED);
-                        this.showErrorMessage($presence.find('error'));
+                        this.showErrorMessage(pres);
                     } else {
                         is_self = ($presence.find("status[code='110']").length) ||
                             ($presence.attr('from') === this.model.get('id')+'/'+Strophe.escapeNode(nick));