comparison libervia/server/pages.py @ 1417:314bba1ae433

pages: breadcrumbs handling: a new `breadcrumbs` list of dict is created in `template_data`. By default it is automatically filled by pages run to reach the requested URI, but a page can customize it. A breadcrumb data dict must have a `label`, should have an `url` and may have an `icon` (which is the name of a SàT Media well-known icon). Pages may now have a `label` attribute, which is used to automatically fill the crumb (otherwise page name then URI is used). A new `add_breadcrumb` method can be used to manually breadcrumb data, in which case auto-filling is disabled.
author Goffi <goffi@goffi.org>
date Thu, 29 Apr 2021 20:48:35 +0200
parents 2c3bdba880bb
children 870b198e98ea
comparison
equal deleted inserted replaced
1416:0554103ec700 1417:314bba1ae433
117 #  Set of tuples (service/node/sub_id) of nodes subscribed for caching 117 #  Set of tuples (service/node/sub_id) of nodes subscribed for caching
118 # sub_id can be empty string if not handled by service 118 # sub_id can be empty string if not handled by service
119 cache_pubsub_sub = set() 119 cache_pubsub_sub = set()
120 120
121 def __init__( 121 def __init__(
122 self, host, vhost_root, root_dir, url, name=None, redirect=None, access=None, 122 self, host, vhost_root, root_dir, url, name=None, label=None, redirect=None,
123 dynamic=False, parse_url=None, prepare_render=None, render=None, template=None, 123 access=None, dynamic=False, parse_url=None, add_breadcrumb=None,
124 on_data_post=None, on_data=None, on_signal=None, url_cache=False, 124 prepare_render=None, render=None, template=None, on_data_post=None, on_data=None,
125 replace_on_conflict=False 125 on_signal=None, url_cache=False, replace_on_conflict=False
126 ): 126 ):
127 """Initiate LiberviaPage instance 127 """Initiate LiberviaPage instance
128 128
129 LiberviaPages are the main resources of Libervia, using easy to set python files 129 LiberviaPages are the main resources of Libervia, using easy to set python files
130 The non mandatory arguments are the variables found in page_meta.py 130 The non mandatory arguments are the variables found in page_meta.py
150 admins. See C.PAGES_ACCESS_* for details 150 admins. See C.PAGES_ACCESS_* for details
151 @param dynamic(bool): if True, activate websocket for bidirectional communication 151 @param dynamic(bool): if True, activate websocket for bidirectional communication
152 @param parse_url(callable, None): if set it will be called to handle the URL path 152 @param parse_url(callable, None): if set it will be called to handle the URL path
153 after this method, the page will be rendered if noting is left in path 153 after this method, the page will be rendered if noting is left in path
154 (request.postpath) else a the request will be transmitted to a subpage 154 (request.postpath) else a the request will be transmitted to a subpage
155 @param add_breadcrumb(callable, None): if set, manage the breadcrumb data for this
156 page, otherwise it will be set automatically from page name or label.
155 @param prepare_render(callable, None): if set, will be used to prepare the 157 @param prepare_render(callable, None): if set, will be used to prepare the
156 rendering. That often means gathering data using the bridge 158 rendering. That often means gathering data using the bridge
157 @param render(callable, None): if template is not set, this method will be 159 @param render(callable, None): if template is not set, this method will be
158 called and what it returns will be rendered. 160 called and what it returns will be rendered.
159 This method is mutually exclusive with template and must return a unicode 161 This method is mutually exclusive with template and must return a unicode
183 self.host = host 185 self.host = host
184 self.vhost_root = vhost_root 186 self.vhost_root = vhost_root
185 self.root_dir = root_dir 187 self.root_dir = root_dir
186 self.url = url 188 self.url = url
187 self.name = name 189 self.name = name
190 self.label = label
188 self.dyn_data = {} 191 self.dyn_data = {}
189 if name is not None: 192 if name is not None:
190 if (name in self.named_pages 193 if (name in self.named_pages
191 and not (replace_on_conflict and self.named_pages[name].url == url)): 194 and not (replace_on_conflict and self.named_pages[name].url == url)):
192 raise exceptions.ConflictError( 195 raise exceptions.ConflictError(
220 "method, check self.pageRedirect if you need to use them")) 223 "method, check self.pageRedirect if you need to use them"))
221 self.redirect = redirect 224 self.redirect = redirect
222 else: 225 else:
223 self.redirect = None 226 self.redirect = None
224 self.parse_url = parse_url 227 self.parse_url = parse_url
228 self.add_breadcrumb = add_breadcrumb
225 self.prepare_render = prepare_render 229 self.prepare_render = prepare_render
226 self.template = template 230 self.template = template
227 self.render_method = render 231 self.render_method = render
228 self.on_data_post = on_data_post 232 self.on_data_post = on_data_post
229 self.on_data = on_data 233 self.on_data = on_data
296 host=host, 300 host=host,
297 vhost_root=vhost_root, 301 vhost_root=vhost_root,
298 root_dir=dir_path, 302 root_dir=dir_path,
299 url="/" + "/".join(url_elts), 303 url="/" + "/".join(url_elts),
300 name=page_data.get("name"), 304 name=page_data.get("name"),
305 label=page_data.get("label"),
301 redirect=page_data.get("redirect"), 306 redirect=page_data.get("redirect"),
302 access=page_data.get("access"), 307 access=page_data.get("access"),
303 dynamic=page_data.get("dynamic", False), 308 dynamic=page_data.get("dynamic", False),
304 parse_url=page_data.get("parse_url"), 309 parse_url=page_data.get("parse_url"),
310 add_breadcrumb=page_data.get("add_breadcrumb"),
305 prepare_render=page_data.get("prepare_render"), 311 prepare_render=page_data.get("prepare_render"),
306 render=page_data.get("render"), 312 render=page_data.get("render"),
307 template=page_data.get("template"), 313 template=page_data.get("template"),
308 on_data_post=page_data.get("on_data_post"), 314 on_data_post=page_data.get("on_data_post"),
309 on_data=page_data.get("on_data"), 315 on_data=page_data.get("on_data"),
1453 request, 1459 request,
1454 cache_path=session_data.cache_dir, 1460 cache_path=session_data.cache_dir,
1455 templates_root_url=str(self.vhost_root.getFrontURL(theme)), 1461 templates_root_url=str(self.vhost_root.getFrontURL(theme)),
1456 profile=session_data.profile) 1462 profile=session_data.profile)
1457 1463
1464 uri = request.uri.decode()
1465 try:
1466 template_data["current_page"] = next(
1467 m[0] for m in self.main_menu if uri.startswith(m[1])
1468 )
1469 except StopIteration:
1470 pass
1471
1458 return self.host.renderer.render( 1472 return self.host.renderer.render(
1459 self.template, 1473 self.template,
1460 theme=theme, 1474 theme=theme,
1461 site_themes=self.site_themes, 1475 site_themes=self.site_themes,
1462 page_url=self.getURL(), 1476 page_url=self.getURL(),
1766 session_data = self.host.getSessionData(request, session_iface.ISATSession) 1780 session_data = self.host.getSessionData(request, session_iface.ISATSession)
1767 request.template_data = { 1781 request.template_data = {
1768 "profile": session_data.profile, 1782 "profile": session_data.profile,
1769 "csrf_token": session_data.csrf_token, 1783 "csrf_token": session_data.csrf_token,
1770 "session_uuid": session_data.uuid, 1784 "session_uuid": session_data.uuid,
1785 "breadcrumbs": []
1771 } 1786 }
1772 1787
1773 # XXX: here is the code which need to be executed once 1788 # XXX: here is the code which need to be executed once
1774 # at the beginning of the request hanling 1789 # at the beginning of the request hanling
1775 if request.postpath and not request.postpath[-1]: 1790 if request.postpath and not request.postpath[-1]:
1828 else: 1843 else:
1829 log.debug(f"using URI cache for {self}") 1844 log.debug(f"using URI cache for {self}")
1830 cache_url.use(request) 1845 cache_url.use(request)
1831 else: 1846 else:
1832 await asDeferred(self.parse_url, self, request) 1847 await asDeferred(self.parse_url, self, request)
1848
1849 if self.add_breadcrumb is None:
1850 label = (
1851 self.label
1852 or self.name
1853 or self.url[self.url.rfind('/')+1:]
1854 )
1855 breadcrumb = {
1856 "url": self.url,
1857 "label": label.title(),
1858 }
1859 request.template_data["breadcrumbs"].append(breadcrumb)
1860 else:
1861 await asDeferred(
1862 self.add_breadcrumb,
1863 self,
1864 request,
1865 request.template_data["breadcrumbs"]
1866 )
1833 1867
1834 self._subpagesHandler(request) 1868 self._subpagesHandler(request)
1835 1869
1836 if request.method not in (C.HTTP_METHOD_GET, C.HTTP_METHOD_POST): 1870 if request.method not in (C.HTTP_METHOD_GET, C.HTTP_METHOD_POST):
1837 # only HTTP GET and POST are handled so far 1871 # only HTTP GET and POST are handled so far