changeset 923:edb322c87ea4

server (pages): pages now handle redirection, check self.pageRedirect docstring for details
author Goffi <goffi@goffi.org>
date Mon, 03 Apr 2017 01:00:29 +0200
parents 16d1084d1371
children 94f88277c2e7
files src/server/server.py
diffstat 1 files changed, 61 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- a/src/server/server.py	Mon Apr 03 01:00:26 2017 +0200
+++ b/src/server/server.py	Mon Apr 03 01:00:29 2017 +0200
@@ -1423,7 +1423,7 @@
     isLeaf = True  # we handle subpages ourself
     named_pages = {}
 
-    def __init__(self, host, root_dir, name=None, access=None, parse_url=None,
+    def __init__(self, host, root_dir, name=None, redirect=None, access=None, parse_url=None,
                  prepare_render=None, render=None, template=None):
         """initiate LiberviaPages
 
@@ -1433,6 +1433,12 @@
         @param root_dir(unicode): aboslute path of the page
         @param name(unicode, None): if not None, a unique name to identify the page
             can then be used for e.g. redirection
+            "/" is not allowed in names (as it can be used to construct URL paths)
+        @param redirect(unicode, None): if not None, this page will be a redirected
+            parameter is used as in self.pageRedirect. parse_url will not be skipped
+            using this redirect parameter is called "full redirection"
+            using self.pageRedirect is called "partial redirection" (because some rendering method
+            can still be used, e.g. parse_url)
         @param access(unicode, None): permission needed to access the page
             None means public access.
             Pages inherit from parent pages: e.g. if a "settings" page is restricted to admins,
@@ -1456,12 +1462,25 @@
         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 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))
         self.access = access
+        if redirect is not None:
+            # only page access and name make sense in case of full redirection
+            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
         self.parse_url = parse_url
         self.prepare_render = prepare_render
         self.template = template
@@ -1503,6 +1522,7 @@
                     host,
                     dir_path,
                     name=page_data.get('name'),
+                    redirect=page_data.get('redirect'),
                     access=page_data.get('access'),
                     parse_url=page_data.get('parse_url'),
                     prepare_render=page_data.get('prepare_render'),
@@ -1529,6 +1549,37 @@
         request.prepath.append(pathElement)
         return urllib.unquote(pathElement).decode('utf-8')
 
+    def pageRedirect(self, page_path, request, skip_parse_url=True):
+        """redirect a page to a named page
+
+        the workflow will continue with the workflow of the named page,
+        skipping named page's parse_url method if it exist.
+        @param page_path(unicode): path to page (elements are separated by "/"):
+            if path starts with a "/":
+                path is a full path starting from root
+            else:
+                - first element is name as registered in name variable
+                - following element are subpages path
+            e.g.: "blog" redirect to page named "blog"
+                  "blog/atom.xml" redirect to atom.xml subpage of "blog"
+                  "/common/blog/atom.xml" redirect to the page at the fiven full path
+        @param request(server.Request): current HTTP request
+        @param skip_parse_url(bool): if True, parse_url method on redirect page will be skipped
+        @raise KeyError: there is no known page with this name
+        """
+        # FIXME: render non LiberviaPage resources
+        path = page_path.rstrip(u'/').split(u'/')
+        if not path[0]:
+            redirect_page = self.host.root
+        else:
+            redirect_page = self.named_pages[path[0]]
+
+        for subpage in path[1:]:
+            redirect_page = redirect_page.childen[subpage]
+
+        redirect_page.renderPage(request, skip_parse_url=True)
+        raise failure.Failure(exceptions.CancelError(u'page redirection is used'))
+
     def pageError(self, request, code=C.HTTP_NOT_FOUND):
         """generate an error page and terminate the request
 
@@ -1626,7 +1677,8 @@
 
         return data
 
-    def render_GET(self, request):
+    def renderPage(self, request, skip_parse_url=False):
+        # template_data are the variables passed to template
         if not hasattr(request, 'template_data'):
             request.template_data = {}
 
@@ -1639,7 +1691,10 @@
         d = defer.Deferred()
         d.addCallback(self._checkAccess, request)
 
-        if self.parse_url is not None:
+        if self.redirect is not None:
+            self.pageRedirect(self.redirect, request, skip_parse_url=False)
+
+        if self.parse_url is not None and not skip_parse_url:
             d.addCallback(self.parse_url, request)
 
         d.addCallback(self._subpagesHandler, request)
@@ -1657,6 +1712,9 @@
         d.callback(self)
         return server.NOT_DONE_YET
 
+    def render_GET(self, request):
+        return self.renderPage(request)
+
 
 class Libervia(service.Service):