# HG changeset patch # User Goffi # Date 1530287126 -7200 # Node ID cdd389ef97bcbfd9b441ea6d4ce69b6a533cfa4d # Parent f287fc8bb31aa0db2e6dcb3fe05f5432d85848b6 server: code style reformatting using black diff -r f287fc8bb31a -r cdd389ef97bc src/common/constants.py --- 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" diff -r f287fc8bb31a -r cdd389ef97bc src/pages/app/page_meta.py --- 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" diff -r f287fc8bb31a -r cdd389ef97bc src/pages/blog/page_meta.py --- 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) diff -r f287fc8bb31a -r cdd389ef97bc src/pages/blog/view/atom.xml/page_meta.py --- 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() diff -r f287fc8bb31a -r cdd389ef97bc src/pages/chat/page_meta.py --- 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)) diff -r f287fc8bb31a -r cdd389ef97bc src/pages/chat/select/page_meta.py --- 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) diff -r f287fc8bb31a -r cdd389ef97bc src/pages/events/admin/page_meta.py --- 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_)) diff -r f287fc8bb31a -r cdd389ef97bc src/pages/events/new/page_meta.py --- 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) diff -r f287fc8bb31a -r cdd389ef97bc src/pages/events/page_meta.py --- 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 diff -r f287fc8bb31a -r cdd389ef97bc src/pages/events/rsvp/page_meta.py --- 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_)) diff -r f287fc8bb31a -r cdd389ef97bc src/pages/events/view/page_meta.py --- 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_)) diff -r f287fc8bb31a -r cdd389ef97bc src/pages/files/list/page_meta.py --- 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]) diff -r f287fc8bb31a -r cdd389ef97bc src/pages/files/page_meta.py --- 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) diff -r f287fc8bb31a -r cdd389ef97bc src/pages/files/view/page_meta.py --- 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)) diff -r f287fc8bb31a -r cdd389ef97bc src/pages/forums/list/page_meta.py --- 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 diff -r f287fc8bb31a -r cdd389ef97bc src/pages/forums/topics/page_meta.py --- 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) diff -r f287fc8bb31a -r cdd389ef97bc src/pages/forums/view/page_meta.py --- 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) diff -r f287fc8bb31a -r cdd389ef97bc src/pages/g/e/page_meta.py --- 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_)) diff -r f287fc8bb31a -r cdd389ef97bc src/pages/g/page_meta.py --- 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) diff -r f287fc8bb31a -r cdd389ef97bc src/pages/login/logged/page_meta.py --- 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 diff -r f287fc8bb31a -r cdd389ef97bc src/pages/login/page_meta.py --- 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) diff -r f287fc8bb31a -r cdd389ef97bc src/pages/merge-requests/disco/page_meta.py --- 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) diff -r f287fc8bb31a -r cdd389ef97bc src/pages/merge-requests/edit/page_meta.py --- 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"@", + ) diff -r f287fc8bb31a -r cdd389ef97bc src/pages/merge-requests/new/page_meta.py --- 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" diff -r f287fc8bb31a -r cdd389ef97bc src/pages/merge-requests/page_meta.py --- 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}" + ) diff -r f287fc8bb31a -r cdd389ef97bc src/pages/merge-requests/view/page_meta.py --- 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_)) diff -r f287fc8bb31a -r cdd389ef97bc src/pages/photos/album/page_meta.py --- 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) diff -r f287fc8bb31a -r cdd389ef97bc src/pages/photos/page_meta.py --- 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) diff -r f287fc8bb31a -r cdd389ef97bc src/pages/register/page_meta.py --- 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) diff -r f287fc8bb31a -r cdd389ef97bc src/pages/tickets/disco/page_meta.py --- 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) diff -r f287fc8bb31a -r cdd389ef97bc src/pages/tickets/edit/page_meta.py --- 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"@", + ) diff -r f287fc8bb31a -r cdd389ef97bc src/pages/tickets/new/page_meta.py --- 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"@", + ) diff -r f287fc8bb31a -r cdd389ef97bc src/pages/tickets/page_meta.py --- 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}" + ) diff -r f287fc8bb31a -r cdd389ef97bc src/pages/tickets/view/page_meta.py --- 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_)) diff -r f287fc8bb31a -r cdd389ef97bc src/pages/u/atom.xml/page_meta.py --- 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" diff -r f287fc8bb31a -r cdd389ef97bc src/pages/u/blog/page_meta.py --- 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"@"] + ) diff -r f287fc8bb31a -r cdd389ef97bc src/pages/u/page_meta.py --- 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) diff -r f287fc8bb31a -r cdd389ef97bc src/server/blog.py --- 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"
|
*?
") # 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 '&' instead of '&'""" - return '&'.join(["{}={}".format(urllib.quote_plus(str(k)), urllib.quote_plus(str(v))) - for k,v in query.iteritems()]) + return "&".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'\n{}'.format(feed_elt.toXml()) + atom_feed = u'\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 = ' ' - self.url = "{}/{}".format(base_url, _quote(entry['id'])) + self.author = " " + 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 diff -r f287fc8bb31a -r cdd389ef97bc src/server/constants.py --- 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", + ) diff -r f287fc8bb31a -r cdd389ef97bc src/server/html_tools.py --- 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 = { "&": "&", '"': """, "'": "'", ">": ">", "<": "<", - } + } return "".join(html_escape_table.get(c, c) for c in text) def convertNewLinesToXHTML(text): - return text.replace('\n', '
') + return text.replace("\n", "
") diff -r f287fc8bb31a -r cdd389ef97bc src/server/pages.py --- 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'', - url = self.url) + return u"LiberviaPage {name} at {url}".format( + name=self.name or u"", 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: diff -r f287fc8bb31a -r cdd389ef97bc src/server/pages_tools.py --- 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) diff -r f287fc8bb31a -r cdd389ef97bc src/server/server.py --- 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 = "

" + D_("You are about to connect to an unsecure service.") + "

 

" + text = ( + "

" + + D_("You are about to connect to an unsecure service.") + + "

 

" + ) - 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': '', 'faq_suffix': ''} - text += "

" + D_('and use the secure version of this website:') - text += '

 

%(url)s' % {'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": '', + "faq_suffix": "", + } + text += "

" + D_("and use the secure version of this website:") + text += '

 

%(url)s' % { + "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 + "

 

" 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 +) diff -r f287fc8bb31a -r cdd389ef97bc src/server/session_iface.py --- 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 diff -r f287fc8bb31a -r cdd389ef97bc src/server/utils.py --- 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 diff -r f287fc8bb31a -r cdd389ef97bc src/server/websockets.py --- 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} diff -r f287fc8bb31a -r cdd389ef97bc src/twisted/plugins/libervia_server.py --- 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 . 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)