Python 3 port:
/!\ this is a huge commit /!\ starting from this commit, SàT is needs Python 3.6+ /!\ SàT maybe be instable or some feature may not work anymore, this will improve with time This patch port backend, bridge and frontends to Python 3. Roughly this has been done this way: - 2to3 tools has been applied (with python 3.7) - all references to python2 have been replaced with python3 (notably shebangs) - fixed files not handled by 2to3 (notably the shell script) - several manual fixes - fixed issues reported by Python 3 that where not handled in Python 2 - replaced "async" with "async_" when needed (it's a reserved word from Python 3.7) - replaced zope's "implements" with @implementer decorator - temporary hack to handle data pickled in database, as str or bytes may be returned, to be checked later - fixed hash comparison for password - removed some code which is not needed anymore with Python 3 - deactivated some code which needs to be checked (notably certificate validation) - tested with jp, fixed reported issues until some basic commands worked - ported Primitivus (after porting dependencies like urwid satext) - more manual fixes
This commit is contained in:
parent
d21a6a660f
commit
fbfe96c505
1 changed files with 6336 additions and 6398 deletions
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# SAT plugin for Pubsub Schemas
|
||||
|
|
@ -20,7 +20,7 @@
|
|||
from collections import Iterable
|
||||
import copy
|
||||
import itertools
|
||||
from zope.interface import implements
|
||||
from zope.interface import implementer
|
||||
from twisted.words.protocols.jabber import jid
|
||||
from twisted.words.protocols.jabber.xmlstream import XMPPHandler
|
||||
from twisted.internet import defer
|
||||
|
|
@ -38,23 +38,23 @@ from sat.core.log import getLogger
|
|||
|
||||
log = getLogger(__name__)
|
||||
|
||||
NS_SCHEMA = u"https://salut-a-toi/protocol/schema:0"
|
||||
NS_SCHEMA = "https://salut-a-toi/protocol/schema:0"
|
||||
|
||||
PLUGIN_INFO = {
|
||||
C.PI_NAME: u"PubSub Schema",
|
||||
C.PI_IMPORT_NAME: u"PUBSUB_SCHEMA",
|
||||
C.PI_TYPE: u"EXP",
|
||||
C.PI_NAME: "PubSub Schema",
|
||||
C.PI_IMPORT_NAME: "PUBSUB_SCHEMA",
|
||||
C.PI_TYPE: "EXP",
|
||||
C.PI_PROTOCOLS: [],
|
||||
C.PI_DEPENDENCIES: [u"XEP-0060", u"IDENTITY"],
|
||||
C.PI_MAIN: u"PubsubSchema",
|
||||
C.PI_HANDLER: u"yes",
|
||||
C.PI_DESCRIPTION: _(u"""Handle Pubsub data schemas"""),
|
||||
C.PI_DEPENDENCIES: ["XEP-0060", "IDENTITY"],
|
||||
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"))
|
||||
log.info(_("PubSub Schema initialization"))
|
||||
self.host = host
|
||||
self._p = self.host.plugins["XEP-0060"]
|
||||
self._i = self.host.plugins["IDENTITY"]
|
||||
|
|
@ -64,7 +64,7 @@ class PubsubSchema(object):
|
|||
in_sign="sss",
|
||||
out_sign="s",
|
||||
method=self._getSchema,
|
||||
async=True,
|
||||
async_=True,
|
||||
)
|
||||
host.bridge.addMethod(
|
||||
"psSchemaSet",
|
||||
|
|
@ -72,15 +72,16 @@ class PubsubSchema(object):
|
|||
in_sign="ssss",
|
||||
out_sign="",
|
||||
method=self._setSchema,
|
||||
async=True,
|
||||
async_=True,
|
||||
)
|
||||
host.bridge.addMethod(
|
||||
"psSchemaUIGet",
|
||||
".plugin",
|
||||
in_sign="sss",
|
||||
out_sign="s",
|
||||
method=utils.partial(self._getUISchema, default_node=None),
|
||||
async=True,
|
||||
method=lambda service, nodeIdentifier, profile_key: self._getUISchema(
|
||||
service, nodeIdentifier, default_node=None, profile_key=profile_key),
|
||||
async_=True,
|
||||
)
|
||||
host.bridge.addMethod(
|
||||
"psItemsFormGet",
|
||||
|
|
@ -88,7 +89,7 @@ class PubsubSchema(object):
|
|||
in_sign="ssssiassa{ss}s",
|
||||
out_sign="(asa{ss})",
|
||||
method=self._getDataFormItems,
|
||||
async=True,
|
||||
async_=True,
|
||||
)
|
||||
host.bridge.addMethod(
|
||||
"psItemFormSend",
|
||||
|
|
@ -96,7 +97,7 @@ class PubsubSchema(object):
|
|||
in_sign="ssa{sas}ssa{ss}s",
|
||||
out_sign="s",
|
||||
method=self._sendDataFormItem,
|
||||
async=True,
|
||||
async_=True,
|
||||
)
|
||||
|
||||
def getHandler(self, client):
|
||||
|
|
@ -104,7 +105,7 @@ class PubsubSchema(object):
|
|||
|
||||
def _getSchemaBridgeCb(self, schema_elt):
|
||||
if schema_elt is None:
|
||||
return u""
|
||||
return ""
|
||||
return schema_elt.toXml()
|
||||
|
||||
def _getSchema(self, service, nodeIdentifier, profile_key=C.PROF_KEY_NONE):
|
||||
|
|
@ -135,7 +136,7 @@ class PubsubSchema(object):
|
|||
@return (domish.Element, None): schema (<x> element)
|
||||
None if not schema has been set on this node
|
||||
"""
|
||||
iq_elt = client.IQ(u"get")
|
||||
iq_elt = client.IQ("get")
|
||||
if service is not None:
|
||||
iq_elt["to"] = service.full()
|
||||
pubsub_elt = iq_elt.addElement((NS_SCHEMA, "pubsub"))
|
||||
|
|
@ -163,12 +164,12 @@ class PubsubSchema(object):
|
|||
the form should not be modified if copy_form is not set
|
||||
"""
|
||||
if schema is None:
|
||||
log.debug(_(u"unspecified schema, we need to request it"))
|
||||
log.debug(_("unspecified schema, we need to request it"))
|
||||
schema = yield self.getSchema(client, service, nodeIdentifier)
|
||||
if schema is None:
|
||||
raise exceptions.DataError(
|
||||
_(
|
||||
u"no schema specified, and this node has no schema either, we can't construct the data form"
|
||||
"no schema specified, and this node has no schema either, we can't construct the data form"
|
||||
)
|
||||
)
|
||||
elif isinstance(schema, data_form.Form):
|
||||
|
|
@ -179,7 +180,7 @@ class PubsubSchema(object):
|
|||
try:
|
||||
form = data_form.Form.fromElement(schema)
|
||||
except data_form.Error as e:
|
||||
raise exceptions.DataError(_(u"Invalid Schema: {msg}").format(msg=e))
|
||||
raise exceptions.DataError(_("Invalid Schema: {msg}").format(msg=e))
|
||||
form.formType = form_type
|
||||
defer.returnValue(form)
|
||||
|
||||
|
|
@ -192,7 +193,7 @@ class PubsubSchema(object):
|
|||
profile_key=C.PROF_KEY_NONE):
|
||||
if not nodeIdentifier:
|
||||
if not default_node:
|
||||
raise ValueError(_(u"nodeIndentifier needs to be set"))
|
||||
raise ValueError(_("nodeIndentifier needs to be set"))
|
||||
nodeIdentifier = default_node
|
||||
client = self.host.getClient(profile_key)
|
||||
service = None if not service else jid.JID(service)
|
||||
|
|
@ -233,7 +234,7 @@ class PubsubSchema(object):
|
|||
client = self.host.getClient(profile_key)
|
||||
service = jid.JID(service) if service else None
|
||||
if not node:
|
||||
raise exceptions.DataError(_(u"empty node is not allowed"))
|
||||
raise exceptions.DataError(_("empty node is not allowed"))
|
||||
if schema:
|
||||
schema = generic.parseXml(schema.encode("utf-8"))
|
||||
else:
|
||||
|
|
@ -276,7 +277,7 @@ class PubsubSchema(object):
|
|||
if not nodeIdentifier:
|
||||
if not default_node:
|
||||
raise ValueError(
|
||||
_(u"default_node must be set if nodeIdentifier is not set")
|
||||
_("default_node must be set if nodeIdentifier is not set")
|
||||
)
|
||||
nodeIdentifier = default_node
|
||||
# we need the initial form to get options of fields when suitable
|
||||
|
|
@ -296,7 +297,7 @@ class PubsubSchema(object):
|
|||
items, metadata = items_data
|
||||
items_xmlui = []
|
||||
for item_elt in items:
|
||||
for x_elt in item_elt.elements((data_form.NS_X_DATA, u"x")):
|
||||
for x_elt in item_elt.elements((data_form.NS_X_DATA, "x")):
|
||||
form = data_form.Form.fromElement(x_elt)
|
||||
if form_ns and form.formNamespace != form_ns:
|
||||
continue
|
||||
|
|
@ -307,9 +308,9 @@ class PubsubSchema(object):
|
|||
# are not checked
|
||||
prepend=(
|
||||
("label", "id"),
|
||||
("text", item_elt["id"], u"id"),
|
||||
("text", item_elt["id"], "id"),
|
||||
("label", "publisher"),
|
||||
("text", item_elt.getAttribute("publisher", ""), u"publisher"),
|
||||
("text", item_elt.getAttribute("publisher", ""), "publisher"),
|
||||
),
|
||||
filters=filters,
|
||||
read_only=False,
|
||||
|
|
@ -336,7 +337,7 @@ class PubsubSchema(object):
|
|||
extra,
|
||||
deserialise=True,
|
||||
)
|
||||
d.addCallback(lambda ret: ret or u"")
|
||||
d.addCallback(lambda ret: ret or "")
|
||||
return d
|
||||
|
||||
@defer.inlineCallbacks
|
||||
|
|
@ -361,22 +362,22 @@ class PubsubSchema(object):
|
|||
client, service, nodeIdentifier, schema, form_type="submit"
|
||||
)
|
||||
|
||||
for name, values_list in values.iteritems():
|
||||
for name, values_list in values.items():
|
||||
try:
|
||||
field = form.fields[name]
|
||||
except KeyError:
|
||||
log.warning(
|
||||
_(u"field {name} doesn't exist, ignoring it").format(name=name)
|
||||
_("field {name} doesn't exist, ignoring it").format(name=name)
|
||||
)
|
||||
continue
|
||||
if isinstance(values_list, basestring) or not isinstance(
|
||||
if isinstance(values_list, str) or not isinstance(
|
||||
values_list, Iterable
|
||||
):
|
||||
values_list = [values_list]
|
||||
if deserialise:
|
||||
if field.fieldType == u"boolean":
|
||||
if field.fieldType == "boolean":
|
||||
values_list = [C.bool(v) for v in values_list]
|
||||
elif field.fieldType == u"text-multi":
|
||||
elif field.fieldType == "text-multi":
|
||||
# for text-multi, lines must be put on separate values
|
||||
values_list = list(
|
||||
itertools.chain(*[v.splitlines() for v in values_list])
|
||||
|
|
@ -384,9 +385,9 @@ class PubsubSchema(object):
|
|||
elif xml_tools.isXHTMLField(field):
|
||||
values_list = [generic.parseXml(v.encode("utf-8"))
|
||||
for v in values_list]
|
||||
elif u"jid" in (field.fieldType or u""):
|
||||
elif "jid" in (field.fieldType or ""):
|
||||
values_list = [jid.JID(v) for v in values_list]
|
||||
if u"list" in (field.fieldType or u""):
|
||||
if "list" in (field.fieldType or ""):
|
||||
# for lists, we check that given values are allowed in form
|
||||
allowed_values = [o.value for o in field.options]
|
||||
values_list = [v for v in values_list if v in allowed_values]
|
||||
|
|
@ -419,26 +420,26 @@ class PubsubSchema(object):
|
|||
|
||||
main use case is using a textbox for labels
|
||||
"""
|
||||
if widget_type != u"textbox":
|
||||
if widget_type != "textbox":
|
||||
return widget_type, args, kwargs
|
||||
widget_type = u"list"
|
||||
options = [o for o in args.pop(0).split(u"\n") if o]
|
||||
widget_type = "list"
|
||||
options = [o for o in args.pop(0).split("\n") if o]
|
||||
kwargs = {
|
||||
"options": options,
|
||||
"name": kwargs.get("name"),
|
||||
"styles": (u"noselect", u"extensible", u"reducible"),
|
||||
"styles": ("noselect", "extensible", "reducible"),
|
||||
}
|
||||
return widget_type, args, kwargs
|
||||
|
||||
def dateFilter(self, form_xmlui, widget_type, args, kwargs):
|
||||
"""Convert a string with a date to a unix timestamp"""
|
||||
if widget_type != u"string" or not args[0]:
|
||||
if widget_type != "string" or not args[0]:
|
||||
return widget_type, args, kwargs
|
||||
# we convert XMPP date to timestamp
|
||||
try:
|
||||
args[0] = unicode(date_utils.date_parse(args[0]))
|
||||
args[0] = str(date_utils.date_parse(args[0]))
|
||||
except Exception as e:
|
||||
log.warning(_(u"Can't parse date field: {msg}").format(msg=e))
|
||||
log.warning(_("Can't parse date field: {msg}").format(msg=e))
|
||||
return widget_type, args, kwargs
|
||||
|
||||
## Helper methods ##
|
||||
|
|
@ -479,7 +480,7 @@ class PubsubSchema(object):
|
|||
# have to modify them
|
||||
if C.bool(extra.get("labels_as_list", C.BOOL_FALSE)):
|
||||
filters = filters.copy()
|
||||
filters[u"labels"] = self.textbox2ListFilter
|
||||
filters["labels"] = self.textbox2ListFilter
|
||||
client, service, node, max_items, extra, sub_id = self.prepareBridgeGet(
|
||||
service, node, max_items, sub_id, extra, profile_key
|
||||
)
|
||||
|
|
@ -536,7 +537,7 @@ class PubsubSchema(object):
|
|||
item_elt = items_data[0][0]
|
||||
except Exception as e:
|
||||
log.warning(
|
||||
_(u"Can't get previous item, update ignored: {reason}").format(
|
||||
_("Can't get previous item, update ignored: {reason}").format(
|
||||
reason=e
|
||||
)
|
||||
)
|
||||
|
|
@ -546,13 +547,13 @@ class PubsubSchema(object):
|
|||
if form is None:
|
||||
log.warning(
|
||||
_(
|
||||
u"Can't parse previous item, update ignored: data form not found"
|
||||
"Can't parse previous item, update ignored: data form not found"
|
||||
).format(reason=e)
|
||||
)
|
||||
else:
|
||||
for name, field in form.fields.iteritems():
|
||||
for name, field in form.fields.items():
|
||||
if name not in values:
|
||||
values[name] = u"\n".join(unicode(v) for v in field.values)
|
||||
values[name] = "\n".join(str(v) for v in field.values)
|
||||
|
||||
def _set(self, service, node, values, schema=None, item_id=None, extra=None,
|
||||
default_node=None, form_ns=None, fill_author=True,
|
||||
|
|
@ -578,7 +579,7 @@ class PubsubSchema(object):
|
|||
default_node=default_node,
|
||||
fill_author=fill_author,
|
||||
)
|
||||
d.addCallback(lambda ret: ret or u"")
|
||||
d.addCallback(lambda ret: ret or "")
|
||||
return d
|
||||
|
||||
@defer.inlineCallbacks
|
||||
|
|
@ -605,15 +606,15 @@ class PubsubSchema(object):
|
|||
extra = {}
|
||||
if not node:
|
||||
if default_node is None:
|
||||
raise ValueError(_(u"default_node must be set if node is not set"))
|
||||
raise ValueError(_("default_node must be set if node is not set"))
|
||||
node = default_node
|
||||
now = utils.xmpp_date()
|
||||
if not item_id:
|
||||
values["created"] = now
|
||||
elif extra.get(u"update", False):
|
||||
elif extra.get("update", False):
|
||||
if item_id is None:
|
||||
raise exceptions.DataError(
|
||||
_(u'if extra["update"] is set, item_id must be set too')
|
||||
_('if extra["update"] is set, item_id must be set too')
|
||||
)
|
||||
yield self.copyMissingValues(client, service, node, item_id, form_ns, values)
|
||||
|
||||
|
|
@ -630,8 +631,8 @@ class PubsubSchema(object):
|
|||
defer.returnValue(item_id)
|
||||
|
||||
|
||||
@implementer(iwokkel.IDisco)
|
||||
class SchemaHandler(XMPPHandler):
|
||||
implements(iwokkel.IDisco)
|
||||
|
||||
def getDiscoInfo(self, requestor, service, nodeIdentifier=""):
|
||||
return [disco.DiscoFeature(NS_SCHEMA)]
|
||||
|
|
|
|||
Loading…
Reference in a new issue