Kaynağa Gözat

New config option `enable_roster_versioning`

JC Brand 2 ay önce
ebeveyn
işleme
e72825e2af

+ 2 - 1
CHANGES.md

@@ -77,10 +77,11 @@
 - Add new themes 'Cyberpunk' and 'Nordic' and remove the old 'Concord' theme.
 - Improved accessibility.
 - New "getOccupantActionButtons" hook, so that plugins can add actions on MUC occupants.
+- Update the "Add MUC" modal to add validation and to allow specifying only the MUC name and not the whole address.
 - MUC occupants badges: displays short labels, with full label as title.
 - New config option [stanza_timeout](https://conversejs.org/docs/html/configuration.html#show-background)
 - New config option [lazy_load_vcards](https://conversejs.org/docs/html/configuration.html#lazy-load-vcards)
-- Update the "Add MUC" modal to add validation and to allow specifying only the MUC name and not the whole address.
+- New config option [enable_roster_versioning](https://conversejs.org/docs/html/configuration.html#enable-roster-versioning)
 
 ### Default config changes
 - Make `fullscreen` the default `view_mode`.

+ 20 - 0
docs/source/configuration.rst

@@ -920,6 +920,16 @@ The app servers are specified with the `push_app_servers`_ option.
     Registering a push app server against a MUC domain is not (yet) standardized
     and this feature should be considered experimental.
 
+
+enable_roster_versioning
+------------------------
+
+* Default: ``true``
+
+Determines support for `roster versioning <https://xmpp.org/rfcs/rfc6121.html#roster-versioning>`_.
+If set to ``false``, the full roster will always be fetched.
+
+
 enable_smacks
 -------------
 
@@ -2097,6 +2107,16 @@ everywhere.
 This warning isn't applicable to all deployments of Converse and can therefore
 be turned off by setting this config variable to ``false``.
 
+
+show_self_in_roster
+-------------------
+
+* Default: ``true``
+
+If true, you'll see yourself in your list of contacts (aka the "roster") and
+can open a chat with yourself.
+
+
 show_send_button
 ----------------
 

+ 8 - 4
src/headless/plugins/roster/contacts.js

@@ -278,15 +278,19 @@ class RosterContacts extends Collection {
         /**
          * When the roster receives a push event from server (i.e. new entry in your contacts roster).
          * @event _converse#rosterPush
-         * @type { Element }
+         * @type {Element}
          * @example _converse.api.listen.on('rosterPush', iq => { ... });
          */
         api.trigger('rosterPush', iq);
         return;
     }
 
-    rosterVersioningSupported() {
-        return api.disco.stream.getFeature('ver', 'urn:xmpp:features:rosterver') && this.data.get('version');
+    shouldUseRosterVersioning() {
+        return (
+            api.settings.get('enable_roster_versioning') &&
+            this.data.get('version') &&
+            api.disco.stream.getFeature('ver', 'urn:xmpp:features:rosterver')
+        );
     }
 
     /**
@@ -298,7 +302,7 @@ class RosterContacts extends Collection {
         const stanza = stx`
             <iq type="get" id="${u.getUniqueId('roster')}" xmlns="jabber:client">
                 <query xmlns="${Strophe.NS.ROSTER}"
-                    ${this.rosterVersioningSupported() ? Stanza.unsafeXML(`ver="${this.data.get('version')}"`) : ''}>
+                    ${this.shouldUseRosterVersioning() ? Stanza.unsafeXML(`ver="${this.data.get('version')}"`) : ''}>
                 </query>
             </iq>`;
 

+ 2 - 1
src/headless/plugins/roster/plugin.js

@@ -26,9 +26,10 @@ converse.plugins.add('converse-roster', {
 
     initialize () {
         api.settings.extend({
-            show_self_in_roster: true,
             allow_contact_requests: true,
             auto_subscribe: false,
+            enable_roster_versioning: true,
+            show_self_in_roster: true,
             synchronize_availability: true
         });
 

+ 1 - 1
src/headless/types/plugins/roster/contacts.d.ts

@@ -64,7 +64,7 @@ declare class RosterContacts extends Collection {
      * @param {Element} iq - The IQ stanza received from the XMPP server.
      */
     onRosterPush(iq: Element): void;
-    rosterVersioningSupported(): any;
+    shouldUseRosterVersioning(): any;
     /**
      * Fetches the roster from the XMPP server and updates the local state
      * @emits _converse#roster

+ 90 - 31
src/plugins/rosterview/tests/roster.js

@@ -65,7 +65,7 @@ describe("The Contacts Roster", function () {
     it("is populated once we have registered a presence handler", mock.initConverse([], {}, async function (_converse) {
         const IQs = _converse.api.connection.get().IQ_stanzas;
         const stanza = await u.waitUntil(
-            () => IQs.filter(iq => iq.querySelector('iq query[xmlns="jabber:iq:roster"]')).pop());
+            () => IQs.filter(iq => sizzle('iq query[xmlns="jabber:iq:roster"]', iq).length).pop());
 
         expect(stanza).toEqualStanza(
             stx`<iq id="${stanza.getAttribute('id')}" type="get" xmlns="jabber:client">
@@ -83,15 +83,20 @@ describe("The Contacts Roster", function () {
     }));
 
     it("supports roster versioning", mock.initConverse([], {}, async function (_converse) {
-        const IQ_stanzas = _converse.api.connection.get().IQ_stanzas;
+        const { IQ_stanzas } = _converse.api.connection.get();
         let stanza = await u.waitUntil(
-            () => IQ_stanzas.filter(iq => iq.querySelector('iq query[xmlns="jabber:iq:roster"]')).pop()
-        );
-        expect(_converse.roster.data.get('version')).toBeUndefined();
-        expect(Strophe.serialize(stanza)).toBe(
-            `<iq id="${stanza.getAttribute('id')}" type="get" xmlns="jabber:client">`+
-                `<query xmlns="jabber:iq:roster"/>`+
-            `</iq>`);
+            () => IQ_stanzas.filter(iq => sizzle('iq query[xmlns="jabber:iq:roster"]', iq).length).pop());
+
+        const { roster } = _converse.state;
+
+        expect(roster.data.get('version')).toBeUndefined();
+        expect(stanza).toEqualStanza(stx`
+            <iq id="${stanza.getAttribute('id')}" type="get" xmlns="jabber:client">
+                <query xmlns="jabber:iq:roster"/>
+            </iq>`);
+
+        while (IQ_stanzas.length) IQ_stanzas.pop();
+
         let result = stx`
             <iq to="${_converse.api.connection.get().jid}" type="result" id="${stanza.getAttribute('id')}" xmlns="jabber:client">
                 <query xmlns="jabber:iq:roster" ver="ver7">
@@ -101,16 +106,17 @@ describe("The Contacts Roster", function () {
             </iq>`;
         _converse.api.connection.get()._dataRecv(mock.createRequest(result));
 
-        await u.waitUntil(() => _converse.roster.models.length > 1);
-        expect(_converse.roster.data.get('version')).toBe('ver7');
-        expect(_converse.roster.models.length).toBe(2);
+        await u.waitUntil(() => roster.models.length > 1);
+        expect(roster.data.get('version')).toBe('ver7');
+        expect(roster.models.length).toBe(2);
 
-        _converse.roster.fetchFromServer();
-        stanza = _converse.api.connection.get().IQ_stanzas.pop();
-        expect(Strophe.serialize(stanza)).toBe(
-            `<iq id="${stanza.getAttribute('id')}" type="get" xmlns="jabber:client">`+
-                `<query ver="ver7" xmlns="jabber:iq:roster"/>`+
-            `</iq>`);
+        roster.fetchFromServer();
+        stanza = await u.waitUntil(
+            () => IQ_stanzas.filter(iq => sizzle('iq query[xmlns="jabber:iq:roster"]', iq).length).pop());
+        expect(stanza).toEqualStanza(
+            stx`<iq id="${stanza.getAttribute('id')}" type="get" xmlns="jabber:client">
+                <query ver="ver7" xmlns="jabber:iq:roster"/>
+            </iq>`);
 
         result = stx`
             <iq to="${_converse.api.connection.get().jid}" type="result" id="${stanza.getAttribute('id')}" xmlns="jabber:client">
@@ -124,16 +130,68 @@ describe("The Contacts Roster", function () {
                 </query>
             </iq>`;
         _converse.api.connection.get()._dataRecv(mock.createRequest(roster_push));
-        expect(_converse.roster.data.get('version')).toBe('ver34');
-        expect(_converse.roster.models.length).toBe(1);
-        expect(_converse.roster.at(0).get('jid')).toBe('nurse@example.com');
+        expect(roster.data.get('version')).toBe('ver34');
+        expect(roster.models.length).toBe(1);
+        expect(roster.at(0).get('jid')).toBe('nurse@example.com');
+    }));
+
+    it("can ignore roster versioning", mock.initConverse([], { enable_roster_versioning: false }, async function (_converse) {
+        const { IQ_stanzas } = _converse.api.connection.get();
+        let stanza = await u.waitUntil(
+            () => IQ_stanzas.filter(iq => sizzle('iq query[xmlns="jabber:iq:roster"]', iq).length).pop());
+
+        const { roster } = _converse.state;
+
+        expect(roster.data.get('version')).toBeUndefined();
+        expect(stanza).toEqualStanza(stx`
+            <iq id="${stanza.getAttribute('id')}" type="get" xmlns="jabber:client">
+                <query xmlns="jabber:iq:roster"/>
+            </iq>`);
+
+        while (IQ_stanzas.length) IQ_stanzas.pop();
+
+        let result = stx`
+            <iq to="${_converse.api.connection.get().jid}" type="result" id="${stanza.getAttribute('id')}" xmlns="jabber:client">
+                <query xmlns="jabber:iq:roster" ver="ver7">
+                    <item jid="nurse@example.com"/>
+                    <item jid="romeo@example.com"/>
+                </query>
+            </iq>`;
+        _converse.api.connection.get()._dataRecv(mock.createRequest(result));
+
+        await u.waitUntil(() => roster.models.length > 1);
+        expect(roster.data.get('version')).toBe('ver7');
+        expect(roster.models.length).toBe(2);
+
+        roster.fetchFromServer();
+        stanza = await u.waitUntil(
+            () => IQ_stanzas.filter(iq => sizzle('iq query[xmlns="jabber:iq:roster"]', iq).length).pop());
+        expect(stanza).toEqualStanza(
+            stx`<iq id="${stanza.getAttribute('id')}" type="get" xmlns="jabber:client">
+                <query xmlns="jabber:iq:roster"/>
+            </iq>`);
+
+        result = stx`
+            <iq to="${_converse.api.connection.get().jid}" type="result" id="${stanza.getAttribute('id')}" xmlns="jabber:client">
+                <query xmlns="jabber:iq:roster" ver="ver8">
+                    <item jid="nurse@example.com"/>
+                    <item jid='romeo@example.com' subscription='remove'/>
+                </query>
+            </iq>`;
+        _converse.api.connection.get()._dataRecv(mock.createRequest(result));
+
+        await u.waitUntil(() => roster.data.get('version') === 'ver8');
+        expect(roster.models.length).toBe(1);
+        expect(roster.at(0).get('jid')).toBe('nurse@example.com');
     }));
 
     it("also contains contacts with subscription of none", mock.initConverse(
         [], {}, async function (_converse) {
 
-        const sent_IQs = _converse.api.connection.get().IQ_stanzas;
-        const stanza = await u.waitUntil(() => sent_IQs.filter(iq => iq.querySelector('iq query[xmlns="jabber:iq:roster"]')).pop());
+        const { IQ_stanzas } = _converse.api.connection.get();
+        let stanza = await u.waitUntil(
+            () => IQ_stanzas.filter(iq => sizzle('iq query[xmlns="jabber:iq:roster"]', iq).length).pop());
+
         _converse.api.connection.get()._dataRecv(mock.createRequest(stx`
             <iq to="${_converse.api.connection.get().jid}" type="result" id="${stanza.getAttribute('id')}" xmlns="jabber:client">
                 <query xmlns="jabber:iq:roster">
@@ -150,7 +208,7 @@ describe("The Contacts Roster", function () {
             </iq>
         `));
 
-        while (sent_IQs.length) sent_IQs.pop();
+        while (IQ_stanzas.length) IQ_stanzas.pop();
 
         await u.waitUntil(() => _converse.roster.length === 3);
         expect(_converse.roster.pluck('jid')).toEqual(['juliet@example.net', 'mercutio@example.net', 'lord.capulet@example.net']);
@@ -160,9 +218,9 @@ describe("The Contacts Roster", function () {
     it("can be refreshed if loglevel is set to debug", mock.initConverse(
         [], {loglevel: 'debug'}, async function (_converse) {
 
-        const sent_IQs = _converse.api.connection.get().IQ_stanzas;
+        const { IQ_stanzas } = _converse.api.connection.get();
         let stanza = await u.waitUntil(
-            () => sent_IQs.filter(iq => iq.querySelector('iq query[xmlns="jabber:iq:roster"]')).pop());
+            () => IQ_stanzas.filter(iq => sizzle('iq query[xmlns="jabber:iq:roster"]', iq).length).pop());
 
         _converse.api.connection.get()._dataRecv(mock.createRequest(stx`
             <iq to="${_converse.api.connection.get().jid}" type="result" id="${stanza.getAttribute('id')}" xmlns="jabber:client">
@@ -177,7 +235,7 @@ describe("The Contacts Roster", function () {
             </iq>
         `));
 
-        while (sent_IQs.length) sent_IQs.pop();
+        while (IQ_stanzas.length) IQ_stanzas.pop();
 
         await u.waitUntil(() => _converse.roster.length === 2);
         expect(_converse.roster.pluck('jid')).toEqual(['juliet@example.net', 'mercutio@example.net']);
@@ -192,8 +250,7 @@ describe("The Contacts Roster", function () {
         sync_button.click();
 
         stanza = await u.waitUntil(
-            () => sent_IQs.filter(iq => iq.querySelector('iq query[xmlns="jabber:iq:roster"]')).pop()
-        );
+            () => IQ_stanzas.filter(iq => sizzle('iq query[xmlns="jabber:iq:roster"]', iq).length).pop());
 
         _converse.api.connection.get()._dataRecv(mock.createRequest(stx`
             <iq to="${_converse.api.connection.get().jid}" type="result" id="${stanza.getAttribute('id')}" xmlns="jabber:client">
@@ -1292,8 +1349,10 @@ describe("The Contacts Roster", function () {
 
             await mock.openControlBox(_converse);
 
-            const sent_IQs = _converse.api.connection.get().IQ_stanzas;
-            const stanza = await u.waitUntil(() => sent_IQs.filter(iq => iq.querySelector('iq query[xmlns="jabber:iq:roster"]')).pop());
+            const { IQ_stanzas } = _converse.api.connection.get();
+            const stanza = await u.waitUntil(
+                () => IQ_stanzas.filter(iq => sizzle('iq query[xmlns="jabber:iq:roster"]', iq).length).pop());
+
             // Taken from the spec
             // https://xmpp.org/rfcs/rfc3921.html#rfc.section.7.3
             const result = stx`