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:
Goffi 2019-08-13 19:08:41 +02:00
commit fbfe96c505
100 changed files with 6336 additions and 6398 deletions

View file

@ -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))

View file

@ -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

View file

@ -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):

View file

@ -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)

View file

@ -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

View file

@ -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))

View file

@ -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,

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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
)

View file

@ -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

View file

@ -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

View file

@ -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,
)

View file

@ -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()

View file

@ -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

View file

@ -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)]

View file

@ -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)

View file

@ -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