diff libervia/server/pages.py @ 1128:6414fd795df4

server, pages: multi-sites refactoring: Libervia is now handling external sites (i.e. other sites than Libervia official site). The external site are declared in sites_path_public_dict (in [DEFAULT] section) which is read by template engine, then they are linked to virtual host with vhosts_dict (linking host name to site name) in [libervia] section. Sites are only instanced once, so adding an alias is just a matter of mapping the alias host name in vhosts_dict with the same site name. menu_json and url_redirections_dict can now accept keys named after site name, which will be linked to the data for the site. Data for default site can still be keyed at first level. Libervia official pages are added to external site (if pages are not overriden), allowing to call pages of the framework and to have facilities like login handling. Deprecated url_redirections_profile option has been removed.
author Goffi <goffi@goffi.org>
date Fri, 14 Sep 2018 21:41:28 +0200
parents 9234f29053b0
children 02fc28aac2b6
line wrap: on
line diff
--- a/libervia/server/pages.py	Sun Sep 09 21:12:22 2018 +0200
+++ b/libervia/server/pages.py	Fri Sep 14 21:41:28 2018 +0200
@@ -25,7 +25,6 @@
 
 from sat.core.i18n import _
 from sat.core import exceptions
-from sat.tools.common import uri as common_uri
 from sat.tools.common import date_utils
 from sat.core.log import getLogger
 
@@ -33,7 +32,6 @@
 from libervia.server.constants import Const as C
 from libervia.server import session_iface
 from libervia.server.utils import quote, SubPage
-import libervia
 
 from collections import namedtuple
 import uuid
@@ -102,65 +100,51 @@
 
 class LiberviaPage(web_resource.Resource):
     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
     # 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,
-    ):
-        """initiate LiberviaPages
+        self, host, vhost_root, 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 LiberviaPage instance
 
         LiberviaPages are the main resources of Libervia, using easy to set python files
-        The arguments are the variables found in page_meta.py
+        The non mandatory arguments are the variables found in page_meta.py
         @param host(Libervia): the running instance of Libervia
+        @param vhost_root(web_resource.Resource): root resource of the virtual host which
+            handle this page.
         @param root_dir(unicode): aboslute file path of the page
         @param url(unicode): relative URL to the page
             this URL may not be valid, as pages may require path arguments
         @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 redirected. A redirected
-            parameter is used as in self.pageRedirect. parse_url will not be skipped
+        @param redirect(unicode, None): if not None, this page will be redirected.
+            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)
+            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,
-            and if "settings/blog" is public, it still can only be accessed by admins.
-            see C.PAGES_ACCESS_* for details
+            Pages inherit from parent pages: e.g. if a "settings" page is restricted
+            to admins, and if "settings/blog" is public, it still can only be accessed by
+            admins. See C.PAGES_ACCESS_* for details
         @param dynamic(bool): if True, activate websocket for bidirectional communication
         @param parse_url(callable, None): if set it will be called to handle the URL path
-            after this method, the page will be rendered if noting is left in path (request.postpath)
-            else a the request will be transmitted to a subpage
-        @param prepare_render(callable, None): if set, will be used to prepare the rendering
-            that often means gathering data using the bridge
-        @param render(callable, None): if not template is set, this method will be called and
-            what it returns will be rendered.
-            This method is mutually exclusive with template and must return a unicode string.
+            after this method, the page will be rendered if noting is left in path
+            (request.postpath) else a the request will be transmitted to a subpage
+        @param prepare_render(callable, None): if set, will be used to prepare the
+            rendering. That often means gathering data using the bridge
+        @param render(callable, None): if not template is set, this method will be
+            called and what it returns will be rendered.
+            This method is mutually exclusive with template and must return a unicode
+            string.
         @param template(unicode, None): path to the template to render.
             This method is mutually exclusive with render
         @param on_data_post(callable, None): method to call when data is posted
@@ -169,20 +153,22 @@
                 - C.POST_NO_CONFIRM: confirm flag will not be set
         @param on_data(callable, None): method to call when dynamic data is sent
             this method is used with Libervia's websocket mechanism
-        @param on_signal(callable, None): method to call when a registered signal is received
-            this method is used with Libervia's websocket mechanism
+        @param on_signal(callable, None): method to call when a registered signal is
+            received. This method is used with Libervia's websocket mechanism
+        @param url_cache(boolean): if set, result of parse_url is cached (per profile).
+            Useful when costly calls (e.g. network) are done while parsing URL.
         """
 
         web_resource.Resource.__init__(self)
         self.host = host
+        self.vhost_root = vhost_root
         self.root_dir = root_dir
         self.url = url
         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))
-                )
+                    _(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:
@@ -208,11 +194,8 @@
                 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"
-                    )
-                )
+                    _(u"you can't use full page redirection with other rendering"
+                      u"method, check self.pageRedirect if you need to use them"))
             self.redirect = redirect
         else:
             self.redirect = None
@@ -238,55 +221,110 @@
         self._do_cache = None
 
     def __unicode__(self):
-        return u"LiberviaPage {name} at {url}".format(
-            name=self.name or u"<anonymous>", url=self.url
-        )
+        return u"LiberviaPage {name} at {url} (vhost: {vhost_root})".format(
+            name=self.name or u"<anonymous>", url=self.url, vhost_root=self.vhost_root)
 
     def __str__(self):
         return self.__unicode__().encode("utf-8")
 
+
+    @property
+    def named_pages(self):
+        return self.vhost_root.named_pages
+
+    @property
+    def uri_callbacks(self):
+        return self.vhost_root.uri_callbacks
+
+    @property
+    def pages_redirects(self):
+        return self.vhost_root.pages_redirects
+
+    @property
+    def cached_urls(self):
+        return self.vhost_root.cached_urls
+
+    @property
+    def main_menu(self):
+        return self.vhost_root.main_menu
+
     @classmethod
-    def importPages(cls, host, parent=None, path=None):
-        """Recursively import Libervia pages"""
-        if path is None:
-            path = []
-        if parent is None:
-            root_dir = os.path.join(os.path.dirname(libervia.__file__), C.PAGES_DIR)
-            parent = host
+    def importPages(cls, host, vhost_root, root_path=None, _parent=None, _path=None,
+        _extra_pages=False):
+        """Recursively import Libervia pages
+
+        @param host(Libervia): Libervia instance
+        @param vhost_root(LiberviaRootResource): root of this VirtualHost
+        @param root_path(unicode, None): use this root path instead of vhost_root's one
+            Used to add default site pages to external sites
+        @param _parent(Resource, None): _parent page. Do not set yourself, this is for
+            internal use only
+        @param _path(list(unicode), None): current path. Do not set yourself, this is for
+            internal use only
+        @param _extra_pages(boolean): set to True when extra pages are used (i.e.
+            root_path is set). Do not set yourself, this is for internal use only
+        """
+        if _path is None:
+            _path = []
+        if _parent is None:
+            if root_path is None:
+                root_dir = os.path.join(vhost_root.site_path, C.PAGES_DIR)
+            else:
+                root_dir = os.path.join(root_path, C.PAGES_DIR)
+                _extra_pages = True
+            _parent = vhost_root
         else:
-            root_dir = parent.root_dir
+            root_dir = _parent.root_dir
         for d in os.listdir(root_dir):
             dir_path = os.path.join(root_dir, d)
             if not os.path.isdir(dir_path):
                 continue
+            if _extra_pages and d in _parent.children:
+                log.debug(_(u"[{host_name}] {path} is already present, ignoring it").format(
+                    host_name=vhost_root.host_name, path=u'/'.join(_path+[d])))
+                continue
             meta_path = os.path.join(dir_path, C.PAGES_META_FILE)
             if os.path.isfile(meta_path):
                 page_data = {}
-                new_path = path + [d]
+                new_path = _path + [d]
                 # we don't want to force the presence of __init__.py
                 # so we use execfile instead of import.
                 # TODO: when moved to Python 3, __init__.py is not mandatory anymore
                 #       so we can switch to import
                 execfile(meta_path, page_data)
-                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),
-                )
-                parent.putChild(d, resource)
-                log.info(u"Added /{path} page".format(path=u"[...]/".join(new_path)))
+                try:
+                    resource = LiberviaPage(
+                        host=host,
+                        vhost_root=vhost_root,
+                        root_dir=dir_path,
+                        url=u"/" + u"/".join(new_path),
+                        name=page_data.get(u"name"),
+                        redirect=page_data.get(u"redirect"),
+                        access=page_data.get(u"access"),
+                        dynamic=page_data.get(u"dynamic", False),
+                        parse_url=page_data.get(u"parse_url"),
+                        prepare_render=page_data.get(u"prepare_render"),
+                        render=page_data.get(u"render"),
+                        template=page_data.get(u"template"),
+                        on_data_post=page_data.get(u"on_data_post"),
+                        on_data=page_data.get(u"on_data"),
+                        on_signal=page_data.get(u"on_signal"),
+                        url_cache=page_data.get(u"url_cache", False),
+                    )
+                except exceptions.ConflictError as e:
+                    if _extra_pages:
+                        # extra pages are discarded if there is already an existing page
+                        continue
+                    else:
+                        raise e
+                _parent.putChild(d, resource)
+                log_msg = (u"[{host_name}] Added /{path} page".format(
+                    host_name=vhost_root.host_name,
+                    path=u"[…]/".join(new_path)))
+                if _extra_pages:
+                    log.debug(log_msg)
+                else:
+                    log.info(log_msg)
                 if "uri_handlers" in page_data:
                     if not isinstance(page_data, dict):
                         log.error(_(u"uri_handlers must be a dict"))
@@ -295,50 +333,20 @@
                             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))
+                            if not _extra_pages:
+                                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)
 
-                LiberviaPage.importPages(host, resource, new_path)
-
-    @classmethod
-    def setMenu(cls, menus):
-        main_menu = []
-        for menu in menus:
-            if not menu:
-                msg = _(u"menu item can't be empty")
-                log.error(msg)
-                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]"
-                    )
-                    log.error(msg)
-                    raise ValueError(msg)
-                page_name, url = menu
-            else:
-                page_name = menu
-                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)
-                    )
-                    raise e
-            main_menu.append((page_name, url))
-        cls.main_menu = main_menu
+                LiberviaPage.importPages(
+                    host, vhost_root, _parent=resource, _path=new_path, _extra_pages=_extra_pages)
 
     def registerURI(self, uri_tuple, get_uri_cb):
         """register a URI handler
@@ -351,11 +359,8 @@
             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):
@@ -369,12 +374,13 @@
         signal handler will be removed when connection with dynamic page will be lost
         @param signal(unicode): name of the signal
             last arg of signal must be profile, as it will be checked to filter signals
-        @param check_profile(bool): if True, signal profile (which MUST be last arg) will be
-            checked against session profile.
-            /!\ if False, profile will not be checked/filtered, be sure to know what you are doing
-                if you unset this option /!\
+        @param check_profile(bool): if True, signal profile (which MUST be last arg)
+            will be checked against session profile.
+            /!\ if False, profile will not be checked/filtered, be sure to know what you
+                are doing if you unset this option /!\
         """
-        # FIXME: add a timeout, if socket is not opened before it, signal handler must be removed
+        # FIXME: add a timeout; if socket is not opened before it, signal handler
+        #        must be removed
         if not self.dynamic:
             log.error(_(u"You can't register signal if page is not dynamic"))
             return
@@ -385,41 +391,11 @@
         )
         request._signals_registered.append(signal)
 
-    @classmethod
-    def getPagePathFromURI(cls, uri):
-        """Retrieve page URL from xmpp: URI
+    def getPageByName(self, name):
+        return self.vhost_root.getPageByName(name)
 
-        @param uri(unicode): URI with a xmpp: scheme
-        @return (unicode,None): absolute path (starting from root "/") to page handling the URI
-            None is returned if no page has been registered for this URI
-        """
-        uri_data = common_uri.parseXMPPUri(uri)
-        try:
-            page, cb = cls.uri_callbacks[uri_data["type"], uri_data["sub_type"]]
-        except KeyError:
-            url = None
-        else:
-            url = cb(page, uri_data)
-        if url is None:
-            # no handler found
-            # we try to find a more generic one
-            try:
-                page, cb = cls.uri_callbacks[uri_data["type"], None]
-            except KeyError:
-                pass
-            else:
-                url = cb(page, uri_data)
-        return url
-
-    @classmethod
-    def getPageByName(cls, name):
-        """retrieve page instance from its name
-
-        @param name(unicode): name of the page
-        @return (LiberviaPage): page instance
-        @raise KeyError: the page doesn't exist
-        """
-        return cls.named_pages[name]
+    def getPagePathFromURI(self, uri):
+        return self.vhost.getPagePathFromURI(uri)
 
     def getPageRedirectURL(self, request, page_name=u"login", url=None):
         """generate URL for a page with redirect_url parameter set
@@ -550,6 +526,7 @@
     def getURLByNames(self, named_path):
         """retrieve URL from pages names and arguments
 
+        @param request(server.Request): request linked to the session
         @param named_path(list[tuple[unicode, list[unicode]]]): path to the page as a list
             of tuples of 2 items:
                 - first item is page name
@@ -570,13 +547,14 @@
                 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(self.vhost_root, u"/".join(path))
 
     def getURLByPath(self, *args):
         """generate URL by path
 
         this method as a similar effect as getURLByNames, but it is more readable
         by using SubPage to get pages instead of using tuples
+        @param request(server.Request): request linked to the session
         @param *args: path element:
             - if unicode, will be used as argument
             - if util.SubPage instance, must be the name of a subpage
@@ -607,7 +585,7 @@
             else:
                 path, current_page = current_page.getSubPageByName(args.pop(0))
                 arguments = [path]
-        return self.host.checkRedirection(u"/".join(url_elts))
+        return self.host.checkRedirection(self.vhost_root, u"/".join(url_elts))
 
     def getChildWithDefault(self, path, request):
         # we handle children ourselves
@@ -655,15 +633,18 @@
         Arguments will be put in request data.
         Missing arguments will have None value
         @param names(list[unicode]): list of arguments to get
-        @param min_args(int): if less than min_args are found, PageError is used with C.HTTP_BAD_REQUEST
-            use 0 to ignore
+        @param min_args(int): if less than min_args are found, PageError is used with
+            C.HTTP_BAD_REQUEST
+            Use 0 to ignore
         @param **kwargs: special value or optional callback to use for arguments
             names of the arguments must correspond to those in names
             special values may be:
                 - '': use empty string instead of None when no value is specified
                 - '@': if value of argument is empty or '@', empty string will be used
-                - 'jid': value must be converted to jid.JID if it exists, else empty string is used
-                - '@jid': if value of arguments is empty or '@', empty string will be used, else it will be converted to jid
+                - 'jid': value must be converted to jid.JID if it exists, else empty
+                    string is used
+                - '@jid': if value of arguments is empty or '@', empty string will be
+                    used, else it will be converted to jid
         """
         data = self.getRData(request)
 
@@ -688,11 +669,8 @@
 
         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 "
+                          u"{min_args})").format(count=values_count, min_args=min_args))
             self.pageError(request, C.HTTP_BAD_REQUEST)
 
         for name in names[values_count:]:
@@ -767,17 +745,16 @@
                     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 '
+                                  u'set, please ensure to set "short" and that it is '
+                                  u'registered'))
                     return
             if profile != C.SERVICE_PROFILE:
                 #  only service profile is cache for now
                 return
             try:
-                cache = self.cache[profile][cache_type][service][node][request.uri][self]
+                cache = (self.cache[profile][cache_type][service][node]
+                         [self.vhost_root][request.uri][self])
             except KeyError:
                 # no cache yet, let's subscribe to the pubsub node
                 d1 = self.host.bridgeCall(
@@ -787,7 +764,8 @@
                 d1.addErrback(self.checkCacheSubscribeEb, service, node)
                 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]
+                self._do_cache = [self, profile, cache_type, service, node,
+                                  self.vhost_root, request.uri]
                 #  we don't return the Deferreds as it is not needed to wait for
                 # the subscription to continue with page rendering
                 return
@@ -802,7 +780,7 @@
         request.finish()
         raise failure.Failure(exceptions.CancelError(u"cache is used"))
 
-    def _cacheURL(self, dummy, request, profile):
+    def _cacheURL(self, __, request, profile):
         self.cached_urls.setdefault(profile, {})[request.uri] = CacheURL(request)
 
     @classmethod
@@ -811,29 +789,20 @@
         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)
-            )
+            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_
-                    )
-                )
-            )
+                        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_
-                    )
-                )
-            )
+                        service=service, node=node, msg=failure_)))
         else:
             cache.clear()
 
@@ -889,11 +858,8 @@
             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 "
+                            u"happen").format(signal=signal))
             else:
                 log.debug(_(u"Removed signal handler"))
 
@@ -952,21 +918,22 @@
                 - 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
+                  "/common/blog/atom.xml" redirect to the page at the given 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
+        @param skip_parse_url(bool): if True, parse_url method on redirect page will be
+            skipped
         @param path_args(list[unicode], None): path arguments to use in redirected page
         @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
+            redirect_page = self.vhost_root
         else:
             redirect_page = self.named_pages[path[0]]
 
         for subpage in path[1:]:
-            if redirect_page is self.host.root:
+            if redirect_page is self.vhost_root:
                 redirect_page = redirect_page.children[subpage]
             else:
                 redirect_page = redirect_page.original.children[subpage]
@@ -995,6 +962,8 @@
             request.finish()
         else:
             template = u"error/" + unicode(code) + ".html"
+            if self.vhost_root.site_name:
+                request.template_data[u'site'] = self.vhost_root.site_name
 
             rendered = self.host.renderer.render(
                 template,
@@ -1016,18 +985,15 @@
             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)
 
         request.write(data_encoded)
         request.finish()
 
-    def _subpagesHandler(self, dummy, request):
+    def _subpagesHandler(self, __, request):
         """render subpage if suitable
 
         this method checks if there is still an unmanaged part of the path
@@ -1045,7 +1011,7 @@
                 child.render(request)
                 raise failure.Failure(exceptions.CancelError(u"subpage page is used"))
 
-    def _prepare_dynamic(self, dummy, request):
+    def _prepare_dynamic(self, __, request):
         # we need to activate dynamic page
         # we set data for template, and create/register token
         socket_token = unicode(uuid.uuid4())
@@ -1060,27 +1026,28 @@
         # we will cache registered signals until socket is opened
         request._signals_cache = []
 
-    def _prepare_render(self, dummy, request):
+    def _prepare_render(self, __, request):
         return defer.maybeDeferred(self.prepare_render, self, request)
 
-    def _render_method(self, dummy, request):
+    def _render_method(self, __, request):
         return defer.maybeDeferred(self.render_method, self, request)
 
-    def _render_template(self, dummy, request):
+    def _render_template(self, __, request):
         template_data = request.template_data
 
         # 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
+        if self.vhost_root.site_name:
+            template_data[u'site'] = self.vhost_root.site_name
 
         return self.host.renderer.render(
             self.template,
             media_path="/" + C.MEDIA_DIR,
             cache_path=session_data.cache_dir,
-            main_menu=LiberviaPage.main_menu,
-            **template_data
-        )
+            main_menu=self.main_menu,
+            **template_data)
 
     def _renderEb(self, failure_, request):
         """don't raise error on CancelError"""
@@ -1088,11 +1055,8 @@
 
     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):
@@ -1100,9 +1064,12 @@
 
         This will do a Post/Redirect/Get pattern.
         this method redirect to the same page or to request.data['post_redirect_page']
-        post_redirect_page can be either a page or a tuple with page as first item, then a list of unicode arguments to append to the url.
-        if post_redirect_page is not used, initial request.uri (i.e. the same page as where the data have been posted) will be used for redirection.
-        HTTP status code "See Other" (303) is used as it is the recommanded code in this case.
+        post_redirect_page can be either a page or a tuple with page as first item, then
+        a list of unicode arguments to append to the url.
+        if post_redirect_page is not used, initial request.uri (i.e. the same page as
+        where the data have been posted) will be used for redirection.
+        HTTP status code "See Other" (303) is used as it is the recommanded code in
+        this case.
         @param ret(None, unicode, iterable): on_data_post return value
             see LiberviaPage.__init__ on_data_post docstring
         """
@@ -1137,7 +1104,7 @@
         request.finish()
         raise failure.Failure(exceptions.CancelError(u"Post/Redirect/Get is used"))
 
-    def _on_data_post(self, dummy, request):
+    def _on_data_post(self, __, request):
         csrf_token = self.host.getSessionData(
             request, session_iface.ISATSession
         ).csrf_token
@@ -1165,10 +1132,12 @@
             iterable to get more than one
         @param multiple(bool): True if multiple values are possible/expected
             if False, the first value is returned
-        @return (iterator[unicode], list[iterator[unicode], unicode, list[unicode]): values received for this(these) key(s)
+        @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
@@ -1222,7 +1191,8 @@
         """helper method to get request data dict
 
         this dictionnary if for the request only, it is not saved in session
-        It is mainly used to pass data between pages/methods called during request workflow
+        It is mainly used to pass data between pages/methods called during request
+        workflow
         @return (dict): request data
         """
         try:
@@ -1266,12 +1236,14 @@
                 _(u"renderPartial must only be used with dynamic pages")
             )
         session_data = self.host.getSessionData(request, session_iface.ISATSession)
+        if self.vhost_root.site_name:
+            template_data[u'site'] = self.vhost_root.site_name
 
         return self.host.renderer.render(
             template,
             media_path="/" + C.MEDIA_DIR,
             cache_path=session_data.cache_dir,
-            main_menu=LiberviaPage.main_menu,
+            main_menu=self.main_menu,
             **template_data
         )
 
@@ -1319,7 +1291,7 @@
 
         if self.redirect is not None:
             d.addCallback(
-                lambda dummy: self.pageRedirect(
+                lambda __: self.pageRedirect(
                     self.redirect, request, skip_parse_url=False
                 )
             )
@@ -1344,13 +1316,13 @@
 
         if request.method not in (C.HTTP_METHOD_GET, C.HTTP_METHOD_POST):
             # only HTTP GET and POST are handled so far
-            d.addCallback(lambda dummy: self.pageError(request, C.HTTP_BAD_REQUEST))
+            d.addCallback(lambda __: self.pageError(request, C.HTTP_BAD_REQUEST))
 
         if request.method == C.HTTP_METHOD_POST:
             if self.on_data_post is None:
                 # if we don't have on_data_post, the page was not expecting POST
                 # so we return an error
-                d.addCallback(lambda dummy: self.pageError(request, C.HTTP_BAD_REQUEST))
+                d.addCallback(lambda __: self.pageError(request, C.HTTP_BAD_REQUEST))
             else:
                 d.addCallback(self._on_data_post, request)
             # by default, POST follow normal behaviour after on_data_post is called