class DocFormRenderer(FormMixin, grend.ServiceBasedPage, grend.HTMLResultRenderMixin): """A renderer displaying a form and delivering core's result as a document. The core must return a pair of mime-type and content; on errors, the form is redisplayed. This is mainly useful with custom cores doing weird things. This renderer will not work with dbBasedCores and similar. """ name = "docform" # I actually don't know the result type, since it's determined by the # core; I probably should have some way to let the core tell me what # it's going to return. resultType = "application/octet-stream" compute = True @classmethod def isBrowseable(cls, service): return True def _formatOutput(self, data, ctx): request = inevow.IRequest(ctx) mime, payload = data.original request.setHeader("content-type", mime) request.write(payload) return "" docFactory = svcs.loadSystemTemplate("defaultresponse.html")
class ReloadPage(grend.GavoRenderMixin, rend.Page): """A page to clear some caches. Right now, we don't use it (e.g., it's not reachable from the web). There's gavo serve reload and reloads of individual RDs, so there may not be much of a niche for this. If it ever gets resurrected, we probably should use user.server._reload as the implementation. """ modsToReload = [] def __init__(self, ctx): rend.Page.__init__(self) self.modulesReloaded = [] def data_reloadedModules(self, ctx, data): return self.modulesReloaded def renderHTTP(self, ctx): return common.runAuthenticated(ctx, "admin", self._reload, ctx) def _reloadModules(self): for modPath in self.modsToReload: parts = modPath.split(".") exec "from %s import %s;reload(%s)" % (".".join( parts[:-1]), parts[-1], parts[-1]) self.modulesReloaded.append(modPath) def _reload(self, ctx): base.caches.clearCaches() self._reloadModules() return self._renderHTTP(ctx) docFactory = svcs.loadSystemTemplate("reloaded.html")
class AuthenticatePage(ErrorPage): handles = svcs.Authenticate status = 401 titleMessage = "Authentication Required" def renderHTTP(self, ctx): request = inevow.IRequest(ctx) request.setHeader('WWW-Authenticate', 'Basic realm="%s"'%str(self.failure.value.realm)) return ErrorPage.renderHTTP(self, ctx) docFactory = svcs.loadSystemTemplate("unauth.html")
class LoginPage(rend.Page, grend.GavoRenderMixin): """a page that logs people in or out. You should usually give a nextURL parameter in the context, the page the user is returned to afte login. If the user is already authenticated, this will do a logout (by sending a 403). """ def __init__(self, ctx): rend.Page.__init__(self) self.request = inevow.IRequest(ctx) self.nextURL = self.request.args.get("nextURL", ["/"])[0] def render_nextURL(self, ctx, data): return ctx.tag(href=self.nextURL) def render_iflogged(self, ctx, data): if self.request.getUser(): return ctx.tag return "" def render_ifnotlogged(self, ctx, data): if not self.request.getUser(): return ctx.tag return "" def data_loggedUser(self, ctx, data): return self.request.getUser() def renderHTTP(self, ctx): relogging = base.parseBooleanLiteral( utils.getfirst(self.request.args, "relog", default="False")) if self.request.getUser(): # user is logged in... if relogging: # ...and wants to log out: show login dialog... raise svcs.Authenticate() else: # ...and has just logged in: forward to destination return url.URL.fromContext(ctx).click(self.nextURL) else: # user is not logged in if relogging: #...but was and has just logged out: forward to dest return url.URL.fromContext(ctx).click(self.nextURL) else: # ... and wants to log in. raise svcs.Authenticate() docFactory = svcs.loadSystemTemplate("loginout.html")
class ServiceUnavailable(rend.Page): """A page to be rendered in emergencies. Essentially, this is a 503 with a text taken from stateDir/MAINT. Root checks for the presence of that file before returning this page, so (ignoring race conditions) this page assumes it's there. """ def renderHTTP(self, ctx): request = inevow.IRequest(ctx) request.setResponseCode(503) request.setHeader("retry-after", "3600") return rend.Page.renderHTTP(self, ctx) def data_maintText(self, ctx, data): with open(os.path.join(base.getConfig("stateDir"), "MAINT")) as f: return f.read().decode("utf-8") docFactory = svcs.loadSystemTemplate("maintenance.html")
class RDInfoRenderer(grend.CustomTemplateMixin, grend.ServiceBasedPage): """A renderer for displaying various properties about a resource descriptor. This renderer could really be attached to any service since it does not call it, but it usually lives on //services/overview. By virtue of builtin vanity, you can reach the rdinfo renderer at /browse, and thus you can access /browse/foo/q to view the RD infos. This is the form used by table registrations. """ name = "rdinfo" customTemplate = svcs.loadSystemTemplate("rdlist.html") def data_publishedRDs(self, ctx, data): td = base.caches.getRD("//services").getById("resources") with base.getTableConn() as conn: table = rsc.TableForDef(td, connection=conn) try: return [ row["sourceRD"] for row in table.iterQuery( [td.getColumnByName("sourceRD")], "", distinct=True, limits=("ORDER BY sourceRD", {})) ] finally: table.close() def locateChild(self, ctx, segments): rdId = "/".join(segments) if not rdId: raise svcs.WebRedirect("browse") clientRD = base.caches.getRD(rdId) return RDInfoPage(ctx, clientRD), () defaultDocFactory = common.doctypedStan(T.html[ T.head[T.title["Missing Template"]], T. body[T. p["The RD list is only available with an rdlist.html template"]]])
class TableInfoRenderer(MetaRenderer): """A renderer for displaying table information. It really doesn't use the underlying service, but conventionally, it is run on __system__/dc_tables/show. """ name = "tableinfo" customTemplate = svcs.loadSystemTemplate("tableinfo.html") def renderHTTP(self, ctx): if not hasattr(self, "table"): # _retrieveTableDef did not run, i.e., no tableName was given raise svcs.UnknownURI( "You must provide a table name to this renderer.") self.metaCarrier = self.table return super(TableInfoRenderer, self).renderHTTP(ctx) def _retrieveTableDef(self, tableName): try: self.tableName = tableName self.table = registry.getTableDef(tableName) self.describingRD = self.table.rd except base.NotFoundError, msg: raise base.ui.logOldExc(svcs.UnknownURI(str(msg)))
class GavoRenderMixin(common.CommonRenderers): """A mixin with renderers useful throughout the data center. Rendering of meta information: * <tag n:render="meta">METAKEY</tag> or * <tag n:render="metahtml">METAKEY</tag> Rendering the sidebar -- <body n:render="withsidebar">. This will only work if the renderer has a service attribute that's enough of a service (i.e., carries meta and knows how to generate URLs). Conditional rendering: * ifmeta * imownmeta * ifdata * ifnodata * ifslot * ifnoslot * ifadmin Obtaining system info * rd <rdId> -- makes the referenced RD the current data (this is not too useful right now, but it lets you check of the existence of RDs already) """ _sidebar = svcs.loadSystemTemplate("sidebar.html") _footer = svcs.loadSystemTemplate("footer.html") # macro package to use when expanding macros. Just set this # in the constructor as necessary (ServiceBasedRenderer has the # service here) macroPackage = None def _initGavoRender(self): # call this to initialize this mixin. # (kept for backward compatibility; don't use this any more) pass def _doRenderMeta(self, ctx, raiseOnFail=False, plain=False, carrier=None): if carrier is None: carrier = self.metaCarrier if not hasattr(carrier, "_metaRenderCache"): carrier._metaRenderCache = {} metaKey = "(inaccessible)" try: metaKey = ctx.tag.children[0].strip() if (metaKey, plain) in carrier._metaRenderCache: rendered = carrier._metaRenderCache[(metaKey, plain)] else: htmlBuilder = common.HTMLMetaBuilder(self.macroPackage) if plain: rendered = base.getMetaText(carrier, metaKey, raiseOnFail=True, macroPackage=self.macroPackage) else: rendered = T.xml( carrier.buildRepr(metaKey, htmlBuilder, raiseOnFail=True)) carrier._metaRenderCache[(metaKey, plain)] = rendered except base.NoMetaKey: if raiseOnFail: raise return T.comment["Meta item %s not given." % metaKey] except Exception, ex: msg = "Meta %s bad (%s)" % (metaKey, str(ex)) base.ui.notifyError(msg) return T.comment[msg] ctx.tag.clear() return ctx.tag[rendered]
class ErrorPage(rend.Page, grend.GavoRenderMixin): """A base for error handling pages. The idea is that you set the "handles" class attribute to the exception you handle. The exception has to match exactly, i.e., no isinstancing is done. You also must set status to the HTTP status code the error should return. All error pages have a failure attribute that's a twisted failure with all the related mess (e.g., tracebacks). You have the status and message data methods. """ handles = None status = 500 titleMessage = "Unspecified Error" beforeMessage = "We're sorry, but something didn't work out:" afterMessage = T.p["This generic text shouldn't be here. The child class should override afterMessage."] # _footer = "delete this when done" def __init__(self, error): self.failure = error def data_status(self, ctx, data): return str(self.status) def data_message(self, ctx, data): return self.failure.getErrorMessage() def render_beforeMessage(self, ctx, data): return ctx.tag[self.beforeMessage] def render_afterMessage(self, ctx, data): return ctx.tag[self.afterMessage] def render_message(self, ctx, data): return ctx.tag(class_="errmsg")[self.failure.getErrorMessage()] def render_hint(self, ctx, data): if (hasattr(self.failure.value, "hint") and self.failure.value.hint): return ctx.tag[T.strong["Hint: "], self.failure.value.hint] return "" def render_rdlink(self, ctx, data): if hasattr(self.failure.value, "rd") and self.failure.value.rd: rdURL = base.makeAbsoluteURL("/browse/%s"% self.failure.value.rd.sourceId) return T.p(class_="rdbacklink")["Also see the ", T.a(href=rdURL)["resources provided by this RD"], "."] return "" def render_titlemessage(self, ctx, data): return ctx.tag["%s: %s"%(base.getConfig("web", "sitename"), self.titleMessage)] def render_footer(self, ctx, data): return ctx.tag[ T.address[ T.span["Contact us: "], T.a(href="mailto:%s"%config.getMeta("contact.email").getContent())[ config.getMeta("contact.email").getContent() ] ] ] def renderHTTP(self, ctx): request = inevow.IRequest(ctx) request.setResponseCode(self.status) return rend.Page.renderHTTP(self, ctx) docFactory = svcs.loadSystemTemplate("error.html")
class AdminRenderer(formal.ResourceMixin, grend.CustomTemplateMixin, grend.ServiceBasedPage): """A renderer allowing to block and/or reload services. This renderer could really be attached to any service since it does not call it, but it usually lives on //services/overview. It will always require authentication. It takes the id of the RD to administer from the path segments following the renderer name. By virtue of builtin vanity, you can reach the admin renderer at /seffe, and thus you can access /seffe/foo/q to administer the foo/q RD. """ name = "admin" customTemplate = svcs.loadSystemTemplate("admin.html") clientRD = None # set below when RD loading failed. reloadExc = None reloadTB = None def form_setDowntime(self, ctx): form = formal.Form() form.addField( "scheduled", formal.String(), label="Schedule downtime for", description="Note that this is purely informative. The server" " will not take down the services at this point in time." " Leave empty to cancel. This will also be cleared on a" " reload.") form.addAction(self.setDowntime, label="Ok") form.data = { "scheduled": base.getMetaText(self.clientRD, "_scheduledDowntime") } return form def setDowntime(self, ctx, form, data): scheduleFor = data.get("scheduled") if scheduleFor is None: self.clientRD.delMeta("_scheduledDowntime") else: try: stc.parseISODT(scheduleFor) # check syntax self.clientRD.setMeta("_scheduledDowntime", scheduleFor) except stc.STCLiteralError: # bad date syntax raise base.ui.logOldExc( formal.FieldError("Doesn't look like ISO", "scheduleFor")) def form_adminOps(self, ctx): form = formal.Form() if hasattr(self.clientRD, "currently_blocked"): label = "Unblock" else: label = "Block" form.addAction(self.toggleBlock, label=label, name="block") form.addAction(self.reloadRD, label="Reload RD", name="submit") return form def toggleBlock(self, ctx, form, data): if hasattr(self.clientRD, "currently_blocked"): delattr(self.clientRD, "currently_blocked") else: self.clientRD.currently_blocked = True def reloadRD(self, ctx, form, data): # XXX TODO: load the supposedly changed RD here and raise errors before # booting out the old stuff. base.caches.clearForName(self.clientRD.sourceId) def data_blockstatus(self, ctx, data): if hasattr(self.clientRD, "currently_blocked"): return "blocked" return "not blocked" def data_services(self, ctx, data): """returns a sequence of service items belonging to clientRD, sorted by id. """ return sorted(self.clientRD.services) def render_svclink(self, ctx, data): """renders a link to a service info with a service title. data must be an item returned from data_services. """ return ctx.tag(href=data.getURL("info"))[base.getMetaText( data, "title")] def render_rdId(self, ctx, data): return ctx.tag[self.clientRD.sourceId] def render_ifexc(self, ctx, data): """render children if there was an exception during RD load. """ if self.reloadExc is None: return "" else: return ctx.tag def render_exc(self, ctx, data): return ctx.tag[repr(self.reloadExc)] def render_traceback(self, ctx, data): return ctx.tag[self.reloadTB] def renderHTTP(self, ctx): # naked renderer means admin services itself if self.clientRD is None: self.clientRD = base.caches.getRD("__system__/services") return common.runAuthenticated(ctx, "admin", super(AdminRenderer, self).renderHTTP, ctx) def _extractDamageInfo(self): """called when reload of RD failed; leaves exc. info in some attributes. """ type, value = sys.exc_info()[:2] self.reloadExc = value self.reloadTB = traceback.format_exc() # the locateChild here is actually the constructor, as it were -- # each request gets a new AdminRender by web.root def locateChild(self, ctx, segments): rdId = "/".join(segments) try: self.clientRD = base.caches.getRD(rdId) if hasattr(self.clientRD, "getRealRD"): self.clientRD = self.clientRD.getRealRD() self.metaCarrier = self.clientRD self.macroPackage = self.clientRD except base.RDNotFound: raise base.ui.logOldExc( svcs.UnknownURI("No such resource descriptor: %s" % rdId)) except Exception: # RD is botched. Clear cache and give an error base.caches.clearForName(rdId) self._extractDamageInfo() return self, () defaultDocFactory = common.doctypedStan( T.html[T.head[T.title["Missing Template"]], T.body[T.p[ "Admin services are only available with a admin.html template"]]])
class Form(FormMixin, grend.CustomTemplateMixin, grend.HTMLResultRenderMixin, grend.ServiceBasedPage): """The "normal" renderer within DaCHS for web-facing services. It will display a form and allow outputs in various formats. It also does error reporting as long as that is possible within the form. """ name = "form" runOnEmptyInputs = False compute = True def __init__(self, ctx, service): grend.ServiceBasedPage.__init__(self, ctx, service) if "form" in self.service.templates: self.customTemplate = self.service.getTemplate("form") # enable special handling if I'm rendering fixed-behaviour services # (i.e., ones that never have inputs) XXX TODO: Figure out where I used this and fix that to use the fixed renderer (or whatever) if not self.service.getInputKeysFor(self): self.runOnEmptyInputs = True self.queryResult = None @classmethod def isBrowseable(self, service): return True @classmethod def isCacheable(self, segments, request): return segments == () def renderHTTP(self, ctx): if self.runOnEmptyInputs: inevow.IRequest(ctx).args[formal.FORMS_KEY] = ["genForm"] return FormMixin.renderHTTP(self, ctx) def render_commonfoot(self, ctx, data): return self._footer def _formatOutput(self, res, ctx): """actually delivers the whole document. This is basically nevow's rend.Page._renderHTTP, changed to provide less blocks. """ request = inevow.IRequest(ctx) if isinstance(res.original, tuple): # core returned a complete document (mime and string) mime, payload = res.original request.setHeader("content-type", mime) request.setHeader( 'content-disposition', 'attachment; filename=result%s' % common.getExtForMime(mime)) return streaming.streamOut(lambda f: f.write(payload), request) self.result = res if "response" in self.service.templates: self.customTemplate = self.service.getTemplate("response") ctx = context.PageContext(parent=ctx, tag=self) self.rememberStuff(ctx) doc = self.docFactory.load(ctx) ctx = context.WovenContext(ctx, T.invisible[doc]) return deliverYielding(doc, ctx, request) defaultDocFactory = svcs.loadSystemTemplate("defaultresponse.html")
class RDInfoPage(grend.CustomTemplateMixin, grend.ResourceBasedPage): """A page giving infos about an RD. This is not a renderer but a helper for RDInfoRenderer. """ customTemplate = svcs.loadSystemTemplate("rdinfo.html") def data_services(self, ctx, data): return sorted(self.rd.services, key=lambda s: base.getMetaText(s, "title", default=s.id)) def data_tables(self, ctx, data): return sorted( (t for t in self.rd.tables if t.onDisk and not t.temporary), key=lambda t: t.id) def data_clientRdId(self, ctx, data): return self.rd.sourceId def _getDescriptionHTML(self, descItem): """returns stan for the "description" of a service or a table. The RD's description is not picked up. """ iDesc = descItem.getMeta("description", propagate=False) if iDesc is None: return "" else: return T.div(class_="lidescription")[T.xml( iDesc.getContent("blockhtml", macroPackage=descItem))] def render_rdsvc(self, ctx, service): return ctx.tag[T.a( href=service.getURL("info") )[base.getMetaText(service, "title", default=service.id)], self._getDescriptionHTML(service)] def render_rdtable(self, ctx, tableDef): qName = tableDef.getQName() adqlNote = "" if tableDef.adql: adqlNote = T.span(class_="adqlnote")[" ", E.ndash, " queriable through ", T.a(href="/tap")["TAP"], " and ", T.a(href="/adql")["ADQL"], " "] return ctx.tag[T.a(href="/tableinfo/%s" % qName)[qName], adqlNote, self._getDescriptionHTML(tableDef)] @classmethod def makePageTitle(cls, rd): """returns a suitable title for the rd info page. This is a class method to allow other renderers to generate titles for link anchors. """ return "Information on resource '%s'" % base.getMetaText( rd, "title", default="%s" % rd.sourceId) def render_title(self, ctx, data): return ctx.tag[self.makePageTitle(self.rd)] defaultDocFactory = common.doctypedStan(T.html[ T.head[T.title["Missing Template"]], T. body[T.p["RD infos are only available with an rdinfo.html template"]]])
class ServiceInfoRenderer(MetaRenderer, utils.IdManagerMixin): """A renderer showing all kinds of metadata on a service. This renderer produces the default referenceURL page. To change its appearance, override the serviceinfo.html template. """ name = "info" customTemplate = svcs.loadSystemTemplate("serviceinfo.html") def __init__(self, *args, **kwargs): grend.ServiceBasedPage.__init__(self, *args, **kwargs) self.describingRD = self.service.rd self.footnotes = set() def render_title(self, ctx, data): return ctx.tag["Information on Service '%s'" % base.getMetaText(self.service, "title")] def render_notebubble(self, ctx, data): if not data["note"]: return "" id = data["note"].tag self.footnotes.add(data["note"]) return ctx.tag(href="#note-%s" % id)["Note %s" % id] def render_footnotes(self, ctx, data): """renders the footnotes as a definition list. """ return T.dl(class_="footnotes")[[ T.xml(note.getContent(targetFormat="html")) for note in sorted(self.footnotes, key=lambda n: n.tag) ]] def data_internalpath(self, ctx, data): return "%s/%s" % (self.service.rd.sourceId, self.service.id) def data_inputFields(self, ctx, data): res = [ f.asInfoDict() for f in self.service.getInputKeysFor("info") + self.service.serviceKeys ] res.sort(key=lambda val: val["name"].lower()) return res def data_htmlOutputFields(self, ctx, data): res = [f.asInfoDict() for f in self.service.getCurOutputFields()] res.sort(key=lambda val: val["name"].lower()) return res def data_votableOutputFields(self, ctx, data): queryMeta = svcs.QueryMeta({"_FORMAT": "VOTable", "_VERB": 3}) res = [ f.asInfoDict() for f in self.service.getCurOutputFields(queryMeta) ] res.sort(key=lambda val: val["verbLevel"]) return res def data_rendAvail(self, ctx, data): return [{ "rendName": rend, "rendExpl": RendExplainer.explain(rend, self.service) } for rend in self.service.allowed] def data_publications(self, ctx, data): res = [{ "sets": ",".join(p.sets), "render": p.render } for p in self.service.publications if p.sets] return sorted(res, key=lambda v: v["render"]) def data_browserURL(self, ctx, data): return self.service.getBrowserURL() def data_service(self, ctx, data): return self.service defaultDocFactory = common.doctypedStan( T.html[T.head[T.title["Missing Template"]], T.body[ T.p["Infos are only available with a serviceinfo.html template"]]])