瀏覽代碼

Login form: Allow user to choose the connection URL

if `websocket_url` and `bosh_service_url` are not set and XEP-0156
lookup was unsuccessful.
JC Brand 3 年之前
父節點
當前提交
048560908e

+ 12 - 18
src/headless/core.js

@@ -612,30 +612,24 @@ _converse.initConnection = function () {
         if (api.settings.get("authentication") === _converse.PREBIND) {
             throw new Error("authentication is set to 'prebind' but we don't have a BOSH connection");
         }
-        if (! api.settings.get("websocket_url")) {
-            throw new Error("initConnection: you must supply a value for either the bosh_service_url or websocket_url or both.");
-        }
     }
 
+    let connection_url = '';
     const XMPPConnection = _converse.isTestEnv() ? MockConnection : Connection;
     if (('WebSocket' in window || 'MozWebSocket' in window) && api.settings.get("websocket_url")) {
-        _converse.connection = new XMPPConnection(
-            api.settings.get("websocket_url"),
-            Object.assign(_converse.default_connection_options, api.settings.get("connection_options"))
-        );
+        connection_url = api.settings.get('websocket_url');
     } else if (api.settings.get('bosh_service_url')) {
-        _converse.connection = new XMPPConnection(
-            api.settings.get('bosh_service_url'),
-            Object.assign(
-                _converse.default_connection_options,
-                api.settings.get("connection_options"),
-                {'keepalive': api.settings.get("keepalive")}
-            )
-        );
-    } else {
-        throw new Error("initConnection: this browser does not support "+
-                        "websockets and bosh_service_url wasn't specified.");
+        connection_url = api.settings.get('bosh_service_url');
     }
+    _converse.connection = new XMPPConnection(
+        connection_url,
+        Object.assign(
+            _converse.default_connection_options,
+            api.settings.get("connection_options"),
+            {'keepalive': api.settings.get("keepalive")}
+        )
+    );
+
     setUpXMLLogging();
     /**
      * Triggered once the `Connection` constructor has been initialized, which

+ 3 - 0
src/headless/shared/connection.js

@@ -102,6 +102,9 @@ export class Connection extends Strophe.Connection {
             const domain = Strophe.getDomainFromJid(jid);
             await this.discoverConnectionMethods(domain);
         }
+        if (!api.settings.get('bosh_service_url') && !api.settings.get("websocket_url")) {
+            throw new Error("You must supply a value for either the bosh_service_url or websocket_url or both.");
+        }
         super.connect(jid, password, callback || this.onConnectStatusChanged, BOSH_WAIT);
     }
 

+ 1 - 19
src/headless/tests/converse.js

@@ -4,24 +4,6 @@ const { Strophe } = converse.env;
 
 describe("Converse", function() {
 
-    describe("Authentication", function () {
-
-        it("needs either a bosh_service_url a websocket_url or both", mock.initConverse(async (_converse) => {
-            const { api } = _converse;
-            const url = api.settings.get('bosh_service_url');
-            const connection = _converse.connection;
-            _converse.api.settings.set('bosh_service_url', undefined);
-            delete _converse.connection;
-            try {
-                await _converse.initConnection();
-            } catch (e) {
-                _converse.api.settings.set('bosh_service_url', url);
-                _converse.connection = connection;
-                expect(e.message).toBe("initConnection: you must supply a value for either the bosh_service_url or websocket_url or both.");
-            }
-        }));
-    });
-
     describe("A chat state indication", function () {
 
         it("are sent out when the client becomes or stops being idle",
@@ -53,7 +35,7 @@ describe("Converse", function() {
     describe("Automatic status change", function () {
 
         it("happens when the client is idle for long enough",
-                mock.initConverse(['initialized'], {}, async (_converse) => {
+                mock.initConverse(['chatBoxesFetched'], {}, async (_converse) => {
 
             const { api } = _converse;
             let i = 0;

+ 40 - 2
src/plugins/controlbox/loginform.js

@@ -1,6 +1,7 @@
 import bootstrap from 'bootstrap.native';
 import tpl_login_panel from './templates/loginform.js';
 import { CustomElement } from 'shared/components/element.js';
+import { Model } from '@converse/skeletor/src/model.js';
 import { __ } from 'i18n';
 import { _converse, api, converse } from '@converse/headless/core';
 
@@ -10,7 +11,9 @@ const { Strophe, u } = converse.env;
 class LoginForm extends CustomElement {
 
     initialize () {
-        this.listenTo(_converse.connfeedback, 'change', this.requesUpdate);
+        this.model = new Model();
+        this.listenTo(_converse.connfeedback, 'change', () => this.requestUpdate());
+        this.listenTo(this.model, 'change', () => this.requestUpdate());
     }
 
     render () {
@@ -21,6 +24,35 @@ class LoginForm extends CustomElement {
         this.initPopovers();
     }
 
+    async onLoginFormSubmitted (ev) {
+        ev?.preventDefault();
+        if (api.settings.get('bosh_service_url') ||
+                api.settings.get('websocket_url') ||
+                this.model.get('show_connection_url_input')) {
+            this.authenticate(ev);
+        }
+        await this.discoverConnectionMethods(ev);
+        if (api.settings.get('bosh_service_url') || api.settings.get('websocket_url')) {
+            this.authenticate(ev);
+        } else {
+            this.model.set('show_connection_url_input', true);
+        }
+    }
+
+    // eslint-disable-next-line class-methods-use-this
+    async discoverConnectionMethods (ev) {
+        if (!api.settings.get("discover_connection_methods")) {
+            return;
+        }
+        const form_data = new FormData(ev.target);
+        const jid = form_data.get('jid');
+        const domain = Strophe.getDomainFromJid(jid);
+        if (!_converse.connection?.jid || (jid && !u.isSameDomain(_converse.connection.jid, jid))) {
+            await _converse.initConnection();
+        }
+        return _converse.connection.discoverConnectionMethods(domain);
+    }
+
     initPopovers () {
         Array.from(this.querySelectorAll('[data-title]')).forEach(el => {
             new bootstrap.Popover(el, {
@@ -52,7 +84,6 @@ class LoginForm extends CustomElement {
      * @param { Event } ev
      */
     authenticate (ev) {
-        ev?.preventDefault();
         if (api.settings.get('authentication') === _converse.ANONYMOUS) {
             return this.connect(_converse.jid, null);
         }
@@ -61,6 +92,13 @@ class LoginForm extends CustomElement {
         }
 
         const form_data = new FormData(ev.target);
+        const connection_url  = form_data.get('connection-url');
+        if (connection_url?.startsWith('ws')) {
+            api.settings.set('websocket_url', connection_url);
+        } else if (connection_url?.startsWith('http')) {
+            api.settings.set('bosh_service_url', connection_url);
+        }
+
         _converse.config.save({ 'trusted': (form_data.get('trusted') && true) || false });
 
         let jid = form_data.get('jid');

+ 20 - 1
src/plugins/controlbox/templates/loginform.js

@@ -24,6 +24,24 @@ const trust_checkbox = (checked) => {
     `;
 }
 
+const connection_url_input = () => {
+    const i18n_connection_url = __('Connection URL');
+    const i18n_form_help = __('HTTP or websocket URL that is used to connect to your XMPP server');
+    const i18n_placeholder = __('e.g. wss://example.org/xmpp-websocket');
+    return html`
+        <div class="form-group fade-in">
+            <label for="converse-conn-url">${i18n_connection_url}</label>
+            <p class="form-help instructions">${i18n_form_help}</p>
+            <input id="converse-conn-url"
+                   class="form-control"
+                   required="required"
+                   type="url"
+                   name="connection-url"
+                   placeholder="${i18n_placeholder}"/>
+        </div>
+    `;
+}
+
 const password_input = () => {
     const i18n_password = __('Password');
     return html`
@@ -73,6 +91,7 @@ const auth_fields = (el) => {
                 placeholder="${placeholder_username}"/>
         </div>
         ${ (authentication !== _converse.EXTERNAL) ? password_input() : '' }
+        ${ el.model.get('show_connection_url_input') ? connection_url_input() : '' }
         ${ show_trust_checkbox ? trust_checkbox(show_trust_checkbox === 'off' ? false : true) : '' }
         <fieldset class="form-group buttons">
             <input class="btn btn-primary" type="submit" value="${i18n_login}"/>
@@ -105,7 +124,7 @@ export default (el) => {
     const conn_feedback_message = _converse.connfeedback.get('message');
     return html`
         <converse-brand-heading></converse-brand-heading>
-        <form id="converse-login" class="converse-form" method="post" @submit=${el.authenticate}>
+        <form id="converse-login" class="converse-form" method="post" @submit=${el.onLoginFormSubmitted}>
             <div class="conn-feedback fade-in ${ !pretty_status ? 'hidden' : feedback_class}">
                 <p class="feedback-subject">${ pretty_status }</p>
                 <p class="feedback-message ${ !conn_feedback_message ? 'hidden' : '' }">${conn_feedback_message}</p>

+ 2 - 1
webpack.html

@@ -38,8 +38,9 @@
             muc_domain: 'conference.chat.example.org',
             muc_respect_autojoin: true,
             view_mode: 'fullscreen',
-            websocket_url: 'ws://chat.example.org:5380/xmpp-websocket',
+            // websocket_url: 'ws://chat.example.org:5380/xmpp-websocket',
             // bosh_service_url: 'http://chat.example.org:5280/http-bind',
+            allow_user_defined_connection_url: true,
             muc_show_logs_before_join: true,
             whitelisted_plugins: ['converse-debug', 'converse-batched-probe'],
             blacklisted_plugins: [],