plugin schema: PubSub node schema, first draft

node schema is a new SàT Pubsub experimental feature allowing to attach a schema to a node.
This commit is contained in:
Goffi 2017-09-06 07:38:39 +02:00
commit 2f3727b85d

View file

@ -0,0 +1,141 @@
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
# SAT plugin for Pubsub Schemas
# Copyright (C) 2009-2017 Jérôme Poisson (goffi@goffi.org)
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from sat.core.i18n import _
from sat.core import exceptions
from sat.core.constants import Const as C
from twisted.words.protocols.jabber import jid
from twisted.words.protocols.jabber.xmlstream import XMPPHandler
from sat.core.log import getLogger
log = getLogger(__name__)
from wokkel import disco, iwokkel
from wokkel import data_form
from wokkel import generic
from zope.interface import implements
NS_SCHEMA = 'https://salut-a-toi/protocol/schema:0'
NS_SCHEMA_FORM = 'https://salut-a-toi/protocol/schema#schema:0'
PLUGIN_INFO = {
C.PI_NAME: "PubSub Schema",
C.PI_IMPORT_NAME: "PUBSUB_SCHEMA",
C.PI_TYPE: "EXP",
C.PI_PROTOCOLS: [],
C.PI_DEPENDENCIES: ["XEP-0060"],
C.PI_MAIN: "PubsubSchema",
C.PI_HANDLER: "yes",
C.PI_DESCRIPTION: _("""Handle Pubsub data schemas""")
}
class PubsubSchema(object):
def __init__(self, host):
log.info(_(u"PubSub Schema initialization"))
self.host = host
host.bridge.addMethod("psSchemaGet", ".plugin",
in_sign='sss', out_sign='s',
method=self._getSchema,
async=True
)
host.bridge.addMethod("psSchemaSet", ".plugin",
in_sign='ssss', out_sign='',
method=self._setSchema,
async=True
)
def getHandler(self, client):
return SchemaHandler()
def _getSchemaBridgeCb(self, schema_elt):
if schema_elt is None:
return u''
return schema_elt.toXml()
def _getSchema(self, service, nodeIdentifier, profile_key=C.PROF_KEY_NONE):
client = self.host.getClient(profile_key)
service = None if not service else jid.JID(service)
d = self.getSchema(client, service, nodeIdentifier)
d.addCallback(self._getSchemaBridgeCb)
return d
def _getSchemaCb(self, iq_elt):
try:
schema_elt = next(iq_elt.elements(NS_SCHEMA, 'schema'))
except StopIteration:
raise exceptions.DataError('missing <schema> element')
schema_form = data_form.findForm(schema_elt, NS_SCHEMA_FORM)
if schema_form is None:
# there is not schema on this node
return None
# we get again the form because we need all elements/namespaces
# while schema_form.toElement while only keep XEP-0004 elements
x_elt = next(schema_elt.elements(data_form.NS_X_DATA, 'x'))
return x_elt
def getSchema(self, client, service, nodeIdentifier):
"""retrieve PubSub node schema
@param service(jid.JID, None): jid of PubSub service
None to use our PEP
@param nodeIdentifier(unicode): node to get schema from
@return (domish.Element, None): schema (<x> element)
None if not schema has been set on this node
"""
iq_elt = client.IQ(u'get')
if service is not None:
iq_elt['to'] = service.full()
pubsub_elt = iq_elt.addElement((NS_SCHEMA, 'pubsub'))
schema_elt = pubsub_elt.addElement((NS_SCHEMA, 'schema'))
schema_elt['node'] = nodeIdentifier
d = iq_elt.send()
d.addCallback(self._getSchemaCb)
return d
def _setSchema(self, service, nodeIdentifier, schema, profile_key=C.PROF_KEY_NONE):
client = self.host.getClient(profile_key)
service = None if not service else jid.JID(service)
schema = generic.parseXml(schema.encode('utf-8'))
return self.setSchema(client, service, nodeIdentifier, schema)
def setSchema(self, client, service, nodeIdentifier, schema):
"""set or replace PubSub node schema
@param schema(domish.Element, None): schema to set
None if schema need to be removed
"""
iq_elt = client.IQ()
if service is not None:
iq_elt['to'] = service.full()
pubsub_elt = iq_elt.addElement((NS_SCHEMA, 'pubsub'))
schema_elt = pubsub_elt.addElement((NS_SCHEMA, 'schema'))
schema_elt['node'] = nodeIdentifier
if schema is not None:
schema_elt.addChild(schema)
return iq_elt.send()
class SchemaHandler(XMPPHandler):
implements(iwokkel.IDisco)
def getDiscoInfo(self, requestor, service, nodeIdentifier=''):
return [disco.DiscoFeature(NS_SCHEMA)]
def getDiscoItems(self, requestor, service, nodeIdentifier=''):
return []