changeset 3:5aa1bf7154b0

Add a simple PEP node viewer and editor.
author Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
date Sat, 22 Dec 2018 02:23:38 +0100
parents db033e5eabcb
children 5e97e1808a35
files avatar.js client.js index.xhtml pep.js util.js
diffstat 5 files changed, 147 insertions(+), 21 deletions(-) [+]
line wrap: on
line diff
--- a/avatar.js
+++ b/avatar.js
@@ -171,20 +171,7 @@ function initAvatar(connection) {
     }
 
     avatar_access.addEventListener('change', function (evt) {
-        const iq = $iq({type: 'set'})
-            .c('pubsub', {xmlns: 'http://jabber.org/protocol/pubsub#owner'})
-                .c('configure', {node: 'urn:xmpp:avatar:metadata'})
-                    .c('x', {xmlns: 'jabber:x:data', type: 'submit'})
-                        .c('field', {'var': 'FORM_TYPE', type: 'hidden'})
-                            .c('value')
-                                .t('http://jabber.org/protocol/pubsub#node_config')
-                                .up()
-                            .up()
-                        .c('field', {'var': 'pubsub#access_model'})
-                            .c('value')
-                                .t(evt.target.value)
-                                .up()
-                            .up()
+        const iq = configurePEPField('urn:xmpp:avatar:metadata', 'pubsub#access_model', evt.target.value);
         connection.sendIQ(iq, onAvatarConfigured, onAvatarConfigureError.bind(null, 'PubSub configuration failed.'));
     });
 
--- a/client.js
+++ b/client.js
@@ -95,6 +95,7 @@ document.addEventListener('DOMContentLoa
     function onConnected()
     {
         connected_div.hidden = false;
+        initPEP(connection);
         initNickname(connection);
         initAvatar(connection);
     }
--- a/index.xhtml
+++ b/index.xhtml
@@ -62,6 +62,13 @@
 <option value="presence">Only your contacts</option>
 </select></label>
 </p>
+<h2>PEP</h2>
+<table>
+<thead>
+<tr><th>PEP node</th><th>Title</th><th>Description</th><th>Type</th><th>⚠️ Delete</th></tr>
+</thead>
+<tbody id="pep-table"/>
+</table>
 <h2>Dangerous zone</h2>
 <p>
 <button disabled="">Change my password</button>
@@ -98,6 +105,7 @@
 <script src="client.js"/>
 <script src="nickname.js"/>
 <script src="avatar.js"/>
+<script src="pep.js"/>
 
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/pep.js
@@ -0,0 +1,107 @@
+'use strict';
+
+function initPEP(connection) {
+    const pep_table = document.getElementById('pep-table');
+
+    const iq = $iq({type: 'get'})
+        .c('query', {xmlns: NS.disco_items});
+    connection.sendIQ(iq, onDiscoItems, onDiscoItemsError.bind(null, 'disco#items query failed.'));
+
+    function onDiscoItems(result_iq)
+    {
+        const items = parseXPath(result_iq, './disco_items:query/disco_items:item', XPathResult.ORDERED_NODE_ITERATOR_TYPE);
+        while (true) {
+            const item = items.iterateNext();
+            if (!item)
+                break;
+            const node = item.getAttributeNS(null, 'node');
+            const tr = document.createElementNS('http://www.w3.org/1999/xhtml', 'tr');
+            const td = document.createElementNS('http://www.w3.org/1999/xhtml', 'td');
+            td.textContent = node;
+            tr.appendChild(td);
+            pep_table.appendChild(tr);
+            const iq = $iq({type: 'get'})
+                .c('query', {xmlns: NS.disco_info, node: node});
+            connection.sendIQ(iq, onDiscoInfo.bind(tr), onDiscoInfoError.bind(tr, 'disco#items query failed.'));
+        }
+    }
+
+    function onDiscoItemsError(string)
+    {
+        console.log('Failed to retrieve the list of PEP nodes: ' + string);
+    }
+
+    function onDiscoInfo(result_iq)
+    {
+        const tr = this;
+        const node = parseXPath(result_iq, './disco_info:query').getAttributeNS(null, 'node');
+        const fields = parseXPath(result_iq, './disco_info:query/dataforms:x/dataforms:field', XPathResult.ORDERED_NODE_ITERATOR_TYPE);
+        const parsed_fields = {};
+        while (true) {
+            const field = fields.iterateNext();
+            if (!field)
+                break;
+            const type = field.getAttributeNS(null, 'type');
+            if (type == 'hidden')
+                continue;
+            const var_ = field.getAttributeNS(null, 'var');
+            const value = parseXPath(field, './dataforms:value');
+            parsed_fields[var_] = value ? value.textContent : '';
+        }
+        for (let i of ['pubsub#title', 'pubsub#description', 'pubsub#type']) {
+            const td = document.createElementNS('http://www.w3.org/1999/xhtml', 'td');
+            const input = document.createElementNS('http://www.w3.org/1999/xhtml', 'input');
+            input.value = parsed_fields[i];
+            input.onblur = function (evt) {
+                const iq = configurePEPField(node, i, evt.target.value);
+                connection.sendIQ(iq, onPEPConfigured, onPEPConfigureError.bind(null, 'PubSub configuration failed.'));
+            };
+            td.appendChild(input);
+            tr.appendChild(td);
+        }
+        const td = document.createElementNS('http://www.w3.org/1999/xhtml', 'td');
+        const button = document.createElementNS('http://www.w3.org/1999/xhtml', 'button');
+        button.textContent = 'Delete this node';
+        button.onclick = function (evt) {
+            const iq = $iq({type: 'set'})
+                .c('pubsub', {xmlns: NS.pubsub_owner})
+                    .c('delete', {node: node});
+            connection.sendIQ(iq, onNodeDeleted.bind(node), onNodeDeletionError.bind(node, 'PEP node deletion failed.'));
+        };
+        td.appendChild(button);
+        tr.appendChild(td);
+    }
+
+    function onDiscoInfoError(string)
+    {
+        console.log('Failed to retrieve PEP node info: ' + string);
+    }
+
+    function onPEPConfigured(result_iq)
+    {
+        console.log('PEP node successfully configured.');
+    }
+
+    function onPEPConfigureError(string)
+    {
+        console.log('Configuration of PEP node failed: ' + string);
+    }
+
+    function onNodeDeleted(result_iq)
+    {
+        const node = this;
+        for (let child of pep_table.children) {
+            if (node != child.firstChild.textContent)
+                continue
+            child.remove();
+            break;
+        }
+        console.log('Node “' + node + '” successfully deleted.');
+    }
+
+    function onNodeDeletionError(string)
+    {
+        const node = this;
+        console.log('Deletion of PEP node “' + node + '” failed: ' + string);
+    }
+}
--- a/util.js
+++ b/util.js
@@ -1,11 +1,17 @@
+const NS = {
+    xrd: 'http://docs.oasis-open.org/ns/xri/xrd-1.0',
+    disco_items: 'http://jabber.org/protocol/disco#items',
+    disco_info: 'http://jabber.org/protocol/disco#info',
+    dataforms: 'jabber:x:data',
+    pubsub: 'http://jabber.org/protocol/pubsub',
+    pubsub_owner: 'http://jabber.org/protocol/pubsub#owner',
+    avatar_metadata: 'urn:xmpp:avatar:metadata',
+    avatar_data: 'urn:xmpp:avatar:data',
+    nickname: 'http://jabber.org/protocol/nick',
+};
+
 function nsResolver(prefix) {
-    return {
-        xrd: 'http://docs.oasis-open.org/ns/xri/xrd-1.0',
-        pubsub: 'http://jabber.org/protocol/pubsub',
-        avatar_metadata: 'urn:xmpp:avatar:metadata',
-        avatar_data: 'urn:xmpp:avatar:data',
-        nickname: 'http://jabber.org/protocol/nick',
-    }[prefix] || null;
+    return NS[prefix] || null;
 }
 
 function parseXPath(elem, xpath, result)
@@ -17,3 +23,20 @@ function parseXPath(elem, xpath, result)
         return value.singleNodeValue;
     return value;
 }
+
+function configurePEPField(node, key, value, cb, err_cb) {
+    return $iq({type: 'set'})
+        .c('pubsub', {xmlns: 'http://jabber.org/protocol/pubsub#owner'})
+            .c('configure', {node: node})
+                .c('x', {xmlns: 'jabber:x:data', type: 'submit'})
+                    .c('field', {'var': 'FORM_TYPE', type: 'hidden'})
+                        .c('value')
+                            .t('http://jabber.org/protocol/pubsub#node_config')
+                            .up()
+                        .up()
+                    .c('field', {'var': key})
+                        .c('value')
+                            .t(value)
+                            .up()
+                        .up();
+}