Commit 38499917 authored by JC Brand's avatar JC Brand

updates #194

Include entity capabilities hash in outgoing presences

Also, started some work on using jsdoc for rendering API documentation.
Ideally that would go into a separate commit but that would take ages to
untangle.
parent ab1c19a9
......@@ -5,6 +5,7 @@
## New Features
- #161 XEP-0363: HTTP File Upload
- #194 Include entity capabilities in outgoing presence stanzas
- #337 API call to update a VCard
- #1094 Show room members who aren't currently online
- It's now also possible to edit your VCard via the UI
......
# You can set these variables from the command line.
UGLIFYJS ?= node_modules/.bin/uglifyjs
BABEL ?= node_modules/.bin/babel
BOURBON = ./node_modules/bourbon/app/assets/stylesheets/
BOOTSTRAP = ./node_modules/
BOURBON = ./node_modules/bourbon/app/assets/stylesheets/
BUILDDIR = ./docs
BUNDLE ?= ./.bundle/bin/bundle
CHROMIUM ?= ./node_modules/.bin/run-headless-chromium
......@@ -11,14 +10,16 @@ ESLINT ?= ./node_modules/.bin/eslint
HTTPSERVE ?= ./node_modules/.bin/http-server
HTTPSERVE_PORT ?= 8000
INKSCAPE ?= inkscape
JSDOC ?= ./node_modules/.bin/jsdoc
OXIPNG ?= oxipng
PAPER =
PO2JSON ?= ./node_modules/.bin/po2json
RJS ?= ./node_modules/.bin/r.js
SASS ?= ./.bundle/bin/sass
SPHINXBUILD ?= ./bin/sphinx-build
SED ?= sed
SPHINXBUILD ?= ./bin/sphinx-build
SPHINXOPTS =
OXIPNG ?= oxipng
UGLIFYJS ?= node_modules/.bin/uglifyjs
# In the case user wishes to use RVM
......@@ -235,3 +236,7 @@ html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
.PHONY: apidoc
apidoc:
$(JSDOC) -d docs/html/api src/converse-disco.js
......@@ -37,8 +37,8 @@
notify_all_room_messages: [
'discuss@conference.conversejs.org'
],
// bosh_service_url: 'http://chat.example.org:5280/http-bind/',
bosh_service_url: 'https://conversejs.org/http-bind/', // Please use this connection manager only for testing purposes
bosh_service_url: 'http://chat.example.org:5280/http-bind/',
// bosh_service_url: 'https://conversejs.org/http-bind/', // Please use this connection manager only for testing purposes
message_archiving: 'always',
debug: true
});
......
......@@ -437,6 +437,64 @@ The **disco** grouping
This grouping collects API functions related to `service discovery
<https://xmpp.org/extensions/xep-0030.html>`_.
The **disco.own** grouping
~~~~~~~~~~~~~~~~~~~~~~~~~~
The **disco.own.features** grouping
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
add
***
Paramters:
* (String) name
get
***
Returns all of the identities registered for this client (i.e. instance of Converse.js).
.. code-block:: javascript
const identities = _converse.api.disco.own.identities.get();
The **disco.own.identities** grouping
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
add
***
Paramters:
* (String) category
* (String) type
* (String) name
* (String) lang
Lets you add new identities for this client (i.e. instance of Converse.js).
.. code-block:: javascript
_converse.api.disco.own.identities.add('client', 'web', 'Converse.js');
get
***
Returns all of the identities registered for this client (i.e. instance of Converse.js).
.. code-block:: javascript
const identities = _converse.api.disco.own.identities.get();
clear
*****
Clears all previously set identities.
getIdentity
~~~~~~~~~~~
......
......@@ -907,6 +907,12 @@
"dev": true,
"optional": true
},
"bluebird": {
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz",
"integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==",
"dev": true
},
"bootstrap": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.0.0.tgz",
......@@ -1052,6 +1058,15 @@
"integrity": "sha1-Sm+gc5nCa7pH8LJJa00PtAjFVQ0=",
"dev": true
},
"catharsis": {
"version": "0.8.9",
"resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.8.9.tgz",
"integrity": "sha1-mMyJDKZS3S7w5ws3klMQ/56Q/Is=",
"dev": true,
"requires": {
"underscore-contrib": "0.3.0"
}
},
"center-align": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz",
......@@ -3562,6 +3577,43 @@
"esprima": "4.0.0"
}
},
"js2xmlparser": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-3.0.0.tgz",
"integrity": "sha1-P7YOqgicVED5MZ9RdgzNB+JJlzM=",
"dev": true,
"requires": {
"xmlcreate": "1.0.2"
}
},
"jsdoc": {
"version": "3.5.5",
"resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.5.5.tgz",
"integrity": "sha512-6PxB65TAU4WO0Wzyr/4/YhlGovXl0EVYfpKbpSroSj0qBxT4/xod/l40Opkm38dRHRdQgdeY836M0uVnJQG7kg==",
"dev": true,
"requires": {
"babylon": "7.0.0-beta.19",
"bluebird": "3.5.1",
"catharsis": "0.8.9",
"escape-string-regexp": "1.0.5",
"js2xmlparser": "3.0.0",
"klaw": "2.0.0",
"marked": "0.3.19",
"mkdirp": "0.5.1",
"requizzle": "0.2.1",
"strip-json-comments": "2.0.1",
"taffydb": "2.6.2",
"underscore": "1.8.3"
},
"dependencies": {
"babylon": {
"version": "7.0.0-beta.19",
"resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.19.tgz",
"integrity": "sha512-Vg0C9s/REX6/WIXN37UKpv5ZhRi6A4pjHlpkE34+8/a6c2W1Q692n3hmc+SZG5lKRnaExLUbxtJ1SVT+KaCQ/A==",
"dev": true
}
}
},
"jsesc": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.1.tgz",
......@@ -3631,6 +3683,15 @@
"is-buffer": "1.1.6"
}
},
"klaw": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/klaw/-/klaw-2.0.0.tgz",
"integrity": "sha1-WcEo4Nxc5BAgEVEZTuucv4WGUPY=",
"dev": true,
"requires": {
"graceful-fs": "4.1.11"
}
},
"latest-version": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/latest-version/-/latest-version-2.0.0.tgz",
......@@ -3768,6 +3829,12 @@
"yallist": "2.1.2"
}
},
"marked": {
"version": "0.3.19",
"resolved": "https://registry.npmjs.org/marked/-/marked-0.3.19.tgz",
"integrity": "sha512-ea2eGWOqNxPcXv8dyERdSr/6FmzvWwzjMxpfGB/sbMccXoct+xY+YukPD+QTUZwyvK7BZwcr4m21WBOW41pAkg==",
"dev": true
},
"micromatch": {
"version": "2.3.11",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz",
......@@ -8604,6 +8671,23 @@
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
"dev": true
},
"requizzle": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.1.tgz",
"integrity": "sha1-aUPDUwxNmn5G8c3dUcFY/GcM294=",
"dev": true,
"requires": {
"underscore": "1.6.0"
},
"dependencies": {
"underscore": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz",
"integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=",
"dev": true
}
}
},
"resolve": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.5.0.tgz",
......@@ -9402,6 +9486,12 @@
}
}
},
"taffydb": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz",
"integrity": "sha1-fLy2S1oUG2ou/CxdLGe04VCyomg=",
"dev": true
},
"tempfile": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/tempfile/-/tempfile-1.1.1.tgz",
......@@ -9547,6 +9637,23 @@
"integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=",
"dev": true
},
"underscore-contrib": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/underscore-contrib/-/underscore-contrib-0.3.0.tgz",
"integrity": "sha1-ZltmwkeD+PorGMn4y7Dix9SMJsc=",
"dev": true,
"requires": {
"underscore": "1.6.0"
},
"dependencies": {
"underscore": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz",
"integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=",
"dev": true
}
}
},
"unicode-canonical-property-names-ecmascript": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.3.tgz",
......@@ -9871,6 +9978,12 @@
"integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=",
"dev": true
},
"xmlcreate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-1.0.2.tgz",
"integrity": "sha1-+mv3YqYKQT+z3Y9LA8WyaSONMI8=",
"dev": true
},
"xss": {
"version": "0.3.7",
"resolved": "https://registry.npmjs.org/xss/-/xss-0.3.7.tgz",
......
......@@ -18,12 +18,36 @@
describe("A sent presence stanza", function () {
it("includes a entity capabilities node",
mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], {},
function (done, _converse) {
_converse.api.disco.own.identities.clear();
_converse.api.disco.own.features.clear();
_converse.api.disco.own.identities.add("client", "pc", "Exodus 0.9.1");
_converse.api.disco.own.features.add("http://jabber.org/protocol/caps");
_converse.api.disco.own.features.add("http://jabber.org/protocol/disco#info");
_converse.api.disco.own.features.add("http://jabber.org/protocol/disco#items");
_converse.api.disco.own.features.add("http://jabber.org/protocol/muc");
const presence = _converse.xmppstatus.constructPresence();
expect(presence.toLocaleString()).toBe(
"<presence xmlns='jabber:client'>"+
"<priority>0</priority>"+
"<c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='https://conversejs.org' ver='QgayPKawpkPSDYmwT/WM94uAlu0='/>"+
"</presence>")
done();
}));
it("has a given priority", mock.initConverse(function (_converse) {
var pres = _converse.xmppstatus.constructPresence('online', 'Hello world');
expect(pres.toLocaleString()).toBe(
"<presence xmlns='jabber:client'>"+
"<status>Hello world</status>"+
"<priority>0</priority>"+
"<c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='https://conversejs.org' ver='1J7kq1MEvnB6ea6vKcgCsSE37gw='/>"+
"</presence>"
);
_converse.priority = 2;
......@@ -33,6 +57,7 @@
"<show>away</show>"+
"<status>Going jogging</status>"+
"<priority>2</priority>"+
"<c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='https://conversejs.org' ver='1J7kq1MEvnB6ea6vKcgCsSE37gw='/>"+
"</presence>"
);
......@@ -43,6 +68,7 @@
"<show>dnd</show>"+
"<status>Doing taxes</status>"+
"<priority>0</priority>"+
"<c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='https://conversejs.org' ver='1J7kq1MEvnB6ea6vKcgCsSE37gw='/>"+
"</presence>"
);
}));
......@@ -68,7 +94,11 @@
modal.el.querySelector('[type="submit"]').click();
expect(view.model.sendPresence).toHaveBeenCalled();
expect(_converse.connection.send.calls.mostRecent().args[0].toLocaleString())
.toBe("<presence xmlns='jabber:client'><status>My custom status</status><priority>0</priority></presence>")
.toBe("<presence xmlns='jabber:client'>"+
"<status>My custom status</status>"+
"<priority>0</priority>"+
"<c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='https://conversejs.org' ver='1J7kq1MEvnB6ea6vKcgCsSE37gw='/>"+
"</presence>")
return test_utils.waitUntil(function () {
return modal.el.getAttribute('aria-hidden') === "true";
......@@ -82,7 +112,9 @@
modal.el.querySelector('label[for="radio-busy"]').click(); // Change status to "dnd"
modal.el.querySelector('[type="submit"]').click();
expect(_converse.connection.send.calls.mostRecent().args[0].toLocaleString())
.toBe("<presence xmlns='jabber:client'><show>dnd</show><status>My custom status</status><priority>0</priority></presence>")
.toBe("<presence xmlns='jabber:client'><show>dnd</show><status>My custom status</status><priority>0</priority>"+
"<c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='https://conversejs.org' ver='1J7kq1MEvnB6ea6vKcgCsSE37gw='/>"+
"</presence>")
done();
});
}));
......
......@@ -10,7 +10,7 @@
_converse.api.user.status.message.set("I'm also happy!");
expect(_converse.connection.send).toHaveBeenCalled();
var $stanza = $(_converse.connection.send.calls.argsFor(0)[0].tree());
expect($stanza.children().length).toBe(2);
expect($stanza.children().length).toBe(3);
expect($stanza.children('status').length).toBe(1);
expect($stanza.children('status').text()).toBe("I'm also happy!");
expect($stanza.children('show').length).toBe(0);
......
......@@ -71,6 +71,7 @@ require.config({
"converse-bookmarks": "src/converse-bookmarks",
"converse-chatboxes": "src/converse-chatboxes",
"converse-caps": "src/converse-caps",
"converse-chatview": "src/converse-chatview",
"converse-controlbox": "src/converse-controlbox",
"converse-core": "src/converse-core",
......
// Converse.js
// http://conversejs.org
//
// Copyright (c) 2013-2018, the Converse.js developers
// Licensed under the Mozilla Public License (MPLv2)
(function (root, factory) {
define(["converse-core"], factory);
}(this, function (converse) {
const { Strophe, $build, _, b64_sha1 } = converse.env;
Strophe.addNamespace('CAPS', "http://jabber.org/protocol/caps");
function propertySort (array, property) {
return array.sort((a, b) => { return a[property] > b[property] ? -1 : 1 });
}
function generateVerificationString (_converse) {
const identities = _converse.api.disco.own.identities.get(),
features = _converse.api.disco.own.features.get();
if (identities.length > 1) {
propertySort(identities, "category");
propertySort(identities, "type");
propertySort(identities, "lang");
}
let S = _.reduce(
identities,
(result, id) => `${result}${id.category}/${id.type}/${_.get(id, 'lang', '')}/${id.name}<`,
"");
features.sort();
S = _.reduce(features, (result, feature) => `${result}${feature}<`, S);
return b64_sha1(S);
}
function createCapsNode (_converse) {
return $build("c", {
'xmlns': Strophe.NS.CAPS,
'hash': "sha-1",
'node': "https://conversejs.org",
'ver': generateVerificationString(_converse)
}).nodeTree;
}
converse.plugins.add('converse-caps', {
overrides: {
// Overrides mentioned here will be picked up by converse.js's
// plugin architecture they will replace existing methods on the
// relevant objects or classes.
XMPPStatus: {
constructPresence () {
const presence = this.__super__.constructPresence.apply(this, arguments);
presence.root().cnode(createCapsNode(this.__super__._converse));
return presence;
}
}
}
});
}));
......@@ -797,8 +797,8 @@
_converse.on('addClientFeatures', () => {
_converse.api.disco.addFeature(Strophe.NS.HTTPUPLOAD);
_converse.api.disco.addFeature(Strophe.NS.OUTOFBAND);
_converse.api.disco.own.features.add(Strophe.NS.HTTPUPLOAD);
_converse.api.disco.own.features.add(Strophe.NS.OUTOFBAND);
});
_converse.api.listen.on('pluginsInitialized', () => {
......
......@@ -1147,7 +1147,7 @@
_converse.on('connected', () => {
// Advertise that we support XEP-0382 Message Spoilers
_converse.api.disco.addFeature(Strophe.NS.SPOILER);
_converse.api.disco.own.features.add(Strophe.NS.SPOILER);
});
/************************ BEGIN API ************************/
......
......@@ -74,6 +74,7 @@
'converse-bookmarks',
'converse-chatboxes',
'converse-chatview',
'converse-caps',
'converse-controlbox',
'converse-core',
'converse-disco',
......
// Converse.js (A browser based XMPP chat client)
// Converse.js
// http://conversejs.org
//
// Copyright (c) 2012-2017, Jan-Carel Brand <jc@opkode.com>
// Copyright (c) 2013-2018, the Converse.js developers
// Licensed under the Mozilla Public License (MPLv2)
//
/* This is a Converse.js plugin which add support for XEP-0030: Service Discovery */
......@@ -205,14 +204,14 @@
function addClientFeatures () {
// See http://xmpp.org/registrar/disco-categories.html
_converse.api.disco.addIdentity('client', 'web', 'Converse.js');
_converse.api.disco.own.identities.add('client', 'web', 'Converse.js');
_converse.api.disco.addFeature(Strophe.NS.BOSH);
_converse.api.disco.addFeature(Strophe.NS.CHATSTATES);
_converse.api.disco.addFeature(Strophe.NS.DISCO_INFO);
_converse.api.disco.addFeature(Strophe.NS.ROSTERX); // Limited support
_converse.api.disco.own.features.add(Strophe.NS.BOSH);
_converse.api.disco.own.features.add(Strophe.NS.CHATSTATES);
_converse.api.disco.own.features.add(Strophe.NS.DISCO_INFO);
_converse.api.disco.own.features.add(Strophe.NS.ROSTERX); // Limited support
if (_converse.message_carbons) {
_converse.api.disco.addFeature(Strophe.NS.CARBONS);
_converse.api.disco.own.features.add(Strophe.NS.CARBONS);
}
_converse.emit('addClientFeatures');
return this;
......@@ -287,7 +286,83 @@
/* We extend the default converse.js API to add methods specific to service discovery */
_.extend(_converse.api, {
/**
* The service discovery API
* @namespace
*/
'disco': {
/**
* The "own" grouping
* @namespace
*/
'own': {
/**
* The "identities" grouping
* @namespace
*/
'identities': {
/**
* Clears all previously registered identities.
* @function
*
* @example
* _converse.api.disco.own.identities.clear();
*/
clear () {
plugin._identities = []
},
/**
* Lets you add new identities for this client (i.e. instance of Converse.js)
* @function
*
* @param {String} category - server, client, gateway, directory, etc.
* @param {String} type - phone, pc, web, etc.
* @param {String} name - "Converse.js"
* @param {String} lang - en, el, de, etc.
*
* @example
* _converse.api.disco.own.identities.clear();
*/
add (category, type, name, lang) {
for (var i=0; i<plugin._identities.length; i++) {
if (plugin._identities[i].category == category &&
plugin._identities[i].type == type &&
plugin._identities[i].name == name &&
plugin._identities[i].lang == lang) {
return false;
}
}
plugin._identities.push({category: category, type: type, name: name, lang: lang});
},
/**
* Returns all of the identities registered for this client
* (i.e. instance of Converse.js).
* @function
*
* @example
* const identities = _converse.api.disco.own.identities.get();
*/
get () {
return plugin._identities;
}
},
'features': {
add (name) {
for (var i=0; i<plugin._features.length; i++) {
if (plugin._features[i] == name) { return false; }
}
plugin._features.push(name);
},
clear () {
plugin._features = []
},
get () {
return plugin._features;
}
}
},
'info' (jid, node, callback, errback, timeout) {
const attrs = {xmlns: Strophe.NS.DISCO_INFO};
if (node) {
......@@ -363,25 +438,6 @@
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
},
'addIdentity' (category, type, name, lang) {
for (var i=0; i<plugin._identities.length; i++) {
if (plugin._identities[i].category == category &&
plugin._identities[i].type == type &&
plugin._identities[i].name == name &&
plugin._identities[i].lang == lang) {
return false;
}
}
plugin._identities.push({category: category, type: type, name: name, lang: lang});
},
'addFeature' (name) {
for (var i=0; i<plugin._features.length; i++) {
if (plugin._features[i] == name) { return false; }
}
plugin._features.push(name);
},
'getIdentity' (category, type, entity_jid) {
/* Returns a Promise which resolves with a map indicating
* whether an identity with a given type is provided by
......
......@@ -384,7 +384,7 @@
});
_converse.on('addClientFeatures', () => {
_converse.api.disco.addFeature(Strophe.NS.MAM);
_converse.api.disco.own.features.add(Strophe.NS.MAM);
});
_converse.on('afterMessagesFetched', (chatboxview) => {
......
// Converse.js
// http://conversejs.org
//
// Copyright (c) 2012-2018, the Converse.js developers
// Copyright (c) 2013-2018, the Converse.js developers
// Licensed under the Mozilla Public License (MPLv2)
(function (root, factory) {
......@@ -1176,10 +1176,10 @@
/************************ BEGIN Event Handlers ************************/
_converse.on('addClientFeatures', () => {
if (_converse.allow_muc) {
_converse.api.disco.addFeature(Strophe.NS.MUC);
_converse.api.disco.own.features.add(Strophe.NS.MUC);
}
if (_converse.allow_muc_invitations) {
_converse.api.disco.addFeature('jabber:x:conference'); // Invites
_converse.api.disco.own.features.add('jabber:x:conference'); // Invites
}
});
_converse.on('chatBoxesFetched', autoJoinRooms);
......
......@@ -57,7 +57,7 @@
_converse.registerPongHandler = function () {
if (!_.isUndefined(_converse.connection.disco)) {
_converse.api.disco.addFeature(Strophe.NS.PING);
_converse.api.disco.own.features.add(Strophe.NS.PING);
}
_converse.connection.ping.addPingHandler(_converse.pong);
};
......
......@@ -108,7 +108,7 @@
_converse.on('addClientFeatures', () => {
_converse.api.disco.addFeature(Strophe.NS.VCARD);
_converse.api.disco.own.features.add(Strophe.NS.VCARD);
});
_.extend(_converse.api, {
......
......@@ -8,6 +8,7 @@ if (typeof define !== 'undefined') {
* Any of the following components may be removed if they're not needed.
*/
"converse-bookmarks", // XEP-0048 Bookmarks
"converse-caps",
"converse-chatview", // Renders standalone chat boxes for single user chat
"converse-controlbox", // The control box
"converse-dragresize", // Allows chat boxes to be resized by dragging them
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment