Source code for gavo.web.constantrender

"""
Renderer not reacting too strongly on user input.

There's StaticRender just delivering certain files from within a service,
and there's FixedPageRenderer that just formats a defined template.
"""

#c Copyright 2008-2023, the GAVO project <gavo@ari.uni-heidelberg.de>
#c
#c This program is free software, covered by the GNU GPL.  See the
#c COPYING file in the source distribution.


import os

from twisted.web import server
from twisted.web import static

from gavo import base
from gavo import svcs
from gavo.web import grend
from gavo.web import ifpages
from gavo.web import weberrors


[docs]class StaticRenderer(grend.ServiceBasedPage): """A renderer that just hands through files. The standard operation here is to set a staticData property pointing to a resdir-relative directory used to serve files for. Indices for directories are created. You can define a root resource by giving an indexFile property on the service. Note in particular that you can use an index file with an extension of shtml. This lets you use nevow templates, but since metadata will be taken from the global context, that's probably not terribly useful. You are probably looking for the fixed renderer if you find yourself needing this. """ name = "static" defaultType = "text/html" def __init__(self, request, service): grend.ServiceBasedPage.__init__(self, request, service) try: self.indexFile = os.path.join(service.rd.resdir, service.getProperty("indexFile")) except base.NotFoundError: self.indexFile = None try: self.staticPath = os.path.join(service.rd.resdir, service.getProperty("staticData")) except base.NotFoundError: self.staticPath = None
[docs] @classmethod def isBrowseable(self, service): return service.getProperty("indexFile", None)
[docs] @classmethod def isCacheable(cls, segments, request): return False
[docs] def render(self, request): if not request.uri.endswith(b"/"): # only render these as directories raise svcs.WebRedirect(request.uri+b"/") if self.indexFile: try: return ifpages.StaticFile(self.indexFile, self.rd).render(request) except os.error as ex: raise base.NotFoundError(self.indexFile, "File", "file system", hint=str(ex)) else: if self.staticPath is None: raise svcs.ForbiddenURI("No static data on this service") return ifpages.DirectoryLister(self.staticPath).render(request)
[docs] def getChild(self, name, request): segments = request.popSegments(name) if segments==[""]: return self if self.staticPath is None: raise svcs.ForbiddenURI("No static data on this service") relPath = "/".join(segments) destName = os.path.join(self.staticPath, relPath) # make sure destName actually is below staticPath so stupid # tricks with .. or similar don't work on us if not os.path.abspath(destName).startswith( os.path.abspath(self.staticPath)): raise svcs.ForbiddenURI("%s is not located below staticData"% destName) if os.path.isdir(destName): if not destName.endswith("/"): raise svcs.WebRedirect(request.uri+b"/") return static.File(destName).directoryListing() elif os.path.isfile(destName): return ifpages.StaticFile(destName, self.rd, defaultType=self.defaultType) else: raise svcs.UnknownURI("No %s available here."%relPath)
[docs]class HiPSRenderer(StaticRenderer): """A static renderer with a few amenities for HiPS trees. To make this work, set the service's staticData property """ name = "hips" # for the benefit of properties defaultType = "text/plain" urlUse = "base" def __init__(self, request, service): super().__init__(request, service) self.indexFile = os.path.join(self.staticPath, "index.html")
[docs] @classmethod def isCacheable(cls, segments, request): return False
[docs] @classmethod def isBrowseable(self, service): return True
[docs]class FixedPageRenderer( grend.CustomTemplateMixin, grend.ServiceBasedPage, grend.HTMLResultRenderMixin): """A renderer that renders a single template. Use something like ``<template key="fixed">res/ft.html</template>`` in the enclosing service to tell the fixed renderer where to get this template from. In the template, you can fetch parameters from the URL using something like ``<n:invisible n:data="parameter FOO" n:render="string"/>``; you can also define new render and data functions on the service using customRF and customDF. This is, in particular, used for the data center's root page. The fixed renderer is intended for non- or slowly changing content. It is annotated as cacheable, which means that DaCHS will in general only render it once and then cache it. If the render functions change independently of the RD, use the volatile renderer. During development, users must add ?nocache=True to a fixed page URI to force DaCHS to reload the template. Built-in services for such browser apps should go through the //run RD. """ name = "fixed" def __init__(self, request, service): grend.ServiceBasedPage.__init__(self, request, service) self.table = None self.customTemplate = None try: self.customTemplate = self.service.getTemplate( self.name, "nocache" in request.strargs) except KeyError: raise base.ui.logOldExc( svcs.UnknownURI("%s renderer needs a '%s' template"%( self.name, self.name))) def _formatOutput(self, result, request): self.table = result and result.getPrimaryTable() grend.ServiceBasedPage.render(self, request)
[docs] def render(self, request): self.runAsync(request.strargs ).addCallback( self._formatOutput, request ).addErrback(weberrors.renderDCErrorPage, request) return server.NOT_DONE_YET
[docs] def data_result(self, request, tag): return self.table
[docs] def data_parameter(self, parName): """lets you insert an URL parameter into the template. Non-existing parameters are returned as an empty string. """ def getParameter(request, tag): val = request.strargs.get(parName, [""])[0] if isinstance(val, bytes): val = val.decode("utf-8", "ignore") else: val = str(val) return val return getParameter
[docs] @classmethod def isCacheable(cls, segments, request): return True
[docs] @classmethod def isBrowseable(self, service): return True
[docs]class VolatilePageRenderer(FixedPageRenderer): """A renderer rendering a single template with fast-changing results. This is like the fixed renderer, except that the results are not cached. """ name = "volatile"
[docs] @classmethod def isCacheable(cls, segments, request): return False