diff --git a/CHANGES.md b/CHANGES.md index 00bae3f13e..99b5f7a0b8 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,6 +5,7 @@ ### Github Issues - #122: Set horizontal layout direction based on the language - #698: Add support for MUC private messages +- #1038: Support setting node config manually - #1057: Removed the `mobile` view mode. Instead of setting `view_mode` to `mobile`, set it to `fullscreen`. - #1174: Show MUC avatars in the rooms list - #1195: Add actions to quote and copy messages diff --git a/src/headless/plugins/pubsub/api.js b/src/headless/plugins/pubsub/api.js index 92f26c9962..82c22dd33b 100644 --- a/src/headless/plugins/pubsub/api.js +++ b/src/headless/plugins/pubsub/api.js @@ -9,7 +9,7 @@ import log from '../../log.js'; import { parseErrorStanza } from '../../shared/parsers.js'; import { parseStanzaForPubSubConfig } from './parsers.js'; -const { Strophe, stx, u } = converse.env; +const { Strophe, stx } = converse.env; export default { /** @@ -49,10 +49,7 @@ export default { try { response = await api.sendIQ(stanza); } catch (error) { - if (u.isErrorStanza(error)) { - throw parseErrorStanza(error); - } - throw error; + throw await parseErrorStanza(error); } return parseStanzaForPubSubConfig(response); }, @@ -72,7 +69,7 @@ export default { const entity_jid = jid || bare_jid; const new_config = { ...(await api.pubsub.config.get(entity_jid, node)), - ...config + ...config, }; const stanza = stx` @@ -95,10 +92,7 @@ export default { try { await api.sendIQ(stanza); } catch (error) { - if (u.isErrorStanza(error)) { - throw parseErrorStanza(error); - } - throw error; + throw await parseErrorStanza(error); } return new_config; }, @@ -119,6 +113,9 @@ export default { * @returns {Promise} */ async publish(jid, node, item, options, strict_options = true) { + if (!node) throw new Error('api.pubsub.config.publish: node value required'); + if (!item) throw new Error('api.pubsub.config.publish: item value required'); + const bare_jid = _converse.session.get('bare_jid'); const entity_jid = jid || bare_jid; @@ -167,30 +164,35 @@ export default { Strophe.getDomainFromJid(entity_jid) ))); - if (!supports_publish_options) { - if (strict_options) { - log.warn(`api.pubsub.publish: #publish-options not supported, refusing to publish item.`); - log.warn(stanza); - return; - } else { - log.warn(`api.pubsub.publish: #publish-options not supported, publishing anyway.`); - } + if (!supports_publish_options && strict_options) { + log.warn(`api.pubsub.publish: #publish-options not supported, refusing to publish item.`); + log.warn(stanza); + return; } try { await api.sendIQ(stanza); } catch (iq) { + const e = await parseErrorStanza(iq); if ( - iq instanceof Element && - !strict_options && - iq.querySelector(`precondition-not-met[xmlns="${Strophe.NS.PUBSUB_ERROR}"]`) + e.name === 'conflict' && + /** @type {import('shared/errors').StanzaError} */(e).extra[Strophe.NS.PUBSUB_ERROR] === 'precondition-not-met' ) { - // The publish-options precondition couldn't be - // met. We re-publish but without publish-options. - const el = stanza.tree(); - el.querySelector('publish-options').outerHTML = ''; - log.warn(`api.pubsub.publish: Republishing without publish options. ${el.outerHTML}`); - await api.sendIQ(el); + // Manually configure the node if we can't set it via publish-options + await api.pubsub.config.set(entity_jid, node, options); + try { + await api.sendIQ(stanza); + } catch (e) { + log.error(e); + if (!strict_options) { + // The publish-options precondition couldn't be met. + // We re-publish but without publish-options. + const el = stanza.tree(); + el.querySelector('publish-options').outerHTML = ''; + log.warn(`api.pubsub.publish: #publish-options precondition-not-met, publishing anyway.`); + await api.sendIQ(el); + } + } } else { throw iq; } diff --git a/src/headless/plugins/pubsub/tests/config.js b/src/headless/plugins/pubsub/tests/config.js index d711fc0e92..85b251ca1f 100644 --- a/src/headless/plugins/pubsub/tests/config.js +++ b/src/headless/plugins/pubsub/tests/config.js @@ -192,7 +192,7 @@ describe('The pubsub API', function () { let first_error_thrown = false; promise .catch((e) => { - expect(e instanceof errors.NotImplementedError).toBe(true); + expect(e instanceof errors.FeatureNotImplementedError).toBe(true); first_error_thrown = true; }) .finally(() => { @@ -467,4 +467,156 @@ describe('The pubsub API', function () { }) ); }); + + describe('publishing to a node', function () { + it( + "will try to manually configure the node if publish-options aren't supported", + mock.initConverse([], {}, async function (_converse) { + await mock.waitForRoster(_converse, 'current', 0); + + const pubsub_jid = 'pubsub.shakespeare.lit'; + + const { api } = _converse; + const sent_stanzas = api.connection.get().sent_stanzas; + const own_jid = _converse.session.get('jid'); + + const node = 'princely_musings'; + const promise = api.pubsub.publish(pubsub_jid, node, stx``, { access_model: 'whitelist' }); + + await mock.waitUntilDiscoConfirmed( + _converse, + pubsub_jid, + [{ 'category': 'pubsub', 'type': 'pep' }], + ['http://jabber.org/protocol/pubsub#publish-options'] + ); + + let sent_stanza = await u.waitUntil(() => + sent_stanzas.filter((iq) => iq.querySelector('pubsub publish')).pop() + ); + expect(sent_stanza).toEqualStanza(stx` + + + + + + + http://jabber.org/protocol/pubsub#publish-options + + whitelist + + + + `); + + let response = stx` + + + + + `; + _converse.api.connection.get()._dataRecv(mock.createRequest(response)); + + sent_stanza = await u.waitUntil(() => + sent_stanzas.filter((iq) => iq.querySelector('pubsub configure')).pop() + ); + _converse.api.connection.get()._dataRecv( + mock.createRequest(stx` + + + + + + + + + + + open + + + + + `) + ); + + sent_stanza = await u.waitUntil(() => + sent_stanzas + .filter((iq) => iq.getAttribute('type') === 'set' && iq.querySelector('pubsub configure')) + .pop() + ); + + expect(sent_stanza).toEqualStanza(stx` + + + + http://jabber.org/protocol/pubsub#nodeconfig + whitelist + + + + `); + + _converse.api.connection.get()._dataRecv( + mock.createRequest(stx` + `) + ); + + // Clear old stanzas + while (sent_stanzas.length) sent_stanzas.pop(); + + sent_stanza = await u.waitUntil(() => + sent_stanzas.filter((iq) => iq.querySelector('pubsub publish')).pop() + ); + expect(sent_stanza).toEqualStanza(stx` + + + + + + + http://jabber.org/protocol/pubsub#publish-options + + whitelist + + + + `); + + _converse.api.connection.get()._dataRecv( + mock.createRequest(stx` + `) + ); + + await promise; + }) + ); + }); });