From 095e7add8600cf49f9e1536d7f468a87dc25b4bd Mon Sep 17 00:00:00 2001 From: JC Brand Date: Fri, 23 Jun 2017 22:53:11 +0200 Subject: [PATCH] mam: Upgrade MAM support to version 2 --- CHANGES.md | 2 + spec/mam.js | 103 ++++++++++++++++++++----------------------- src/converse-core.js | 2 +- src/converse-mam.js | 69 +++++++++++++++++++---------- 4 files changed, 97 insertions(+), 79 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 3a81c1676..c326315f5 100755 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,8 @@ ## 3.1.0 (Unreleased) +- Support for [XMPP-0313 Message Archive Management](https://xmpp.org/extensions/xep-0313.html) + has been upgraded to version 2. [jcbrand] - New non-core plugin `converse-singleton` which ensures that no more than one chat is visible at any given time. Used in the mobile build: `converse-mobile.js` and makes the unread messages counter possible there. diff --git a/spec/mam.js b/spec/mam.js index bfc9a6cae..f63b5c00f 100644 --- a/spec/mam.js +++ b/spec/mam.js @@ -28,7 +28,7 @@ _converse.api.archive.query(); var queryid = $(sent_stanza.toString()).find('query').attr('queryid'); expect(sent_stanza.toString()).toBe( - ""); + ""); })); it("can be used to query for all messages to/from a particular JID", mock.initConverse(function (_converse) { @@ -45,10 +45,10 @@ var queryid = $(sent_stanza.toString()).find('query').attr('queryid'); expect(sent_stanza.toString()).toBe( ""+ - ""+ + ""+ ""+ ""+ - "urn:xmpp:mam:0"+ + "urn:xmpp:mam:2"+ ""+ ""+ "juliet@capulet.lit"+ @@ -79,10 +79,10 @@ var queryid = $(sent_stanza.toString()).find('query').attr('queryid'); expect(sent_stanza.toString()).toBe( ""+ - ""+ + ""+ ""+ ""+ - "urn:xmpp:mam:0"+ + "urn:xmpp:mam:2"+ ""+ ""+ ""+moment(start).format()+""+ @@ -120,10 +120,10 @@ var queryid = $(sent_stanza.toString()).find('query').attr('queryid'); expect(sent_stanza.toString()).toBe( ""+ - ""+ + ""+ ""+ ""+ - "urn:xmpp:mam:0"+ + "urn:xmpp:mam:2"+ ""+ ""+ ""+moment(start).format()+""+ @@ -149,10 +149,10 @@ var queryid = $(sent_stanza.toString()).find('query').attr('queryid'); expect(sent_stanza.toString()).toBe( ""+ - ""+ + ""+ ""+ ""+ - "urn:xmpp:mam:0"+ + "urn:xmpp:mam:2"+ ""+ ""+ ""+moment(start).format()+""+ @@ -185,10 +185,10 @@ var queryid = $(sent_stanza.toString()).find('query').attr('queryid'); expect(sent_stanza.toString()).toBe( ""+ - ""+ + ""+ ""+ ""+ - "urn:xmpp:mam:0"+ + "urn:xmpp:mam:2"+ ""+ ""+ ""+moment(start).format()+""+ @@ -217,10 +217,10 @@ var queryid = $(sent_stanza.toString()).find('query').attr('queryid'); expect(sent_stanza.toString()).toBe( ""+ - ""+ + ""+ ""+ ""+ - "urn:xmpp:mam:0"+ + "urn:xmpp:mam:2"+ ""+ ""+ ""+ @@ -254,10 +254,10 @@ var queryid = $(sent_stanza.toString()).find('query').attr('queryid'); expect(sent_stanza.toString()).toBe( ""+ - ""+ + ""+ ""+ ""+ - "urn:xmpp:mam:0"+ + "urn:xmpp:mam:2"+ ""+ ""+ "romeo@montague.lit"+ @@ -289,27 +289,22 @@ _converse.api.archive.query({'with': 'romeo@capulet.lit', 'max':'10'}, callback); var queryid = $(sent_stanza.toString()).find('query').attr('queryid'); - // Send the result stanza, so that the callback is called. - var stanza = $iq({'type': 'result', 'id': IQ_id}); - _converse.connection._dataRecv(test_utils.createRequest(stanza)); - - /* - * - * - * - * - * Call me but love, and I'll be new baptized; Henceforth I never will be Romeo. - * - * - * - * + /* + * + * + * + * + * Call me but love, and I'll be new baptized; Henceforth I never will be Romeo. + * + * + * + * */ var msg1 = $msg({'id':'aeb213', 'to':'juliet@capulet.lit/chamber'}) - .c('result', {'xmlns': 'urn:xmpp:mam:0', 'queryid':queryid, 'id':'28482-98726-73623'}) + .c('result', {'xmlns': 'urn:xmpp:mam:2', 'queryid':queryid, 'id':'28482-98726-73623'}) .c('forwarded', {'xmlns':'urn:xmpp:forward:0'}) .c('delay', {'xmlns':'urn:xmpp:delay', 'stamp':'2010-07-10T23:08:25Z'}).up() .c('message', { @@ -321,7 +316,7 @@ _converse.connection._dataRecv(test_utils.createRequest(msg1)); var msg2 = $msg({'id':'aeb213', 'to':'juliet@capulet.lit/chamber'}) - .c('result', {'xmlns': 'urn:xmpp:mam:0', 'queryid':queryid, 'id':'28482-98726-73624'}) + .c('result', {'xmlns': 'urn:xmpp:mam:2', 'queryid':queryid, 'id':'28482-98726-73624'}) .c('forwarded', {'xmlns':'urn:xmpp:forward:0'}) .c('delay', {'xmlns':'urn:xmpp:delay', 'stamp':'2010-07-10T23:08:25Z'}).up() .c('message', { @@ -332,23 +327,23 @@ .c('body').t("Henceforth I never will be Romeo."); _converse.connection._dataRecv(test_utils.createRequest(msg2)); - /* Send a message to indicate the end of the result set. + /* Send an stanza to indicate the end of the result set. * - * - * - * - * 23452-4534-1 - * 390-2342-22 - * 16 - * - * - * + * + * + * + * 28482-98726-73623 + * 09af3-cc343-b409f + * 20 + * + * */ - stanza = $msg().c('fin', {'xmlns': 'urn:xmpp:mam:0', 'queryid':queryid, 'complete': 'true'}) - .c('set', {'xmlns': 'http://jabber.org/protocol/rsm'}) - .c('first', {'index': '0'}).t('23452-4534-1').up() - .c('last').t('390-2342-22').up() - .c('count').t('16'); + var stanza = $iq({'type': 'result', 'id': IQ_id}) + .c('fin', {'xmlns': 'urn:xmpp:mam:2'}) + .c('set', {'xmlns': 'http://jabber.org/protocol/rsm'}) + .c('first', {'index': '0'}).t('23452-4534-1').up() + .c('last').t('09af3-cc343-b409f').up() + .c('count').t('16'); _converse.connection._dataRecv(test_utils.createRequest(stanza)); expect(callback).toHaveBeenCalled(); @@ -360,7 +355,7 @@ expect(args[1].max).toBe('10'); expect(args[1].count).toBe('16'); expect(args[1].first).toBe('23452-4534-1'); - expect(args[1].last).toBe('390-2342-22'); + expect(args[1].last).toBe('09af3-cc343-b409f'); })); }); @@ -386,11 +381,11 @@ expect(_converse.connection.sendIQ).toHaveBeenCalled(); expect(sent_stanza.toLocaleString()).toBe( ""+ - ""+ + ""+ "" ); - /* Example 15. Server responds with current preferences + /* Example 20. Server responds with current preferences * * * @@ -410,7 +405,7 @@ expect(_converse.connection.sendIQ.calls.count()).toBe(2); expect(sent_stanza.toString()).toBe( ""+ - ""+ + ""+ "romeo@montague.lit"+ "montague@montague.lit"+ ""+ diff --git a/src/converse-core.js b/src/converse-core.js index d4da3ee2c..625274c0e 100755 --- a/src/converse-core.js +++ b/src/converse-core.js @@ -192,7 +192,7 @@ Strophe.addNamespace('CSI', 'urn:xmpp:csi:0'); Strophe.addNamespace('DELAY', 'urn:xmpp:delay'); Strophe.addNamespace('HINTS', 'urn:xmpp:hints'); - Strophe.addNamespace('MAM', 'urn:xmpp:mam:0'); + Strophe.addNamespace('MAM', 'urn:xmpp:mam:2'); Strophe.addNamespace('NICK', 'http://jabber.org/protocol/nick'); Strophe.addNamespace('PUBSUB', 'http://jabber.org/protocol/pubsub'); Strophe.addNamespace('ROSTERX', 'http://jabber.org/protocol/rosterx'); diff --git a/src/converse-mam.js b/src/converse-mam.js index 9e243d572..3308da625 100644 --- a/src/converse-mam.js +++ b/src/converse-mam.js @@ -125,6 +125,23 @@ }, ChatRoomView: { + + initialize: function () { + var _converse = this.__super__._converse; + this.__super__.initialize.apply(this, arguments); + this.model.on('change:mam_enabled', function () { + // Fetch messages again if we find out that mam has + // been enabled (because the first attempt would then + // have failed. + this.fetchArchivedMessages({ + 'before': '', // Page backwards from the most recent message + 'with': this.model.get('jid'), + 'max': _converse.archived_messages_page_size + }); + this.model.save({'mam_initialized': true}); + }, this); + }, + render: function () { var result = this.__super__.render.apply(this, arguments); if (!this.disable_mam) { @@ -150,7 +167,6 @@ * Then, upon receiving them, call onChatRoomMessage * so that they are displayed inside it. */ - var that = this; var _converse = this.__super__._converse; if (!_converse.features.findWhere({'var': Strophe.NS.MAM})) { _converse.log( @@ -162,6 +178,8 @@ return; } this.addSpinner(); + + var that = this; _converse.api.archive.query(_.extend(options, {'groupchat': true}), function (messages) { that.clearSpinner(); @@ -216,18 +234,13 @@ callback = options; errback = callback; } - /* - if (!_converse.features.findWhere({'var': Strophe.NS.MAM})) { - _converse.log('This server does not support XEP-0313, Message Archive Management'); - errback(null); - return; - } - */ var queryid = _converse.connection.getUniqueId(); var attrs = {'type':'set'}; if (!_.isUndefined(options) && options.groupchat) { if (!options['with']) { - throw new Error('You need to specify a "with" value containing the chat room JID, when querying groupchat messages.'); + throw new Error( + 'You need to specify a "with" value containing '+ + 'the chat room JID, when querying groupchat messages.'); } attrs.to = options['with']; } @@ -258,23 +271,31 @@ } } - if (_.isFunction(callback)) { - _converse.connection.addHandler(function (message) { - var $msg = $(message), rsm, - $fin = $msg.find('fin[xmlns="'+Strophe.NS.MAM+'"]'); - if ($fin.length && $fin.attr('queryid') === queryid) { - rsm = new Strophe.RSM({xml: $fin.find('set')[0]}); - _.extend(rsm, _.pick(options, ['max'])); - _.extend(rsm, _.pick(options, MAM_ATTRIBUTES)); + var message_handler = _converse.connection.addHandler(function (message) { + var result = message.querySelector('result'); + if (!_.isNull(result) && result.getAttribute('queryid') === queryid) { + messages.push(message); + } + return true; + }, Strophe.NS.MAM); + + _converse.connection.sendIQ( + stanza, + function (iq) { + _converse.connection.deleteHandler(message_handler); + if (_.isFunction(callback)) { + var set = iq.querySelector('set'); + var rsm = new Strophe.RSM({xml: set}); + _.extend(rsm, _.pick(options, _.concat(MAM_ATTRIBUTES, ['max']))); callback(messages, rsm); - return false; // We've received all messages, decommission this handler - } else if (queryid === $msg.find('result').attr('queryid')) { - messages.push(message); } - return true; - }, Strophe.NS.MAM); - } - _converse.connection.sendIQ(stanza, null, errback, _converse.message_archiving_timeout); + }, + function () { + _converse.connection.deleteHandler(message_handler); + if (_.isFunction(errback)) { errback.apply(this, arguments); } + }, + _converse.message_archiving_timeout + ); }; _.extend(_converse.api, {