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