Browse Source

Re-add support for `muc_domain` and add `locked_muc_domain`.

updates #1373
JC Brand 6 years ago
parent
commit
d3a4555165

+ 3 - 1
CHANGES.md

@@ -2,6 +2,8 @@
 
 ## 4.1.3 (Unreleased)
 
+- New config setting [locked_muc_domain](https://conversejs.org/docs/html/configuration.html#locked-muc-domain)
+- #1373: Re-add support for the [muc_domain](https://conversejs.org/docs/html/configuration.html#muc-domain) setting
 - #1437: List of groupchats in modal doesn't scroll
 
 ## 4.1.2 (2019-02-22)
@@ -23,7 +25,7 @@
 - Bugfix: MUC invite form not appearing
 - #1369 Don't wrongly interpret message with `subject` as a topic change.
 - #1405 Status of contacts list are not displayed properly
-- #1408 New config option `roomconfig_whitelist`
+- #1408 New config option [roomconfig_whitelist](https://conversejs.org/docs/html/configuration.html#roomconfig-whitelist)
 - #1410 HTTP upload not working if conversations push proxy is used
 - #1412 MUC moderator commands can be disabled selectively by config
 - #1413 Fix moderator commands that change affiliation

+ 55 - 12
dist/converse.js

@@ -53303,6 +53303,8 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].plugins
     _converse.api.settings.update({
       'auto_list_rooms': false,
       'muc_disable_moderator_commands': false,
+      'muc_domain': undefined,
+      'locked_muc_domain': undefined,
       'muc_show_join_leave': true,
       'roomconfig_whitelist': [],
       'visible_toolbar_buttons': {
@@ -53310,6 +53312,10 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].plugins
       }
     });
 
+    if (_converse.locked_muc_domain && !_.isString(_converse.muc_domain)) {
+      throw new Error("Config Error: it makes no sense to set locked_muc_domain " + "to true when muc_domain is not set");
+    }
+
     function ___(str) {
       /* This is part of a hack to get gettext to scan strings to be
       * translated. Strings we cannot send to the function above because
@@ -53459,22 +53465,31 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].plugins
       initialize() {
         _converse.BootstrapModal.prototype.initialize.apply(this, arguments);
 
+        if (_converse.muc_domain && !this.model.get('muc_domain')) {
+          this.model.save('muc_domain', _converse.muc_domain);
+        }
+
         this.model.on('change:muc_domain', this.onDomainChange, this);
       },
 
       toHTML() {
+        const muc_domain = this.model.get('muc_domain') || _converse.muc_domain;
+
         return templates_list_chatrooms_modal_html__WEBPACK_IMPORTED_MODULE_19___default()(_.extend(this.model.toJSON(), {
           'heading_list_chatrooms': __('Query for Groupchats'),
           'label_server_address': __('Server address'),
           'label_query': __('Show groupchats'),
-          'server_placeholder': __('conference.example.org')
+          'show_form': !_converse.locked_muc_domain,
+          'server_placeholder': muc_domain ? muc_domain : __('conference.example.org')
         }));
       },
 
       afterRender() {
-        this.el.addEventListener('shown.bs.modal', () => {
-          this.el.querySelector('input[name="server"]').focus();
-        }, false);
+        if (_converse.locked_muc_domain) {
+          this.updateRoomsList();
+        } else {
+          this.el.addEventListener('shown.bs.modal', () => this.el.querySelector('input[name="server"]').focus(), false);
+        }
       },
 
       openRoom(ev) {
@@ -53588,12 +53603,26 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].plugins
         'submit form.add-chatroom': 'openChatRoom'
       },
 
+      initialize() {
+        _converse.BootstrapModal.prototype.initialize.apply(this, arguments);
+
+        this.model.on('change:muc_domain', this.render, this);
+      },
+
       toHTML() {
+        let placeholder = '';
+
+        if (!_converse.locked_muc_domain) {
+          const muc_domain = this.model.get('muc_domain') || _converse.muc_domain;
+
+          placeholder = muc_domain ? `name@${muc_domain}` : __('name@conference.example.org');
+        }
+
         return templates_add_chatroom_modal_html__WEBPACK_IMPORTED_MODULE_5___default()(_.extend(this.model.toJSON(), {
           'heading_new_chatroom': __('Enter a new Groupchat'),
-          'label_room_address': __('Groupchat address'),
+          'label_room_address': _converse.muc_domain ? __('Groupchat name') : __('Groupchat address'),
           'label_nickname': __('Optional nickname'),
-          'chatroom_placeholder': __('name@conference.example.org'),
+          'chatroom_placeholder': placeholder,
           'label_join': __('Join')
         }));
       },
@@ -53623,7 +53652,17 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].plugins
           data.nick = undefined;
         }
 
-        _converse.api.rooms.open(data.jid, data);
+        let jid;
+
+        if (_converse.locked_muc_domain || _converse.muc_domain && !u.isValidJID(data.jid)) {
+          jid = `${Strophe.escapeNode(data.jid)}@${_converse.muc_domain}`;
+        } else {
+          jid = data.jid;
+        }
+
+        _converse.api.rooms.open(jid, _.extend(data, {
+          jid
+        }));
 
         this.modal.hide();
         ev.target.reset();
@@ -66093,7 +66132,6 @@ _converse_core__WEBPACK_IMPORTED_MODULE_6__["default"].plugins.add('converse-muc
       auto_join_on_invite: false,
       auto_join_rooms: [],
       auto_register_muc_nickname: false,
-      muc_domain: undefined,
       muc_history_max_stanzas: undefined,
       muc_instant_rooms: true,
       muc_nickname_from_jid: false
@@ -93667,18 +93705,23 @@ return __p
 
 var _ = {escape:__webpack_require__(/*! ./node_modules/lodash/escape.js */ "./node_modules/lodash/escape.js")};
 module.exports = function(o) {
-var __t, __p = '', __e = _.escape;
+var __t, __p = '', __e = _.escape, __j = Array.prototype.join;
+function print() { __p += __j.call(arguments, '') }
 __p += '<!-- src/templates/list_chatrooms_modal.html -->\n<div class="modal fade" id="list-chatrooms-modal" tabindex="-1" role="dialog" aria-labelledby="list-chatrooms-modal-label" aria-hidden="true">\n    <div class="modal-dialog" role="document">\n        <div class="modal-content">\n            <div class="modal-header">\n                <h5 class="modal-title"\n                    id="list-chatrooms-modal-label">' +
 __e(o.heading_list_chatrooms) +
-'</h5>\n                <button type="button" class="close" data-dismiss="modal" aria-label="Close">\n                    <span aria-hidden="true">×</span>\n                </button>\n            </div>\n            <div class="modal-body d-flex flex-column">\n                <form class="converse-form list-chatrooms">\n                    <div class="form-group">\n                        <label for="chatroom">' +
+'</h5>\n                <button type="button" class="close" data-dismiss="modal" aria-label="Close">\n                    <span aria-hidden="true">×</span>\n                </button>\n            </div>\n            <div class="modal-body d-flex flex-column">\n                ';
+ if (o.show_form) { ;
+__p += '\n                <form class="converse-form list-chatrooms">\n                    <div class="form-group">\n                        <label for="chatroom">' +
 __e(o.label_server_address) +
 ':</label>\n                        <input type="text" value="' +
 __e(o.muc_domain) +
 '" required="required" name="server" class="form-control" placeholder="' +
 __e(o.server_placeholder) +
-'"/>\n                    </div>\n                    <input type="submit" class="btn btn-primary" name="join" value="' +
+'"/>\n                    </div>\n                    <input type="submit" class="btn btn-primary" name="list" value="' +
 __e(o.label_query) +
-'"/>\n                </form>\n                <ul class="available-chatrooms list-group"></ul>\n            </div>\n        </div>\n    </div>\n</div>\n';
+'"/>\n                </form>\n                ';
+ } ;
+__p += '\n                <ul class="available-chatrooms list-group"></ul>\n            </div>\n        </div>\n    </div>\n</div>\n';
 return __p
 };
 

+ 24 - 5
docs/source/configuration.rst

@@ -256,7 +256,8 @@ auto_list_rooms
 * Default:  ``false``
 
 If true, and the XMPP server on which the current user is logged in supports
-multi-user chat, then a list of rooms on that server will be fetched.
+multi-user chat, then a list of rooms on that server will be fetched in the
+"Query for Groupchats" modal.
 
 Not recommended for servers with lots of chatrooms.
 
@@ -264,6 +265,10 @@ For each room on the server a query is made to fetch further details (e.g.
 features, number of occupants etc.), so on servers with many rooms this
 option will create lots of extra connection traffic.
 
+If the `muc_domain`_ is locked with the `locked_muc_domain`_ setting, then
+rooms will automatically be fetched in the "Query for Groupchats" modal,
+regardless of the value of this setting.
+
 .. _`auto_login`:
 
 auto_login
@@ -869,6 +874,15 @@ For example, if ``locked_domain`` is set to ``example.org``, then the user
 Additionally, only users registered on the ``example.org`` host can log in, no
 other users are allowed to log in.
 
+locked_muc_domain
+-----------------
+
+* Default: ``false``
+
+This setting allows you to restrict the multi-user chat (MUC) domain to only the value
+specified in `muc_domain`_.
+
+
 message_archiving
 -----------------
 
@@ -941,11 +955,16 @@ muc_domain
 
 * Default:  ``undefined``
 
-The MUC (multi-user chat) domain that should be used. By default Converse
-will attempt to get the MUC domain from the XMPP host of the currently logged in
-user.
+The default MUC (multi-user chat) domain that should be used.
+
+When setting this value, users can only enter the name when opening a new MUC,
+and don't have to add the whole address (i.e. including the domain part).
+
+Users can however still enter the domain and they can still open MUCs with
+other domains.
 
-This setting will override that.
+If you want to restrict MUCs to only this domain, then set `locked_domain`_ to
+``true``.
 
 muc_history_max_stanzas
 -----------------------

+ 159 - 7
spec/muc.js

@@ -3958,13 +3958,19 @@
                     async function (done, _converse) {
 
                 test_utils.openControlBox();
-                _converse.emit('rosterContactsFetched');
+                await test_utils.waitForRoster(_converse, 'current', 0);
 
                 const roomspanel = _converse.chatboxviews.get('controlbox').roomspanel;
                 roomspanel.el.querySelector('.show-add-muc-modal').click();
                 test_utils.closeControlBox(_converse);
                 const modal = roomspanel.add_room_modal;
                 await test_utils.waitUntil(() => u.isVisible(modal.el), 1000)
+
+                let label_name = modal.el.querySelector('label[for="chatroom"]');
+                expect(label_name.textContent).toBe('Groupchat address:');
+                let name_input = modal.el.querySelector('input[name="chatroom"]');
+                expect(name_input.placeholder).toBe('name@conference.example.org');
+
                 expect(modal.el.querySelector('.modal-title').textContent).toBe('Enter a new Groupchat');
                 spyOn(_converse.ChatRoom.prototype, 'getRoomFeatures').and.callFake(() => Promise.resolve());
                 roomspanel.delegateEvents(); // We need to rebind all events otherwise our spy won't be called
@@ -3972,6 +3978,83 @@
                 modal.el.querySelector('form input[type="submit"]').click();
                 await test_utils.waitUntil(() => _converse.chatboxes.length);
                 await test_utils.waitUntil(() => sizzle('.chatroom', _converse.el).filter(u.isVisible).length === 1);
+
+                roomspanel.model.set('muc_domain', 'muc.example.org');
+                roomspanel.el.querySelector('.show-add-muc-modal').click();
+                label_name = modal.el.querySelector('label[for="chatroom"]');
+                expect(label_name.textContent).toBe('Groupchat address:');
+                name_input = modal.el.querySelector('input[name="chatroom"]');
+                expect(name_input.placeholder).toBe('name@muc.example.org');
+                done();
+            }));
+
+            it("doesn't require the domain when muc_domain is set",
+                mock.initConverse(
+                    null, ['rosterGroupsFetched', 'chatBoxesFetched'], {'muc_domain': 'muc.example.org'},
+                    async function (done, _converse) {
+
+                test_utils.openControlBox();
+                const roomspanel = _converse.chatboxviews.get('controlbox').roomspanel;
+                roomspanel.el.querySelector('.show-add-muc-modal').click();
+                const modal = roomspanel.add_room_modal;
+                await test_utils.waitUntil(() => u.isVisible(modal.el), 1000)
+                expect(modal.el.querySelector('.modal-title').textContent).toBe('Enter a new Groupchat');
+                spyOn(_converse.ChatRoom.prototype, 'getRoomFeatures').and.callFake(() => Promise.resolve());
+                roomspanel.delegateEvents(); // We need to rebind all events otherwise our spy won't be called
+                const label_name = modal.el.querySelector('label[for="chatroom"]');
+                expect(label_name.textContent).toBe('Groupchat name:');
+                let name_input = modal.el.querySelector('input[name="chatroom"]');
+                expect(name_input.placeholder).toBe('name@muc.example.org');
+                name_input.value = 'lounge';
+                modal.el.querySelector('form input[type="submit"]').click();
+                await test_utils.waitUntil(() => _converse.chatboxes.length);
+                await test_utils.waitUntil(() => sizzle('.chatroom', _converse.el).filter(u.isVisible).length === 1);
+                expect(_.includes(_converse.chatboxes.models.map(m => m.get('id')), 'lounge@muc.example.org')).toBe(true);
+
+                // However, you can still open MUCs with different domains
+                roomspanel.el.querySelector('.show-add-muc-modal').click();
+                await test_utils.waitUntil(() => u.isVisible(modal.el), 1000);
+                name_input = modal.el.querySelector('input[name="chatroom"]');
+                name_input.value = 'lounge@conference.example.org';
+                modal.el.querySelector('form input[type="submit"]').click();
+                await test_utils.waitUntil(() => _converse.chatboxes.models.filter(c => c.get('type') === 'chatroom').length === 2);
+                await test_utils.waitUntil(() => sizzle('.chatroom', _converse.el).filter(u.isVisible).length === 2);
+                expect(_.includes(_converse.chatboxes.models.map(m => m.get('id')), 'lounge@conference.example.org')).toBe(true);
+                done();
+            }));
+
+            it("only uses the muc_domain is locked_muc_domain is true",
+                mock.initConverse(
+                    null, ['rosterGroupsFetched', 'chatBoxesFetched'], {'muc_domain': 'muc.example.org', 'locked_muc_domain': true},
+                    async function (done, _converse) {
+
+                test_utils.openControlBox();
+                const roomspanel = _converse.chatboxviews.get('controlbox').roomspanel;
+                roomspanel.el.querySelector('.show-add-muc-modal').click();
+                const modal = roomspanel.add_room_modal;
+                await test_utils.waitUntil(() => u.isVisible(modal.el), 1000)
+                expect(modal.el.querySelector('.modal-title').textContent).toBe('Enter a new Groupchat');
+                spyOn(_converse.ChatRoom.prototype, 'getRoomFeatures').and.callFake(() => Promise.resolve());
+                roomspanel.delegateEvents(); // We need to rebind all events otherwise our spy won't be called
+                const label_name = modal.el.querySelector('label[for="chatroom"]');
+                expect(label_name.textContent).toBe('Groupchat name:');
+                let name_input = modal.el.querySelector('input[name="chatroom"]');
+                expect(name_input.placeholder).toBe('');
+                name_input.value = 'lounge';
+                modal.el.querySelector('form input[type="submit"]').click();
+                await test_utils.waitUntil(() => _converse.chatboxes.length);
+                await test_utils.waitUntil(() => sizzle('.chatroom', _converse.el).filter(u.isVisible).length === 1);
+                expect(_.includes(_converse.chatboxes.models.map(m => m.get('id')), 'lounge@muc.example.org')).toBe(true);
+
+                // However, you can still open MUCs with different domains
+                roomspanel.el.querySelector('.show-add-muc-modal').click();
+                await test_utils.waitUntil(() => u.isVisible(modal.el), 1000);
+                name_input = modal.el.querySelector('input[name="chatroom"]');
+                name_input.value = 'lounge@conference';
+                modal.el.querySelector('form input[type="submit"]').click();
+                await test_utils.waitUntil(() => _converse.chatboxes.models.filter(c => c.get('type') === 'chatroom').length === 2);
+                await test_utils.waitUntil(() => sizzle('.chatroom', _converse.el).filter(u.isVisible).length === 2);
+                expect(_.includes(_converse.chatboxes.models.map(m => m.get('id')), 'lounge\\40conference@muc.example.org')).toBe(true);
                 done();
             }));
         });
@@ -4002,7 +4085,9 @@
                 // See: http://xmpp.org/extensions/xep-0045.html#disco-rooms
                 expect(modal.el.querySelectorAll('.available-chatrooms li').length).toBe(0);
 
-                const input = modal.el.querySelector('input[name="server"]').value = 'chat.shakespear.lit';
+                const server_input = modal.el.querySelector('input[name="server"]');
+                expect(server_input.placeholder).toBe('conference.example.org');
+                const input = server_input.value = 'chat.shakespear.lit';
                 modal.el.querySelector('input[type="submit"]').click();
                 await test_utils.waitUntil(() => _converse.chatboxes.length);
                 expect(sent_stanza.toLocaleString()).toBe(
@@ -4010,7 +4095,6 @@
                         `<query xmlns="http://jabber.org/protocol/disco#items"/>`+
                     `</iq>`
                 );
-
                 const iq = $iq({
                     from:'muc.localhost',
                     to:'dummy@localhost/pda',
@@ -4029,13 +4113,19 @@
                 .c('item', { jid:'street@chat.shakespeare.lit', name:'A street'}).nodeTree;
                 _converse.connection._dataRecv(test_utils.createRequest(iq));
 
-                await test_utils.waitUntil(() => modal.el.querySelectorAll('.available-chatrooms li').length === 5);
+                await test_utils.waitUntil(() => modal.el.querySelectorAll('.available-chatrooms li').length === 11);
                 const rooms = modal.el.querySelectorAll('.available-chatrooms li');
                 expect(rooms[0].textContent.trim()).toBe("Groupchats found:");
                 expect(rooms[1].textContent.trim()).toBe("A Lonely Heath");
                 expect(rooms[2].textContent.trim()).toBe("A Dark Cave");
                 expect(rooms[3].textContent.trim()).toBe("The Palace");
                 expect(rooms[4].textContent.trim()).toBe("Macbeth's Castle");
+                expect(rooms[5].textContent.trim()).toBe('Capulet\'s Orchard');
+                expect(rooms[6].textContent.trim()).toBe('Friar Laurence\'s cell');
+                expect(rooms[7].textContent.trim()).toBe('Hall in Capulet\'s house');
+                expect(rooms[8].textContent.trim()).toBe('Juliet\'s chamber');
+                expect(rooms[9].textContent.trim()).toBe('A public place');
+                expect(rooms[10].textContent.trim()).toBe('A street');
 
                 rooms[4].querySelector('.open-room').click();
                 await test_utils.waitUntil(() => _converse.chatboxes.length > 1);
@@ -4044,6 +4134,71 @@
                 expect(view.el.querySelector('.chat-head-chatroom').textContent.trim()).toBe("Macbeth's Castle");
                 done();
             }));
+
+            it("is pre-filled with the muc_domain",
+                mock.initConverse(
+                    null, ['rosterGroupsFetched', 'chatBoxesFetched'],
+                    {'muc_domain': 'muc.example.org'},
+                    async function (done, _converse) {
+
+                test_utils.openControlBox();
+                const roomspanel = _converse.chatboxviews.get('controlbox').roomspanel;
+                roomspanel.el.querySelector('.show-list-muc-modal').click();
+                test_utils.closeControlBox(_converse);
+                const modal = roomspanel.list_rooms_modal;
+                await test_utils.waitUntil(() => u.isVisible(modal.el), 1000);
+                const server_input = modal.el.querySelector('input[name="server"]');
+                expect(server_input.value).toBe('muc.example.org');
+                done();
+            }));
+
+            it("doesn't let you set the MUC domain if it's locked",
+                mock.initConverse(
+                    null, ['rosterGroupsFetched', 'chatBoxesFetched'],
+                    {'muc_domain': 'chat.shakespeare.lit', 'locked_muc_domain': true},
+                    async function (done, _converse) {
+
+                test_utils.openControlBox();
+                const roomspanel = _converse.chatboxviews.get('controlbox').roomspanel;
+                roomspanel.el.querySelector('.show-list-muc-modal').click();
+                test_utils.closeControlBox(_converse);
+                const modal = roomspanel.list_rooms_modal;
+                await test_utils.waitUntil(() => u.isVisible(modal.el), 1000);
+                spyOn(_converse.ChatRoom.prototype, 'getRoomFeatures').and.callFake(() => Promise.resolve());
+                roomspanel.delegateEvents(); // We need to rebind all events otherwise our spy won't be called
+
+                expect(modal.el.querySelector('input[name="server"]')).toBe(null);
+                expect(modal.el.querySelector('input[type="submit"]')).toBe(null);
+                await test_utils.waitUntil(() => _converse.chatboxes.length);
+                const sent_stanza = await test_utils.waitUntil(() =>
+                    _converse.connection.sent_stanzas.filter(
+                        s => sizzle(`query[xmlns="http://jabber.org/protocol/disco#items"]`, s).length).pop()
+                );
+                expect(Strophe.serialize(sent_stanza)).toBe(
+                    `<iq from="dummy@localhost/resource" id="${sent_stanza.getAttribute('id')}" `+
+                            `to="chat.shakespeare.lit" type="get" xmlns="jabber:client">`+
+                        `<query xmlns="http://jabber.org/protocol/disco#items"/>`+
+                    `</iq>`
+                );
+                const iq = $iq({
+                    from:'muc.localhost',
+                    to:'dummy@localhost/pda',
+                    id: sent_stanza.getAttribute('id'),
+                    type:'result'
+                }).c('query')
+                .c('item', { jid:'heath@chat.shakespeare.lit', name:'A Lonely Heath'}).up()
+                .c('item', { jid:'coven@chat.shakespeare.lit', name:'A Dark Cave'}).up()
+                .c('item', { jid:'forres@chat.shakespeare.lit', name:'The Palace'}).up()
+                _converse.connection._dataRecv(test_utils.createRequest(iq));
+
+                await test_utils.waitUntil(() => modal.el.querySelectorAll('.available-chatrooms li').length === 4);
+                const rooms = modal.el.querySelectorAll('.available-chatrooms li');
+                expect(rooms[0].textContent.trim()).toBe("Groupchats found:");
+                expect(rooms[1].textContent.trim()).toBe("A Lonely Heath");
+                expect(rooms[2].textContent.trim()).toBe("A Dark Cave");
+                expect(rooms[3].textContent.trim()).toBe("The Palace");
+                done();
+            }));
         });
 
         describe("The \"Groupchats\" section", function () {
@@ -4052,9 +4207,6 @@
                 mock.initConverse(
                     null, ['rosterGroupsFetched'], {'allow_bookmarks': false},
                     async function (done, _converse) {
-                // XXX: we set `allow_bookmarks` to false, so that the groupchats
-                // list gets rendered. Otherwise we would have to mock
-                // the bookmark stanza exchange.
 
                 test_utils.openControlBox();
                 const roomspanel = _converse.chatboxviews.get('controlbox').roomspanel;

+ 39 - 7
src/converse-muc-views.js

@@ -103,6 +103,8 @@ converse.plugins.add('converse-muc-views', {
         _converse.api.settings.update({
             'auto_list_rooms': false,
             'muc_disable_moderator_commands': false,
+            'muc_domain': undefined,
+            'locked_muc_domain': undefined,
             'muc_show_join_leave': true,
             'roomconfig_whitelist': [],
             'visible_toolbar_buttons': {
@@ -110,6 +112,10 @@ converse.plugins.add('converse-muc-views', {
             }
         });
 
+        if (_converse.locked_muc_domain && !_.isString(_converse.muc_domain)) {
+            throw new Error("Config Error: it makes no sense to set locked_muc_domain "+
+                            "to true when muc_domain is not set");
+        }
 
         function ___ (str) {
             /* This is part of a hack to get gettext to scan strings to be
@@ -266,22 +272,32 @@ converse.plugins.add('converse-muc-views', {
 
             initialize () {
                 _converse.BootstrapModal.prototype.initialize.apply(this, arguments);
+                if (_converse.muc_domain && !this.model.get('muc_domain')) {
+                    this.model.save('muc_domain', _converse.muc_domain);
+                }
                 this.model.on('change:muc_domain', this.onDomainChange, this);
             },
 
             toHTML () {
+                const muc_domain = this.model.get('muc_domain') || _converse.muc_domain;
                 return tpl_list_chatrooms_modal(_.extend(this.model.toJSON(), {
                     'heading_list_chatrooms': __('Query for Groupchats'),
                     'label_server_address': __('Server address'),
                     'label_query': __('Show groupchats'),
-                    'server_placeholder': __('conference.example.org')
+                    'show_form': !_converse.locked_muc_domain,
+                    'server_placeholder': muc_domain ? muc_domain : __('conference.example.org')
                 }));
             },
 
             afterRender () {
-                this.el.addEventListener('shown.bs.modal', () => {
-                    this.el.querySelector('input[name="server"]').focus();
-                }, false);
+                if (_converse.locked_muc_domain) {
+                    this.updateRoomsList();
+                } else {
+                    this.el.addEventListener('shown.bs.modal',
+                        () => this.el.querySelector('input[name="server"]').focus(),
+                        false
+                    );
+                }
             },
 
             openRoom (ev) {
@@ -384,12 +400,22 @@ converse.plugins.add('converse-muc-views', {
                 'submit form.add-chatroom': 'openChatRoom'
             },
 
+            initialize () {
+                _converse.BootstrapModal.prototype.initialize.apply(this, arguments);
+                this.model.on('change:muc_domain', this.render, this);
+            },
+
             toHTML () {
+                let placeholder = '';
+                if (!_converse.locked_muc_domain) {
+                    const muc_domain = this.model.get('muc_domain') || _converse.muc_domain;
+                    placeholder = muc_domain ? `name@${muc_domain}` : __('name@conference.example.org');
+                }
                 return tpl_add_chatroom_modal(_.extend(this.model.toJSON(), {
                     'heading_new_chatroom': __('Enter a new Groupchat'),
-                    'label_room_address': __('Groupchat address'),
+                    'label_room_address': _converse.muc_domain ? __('Groupchat name') :  __('Groupchat address'),
                     'label_nickname': __('Optional nickname'),
-                    'chatroom_placeholder': __('name@conference.example.org'),
+                    'chatroom_placeholder': placeholder,
                     'label_join': __('Join'),
                 }));
             },
@@ -417,7 +443,13 @@ converse.plugins.add('converse-muc-views', {
                     // Make sure defaults apply if no nick is provided.
                     data.nick = undefined;
                 }
-                _converse.api.rooms.open(data.jid, data);
+                let jid;
+                if (_converse.locked_muc_domain || (_converse.muc_domain && !u.isValidJID(data.jid))) {
+                    jid = `${Strophe.escapeNode(data.jid)}@${_converse.muc_domain}`;
+                } else {
+                    jid = data.jid
+                }
+                _converse.api.rooms.open(jid, _.extend(data, {jid}));
                 this.modal.hide();
                 ev.target.reset();
             }

+ 0 - 1
src/headless/converse-muc.js

@@ -118,7 +118,6 @@ converse.plugins.add('converse-muc', {
             auto_join_on_invite: false,
             auto_join_rooms: [],
             auto_register_muc_nickname: false,
-            muc_domain: undefined,
             muc_history_max_stanzas: undefined,
             muc_instant_rooms: true,
             muc_nickname_from_jid: false

+ 3 - 1
src/templates/list_chatrooms_modal.html

@@ -9,13 +9,15 @@
                 </button>
             </div>
             <div class="modal-body d-flex flex-column">
+                {[ if (o.show_form) { ]}
                 <form class="converse-form list-chatrooms">
                     <div class="form-group">
                         <label for="chatroom">{{{o.label_server_address}}}:</label>
                         <input type="text" value="{{{o.muc_domain}}}" required="required" name="server" class="form-control" placeholder="{{{o.server_placeholder}}}"/>
                     </div>
-                    <input type="submit" class="btn btn-primary" name="join" value="{{{o.label_query}}}"/>
+                    <input type="submit" class="btn btn-primary" name="list" value="{{{o.label_query}}}"/>
                 </form>
+                {[ } ]}
                 <ul class="available-chatrooms list-group"></ul>
             </div>
         </div>