comparison src/server/server.py @ 980:bcacf970f970

core (pages redirection): inverted redirection + getSubPageURL: - page redirection now also work in other redirection meaning that if "/redirecting_url" => "/page_name/arg_1/arg_2" is used, a getURL(page_name, arg_1, arg_2) will return the redirecting_url. query_args are not handled yet by getURL. - page redirection dict now use path_args and query_args insteand of args and kwargs, which show the relation with URL. - new getSubPageURL return absolute URL to a subpage, which is more solid than relative URL and allows to use it in sub-hierarchy.
author Goffi <goffi@goffi.org>
date Tue, 14 Nov 2017 08:35:17 +0100
parents 1d558dfb32ca
children f0fc28b3bd1e
comparison
equal deleted inserted replaced
979:1d558dfb32ca 980:bcacf970f970
132 if 'url' in new_data: 132 if 'url' in new_data:
133 new = new_data['url'] 133 new = new_data['url']
134 elif 'page' in new_data: 134 elif 'page' in new_data:
135 new = new_data 135 new = new_data
136 new['type'] = 'page' 136 new['type'] = 'page'
137 if 'args' in new: 137 new.setdefault('path_args', [])
138 # we need lists in args because it will be used 138 if not isinstance(new['path_args'], list):
139 # as it in request.args 139 log.error(_(u'"path_args" in redirection of {old} must be a list. Ignoring the redirection'.format(
140 for k,v in new['args'].iteritems(): 140 old = old)))
141 if isinstance(v, basestring): 141 continue
142 new['args'][k] = [v] 142 new.setdefault('query_args', {})
143 if not isinstance(new['query_args'], dict):
144 log.error(_(u'"query_args" in redirection of {old} must be a dictionary. Ignoring the redirection'.format(
145 old = old)))
146 continue
147 new['path_args'] = [quote(a) for a in new['path_args']]
148 # we keep an inversed dict of page redirection (page/path_args => redirecting URL)
149 # so getURL can return the redirecting URL if the same arguments are used
150 # making the URL consistent
151 args_hash = tuple(new['path_args'])
152 LiberviaPage.pages_redirects.setdefault(new_data['page'], {})[args_hash] = old
153
154 # we need lists in query_args because it will be used
155 # as it in request.path_args
156 for k,v in new['query_args'].iteritems():
157 if isinstance(v, basestring):
158 new['query_args'][k] = [v]
143 elif 'path' in new_data: 159 elif 'path' in new_data:
144 new = 'file:{}'.format(urllib.quote(new_data['path'])) 160 new = 'file:{}'.format(urllib.quote(new_data['path']))
145 else: 161 else:
146 new = new_data 162 new = new_data
147 new_data = {} 163 new_data = {}
296 page = LiberviaPage.getPageByName(request_data['page']) 312 page = LiberviaPage.getPageByName(request_data['page'])
297 except KeyError: 313 except KeyError:
298 log.error(_(u"Can't find page named \"{name}\" requested in redirection").format( 314 log.error(_(u"Can't find page named \"{name}\" requested in redirection").format(
299 name = request_data['page'])) 315 name = request_data['page']))
300 return web_resource.NoResource() 316 return web_resource.NoResource()
317 request.postpath = request_data['path_args'][:] + request.postpath
318
301 try: 319 try:
302 request.args.update(request_data['args']) 320 request.args.update(request_data['query_args'])
303 except KeyError: 321 except (TypeError, ValueError):
304 pass 322 log.error(_(u"Invalid args in redirection: {query_args}").format(
305 except Exception: 323 query_args=request_data['query_args']))
306 log.error(_(u"Invalid args in redirection: {args}").format(request_data['args']))
307 return web_resource.NoResource() 324 return web_resource.NoResource()
308 return page 325 return page
309 else: 326 else:
310 raise exceptions.InternalError(u'unknown request_data type') 327 raise exceptions.InternalError(u'unknown request_data type')
311 else: 328 else:
1351 1368
1352 class LiberviaPage(web_resource.Resource): 1369 class LiberviaPage(web_resource.Resource):
1353 isLeaf = True # we handle subpages ourself 1370 isLeaf = True # we handle subpages ourself
1354 named_pages = {} 1371 named_pages = {}
1355 uri_callbacks = {} 1372 uri_callbacks = {}
1373 pages_redirects = {}
1356 1374
1357 def __init__(self, host, root_dir, url, name=None, redirect=None, access=None, parse_url=None, 1375 def __init__(self, host, root_dir, url, name=None, redirect=None, access=None, parse_url=None,
1358 prepare_render=None, render=None, template=None, on_data_post=None): 1376 prepare_render=None, render=None, template=None, on_data_post=None):
1359 """initiate LiberviaPages 1377 """initiate LiberviaPages
1360 1378
1395 1413
1396 web_resource.Resource.__init__(self) 1414 web_resource.Resource.__init__(self)
1397 self.host = host 1415 self.host = host
1398 self.root_dir = root_dir 1416 self.root_dir = root_dir
1399 self.url = url 1417 self.url = url
1418 self.name = name
1400 if name is not None: 1419 if name is not None:
1401 if name in self.named_pages: 1420 if name in self.named_pages:
1402 raise exceptions.ConflictError(_(u'a Libervia page named "{}" already exists'.format(name))) 1421 raise exceptions.ConflictError(_(u'a Libervia page named "{}" already exists'.format(name)))
1403 if u'/' in name: 1422 if u'/' in name:
1404 raise ValueError(_(u'"/" is not allowed in page names')) 1423 raise ValueError(_(u'"/" is not allowed in page names'))
1410 if access not in (C.PAGES_ACCESS_PUBLIC, C.PAGES_ACCESS_PROFILE, C.PAGES_ACCESS_NONE): 1429 if access not in (C.PAGES_ACCESS_PUBLIC, C.PAGES_ACCESS_PROFILE, C.PAGES_ACCESS_NONE):
1411 raise NotImplementedError(_(u"{} access is not implemented yet").format(access)) 1430 raise NotImplementedError(_(u"{} access is not implemented yet").format(access))
1412 self.access = access 1431 self.access = access
1413 if redirect is not None: 1432 if redirect is not None:
1414 # only page access and name make sense in case of full redirection 1433 # only page access and name make sense in case of full redirection
1434 # so we check that rendering methods/values are not set
1415 if not all(lambda x: x is not None 1435 if not all(lambda x: x is not None
1416 for x in (parse_url, prepare_render, render, template)): 1436 for x in (parse_url, prepare_render, render, template)):
1417 raise ValueError(_(u"you can't use full page redirection with other rendering method," 1437 raise ValueError(_(u"you can't use full page redirection with other rendering method,"
1418 u"check self.pageRedirect if you need to use them")) 1438 u"check self.pageRedirect if you need to use them"))
1419 self.redirect = redirect 1439 self.redirect = redirect
1420 else: 1440 else:
1421 self.redirect = None 1441 self.redirect = None
1422 self.parse_url = parse_url 1442 self.parse_url = parse_url
1423 self.prepare_render = prepare_render 1443 self.prepare_render = prepare_render
1552 """retrieve URL of the page set arguments 1572 """retrieve URL of the page set arguments
1553 1573
1554 *args(list[unicode]): argument to add to the URL as path elements 1574 *args(list[unicode]): argument to add to the URL as path elements
1555 """ 1575 """
1556 url_args = [quote(a) for a in args] 1576 url_args = [quote(a) for a in args]
1577
1578 if self.name is not None and self.name in self.pages_redirects:
1579 # we check for redirection
1580 redirect_data = self.pages_redirects[self.name]
1581 args_hash = tuple(args)
1582 for limit in xrange(len(args)+1):
1583 current_hash = args_hash[:limit]
1584 if current_hash in redirect_data:
1585 url_base = redirect_data[current_hash]
1586 remaining = args[limit:]
1587 remaining_url = '/'.join(remaining)
1588 return os.path.join('/', url_base, remaining_url)
1589
1557 return os.path.join(self.url, *url_args) 1590 return os.path.join(self.url, *url_args)
1591
1592 def getSubPageURL(self, request, page_name, *args):
1593 """retrieve a page in direct children and build its URL according to request
1594
1595 request's current path is used as base (at current parsing point,
1596 i.e. it's more prepath than path).
1597 Requested page is checked in children and an absolute URL is then built
1598 by the resulting combination.
1599 This method is useful to construct absolute URLs for children instead of
1600 using relative path, which may not work in subpages, and are linked to the
1601 names of directories (i.e. relative URL will break if subdirectory is renamed
1602 while getSubPageURL won't as long as page_name is consistent).
1603 Also, request.path is used, keeping real path used by user,
1604 and potential redirections.
1605 @param request(server.Request): current HTTP request
1606 @param page_name(unicode): name of the page to retrieve
1607 it must be a direct children of current page
1608 @param *args(list[unicode]): arguments to add as path elements
1609 @return unicode: absolute URL to the sub page
1610 """
1611 # we get url in the following way (splitting request.path instead of using
1612 # request.prepath) because request.prepath may have been modified by
1613 # redirection (if redirection args have been specified), while path reflect
1614 # the real request
1615
1616 # we ignore empty path elements (i.e. double '/' or '/' at the end)
1617 path_elts = [p for p in request.path.split('/') if p]
1618
1619 if request.postpath:
1620 if not request.postpath[-1]:
1621 # we remove trailing slash
1622 request.postpath = request.postpath[:-1]
1623 if request.postpath:
1624 # getSubPageURL must return subpage from the point where
1625 # the it is called, so we have to remove remanining
1626 # path elements
1627 path_elts = path_elts[:-len(request.postpath)]
1628
1629 current_url = '/' + '/'.join(path_elts).decode('utf-8')
1630
1631 for path, child in self.children.iteritems():
1632 try:
1633 child_name = child.name
1634 except AttributeError:
1635 # LiberviaPage have a name, but maybe this is an other Resource
1636 continue
1637 if child_name == page_name:
1638 return os.path.join(u'/', current_url, path, *args)
1639 raise exceptions.NotFound(_(u'requested sub page has not been found'))
1558 1640
1559 def getChildWithDefault(self, path, request): 1641 def getChildWithDefault(self, path, request):
1560 # we handle children ourselves 1642 # we handle children ourselves
1561 raise exceptions.InternalError(u"this method should not be used with LiberviaPage") 1643 raise exceptions.InternalError(u"this method should not be used with LiberviaPage")
1562 1644