Mercurial > libervia-web
comparison src/server/pages.py @ 1113:cdd389ef97bc
server: code style reformatting using black
author | Goffi <goffi@goffi.org> |
---|---|
date | Fri, 29 Jun 2018 17:45:26 +0200 |
parents | 045e8bdaed4f |
children |
comparison
equal
deleted
inserted
replaced
1112:f287fc8bb31a | 1113:cdd389ef97bc |
---|---|
26 from sat.core.i18n import _ | 26 from sat.core.i18n import _ |
27 from sat.core import exceptions | 27 from sat.core import exceptions |
28 from sat.tools.common import uri as common_uri | 28 from sat.tools.common import uri as common_uri |
29 from sat.tools.common import date_utils | 29 from sat.tools.common import date_utils |
30 from sat.core.log import getLogger | 30 from sat.core.log import getLogger |
31 | |
31 log = getLogger(__name__) | 32 log = getLogger(__name__) |
32 from libervia.server.constants import Const as C | 33 from libervia.server.constants import Const as C |
33 from libervia.server import session_iface | 34 from libervia.server import session_iface |
34 from libervia.server.utils import quote, SubPage | 35 from libervia.server.utils import quote, SubPage |
35 import libervia | 36 import libervia |
39 import os.path | 40 import os.path |
40 import urllib | 41 import urllib |
41 import time | 42 import time |
42 import hashlib | 43 import hashlib |
43 | 44 |
44 WebsocketMeta = namedtuple("WebsocketMeta", ('url', 'token', 'debug')) | 45 WebsocketMeta = namedtuple("WebsocketMeta", ("url", "token", "debug")) |
45 | |
46 | 46 |
47 | 47 |
48 class CacheBase(object): | 48 class CacheBase(object): |
49 | |
50 def __init__(self): | 49 def __init__(self): |
51 self._created = time.time() | 50 self._created = time.time() |
52 self._last_access = self._created | 51 self._last_access = self._created |
53 | 52 |
54 @property | 53 @property |
63 def last_access(self, timestamp): | 62 def last_access(self, timestamp): |
64 self._last_access = timestamp | 63 self._last_access = timestamp |
65 | 64 |
66 | 65 |
67 class CachePage(CacheBase): | 66 class CachePage(CacheBase): |
68 | |
69 def __init__(self, rendered): | 67 def __init__(self, rendered): |
70 super(CachePage, self).__init__() | 68 super(CachePage, self).__init__() |
71 self._created = time.time() | 69 self._created = time.time() |
72 self._last_access = self._created | 70 self._last_access = self._created |
73 self._rendered = rendered | 71 self._rendered = rendered |
81 def hash(self): | 79 def hash(self): |
82 return self._hash | 80 return self._hash |
83 | 81 |
84 | 82 |
85 class CacheURL(CacheBase): | 83 class CacheURL(CacheBase): |
86 | |
87 def __init__(self, request): | 84 def __init__(self, request): |
88 super(CacheURL, self).__init__() | 85 super(CacheURL, self).__init__() |
89 try: | 86 try: |
90 self._data = request.data.copy() | 87 self._data = request.data.copy() |
91 except AttributeError: | 88 except AttributeError: |
92 self._data = {} | 89 self._data = {} |
93 self._template_data = request.template_data.copy() | 90 self._template_data = request.template_data.copy() |
94 self._prepath = request.prepath[:] | 91 self._prepath = request.prepath[:] |
95 self._postpath = request.postpath[:] | 92 self._postpath = request.postpath[:] |
96 del self._template_data['csrf_token'] | 93 del self._template_data["csrf_token"] |
97 | 94 |
98 def use(self, request): | 95 def use(self, request): |
99 self.last_access = time.time() | 96 self.last_access = time.time() |
100 request.data = self._data.copy() | 97 request.data = self._data.copy() |
101 request.template_data.update(self._template_data) | 98 request.template_data.update(self._template_data) |
102 request.prepath = self._prepath[:] | 99 request.prepath = self._prepath[:] |
103 request.postpath = self._postpath[:] | 100 request.postpath = self._postpath[:] |
104 | 101 |
105 | 102 |
106 class LiberviaPage(web_resource.Resource): | 103 class LiberviaPage(web_resource.Resource): |
107 isLeaf = True # we handle subpages ourself | 104 isLeaf = True # we handle subpages ourself |
108 named_pages = {} | 105 named_pages = {} |
109 uri_callbacks = {} | 106 uri_callbacks = {} |
110 signals_handlers = {} | 107 signals_handlers = {} |
111 pages_redirects = {} | 108 pages_redirects = {} |
112 cache = {} | 109 cache = {} |
113 cached_urls = {} | 110 cached_urls = {} |
114 # Set of tuples (service/node/sub_id) of nodes subscribed for caching | 111 # Set of tuples (service/node/sub_id) of nodes subscribed for caching |
115 # sub_id can be empty string if not handled by service | 112 # sub_id can be empty string if not handled by service |
116 cache_pubsub_sub = set() | 113 cache_pubsub_sub = set() |
117 main_menu = None | 114 main_menu = None |
118 | 115 |
119 def __init__(self, host, root_dir, url, name=None, redirect=None, access=None, dynamic=False, parse_url=None, | 116 def __init__( |
120 prepare_render=None, render=None, template=None, | 117 self, |
121 on_data_post=None, on_data=None, on_signal=None, | 118 host, |
122 url_cache=False): | 119 root_dir, |
120 url, | |
121 name=None, | |
122 redirect=None, | |
123 access=None, | |
124 dynamic=False, | |
125 parse_url=None, | |
126 prepare_render=None, | |
127 render=None, | |
128 template=None, | |
129 on_data_post=None, | |
130 on_data=None, | |
131 on_signal=None, | |
132 url_cache=False, | |
133 ): | |
123 """initiate LiberviaPages | 134 """initiate LiberviaPages |
124 | 135 |
125 LiberviaPages are the main resources of Libervia, using easy to set python files | 136 LiberviaPages are the main resources of Libervia, using easy to set python files |
126 The arguments are the variables found in page_meta.py | 137 The arguments are the variables found in page_meta.py |
127 @param host(Libervia): the running instance of Libervia | 138 @param host(Libervia): the running instance of Libervia |
167 self.root_dir = root_dir | 178 self.root_dir = root_dir |
168 self.url = url | 179 self.url = url |
169 self.name = name | 180 self.name = name |
170 if name is not None: | 181 if name is not None: |
171 if name in self.named_pages: | 182 if name in self.named_pages: |
172 raise exceptions.ConflictError(_(u'a Libervia page named "{}" already exists'.format(name))) | 183 raise exceptions.ConflictError( |
173 if u'/' in name: | 184 _(u'a Libervia page named "{}" already exists'.format(name)) |
185 ) | |
186 if u"/" in name: | |
174 raise ValueError(_(u'"/" is not allowed in page names')) | 187 raise ValueError(_(u'"/" is not allowed in page names')) |
175 if not name: | 188 if not name: |
176 raise ValueError(_(u"a page name can't be empty")) | 189 raise ValueError(_(u"a page name can't be empty")) |
177 self.named_pages[name] = self | 190 self.named_pages[name] = self |
178 if access is None: | 191 if access is None: |
179 access = C.PAGES_ACCESS_PUBLIC | 192 access = C.PAGES_ACCESS_PUBLIC |
180 if access not in (C.PAGES_ACCESS_PUBLIC, C.PAGES_ACCESS_PROFILE, C.PAGES_ACCESS_NONE): | 193 if access not in ( |
181 raise NotImplementedError(_(u"{} access is not implemented yet").format(access)) | 194 C.PAGES_ACCESS_PUBLIC, |
195 C.PAGES_ACCESS_PROFILE, | |
196 C.PAGES_ACCESS_NONE, | |
197 ): | |
198 raise NotImplementedError( | |
199 _(u"{} access is not implemented yet").format(access) | |
200 ) | |
182 self.access = access | 201 self.access = access |
183 self.dynamic = dynamic | 202 self.dynamic = dynamic |
184 if redirect is not None: | 203 if redirect is not None: |
185 # only page access and name make sense in case of full redirection | 204 # only page access and name make sense in case of full redirection |
186 # so we check that rendering methods/values are not set | 205 # so we check that rendering methods/values are not set |
187 if not all(lambda x: x is not None | 206 if not all( |
188 for x in (parse_url, prepare_render, render, template)): | 207 lambda x: x is not None |
189 raise ValueError(_(u"you can't use full page redirection with other rendering method," | 208 for x in (parse_url, prepare_render, render, template) |
190 u"check self.pageRedirect if you need to use them")) | 209 ): |
210 raise ValueError( | |
211 _( | |
212 u"you can't use full page redirection with other rendering method," | |
213 u"check self.pageRedirect if you need to use them" | |
214 ) | |
215 ) | |
191 self.redirect = redirect | 216 self.redirect = redirect |
192 else: | 217 else: |
193 self.redirect = None | 218 self.redirect = None |
194 self.parse_url = parse_url | 219 self.parse_url = parse_url |
195 self.prepare_render = prepare_render | 220 self.prepare_render = prepare_render |
206 log.error(_(u"render and template methods can't be used at the same time")) | 231 log.error(_(u"render and template methods can't be used at the same time")) |
207 if parse_url is not None and not callable(parse_url): | 232 if parse_url is not None and not callable(parse_url): |
208 log.error(_(u"parse_url must be a callable")) | 233 log.error(_(u"parse_url must be a callable")) |
209 | 234 |
210 # if not None, next rendering will be cached | 235 # if not None, next rendering will be cached |
211 # it must then contain a list of the the keys to use (without the page instance) | 236 # it must then contain a list of the the keys to use (without the page instance) |
212 # e.g. [C.SERVICE_PROFILE, "pubsub", server@example.tld, pubsub_node] | 237 # e.g. [C.SERVICE_PROFILE, "pubsub", server@example.tld, pubsub_node] |
213 self._do_cache = None | 238 self._do_cache = None |
214 | 239 |
215 def __unicode__(self): | 240 def __unicode__(self): |
216 return u'LiberviaPage {name} at {url}'.format( | 241 return u"LiberviaPage {name} at {url}".format( |
217 name = self.name or u'<anonymous>', | 242 name=self.name or u"<anonymous>", url=self.url |
218 url = self.url) | 243 ) |
219 | 244 |
220 def __str__(self): | 245 def __str__(self): |
221 return self.__unicode__().encode('utf-8') | 246 return self.__unicode__().encode("utf-8") |
222 | 247 |
223 @classmethod | 248 @classmethod |
224 def importPages(cls, host, parent=None, path=None): | 249 def importPages(cls, host, parent=None, path=None): |
225 """Recursively import Libervia pages""" | 250 """Recursively import Libervia pages""" |
226 if path is None: | 251 if path is None: |
244 # so we can switch to import | 269 # so we can switch to import |
245 execfile(meta_path, page_data) | 270 execfile(meta_path, page_data) |
246 resource = LiberviaPage( | 271 resource = LiberviaPage( |
247 host, | 272 host, |
248 dir_path, | 273 dir_path, |
249 u'/' + u'/'.join(new_path), | 274 u"/" + u"/".join(new_path), |
250 name=page_data.get('name'), | 275 name=page_data.get("name"), |
251 redirect=page_data.get('redirect'), | 276 redirect=page_data.get("redirect"), |
252 access=page_data.get('access'), | 277 access=page_data.get("access"), |
253 dynamic=page_data.get('dynamic', False), | 278 dynamic=page_data.get("dynamic", False), |
254 parse_url=page_data.get('parse_url'), | 279 parse_url=page_data.get("parse_url"), |
255 prepare_render=page_data.get('prepare_render'), | 280 prepare_render=page_data.get("prepare_render"), |
256 render=page_data.get('render'), | 281 render=page_data.get("render"), |
257 template=page_data.get('template'), | 282 template=page_data.get("template"), |
258 on_data_post=page_data.get('on_data_post'), | 283 on_data_post=page_data.get("on_data_post"), |
259 on_data=page_data.get('on_data'), | 284 on_data=page_data.get("on_data"), |
260 on_signal=page_data.get('on_signal'), | 285 on_signal=page_data.get("on_signal"), |
261 url_cache=page_data.get('url_cache', False), | 286 url_cache=page_data.get("url_cache", False), |
262 ) | 287 ) |
263 parent.putChild(d, resource) | 288 parent.putChild(d, resource) |
264 log.info(u"Added /{path} page".format(path=u'[...]/'.join(new_path))) | 289 log.info(u"Added /{path} page".format(path=u"[...]/".join(new_path))) |
265 if 'uri_handlers' in page_data: | 290 if "uri_handlers" in page_data: |
266 if not isinstance(page_data, dict): | 291 if not isinstance(page_data, dict): |
267 log.error(_(u'uri_handlers must be a dict')) | 292 log.error(_(u"uri_handlers must be a dict")) |
268 else: | 293 else: |
269 for uri_tuple, cb_name in page_data['uri_handlers'].iteritems(): | 294 for uri_tuple, cb_name in page_data["uri_handlers"].iteritems(): |
270 if len(uri_tuple) != 2 or not isinstance(cb_name, basestring): | 295 if len(uri_tuple) != 2 or not isinstance(cb_name, basestring): |
271 log.error(_(u"invalid uri_tuple")) | 296 log.error(_(u"invalid uri_tuple")) |
272 continue | 297 continue |
273 log.info(_(u'setting {}/{} URIs handler').format(*uri_tuple)) | 298 log.info(_(u"setting {}/{} URIs handler").format(*uri_tuple)) |
274 try: | 299 try: |
275 cb = page_data[cb_name] | 300 cb = page_data[cb_name] |
276 except KeyError: | 301 except KeyError: |
277 log.error(_(u'missing {name} method to handle {1}/{2}').format( | 302 log.error( |
278 name = cb_name, *uri_tuple)) | 303 _(u"missing {name} method to handle {1}/{2}").format( |
304 name=cb_name, *uri_tuple | |
305 ) | |
306 ) | |
279 continue | 307 continue |
280 else: | 308 else: |
281 resource.registerURI(uri_tuple, cb) | 309 resource.registerURI(uri_tuple, cb) |
282 | 310 |
283 LiberviaPage.importPages(host, resource, new_path) | 311 LiberviaPage.importPages(host, resource, new_path) |
290 msg = _(u"menu item can't be empty") | 318 msg = _(u"menu item can't be empty") |
291 log.error(msg) | 319 log.error(msg) |
292 raise ValueError(msg) | 320 raise ValueError(msg) |
293 elif isinstance(menu, list): | 321 elif isinstance(menu, list): |
294 if len(menu) != 2: | 322 if len(menu) != 2: |
295 msg = _(u"menu item as list must be in the form [page_name, absolue URL]") | 323 msg = _( |
324 u"menu item as list must be in the form [page_name, absolue URL]" | |
325 ) | |
296 log.error(msg) | 326 log.error(msg) |
297 raise ValueError(msg) | 327 raise ValueError(msg) |
298 page_name, url = menu | 328 page_name, url = menu |
299 else: | 329 else: |
300 page_name = menu | 330 page_name = menu |
301 try: | 331 try: |
302 url = cls.getPageByName(page_name).url | 332 url = cls.getPageByName(page_name).url |
303 except KeyError as e: | 333 except KeyError as e: |
304 log.error(_(u"Can'find a named page ({msg}), please check menu_json in configuration.").format(msg=e)) | 334 log.error( |
335 _( | |
336 u"Can'find a named page ({msg}), please check menu_json in configuration." | |
337 ).format(msg=e) | |
338 ) | |
305 raise e | 339 raise e |
306 main_menu.append((page_name, url)) | 340 main_menu.append((page_name, url)) |
307 cls.main_menu = main_menu | 341 cls.main_menu = main_menu |
308 | 342 |
309 def registerURI(self, uri_tuple, get_uri_cb): | 343 def registerURI(self, uri_tuple, get_uri_cb): |
315 @param get_uri_cb(callable): method which take uri_data dict as only argument | 349 @param get_uri_cb(callable): method which take uri_data dict as only argument |
316 and return absolute path with correct arguments or None if the page | 350 and return absolute path with correct arguments or None if the page |
317 can't handle this URL | 351 can't handle this URL |
318 """ | 352 """ |
319 if uri_tuple in self.uri_callbacks: | 353 if uri_tuple in self.uri_callbacks: |
320 log.info(_(u"{}/{} URIs are already handled, replacing by the new handler").format(*uri_tuple)) | 354 log.info( |
355 _(u"{}/{} URIs are already handled, replacing by the new handler").format( | |
356 *uri_tuple | |
357 ) | |
358 ) | |
321 self.uri_callbacks[uri_tuple] = (self, get_uri_cb) | 359 self.uri_callbacks[uri_tuple] = (self, get_uri_cb) |
322 | 360 |
323 def registerSignal(self, request, signal, check_profile=True): | 361 def registerSignal(self, request, signal, check_profile=True): |
324 r"""register a signal handler | 362 r"""register a signal handler |
325 | 363 |
338 """ | 376 """ |
339 # FIXME: add a timeout, if socket is not opened before it, signal handler must be removed | 377 # FIXME: add a timeout, if socket is not opened before it, signal handler must be removed |
340 if not self.dynamic: | 378 if not self.dynamic: |
341 log.error(_(u"You can't register signal if page is not dynamic")) | 379 log.error(_(u"You can't register signal if page is not dynamic")) |
342 return | 380 return |
343 LiberviaPage.signals_handlers.setdefault(signal, {})[id(request)] = (self, request, check_profile) | 381 LiberviaPage.signals_handlers.setdefault(signal, {})[id(request)] = ( |
382 self, | |
383 request, | |
384 check_profile, | |
385 ) | |
344 request._signals_registered.append(signal) | 386 request._signals_registered.append(signal) |
345 | 387 |
346 @classmethod | 388 @classmethod |
347 def getPagePathFromURI(cls, uri): | 389 def getPagePathFromURI(cls, uri): |
348 """Retrieve page URL from xmpp: URI | 390 """Retrieve page URL from xmpp: URI |
351 @return (unicode,None): absolute path (starting from root "/") to page handling the URI | 393 @return (unicode,None): absolute path (starting from root "/") to page handling the URI |
352 None is returned if no page has been registered for this URI | 394 None is returned if no page has been registered for this URI |
353 """ | 395 """ |
354 uri_data = common_uri.parseXMPPUri(uri) | 396 uri_data = common_uri.parseXMPPUri(uri) |
355 try: | 397 try: |
356 page, cb = cls.uri_callbacks[uri_data['type'], uri_data['sub_type']] | 398 page, cb = cls.uri_callbacks[uri_data["type"], uri_data["sub_type"]] |
357 except KeyError: | 399 except KeyError: |
358 url = None | 400 url = None |
359 else: | 401 else: |
360 url = cb(page, uri_data) | 402 url = cb(page, uri_data) |
361 if url is None: | 403 if url is None: |
362 # no handler found | 404 # no handler found |
363 # we try to find a more generic one | 405 # we try to find a more generic one |
364 try: | 406 try: |
365 page, cb = cls.uri_callbacks[uri_data['type'], None] | 407 page, cb = cls.uri_callbacks[uri_data["type"], None] |
366 except KeyError: | 408 except KeyError: |
367 pass | 409 pass |
368 else: | 410 else: |
369 url = cb(page, uri_data) | 411 url = cb(page, uri_data) |
370 return url | 412 return url |
377 @return (LiberviaPage): page instance | 419 @return (LiberviaPage): page instance |
378 @raise KeyError: the page doesn't exist | 420 @raise KeyError: the page doesn't exist |
379 """ | 421 """ |
380 return cls.named_pages[name] | 422 return cls.named_pages[name] |
381 | 423 |
382 def getPageRedirectURL(self, request, page_name=u'login', url=None): | 424 def getPageRedirectURL(self, request, page_name=u"login", url=None): |
383 """generate URL for a page with redirect_url parameter set | 425 """generate URL for a page with redirect_url parameter set |
384 | 426 |
385 mainly used for login page with redirection to current page | 427 mainly used for login page with redirection to current page |
386 @param request(server.Request): current HTTP request | 428 @param request(server.Request): current HTTP request |
387 @param page_name(unicode): name of the page to go | 429 @param page_name(unicode): name of the page to go |
388 @param url(None, unicode): url to redirect to | 430 @param url(None, unicode): url to redirect to |
389 None to use request path (i.e. current page) | 431 None to use request path (i.e. current page) |
390 @return (unicode): URL to use | 432 @return (unicode): URL to use |
391 """ | 433 """ |
392 return u'{root_url}?redirect_url={redirect_url}'.format( | 434 return u"{root_url}?redirect_url={redirect_url}".format( |
393 root_url = self.getPageByName(page_name).url, | 435 root_url=self.getPageByName(page_name).url, |
394 redirect_url=urllib.quote_plus(request.uri) if url is None else url.encode('utf-8')) | 436 redirect_url=urllib.quote_plus(request.uri) |
437 if url is None | |
438 else url.encode("utf-8"), | |
439 ) | |
395 | 440 |
396 def getURL(self, *args): | 441 def getURL(self, *args): |
397 """retrieve URL of the page set arguments | 442 """retrieve URL of the page set arguments |
398 | 443 |
399 *args(list[unicode]): argument to add to the URL as path elements | 444 *args(list[unicode]): argument to add to the URL as path elements |
400 empty or None arguments will be ignored | 445 empty or None arguments will be ignored |
401 """ | 446 """ |
402 url_args = [quote(a) for a in args if a] | 447 url_args = [quote(a) for a in args if a] |
403 | 448 |
404 if self.name is not None and self.name in self.pages_redirects: | 449 if self.name is not None and self.name in self.pages_redirects: |
405 # we check for redirection | 450 # we check for redirection |
406 redirect_data = self.pages_redirects[self.name] | 451 redirect_data = self.pages_redirects[self.name] |
407 args_hash = tuple(args) | 452 args_hash = tuple(args) |
408 for limit in xrange(len(args)+1): | 453 for limit in xrange(len(args) + 1): |
409 current_hash = args_hash[:limit] | 454 current_hash = args_hash[:limit] |
410 if current_hash in redirect_data: | 455 if current_hash in redirect_data: |
411 url_base = redirect_data[current_hash] | 456 url_base = redirect_data[current_hash] |
412 remaining = args[limit:] | 457 remaining = args[limit:] |
413 remaining_url = '/'.join(remaining) | 458 remaining_url = "/".join(remaining) |
414 return os.path.join('/', url_base, remaining_url) | 459 return os.path.join("/", url_base, remaining_url) |
415 | 460 |
416 return os.path.join(self.url, *url_args) | 461 return os.path.join(self.url, *url_args) |
417 | 462 |
418 def getCurrentURL(self, request): | 463 def getCurrentURL(self, request): |
419 """retrieve URL used to access this page | 464 """retrieve URL used to access this page |
424 # request.prepath) because request.prepath may have been modified by | 469 # request.prepath) because request.prepath may have been modified by |
425 # redirection (if redirection args have been specified), while path reflect | 470 # redirection (if redirection args have been specified), while path reflect |
426 # the real request | 471 # the real request |
427 | 472 |
428 # we ignore empty path elements (i.e. double '/' or '/' at the end) | 473 # we ignore empty path elements (i.e. double '/' or '/' at the end) |
429 path_elts = [p for p in request.path.split('/') if p] | 474 path_elts = [p for p in request.path.split("/") if p] |
430 | 475 |
431 if request.postpath: | 476 if request.postpath: |
432 if not request.postpath[-1]: | 477 if not request.postpath[-1]: |
433 # we remove trailing slash | 478 # we remove trailing slash |
434 request.postpath = request.postpath[:-1] | 479 request.postpath = request.postpath[:-1] |
435 if request.postpath: | 480 if request.postpath: |
436 # getSubPageURL must return subpage from the point where | 481 # getSubPageURL must return subpage from the point where |
437 # the it is called, so we have to remove remanining | 482 # the it is called, so we have to remove remanining |
438 # path elements | 483 # path elements |
439 path_elts = path_elts[:-len(request.postpath)] | 484 path_elts = path_elts[: -len(request.postpath)] |
440 | 485 |
441 return u'/' + '/'.join(path_elts).decode('utf-8') | 486 return u"/" + "/".join(path_elts).decode("utf-8") |
442 | 487 |
443 def getParamURL(self, request, **kwargs): | 488 def getParamURL(self, request, **kwargs): |
444 """use URL of current request but modify the parameters in query part | 489 """use URL of current request but modify the parameters in query part |
445 | 490 |
446 **kwargs(dict[str, unicode]): argument to use as query parameters | 491 **kwargs(dict[str, unicode]): argument to use as query parameters |
447 @return (unicode): constructed URL | 492 @return (unicode): constructed URL |
448 """ | 493 """ |
449 current_url = self.getCurrentURL(request) | 494 current_url = self.getCurrentURL(request) |
450 if kwargs: | 495 if kwargs: |
451 encoded = urllib.urlencode({k:v.encode('utf-8') for k,v in kwargs.iteritems()}).decode('utf-8') | 496 encoded = urllib.urlencode( |
452 current_url = current_url + u'?' + encoded | 497 {k: v.encode("utf-8") for k, v in kwargs.iteritems()} |
498 ).decode("utf-8") | |
499 current_url = current_url + u"?" + encoded | |
453 return current_url | 500 return current_url |
454 | 501 |
455 def getSubPageByName(self, subpage_name, parent=None): | 502 def getSubPageByName(self, subpage_name, parent=None): |
456 """retrieve a subpage and its path using its name | 503 """retrieve a subpage and its path using its name |
457 | 504 |
466 parent = self | 513 parent = self |
467 for path, child in parent.children.iteritems(): | 514 for path, child in parent.children.iteritems(): |
468 try: | 515 try: |
469 child_name = child.name | 516 child_name = child.name |
470 except AttributeError: | 517 except AttributeError: |
471 # LiberviaPages have a name, but maybe this is an other Resource | 518 # LiberviaPages have a name, but maybe this is an other Resource |
472 continue | 519 continue |
473 if child_name == subpage_name: | 520 if child_name == subpage_name: |
474 return path, child | 521 return path, child |
475 raise exceptions.NotFound(_(u'requested sub page has not been found')) | 522 raise exceptions.NotFound(_(u"requested sub page has not been found")) |
476 | 523 |
477 def getSubPageURL(self, request, page_name, *args): | 524 def getSubPageURL(self, request, page_name, *args): |
478 """retrieve a page in direct children and build its URL according to request | 525 """retrieve a page in direct children and build its URL according to request |
479 | 526 |
480 request's current path is used as base (at current parsing point, | 527 request's current path is used as base (at current parsing point, |
494 if an arg is None, it will be ignored | 541 if an arg is None, it will be ignored |
495 @return (unicode): absolute URL to the sub page | 542 @return (unicode): absolute URL to the sub page |
496 """ | 543 """ |
497 current_url = self.getCurrentURL(request) | 544 current_url = self.getCurrentURL(request) |
498 path, child = self.getSubPageByName(page_name) | 545 path, child = self.getSubPageByName(page_name) |
499 return os.path.join(u'/', current_url, path, *[quote(a) for a in args if a is not None]) | 546 return os.path.join( |
547 u"/", current_url, path, *[quote(a) for a in args if a is not None] | |
548 ) | |
500 | 549 |
501 def getURLByNames(self, named_path): | 550 def getURLByNames(self, named_path): |
502 """retrieve URL from pages names and arguments | 551 """retrieve URL from pages names and arguments |
503 | 552 |
504 @param named_path(list[tuple[unicode, list[unicode]]]): path to the page as a list | 553 @param named_path(list[tuple[unicode, list[unicode]]]): path to the page as a list |
513 for page_name, page_args in named_path: | 562 for page_name, page_args in named_path: |
514 if current_page is None: | 563 if current_page is None: |
515 current_page = self.getPageByName(page_name) | 564 current_page = self.getPageByName(page_name) |
516 path.append(current_page.getURL(*page_args)) | 565 path.append(current_page.getURL(*page_args)) |
517 else: | 566 else: |
518 sub_path, current_page = self.getSubPageByName(page_name, parent=current_page) | 567 sub_path, current_page = self.getSubPageByName( |
568 page_name, parent=current_page | |
569 ) | |
519 path.append(sub_path) | 570 path.append(sub_path) |
520 if page_args: | 571 if page_args: |
521 path.extend([quote(a) for a in page_args]) | 572 path.extend([quote(a) for a in page_args]) |
522 return self.host.checkRedirection(u'/'.join(path)) | 573 return self.host.checkRedirection(u"/".join(path)) |
523 | 574 |
524 def getURLByPath(self, *args): | 575 def getURLByPath(self, *args): |
525 """generate URL by path | 576 """generate URL by path |
526 | 577 |
527 this method as a similar effect as getURLByNames, but it is more readable | 578 this method as a similar effect as getURLByNames, but it is more readable |
531 - if util.SubPage instance, must be the name of a subpage | 582 - if util.SubPage instance, must be the name of a subpage |
532 @return (unicode): generated path | 583 @return (unicode): generated path |
533 """ | 584 """ |
534 args = list(args) | 585 args = list(args) |
535 if not args: | 586 if not args: |
536 raise ValueError('You must specify path elements') | 587 raise ValueError("You must specify path elements") |
537 # root page is the one needed to construct the base of the URL | 588 # root page is the one needed to construct the base of the URL |
538 # if first arg is not a SubPage instance, we use current page | 589 # if first arg is not a SubPage instance, we use current page |
539 if not isinstance(args[0], SubPage): | 590 if not isinstance(args[0], SubPage): |
540 root = self | 591 root = self |
541 else: | 592 else: |
554 if not args: | 605 if not args: |
555 break | 606 break |
556 else: | 607 else: |
557 path, current_page = current_page.getSubPageByName(args.pop(0)) | 608 path, current_page = current_page.getSubPageByName(args.pop(0)) |
558 arguments = [path] | 609 arguments = [path] |
559 return self.host.checkRedirection(u'/'.join(url_elts)) | 610 return self.host.checkRedirection(u"/".join(url_elts)) |
560 | 611 |
561 def getChildWithDefault(self, path, request): | 612 def getChildWithDefault(self, path, request): |
562 # we handle children ourselves | 613 # we handle children ourselves |
563 raise exceptions.InternalError(u"this method should not be used with LiberviaPage") | 614 raise exceptions.InternalError( |
615 u"this method should not be used with LiberviaPage" | |
616 ) | |
564 | 617 |
565 def nextPath(self, request): | 618 def nextPath(self, request): |
566 """get next URL path segment, and update request accordingly | 619 """get next URL path segment, and update request accordingly |
567 | 620 |
568 will move first segment of postpath in prepath | 621 will move first segment of postpath in prepath |
570 @return (unicode): unquoted segment | 623 @return (unicode): unquoted segment |
571 @raise IndexError: there is no segment left | 624 @raise IndexError: there is no segment left |
572 """ | 625 """ |
573 pathElement = request.postpath.pop(0) | 626 pathElement = request.postpath.pop(0) |
574 request.prepath.append(pathElement) | 627 request.prepath.append(pathElement) |
575 return urllib.unquote(pathElement).decode('utf-8') | 628 return urllib.unquote(pathElement).decode("utf-8") |
576 | 629 |
577 def _filterPathValue(self, value, handler, name, request): | 630 def _filterPathValue(self, value, handler, name, request): |
578 """Modify a path value according to handler (see [getPathArgs])""" | 631 """Modify a path value according to handler (see [getPathArgs])""" |
579 if handler in (u'@', u'@jid') and value == u'@': | 632 if handler in (u"@", u"@jid") and value == u"@": |
580 value = None | 633 value = None |
581 | 634 |
582 if handler in (u'', u'@'): | 635 if handler in (u"", u"@"): |
583 if value is None: | 636 if value is None: |
584 return u'' | 637 return u"" |
585 elif handler in (u'jid', u'@jid'): | 638 elif handler in (u"jid", u"@jid"): |
586 if value: | 639 if value: |
587 try: | 640 try: |
588 return jid.JID(value) | 641 return jid.JID(value) |
589 except RuntimeError: | 642 except RuntimeError: |
590 log.warning(_(u'invalid jid argument: {value}').format(value=value)) | 643 log.warning(_(u"invalid jid argument: {value}").format(value=value)) |
591 self.pageError(request, C.HTTP_BAD_REQUEST) | 644 self.pageError(request, C.HTTP_BAD_REQUEST) |
592 else: | 645 else: |
593 return u'' | 646 return u"" |
594 else: | 647 else: |
595 return handler(self, value, name, request) | 648 return handler(self, value, name, request) |
596 | 649 |
597 return value | 650 return value |
598 | 651 |
613 - '@jid': if value of arguments is empty or '@', empty string will be used, else it will be converted to jid | 666 - '@jid': if value of arguments is empty or '@', empty string will be used, else it will be converted to jid |
614 """ | 667 """ |
615 data = self.getRData(request) | 668 data = self.getRData(request) |
616 | 669 |
617 for idx, name in enumerate(names): | 670 for idx, name in enumerate(names): |
618 if name[0] == u'*': | 671 if name[0] == u"*": |
619 value = data[name[1:]] = [] | 672 value = data[name[1:]] = [] |
620 while True: | 673 while True: |
621 try: | 674 try: |
622 value.append(self.nextPath(request)) | 675 value.append(self.nextPath(request)) |
623 except IndexError: | 676 except IndexError: |
624 idx-=1 | 677 idx -= 1 |
625 break | 678 break |
626 else: | 679 else: |
627 idx+=1 | 680 idx += 1 |
628 else: | 681 else: |
629 try: | 682 try: |
630 value = data[name] = self.nextPath(request) | 683 value = data[name] = self.nextPath(request) |
631 except IndexError: | 684 except IndexError: |
632 data[name] = None | 685 data[name] = None |
633 idx-=1 | 686 idx -= 1 |
634 break | 687 break |
635 | 688 |
636 values_count = idx+1 | 689 values_count = idx + 1 |
637 if values_count < min_args: | 690 if values_count < min_args: |
638 log.warning(_(u"Missing arguments in URL (got {count}, expected at least {min_args})").format( | 691 log.warning( |
639 count = values_count, min_args = min_args)) | 692 _( |
693 u"Missing arguments in URL (got {count}, expected at least {min_args})" | |
694 ).format(count=values_count, min_args=min_args) | |
695 ) | |
640 self.pageError(request, C.HTTP_BAD_REQUEST) | 696 self.pageError(request, C.HTTP_BAD_REQUEST) |
641 | 697 |
642 for name in names[values_count:]: | 698 for name in names[values_count:]: |
643 data[name] = None | 699 data[name] = None |
644 | 700 |
645 for name, handler in kwargs.iteritems(): | 701 for name, handler in kwargs.iteritems(): |
646 if name[0] == '*': | 702 if name[0] == "*": |
647 data[name] = [self._filterPathValue(v, handler, name, request) for v in data[name]] | 703 data[name] = [ |
704 self._filterPathValue(v, handler, name, request) for v in data[name] | |
705 ] | |
648 else: | 706 else: |
649 data[name] = self._filterPathValue(data[name], handler, name, request) | 707 data[name] = self._filterPathValue(data[name], handler, name, request) |
650 | |
651 | 708 |
652 ## Cache handling ## | 709 ## Cache handling ## |
653 | 710 |
654 def _setCacheHeaders(self, request, cache): | 711 def _setCacheHeaders(self, request, cache): |
655 """Set ETag and Last-Modified HTTP headers, used for caching""" | 712 """Set ETag and Last-Modified HTTP headers, used for caching""" |
656 request.setHeader('ETag', cache.hash) | 713 request.setHeader("ETag", cache.hash) |
657 last_modified = self.host.getHTTPDate(cache.created) | 714 last_modified = self.host.getHTTPDate(cache.created) |
658 request.setHeader('Last-Modified', last_modified) | 715 request.setHeader("Last-Modified", last_modified) |
659 | 716 |
660 def _checkCacheHeaders(self, request, cache): | 717 def _checkCacheHeaders(self, request, cache): |
661 """Check if a cache condition is set on the request | 718 """Check if a cache condition is set on the request |
662 | 719 |
663 if condition is valid, C.HTTP_NOT_MODIFIED is returned | 720 if condition is valid, C.HTTP_NOT_MODIFIED is returned |
664 """ | 721 """ |
665 etag_match = request.getHeader('If-None-Match') | 722 etag_match = request.getHeader("If-None-Match") |
666 if etag_match is not None: | 723 if etag_match is not None: |
667 if cache.hash == etag_match: | 724 if cache.hash == etag_match: |
668 self.pageError(request, C.HTTP_NOT_MODIFIED, no_body=True) | 725 self.pageError(request, C.HTTP_NOT_MODIFIED, no_body=True) |
669 else: | 726 else: |
670 modified_match = request.getHeader('If-Modified-Since') | 727 modified_match = request.getHeader("If-Modified-Since") |
671 if modified_match is not None: | 728 if modified_match is not None: |
672 modified = date_utils.date_parse(modified_match) | 729 modified = date_utils.date_parse(modified_match) |
673 if modified >= int(cache.created): | 730 if modified >= int(cache.created): |
674 self.pageError(request, C.HTTP_NOT_MODIFIED, no_body=True) | 731 self.pageError(request, C.HTTP_NOT_MODIFIED, no_body=True) |
675 | 732 |
696 node: pubsub node | 753 node: pubsub node |
697 short: short name of feature (needed if node is empty to find namespace) | 754 short: short name of feature (needed if node is empty to find namespace) |
698 | 755 |
699 """ | 756 """ |
700 if request.postpath: | 757 if request.postpath: |
701 # we are not on the final page, no need to go further | 758 # we are not on the final page, no need to go further |
702 return | 759 return |
703 | 760 |
704 profile = self.getProfile(request) or C.SERVICE_PROFILE | 761 profile = self.getProfile(request) or C.SERVICE_PROFILE |
705 | 762 |
706 if cache_type == C.CACHE_PUBSUB: | 763 if cache_type == C.CACHE_PUBSUB: |
707 service, node = kwargs['service'], kwargs['node'] | 764 service, node = kwargs["service"], kwargs["node"] |
708 if not node: | 765 if not node: |
709 try: | 766 try: |
710 short = kwargs['short'] | 767 short = kwargs["short"] |
711 node = self.host.ns_map[short] | 768 node = self.host.ns_map[short] |
712 except KeyError: | 769 except KeyError: |
713 log.warning(_(u"Can't use cache for empty node without namespace set, please ensure to set \"short\" and that it is registered")) | 770 log.warning( |
771 _( | |
772 u'Can\'t use cache for empty node without namespace set, please ensure to set "short" and that it is registered' | |
773 ) | |
774 ) | |
714 return | 775 return |
715 if profile != C.SERVICE_PROFILE: | 776 if profile != C.SERVICE_PROFILE: |
716 # only service profile is cache for now | 777 # only service profile is cache for now |
717 return | 778 return |
718 try: | 779 try: |
719 cache = self.cache[profile][cache_type][service][node][request.uri][self] | 780 cache = self.cache[profile][cache_type][service][node][request.uri][self] |
720 except KeyError: | 781 except KeyError: |
721 # no cache yet, let's subscribe to the pubsub node | 782 # no cache yet, let's subscribe to the pubsub node |
722 d1 = self.host.bridgeCall('psSubscribe', service.full(), node, {}, profile) | 783 d1 = self.host.bridgeCall( |
784 "psSubscribe", service.full(), node, {}, profile | |
785 ) | |
723 d1.addCallback(self.checkCacheSubscribeCb, service, node) | 786 d1.addCallback(self.checkCacheSubscribeCb, service, node) |
724 d1.addErrback(self.checkCacheSubscribeEb, service, node) | 787 d1.addErrback(self.checkCacheSubscribeEb, service, node) |
725 d2 = self.host.bridgeCall('psNodeWatchAdd', service.full(), node, profile) | 788 d2 = self.host.bridgeCall("psNodeWatchAdd", service.full(), node, profile) |
726 d2.addErrback(self.psNodeWatchAddEb, service, node) | 789 d2.addErrback(self.psNodeWatchAddEb, service, node) |
727 self._do_cache = [self, profile, cache_type, service, node, request.uri] | 790 self._do_cache = [self, profile, cache_type, service, node, request.uri] |
728 # we don't return the Deferreds as it is not needed to wait for | 791 # we don't return the Deferreds as it is not needed to wait for |
729 # the subscription to continue with page rendering | 792 # the subscription to continue with page rendering |
730 return | 793 return |
731 | 794 |
732 else: | 795 else: |
733 raise exceptions.InternalError(u'Unknown cache_type') | 796 raise exceptions.InternalError(u"Unknown cache_type") |
734 log.debug(u'using cache for {page}'.format(page=self)) | 797 log.debug(u"using cache for {page}".format(page=self)) |
735 cache.last_access = time.time() | 798 cache.last_access = time.time() |
736 self._setCacheHeaders(request, cache) | 799 self._setCacheHeaders(request, cache) |
737 self._checkCacheHeaders(request, cache) | 800 self._checkCacheHeaders(request, cache) |
738 request.write(cache.rendered) | 801 request.write(cache.rendered) |
739 request.finish() | 802 request.finish() |
740 raise failure.Failure(exceptions.CancelError(u'cache is used')) | 803 raise failure.Failure(exceptions.CancelError(u"cache is used")) |
741 | 804 |
742 def _cacheURL(self, dummy, request, profile): | 805 def _cacheURL(self, dummy, request, profile): |
743 self.cached_urls.setdefault(profile, {})[request.uri] = CacheURL(request) | 806 self.cached_urls.setdefault(profile, {})[request.uri] = CacheURL(request) |
744 | 807 |
745 @classmethod | 808 @classmethod |
746 def onNodeEvent(cls, host, service, node, event_type, items, profile): | 809 def onNodeEvent(cls, host, service, node, event_type, items, profile): |
747 """Invalidate cache for all pages linked to this node""" | 810 """Invalidate cache for all pages linked to this node""" |
748 try: | 811 try: |
749 cache = cls.cache[profile][C.CACHE_PUBSUB][jid.JID(service)][node] | 812 cache = cls.cache[profile][C.CACHE_PUBSUB][jid.JID(service)][node] |
750 except KeyError: | 813 except KeyError: |
751 log.info(_(u'Removing subscription for {service}/{node}: ' | 814 log.info( |
752 u'the page is not cached').format(service=service, node=node)) | 815 _( |
753 d1 = host.bridgeCall('psUnsubscribe', service, node, profile) | 816 u"Removing subscription for {service}/{node}: " |
754 d1.addErrback(lambda failure_: | 817 u"the page is not cached" |
755 log.warning(_(u"Can't unsubscribe from {service}/{node}: {msg}").format( | 818 ).format(service=service, node=node) |
756 service=service, node=node, msg=failure_))) | 819 ) |
757 d2 = host.bridgeCall('psNodeWatchAdd', service, node, profile) | 820 d1 = host.bridgeCall("psUnsubscribe", service, node, profile) |
821 d1.addErrback( | |
822 lambda failure_: log.warning( | |
823 _(u"Can't unsubscribe from {service}/{node}: {msg}").format( | |
824 service=service, node=node, msg=failure_ | |
825 ) | |
826 ) | |
827 ) | |
828 d2 = host.bridgeCall("psNodeWatchAdd", service, node, profile) | |
758 # TODO: check why the page is not in cache, remove subscription? | 829 # TODO: check why the page is not in cache, remove subscription? |
759 d2.addErrback(lambda failure_: | 830 d2.addErrback( |
760 log.warning(_(u"Can't remove watch for {service}/{node}: {msg}").format( | 831 lambda failure_: log.warning( |
761 service=service, node=node, msg=failure_))) | 832 _(u"Can't remove watch for {service}/{node}: {msg}").format( |
833 service=service, node=node, msg=failure_ | |
834 ) | |
835 ) | |
836 ) | |
762 else: | 837 else: |
763 cache.clear() | 838 cache.clear() |
764 | 839 |
765 @classmethod | 840 @classmethod |
766 def onSignal(cls, host, signal, *args): | 841 def onSignal(cls, host, signal, *args): |
769 if a callback is registered for this signal, call it | 844 if a callback is registered for this signal, call it |
770 @param host: Libervia instance | 845 @param host: Libervia instance |
771 @param signal(unicode): name of the signal | 846 @param signal(unicode): name of the signal |
772 @param *args: args of the signals | 847 @param *args: args of the signals |
773 """ | 848 """ |
774 for page, request, check_profile in cls.signals_handlers.get(signal, {}).itervalues(): | 849 for page, request, check_profile in cls.signals_handlers.get( |
850 signal, {} | |
851 ).itervalues(): | |
775 if check_profile: | 852 if check_profile: |
776 signal_profile = args[-1] | 853 signal_profile = args[-1] |
777 request_profile = page.getProfile(request) | 854 request_profile = page.getProfile(request) |
778 if not request_profile: | 855 if not request_profile: |
779 # if you want to use signal without session, unset check_profile | 856 # if you want to use signal without session, unset check_profile |
780 # (be sure to know what you are doing) | 857 # (be sure to know what you are doing) |
781 log.error(_(u"no session started, signal can't be checked")) | 858 log.error(_(u"no session started, signal can't be checked")) |
782 continue | 859 continue |
783 if signal_profile != request_profile: | 860 if signal_profile != request_profile: |
784 # we ignore the signal, it's not for our profile | 861 # we ignore the signal, it's not for our profile |
785 continue | 862 continue |
786 if request._signals_cache is not None: | 863 if request._signals_cache is not None: |
787 # socket is not yet opened, we cache the signal | 864 # socket is not yet opened, we cache the signal |
788 request._signals_cache.append((request, signal, args)) | 865 request._signals_cache.append((request, signal, args)) |
789 log.debug(u"signal [{signal}] cached: {args}".format( | 866 log.debug( |
790 signal = signal, | 867 u"signal [{signal}] cached: {args}".format(signal=signal, args=args) |
791 args = args)) | 868 ) |
792 else: | 869 else: |
793 page.on_signal(page, request, signal, *args) | 870 page.on_signal(page, request, signal, *args) |
794 | 871 |
795 def onSocketOpen(self, request): | 872 def onSocketOpen(self, request): |
796 """Called for dynamic pages when socket has just been opened | 873 """Called for dynamic pages when socket has just been opened |
810 """ | 887 """ |
811 for signal in request._signals_registered: | 888 for signal in request._signals_registered: |
812 try: | 889 try: |
813 del LiberviaPage.signals_handlers[signal][id(request)] | 890 del LiberviaPage.signals_handlers[signal][id(request)] |
814 except KeyError: | 891 except KeyError: |
815 log.error(_(u"Can't find signal handler for [{signal}], this should not happen").format( | 892 log.error( |
816 signal = signal)) | 893 _( |
894 u"Can't find signal handler for [{signal}], this should not happen" | |
895 ).format(signal=signal) | |
896 ) | |
817 else: | 897 else: |
818 log.debug(_(u"Removed signal handler")) | 898 log.debug(_(u"Removed signal handler")) |
819 | 899 |
820 def delegateToResource(self, request, resource): | 900 def delegateToResource(self, request, resource): |
821 """continue workflow with Twisted Resource""" | 901 """continue workflow with Twisted Resource""" |
823 if buf == server.NOT_DONE_YET: | 903 if buf == server.NOT_DONE_YET: |
824 pass | 904 pass |
825 else: | 905 else: |
826 request.write(buf) | 906 request.write(buf) |
827 request.finish() | 907 request.finish() |
828 raise failure.Failure(exceptions.CancelError(u'resource delegation')) | 908 raise failure.Failure(exceptions.CancelError(u"resource delegation")) |
829 | 909 |
830 def HTTPRedirect(self, request, url): | 910 def HTTPRedirect(self, request, url): |
831 """redirect to an URL using HTTP redirection | 911 """redirect to an URL using HTTP redirection |
832 | 912 |
833 @param request(server.Request): current HTTP request | 913 @param request(server.Request): current HTTP request |
834 @param url(unicode): url to redirect to | 914 @param url(unicode): url to redirect to |
835 """ | 915 """ |
836 web_util.redirectTo(url.encode('utf-8'), request) | 916 web_util.redirectTo(url.encode("utf-8"), request) |
837 request.finish() | 917 request.finish() |
838 raise failure.Failure(exceptions.CancelError(u'HTTP redirection is used')) | 918 raise failure.Failure(exceptions.CancelError(u"HTTP redirection is used")) |
839 | 919 |
840 def redirectOrContinue(self, request, redirect_arg=u'redirect_url'): | 920 def redirectOrContinue(self, request, redirect_arg=u"redirect_url"): |
841 """helper method to redirect a page to an url given as arg | 921 """helper method to redirect a page to an url given as arg |
842 | 922 |
843 if the arg is not present, the page will continue normal workflow | 923 if the arg is not present, the page will continue normal workflow |
844 @param request(server.Request): current HTTP request | 924 @param request(server.Request): current HTTP request |
845 @param redirect_arg(unicode): argument to use to get redirection URL | 925 @param redirect_arg(unicode): argument to use to get redirection URL |
846 @interrupt: redirect the page to requested URL | 926 @interrupt: redirect the page to requested URL |
847 @interrupt pageError(C.HTTP_BAD_REQUEST): empty or non local URL is used | 927 @interrupt pageError(C.HTTP_BAD_REQUEST): empty or non local URL is used |
848 """ | 928 """ |
849 try: | 929 try: |
850 url = request.args['redirect_url'][0] | 930 url = request.args["redirect_url"][0] |
851 except (KeyError, IndexError): | 931 except (KeyError, IndexError): |
852 pass | 932 pass |
853 else: | 933 else: |
854 # a redirection is requested | 934 # a redirection is requested |
855 if not url or url[0] != u'/': | 935 if not url or url[0] != u"/": |
856 # we only want local urls | 936 # we only want local urls |
857 self.pageError(request, C.HTTP_BAD_REQUEST) | 937 self.pageError(request, C.HTTP_BAD_REQUEST) |
858 else: | 938 else: |
859 self.HTTPRedirect(request, url) | 939 self.HTTPRedirect(request, url) |
860 | 940 |
877 @param skip_parse_url(bool): if True, parse_url method on redirect page will be skipped | 957 @param skip_parse_url(bool): if True, parse_url method on redirect page will be skipped |
878 @param path_args(list[unicode], None): path arguments to use in redirected page | 958 @param path_args(list[unicode], None): path arguments to use in redirected page |
879 @raise KeyError: there is no known page with this name | 959 @raise KeyError: there is no known page with this name |
880 """ | 960 """ |
881 # FIXME: render non LiberviaPage resources | 961 # FIXME: render non LiberviaPage resources |
882 path = page_path.rstrip(u'/').split(u'/') | 962 path = page_path.rstrip(u"/").split(u"/") |
883 if not path[0]: | 963 if not path[0]: |
884 redirect_page = self.host.root | 964 redirect_page = self.host.root |
885 else: | 965 else: |
886 redirect_page = self.named_pages[path[0]] | 966 redirect_page = self.named_pages[path[0]] |
887 | 967 |
899 # if cache is needed, it will be handled by final page | 979 # if cache is needed, it will be handled by final page |
900 redirect_page._do_cache = self._do_cache | 980 redirect_page._do_cache = self._do_cache |
901 self._do_cache = None | 981 self._do_cache = None |
902 | 982 |
903 redirect_page.renderPage(request, skip_parse_url=skip_parse_url) | 983 redirect_page.renderPage(request, skip_parse_url=skip_parse_url) |
904 raise failure.Failure(exceptions.CancelError(u'page redirection is used')) | 984 raise failure.Failure(exceptions.CancelError(u"page redirection is used")) |
905 | 985 |
906 def pageError(self, request, code=C.HTTP_NOT_FOUND, no_body=False): | 986 def pageError(self, request, code=C.HTTP_NOT_FOUND, no_body=False): |
907 """generate an error page and terminate the request | 987 """generate an error page and terminate the request |
908 | 988 |
909 @param request(server.Request): HTTP request | 989 @param request(server.Request): HTTP request |
912 """ | 992 """ |
913 request.setResponseCode(code) | 993 request.setResponseCode(code) |
914 if no_body: | 994 if no_body: |
915 request.finish() | 995 request.finish() |
916 else: | 996 else: |
917 template = u'error/' + unicode(code) + '.html' | 997 template = u"error/" + unicode(code) + ".html" |
918 | 998 |
919 rendered = self.host.renderer.render( | 999 rendered = self.host.renderer.render( |
920 template, | 1000 template, |
921 root_path = '/templates/', | 1001 root_path="/templates/", |
922 error_code = code, | 1002 error_code=code, |
923 **request.template_data) | 1003 **request.template_data |
1004 ) | |
924 | 1005 |
925 self.writeData(rendered, request) | 1006 self.writeData(rendered, request) |
926 raise failure.Failure(exceptions.CancelError(u'error page is used')) | 1007 raise failure.Failure(exceptions.CancelError(u"error page is used")) |
927 | 1008 |
928 def writeData(self, data, request): | 1009 def writeData(self, data, request): |
929 """write data to transport and finish the request""" | 1010 """write data to transport and finish the request""" |
930 if data is None: | 1011 if data is None: |
931 self.pageError(request) | 1012 self.pageError(request) |
932 data_encoded = data.encode('utf-8') | 1013 data_encoded = data.encode("utf-8") |
933 | 1014 |
934 if self._do_cache is not None: | 1015 if self._do_cache is not None: |
935 redirected_page = self._do_cache.pop(0) | 1016 redirected_page = self._do_cache.pop(0) |
936 cache = reduce(lambda d, k: d.setdefault(k, {}), self._do_cache, self.cache) | 1017 cache = reduce(lambda d, k: d.setdefault(k, {}), self._do_cache, self.cache) |
937 page_cache = cache[redirected_page] = CachePage(data_encoded) | 1018 page_cache = cache[redirected_page] = CachePage(data_encoded) |
938 self._setCacheHeaders(request, page_cache) | 1019 self._setCacheHeaders(request, page_cache) |
939 log.debug(_(u'{page} put in cache for [{profile}]').format( | 1020 log.debug( |
940 page=self, | 1021 _(u"{page} put in cache for [{profile}]").format( |
941 profile=self._do_cache[0])) | 1022 page=self, profile=self._do_cache[0] |
1023 ) | |
1024 ) | |
942 self._do_cache = None | 1025 self._do_cache = None |
943 self._checkCacheHeaders(request, page_cache) | 1026 self._checkCacheHeaders(request, page_cache) |
944 | 1027 |
945 request.write(data_encoded) | 1028 request.write(data_encoded) |
946 request.finish() | 1029 request.finish() |
959 child = self.children[subpage] | 1042 child = self.children[subpage] |
960 except KeyError: | 1043 except KeyError: |
961 self.pageError(request) | 1044 self.pageError(request) |
962 else: | 1045 else: |
963 child.render(request) | 1046 child.render(request) |
964 raise failure.Failure(exceptions.CancelError(u'subpage page is used')) | 1047 raise failure.Failure(exceptions.CancelError(u"subpage page is used")) |
965 | 1048 |
966 def _prepare_dynamic(self, dummy, request): | 1049 def _prepare_dynamic(self, dummy, request): |
967 # we need to activate dynamic page | 1050 # we need to activate dynamic page |
968 # we set data for template, and create/register token | 1051 # we set data for template, and create/register token |
969 socket_token = unicode(uuid.uuid4()) | 1052 socket_token = unicode(uuid.uuid4()) |
970 socket_url = self.host.getWebsocketURL(request) | 1053 socket_url = self.host.getWebsocketURL(request) |
971 socket_debug = C.boolConst(self.host.debug) | 1054 socket_debug = C.boolConst(self.host.debug) |
972 request.template_data['websocket'] = WebsocketMeta(socket_url, socket_token, socket_debug) | 1055 request.template_data["websocket"] = WebsocketMeta( |
1056 socket_url, socket_token, socket_debug | |
1057 ) | |
973 self.host.registerWSToken(socket_token, self, request) | 1058 self.host.registerWSToken(socket_token, self, request) |
974 # we will keep track of handlers to remove | 1059 # we will keep track of handlers to remove |
975 request._signals_registered = [] | 1060 request._signals_registered = [] |
976 # we will cache registered signals until socket is opened | 1061 # we will cache registered signals until socket is opened |
977 request._signals_cache = [] | 1062 request._signals_cache = [] |
986 template_data = request.template_data | 1071 template_data = request.template_data |
987 | 1072 |
988 # if confirm variable is set in case of successfuly data post | 1073 # if confirm variable is set in case of successfuly data post |
989 session_data = self.host.getSessionData(request, session_iface.ISATSession) | 1074 session_data = self.host.getSessionData(request, session_iface.ISATSession) |
990 if session_data.popPageFlag(self, C.FLAG_CONFIRM): | 1075 if session_data.popPageFlag(self, C.FLAG_CONFIRM): |
991 template_data[u'confirm'] = True | 1076 template_data[u"confirm"] = True |
992 | 1077 |
993 return self.host.renderer.render( | 1078 return self.host.renderer.render( |
994 self.template, | 1079 self.template, |
995 root_path = '/templates/', | 1080 root_path="/templates/", |
996 media_path = '/' + C.MEDIA_DIR, | 1081 media_path="/" + C.MEDIA_DIR, |
997 cache_path = session_data.cache_dir, | 1082 cache_path=session_data.cache_dir, |
998 main_menu = LiberviaPage.main_menu, | 1083 main_menu=LiberviaPage.main_menu, |
999 **template_data) | 1084 **template_data |
1085 ) | |
1000 | 1086 |
1001 def _renderEb(self, failure_, request): | 1087 def _renderEb(self, failure_, request): |
1002 """don't raise error on CancelError""" | 1088 """don't raise error on CancelError""" |
1003 failure_.trap(exceptions.CancelError) | 1089 failure_.trap(exceptions.CancelError) |
1004 | 1090 |
1005 def _internalError(self, failure_, request): | 1091 def _internalError(self, failure_, request): |
1006 """called if an error is not catched""" | 1092 """called if an error is not catched""" |
1007 log.error(_(u"Uncatched error for HTTP request on {url}: {msg}").format( | 1093 log.error( |
1008 url = request.URLPath(), | 1094 _(u"Uncatched error for HTTP request on {url}: {msg}").format( |
1009 msg = failure_)) | 1095 url=request.URLPath(), msg=failure_ |
1096 ) | |
1097 ) | |
1010 self.pageError(request, C.HTTP_INTERNAL_ERROR) | 1098 self.pageError(request, C.HTTP_INTERNAL_ERROR) |
1011 | 1099 |
1012 def _on_data_post_redirect(self, ret, request): | 1100 def _on_data_post_redirect(self, ret, request): |
1013 """called when page's on_data_post has been done successfuly | 1101 """called when page's on_data_post has been done successfuly |
1014 | 1102 |
1024 ret = () | 1112 ret = () |
1025 elif isinstance(ret, basestring): | 1113 elif isinstance(ret, basestring): |
1026 ret = (ret,) | 1114 ret = (ret,) |
1027 else: | 1115 else: |
1028 ret = tuple(ret) | 1116 ret = tuple(ret) |
1029 raise NotImplementedError(_(u'iterable in on_data_post return value is not used yet')) | 1117 raise NotImplementedError( |
1118 _(u"iterable in on_data_post return value is not used yet") | |
1119 ) | |
1030 session_data = self.host.getSessionData(request, session_iface.ISATSession) | 1120 session_data = self.host.getSessionData(request, session_iface.ISATSession) |
1031 request_data = self.getRData(request) | 1121 request_data = self.getRData(request) |
1032 if 'post_redirect_page' in request_data: | 1122 if "post_redirect_page" in request_data: |
1033 redirect_page_data = request_data['post_redirect_page'] | 1123 redirect_page_data = request_data["post_redirect_page"] |
1034 if isinstance(redirect_page_data, tuple): | 1124 if isinstance(redirect_page_data, tuple): |
1035 redirect_page = redirect_page_data[0] | 1125 redirect_page = redirect_page_data[0] |
1036 redirect_page_args = redirect_page_data[1:] | 1126 redirect_page_args = redirect_page_data[1:] |
1037 redirect_uri = redirect_page.getURL(*redirect_page_args) | 1127 redirect_uri = redirect_page.getURL(*redirect_page_args) |
1038 else: | 1128 else: |
1045 if not C.POST_NO_CONFIRM in ret: | 1135 if not C.POST_NO_CONFIRM in ret: |
1046 session_data.setPageFlag(redirect_page, C.FLAG_CONFIRM) | 1136 session_data.setPageFlag(redirect_page, C.FLAG_CONFIRM) |
1047 request.setResponseCode(C.HTTP_SEE_OTHER) | 1137 request.setResponseCode(C.HTTP_SEE_OTHER) |
1048 request.setHeader("location", redirect_uri) | 1138 request.setHeader("location", redirect_uri) |
1049 request.finish() | 1139 request.finish() |
1050 raise failure.Failure(exceptions.CancelError(u'Post/Redirect/Get is used')) | 1140 raise failure.Failure(exceptions.CancelError(u"Post/Redirect/Get is used")) |
1051 | 1141 |
1052 def _on_data_post(self, dummy, request): | 1142 def _on_data_post(self, dummy, request): |
1053 csrf_token = self.host.getSessionData(request, session_iface.ISATSession).csrf_token | 1143 csrf_token = self.host.getSessionData( |
1144 request, session_iface.ISATSession | |
1145 ).csrf_token | |
1054 try: | 1146 try: |
1055 given_csrf = self.getPostedData(request, u'csrf_token') | 1147 given_csrf = self.getPostedData(request, u"csrf_token") |
1056 except KeyError: | 1148 except KeyError: |
1057 given_csrf = None | 1149 given_csrf = None |
1058 if given_csrf is None or given_csrf != csrf_token: | 1150 if given_csrf is None or given_csrf != csrf_token: |
1059 log.warning(_(u"invalid CSRF token, hack attempt? URL: {url}, IP: {ip}").format( | 1151 log.warning( |
1060 url=request.uri, | 1152 _(u"invalid CSRF token, hack attempt? URL: {url}, IP: {ip}").format( |
1061 ip=request.getClientIP())) | 1153 url=request.uri, ip=request.getClientIP() |
1154 ) | |
1155 ) | |
1062 self.pageError(request, C.HTTP_UNAUTHORIZED) | 1156 self.pageError(request, C.HTTP_UNAUTHORIZED) |
1063 d = defer.maybeDeferred(self.on_data_post, self, request) | 1157 d = defer.maybeDeferred(self.on_data_post, self, request) |
1064 d.addCallback(self._on_data_post_redirect, request) | 1158 d.addCallback(self._on_data_post_redirect, request) |
1065 return d | 1159 return d |
1066 | 1160 |
1074 @param multiple(bool): True if multiple values are possible/expected | 1168 @param multiple(bool): True if multiple values are possible/expected |
1075 if False, the first value is returned | 1169 if False, the first value is returned |
1076 @return (iterator[unicode], list[iterator[unicode], unicode, list[unicode]): values received for this(these) key(s) | 1170 @return (iterator[unicode], list[iterator[unicode], unicode, list[unicode]): values received for this(these) key(s) |
1077 @raise KeyError: one specific key has been requested, and it is missing | 1171 @raise KeyError: one specific key has been requested, and it is missing |
1078 """ | 1172 """ |
1079 # FIXME: request.args is already unquoting the value, it seems we are doing double unquote | 1173 # FIXME: request.args is already unquoting the value, it seems we are doing double unquote |
1080 if isinstance(keys, basestring): | 1174 if isinstance(keys, basestring): |
1081 keys = [keys] | 1175 keys = [keys] |
1082 get_first = True | 1176 get_first = True |
1083 else: | 1177 else: |
1084 get_first = False | 1178 get_first = False |
1085 | 1179 |
1086 ret = [] | 1180 ret = [] |
1087 for key in keys: | 1181 for key in keys: |
1088 gen = (urllib.unquote(v).decode('utf-8') for v in request.args.get(key,[])) | 1182 gen = (urllib.unquote(v).decode("utf-8") for v in request.args.get(key, [])) |
1089 if multiple: | 1183 if multiple: |
1090 ret.append(gen) | 1184 ret.append(gen) |
1091 else: | 1185 else: |
1092 try: | 1186 try: |
1093 ret.append(next(gen)) | 1187 ret.append(next(gen)) |
1103 @param except_(iterable[unicode]): key of values to ignore | 1197 @param except_(iterable[unicode]): key of values to ignore |
1104 csrf_token will always be ignored | 1198 csrf_token will always be ignored |
1105 @param multiple(bool): if False, only the first values are returned | 1199 @param multiple(bool): if False, only the first values are returned |
1106 @return (dict[unicode, list[unicode]]): post values | 1200 @return (dict[unicode, list[unicode]]): post values |
1107 """ | 1201 """ |
1108 except_ = tuple(except_) + (u'csrf_token',) | 1202 except_ = tuple(except_) + (u"csrf_token",) |
1109 ret = {} | 1203 ret = {} |
1110 for key, values in request.args.iteritems(): | 1204 for key, values in request.args.iteritems(): |
1111 key = urllib.unquote(key).decode('utf-8') | 1205 key = urllib.unquote(key).decode("utf-8") |
1112 if key in except_: | 1206 if key in except_: |
1113 continue | 1207 continue |
1114 if not multiple: | 1208 if not multiple: |
1115 ret[key] = urllib.unquote(values[0]).decode('utf-8') | 1209 ret[key] = urllib.unquote(values[0]).decode("utf-8") |
1116 else: | 1210 else: |
1117 ret[key] = [urllib.unquote(v).decode('utf-8') for v in values] | 1211 ret[key] = [urllib.unquote(v).decode("utf-8") for v in values] |
1118 return ret | 1212 return ret |
1119 | 1213 |
1120 def getProfile(self, request): | 1214 def getProfile(self, request): |
1121 """helper method to easily get current profile | 1215 """helper method to easily get current profile |
1122 | 1216 |
1168 dynamic pages | 1262 dynamic pages |
1169 @param template(unicode): path of the template to render | 1263 @param template(unicode): path of the template to render |
1170 @param template_data(dict): template_data to use | 1264 @param template_data(dict): template_data to use |
1171 """ | 1265 """ |
1172 if not self.dynamic: | 1266 if not self.dynamic: |
1173 raise exceptions.InternalError(_(u"renderPartial must only be used with dynamic pages")) | 1267 raise exceptions.InternalError( |
1268 _(u"renderPartial must only be used with dynamic pages") | |
1269 ) | |
1174 session_data = self.host.getSessionData(request, session_iface.ISATSession) | 1270 session_data = self.host.getSessionData(request, session_iface.ISATSession) |
1175 | 1271 |
1176 return self.host.renderer.render( | 1272 return self.host.renderer.render( |
1177 template, | 1273 template, |
1178 root_path = '/templates/', | 1274 root_path="/templates/", |
1179 media_path = '/' + C.MEDIA_DIR, | 1275 media_path="/" + C.MEDIA_DIR, |
1180 cache_path = session_data.cache_dir, | 1276 cache_path=session_data.cache_dir, |
1181 main_menu = LiberviaPage.main_menu, | 1277 main_menu=LiberviaPage.main_menu, |
1182 **template_data) | 1278 **template_data |
1183 | 1279 ) |
1184 def renderAndUpdate(self, request, template, selectors, template_data_update, update_type="append"): | 1280 |
1281 def renderAndUpdate( | |
1282 self, request, template, selectors, template_data_update, update_type="append" | |
1283 ): | |
1185 """Helper method to render a partial page element and update the page | 1284 """Helper method to render a partial page element and update the page |
1186 | 1285 |
1187 this is NOT the normal page rendering method, it is used only to update | 1286 this is NOT the normal page rendering method, it is used only to update |
1188 dynamic pages | 1287 dynamic pages |
1189 @param request(server.Request): current HTTP request | 1288 @param request(server.Request): current HTTP request |
1196 append: append rendered element to selected element | 1295 append: append rendered element to selected element |
1197 """ | 1296 """ |
1198 template_data = request.template_data.copy() | 1297 template_data = request.template_data.copy() |
1199 template_data.update(template_data_update) | 1298 template_data.update(template_data_update) |
1200 html = self.renderPartial(request, template, template_data) | 1299 html = self.renderPartial(request, template, template_data) |
1201 request.sendData(u'dom', | 1300 request.sendData(u"dom", selectors=selectors, update_type=update_type, html=html) |
1202 selectors=selectors, | |
1203 update_type=update_type, | |
1204 html=html) | |
1205 | 1301 |
1206 def renderPage(self, request, skip_parse_url=False): | 1302 def renderPage(self, request, skip_parse_url=False): |
1207 """Main method to handle the workflow of a LiberviaPage""" | 1303 """Main method to handle the workflow of a LiberviaPage""" |
1208 | 1304 |
1209 # template_data are the variables passed to template | 1305 # template_data are the variables passed to template |
1210 if not hasattr(request, 'template_data'): | 1306 if not hasattr(request, "template_data"): |
1211 session_data = self.host.getSessionData(request, session_iface.ISATSession) | 1307 session_data = self.host.getSessionData(request, session_iface.ISATSession) |
1212 csrf_token = session_data.csrf_token | 1308 csrf_token = session_data.csrf_token |
1213 request.template_data = {u'profile': session_data.profile, | 1309 request.template_data = { |
1214 u'csrf_token': csrf_token} | 1310 u"profile": session_data.profile, |
1311 u"csrf_token": csrf_token, | |
1312 } | |
1215 | 1313 |
1216 # XXX: here is the code which need to be executed once | 1314 # XXX: here is the code which need to be executed once |
1217 # at the beginning of the request hanling | 1315 # at the beginning of the request hanling |
1218 if request.postpath and not request.postpath[-1]: | 1316 if request.postpath and not request.postpath[-1]: |
1219 # we don't differenciate URLs finishing with '/' or not | 1317 # we don't differenciate URLs finishing with '/' or not |
1221 | 1319 |
1222 d = defer.Deferred() | 1320 d = defer.Deferred() |
1223 d.addCallback(self._checkAccess, request) | 1321 d.addCallback(self._checkAccess, request) |
1224 | 1322 |
1225 if self.redirect is not None: | 1323 if self.redirect is not None: |
1226 d.addCallback(lambda dummy: self.pageRedirect(self.redirect, request, skip_parse_url=False)) | 1324 d.addCallback( |
1325 lambda dummy: self.pageRedirect( | |
1326 self.redirect, request, skip_parse_url=False | |
1327 ) | |
1328 ) | |
1227 | 1329 |
1228 if self.parse_url is not None and not skip_parse_url: | 1330 if self.parse_url is not None and not skip_parse_url: |
1229 if self.url_cache: | 1331 if self.url_cache: |
1230 profile = self.getProfile(request) | 1332 profile = self.getProfile(request) |
1231 try: | 1333 try: |
1232 cache_url = self.cached_urls[profile][request.uri] | 1334 cache_url = self.cached_urls[profile][request.uri] |
1233 except KeyError: | 1335 except KeyError: |
1234 # no cache for this URI yet | 1336 # no cache for this URI yet |
1235 # we do normal URL parsing, and then the cache | 1337 # we do normal URL parsing, and then the cache |
1236 d.addCallback(self.parse_url, request) | 1338 d.addCallback(self.parse_url, request) |
1237 d.addCallback(self._cacheURL, request, profile) | 1339 d.addCallback(self._cacheURL, request, profile) |
1238 else: | 1340 else: |
1239 log.debug(_(u"using URI cache for {page}").format(page=self)) | 1341 log.debug(_(u"using URI cache for {page}").format(page=self)) |
1240 cache_url.use(request) | 1342 cache_url.use(request) |