Mercurial > libervia-web
comparison libervia/server/pages.py @ 1153:94f9d81a475e
pages: auto reloading
when developer mode is activated, pages are automatically reloaded when page_meta.py is modified.
author | Goffi <goffi@goffi.org> |
---|---|
date | Fri, 22 Feb 2019 16:57:37 +0100 |
parents | 3c7a64adfd42 |
children | 64952ba7affe |
comparison
equal
deleted
inserted
replaced
1152:1c23252958ed | 1153:94f9d81a475e |
---|---|
109 | 109 |
110 def __init__( | 110 def __init__( |
111 self, host, vhost_root, root_dir, url, name=None, redirect=None, access=None, | 111 self, host, vhost_root, root_dir, url, name=None, redirect=None, access=None, |
112 dynamic=False, parse_url=None, prepare_render=None, render=None, template=None, | 112 dynamic=False, parse_url=None, prepare_render=None, render=None, template=None, |
113 on_data_post=None, on_data=None, on_signal=None, url_cache=False, | 113 on_data_post=None, on_data=None, on_signal=None, url_cache=False, |
114 replace_on_conflict=False | |
114 ): | 115 ): |
115 """Initiate LiberviaPage instance | 116 """Initiate LiberviaPage instance |
116 | 117 |
117 LiberviaPages are the main resources of Libervia, using easy to set python files | 118 LiberviaPages are the main resources of Libervia, using easy to set python files |
118 The non mandatory arguments are the variables found in page_meta.py | 119 The non mandatory arguments are the variables found in page_meta.py |
156 this method is used with Libervia's websocket mechanism | 157 this method is used with Libervia's websocket mechanism |
157 @param on_signal(callable, None): method to call when a registered signal is | 158 @param on_signal(callable, None): method to call when a registered signal is |
158 received. This method is used with Libervia's websocket mechanism | 159 received. This method is used with Libervia's websocket mechanism |
159 @param url_cache(boolean): if set, result of parse_url is cached (per profile). | 160 @param url_cache(boolean): if set, result of parse_url is cached (per profile). |
160 Useful when costly calls (e.g. network) are done while parsing URL. | 161 Useful when costly calls (e.g. network) are done while parsing URL. |
162 @param replace_on_conflict(boolean): if True, don't raise ConflictError if a | |
163 page of this name already exists, but replace it | |
161 """ | 164 """ |
162 | 165 |
163 web_resource.Resource.__init__(self) | 166 web_resource.Resource.__init__(self) |
164 self.host = host | 167 self.host = host |
165 self.vhost_root = vhost_root | 168 self.vhost_root = vhost_root |
166 self.root_dir = root_dir | 169 self.root_dir = root_dir |
167 self.url = url | 170 self.url = url |
168 self.name = name | 171 self.name = name |
169 if name is not None: | 172 if name is not None: |
170 if name in self.named_pages: | 173 if (name in self.named_pages |
174 and not (replace_on_conflict and self.named_pages[name].url == url)): | |
171 raise exceptions.ConflictError( | 175 raise exceptions.ConflictError( |
172 _(u'a Libervia page named "{}" already exists'.format(name))) | 176 _(u'a Libervia page named "{}" already exists'.format(name))) |
173 if u"/" in name: | 177 if u"/" in name: |
174 raise ValueError(_(u'"/" is not allowed in page names')) | 178 raise ValueError(_(u'"/" is not allowed in page names')) |
175 if not name: | 179 if not name: |
247 | 251 |
248 @property | 252 @property |
249 def main_menu(self): | 253 def main_menu(self): |
250 return self.vhost_root.main_menu | 254 return self.vhost_root.main_menu |
251 | 255 |
256 @staticmethod | |
257 def createPage(host, meta_path, vhost_root, url_elts, replace_on_conflict=False): | |
258 """Create a LiberviaPage instance | |
259 | |
260 @param meta_path(unicode): path to the page_meta.py file | |
261 @param vhost_root(resource.Resource): root resource of the virtual host | |
262 @param url_elts(list[unicode]): list of path element from root site to this page | |
263 @param replace_on_conflict(bool): same as for [LiberviaPage] | |
264 @return (tuple[dict, LiberviaPage]): tuple with: | |
265 - page_data: dict containing data of the page | |
266 - libervia_page: created resource | |
267 """ | |
268 dir_path = os.path.dirname(meta_path) | |
269 page_data = {"__name__": u".".join([u"page"] + url_elts)} | |
270 # we don't want to force the presence of __init__.py | |
271 # so we use execfile instead of import. | |
272 # TODO: when moved to Python 3, __init__.py is not mandatory anymore | |
273 # so we can switch to import | |
274 execfile(meta_path, page_data) | |
275 return page_data, LiberviaPage( | |
276 host=host, | |
277 vhost_root=vhost_root, | |
278 root_dir=dir_path, | |
279 url=u"/" + u"/".join(url_elts), | |
280 name=page_data.get(u"name"), | |
281 redirect=page_data.get(u"redirect"), | |
282 access=page_data.get(u"access"), | |
283 dynamic=page_data.get(u"dynamic", False), | |
284 parse_url=page_data.get(u"parse_url"), | |
285 prepare_render=page_data.get(u"prepare_render"), | |
286 render=page_data.get(u"render"), | |
287 template=page_data.get(u"template"), | |
288 on_data_post=page_data.get(u"on_data_post"), | |
289 on_data=page_data.get(u"on_data"), | |
290 on_signal=page_data.get(u"on_signal"), | |
291 url_cache=page_data.get(u"url_cache", False), | |
292 replace_on_conflict=replace_on_conflict | |
293 ) | |
294 | |
252 @classmethod | 295 @classmethod |
253 def importPages(cls, host, vhost_root, root_path=None, _parent=None, _path=None, | 296 def importPages(cls, host, vhost_root, root_path=None, _parent=None, _path=None, |
254 _extra_pages=False): | 297 _extra_pages=False): |
255 """Recursively import Libervia pages | 298 """Recursively import Libervia pages |
256 | 299 |
284 log.debug(_(u"[{host_name}] {path} is already present, ignoring it") | 327 log.debug(_(u"[{host_name}] {path} is already present, ignoring it") |
285 .format(host_name=vhost_root.host_name, path=u'/'.join(_path+[d]))) | 328 .format(host_name=vhost_root.host_name, path=u'/'.join(_path+[d]))) |
286 continue | 329 continue |
287 meta_path = os.path.join(dir_path, C.PAGES_META_FILE) | 330 meta_path = os.path.join(dir_path, C.PAGES_META_FILE) |
288 if os.path.isfile(meta_path): | 331 if os.path.isfile(meta_path): |
289 page_data = {"__name__": u".".join([u"page"] + _path + [d])} | |
290 new_path = _path + [d] | 332 new_path = _path + [d] |
291 # we don't want to force the presence of __init__.py | |
292 # so we use execfile instead of import. | |
293 # TODO: when moved to Python 3, __init__.py is not mandatory anymore | |
294 # so we can switch to import | |
295 execfile(meta_path, page_data) | |
296 try: | 333 try: |
297 resource = LiberviaPage( | 334 page_data, resource = cls.createPage(host, meta_path, vhost_root, new_path) |
298 host=host, | |
299 vhost_root=vhost_root, | |
300 root_dir=dir_path, | |
301 url=u"/" + u"/".join(new_path), | |
302 name=page_data.get(u"name"), | |
303 redirect=page_data.get(u"redirect"), | |
304 access=page_data.get(u"access"), | |
305 dynamic=page_data.get(u"dynamic", False), | |
306 parse_url=page_data.get(u"parse_url"), | |
307 prepare_render=page_data.get(u"prepare_render"), | |
308 render=page_data.get(u"render"), | |
309 template=page_data.get(u"template"), | |
310 on_data_post=page_data.get(u"on_data_post"), | |
311 on_data=page_data.get(u"on_data"), | |
312 on_signal=page_data.get(u"on_signal"), | |
313 url_cache=page_data.get(u"url_cache", False), | |
314 ) | |
315 except exceptions.ConflictError as e: | 335 except exceptions.ConflictError as e: |
316 if _extra_pages: | 336 if _extra_pages: |
317 # extra pages are discarded if there is already an existing page | 337 # extra pages are discarded if there is already an existing page |
318 continue | 338 continue |
319 else: | 339 else: |
345 continue | 365 continue |
346 else: | 366 else: |
347 resource.registerURI(uri_tuple, cb) | 367 resource.registerURI(uri_tuple, cb) |
348 | 368 |
349 LiberviaPage.importPages( | 369 LiberviaPage.importPages( |
350 host, vhost_root, _parent=resource, _path=new_path, _extra_pages=_extra_pages) | 370 host, vhost_root, _parent=resource, _path=new_path, |
371 _extra_pages=_extra_pages) | |
372 | |
373 @classmethod | |
374 def onFileChange(cls, host, file_path, flags, site_root, site_path): | |
375 """Method triggered by file_watcher when something is changed in files | |
376 | |
377 This method is used in dev mode to reload pages when needed | |
378 @param file_path(filepath.FilePath): path of the file which triggered the event | |
379 @param flags[list[unicode]): human readable flags of the event (from | |
380 internet.inotify) | |
381 @param site_root(LiberviaRootResource): root of the site | |
382 @param site_path(unicode): absolute path of the site | |
383 """ | |
384 if flags == ['create']: | |
385 return | |
386 path = file_path.path.decode('utf-8') | |
387 base_name = os.path.basename(path) | |
388 if base_name != u"page_meta.py": | |
389 # we only handle libervia pages | |
390 return | |
391 | |
392 log.debug(u"{flags} event(s) received for {file_path}".format( | |
393 flags=u", ".join(flags), file_path=file_path)) | |
394 | |
395 dir_path = os.path.dirname(path) | |
396 if not dir_path.startswith(site_path): | |
397 raise exceptions.InternalError(u"watched file should start with site path") | |
398 | |
399 path_elts = [p for p in dir_path[len(site_path):].split('/') if p] | |
400 if not path_elts: | |
401 return | |
402 | |
403 if path_elts[0] == C.PAGES_DIR: | |
404 # a page has been modified | |
405 del path_elts[0] | |
406 if not path_elts: | |
407 # we need at least one element to parse | |
408 return | |
409 # we retrieve page by starting from site root and finding each path element | |
410 parent = page = site_root | |
411 new_page = False | |
412 for idx, child_name in enumerate(path_elts): | |
413 try: | |
414 page = page.children[child_name] | |
415 except KeyError: | |
416 if idx != len(path_elts)-1: | |
417 # a page has been created in a subdir when one or more | |
418 # page_meta.py are missing on the way | |
419 log.warning(_(u"Can't create a page at {path}, missing parents") | |
420 .format(path=path)) | |
421 return | |
422 new_page = True | |
423 else: | |
424 if idx<len(path_elts)-1: | |
425 parent = page.original | |
426 | |
427 try: | |
428 # we (re)create a page with the new/modified code | |
429 __, resource = cls.createPage(host, path, site_root, path_elts, | |
430 replace_on_conflict=True) | |
431 if not new_page: | |
432 resource.children = page.original.children | |
433 except Exception as e: | |
434 log.warning(_(u"Can't create page: {reason}").format(reason=e)) | |
435 else: | |
436 url_elt = path_elts[-1] | |
437 if not new_page: | |
438 # the page was already existing, we remove it | |
439 del parent.children[url_elt] | |
440 # we can now add the new page | |
441 parent.putChild(url_elt, resource) | |
442 if new_page: | |
443 log.info(_(u"{page} created").format(page=resource)) | |
444 else: | |
445 log.info(_(u"{page} reloaded").format(page=resource)) | |
351 | 446 |
352 def registerURI(self, uri_tuple, get_uri_cb): | 447 def registerURI(self, uri_tuple, get_uri_cb): |
353 """register a URI handler | 448 """Register a URI handler |
354 | 449 |
355 @param uri_tuple(tuple[unicode, unicode]): type or URIs handler | 450 @param uri_tuple(tuple[unicode, unicode]): type or URIs handler |
356 type/subtype as returned by tools/common/parseXMPPUri | 451 type/subtype as returned by tools/common/parseXMPPUri |
357 or type/None to handle all subtypes | 452 or type/None to handle all subtypes |
358 @param get_uri_cb(callable): method which take uri_data dict as only argument | 453 @param get_uri_cb(callable): method which take uri_data dict as only argument |