changeset 1113:cdd389ef97bc

server: code style reformatting using black
author Goffi <goffi@goffi.org>
date Fri, 29 Jun 2018 17:45:26 +0200
parents f287fc8bb31a
children 1957d02df281
files src/common/constants.py src/pages/app/page_meta.py src/pages/blog/page_meta.py src/pages/blog/view/atom.xml/page_meta.py src/pages/chat/page_meta.py src/pages/chat/select/page_meta.py src/pages/events/admin/page_meta.py src/pages/events/new/page_meta.py src/pages/events/page_meta.py src/pages/events/rsvp/page_meta.py src/pages/events/view/page_meta.py src/pages/files/list/page_meta.py src/pages/files/page_meta.py src/pages/files/view/page_meta.py src/pages/forums/list/page_meta.py src/pages/forums/topics/page_meta.py src/pages/forums/view/page_meta.py src/pages/g/e/page_meta.py src/pages/g/page_meta.py src/pages/login/logged/page_meta.py src/pages/login/page_meta.py src/pages/merge-requests/disco/page_meta.py src/pages/merge-requests/edit/page_meta.py src/pages/merge-requests/new/page_meta.py src/pages/merge-requests/page_meta.py src/pages/merge-requests/view/page_meta.py src/pages/photos/album/page_meta.py src/pages/photos/page_meta.py src/pages/register/page_meta.py src/pages/tickets/disco/page_meta.py src/pages/tickets/edit/page_meta.py src/pages/tickets/new/page_meta.py src/pages/tickets/page_meta.py src/pages/tickets/view/page_meta.py src/pages/u/atom.xml/page_meta.py src/pages/u/blog/page_meta.py src/pages/u/page_meta.py src/server/blog.py src/server/constants.py src/server/html_tools.py src/server/pages.py src/server/pages_tools.py src/server/server.py src/server/session_iface.py src/server/utils.py src/server/websockets.py src/twisted/plugins/libervia_server.py
diffstat 47 files changed, 2557 insertions(+), 1463 deletions(-) [+]
line wrap: on
line diff
--- a/src/common/constants.py	Sun Jun 24 22:21:25 2018 +0200
+++ b/src/common/constants.py	Fri Jun 29 17:45:26 2018 +0200
@@ -25,33 +25,33 @@
 
     # XXX: we don't want to use the APP_VERSION inherited from sat.core.constants version
     #      as we use this version to check that there is not a mismatch with backend
-    APP_VERSION = u'0.7.0D'  # Please add 'D' at the end for dev versions
+    APP_VERSION = u"0.7.0D"  # Please add 'D' at the end for dev versions
     LIBERVIA_MAIN_PAGE = "libervia.html"
 
     # REGISTRATION
     # XXX: for now libervia forces the creation to lower case
     # XXX: Regex patterns must be compatible with both Python and JS
-    REG_LOGIN_RE = r'^[a-z0-9_-]+$'
-    REG_EMAIL_RE = r'^.+@.+\..+'
+    REG_LOGIN_RE = r"^[a-z0-9_-]+$"
+    REG_EMAIL_RE = r"^.+@.+\..+"
     PASSWORD_MIN_LENGTH = 6
 
     # HTTP REQUEST RESULT VALUES
-    PROFILE_AUTH_ERROR = 'PROFILE AUTH ERROR'
-    XMPP_AUTH_ERROR = 'XMPP AUTH ERROR'
-    ALREADY_WAITING = 'ALREADY WAITING'
-    SESSION_ACTIVE = 'SESSION ACTIVE'
-    NOT_CONNECTED = 'NOT CONNECTED'
-    PROFILE_LOGGED = 'LOGGED'
-    PROFILE_LOGGED_EXT_JID = 'LOGGED (REGISTERED WITH EXTERNAL JID)'
-    ALREADY_EXISTS = 'ALREADY EXISTS'
-    REGISTRATION_SUCCEED = 'REGISTRATION'
-    INTERNAL_ERROR = 'INTERNAL ERROR'
-    INVALID_INPUT = 'INVALID INPUT'
-    BAD_REQUEST = 'BAD REQUEST'
-    NO_REPLY = 'NO REPLY'
-    NOT_ALLOWED = 'NOT ALLOWED'
-    UPLOAD_OK = 'UPLOAD OK'
-    UPLOAD_KO = 'UPLOAD KO'
+    PROFILE_AUTH_ERROR = "PROFILE AUTH ERROR"
+    XMPP_AUTH_ERROR = "XMPP AUTH ERROR"
+    ALREADY_WAITING = "ALREADY WAITING"
+    SESSION_ACTIVE = "SESSION ACTIVE"
+    NOT_CONNECTED = "NOT CONNECTED"
+    PROFILE_LOGGED = "LOGGED"
+    PROFILE_LOGGED_EXT_JID = "LOGGED (REGISTERED WITH EXTERNAL JID)"
+    ALREADY_EXISTS = "ALREADY EXISTS"
+    REGISTRATION_SUCCEED = "REGISTRATION"
+    INTERNAL_ERROR = "INTERNAL ERROR"
+    INVALID_INPUT = "INVALID INPUT"
+    BAD_REQUEST = "BAD REQUEST"
+    NO_REPLY = "NO REPLY"
+    NOT_ALLOWED = "NOT ALLOWED"
+    UPLOAD_OK = "UPLOAD OK"
+    UPLOAD_KO = "UPLOAD KO"
 
     # directories
     MEDIA_DIR = "media/"
@@ -64,4 +64,4 @@
     EMPTY_AVATAR_URL = os.path.join(MEDIA_DIR, "misc", EMPTY_AVATAR_FILE)
 
     # blog
-    MAM_FILTER_CATEGORY = 'http://salut-a-toi.org/protocols/mam_filter_category'
+    MAM_FILTER_CATEGORY = "http://salut-a-toi.org/protocols/mam_filter_category"
--- a/src/pages/app/page_meta.py	Sun Jun 24 22:21:25 2018 +0200
+++ b/src/pages/app/page_meta.py	Fri Jun 29 17:45:26 2018 +0200
@@ -1,5 +1,5 @@
 #!/usr/bin/env python2.7
 # -*- coding: utf-8 -*-
 
-name = u'app'
+name = u"app"
 template = u"app/app.html"
--- a/src/pages/blog/page_meta.py	Sun Jun 24 22:21:25 2018 +0200
+++ b/src/pages/blog/page_meta.py	Fri Jun 29 17:45:26 2018 +0200
@@ -5,9 +5,10 @@
 from twisted.internet import defer
 from libervia.server import session_iface
 from sat.core.log import getLogger
-log = getLogger('pages/blog')
 
-name = u'blog'
+log = getLogger("pages/blog")
+
+name = u"blog"
 access = C.PAGES_ACCESS_PUBLIC
 template = u"blog/discover.html"
 
@@ -17,20 +18,39 @@
     profile = self.getProfile(request)
     template_data = request.template_data
     if profile is not None:
-        __, entities_own, entities_roster = yield self.host.bridgeCall('discoFindByFeatures', [], [(u'pubsub', u'pep')], True, False, True, True, True, profile)
-        entities = template_data[u'disco_entities'] = entities_own.keys() + entities_roster.keys()
-        entities_url = template_data[u'entities_url'] = {}
-        identities = template_data[u'identities'] = self.host.getSessionData(request, session_iface.ISATSession).identities
+        __, entities_own, entities_roster = yield self.host.bridgeCall(
+            "discoFindByFeatures",
+            [],
+            [(u"pubsub", u"pep")],
+            True,
+            False,
+            True,
+            True,
+            True,
+            profile,
+        )
+        entities = template_data[u"disco_entities"] = (
+            entities_own.keys() + entities_roster.keys()
+        )
+        entities_url = template_data[u"entities_url"] = {}
+        identities = template_data[u"identities"] = self.host.getSessionData(
+            request, session_iface.ISATSession
+        ).identities
         for entity_jid_s in entities:
-            entities_url[entity_jid_s] = self.getPageByName('blog_view').getURL(entity_jid_s)
+            entities_url[entity_jid_s] = self.getPageByName("blog_view").getURL(
+                entity_jid_s
+            )
             if entity_jid_s not in identities:
-                identities[entity_jid_s] = yield self.host.bridgeCall(u'identityGet', entity_jid_s, profile)
+                identities[entity_jid_s] = yield self.host.bridgeCall(
+                    u"identityGet", entity_jid_s, profile
+                )
+
 
 def on_data_post(self, request):
-    jid_str = self.getPostedData(request, u'jid')
+    jid_str = self.getPostedData(request, u"jid")
     try:
-       jid_ = jid.JID(jid_str)
+        jid_ = jid.JID(jid_str)
     except RuntimeError:
         self.pageError(request, C.HTTP_BAD_REQUEST)
-    url = self.getPageByName(u'blog_view').getURL(jid_.full())
+    url = self.getPageByName(u"blog_view").getURL(jid_.full())
     self.HTTPRedirect(request, url)
--- a/src/pages/blog/view/atom.xml/page_meta.py	Sun Jun 24 22:21:25 2018 +0200
+++ b/src/pages/blog/view/atom.xml/page_meta.py	Fri Jun 29 17:45:26 2018 +0200
@@ -13,24 +13,29 @@
 
 @defer.inlineCallbacks
 def prepare_render(self, request):
-    request.setHeader('Content-Type', 'application/atom+xml; charset=utf-8')
+    request.setHeader("Content-Type", "application/atom+xml; charset=utf-8")
     data = self.getRData(request)
-    service, node = data[u'service'], data.get(u'node')
-    self.checkCache(request, C.CACHE_PUBSUB, service=service, node=node, short='microblog')
-    data['show_comments'] = False
+    service, node = data[u"service"], data.get(u"node")
+    self.checkCache(
+        request, C.CACHE_PUBSUB, service=service, node=node, short="microblog"
+    )
+    data["show_comments"] = False
     template_data = request.template_data
-    blog_page = self.getPageByName(u'blog_view')
+    blog_page = self.getPageByName(u"blog_view")
     yield blog_page.prepare_render(self, request)
-    items = data[u'items']
+    items = data[u"items"]
 
-    template_data[u'request_uri'] = self.host.getExtBaseURL(request, request.path.decode('utf-8'))
-    template_data[u'xmpp_uri'] = uri.buildXMPPUri(u'pubsub',
-                                                 subtype=u'microblog',
-                                                 path=service.full(),
-                                                 node=node)
-    blog_view = self.getPageByName(u'blog_view')
-    template_data[u'http_uri'] = self.host.getExtBaseURL(request, blog_view.getURL(service.full(), node))
+    template_data[u"request_uri"] = self.host.getExtBaseURL(
+        request, request.path.decode("utf-8")
+    )
+    template_data[u"xmpp_uri"] = uri.buildXMPPUri(
+        u"pubsub", subtype=u"microblog", path=service.full(), node=node
+    )
+    blog_view = self.getPageByName(u"blog_view")
+    template_data[u"http_uri"] = self.host.getExtBaseURL(
+        request, blog_view.getURL(service.full(), node)
+    )
     if items:
-        template_data[u'updated'] = items[0].updated
+        template_data[u"updated"] = items[0].updated
     else:
-        template_data[u'updated'] = time.time()
+        template_data[u"updated"] = time.time()
--- a/src/pages/chat/page_meta.py	Sun Jun 24 22:21:25 2018 +0200
+++ b/src/pages/chat/page_meta.py	Fri Jun 29 17:45:26 2018 +0200
@@ -4,13 +4,14 @@
 from sat.core.i18n import _
 from twisted.internet import defer
 from sat.core.log import getLogger
-log = getLogger('pages/chat')
+
+log = getLogger("pages/chat")
 from sat.tools.common import data_objects
 from twisted.words.protocols.jabber import jid
 from libervia.server.constants import Const as C
 from libervia.server import session_iface
 
-name = u'chat'
+name = u"chat"
 access = C.PAGES_ACCESS_PROFILE
 template = u"chat/chat.html"
 dynamic = True
@@ -30,87 +31,115 @@
         if not target_jid.user:
             raise ValueError(_(u"invalid jid for chat (no local part)"))
     except Exception as e:
-        log.warning(_(u"bad chat jid entered: {jid} ({msg})").format(
-            jid=target_jid,
-            msg = e))
+        log.warning(
+            _(u"bad chat jid entered: {jid} ({msg})").format(jid=target_jid, msg=e)
+        )
         self.pageError(request, C.HTTP_BAD_REQUEST)
     else:
-        rdata['target'] = target_jid
+        rdata["target"] = target_jid
 
 
 @defer.inlineCallbacks
 def prepare_render(self, request):
-    # FIXME: bug on room filtering (currently display messages from all rooms)
+    #  FIXME: bug on room filtering (currently display messages from all rooms)
     session = self.host.getSessionData(request, session_iface.ISATSession)
     template_data = request.template_data
     rdata = self.getRData(request)
-    target_jid = rdata['target']
+    target_jid = rdata["target"]
     profile = session.profile
     profile_jid = session.jid
 
-    disco = yield self.host.bridgeCall(u"discoInfos", target_jid.host, u'', True, profile)
+    disco = yield self.host.bridgeCall(u"discoInfos", target_jid.host, u"", True, profile)
     if "conference" in [i[0] for i in disco[1]]:
         chat_type = C.CHAT_GROUP
-        join_ret = yield self.host.bridgeCall(u"mucJoin", target_jid.userhost(), "", "", profile)
+        join_ret = yield self.host.bridgeCall(
+            u"mucJoin", target_jid.userhost(), "", "", profile
+        )
         already_joined, room_jid_s, occupants, user_nick, room_subject, dummy = join_ret
-        template_data[u'subject'] = room_subject
+        template_data[u"subject"] = room_subject
         own_jid = jid.JID(room_jid_s)
         own_jid.resource = user_nick
     else:
         chat_type = C.CHAT_ONE2ONE
         own_jid = profile_jid
-    rdata['chat_type'] = chat_type
-    template_data['own_jid'] = own_jid
+    rdata["chat_type"] = chat_type
+    template_data["own_jid"] = own_jid
 
     self.registerSignal(request, u"messageNew")
-    history = yield self.host.bridgeCall(u'historyGet', profile_jid.userhost(), target_jid.userhost(), 20, True, {}, profile)
+    history = yield self.host.bridgeCall(
+        u"historyGet",
+        profile_jid.userhost(),
+        target_jid.userhost(),
+        20,
+        True,
+        {},
+        profile,
+    )
     authors = {m[2] for m in history}
     identities = {}
     for author in authors:
-        identities[author] = yield self.host.bridgeCall(u'identityGet', author, profile)
+        identities[author] = yield self.host.bridgeCall(u"identityGet", author, profile)
 
-    template_data[u'messages'] = data_objects.Messages(history)
-    template_data[u'identities'] = identities
-    template_data[u'target_jid'] = target_jid
-    template_data[u'chat_type'] = chat_type
+    template_data[u"messages"] = data_objects.Messages(history)
+    template_data[u"identities"] = identities
+    template_data[u"target_jid"] = target_jid
+    template_data[u"chat_type"] = chat_type
 
 
 def on_data(self, request, data):
     session = self.host.getSessionData(request, session_iface.ISATSession)
     rdata = self.getRData(request)
-    target = rdata['target']
-    data_type = data.get(u'type', '')
-    if data_type == 'msg':
-        message = data[u'body']
-        mess_type = C.MESS_TYPE_GROUPCHAT if rdata['chat_type'] == C.CHAT_GROUP else C.MESS_TYPE_CHAT
-        log.debug(u'message received: {}'.format(message))
-        self.host.bridgeCall(u'messageSend', target.full(), {u'': message}, {}, mess_type, {}, session.profile)
+    target = rdata["target"]
+    data_type = data.get(u"type", "")
+    if data_type == "msg":
+        message = data[u"body"]
+        mess_type = (
+            C.MESS_TYPE_GROUPCHAT
+            if rdata["chat_type"] == C.CHAT_GROUP
+            else C.MESS_TYPE_CHAT
+        )
+        log.debug(u"message received: {}".format(message))
+        self.host.bridgeCall(
+            u"messageSend",
+            target.full(),
+            {u"": message},
+            {},
+            mess_type,
+            {},
+            session.profile,
+        )
     else:
-        log.warning(u'unknown message type: {type}'.format(type=data_type))
+        log.warning(u"unknown message type: {type}".format(type=data_type))
 
 
 @defer.inlineCallbacks
 def on_signal(self, request, signal, *args):
-    if signal == 'messageNew':
+    if signal == "messageNew":
         rdata = self.getRData(request)
         template_data = request.template_data
         template_data_update = {u"msg": data_objects.Message((args))}
-        target_jid = rdata['target']
-        identities = template_data['identities']
-        uid, timestamp, from_jid_s, to_jid_s, message, subject, mess_type, extra, dummy = args
+        target_jid = rdata["target"]
+        identities = template_data["identities"]
+        uid, timestamp, from_jid_s, to_jid_s, message, subject, mess_type, extra, dummy = (
+            args
+        )
         from_jid = jid.JID(from_jid_s)
         to_jid = jid.JID(to_jid_s)
-        if (target_jid.userhostJID() != from_jid.userhostJID() and
-            target_jid.userhostJID() != to_jid.userhostJID()):
+        if (
+            target_jid.userhostJID() != from_jid.userhostJID()
+            and target_jid.userhostJID() != to_jid.userhostJID()
+        ):
             # the message is not linked with page's room/user
             return
 
-
         if from_jid_s not in identities:
             profile = self.getProfile(request)
-            identities[from_jid_s] = yield self.host.bridgeCall(u'identityGet', from_jid_s, profile)
-            template_data_update['identities'] = identities
-        self.renderAndUpdate(request, u"chat/message.html", "#messages",
-                             template_data_update)
+            identities[from_jid_s] = yield self.host.bridgeCall(
+                u"identityGet", from_jid_s, profile
+            )
+            template_data_update["identities"] = identities
+        self.renderAndUpdate(
+            request, u"chat/message.html", "#messages", template_data_update
+        )
     else:
         log.error(_(u"Unexpected signal: {signal}").format(signal=signal))
--- a/src/pages/chat/select/page_meta.py	Sun Jun 24 22:21:25 2018 +0200
+++ b/src/pages/chat/select/page_meta.py	Fri Jun 29 17:45:26 2018 +0200
@@ -7,9 +7,10 @@
 from twisted.words.protocols.jabber import jid
 from sat.tools.common import data_objects
 from sat.core.log import getLogger
-log = getLogger('pages/chat_select')
 
-name = u'chat_select'
+log = getLogger("pages/chat_select")
+
+name = u"chat_select"
 access = C.PAGES_ACCESS_PROFILE
 template = u"chat/select.html"
 
@@ -18,23 +19,21 @@
 def prepare_render(self, request):
     profile = self.getProfile(request)
     template_data = request.template_data
-    rooms = template_data['rooms'] = []
-    bookmarks = yield self.host.bridgeCall('bookmarksList', 'muc', 'all', profile)
+    rooms = template_data["rooms"] = []
+    bookmarks = yield self.host.bridgeCall("bookmarksList", "muc", "all", profile)
     for bm_values in bookmarks.values():
         for room_jid, room_data in bm_values.iteritems():
-            url = self.getPageByName(u'chat').getURL(room_jid)
-            rooms.append(data_objects.Room(room_jid,
-                                           name=room_data.get('name'),
-                                           url=url))
+            url = self.getPageByName(u"chat").getURL(room_jid)
+            rooms.append(data_objects.Room(room_jid, name=room_data.get("name"), url=url))
     rooms.sort(key=lambda r: r.name)
 
 
 @defer.inlineCallbacks
 def on_data_post(self, request):
-    jid_ = self.getPostedData(request, u'jid')
-    if u'@' not in jid_:
+    jid_ = self.getPostedData(request, u"jid")
+    if u"@" not in jid_:
         profile = self.getProfile(request)
-        service = yield self.host.bridgeCall('mucGetService', '', profile)
+        service = yield self.host.bridgeCall("mucGetService", "", profile)
         if service:
             muc_jid = jid.JID(service)
             muc_jid.user = jid_
@@ -42,5 +41,5 @@
         else:
             log.warning(_(u"Invalid jid received: {jid}".format(jid=jid_)))
             defer.returnValue(C.POST_NO_CONFIRM)
-    url = self.getPageByName(u'chat').getURL(jid_)
+    url = self.getPageByName(u"chat").getURL(jid_)
     self.HTTPRedirect(request, url)
--- a/src/pages/events/admin/page_meta.py	Sun Jun 24 22:21:25 2018 +0200
+++ b/src/pages/events/admin/page_meta.py	Fri Jun 29 17:45:26 2018 +0200
@@ -12,15 +12,22 @@
 import math
 import re
 
-name = u'event_admin'
+name = u"event_admin"
 access = C.PAGES_ACCESS_PROFILE
 template = u"event/admin.html"
-log = getLogger(u'pages/' + name)
+log = getLogger(u"pages/" + name)
 REG_EMAIL_RE = re.compile(C.REG_EMAIL_RE, re.IGNORECASE)
 
 
 def parse_url(self, request):
-    self.getPathArgs(request, ("event_service", "event_node", "event_id"), min_args=2, event_service="@jid", event_id='')
+    self.getPathArgs(
+        request,
+        ("event_service", "event_node", "event_id"),
+        min_args=2,
+        event_service="@jid",
+        event_id="",
+    )
+
 
 @defer.inlineCallbacks
 def prepare_render(self, request):
@@ -29,119 +36,138 @@
 
     ## Event ##
 
-    event_service = template_data[u'event_service'] = data['event_service']
-    event_node = template_data[u'event_node'] = data['event_node']
-    event_id = template_data[u'event_id'] = data['event_id']
+    event_service = template_data[u"event_service"] = data["event_service"]
+    event_node = template_data[u"event_node"] = data["event_node"]
+    event_id = template_data[u"event_id"] = data["event_id"]
     profile = self.getProfile(request)
-    event_timestamp, event_data = yield self.host.bridgeCall(u"eventGet", event_service.userhost() if event_service else '', event_node, event_id, profile)
+    event_timestamp, event_data = yield self.host.bridgeCall(
+        u"eventGet",
+        event_service.userhost() if event_service else "",
+        event_node,
+        event_id,
+        profile,
+    )
     try:
-        background_image = event_data.pop('background-image')
+        background_image = event_data.pop("background-image")
     except KeyError:
         pass
     else:
-        template_data['dynamic_style'] = safe(u"""
+        template_data["dynamic_style"] = safe(
+            u"""
             html {
                 background-image: url("%s");
                 background-size: 15em;
             }
-            """ % cgi.escape(background_image, True))
-    template_data['event'] = event_data
+            """
+            % cgi.escape(background_image, True)
+        )
+    template_data["event"] = event_data
     invitees = yield self.host.bridgeCall(
         u"eventInviteesList",
-        event_data['invitees_service'],
-        event_data['invitees_node'],
-        profile)
-    template_data['invitees'] = invitees
+        event_data["invitees_service"],
+        event_data["invitees_node"],
+        profile,
+    )
+    template_data["invitees"] = invitees
     invitees_guests = 0
     for invitee_data in invitees.itervalues():
-        if invitee_data.get('attend', 'no') == 'no':
+        if invitee_data.get("attend", "no") == "no":
             continue
         try:
-            invitees_guests += int(invitee_data.get('guests', 0))
+            invitees_guests += int(invitee_data.get("guests", 0))
         except ValueError:
-            log.warning(_(u"guests value is not valid: {invitee}").format(invitee=invitee_data))
-    template_data['invitees_guests'] = invitees_guests
-    template_data['days_left'] = int(math.ceil((event_timestamp - time.time()) / (60 * 60 * 24)))
+            log.warning(
+                _(u"guests value is not valid: {invitee}").format(invitee=invitee_data)
+            )
+    template_data["invitees_guests"] = invitees_guests
+    template_data["days_left"] = int(
+        math.ceil((event_timestamp - time.time()) / (60 * 60 * 24))
+    )
 
     ## Blog ##
 
-    data[u'service'] = jid.JID(event_data[u'blog_service'])
-    data[u'node'] = event_data[u'blog_node']
-    data[u'allow_commenting'] = u'simple'
+    data[u"service"] = jid.JID(event_data[u"blog_service"])
+    data[u"node"] = event_data[u"blog_node"]
+    data[u"allow_commenting"] = u"simple"
 
     # we now need blog items, using blog common page
     # this will fill the "items" template data
-    blog_page = self.getPageByName(u'blog_view')
+    blog_page = self.getPageByName(u"blog_view")
     yield blog_page.prepare_render(self, request)
 
+
 @defer.inlineCallbacks
 def on_data_post(self, request):
     profile = self.getProfile(request)
     if not profile:
         log.error(u"got post data without profile")
         self.pageError(request, C.HTTP_INTERNAL_ERROR)
-    type_ = self.getPostedData(request, 'type')
-    if type_ == 'blog':
+    type_ = self.getPostedData(request, "type")
+    if type_ == "blog":
         service, node, title, body, lang = self.getPostedData(
-            request, (u'service', u'node', u'title',  u'body', u'language'))
+            request, (u"service", u"node", u"title", u"body", u"language")
+        )
 
         if not body.strip():
             self.pageError(request, C.HTTP_BAD_REQUEST)
         data = {u"content": body}
         if title:
-            data[u'title'] = title
+            data[u"title"] = title
         if lang:
-            data[u'language'] = lang
+            data[u"language"] = lang
         try:
-            comments = bool(self.getPostedData(request, u'comments').strip())
+            comments = bool(self.getPostedData(request, u"comments").strip())
         except KeyError:
             pass
         else:
             if comments:
-                data[u'allow_comments'] = C.BOOL_TRUE
+                data[u"allow_comments"] = C.BOOL_TRUE
 
         try:
-            yield self.host.bridgeCall(u'mbSend', service, node, data, profile)
+            yield self.host.bridgeCall(u"mbSend", service, node, data, profile)
         except Exception as e:
             if u"forbidden" in unicode(e):
                 self.pageError(request, C.HTTP_UNAUTHORIZED)
             else:
                 raise e
-    elif type_ == 'event':
-        service, node, event_id, jids, emails = self.getPostedData(request, (u'service',
-                                                                             u'node',
-                                                                             u'event_id',
-                                                                             u'jids',
-                                                                             u'emails'))
+    elif type_ == "event":
+        service, node, event_id, jids, emails = self.getPostedData(
+            request, (u"service", u"node", u"event_id", u"jids", u"emails")
+        )
         for invitee_jid_s in jids.split():
             try:
                 invitee_jid = jid.JID(invitee_jid_s)
             except RuntimeError as e:
-                log.warning(_(u"this is not a valid jid: {jid}").format(jid=invitee_jid_s))
+                log.warning(
+                    _(u"this is not a valid jid: {jid}").format(jid=invitee_jid_s)
+                )
                 continue
-            yield self.host.bridgeCall('eventInvite', invitee_jid.userhost(),
-                                                      service,
-                                                      node,
-                                                      event_id,
-                                                      profile)
+            yield self.host.bridgeCall(
+                "eventInvite", invitee_jid.userhost(), service, node, event_id, profile
+            )
         for email_addr in emails.split():
             if not REG_EMAIL_RE.match(email_addr):
-                log.warning(_(u"this is not a valid email address: {email}").format(email=email_addr))
+                log.warning(
+                    _(u"this is not a valid email address: {email}").format(
+                        email=email_addr
+                    )
+                )
                 continue
-            yield self.host.bridgeCall('eventInviteByEmail',
-                                                      service,
-                                                      node,
-                                                      event_id,
-                                                      email_addr,
-                                                      {},
-                                                      u'',
-                                                      u'',
-                                                      u'',
-                                                      u'',
-                                                      u'',
-                                                      u'',
-                                                      profile)
-
+            yield self.host.bridgeCall(
+                "eventInviteByEmail",
+                service,
+                node,
+                event_id,
+                email_addr,
+                {},
+                u"",
+                u"",
+                u"",
+                u"",
+                u"",
+                u"",
+                profile,
+            )
 
     else:
         log.warning(_(u"Unhandled data type: {}").format(type_))
--- a/src/pages/events/new/page_meta.py	Sun Jun 24 22:21:25 2018 +0200
+++ b/src/pages/events/new/page_meta.py	Fri Jun 29 17:45:26 2018 +0200
@@ -5,38 +5,35 @@
 from twisted.internet import defer
 from sat.core.log import getLogger
 from sat.tools.common import date_utils
+
 """creation of new events"""
 
-name = u'event_new'
+name = u"event_new"
 access = C.PAGES_ACCESS_PROFILE
 template = u"event/create.html"
-log = getLogger(u'pages/' + name)
+log = getLogger(u"pages/" + name)
+
 
 @defer.inlineCallbacks
 def on_data_post(self, request):
     request_data = self.getRData(request)
     profile = self.getProfile(request)
     title, location, body, date, main_img, bg_img = self.getPostedData(
-        request, ('name', 'location', 'body', 'date', 'main_image', 'bg_image'))
+        request, ("name", "location", "body", "date", "main_image", "bg_image")
+    )
     timestamp = date_utils.date_parse(date)
-    data = {'name': title,
-            'description': body,
-            'location': location,
-            }
+    data = {"name": title, "description": body, "location": location}
 
-    for value, var in ((main_img, 'image'),
-                       (bg_img, 'background-image')):
+    for value, var in ((main_img, "image"), (bg_img, "background-image")):
         value = value.strip()
         if not value:
             continue
-        if not value.startswith('http'):
+        if not value.startswith("http"):
             self.pageError(request, C.HTTP_BAD_REQUEST)
         data[var] = value
-    data[u'register'] = C.BOOL_TRUE
-    node = yield self.host.bridgeCall('eventCreate', timestamp, data, '', '', '', profile)
+    data[u"register"] = C.BOOL_TRUE
+    node = yield self.host.bridgeCall("eventCreate", timestamp, data, "", "", "", profile)
     log.info(u"Event node created at {node}".format(node=node))
 
-    request_data['post_redirect_page'] = (self.getPageByName(u'event_admin'),
-                                          '@',
-                                          node)
+    request_data["post_redirect_page"] = (self.getPageByName(u"event_admin"), "@", node)
     defer.returnValue(C.POST_NO_CONFIRM)
--- a/src/pages/events/page_meta.py	Sun Jun 24 22:21:25 2018 +0200
+++ b/src/pages/events/page_meta.py	Fri Jun 29 17:45:26 2018 +0200
@@ -5,10 +5,11 @@
 from twisted.internet import defer
 from sat.core.i18n import _
 from sat.core.log import getLogger
-log = getLogger('pages/ticket')
+
+log = getLogger("pages/ticket")
 """ticket handling pages"""
 
-name = u'events'
+name = u"events"
 access = C.PAGES_ACCESS_PUBLIC
 template = u"event/overview.html"
 
@@ -17,7 +18,7 @@
 def parse_url(self, request):
     profile = self.getProfile(request)
     template_data = request.template_data
-    template_data[u'url_event_new'] = self.getSubPageURL(request, 'event_new')
+    template_data[u"url_event_new"] = self.getSubPageURL(request, "event_new")
     if profile is not None:
         try:
             events = yield self.host.bridgeCall("eventsList", "", "", profile)
@@ -27,19 +28,23 @@
             own_events = []
             other_events = []
             for event in events:
-                if C.bool(event.get('creator', C.BOOL_FALSE)):
+                if C.bool(event.get("creator", C.BOOL_FALSE)):
                     own_events.append(event)
-                    event['url'] = self.getSubPageURL(request,
-                                                      u'event_admin',
-                                                      event.get('service',''),
-                                                      event.get('node',''),
-                                                      event.get('item'))
+                    event["url"] = self.getSubPageURL(
+                        request,
+                        u"event_admin",
+                        event.get("service", ""),
+                        event.get("node", ""),
+                        event.get("item"),
+                    )
                 else:
                     other_events.append(event)
-                    event['url'] = self.getSubPageURL(request,
-                                                      u'event_rsvp',
-                                                      event.get('service',''),
-                                                      event.get('node',''),
-                                                      event.get('item'))
+                    event["url"] = self.getSubPageURL(
+                        request,
+                        u"event_rsvp",
+                        event.get("service", ""),
+                        event.get("node", ""),
+                        event.get("item"),
+                    )
 
-            template_data[u'events'] = own_events + other_events
+            template_data[u"events"] = own_events + other_events
--- a/src/pages/events/rsvp/page_meta.py	Sun Jun 24 22:21:25 2018 +0200
+++ b/src/pages/events/rsvp/page_meta.py	Fri Jun 29 17:45:26 2018 +0200
@@ -9,16 +9,24 @@
 from sat.tools.common.template import safe
 import time
 import cgi
+
 """creation of new events"""
 
-name = u'event_rsvp'
+name = u"event_rsvp"
 access = C.PAGES_ACCESS_PROFILE
 template = u"event/invitation.html"
-log = getLogger(u'pages/' + name)
+log = getLogger(u"pages/" + name)
 
 
 def parse_url(self, request):
-    self.getPathArgs(request, ("event_service", "event_node", "event_id"), min_args=2, event_service="@jid", event_id='')
+    self.getPathArgs(
+        request,
+        ("event_service", "event_node", "event_id"),
+        min_args=2,
+        event_service="@jid",
+        event_id="",
+    )
+
 
 @defer.inlineCallbacks
 def prepare_render(self, request):
@@ -28,56 +36,64 @@
 
     ## Event ##
 
-    event_service = data['event_service']
-    event_node = data[u'event_node']
-    event_id = data[u'event_id']
-    event_timestamp, event_data = yield self.host.bridgeCall(u"eventGet",
-                                                             event_service.userhost() if event_service else '',
-                                                             event_node,
-                                                             event_id,
-                                                             profile)
+    event_service = data["event_service"]
+    event_node = data[u"event_node"]
+    event_id = data[u"event_id"]
+    event_timestamp, event_data = yield self.host.bridgeCall(
+        u"eventGet",
+        event_service.userhost() if event_service else "",
+        event_node,
+        event_id,
+        profile,
+    )
     try:
-        background_image = event_data.pop('background-image')
+        background_image = event_data.pop("background-image")
     except KeyError:
         pass
     else:
-        template_data['dynamic_style'] = safe(u"""
+        template_data["dynamic_style"] = safe(
+            u"""
             html {
                 background-image: url("%s");
                 background-size: 15em;
             }
-            """ % cgi.escape(background_image, True))
-    template_data['event'] = event_data
+            """
+            % cgi.escape(background_image, True)
+        )
+    template_data["event"] = event_data
     event_invitee_data = yield self.host.bridgeCall(
         u"eventInviteeGet",
-        event_data['invitees_service'],
-        event_data['invitees_node'],
-        profile)
-    template_data['invitee'] = event_invitee_data
-    template_data['days_left'] = int((event_timestamp - time.time()) / (60 * 60 * 24))
+        event_data["invitees_service"],
+        event_data["invitees_node"],
+        profile,
+    )
+    template_data["invitee"] = event_invitee_data
+    template_data["days_left"] = int((event_timestamp - time.time()) / (60 * 60 * 24))
 
     ## Blog ##
 
-    data[u'service'] = jid.JID(event_data[u'blog_service'])
-    data[u'node'] = event_data[u'blog_node']
-    data[u'allow_commenting'] = u'simple'
+    data[u"service"] = jid.JID(event_data[u"blog_service"])
+    data[u"node"] = event_data[u"blog_node"]
+    data[u"allow_commenting"] = u"simple"
 
     # we now need blog items, using blog common page
     # this will fill the "items" template data
-    blog_page = self.getPageByName(u'blog_view')
+    blog_page = self.getPageByName(u"blog_view")
     yield blog_page.prepare_render(self, request)
 
+
 @defer.inlineCallbacks
 def on_data_post(self, request):
-    type_ = self.getPostedData(request, u'type')
-    if type_ == u'comment':
-        blog_page = self.getPageByName(u'blog_view')
+    type_ = self.getPostedData(request, u"type")
+    if type_ == u"comment":
+        blog_page = self.getPageByName(u"blog_view")
         yield blog_page.on_data_post(self, request)
-    elif type_ == u'attendance':
+    elif type_ == u"attendance":
         profile = self.getProfile(request)
-        service, node, attend, guests = self.getPostedData(request, (u'service', u'node', u'attend', u'guests'))
-        data = {u'attend': attend,
-                u'guests': guests}
+        service, node, attend, guests = self.getPostedData(
+            request, (u"service", u"node", u"attend", u"guests")
+        )
+        data = {u"attend": attend, u"guests": guests}
         yield self.host.bridgeCall(u"eventInviteeSet", service, node, data, profile)
     else:
         log.warning(_(u"Unhandled data type: {}").format(type_))
--- a/src/pages/events/view/page_meta.py	Sun Jun 24 22:21:25 2018 +0200
+++ b/src/pages/events/view/page_meta.py	Fri Jun 29 17:45:26 2018 +0200
@@ -12,10 +12,10 @@
 import cgi
 from sat.core.log import getLogger
 
-name = u'event_view'
+name = u"event_view"
 access = C.PAGES_ACCESS_PROFILE
 template = u"event/invitation.html"
-log = getLogger(u'pages/' + name)
+log = getLogger(u"pages/" + name)
 
 
 @defer.inlineCallbacks
@@ -23,7 +23,7 @@
     template_data = request.template_data
     guest_session = self.host.getSessionData(request, session_iface.ISATGuestSession)
     try:
-        event_uri = guest_session.data['event_uri']
+        event_uri = guest_session.data["event_uri"]
     except KeyError:
         log.warning(_(u"event URI not found, can't render event page"))
         self.pageError(request, C.HTTP_SERVICE_UNAVAILABLE)
@@ -33,56 +33,64 @@
     ## Event ##
 
     event_uri_data = uri.parseXMPPUri(event_uri)
-    if event_uri_data[u'type'] != u'pubsub':
+    if event_uri_data[u"type"] != u"pubsub":
         self.pageError(request, C.HTTP_SERVICE_UNAVAILABLE)
 
-    event_service = template_data[u'event_service'] = jid.JID(event_uri_data[u'path'])
-    event_node = template_data[u'event_node'] = event_uri_data[u'node']
-    event_id = template_data[u'event_id'] = event_uri_data.get(u'item','')
+    event_service = template_data[u"event_service"] = jid.JID(event_uri_data[u"path"])
+    event_node = template_data[u"event_node"] = event_uri_data[u"node"]
+    event_id = template_data[u"event_id"] = event_uri_data.get(u"item", "")
     profile = self.getProfile(request)
-    event_timestamp, event_data = yield self.host.bridgeCall(u"eventGet", event_service.userhost(), event_node, event_id, profile)
+    event_timestamp, event_data = yield self.host.bridgeCall(
+        u"eventGet", event_service.userhost(), event_node, event_id, profile
+    )
     try:
-        background_image = event_data.pop('background-image')
+        background_image = event_data.pop("background-image")
     except KeyError:
         pass
     else:
-        template_data['dynamic_style'] = safe(u"""
+        template_data["dynamic_style"] = safe(
+            u"""
             html {
                 background-image: url("%s");
                 background-size: 15em;
             }
-            """ % cgi.escape(background_image, True))
-    template_data['event'] = event_data
+            """
+            % cgi.escape(background_image, True)
+        )
+    template_data["event"] = event_data
     event_invitee_data = yield self.host.bridgeCall(
         u"eventInviteeGet",
-        event_data['invitees_service'],
-        event_data['invitees_node'],
-        profile)
-    template_data['invitee'] = event_invitee_data
-    template_data['days_left'] = int((event_timestamp - time.time()) / (60 * 60 * 24))
+        event_data["invitees_service"],
+        event_data["invitees_node"],
+        profile,
+    )
+    template_data["invitee"] = event_invitee_data
+    template_data["days_left"] = int((event_timestamp - time.time()) / (60 * 60 * 24))
 
     ## Blog ##
 
-    data[u'service'] = jid.JID(event_data[u'blog_service'])
-    data[u'node'] = event_data[u'blog_node']
-    data[u'allow_commenting'] = u'simple'
+    data[u"service"] = jid.JID(event_data[u"blog_service"])
+    data[u"node"] = event_data[u"blog_node"]
+    data[u"allow_commenting"] = u"simple"
 
     # we now need blog items, using blog common page
     # this will fill the "items" template data
-    blog_page = self.getPageByName(u'blog_view')
+    blog_page = self.getPageByName(u"blog_view")
     yield blog_page.prepare_render(self, request)
 
+
 @defer.inlineCallbacks
 def on_data_post(self, request):
-    type_ = self.getPostedData(request, u'type')
-    if type_ == u'comment':
-        blog_page = self.getPageByName(u'blog_view')
+    type_ = self.getPostedData(request, u"type")
+    if type_ == u"comment":
+        blog_page = self.getPageByName(u"blog_view")
         yield blog_page.on_data_post(self, request)
-    elif type_ == u'attendance':
+    elif type_ == u"attendance":
         profile = self.getProfile(request)
-        service, node, attend, guests = self.getPostedData(request, (u'service', u'node', u'attend', u'guests'))
-        data = {u'attend': attend,
-                u'guests': guests}
+        service, node, attend, guests = self.getPostedData(
+            request, (u"service", u"node", u"attend", u"guests")
+        )
+        data = {u"attend": attend, u"guests": guests}
         yield self.host.bridgeCall(u"eventInviteeSet", service, node, data, profile)
     else:
         log.warning(_(u"Unhandled data type: {}").format(type_))
--- a/src/pages/files/list/page_meta.py	Sun Jun 24 22:21:25 2018 +0200
+++ b/src/pages/files/list/page_meta.py	Fri Jun 29 17:45:26 2018 +0200
@@ -10,86 +10,99 @@
 from sat.tools.common import uri
 import json
 import os
-log = getLogger('pages/files/list')
+
+log = getLogger("pages/files/list")
 """files handling pages"""
 
-name = u'files_list'
+name = u"files_list"
 access = C.PAGES_ACCESS_PROFILE
 template = u"file/overview.html"
 
+
 def parse_url(self, request):
-    self.getPathArgs(request, ['service', '*path'], min_args=1, service='jid', path='')
+    self.getPathArgs(request, ["service", "*path"], min_args=1, service="jid", path="")
+
 
 @defer.inlineCallbacks
 def prepare_render(self, request):
     data = self.getRData(request)
-    thumb_limit = data.get('thumb_limit', 300)
+    thumb_limit = data.get("thumb_limit", 300)
     template_data = request.template_data
-    service, path_elts = data[u'service'], data[u'path']
-    path = u'/'.join(path_elts)
+    service, path_elts = data[u"service"], data[u"path"]
+    path = u"/".join(path_elts)
     profile = self.getProfile(request) or C.SERVICE_PROFILE
 
-    files_data = yield self.host.bridgeCall('FISList', service.full(), path, {}, profile)
+    files_data = yield self.host.bridgeCall("FISList", service.full(), path, {}, profile)
     for file_data in files_data:
         try:
-            extra_raw = file_data[u'extra']
+            extra_raw = file_data[u"extra"]
         except KeyError:
             pass
         else:
-            file_data[u'extra'] = json.loads(extra_raw) if extra_raw else {}
-        dir_path = path_elts + [file_data['name']]
-        if file_data[u'type'] == C.FILE_TYPE_DIRECTORY:
+            file_data[u"extra"] = json.loads(extra_raw) if extra_raw else {}
+        dir_path = path_elts + [file_data["name"]]
+        if file_data[u"type"] == C.FILE_TYPE_DIRECTORY:
             page = self
-        elif file_data[u'type'] == C.FILE_TYPE_FILE:
-            page = self.getPageByName('files_view')
+        elif file_data[u"type"] == C.FILE_TYPE_FILE:
+            page = self.getPageByName("files_view")
 
             ## thumbnails ##
             try:
-                thumbnails = file_data[u'extra']['thumbnails']
+                thumbnails = file_data[u"extra"]["thumbnails"]
                 if not thumbnails:
                     raise KeyError
             except KeyError:
                 pass
             else:
-                thumbnails.sort(key = lambda t: t['size'])
+                thumbnails.sort(key=lambda t: t["size"])
                 thumb = thumbnails[0]
                 for thumb_data in thumbnails:
-                    if thumb_data['size'][0] > thumb_limit:
+                    if thumb_data["size"][0] > thumb_limit:
                         break
                     thumb = thumb_data
-                if u'url' in thumb:
-                    file_data['thumb_url'] = thumb['url']
-                elif u'id' in thumb:
+                if u"url" in thumb:
+                    file_data["thumb_url"] = thumb["url"]
+                elif u"id" in thumb:
                     try:
-                        thumb_path = yield self.host.bridgeCall('bobGetFile', service.full(), thumb[u'id'], profile)
+                        thumb_path = yield self.host.bridgeCall(
+                            "bobGetFile", service.full(), thumb[u"id"], profile
+                        )
                     except Exception as e:
-                        log.warning(_(u"Can't retrieve thumbnail: {reason}").format(reason=e))
+                        log.warning(
+                            _(u"Can't retrieve thumbnail: {reason}").format(reason=e)
+                        )
                     else:
                         filename = os.path.basename(thumb_path)
-                        session_data = self.host.getSessionData(request, session_iface.ISATSession)
-                        file_data['thumb_url'] = os.path.join(session_data.cache_dir, filename)
+                        session_data = self.host.getSessionData(
+                            request, session_iface.ISATSession
+                        )
+                        file_data["thumb_url"] = os.path.join(
+                            session_data.cache_dir, filename
+                        )
         else:
-            raise ValueError(u'unexpected file type: {file_type}'.format(file_type=file_data[u'type']))
-        file_data[u'url'] = page.getURL(service.full(), *dir_path)
+            raise ValueError(
+                u"unexpected file type: {file_type}".format(file_type=file_data[u"type"])
+            )
+        file_data[u"url"] = page.getURL(service.full(), *dir_path)
 
         ## comments ##
-        comments_url = file_data.get(u'comments_url')
+        comments_url = file_data.get(u"comments_url")
         if comments_url:
             parsed_url = uri.parseXMPPUri(comments_url)
-            comments_service = file_data[u'comments_service'] = parsed_url['path']
-            comments_node = file_data[u'comments_node'] = parsed_url['node']
+            comments_service = file_data[u"comments_service"] = parsed_url["path"]
+            comments_node = file_data[u"comments_node"] = parsed_url["node"]
             try:
-                comments_count = file_data[u'comments_count'] = int(file_data['comments_count'])
+                comments_count = file_data[u"comments_count"] = int(
+                    file_data["comments_count"]
+                )
             except KeyError:
                 comments_count = None
-            if comments_count and data.get('retrieve_comments', False):
-                file_data[u'comments'] = yield pages_tools.retrieveComments(self,
-                                                                            comments_service,
-                                                                            comments_node,
-                                                                            profile=profile)
+            if comments_count and data.get("retrieve_comments", False):
+                file_data[u"comments"] = yield pages_tools.retrieveComments(
+                    self, comments_service, comments_node, profile=profile
+                )
 
-    template_data[u'files_data'] = files_data
-    template_data[u'path'] = path
+    template_data[u"files_data"] = files_data
+    template_data[u"path"] = path
     if path_elts:
-        template_data[u'parent_url'] = self.getURL(service.full(), *path_elts[:-1])
-
+        template_data[u"parent_url"] = self.getURL(service.full(), *path_elts[:-1])
--- a/src/pages/files/page_meta.py	Sun Jun 24 22:21:25 2018 +0200
+++ b/src/pages/files/page_meta.py	Fri Jun 29 17:45:26 2018 +0200
@@ -5,10 +5,11 @@
 from twisted.internet import defer
 from twisted.words.protocols.jabber import jid
 from sat.core.log import getLogger
-log = getLogger('pages/files')
+
+log = getLogger("pages/files")
 """files handling pages"""
 
-name = u'files'
+name = u"files"
 access = C.PAGES_ACCESS_PROFILE
 template = u"file/discover.html"
 
@@ -17,30 +18,37 @@
 def prepare_render(self, request):
     profile = self.getProfile(request)
     template_data = request.template_data
-    namespace = self.host.ns_map['fis']
-    entities_services, entities_own, entities_roster = yield self.host.bridgeCall('discoFindByFeatures', [namespace], [], False, True, True, True, False, profile)
-    tpl_service_entities = template_data['disco_service_entities'] = {}
-    tpl_own_entities = template_data['disco_own_entities'] = {}
-    tpl_roster_entities = template_data['disco_roster_entities'] = {}
-    entities_url = template_data['entities_url'] = {}
+    namespace = self.host.ns_map["fis"]
+    entities_services, entities_own, entities_roster = yield self.host.bridgeCall(
+        "discoFindByFeatures", [namespace], [], False, True, True, True, False, profile
+    )
+    tpl_service_entities = template_data["disco_service_entities"] = {}
+    tpl_own_entities = template_data["disco_own_entities"] = {}
+    tpl_roster_entities = template_data["disco_roster_entities"] = {}
+    entities_url = template_data["entities_url"] = {}
 
     # we store identities in dict of dict using category and type as keys
     # this way it's easier to test category in the template
-    for tpl_entities, entities_map in ((tpl_service_entities, entities_services),
-                                       (tpl_own_entities, entities_own),
-                                       (tpl_roster_entities, entities_roster)):
+    for tpl_entities, entities_map in (
+        (tpl_service_entities, entities_services),
+        (tpl_own_entities, entities_own),
+        (tpl_roster_entities, entities_roster),
+    ):
         for entity_str, entity_ids in entities_map.iteritems():
             entity_jid = jid.JID(entity_str)
             tpl_entities[entity_jid] = identities = {}
             for cat, type_, name in entity_ids:
                 identities.setdefault(cat, {}).setdefault(type_, []).append(name)
-            entities_url[entity_jid] = self.getPageByName('files_list').getURL(entity_jid.full())
+            entities_url[entity_jid] = self.getPageByName("files_list").getURL(
+                entity_jid.full()
+            )
+
 
 def on_data_post(self, request):
-    jid_str = self.getPostedData(request, u'jid')
+    jid_str = self.getPostedData(request, u"jid")
     try:
-       jid_ = jid.JID(jid_str)
+        jid_ = jid.JID(jid_str)
     except RuntimeError:
         self.pageError(request, C.HTTP_BAD_REQUEST)
-    url = self.getPageByName(u'files_list').getURL(jid_.full())
+    url = self.getPageByName(u"files_list").getURL(jid_.full())
     self.HTTPRedirect(request, url)
--- a/src/pages/files/view/page_meta.py	Sun Jun 24 22:21:25 2018 +0200
+++ b/src/pages/files/view/page_meta.py	Fri Jun 29 17:45:26 2018 +0200
@@ -10,14 +10,17 @@
 import os
 import os.path
 from sat.core.log import getLogger
-log = getLogger('pages/files/view')
+
+log = getLogger("pages/files/view")
 """files handling pages"""
 
-name = u'files_view'
+name = u"files_view"
 access = C.PAGES_ACCESS_PROFILE
 
+
 def parse_url(self, request):
-    self.getPathArgs(request, ['service', '*path'], min_args=2, service='jid', path='')
+    self.getPathArgs(request, ["service", "*path"], min_args=2, service="jid", path="")
+
 
 def cleanup(dummy, tmp_dir, dest_path):
     try:
@@ -34,22 +37,24 @@
 def render(self, request):
     data = self.getRData(request)
     profile = self.getProfile(request)
-    service, path_elts = data[u'service'], data[u'path']
+    service, path_elts = data[u"service"], data[u"path"]
     basename = path_elts[-1]
     dir_elts = path_elts[:-1]
-    dir_path = u'/'.join(dir_elts)
+    dir_path = u"/".join(dir_elts)
     tmp_dir = tempfile.mkdtemp()
     dest_path = os.path.join(tmp_dir, basename)
     request.notifyFinish().addCallback(cleanup, tmp_dir, dest_path)
-    progress_id = yield self.host.bridgeCall('fileJingleRequest',
-                                             service.full(),
-                                             dest_path,
-                                             basename,
-                                             u'',
-                                             u'',
-                                             {u'path': dir_path},
-                                             profile)
-    log.debug(u'file requested')
+    progress_id = yield self.host.bridgeCall(
+        "fileJingleRequest",
+        service.full(),
+        dest_path,
+        basename,
+        u"",
+        u"",
+        {u"path": dir_path},
+        profile,
+    )
+    log.debug(u"file requested")
     yield ProgressHandler(self.host, progress_id, profile).register()
-    log.debug(u'file downloaded')
+    log.debug(u"file downloaded")
     self.delegateToResource(request, static.File(dest_path))
--- a/src/pages/forums/list/page_meta.py	Sun Jun 24 22:21:25 2018 +0200
+++ b/src/pages/forums/list/page_meta.py	Fri Jun 29 17:45:26 2018 +0200
@@ -6,43 +6,53 @@
 from sat.core.log import getLogger
 from sat.core.i18n import _
 from sat.tools.common import uri as xmpp_uri
-log = getLogger('pages/forum')
+
+log = getLogger("pages/forum")
 import json
+
 """forum handling pages"""
 
-name = u'forums'
+name = u"forums"
 access = C.PAGES_ACCESS_PUBLIC
 template = u"forum/overview.html"
 
+
 def parse_url(self, request):
-    self.getPathArgs(request, ['service', 'node', 'forum_key'],
-                     service = u'@jid',
-                     node = u'@',
-                     forum_key = u'')
+    self.getPathArgs(
+        request,
+        ["service", "node", "forum_key"],
+        service=u"@jid",
+        node=u"@",
+        forum_key=u"",
+    )
+
 
 def getLinks(self, forums):
     for forum in forums:
         try:
-            uri = forum['uri']
+            uri = forum["uri"]
         except KeyError:
             pass
         else:
             uri = xmpp_uri.parseXMPPUri(uri)
-            service = uri[u'path']
-            node = uri[u'node']
-            forum['http_url'] = self.getPageByName(u'forum_topics').getURL(service, node)
-        if u'sub-forums' in forum:
-            getLinks(self, forum[u'sub-forums'])
+            service = uri[u"path"]
+            node = uri[u"node"]
+            forum["http_url"] = self.getPageByName(u"forum_topics").getURL(service, node)
+        if u"sub-forums" in forum:
+            getLinks(self, forum[u"sub-forums"])
+
 
 @defer.inlineCallbacks
 def prepare_render(self, request):
     data = self.getRData(request)
     template_data = request.template_data
-    service, node, key = data[u'service'], data[u'node'], data[u'forum_key']
+    service, node, key = data[u"service"], data[u"node"], data[u"forum_key"]
     profile = self.getProfile(request) or C.SERVICE_PROFILE
 
     try:
-        forums_raw = yield self.host.bridgeCall('forumsGet', service.full() if service else u'', node, key, profile)
+        forums_raw = yield self.host.bridgeCall(
+            "forumsGet", service.full() if service else u"", node, key, profile
+        )
     except Exception as e:
         log.warning(_(u"Can't retrieve forums: {msg}").format(msg=e))
         forums = []
@@ -50,4 +60,4 @@
         forums = json.loads(forums_raw)
     getLinks(self, forums)
 
-    template_data[u'forums'] = forums
+    template_data[u"forums"] = forums
--- a/src/pages/forums/topics/page_meta.py	Sun Jun 24 22:21:25 2018 +0200
+++ b/src/pages/forums/topics/page_meta.py	Fri Jun 29 17:45:26 2018 +0200
@@ -6,7 +6,8 @@
 from sat.core.i18n import _
 from sat.core.log import getLogger
 from sat.tools.common import uri as xmpp_uri
-log = getLogger('pages/forums/topics')
+
+log = getLogger("pages/forums/topics")
 
 name = u"forum_topics"
 access = C.PAGES_ACCESS_PUBLIC
@@ -14,41 +15,51 @@
 
 
 def parse_url(self, request):
-    self.getPathArgs(request, ['service', 'node'], 2,
-                     service = u'jid')
+    self.getPathArgs(request, ["service", "node"], 2, service=u"jid")
 
 
 @defer.inlineCallbacks
 def prepare_render(self, request):
     profile = self.getProfile(request) or C.SERVICE_PROFILE
     data = self.getRData(request)
-    service, node = data[u'service'], data[u'node']
-    request.template_data.update({u'service': service, u'node': node})
+    service, node = data[u"service"], data[u"node"]
+    request.template_data.update({u"service": service, u"node": node})
     template_data = request.template_data
-    topics, metadata = yield self.host.bridgeCall(u'forumTopicsGet', service.full(), node, {}, profile)
-    template_data[u'identities'] = identities = {}
+    topics, metadata = yield self.host.bridgeCall(
+        u"forumTopicsGet", service.full(), node, {}, profile
+    )
+    template_data[u"identities"] = identities = {}
     for topic in topics:
-        parsed_uri = xmpp_uri.parseXMPPUri(topic[u'uri'])
-        author = topic[u'author']
-        topic[u'http_uri'] = self.getPageByName(u'forum_view').getURL(parsed_uri[u'path'], parsed_uri[u'node'])
+        parsed_uri = xmpp_uri.parseXMPPUri(topic[u"uri"])
+        author = topic[u"author"]
+        topic[u"http_uri"] = self.getPageByName(u"forum_view").getURL(
+            parsed_uri[u"path"], parsed_uri[u"node"]
+        )
         if author not in identities:
-            identities[topic[u'author']] = yield self.host.bridgeCall(u'identityGet', author, profile)
-    template_data[u'topics'] = topics
+            identities[topic[u"author"]] = yield self.host.bridgeCall(
+                u"identityGet", author, profile
+            )
+    template_data[u"topics"] = topics
+
 
 @defer.inlineCallbacks
 def on_data_post(self, request):
     profile = self.getProfile(request)
     if profile is None:
         self.pageError(request, C.HTTP_UNAUTHORIZED)
-    type_ = self.getPostedData(request, u'type')
-    if type_ == u'new_topic':
-        service, node, title, body = self.getPostedData(request, (u'service', u'node', u'title', u'body'))
+    type_ = self.getPostedData(request, u"type")
+    if type_ == u"new_topic":
+        service, node, title, body = self.getPostedData(
+            request, (u"service", u"node", u"title", u"body")
+        )
 
         if not title or not body:
             self.pageError(request, C.HTTP_BAD_REQUEST)
         topic_data = {u"title": title, u"content": body}
         try:
-            yield self.host.bridgeCall(u'forumTopicCreate', service, node, topic_data, profile)
+            yield self.host.bridgeCall(
+                u"forumTopicCreate", service, node, topic_data, profile
+            )
         except Exception as e:
             if u"forbidden" in unicode(e):
                 self.pageError(request, 401)
--- a/src/pages/forums/view/page_meta.py	Sun Jun 24 22:21:25 2018 +0200
+++ b/src/pages/forums/view/page_meta.py	Fri Jun 29 17:45:26 2018 +0200
@@ -5,7 +5,8 @@
 from twisted.internet import defer
 from sat.core.i18n import _
 from sat.core.log import getLogger
-log = getLogger('pages/forums/view')
+
+log = getLogger("pages/forums/view")
 
 name = u"forum_view"
 access = C.PAGES_ACCESS_PUBLIC
@@ -13,33 +14,34 @@
 
 
 def parse_url(self, request):
-    self.getPathArgs(request, ['service', 'node'], 2,
-                     service = u'jid')
+    self.getPathArgs(request, ["service", "node"], 2, service=u"jid")
+
 
 @defer.inlineCallbacks
 def prepare_render(self, request):
     data = self.getRData(request)
-    data['show_comments'] = False
-    blog_page = self.getPageByName(u'blog_view')
-    request.args['before'] = ['']
-    request.args['reverse'] = ['1']
+    data["show_comments"] = False
+    blog_page = self.getPageByName(u"blog_view")
+    request.args["before"] = [""]
+    request.args["reverse"] = ["1"]
     yield blog_page.prepare_render(self, request)
-    request.template_data[u'login_url'] = self.getPageRedirectURL(request)
+    request.template_data[u"login_url"] = self.getPageRedirectURL(request)
+
 
 @defer.inlineCallbacks
 def on_data_post(self, request):
     profile = self.getProfile(request)
     if profile is None:
         self.pageError(request, C.HTTP_UNAUTHORIZED)
-    type_ = self.getPostedData(request, u'type')
-    if type_ == u'comment':
-        service, node, body = self.getPostedData(request, (u'service', u'node', u'body'))
+    type_ = self.getPostedData(request, u"type")
+    if type_ == u"comment":
+        service, node, body = self.getPostedData(request, (u"service", u"node", u"body"))
 
         if not body:
             self.pageError(request, C.HTTP_BAD_REQUEST)
         mb_data = {u"content": body}
         try:
-            yield self.host.bridgeCall(u'mbSend', service, node, mb_data, profile)
+            yield self.host.bridgeCall(u"mbSend", service, node, mb_data, profile)
         except Exception as e:
             if u"forbidden" in unicode(e):
                 self.pageError(request, 401)
--- a/src/pages/g/e/page_meta.py	Sun Jun 24 22:21:25 2018 +0200
+++ b/src/pages/g/e/page_meta.py	Fri Jun 29 17:45:26 2018 +0200
@@ -11,7 +11,8 @@
 import time
 import cgi
 from sat.core.log import getLogger
-log = getLogger('pages/g/e')
+
+log = getLogger("pages/g/e")
 
 access = C.PAGES_ACCESS_PROFILE
 template = u"event/invitation.html"
@@ -22,7 +23,7 @@
     template_data = request.template_data
     guest_session = self.host.getSessionData(request, session_iface.ISATGuestSession)
     try:
-        event_uri = guest_session.data['event_uri']
+        event_uri = guest_session.data["event_uri"]
     except KeyError:
         log.warning(_(u"event URI not found, can't render event page"))
         self.pageError(request, C.HTTP_SERVICE_UNAVAILABLE)
@@ -32,56 +33,64 @@
     ## Event ##
 
     event_uri_data = uri.parseXMPPUri(event_uri)
-    if event_uri_data[u'type'] != u'pubsub':
+    if event_uri_data[u"type"] != u"pubsub":
         self.pageError(request, C.HTTP_SERVICE_UNAVAILABLE)
 
-    event_service = template_data[u'event_service'] = jid.JID(event_uri_data[u'path'])
-    event_node = template_data[u'event_node'] = event_uri_data[u'node']
-    event_id = template_data[u'event_id'] = event_uri_data.get(u'item','')
+    event_service = template_data[u"event_service"] = jid.JID(event_uri_data[u"path"])
+    event_node = template_data[u"event_node"] = event_uri_data[u"node"]
+    event_id = template_data[u"event_id"] = event_uri_data.get(u"item", "")
     profile = self.getProfile(request)
-    event_timestamp, event_data = yield self.host.bridgeCall(u"eventGet", event_service.userhost(), event_node, event_id, profile)
+    event_timestamp, event_data = yield self.host.bridgeCall(
+        u"eventGet", event_service.userhost(), event_node, event_id, profile
+    )
     try:
-        background_image = event_data.pop('background-image')
+        background_image = event_data.pop("background-image")
     except KeyError:
         pass
     else:
-        template_data['dynamic_style'] = safe(u"""
+        template_data["dynamic_style"] = safe(
+            u"""
             html {
                 background-image: url("%s");
                 background-size: 15em;
             }
-            """ % cgi.escape(background_image, True))
-    template_data['event'] = event_data
+            """
+            % cgi.escape(background_image, True)
+        )
+    template_data["event"] = event_data
     event_invitee_data = yield self.host.bridgeCall(
         u"eventInviteeGet",
-        event_data['invitees_service'],
-        event_data['invitees_node'],
-        profile)
-    template_data['invitee'] = event_invitee_data
-    template_data['days_left'] = int((event_timestamp - time.time()) / (60 * 60 * 24))
+        event_data["invitees_service"],
+        event_data["invitees_node"],
+        profile,
+    )
+    template_data["invitee"] = event_invitee_data
+    template_data["days_left"] = int((event_timestamp - time.time()) / (60 * 60 * 24))
 
     ## Blog ##
 
-    data[u'service'] = jid.JID(event_data[u'blog_service'])
-    data[u'node'] = event_data[u'blog_node']
-    data[u'allow_commenting'] = u'simple'
+    data[u"service"] = jid.JID(event_data[u"blog_service"])
+    data[u"node"] = event_data[u"blog_node"]
+    data[u"allow_commenting"] = u"simple"
 
     # we now need blog items, using blog common page
     # this will fill the "items" template data
-    blog_page = self.getPageByName(u'blog_view')
+    blog_page = self.getPageByName(u"blog_view")
     yield blog_page.prepare_render(self, request)
 
+
 @defer.inlineCallbacks
 def on_data_post(self, request):
-    type_ = self.getPostedData(request, u'type')
-    if type_ == u'comment':
-        blog_page = self.getPageByName(u'blog_view')
+    type_ = self.getPostedData(request, u"type")
+    if type_ == u"comment":
+        blog_page = self.getPageByName(u"blog_view")
         yield blog_page.on_data_post(self, request)
-    elif type_ == u'attendance':
+    elif type_ == u"attendance":
         profile = self.getProfile(request)
-        service, node, attend, guests = self.getPostedData(request, (u'service', u'node', u'attend', u'guests'))
-        data = {u'attend': attend,
-                u'guests': guests}
+        service, node, attend, guests = self.getPostedData(
+            request, (u"service", u"node", u"attend", u"guests")
+        )
+        data = {u"attend": attend, u"guests": guests}
         yield self.host.bridgeCall(u"eventInviteeSet", service, node, data, profile)
     else:
         log.warning(_(u"Unhandled data type: {}").format(type_))
--- a/src/pages/g/page_meta.py	Sun Jun 24 22:21:25 2018 +0200
+++ b/src/pages/g/page_meta.py	Fri Jun 29 17:45:26 2018 +0200
@@ -6,7 +6,8 @@
 from twisted.internet import defer
 from libervia.server import session_iface
 from sat.core.log import getLogger
-log = getLogger('pages/g')
+
+log = getLogger("pages/g")
 
 access = C.PAGES_ACCESS_PUBLIC
 template = u"invitation/welcome.html"
@@ -23,24 +24,35 @@
     except IndexError:
         self.pageError(request)
 
-    sat_session, guest_session = self.host.getSessionData(request, session_iface.ISATSession, session_iface.ISATGuestSession)
+    sat_session, guest_session = self.host.getSessionData(
+        request, session_iface.ISATSession, session_iface.ISATGuestSession
+    )
     current_id = guest_session.id
 
     if current_id is not None and current_id != invitation_id:
-        log.info(_(u'killing guest session [{old_id}] because it is connecting with an other ID [{new_id}]').format(
-        old_id = current_id,
-        new_id = invitation_id))
+        log.info(
+            _(
+                u"killing guest session [{old_id}] because it is connecting with an other ID [{new_id}]"
+            ).format(old_id=current_id, new_id=invitation_id)
+        )
         self.host.purgeSession(request)
-        sat_session, guest_session = self.host.getSessionData(request, session_iface.ISATSession, session_iface.ISATGuestSession)
+        sat_session, guest_session = self.host.getSessionData(
+            request, session_iface.ISATSession, session_iface.ISATGuestSession
+        )
         current_id = None  # FIXME: id non mis à zéro ici
         profile = None
 
     profile = sat_session.profile
-    if profile is not None  and current_id is None:
-        log.info(_(u'killing current profile session [{profile}] because a guest id is used').format(
-            profile = profile))
+    if profile is not None and current_id is None:
+        log.info(
+            _(
+                u"killing current profile session [{profile}] because a guest id is used"
+            ).format(profile=profile)
+        )
         self.host.purgeSession(request)
-        sat_session, guest_session = self.host.getSessionData(request, session_iface.ISATSession, session_iface.ISATGuestSession)
+        sat_session, guest_session = self.host.getSessionData(
+            request, session_iface.ISATSession, session_iface.ISATGuestSession
+        )
         profile = None
 
     if current_id is None:
@@ -58,31 +70,35 @@
     if profile is None:
         log.debug(_(u"connecting profile [{}]").format(profile))
         # we need to connect the profile
-        profile = data['guest_profile']
-        password = data['password']
+        profile = data["guest_profile"]
+        password = data["password"]
         try:
             yield self.host.connect(request, profile, password)
         except Exception as e:
-            log.warning(_(u"Can't connect profile: {msg}").format(
-                msg=e))
+            log.warning(_(u"Can't connect profile: {msg}").format(msg=e))
             # FIXME: no good error code correspond
             #        maybe use a custom one?
             self.pageError(request, code=C.HTTP_SERVICE_UNAVAILABLE)
 
-        log.info(_(u"guest session started, connected with profile [{profile}]".format(
-            profile = profile)))
+        log.info(
+            _(
+                u"guest session started, connected with profile [{profile}]".format(
+                    profile=profile
+                )
+            )
+        )
 
     # we copy data useful in templates
     template_data = request.template_data
-    template_data['norobots'] = True
-    if u'name' in data:
-        template_data[u'name'] = data[u'name']
-    if u'language' in data:
-        template_data[u'locale'] = data[u'language']
+    template_data["norobots"] = True
+    if u"name" in data:
+        template_data[u"name"] = data[u"name"]
+    if u"language" in data:
+        template_data[u"locale"] = data[u"language"]
 
 
 def prepare_render(self, request):
     template_data = request.template_data
     guest_session = self.host.getSessionData(request, session_iface.ISATGuestSession)
-    main_uri = guest_session.data.get('main_uri')
+    main_uri = guest_session.data.get("main_uri")
     template_data[u"include_url"] = self.getPagePathFromURI(main_uri)
--- a/src/pages/login/logged/page_meta.py	Sun Jun 24 22:21:25 2018 +0200
+++ b/src/pages/login/logged/page_meta.py	Fri Jun 29 17:45:26 2018 +0200
@@ -3,7 +3,8 @@
 
 from libervia.server import session_iface
 from sat.core.log import getLogger
-log = getLogger('pages/login')
+
+log = getLogger("pages/login")
 
 """SàT log-in page, with link to create an account"""
 
@@ -13,5 +14,5 @@
 def prepare_render(self, request):
     template_data = request.template_data
     session_data = self.host.getSessionData(request, session_iface.ISATSession)
-    template_data['guest_session'] = session_data.guest
-    template_data['session_started'] = session_data.started
+    template_data["guest_session"] = session_data.guest
+    template_data["session_started"] = session_data.started
--- a/src/pages/login/page_meta.py	Sun Jun 24 22:21:25 2018 +0200
+++ b/src/pages/login/page_meta.py	Fri Jun 29 17:45:26 2018 +0200
@@ -7,7 +7,8 @@
 from libervia.server import session_iface
 from twisted.internet import defer
 from sat.core.log import getLogger
-log = getLogger('pages/login')
+
+log = getLogger("pages/login")
 
 """SàT log-in page, with link to create an account"""
 
@@ -19,24 +20,27 @@
 def prepare_render(self, request):
     template_data = request.template_data
 
-    # we redirect to logged page if a session is active
+    #  we redirect to logged page if a session is active
     profile = self.getProfile(request)
     if profile is not None:
-        self.pageRedirect('/login/logged', request)
+        self.pageRedirect("/login/logged", request)
 
     # login error message
     session_data = self.host.getSessionData(request, session_iface.ISATSession)
-    login_error = session_data.popPageData(self, 'login_error')
+    login_error = session_data.popPageData(self, "login_error")
     if login_error is not None:
-        template_data['S_C'] = C  # we need server constants in template
-        template_data['login_error'] = login_error
-    template_data['empty_password_allowed'] = bool(self.host.options['empty_password_allowed_warning_dangerous_list'])
+        template_data["S_C"] = C  # we need server constants in template
+        template_data["login_error"] = login_error
+    template_data["empty_password_allowed"] = bool(
+        self.host.options["empty_password_allowed_warning_dangerous_list"]
+    )
 
     # register page url
-    template_data['register_url'] = self.getPageRedirectURL(request, 'register')
+    template_data["register_url"] = self.getPageRedirectURL(request, "register")
 
-    # if login is set, we put it in template to prefill field
-    template_data['login'] = session_data.popPageData(self, 'login')
+    #  if login is set, we put it in template to prefill field
+    template_data["login"] = session_data.popPageData(self, "login")
+
 
 def login_error(self, request, error_const):
     """set login_error in page data
@@ -48,19 +52,20 @@
     session_data.setPageData(self, "login_error", error_const)
     return C.POST_NO_CONFIRM
 
+
 @defer.inlineCallbacks
 def on_data_post(self, request):
     profile = self.getProfile(request)
-    type_ = self.getPostedData(request, 'type')
-    if type_ == 'disconnect':
+    type_ = self.getPostedData(request, "type")
+    if type_ == "disconnect":
         if profile is None:
-            log.warning(_(u'Disconnect called when no profile is logged'))
+            log.warning(_(u"Disconnect called when no profile is logged"))
             self.pageError(request, C.HTTP_BAD_REQUEST)
         else:
             self.host.purgeSession(request)
             defer.returnValue(C.POST_NO_CONFIRM)
-    elif type_ == 'login':
-        login, password = self.getPostedData(request, (u'login', u'password'))
+    elif type_ == "login":
+        login, password = self.getPostedData(request, (u"login", u"password"))
         try:
             status = yield self.host.connect(request, login, password)
         except ValueError as e:
@@ -76,6 +81,6 @@
                 # Profile has been logged correctly
                 self.redirectOrContinue(request)
             else:
-                log.error(_(u'Unhandled status: {status}'.format(status=status)))
+                log.error(_(u"Unhandled status: {status}".format(status=status)))
     else:
         self.pageError(request, C.HTTP_BAD_REQUEST)
--- a/src/pages/merge-requests/disco/page_meta.py	Sun Jun 24 22:21:25 2018 +0200
+++ b/src/pages/merge-requests/disco/page_meta.py	Fri Jun 29 17:45:26 2018 +0200
@@ -4,36 +4,38 @@
 from libervia.server.constants import Const as C
 from twisted.words.protocols.jabber import jid
 from sat.core.log import getLogger
-log = getLogger('pages/ticket')
+
+log = getLogger("pages/ticket")
 """ticket handling pages"""
 
-name = u'merge-requests_disco'
+name = u"merge-requests_disco"
 access = C.PAGES_ACCESS_PUBLIC
 template = u"merge-request/discover.html"
 
 
 def prepare_render(self, request):
-    mr_handlers_config = self.host.options['mr_handlers_json']
+    mr_handlers_config = self.host.options["mr_handlers_json"]
     if mr_handlers_config:
-        handlers = request.template_data['mr_handlers'] = []
+        handlers = request.template_data["mr_handlers"] = []
         try:
             for handler_data in mr_handlers_config:
-                service = handler_data[u'service']
-                node = handler_data[u'node']
-                name = handler_data[u'name']
-                url = self.getPageByName(u'merge-requests').getURL(service, node)
-                handlers.append({u'name': name, u'url': url})
+                service = handler_data[u"service"]
+                node = handler_data[u"node"]
+                name = handler_data[u"name"]
+                url = self.getPageByName(u"merge-requests").getURL(service, node)
+                handlers.append({u"name": name, u"url": url})
         except KeyError as e:
             log.warning(u"Missing field in mr_handlers_json: {msg}".format(msg=e))
         except Exception as e:
             log.warning(u"Can't decode mr handlers: {msg}".format(msg=e))
 
+
 def on_data_post(self, request):
-    jid_str = self.getPostedData(request, u'jid')
+    jid_str = self.getPostedData(request, u"jid")
     try:
-       jid_ = jid.JID(jid_str)
+        jid_ = jid.JID(jid_str)
     except RuntimeError:
         self.pageError(request, C.HTTP_BAD_REQUEST)
     # for now we just use default node
-    url = self.getPageByName(u'merge-requests').getURL(jid_.full(), u'@')
+    url = self.getPageByName(u"merge-requests").getURL(jid_.full(), u"@")
     self.HTTPRedirect(request, url)
--- a/src/pages/merge-requests/edit/page_meta.py	Sun Jun 24 22:21:25 2018 +0200
+++ b/src/pages/merge-requests/edit/page_meta.py	Fri Jun 29 17:45:26 2018 +0200
@@ -6,12 +6,13 @@
 from twisted.internet import defer
 from sat.tools.common import template_xmlui
 from sat.core.log import getLogger
+
 """merge-requests edition"""
 
-name = u'merge-requests_edit'
+name = u"merge-requests_edit"
 access = C.PAGES_ACCESS_PROFILE
 template = u"merge-request/edit.html"
-log = getLogger('pages/' + name)
+log = getLogger("pages/" + name)
 
 
 def parse_url(self, request):
@@ -22,52 +23,83 @@
         self.pageError(request, C.HTTP_BAD_REQUEST)
 
     data = self.getRData(request)
-    data[u'ticket_id'] = item_id
+    data[u"ticket_id"] = item_id
+
 
 @defer.inlineCallbacks
 def prepare_render(self, request):
     data = self.getRData(request)
     template_data = request.template_data
-    service, node, ticket_id = data.get(u'service', u''), data.get(u'node', u''), data[u'ticket_id']
+    service, node, ticket_id = (
+        data.get(u"service", u""),
+        data.get(u"node", u""),
+        data[u"ticket_id"],
+    )
     profile = self.getProfile(request)
 
-
-    ignore = ('publisher', 'author', 'author_jid', 'author_email', 'created', 'updated', 'comments_uri', 'request_data', 'type')
-    tickets = yield self.host.bridgeCall("mergeRequestsGet", service.full() if service else u'', node, C.NO_LIMIT, [ticket_id], '', {}, profile)
+    ignore = (
+        "publisher",
+        "author",
+        "author_jid",
+        "author_email",
+        "created",
+        "updated",
+        "comments_uri",
+        "request_data",
+        "type",
+    )
+    tickets = yield self.host.bridgeCall(
+        "mergeRequestsGet",
+        service.full() if service else u"",
+        node,
+        C.NO_LIMIT,
+        [ticket_id],
+        "",
+        {},
+        profile,
+    )
     ticket = [template_xmlui.create(self.host, x, ignore=ignore) for x in tickets[0]][0]
 
     try:
         # small trick to get a one line text input instead of the big textarea
-        ticket.widgets[u'labels'].type = u'string'
-        ticket.widgets[u'labels'].value = ticket.widgets[u'labels'].value.replace(u'\n', ', ')
+        ticket.widgets[u"labels"].type = u"string"
+        ticket.widgets[u"labels"].value = ticket.widgets[u"labels"].value.replace(
+            u"\n", ", "
+        )
     except KeyError:
         pass
-    template_data[u'new_ticket_xmlui'] = ticket
+    template_data[u"new_ticket_xmlui"] = ticket
+
 
 @defer.inlineCallbacks
 def on_data_post(self, request):
     data = self.getRData(request)
-    service = data['service']
-    node = data['node']
-    ticket_id = data['ticket_id']
+    service = data["service"]
+    node = data["node"]
+    ticket_id = data["ticket_id"]
     posted_data = self.getAllPostedData(request)
-    if not posted_data['title'] or not posted_data['body']:
+    if not posted_data["title"] or not posted_data["body"]:
         self.pageError(request, C.HTTP_BAD_REQUEST)
     try:
-        posted_data['labels'] = [l.strip() for l in posted_data['labels'][0].split(',')]
+        posted_data["labels"] = [l.strip() for l in posted_data["labels"][0].split(",")]
     except (KeyError, IndexError):
         pass
     profile = self.getProfile(request)
-    yield self.host.bridgeCall("mergeRequestSet", service.full(),
-                               node,
-                               u'',
-                               u'auto',
-                               posted_data,
-                               u'',
-                               ticket_id,
-                               {'update': C.BOOL_TRUE},
-                               profile)
+    yield self.host.bridgeCall(
+        "mergeRequestSet",
+        service.full(),
+        node,
+        u"",
+        u"auto",
+        posted_data,
+        u"",
+        ticket_id,
+        {"update": C.BOOL_TRUE},
+        profile,
+    )
     # we don't want to redirect to edit page on success, but to tickets list
-    data['post_redirect_page'] = (self.getPageByName(u'merge-requests'),
-                                  service.full(),
-                                  node or u'@')
+    data["post_redirect_page"] = (
+        self.getPageByName(u"merge-requests"),
+        service.full(),
+        node or u"@",
+    )
--- a/src/pages/merge-requests/new/page_meta.py	Sun Jun 24 22:21:25 2018 +0200
+++ b/src/pages/merge-requests/new/page_meta.py	Fri Jun 29 17:45:26 2018 +0200
@@ -3,9 +3,10 @@
 
 from libervia.server.constants import Const as C
 from sat.core.log import getLogger
-log = getLogger('pages/ticket')
+
+log = getLogger("pages/ticket")
 """ticket handling pages"""
 
-name = u'merge-requests_new'
+name = u"merge-requests_new"
 access = C.PAGES_ACCESS_PUBLIC
 template = u"merge-request/create.html"
--- a/src/pages/merge-requests/page_meta.py	Sun Jun 24 22:21:25 2018 +0200
+++ b/src/pages/merge-requests/page_meta.py	Fri Jun 29 17:45:26 2018 +0200
@@ -6,34 +6,53 @@
 from sat.tools.common import template_xmlui
 from sat.tools.common import data_objects
 from sat.core.log import getLogger
-log = getLogger('pages/ticket')
+
+log = getLogger("pages/ticket")
 """ticket handling pages"""
 
-name = u'merge-requests'
+name = u"merge-requests"
 access = C.PAGES_ACCESS_PUBLIC
 template = u"ticket/overview.html"
 
 
 def parse_url(self, request):
-    self.getPathArgs(request, ['service', 'node'], service='jid')
+    self.getPathArgs(request, ["service", "node"], service="jid")
     data = self.getRData(request)
-    service, node = data[u'service'], data[u'node']
+    service, node = data[u"service"], data[u"node"]
     if node is None:
         self.pageRedirect(u"merge-requests_disco", request)
-    if node == u'@':
-        node = data[u'node'] = u''
-    self.checkCache(request, C.CACHE_PUBSUB, service=service, node=node, short='merge-requests')
+    if node == u"@":
+        node = data[u"node"] = u""
+    self.checkCache(
+        request, C.CACHE_PUBSUB, service=service, node=node, short="merge-requests"
+    )
     template_data = request.template_data
-    template_data[u'url_tickets_list'] = self.getPageByName('merge-requests').getURL(service.full(), node)
-    template_data[u'url_tickets_new'] = self.getSubPageURL(request, 'merge-requests_new')
+    template_data[u"url_tickets_list"] = self.getPageByName("merge-requests").getURL(
+        service.full(), node
+    )
+    template_data[u"url_tickets_new"] = self.getSubPageURL(request, "merge-requests_new")
+
 
 @defer.inlineCallbacks
 def prepare_render(self, request):
     data = self.getRData(request)
     template_data = request.template_data
-    service, node = data[u'service'], data[u'node']
+    service, node = data[u"service"], data[u"node"]
     profile = self.getProfile(request) or C.SERVICE_PROFILE
 
-    merge_requests = yield self.host.bridgeCall("mergeRequestsGet", service.full() if service else u'', node, C.NO_LIMIT, [], '', {'labels_as_list': C.BOOL_TRUE}, profile)
-    template_data[u'tickets'] = [template_xmlui.create(self.host, x) for x in merge_requests[0]]
-    template_data[u'on_ticket_click'] = data_objects.OnClick(url=self.getSubPageURL(request, u'merge-requests_view') + u'/{item.id}')
+    merge_requests = yield self.host.bridgeCall(
+        "mergeRequestsGet",
+        service.full() if service else u"",
+        node,
+        C.NO_LIMIT,
+        [],
+        "",
+        {"labels_as_list": C.BOOL_TRUE},
+        profile,
+    )
+    template_data[u"tickets"] = [
+        template_xmlui.create(self.host, x) for x in merge_requests[0]
+    ]
+    template_data[u"on_ticket_click"] = data_objects.OnClick(
+        url=self.getSubPageURL(request, u"merge-requests_view") + u"/{item.id}"
+    )
--- a/src/pages/merge-requests/view/page_meta.py	Sun Jun 24 22:21:25 2018 +0200
+++ b/src/pages/merge-requests/view/page_meta.py	Fri Jun 29 17:45:26 2018 +0200
@@ -15,7 +15,7 @@
 name = u"merge-requests_view"
 access = C.PAGES_ACCESS_PUBLIC
 template = u"merge-request/item.html"
-log = getLogger(u'pages/' + name)
+log = getLogger(u"pages/" + name)
 
 
 def parse_url(self, request):
@@ -26,62 +26,72 @@
         self.pageError(request, C.HTTP_BAD_REQUEST)
 
     data = self.getRData(request)
-    data[u'ticket_id'] = item_id
+    data[u"ticket_id"] = item_id
+
 
 @defer.inlineCallbacks
 def prepare_render(self, request):
     data = self.getRData(request)
     template_data = request.template_data
     session = self.host.getSessionData(request, session_iface.ISATSession)
-    service, node, ticket_id = data.get(u'service', u''), data.get(u'node', u''), data[u'ticket_id']
+    service, node, ticket_id = (
+        data.get(u"service", u""),
+        data.get(u"node", u""),
+        data[u"ticket_id"],
+    )
     profile = self.getProfile(request)
 
     if profile is None:
         profile = C.SERVICE_PROFILE
 
-    tickets, metadata, parsed_tickets = yield self.host.bridgeCall("mergeRequestsGet", service.full() if service else u'',
-                                                                   node,
-                                                                   C.NO_LIMIT,
-                                                                   [ticket_id],
-                                                                   '',
-                                                                   {'parse': C.BOOL_TRUE,
-                                                                    'labels_as_list': C.BOOL_TRUE},
-                                                                   profile)
-    ticket = template_xmlui.create(self.host, tickets[0], ignore=['request_data', 'type'])
-    template_data[u'item'] = ticket
-    template_data['patches'] = parsed_tickets[0]
-    comments_uri = ticket.widgets['comments_uri'].value
+    tickets, metadata, parsed_tickets = yield self.host.bridgeCall(
+        "mergeRequestsGet",
+        service.full() if service else u"",
+        node,
+        C.NO_LIMIT,
+        [ticket_id],
+        "",
+        {"parse": C.BOOL_TRUE, "labels_as_list": C.BOOL_TRUE},
+        profile,
+    )
+    ticket = template_xmlui.create(self.host, tickets[0], ignore=["request_data", "type"])
+    template_data[u"item"] = ticket
+    template_data["patches"] = parsed_tickets[0]
+    comments_uri = ticket.widgets["comments_uri"].value
     if comments_uri:
         uri_data = uri.parseXMPPUri(comments_uri)
-        template_data['comments_node'] = comments_node = uri_data['node']
-        template_data['comments_service'] = comments_service = uri_data['path']
-        comments = yield self.host.bridgeCall("mbGet",
-                                              comments_service,
-                                              comments_node,
-                                              C.NO_LIMIT,
-                                              [],
-                                              {},
-                                              profile)
+        template_data["comments_node"] = comments_node = uri_data["node"]
+        template_data["comments_service"] = comments_service = uri_data["path"]
+        comments = yield self.host.bridgeCall(
+            "mbGet", comments_service, comments_node, C.NO_LIMIT, [], {}, profile
+        )
 
-        template_data[u'comments'] = data_objects.BlogItems(comments)
-        template_data[u'login_url'] = self.getPageRedirectURL(request)
+        template_data[u"comments"] = data_objects.BlogItems(comments)
+        template_data[u"login_url"] = self.getPageRedirectURL(request)
 
     if session.connected:
         # we set edition URL only if user is the publisher or the node owner
-        publisher = jid.JID(ticket.widgets['publisher'].value)
+        publisher = jid.JID(ticket.widgets["publisher"].value)
         is_publisher = publisher.userhostJID() == session.jid.userhostJID()
         affiliation = None
         if not is_publisher:
-            node = node or self.host.ns_map['merge_requests']
+            node = node or self.host.ns_map["merge_requests"]
             affiliation = yield self.host.getAffiliation(request, service, node)
-        if is_publisher or affiliation == 'owner':
-            template_data[u'url_ticket_edit'] = self.getURLByPath(SubPage('merge-requests'), service.full(), node or u'@', SubPage('merge-requests_edit'), ticket_id)
+        if is_publisher or affiliation == "owner":
+            template_data[u"url_ticket_edit"] = self.getURLByPath(
+                SubPage("merge-requests"),
+                service.full(),
+                node or u"@",
+                SubPage("merge-requests_edit"),
+                ticket_id,
+            )
+
 
 @defer.inlineCallbacks
 def on_data_post(self, request):
-    type_ = self.getPostedData(request, u'type')
-    if type_ == u'comment':
-        blog_page = self.getPageByName(u'blog_view')
+    type_ = self.getPostedData(request, u"type")
+    if type_ == u"comment":
+        blog_page = self.getPageByName(u"blog_view")
         yield blog_page.on_data_post(self, request)
     else:
         log.warning(_(u"Unhandled data type: {}").format(type_))
--- a/src/pages/photos/album/page_meta.py	Sun Jun 24 22:21:25 2018 +0200
+++ b/src/pages/photos/album/page_meta.py	Fri Jun 29 17:45:26 2018 +0200
@@ -3,22 +3,26 @@
 
 from libervia.server.constants import Const as C
 from sat.core.log import getLogger
-log = getLogger('pages/photo/album')
 
-name = u'photos_album'
+log = getLogger("pages/photo/album")
+
+name = u"photos_album"
 access = C.PAGES_ACCESS_PROFILE
 template = u"photo/album.html"
 
+
 def parse_url(self, request):
-    self.getPathArgs(request, ['service', '*path'], min_args=1, service='jid', path='')
+    self.getPathArgs(request, ["service", "*path"], min_args=1, service="jid", path="")
+
 
 def prepare_render(self, request):
     data = self.getRData(request)
-    data['thumb_limit'] = 1200
-    data['retrieve_comments'] = True
-    files_page = self.getPageByName(u'files_list')
+    data["thumb_limit"] = 1200
+    data["retrieve_comments"] = True
+    files_page = self.getPageByName(u"files_list")
     return files_page.prepare_render(self, request)
 
+
 def on_data_post(self, request):
-    blog_page = self.getPageByName(u'blog_view')
+    blog_page = self.getPageByName(u"blog_view")
     return blog_page.on_data_post(self, request)
--- a/src/pages/photos/page_meta.py	Sun Jun 24 22:21:25 2018 +0200
+++ b/src/pages/photos/page_meta.py	Fri Jun 29 17:45:26 2018 +0200
@@ -4,13 +4,13 @@
 from libervia.server.constants import Const as C
 from twisted.internet import defer
 
-name = u'photos'
+name = u"photos"
 access = C.PAGES_ACCESS_PROFILE
 template = u"photo/discover.html"
 
 
 @defer.inlineCallbacks
 def on_data_post(self, request):
-    jid_ = self.getPostedData(request, u'jid')
-    url = self.getPageByName(u'photos_album').getURL(jid_)
+    jid_ = self.getPostedData(request, u"jid")
+    url = self.getPageByName(u"photos_album").getURL(jid_)
     self.HTTPRedirect(request, url)
--- a/src/pages/register/page_meta.py	Sun Jun 24 22:21:25 2018 +0200
+++ b/src/pages/register/page_meta.py	Fri Jun 29 17:45:26 2018 +0200
@@ -5,7 +5,8 @@
 from libervia.server import session_iface
 from twisted.internet import defer
 from sat.core.log import getLogger
-log = getLogger('pages/register')
+
+log = getLogger("pages/register")
 
 """SàT account registration page"""
 
@@ -17,39 +18,42 @@
 def prepare_render(self, request):
     profile = self.getProfile(request)
     if profile is not None:
-        self.pageRedirect('/login/logged', request)
+        self.pageRedirect("/login/logged", request)
     template_data = request.template_data
-    template_data['login_url'] = self.getPageByName('login').url
-    template_data['S_C'] = C  # we need server constants in template
+    template_data["login_url"] = self.getPageByName("login").url
+    template_data["S_C"] = C  # we need server constants in template
 
     # login error message
     session_data = self.host.getSessionData(request, session_iface.ISATSession)
-    login_error = session_data.popPageData(self, 'login_error')
+    login_error = session_data.popPageData(self, "login_error")
     if login_error is not None:
-        template_data['login_error'] = login_error
+        template_data["login_error"] = login_error
 
-    # if fields were already filled, we reuse them
-    for k in (u'login', u'email', u'password'):
+    #  if fields were already filled, we reuse them
+    for k in (u"login", u"email", u"password"):
         template_data[k] = session_data.popPageData(self, k)
 
+
 @defer.inlineCallbacks
 def on_data_post(self, request):
-    type_ = self.getPostedData(request, u'type')
-    if type_ == u'register':
-        login, email, password = self.getPostedData(request, (u'login', u'email', u'password'))
+    type_ = self.getPostedData(request, u"type")
+    if type_ == u"register":
+        login, email, password = self.getPostedData(
+            request, (u"login", u"email", u"password")
+        )
         status = yield self.host.registerNewAccount(request, login, password, email)
         session_data = self.host.getSessionData(request, session_iface.ISATSession)
         if status == C.REGISTRATION_SUCCEED:
             # we prefill login field for login page
-            session_data.setPageData(self.getPageByName(u'login'), u'login', login)
+            session_data.setPageData(self.getPageByName(u"login"), u"login", login)
             # if we have a redirect_url we follow it
             self.redirectOrContinue(request)
             # else we redirect to login page
-            self.HTTPRedirect(request, self.getPageByName(u'login').url)
+            self.HTTPRedirect(request, self.getPageByName(u"login").url)
         else:
             session_data.setPageData(self, u"login_error", status)
             l = locals()
-            for k in (u'login', u'email', u'password'):
+            for k in (u"login", u"email", u"password"):
                 # we save fields so user doesn't have to enter them again
                 session_data.setPageData(self, k, l[k])
             defer.returnValue(C.POST_NO_CONFIRM)
--- a/src/pages/tickets/disco/page_meta.py	Sun Jun 24 22:21:25 2018 +0200
+++ b/src/pages/tickets/disco/page_meta.py	Fri Jun 29 17:45:26 2018 +0200
@@ -4,36 +4,38 @@
 from libervia.server.constants import Const as C
 from twisted.words.protocols.jabber import jid
 from sat.core.log import getLogger
-log = getLogger('pages/ticket')
+
+log = getLogger("pages/ticket")
 """ticket handling pages"""
 
-name = u'tickets_disco'
+name = u"tickets_disco"
 access = C.PAGES_ACCESS_PUBLIC
 template = u"ticket/discover.html"
 
 
 def prepare_render(self, request):
-    tickets_trackers_config = self.host.options['tickets_trackers_json']
+    tickets_trackers_config = self.host.options["tickets_trackers_json"]
     if tickets_trackers_config:
-        trackers = request.template_data['tickets_trackers'] = []
+        trackers = request.template_data["tickets_trackers"] = []
         try:
             for tracker_data in tickets_trackers_config:
-                service = tracker_data[u'service']
-                node = tracker_data[u'node']
-                name = tracker_data[u'name']
-                url = self.getPageByName(u'tickets').getURL(service, node)
-                trackers.append({u'name': name, u'url': url})
+                service = tracker_data[u"service"]
+                node = tracker_data[u"node"]
+                name = tracker_data[u"name"]
+                url = self.getPageByName(u"tickets").getURL(service, node)
+                trackers.append({u"name": name, u"url": url})
         except KeyError as e:
             log.warning(u"Missing field in tickets_trackers_json: {msg}".format(msg=e))
         except Exception as e:
             log.warning(u"Can't decode tickets trackers: {msg}".format(msg=e))
 
+
 def on_data_post(self, request):
-    jid_str = self.getPostedData(request, u'jid')
+    jid_str = self.getPostedData(request, u"jid")
     try:
-       jid_ = jid.JID(jid_str)
+        jid_ = jid.JID(jid_str)
     except RuntimeError:
         self.pageError(request, C.HTTP_BAD_REQUEST)
     # for now we just use default node
-    url = self.getPageByName(u'tickets').getURL(jid_.full(), u'@')
+    url = self.getPageByName(u"tickets").getURL(jid_.full(), u"@")
     self.HTTPRedirect(request, url)
--- a/src/pages/tickets/edit/page_meta.py	Sun Jun 24 22:21:25 2018 +0200
+++ b/src/pages/tickets/edit/page_meta.py	Fri Jun 29 17:45:26 2018 +0200
@@ -6,10 +6,11 @@
 from twisted.internet import defer
 from sat.tools.common import template_xmlui
 from sat.core.log import getLogger
-log = getLogger('pages/ticket')
+
+log = getLogger("pages/ticket")
 """ticket handling pages"""
 
-name = u'tickets_edit'
+name = u"tickets_edit"
 access = C.PAGES_ACCESS_PROFILE
 template = u"ticket/edit.html"
 
@@ -22,44 +23,72 @@
         self.pageError(request, C.HTTP_BAD_REQUEST)
 
     data = self.getRData(request)
-    data[u'ticket_id'] = item_id
+    data[u"ticket_id"] = item_id
+
 
 @defer.inlineCallbacks
 def prepare_render(self, request):
     data = self.getRData(request)
     template_data = request.template_data
-    service, node, ticket_id = data.get(u'service', u''), data.get(u'node', u''), data[u'ticket_id']
+    service, node, ticket_id = (
+        data.get(u"service", u""),
+        data.get(u"node", u""),
+        data[u"ticket_id"],
+    )
     profile = self.getProfile(request)
 
-
-    ignore = ('publisher', 'author', 'author_jid', 'author_email', 'created', 'updated', 'comments_uri')
-    tickets = yield self.host.bridgeCall("ticketsGet", service.full() if service else u'', node, C.NO_LIMIT, [ticket_id], '', {}, profile)
+    ignore = (
+        "publisher",
+        "author",
+        "author_jid",
+        "author_email",
+        "created",
+        "updated",
+        "comments_uri",
+    )
+    tickets = yield self.host.bridgeCall(
+        "ticketsGet",
+        service.full() if service else u"",
+        node,
+        C.NO_LIMIT,
+        [ticket_id],
+        "",
+        {},
+        profile,
+    )
     ticket = [template_xmlui.create(self.host, x, ignore=ignore) for x in tickets[0]][0]
 
     try:
         # small trick to get a one line text input instead of the big textarea
-        ticket.widgets[u'labels'].type = u'string'
-        ticket.widgets[u'labels'].value = ticket.widgets[u'labels'].value.replace(u'\n', ', ')
+        ticket.widgets[u"labels"].type = u"string"
+        ticket.widgets[u"labels"].value = ticket.widgets[u"labels"].value.replace(
+            u"\n", ", "
+        )
     except KeyError:
         pass
-    template_data[u'new_ticket_xmlui'] = ticket
+    template_data[u"new_ticket_xmlui"] = ticket
+
 
 @defer.inlineCallbacks
 def on_data_post(self, request):
     data = self.getRData(request)
-    service = data['service']
-    node = data['node']
-    ticket_id = data['ticket_id']
+    service = data["service"]
+    node = data["node"]
+    ticket_id = data["ticket_id"]
     posted_data = self.getAllPostedData(request)
-    if not posted_data['title'] or not posted_data['body']:
+    if not posted_data["title"] or not posted_data["body"]:
         self.pageError(request, C.HTTP_BAD_REQUEST)
     try:
-        posted_data['labels'] = [l.strip() for l in posted_data['labels'][0].split(',')]
+        posted_data["labels"] = [l.strip() for l in posted_data["labels"][0].split(",")]
     except (KeyError, IndexError):
         pass
     profile = self.getProfile(request)
-    yield self.host.bridgeCall("ticketSet", service.full(), node, posted_data, u'', ticket_id, {}, profile)
+    yield self.host.bridgeCall(
+        "ticketSet", service.full(), node, posted_data, u"", ticket_id, {}, profile
+    )
     # we don't want to redirect to edit page on success, but to tickets list
-    data['post_redirect_page'] = (self.getPageByName(u'tickets'),
-                                  service.full(),
-                                  node or u'@')
+    data["post_redirect_page"] = (
+        self.getPageByName(u"tickets"),
+        service.full(),
+        node or u"@",
+    )
--- a/src/pages/tickets/new/page_meta.py	Sun Jun 24 22:21:25 2018 +0200
+++ b/src/pages/tickets/new/page_meta.py	Fri Jun 29 17:45:26 2018 +0200
@@ -5,10 +5,11 @@
 from twisted.internet import defer
 from sat.tools.common import template_xmlui
 from sat.core.log import getLogger
-log = getLogger('pages/ticket')
+
+log = getLogger("pages/ticket")
 """ticket handling pages"""
 
-name = u'tickets_new'
+name = u"tickets_new"
 access = C.PAGES_ACCESS_PROFILE
 template = u"ticket/create.html"
 
@@ -17,35 +18,50 @@
 def prepare_render(self, request):
     data = self.getRData(request)
     template_data = request.template_data
-    service, node = data.get(u'service', u''), data.get(u'node', u'')
+    service, node = data.get(u"service", u""), data.get(u"node", u"")
     profile = self.getProfile(request)
     schema = yield self.host.bridgeCall("ticketsSchemaGet", service.full(), node, profile)
-    data['schema'] = schema
+    data["schema"] = schema
     # following fields are handled in backend
-    ignore = ('author', 'author_jid', 'author_email', 'created', 'updated', 'comments_uri', 'status', 'milestone', 'priority')
+    ignore = (
+        "author",
+        "author_jid",
+        "author_email",
+        "created",
+        "updated",
+        "comments_uri",
+        "status",
+        "milestone",
+        "priority",
+    )
     xmlui_obj = template_xmlui.create(self.host, schema, ignore=ignore)
     try:
         # small trick to get a one line text input instead of the big textarea
-        xmlui_obj.widgets[u'labels'].type = u'string'
+        xmlui_obj.widgets[u"labels"].type = u"string"
     except KeyError:
         pass
-    template_data[u'new_ticket_xmlui'] = xmlui_obj
+    template_data[u"new_ticket_xmlui"] = xmlui_obj
+
 
 @defer.inlineCallbacks
 def on_data_post(self, request):
     data = self.getRData(request)
-    service = data['service']
-    node = data['node']
+    service = data["service"]
+    node = data["node"]
     posted_data = self.getAllPostedData(request)
-    if not posted_data['title'] or not posted_data['body']:
+    if not posted_data["title"] or not posted_data["body"]:
         self.pageError(request, C.HTTP_BAD_REQUEST)
     try:
-        posted_data['labels'] = [l.strip() for l in posted_data['labels'][0].split(',')]
+        posted_data["labels"] = [l.strip() for l in posted_data["labels"][0].split(",")]
     except (KeyError, IndexError):
         pass
     profile = self.getProfile(request)
-    yield self.host.bridgeCall("ticketSet", service.full(), node, posted_data, u'', u'', {}, profile)
+    yield self.host.bridgeCall(
+        "ticketSet", service.full(), node, posted_data, u"", u"", {}, profile
+    )
     # we don't want to redirect to creation page on success, but to tickets list
-    data['post_redirect_page'] = (self.getPageByName(u'tickets'),
-                                  service.full(),
-                                  node or u'@')
+    data["post_redirect_page"] = (
+        self.getPageByName(u"tickets"),
+        service.full(),
+        node or u"@",
+    )
--- a/src/pages/tickets/page_meta.py	Sun Jun 24 22:21:25 2018 +0200
+++ b/src/pages/tickets/page_meta.py	Fri Jun 29 17:45:26 2018 +0200
@@ -6,35 +6,48 @@
 from sat.tools.common import template_xmlui
 from sat.tools.common import data_objects
 from sat.core.log import getLogger
-log = getLogger('pages/ticket')
+
+log = getLogger("pages/ticket")
 """ticket handling pages"""
 
-name = u'tickets'
+name = u"tickets"
 access = C.PAGES_ACCESS_PUBLIC
 template = u"ticket/overview.html"
 
 
 def parse_url(self, request):
-    self.getPathArgs(request, ['service', 'node'], service='jid')
+    self.getPathArgs(request, ["service", "node"], service="jid")
     data = self.getRData(request)
-    service, node = data[u'service'], data[u'node']
+    service, node = data[u"service"], data[u"node"]
     if node is None:
         self.pageRedirect(u"tickets_disco", request)
-    if node == u'@':
-        node = data[u'node'] = u''
+    if node == u"@":
+        node = data[u"node"] = u""
     template_data = request.template_data
-    template_data[u'url_tickets_list'] = self.getURL(service.full(), node or u'@')
-    template_data[u'url_tickets_new'] = self.getSubPageURL(request, 'tickets_new')
+    template_data[u"url_tickets_list"] = self.getURL(service.full(), node or u"@")
+    template_data[u"url_tickets_new"] = self.getSubPageURL(request, "tickets_new")
+
 
 @defer.inlineCallbacks
 def prepare_render(self, request):
     data = self.getRData(request)
     template_data = request.template_data
-    service, node = data[u'service'], data[u'node']
+    service, node = data[u"service"], data[u"node"]
     profile = self.getProfile(request) or C.SERVICE_PROFILE
 
-    self.checkCache(request, C.CACHE_PUBSUB, service=service, node=node, short='tickets')
+    self.checkCache(request, C.CACHE_PUBSUB, service=service, node=node, short="tickets")
 
-    tickets = yield self.host.bridgeCall('ticketsGet', service.full() if service else u'', node, C.NO_LIMIT, [], '', {'labels_as_list': C.BOOL_TRUE}, profile)
-    template_data[u'tickets'] = [template_xmlui.create(self.host, x) for x in tickets[0]]
-    template_data[u'on_ticket_click'] = data_objects.OnClick(url=self.getSubPageURL(request, u'tickets_view') + u'/{item.id}')
+    tickets = yield self.host.bridgeCall(
+        "ticketsGet",
+        service.full() if service else u"",
+        node,
+        C.NO_LIMIT,
+        [],
+        "",
+        {"labels_as_list": C.BOOL_TRUE},
+        profile,
+    )
+    template_data[u"tickets"] = [template_xmlui.create(self.host, x) for x in tickets[0]]
+    template_data[u"on_ticket_click"] = data_objects.OnClick(
+        url=self.getSubPageURL(request, u"tickets_view") + u"/{item.id}"
+    )
--- a/src/pages/tickets/view/page_meta.py	Sun Jun 24 22:21:25 2018 +0200
+++ b/src/pages/tickets/view/page_meta.py	Fri Jun 29 17:45:26 2018 +0200
@@ -11,7 +11,8 @@
 from sat.tools.common import uri
 from sat.tools.common import data_objects
 from sat.core.log import getLogger
-log = getLogger('pages/tickets/view')
+
+log = getLogger("pages/tickets/view")
 """ticket handling pages"""
 
 name = u"tickets_view"
@@ -27,53 +28,71 @@
         self.pageError(request, C.HTTP_BAD_REQUEST)
 
     data = self.getRData(request)
-    data[u'ticket_id'] = item_id
+    data[u"ticket_id"] = item_id
+
 
 @defer.inlineCallbacks
 def prepare_render(self, request):
     data = self.getRData(request)
     template_data = request.template_data
     session = self.host.getSessionData(request, session_iface.ISATSession)
-    service, node, ticket_id = data.get(u'service', u''), data.get(u'node', u''), data[u'ticket_id']
+    service, node, ticket_id = (
+        data.get(u"service", u""),
+        data.get(u"node", u""),
+        data[u"ticket_id"],
+    )
     profile = self.getProfile(request)
 
     if profile is None:
         profile = C.SERVICE_PROFILE
 
-    tickets = yield self.host.bridgeCall("ticketsGet", service.full() if service else u'', node, C.NO_LIMIT, [ticket_id], '', {'labels_as_list': C.BOOL_TRUE}, profile)
+    tickets = yield self.host.bridgeCall(
+        "ticketsGet",
+        service.full() if service else u"",
+        node,
+        C.NO_LIMIT,
+        [ticket_id],
+        "",
+        {"labels_as_list": C.BOOL_TRUE},
+        profile,
+    )
     ticket = [template_xmlui.create(self.host, x) for x in tickets[0]][0]
-    template_data[u'item'] = ticket
-    comments_uri = ticket.widgets['comments_uri'].value
+    template_data[u"item"] = ticket
+    comments_uri = ticket.widgets["comments_uri"].value
     if comments_uri:
         uri_data = uri.parseXMPPUri(comments_uri)
-        template_data['comments_node'] = comments_node = uri_data['node']
-        template_data['comments_service'] = comments_service = uri_data['path']
-        comments = yield self.host.bridgeCall("mbGet", comments_service,
-                                              comments_node,
-                                              C.NO_LIMIT,
-                                              [],
-                                              {},
-                                              profile)
+        template_data["comments_node"] = comments_node = uri_data["node"]
+        template_data["comments_service"] = comments_service = uri_data["path"]
+        comments = yield self.host.bridgeCall(
+            "mbGet", comments_service, comments_node, C.NO_LIMIT, [], {}, profile
+        )
 
-        template_data[u'comments'] = data_objects.BlogItems(comments)
-        template_data[u'login_url'] = self.getPageRedirectURL(request)
+        template_data[u"comments"] = data_objects.BlogItems(comments)
+        template_data[u"login_url"] = self.getPageRedirectURL(request)
 
     if session.connected:
         # we set edition URL only if user is the publisher or the node owner
-        publisher = jid.JID(ticket.widgets['publisher'].value)
+        publisher = jid.JID(ticket.widgets["publisher"].value)
         is_publisher = publisher.userhostJID() == session.jid.userhostJID()
         affiliation = None
         if not is_publisher:
-            node = node or self.host.ns_map['tickets']
+            node = node or self.host.ns_map["tickets"]
             affiliation = yield self.host.getAffiliation(request, service, node)
-        if is_publisher or affiliation == 'owner':
-            template_data[u'url_ticket_edit'] = self.getURLByPath(SubPage('tickets'), service.full(), node or u'@', SubPage('tickets_edit'), ticket_id)
+        if is_publisher or affiliation == "owner":
+            template_data[u"url_ticket_edit"] = self.getURLByPath(
+                SubPage("tickets"),
+                service.full(),
+                node or u"@",
+                SubPage("tickets_edit"),
+                ticket_id,
+            )
+
 
 @defer.inlineCallbacks
 def on_data_post(self, request):
-    type_ = self.getPostedData(request, u'type')
-    if type_ == u'comment':
-        blog_page = self.getPageByName(u'blog_view')
+    type_ = self.getPostedData(request, u"type")
+    if type_ == u"comment":
+        blog_page = self.getPageByName(u"blog_view")
         yield blog_page.on_data_post(self, request)
     else:
         log.warning(_(u"Unhandled data type: {}").format(type_))
--- a/src/pages/u/atom.xml/page_meta.py	Sun Jun 24 22:21:25 2018 +0200
+++ b/src/pages/u/atom.xml/page_meta.py	Fri Jun 29 17:45:26 2018 +0200
@@ -1,3 +1,3 @@
 #!/usr/bin/env python2.7
 # -*- coding: utf-8 -*-
-redirect = u'blog_feed_atom'
+redirect = u"blog_feed_atom"
--- a/src/pages/u/blog/page_meta.py	Sun Jun 24 22:21:25 2018 +0200
+++ b/src/pages/u/blog/page_meta.py	Fri Jun 29 17:45:26 2018 +0200
@@ -9,5 +9,7 @@
     # (i.e. what's remaining in URL: filters, id, etc.)
     # to be used by blog's url parser, so we don't skip parse_url
     data = self.getRData(request)
-    service = data[u'service']
-    self.pageRedirect(u'blog_view', request, skip_parse_url=False, path_args=[service.full(), u'@'])
+    service = data[u"service"]
+    self.pageRedirect(
+        u"blog_view", request, skip_parse_url=False, path_args=[service.full(), u"@"]
+    )
--- a/src/pages/u/page_meta.py	Sun Jun 24 22:21:25 2018 +0200
+++ b/src/pages/u/page_meta.py	Fri Jun 29 17:45:26 2018 +0200
@@ -23,14 +23,18 @@
     data = self.getRData(request)
 
     target_profile = yield self.host.bridgeCall("profileNameGet", prof_requested)
-    request.template_data[u'target_profile'] = target_profile
-    target_jid = yield self.host.bridgeCall("asyncGetParamA", 'JabberID', 'Connection', 'value', profile_key=target_profile)
+    request.template_data[u"target_profile"] = target_profile
+    target_jid = yield self.host.bridgeCall(
+        "asyncGetParamA", "JabberID", "Connection", "value", profile_key=target_profile
+    )
     target_jid = jid.JID(target_jid)
-    data[u'service'] = target_jid
+    data[u"service"] = target_jid
 
 
 @defer.inlineCallbacks
 def prepare_render(self, request):
     data = self.getRData(request)
-    self.checkCache(request, C.CACHE_PUBSUB, service=data[u'service'], node=None, short='microblog')
-    self.pageRedirect(u'blog_view', request)
+    self.checkCache(
+        request, C.CACHE_PUBSUB, service=data[u"service"], node=None, short="microblog"
+    )
+    self.pageRedirect(u"blog_view", request)
--- a/src/server/blog.py	Sun Jun 24 22:21:25 2018 +0200
+++ b/src/server/blog.py	Fri Jun 29 17:45:26 2018 +0200
@@ -21,6 +21,7 @@
 from sat.core.i18n import _, D_
 from sat_frontends.tools.strings import addURLToText, fixXHTMLLinks
 from sat.core.log import getLogger
+
 log = getLogger(__name__)
 from sat.tools.common import data_format
 from sat.tools import xml_tools
@@ -40,8 +41,13 @@
 from libervia.server.html_tools import sanitizeHtml, convertNewLinesToXHTML
 from libervia.server.constants import Const as C
 
-NS_ATOM = 'http://www.w3.org/2005/Atom'
-PARAMS_TO_GET = (C.STATIC_BLOG_PARAM_TITLE, C.STATIC_BLOG_PARAM_BANNER, C.STATIC_BLOG_PARAM_KEYWORDS, C.STATIC_BLOG_PARAM_DESCRIPTION)
+NS_ATOM = "http://www.w3.org/2005/Atom"
+PARAMS_TO_GET = (
+    C.STATIC_BLOG_PARAM_TITLE,
+    C.STATIC_BLOG_PARAM_BANNER,
+    C.STATIC_BLOG_PARAM_KEYWORDS,
+    C.STATIC_BLOG_PARAM_DESCRIPTION,
+)
 re_strip_empty_div = re.compile(r"<div ?/>|<div> *?</div>")
 
 # TODO: check disco features and use max_items when RSM is not available
@@ -58,7 +64,9 @@
     """
     default_query_data = {}
     try:
-        default_query_data['tag'] = request.extra_dict['mam_filter_{}'.format(C.MAM_FILTER_CATEGORY)].encode('utf-8')
+        default_query_data["tag"] = request.extra_dict[
+            "mam_filter_{}".format(C.MAM_FILTER_CATEGORY)
+        ].encode("utf-8")
     except KeyError:
         pass
     return default_query_data
@@ -70,7 +78,7 @@
     @param value(unicode): value to quote
     @return (str): quoted value
     """
-    return urllib.quote(value.encode('utf-8'), '')
+    return urllib.quote(value.encode("utf-8"), "")
 
 
 def _unquote(quoted_value):
@@ -80,18 +88,22 @@
     @return (unicode): unquoted value
     """
     assert not isinstance(quoted_value, unicode)
-    return urllib.unquote(quoted_value).decode('utf-8')
+    return urllib.unquote(quoted_value).decode("utf-8")
 
 
 def _urlencode(query):
     """Same as urllib.urlencode, but use '&amp;' instead of '&'"""
-    return '&amp;'.join(["{}={}".format(urllib.quote_plus(str(k)), urllib.quote_plus(str(v)))
-                          for k,v in query.iteritems()])
+    return "&amp;".join(
+        [
+            "{}={}".format(urllib.quote_plus(str(k)), urllib.quote_plus(str(v)))
+            for k, v in query.iteritems()
+        ]
+    )
 
 
 class TemplateProcessor(object):
 
-    THEME = 'default'
+    THEME = "default"
 
     def __init__(self, host):
         self.host = host
@@ -102,16 +114,17 @@
         self.env = Environment(loader=PackageLoader(themes, self.THEME))
 
     def useTemplate(self, request, tpl, data=None):
-        theme_url = os.path.join('/', C.THEMES_URL, self.THEME)
+        theme_url = os.path.join("/", C.THEMES_URL, self.THEME)
 
-        data_ = {'images': os.path.join(theme_url, 'images'),
-                 'styles': os.path.join(theme_url, 'styles'),
-                 }
+        data_ = {
+            "images": os.path.join(theme_url, "images"),
+            "styles": os.path.join(theme_url, "styles"),
+        }
         if data:
             data_.update(data)
 
-        template = self.env.get_template('{}.html'.format(tpl))
-        return template.render(**data_).encode('utf-8')
+        template = self.env.get_template("{}.html".format(tpl))
+        return template.render(**data_).encode("utf-8")
 
 
 class MicroBlog(Resource, TemplateProcessor):
@@ -129,7 +142,6 @@
         self.avatars_cache[bare_jid_s] = avatar_url
         return avatar_url
 
-
     def getAvatarURL(self, pub_jid, request):
         """Return avatar of a jid if in cache, else ask for it.
 
@@ -140,15 +152,21 @@
         try:
             url = self.avatars_cache[bare_jid_s]
         except KeyError:
-            self.avatars_cache[bare_jid_s] = ''  # avoid to request the vcard several times
-            d = self.host.bridgeCall('avatarGet', bare_jid_s, False, False, C.SERVICE_PROFILE)
+            self.avatars_cache[
+                bare_jid_s
+            ] = ""  # avoid to request the vcard several times
+            d = self.host.bridgeCall(
+                "avatarGet", bare_jid_s, False, False, C.SERVICE_PROFILE
+            )
             d.addCallback(self._avatarPathToUrl, request, bare_jid_s)
             return d
         return defer.succeed(url if url else C.DEFAULT_AVATAR_URL)
 
     def render_GET(self, request):
         if not request.postpath or len(request.postpath) > 2:
-            return self.useTemplate(request, "static_blog_error", {'message': "You must indicate a nickname"})
+            return self.useTemplate(
+                request, "static_blog_error", {"message": "You must indicate a nickname"}
+            )
 
         prof_requested = _unquote(request.postpath[0])
 
@@ -157,25 +175,34 @@
         except DBusException:
             prof_found = None
         if not prof_found or prof_found == C.SERVICE_PROFILE:
-            return self.useTemplate(request, "static_blog_error", {'message': "Invalid nickname"})
+            return self.useTemplate(
+                request, "static_blog_error", {"message": "Invalid nickname"}
+            )
 
         d = defer.Deferred()
         # TODO: jid caching
-        self.host.bridge.asyncGetParamA('JabberID', 'Connection', 'value', profile_key=prof_found, callback=d.callback, errback=d.errback)
+        self.host.bridge.asyncGetParamA(
+            "JabberID",
+            "Connection",
+            "value",
+            profile_key=prof_found,
+            callback=d.callback,
+            errback=d.errback,
+        )
         d.addCallback(self.render_gotJID, request, prof_found)
         return server.NOT_DONE_YET
 
     def render_gotJID(self, pub_jid_s, request, profile):
         pub_jid = JID(pub_jid_s)
 
-        request.extra_dict = {} # will be used for RSM and MAM
+        request.extra_dict = {}  # will be used for RSM and MAM
         self.parseURLParams(request)
         if request.item_id:
             # FIXME: this part seems useless
             # we want a specific item
             # item_ids = [request.item_id]
             # max_items = 1
-            max_items = C.NO_LIMIT # FIXME
+            max_items = C.NO_LIMIT  # FIXME
         else:
             # max_items = int(request.extra_dict['rsm_max']) # FIXME
             max_items = C.NO_LIMIT
@@ -183,16 +210,35 @@
 
         if request.atom:
             request.extra_dict.update(request.mam_extra)
-            self.getAtom(pub_jid, max_items, request.extra_dict, request.extra_comments_dict, request, profile)
+            self.getAtom(
+                pub_jid,
+                max_items,
+                request.extra_dict,
+                request.extra_comments_dict,
+                request,
+                profile,
+            )
 
         elif request.item_id:
             # we can't merge mam_extra now because we'll use item_ids
-            self.getItemById(pub_jid, request.item_id, request.extra_dict,
-                             request.extra_comments_dict, request, profile)
+            self.getItemById(
+                pub_jid,
+                request.item_id,
+                request.extra_dict,
+                request.extra_comments_dict,
+                request,
+                profile,
+            )
         else:
             request.extra_dict.update(request.mam_extra)
-            self.getItems(pub_jid, max_items, request.extra_dict,
-                          request.extra_comments_dict, request, profile)
+            self.getItems(
+                pub_jid,
+                max_items,
+                request.extra_dict,
+                request.extra_comments_dict,
+                request,
+                profile,
+            )
 
     ## URL parsing
 
@@ -202,7 +248,7 @@
         @param request: HTTP request
         """
         if len(request.postpath) > 1:
-            if request.postpath[1] == 'atom.xml':  # return the atom feed
+            if request.postpath[1] == "atom.xml":  # return the atom feed
                 request.atom = True
                 request.item_id = None
             else:
@@ -214,7 +260,9 @@
 
         self.parseURLParamsRSM(request)
         # XXX: request.display_single is True when only one blog post is visible
-        request.display_single = (request.item_id is not None) or int(request.extra_dict['rsm_max']) == 1
+        request.display_single = (request.item_id is not None) or int(
+            request.extra_dict["rsm_max"]
+        ) == 1
         self.parseURLParamsCommentsRSM(request)
         self.parseURLParamsMAM(request)
 
@@ -227,21 +275,25 @@
         if request.item_id:  # XXX: item_id and RSM are not compatible
             return
         try:
-            rsm_max = int(request.args['max'][0])
+            rsm_max = int(request.args["max"][0])
             if rsm_max > C.STATIC_RSM_MAX_LIMIT:
                 log.warning(u"Request with rsm_max over limit ({})".format(rsm_max))
                 rsm_max = C.STATIC_RSM_MAX_LIMIT
-            request.extra_dict['rsm_max'] = unicode(rsm_max)
+            request.extra_dict["rsm_max"] = unicode(rsm_max)
         except (ValueError, KeyError):
-            request.extra_dict['rsm_max'] = unicode(C.STATIC_RSM_MAX_DEFAULT)
+            request.extra_dict["rsm_max"] = unicode(C.STATIC_RSM_MAX_DEFAULT)
         try:
-            request.extra_dict['rsm_index'] = request.args['index'][0]
+            request.extra_dict["rsm_index"] = request.args["index"][0]
         except (ValueError, KeyError):
             try:
-                request.extra_dict['rsm_before'] = request.args['before'][0].decode('utf-8')
+                request.extra_dict["rsm_before"] = request.args["before"][0].decode(
+                    "utf-8"
+                )
             except KeyError:
                 try:
-                    request.extra_dict['rsm_after'] = request.args['after'][0].decode('utf-8')
+                    request.extra_dict["rsm_after"] = request.args["after"][0].decode(
+                        "utf-8"
+                    )
                 except KeyError:
                     pass
 
@@ -254,15 +306,17 @@
         request.extra_comments_dict = {}
         if request.display_single:
             try:
-                rsm_max = int(request.args['comments_max'][0])
+                rsm_max = int(request.args["comments_max"][0])
                 if rsm_max > C.STATIC_RSM_MAX_LIMIT:
                     log.warning(u"Request with rsm_max over limit ({})".format(rsm_max))
                     rsm_max = C.STATIC_RSM_MAX_LIMIT
-                request.extra_comments_dict['rsm_max'] = unicode(rsm_max)
+                request.extra_comments_dict["rsm_max"] = unicode(rsm_max)
             except (ValueError, KeyError):
-                request.extra_comments_dict['rsm_max'] = unicode(C.STATIC_RSM_MAX_COMMENTS_DEFAULT)
+                request.extra_comments_dict["rsm_max"] = unicode(
+                    C.STATIC_RSM_MAX_COMMENTS_DEFAULT
+                )
         else:
-            request.extra_comments_dict['rsm_max'] = "0"
+            request.extra_comments_dict["rsm_max"] = "0"
 
     def parseURLParamsMAM(self, request):
         """Parse MAM request data from the URL parameters for main items
@@ -276,13 +330,17 @@
         #      for navigation links.
         request.mam_extra = {}
         try:
-            request.mam_extra['mam_filter_{}'.format(C.MAM_FILTER_CATEGORY)] = request.args['tag'][0].decode('utf-8')
+            request.mam_extra[
+                "mam_filter_{}".format(C.MAM_FILTER_CATEGORY)
+            ] = request.args["tag"][0].decode("utf-8")
         except KeyError:
             pass
 
     ## Items retrieval
 
-    def getItemById(self, pub_jid, item_id, extra_dict, extra_comments_dict, request, profile):
+    def getItemById(
+        self, pub_jid, item_id, extra_dict, extra_comments_dict, request, profile
+    ):
         """
 
         @param pub_jid (jid.JID): publisher JID
@@ -300,46 +358,76 @@
             def gotMetadata(result):
                 dummy, rsm_metadata = result
                 try:
-                    metadata['rsm_count'] = rsm_metadata['rsm_count']
+                    metadata["rsm_count"] = rsm_metadata["rsm_count"]
                 except KeyError:
                     pass
                 try:
-                    metadata['rsm_index'] = unicode(int(rsm_metadata['rsm_index'])-1)
+                    metadata["rsm_index"] = unicode(int(rsm_metadata["rsm_index"]) - 1)
                 except KeyError:
                     pass
 
-                metadata['rsm_first'] = metadata['rsm_last'] = item["id"]
+                metadata["rsm_first"] = metadata["rsm_last"] = item["id"]
 
                 def gotComments(comments):
                     # at this point we can merge mam dict
                     request.extra_dict.update(request.mam_extra)
                     # build the items as self.getItems would do it (and as self.renderHTML expects them to be)
-                    comments = [(item['comments_service'], item['comments_node'], "", comments[0], comments[1])]
-                    self.renderHTML([(item, comments)], metadata, request, pub_jid, profile)
+                    comments = [
+                        (
+                            item["comments_service"],
+                            item["comments_node"],
+                            "",
+                            comments[0],
+                            comments[1],
+                        )
+                    ]
+                    self.renderHTML(
+                        [(item, comments)], metadata, request, pub_jid, profile
+                    )
 
                 # get the comments
                 # max_comments = int(extra_comments_dict['rsm_max']) # FIXME
                 max_comments = C.NO_LIMIT
                 # TODO: use max_comments only when RSM is not available
-                self.host.bridge.mbGet(item['comments_service'], item['comments_node'], max_comments, [],
-                    extra_comments_dict, C.SERVICE_PROFILE,
+                self.host.bridge.mbGet(
+                    item["comments_service"],
+                    item["comments_node"],
+                    max_comments,
+                    [],
+                    extra_comments_dict,
+                    C.SERVICE_PROFILE,
                     callback=gotComments,
-                    errback=lambda failure: self.renderError(failure, request, pub_jid))
+                    errback=lambda failure: self.renderError(failure, request, pub_jid),
+                )
 
             # XXX: retrieve RSM information related to the main item. We can't do it while
             # retrieving the item, because item_ids and rsm should not be used together.
-            self.host.bridge.mbGet(pub_jid.userhost(), '', 0, [],
-                {"rsm_max": "1", "rsm_after": item["id"]}, C.SERVICE_PROFILE,
+            self.host.bridge.mbGet(
+                pub_jid.userhost(),
+                "",
+                0,
+                [],
+                {"rsm_max": "1", "rsm_after": item["id"]},
+                C.SERVICE_PROFILE,
                 callback=gotMetadata,
-                errback=lambda failure: self.renderError(failure, request, pub_jid))
+                errback=lambda failure: self.renderError(failure, request, pub_jid),
+            )
 
         # get the main item
-        self.host.bridge.mbGet(pub_jid.userhost(), '', 0, [item_id],
-            extra_dict, C.SERVICE_PROFILE,
+        self.host.bridge.mbGet(
+            pub_jid.userhost(),
+            "",
+            0,
+            [item_id],
+            extra_dict,
+            C.SERVICE_PROFILE,
             callback=gotItems,
-            errback=lambda failure: self.renderError(failure, request, pub_jid))
+            errback=lambda failure: self.renderError(failure, request, pub_jid),
+        )
 
-    def getItems(self, pub_jid, max_items, extra_dict, extra_comments_dict, request, profile):
+    def getItems(
+        self, pub_jid, max_items, extra_dict, extra_comments_dict, request, profile
+    ):
         """
 
         @param pub_jid (jid.JID): publisher JID
@@ -349,6 +437,7 @@
         @param request: HTTP request
         @param profile
         """
+
         def getResultCb(data, rt_session):
             remaining, results = data
             # we have requested one node only
@@ -361,18 +450,30 @@
                 self.renderHTML(items, metadata, request, pub_jid, profile)
 
         def getResult(rt_session):
-            self.host.bridge.mbGetFromManyWithCommentsRTResult(rt_session, C.SERVICE_PROFILE,
-                                                               callback=lambda data: getResultCb(data, rt_session),
-                                                               errback=lambda failure: self.renderError(failure, request, pub_jid))
+            self.host.bridge.mbGetFromManyWithCommentsRTResult(
+                rt_session,
+                C.SERVICE_PROFILE,
+                callback=lambda data: getResultCb(data, rt_session),
+                errback=lambda failure: self.renderError(failure, request, pub_jid),
+            )
 
         # max_comments = int(extra_comments_dict['rsm_max']) # FIXME
         max_comments = 0
         # TODO: use max_comments only when RSM is not available
-        self.host.bridge.mbGetFromManyWithComments(C.JID, [pub_jid.userhost()], max_items,
-                                                   max_comments, extra_dict, extra_comments_dict,
-                                                   C.SERVICE_PROFILE, callback=getResult)
+        self.host.bridge.mbGetFromManyWithComments(
+            C.JID,
+            [pub_jid.userhost()],
+            max_items,
+            max_comments,
+            extra_dict,
+            extra_comments_dict,
+            C.SERVICE_PROFILE,
+            callback=getResult,
+        )
 
-    def getAtom(self, pub_jid, max_items, extra_dict, extra_comments_dict, request, profile):
+    def getAtom(
+        self, pub_jid, max_items, extra_dict, extra_comments_dict, request, profile
+    ):
         """
 
         @param pub_jid (jid.JID): publisher JID
@@ -382,79 +483,93 @@
         @param request: HTTP request
         @param profile
         """
+
         def gotItems(data):
             # Generate a clean atom feed with uri linking to this blog
             # from microblog data
-            items, metadata= data
-            feed_elt = domish.Element((NS_ATOM, u'feed'))
+            items, metadata = data
+            feed_elt = domish.Element((NS_ATOM, u"feed"))
             title = _(u"{user}'s blog").format(user=profile)
-            feed_elt.addElement(u'title', content=title)
+            feed_elt.addElement(u"title", content=title)
 
-            base_blog_url = self.host.getExtBaseURL(request,
-                u'blog/{user}'.format(user=profile))
+            base_blog_url = self.host.getExtBaseURL(
+                request, u"blog/{user}".format(user=profile)
+            )
 
             # atom link
-            link_feed_elt = feed_elt.addElement('link')
-            link_feed_elt['href'] = u'{base}/atom.xml'.format(base=base_blog_url)
-            link_feed_elt['type'] = u'application/atom+xml'
-            link_feed_elt['rel'] = u'self'
+            link_feed_elt = feed_elt.addElement("link")
+            link_feed_elt["href"] = u"{base}/atom.xml".format(base=base_blog_url)
+            link_feed_elt["type"] = u"application/atom+xml"
+            link_feed_elt["rel"] = u"self"
 
             # blog link
-            link_blog_elt = feed_elt.addElement('link')
-            link_blog_elt['rel'] = u'alternate'
-            link_blog_elt['type'] = u'text/html'
-            link_blog_elt['href'] = base_blog_url
+            link_blog_elt = feed_elt.addElement("link")
+            link_blog_elt["rel"] = u"alternate"
+            link_blog_elt["type"] = u"text/html"
+            link_blog_elt["href"] = base_blog_url
 
             # blog link XMPP uri
-            blog_xmpp_uri = metadata['uri']
-            link_blog_elt = feed_elt.addElement('link')
-            link_blog_elt['rel'] = u'alternate'
-            link_blog_elt['type'] = u'application/atom+xml'
-            link_blog_elt['href'] = blog_xmpp_uri
+            blog_xmpp_uri = metadata["uri"]
+            link_blog_elt = feed_elt.addElement("link")
+            link_blog_elt["rel"] = u"alternate"
+            link_blog_elt["type"] = u"application/atom+xml"
+            link_blog_elt["href"] = blog_xmpp_uri
 
-            feed_elt.addElement('id', content=_quote(blog_xmpp_uri))
-            updated_unix = max([float(item['updated']) for item in items])
+            feed_elt.addElement("id", content=_quote(blog_xmpp_uri))
+            updated_unix = max([float(item["updated"]) for item in items])
             updated_dt = datetime.fromtimestamp(updated_unix)
-            feed_elt.addElement(u'updated', content=u'{}Z'.format(updated_dt.isoformat("T")))
+            feed_elt.addElement(
+                u"updated", content=u"{}Z".format(updated_dt.isoformat("T"))
+            )
 
             for item in items:
-                entry_elt = feed_elt.addElement(u'entry')
+                entry_elt = feed_elt.addElement(u"entry")
 
                 # Title
                 try:
-                    title = item['title']
+                    title = item["title"]
                 except KeyError:
                     # for microblog (without title), we use an abstract of content as title
-                    title = u'{}…'.format(u' '.join(item['content'][:70].split()))
-                entry_elt.addElement(u'title', content=title)
+                    title = u"{}…".format(u" ".join(item["content"][:70].split()))
+                entry_elt.addElement(u"title", content=title)
 
                 # HTTP link
-                http_link_elt = entry_elt.addElement(u'link')
-                http_link_elt['rel'] = u'alternate'
-                http_link_elt['type'] = u'text/html'
-                http_link_elt['href'] = u'{base}/{quoted_id}'.format(base=base_blog_url, quoted_id=_quote(item['id']))
+                http_link_elt = entry_elt.addElement(u"link")
+                http_link_elt["rel"] = u"alternate"
+                http_link_elt["type"] = u"text/html"
+                http_link_elt["href"] = u"{base}/{quoted_id}".format(
+                    base=base_blog_url, quoted_id=_quote(item["id"])
+                )
                 # XMPP link
-                xmpp_link_elt = entry_elt.addElement(u'link')
-                xmpp_link_elt['rel'] = u'alternate'
-                xmpp_link_elt['type'] = u'application/atom+xml'
-                xmpp_link_elt['href'] = u'{blog_uri};item={item_id}'.format(blog_uri=blog_xmpp_uri, item_id=item['id'])
+                xmpp_link_elt = entry_elt.addElement(u"link")
+                xmpp_link_elt["rel"] = u"alternate"
+                xmpp_link_elt["type"] = u"application/atom+xml"
+                xmpp_link_elt["href"] = u"{blog_uri};item={item_id}".format(
+                    blog_uri=blog_xmpp_uri, item_id=item["id"]
+                )
 
                 # date metadata
-                entry_elt.addElement(u'id', content=item['atom_id'])
-                updated = datetime.fromtimestamp(float(item['updated']))
-                entry_elt.addElement(u'updated', content=u'{}Z'.format(updated.isoformat("T")))
-                published = datetime.fromtimestamp(float(item['published']))
-                entry_elt.addElement(u'published', content=u'{}Z'.format(published.isoformat("T")))
+                entry_elt.addElement(u"id", content=item["atom_id"])
+                updated = datetime.fromtimestamp(float(item["updated"]))
+                entry_elt.addElement(
+                    u"updated", content=u"{}Z".format(updated.isoformat("T"))
+                )
+                published = datetime.fromtimestamp(float(item["published"]))
+                entry_elt.addElement(
+                    u"published", content=u"{}Z".format(published.isoformat("T"))
+                )
 
                 # author metadata
-                author_elt = entry_elt.addElement(u'author')
-                author_elt.addElement('name', content=item.get('author', profile))
+                author_elt = entry_elt.addElement(u"author")
+                author_elt.addElement("name", content=item.get("author", profile))
                 try:
-                    author_elt.addElement('uri', content=u'xmpp:{}'.format(item['author_jid']))
+                    author_elt.addElement(
+                        "uri", content=u"xmpp:{}".format(item["author_jid"])
+                    )
                 except KeyError:
                     pass
                 try:
-                    author_elt.addElement('email', content=item['author_email'])
+                    author_elt.addElement("email", content=item["author_email"])
                 except KeyError:
                     pass
 
@@ -465,19 +580,31 @@
 
                 # content
                 try:
-                    content_xhtml = item['content_xhtml']
+                    content_xhtml = item["content_xhtml"]
                 except KeyError:
-                    content_elt = entry_elt.addElement('content', content='content')
-                    content_elt['type'] = 'text'
+                    content_elt = entry_elt.addElement("content", content="content")
+                    content_elt["type"] = "text"
                 else:
-                    content_elt = entry_elt.addElement('content')
-                    content_elt['type'] = 'xhtml'
-                    content_elt.addChild(xml_tools.ElementParser()(content_xhtml, namespace=C.NS_XHTML))
+                    content_elt = entry_elt.addElement("content")
+                    content_elt["type"] = "xhtml"
+                    content_elt.addChild(
+                        xml_tools.ElementParser()(content_xhtml, namespace=C.NS_XHTML)
+                    )
 
-            atom_feed = u'<?xml version="1.0" encoding="utf-8"?>\n{}'.format(feed_elt.toXml())
+            atom_feed = u'<?xml version="1.0" encoding="utf-8"?>\n{}'.format(
+                feed_elt.toXml()
+            )
             self.renderAtomFeed(atom_feed, request),
 
-        self.host.bridge.mbGet(pub_jid.userhost(), '', max_items, [], extra_dict, C.SERVICE_PROFILE, callback=gotItems)
+        self.host.bridge.mbGet(
+            pub_jid.userhost(),
+            "",
+            max_items,
+            [],
+            extra_dict,
+            C.SERVICE_PROFILE,
+            callback=gotItems,
+        )
 
     ## rendering
 
@@ -486,8 +613,10 @@
 
     def _getImageParams(self, options, key, default, alt):
         """regexp from http://answers.oreilly.com/topic/280-how-to-validate-urls-with-regular-expressions/"""
-        url = options[key] if key in options else ''
-        regexp = r"^(https?|ftp)://[a-z0-9-]+(\.[a-z0-9-]+)+(/[\w-]+)*/[\w-]+\.(gif|png|jpg)$"
+        url = options[key] if key in options else ""
+        regexp = (
+            r"^(https?|ftp)://[a-z0-9-]+(\.[a-z0-9-]+)+(/[\w-]+)*/[\w-]+\.(gif|png|jpg)$"
+        )
         if re.match(regexp, url):
             url = url
         else:
@@ -496,7 +625,11 @@
 
     def renderError(self, failure, request, pub_jid):
         request.setResponseCode(500)
-        request.write(self.useTemplate(request, "static_blog_error", {'message': "Can't access requested data"}))
+        request.write(
+            self.useTemplate(
+                request, "static_blog_error", {"message": "Can't access requested data"}
+            )
+        )
         request.finish()
 
     def renderHTML(self, items, metadata, request, pub_jid, profile):
@@ -512,19 +645,29 @@
         options = {}
 
         d = self.getAvatarURL(pub_jid, request)
-        d.addCallback(self._updateDict, options, 'avatar')
+        d.addCallback(self._updateDict, options, "avatar")
         d.addErrback(self.renderError, request, pub_jid)
         d_list.append(d)
 
         for param_name in PARAMS_TO_GET:
             d = defer.Deferred()
-            self.host.bridge.asyncGetParamA(param_name, C.STATIC_BLOG_KEY, 'value', C.SERVER_SECURITY_LIMIT, profile, callback=d.callback, errback=d.errback)
+            self.host.bridge.asyncGetParamA(
+                param_name,
+                C.STATIC_BLOG_KEY,
+                "value",
+                C.SERVER_SECURITY_LIMIT,
+                profile,
+                callback=d.callback,
+                errback=d.errback,
+            )
             d.addCallback(self._updateDict, options, param_name)
             d.addErrback(self.renderError, request, pub_jid)
             d_list.append(d)
 
         dlist_d = defer.DeferredList(d_list)
-        dlist_d.addCallback(lambda dummy: self._renderHTML(items, metadata, options, request, pub_jid))
+        dlist_d.addCallback(
+            lambda dummy: self._renderHTML(items, metadata, options, request, pub_jid)
+        )
 
     def _renderHTML(self, items, metadata, options, request, pub_jid):
         """Actually render the static blog.
@@ -548,55 +691,59 @@
         if not isinstance(options, dict):
             options = {}
         user = sanitizeHtml(pub_jid.user)
-        base_url = os.path.join('/blog/',user)
+        base_url = os.path.join("/blog/", user)
 
         def getOption(key):
-            return sanitizeHtml(options[key]) if key in options else ''
+            return sanitizeHtml(options[key]) if key in options else ""
 
-        avatar = os.path.normpath('/{}'.format(getOption('avatar')))
+        avatar = os.path.normpath("/{}".format(getOption("avatar")))
         title = getOption(C.STATIC_BLOG_PARAM_TITLE) or user
-        query_data = _urlencode(getDefaultQueryData(request)).decode('utf-8')
+        query_data = _urlencode(getDefaultQueryData(request)).decode("utf-8")
 
-        xmpp_uri = metadata['uri']
+        xmpp_uri = metadata["uri"]
         if len(items) == 1:
             # FIXME: that's really not a good way to get item id
             #        this must be changed after static blog refactorisation
-            item_id = items[0][0]['id']
-            xmpp_uri+=u";item={}".format(_quote(item_id))
+            item_id = items[0][0]["id"]
+            xmpp_uri += u";item={}".format(_quote(item_id))
 
-        data = {'url_base': base_url,
-                'xmpp_uri': xmpp_uri,
-                'url_query': u'?{}'.format(query_data) if query_data else '' ,
-                'keywords': getOption(C.STATIC_BLOG_PARAM_KEYWORDS),
-                'description': getOption(C.STATIC_BLOG_PARAM_DESCRIPTION),
-                'title': title,
-                'favicon': avatar,
-                'banner_img': self._getImageParams(options, C.STATIC_BLOG_PARAM_BANNER, avatar, title)
-                }
+        data = {
+            "url_base": base_url,
+            "xmpp_uri": xmpp_uri,
+            "url_query": u"?{}".format(query_data) if query_data else "",
+            "keywords": getOption(C.STATIC_BLOG_PARAM_KEYWORDS),
+            "description": getOption(C.STATIC_BLOG_PARAM_DESCRIPTION),
+            "title": title,
+            "favicon": avatar,
+            "banner_img": self._getImageParams(
+                options, C.STATIC_BLOG_PARAM_BANNER, avatar, title
+            ),
+        }
 
-        data['navlinks'] = NavigationLinks(request, items, metadata, base_url)
-        data['messages'] = []
+        data["navlinks"] = NavigationLinks(request, items, metadata, base_url)
+        data["messages"] = []
         for item in items:
             item, comments_list = item
             comments, comments_count = [], 0
             for node_comments in comments_list:
                 comments.extend(node_comments[3])
                 try:
-                    comments_count += int(node_comments[4]['rsm_count'])
+                    comments_count += int(node_comments[4]["rsm_count"])
                 except KeyError:
                     pass
-            data['messages'].append(BlogMessage(request, base_url, item, comments, comments_count))
+            data["messages"].append(
+                BlogMessage(request, base_url, item, comments, comments_count)
+            )
 
-        request.write(self.useTemplate(request, 'static_blog', data))
+        request.write(self.useTemplate(request, "static_blog", data))
         request.finish()
 
     def renderAtomFeed(self, feed, request):
-        request.write(feed.encode('utf-8'))
+        request.write(feed.encode("utf-8"))
         request.finish()
 
 
 class NavigationLinks(object):
-
     def __init__(self, request, items, metadata, base_url):
         """Build the navigation links.
 
@@ -613,35 +760,35 @@
 
         # which links we need to display
         if request.display_single:
-            links = ('later_message', 'older_message')
+            links = ("later_message", "older_message")
             # key must exist when using the template
-            self.later_messages = self.older_messages = ''
+            self.later_messages = self.older_messages = ""
         else:
-            links = ('later_messages', 'older_messages')
-            self.later_message = self.older_message = ''
+            links = ("later_messages", "older_messages")
+            self.later_message = self.older_message = ""
 
         # now we set the links according to RSM
         for key in links:
             query_data = default_query_data.copy()
 
-            if key.startswith('later_message'):
+            if key.startswith("later_message"):
                 try:
-                    index = int(metadata['rsm_index'])
+                    index = int(metadata["rsm_index"])
                 except (KeyError, ValueError):
                     pass
                 else:
                     if index == 0:
                         # we don't show this link on first page
-                        setattr(self, key, '')
+                        setattr(self, key, "")
                         continue
                 try:
-                    query_data['before'] = metadata['rsm_first'].encode('utf-8')
+                    query_data["before"] = metadata["rsm_first"].encode("utf-8")
                 except KeyError:
                     pass
             else:
                 try:
-                    index = int(metadata['rsm_index'])
-                    count = int(metadata.get('rsm_count'))
+                    index = int(metadata["rsm_index"])
+                    count = int(metadata.get("rsm_count"))
                 except (KeyError, ValueError):
                     # XXX: if we don't have index or count, we can't know if we
                     #      are on the last page or not
@@ -650,29 +797,27 @@
                     # if we have index, we don't show the after link
                     # on the last page
                     if index + len(items) >= count:
-                        setattr(self, key, '')
+                        setattr(self, key, "")
                         continue
                 try:
-                    query_data['after'] = metadata['rsm_last'].encode('utf-8')
+                    query_data["after"] = metadata["rsm_last"].encode("utf-8")
                 except KeyError:
                     pass
 
             if request.display_single:
-                query_data['max'] = 1
+                query_data["max"] = 1
 
             link = "{}?{}".format(base_url, _urlencode(query_data))
-            setattr(self, key, BlogLink(link, key, key.replace('_', ' ')))
+            setattr(self, key, BlogLink(link, key, key.replace("_", " ")))
 
 
 class BlogImage(object):
-
     def __init__(self, url_, alt):
         self.url = url_
         self.alt = alt
 
 
 class BlogLink(object):
-
     def __init__(self, url_, style, text):
         self.url = url_
         self.style = style
@@ -680,7 +825,6 @@
 
 
 class BlogMessage(object):
-
     def __init__(self, request, base_url, entry, comments=None, comments_count=0):
         """
 
@@ -692,47 +836,54 @@
         """
         if comments is None:
             comments = []
-        timestamp = float(entry.get('published', 0))
+        timestamp = float(entry.get("published", 0))
 
         # FIXME: for now we assume that the comments' depth is only 1
-        is_comment = not entry.get('comments', False)
+        is_comment = not entry.get("comments", False)
 
         self.date = datetime.fromtimestamp(timestamp)
         self.type = "comment" if is_comment else "main_item"
-        self.style = 'mblog_comment' if is_comment else ''
-        self.content = self.getText(entry, 'content')
+        self.style = "mblog_comment" if is_comment else ""
+        self.content = self.getText(entry, "content")
 
         if is_comment:
-            self.author = (_(u"from {}").format(entry['author']))
+            self.author = _(u"from {}").format(entry["author"])
         else:
-            self.author = '&nbsp;'
-            self.url = "{}/{}".format(base_url, _quote(entry['id']))
+            self.author = "&nbsp;"
+            self.url = "{}/{}".format(base_url, _quote(entry["id"]))
             query_data = getDefaultQueryData(request)
             if query_data:
-                self.url += '?{}'.format(_urlencode(query_data))
-            self.title = self.getText(entry, 'title')
-            self.tags = [sanitizeHtml(tag) for tag in data_format.dict2iter('tag', entry)]
+                self.url += "?{}".format(_urlencode(query_data))
+            self.title = self.getText(entry, "title")
+            self.tags = [sanitizeHtml(tag) for tag in data_format.dict2iter("tag", entry)]
 
-            count_text = lambda count: D_(u'comments') if count > 1 else D_(u'comment')
+            count_text = lambda count: D_(u"comments") if count > 1 else D_(u"comment")
 
-            self.comments_text = u"{} {}".format(comments_count, count_text(comments_count))
+            self.comments_text = u"{} {}".format(
+                comments_count, count_text(comments_count)
+            )
 
             delta = comments_count - len(comments)
             if request.display_single and delta > 0:
-                prev_url = "{}?{}".format(self.url, _urlencode({'comments_max': comments_count}))
+                prev_url = "{}?{}".format(
+                    self.url, _urlencode({"comments_max": comments_count})
+                )
                 prev_text = D_(u"show {count} previous {comments}").format(
-                    count = delta, comments = count_text(delta))
+                    count=delta, comments=count_text(delta)
+                )
                 self.all_comments_link = BlogLink(prev_url, "comments_link", prev_text)
 
         if comments:
-            self.comments = [BlogMessage(request, base_url, comment) for comment in comments]
+            self.comments = [
+                BlogMessage(request, base_url, comment) for comment in comments
+            ]
 
     def getText(self, entry, key):
         try:
-            xhtml = entry['{}_xhtml'.format(key)]
+            xhtml = entry["{}_xhtml".format(key)]
         except KeyError:
             try:
-                processor = addURLToText if key.startswith('content') else sanitizeHtml
+                processor = addURLToText if key.startswith("content") else sanitizeHtml
                 return convertNewLinesToXHTML(processor(entry[key]))
             except KeyError:
                 return None
--- a/src/server/constants.py	Sun Jun 24 22:21:25 2018 +0200
+++ b/src/server/constants.py	Fri Jun 29 17:45:26 2018 +0200
@@ -22,9 +22,9 @@
 
 class Const(constants.Const):
 
-    APP_NAME = 'Libervia'
+    APP_NAME = "Libervia"
     APP_NAME_FILE = "libervia"
-    SERVICE_PROFILE = 'libervia'  # the SàT profile that is used for exporting the service
+    SERVICE_PROFILE = "libervia"  # the SàT profile that is used for exporting the service
 
     SESSION_TIMEOUT = 7200  # Session's timeout, after that the user will be disconnected
     HTML_DIR = "html/"
@@ -44,7 +44,7 @@
     SERVER_SECURITY_LIMIT = constants.Const.NO_SECURITY_LIMIT
 
     # keys for cache values we can get from browser
-    ALLOWED_ENTITY_DATA = {'avatar', 'nick'}
+    ALLOWED_ENTITY_DATA = {"avatar", "nick"}
 
     STATIC_RSM_MAX_LIMIT = 100
     STATIC_RSM_MAX_DEFAULT = 10
@@ -52,13 +52,33 @@
 
     ## Libervia pages ##
     PAGES_META_FILE = u"page_meta.py"
-    PAGES_ACCESS_NONE = u"none"  # no access to this page (using its path will return a 404 error)
+    PAGES_ACCESS_NONE = (
+        u"none"
+    )  #  no access to this page (using its path will return a 404 error)
     PAGES_ACCESS_PUBLIC = u"public"
-    PAGES_ACCESS_PROFILE = u"profile"  # a session with an existing profile must be started
-    PAGES_ACCESS_ADMIN = u"admin"  # only profiles set in admins_list can access the page
-    PAGES_ACCESS_ALL = (PAGES_ACCESS_NONE, PAGES_ACCESS_PUBLIC, PAGES_ACCESS_PROFILE, PAGES_ACCESS_ADMIN)
+    PAGES_ACCESS_PROFILE = (
+        u"profile"
+    )  # a session with an existing profile must be started
+    PAGES_ACCESS_ADMIN = u"admin"  #  only profiles set in admins_list can access the page
+    PAGES_ACCESS_ALL = (
+        PAGES_ACCESS_NONE,
+        PAGES_ACCESS_PUBLIC,
+        PAGES_ACCESS_PROFILE,
+        PAGES_ACCESS_ADMIN,
+    )
     # names of the page to use for menu
-    DEFAULT_MENU = ['login', 'chat', 'blog', 'forums', 'photos', 'files', 'events', 'tickets', 'merge-requests', 'app']
+    DEFAULT_MENU = [
+        "login",
+        "chat",
+        "blog",
+        "forums",
+        "photos",
+        "files",
+        "events",
+        "tickets",
+        "merge-requests",
+        "app",
+    ]
 
     ## Session flags ##
     FLAG_CONFIRM = u"CONFIRM"
@@ -67,8 +87,8 @@
     POST_NO_CONFIRM = u"POST_NO_CONFIRM"
 
     ## HTTP methods ##
-    HTTP_METHOD_GET = u'GET'
-    HTTP_METHOD_POST = u'POST'
+    HTTP_METHOD_GET = u"GET"
+    HTTP_METHOD_POST = u"POST"
 
     ## HTTP codes ##
     HTTP_SEE_OTHER = 303
@@ -84,4 +104,17 @@
 
     ## Date/Time ##
     HTTP_DAYS = ("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun")
-    HTTP_MONTH = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec")
+    HTTP_MONTH = (
+        "Jan",
+        "Feb",
+        "Mar",
+        "Apr",
+        "May",
+        "Jun",
+        "Jul",
+        "Aug",
+        "Sep",
+        "Oct",
+        "Nov",
+        "Dec",
+    )
--- a/src/server/html_tools.py	Sun Jun 24 22:21:25 2018 +0200
+++ b/src/server/html_tools.py	Fri Jun 29 17:45:26 2018 +0200
@@ -20,17 +20,17 @@
 
 def sanitizeHtml(text):
     """Sanitize HTML by escaping everything"""
-    #this code comes from official python wiki: http://wiki.python.org/moin/EscapingHtml
+    # this code comes from official python wiki: http://wiki.python.org/moin/EscapingHtml
     html_escape_table = {
         "&": "&amp;",
         '"': "&quot;",
         "'": "&apos;",
         ">": "&gt;",
         "<": "&lt;",
-        }
+    }
 
     return "".join(html_escape_table.get(c, c) for c in text)
 
 
 def convertNewLinesToXHTML(text):
-    return text.replace('\n', '<br/>')
+    return text.replace("\n", "<br/>")
--- a/src/server/pages.py	Sun Jun 24 22:21:25 2018 +0200
+++ b/src/server/pages.py	Fri Jun 29 17:45:26 2018 +0200
@@ -28,6 +28,7 @@
 from sat.tools.common import uri as common_uri
 from sat.tools.common import date_utils
 from sat.core.log import getLogger
+
 log = getLogger(__name__)
 from libervia.server.constants import Const as C
 from libervia.server import session_iface
@@ -41,12 +42,10 @@
 import time
 import hashlib
 
-WebsocketMeta = namedtuple("WebsocketMeta", ('url', 'token', 'debug'))
-
+WebsocketMeta = namedtuple("WebsocketMeta", ("url", "token", "debug"))
 
 
 class CacheBase(object):
-
     def __init__(self):
         self._created = time.time()
         self._last_access = self._created
@@ -65,7 +64,6 @@
 
 
 class CachePage(CacheBase):
-
     def __init__(self, rendered):
         super(CachePage, self).__init__()
         self._created = time.time()
@@ -83,7 +81,6 @@
 
 
 class CacheURL(CacheBase):
-
     def __init__(self, request):
         super(CacheURL, self).__init__()
         try:
@@ -93,7 +90,7 @@
         self._template_data = request.template_data.copy()
         self._prepath = request.prepath[:]
         self._postpath = request.postpath[:]
-        del self._template_data['csrf_token']
+        del self._template_data["csrf_token"]
 
     def use(self, request):
         self.last_access = time.time()
@@ -104,22 +101,36 @@
 
 
 class LiberviaPage(web_resource.Resource):
-    isLeaf = True  # we handle subpages ourself
+    isLeaf = True  #  we handle subpages ourself
     named_pages = {}
     uri_callbacks = {}
     signals_handlers = {}
     pages_redirects = {}
     cache = {}
     cached_urls = {}
-    # Set of tuples (service/node/sub_id) of nodes subscribed for caching
+    #  Set of tuples (service/node/sub_id) of nodes subscribed for caching
     # sub_id can be empty string if not handled by service
     cache_pubsub_sub = set()
     main_menu = None
 
-    def __init__(self, host, root_dir, url, name=None, redirect=None, access=None, dynamic=False, parse_url=None,
-                 prepare_render=None, render=None, template=None,
-                 on_data_post=None, on_data=None, on_signal=None,
-                 url_cache=False):
+    def __init__(
+        self,
+        host,
+        root_dir,
+        url,
+        name=None,
+        redirect=None,
+        access=None,
+        dynamic=False,
+        parse_url=None,
+        prepare_render=None,
+        render=None,
+        template=None,
+        on_data_post=None,
+        on_data=None,
+        on_signal=None,
+        url_cache=False,
+    ):
         """initiate LiberviaPages
 
         LiberviaPages are the main resources of Libervia, using easy to set python files
@@ -169,25 +180,39 @@
         self.name = name
         if name is not None:
             if name in self.named_pages:
-                raise exceptions.ConflictError(_(u'a Libervia page named "{}" already exists'.format(name)))
-            if u'/' in name:
+                raise exceptions.ConflictError(
+                    _(u'a Libervia page named "{}" already exists'.format(name))
+                )
+            if u"/" in name:
                 raise ValueError(_(u'"/" is not allowed in page names'))
             if not name:
                 raise ValueError(_(u"a page name can't be empty"))
             self.named_pages[name] = self
         if access is None:
             access = C.PAGES_ACCESS_PUBLIC
-        if access not in (C.PAGES_ACCESS_PUBLIC, C.PAGES_ACCESS_PROFILE, C.PAGES_ACCESS_NONE):
-            raise NotImplementedError(_(u"{} access is not implemented yet").format(access))
+        if access not in (
+            C.PAGES_ACCESS_PUBLIC,
+            C.PAGES_ACCESS_PROFILE,
+            C.PAGES_ACCESS_NONE,
+        ):
+            raise NotImplementedError(
+                _(u"{} access is not implemented yet").format(access)
+            )
         self.access = access
         self.dynamic = dynamic
         if redirect is not None:
             # only page access and name make sense in case of full redirection
             # so we check that rendering methods/values are not set
-            if not all(lambda x: x is not None
-                for x in (parse_url, prepare_render, render, template)):
-                raise ValueError(_(u"you can't use full page redirection with other rendering method,"
-                                   u"check self.pageRedirect if you need to use them"))
+            if not all(
+                lambda x: x is not None
+                for x in (parse_url, prepare_render, render, template)
+            ):
+                raise ValueError(
+                    _(
+                        u"you can't use full page redirection with other rendering method,"
+                        u"check self.pageRedirect if you need to use them"
+                    )
+                )
             self.redirect = redirect
         else:
             self.redirect = None
@@ -208,17 +233,17 @@
             log.error(_(u"parse_url must be a callable"))
 
         # if not None, next rendering will be cached
-        # it must then contain a list of the the keys to use (without the page instance)
-        # e.g. [C.SERVICE_PROFILE, "pubsub", server@example.tld, pubsub_node] 
+        #  it must then contain a list of the the keys to use (without the page instance)
+        # e.g. [C.SERVICE_PROFILE, "pubsub", server@example.tld, pubsub_node]
         self._do_cache = None
 
     def __unicode__(self):
-        return u'LiberviaPage {name} at {url}'.format(
-            name = self.name or u'<anonymous>',
-            url = self.url)
+        return u"LiberviaPage {name} at {url}".format(
+            name=self.name or u"<anonymous>", url=self.url
+        )
 
     def __str__(self):
-        return self.__unicode__().encode('utf-8')
+        return self.__unicode__().encode("utf-8")
 
     @classmethod
     def importPages(cls, host, parent=None, path=None):
@@ -246,36 +271,39 @@
                 resource = LiberviaPage(
                     host,
                     dir_path,
-                    u'/' + u'/'.join(new_path),
-                    name=page_data.get('name'),
-                    redirect=page_data.get('redirect'),
-                    access=page_data.get('access'),
-                    dynamic=page_data.get('dynamic', False),
-                    parse_url=page_data.get('parse_url'),
-                    prepare_render=page_data.get('prepare_render'),
-                    render=page_data.get('render'),
-                    template=page_data.get('template'),
-                    on_data_post=page_data.get('on_data_post'),
-                    on_data=page_data.get('on_data'),
-                    on_signal=page_data.get('on_signal'),
-                    url_cache=page_data.get('url_cache', False),
-                    )
+                    u"/" + u"/".join(new_path),
+                    name=page_data.get("name"),
+                    redirect=page_data.get("redirect"),
+                    access=page_data.get("access"),
+                    dynamic=page_data.get("dynamic", False),
+                    parse_url=page_data.get("parse_url"),
+                    prepare_render=page_data.get("prepare_render"),
+                    render=page_data.get("render"),
+                    template=page_data.get("template"),
+                    on_data_post=page_data.get("on_data_post"),
+                    on_data=page_data.get("on_data"),
+                    on_signal=page_data.get("on_signal"),
+                    url_cache=page_data.get("url_cache", False),
+                )
                 parent.putChild(d, resource)
-                log.info(u"Added /{path} page".format(path=u'[...]/'.join(new_path)))
-                if 'uri_handlers' in page_data:
+                log.info(u"Added /{path} page".format(path=u"[...]/".join(new_path)))
+                if "uri_handlers" in page_data:
                     if not isinstance(page_data, dict):
-                        log.error(_(u'uri_handlers must be a dict'))
+                        log.error(_(u"uri_handlers must be a dict"))
                     else:
-                        for uri_tuple, cb_name in page_data['uri_handlers'].iteritems():
+                        for uri_tuple, cb_name in page_data["uri_handlers"].iteritems():
                             if len(uri_tuple) != 2 or not isinstance(cb_name, basestring):
                                 log.error(_(u"invalid uri_tuple"))
                                 continue
-                            log.info(_(u'setting {}/{} URIs handler').format(*uri_tuple))
+                            log.info(_(u"setting {}/{} URIs handler").format(*uri_tuple))
                             try:
                                 cb = page_data[cb_name]
                             except KeyError:
-                                log.error(_(u'missing {name} method to handle {1}/{2}').format(
-                                    name = cb_name, *uri_tuple))
+                                log.error(
+                                    _(u"missing {name} method to handle {1}/{2}").format(
+                                        name=cb_name, *uri_tuple
+                                    )
+                                )
                                 continue
                             else:
                                 resource.registerURI(uri_tuple, cb)
@@ -292,7 +320,9 @@
                 raise ValueError(msg)
             elif isinstance(menu, list):
                 if len(menu) != 2:
-                    msg = _(u"menu item as list must be in the form [page_name, absolue URL]")
+                    msg = _(
+                        u"menu item as list must be in the form [page_name, absolue URL]"
+                    )
                     log.error(msg)
                     raise ValueError(msg)
                 page_name, url = menu
@@ -301,7 +331,11 @@
                 try:
                     url = cls.getPageByName(page_name).url
                 except KeyError as e:
-                    log.error(_(u"Can'find a named page ({msg}), please check menu_json in configuration.").format(msg=e))
+                    log.error(
+                        _(
+                            u"Can'find a named page ({msg}), please check menu_json in configuration."
+                        ).format(msg=e)
+                    )
                     raise e
             main_menu.append((page_name, url))
         cls.main_menu = main_menu
@@ -317,7 +351,11 @@
             can't handle this URL
         """
         if uri_tuple in self.uri_callbacks:
-            log.info(_(u"{}/{} URIs are already handled, replacing by the new handler").format(*uri_tuple))
+            log.info(
+                _(u"{}/{} URIs are already handled, replacing by the new handler").format(
+                    *uri_tuple
+                )
+            )
         self.uri_callbacks[uri_tuple] = (self, get_uri_cb)
 
     def registerSignal(self, request, signal, check_profile=True):
@@ -340,7 +378,11 @@
         if not self.dynamic:
             log.error(_(u"You can't register signal if page is not dynamic"))
             return
-        LiberviaPage.signals_handlers.setdefault(signal, {})[id(request)] = (self, request, check_profile)
+        LiberviaPage.signals_handlers.setdefault(signal, {})[id(request)] = (
+            self,
+            request,
+            check_profile,
+        )
         request._signals_registered.append(signal)
 
     @classmethod
@@ -353,7 +395,7 @@
         """
         uri_data = common_uri.parseXMPPUri(uri)
         try:
-            page, cb = cls.uri_callbacks[uri_data['type'], uri_data['sub_type']]
+            page, cb = cls.uri_callbacks[uri_data["type"], uri_data["sub_type"]]
         except KeyError:
             url = None
         else:
@@ -362,7 +404,7 @@
             # no handler found
             # we try to find a more generic one
             try:
-                page, cb = cls.uri_callbacks[uri_data['type'], None]
+                page, cb = cls.uri_callbacks[uri_data["type"], None]
             except KeyError:
                 pass
             else:
@@ -379,7 +421,7 @@
         """
         return cls.named_pages[name]
 
-    def getPageRedirectURL(self, request, page_name=u'login', url=None):
+    def getPageRedirectURL(self, request, page_name=u"login", url=None):
         """generate URL for a page with redirect_url parameter set
 
         mainly used for login page with redirection to current page
@@ -389,9 +431,12 @@
             None to use request path (i.e. current page)
         @return (unicode): URL to use
         """
-        return u'{root_url}?redirect_url={redirect_url}'.format(
-            root_url = self.getPageByName(page_name).url,
-            redirect_url=urllib.quote_plus(request.uri) if url is None else url.encode('utf-8'))
+        return u"{root_url}?redirect_url={redirect_url}".format(
+            root_url=self.getPageByName(page_name).url,
+            redirect_url=urllib.quote_plus(request.uri)
+            if url is None
+            else url.encode("utf-8"),
+        )
 
     def getURL(self, *args):
         """retrieve URL of the page set arguments
@@ -402,16 +447,16 @@
         url_args = [quote(a) for a in args if a]
 
         if self.name is not None and self.name in self.pages_redirects:
-            # we check for redirection
+            #  we check for redirection
             redirect_data = self.pages_redirects[self.name]
             args_hash = tuple(args)
-            for limit in xrange(len(args)+1):
+            for limit in xrange(len(args) + 1):
                 current_hash = args_hash[:limit]
                 if current_hash in redirect_data:
                     url_base = redirect_data[current_hash]
                     remaining = args[limit:]
-                    remaining_url = '/'.join(remaining)
-                    return os.path.join('/', url_base, remaining_url)
+                    remaining_url = "/".join(remaining)
+                    return os.path.join("/", url_base, remaining_url)
 
         return os.path.join(self.url, *url_args)
 
@@ -426,19 +471,19 @@
         # the real request
 
         # we ignore empty path elements (i.e. double '/' or '/' at the end)
-        path_elts = [p for p in request.path.split('/') if p]
+        path_elts = [p for p in request.path.split("/") if p]
 
         if request.postpath:
             if not request.postpath[-1]:
-                # we remove trailing slash
+                #  we remove trailing slash
                 request.postpath = request.postpath[:-1]
             if request.postpath:
-                # getSubPageURL must return subpage from the point where
+                #  getSubPageURL must return subpage from the point where
                 # the it is called, so we have to remove remanining
                 # path elements
-                path_elts = path_elts[:-len(request.postpath)]
+                path_elts = path_elts[: -len(request.postpath)]
 
-        return u'/' + '/'.join(path_elts).decode('utf-8')
+        return u"/" + "/".join(path_elts).decode("utf-8")
 
     def getParamURL(self, request, **kwargs):
         """use URL of current request but modify the parameters in query part
@@ -448,8 +493,10 @@
         """
         current_url = self.getCurrentURL(request)
         if kwargs:
-            encoded = urllib.urlencode({k:v.encode('utf-8') for k,v in kwargs.iteritems()}).decode('utf-8')
-            current_url = current_url + u'?' + encoded
+            encoded = urllib.urlencode(
+                {k: v.encode("utf-8") for k, v in kwargs.iteritems()}
+            ).decode("utf-8")
+            current_url = current_url + u"?" + encoded
         return current_url
 
     def getSubPageByName(self, subpage_name, parent=None):
@@ -468,11 +515,11 @@
             try:
                 child_name = child.name
             except AttributeError:
-                # LiberviaPages have a name, but maybe this is an other Resource
+                #  LiberviaPages have a name, but maybe this is an other Resource
                 continue
             if child_name == subpage_name:
                 return path, child
-        raise exceptions.NotFound(_(u'requested sub page has not been found'))
+        raise exceptions.NotFound(_(u"requested sub page has not been found"))
 
     def getSubPageURL(self, request, page_name, *args):
         """retrieve a page in direct children and build its URL according to request
@@ -496,7 +543,9 @@
         """
         current_url = self.getCurrentURL(request)
         path, child = self.getSubPageByName(page_name)
-        return os.path.join(u'/', current_url, path, *[quote(a) for a in args if a is not None])
+        return os.path.join(
+            u"/", current_url, path, *[quote(a) for a in args if a is not None]
+        )
 
     def getURLByNames(self, named_path):
         """retrieve URL from pages names and arguments
@@ -515,11 +564,13 @@
                 current_page = self.getPageByName(page_name)
                 path.append(current_page.getURL(*page_args))
             else:
-                sub_path, current_page = self.getSubPageByName(page_name, parent=current_page)
+                sub_path, current_page = self.getSubPageByName(
+                    page_name, parent=current_page
+                )
                 path.append(sub_path)
                 if page_args:
                     path.extend([quote(a) for a in page_args])
-        return self.host.checkRedirection(u'/'.join(path))
+        return self.host.checkRedirection(u"/".join(path))
 
     def getURLByPath(self, *args):
         """generate URL by path
@@ -533,7 +584,7 @@
         """
         args = list(args)
         if not args:
-            raise ValueError('You must specify path elements')
+            raise ValueError("You must specify path elements")
         # root page is the one needed to construct the base of the URL
         # if first arg is not a SubPage instance, we use current page
         if not isinstance(args[0], SubPage):
@@ -556,11 +607,13 @@
             else:
                 path, current_page = current_page.getSubPageByName(args.pop(0))
                 arguments = [path]
-        return self.host.checkRedirection(u'/'.join(url_elts))
+        return self.host.checkRedirection(u"/".join(url_elts))
 
     def getChildWithDefault(self, path, request):
         # we handle children ourselves
-        raise exceptions.InternalError(u"this method should not be used with LiberviaPage")
+        raise exceptions.InternalError(
+            u"this method should not be used with LiberviaPage"
+        )
 
     def nextPath(self, request):
         """get next URL path segment, and update request accordingly
@@ -572,25 +625,25 @@
         """
         pathElement = request.postpath.pop(0)
         request.prepath.append(pathElement)
-        return urllib.unquote(pathElement).decode('utf-8')
+        return urllib.unquote(pathElement).decode("utf-8")
 
     def _filterPathValue(self, value, handler, name, request):
         """Modify a path value according to handler (see [getPathArgs])"""
-        if handler in (u'@', u'@jid') and value == u'@':
+        if handler in (u"@", u"@jid") and value == u"@":
             value = None
 
-        if handler in (u'', u'@'):
+        if handler in (u"", u"@"):
             if value is None:
-                return u''
-        elif handler in (u'jid', u'@jid'):
+                return u""
+        elif handler in (u"jid", u"@jid"):
             if value:
                 try:
                     return jid.JID(value)
                 except RuntimeError:
-                    log.warning(_(u'invalid jid argument: {value}').format(value=value))
+                    log.warning(_(u"invalid jid argument: {value}").format(value=value))
                     self.pageError(request, C.HTTP_BAD_REQUEST)
             else:
-                return u''
+                return u""
         else:
             return handler(self, value, name, request)
 
@@ -615,59 +668,63 @@
         data = self.getRData(request)
 
         for idx, name in enumerate(names):
-            if name[0] == u'*':
+            if name[0] == u"*":
                 value = data[name[1:]] = []
                 while True:
                     try:
                         value.append(self.nextPath(request))
                     except IndexError:
-                        idx-=1
+                        idx -= 1
                         break
                     else:
-                        idx+=1
+                        idx += 1
             else:
                 try:
                     value = data[name] = self.nextPath(request)
                 except IndexError:
                     data[name] = None
-                    idx-=1
+                    idx -= 1
                     break
 
-        values_count = idx+1
+        values_count = idx + 1
         if values_count < min_args:
-            log.warning(_(u"Missing arguments in URL (got {count}, expected at least {min_args})").format(
-                count = values_count, min_args = min_args))
+            log.warning(
+                _(
+                    u"Missing arguments in URL (got {count}, expected at least {min_args})"
+                ).format(count=values_count, min_args=min_args)
+            )
             self.pageError(request, C.HTTP_BAD_REQUEST)
 
         for name in names[values_count:]:
             data[name] = None
 
         for name, handler in kwargs.iteritems():
-            if name[0] == '*':
-                data[name] = [self._filterPathValue(v, handler, name, request) for v in data[name]]
+            if name[0] == "*":
+                data[name] = [
+                    self._filterPathValue(v, handler, name, request) for v in data[name]
+                ]
             else:
                 data[name] = self._filterPathValue(data[name], handler, name, request)
 
-
     ## Cache handling ##
 
     def _setCacheHeaders(self, request, cache):
         """Set ETag and Last-Modified HTTP headers, used for caching"""
-        request.setHeader('ETag', cache.hash)
+        request.setHeader("ETag", cache.hash)
         last_modified = self.host.getHTTPDate(cache.created)
-        request.setHeader('Last-Modified', last_modified)
+        request.setHeader("Last-Modified", last_modified)
 
     def _checkCacheHeaders(self, request, cache):
         """Check if a cache condition is set on the request
 
         if condition is valid, C.HTTP_NOT_MODIFIED is returned
         """
-        etag_match = request.getHeader('If-None-Match')
+        etag_match = request.getHeader("If-None-Match")
         if etag_match is not None:
             if cache.hash == etag_match:
                 self.pageError(request, C.HTTP_NOT_MODIFIED, no_body=True)
         else:
-            modified_match = request.getHeader('If-Modified-Since')
+            modified_match = request.getHeader("If-Modified-Since")
             if modified_match is not None:
                 modified = date_utils.date_parse(modified_match)
                 if modified >= int(cache.created):
@@ -698,46 +755,52 @@
 
         """
         if request.postpath:
-            # we are not on the final page, no need to go further
+            #  we are not on the final page, no need to go further
             return
 
         profile = self.getProfile(request) or C.SERVICE_PROFILE
 
         if cache_type == C.CACHE_PUBSUB:
-            service, node = kwargs['service'], kwargs['node']
+            service, node = kwargs["service"], kwargs["node"]
             if not node:
                 try:
-                    short = kwargs['short']
+                    short = kwargs["short"]
                     node = self.host.ns_map[short]
                 except KeyError:
-                    log.warning(_(u"Can't use cache for empty node without namespace set, please ensure to set \"short\" and that it is registered"))
+                    log.warning(
+                        _(
+                            u'Can\'t use cache for empty node without namespace set, please ensure to set "short" and that it is registered'
+                        )
+                    )
                     return
             if profile != C.SERVICE_PROFILE:
-                # only service profile is cache for now
+                #  only service profile is cache for now
                 return
             try:
                 cache = self.cache[profile][cache_type][service][node][request.uri][self]
             except KeyError:
                 # no cache yet, let's subscribe to the pubsub node
-                d1 = self.host.bridgeCall('psSubscribe', service.full(), node, {}, profile)
+                d1 = self.host.bridgeCall(
+                    "psSubscribe", service.full(), node, {}, profile
+                )
                 d1.addCallback(self.checkCacheSubscribeCb, service, node)
                 d1.addErrback(self.checkCacheSubscribeEb, service, node)
-                d2 = self.host.bridgeCall('psNodeWatchAdd', service.full(), node, profile)
+                d2 = self.host.bridgeCall("psNodeWatchAdd", service.full(), node, profile)
                 d2.addErrback(self.psNodeWatchAddEb, service, node)
                 self._do_cache = [self, profile, cache_type, service, node, request.uri]
-                # we don't return the Deferreds as it is not needed to wait for
+                #  we don't return the Deferreds as it is not needed to wait for
                 # the subscription to continue with page rendering
                 return
 
         else:
-            raise exceptions.InternalError(u'Unknown cache_type')
-        log.debug(u'using cache for {page}'.format(page=self))
+            raise exceptions.InternalError(u"Unknown cache_type")
+        log.debug(u"using cache for {page}".format(page=self))
         cache.last_access = time.time()
         self._setCacheHeaders(request, cache)
         self._checkCacheHeaders(request, cache)
         request.write(cache.rendered)
         request.finish()
-        raise failure.Failure(exceptions.CancelError(u'cache is used'))
+        raise failure.Failure(exceptions.CancelError(u"cache is used"))
 
     def _cacheURL(self, dummy, request, profile):
         self.cached_urls.setdefault(profile, {})[request.uri] = CacheURL(request)
@@ -748,17 +811,29 @@
         try:
             cache = cls.cache[profile][C.CACHE_PUBSUB][jid.JID(service)][node]
         except KeyError:
-            log.info(_(u'Removing subscription for {service}/{node}: '
-                       u'the page is not cached').format(service=service, node=node))
-            d1 = host.bridgeCall('psUnsubscribe', service, node, profile)
-            d1.addErrback(lambda failure_:
-                log.warning(_(u"Can't unsubscribe from {service}/{node}: {msg}").format(
-                    service=service, node=node, msg=failure_)))
-            d2 = host.bridgeCall('psNodeWatchAdd', service, node, profile)
+            log.info(
+                _(
+                    u"Removing subscription for {service}/{node}: "
+                    u"the page is not cached"
+                ).format(service=service, node=node)
+            )
+            d1 = host.bridgeCall("psUnsubscribe", service, node, profile)
+            d1.addErrback(
+                lambda failure_: log.warning(
+                    _(u"Can't unsubscribe from {service}/{node}: {msg}").format(
+                        service=service, node=node, msg=failure_
+                    )
+                )
+            )
+            d2 = host.bridgeCall("psNodeWatchAdd", service, node, profile)
             # TODO: check why the page is not in cache, remove subscription?
-            d2.addErrback(lambda failure_:
-                log.warning(_(u"Can't remove watch for {service}/{node}: {msg}").format(
-                    service=service, node=node, msg=failure_)))
+            d2.addErrback(
+                lambda failure_: log.warning(
+                    _(u"Can't remove watch for {service}/{node}: {msg}").format(
+                        service=service, node=node, msg=failure_
+                    )
+                )
+            )
         else:
             cache.clear()
 
@@ -771,7 +846,9 @@
         @param signal(unicode): name of the signal
         @param *args: args of the signals
         """
-        for page, request, check_profile in cls.signals_handlers.get(signal, {}).itervalues():
+        for page, request, check_profile in cls.signals_handlers.get(
+            signal, {}
+        ).itervalues():
             if check_profile:
                 signal_profile = args[-1]
                 request_profile = page.getProfile(request)
@@ -781,14 +858,14 @@
                     log.error(_(u"no session started, signal can't be checked"))
                     continue
                 if signal_profile != request_profile:
-                    # we ignore the signal, it's not for our profile
+                    #  we ignore the signal, it's not for our profile
                     continue
             if request._signals_cache is not None:
                 # socket is not yet opened, we cache the signal
                 request._signals_cache.append((request, signal, args))
-                log.debug(u"signal [{signal}] cached: {args}".format(
-                    signal = signal,
-                    args = args))
+                log.debug(
+                    u"signal [{signal}] cached: {args}".format(signal=signal, args=args)
+                )
             else:
                 page.on_signal(page, request, signal, *args)
 
@@ -812,8 +889,11 @@
             try:
                 del LiberviaPage.signals_handlers[signal][id(request)]
             except KeyError:
-                log.error(_(u"Can't find signal handler for [{signal}], this should not happen").format(
-                    signal = signal))
+                log.error(
+                    _(
+                        u"Can't find signal handler for [{signal}], this should not happen"
+                    ).format(signal=signal)
+                )
             else:
                 log.debug(_(u"Removed signal handler"))
 
@@ -825,7 +905,7 @@
         else:
             request.write(buf)
             request.finish()
-        raise failure.Failure(exceptions.CancelError(u'resource delegation'))
+        raise failure.Failure(exceptions.CancelError(u"resource delegation"))
 
     def HTTPRedirect(self, request, url):
         """redirect to an URL using HTTP redirection
@@ -833,11 +913,11 @@
         @param request(server.Request): current HTTP request
         @param url(unicode): url to redirect to
         """
-        web_util.redirectTo(url.encode('utf-8'), request)
+        web_util.redirectTo(url.encode("utf-8"), request)
         request.finish()
-        raise failure.Failure(exceptions.CancelError(u'HTTP redirection is used'))
+        raise failure.Failure(exceptions.CancelError(u"HTTP redirection is used"))
 
-    def redirectOrContinue(self, request, redirect_arg=u'redirect_url'):
+    def redirectOrContinue(self, request, redirect_arg=u"redirect_url"):
         """helper method to redirect a page to an url given as arg
 
         if the arg is not present, the page will continue normal workflow
@@ -847,12 +927,12 @@
         @interrupt pageError(C.HTTP_BAD_REQUEST): empty or non local URL is used
         """
         try:
-            url = request.args['redirect_url'][0]
+            url = request.args["redirect_url"][0]
         except (KeyError, IndexError):
             pass
         else:
-            # a redirection is requested
-            if not url or url[0] != u'/':
+            #  a redirection is requested
+            if not url or url[0] != u"/":
                 # we only want local urls
                 self.pageError(request, C.HTTP_BAD_REQUEST)
             else:
@@ -879,7 +959,7 @@
         @raise KeyError: there is no known page with this name
         """
         # FIXME: render non LiberviaPage resources
-        path = page_path.rstrip(u'/').split(u'/')
+        path = page_path.rstrip(u"/").split(u"/")
         if not path[0]:
             redirect_page = self.host.root
         else:
@@ -901,7 +981,7 @@
             self._do_cache = None
 
         redirect_page.renderPage(request, skip_parse_url=skip_parse_url)
-        raise failure.Failure(exceptions.CancelError(u'page redirection is used'))
+        raise failure.Failure(exceptions.CancelError(u"page redirection is used"))
 
     def pageError(self, request, code=C.HTTP_NOT_FOUND, no_body=False):
         """generate an error page and terminate the request
@@ -914,31 +994,34 @@
         if no_body:
             request.finish()
         else:
-            template = u'error/' + unicode(code) + '.html'
+            template = u"error/" + unicode(code) + ".html"
 
             rendered = self.host.renderer.render(
                 template,
-                root_path = '/templates/',
-                error_code = code,
-                **request.template_data)
+                root_path="/templates/",
+                error_code=code,
+                **request.template_data
+            )
 
             self.writeData(rendered, request)
-        raise failure.Failure(exceptions.CancelError(u'error page is used'))
+        raise failure.Failure(exceptions.CancelError(u"error page is used"))
 
     def writeData(self, data, request):
         """write data to transport and finish the request"""
         if data is None:
             self.pageError(request)
-        data_encoded = data.encode('utf-8')
+        data_encoded = data.encode("utf-8")
 
         if self._do_cache is not None:
             redirected_page = self._do_cache.pop(0)
             cache = reduce(lambda d, k: d.setdefault(k, {}), self._do_cache, self.cache)
             page_cache = cache[redirected_page] = CachePage(data_encoded)
             self._setCacheHeaders(request, page_cache)
-            log.debug(_(u'{page} put in cache for [{profile}]').format(
-                page=self,
-                profile=self._do_cache[0]))
+            log.debug(
+                _(u"{page} put in cache for [{profile}]").format(
+                    page=self, profile=self._do_cache[0]
+                )
+            )
             self._do_cache = None
             self._checkCacheHeaders(request, page_cache)
 
@@ -961,7 +1044,7 @@
                 self.pageError(request)
             else:
                 child.render(request)
-                raise failure.Failure(exceptions.CancelError(u'subpage page is used'))
+                raise failure.Failure(exceptions.CancelError(u"subpage page is used"))
 
     def _prepare_dynamic(self, dummy, request):
         # we need to activate dynamic page
@@ -969,7 +1052,9 @@
         socket_token = unicode(uuid.uuid4())
         socket_url = self.host.getWebsocketURL(request)
         socket_debug = C.boolConst(self.host.debug)
-        request.template_data['websocket'] = WebsocketMeta(socket_url, socket_token, socket_debug)
+        request.template_data["websocket"] = WebsocketMeta(
+            socket_url, socket_token, socket_debug
+        )
         self.host.registerWSToken(socket_token, self, request)
         # we will keep track of handlers to remove
         request._signals_registered = []
@@ -988,15 +1073,16 @@
         # if confirm variable is set in case of successfuly data post
         session_data = self.host.getSessionData(request, session_iface.ISATSession)
         if session_data.popPageFlag(self, C.FLAG_CONFIRM):
-            template_data[u'confirm'] = True
+            template_data[u"confirm"] = True
 
         return self.host.renderer.render(
             self.template,
-            root_path = '/templates/',
-            media_path = '/' + C.MEDIA_DIR,
-            cache_path = session_data.cache_dir,
-            main_menu = LiberviaPage.main_menu,
-            **template_data)
+            root_path="/templates/",
+            media_path="/" + C.MEDIA_DIR,
+            cache_path=session_data.cache_dir,
+            main_menu=LiberviaPage.main_menu,
+            **template_data
+        )
 
     def _renderEb(self, failure_, request):
         """don't raise error on CancelError"""
@@ -1004,9 +1090,11 @@
 
     def _internalError(self, failure_, request):
         """called if an error is not catched"""
-        log.error(_(u"Uncatched error for HTTP request on {url}: {msg}").format(
-            url = request.URLPath(),
-            msg = failure_))
+        log.error(
+            _(u"Uncatched error for HTTP request on {url}: {msg}").format(
+                url=request.URLPath(), msg=failure_
+            )
+        )
         self.pageError(request, C.HTTP_INTERNAL_ERROR)
 
     def _on_data_post_redirect(self, ret, request):
@@ -1026,11 +1114,13 @@
             ret = (ret,)
         else:
             ret = tuple(ret)
-            raise NotImplementedError(_(u'iterable in on_data_post return value is not used yet'))
+            raise NotImplementedError(
+                _(u"iterable in on_data_post return value is not used yet")
+            )
         session_data = self.host.getSessionData(request, session_iface.ISATSession)
         request_data = self.getRData(request)
-        if 'post_redirect_page' in request_data:
-            redirect_page_data = request_data['post_redirect_page']
+        if "post_redirect_page" in request_data:
+            redirect_page_data = request_data["post_redirect_page"]
             if isinstance(redirect_page_data, tuple):
                 redirect_page = redirect_page_data[0]
                 redirect_page_args = redirect_page_data[1:]
@@ -1047,18 +1137,22 @@
         request.setResponseCode(C.HTTP_SEE_OTHER)
         request.setHeader("location", redirect_uri)
         request.finish()
-        raise failure.Failure(exceptions.CancelError(u'Post/Redirect/Get is used'))
+        raise failure.Failure(exceptions.CancelError(u"Post/Redirect/Get is used"))
 
     def _on_data_post(self, dummy, request):
-        csrf_token = self.host.getSessionData(request, session_iface.ISATSession).csrf_token
+        csrf_token = self.host.getSessionData(
+            request, session_iface.ISATSession
+        ).csrf_token
         try:
-            given_csrf = self.getPostedData(request, u'csrf_token')
+            given_csrf = self.getPostedData(request, u"csrf_token")
         except KeyError:
             given_csrf = None
         if given_csrf is None or given_csrf != csrf_token:
-            log.warning(_(u"invalid CSRF token, hack attempt? URL: {url}, IP: {ip}").format(
-                url=request.uri,
-                ip=request.getClientIP()))
+            log.warning(
+                _(u"invalid CSRF token, hack attempt? URL: {url}, IP: {ip}").format(
+                    url=request.uri, ip=request.getClientIP()
+                )
+            )
             self.pageError(request, C.HTTP_UNAUTHORIZED)
         d = defer.maybeDeferred(self.on_data_post, self, request)
         d.addCallback(self._on_data_post_redirect, request)
@@ -1076,7 +1170,7 @@
         @return (iterator[unicode], list[iterator[unicode], unicode, list[unicode]): values received for this(these) key(s)
         @raise KeyError: one specific key has been requested, and it is missing
         """
-        # FIXME: request.args is already unquoting the value, it seems we are doing double unquote
+        #  FIXME: request.args is already unquoting the value, it seems we are doing double unquote
         if isinstance(keys, basestring):
             keys = [keys]
             get_first = True
@@ -1085,7 +1179,7 @@
 
         ret = []
         for key in keys:
-            gen = (urllib.unquote(v).decode('utf-8') for v in request.args.get(key,[]))
+            gen = (urllib.unquote(v).decode("utf-8") for v in request.args.get(key, []))
             if multiple:
                 ret.append(gen)
             else:
@@ -1105,16 +1199,16 @@
         @param multiple(bool): if False, only the first values are returned
         @return (dict[unicode, list[unicode]]): post values
         """
-        except_ = tuple(except_) + (u'csrf_token',)
+        except_ = tuple(except_) + (u"csrf_token",)
         ret = {}
         for key, values in request.args.iteritems():
-            key = urllib.unquote(key).decode('utf-8')
+            key = urllib.unquote(key).decode("utf-8")
             if key in except_:
                 continue
             if not multiple:
-                ret[key] = urllib.unquote(values[0]).decode('utf-8')
+                ret[key] = urllib.unquote(values[0]).decode("utf-8")
             else:
-                ret[key] = [urllib.unquote(v).decode('utf-8') for v in values]
+                ret[key] = [urllib.unquote(v).decode("utf-8") for v in values]
         return ret
 
     def getProfile(self, request):
@@ -1170,18 +1264,23 @@
         @param template_data(dict): template_data to use
         """
         if not self.dynamic:
-            raise exceptions.InternalError(_(u"renderPartial must only be used with dynamic pages"))
+            raise exceptions.InternalError(
+                _(u"renderPartial must only be used with dynamic pages")
+            )
         session_data = self.host.getSessionData(request, session_iface.ISATSession)
 
         return self.host.renderer.render(
             template,
-            root_path = '/templates/',
-            media_path = '/' + C.MEDIA_DIR,
-            cache_path = session_data.cache_dir,
-            main_menu = LiberviaPage.main_menu,
-            **template_data)
+            root_path="/templates/",
+            media_path="/" + C.MEDIA_DIR,
+            cache_path=session_data.cache_dir,
+            main_menu=LiberviaPage.main_menu,
+            **template_data
+        )
 
-    def renderAndUpdate(self, request, template, selectors, template_data_update, update_type="append"):
+    def renderAndUpdate(
+        self, request, template, selectors, template_data_update, update_type="append"
+    ):
         """Helper method to render a partial page element and update the page
 
         this is NOT the normal page rendering method, it is used only to update
@@ -1198,20 +1297,19 @@
         template_data = request.template_data.copy()
         template_data.update(template_data_update)
         html = self.renderPartial(request, template, template_data)
-        request.sendData(u'dom',
-                        selectors=selectors,
-                        update_type=update_type,
-                        html=html)
+        request.sendData(u"dom", selectors=selectors, update_type=update_type, html=html)
 
     def renderPage(self, request, skip_parse_url=False):
         """Main method to handle the workflow of a LiberviaPage"""
 
         # template_data are the variables passed to template
-        if not hasattr(request, 'template_data'):
+        if not hasattr(request, "template_data"):
             session_data = self.host.getSessionData(request, session_iface.ISATSession)
             csrf_token = session_data.csrf_token
-            request.template_data = {u'profile': session_data.profile,
-                                     u'csrf_token': csrf_token}
+            request.template_data = {
+                u"profile": session_data.profile,
+                u"csrf_token": csrf_token,
+            }
 
             # XXX: here is the code which need to be executed once
             #      at the beginning of the request hanling
@@ -1223,7 +1321,11 @@
         d.addCallback(self._checkAccess, request)
 
         if self.redirect is not None:
-            d.addCallback(lambda dummy: self.pageRedirect(self.redirect, request, skip_parse_url=False))
+            d.addCallback(
+                lambda dummy: self.pageRedirect(
+                    self.redirect, request, skip_parse_url=False
+                )
+            )
 
         if self.parse_url is not None and not skip_parse_url:
             if self.url_cache:
@@ -1232,7 +1334,7 @@
                     cache_url = self.cached_urls[profile][request.uri]
                 except KeyError:
                     # no cache for this URI yet
-                    # we do normal URL parsing, and then the cache
+                    #  we do normal URL parsing, and then the cache
                     d.addCallback(self.parse_url, request)
                     d.addCallback(self._cacheURL, request, profile)
                 else:
--- a/src/server/pages_tools.py	Sun Jun 24 22:21:25 2018 +0200
+++ b/src/server/pages_tools.py	Fri Jun 29 17:45:26 2018 +0200
@@ -23,6 +23,7 @@
 from libervia.server.constants import Const as C
 from twisted.internet import defer
 from sat.core.log import getLogger
+
 log = getLogger(__name__)
 from sat.tools.common import data_objects
 
@@ -41,21 +42,16 @@
         else exception will be raised
     """
     try:
-        d = self.host.bridgeCall(u'mbGet',
-            service,
-            node,
-            C.NO_LIMIT,
-            [],
-            {},
-            profile)
+        d = self.host.bridgeCall(u"mbGet", service, node, C.NO_LIMIT, [], {}, profile)
     except Exception as e:
         if not pass_exceptions:
             raise e
         else:
-            log.warning(_(u"Can't get comments at {node} (service: {service}): {msg}").format(
-                service=service,
-                node=node,
-                msg=e))
+            log.warning(
+                _(u"Can't get comments at {node} (service: {service}): {msg}").format(
+                    service=service, node=node, msg=e
+                )
+            )
             return defer.succeed([])
 
     d.addCallback(commentsDataToObjects)
--- a/src/server/server.py	Sun Jun 24 22:21:25 2018 +0200
+++ b/src/server/server.py	Fri Jun 29 17:45:26 2018 +0200
@@ -32,8 +32,13 @@
 from txjsonrpc import jsonrpclib
 
 from sat.core.log import getLogger
+
 log = getLogger(__name__)
-from sat_frontends.bridge.dbus_bridge import Bridge, BridgeExceptionNoService, const_TIMEOUT as BRIDGE_TIMEOUT
+from sat_frontends.bridge.dbus_bridge import (
+    Bridge,
+    BridgeExceptionNoService,
+    const_TIMEOUT as BRIDGE_TIMEOUT,
+)
 from sat.core.i18n import _, D_
 from sat.core import exceptions
 from sat.tools import utils
@@ -112,61 +117,85 @@
         self.redirections = {}
         self.inv_redirections = {}  # new URL to old URL map
 
-        if options['url_redirections_dict'] and not options['url_redirections_profile']:
+        if options["url_redirections_dict"] and not options["url_redirections_profile"]:
             # FIXME: url_redirections_profile should not be needed. It is currently used to
             #        redirect to an URL which associate the profile with the service, but this
             #        is not clean, and service should be explicitly specified
-            raise ValueError(u"url_redirections_profile need to be filled if you want to use url_redirections_dict")
+            raise ValueError(
+                u"url_redirections_profile need to be filled if you want to use url_redirections_dict"
+            )
 
-        for old, new_data in options['url_redirections_dict'].iteritems():
+        for old, new_data in options["url_redirections_dict"].iteritems():
             # new_data can be a dictionary or a unicode url
             if isinstance(new_data, dict):
                 # new_data dict must contain either "url", "page" or "path" key (exclusive)
                 # if "path" is used, a file url is constructed with it
-                if len({'path', 'url', 'page'}.intersection(new_data.keys())) != 1:
-                    raise ValueError(u'You must have one and only one of "url", "page" or "path" key in your url_redirections_dict data')
-                if 'url' in new_data:
-                    new = new_data['url']
-                elif 'page' in new_data:
+                if len({"path", "url", "page"}.intersection(new_data.keys())) != 1:
+                    raise ValueError(
+                        u'You must have one and only one of "url", "page" or "path" key in your url_redirections_dict data'
+                    )
+                if "url" in new_data:
+                    new = new_data["url"]
+                elif "page" in new_data:
                     new = new_data
-                    new['type'] = 'page'
-                    new.setdefault('path_args', [])
-                    if not isinstance(new['path_args'], list):
-                        log.error(_(u'"path_args" in redirection of {old} must be a list. Ignoring the redirection'.format(
-                            old = old)))
+                    new["type"] = "page"
+                    new.setdefault("path_args", [])
+                    if not isinstance(new["path_args"], list):
+                        log.error(
+                            _(
+                                u'"path_args" in redirection of {old} must be a list. Ignoring the redirection'.format(
+                                    old=old
+                                )
+                            )
+                        )
                         continue
-                    new.setdefault('query_args', {})
-                    if not isinstance(new['query_args'], dict):
-                        log.error(_(u'"query_args" in redirection of {old} must be a dictionary. Ignoring the redirection'.format(
-                            old = old)))
+                    new.setdefault("query_args", {})
+                    if not isinstance(new["query_args"], dict):
+                        log.error(
+                            _(
+                                u'"query_args" in redirection of {old} must be a dictionary. Ignoring the redirection'.format(
+                                    old=old
+                                )
+                            )
+                        )
                         continue
-                    new['path_args'] = [quote(a) for a in new['path_args']]
+                    new["path_args"] = [quote(a) for a in new["path_args"]]
                     # we keep an inversed dict of page redirection (page/path_args => redirecting URL)
                     # so getURL can return the redirecting URL if the same arguments are used
                     # making the URL consistent
-                    args_hash = tuple(new['path_args'])
-                    LiberviaPage.pages_redirects.setdefault(new_data['page'], {})[args_hash] = old
+                    args_hash = tuple(new["path_args"])
+                    LiberviaPage.pages_redirects.setdefault(new_data["page"], {})[
+                        args_hash
+                    ] = old
 
                     # we need lists in query_args because it will be used
                     # as it in request.path_args
-                    for k,v in new['query_args'].iteritems():
+                    for k, v in new["query_args"].iteritems():
                         if isinstance(v, basestring):
-                            new['query_args'][k] = [v]
-                elif 'path' in new_data:
-                    new = 'file:{}'.format(urllib.quote(new_data['path']))
+                            new["query_args"][k] = [v]
+                elif "path" in new_data:
+                    new = "file:{}".format(urllib.quote(new_data["path"]))
             elif isinstance(new_data, basestring):
                 new = new_data
                 new_data = {}
             else:
-                log.error(_(u"ignoring invalid redirection value: {new_data}").format(new_data=new_data))
+                log.error(
+                    _(u"ignoring invalid redirection value: {new_data}").format(
+                        new_data=new_data
+                    )
+                )
                 continue
 
             # some normalization
             if not old.strip():
                 # root URL special case
-                old = ''
-            elif not old.startswith('/'):
-                log.error(_(u"redirected url must start with '/', got {value}. Ignoring").format(value=old))
+                old = ""
+            elif not old.startswith("/"):
+                log.error(
+                    _(
+                        u"redirected url must start with '/', got {value}. Ignoring"
+                    ).format(value=old)
+                )
                 continue
             else:
                 old = self._normalizeURL(old)
@@ -176,73 +205,105 @@
                 # which ared use dynamically when the request is done
                 self.redirections[old] = new
                 if not old:
-                    if new[u'type'] == u'page':
-                        log.info(_(u"Root URL redirected to page {name}").format(name=new[u'page']))
+                    if new[u"type"] == u"page":
+                        log.info(
+                            _(u"Root URL redirected to page {name}").format(
+                                name=new[u"page"]
+                            )
+                        )
                 else:
-                    if new[u'type'] == u'page':
-                        page = LiberviaPage.getPageByName(new[u'page'])
-                        url = page.getURL(*new.get(u'path_args', []))
+                    if new[u"type"] == u"page":
+                        page = LiberviaPage.getPageByName(new[u"page"])
+                        url = page.getURL(*new.get(u"path_args", []))
                         self.inv_redirections[url] = old
                 continue
 
             # at this point we have a redirection URL in new, we can parse it
-            new_url = urlparse.urlsplit(new.encode('utf-8'))
+            new_url = urlparse.urlsplit(new.encode("utf-8"))
 
             # we handle the known URL schemes
-            if new_url.scheme == 'xmpp':
+            if new_url.scheme == "xmpp":
                 location = LiberviaPage.getPagePathFromURI(new)
                 if location is None:
-                    log.warning(_(u"ignoring redirection, no page found to handle this URI: {uri}").format(uri=new))
+                    log.warning(
+                        _(
+                            u"ignoring redirection, no page found to handle this URI: {uri}"
+                        ).format(uri=new)
+                    )
                     continue
                 request_data = self._getRequestData(location)
                 if old:
                     self.inv_redirections[location] = old
 
-            elif new_url.scheme in ('', 'http', 'https'):
+            elif new_url.scheme in ("", "http", "https"):
                 # direct redirection
                 if new_url.netloc:
-                    raise NotImplementedError(u"netloc ({netloc}) is not implemented yet for url_redirections_dict, it is not possible to redirect to an external website".format(
-                        netloc = new_url.netloc))
-                location = urlparse.urlunsplit(('', '', new_url.path, new_url.query, new_url.fragment)).decode('utf-8')
+                    raise NotImplementedError(
+                        u"netloc ({netloc}) is not implemented yet for url_redirections_dict, it is not possible to redirect to an external website".format(
+                            netloc=new_url.netloc
+                        )
+                    )
+                location = urlparse.urlunsplit(
+                    ("", "", new_url.path, new_url.query, new_url.fragment)
+                ).decode("utf-8")
                 request_data = self._getRequestData(location)
                 if old:
                     self.inv_redirections[location] = old
 
-            elif new_url.scheme in ('file'):
+            elif new_url.scheme in ("file"):
                 # file or directory
                 if new_url.netloc:
-                    raise NotImplementedError(u"netloc ({netloc}) is not implemented for url redirection to file system, it is not possible to redirect to an external host".format(
-                        netloc = new_url.netloc))
+                    raise NotImplementedError(
+                        u"netloc ({netloc}) is not implemented for url redirection to file system, it is not possible to redirect to an external host".format(
+                            netloc=new_url.netloc
+                        )
+                    )
                 path = urllib.unquote(new_url.path)
                 if not os.path.isabs(path):
-                    raise ValueError(u'file redirection must have an absolute path: e.g. file:/path/to/my/file')
+                    raise ValueError(
+                        u"file redirection must have an absolute path: e.g. file:/path/to/my/file"
+                    )
                 # for file redirection, we directly put child here
-                segments, dummy, last_segment = old.rpartition('/')
-                url_segments = segments.split('/') if segments else []
+                segments, dummy, last_segment = old.rpartition("/")
+                url_segments = segments.split("/") if segments else []
                 current = self
                 for segment in url_segments:
                     resource = web_resource.NoResource()
                     current.putChild(segment, resource)
                     current = resource
-                resource_class = ProtectedFile if new_data.get('protected',True) else static.File
+                resource_class = (
+                    ProtectedFile if new_data.get("protected", True) else static.File
+                )
                 current.putChild(last_segment, resource_class(path))
-                log.info(u"Added redirection from /{old} to file system path {path}".format(old=old.decode('utf-8'), path=path.decode('utf-8')))
-                continue # we don't want to use redirection system, so we continue here
+                log.info(
+                    u"Added redirection from /{old} to file system path {path}".format(
+                        old=old.decode("utf-8"), path=path.decode("utf-8")
+                    )
+                )
+                continue  # we don't want to use redirection system, so we continue here
 
             else:
-                raise NotImplementedError(u"{scheme}: scheme is not managed for url_redirections_dict".format(scheme=new_url.scheme))
+                raise NotImplementedError(
+                    u"{scheme}: scheme is not managed for url_redirections_dict".format(
+                        scheme=new_url.scheme
+                    )
+                )
 
             self.redirections[old] = request_data
             if not old:
-                log.info(_(u"Root URL redirected to {uri}").format(uri=request_data[1].decode('utf-8')))
+                log.info(
+                    _(u"Root URL redirected to {uri}").format(
+                        uri=request_data[1].decode("utf-8")
+                    )
+                )
 
         # no need to keep url_redirections*, they will not be used anymore
-        del options['url_redirections_dict']
-        del options['url_redirections_profile']
+        del options["url_redirections_dict"]
+        del options["url_redirections_profile"]
 
         # the default root URL, if not redirected
-        if not '' in self.redirections:
-            self.redirections[''] = self._getRequestData(C.LIBERVIA_MAIN_PAGE)
+        if not "" in self.redirections:
+            self.redirections[""] = self._getRequestData(C.LIBERVIA_MAIN_PAGE)
 
     def _normalizeURL(self, url, lower=True):
         """Return URL normalized for self.redirections dict
@@ -253,7 +314,7 @@
         """
         if lower:
             url = url.lower()
-        return '/'.join((p for p in url.encode('utf-8').split('/') if p))
+        return "/".join((p for p in url.encode("utf-8").split("/") if p))
 
     def _getRequestData(self, uri):
         """Return data needed to redirect request
@@ -265,10 +326,10 @@
             path as in Request.path
             args as in Request.args
         """
-        uri = uri.encode('utf-8')
+        uri = uri.encode("utf-8")
         # XXX: we reuse code from twisted.web.http.py here
         #      as we need to have the same behaviour
-        x = uri.split(b'?', 1)
+        x = uri.split(b"?", 1)
 
         if len(x) == 1:
             path = uri
@@ -279,7 +340,12 @@
 
         # XXX: splitted path case must not be changed, as it may be significant
         #      (e.g. for blog items)
-        return self._normalizeURL(path.decode('utf-8'), lower=False).split('/'), uri, path, args
+        return (
+            self._normalizeURL(path.decode("utf-8"), lower=False).split("/"),
+            uri,
+            path,
+            args,
+        )
 
     def _redirect(self, request, request_data):
         """Redirect an URL by rewritting request
@@ -298,40 +364,48 @@
             try:
                 dummy, uri, dummy, dummy = request_data
             except ValueError:
-                uri = u''
-            log.error(D_(u"recursive redirection, please fix this URL:\n{old} ==> {new}").format(
-                old=request.uri.decode('utf-8'),
-                new=uri.decode('utf-8'),
-                ))
+                uri = u""
+            log.error(
+                D_(
+                    u"recursive redirection, please fix this URL:\n{old} ==> {new}"
+                ).format(old=request.uri.decode("utf-8"), new=uri.decode("utf-8"))
+            )
             return web_resource.NoResource()
 
-        request._redirected = True # here to avoid recursive redirections
+        request._redirected = True  # here to avoid recursive redirections
 
         if isinstance(request_data, dict):
-            if request_data['type'] == 'page':
+            if request_data["type"] == "page":
                 try:
-                    page = LiberviaPage.getPageByName(request_data['page'])
+                    page = LiberviaPage.getPageByName(request_data["page"])
                 except KeyError:
-                    log.error(_(u"Can't find page named \"{name}\" requested in redirection").format(
-                        name = request_data['page']))
+                    log.error(
+                        _(
+                            u'Can\'t find page named "{name}" requested in redirection'
+                        ).format(name=request_data["page"])
+                    )
                     return web_resource.NoResource()
-                request.postpath = request_data['path_args'][:] + request.postpath
+                request.postpath = request_data["path_args"][:] + request.postpath
 
                 try:
-                    request.args.update(request_data['query_args'])
+                    request.args.update(request_data["query_args"])
                 except (TypeError, ValueError):
-                    log.error(_(u"Invalid args in redirection: {query_args}").format(
-                        query_args=request_data['query_args']))
+                    log.error(
+                        _(u"Invalid args in redirection: {query_args}").format(
+                            query_args=request_data["query_args"]
+                        )
+                    )
                     return web_resource.NoResource()
                 return page
             else:
-                raise exceptions.InternalError(u'unknown request_data type')
+                raise exceptions.InternalError(u"unknown request_data type")
         else:
             path_list, uri, path, args = request_data
-            log.debug(u"Redirecting URL {old} to {new}".format(
-                old=request.uri.decode('utf-8'),
-                new=uri.decode('utf-8'),
-                ))
+            log.debug(
+                u"Redirecting URL {old} to {new}".format(
+                    old=request.uri.decode("utf-8"), new=uri.decode("utf-8")
+                )
+            )
             # we change the request to reflect the new url
             request.postpath = path_list[1:] + request.postpath
             request.args = args
@@ -342,8 +416,8 @@
     def getChildWithDefault(self, name, request):
         # XXX: this method is overriden only for root url
         #      which is the only ones who need to be handled before other children
-        if name == '' and not request.postpath:
-            return self._redirect(request, self.redirections[''])
+        if name == "" and not request.postpath:
+            return self._redirect(request, self.redirections[""])
         return super(LiberviaRootResource, self).getChildWithDefault(name, request)
 
     def getChild(self, name, request):
@@ -354,7 +428,7 @@
             # XXX: we want redirections to happen only if everything else failed
             path_elt = request.prepath + request.postpath
             for idx in xrange(len(path_elt), 0, -1):
-                test_url = '/'.join(path_elt[:idx]).lower()
+                test_url = "/".join(path_elt[:idx]).lower()
                 if test_url in self.redirections:
                     request_data = self.redirections[test_url]
                     request.postpath = path_elt[idx:]
@@ -365,7 +439,9 @@
     def createSimilarFile(self, path):
         # XXX: this method need to be overriden to avoid recreating a LiberviaRootResource
 
-        f = LiberviaRootResource.__base__(path, self.defaultType, self.ignoredExts, self.registry)
+        f = LiberviaRootResource.__base__(
+            path, self.defaultType, self.ignoredExts, self.registry
+        )
         # refactoring by steps, here - constructor should almost certainly take these
         f.processors = self.processors
         f.indexNames = self.indexNames[:]
@@ -374,7 +450,6 @@
 
 
 class JSONRPCMethodManager(jsonrpc.JSONRPC):
-
     def __init__(self, sat_host):
         jsonrpc.JSONRPC.__init__(self)
         self.sat_host = sat_host
@@ -384,7 +459,6 @@
 
 
 class MethodHandler(JSONRPCMethodManager):
-
     def __init__(self, sat_host):
         JSONRPCMethodManager.__init__(self, sat_host)
 
@@ -392,10 +466,14 @@
         self.session = request.getSession()
         profile = session_iface.ISATSession(self.session).profile
         if not profile:
-            #user is not identified, we return a jsonrpc fault
+            # user is not identified, we return a jsonrpc fault
             parsed = jsonrpclib.loads(request.content.read())
-            fault = jsonrpclib.Fault(C.ERRNUM_LIBERVIA, C.NOT_ALLOWED)  # FIXME: define some standard error codes for libervia
-            return jsonrpc.JSONRPC._cbRender(self, fault, request, parsed.get('id'), parsed.get('jsonrpc'))  # pylint: disable=E1103
+            fault = jsonrpclib.Fault(
+                C.ERRNUM_LIBERVIA, C.NOT_ALLOWED
+            )  # FIXME: define some standard error codes for libervia
+            return jsonrpc.JSONRPC._cbRender(
+                self, fault, request, parsed.get("id"), parsed.get("jsonrpc")
+            )  # pylint: disable=E1103
         return jsonrpc.JSONRPC.render(self, request)
 
     @defer.inlineCallbacks
@@ -456,12 +534,16 @@
         @param status: any string to describe your status
         """
         profile = session_iface.ISATSession(self.session).profile
-        return self.sat_host.bridgeCall("setPresence", '', presence, {'': status}, profile)
+        return self.sat_host.bridgeCall(
+            "setPresence", "", presence, {"": status}, profile
+        )
 
     def jsonrpc_messageSend(self, to_jid, msg, subject, type_, extra={}):
         """send message"""
         profile = session_iface.ISATSession(self.session).profile
-        return self.asyncBridgeCall("messageSend", to_jid, msg, subject, type_, extra, profile)
+        return self.asyncBridgeCall(
+            "messageSend", to_jid, msg, subject, type_, extra, profile
+        )
 
     ## PubSub ##
 
@@ -530,7 +612,9 @@
         @return: a deferred couple with the list of items and metadatas.
         """
         profile = session_iface.ISATSession(self.session).profile
-        return self.asyncBridgeCall("mbGet", service_jid, node, max_items, item_ids, extra, profile)
+        return self.asyncBridgeCall(
+            "mbGet", service_jid, node, max_items, item_ids, extra, profile
+        )
 
     def jsonrpc_mbGetFromMany(self, publishers_type, publishers, max_items, extra):
         """Get many blog nodes at once
@@ -542,7 +626,9 @@
         @return (str): RT Deferred session id
         """
         profile = session_iface.ISATSession(self.session).profile
-        return self.sat_host.bridgeCall("mbGetFromMany", publishers_type, publishers, max_items, extra, profile)
+        return self.sat_host.bridgeCall(
+            "mbGetFromMany", publishers_type, publishers, max_items, extra, profile
+        )
 
     def jsonrpc_mbGetFromManyRTResult(self, rt_session):
         """Get results from RealTime mbGetFromMany session
@@ -552,7 +638,15 @@
         profile = session_iface.ISATSession(self.session).profile
         return self.asyncBridgeCall("mbGetFromManyRTResult", rt_session, profile)
 
-    def jsonrpc_mbGetFromManyWithComments(self, publishers_type, publishers, max_items, max_comments, rsm_dict, rsm_comments_dict):
+    def jsonrpc_mbGetFromManyWithComments(
+        self,
+        publishers_type,
+        publishers,
+        max_items,
+        max_comments,
+        rsm_dict,
+        rsm_comments_dict,
+    ):
         """Helper method to get the microblogs and their comments in one shot
 
         @param publishers_type (str): type of the list of publishers (one of "GROUP" or "JID" or "ALL")
@@ -565,7 +659,16 @@
         @return (str): RT Deferred session id
         """
         profile = session_iface.ISATSession(self.session).profile
-        return self.sat_host.bridgeCall("mbGetFromManyWithComments", publishers_type, publishers, max_items, max_comments, rsm_dict, rsm_comments_dict, profile)
+        return self.sat_host.bridgeCall(
+            "mbGetFromManyWithComments",
+            publishers_type,
+            publishers,
+            max_items,
+            max_comments,
+            rsm_dict,
+            rsm_comments_dict,
+            profile,
+        )
 
     def jsonrpc_mbGetFromManyWithCommentsRTResult(self, rt_session):
         """Get results from RealTime mbGetFromManyWithComments session
@@ -573,8 +676,9 @@
         @param rt_session (str): RT Deferred session id
         """
         profile = session_iface.ISATSession(self.session).profile
-        return self.asyncBridgeCall("mbGetFromManyWithCommentsRTResult", rt_session, profile)
-
+        return self.asyncBridgeCall(
+            "mbGetFromManyWithCommentsRTResult", rt_session, profile
+        )
 
     # def jsonrpc_sendMblog(self, type_, dest, text, extra={}):
     #     """ Send microblog message
@@ -684,26 +788,49 @@
         profile = session_iface.ISATSession(self.session).profile
         return self.sat_host.bridgeCall("getPresenceStatuses", profile)
 
-    def jsonrpc_historyGet(self, from_jid, to_jid, size, between, search=''):
+    def jsonrpc_historyGet(self, from_jid, to_jid, size, between, search=""):
         """Return history for the from_jid/to_jid couple"""
         sat_session = session_iface.ISATSession(self.session)
         profile = sat_session.profile
         sat_jid = sat_session.jid
         if not sat_jid:
-            raise exceptions.InternalError('session jid should be set')
-        if jid.JID(from_jid).userhost() != sat_jid.userhost() and jid.JID(to_jid).userhost() != sat_jid.userhost():
-            log.error(u"Trying to get history from a different jid (given (browser): {}, real (backend): {}), maybe a hack attempt ?".format(from_jid, sat_jid))
+            raise exceptions.InternalError("session jid should be set")
+        if (
+            jid.JID(from_jid).userhost() != sat_jid.userhost()
+            and jid.JID(to_jid).userhost() != sat_jid.userhost()
+        ):
+            log.error(
+                u"Trying to get history from a different jid (given (browser): {}, real (backend): {}), maybe a hack attempt ?".format(
+                    from_jid, sat_jid
+                )
+            )
             return {}
-        d = self.asyncBridgeCall("historyGet", from_jid, to_jid, size, between, search, profile)
+        d = self.asyncBridgeCall(
+            "historyGet", from_jid, to_jid, size, between, search, profile
+        )
 
         def show(result_dbus):
             result = []
             for line in result_dbus:
-                #XXX: we have to do this stupid thing because Python D-Bus use its own types instead of standard types
+                # XXX: we have to do this stupid thing because Python D-Bus use its own types instead of standard types
                 #     and txJsonRPC doesn't accept D-Bus types, resulting in a empty query
-                uuid, timestamp, from_jid, to_jid, message, subject, mess_type, extra = line
-                result.append((unicode(uuid), float(timestamp), unicode(from_jid), unicode(to_jid), dict(message), dict(subject), unicode(mess_type), dict(extra)))
+                uuid, timestamp, from_jid, to_jid, message, subject, mess_type, extra = (
+                    line
+                )
+                result.append(
+                    (
+                        unicode(uuid),
+                        float(timestamp),
+                        unicode(from_jid),
+                        unicode(to_jid),
+                        dict(message),
+                        dict(subject),
+                        unicode(mess_type),
+                        dict(extra),
+                    )
+                )
             return result
+
         d.addCallback(show)
         return d
 
@@ -726,7 +853,9 @@
         profile = session_iface.ISATSession(self.session).profile
         room_id = room_jid.split("@")[0]
         service = room_jid.split("@")[1]
-        return self.sat_host.bridgeCall("inviteMUC", contact_jid, service, room_id, {}, profile)
+        return self.sat_host.bridgeCall(
+            "inviteMUC", contact_jid, service, room_id, {}, profile
+        )
 
     def jsonrpc_mucLeave(self, room_jid):
         """Quit a Multi-User Chat room"""
@@ -734,7 +863,7 @@
         try:
             room_jid = jid.JID(room_jid)
         except:
-            log.warning('Invalid room jid')
+            log.warning("Invalid room jid")
             return
         return self.sat_host.bridgeCall("mucLeave", room_jid.userhost(), profile)
 
@@ -755,13 +884,18 @@
         @param room_jid (unicode): room JID or empty string to generate a unique name
         """
         profile = session_iface.ISATSession(self.session).profile
-        return self.sat_host.bridgeCall("tarotGameLaunch", other_players, room_jid, profile)
+        return self.sat_host.bridgeCall(
+            "tarotGameLaunch", other_players, room_jid, profile
+        )
 
     def jsonrpc_getTarotCardsPaths(self):
         """Give the path of all the tarot cards"""
         _join = os.path.join
-        _media_dir = _join(self.sat_host.media_dir, '')
-        return map(lambda x: _join(C.MEDIA_DIR, x[len(_media_dir):]), glob.glob(_join(_media_dir, C.CARDS_DIR, '*_*.png')))
+        _media_dir = _join(self.sat_host.media_dir, "")
+        return map(
+            lambda x: _join(C.MEDIA_DIR, x[len(_media_dir) :]),
+            glob.glob(_join(_media_dir, C.CARDS_DIR, "*_*.png")),
+        )
 
     def jsonrpc_tarotGameReady(self, player, referee):
         """Tell to the server that we are ready to start the game"""
@@ -771,7 +905,9 @@
     def jsonrpc_tarotGamePlayCards(self, player_nick, referee, cards):
         """Tell to the server the cards we want to put on the table"""
         profile = session_iface.ISATSession(self.session).profile
-        return self.sat_host.bridgeCall("tarotGamePlayCards", player_nick, referee, cards, profile)
+        return self.sat_host.bridgeCall(
+            "tarotGamePlayCards", player_nick, referee, cards, profile
+        )
 
     def jsonrpc_launchRadioCollective(self, invited, room_jid=""):
         """Create a room, invite people, and start a radio collective.
@@ -789,7 +925,9 @@
         @param keys: name of data we want (list)
         @return: requested data"""
         if not C.ALLOWED_ENTITY_DATA.issuperset(keys):
-            raise exceptions.PermissionError("Trying to access unallowed data (hack attempt ?)")
+            raise exceptions.PermissionError(
+                "Trying to access unallowed data (hack attempt ?)"
+            )
         profile = session_iface.ISATSession(self.session).profile
         try:
             return self.sat_host.bridgeCall("getEntitiesData", jids, keys, profile)
@@ -803,7 +941,9 @@
         @param keys: name of data we want (list)
         @return: requested data"""
         if not C.ALLOWED_ENTITY_DATA.issuperset(keys):
-            raise exceptions.PermissionError("Trying to access unallowed data (hack attempt ?)")
+            raise exceptions.PermissionError(
+                "Trying to access unallowed data (hack attempt ?)"
+            )
         profile = session_iface.ISATSession(self.session).profile
         try:
             return self.sat_host.bridgeCall("getEntityData", jid, keys, profile)
@@ -822,7 +962,9 @@
         session_data = session_iface.ISATSession(self.session)
         profile = session_data.profile
         # profile_uuid = session_data.uuid
-        avatar = yield self.asyncBridgeCall("avatarGet", entity, cache_only, hash_only, profile)
+        avatar = yield self.asyncBridgeCall(
+            "avatarGet", entity, cache_only, hash_only, profile
+        )
         if hash_only:
             defer.returnValue(avatar)
         else:
@@ -847,18 +989,29 @@
         if category == "Connection":
             # we need to manage the followings params here, else SECURITY_LIMIT would block them
             if param == "JabberID":
-                return self.asyncBridgeCall("asyncGetParamA", param, category, attribute, profile_key=profile)
+                return self.asyncBridgeCall(
+                    "asyncGetParamA", param, category, attribute, profile_key=profile
+                )
             elif param == "autoconnect":
                 return defer.succeed(C.BOOL_TRUE)
-        d = self.asyncBridgeCall("asyncGetParamA", param, category, attribute, C.SECURITY_LIMIT, profile_key=profile)
+        d = self.asyncBridgeCall(
+            "asyncGetParamA",
+            param,
+            category,
+            attribute,
+            C.SECURITY_LIMIT,
+            profile_key=profile,
+        )
         return d
 
     def jsonrpc_setParam(self, name, value, category):
         profile = session_iface.ISATSession(self.session).profile
-        return self.sat_host.bridgeCall("setParam", name, value, category, C.SECURITY_LIMIT, profile)
+        return self.sat_host.bridgeCall(
+            "setParam", name, value, category, C.SECURITY_LIMIT, profile
+        )
 
     def jsonrpc_launchAction(self, callback_id, data):
-        #FIXME: any action can be launched, this can be a huge security issue if callback_id can be guessed
+        # FIXME: any action can be launched, this can be a huge security issue if callback_id can be guessed
         #       a security system with authorised callback_id must be implemented, similar to the one for authorised params
         profile = session_iface.ISATSession(self.session).profile
         d = self.asyncBridgeCall("launchAction", callback_id, data, profile)
@@ -876,7 +1029,9 @@
         d = self.asyncBridgeCall("getNewAccountDomain")
         return d
 
-    def jsonrpc_syntaxConvert(self, text, syntax_from=C.SYNTAX_XHTML, syntax_to=C.SYNTAX_CURRENT):
+    def jsonrpc_syntaxConvert(
+        self, text, syntax_from=C.SYNTAX_XHTML, syntax_to=C.SYNTAX_CURRENT
+    ):
         """ Convert a text between two syntaxes
         @param text: text to convert
         @param syntax_from: source syntax (e.g. "markdown")
@@ -884,7 +1039,9 @@
         @param safe: clean resulting XHTML to avoid malicious code if True (forced here)
         @return: converted text """
         profile = session_iface.ISATSession(self.session).profile
-        return self.sat_host.bridgeCall("syntaxConvert", text, syntax_from, syntax_to, True, profile)
+        return self.sat_host.bridgeCall(
+            "syntaxConvert", text, syntax_from, syntax_to, True, profile
+        )
 
     def jsonrpc_getLastResource(self, jid_s):
         """Get the last active resource of that contact."""
@@ -902,11 +1059,10 @@
         return self.sat_host.bridgeCall("skipOTR", profile)
 
     def jsonrpc_namespacesGet(self):
-        return self.sat_host.bridgeCall('namespacesGet')
+        return self.sat_host.bridgeCall("namespacesGet")
 
 
 class WaitingRequests(dict):
-
     def setRequest(self, request, profile, register_with_ext_jid=False):
         """Add the given profile to the waiting list.
 
@@ -963,18 +1119,22 @@
            - except login, every method is jsonrpc
            - user doesn't need to be authentified for explicitely listed methods, but must be for all others
         """
-        if request.postpath == ['login']:
+        if request.postpath == ["login"]:
             return self.loginOrRegister(request)
         _session = request.getSession()
         parsed = jsonrpclib.loads(request.content.read())
         method = parsed.get("method")  # pylint: disable=E1103
-        if  method not in ['getSessionMetadata', 'registerParams', 'menusGet']:
-            #if we don't call these methods, we need to be identified
+        if method not in ["getSessionMetadata", "registerParams", "menusGet"]:
+            # if we don't call these methods, we need to be identified
             profile = session_iface.ISATSession(_session).profile
             if not profile:
-                #user is not identified, we return a jsonrpc fault
-                fault = jsonrpclib.Fault(C.ERRNUM_LIBERVIA, C.NOT_ALLOWED)  # FIXME: define some standard error codes for libervia
-                return jsonrpc.JSONRPC._cbRender(self, fault, request, parsed.get('id'), parsed.get('jsonrpc'))  # pylint: disable=E1103
+                # user is not identified, we return a jsonrpc fault
+                fault = jsonrpclib.Fault(
+                    C.ERRNUM_LIBERVIA, C.NOT_ALLOWED
+                )  # FIXME: define some standard error codes for libervia
+                return jsonrpc.JSONRPC._cbRender(
+                    self, fault, request, parsed.get("id"), parsed.get("jsonrpc")
+                )  # pylint: disable=E1103
         self.request = request
         return jsonrpc.JSONRPC.render(self, request)
 
@@ -987,24 +1147,24 @@
             - a return value from self._loginAccount or self._registerNewAccount
         """
         try:
-            submit_type = request.args['submit_type'][0]
+            submit_type = request.args["submit_type"][0]
         except KeyError:
             return C.BAD_REQUEST
 
-        if submit_type == 'register':
+        if submit_type == "register":
             self._registerNewAccount(request)
             return server.NOT_DONE_YET
-        elif submit_type == 'login':
+        elif submit_type == "login":
             self._loginAccount(request)
             return server.NOT_DONE_YET
-        return Exception('Unknown submit type')
+        return Exception("Unknown submit type")
 
     @defer.inlineCallbacks
     def _registerNewAccount(self, request):
         try:
-            login = request.args['register_login'][0]
-            password = request.args['register_password'][0]
-            email = request.args['email'][0]
+            login = request.args["register_login"][0]
+            password = request.args["register_password"][0]
+            email = request.args["email"][0]
         except KeyError:
             request.write(C.BAD_REQUEST)
             request.finish()
@@ -1030,8 +1190,8 @@
         @param request: request of the register form
         """
         try:
-            login = request.args['login'][0]
-            password = request.args['login_password'][0]
+            login = request.args["login"][0]
+            password = request.args["login_password"][0]
         except KeyError:
             request.write(C.BAD_REQUEST)
             request.finish()
@@ -1041,9 +1201,11 @@
 
         try:
             status = yield self.sat_host.connect(request, login, password)
-        except (exceptions.DataError,
-                exceptions.ProfileUnknownError,
-                exceptions.PermissionError):
+        except (
+            exceptions.DataError,
+            exceptions.ProfileUnknownError,
+            exceptions.PermissionError,
+        ):
             request.write(C.PROFILE_AUTH_ERROR)
             request.finish()
             return
@@ -1084,7 +1246,9 @@
         _session = self.request.getSession()
         profile = session_iface.ISATSession(_session).profile
         if self.waiting_profiles.getRequest(profile):
-            raise jsonrpclib.Fault(1, C.ALREADY_WAITING)  # FIXME: define some standard error codes for libervia
+            raise jsonrpclib.Fault(
+                1, C.ALREADY_WAITING
+            )  # FIXME: define some standard error codes for libervia
         self.waiting_profiles.setRequest(self.request, profile)
         self.sat_host.bridgeCall("connect", profile)
         return server.NOT_DONE_YET
@@ -1121,35 +1285,54 @@
     def jsonrpc_menusGet(self):
         """Return the parameters XML for profile"""
         # XXX: we put this method in Register because we get menus before being logged
-        return self.sat_host.bridgeCall("menusGet", '', C.SECURITY_LIMIT)
+        return self.sat_host.bridgeCall("menusGet", "", C.SECURITY_LIMIT)
 
     def _getSecurityWarning(self):
         """@return: a security warning message, or None if the connection is secure"""
-        if self.request.URLPath().scheme == 'https' or not self.sat_host.options['security_warning']:
+        if (
+            self.request.URLPath().scheme == "https"
+            or not self.sat_host.options["security_warning"]
+        ):
             return None
-        text = "<p>" + D_("You are about to connect to an unsecure service.") + "</p><p>&nbsp;</p><p>"
+        text = (
+            "<p>"
+            + D_("You are about to connect to an unsecure service.")
+            + "</p><p>&nbsp;</p><p>"
+        )
 
-        if self.sat_host.options['connection_type'] == 'both':
-            new_port = (':%s' % self.sat_host.options['port_https_ext']) if self.sat_host.options['port_https_ext'] != HTTPS_PORT else ''
-            url = "https://%s" % self.request.URLPath().netloc.replace(':%s' % self.sat_host.options['port'], new_port)
-            text += D_('Please read our %(faq_prefix)ssecurity notice%(faq_suffix)s regarding HTTPS') % {'faq_prefix': '<a href="http://salut-a-toi.org/faq.html#https" target="#">', 'faq_suffix': '</a>'}
-            text += "</p><p>" + D_('and use the secure version of this website:')
-            text += '</p><p>&nbsp;</p><p align="center"><a href="%(url)s">%(url)s</a>' % {'url': url}
+        if self.sat_host.options["connection_type"] == "both":
+            new_port = (
+                (":%s" % self.sat_host.options["port_https_ext"])
+                if self.sat_host.options["port_https_ext"] != HTTPS_PORT
+                else ""
+            )
+            url = "https://%s" % self.request.URLPath().netloc.replace(
+                ":%s" % self.sat_host.options["port"], new_port
+            )
+            text += D_(
+                "Please read our %(faq_prefix)ssecurity notice%(faq_suffix)s regarding HTTPS"
+            ) % {
+                "faq_prefix": '<a href="http://salut-a-toi.org/faq.html#https" target="#">',
+                "faq_suffix": "</a>",
+            }
+            text += "</p><p>" + D_("and use the secure version of this website:")
+            text += '</p><p>&nbsp;</p><p align="center"><a href="%(url)s">%(url)s</a>' % {
+                "url": url
+            }
         else:
-            text += D_('You should ask your administrator to turn on HTTPS.')
+            text += D_("You should ask your administrator to turn on HTTPS.")
 
         return text + "</p><p>&nbsp;</p>"
 
 
 class SignalHandler(jsonrpc.JSONRPC):
-
     def __init__(self, sat_host):
         web_resource.Resource.__init__(self)
         self.register = None
         self.sat_host = sat_host
         self._last_service_prof_disconnect = time.time()
-        self.signalDeferred = {} # dict of deferred (key: profile, value: Deferred)
-                                 # which manages the long polling HTTP request with signals
+        self.signalDeferred = {}  # dict of deferred (key: profile, value: Deferred)
+        # which manages the long polling HTTP request with signals
         self.queue = {}
 
     def plugRegister(self, register):
@@ -1164,7 +1347,7 @@
             if self.queue[profile]:
                 return self.queue[profile].pop(0)
             else:
-                #the queue is empty, we delete the profile from queue
+                # the queue is empty, we delete the profile from queue
                 del self.queue[profile]
         _session.lock()  # we don't want the session to expire as long as this connection is active
 
@@ -1177,9 +1360,9 @@
                     try:
                         _session.expire()
                     except KeyError:
-                        # FIXME: happen if session is ended using login page
+                        #  FIXME: happen if session is ended using login page
                         #        when pyjamas page is also launched
-                        log.warning(u'session is already expired')
+                        log.warning(u"session is already expired")
             except IndexError:
                 log.error("Deferred result should be a tuple with fonction name first")
 
@@ -1190,6 +1373,7 @@
     def getGenericCb(self, function_name):
         """Return a generic function which send all params to signalDeferred.callback
         function must have profile as last argument"""
+
         def genericCb(*args):
             profile = args[-1]
             if not profile in self.sat_host.prof_connected:
@@ -1198,10 +1382,11 @@
             try:
                 signal_callback = self.signalDeferred[profile].callback
             except KeyError:
-                self.queue.setdefault(profile,[]).append(signal_data)
+                self.queue.setdefault(profile, []).append(signal_data)
             else:
                 signal_callback(signal_data)
                 del self.signalDeferred[profile]
+
         return genericCb
 
     def actionNewHandler(self, action_data, action_id, security_limit, profile):
@@ -1219,13 +1404,17 @@
         #        raise an exception if it's not OK
         #        and read value in sat.conf
         if security_limit >= C.SECURITY_LIMIT:
-            log.debug(u"Ignoring action  {action_id}, blocked by security limit".format(action_id=action_id))
+            log.debug(
+                u"Ignoring action  {action_id}, blocked by security limit".format(
+                    action_id=action_id
+                )
+            )
             return
         signal_data = ("actionNew", (action_data, action_id, security_limit))
         try:
             signal_callback = self.signalDeferred[profile].callback
         except KeyError:
-            self.queue.setdefault(profile,[]).append(signal_data)
+            self.queue.setdefault(profile, []).append(signal_data)
         else:
             signal_callback(signal_data)
             del self.signalDeferred[profile]
@@ -1252,20 +1441,36 @@
             # and display an error message
             disconnect_delta = time.time() - self._last_service_prof_disconnect
             if disconnect_delta < 15:
-                log.error(_(u'Service profile disconnected twice in a short time, please check connection'))
+                log.error(
+                    _(
+                        u"Service profile disconnected twice in a short time, please check connection"
+                    )
+                )
             else:
-                log.info(_(u"Service profile has been disconnected, but we need it! Reconnecting it..."))
-                d = self.sat_host.bridgeCall("connect", profile,
-                    self.sat_host.options['passphrase'],
-                    {},
+                log.info(
+                    _(
+                        u"Service profile has been disconnected, but we need it! Reconnecting it..."
                     )
-                d.addErrback(lambda failure_: log.error(_(u"Can't reconnect service profile, please check connection: {reason}").format(
-                    reason=failure_)))
+                )
+                d = self.sat_host.bridgeCall(
+                    "connect", profile, self.sat_host.options["passphrase"], {}
+                )
+                d.addErrback(
+                    lambda failure_: log.error(
+                        _(
+                            u"Can't reconnect service profile, please check connection: {reason}"
+                        ).format(reason=failure_)
+                    )
+                )
             self._last_service_prof_disconnect = time.time()
             return
 
         if not profile in self.sat_host.prof_connected:
-            log.info(_(u"'disconnected' signal received for a not connected profile ({profile})").format(profile=profile))
+            log.info(
+                _(
+                    u"'disconnected' signal received for a not connected profile ({profile})"
+                ).format(profile=profile)
+            )
             return
         self.sat_host.prof_connected.remove(profile)
         if profile in self.signalDeferred:
@@ -1284,9 +1489,13 @@
         parsed = jsonrpclib.loads(request.content.read())
         profile = session_iface.ISATSession(_session).profile
         if not profile:
-            #user is not identified, we return a jsonrpc fault
-            fault = jsonrpclib.Fault(C.ERRNUM_LIBERVIA, C.NOT_ALLOWED)  # FIXME: define some standard error codes for libervia
-            return jsonrpc.JSONRPC._cbRender(self, fault, request, parsed.get('id'), parsed.get('jsonrpc'))  # pylint: disable=E1103
+            # user is not identified, we return a jsonrpc fault
+            fault = jsonrpclib.Fault(
+                C.ERRNUM_LIBERVIA, C.NOT_ALLOWED
+            )  # FIXME: define some standard error codes for libervia
+            return jsonrpc.JSONRPC._cbRender(
+                self, fault, request, parsed.get("id"), parsed.get("jsonrpc")
+            )  # pylint: disable=E1103
         self.request = request
         return jsonrpc.JSONRPC.render(self, request)
 
@@ -1294,8 +1503,9 @@
 class UploadManager(web_resource.Resource):
     """This class manage the upload of a file
     It redirect the stream to SàT core backend"""
+
     isLeaf = True
-    NAME = 'path'  # name use by the FileUpload
+    NAME = "path"  # name use by the FileUpload
 
     def __init__(self, sat_host):
         self.sat_host = sat_host
@@ -1327,12 +1537,12 @@
         """
         filename = self._getFileName(request)
         filepath = os.path.join(self.upload_dir, filename)
-        #FIXME: the uploaded file is fully loaded in memory at form parsing time so far
+        # FIXME: the uploaded file is fully loaded in memory at form parsing time so far
         #       (see twisted.web.http.Request.requestReceived). A custom requestReceived should
         #       be written in the futur. In addition, it is not yet possible to get progression informations
         #       (see http://twistedmatrix.com/trac/ticket/288)
 
-        with open(filepath, 'w') as f:
+        with open(filepath, "w") as f:
             f.write(request.args[self.NAME][0])
 
         def finish(d):
@@ -1343,17 +1553,22 @@
             # a DBusException instance --> extract the data from the backtrace?
             request.finish()
 
-        d = JSONRPCMethodManager(self.sat_host).asyncBridgeCall(*self._fileWritten(request, filepath))
+        d = JSONRPCMethodManager(self.sat_host).asyncBridgeCall(
+            *self._fileWritten(request, filepath)
+        )
         d.addCallbacks(lambda d: finish(d), lambda failure: finish(failure))
         return server.NOT_DONE_YET
 
 
 class UploadManagerRadioCol(UploadManager):
-    NAME = 'song'
+    NAME = "song"
 
     def _getFileName(self, request):
-        extension = os.path.splitext(request.args['filename'][0])[1]
-        return "%s%s" % (str(uuid.uuid4()), extension)  # XXX: chromium doesn't seem to play song without the .ogg extension, even with audio/ogg mime-type
+        extension = os.path.splitext(request.args["filename"][0])[1]
+        return "%s%s" % (
+            str(uuid.uuid4()),
+            extension,
+        )  # XXX: chromium doesn't seem to play song without the .ogg extension, even with audio/ogg mime-type
 
     def _fileWritten(self, request, filepath):
         """Called once the file is actually written on disk
@@ -1363,11 +1578,11 @@
         to be called followed by its arguments.
         """
         profile = session_iface.ISATSession(request.getSession()).profile
-        return ("radiocolSongAdded", request.args['referee'][0], filepath, profile)
+        return ("radiocolSongAdded", request.args["referee"][0], filepath, profile)
 
 
 class UploadManagerAvatar(UploadManager):
-    NAME = 'avatar_path'
+    NAME = "avatar_path"
 
     def _getFileName(self, request):
         return str(uuid.uuid4())
@@ -1391,24 +1606,26 @@
         self.initialised = defer.Deferred()
         self.waiting_profiles = WaitingRequests()  # FIXME: should be removed
 
-        if self.options['base_url_ext']:
-            self.base_url_ext = self.options.pop('base_url_ext')
-            if self.base_url_ext[-1] != '/':
-                self.base_url_ext += '/'
+        if self.options["base_url_ext"]:
+            self.base_url_ext = self.options.pop("base_url_ext")
+            if self.base_url_ext[-1] != "/":
+                self.base_url_ext += "/"
             self.base_url_ext_data = urlparse.urlsplit(self.base_url_ext)
         else:
             self.base_url_ext = None
             # we split empty string anyway so we can do things like
             # scheme = self.base_url_ext_data.scheme or 'https'
-            self.base_url_ext_data = urlparse.urlsplit('')
+            self.base_url_ext_data = urlparse.urlsplit("")
 
-        if not self.options['port_https_ext']:
-            self.options['port_https_ext'] = self.options['port_https']
-        if self.options['data_dir'] == DATA_DIR_DEFAULT:
-            coerceDataDir(self.options['data_dir'])  # this is not done when using the default value
+        if not self.options["port_https_ext"]:
+            self.options["port_https_ext"] = self.options["port_https"]
+        if self.options["data_dir"] == DATA_DIR_DEFAULT:
+            coerceDataDir(
+                self.options["data_dir"]
+            )  # this is not done when using the default value
 
-        self.html_dir = os.path.join(self.options['data_dir'], C.HTML_DIR)
-        self.themes_dir = os.path.join(self.options['data_dir'], C.THEMES_DIR)
+        self.html_dir = os.path.join(self.options["data_dir"], C.HTML_DIR)
+        self.themes_dir = os.path.join(self.options["data_dir"], C.THEMES_DIR)
 
         self._cleanup = []
 
@@ -1436,63 +1653,100 @@
         _register = Register(self)
         _upload_radiocol = UploadManagerRadioCol(self)
         _upload_avatar = UploadManagerAvatar(self)
-        d = self.bridgeCall('namespacesGet')
+        d = self.bridgeCall("namespacesGet")
         d.addCallback(self._namespacesGetCb)
         d.addErrback(self._namespacesGetEb)
         self.signal_handler.plugRegister(_register)
         self.bridge.register_signal("connected", self.signal_handler.connected)
         self.bridge.register_signal("disconnected", self.signal_handler.disconnected)
-        #core
-        for signal_name in ['presenceUpdate', 'messageNew', 'subscribe', 'contactDeleted',
-                            'newContact', 'entityDataUpdated', 'paramUpdate']:
-            self.bridge.register_signal(signal_name, self.signal_handler.getGenericCb(signal_name))
+        # core
+        for signal_name in [
+            "presenceUpdate",
+            "messageNew",
+            "subscribe",
+            "contactDeleted",
+            "newContact",
+            "entityDataUpdated",
+            "paramUpdate",
+        ]:
+            self.bridge.register_signal(
+                signal_name, self.signal_handler.getGenericCb(signal_name)
+            )
         # XXX: actionNew is handled separately because the handler must manage security_limit
-        self.bridge.register_signal('actionNew', self.signal_handler.actionNewHandler)
-        #plugins
-        for signal_name in ['psEvent', 'mucRoomJoined', 'tarotGameStarted', 'tarotGameNew', 'tarotGameChooseContrat',
-                            'tarotGameShowCards', 'tarotGameInvalidCards', 'tarotGameCardsPlayed', 'tarotGameYourTurn', 'tarotGameScore', 'tarotGamePlayers',
-                            'radiocolStarted', 'radiocolPreload', 'radiocolPlay', 'radiocolNoUpload', 'radiocolUploadOk', 'radiocolSongRejected', 'radiocolPlayers',
-                            'mucRoomLeft', 'mucRoomUserChangedNick', 'chatStateReceived']:
-            self.bridge.register_signal(signal_name, self.signal_handler.getGenericCb(signal_name), "plugin")
-        self.media_dir = self.bridge.getConfig('', 'media_dir')
-        self.local_dir = self.bridge.getConfig('', 'local_dir')
-        self.cache_root_dir = os.path.join(
-            self.local_dir,
-            C.CACHE_DIR)
+        self.bridge.register_signal("actionNew", self.signal_handler.actionNewHandler)
+        # plugins
+        for signal_name in [
+            "psEvent",
+            "mucRoomJoined",
+            "tarotGameStarted",
+            "tarotGameNew",
+            "tarotGameChooseContrat",
+            "tarotGameShowCards",
+            "tarotGameInvalidCards",
+            "tarotGameCardsPlayed",
+            "tarotGameYourTurn",
+            "tarotGameScore",
+            "tarotGamePlayers",
+            "radiocolStarted",
+            "radiocolPreload",
+            "radiocolPlay",
+            "radiocolNoUpload",
+            "radiocolUploadOk",
+            "radiocolSongRejected",
+            "radiocolPlayers",
+            "mucRoomLeft",
+            "mucRoomUserChangedNick",
+            "chatStateReceived",
+        ]:
+            self.bridge.register_signal(
+                signal_name, self.signal_handler.getGenericCb(signal_name), "plugin"
+            )
+        self.media_dir = self.bridge.getConfig("", "media_dir")
+        self.local_dir = self.bridge.getConfig("", "local_dir")
+        self.cache_root_dir = os.path.join(self.local_dir, C.CACHE_DIR)
 
         # JSON APIs
-        self.putChild('json_signal_api', self.signal_handler)
-        self.putChild('json_api', MethodHandler(self))
-        self.putChild('register_api', _register)
+        self.putChild("json_signal_api", self.signal_handler)
+        self.putChild("json_api", MethodHandler(self))
+        self.putChild("register_api", _register)
 
         # files upload
-        self.putChild('upload_radiocol', _upload_radiocol)
-        self.putChild('upload_avatar', _upload_avatar)
+        self.putChild("upload_radiocol", _upload_radiocol)
+        self.putChild("upload_avatar", _upload_avatar)
 
         # static pages
-        self.putChild('blog_legacy', MicroBlog(self))
+        self.putChild("blog_legacy", MicroBlog(self))
         self.putChild(C.THEMES_URL, ProtectedFile(self.themes_dir))
 
         # websocket
-        if self.options['connection_type'] in ('https', 'both'):
+        if self.options["connection_type"] in ("https", "both"):
             wss = websockets.LiberviaPageWSProtocol.getResource(self, secure=True)
-            self.putChild('wss', wss)
-        if self.options['connection_type'] in ('http', 'both'):
+            self.putChild("wss", wss)
+        if self.options["connection_type"] in ("http", "both"):
             ws = websockets.LiberviaPageWSProtocol.getResource(self, secure=False)
-            self.putChild('ws', ws)
+            self.putChild("ws", ws)
 
-        # Libervia pages
+        #  Libervia pages
         LiberviaPage.importPages(self)
-        LiberviaPage.setMenu(self.options['menu_json'])
+        LiberviaPage.setMenu(self.options["menu_json"])
         ## following signal is needed for cache handling in Libervia pages
-        self.bridge.register_signal("psEventRaw", partial(LiberviaPage.onNodeEvent, self), "plugin")
-        self.bridge.register_signal("messageNew", partial(LiberviaPage.onSignal, self, "messageNew"))
+        self.bridge.register_signal(
+            "psEventRaw", partial(LiberviaPage.onNodeEvent, self), "plugin"
+        )
+        self.bridge.register_signal(
+            "messageNew", partial(LiberviaPage.onSignal, self, "messageNew")
+        )
 
-        # Progress handling
-        self.bridge.register_signal("progressStarted", partial(ProgressHandler._signal, "started"))
-        self.bridge.register_signal("progressFinished", partial(ProgressHandler._signal, "finished"))
-        self.bridge.register_signal("progressError", partial(ProgressHandler._signal, "error"))
-
+        #  Progress handling
+        self.bridge.register_signal(
+            "progressStarted", partial(ProgressHandler._signal, "started")
+        )
+        self.bridge.register_signal(
+            "progressFinished", partial(ProgressHandler._signal, "finished")
+        )
+        self.bridge.register_signal(
+            "progressError", partial(ProgressHandler._signal, "error")
+        )
 
         # media dirs
         # FIXME: get rid of dirname and "/" in C.XXX_DIR
@@ -1501,20 +1755,25 @@
         self.putChild(C.CACHE_DIR, self.cache_resource)
 
         # special
-        self.putChild('radiocol', ProtectedFile(_upload_radiocol.getTmpDir(), defaultType="audio/ogg"))  # FIXME: We cheat for PoC because we know we are on the same host, so we use directly upload dir
+        self.putChild(
+            "radiocol",
+            ProtectedFile(_upload_radiocol.getTmpDir(), defaultType="audio/ogg"),
+        )  # FIXME: We cheat for PoC because we know we are on the same host, so we use directly upload dir
         # pyjamas tests, redirected only for dev versions
-        if self.version[-1] == 'D':
-            self.putChild('test', web_util.Redirect('/libervia_test.html'))
+        if self.version[-1] == "D":
+            self.putChild("test", web_util.Redirect("/libervia_test.html"))
 
         # redirections
         root._initRedirections(self.options)
 
-        server.Request.defaultContentType = 'text/html; charset=utf-8'
-        wrapped = web_resource.EncodingResourceWrapper(root, [server.GzipEncoderFactory()])
+        server.Request.defaultContentType = "text/html; charset=utf-8"
+        wrapped = web_resource.EncodingResourceWrapper(
+            root, [server.GzipEncoderFactory()]
+        )
         self.site = server.Site(wrapped)
         self.site.sessionFactory = LiberviaSession
         self.renderer = template.Renderer(self)
-        self.putChild('templates', ProtectedFile(self.renderer.base_dir))
+        self.putChild("templates", ProtectedFile(self.renderer.base_dir))
 
     def initEb(self, failure):
         log.error(_(u"Init error: {msg}").format(msg=failure))
@@ -1522,8 +1781,10 @@
         return failure
 
     def _bridgeCb(self):
-        self.bridge.getReady(lambda: self.initialised.callback(None),
-                             lambda failure: self.initialised.errback(Exception(failure)))
+        self.bridge.getReady(
+            lambda: self.initialised.callback(None),
+            lambda failure: self.initialised.errback(Exception(failure)),
+        )
         self.initialised.addCallback(self.backendReady)
         self.initialised.addErrback(self.initEb)
 
@@ -1539,12 +1800,14 @@
     def full_version(self):
         """Return the full version of Libervia (with extra data when in development mode)"""
         version = self.version
-        if version[-1] == 'D':
+        if version[-1] == "D":
             # we are in debug version, we add extra data
             try:
                 return self._version_cache
             except AttributeError:
-                self._version_cache = u"{} ({})".format(version, utils.getRepositoryData(libervia))
+                self._version_cache = u"{} ({})".format(
+                    version, utils.getRepositoryData(libervia)
+                )
                 return self._version_cache
         else:
             return version
@@ -1567,7 +1830,11 @@
                 d.callback(args[0])
 
         def _errback(result):
-            d.errback(failure.Failure(jsonrpclib.Fault(C.ERRNUM_BRIDGE_ERRBACK, result.classname)))
+            d.errback(
+                failure.Failure(
+                    jsonrpclib.Fault(C.ERRNUM_BRIDGE_ERRBACK, result.classname)
+                )
+            )
 
         kwargs["callback"] = _callback
         kwargs["errback"] = _errback
@@ -1590,34 +1857,42 @@
         session = request.getSession()
         sat_session = session_iface.ISATSession(session)
         if sat_session.profile:
-            log.error(_(u'/!\\ Session has already a profile, this should NEVER happen!'))
+            log.error(_(u"/!\\ Session has already a profile, this should NEVER happen!"))
             raise failure.Failure(exceptions.ConflictError("Already active"))
 
         sat_session.profile = profile
         self.prof_connected.add(profile)
-        cache_dir = os.path.join(self.cache_root_dir, u'profiles', regex.pathEscape(profile))
+        cache_dir = os.path.join(
+            self.cache_root_dir, u"profiles", regex.pathEscape(profile)
+        )
         # FIXME: would be better to have a global /cache URL which redirect to profile's cache directory, without uuid
         self.cache_resource.putChild(sat_session.uuid, ProtectedFile(cache_dir))
-        log.debug(_(u"profile cache resource added from {uuid} to {path}").format(uuid=sat_session.uuid, path=cache_dir))
+        log.debug(
+            _(u"profile cache resource added from {uuid} to {path}").format(
+                uuid=sat_session.uuid, path=cache_dir
+            )
+        )
 
         def onExpire():
-            log.info(u"Session expired (profile={profile})".format(profile=profile,))
+            log.info(u"Session expired (profile={profile})".format(profile=profile))
             self.cache_resource.delEntity(sat_session.uuid)
-            log.debug(_(u"profile cache resource {uuid} deleted").format(uuid = sat_session.uuid))
+            log.debug(
+                _(u"profile cache resource {uuid} deleted").format(uuid=sat_session.uuid)
+            )
             try:
-                #We purge the queue
+                # We purge the queue
                 del self.signal_handler.queue[profile]
             except KeyError:
                 pass
-            #and now we disconnect the profile
+            # and now we disconnect the profile
             self.bridgeCall("disconnect", profile)
 
         session.notifyOnExpire(onExpire)
 
         # FIXME: those session infos should be returned by connect or isConnected
-        infos = yield self.bridgeCall('sessionInfosGet', profile)
-        sat_session.jid = jid.JID(infos['jid'])
-        sat_session.backend_started = int(infos['started'])
+        infos = yield self.bridgeCall("sessionInfosGet", profile)
+        sat_session.jid = jid.JID(infos["jid"])
+        sat_session.backend_started = int(infos["started"])
 
         state = C.PROFILE_LOGGED_EXT_JID if register_with_ext_jid else C.PROFILE_LOGGED
         defer.returnValue(state)
@@ -1645,18 +1920,20 @@
         """
 
         # XXX: all security checks must be done here, even if present in javascript
-        if login.startswith('@'):
-            raise failure.Failure(exceptions.DataError('No profile_key allowed'))
+        if login.startswith("@"):
+            raise failure.Failure(exceptions.DataError("No profile_key allowed"))
 
-        if login.startswith('guest@@') and login.count('@') == 2:
+        if login.startswith("guest@@") and login.count("@") == 2:
             log.debug("logging a guest account")
-        elif '@' in login:
-            if login.count('@') != 1:
-                raise failure.Failure(exceptions.DataError('Invalid login: {login}'.format(login=login)))
+        elif "@" in login:
+            if login.count("@") != 1:
+                raise failure.Failure(
+                    exceptions.DataError("Invalid login: {login}".format(login=login))
+                )
             try:
                 login_jid = jid.JID(login)
             except (RuntimeError, jid.InvalidFormat, AttributeError):
-                raise failure.Failure(exceptions.DataError('No profile_key allowed'))
+                raise failure.Failure(exceptions.DataError("No profile_key allowed"))
 
             # FIXME: should it be cached?
             new_account_domain = yield self.bridgeCall("getNewAccountDomain")
@@ -1672,17 +1949,29 @@
             profile = yield self.bridgeCall("profileNameGet", login)
         except Exception:  # XXX: ProfileUnknownError wouldn't work, it's encapsulated
             # FIXME: find a better way to handle bridge errors
-            if login_jid is not None and login_jid.user:  # try to create a new sat profile using the XMPP credentials
+            if (
+                login_jid is not None and login_jid.user
+            ):  # try to create a new sat profile using the XMPP credentials
                 if not self.options["allow_registration"]:
-                    log.warning(u"Trying to register JID account while registration is not allowed")
-                    raise failure.Failure(exceptions.DataError(u"JID login while registration is not allowed"))
-                profile = login # FIXME: what if there is a resource?
+                    log.warning(
+                        u"Trying to register JID account while registration is not allowed"
+                    )
+                    raise failure.Failure(
+                        exceptions.DataError(
+                            u"JID login while registration is not allowed"
+                        )
+                    )
+                profile = login  # FIXME: what if there is a resource?
                 connect_method = "asyncConnectWithXMPPCredentials"
                 register_with_ext_jid = True
-            else: # non existing username
+            else:  # non existing username
                 raise failure.Failure(exceptions.ProfileUnknownError())
         else:
-            if profile != login or (not password and profile not in self.options['empty_password_allowed_warning_dangerous_list']):
+            if profile != login or (
+                not password
+                and profile
+                not in self.options["empty_password_allowed_warning_dangerous_list"]
+            ):
                 # profiles with empty passwords are restricted to local frontends
                 raise exceptions.PermissionError
             register_with_ext_jid = False
@@ -1695,13 +1984,15 @@
             # yes, there is
             if sat_session.profile != profile:
                 # it's a different profile, we need to disconnect it
-                log.warning(_(u"{new_profile} requested login, but {old_profile} was already connected, disconnecting {old_profile}").format(
-                    old_profile = sat_session.profile,
-                    new_profile = profile))
+                log.warning(
+                    _(
+                        u"{new_profile} requested login, but {old_profile} was already connected, disconnecting {old_profile}"
+                    ).format(old_profile=sat_session.profile, new_profile=profile)
+                )
                 self.purgeSession(request)
 
         if self.waiting_profiles.getRequest(profile):
-            # FIXME: check if and when this can happen
+            #  FIXME: check if and when this can happen
             raise failure.Failure(exceptions.NotReady("Already waiting"))
 
         self.waiting_profiles.setRequest(request, profile, register_with_ext_jid)
@@ -1710,22 +2001,37 @@
         except Exception as failure_:
             fault = failure_.faultString
             self.waiting_profiles.purgeRequest(profile)
-            if fault in ('PasswordError', 'ProfileUnknownError'):
-                log.info(u"Profile {profile} doesn't exist or the submitted password is wrong".format(profile=profile))
+            if fault in ("PasswordError", "ProfileUnknownError"):
+                log.info(
+                    u"Profile {profile} doesn't exist or the submitted password is wrong".format(
+                        profile=profile
+                    )
+                )
                 raise failure.Failure(ValueError(C.PROFILE_AUTH_ERROR))
-            elif fault == 'SASLAuthError':
-                log.info(u"The XMPP password of profile {profile} is wrong".format(profile=profile))
+            elif fault == "SASLAuthError":
+                log.info(
+                    u"The XMPP password of profile {profile} is wrong".format(
+                        profile=profile
+                    )
+                )
                 raise failure.Failure(ValueError(C.XMPP_AUTH_ERROR))
-            elif fault == 'NoReply':
-                log.info(_("Did not receive a reply (the timeout expired or the connection is broken)"))
+            elif fault == "NoReply":
+                log.info(
+                    _(
+                        "Did not receive a reply (the timeout expired or the connection is broken)"
+                    )
+                )
                 raise exceptions.TimeOutError
             else:
-                log.error(u'Unmanaged fault string "{fault}" in errback for the connection of profile {profile}'.format(
-                    fault=fault, profile=profile))
+                log.error(
+                    u'Unmanaged fault string "{fault}" in errback for the connection of profile {profile}'.format(
+                        fault=fault, profile=profile
+                    )
+                )
                 raise failure.Failure(exceptions.InternalError(fault))
 
         if connected:
-            # profile is already connected in backend
+            #  profile is already connected in backend
             # do we have a corresponding session in Libervia?
             sat_session = session_iface.ISATSession(request.getSession())
             if sat_session.profile:
@@ -1733,14 +2039,21 @@
                 if sat_session.profile != profile:
                     # existing session should have been ended above
                     # so this line should never be reached
-                    log.error(_(u'session profile [{session_profile}] differs from login profile [{profile}], this should not happen!').format(
-                        session_profile = sat_session.profile,
-                        profile = profile
-                        ))
+                    log.error(
+                        _(
+                            u"session profile [{session_profile}] differs from login profile [{profile}], this should not happen!"
+                        ).format(session_profile=sat_session.profile, profile=profile)
+                    )
                     raise exceptions.InternalError("profile mismatch")
                 defer.returnValue(C.SESSION_ACTIVE)
-            log.info(_(u"profile {profile} was already connected in backend".format(profile=profile)))
-            # no, we have to create it
+            log.info(
+                _(
+                    u"profile {profile} was already connected in backend".format(
+                        profile=profile
+                    )
+                )
+            )
+            #  no, we have to create it
 
         state = yield self._logged(profile, request)
         defer.returnValue(state)
@@ -1760,12 +2073,18 @@
         @raise PermissionError: registration is now allowed in server configuration
         """
         if not self.options["allow_registration"]:
-            log.warning(_(u"Registration received while it is not allowed, hack attempt?"))
-            raise failure.Failure(exceptions.PermissionError(u"Registration is not allowed on this server"))
+            log.warning(
+                _(u"Registration received while it is not allowed, hack attempt?")
+            )
+            raise failure.Failure(
+                exceptions.PermissionError(u"Registration is not allowed on this server")
+            )
 
-        if not re.match(C.REG_LOGIN_RE, login) or \
-           not re.match(C.REG_EMAIL_RE, email, re.IGNORECASE) or \
-           len(password) < C.PASSWORD_MIN_LENGTH:
+        if (
+            not re.match(C.REG_LOGIN_RE, login)
+            or not re.match(C.REG_EMAIL_RE, email, re.IGNORECASE)
+            or len(password) < C.PASSWORD_MIN_LENGTH
+        ):
             return C.INVALID_INPUT
 
         def registered(result):
@@ -1778,8 +2097,11 @@
             elif status == "InternalError":
                 return C.INTERNAL_ERROR
             else:
-                log.error(_(u'Unknown registering error status: {status }').format(
-                    status = status))
+                log.error(
+                    _(u"Unknown registering error status: {status }").format(
+                        status=status
+                    )
+                )
                 return status
 
         d = self.bridgeCall("registerSatAccount", email, password, login)
@@ -1798,6 +2120,7 @@
 
     def startService(self):
         """Connect the profile for Libervia and start the HTTP(S) server(s)"""
+
         def eb(e):
             log.error(_(u"Connection failed: %s") % e)
             self.stop()
@@ -1807,14 +2130,22 @@
                 connected = self.bridge.isConnected(C.SERVICE_PROFILE)
             except Exception as e:
                 # we don't want the traceback
-                msg = [l for l in unicode(e).split('\n') if l][-1]
-                log.error(u"Can't check service profile ({profile}), are you sure it exists ?\n{error}".format(
-                    profile=C.SERVICE_PROFILE, error=msg))
+                msg = [l for l in unicode(e).split("\n") if l][-1]
+                log.error(
+                    u"Can't check service profile ({profile}), are you sure it exists ?\n{error}".format(
+                        profile=C.SERVICE_PROFILE, error=msg
+                    )
+                )
                 self.stop()
                 return
             if not connected:
-                self.bridge.connect(C.SERVICE_PROFILE, self.options['passphrase'],
-                                    {}, callback=self._startService, errback=eb)
+                self.bridge.connect(
+                    C.SERVICE_PROFILE,
+                    self.options["passphrase"],
+                    {},
+                    callback=self._startService,
+                    errback=eb,
+                )
             else:
                 self._startService()
 
@@ -1825,7 +2156,10 @@
     def putChild(self, path, resource):
         """Add a child to the root resource"""
         # FIXME: check that no information is leaked (c.f. https://twistedmatrix.com/documents/current/web/howto/using-twistedweb.html#request-encoders)
-        self.root.putChild(path, web_resource.EncodingResourceWrapper(resource, [server.GzipEncoderFactory()]))
+        self.root.putChild(
+            path,
+            web_resource.EncodingResourceWrapper(resource, [server.GzipEncoderFactory()]),
+        )
 
     def getExtBaseURLData(self, request):
         """Retrieve external base URL Data
@@ -1840,23 +2174,31 @@
         ext_data = self.base_url_ext_data
         url_path = request.URLPath()
         if not ext_data.scheme or not ext_data.netloc:
-            # ext_data is not specified, we check headers
-            if request.requestHeaders.hasHeader('x-forwarded-host'):
+            #  ext_data is not specified, we check headers
+            if request.requestHeaders.hasHeader("x-forwarded-host"):
                 # we are behing a proxy
                 # we fill proxy_scheme and proxy_netloc value
-                proxy_host = request.requestHeaders.getRawHeaders('x-forwarded-host')[0]
+                proxy_host = request.requestHeaders.getRawHeaders("x-forwarded-host")[0]
                 try:
-                    proxy_server = request.requestHeaders.getRawHeaders('x-forwarded-server')[0]
+                    proxy_server = request.requestHeaders.getRawHeaders(
+                        "x-forwarded-server"
+                    )[0]
                 except TypeError:
                     # no x-forwarded-server found, we use proxy_host
                     proxy_netloc = proxy_host
                 else:
                     # if the proxy host has a port, we use it with server name
-                    proxy_port = urlparse.urlsplit(u'//{}'.format(proxy_host)).port
-                    proxy_netloc = u'{}:{}'.format(proxy_server, proxy_port) if proxy_port is not None else proxy_server
-                proxy_netloc = proxy_netloc.decode('utf-8')
+                    proxy_port = urlparse.urlsplit(u"//{}".format(proxy_host)).port
+                    proxy_netloc = (
+                        u"{}:{}".format(proxy_server, proxy_port)
+                        if proxy_port is not None
+                        else proxy_server
+                    )
+                proxy_netloc = proxy_netloc.decode("utf-8")
                 try:
-                    proxy_scheme = request.requestHeaders.getRawHeaders('x-forwarded-proto')[0].decode('utf-8')
+                    proxy_scheme = request.requestHeaders.getRawHeaders(
+                        "x-forwarded-proto"
+                    )[0].decode("utf-8")
                 except TypeError:
                     proxy_scheme = None
             else:
@@ -1865,12 +2207,14 @@
             proxy_scheme, proxy_netloc = None, None
 
         return urlparse.SplitResult(
-            ext_data.scheme or proxy_scheme or url_path.scheme.decode('utf-8'),
-            ext_data.netloc or proxy_netloc or url_path.netloc.decode('utf-8'),
-            ext_data.path or u'/',
-            '', '')
+            ext_data.scheme or proxy_scheme or url_path.scheme.decode("utf-8"),
+            ext_data.netloc or proxy_netloc or url_path.netloc.decode("utf-8"),
+            ext_data.path or u"/",
+            "",
+            "",
+        )
 
-    def getExtBaseURL(self, request, path='', query='', fragment='', scheme=None):
+    def getExtBaseURL(self, request, path="", query="", fragment="", scheme=None):
         """Get external URL according to given elements
 
         external URL is the URL seen by external user
@@ -1883,11 +2227,15 @@
         @return (unicode): external URL
         """
         split_result = self.getExtBaseURLData(request)
-        return urlparse.urlunsplit((
-            split_result.scheme.decode('utf-8') if scheme is None else scheme,
-            split_result.netloc.decode('utf-8'),
-            os.path.join(split_result.path, path),
-            query, fragment))
+        return urlparse.urlunsplit(
+            (
+                split_result.scheme.decode("utf-8") if scheme is None else scheme,
+                split_result.netloc.decode("utf-8"),
+                os.path.join(split_result.path, path),
+                query,
+                fragment,
+            )
+        )
 
     def checkRedirection(self, url):
         """check is a part of the URL prefix is redirected then replace it
@@ -1896,14 +2244,14 @@
         @return (unicode): possibly redirected URL which should link to the same location
         """
         inv_redirections = self.root.inv_redirections
-        url_parts = url.strip(u'/').split(u'/')
+        url_parts = url.strip(u"/").split(u"/")
         for idx in xrange(len(url), 0, -1):
-            test_url = u'/' + u'/'.join(url_parts[:idx])
+            test_url = u"/" + u"/".join(url_parts[:idx])
             if test_url in inv_redirections:
                 rem_url = url_parts[idx:]
                 return os.path.join(
-                    u'/',
-                    u'/'.join([inv_redirections[test_url]] + rem_url))
+                    u"/", u"/".join([inv_redirections[test_url]] + rem_url)
+                )
         return url
 
     ## Sessions ##
@@ -1943,16 +2291,21 @@
         """
         sat_session = self.getSessionData(request, session_iface.ISATSession)
         if sat_session.profile is None:
-            raise exceptions.InternalError(u'profile must be set to use this method')
+            raise exceptions.InternalError(u"profile must be set to use this method")
         affiliation = sat_session.getAffiliation(service, node)
         if affiliation is not None:
             defer.returnValue(affiliation)
         else:
             try:
-                affiliations = yield self.bridgeCall('psAffiliationsGet', service.full(), node, sat_session.profile)
+                affiliations = yield self.bridgeCall(
+                    "psAffiliationsGet", service.full(), node, sat_session.profile
+                )
             except Exception as e:
-                log.warning("Can't retrieve affiliation for {service}/{node}: {reason}".format(
-                    service=service, node=node, reason=e))
+                log.warning(
+                    "Can't retrieve affiliation for {service}/{node}: {reason}".format(
+                        service=service, node=node, reason=e
+                    )
+                )
                 affiliation = u""
             else:
                 try:
@@ -1966,10 +2319,10 @@
 
     def getWebsocketURL(self, request):
         base_url_split = self.getExtBaseURLData(request)
-        if base_url_split.scheme.endswith('s'):
-            scheme = u'wss'
+        if base_url_split.scheme.endswith("s"):
+            scheme = u"wss"
         else:
-            scheme = u'ws'
+            scheme = u"ws"
 
         return self.getExtBaseURL(request, path=scheme, scheme=scheme)
 
@@ -1981,8 +2334,8 @@
     def getHTTPDate(self, timestamp=None):
         now = time.gmtime(timestamp)
         fmt_date = u"{day_name}, %d {month_name} %Y %H:%M:%S GMT".format(
-            day_name = C.HTTP_DAYS[now.tm_wday],
-            month_name = C.HTTP_MONTH[now.tm_mon-1])
+            day_name=C.HTTP_DAYS[now.tm_wday], month_name=C.HTTP_MONTH[now.tm_mon - 1]
+        )
         return time.strftime(fmt_date, now)
 
     ## TLS related methods ##
@@ -1992,15 +2345,14 @@
 
         Must be called only if TLS is activated
         """
-        if not self.options['tls_certificate']:
+        if not self.options["tls_certificate"]:
             log.error(u"a TLS certificate is needed to activate HTTPS connection")
             self.quit(1)
-        if not self.options['tls_private_key']:
-            self.options['tls_private_key'] = self.options['tls_certificate']
+        if not self.options["tls_private_key"]:
+            self.options["tls_private_key"] = self.options["tls_certificate"]
 
-
-        if not self.options['tls_private_key']:
-            self.options['tls_private_key'] = self.options['tls_certificate']
+        if not self.options["tls_private_key"]:
+            self.options["tls_private_key"] = self.options["tls_certificate"]
 
     def _loadCertificates(self, f):
         """Read a .pem file with a list of certificates
@@ -2016,9 +2368,13 @@
         while True:
             line = f.readline()
             buf.append(line)
-            if '-----END CERTIFICATE-----' in line:
-                certificates.append(OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, ''.join(buf)))
-                buf=[]
+            if "-----END CERTIFICATE-----" in line:
+                certificates.append(
+                    OpenSSL.crypto.load_certificate(
+                        OpenSSL.crypto.FILETYPE_PEM, "".join(buf)
+                    )
+                )
+                buf = []
             elif not line:
                 log.debug(u"{} certificate(s) found".format(len(certificates)))
                 return certificates
@@ -2048,24 +2404,41 @@
 
         cert_options = {}
 
-        for name, option, method in [('privateKey', 'tls_private_key', self._loadPKey),
-                                    ('certificate', 'tls_certificate', self._loadCertificate),
-                                    ('extraCertChain', 'tls_chain', self._loadCertificates)]:
+        for name, option, method in [
+            ("privateKey", "tls_private_key", self._loadPKey),
+            ("certificate", "tls_certificate", self._loadCertificate),
+            ("extraCertChain", "tls_chain", self._loadCertificates),
+        ]:
             path = self.options[option]
             if not path:
-                assert option=='tls_chain'
+                assert option == "tls_chain"
                 continue
             log.debug(u"loading {option} from {path}".format(option=option, path=path))
             try:
                 with open(path) as f:
                     cert_options[name] = method(f)
             except IOError as e:
-                log.error(u"Error while reading file {path} for option {option}: {error}".format(path=path, option=option, error=e))
+                log.error(
+                    u"Error while reading file {path} for option {option}: {error}".format(
+                        path=path, option=option, error=e
+                    )
+                )
                 self.quit(2)
             except OpenSSL.crypto.Error:
-                log.error(u"Error while parsing file {path} for option {option}, are you sure it is a valid .pem file?".format(path=path, option=option))
-                if option=='tls_private_key' and self.options['tls_certificate'] == path:
-                    log.error(u"You are using the same file for private key and public certificate, make sure that both a in {path} or use --tls_private_key option".format(path=path))
+                log.error(
+                    u"Error while parsing file {path} for option {option}, are you sure it is a valid .pem file?".format(
+                        path=path, option=option
+                    )
+                )
+                if (
+                    option == "tls_private_key"
+                    and self.options["tls_certificate"] == path
+                ):
+                    log.error(
+                        u"You are using the same file for private key and public certificate, make sure that both a in {path} or use --tls_private_key option".format(
+                            path=path
+                        )
+                    )
                 self.quit(2)
 
         return ssl.CertificateOptions(**cert_options)
@@ -2081,20 +2454,30 @@
         """
         # now that we have service profile connected, we add resource for its cache
         service_path = regex.pathEscape(C.SERVICE_PROFILE)
-        cache_dir = os.path.join(self.cache_root_dir, u'profiles', service_path)
+        cache_dir = os.path.join(self.cache_root_dir, u"profiles", service_path)
         self.cache_resource.putChild(service_path, ProtectedFile(cache_dir))
-        self.service_cache_url = u'/' + os.path.join(C.CACHE_DIR, service_path)
+        self.service_cache_url = u"/" + os.path.join(C.CACHE_DIR, service_path)
         session_iface.SATSession.service_cache_url = self.service_cache_url
 
-        if self.options['connection_type'] in ('https', 'both'):
+        if self.options["connection_type"] in ("https", "both"):
             self._TLSOptionsCheck()
             context_factory = self._getTLSContextFactory()
-            reactor.listenSSL(self.options['port_https'], self.site, context_factory)
-        if self.options['connection_type'] in ('http', 'both'):
-            if self.options['connection_type'] == 'both' and self.options['redirect_to_https']:
-                reactor.listenTCP(self.options['port'], server.Site(RedirectToHTTPS(self.options['port'], self.options['port_https_ext'])))
+            reactor.listenSSL(self.options["port_https"], self.site, context_factory)
+        if self.options["connection_type"] in ("http", "both"):
+            if (
+                self.options["connection_type"] == "both"
+                and self.options["redirect_to_https"]
+            ):
+                reactor.listenTCP(
+                    self.options["port"],
+                    server.Site(
+                        RedirectToHTTPS(
+                            self.options["port"], self.options["port_https_ext"]
+                        )
+                    ),
+                )
             else:
-                reactor.listenTCP(self.options['port'], self.site)
+                reactor.listenTCP(self.options["port"], self.site)
 
     @defer.inlineCallbacks
     def stopService(self):
@@ -2122,7 +2505,6 @@
 
 
 class RedirectToHTTPS(web_resource.Resource):
-
     def __init__(self, old_port, new_port):
         web_resource.Resource.__init__(self)
         self.isLeaf = True
@@ -2130,10 +2512,14 @@
         self.new_port = new_port
 
     def render(self, request):
-        netloc = request.URLPath().netloc.replace(':%s' % self.old_port, ':%s' % self.new_port)
+        netloc = request.URLPath().netloc.replace(
+            ":%s" % self.old_port, ":%s" % self.new_port
+        )
         url = "https://" + netloc + request.uri
         return web_util.redirectTo(url, request)
 
 
 registerAdapter(session_iface.SATSession, server.Session, session_iface.ISATSession)
-registerAdapter(session_iface.SATGuestSession, server.Session, session_iface.ISATGuestSession)
+registerAdapter(
+    session_iface.SATGuestSession, server.Session, session_iface.ISATGuestSession
+)
--- a/src/server/session_iface.py	Sun Jun 24 22:21:25 2018 +0200
+++ b/src/server/session_iface.py	Fri Jun 29 17:45:26 2018 +0200
@@ -24,9 +24,10 @@
 import shortuuid
 import time
 
-FLAGS_KEY = '_flags'
+FLAGS_KEY = "_flags"
 MAX_CACHE_AFFILIATIONS = 100  # number of nodes to keep in cache
 
+
 class ISATSession(Interface):
     profile = Attribute("Sat profile")
     jid = Attribute("JID associated with the profile")
@@ -52,8 +53,8 @@
     @property
     def cache_dir(self):
         if self.profile is None:
-            return self.service_cache_url + u'/'
-        return os.path.join(u'/', C.CACHE_DIR, self.uuid) + u'/'
+            return self.service_cache_url + u"/"
+        return os.path.join(u"/", C.CACHE_DIR, self.uuid) + u"/"
 
     @property
     def connected(self):
@@ -65,7 +66,7 @@
         if self.profile is None:
             return False
         else:
-            return self.profile.startswith('guest@@')
+            return self.profile.startswith("guest@@")
 
     def getPageData(self, page, key):
         """get session data for a page
--- a/src/server/utils.py	Sun Jun 24 22:21:25 2018 +0200
+++ b/src/server/utils.py	Fri Jun 29 17:45:26 2018 +0200
@@ -22,16 +22,18 @@
 from sat.core import exceptions
 from sat.core.log import getLogger
 import urllib
+
 log = getLogger(__name__)
 
 
-def quote(value, safe='@'):
+def quote(value, safe="@"):
     """shortcut to quote an unicode value for URL"""
-    return urllib.quote(value.encode('utf-8'), safe=safe)
+    return urllib.quote(value.encode("utf-8"), safe=safe)
 
 
 class ProgressHandler(object):
     """class to help the management of progressions"""
+
     handlers = {}
 
     def __init__(self, host, progress_id, profile):
@@ -44,34 +46,40 @@
         handlers = cls.handlers
         if profile in handlers and progress_id in handlers[profile]:
             handler_data = handlers[profile][progress_id]
-            timeout = handler_data[u'timeout']
+            timeout = handler_data[u"timeout"]
             if timeout.active():
                 timeout.cancel()
             cb = handler_data[name]
             if cb is not None:
                 cb(data)
-            if name == u'started':
+            if name == u"started":
                 pass
-            elif name == u'finished':
-                handler_data[u'deferred'].callback(data)
-                handler_data[u'instance'].unregister_handler()
-            elif name == u'error':
-                handler_data[u'deferred'].errback(Exception(data))
-                handler_data[u'instance'].unregister_handler()
+            elif name == u"finished":
+                handler_data[u"deferred"].callback(data)
+                handler_data[u"instance"].unregister_handler()
+            elif name == u"error":
+                handler_data[u"deferred"].errback(Exception(data))
+                handler_data[u"instance"].unregister_handler()
             else:
-                log.error(u'unexpected signal: {name}'.format(name=name))
+                log.error(u"unexpected signal: {name}".format(name=name))
 
     def _timeout(self):
-        log.warning(_(u"No progress received, cancelling handler: {progress_id} [{profile}]").format(
-            progress_id = self.progress_id, profile = self.profile))
+        log.warning(
+            _(
+                u"No progress received, cancelling handler: {progress_id} [{profile}]"
+            ).format(progress_id=self.progress_id, profile=self.profile)
+        )
 
     def unregister_handler(self):
         """remove a previously registered handler"""
         try:
             del self.handlers[self.profile][self.progress_id]
         except KeyError:
-            log.warning(_(u"Trying to remove unknown handler: {progress_id} [{profile}]").format(
-                progress_id = self.progress_id, profile = self.profile))
+            log.warning(
+                _(u"Trying to remove unknown handler: {progress_id} [{profile}]").format(
+                    progress_id=self.progress_id, profile=self.profile
+                )
+            )
         else:
             if not self.handlers[self.profile]:
                 self.handlers[self.profile]
@@ -87,15 +95,19 @@
             an exception is raised
         @return (D(dict[unicode,unicode])): a deferred called when progression is finished
         """
-        handler_data = self.handlers.setdefault(self.profile, {}).setdefault(self.progress_id, {})
+        handler_data = self.handlers.setdefault(self.profile, {}).setdefault(
+            self.progress_id, {}
+        )
         if handler_data:
-            raise exceptions.ConflictError(u"There is already one handler for this progression")
-        handler_data[u'instance'] = self
-        deferred = handler_data[u'deferred'] = defer.Deferred()
-        handler_data[u'started'] = started_cb
-        handler_data[u'finished'] = finished_cb
-        handler_data[u'error'] = error_cb
-        handler_data[u'timeout'] = reactor.callLater(timeout, self._timeout)
+            raise exceptions.ConflictError(
+                u"There is already one handler for this progression"
+            )
+        handler_data[u"instance"] = self
+        deferred = handler_data[u"deferred"] = defer.Deferred()
+        handler_data[u"started"] = started_cb
+        handler_data[u"finished"] = finished_cb
+        handler_data[u"error"] = error_cb
+        handler_data[u"timeout"] = reactor.callLater(timeout, self._timeout)
         return deferred
 
 
--- a/src/server/websockets.py	Sun Jun 24 22:21:25 2018 +0200
+++ b/src/server/websockets.py	Fri Jun 29 17:45:26 2018 +0200
@@ -19,6 +19,7 @@
 
 from sat.core.i18n import _
 from sat.core.log import getLogger
+
 log = getLogger(__name__)
 from sat.core import exceptions
 
@@ -28,7 +29,7 @@
 
 import json
 
-LIBERVIA_PROTOCOL = 'libervia_page'
+LIBERVIA_PROTOCOL = "libervia_page"
 
 
 class LiberviaPageWSProtocol(websocket.WebSocketServerProtocol):
@@ -36,45 +37,59 @@
     tokens_map = {}
 
     def onConnect(self, request):
-        prefix = LIBERVIA_PROTOCOL + u'_'
+        prefix = LIBERVIA_PROTOCOL + u"_"
         for protocol in request.protocols:
             if protocol.startswith(prefix):
-                token = protocol[len(prefix):].strip()
+                token = protocol[len(prefix) :].strip()
                 if token:
                     break
         else:
-            raise types.ConnectionDeny(types.ConnectionDeny.NOT_IMPLEMENTED,
-                                       u"Can't use this subprotocol")
+            raise types.ConnectionDeny(
+                types.ConnectionDeny.NOT_IMPLEMENTED, u"Can't use this subprotocol"
+            )
 
         if token not in self.tokens_map:
             log.warning(_(u"Can't activate page socket: unknown token"))
-            raise types.ConnectionDeny(types.ConnectionDeny.FORBIDDEN,
-                                       u"Bad token, please reload page")
+            raise types.ConnectionDeny(
+                types.ConnectionDeny.FORBIDDEN, u"Bad token, please reload page"
+            )
         self.token = token
-        self.page = self.tokens_map[token]['page']
-        self.request = self.tokens_map[token]['request']
+        self.page = self.tokens_map[token]["page"]
+        self.request = self.tokens_map[token]["request"]
         return protocol
 
     def onOpen(self):
-        log.debug(_(u"Websocket opened for {page} (token: {token})".format(
-            page = self.page,
-            token = self.token)))
+        log.debug(
+            _(
+                u"Websocket opened for {page} (token: {token})".format(
+                    page=self.page, token=self.token
+                )
+            )
+        )
         self.request.sendData = self.sendJSONData
         self.page.onSocketOpen(self.request)
 
     def onMessage(self, payload, isBinary):
         try:
-            data_json = json.loads(payload.decode('utf8'))
+            data_json = json.loads(payload.decode("utf8"))
         except ValueError as e:
-            log.warning(_(u"Not valid JSON, ignoring data: {msg}\n{data}").format(msg=e, data=payload))
+            log.warning(
+                _(u"Not valid JSON, ignoring data: {msg}\n{data}").format(
+                    msg=e, data=payload
+                )
+            )
             return
-        # we request page first, to raise an AttributeError
+        #  we request page first, to raise an AttributeError
         # if it is not set (which should never happen)
         page = self.page
         try:
             cb = page.on_data
         except AttributeError:
-            log.warning(_(u'No "on_data" method set on dynamic page, ignoring data:\n{data}').format(data=data_json))
+            log.warning(
+                _(
+                    u'No "on_data" method set on dynamic page, ignoring data:\n{data}'
+                ).format(data=data_json)
+            )
         else:
             cb(page, self.request, data_json)
 
@@ -82,7 +97,7 @@
         try:
             token = self.token
         except AttributeError:
-            log.warning(_(u'Websocket closed but no token is associated'))
+            log.warning(_(u"Websocket closed but no token is associated"))
             return
 
         self.page.onSocketClose(self.request)
@@ -91,22 +106,32 @@
             del self.tokens_map[token]
             del self.request.sendData
         except (KeyError, AttributeError):
-            raise exceptions.InternalError(_(u"Token or sendData doesn't exist, this should never happen!"))
-        log.debug(_(u"Websocket closed for {page} (token: {token}). {reason}".format(
-            page = self.page,
-            token = self.token,
-            reason = u'' if wasClean else _(u'Reason: {reason}').format(reason=reason))))
+            raise exceptions.InternalError(
+                _(u"Token or sendData doesn't exist, this should never happen!")
+            )
+        log.debug(
+            _(
+                u"Websocket closed for {page} (token: {token}). {reason}".format(
+                    page=self.page,
+                    token=self.token,
+                    reason=u""
+                    if wasClean
+                    else _(u"Reason: {reason}").format(reason=reason),
+                )
+            )
+        )
 
     def sendJSONData(self, type_, **data):
-        assert 'type' not in data
-        data['type'] = type_
-        self.sendMessage(json.dumps(data, ensure_ascii = False).encode('utf8'))
+        assert "type" not in data
+        data["type"] = type_
+        self.sendMessage(json.dumps(data, ensure_ascii=False).encode("utf8"))
 
     @classmethod
     def getBaseURL(cls, host, secure):
         return u"ws{sec}://localhost:{port}".format(
-               sec='s' if secure else '',
-               port=cls.host.options['port_https' if secure else 'port'])
+            sec="s" if secure else "",
+            port=cls.host.options["port_https" if secure else "port"],
+        )
 
     @classmethod
     def getResource(cls, host, secure):
@@ -119,6 +144,5 @@
     @classmethod
     def registerToken(cls, token, page, request):
         if token in cls.tokens_map:
-            raise exceptions.ConflictError(_(u'This token is already registered'))
-        cls.tokens_map[token] = {'page': page,
-                                 'request': request}
+            raise exceptions.ConflictError(_(u"This token is already registered"))
+        cls.tokens_map[token] = {"page": page, "request": request}
--- a/src/twisted/plugins/libervia_server.py	Sun Jun 24 22:21:25 2018 +0200
+++ b/src/twisted/plugins/libervia_server.py	Fri Jun 29 17:45:26 2018 +0200
@@ -20,11 +20,13 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 from twisted.internet import defer
+
 if defer.Deferred.debug:
     # if we are in debug mode, we want to use ipdb instead of pdb
     try:
         import ipdb
         import pdb
+
         pdb.set_trace = ipdb.set_trace
         pdb.post_mortem = ipdb.post_mortem
     except ImportError:
@@ -50,86 +52,127 @@
 CONFIG_SECTION = C.APP_NAME.lower()
 if libervia.__version__ != sat.__version__:
     import sys
-    sys.stderr.write(u"""sat module version ({sat_version}) and {current_app} version ({current_version}) mismatch
+
+    sys.stderr.write(
+        u"""sat module version ({sat_version}) and {current_app} version ({current_version}) mismatch
 
 sat module is located at {sat_path}
 libervia module is located at {libervia_path}
 
 Please be sure to have the same version running
 """.format(
-        sat_version = sat.__version__,
-        current_app = C.APP_NAME,
-        current_version = libervia.__version__,
-        sat_path = os.path.dirname(sat.__file__),
-        libervia_path = os.path.dirname(libervia.__file__),
-        ).encode('utf-8'))
+            sat_version=sat.__version__,
+            current_app=C.APP_NAME,
+            current_version=libervia.__version__,
+            sat_path=os.path.dirname(sat.__file__),
+            libervia_path=os.path.dirname(libervia.__file__),
+        ).encode(
+            "utf-8"
+        )
+    )
     sys.stderr.flush()
     # we call os._exit to avoid help to be printed by twisted
     import os
+
     os._exit(1)
 
 
 def coerceConnectionType(value):  # called from Libervia.OPT_PARAMETERS
-    allowed_values = ('http', 'https', 'both')
+    allowed_values = ("http", "https", "both")
     if value not in allowed_values:
-        raise ValueError("%(given)s not in %(expected)s" % {'given': value, 'expected': str(allowed_values)})
+        raise ValueError(
+            "%(given)s not in %(expected)s"
+            % {"given": value, "expected": str(allowed_values)}
+        )
     return value
 
+
 def coerceDataDir(value):  # called from Libervia.OPT_PARAMETERS
     if isinstance(value, unicode):
         # XXX: if value comes from sat.conf, it's unicode,
         # and we need byte str here (for twisted)
-        value = value.encode('utf-8')
-    value = value.encode('utf-8')
+        value = value.encode("utf-8")
+    value = value.encode("utf-8")
     html = os.path.join(value, C.HTML_DIR)
     if not os.path.isfile(os.path.join(html, C.LIBERVIA_MAIN_PAGE)):
-        raise ValueError("%s is not a Libervia's browser HTML directory" % os.path.realpath(html))
+        raise ValueError(
+            "%s is not a Libervia's browser HTML directory" % os.path.realpath(html)
+        )
     themes_dir = os.path.join(value, C.THEMES_DIR)
-    if not os.path.isfile(os.path.join(themes_dir, 'default/styles/blog.css')):
-        raise ValueError("%s is not a Libervia's server data directory" % os.path.realpath(themes_dir))
+    if not os.path.isfile(os.path.join(themes_dir, "default/styles/blog.css")):
+        raise ValueError(
+            "%s is not a Libervia's server data directory" % os.path.realpath(themes_dir)
+        )
     return value
 
+
 def coerceBool(value):
     return C.bool(value)
 
+
 def coerceUnicode(value):
     # XXX: we use this method to check which value to convert to Unicode
     #      but we don't do the conversion here as Twisted expect str
     return value
 
+
 DATA_DIR_DEFAULT = ''
-OPT_PARAMETERS_BOTH = [['connection_type', 't', 'https', _(u"'http', 'https' or 'both' (to launch both servers).").encode('utf-8'), coerceConnectionType],
-                       ['port', 'p', 8080, _(u'The port number to listen HTTP on.').encode('utf-8'), int],
-                       ['port_https', 's', 8443, _(u'The port number to listen HTTPS on.').encode('utf-8'), int],
-                       ['port_https_ext', 'e', 0, _(u'The external port number used for HTTPS (0 means port_https value).').encode('utf-8'), int],
-                       ['tls_private_key', '', '', _(u'TLS certificate private key (PEM format)').encode('utf-8'), coerceUnicode],
-                       ['tls_certificate', 'c', 'libervia.pem', _(u'TLS public certificate or private key and public certificate combined (PEM format)').encode('utf-8'), coerceUnicode],
-                       ['tls_chain', '', '', _(u'TLS certificate intermediate chain (PEM format)').encode('utf-8'), coerceUnicode],
-                       ['redirect_to_https', 'r', True, _(u'Automatically redirect from HTTP to HTTPS.').encode('utf-8'), coerceBool],
-                       ['security_warning', 'w', True, _(u'Warn user that he is about to connect on HTTP.').encode('utf-8'), coerceBool],
-                       ['passphrase', 'k', '', (_(u"Passphrase for the SàT profile named '%s'") % C.SERVICE_PROFILE).encode('utf-8'), coerceUnicode],
-                       ['data_dir', 'd', DATA_DIR_DEFAULT, _(u'Data directory for Libervia').encode('utf-8'), coerceDataDir],
-                       ['allow_registration', '', True, _(u'Allow user to register new account').encode('utf-8'), coerceBool],
-                       ['base_url_ext', '', '', _(u'The external URL to use as base URL').encode('utf-8'), coerceUnicode],
-                      ]  # options which are in sat.conf and on command line, see https://twistedmatrix.com/documents/current/api/twisted.python.usage.Options.html
+# options which are in sat.conf and on command line,
+# see https://twistedmatrix.com/documents/current/api/twisted.python.usage.Options.html
+OPT_PARAMETERS_BOTH = [['connection_type', 't', 'https', _(u"'http', 'https' or 'both' "
+                        "(to launch both servers).").encode('utf-8'),
+                        coerceConnectionType],
+                       ['port', 'p', 8080,
+                        _(u'The port number to listen HTTP on.').encode('utf-8'), int],
+                       ['port_https', 's', 8443,
+                        _(u'The port number to listen HTTPS on.').encode('utf-8'), int],
+                       ['port_https_ext', 'e', 0, _(u'The external port number used for '
+                        u'HTTPS (0 means port_https value).').encode('utf-8'), int],
+                       ['tls_private_key', '', '', _(u'TLS certificate private key (PEM '
+                        u'format)').encode('utf-8'), coerceUnicode],
+                       ['tls_certificate', 'c', 'libervia.pem', _(u'TLS public '
+                        u'certificate or private key and public certificate combined '
+                        u'(PEM format)').encode('utf-8'), coerceUnicode],
+                       ['tls_chain', '', '', _(u'TLS certificate intermediate chain (PEM '
+                        u'format)').encode('utf-8'), coerceUnicode],
+                       ['redirect_to_https', 'r', True, _(u'Automatically redirect from '
+                        u'HTTP to HTTPS.').encode('utf-8'), coerceBool],
+                       ['security_warning', 'w', True, _(u'Warn user that he is about to '
+                        u'connect on HTTP.').encode('utf-8'), coerceBool],
+                       ['passphrase', 'k', '', (_(u"Passphrase for the SàT profile "
+                        u"named '%s'") % C.SERVICE_PROFILE).encode('utf-8'),
+                        coerceUnicode],
+                       ['data_dir', 'd', DATA_DIR_DEFAULT, _(u'Data directory for '
+                        u'Libervia').encode('utf-8'), coerceDataDir],
+                       ['allow_registration', '', True, _(u'Allow user to register new '
+                        u'account').encode('utf-8'), coerceBool],
+                       ['base_url_ext', '', '',
+                        _(u'The external URL to use as base URL').encode('utf-8'),
+                        coerceUnicode],
+                      ]
 # Options which are in sat.conf only
 OPT_PARAMETERS_CFG = [
-    ['empty_password_allowed_warning_dangerous_list', None, '', None],
-    ['url_redirections_profile', None, '', None],
-    ['url_redirections_dict', None, {}, None],
-    ['menu_json', None, C.DEFAULT_MENU, None],
-    ['tickets_trackers_json', None, None, None],
-    ['mr_handlers_json', None, None, None],
+    ["empty_password_allowed_warning_dangerous_list", None, "", None],
+    ["url_redirections_profile", None, "", None],
+    ["url_redirections_dict", None, {}, None],
+    ["menu_json", None, C.DEFAULT_MENU, None],
+    ["tickets_trackers_json", None, None, None],
+    ["mr_handlers_json", None, None, None],
 ]
 
+
 def initialise(options):
     """Method to initialise global modules"""
     from twisted.internet import glib2reactor
+
     glib2reactor.install()
-    # XXX: We need to configure logs before any log method is used, so here is the best place.
+    # XXX: We need to configure logs before any log method is used,
+    #      so here is the best place.
     from sat.core import log_config
+
     log_config.satConfigure(C.LOG_BACKEND_TWISTED, C, backend_data=options)
     from libervia.server import server
+
     # we can't import this file from libervia.server.server because it's not a true module
     # (there is no __init__.py file, as required by twistd plugin system), so we set the
     # global values from here
@@ -144,15 +187,17 @@
     optParameters = OPT_PARAMETERS_BOTH
 
     def __init__(self):
-        """Read SàT configuration file in order to overwrite the hard-coded default values.
+        """Read SàT configuration file in order to overwrite the hard-coded default values
 
         Priority for the usage of the values is (from lowest to highest):
             - hard-coded default values
             - values from SàT configuration files
             - values passed on the command line
         """
-        # If we do it the reading later: after the command line options have been parsed, there's no good way to know
-        # if the  options values are the hard-coded ones or if they have been passed on the command line.
+        # If we do it the reading later: after the command line options have been parsed,
+        # there's no good way to know
+        # if the  options values are the hard-coded ones or if they have been passed
+        # on the command line.
 
         # FIXME: must be refactored + code can be factorised with backend
         config_parser = ConfigParser.SafeConfigParser()
@@ -163,10 +208,10 @@
             try:
                 value = config.getConfig(config_parser, CONFIG_SECTION, name, Exception)
                 if isinstance(value, unicode):
-                    value = value.encode('utf-8')
+                    value = value.encode("utf-8")
                 try:
                     param[2] = param[4](value)
-                except IndexError: # the coerce method is optional
+                except IndexError:  # the coerce method is optional
                     param[2] = value
             except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
                 pass
@@ -179,14 +224,15 @@
 
         param config_parser(ConfigParser): read ConfigParser instance for sat.conf
         """
-        replacements = (('ssl_certificate', 'tls_certificate'),)
+        replacements = (("ssl_certificate", "tls_certificate"),)
         for old, new in replacements:
             try:
                 value = config.getConfig(config_parser, CONFIG_SECTION, old, Exception)
             except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
                 pass
             else:
-                print u"\n/!\\ Use of {old} is deprecated, please use {new} instead\n".format(old=old, new=new)
+                print(u"\n/!\\ Use of {old} is deprecated, please use {new} instead\n"
+                      .format(old=old, new=new))
                 config_parser.set(CONFIG_SECTION, new, value)
 
 
@@ -194,7 +240,7 @@
     implements(IServiceMaker, IPlugin)
 
     tapname = C.APP_NAME_FILE
-    description = _(u'The web frontend of Salut à Toi')
+    description = _(u"The web frontend of Salut à Toi")
     options = Options
 
     def makeService(self, options):
@@ -207,9 +253,10 @@
             except IndexError:
                 continue
             if coerce_cb == coerceUnicode:
-                options[opt[0]] = options[opt[0]].decode('utf-8')
+                options[opt[0]] = options[opt[0]].decode("utf-8")
         initialise(options.parent)
         from libervia.server import server
+
         return server.Libervia(options)