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
100 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 adding D-Bus to Ad-Hoc Commands
|
||||
|
|
@ -30,8 +30,8 @@ try:
|
|||
from lxml import etree
|
||||
except ImportError:
|
||||
etree = None
|
||||
log.warning(u"Missing module lxml, please download/install it from http://lxml.de/ ."
|
||||
u"Auto D-Bus discovery will be disabled")
|
||||
log.warning("Missing module lxml, please download/install it from http://lxml.de/ ."
|
||||
"Auto D-Bus discovery will be disabled")
|
||||
from collections import OrderedDict
|
||||
import os.path
|
||||
import uuid
|
||||
|
|
@ -40,8 +40,8 @@ try:
|
|||
from dbus.mainloop.glib import DBusGMainLoop
|
||||
except ImportError:
|
||||
dbus = None
|
||||
log.warning(u"Missing module dbus, please download/install it"
|
||||
u"auto D-Bus discovery will be disabled")
|
||||
log.warning("Missing module dbus, please download/install it"
|
||||
"auto D-Bus discovery will be disabled")
|
||||
|
||||
else:
|
||||
DBusGMainLoop(set_as_default=True)
|
||||
|
|
@ -50,18 +50,18 @@ NS_MEDIA_PLAYER = "org.salutatoi.mediaplayer"
|
|||
FD_NAME = "org.freedesktop.DBus"
|
||||
FD_PATH = "/org/freedekstop/DBus"
|
||||
INTROSPECT_IFACE = "org.freedesktop.DBus.Introspectable"
|
||||
MPRIS_PREFIX = u"org.mpris.MediaPlayer2"
|
||||
CMD_GO_BACK = u"GoBack"
|
||||
CMD_GO_FWD = u"GoFW"
|
||||
MPRIS_PREFIX = "org.mpris.MediaPlayer2"
|
||||
CMD_GO_BACK = "GoBack"
|
||||
CMD_GO_FWD = "GoFW"
|
||||
SEEK_OFFSET = 5 * 1000 * 1000
|
||||
MPRIS_COMMANDS = [u"org.mpris.MediaPlayer2.Player." + cmd for cmd in (
|
||||
u"Previous", CMD_GO_BACK, u"PlayPause", CMD_GO_FWD, u"Next")]
|
||||
MPRIS_PATH = u"/org/mpris/MediaPlayer2"
|
||||
MPRIS_COMMANDS = ["org.mpris.MediaPlayer2.Player." + cmd for cmd in (
|
||||
"Previous", CMD_GO_BACK, "PlayPause", CMD_GO_FWD, "Next")]
|
||||
MPRIS_PATH = "/org/mpris/MediaPlayer2"
|
||||
MPRIS_PROPERTIES = OrderedDict((
|
||||
(u"org.mpris.MediaPlayer2", (
|
||||
("org.mpris.MediaPlayer2", (
|
||||
"Identity",
|
||||
)),
|
||||
(u"org.mpris.MediaPlayer2.Player", (
|
||||
("org.mpris.MediaPlayer2.Player", (
|
||||
"Metadata",
|
||||
"PlaybackStatus",
|
||||
"Volume",
|
||||
|
|
@ -69,7 +69,7 @@ MPRIS_PROPERTIES = OrderedDict((
|
|||
))
|
||||
MPRIS_METADATA_KEY = "Metadata"
|
||||
MPRIS_METADATA_MAP = OrderedDict((
|
||||
("xesam:title", u"Title"),
|
||||
("xesam:title", "Title"),
|
||||
))
|
||||
|
||||
INTROSPECT_METHOD = "Introspect"
|
||||
|
|
@ -88,7 +88,7 @@ PLUGIN_INFO = {
|
|||
C.PI_DEPENDENCIES: ["XEP-0050"],
|
||||
C.PI_MAIN: "AdHocDBus",
|
||||
C.PI_HANDLER: "no",
|
||||
C.PI_DESCRIPTION: _(u"""Add D-Bus management to Ad-Hoc commands"""),
|
||||
C.PI_DESCRIPTION: _("""Add D-Bus management to Ad-Hoc commands"""),
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -104,7 +104,7 @@ class AdHocDBus(object):
|
|||
in_sign="sasasasasasass",
|
||||
out_sign="(sa(sss))",
|
||||
method=self._adHocDBusAddAuto,
|
||||
async=True,
|
||||
async_=True,
|
||||
)
|
||||
host.bridge.addMethod(
|
||||
"adHocRemotesGet",
|
||||
|
|
@ -112,10 +112,10 @@ class AdHocDBus(object):
|
|||
in_sign="s",
|
||||
out_sign="a(sss)",
|
||||
method=self._adHocRemotesGet,
|
||||
async=True,
|
||||
async_=True,
|
||||
)
|
||||
self._c = host.plugins["XEP-0050"]
|
||||
host.registerNamespace(u"mediaplayer", NS_MEDIA_PLAYER)
|
||||
host.registerNamespace("mediaplayer", NS_MEDIA_PLAYER)
|
||||
if dbus is not None:
|
||||
self.session_bus = dbus.SessionBus()
|
||||
self.fd_object = self.session_bus.get_object(
|
||||
|
|
@ -124,7 +124,7 @@ class AdHocDBus(object):
|
|||
def profileConnected(self, client):
|
||||
if dbus is not None:
|
||||
self._c.addAdHocCommand(
|
||||
client, self.localMediaCb, D_(u"Media Players"),
|
||||
client, self.localMediaCb, D_("Media Players"),
|
||||
node=NS_MEDIA_PLAYER,
|
||||
timeout=60*60*6 # 6 hours timeout, to avoid breaking remote
|
||||
# in the middle of a movie
|
||||
|
|
@ -151,7 +151,7 @@ class AdHocDBus(object):
|
|||
|
||||
def _DBusGetProperty(self, proxy, interface, name):
|
||||
return self._DBusAsyncCall(
|
||||
proxy, u"Get", interface, name, interface=u"org.freedesktop.DBus.Properties")
|
||||
proxy, "Get", interface, name, interface="org.freedesktop.DBus.Properties")
|
||||
|
||||
|
||||
def _DBusListNames(self):
|
||||
|
|
@ -271,7 +271,7 @@ class AdHocDBus(object):
|
|||
elif len(actions) == 2:
|
||||
# we should have the answer here
|
||||
try:
|
||||
x_elt = command_elt.elements(data_form.NS_X_DATA, "x").next()
|
||||
x_elt = next(command_elt.elements(data_form.NS_X_DATA, "x"))
|
||||
answer_form = data_form.Form.fromElement(x_elt)
|
||||
command = answer_form["command"]
|
||||
except (KeyError, StopIteration):
|
||||
|
|
@ -295,11 +295,11 @@ class AdHocDBus(object):
|
|||
return DBusCallback(
|
||||
client, None, session_data, self._c.ACTION.EXECUTE, node
|
||||
)
|
||||
form = data_form.Form("form", title=_(u"Updated"))
|
||||
form.addField(data_form.Field("fixed", u"Command sent"))
|
||||
form = data_form.Form("form", title=_("Updated"))
|
||||
form.addField(data_form.Field("fixed", "Command sent"))
|
||||
status = self._c.STATUS.COMPLETED
|
||||
payload = None
|
||||
note = (self._c.NOTE.INFO, _(u"Command sent"))
|
||||
note = (self._c.NOTE.INFO, _("Command sent"))
|
||||
else:
|
||||
raise self._c.AdHocError(self._c.ERROR.INTERNAL)
|
||||
|
||||
|
|
@ -363,18 +363,18 @@ class AdHocDBus(object):
|
|||
opt.label or opt.value))
|
||||
except Exception as e:
|
||||
log.warning(_(
|
||||
u"Can't retrieve remote controllers on {device_jid}: "
|
||||
u"{reason}".format(device_jid=device_jid, reason=e)))
|
||||
"Can't retrieve remote controllers on {device_jid}: "
|
||||
"{reason}".format(device_jid=device_jid, reason=e)))
|
||||
break
|
||||
defer.returnValue(remotes)
|
||||
|
||||
def doMPRISCommand(self, proxy, command):
|
||||
iface, command = command.rsplit(u".", 1)
|
||||
iface, command = command.rsplit(".", 1)
|
||||
if command == CMD_GO_BACK:
|
||||
command = u'Seek'
|
||||
command = 'Seek'
|
||||
args = [-SEEK_OFFSET]
|
||||
elif command == CMD_GO_FWD:
|
||||
command = u'Seek'
|
||||
command = 'Seek'
|
||||
args = [SEEK_OFFSET]
|
||||
else:
|
||||
args = []
|
||||
|
|
@ -382,17 +382,17 @@ class AdHocDBus(object):
|
|||
|
||||
def addMPRISMetadata(self, form, metadata):
|
||||
"""Serialise MRPIS Metadata according to MPRIS_METADATA_MAP"""
|
||||
for mpris_key, name in MPRIS_METADATA_MAP.iteritems():
|
||||
for mpris_key, name in MPRIS_METADATA_MAP.items():
|
||||
if mpris_key in metadata:
|
||||
value = unicode(metadata[mpris_key])
|
||||
form.addField(data_form.Field(fieldType=u"fixed",
|
||||
value = str(metadata[mpris_key])
|
||||
form.addField(data_form.Field(fieldType="fixed",
|
||||
var=name,
|
||||
value=value))
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def localMediaCb(self, client, command_elt, session_data, action, node):
|
||||
try:
|
||||
x_elt = command_elt.elements(data_form.NS_X_DATA, "x").next()
|
||||
x_elt = next(command_elt.elements(data_form.NS_X_DATA, "x"))
|
||||
command_form = data_form.Form.fromElement(x_elt)
|
||||
except StopIteration:
|
||||
command_form = None
|
||||
|
|
@ -402,16 +402,16 @@ class AdHocDBus(object):
|
|||
bus_names = yield self._DBusListNames()
|
||||
bus_names = [b for b in bus_names if b.startswith(MPRIS_PREFIX)]
|
||||
if len(bus_names) == 0:
|
||||
note = (self._c.NOTE.INFO, D_(u"No media player found."))
|
||||
note = (self._c.NOTE.INFO, D_("No media player found."))
|
||||
defer.returnValue((None, self._c.STATUS.COMPLETED, None, note))
|
||||
options = []
|
||||
status = self._c.STATUS.EXECUTING
|
||||
form = data_form.Form("form", title=D_(u"Media Player Selection"),
|
||||
form = data_form.Form("form", title=D_("Media Player Selection"),
|
||||
formNamespace=NS_MEDIA_PLAYER)
|
||||
for bus in bus_names:
|
||||
player_name = bus[len(MPRIS_PREFIX)+1:]
|
||||
if not player_name:
|
||||
log.warning(_(u"Ignoring MPRIS bus without suffix"))
|
||||
log.warning(_("Ignoring MPRIS bus without suffix"))
|
||||
continue
|
||||
options.append(data_form.Option(bus, player_name))
|
||||
field = data_form.Field(
|
||||
|
|
@ -423,53 +423,53 @@ class AdHocDBus(object):
|
|||
else:
|
||||
# player request
|
||||
try:
|
||||
bus_name = command_form[u"media_player"]
|
||||
bus_name = command_form["media_player"]
|
||||
except KeyError:
|
||||
raise ValueError(_(u"missing media_player value"))
|
||||
raise ValueError(_("missing media_player value"))
|
||||
|
||||
if not bus_name.startswith(MPRIS_PREFIX):
|
||||
log.warning(_(u"Media player ad-hoc command trying to use non MPRIS bus. "
|
||||
u"Hack attempt? Refused bus: {bus_name}").format(
|
||||
log.warning(_("Media player ad-hoc command trying to use non MPRIS bus. "
|
||||
"Hack attempt? Refused bus: {bus_name}").format(
|
||||
bus_name=bus_name))
|
||||
note = (self._c.NOTE.ERROR, D_(u"Invalid player name."))
|
||||
note = (self._c.NOTE.ERROR, D_("Invalid player name."))
|
||||
defer.returnValue((None, self._c.STATUS.COMPLETED, None, note))
|
||||
|
||||
try:
|
||||
proxy = self.session_bus.get_object(bus_name, MPRIS_PATH)
|
||||
except dbus.exceptions.DBusException as e:
|
||||
log.warning(_(u"Can't get D-Bus proxy: {reason}").format(reason=e))
|
||||
note = (self._c.NOTE.ERROR, D_(u"Media player is not available anymore"))
|
||||
log.warning(_("Can't get D-Bus proxy: {reason}").format(reason=e))
|
||||
note = (self._c.NOTE.ERROR, D_("Media player is not available anymore"))
|
||||
defer.returnValue((None, self._c.STATUS.COMPLETED, None, note))
|
||||
try:
|
||||
command = command_form[u"command"]
|
||||
command = command_form["command"]
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
yield self.doMPRISCommand(proxy, command)
|
||||
|
||||
# we construct the remote control form
|
||||
form = data_form.Form("form", title=D_(u"Media Player Selection"))
|
||||
form.addField(data_form.Field(fieldType=u"hidden",
|
||||
var=u"media_player",
|
||||
form = data_form.Form("form", title=D_("Media Player Selection"))
|
||||
form.addField(data_form.Field(fieldType="hidden",
|
||||
var="media_player",
|
||||
value=bus_name))
|
||||
for iface, properties_names in MPRIS_PROPERTIES.iteritems():
|
||||
for iface, properties_names in MPRIS_PROPERTIES.items():
|
||||
for name in properties_names:
|
||||
try:
|
||||
value = yield self._DBusGetProperty(proxy, iface, name)
|
||||
except Exception as e:
|
||||
log.warning(_(u"Can't retrieve attribute {name}: {reason}")
|
||||
log.warning(_("Can't retrieve attribute {name}: {reason}")
|
||||
.format(name=name, reason=e))
|
||||
continue
|
||||
if name == MPRIS_METADATA_KEY:
|
||||
self.addMPRISMetadata(form, value)
|
||||
else:
|
||||
form.addField(data_form.Field(fieldType=u"fixed",
|
||||
form.addField(data_form.Field(fieldType="fixed",
|
||||
var=name,
|
||||
value=unicode(value)))
|
||||
value=str(value)))
|
||||
|
||||
commands = [data_form.Option(c, c.rsplit(u".", 1)[1]) for c in MPRIS_COMMANDS]
|
||||
form.addField(data_form.Field(fieldType=u"list-single",
|
||||
var=u"command",
|
||||
commands = [data_form.Option(c, c.rsplit(".", 1)[1]) for c in MPRIS_COMMANDS]
|
||||
form.addField(data_form.Field(fieldType="list-single",
|
||||
var="command",
|
||||
options=commands,
|
||||
required=True))
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# SàT plugin for import external blogs
|
||||
|
|
@ -31,7 +31,7 @@ from sat.tools import xml_tools
|
|||
import os
|
||||
import os.path
|
||||
import tempfile
|
||||
import urlparse
|
||||
import urllib.parse
|
||||
import shortuuid
|
||||
|
||||
|
||||
|
|
@ -43,7 +43,7 @@ PLUGIN_INFO = {
|
|||
C.PI_MAIN: "BlogImportPlugin",
|
||||
C.PI_HANDLER: "no",
|
||||
C.PI_DESCRIPTION: _(
|
||||
u"""Blog import management:
|
||||
"""Blog import management:
|
||||
This plugin manage the different blog importers which can register to it, and handle generic importing tasks."""
|
||||
),
|
||||
}
|
||||
|
|
@ -67,7 +67,7 @@ class BlogImportPlugin(object):
|
|||
self._p = host.plugins["XEP-0060"]
|
||||
self._m = host.plugins["XEP-0277"]
|
||||
self._s = self.host.plugins["TEXT_SYNTAXES"]
|
||||
host.plugins["IMPORT"].initialize(self, u"blog")
|
||||
host.plugins["IMPORT"].initialize(self, "blog")
|
||||
|
||||
def importItem(
|
||||
self, client, item_import_data, session, options, return_data, service, node
|
||||
|
|
@ -107,7 +107,7 @@ class BlogImportPlugin(object):
|
|||
try:
|
||||
item_id = mb_data["id"]
|
||||
except KeyError:
|
||||
item_id = mb_data["id"] = unicode(shortuuid.uuid())
|
||||
item_id = mb_data["id"] = str(shortuuid.uuid())
|
||||
|
||||
try:
|
||||
# we keep the link between old url and new blog item
|
||||
|
|
@ -121,7 +121,7 @@ class BlogImportPlugin(object):
|
|||
node or self._m.namespace,
|
||||
item_id,
|
||||
)
|
||||
log.info(u"url link from {old} to {new}".format(old=old_uri, new=new_uri))
|
||||
log.info("url link from {old} to {new}".format(old=old_uri, new=new_uri))
|
||||
|
||||
return mb_data
|
||||
|
||||
|
|
@ -129,7 +129,7 @@ class BlogImportPlugin(object):
|
|||
def importSubItems(self, client, item_import_data, mb_data, session, options):
|
||||
# comments data
|
||||
if len(item_import_data["comments"]) != 1:
|
||||
raise NotImplementedError(u"can't manage multiple comment links")
|
||||
raise NotImplementedError("can't manage multiple comment links")
|
||||
allow_comments = C.bool(mb_data.get("allow_comments", C.BOOL_FALSE))
|
||||
if allow_comments:
|
||||
comments_service = yield self._m.getCommentsService(client)
|
||||
|
|
@ -145,13 +145,13 @@ class BlogImportPlugin(object):
|
|||
else:
|
||||
if item_import_data["comments"][0]:
|
||||
raise exceptions.DataError(
|
||||
u"allow_comments set to False, but comments are there"
|
||||
"allow_comments set to False, but comments are there"
|
||||
)
|
||||
defer.returnValue(None)
|
||||
|
||||
def publishItem(self, client, mb_data, service, node, session):
|
||||
log.debug(
|
||||
u"uploading item [{id}]: {title}".format(
|
||||
"uploading item [{id}]: {title}".format(
|
||||
id=mb_data["id"], title=mb_data.get("title", "")
|
||||
)
|
||||
)
|
||||
|
|
@ -182,7 +182,7 @@ class BlogImportPlugin(object):
|
|||
else:
|
||||
if "{}_xhtml".format(prefix) in mb_data:
|
||||
raise exceptions.DataError(
|
||||
u"importer gave {prefix}_rich and {prefix}_xhtml at the same time, this is not allowed".format(
|
||||
"importer gave {prefix}_rich and {prefix}_xhtml at the same time, this is not allowed".format(
|
||||
prefix=prefix
|
||||
)
|
||||
)
|
||||
|
|
@ -200,14 +200,14 @@ class BlogImportPlugin(object):
|
|||
else:
|
||||
if "{}_xhtml".format(prefix) in mb_data:
|
||||
log.warning(
|
||||
u"{prefix}_text will be replaced by converted {prefix}_xhtml, so filters can be handled".format(
|
||||
"{prefix}_text will be replaced by converted {prefix}_xhtml, so filters can be handled".format(
|
||||
prefix=prefix
|
||||
)
|
||||
)
|
||||
del mb_data["{}_text".format(prefix)]
|
||||
else:
|
||||
log.warning(
|
||||
u"importer gave a text {prefix}, blog filters don't work on text {prefix}".format(
|
||||
"importer gave a text {prefix}, blog filters don't work on text {prefix}".format(
|
||||
prefix=prefix
|
||||
)
|
||||
)
|
||||
|
|
@ -225,8 +225,8 @@ class BlogImportPlugin(object):
|
|||
opt_host = options.get(OPT_HOST)
|
||||
if opt_host:
|
||||
# we normalise the domain
|
||||
parsed_host = urlparse.urlsplit(opt_host)
|
||||
opt_host = urlparse.urlunsplit(
|
||||
parsed_host = urllib.parse.urlsplit(opt_host)
|
||||
opt_host = urllib.parse.urlunsplit(
|
||||
(
|
||||
parsed_host.scheme or "http",
|
||||
parsed_host.netloc or parsed_host.path,
|
||||
|
|
@ -239,7 +239,7 @@ class BlogImportPlugin(object):
|
|||
tmp_dir = tempfile.mkdtemp()
|
||||
try:
|
||||
# TODO: would be nice to also update the hyperlinks to these images, e.g. when you have <a href="{url}"><img src="{url}"></a>
|
||||
for img_elt in xml_tools.findAll(top_elt, names=[u"img"]):
|
||||
for img_elt in xml_tools.findAll(top_elt, names=["img"]):
|
||||
yield self.imgFilters(client, img_elt, options, opt_host, tmp_dir)
|
||||
finally:
|
||||
os.rmdir(tmp_dir) # XXX: tmp_dir should be empty, or something went wrong
|
||||
|
|
@ -260,21 +260,21 @@ class BlogImportPlugin(object):
|
|||
"""
|
||||
try:
|
||||
url = img_elt["src"]
|
||||
if url[0] == u"/":
|
||||
if url[0] == "/":
|
||||
if not opt_host:
|
||||
log.warning(
|
||||
u"host was not specified, we can't deal with src without host ({url}) and have to ignore the following <img/>:\n{xml}".format(
|
||||
"host was not specified, we can't deal with src without host ({url}) and have to ignore the following <img/>:\n{xml}".format(
|
||||
url=url, xml=img_elt.toXml()
|
||||
)
|
||||
)
|
||||
return
|
||||
else:
|
||||
url = urlparse.urljoin(opt_host, url)
|
||||
url = urllib.parse.urljoin(opt_host, url)
|
||||
filename = url.rsplit("/", 1)[-1].strip()
|
||||
if not filename:
|
||||
raise KeyError
|
||||
except (KeyError, IndexError):
|
||||
log.warning(u"ignoring invalid img element: {}".format(img_elt.toXml()))
|
||||
log.warning("ignoring invalid img element: {}".format(img_elt.toXml()))
|
||||
return
|
||||
|
||||
# we change the url for the normalized one
|
||||
|
|
@ -288,10 +288,10 @@ class BlogImportPlugin(object):
|
|||
pass
|
||||
else:
|
||||
# host is the ignored one, we skip
|
||||
parsed_url = urlparse.urlsplit(url)
|
||||
parsed_url = urllib.parse.urlsplit(url)
|
||||
if ignore_host in parsed_url.hostname:
|
||||
log.info(
|
||||
u"Don't upload image at {url} because of {opt} option".format(
|
||||
"Don't upload image at {url} because of {opt} option".format(
|
||||
url=url, opt=OPT_UPLOAD_IGNORE_HOST
|
||||
)
|
||||
)
|
||||
|
|
@ -304,7 +304,7 @@ class BlogImportPlugin(object):
|
|||
try:
|
||||
yield web_client.downloadPage(url.encode("utf-8"), tmp_file)
|
||||
filename = filename.replace(
|
||||
u"%", u"_"
|
||||
"%", "_"
|
||||
) # FIXME: tmp workaround for a bug in prosody http upload
|
||||
__, download_d = yield self._u.upload(
|
||||
client, tmp_file, filename, options=upload_options
|
||||
|
|
@ -312,7 +312,7 @@ class BlogImportPlugin(object):
|
|||
download_url = yield download_d
|
||||
except Exception as e:
|
||||
log.warning(
|
||||
u"can't download image at {url}: {reason}".format(url=url, reason=e)
|
||||
"can't download image at {url}: {reason}".format(url=url, reason=e)
|
||||
)
|
||||
else:
|
||||
img_elt["src"] = download_url
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# SàT plugin to import dokuwiki blogs
|
||||
|
|
@ -28,8 +28,8 @@ from sat.tools import xml_tools
|
|||
from twisted.internet import threads
|
||||
from collections import OrderedDict
|
||||
import calendar
|
||||
import urllib
|
||||
import urlparse
|
||||
import urllib.request, urllib.parse, urllib.error
|
||||
import urllib.parse
|
||||
import tempfile
|
||||
import re
|
||||
import time
|
||||
|
|
@ -39,13 +39,13 @@ try:
|
|||
from dokuwiki import DokuWiki, DokuWikiError # this is a new dependency
|
||||
except ImportError:
|
||||
raise exceptions.MissingModule(
|
||||
u'Missing module dokuwiki, please install it with "pip install dokuwiki"'
|
||||
'Missing module dokuwiki, please install it with "pip install dokuwiki"'
|
||||
)
|
||||
try:
|
||||
from PIL import Image # this is already needed by plugin XEP-0054
|
||||
except:
|
||||
raise exceptions.MissingModule(
|
||||
u"Missing module pillow, please download/install it from https://python-pillow.github.io"
|
||||
"Missing module pillow, please download/install it from https://python-pillow.github.io"
|
||||
)
|
||||
|
||||
PLUGIN_INFO = {
|
||||
|
|
@ -58,10 +58,10 @@ PLUGIN_INFO = {
|
|||
C.PI_DESCRIPTION: _("""Blog importer for Dokuwiki blog engine."""),
|
||||
}
|
||||
|
||||
SHORT_DESC = D_(u"import posts from Dokuwiki blog engine")
|
||||
SHORT_DESC = D_("import posts from Dokuwiki blog engine")
|
||||
|
||||
LONG_DESC = D_(
|
||||
u"""This importer handle Dokuwiki blog engine.
|
||||
"""This importer handle Dokuwiki blog engine.
|
||||
|
||||
To use it, you need an admin access to a running Dokuwiki website
|
||||
(local or on the Internet). The importer retrieves the data using
|
||||
|
|
@ -129,7 +129,7 @@ class Importer(DokuWiki):
|
|||
@param post(dict): parsed post data
|
||||
@return (unicode): post unique item id
|
||||
"""
|
||||
return unicode(post["id"])
|
||||
return str(post["id"])
|
||||
|
||||
def getPostUpdated(self, post):
|
||||
"""Return the update date.
|
||||
|
|
@ -137,7 +137,7 @@ class Importer(DokuWiki):
|
|||
@param post(dict): parsed post data
|
||||
@return (unicode): update date
|
||||
"""
|
||||
return unicode(post["mtime"])
|
||||
return str(post["mtime"])
|
||||
|
||||
def getPostPublished(self, post):
|
||||
"""Try to parse the date from the message ID, else use "mtime".
|
||||
|
|
@ -148,7 +148,7 @@ class Importer(DokuWiki):
|
|||
@param post (dict): parsed post data
|
||||
@return (unicode): publication date
|
||||
"""
|
||||
id_, default = unicode(post["id"]), unicode(post["mtime"])
|
||||
id_, default = str(post["id"]), str(post["mtime"])
|
||||
try:
|
||||
date = id_.split(":")[-1].split("_")[0]
|
||||
except KeyError:
|
||||
|
|
@ -160,7 +160,7 @@ class Importer(DokuWiki):
|
|||
time_struct = time.strptime(date, "%Y%m%d")
|
||||
except ValueError:
|
||||
return default
|
||||
return unicode(calendar.timegm(time_struct))
|
||||
return str(calendar.timegm(time_struct))
|
||||
|
||||
def processPost(self, post, profile_jid):
|
||||
"""Process a single page.
|
||||
|
|
@ -235,7 +235,7 @@ class Importer(DokuWiki):
|
|||
if count >= self.limit:
|
||||
break
|
||||
|
||||
return (self.posts_data.itervalues(), len(self.posts_data))
|
||||
return (iter(self.posts_data.values()), len(self.posts_data))
|
||||
|
||||
def processContent(self, text, backlinks, profile_jid):
|
||||
"""Do text substitutions and file copy.
|
||||
|
|
@ -243,7 +243,7 @@ class Importer(DokuWiki):
|
|||
@param text (unicode): message content
|
||||
@param backlinks (list[unicode]): list of backlinks
|
||||
"""
|
||||
text = text.strip(u"\ufeff") # this is at the beginning of the file (BOM)
|
||||
text = text.strip("\ufeff") # this is at the beginning of the file (BOM)
|
||||
|
||||
for backlink in backlinks:
|
||||
src = '/doku.php?id=%s"' % backlink
|
||||
|
|
@ -261,9 +261,9 @@ class Importer(DokuWiki):
|
|||
if self.media_repo:
|
||||
self.moveMedia(link, subs)
|
||||
elif link not in subs:
|
||||
subs[link] = urlparse.urljoin(self.url, link)
|
||||
subs[link] = urllib.parse.urljoin(self.url, link)
|
||||
|
||||
for url, new_url in subs.iteritems():
|
||||
for url, new_url in subs.items():
|
||||
text = text.replace(url, new_url)
|
||||
return text
|
||||
|
||||
|
|
@ -274,12 +274,12 @@ class Importer(DokuWiki):
|
|||
@param link (unicode): media link
|
||||
@param subs (dict): substitutions data
|
||||
"""
|
||||
url = urlparse.urljoin(self.url, link)
|
||||
url = urllib.parse.urljoin(self.url, link)
|
||||
user_media = re.match(r"(/lib/exe/\w+.php\?)(.*)", link)
|
||||
thumb_width = None
|
||||
|
||||
if user_media: # media that has been added by the user
|
||||
params = urlparse.parse_qs(urlparse.urlparse(url).query)
|
||||
params = urllib.parse.parse_qs(urllib.parse.urlparse(url).query)
|
||||
try:
|
||||
media = params["media"][0]
|
||||
except KeyError:
|
||||
|
|
@ -295,7 +295,7 @@ class Importer(DokuWiki):
|
|||
|
||||
filename = media.replace(":", "/")
|
||||
# XXX: avoid "precondition failed" error (only keep the media parameter)
|
||||
url = urlparse.urljoin(self.url, "/lib/exe/fetch.php?media=%s" % media)
|
||||
url = urllib.parse.urljoin(self.url, "/lib/exe/fetch.php?media=%s" % media)
|
||||
|
||||
elif link.startswith("/lib/plugins/"):
|
||||
# other link added by a plugin or something else
|
||||
|
|
@ -324,7 +324,7 @@ class Importer(DokuWiki):
|
|||
if not os.path.exists(dest):
|
||||
if not os.path.exists(dirname):
|
||||
os.makedirs(dirname)
|
||||
urllib.urlretrieve(source, dest)
|
||||
urllib.request.urlretrieve(source, dest)
|
||||
log.debug("DokuWiki media file copied to %s" % dest)
|
||||
|
||||
def createThumbnail(self, source, dest, width):
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# SàT plugin for import external blogs
|
||||
|
|
@ -42,10 +42,10 @@ PLUGIN_INFO = {
|
|||
C.PI_DESCRIPTION: _("""Blog importer for Dotclear blog engine."""),
|
||||
}
|
||||
|
||||
SHORT_DESC = D_(u"import posts from Dotclear blog engine")
|
||||
SHORT_DESC = D_("import posts from Dotclear blog engine")
|
||||
|
||||
LONG_DESC = D_(
|
||||
u"""This importer handle Dotclear blog engine.
|
||||
"""This importer handle Dotclear blog engine.
|
||||
|
||||
To use it, you'll need to export your blog to a flat file.
|
||||
You must go in your admin interface and select Plugins/Maintenance then Backup.
|
||||
|
|
@ -55,7 +55,7 @@ Depending on your configuration, your may need to use Import/Export plugin and e
|
|||
location: you must use the absolute path to your backup for the location parameter
|
||||
"""
|
||||
)
|
||||
POST_ID_PREFIX = u"sat_dc_"
|
||||
POST_ID_PREFIX = "sat_dc_"
|
||||
KNOWN_DATA_TYPES = (
|
||||
"link",
|
||||
"setting",
|
||||
|
|
@ -66,7 +66,7 @@ KNOWN_DATA_TYPES = (
|
|||
"comment",
|
||||
"captcha",
|
||||
)
|
||||
ESCAPE_MAP = {"r": u"\r", "n": u"\n", '"': u'"', "\\": u"\\"}
|
||||
ESCAPE_MAP = {"r": "\r", "n": "\n", '"': '"', "\\": "\\"}
|
||||
|
||||
|
||||
class DotclearParser(object):
|
||||
|
|
@ -83,7 +83,7 @@ class DotclearParser(object):
|
|||
@param post(dict): parsed post data
|
||||
@return (unicode): post unique item id
|
||||
"""
|
||||
return u"{}_{}_{}_{}:{}".format(
|
||||
return "{}_{}_{}_{}:{}".format(
|
||||
POST_ID_PREFIX,
|
||||
post["blog_id"],
|
||||
post["user_id"],
|
||||
|
|
@ -99,7 +99,7 @@ class DotclearParser(object):
|
|||
"""
|
||||
post_id = comment["post_id"]
|
||||
parent_item_id = self.posts_data[post_id]["blog"]["id"]
|
||||
return u"{}_comment_{}".format(parent_item_id, comment["comment_id"])
|
||||
return "{}_comment_{}".format(parent_item_id, comment["comment_id"])
|
||||
|
||||
def getTime(self, data, key):
|
||||
"""Parse time as given by dotclear, with timezone handling
|
||||
|
|
@ -125,18 +125,18 @@ class DotclearParser(object):
|
|||
if char == '"':
|
||||
# we have reached the end of this field,
|
||||
# we try to parse a new one
|
||||
yield u"".join(buf)
|
||||
yield "".join(buf)
|
||||
buf = []
|
||||
idx += 1
|
||||
try:
|
||||
separator = fields_data[idx]
|
||||
except IndexError:
|
||||
return
|
||||
if separator != u",":
|
||||
if separator != ",":
|
||||
raise exceptions.ParsingError("Field separator was expeceted")
|
||||
idx += 1
|
||||
break # we have a new field
|
||||
elif char == u"\\":
|
||||
elif char == "\\":
|
||||
idx += 1
|
||||
try:
|
||||
char = ESCAPE_MAP[fields_data[idx]]
|
||||
|
|
@ -144,22 +144,22 @@ class DotclearParser(object):
|
|||
raise exceptions.ParsingError("Escaped char was expected")
|
||||
except KeyError:
|
||||
char = fields_data[idx]
|
||||
log.warning(u"Unknown key to escape: {}".format(char))
|
||||
log.warning("Unknown key to escape: {}".format(char))
|
||||
buf.append(char)
|
||||
|
||||
def parseFields(self, headers, data):
|
||||
return dict(itertools.izip(headers, self.readFields(data)))
|
||||
return dict(zip(headers, self.readFields(data)))
|
||||
|
||||
def postHandler(self, headers, data, index):
|
||||
post = self.parseFields(headers, data)
|
||||
log.debug(u"({}) post found: {}".format(index, post["post_title"]))
|
||||
log.debug("({}) post found: {}".format(index, post["post_title"]))
|
||||
mb_data = {
|
||||
"id": self.getPostId(post),
|
||||
"published": self.getTime(post, "post_creadt"),
|
||||
"updated": self.getTime(post, "post_upddt"),
|
||||
"author": post["user_id"], # there use info are not in the archive
|
||||
# TODO: option to specify user info
|
||||
"content_xhtml": u"{}{}".format(
|
||||
"content_xhtml": "{}{}".format(
|
||||
post["post_content_xhtml"], post["post_excerpt_xhtml"]
|
||||
),
|
||||
"title": post["post_title"],
|
||||
|
|
@ -168,7 +168,7 @@ class DotclearParser(object):
|
|||
self.posts_data[post["post_id"]] = {
|
||||
"blog": mb_data,
|
||||
"comments": [[]],
|
||||
"url": u"/post/{}".format(post["post_url"]),
|
||||
"url": "/post/{}".format(post["post_url"]),
|
||||
}
|
||||
|
||||
def metaHandler(self, headers, data, index):
|
||||
|
|
@ -178,7 +178,7 @@ class DotclearParser(object):
|
|||
tags.add(meta["meta_id"])
|
||||
|
||||
def metaFinishedHandler(self):
|
||||
for post_id, tags in self.tags.iteritems():
|
||||
for post_id, tags in self.tags.items():
|
||||
data_format.iter2dict("tag", tags, self.posts_data[post_id]["blog"])
|
||||
del self.tags
|
||||
|
||||
|
|
@ -186,9 +186,9 @@ class DotclearParser(object):
|
|||
comment = self.parseFields(headers, data)
|
||||
if comment["comment_site"]:
|
||||
# we don't use atom:uri because it's used for jid in XMPP
|
||||
content = u'{}\n<hr>\n<a href="{}">author website</a>'.format(
|
||||
content = '{}\n<hr>\n<a href="{}">author website</a>'.format(
|
||||
comment["comment_content"],
|
||||
cgi.escape(comment["comment_site"]).replace('"', u"%22"),
|
||||
cgi.escape(comment["comment_site"]).replace('"', "%22"),
|
||||
)
|
||||
else:
|
||||
content = comment["comment_content"]
|
||||
|
|
@ -208,24 +208,24 @@ class DotclearParser(object):
|
|||
|
||||
def parse(self, db_path):
|
||||
with open(db_path) as f:
|
||||
signature = f.readline().decode("utf-8")
|
||||
signature = f.readline()
|
||||
try:
|
||||
version = signature.split("|")[1]
|
||||
except IndexError:
|
||||
version = None
|
||||
log.debug(u"Dotclear version: {}".format(version))
|
||||
log.debug("Dotclear version: {}".format(version))
|
||||
data_type = None
|
||||
data_headers = None
|
||||
index = None
|
||||
while True:
|
||||
buf = f.readline().decode("utf-8")
|
||||
buf = f.readline()
|
||||
if not buf:
|
||||
break
|
||||
if buf.startswith("["):
|
||||
header = buf.split(" ", 1)
|
||||
data_type = header[0][1:]
|
||||
if data_type not in KNOWN_DATA_TYPES:
|
||||
log.warning(u"unkown data type: {}".format(data_type))
|
||||
log.warning("unkown data type: {}".format(data_type))
|
||||
index = 0
|
||||
try:
|
||||
data_headers = header[1].split(",")
|
||||
|
|
@ -233,7 +233,7 @@ class DotclearParser(object):
|
|||
last_header = data_headers[-1]
|
||||
data_headers[-1] = last_header[: last_header.rfind("]")]
|
||||
except IndexError:
|
||||
log.warning(u"Can't read data)")
|
||||
log.warning("Can't read data)")
|
||||
else:
|
||||
if data_type is None:
|
||||
continue
|
||||
|
|
@ -247,7 +247,7 @@ class DotclearParser(object):
|
|||
pass
|
||||
else:
|
||||
finished_handler()
|
||||
log.debug(u"{} data finished".format(data_type))
|
||||
log.debug("{} data finished".format(data_type))
|
||||
data_type = None
|
||||
continue
|
||||
assert data_type
|
||||
|
|
@ -258,7 +258,7 @@ class DotclearParser(object):
|
|||
else:
|
||||
fields_handler(data_headers, buf, index)
|
||||
index += 1
|
||||
return (self.posts_data.itervalues(), len(self.posts_data))
|
||||
return (iter(self.posts_data.values()), len(self.posts_data))
|
||||
|
||||
|
||||
class DotclearImport(object):
|
||||
|
|
@ -272,7 +272,7 @@ class DotclearImport(object):
|
|||
def DcImport(self, client, location, options=None):
|
||||
if not os.path.isabs(location):
|
||||
raise exceptions.DataError(
|
||||
u"An absolute path to backup data need to be given as location"
|
||||
"An absolute path to backup data need to be given as location"
|
||||
)
|
||||
dc_parser = DotclearParser()
|
||||
d = threads.deferToThread(dc_parser.parse, location)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# SAT plugin for parrot mode (experimental)
|
||||
|
|
@ -55,17 +55,17 @@ PLUGIN_INFO = {
|
|||
C.PI_RECOMMENDATIONS: [],
|
||||
C.PI_MAIN: "FileSharing",
|
||||
C.PI_HANDLER: C.BOOL_TRUE,
|
||||
C.PI_DESCRIPTION: _(u"""Component hosting and sharing files"""),
|
||||
C.PI_DESCRIPTION: _("""Component hosting and sharing files"""),
|
||||
}
|
||||
|
||||
HASH_ALGO = u"sha-256"
|
||||
HASH_ALGO = "sha-256"
|
||||
NS_COMMENTS = "org.salut-a-toi.comments"
|
||||
COMMENT_NODE_PREFIX = "org.salut-a-toi.file_comments/"
|
||||
|
||||
|
||||
class FileSharing(object):
|
||||
def __init__(self, host):
|
||||
log.info(_(u"File Sharing initialization"))
|
||||
log.info(_("File Sharing initialization"))
|
||||
self.host = host
|
||||
self._f = host.plugins["FILE"]
|
||||
self._jf = host.plugins["XEP-0234"]
|
||||
|
|
@ -99,12 +99,12 @@ class FileSharing(object):
|
|||
on file is received, this method create hash/thumbnails if necessary
|
||||
move the file to the right location, and create metadata entry in database
|
||||
"""
|
||||
name = file_data[u"name"]
|
||||
name = file_data["name"]
|
||||
extra = {}
|
||||
|
||||
if file_data[u"hash_algo"] == HASH_ALGO:
|
||||
log.debug(_(u"Reusing already generated hash"))
|
||||
file_hash = file_data[u"hash_hasher"].hexdigest()
|
||||
if file_data["hash_algo"] == HASH_ALGO:
|
||||
log.debug(_("Reusing already generated hash"))
|
||||
file_hash = file_data["hash_hasher"].hexdigest()
|
||||
else:
|
||||
hasher = self._h.getHasher(HASH_ALGO)
|
||||
with open("file_path") as f:
|
||||
|
|
@ -113,7 +113,7 @@ class FileSharing(object):
|
|||
|
||||
if os.path.isfile(final_path):
|
||||
log.debug(
|
||||
u"file [{file_hash}] already exists, we can remove temporary one".format(
|
||||
"file [{file_hash}] already exists, we can remove temporary one".format(
|
||||
file_hash=file_hash
|
||||
)
|
||||
)
|
||||
|
|
@ -121,16 +121,16 @@ class FileSharing(object):
|
|||
else:
|
||||
os.rename(file_path, final_path)
|
||||
log.debug(
|
||||
u"file [{file_hash}] moved to {files_path}".format(
|
||||
"file [{file_hash}] moved to {files_path}".format(
|
||||
file_hash=file_hash, files_path=self.files_path
|
||||
)
|
||||
)
|
||||
|
||||
mime_type = file_data.get(u"mime_type")
|
||||
if not mime_type or mime_type == u"application/octet-stream":
|
||||
mime_type = file_data.get("mime_type")
|
||||
if not mime_type or mime_type == "application/octet-stream":
|
||||
mime_type = mimetypes.guess_type(name)[0]
|
||||
|
||||
if mime_type is not None and mime_type.startswith(u"image"):
|
||||
if mime_type is not None and mime_type.startswith("image"):
|
||||
thumbnails = extra.setdefault(C.KEY_THUMBNAILS, [])
|
||||
for max_thumb_size in (self._t.SIZE_SMALL, self._t.SIZE_MEDIUM):
|
||||
try:
|
||||
|
|
@ -141,19 +141,19 @@ class FileSharing(object):
|
|||
60 * 60 * 24 * 31 * 6,
|
||||
)
|
||||
except Exception as e:
|
||||
log.warning(_(u"Can't create thumbnail: {reason}").format(reason=e))
|
||||
log.warning(_("Can't create thumbnail: {reason}").format(reason=e))
|
||||
break
|
||||
thumbnails.append({u"id": thumb_id, u"size": thumb_size})
|
||||
thumbnails.append({"id": thumb_id, "size": thumb_size})
|
||||
|
||||
self.host.memory.setFile(
|
||||
client,
|
||||
name=name,
|
||||
version=u"",
|
||||
version="",
|
||||
file_hash=file_hash,
|
||||
hash_algo=HASH_ALGO,
|
||||
size=file_data[u"size"],
|
||||
path=file_data.get(u"path"),
|
||||
namespace=file_data.get(u"namespace"),
|
||||
size=file_data["size"],
|
||||
path=file_data.get("path"),
|
||||
namespace=file_data.get("namespace"),
|
||||
mime_type=mime_type,
|
||||
owner=peer_jid,
|
||||
extra=extra,
|
||||
|
|
@ -191,49 +191,49 @@ class FileSharing(object):
|
|||
self, client, session, content_data, content_name, file_data, file_elt
|
||||
):
|
||||
"""This method retrieve a file on request, and send if after checking permissions"""
|
||||
peer_jid = session[u"peer_jid"]
|
||||
peer_jid = session["peer_jid"]
|
||||
try:
|
||||
found_files = yield self.host.memory.getFiles(
|
||||
client,
|
||||
peer_jid=peer_jid,
|
||||
name=file_data.get(u"name"),
|
||||
file_hash=file_data.get(u"file_hash"),
|
||||
hash_algo=file_data.get(u"hash_algo"),
|
||||
path=file_data.get(u"path"),
|
||||
namespace=file_data.get(u"namespace"),
|
||||
name=file_data.get("name"),
|
||||
file_hash=file_data.get("file_hash"),
|
||||
hash_algo=file_data.get("hash_algo"),
|
||||
path=file_data.get("path"),
|
||||
namespace=file_data.get("namespace"),
|
||||
)
|
||||
except exceptions.NotFound:
|
||||
found_files = None
|
||||
except exceptions.PermissionError:
|
||||
log.warning(
|
||||
_(u"{peer_jid} is trying to access an unauthorized file: {name}").format(
|
||||
peer_jid=peer_jid, name=file_data.get(u"name")
|
||||
_("{peer_jid} is trying to access an unauthorized file: {name}").format(
|
||||
peer_jid=peer_jid, name=file_data.get("name")
|
||||
)
|
||||
)
|
||||
defer.returnValue(False)
|
||||
|
||||
if not found_files:
|
||||
log.warning(
|
||||
_(u"no matching file found ({file_data})").format(file_data=file_data)
|
||||
_("no matching file found ({file_data})").format(file_data=file_data)
|
||||
)
|
||||
defer.returnValue(False)
|
||||
|
||||
# we only use the first found file
|
||||
found_file = found_files[0]
|
||||
if found_file[u'type'] != C.FILE_TYPE_FILE:
|
||||
raise TypeError(u"a file was expected, type is {type_}".format(
|
||||
type_=found_file[u'type']))
|
||||
file_hash = found_file[u"file_hash"]
|
||||
if found_file['type'] != C.FILE_TYPE_FILE:
|
||||
raise TypeError("a file was expected, type is {type_}".format(
|
||||
type_=found_file['type']))
|
||||
file_hash = found_file["file_hash"]
|
||||
file_path = os.path.join(self.files_path, file_hash)
|
||||
file_data[u"hash_hasher"] = hasher = self._h.getHasher(found_file[u"hash_algo"])
|
||||
size = file_data[u"size"] = found_file[u"size"]
|
||||
file_data[u"file_hash"] = file_hash
|
||||
file_data[u"hash_algo"] = found_file[u"hash_algo"]
|
||||
file_data["hash_hasher"] = hasher = self._h.getHasher(found_file["hash_algo"])
|
||||
size = file_data["size"] = found_file["size"]
|
||||
file_data["file_hash"] = file_hash
|
||||
file_data["hash_algo"] = found_file["hash_algo"]
|
||||
|
||||
# we complete file_elt so peer can have some details on the file
|
||||
if u"name" not in file_data:
|
||||
file_elt.addElement(u"name", content=found_file[u"name"])
|
||||
file_elt.addElement(u"size", content=unicode(size))
|
||||
if "name" not in file_data:
|
||||
file_elt.addElement("name", content=found_file["name"])
|
||||
file_elt.addElement("size", content=str(size))
|
||||
content_data["stream_object"] = stream.FileStreamObject(
|
||||
self.host,
|
||||
client,
|
||||
|
|
@ -268,11 +268,11 @@ class FileSharing(object):
|
|||
comment_elt = file_elt.addElement((NS_COMMENTS, "comments"), content=comments_url)
|
||||
|
||||
try:
|
||||
count = len(extra_args[u"extra"][u"comments"])
|
||||
count = len(extra_args["extra"]["comments"])
|
||||
except KeyError:
|
||||
count = 0
|
||||
|
||||
comment_elt["count"] = unicode(count)
|
||||
comment_elt["count"] = str(count)
|
||||
return True
|
||||
|
||||
def _getFileComments(self, file_elt, file_data):
|
||||
|
|
@ -280,7 +280,7 @@ class FileSharing(object):
|
|||
comments_elt = next(file_elt.elements(NS_COMMENTS, "comments"))
|
||||
except StopIteration:
|
||||
return
|
||||
file_data["comments_url"] = unicode(comments_elt)
|
||||
file_data["comments_url"] = str(comments_elt)
|
||||
file_data["comments_count"] = comments_elt["count"]
|
||||
return True
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# SAT plugin to detect language (experimental)
|
||||
|
|
@ -31,25 +31,25 @@ log = getLogger(__name__)
|
|||
|
||||
|
||||
PLUGIN_INFO = {
|
||||
C.PI_NAME: u"File Sharing Management",
|
||||
C.PI_IMPORT_NAME: u"FILE_SHARING_MANAGEMENT",
|
||||
C.PI_NAME: "File Sharing Management",
|
||||
C.PI_IMPORT_NAME: "FILE_SHARING_MANAGEMENT",
|
||||
C.PI_MODES: [C.PLUG_MODE_COMPONENT],
|
||||
C.PI_TYPE: u"EXP",
|
||||
C.PI_TYPE: "EXP",
|
||||
C.PI_PROTOCOLS: [],
|
||||
C.PI_DEPENDENCIES: [u"XEP-0050", u"XEP-0264"],
|
||||
C.PI_DEPENDENCIES: ["XEP-0050", "XEP-0264"],
|
||||
C.PI_RECOMMENDATIONS: [],
|
||||
C.PI_MAIN: u"FileSharingManagement",
|
||||
C.PI_HANDLER: u"no",
|
||||
C.PI_MAIN: "FileSharingManagement",
|
||||
C.PI_HANDLER: "no",
|
||||
C.PI_DESCRIPTION: _(
|
||||
u"Experimental handling of file management for file sharing. This plugins allows "
|
||||
u"to change permissions of stored files/directories or remove them."
|
||||
"Experimental handling of file management for file sharing. This plugins allows "
|
||||
"to change permissions of stored files/directories or remove them."
|
||||
),
|
||||
}
|
||||
|
||||
NS_FILE_MANAGEMENT = u"https://salut-a-toi.org/protocol/file-management:0"
|
||||
NS_FILE_MANAGEMENT_PERM = u"https://salut-a-toi.org/protocol/file-management:0#perm"
|
||||
NS_FILE_MANAGEMENT_DELETE = u"https://salut-a-toi.org/protocol/file-management:0#delete"
|
||||
NS_FILE_MANAGEMENT_THUMB = u"https://salut-a-toi.org/protocol/file-management:0#thumb"
|
||||
NS_FILE_MANAGEMENT = "https://salut-a-toi.org/protocol/file-management:0"
|
||||
NS_FILE_MANAGEMENT_PERM = "https://salut-a-toi.org/protocol/file-management:0#perm"
|
||||
NS_FILE_MANAGEMENT_DELETE = "https://salut-a-toi.org/protocol/file-management:0#delete"
|
||||
NS_FILE_MANAGEMENT_THUMB = "https://salut-a-toi.org/protocol/file-management:0#thumb"
|
||||
|
||||
|
||||
class WorkflowError(Exception):
|
||||
|
|
@ -68,7 +68,7 @@ class FileSharingManagement(object):
|
|||
# syntax?) should be elaborated and proposed as a standard.
|
||||
|
||||
def __init__(self, host):
|
||||
log.info(_(u"File Sharing Management plugin initialization"))
|
||||
log.info(_("File Sharing Management plugin initialization"))
|
||||
self.host = host
|
||||
self._c = host.plugins["XEP-0050"]
|
||||
self._t = host.plugins["XEP-0264"]
|
||||
|
|
@ -76,17 +76,17 @@ class FileSharingManagement(object):
|
|||
|
||||
def profileConnected(self, client):
|
||||
self._c.addAdHocCommand(
|
||||
client, self._onChangeFile, u"Change Permissions of File(s)",
|
||||
client, self._onChangeFile, "Change Permissions of File(s)",
|
||||
node=NS_FILE_MANAGEMENT_PERM,
|
||||
allowed_magics=C.ENTITY_ALL,
|
||||
)
|
||||
self._c.addAdHocCommand(
|
||||
client, self._onDeleteFile, u"Delete File(s)",
|
||||
client, self._onDeleteFile, "Delete File(s)",
|
||||
node=NS_FILE_MANAGEMENT_DELETE,
|
||||
allowed_magics=C.ENTITY_ALL,
|
||||
)
|
||||
self._c.addAdHocCommand(
|
||||
client, self._onGenThumbnails, u"Generate Thumbnails",
|
||||
client, self._onGenThumbnails, "Generate Thumbnails",
|
||||
node=NS_FILE_MANAGEMENT_THUMB,
|
||||
allowed_magics=C.ENTITY_ALL,
|
||||
)
|
||||
|
|
@ -109,7 +109,7 @@ class FileSharingManagement(object):
|
|||
@return (tuple): arguments to use in defer.returnValue
|
||||
"""
|
||||
status = self._c.STATUS.EXECUTING
|
||||
form = data_form.Form("form", title=u"File Management",
|
||||
form = data_form.Form("form", title="File Management",
|
||||
formNamespace=NS_FILE_MANAGEMENT)
|
||||
|
||||
field = data_form.Field(
|
||||
|
|
@ -136,17 +136,17 @@ class FileSharingManagement(object):
|
|||
"""
|
||||
fields = command_form.fields
|
||||
try:
|
||||
path = fields[u'path'].value.strip()
|
||||
namespace = fields[u'namespace'].value or None
|
||||
path = fields['path'].value.strip()
|
||||
namespace = fields['namespace'].value or None
|
||||
except KeyError:
|
||||
self._c.adHocError(self._c.ERROR.BAD_PAYLOAD)
|
||||
|
||||
if not path:
|
||||
self._c.adHocError(self._c.ERROR.BAD_PAYLOAD)
|
||||
|
||||
requestor = session_data[u'requestor']
|
||||
requestor = session_data['requestor']
|
||||
requestor_bare = requestor.userhostJID()
|
||||
path = path.rstrip(u'/')
|
||||
path = path.rstrip('/')
|
||||
parent_path, basename = os.path.split(path)
|
||||
|
||||
# TODO: if parent_path and basename are empty, we ask for root directory
|
||||
|
|
@ -158,31 +158,31 @@ class FileSharingManagement(object):
|
|||
namespace=namespace)
|
||||
found_file = found_files[0]
|
||||
except (exceptions.NotFound, IndexError):
|
||||
raise WorkflowError(self._err(_(u"file not found")))
|
||||
raise WorkflowError(self._err(_("file not found")))
|
||||
except exceptions.PermissionError:
|
||||
raise WorkflowError(self._err(_(u"forbidden")))
|
||||
raise WorkflowError(self._err(_("forbidden")))
|
||||
|
||||
if found_file['owner'] != requestor_bare:
|
||||
# only owner can manage files
|
||||
log.warning(_(u"Only owner can manage files"))
|
||||
raise WorkflowError(self._err(_(u"forbidden")))
|
||||
log.warning(_("Only owner can manage files"))
|
||||
raise WorkflowError(self._err(_("forbidden")))
|
||||
|
||||
session_data[u'found_file'] = found_file
|
||||
session_data[u'namespace'] = namespace
|
||||
session_data['found_file'] = found_file
|
||||
session_data['namespace'] = namespace
|
||||
defer.returnValue(found_file)
|
||||
|
||||
def _updateReadPermission(self, access, allowed_jids):
|
||||
if not allowed_jids:
|
||||
if C.ACCESS_PERM_READ in access:
|
||||
del access[C.ACCESS_PERM_READ]
|
||||
elif allowed_jids == u'PUBLIC':
|
||||
elif allowed_jids == 'PUBLIC':
|
||||
access[C.ACCESS_PERM_READ] = {
|
||||
u"type": C.ACCESS_TYPE_PUBLIC
|
||||
"type": C.ACCESS_TYPE_PUBLIC
|
||||
}
|
||||
else:
|
||||
access[C.ACCESS_PERM_READ] = {
|
||||
u"type": C.ACCESS_TYPE_WHITELIST,
|
||||
u"jids": [j.full() for j in allowed_jids]
|
||||
"type": C.ACCESS_TYPE_WHITELIST,
|
||||
"jids": [j.full() for j in allowed_jids]
|
||||
}
|
||||
|
||||
@defer.inlineCallbacks
|
||||
|
|
@ -192,30 +192,30 @@ class FileSharingManagement(object):
|
|||
@param file_data(dict): metadata of the file
|
||||
@param allowed_jids(list[jid.JID]): list of entities allowed to read the file
|
||||
"""
|
||||
assert file_data[u'type'] == C.FILE_TYPE_DIRECTORY
|
||||
assert file_data['type'] == C.FILE_TYPE_DIRECTORY
|
||||
files_data = yield self.host.memory.getFiles(
|
||||
client, requestor, parent=file_data[u'id'], namespace=namespace)
|
||||
client, requestor, parent=file_data['id'], namespace=namespace)
|
||||
|
||||
for file_data in files_data:
|
||||
if not file_data[u'access'].get(C.ACCESS_PERM_READ, {}):
|
||||
log.debug(u"setting {perm} read permission for {name}".format(
|
||||
perm=allowed_jids, name=file_data[u'name']))
|
||||
if not file_data['access'].get(C.ACCESS_PERM_READ, {}):
|
||||
log.debug("setting {perm} read permission for {name}".format(
|
||||
perm=allowed_jids, name=file_data['name']))
|
||||
yield self.host.memory.fileUpdate(
|
||||
file_data[u'id'], u'access',
|
||||
file_data['id'], 'access',
|
||||
partial(self._updateReadPermission, allowed_jids=allowed_jids))
|
||||
if file_data[u'type'] == C.FILE_TYPE_DIRECTORY:
|
||||
yield self._updateDir(client, requestor, namespace, file_data, u'PUBLIC')
|
||||
if file_data['type'] == C.FILE_TYPE_DIRECTORY:
|
||||
yield self._updateDir(client, requestor, namespace, file_data, 'PUBLIC')
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def _onChangeFile(self, client, command_elt, session_data, action, node):
|
||||
try:
|
||||
x_elt = command_elt.elements(data_form.NS_X_DATA, "x").next()
|
||||
x_elt = next(command_elt.elements(data_form.NS_X_DATA, "x"))
|
||||
command_form = data_form.Form.fromElement(x_elt)
|
||||
except StopIteration:
|
||||
command_form = None
|
||||
|
||||
found_file = session_data.get('found_file')
|
||||
requestor = session_data[u'requestor']
|
||||
requestor = session_data['requestor']
|
||||
requestor_bare = requestor.userhostJID()
|
||||
|
||||
if command_form is None or len(command_form.fields) == 0:
|
||||
|
|
@ -230,31 +230,31 @@ class FileSharingManagement(object):
|
|||
defer.returnValue(e.err_args)
|
||||
|
||||
# management request
|
||||
if found_file[u'type'] == C.FILE_TYPE_DIRECTORY:
|
||||
instructions = D_(u"Please select permissions for this directory")
|
||||
if found_file['type'] == C.FILE_TYPE_DIRECTORY:
|
||||
instructions = D_("Please select permissions for this directory")
|
||||
else:
|
||||
instructions = D_(u"Please select permissions for this file")
|
||||
instructions = D_("Please select permissions for this file")
|
||||
|
||||
form = data_form.Form("form", title=u"File Management",
|
||||
form = data_form.Form("form", title="File Management",
|
||||
instructions=[instructions],
|
||||
formNamespace=NS_FILE_MANAGEMENT)
|
||||
field = data_form.Field(
|
||||
"text-multi", "read_allowed", required=False,
|
||||
desc=u'list of jids allowed to read this file (beside yourself), or '
|
||||
u'"PUBLIC" to let a public access'
|
||||
desc='list of jids allowed to read this file (beside yourself), or '
|
||||
'"PUBLIC" to let a public access'
|
||||
)
|
||||
read_access = found_file[u"access"].get(C.ACCESS_PERM_READ, {})
|
||||
access_type = read_access.get(u'type', C.ACCESS_TYPE_WHITELIST)
|
||||
read_access = found_file["access"].get(C.ACCESS_PERM_READ, {})
|
||||
access_type = read_access.get('type', C.ACCESS_TYPE_WHITELIST)
|
||||
if access_type == C.ACCESS_TYPE_PUBLIC:
|
||||
field.values = [u'PUBLIC']
|
||||
field.values = ['PUBLIC']
|
||||
else:
|
||||
field.values = read_access.get('jids', [])
|
||||
form.addField(field)
|
||||
if found_file[u'type'] == C.FILE_TYPE_DIRECTORY:
|
||||
if found_file['type'] == C.FILE_TYPE_DIRECTORY:
|
||||
field = data_form.Field(
|
||||
"boolean", "recursive", value=False, required=False,
|
||||
desc=u"Files under it will be made public to follow this dir "
|
||||
u"permission (only if they don't have already a permission set)."
|
||||
desc="Files under it will be made public to follow this dir "
|
||||
"permission (only if they don't have already a permission set)."
|
||||
)
|
||||
form.addField(field)
|
||||
|
||||
|
|
@ -269,22 +269,22 @@ class FileSharingManagement(object):
|
|||
except KeyError:
|
||||
self._c.adHocError(self._c.ERROR.BAD_PAYLOAD)
|
||||
|
||||
if read_allowed.value == u'PUBLIC':
|
||||
allowed_jids = u'PUBLIC'
|
||||
elif read_allowed.value.strip() == u'':
|
||||
if read_allowed.value == 'PUBLIC':
|
||||
allowed_jids = 'PUBLIC'
|
||||
elif read_allowed.value.strip() == '':
|
||||
allowed_jids = None
|
||||
else:
|
||||
try:
|
||||
allowed_jids = [jid.JID(v.strip()) for v in read_allowed.values
|
||||
if v.strip()]
|
||||
except RuntimeError as e:
|
||||
log.warning(_(u"Can't use read_allowed values: {reason}").format(
|
||||
log.warning(_("Can't use read_allowed values: {reason}").format(
|
||||
reason=e))
|
||||
self._c.adHocError(self._c.ERROR.BAD_PAYLOAD)
|
||||
|
||||
if found_file[u'type'] == C.FILE_TYPE_FILE:
|
||||
if found_file['type'] == C.FILE_TYPE_FILE:
|
||||
yield self.host.memory.fileUpdate(
|
||||
found_file[u'id'], u'access',
|
||||
found_file['id'], 'access',
|
||||
partial(self._updateReadPermission, allowed_jids=allowed_jids))
|
||||
else:
|
||||
try:
|
||||
|
|
@ -292,32 +292,32 @@ class FileSharingManagement(object):
|
|||
except KeyError:
|
||||
self._c.adHocError(self._c.ERROR.BAD_PAYLOAD)
|
||||
yield self.host.memory.fileUpdate(
|
||||
found_file[u'id'], u'access',
|
||||
found_file['id'], 'access',
|
||||
partial(self._updateReadPermission, allowed_jids=allowed_jids))
|
||||
if recursive:
|
||||
# we set all file under the directory as public (if they haven't
|
||||
# already a permission set), so allowed entities of root directory
|
||||
# can read them.
|
||||
namespace = session_data[u'namespace']
|
||||
namespace = session_data['namespace']
|
||||
yield self._updateDir(
|
||||
client, requestor_bare, namespace, found_file, u'PUBLIC')
|
||||
client, requestor_bare, namespace, found_file, 'PUBLIC')
|
||||
|
||||
# job done, we can end the session
|
||||
status = self._c.STATUS.COMPLETED
|
||||
payload = None
|
||||
note = (self._c.NOTE.INFO, _(u"management session done"))
|
||||
note = (self._c.NOTE.INFO, _("management session done"))
|
||||
defer.returnValue((payload, status, None, note))
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def _onDeleteFile(self, client, command_elt, session_data, action, node):
|
||||
try:
|
||||
x_elt = command_elt.elements(data_form.NS_X_DATA, "x").next()
|
||||
x_elt = next(command_elt.elements(data_form.NS_X_DATA, "x"))
|
||||
command_form = data_form.Form.fromElement(x_elt)
|
||||
except StopIteration:
|
||||
command_form = None
|
||||
|
||||
found_file = session_data.get('found_file')
|
||||
requestor = session_data[u'requestor']
|
||||
requestor = session_data['requestor']
|
||||
requestor_bare = requestor.userhostJID()
|
||||
|
||||
if command_form is None or len(command_form.fields) == 0:
|
||||
|
|
@ -330,18 +330,18 @@ class FileSharingManagement(object):
|
|||
found_file = yield self._getFileData(client, session_data, command_form)
|
||||
except WorkflowError as e:
|
||||
defer.returnValue(e.err_args)
|
||||
if found_file[u'type'] == C.FILE_TYPE_DIRECTORY:
|
||||
msg = D_(u"Are you sure to delete directory {name} and all files and "
|
||||
u"directories under it?").format(name=found_file[u'name'])
|
||||
if found_file['type'] == C.FILE_TYPE_DIRECTORY:
|
||||
msg = D_("Are you sure to delete directory {name} and all files and "
|
||||
"directories under it?").format(name=found_file['name'])
|
||||
else:
|
||||
msg = D_(u"Are you sure to delete file {name}?"
|
||||
.format(name=found_file[u'name']))
|
||||
form = data_form.Form("form", title=u"File Management",
|
||||
msg = D_("Are you sure to delete file {name}?"
|
||||
.format(name=found_file['name']))
|
||||
form = data_form.Form("form", title="File Management",
|
||||
instructions = [msg],
|
||||
formNamespace=NS_FILE_MANAGEMENT)
|
||||
field = data_form.Field(
|
||||
"boolean", "confirm", value=False, required=True,
|
||||
desc=u"check this box to confirm"
|
||||
desc="check this box to confirm"
|
||||
)
|
||||
form.addField(field)
|
||||
status = self._c.STATUS.EXECUTING
|
||||
|
|
@ -357,10 +357,10 @@ class FileSharingManagement(object):
|
|||
if not confirmed:
|
||||
note = None
|
||||
else:
|
||||
recursive = found_file[u'type'] == C.FILE_TYPE_DIRECTORY
|
||||
recursive = found_file['type'] == C.FILE_TYPE_DIRECTORY
|
||||
yield self.host.memory.fileDelete(
|
||||
client, requestor_bare, found_file[u'id'], recursive)
|
||||
note = (self._c.NOTE.INFO, _(u"file deleted"))
|
||||
client, requestor_bare, found_file['id'], recursive)
|
||||
note = (self._c.NOTE.INFO, _("file deleted"))
|
||||
status = self._c.STATUS.COMPLETED
|
||||
payload = None
|
||||
defer.returnValue((payload, status, None, note))
|
||||
|
|
@ -374,16 +374,16 @@ class FileSharingManagement(object):
|
|||
|
||||
@param file_data(dict): metadata of the file
|
||||
"""
|
||||
if file_data[u'type'] == C.FILE_TYPE_DIRECTORY:
|
||||
if file_data['type'] == C.FILE_TYPE_DIRECTORY:
|
||||
sub_files_data = yield self.host.memory.getFiles(
|
||||
client, requestor, parent=file_data[u'id'], namespace=namespace)
|
||||
client, requestor, parent=file_data['id'], namespace=namespace)
|
||||
for sub_file_data in sub_files_data:
|
||||
yield self._genThumbs(client, requestor, namespace, sub_file_data)
|
||||
|
||||
elif file_data[u'type'] == C.FILE_TYPE_FILE:
|
||||
mime_type = file_data[u'mime_type']
|
||||
file_path = os.path.join(self.files_path, file_data[u'file_hash'])
|
||||
if mime_type is not None and mime_type.startswith(u"image"):
|
||||
elif file_data['type'] == C.FILE_TYPE_FILE:
|
||||
mime_type = file_data['mime_type']
|
||||
file_path = os.path.join(self.files_path, file_data['file_hash'])
|
||||
if mime_type is not None and mime_type.startswith("image"):
|
||||
thumbnails = []
|
||||
|
||||
for max_thumb_size in (self._t.SIZE_SMALL, self._t.SIZE_MEDIUM):
|
||||
|
|
@ -395,31 +395,31 @@ class FileSharingManagement(object):
|
|||
60 * 60 * 24 * 31 * 6,
|
||||
)
|
||||
except Exception as e:
|
||||
log.warning(_(u"Can't create thumbnail: {reason}")
|
||||
log.warning(_("Can't create thumbnail: {reason}")
|
||||
.format(reason=e))
|
||||
break
|
||||
thumbnails.append({u"id": thumb_id, u"size": thumb_size})
|
||||
thumbnails.append({"id": thumb_id, "size": thumb_size})
|
||||
|
||||
yield self.host.memory.fileUpdate(
|
||||
file_data[u'id'], u'extra',
|
||||
file_data['id'], 'extra',
|
||||
partial(self._updateThumbs, thumbnails=thumbnails))
|
||||
|
||||
log.info(u"thumbnails for [{file_name}] generated"
|
||||
.format(file_name=file_data[u'name']))
|
||||
log.info("thumbnails for [{file_name}] generated"
|
||||
.format(file_name=file_data['name']))
|
||||
|
||||
else:
|
||||
log.warning(u"unmanaged file type: {type_}".format(type_=file_data[u'type']))
|
||||
log.warning("unmanaged file type: {type_}".format(type_=file_data['type']))
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def _onGenThumbnails(self, client, command_elt, session_data, action, node):
|
||||
try:
|
||||
x_elt = command_elt.elements(data_form.NS_X_DATA, "x").next()
|
||||
x_elt = next(command_elt.elements(data_form.NS_X_DATA, "x"))
|
||||
command_form = data_form.Form.fromElement(x_elt)
|
||||
except StopIteration:
|
||||
command_form = None
|
||||
|
||||
found_file = session_data.get('found_file')
|
||||
requestor = session_data[u'requestor']
|
||||
requestor = session_data['requestor']
|
||||
|
||||
if command_form is None or len(command_form.fields) == 0:
|
||||
# root request
|
||||
|
|
@ -432,11 +432,11 @@ class FileSharingManagement(object):
|
|||
except WorkflowError as e:
|
||||
defer.returnValue(e.err_args)
|
||||
|
||||
log.info(u"Generating thumbnails as requested")
|
||||
yield self._genThumbs(client, requestor, found_file[u'namespace'], found_file)
|
||||
log.info("Generating thumbnails as requested")
|
||||
yield self._genThumbs(client, requestor, found_file['namespace'], found_file)
|
||||
|
||||
# job done, we can end the session
|
||||
status = self._c.STATUS.COMPLETED
|
||||
payload = None
|
||||
note = (self._c.NOTE.INFO, _(u"thumbnails generated"))
|
||||
note = (self._c.NOTE.INFO, _("thumbnails generated"))
|
||||
defer.returnValue((payload, status, None, note))
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# SAT plugin for debugging, using a manhole
|
||||
|
|
@ -29,14 +29,14 @@ from twisted.conch.manhole import ColoredManhole
|
|||
log = getLogger(__name__)
|
||||
|
||||
PLUGIN_INFO = {
|
||||
C.PI_NAME: u"Manhole debug plugin",
|
||||
C.PI_IMPORT_NAME: u"manhole",
|
||||
C.PI_TYPE: u"DEBUG",
|
||||
C.PI_NAME: "Manhole debug plugin",
|
||||
C.PI_IMPORT_NAME: "manhole",
|
||||
C.PI_TYPE: "DEBUG",
|
||||
C.PI_PROTOCOLS: [],
|
||||
C.PI_DEPENDENCIES: [],
|
||||
C.PI_MAIN: u"Manhole",
|
||||
C.PI_HANDLER: u"no",
|
||||
C.PI_DESCRIPTION: _(u"""Debug plugin to have a telnet server"""),
|
||||
C.PI_MAIN: "Manhole",
|
||||
C.PI_HANDLER: "no",
|
||||
C.PI_DESCRIPTION: _("""Debug plugin to have a telnet server"""),
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -50,14 +50,14 @@ class Manhole(object):
|
|||
self.startManhole(port)
|
||||
|
||||
def startManhole(self, port):
|
||||
log.warning(_(u"/!\\ Manhole debug server activated, be sure to not use it in "
|
||||
u"production, this is dangerous /!\\"))
|
||||
log.info(_(u"You can connect to manhole server using telnet on port {port}")
|
||||
log.warning(_("/!\\ Manhole debug server activated, be sure to not use it in "
|
||||
"production, this is dangerous /!\\"))
|
||||
log.info(_("You can connect to manhole server using telnet on port {port}")
|
||||
.format(port=port))
|
||||
f = protocol.ServerFactory()
|
||||
namespace = {
|
||||
u"host": self.host,
|
||||
u"jid": jid,
|
||||
"host": self.host,
|
||||
"jid": jid,
|
||||
}
|
||||
f.protocol = lambda: TelnetTransport(TelnetBootstrapProtocol,
|
||||
insults.ServerProtocol,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# SAT plugin to export commands (experimental)
|
||||
|
|
@ -52,7 +52,7 @@ class ExportCommandProtocol(protocol.ProcessProtocol):
|
|||
def _clean(self, data):
|
||||
if not data:
|
||||
log.error("data should not be empty !")
|
||||
return u""
|
||||
return ""
|
||||
decoded = data.decode("utf-8", "ignore")[: -1 if data[-1] == "\n" else None]
|
||||
return clean_ustr(decoded)
|
||||
|
||||
|
|
@ -66,7 +66,7 @@ class ExportCommandProtocol(protocol.ProcessProtocol):
|
|||
self.client.sendMessage(self.target, {"": self._clean(data)}, no_trigger=True)
|
||||
|
||||
def processEnded(self, reason):
|
||||
log.info(u"process finished: %d" % (reason.value.exitCode,))
|
||||
log.info("process finished: %d" % (reason.value.exitCode,))
|
||||
self.parent.removeProcess(self.target, self)
|
||||
|
||||
def write(self, message):
|
||||
|
|
@ -120,12 +120,12 @@ class CommandExport(object):
|
|||
|
||||
if spawned_key in self.spawned:
|
||||
try:
|
||||
body = message_elt.elements(C.NS_CLIENT, "body").next()
|
||||
body = next(message_elt.elements(C.NS_CLIENT, "body"))
|
||||
except StopIteration:
|
||||
# do not block message without body (chat state notification...)
|
||||
return True
|
||||
|
||||
mess_data = unicode(body) + "\n"
|
||||
mess_data = str(body) + "\n"
|
||||
processes_set = self.spawned[spawned_key]
|
||||
_continue = False
|
||||
exclusive = False
|
||||
|
|
@ -158,7 +158,7 @@ class CommandExport(object):
|
|||
raise jid.InvalidFormat
|
||||
_jid = _jid.userhostJID()
|
||||
except (RuntimeError, jid.InvalidFormat, AttributeError):
|
||||
log.info(u"invalid target ignored: %s" % (target,))
|
||||
log.info("invalid target ignored: %s" % (target,))
|
||||
continue
|
||||
process_prot = ExportCommandProtocol(self, client, _jid, options)
|
||||
self.spawned.setdefault((_jid, client.profile), set()).add(process_prot)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# SAT plugin to detect language (experimental)
|
||||
|
|
@ -29,7 +29,7 @@ from twisted.internet import defer
|
|||
from twisted.words.protocols.jabber import jid, error
|
||||
from twisted.words.xish import domish
|
||||
from wokkel import disco, iwokkel
|
||||
from zope.interface import implements
|
||||
from zope.interface import implementer
|
||||
from twisted.words.protocols.jabber.xmlstream import XMPPHandler
|
||||
from wokkel import pubsub
|
||||
|
||||
|
|
@ -41,11 +41,11 @@ PLUGIN_INFO = {
|
|||
C.PI_IMPORT_NAME: "EVENTS",
|
||||
C.PI_TYPE: "EXP",
|
||||
C.PI_PROTOCOLS: [],
|
||||
C.PI_DEPENDENCIES: [u"XEP-0060", u"INVITATION", u"LIST_INTEREST"],
|
||||
C.PI_DEPENDENCIES: ["XEP-0060", "INVITATION", "LIST_INTEREST"],
|
||||
C.PI_RECOMMENDATIONS: ["XEP-0277", "EMAIL_INVITATION"],
|
||||
C.PI_MAIN: "Events",
|
||||
C.PI_HANDLER: "yes",
|
||||
C.PI_DESCRIPTION: _(u"""Experimental implementation of XMPP events management"""),
|
||||
C.PI_DESCRIPTION: _("""Experimental implementation of XMPP events management"""),
|
||||
}
|
||||
|
||||
NS_EVENT = "org.salut-a-toi.event:0"
|
||||
|
|
@ -55,13 +55,13 @@ class Events(object):
|
|||
"""Q&D module to handle event attendance answer, experimentation only"""
|
||||
|
||||
def __init__(self, host):
|
||||
log.info(_(u"Event plugin initialization"))
|
||||
log.info(_("Event plugin initialization"))
|
||||
self.host = host
|
||||
self._p = self.host.plugins["XEP-0060"]
|
||||
self._i = self.host.plugins.get("EMAIL_INVITATION")
|
||||
self._b = self.host.plugins.get("XEP-0277")
|
||||
self.host.registerNamespace(u"event", NS_EVENT)
|
||||
self.host.plugins[u"INVITATION"].registerNamespace(NS_EVENT,
|
||||
self.host.registerNamespace("event", NS_EVENT)
|
||||
self.host.plugins["INVITATION"].registerNamespace(NS_EVENT,
|
||||
self.register)
|
||||
host.bridge.addMethod(
|
||||
"eventGet",
|
||||
|
|
@ -69,7 +69,7 @@ class Events(object):
|
|||
in_sign="ssss",
|
||||
out_sign="(ia{ss})",
|
||||
method=self._eventGet,
|
||||
async=True,
|
||||
async_=True,
|
||||
)
|
||||
host.bridge.addMethod(
|
||||
"eventCreate",
|
||||
|
|
@ -77,7 +77,7 @@ class Events(object):
|
|||
in_sign="ia{ss}ssss",
|
||||
out_sign="s",
|
||||
method=self._eventCreate,
|
||||
async=True,
|
||||
async_=True,
|
||||
)
|
||||
host.bridge.addMethod(
|
||||
"eventModify",
|
||||
|
|
@ -85,7 +85,7 @@ class Events(object):
|
|||
in_sign="sssia{ss}s",
|
||||
out_sign="",
|
||||
method=self._eventModify,
|
||||
async=True,
|
||||
async_=True,
|
||||
)
|
||||
host.bridge.addMethod(
|
||||
"eventsList",
|
||||
|
|
@ -93,7 +93,7 @@ class Events(object):
|
|||
in_sign="sss",
|
||||
out_sign="aa{ss}",
|
||||
method=self._eventsList,
|
||||
async=True,
|
||||
async_=True,
|
||||
)
|
||||
host.bridge.addMethod(
|
||||
"eventInviteeGet",
|
||||
|
|
@ -101,7 +101,7 @@ class Events(object):
|
|||
in_sign="sss",
|
||||
out_sign="a{ss}",
|
||||
method=self._eventInviteeGet,
|
||||
async=True,
|
||||
async_=True,
|
||||
)
|
||||
host.bridge.addMethod(
|
||||
"eventInviteeSet",
|
||||
|
|
@ -109,7 +109,7 @@ class Events(object):
|
|||
in_sign="ssa{ss}s",
|
||||
out_sign="",
|
||||
method=self._eventInviteeSet,
|
||||
async=True,
|
||||
async_=True,
|
||||
)
|
||||
host.bridge.addMethod(
|
||||
"eventInviteesList",
|
||||
|
|
@ -117,7 +117,7 @@ class Events(object):
|
|||
in_sign="sss",
|
||||
out_sign="a{sa{ss}}",
|
||||
method=self._eventInviteesList,
|
||||
async=True,
|
||||
async_=True,
|
||||
),
|
||||
host.bridge.addMethod(
|
||||
"eventInvite",
|
||||
|
|
@ -125,7 +125,7 @@ class Events(object):
|
|||
in_sign="sssss",
|
||||
out_sign="",
|
||||
method=self._invite,
|
||||
async=True,
|
||||
async_=True,
|
||||
)
|
||||
host.bridge.addMethod(
|
||||
"eventInviteByEmail",
|
||||
|
|
@ -133,7 +133,7 @@ class Events(object):
|
|||
in_sign="ssssassssssss",
|
||||
out_sign="",
|
||||
method=self._inviteByEmail,
|
||||
async=True,
|
||||
async_=True,
|
||||
)
|
||||
|
||||
def getHandler(self, client):
|
||||
|
|
@ -152,56 +152,56 @@ class Events(object):
|
|||
|
||||
data = {}
|
||||
|
||||
for key in (u"name",):
|
||||
for key in ("name",):
|
||||
try:
|
||||
data[key] = event_elt[key]
|
||||
except KeyError:
|
||||
continue
|
||||
|
||||
for elt_name in (u"description",):
|
||||
for elt_name in ("description",):
|
||||
try:
|
||||
elt = next(event_elt.elements(NS_EVENT, elt_name))
|
||||
except StopIteration:
|
||||
continue
|
||||
else:
|
||||
data[elt_name] = unicode(elt)
|
||||
data[elt_name] = str(elt)
|
||||
|
||||
for elt_name in (u"image", "background-image"):
|
||||
for elt_name in ("image", "background-image"):
|
||||
try:
|
||||
image_elt = next(event_elt.elements(NS_EVENT, elt_name))
|
||||
data[elt_name] = image_elt["src"]
|
||||
except StopIteration:
|
||||
continue
|
||||
except KeyError:
|
||||
log.warning(_(u"no src found for image"))
|
||||
log.warning(_("no src found for image"))
|
||||
|
||||
for uri_type in (u"invitees", u"blog"):
|
||||
for uri_type in ("invitees", "blog"):
|
||||
try:
|
||||
elt = next(event_elt.elements(NS_EVENT, uri_type))
|
||||
uri = data[uri_type + u"_uri"] = elt["uri"]
|
||||
uri = data[uri_type + "_uri"] = elt["uri"]
|
||||
uri_data = xmpp_uri.parseXMPPUri(uri)
|
||||
if uri_data[u"type"] != u"pubsub":
|
||||
if uri_data["type"] != "pubsub":
|
||||
raise ValueError
|
||||
except StopIteration:
|
||||
log.warning(_(u"no {uri_type} element found!").format(uri_type=uri_type))
|
||||
log.warning(_("no {uri_type} element found!").format(uri_type=uri_type))
|
||||
except KeyError:
|
||||
log.warning(_(u"incomplete {uri_type} element").format(uri_type=uri_type))
|
||||
log.warning(_("incomplete {uri_type} element").format(uri_type=uri_type))
|
||||
except ValueError:
|
||||
log.warning(_(u"bad {uri_type} element").format(uri_type=uri_type))
|
||||
log.warning(_("bad {uri_type} element").format(uri_type=uri_type))
|
||||
else:
|
||||
data[uri_type + u"_service"] = uri_data[u"path"]
|
||||
data[uri_type + u"_node"] = uri_data[u"node"]
|
||||
data[uri_type + "_service"] = uri_data["path"]
|
||||
data[uri_type + "_node"] = uri_data["node"]
|
||||
|
||||
for meta_elt in event_elt.elements(NS_EVENT, "meta"):
|
||||
key = meta_elt[u"name"]
|
||||
key = meta_elt["name"]
|
||||
if key in data:
|
||||
log.warning(
|
||||
u"Ignoring conflicting meta element: {xml}".format(
|
||||
"Ignoring conflicting meta element: {xml}".format(
|
||||
xml=meta_elt.toXml()
|
||||
)
|
||||
)
|
||||
continue
|
||||
data[key] = unicode(meta_elt)
|
||||
data[key] = str(meta_elt)
|
||||
if event_elt.link:
|
||||
link_elt = event_elt.link
|
||||
data["service"] = link_elt["service"]
|
||||
|
|
@ -225,11 +225,11 @@ class Events(object):
|
|||
id_ = NS_EVENT
|
||||
items, metadata = yield self._p.getItems(client, service, node, item_ids=[id_])
|
||||
try:
|
||||
event_elt = next(items[0].elements(NS_EVENT, u"event"))
|
||||
event_elt = next(items[0].elements(NS_EVENT, "event"))
|
||||
except StopIteration:
|
||||
raise exceptions.NotFound(_(u"No event element has been found"))
|
||||
raise exceptions.NotFound(_("No event element has been found"))
|
||||
except IndexError:
|
||||
raise exceptions.NotFound(_(u"No event with this id has been found"))
|
||||
raise exceptions.NotFound(_("No event with this id has been found"))
|
||||
defer.returnValue(event_elt)
|
||||
|
||||
def register(self, client, name, extra, service, node, event_id, item_elt,
|
||||
|
|
@ -249,16 +249,16 @@ class Events(object):
|
|||
link_elt["node"] = node
|
||||
link_elt["item"] = event_id
|
||||
__, event_data = self._parseEventElt(event_elt)
|
||||
name = event_data.get(u'name')
|
||||
if u'image' in event_data:
|
||||
extra = {u'thumb_url': event_data[u'image']}
|
||||
name = event_data.get('name')
|
||||
if 'image' in event_data:
|
||||
extra = {'thumb_url': event_data['image']}
|
||||
else:
|
||||
extra = None
|
||||
return self.host.plugins[u'LIST_INTEREST'].registerPubsub(
|
||||
return self.host.plugins['LIST_INTEREST'].registerPubsub(
|
||||
client, NS_EVENT, service, node, event_id, creator,
|
||||
name=name, element=event_elt, extra=extra)
|
||||
|
||||
def _eventGet(self, service, node, id_=u"", profile_key=C.PROF_KEY_NONE):
|
||||
def _eventGet(self, service, node, id_="", profile_key=C.PROF_KEY_NONE):
|
||||
service = jid.JID(service) if service else None
|
||||
node = node if node else NS_EVENT
|
||||
client = self.host.getClient(profile_key)
|
||||
|
|
@ -283,12 +283,12 @@ class Events(object):
|
|||
defer.returnValue(self._parseEventElt(event_elt))
|
||||
|
||||
def _eventCreate(
|
||||
self, timestamp, data, service, node, id_=u"", profile_key=C.PROF_KEY_NONE
|
||||
self, timestamp, data, service, node, id_="", profile_key=C.PROF_KEY_NONE
|
||||
):
|
||||
service = jid.JID(service) if service else None
|
||||
node = node or None
|
||||
client = self.host.getClient(profile_key)
|
||||
data[u"register"] = C.bool(data.get(u"register", C.BOOL_FALSE))
|
||||
data["register"] = C.bool(data.get("register", C.BOOL_FALSE))
|
||||
return self.eventCreate(client, timestamp, data, service, node, id_ or NS_EVENT)
|
||||
|
||||
@defer.inlineCallbacks
|
||||
|
|
@ -311,32 +311,32 @@ class Events(object):
|
|||
@return (unicode): created node
|
||||
"""
|
||||
if not event_id:
|
||||
raise ValueError(_(u"event_id must be set"))
|
||||
raise ValueError(_("event_id must be set"))
|
||||
if not service:
|
||||
service = client.jid.userhostJID()
|
||||
if not node:
|
||||
node = NS_EVENT + u"__" + shortuuid.uuid()
|
||||
node = NS_EVENT + "__" + shortuuid.uuid()
|
||||
event_elt = domish.Element((NS_EVENT, "event"))
|
||||
if timestamp is not None and timestamp != -1:
|
||||
formatted_date = utils.xmpp_date(timestamp)
|
||||
event_elt.addElement((NS_EVENT, "date"), content=formatted_date)
|
||||
register = data.pop("register", False)
|
||||
for key in (u"name",):
|
||||
for key in ("name",):
|
||||
if key in data:
|
||||
event_elt[key] = data.pop(key)
|
||||
for key in (u"description",):
|
||||
for key in ("description",):
|
||||
if key in data:
|
||||
event_elt.addElement((NS_EVENT, key), content=data.pop(key))
|
||||
for key in (u"image", u"background-image"):
|
||||
for key in ("image", "background-image"):
|
||||
if key in data:
|
||||
elt = event_elt.addElement((NS_EVENT, key))
|
||||
elt["src"] = data.pop(key)
|
||||
|
||||
# we first create the invitees and blog nodes (if not specified in data)
|
||||
for uri_type in (u"invitees", u"blog"):
|
||||
key = uri_type + u"_uri"
|
||||
for to_delete in (u"service", u"node"):
|
||||
k = uri_type + u"_" + to_delete
|
||||
for uri_type in ("invitees", "blog"):
|
||||
key = uri_type + "_uri"
|
||||
for to_delete in ("service", "node"):
|
||||
k = uri_type + "_" + to_delete
|
||||
if k in data:
|
||||
del data[k]
|
||||
if key not in data:
|
||||
|
|
@ -352,12 +352,12 @@ class Events(object):
|
|||
else:
|
||||
uri = data.pop(key)
|
||||
uri_data = xmpp_uri.parseXMPPUri(uri)
|
||||
if uri_data[u"type"] != u"pubsub":
|
||||
if uri_data["type"] != "pubsub":
|
||||
raise ValueError(
|
||||
_(u"The given URI is not valid: {uri}").format(uri=uri)
|
||||
_("The given URI is not valid: {uri}").format(uri=uri)
|
||||
)
|
||||
uri_service = jid.JID(uri_data[u"path"])
|
||||
uri_node = uri_data[u"node"]
|
||||
uri_service = jid.JID(uri_data["path"])
|
||||
uri_node = uri_data["node"]
|
||||
|
||||
elt = event_elt.addElement((NS_EVENT, uri_type))
|
||||
elt["uri"] = xmpp_uri.buildXMPPUri(
|
||||
|
|
@ -365,7 +365,7 @@ class Events(object):
|
|||
)
|
||||
|
||||
# remaining data are put in <meta> elements
|
||||
for key in data.keys():
|
||||
for key in list(data.keys()):
|
||||
elt = event_elt.addElement((NS_EVENT, "meta"), content=data.pop(key))
|
||||
elt["name"] = key
|
||||
|
||||
|
|
@ -374,8 +374,8 @@ class Events(object):
|
|||
# TODO: check auto-create, no need to create node first if available
|
||||
node = yield self._p.createNode(client, service, nodeIdentifier=node)
|
||||
except error.StanzaError as e:
|
||||
if e.condition == u"conflict":
|
||||
log.debug(_(u"requested node already exists"))
|
||||
if e.condition == "conflict":
|
||||
log.debug(_("requested node already exists"))
|
||||
|
||||
yield self._p.publish(client, service, node, items=[item_elt])
|
||||
|
||||
|
|
@ -388,7 +388,7 @@ class Events(object):
|
|||
profile_key=C.PROF_KEY_NONE):
|
||||
service = jid.JID(service) if service else None
|
||||
if not node:
|
||||
raise ValueError(_(u"missing node"))
|
||||
raise ValueError(_("missing node"))
|
||||
client = self.host.getClient(profile_key)
|
||||
return self.eventModify(
|
||||
client, service, node, id_ or NS_EVENT, timestamp_update or None, data_update
|
||||
|
|
@ -407,13 +407,13 @@ class Events(object):
|
|||
new_timestamp = event_timestamp if timestamp_update is None else timestamp_update
|
||||
new_data = event_metadata
|
||||
if data_update:
|
||||
for k, v in data_update.iteritems():
|
||||
for k, v in data_update.items():
|
||||
new_data[k] = v
|
||||
yield self.eventCreate(client, new_timestamp, new_data, service, node, id_)
|
||||
|
||||
def _eventsListSerialise(self, events):
|
||||
for timestamp, data in events:
|
||||
data["date"] = unicode(timestamp)
|
||||
data["date"] = str(timestamp)
|
||||
data["creator"] = C.boolConst(data.get("creator", False))
|
||||
return [e[1] for e in events]
|
||||
|
||||
|
|
@ -431,15 +431,15 @@ class Events(object):
|
|||
|
||||
@return list(tuple(int, dict)): list of events (timestamp + metadata)
|
||||
"""
|
||||
items, metadata = yield self.host.plugins[u'LIST_INTEREST'].listInterests(
|
||||
items, metadata = yield self.host.plugins['LIST_INTEREST'].listInterests(
|
||||
client, service, node, namespace=NS_EVENT)
|
||||
events = []
|
||||
for item in items:
|
||||
try:
|
||||
event_elt = next(item.interest.pubsub.elements(NS_EVENT, u"event"))
|
||||
event_elt = next(item.interest.pubsub.elements(NS_EVENT, "event"))
|
||||
except StopIteration:
|
||||
log.warning(
|
||||
_(u"No event found in item {item_id}, ignoring").format(
|
||||
_("No event found in item {item_id}, ignoring").format(
|
||||
item_id=item["id"])
|
||||
)
|
||||
else:
|
||||
|
|
@ -466,12 +466,12 @@ class Events(object):
|
|||
items, metadata = yield self._p.getItems(
|
||||
client, service, node, item_ids=[client.jid.userhost()]
|
||||
)
|
||||
event_elt = next(items[0].elements(NS_EVENT, u"invitee"))
|
||||
event_elt = next(items[0].elements(NS_EVENT, "invitee"))
|
||||
except (exceptions.NotFound, IndexError):
|
||||
# no item found, event data are not set yet
|
||||
defer.returnValue({})
|
||||
data = {}
|
||||
for key in (u"attend", u"guests"):
|
||||
for key in ("attend", "guests"):
|
||||
try:
|
||||
data[key] = event_elt[key]
|
||||
except KeyError:
|
||||
|
|
@ -495,7 +495,7 @@ class Events(object):
|
|||
guests: an int
|
||||
"""
|
||||
event_elt = domish.Element((NS_EVENT, "invitee"))
|
||||
for key in (u"attend", u"guests"):
|
||||
for key in ("attend", "guests"):
|
||||
try:
|
||||
event_elt[key] = data.pop(key)
|
||||
except KeyError:
|
||||
|
|
@ -522,15 +522,15 @@ class Events(object):
|
|||
invitees = {}
|
||||
for item in items:
|
||||
try:
|
||||
event_elt = next(item.elements(NS_EVENT, u"invitee"))
|
||||
event_elt = next(item.elements(NS_EVENT, "invitee"))
|
||||
except StopIteration:
|
||||
# no item found, event data are not set yet
|
||||
log.warning(_(
|
||||
u"no data found for {item_id} (service: {service}, node: {node})"
|
||||
"no data found for {item_id} (service: {service}, node: {node})"
|
||||
.format(item_id=item["id"], service=service, node=node)))
|
||||
else:
|
||||
data = {}
|
||||
for key in (u"attend", u"guests"):
|
||||
for key in ("attend", "guests"):
|
||||
try:
|
||||
data[key] = event_elt[key]
|
||||
except KeyError:
|
||||
|
|
@ -558,30 +558,30 @@ class Events(object):
|
|||
@param item_id(unicode): event id
|
||||
"""
|
||||
# FIXME: handle name and extra
|
||||
name = u''
|
||||
name = ''
|
||||
extra = {}
|
||||
if self._b is None:
|
||||
raise exceptions.FeatureNotFound(
|
||||
_(u'"XEP-0277" (blog) plugin is needed for this feature')
|
||||
_('"XEP-0277" (blog) plugin is needed for this feature')
|
||||
)
|
||||
if item_id is None:
|
||||
item_id = NS_EVENT
|
||||
|
||||
# first we authorize our invitee to see the nodes of interest
|
||||
yield self._p.setNodeAffiliations(client, service, node, {invitee_jid: u"member"})
|
||||
log.debug(_(u"affiliation set on event node"))
|
||||
yield self._p.setNodeAffiliations(client, service, node, {invitee_jid: "member"})
|
||||
log.debug(_("affiliation set on event node"))
|
||||
__, event_data = yield self.eventGet(client, service, node, item_id)
|
||||
log.debug(_(u"got event data"))
|
||||
log.debug(_("got event data"))
|
||||
invitees_service = jid.JID(event_data["invitees_service"])
|
||||
invitees_node = event_data["invitees_node"]
|
||||
blog_service = jid.JID(event_data["blog_service"])
|
||||
blog_node = event_data["blog_node"]
|
||||
yield self._p.setNodeAffiliations(
|
||||
client, invitees_service, invitees_node, {invitee_jid: u"publisher"}
|
||||
client, invitees_service, invitees_node, {invitee_jid: "publisher"}
|
||||
)
|
||||
log.debug(_(u"affiliation set on invitee node"))
|
||||
log.debug(_("affiliation set on invitee node"))
|
||||
yield self._p.setNodeAffiliations(
|
||||
client, blog_service, blog_node, {invitee_jid: u"member"}
|
||||
client, blog_service, blog_node, {invitee_jid: "member"}
|
||||
)
|
||||
blog_items, __ = yield self._b.mbGet(client, blog_service, blog_node, None)
|
||||
|
||||
|
|
@ -591,29 +591,29 @@ class Events(object):
|
|||
comments_node = item["comments_node"]
|
||||
except KeyError:
|
||||
log.debug(
|
||||
u"no comment service set for item {item_id}".format(
|
||||
"no comment service set for item {item_id}".format(
|
||||
item_id=item["id"]
|
||||
)
|
||||
)
|
||||
else:
|
||||
yield self._p.setNodeAffiliations(
|
||||
client, comments_service, comments_node, {invitee_jid: u"publisher"}
|
||||
client, comments_service, comments_node, {invitee_jid: "publisher"}
|
||||
)
|
||||
log.debug(_(u"affiliation set on blog and comments nodes"))
|
||||
log.debug(_("affiliation set on blog and comments nodes"))
|
||||
|
||||
# now we send the invitation
|
||||
pubsub_invitation = self.host.plugins[u'INVITATION']
|
||||
pubsub_invitation = self.host.plugins['INVITATION']
|
||||
pubsub_invitation.sendPubsubInvitation(client, invitee_jid, service, node,
|
||||
item_id, name, extra)
|
||||
|
||||
def _inviteByEmail(self, service, node, id_=NS_EVENT, email=u"", emails_extra=None,
|
||||
name=u"", host_name=u"", language=u"", url_template=u"",
|
||||
message_subject=u"", message_body=u"",
|
||||
def _inviteByEmail(self, service, node, id_=NS_EVENT, email="", emails_extra=None,
|
||||
name="", host_name="", language="", url_template="",
|
||||
message_subject="", message_body="",
|
||||
profile_key=C.PROF_KEY_NONE):
|
||||
client = self.host.getClient(profile_key)
|
||||
kwargs = {
|
||||
u"profile": client.profile,
|
||||
u"emails_extra": [unicode(e) for e in emails_extra],
|
||||
"profile": client.profile,
|
||||
"emails_extra": [str(e) for e in emails_extra],
|
||||
}
|
||||
for key in (
|
||||
"email",
|
||||
|
|
@ -625,7 +625,7 @@ class Events(object):
|
|||
"message_body",
|
||||
):
|
||||
value = locals()[key]
|
||||
kwargs[key] = unicode(value)
|
||||
kwargs[key] = str(value)
|
||||
return self.inviteByEmail(
|
||||
client, jid.JID(service) if service else None, node, id_ or NS_EVENT, **kwargs
|
||||
)
|
||||
|
|
@ -640,26 +640,26 @@ class Events(object):
|
|||
"""
|
||||
if self._i is None:
|
||||
raise exceptions.FeatureNotFound(
|
||||
_(u'"Invitations" plugin is needed for this feature')
|
||||
_('"Invitations" plugin is needed for this feature')
|
||||
)
|
||||
if self._b is None:
|
||||
raise exceptions.FeatureNotFound(
|
||||
_(u'"XEP-0277" (blog) plugin is needed for this feature')
|
||||
_('"XEP-0277" (blog) plugin is needed for this feature')
|
||||
)
|
||||
service = service or client.jid.userhostJID()
|
||||
event_uri = xmpp_uri.buildXMPPUri(
|
||||
"pubsub", path=service.full(), node=node, item=id_
|
||||
)
|
||||
kwargs["extra"] = {u"event_uri": event_uri}
|
||||
kwargs["extra"] = {"event_uri": event_uri}
|
||||
invitation_data = yield self._i.create(**kwargs)
|
||||
invitee_jid = invitation_data[u"jid"]
|
||||
log.debug(_(u"invitation created"))
|
||||
invitee_jid = invitation_data["jid"]
|
||||
log.debug(_("invitation created"))
|
||||
# now that we have a jid, we can send normal invitation
|
||||
yield self.invite(client, invitee_jid, service, node, id_)
|
||||
|
||||
|
||||
@implementer(iwokkel.IDisco)
|
||||
class EventsHandler(XMPPHandler):
|
||||
implements(iwokkel.IDisco)
|
||||
|
||||
def __init__(self, plugin_parent):
|
||||
self.plugin_parent = plugin_parent
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# SAT plugin to detect language (experimental)
|
||||
|
|
@ -24,7 +24,7 @@ from sat.core.log import getLogger
|
|||
from twisted.internet import defer
|
||||
from twisted.words.protocols.jabber import jid
|
||||
from wokkel import disco, iwokkel
|
||||
from zope.interface import implements
|
||||
from zope.interface import implementer
|
||||
from twisted.words.protocols.jabber.xmlstream import XMPPHandler
|
||||
|
||||
log = getLogger(__name__)
|
||||
|
|
@ -35,24 +35,24 @@ PLUGIN_INFO = {
|
|||
C.PI_IMPORT_NAME: "INVITATION",
|
||||
C.PI_TYPE: "EXP",
|
||||
C.PI_PROTOCOLS: [],
|
||||
C.PI_DEPENDENCIES: [u"XEP-0060", u"XEP-0329"],
|
||||
C.PI_DEPENDENCIES: ["XEP-0060", "XEP-0329"],
|
||||
C.PI_RECOMMENDATIONS: [],
|
||||
C.PI_MAIN: "Invitation",
|
||||
C.PI_HANDLER: "yes",
|
||||
C.PI_DESCRIPTION: _(u"Experimental handling of invitations"),
|
||||
C.PI_DESCRIPTION: _("Experimental handling of invitations"),
|
||||
}
|
||||
|
||||
NS_INVITATION = u"https://salut-a-toi/protocol/invitation:0"
|
||||
NS_INVITATION = "https://salut-a-toi/protocol/invitation:0"
|
||||
INVITATION = '/message/invitation[@xmlns="{ns_invit}"]'.format(
|
||||
ns_invit=NS_INVITATION
|
||||
)
|
||||
NS_INVITATION_LIST = NS_INVITATION + u"#list"
|
||||
NS_INVITATION_LIST = NS_INVITATION + "#list"
|
||||
|
||||
|
||||
class Invitation(object):
|
||||
|
||||
def __init__(self, host):
|
||||
log.info(_(u"Invitation plugin initialization"))
|
||||
log.info(_("Invitation plugin initialization"))
|
||||
self.host = host
|
||||
self._p = self.host.plugins["XEP-0060"]
|
||||
# map from namespace of the invitation to callback handling it
|
||||
|
|
@ -88,7 +88,7 @@ class Invitation(object):
|
|||
"""
|
||||
if namespace in self._ns_cb:
|
||||
raise exceptions.ConflictError(
|
||||
u"invitation namespace {namespace} is already register with {callback}"
|
||||
"invitation namespace {namespace} is already register with {callback}"
|
||||
.format(namespace=namespace, callback=self._ns_cb[namespace]))
|
||||
self._ns_cb[namespace] = callback
|
||||
|
||||
|
|
@ -113,15 +113,15 @@ class Invitation(object):
|
|||
client.generateMessageXML(mess_data)
|
||||
invitation_elt = mess_data["xml"].addElement("invitation", NS_INVITATION)
|
||||
if name is not None:
|
||||
invitation_elt[u"name"] = name
|
||||
thumb_url = extra.get(u'thumb_url')
|
||||
invitation_elt["name"] = name
|
||||
thumb_url = extra.get('thumb_url')
|
||||
if thumb_url:
|
||||
if not thumb_url.startswith(u'http'):
|
||||
if not thumb_url.startswith('http'):
|
||||
log.warning(
|
||||
u"only http URLs are allowed for thumbnails, got {url}, ignoring"
|
||||
"only http URLs are allowed for thumbnails, got {url}, ignoring"
|
||||
.format(url=thumb_url))
|
||||
else:
|
||||
invitation_elt[u'thumb_url'] = thumb_url
|
||||
invitation_elt['thumb_url'] = thumb_url
|
||||
return mess_data, invitation_elt
|
||||
|
||||
def sendPubsubInvitation(self, client, invitee_jid, service, node,
|
||||
|
|
@ -139,11 +139,11 @@ class Invitation(object):
|
|||
extra = {}
|
||||
mess_data, invitation_elt = self._generateBaseInvitation(
|
||||
client, invitee_jid, name, extra)
|
||||
pubsub_elt = invitation_elt.addElement(u"pubsub")
|
||||
pubsub_elt[u"service"] = service.full()
|
||||
pubsub_elt[u"node"] = node
|
||||
pubsub_elt[u"item"] = item_id
|
||||
return client.send(mess_data[u"xml"])
|
||||
pubsub_elt = invitation_elt.addElement("pubsub")
|
||||
pubsub_elt["service"] = service.full()
|
||||
pubsub_elt["node"] = node
|
||||
pubsub_elt["item"] = item_id
|
||||
return client.send(mess_data["xml"])
|
||||
|
||||
def sendFileSharingInvitation(self, client, invitee_jid, service, repos_type=None,
|
||||
namespace=None, path=None, name=None, extra=None):
|
||||
|
|
@ -163,20 +163,20 @@ class Invitation(object):
|
|||
extra = {}
|
||||
mess_data, invitation_elt = self._generateBaseInvitation(
|
||||
client, invitee_jid, name, extra)
|
||||
file_sharing_elt = invitation_elt.addElement(u"file_sharing")
|
||||
file_sharing_elt[u"service"] = service.full()
|
||||
file_sharing_elt = invitation_elt.addElement("file_sharing")
|
||||
file_sharing_elt["service"] = service.full()
|
||||
if repos_type is not None:
|
||||
if repos_type not in (u"files", "photos"):
|
||||
msg = u"unknown repository type: {repos_type}".format(
|
||||
if repos_type not in ("files", "photos"):
|
||||
msg = "unknown repository type: {repos_type}".format(
|
||||
repos_type=repos_type)
|
||||
log.warning(msg)
|
||||
raise exceptions.DateError(msg)
|
||||
file_sharing_elt[u"type"] = repos_type
|
||||
file_sharing_elt["type"] = repos_type
|
||||
if namespace is not None:
|
||||
file_sharing_elt[u"namespace"] = namespace
|
||||
file_sharing_elt["namespace"] = namespace
|
||||
if path is not None:
|
||||
file_sharing_elt[u"path"] = path
|
||||
return client.send(mess_data[u"xml"])
|
||||
file_sharing_elt["path"] = path
|
||||
return client.send(mess_data["xml"])
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def _parsePubsubElt(self, client, pubsub_elt):
|
||||
|
|
@ -185,25 +185,25 @@ class Invitation(object):
|
|||
node = pubsub_elt["node"]
|
||||
item_id = pubsub_elt.getAttribute("item")
|
||||
except (RuntimeError, KeyError):
|
||||
log.warning(_(u"Bad invitation, ignoring"))
|
||||
log.warning(_("Bad invitation, ignoring"))
|
||||
raise exceptions.DataError
|
||||
|
||||
try:
|
||||
items, metadata = yield self._p.getItems(client, service, node,
|
||||
item_ids=[item_id])
|
||||
except Exception as e:
|
||||
log.warning(_(u"Can't get item linked with invitation: {reason}").format(
|
||||
log.warning(_("Can't get item linked with invitation: {reason}").format(
|
||||
reason=e))
|
||||
try:
|
||||
item_elt = items[0]
|
||||
except IndexError:
|
||||
log.warning(_(u"Invitation was linking to a non existing item"))
|
||||
log.warning(_("Invitation was linking to a non existing item"))
|
||||
raise exceptions.DataError
|
||||
|
||||
try:
|
||||
namespace = item_elt.firstChildElement().uri
|
||||
except Exception as e:
|
||||
log.warning(_(u"Can't retrieve namespace of invitation: {reason}").format(
|
||||
log.warning(_("Can't retrieve namespace of invitation: {reason}").format(
|
||||
reason = e))
|
||||
raise exceptions.DataError
|
||||
|
||||
|
|
@ -214,41 +214,41 @@ class Invitation(object):
|
|||
try:
|
||||
service = jid.JID(file_sharing_elt["service"])
|
||||
except (RuntimeError, KeyError):
|
||||
log.warning(_(u"Bad invitation, ignoring"))
|
||||
log.warning(_("Bad invitation, ignoring"))
|
||||
raise exceptions.DataError
|
||||
repos_type = file_sharing_elt.getAttribute(u"type", u"files")
|
||||
namespace = file_sharing_elt.getAttribute(u"namespace")
|
||||
path = file_sharing_elt.getAttribute(u"path")
|
||||
repos_type = file_sharing_elt.getAttribute("type", "files")
|
||||
namespace = file_sharing_elt.getAttribute("namespace")
|
||||
path = file_sharing_elt.getAttribute("path")
|
||||
args = [service, repos_type, namespace, path]
|
||||
ns_fis = self.host.getNamespace(u"fis")
|
||||
ns_fis = self.host.getNamespace("fis")
|
||||
return ns_fis, args
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def onInvitation(self, message_elt, client):
|
||||
log.debug(u"invitation received [{profile}]".format(profile=client.profile))
|
||||
log.debug("invitation received [{profile}]".format(profile=client.profile))
|
||||
invitation_elt = message_elt.invitation
|
||||
|
||||
name = invitation_elt.getAttribute(u"name")
|
||||
name = invitation_elt.getAttribute("name")
|
||||
extra = {}
|
||||
if invitation_elt.hasAttribute(u"thumb_url"):
|
||||
extra[u'thumb_url'] = invitation_elt[u'thumb_url']
|
||||
if invitation_elt.hasAttribute("thumb_url"):
|
||||
extra['thumb_url'] = invitation_elt['thumb_url']
|
||||
|
||||
for elt in invitation_elt.elements():
|
||||
if elt.uri != NS_INVITATION:
|
||||
log.warning(u"unexpected element: {xml}".format(xml=elt.toXml()))
|
||||
log.warning("unexpected element: {xml}".format(xml=elt.toXml()))
|
||||
continue
|
||||
if elt.name == u"pubsub":
|
||||
if elt.name == "pubsub":
|
||||
method = self._parsePubsubElt
|
||||
elif elt.name == u"file_sharing":
|
||||
elif elt.name == "file_sharing":
|
||||
method = self._parseFileSharingElt
|
||||
else:
|
||||
log.warning(u"not implemented invitation element: {xml}".format(
|
||||
log.warning("not implemented invitation element: {xml}".format(
|
||||
xml = elt.toXml()))
|
||||
continue
|
||||
try:
|
||||
namespace, args = yield method(client, elt)
|
||||
except exceptions.DataError:
|
||||
log.warning(u"Can't parse invitation element: {xml}".format(
|
||||
log.warning("Can't parse invitation element: {xml}".format(
|
||||
xml = elt.toXml()))
|
||||
continue
|
||||
|
||||
|
|
@ -256,14 +256,14 @@ class Invitation(object):
|
|||
cb = self._ns_cb[namespace]
|
||||
except KeyError:
|
||||
log.warning(_(
|
||||
u'No handler for namespace "{namespace}", invitation ignored')
|
||||
'No handler for namespace "{namespace}", invitation ignored')
|
||||
.format(namespace=namespace))
|
||||
else:
|
||||
cb(client, name, extra, *args)
|
||||
|
||||
|
||||
@implementer(iwokkel.IDisco)
|
||||
class PubsubInvitationHandler(XMPPHandler):
|
||||
implements(iwokkel.IDisco)
|
||||
|
||||
def __init__(self, plugin_parent):
|
||||
self.plugin_parent = plugin_parent
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# SAT plugin to detect language (experimental)
|
||||
|
|
@ -31,21 +31,21 @@ PLUGIN_INFO = {
|
|||
C.PI_IMPORT_NAME: "FILE_SHARING_INVITATION",
|
||||
C.PI_TYPE: "EXP",
|
||||
C.PI_PROTOCOLS: [],
|
||||
C.PI_DEPENDENCIES: ["XEP-0329", u"INVITATION"],
|
||||
C.PI_DEPENDENCIES: ["XEP-0329", "INVITATION"],
|
||||
C.PI_RECOMMENDATIONS: [],
|
||||
C.PI_MAIN: "FileSharingInvitation",
|
||||
C.PI_HANDLER: "no",
|
||||
C.PI_DESCRIPTION: _(u"Experimental handling of invitations for file sharing"),
|
||||
C.PI_DESCRIPTION: _("Experimental handling of invitations for file sharing"),
|
||||
}
|
||||
|
||||
|
||||
class FileSharingInvitation(object):
|
||||
|
||||
def __init__(self, host):
|
||||
log.info(_(u"File Sharing Invitation plugin initialization"))
|
||||
log.info(_("File Sharing Invitation plugin initialization"))
|
||||
self.host = host
|
||||
ns_fis = host.getNamespace(u"fis")
|
||||
host.plugins[u"INVITATION"].registerNamespace(ns_fis, self.onInvitation)
|
||||
ns_fis = host.getNamespace("fis")
|
||||
host.plugins["INVITATION"].registerNamespace(ns_fis, self.onInvitation)
|
||||
host.bridge.addMethod(
|
||||
"FISInvite",
|
||||
".plugin",
|
||||
|
|
@ -56,30 +56,30 @@ class FileSharingInvitation(object):
|
|||
|
||||
def _sendFileSharingInvitation(
|
||||
self, invitee_jid_s, service_s, repos_type=None, namespace=None, path=None,
|
||||
name=None, extra_s=u'', profile_key=C.PROF_KEY_NONE):
|
||||
name=None, extra_s='', profile_key=C.PROF_KEY_NONE):
|
||||
client = self.host.getClient(profile_key)
|
||||
invitee_jid = jid.JID(invitee_jid_s)
|
||||
service = jid.JID(service_s)
|
||||
extra = data_format.deserialise(extra_s)
|
||||
return self.host.plugins[u"INVITATION"].sendFileSharingInvitation(
|
||||
return self.host.plugins["INVITATION"].sendFileSharingInvitation(
|
||||
client, invitee_jid, service, repos_type=repos_type or None,
|
||||
namespace=namespace or None, path=path or None, name=name or None,
|
||||
extra=extra)
|
||||
|
||||
def onInvitation(self, client, name, extra, service, repos_type, namespace, path):
|
||||
if repos_type == u"files":
|
||||
type_human = _(u"file sharing")
|
||||
elif repos_type == u"photos":
|
||||
type_human = _(u"photos album")
|
||||
if repos_type == "files":
|
||||
type_human = _("file sharing")
|
||||
elif repos_type == "photos":
|
||||
type_human = _("photos album")
|
||||
else:
|
||||
log.warning(u"Unknown repository type: {repos_type}".format(
|
||||
log.warning("Unknown repository type: {repos_type}".format(
|
||||
repos_type=repos_type))
|
||||
repos_type = u"file"
|
||||
type_human = _(u"file sharing")
|
||||
repos_type = "file"
|
||||
type_human = _("file sharing")
|
||||
log.info(_(
|
||||
u'{profile} has received an invitation for a files repository ({type_human}) '
|
||||
u'with namespace "{namespace}" at path [{path}]').format(
|
||||
'{profile} has received an invitation for a files repository ({type_human}) '
|
||||
'with namespace "{namespace}" at path [{path}]').format(
|
||||
profile=client.profile, type_human=type_human, namespace=namespace, path=path)
|
||||
)
|
||||
return self.host.plugins[u'LIST_INTEREST'].registerFileSharing(
|
||||
return self.host.plugins['LIST_INTEREST'].registerFileSharing(
|
||||
client, service, repos_type, namespace, path, name, extra)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# SAT plugin for managing pipes (experimental)
|
||||
|
|
@ -51,8 +51,8 @@ PLUGIN_INFO = {
|
|||
C.PI_DESCRIPTION: _("""Jingle Stream plugin"""),
|
||||
}
|
||||
|
||||
CONFIRM = D_(u"{peer} wants to send you a stream, do you accept ?")
|
||||
CONFIRM_TITLE = D_(u"Stream Request")
|
||||
CONFIRM = D_("{peer} wants to send you a stream, do you accept ?")
|
||||
CONFIRM_TITLE = D_("Stream Request")
|
||||
|
||||
|
||||
class StreamProtocol(protocol.Protocol):
|
||||
|
|
@ -119,7 +119,7 @@ class StreamFactory(protocol.Factory):
|
|||
def startStream(self, consumer):
|
||||
if self.consumer is not None:
|
||||
raise exceptions.InternalError(
|
||||
_(u"stream can't be used with multiple consumers")
|
||||
_("stream can't be used with multiple consumers")
|
||||
)
|
||||
assert self.deferred is None
|
||||
self.consumer = consumer
|
||||
|
|
@ -166,7 +166,7 @@ class StreamFactory(protocol.Factory):
|
|||
try:
|
||||
self.client_conn.sendData(data)
|
||||
except AttributeError:
|
||||
log.warning(_(u"No client connected, can't send data"))
|
||||
log.warning(_("No client connected, can't send data"))
|
||||
|
||||
def writeToConsumer(self, data):
|
||||
self.consumer.write(data)
|
||||
|
|
@ -186,7 +186,7 @@ class JingleStream(object):
|
|||
in_sign="ss",
|
||||
out_sign="s",
|
||||
method=self._streamOut,
|
||||
async=True,
|
||||
async_=True,
|
||||
)
|
||||
|
||||
# jingle callbacks
|
||||
|
|
@ -227,7 +227,7 @@ class JingleStream(object):
|
|||
}
|
||||
],
|
||||
)
|
||||
defer.returnValue(unicode(port))
|
||||
defer.returnValue(str(port))
|
||||
|
||||
def jingleSessionInit(self, client, session, content_name, stream_object):
|
||||
content_data = session["contents"][content_name]
|
||||
|
|
@ -245,7 +245,7 @@ class JingleStream(object):
|
|||
self._j.ROLE_INITIATOR,
|
||||
self._j.ROLE_RESPONDER,
|
||||
):
|
||||
log.warning(u"Bad sender, assuming initiator")
|
||||
log.warning("Bad sender, assuming initiator")
|
||||
content_data["senders"] = self._j.ROLE_INITIATOR
|
||||
|
||||
confirm_data = yield xml_tools.deferDialog(
|
||||
|
|
@ -266,7 +266,7 @@ class JingleStream(object):
|
|||
try:
|
||||
port = int(confirm_data["port"])
|
||||
except (ValueError, KeyError):
|
||||
raise exceptions.DataError(_(u"given port is invalid"))
|
||||
raise exceptions.DataError(_("given port is invalid"))
|
||||
endpoint = endpoints.TCP4ClientEndpoint(reactor, "localhost", port)
|
||||
factory = StreamFactory()
|
||||
yield endpoint.connect(factory)
|
||||
|
|
@ -288,16 +288,16 @@ class JingleStream(object):
|
|||
args = [client, session, content_name, content_data]
|
||||
finished_d.addCallbacks(self._finishedCb, self._finishedEb, args, None, args)
|
||||
else:
|
||||
log.warning(u"FIXME: unmanaged action {}".format(action))
|
||||
log.warning("FIXME: unmanaged action {}".format(action))
|
||||
return desc_elt
|
||||
|
||||
def _finishedCb(self, __, client, session, content_name, content_data):
|
||||
log.info(u"Pipe transfer completed")
|
||||
log.info("Pipe transfer completed")
|
||||
self._j.contentTerminate(client, session, content_name)
|
||||
content_data["stream_object"].stopStream()
|
||||
|
||||
def _finishedEb(self, failure, client, session, content_name, content_data):
|
||||
log.warning(u"Error while streaming pipe: {}".format(failure))
|
||||
log.warning("Error while streaming pipe: {}".format(failure))
|
||||
self._j.contentTerminate(
|
||||
client, session, content_name, reason=self._j.REASON_FAILED_TRANSPORT
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# SAT plugin to detect language (experimental)
|
||||
|
|
@ -28,7 +28,7 @@ try:
|
|||
from langid.langid import LanguageIdentifier, model
|
||||
except ImportError:
|
||||
raise exceptions.MissingModule(
|
||||
u'Missing module langid, please download/install it with "pip install langid")'
|
||||
'Missing module langid, please download/install it with "pip install langid")'
|
||||
)
|
||||
|
||||
identifier = LanguageIdentifier.from_modelstring(model, norm_probs=False)
|
||||
|
|
@ -45,9 +45,9 @@ PLUGIN_INFO = {
|
|||
C.PI_DESCRIPTION: _("""Detect and set message language when unknown"""),
|
||||
}
|
||||
|
||||
CATEGORY = D_(u"Misc")
|
||||
NAME = u"lang_detect"
|
||||
LABEL = D_(u"language detection")
|
||||
CATEGORY = D_("Misc")
|
||||
NAME = "lang_detect"
|
||||
LABEL = D_("language detection")
|
||||
PARAMS = """
|
||||
<params>
|
||||
<individual>
|
||||
|
|
@ -63,7 +63,7 @@ PARAMS = """
|
|||
|
||||
class LangDetect(object):
|
||||
def __init__(self, host):
|
||||
log.info(_(u"Language detection plugin initialization"))
|
||||
log.info(_("Language detection plugin initialization"))
|
||||
self.host = host
|
||||
host.memory.updateParams(PARAMS)
|
||||
host.trigger.add("MessageReceived", self.MessageReceivedTrigger)
|
||||
|
|
@ -71,8 +71,8 @@ class LangDetect(object):
|
|||
|
||||
def addLanguage(self, mess_data):
|
||||
message = mess_data["message"]
|
||||
if len(message) == 1 and message.keys()[0] == "":
|
||||
msg = message.values()[0]
|
||||
if len(message) == 1 and list(message.keys())[0] == "":
|
||||
msg = list(message.values())[0]
|
||||
lang = identifier.classify(msg)[0]
|
||||
mess_data["message"] = {lang: msg}
|
||||
return mess_data
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# SAT plugin to detect language (experimental)
|
||||
|
|
@ -21,7 +21,7 @@ from sat.core.i18n import _
|
|||
from sat.core.constants import Const as C
|
||||
from sat.core.log import getLogger
|
||||
from wokkel import disco, iwokkel, pubsub
|
||||
from zope.interface import implements
|
||||
from zope.interface import implementer
|
||||
from twisted.internet import defer
|
||||
from twisted.words.protocols.jabber import error as jabber_error, jid
|
||||
from twisted.words.protocols.jabber.xmlstream import XMPPHandler
|
||||
|
|
@ -35,11 +35,11 @@ PLUGIN_INFO = {
|
|||
C.PI_IMPORT_NAME: "LIST_INTEREST",
|
||||
C.PI_TYPE: "EXP",
|
||||
C.PI_PROTOCOLS: [],
|
||||
C.PI_DEPENDENCIES: [u"XEP-0060", u"XEP-0329"],
|
||||
C.PI_DEPENDENCIES: ["XEP-0060", "XEP-0329"],
|
||||
C.PI_RECOMMENDATIONS: [],
|
||||
C.PI_MAIN: "ListInterest",
|
||||
C.PI_HANDLER: "yes",
|
||||
C.PI_DESCRIPTION: _(u"Experimental handling of interesting XMPP locations"),
|
||||
C.PI_DESCRIPTION: _("Experimental handling of interesting XMPP locations"),
|
||||
}
|
||||
|
||||
NS_LIST_INTEREST = "https://salut-a-toi/protocol/list-interest:0"
|
||||
|
|
@ -49,7 +49,7 @@ class ListInterest(object):
|
|||
namespace = NS_LIST_INTEREST
|
||||
|
||||
def __init__(self, host):
|
||||
log.info(_(u"List of Interest plugin initialization"))
|
||||
log.info(_("List of Interest plugin initialization"))
|
||||
self.host = host
|
||||
self._p = self.host.plugins["XEP-0060"]
|
||||
host.bridge.addMethod(
|
||||
|
|
@ -58,7 +58,7 @@ class ListInterest(object):
|
|||
in_sign="ssss",
|
||||
out_sign="aa{ss}",
|
||||
method=self._listInterests,
|
||||
async=True,
|
||||
async_=True,
|
||||
)
|
||||
|
||||
def getHandler(self, client):
|
||||
|
|
@ -76,8 +76,8 @@ class ListInterest(object):
|
|||
options=options,
|
||||
)
|
||||
except jabber_error.StanzaError as e:
|
||||
if e.condition == u"conflict":
|
||||
log.debug(_(u"requested node already exists"))
|
||||
if e.condition == "conflict":
|
||||
log.debug(_("requested node already exists"))
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def registerPubsub(self, client, namespace, service, node, item_id=None,
|
||||
|
|
@ -101,20 +101,20 @@ class ListInterest(object):
|
|||
if extra is None:
|
||||
extra = {}
|
||||
yield self.createNode(client)
|
||||
interest_elt = domish.Element((NS_LIST_INTEREST, u"interest"))
|
||||
interest_elt[u"namespace"] = namespace
|
||||
interest_elt = domish.Element((NS_LIST_INTEREST, "interest"))
|
||||
interest_elt["namespace"] = namespace
|
||||
if name is not None:
|
||||
interest_elt[u'name'] = name
|
||||
thumb_url = extra.get(u'thumb_url')
|
||||
interest_elt['name'] = name
|
||||
thumb_url = extra.get('thumb_url')
|
||||
if thumb_url:
|
||||
interest_elt[u'thumb_url'] = thumb_url
|
||||
pubsub_elt = interest_elt.addElement(u"pubsub")
|
||||
pubsub_elt[u"service"] = service.full()
|
||||
pubsub_elt[u"node"] = node
|
||||
interest_elt['thumb_url'] = thumb_url
|
||||
pubsub_elt = interest_elt.addElement("pubsub")
|
||||
pubsub_elt["service"] = service.full()
|
||||
pubsub_elt["node"] = node
|
||||
if item_id is not None:
|
||||
pubsub_elt[u"item"] = item_id
|
||||
pubsub_elt["item"] = item_id
|
||||
if creator:
|
||||
pubsub_elt[u"creator"] = C.BOOL_TRUE
|
||||
pubsub_elt["creator"] = C.BOOL_TRUE
|
||||
if element is not None:
|
||||
pubsub_elt.addChild(element)
|
||||
item_elt = pubsub.Item(payload=interest_elt)
|
||||
|
|
@ -138,21 +138,21 @@ class ListInterest(object):
|
|||
if extra is None:
|
||||
extra = {}
|
||||
yield self.createNode(client)
|
||||
interest_elt = domish.Element((NS_LIST_INTEREST, u"interest"))
|
||||
interest_elt[u"namespace"] = self.host.getNamespace(u"fis")
|
||||
interest_elt = domish.Element((NS_LIST_INTEREST, "interest"))
|
||||
interest_elt["namespace"] = self.host.getNamespace("fis")
|
||||
if name is not None:
|
||||
interest_elt[u'name'] = name
|
||||
thumb_url = extra.get(u'thumb_url')
|
||||
interest_elt['name'] = name
|
||||
thumb_url = extra.get('thumb_url')
|
||||
if thumb_url:
|
||||
interest_elt[u'thumb_url'] = thumb_url
|
||||
file_sharing_elt = interest_elt.addElement(u"file_sharing")
|
||||
file_sharing_elt[u"service"] = service.full()
|
||||
interest_elt['thumb_url'] = thumb_url
|
||||
file_sharing_elt = interest_elt.addElement("file_sharing")
|
||||
file_sharing_elt["service"] = service.full()
|
||||
if repos_type is not None:
|
||||
file_sharing_elt[u"type"] = repos_type
|
||||
file_sharing_elt["type"] = repos_type
|
||||
if namespace is not None:
|
||||
file_sharing_elt[u"namespace"] = namespace
|
||||
file_sharing_elt["namespace"] = namespace
|
||||
if path is not None:
|
||||
file_sharing_elt[u"path"] = path
|
||||
file_sharing_elt["path"] = path
|
||||
item_elt = pubsub.Item(payload=interest_elt)
|
||||
yield self._p.publish(
|
||||
client, client.jid.userhostJID(), NS_LIST_INTEREST, items=[item_elt]
|
||||
|
|
@ -163,38 +163,38 @@ class ListInterest(object):
|
|||
for item_elt in interests_data[0]:
|
||||
interest_data = {}
|
||||
interest_elt = item_elt.interest
|
||||
if interest_elt.hasAttribute(u'namespace'):
|
||||
interest_data[u'namespace'] = interest_elt.getAttribute(u'namespace')
|
||||
if interest_elt.hasAttribute(u'name'):
|
||||
interest_data[u'name'] = interest_elt.getAttribute(u'name')
|
||||
if interest_elt.hasAttribute(u'thumb_url'):
|
||||
interest_data[u'thumb_url'] = interest_elt.getAttribute(u'thumb_url')
|
||||
if interest_elt.hasAttribute('namespace'):
|
||||
interest_data['namespace'] = interest_elt.getAttribute('namespace')
|
||||
if interest_elt.hasAttribute('name'):
|
||||
interest_data['name'] = interest_elt.getAttribute('name')
|
||||
if interest_elt.hasAttribute('thumb_url'):
|
||||
interest_data['thumb_url'] = interest_elt.getAttribute('thumb_url')
|
||||
elt = interest_elt.firstChildElement()
|
||||
if elt.uri != NS_LIST_INTEREST:
|
||||
log.warning(u"unexpected child element, ignoring: {xml}".format(
|
||||
log.warning("unexpected child element, ignoring: {xml}".format(
|
||||
xml = elt.toXml()))
|
||||
continue
|
||||
if elt.name == u'pubsub':
|
||||
if elt.name == 'pubsub':
|
||||
interest_data.update({
|
||||
u"type": u"pubsub",
|
||||
u"service": elt[u'service'],
|
||||
u"node": elt[u'node'],
|
||||
"type": "pubsub",
|
||||
"service": elt['service'],
|
||||
"node": elt['node'],
|
||||
})
|
||||
for attr in (u'item', u'creator'):
|
||||
for attr in ('item', 'creator'):
|
||||
if elt.hasAttribute(attr):
|
||||
interest_data[attr] = elt[attr]
|
||||
elif elt.name == u'file_sharing':
|
||||
elif elt.name == 'file_sharing':
|
||||
interest_data.update({
|
||||
u"type": u"file_sharing",
|
||||
u"service": elt[u'service'],
|
||||
"type": "file_sharing",
|
||||
"service": elt['service'],
|
||||
})
|
||||
if elt.hasAttribute(u'type'):
|
||||
interest_data[u'subtype'] = elt[u'type']
|
||||
for attr in (u'namespace', u'path'):
|
||||
if elt.hasAttribute('type'):
|
||||
interest_data['subtype'] = elt['type']
|
||||
for attr in ('namespace', 'path'):
|
||||
if elt.hasAttribute(attr):
|
||||
interest_data[attr] = elt[attr]
|
||||
else:
|
||||
log.warning(u"unknown element, ignoring: {xml}".format(xml=elt.toXml()))
|
||||
log.warning("unknown element, ignoring: {xml}".format(xml=elt.toXml()))
|
||||
continue
|
||||
interests.append(interest_data)
|
||||
|
||||
|
|
@ -229,20 +229,20 @@ class ListInterest(object):
|
|||
filtered_items = []
|
||||
for item in items:
|
||||
try:
|
||||
interest_elt = next(item.elements(NS_LIST_INTEREST, u"interest"))
|
||||
interest_elt = next(item.elements(NS_LIST_INTEREST, "interest"))
|
||||
except StopIteration:
|
||||
log.warning(_(u"Missing interest element: {xml}").format(
|
||||
log.warning(_("Missing interest element: {xml}").format(
|
||||
xml=interest_elt.toXml()))
|
||||
continue
|
||||
if interest_elt.getAttribute(u"namespace") == namespace:
|
||||
if interest_elt.getAttribute("namespace") == namespace:
|
||||
filtered_items.append(item)
|
||||
items = filtered_items
|
||||
|
||||
defer.returnValue((items, metadata))
|
||||
|
||||
|
||||
@implementer(iwokkel.IDisco)
|
||||
class ListInterestHandler(XMPPHandler):
|
||||
implements(iwokkel.IDisco)
|
||||
|
||||
def __init__(self, plugin_parent):
|
||||
self.plugin_parent = plugin_parent
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# SAT plugin for parrot mode (experimental)
|
||||
|
|
@ -38,7 +38,7 @@ PLUGIN_INFO = {
|
|||
C.PI_MAIN: "Exp_Parrot",
|
||||
C.PI_HANDLER: "no",
|
||||
C.PI_DESCRIPTION: _(
|
||||
u"""Implementation of parrot mode (repeat messages between 2 entities)"""
|
||||
"""Implementation of parrot mode (repeat messages between 2 entities)"""
|
||||
),
|
||||
}
|
||||
|
||||
|
|
@ -60,7 +60,7 @@ class Exp_Parrot(object):
|
|||
try:
|
||||
self.host.plugins[C.TEXT_CMDS].registerTextCommands(self)
|
||||
except KeyError:
|
||||
log.info(_(u"Text commands not available"))
|
||||
log.info(_("Text commands not available"))
|
||||
|
||||
# def sendMessageTrigger(self, client, mess_data, treatments):
|
||||
# """ Deactivate other triggers if recipient is in parrot links """
|
||||
|
|
@ -90,7 +90,7 @@ class Exp_Parrot(object):
|
|||
|
||||
message = {}
|
||||
for e in message_elt.elements(C.NS_CLIENT, "body"):
|
||||
body = unicode(e)
|
||||
body = str(e)
|
||||
lang = e.getAttribute("lang") or ""
|
||||
|
||||
try:
|
||||
|
|
@ -107,12 +107,12 @@ class Exp_Parrot(object):
|
|||
return True
|
||||
else:
|
||||
src_txt = from_jid.user
|
||||
message[lang] = u"[{}] {}".format(src_txt, body)
|
||||
message[lang] = "[{}] {}".format(src_txt, body)
|
||||
|
||||
linked = _links[from_jid.userhostJID()]
|
||||
|
||||
client.sendMessage(
|
||||
jid.JID(unicode(linked)), message, None, "auto", no_trigger=True
|
||||
jid.JID(str(linked)), message, None, "auto", no_trigger=True
|
||||
)
|
||||
|
||||
return True
|
||||
|
|
@ -130,8 +130,8 @@ class Exp_Parrot(object):
|
|||
|
||||
_links[source_jid.userhostJID()] = dest_jid
|
||||
log.info(
|
||||
u"Parrot mode: %s will be repeated to %s"
|
||||
% (source_jid.userhost(), unicode(dest_jid))
|
||||
"Parrot mode: %s will be repeated to %s"
|
||||
% (source_jid.userhost(), str(dest_jid))
|
||||
)
|
||||
|
||||
def removeParrot(self, client, source_jid):
|
||||
|
|
@ -166,7 +166,7 @@ class Exp_Parrot(object):
|
|||
|
||||
txt_cmd.feedBack(
|
||||
client,
|
||||
"Parrot mode activated for {}".format(unicode(link_left_jid)),
|
||||
"Parrot mode activated for {}".format(str(link_left_jid)),
|
||||
mess_data,
|
||||
)
|
||||
|
||||
|
|
@ -183,7 +183,7 @@ class Exp_Parrot(object):
|
|||
raise jid.InvalidFormat
|
||||
except jid.InvalidFormat:
|
||||
txt_cmd.feedBack(
|
||||
client, u"Can't deactivate Parrot mode for invalid jid", mess_data
|
||||
client, "Can't deactivate Parrot mode for invalid jid", mess_data
|
||||
)
|
||||
return False
|
||||
|
||||
|
|
@ -194,8 +194,8 @@ class Exp_Parrot(object):
|
|||
|
||||
txt_cmd.feedBack(
|
||||
client,
|
||||
u"Parrot mode deactivated for {} and {}".format(
|
||||
unicode(link_left_jid), unicode(link_right_jid)
|
||||
"Parrot mode deactivated for {} and {}".format(
|
||||
str(link_left_jid), str(link_right_jid)
|
||||
),
|
||||
mess_data,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# SAT plugin to send pubsub requests with administrator privilege
|
||||
|
|
@ -29,20 +29,20 @@ from wokkel import generic
|
|||
log = getLogger(__name__)
|
||||
|
||||
PLUGIN_INFO = {
|
||||
C.PI_NAME: u"Pubsub Administrator",
|
||||
C.PI_IMPORT_NAME: u"PUBSUB_ADMIN",
|
||||
C.PI_NAME: "Pubsub Administrator",
|
||||
C.PI_IMPORT_NAME: "PUBSUB_ADMIN",
|
||||
C.PI_TYPE: C.PLUG_TYPE_EXP,
|
||||
C.PI_PROTOCOLS: [],
|
||||
C.PI_DEPENDENCIES: [],
|
||||
C.PI_RECOMMENDATIONS: [],
|
||||
C.PI_MAIN: u"PubsubAdmin",
|
||||
C.PI_HANDLER: u"no",
|
||||
C.PI_DESCRIPTION: _(u"""\Implementation of Pubsub Administrator
|
||||
C.PI_MAIN: "PubsubAdmin",
|
||||
C.PI_HANDLER: "no",
|
||||
C.PI_DESCRIPTION: _("""\Implementation of Pubsub Administrator
|
||||
This allows a pubsub administrator to overwrite completly items, including publisher.
|
||||
Specially useful when importing a node."""),
|
||||
}
|
||||
|
||||
NS_PUBSUB_ADMIN = u"https://salut-a-toi.org/spec/pubsub_admin:0"
|
||||
NS_PUBSUB_ADMIN = "https://salut-a-toi.org/spec/pubsub_admin:0"
|
||||
|
||||
|
||||
class PubsubAdmin(object):
|
||||
|
|
@ -55,7 +55,7 @@ class PubsubAdmin(object):
|
|||
in_sign="ssasss",
|
||||
out_sign="as",
|
||||
method=self._publish,
|
||||
async=True,
|
||||
async_=True,
|
||||
)
|
||||
|
||||
def _publish(self, service, nodeIdentifier, items, extra=None,
|
||||
|
|
@ -71,22 +71,22 @@ class PubsubAdmin(object):
|
|||
def _sendCb(self, iq_result):
|
||||
publish_elt = iq_result.admin.pubsub.publish
|
||||
ids = []
|
||||
for item_elt in publish_elt.elements(pubsub.NS_PUBSUB, u'item'):
|
||||
ids.append(item_elt[u'id'])
|
||||
for item_elt in publish_elt.elements(pubsub.NS_PUBSUB, 'item'):
|
||||
ids.append(item_elt['id'])
|
||||
return ids
|
||||
|
||||
def publish(self, client, service, nodeIdentifier, items, extra=None):
|
||||
for item in items:
|
||||
if item.name != u'item' or item.uri != pubsub.NS_PUBSUB:
|
||||
if item.name != 'item' or item.uri != pubsub.NS_PUBSUB:
|
||||
raise exceptions.DataError(
|
||||
u'Invalid element, a pubsub item is expected: {xml}'.format(
|
||||
'Invalid element, a pubsub item is expected: {xml}'.format(
|
||||
xml=item.toXml()))
|
||||
iq_elt = client.IQ()
|
||||
iq_elt['to'] = service.full() if service else client.jid.userhost()
|
||||
admin_elt = iq_elt.addElement((NS_PUBSUB_ADMIN, u'admin'))
|
||||
pubsub_elt = admin_elt.addElement((pubsub.NS_PUBSUB, u'pubsub'))
|
||||
admin_elt = iq_elt.addElement((NS_PUBSUB_ADMIN, 'admin'))
|
||||
pubsub_elt = admin_elt.addElement((pubsub.NS_PUBSUB, 'pubsub'))
|
||||
publish_elt = pubsub_elt.addElement('publish')
|
||||
publish_elt[u'node'] = nodeIdentifier
|
||||
publish_elt['node'] = nodeIdentifier
|
||||
for item in items:
|
||||
publish_elt.addChild(item)
|
||||
d = iq_elt.send()
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# SAT plugin for Pubsub Hooks
|
||||
|
|
@ -43,17 +43,17 @@ PLUGIN_INFO = {
|
|||
}
|
||||
|
||||
# python module
|
||||
HOOK_TYPE_PYTHON = u"python"
|
||||
HOOK_TYPE_PYTHON = "python"
|
||||
# python file path
|
||||
HOOK_TYPE_PYTHON_FILE = u"python_file"
|
||||
HOOK_TYPE_PYTHON_FILE = "python_file"
|
||||
# python code directly
|
||||
HOOK_TYPE_PYTHON_CODE = u"python_code"
|
||||
HOOK_TYPE_PYTHON_CODE = "python_code"
|
||||
HOOK_TYPES = (HOOK_TYPE_PYTHON, HOOK_TYPE_PYTHON_FILE, HOOK_TYPE_PYTHON_CODE)
|
||||
|
||||
|
||||
class PubsubHook(object):
|
||||
def __init__(self, host):
|
||||
log.info(_(u"PubSub Hook initialization"))
|
||||
log.info(_("PubSub Hook initialization"))
|
||||
self.host = host
|
||||
self.node_hooks = {} # keep track of the number of hooks per node (for all profiles)
|
||||
host.bridge.addMethod(
|
||||
|
|
@ -90,7 +90,7 @@ class PubsubHook(object):
|
|||
|
||||
def _installNodeManager(self, client, node):
|
||||
if node in self.node_hooks:
|
||||
log.debug(_(u"node manager already set for {node}").format(node=node))
|
||||
log.debug(_("node manager already set for {node}").format(node=node))
|
||||
self.node_hooks[node] += 1
|
||||
else:
|
||||
# first hook on this node
|
||||
|
|
@ -98,29 +98,29 @@ class PubsubHook(object):
|
|||
node, items_cb=self._itemsReceived
|
||||
)
|
||||
self.node_hooks[node] = 0
|
||||
log.info(_(u"node manager installed on {node}").format(node=node))
|
||||
log.info(_("node manager installed on {node}").format(node=node))
|
||||
|
||||
def _removeNodeManager(self, client, node):
|
||||
try:
|
||||
self.node_hooks[node] -= 1
|
||||
except KeyError:
|
||||
log.error(_(u"trying to remove a {node} without hook").format(node=node))
|
||||
log.error(_("trying to remove a {node} without hook").format(node=node))
|
||||
else:
|
||||
if self.node_hooks[node] == 0:
|
||||
del self.node_hooks[node]
|
||||
self.host.plugins["XEP-0060"].removeManagedNode(node, self._itemsReceived)
|
||||
log.debug(_(u"hook removed"))
|
||||
log.debug(_("hook removed"))
|
||||
else:
|
||||
log.debug(_(u"node still needed for an other hook"))
|
||||
log.debug(_("node still needed for an other hook"))
|
||||
|
||||
def installHook(self, client, service, node, hook_type, hook_arg, persistent):
|
||||
if hook_type not in HOOK_TYPES:
|
||||
raise exceptions.DataError(
|
||||
_(u"{hook_type} is not handled").format(hook_type=hook_type)
|
||||
_("{hook_type} is not handled").format(hook_type=hook_type)
|
||||
)
|
||||
if hook_type != HOOK_TYPE_PYTHON_FILE:
|
||||
raise NotImplementedError(
|
||||
_(u"{hook_type} hook type not implemented yet").format(
|
||||
_("{hook_type} hook type not implemented yet").format(
|
||||
hook_type=hook_type
|
||||
)
|
||||
)
|
||||
|
|
@ -136,8 +136,8 @@ class PubsubHook(object):
|
|||
hooks_list.append(hook_data)
|
||||
|
||||
log.info(
|
||||
_(u"{persistent} hook installed on {node} for {profile}").format(
|
||||
persistent=_(u"persistent") if persistent else _(u"temporary"),
|
||||
_("{persistent} hook installed on {node} for {profile}").format(
|
||||
persistent=_("persistent") if persistent else _("temporary"),
|
||||
node=node,
|
||||
profile=client.profile,
|
||||
)
|
||||
|
|
@ -160,18 +160,18 @@ class PubsubHook(object):
|
|||
try:
|
||||
if hook_type == HOOK_TYPE_PYTHON_FILE:
|
||||
hook_globals = {}
|
||||
execfile(hook_data["arg"], hook_globals)
|
||||
exec(compile(open(hook_data["arg"], "rb").read(), hook_data["arg"], 'exec'), hook_globals)
|
||||
callback = hook_globals["hook"]
|
||||
else:
|
||||
raise NotImplementedError(
|
||||
_(u"{hook_type} hook type not implemented yet").format(
|
||||
_("{hook_type} hook type not implemented yet").format(
|
||||
hook_type=hook_type
|
||||
)
|
||||
)
|
||||
except Exception as e:
|
||||
log.warning(
|
||||
_(
|
||||
u"Can't load Pubsub hook at node {node}, it will be removed: {reason}"
|
||||
"Can't load Pubsub hook at node {node}, it will be removed: {reason}"
|
||||
).format(node=node, reason=e)
|
||||
)
|
||||
hooks_list.remove(hook_data)
|
||||
|
|
@ -183,7 +183,7 @@ class PubsubHook(object):
|
|||
except Exception as e:
|
||||
log.warning(
|
||||
_(
|
||||
u"Error while running Pubsub hook for node {node}: {msg}"
|
||||
"Error while running Pubsub hook for node {node}: {msg}"
|
||||
).format(node=node, msg=e)
|
||||
)
|
||||
|
||||
|
|
@ -193,9 +193,9 @@ class PubsubHook(object):
|
|||
return self.addHook(
|
||||
client,
|
||||
service,
|
||||
unicode(node),
|
||||
unicode(hook_type),
|
||||
unicode(hook_arg),
|
||||
str(node),
|
||||
str(hook_type),
|
||||
str(hook_arg),
|
||||
persistent,
|
||||
)
|
||||
|
||||
|
|
@ -242,11 +242,11 @@ class PubsubHook(object):
|
|||
if node in hooks:
|
||||
for hook_data in hooks[node]:
|
||||
if (
|
||||
service != hook_data[u"service"]
|
||||
service != hook_data["service"]
|
||||
or hook_type is not None
|
||||
and hook_type != hook_data[u"type"]
|
||||
and hook_type != hook_data["type"]
|
||||
or hook_arg is not None
|
||||
and hook_arg != hook_data[u"arg"]
|
||||
and hook_arg != hook_data["arg"]
|
||||
):
|
||||
continue
|
||||
hooks[node].remove(hook_data)
|
||||
|
|
@ -263,8 +263,8 @@ class PubsubHook(object):
|
|||
def _listHooks(self, profile):
|
||||
hooks_list = self.listHooks(self.host.getClient(profile))
|
||||
for hook in hooks_list:
|
||||
hook[u"service"] = hook[u"service"].full()
|
||||
hook[u"persistent"] = C.boolConst(hook[u"persistent"])
|
||||
hook["service"] = hook["service"].full()
|
||||
hook["persistent"] = C.boolConst(hook["persistent"])
|
||||
return hooks_list
|
||||
|
||||
def listHooks(self, client):
|
||||
|
|
@ -272,15 +272,15 @@ class PubsubHook(object):
|
|||
hooks_list = []
|
||||
for hooks in (client._hooks, client._hooks_temporary):
|
||||
persistent = hooks is client._hooks
|
||||
for node, hooks_data in hooks.iteritems():
|
||||
for node, hooks_data in hooks.items():
|
||||
for hook_data in hooks_data:
|
||||
hooks_list.append(
|
||||
{
|
||||
u"service": hook_data[u"service"],
|
||||
u"node": node,
|
||||
u"type": hook_data[u"type"],
|
||||
u"arg": hook_data[u"arg"],
|
||||
u"persistent": persistent,
|
||||
"service": hook_data["service"],
|
||||
"node": node,
|
||||
"type": hook_data["type"],
|
||||
"arg": hook_data["arg"],
|
||||
"persistent": persistent,
|
||||
}
|
||||
)
|
||||
return hooks_list
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# SàT plugin for generic data import handling
|
||||
|
|
@ -38,7 +38,7 @@ PLUGIN_INFO = {
|
|||
C.PI_DEPENDENCIES: [],
|
||||
C.PI_MAIN: "ImportPlugin",
|
||||
C.PI_HANDLER: "no",
|
||||
C.PI_DESCRIPTION: _(u"""Generic import plugin, base for specialized importers"""),
|
||||
C.PI_DESCRIPTION: _("""Generic import plugin, base for specialized importers"""),
|
||||
}
|
||||
|
||||
Importer = collections.namedtuple("Importer", ("callback", "short_desc", "long_desc"))
|
||||
|
|
@ -64,7 +64,7 @@ class ImportPlugin(object):
|
|||
@param name(unicode): import handler name
|
||||
"""
|
||||
assert name == name.lower().strip()
|
||||
log.info(_(u"initializing {name} import handler").format(name=name))
|
||||
log.info(_("initializing {name} import handler").format(name=name))
|
||||
import_handler.name = name
|
||||
import_handler.register = partial(self.register, import_handler)
|
||||
import_handler.unregister = partial(self.unregister, import_handler)
|
||||
|
|
@ -93,7 +93,7 @@ class ImportPlugin(object):
|
|||
in_sign="ssa{ss}sss",
|
||||
out_sign="s",
|
||||
method=_import,
|
||||
async=True,
|
||||
async_=True,
|
||||
)
|
||||
self.host.bridge.addMethod(
|
||||
name + "ImportList",
|
||||
|
|
@ -115,7 +115,7 @@ class ImportPlugin(object):
|
|||
return client._import[import_handler.name][progress_id]
|
||||
|
||||
def listImporters(self, import_handler):
|
||||
importers = import_handler.importers.keys()
|
||||
importers = list(import_handler.importers.keys())
|
||||
importers.sort()
|
||||
return [
|
||||
(name, import_handler.importers[name].short_desc)
|
||||
|
|
@ -132,7 +132,7 @@ class ImportPlugin(object):
|
|||
importer = import_handler.importers[name]
|
||||
except KeyError:
|
||||
raise exceptions.NotFound(
|
||||
u"{handler_name} importer not found [{name}]".format(
|
||||
"{handler_name} importer not found [{name}]".format(
|
||||
handler_name=import_handler.name, name=name
|
||||
)
|
||||
)
|
||||
|
|
@ -150,7 +150,7 @@ class ImportPlugin(object):
|
|||
profile=C.PROF_KEY_NONE,
|
||||
):
|
||||
client = self.host.getClient(profile)
|
||||
options = {key: unicode(value) for key, value in options.iteritems()}
|
||||
options = {key: str(value) for key, value in options.items()}
|
||||
for option in import_handler.BOOL_OPTIONS:
|
||||
try:
|
||||
options[option] = C.bool(options[option])
|
||||
|
|
@ -161,14 +161,14 @@ class ImportPlugin(object):
|
|||
options[option] = json.loads(options[option])
|
||||
except ValueError:
|
||||
raise exceptions.DataError(
|
||||
_(u"invalid json option: {name}").format(name=option)
|
||||
_("invalid json option: {name}").format(name=option)
|
||||
)
|
||||
pubsub_service = jid.JID(pubsub_service) if pubsub_service else None
|
||||
return self.doImport(
|
||||
client,
|
||||
import_handler,
|
||||
unicode(name),
|
||||
unicode(location),
|
||||
str(name),
|
||||
str(location),
|
||||
options,
|
||||
pubsub_service,
|
||||
pubsub_node or None,
|
||||
|
|
@ -202,7 +202,7 @@ class ImportPlugin(object):
|
|||
if options is None:
|
||||
options = {}
|
||||
else:
|
||||
for opt_name, opt_default in import_handler.OPT_DEFAULTS.iteritems():
|
||||
for opt_name, opt_default in import_handler.OPT_DEFAULTS.items():
|
||||
# we want a filled options dict, with all empty or False values removed
|
||||
try:
|
||||
value = options[opt_name]
|
||||
|
|
@ -216,21 +216,21 @@ class ImportPlugin(object):
|
|||
try:
|
||||
importer = import_handler.importers[name]
|
||||
except KeyError:
|
||||
raise exceptions.NotFound(u"Importer [{}] not found".format(name))
|
||||
raise exceptions.NotFound("Importer [{}] not found".format(name))
|
||||
items_import_data, items_count = yield importer.callback(
|
||||
client, location, options
|
||||
)
|
||||
progress_id = unicode(uuid.uuid4())
|
||||
progress_id = str(uuid.uuid4())
|
||||
try:
|
||||
_import = client._import
|
||||
except AttributeError:
|
||||
_import = client._import = {}
|
||||
progress_data = _import.setdefault(import_handler.name, {})
|
||||
progress_data[progress_id] = {u"position": "0"}
|
||||
progress_data[progress_id] = {"position": "0"}
|
||||
if items_count is not None:
|
||||
progress_data[progress_id]["size"] = unicode(items_count)
|
||||
progress_data[progress_id]["size"] = str(items_count)
|
||||
metadata = {
|
||||
"name": u"{}: {}".format(name, location),
|
||||
"name": "{}: {}".format(name, location),
|
||||
"direction": "out",
|
||||
"type": import_handler.name.upper() + "_IMPORT",
|
||||
}
|
||||
|
|
@ -242,8 +242,8 @@ class ImportPlugin(object):
|
|||
)
|
||||
self.host.bridge.progressStarted(progress_id, metadata, client.profile)
|
||||
session = { # session data, can be used by importers
|
||||
u"root_service": pubsub_service,
|
||||
u"root_node": pubsub_node,
|
||||
"root_service": pubsub_service,
|
||||
"root_node": pubsub_node,
|
||||
}
|
||||
self.recursiveImport(
|
||||
client,
|
||||
|
|
@ -306,11 +306,11 @@ class ImportPlugin(object):
|
|||
recurse_kwargs.setdefault("options", options)
|
||||
recurse_kwargs["return_data"] = return_data
|
||||
recurse_kwargs["depth"] = depth + 1
|
||||
log.debug(_(u"uploading subitems"))
|
||||
log.debug(_("uploading subitems"))
|
||||
yield self.recursiveImport(**recurse_kwargs)
|
||||
|
||||
if depth == 0:
|
||||
client._import[import_handler.name][progress_id]["position"] = unicode(
|
||||
client._import[import_handler.name][progress_id]["position"] = str(
|
||||
idx + 1
|
||||
)
|
||||
|
||||
|
|
@ -338,7 +338,7 @@ class ImportPlugin(object):
|
|||
if name in import_handler.importers:
|
||||
raise exceptions.ConflictError(
|
||||
_(
|
||||
u"An {handler_name} importer with the name {name} already exist"
|
||||
"An {handler_name} importer with the name {name} already exist"
|
||||
).format(handler_name=import_handler.name, name=name)
|
||||
)
|
||||
import_handler.importers[name] = Importer(callback, short_desc, long_desc)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# SàT plugin for import external blogs
|
||||
|
|
@ -35,16 +35,16 @@ PLUGIN_INFO = {
|
|||
C.PI_DEPENDENCIES: ["MERGE_REQUESTS"],
|
||||
C.PI_MAIN: "MercurialHandler",
|
||||
C.PI_HANDLER: "no",
|
||||
C.PI_DESCRIPTION: _(u"""Merge request handler for Mercurial""")
|
||||
C.PI_DESCRIPTION: _("""Merge request handler for Mercurial""")
|
||||
}
|
||||
|
||||
SHORT_DESC = D_(u"handle Mercurial repository")
|
||||
CLEAN_RE = re.compile(ur'[^\w -._]', flags=re.UNICODE)
|
||||
SHORT_DESC = D_("handle Mercurial repository")
|
||||
CLEAN_RE = re.compile(r'[^\w -._]', flags=re.UNICODE)
|
||||
|
||||
|
||||
class MercurialProtocol(async_process.CommandProtocol):
|
||||
"""handle hg commands"""
|
||||
name = u"Mercurial"
|
||||
name = "Mercurial"
|
||||
command = None
|
||||
|
||||
@classmethod
|
||||
|
|
@ -54,7 +54,7 @@ class MercurialProtocol(async_process.CommandProtocol):
|
|||
@param path(unicode): path to the repository
|
||||
@param command(unicode): hg command to run
|
||||
"""
|
||||
assert u"path" not in kwargs
|
||||
assert "path" not in kwargs
|
||||
kwargs["path"] = path
|
||||
# FIXME: we have to use this workaround because Twisted's protocol.ProcessProtocol
|
||||
# is not using new style classes. This can be removed once moved to
|
||||
|
|
@ -65,15 +65,15 @@ class MercurialProtocol(async_process.CommandProtocol):
|
|||
|
||||
|
||||
class MercurialHandler(object):
|
||||
data_types = (u'mercurial_changeset',)
|
||||
data_types = ('mercurial_changeset',)
|
||||
|
||||
def __init__(self, host):
|
||||
log.info(_(u"Mercurial merge request handler initialization"))
|
||||
log.info(_("Mercurial merge request handler initialization"))
|
||||
try:
|
||||
MercurialProtocol.command = which('hg')[0]
|
||||
except IndexError:
|
||||
raise exceptions.NotFound(_(u"Mercurial executable (hg) not found, "
|
||||
u"can't use Mercurial handler"))
|
||||
raise exceptions.NotFound(_("Mercurial executable (hg) not found, "
|
||||
"can't use Mercurial handler"))
|
||||
self.host = host
|
||||
self._m = host.plugins['MERGE_REQUESTS']
|
||||
self._m.register('mercurial', self, self.data_types, SHORT_DESC)
|
||||
|
|
@ -92,11 +92,11 @@ class MercurialHandler(object):
|
|||
def import_(self, repository, data, data_type, item_id, service, node, extra):
|
||||
parsed_data = self.parse(data)
|
||||
try:
|
||||
parsed_name = parsed_data[0][u'commit_msg'].split(u'\n')[0]
|
||||
parsed_name = CLEAN_RE.sub(u'', parsed_name)[:40]
|
||||
parsed_name = parsed_data[0]['commit_msg'].split('\n')[0]
|
||||
parsed_name = CLEAN_RE.sub('', parsed_name)[:40]
|
||||
except Exception:
|
||||
parsed_name = u''
|
||||
name = u'mr_{item_id}_{parsed_name}'.format(item_id=CLEAN_RE.sub(u'', item_id),
|
||||
parsed_name = ''
|
||||
name = 'mr_{item_id}_{parsed_name}'.format(item_id=CLEAN_RE.sub('', item_id),
|
||||
parsed_name=parsed_name)
|
||||
return MercurialProtocol.run(repository, 'qimport', '-g', '--name', name,
|
||||
'--encoding=utf-8', '-', stdin=data)
|
||||
|
|
@ -111,51 +111,51 @@ class MercurialHandler(object):
|
|||
diff = []
|
||||
state = 'init'
|
||||
if lines[0] != '# HG changeset patch':
|
||||
raise exceptions.DataError(_(u'invalid changeset signature'))
|
||||
raise exceptions.DataError(_('invalid changeset signature'))
|
||||
# line index of this patch in the whole data
|
||||
patch_idx = total_lines - len(lines)
|
||||
del lines[0]
|
||||
|
||||
for idx, line in enumerate(lines):
|
||||
if state == 'init':
|
||||
if line.startswith(u'# '):
|
||||
if line.startswith(u'# User '):
|
||||
if line.startswith('# '):
|
||||
if line.startswith('# User '):
|
||||
elems = line[7:].split()
|
||||
if not elems:
|
||||
continue
|
||||
last = elems[-1]
|
||||
if (last.startswith(u'<') and last.endswith(u'>')
|
||||
and u'@' in last):
|
||||
if (last.startswith('<') and last.endswith('>')
|
||||
and '@' in last):
|
||||
patch[self._m.META_EMAIL] = elems.pop()[1:-1]
|
||||
patch[self._m.META_AUTHOR] = u' '.join(elems)
|
||||
elif line.startswith(u'# Date '):
|
||||
patch[self._m.META_AUTHOR] = ' '.join(elems)
|
||||
elif line.startswith('# Date '):
|
||||
time_data = line[7:].split()
|
||||
if len(time_data) != 2:
|
||||
log.warning(_(u'unexpected time data: {data}')
|
||||
log.warning(_('unexpected time data: {data}')
|
||||
.format(data=line[7:]))
|
||||
continue
|
||||
patch[self._m.META_TIMESTAMP] = (int(time_data[0])
|
||||
+ int(time_data[1]))
|
||||
elif line.startswith(u'# Node ID '):
|
||||
elif line.startswith('# Node ID '):
|
||||
patch[self._m.META_HASH] = line[10:]
|
||||
elif line.startswith(u'# Parent '):
|
||||
elif line.startswith('# Parent '):
|
||||
patch[self._m.META_PARENT_HASH] = line[10:]
|
||||
else:
|
||||
state = 'commit_msg'
|
||||
if state == 'commit_msg':
|
||||
if line.startswith(u'diff --git a/'):
|
||||
if line.startswith('diff --git a/'):
|
||||
state = 'diff'
|
||||
patch[self._m.META_DIFF_IDX] = patch_idx + idx + 1
|
||||
else:
|
||||
commit_msg.append(line)
|
||||
if state == 'diff':
|
||||
if line.startswith(u'# ') or idx == len(lines)-1:
|
||||
if line.startswith('# ') or idx == len(lines)-1:
|
||||
# a new patch is starting or we have reached end of patches
|
||||
if idx == len(lines)-1:
|
||||
# end of patches, we need to keep the line
|
||||
diff.append(line)
|
||||
patch[self._m.META_COMMIT_MSG] = u'\n'.join(commit_msg)
|
||||
patch[self._m.META_DIFF] = u'\n'.join(diff)
|
||||
patch[self._m.META_COMMIT_MSG] = '\n'.join(commit_msg)
|
||||
patch[self._m.META_DIFF] = '\n'.join(diff)
|
||||
patches.append(patch)
|
||||
if idx == len(lines)-1:
|
||||
del lines[:]
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue