def _actuallySend(self, scripts): output = self.outputConduit written = [] def writer(write): #print "WRITER", write written.append(write) def finisher(finish): towrite = '\n'.join(written) jslog("<<<<<<\n%s\n" % towrite) output.callback(towrite) flat.flattenFactory(scripts, self.outputContext, writer, finisher) self.outputConduit = None self.outputContext = None
def renderHTTP(self, ctx): # The URL may contain deferreds so we need to flatten it using # flattenFactory that will collect the bits into the bits list and # call flattened to finish. bits = [] def flattened(spam): # Join the bits to make a complete URL. u = ''.join(bits) # It might also be relative so resolve it against the current URL # and flatten it again. u = flat.flatten(URL.fromContext(ctx).click(u), ctx) return redirectTo(u, inevow.IRequest(ctx)) return flat.flattenFactory(self.original, ctx, bits.append, flattened)
class Page(Fragment, ConfigurableFactory, ChildLookupMixin): """A page is the main Nevow resource and renders a document loaded via the document factory (docFactory). """ implements(inevow.IResource) buffered = False beforeRender = None afterRender = None addSlash = None flattenFactory = lambda self, *args: flat.flattenFactory(*args) def renderHTTP(self, ctx): if self.beforeRender is not None: return util.maybeDeferred(self.beforeRender, ctx).addCallback( lambda result, ctx: self._renderHTTP(ctx), ctx) return self._renderHTTP(ctx) def _renderHTTP(self, ctx): request = inevow.IRequest(ctx) ## XXX request is really ctx now, change the name here if self.addSlash and inevow.ICurrentSegments(ctx)[-1] != '': request.redirect(str(request.URLPath().child(''))) return '' log.msg(http_render=None, uri=request.uri) self.rememberStuff(ctx) def finishRequest(): carryover = request.args.get('_nevow_carryover_', [None])[0] if carryover is not None and carryover in _CARRYOVER: del _CARRYOVER[carryover] if self.afterRender is not None: return util.maybeDeferred(self.afterRender, ctx) if self.buffered: io = StringIO() writer = io.write def finisher(result): request.write(io.getvalue()) return util.maybeDeferred(finishRequest).addCallback( lambda r: result) else: writer = request.write def finisher(result): return util.maybeDeferred(finishRequest).addCallback( lambda r: result) preprocessors = _getPreprocessors(self) doc = self.docFactory.load(ctx, preprocessors) ctx = WovenContext(ctx, tags.invisible[doc]) return self.flattenFactory(doc, ctx, writer, finisher) def rememberStuff(self, ctx): Fragment.rememberStuff(self, ctx) ctx.remember(self, inevow.IResource) def renderString(self, ctx=None): """Render this page outside of the context of a web request, returning a Deferred which will result in a string. If twisted is not installed, this method will return a string result immediately, and this method is equivalent to renderSynchronously. """ io = StringIO() writer = io.write def finisher(result): return io.getvalue() ctx = PageContext(parent=ctx, tag=self) self.rememberStuff(ctx) doc = self.docFactory.load(ctx) ctx = WovenContext(ctx, tags.invisible[doc]) return self.flattenFactory(doc, ctx, writer, finisher) def renderSynchronously(self, ctx=None): """Render this page synchronously, returning a string result immediately. Raise an exception if a Deferred is required to complete the rendering process. """ io = StringIO() ctx = PageContext(parent=ctx, tag=self) self.rememberStuff(ctx) doc = self.docFactory.load(ctx) ctx = WovenContext(ctx, tags.invisible[doc]) def raiseAlways(item): raise NotImplementedError("renderSynchronously can not support" " rendering: %s" % (item, )) list(flat.iterflatten(doc, ctx, io.write, raiseAlways)) return io.getvalue() def child_(self, ctx): """When addSlash is True, a page rendered at a url with no trailing slash and a page rendered at a url with a trailing slash will be identical. addSlash is useful for the root resource of a site or directory-like resources. """ # Only allow an empty child, by default, if it's on the end # and we're a directoryish resource (addSlash = True) if self.addSlash and len(inevow.IRemainingSegments(ctx)) == 1: return self return None def webFormPost(self, request, res, configurable, ctx, bindingName, args): """Accept a web form post, either redisplaying the original form (with errors) if validation fails, or redirecting to the appropriate location after the post succeeds. This hook exists specifically for formless. New in 0.5, _nevow_carryover_ is only used if an autocallable method returns a result that needs to be carried over. New in 0.5, autocallables may return a nevow.url.URL or URLOverlay instance rather than setting IRedirectAfterPost on the request. New in 0.5, autocallables may return a Page instance to have that Page instance rendered at the post target URL with no redirects at all. Useful for multi-step wizards. """ def redirectAfterPost(aspects): hand = aspects.get(inevow.IHand) refpath = None if hand is not None: if isinstance(hand, Page): refpath = url.here if 'freeform_hand' not in inevow.IRequest(ctx).prepath: refpath = refpath.child('freeform_hand') if isinstance(hand, (url.URL, url.URLOverlay)): refpath, hand = hand, None if refpath is None: redirectAfterPost = request.getComponent( iformless.IRedirectAfterPost, None) if redirectAfterPost is None: ref = request.getHeader('referer') if ref: refpath = url.URL.fromString(ref) else: refpath = url.here else: warnings.warn( "[0.5] IRedirectAfterPost is deprecated. Return a URL instance from your autocallable instead.", DeprecationWarning, 2) ## Use the redirectAfterPost url ref = str(redirectAfterPost) refpath = url.URL.fromString(ref) if hand is not None or aspects.get( iformless.IFormErrors) is not None: magicCookie = '%s%s%s' % (now(), request.getClientIP(), random.random()) refpath = refpath.replace('_nevow_carryover_', magicCookie) _CARRYOVER[magicCookie] = C = tpc.Componentized() for k, v in aspects.items(): C.setComponent(k, v) destination = flat.flatten(refpath, ctx) request.redirect(destination) from nevow import static return static.Data('You posted a form to %s' % bindingName, 'text/plain'), () return util.maybeDeferred( configurable.postForm, ctx, bindingName, args).addCallback( self.onPostSuccess, request, ctx, bindingName, redirectAfterPost).addErrback(self.onPostFailure, request, ctx, bindingName, redirectAfterPost) def onPostSuccess(self, result, request, ctx, bindingName, redirectAfterPost): if result is None: message = "%s success." % formless.nameToLabel(bindingName) else: message = result return redirectAfterPost({ inevow.IHand: result, inevow.IStatusMessage: message }) def onPostFailure(self, reason, request, ctx, bindingName, redirectAfterPost): reason.trap(formless.ValidateError) return redirectAfterPost( {iformless.IFormErrors: { bindingName: reason.value }})
class JsonPage(rend.Page): flattenFactory = lambda self, *args: flat.flattenFactory(*args) addSlash = True def renderHTTP(self, ctx): request = inevow.IRequest(ctx) if inevow.ICurrentSegments(ctx)[-1] != '': request.redirect(request.URLPath().child('')) return '' request.setHeader("Content-Type", "application/json; charset=UTF-8") d = defer.maybeDeferred(self.data_json, ctx, None) d.addCallback(lambda x: self.render_json(ctx, x)) return d def render_json(self, ctx, data): """Render the given data in a proper JSON string""" def sanitize(data, d=None): """Nevow JSON serializer is not able to handle some types. We convert those types in proper types: - string to unicode string - PgSQL result set into list - handling of deferreds """ if type(data) in [list, tuple] or \ (PgSQL and isinstance(data, PgSQL.PgResultSet)): return [sanitize(x, d) for x in data] if PgSQL and isinstance(data, PgSQL.PgBooleanType): if data: return u"true" return u"false" if type(data) == str: return unicode(data, errors='ignore') if isinstance(data, rend.Fragment): io = StringIO() writer = io.write finisher = lambda result: io.getvalue() newctx = context.PageContext(parent=ctx, tag=data) data.rememberStuff(newctx) doc = data.docFactory.load() newctx = context.WovenContext(newctx, T.invisible[doc]) fl = self.flattenFactory(doc, newctx, writer, finisher) fl.addCallback(sanitize, None) d.append(fl) return fl if isinstance(data, defer.Deferred): if data.called: return sanitize(data.result) return data if isinstance(data, failure.Failure): return unicode( "<span class='error'>An error occured (%s)</span>" % data.getErrorMessage(), errors='ignore') return data def serialize(data): return json.serialize(sanitize(data)) d = [] data = sanitize(data, d) d = defer.DeferredList(d) d.addCallback(lambda x: serialize(data)) return d
class Page(Fragment, ConfigurableFactory, ChildLookupMixin): """A page is the main Nevow resource and renders a document loaded via the document factory (docFactory). """ __implements__ = Fragment.__implements__, inevow.IResource, ConfigurableFactory.__implements__ buffered = False beforeRender = None afterRender = None addSlash = None flattenFactory = lambda self, *args: flat.flattenFactory(*args) def renderHTTP(self, ctx): if self.beforeRender is not None: return util.maybeDeferred(self.beforeRender, ctx).addCallback( lambda result, ctx: self._renderHTTP(ctx), ctx) return self._renderHTTP(ctx) def _renderHTTP(self, ctx): request = inevow.IRequest(ctx) ## XXX request is really ctx now, change the name here if self.addSlash and inevow.ICurrentSegments(ctx)[-1] != '': request.redirect(request.URLPath().child('')) return '' log.msg(http_render=None, uri=request.uri) self.rememberStuff(ctx) def finishRequest(): carryover = request.args.get('_nevow_carryover_', [None])[0] if carryover is not None and _CARRYOVER.has_key(carryover): del _CARRYOVER[carryover] if self.afterRender is not None: return util.maybeDeferred(self.afterRender, ctx) if self.buffered: io = StringIO() writer = io.write def finisher(result): request.write(io.getvalue()) return util.maybeDeferred(finishRequest).addCallback( lambda r: result) else: writer = request.write def finisher(result): return util.maybeDeferred(finishRequest).addCallback( lambda r: result) doc = self.docFactory.load(ctx) ctx = WovenContext(ctx, tags.invisible[doc]) return self.flattenFactory(doc, ctx, writer, finisher) def rememberStuff(self, ctx): Fragment.rememberStuff(self, ctx) ctx.remember(self, inevow.IResource) def renderString(self): """Render this page outside of the context of a web request, returning a Deferred which will result in a string. If twisted is not installed, this method will return a string result immediately, and this method is equivalent to renderSynchronously. """ io = StringIO() writer = io.write def finisher(result): return io.getvalue() ctx = PageContext(tag=self) self.rememberStuff(ctx) doc = self.docFactory.load(ctx) ctx = WovenContext(ctx, tags.invisible[doc]) return self.flattenFactory(doc, ctx, writer, finisher) def renderSynchronously(self): """Render this page synchronously, returning a string result immediately. Raise an exception if a Deferred is required to complete the rendering process. """ io = StringIO() ctx = PageContext(tag=self) self.rememberStuff(ctx) doc = self.docFactory.load(ctx) ctx = WovenContext(ctx, tags.invisible[doc]) def raiseAlways(item): raise NotImplementedError("renderSynchronously can not support" " rendering: %s" % (item, )) list(flat.iterflatten(doc, ctx, io.write, raiseAlways)) return io.getvalue() def child_(self, ctx): """When addSlash is True, a page rendered at a url with no trailing slash and a page rendered at a url with a trailing slash will be identical. addSlash is useful for the root resource of a site or directory-like resources. """ # Only allow an empty child, by default, if it's on the end # and we're a directoryish resource (addSlash = True) if self.addSlash and len(inevow.IRemainingSegments(ctx)) == 1: return self # After deprecation is removed, this should return None warnings.warn( "Allowing an empty child ('/') of resources automatically is " "deprecated. If the class '%s' is a directory-index-like resource, " "please add addSlash=True to the class definition." % (self.__class__), DeprecationWarning, 2) return self def webFormPost(self, request, res, configurable, ctx, bindingName, args): def redirectAfterPost(aspects): redirectAfterPost = request.getComponent( iformless.IRedirectAfterPost, None) if redirectAfterPost is None: ref = request.getHeader('referer') or '' else: ## Use the redirectAfterPost url ref = str(redirectAfterPost) from nevow import url refpath = url.URL.fromString(ref) magicCookie = '%s%s%s' % (now(), request.getClientIP(), random.random()) refpath = refpath.replace('_nevow_carryover_', magicCookie) _CARRYOVER[magicCookie] = C = compy.Componentized(aspects) request.redirect(str(refpath)) from nevow import static return static.Data('You posted a form to %s' % bindingName, 'text/plain'), () return util.maybeDeferred( configurable.postForm, ctx, bindingName, args).addCallback( self.onPostSuccess, request, ctx, bindingName, redirectAfterPost).addErrback(self.onPostFailure, request, ctx, bindingName, redirectAfterPost) def onPostSuccess(self, result, request, ctx, bindingName, redirectAfterPost): if result is None: message = "%s success." % formless.nameToLabel(bindingName) else: message = result return redirectAfterPost({ inevow.IHand: result, inevow.IStatusMessage: message }) def onPostFailure(self, reason, request, ctx, bindingName, redirectAfterPost): reason.trap(formless.ValidateError) return redirectAfterPost( {iformless.IFormErrors: { bindingName: reason.value }})
def _flatten(request, root, slotData, renderFactory, inAttribute, inXML): """ Make C{root} slightly more flat by yielding all or part of it as strings or generators. @param request: A request object which will be passed to L{IRenderable.render}. @param root: An object to be made flatter. This may be of type C{unicode}, C{str}, L{raw}, L{Proto}, L{xml}, L{slot}, L{_PrecompiledSlot}, L{Tag}, L{URL}, L{tuple}, L{list}, L{GeneratorType}, L{Entity}, L{Deferred}, or it may be an object which is adaptable to L{IRenderable}. Deprecated backwards-compatibility support is also present for objects adaptable to L{IRenderer} or for which a flattener has been registered via L{registerFlattener}. @param slotData: A C{list} of C{dict} mapping C{str} slot names to data with which those slots will be replaced. @param inAttribute: A flag which, if set, indicates that C{str} and C{unicode} instances encountered must be quoted as for XML tag attribute values. @param inXML: A flag which, if set, indicates that C{str} and C{unicode} instances encountered must be quoted as for XML text node data. @return: An iterator which yields C{str}, L{Deferred}, and more iterators of the same type. """ if isinstance(root, unicode): root = root.encode('utf-8') elif isinstance(root, WovenContext): # WovenContext is supported via the getFlattener case, but that is a # very slow case. Checking here is an optimization. It also lets us # avoid the deprecation warning which would be emitted whenever a # precompiled document was flattened, since those contain WovenContexts # for tags with render directives. -exarkun inAttribute = root.isAttrib inXML = True root = root.tag if isinstance(root, raw): root = str(root) if inAttribute: root = root.replace('"', '"') yield root elif isinstance(root, Proto): root = str(root) if root: if root in allowSingleton: yield '<' + root + ' />' else: yield '<' + root + '></' + root + '>' elif isinstance(root, str): yield escapedData(root, inAttribute, inXML) elif isinstance(root, slot): slotValue = _getSlotValue(root.name, slotData) yield _flatten(request, slotValue, slotData, renderFactory, inAttribute, inXML) elif isinstance(root, _PrecompiledSlot): slotValue = _getSlotValue(root.name, slotData) yield _flatten(request, slotValue, slotData, renderFactory, root.isAttrib, inXML) elif isinstance(root, Tag): if root.pattern is Unset or root.pattern is None: slotData.append(root.slotData) if root.render is Unset: if not root.tagName: for element in _flatten(request, root.children, slotData, renderFactory, False, True): yield element else: yield '<' if isinstance(root.tagName, unicode): tagName = root.tagName.encode('ascii') else: tagName = str(root.tagName) yield tagName for k, v in root.attributes.iteritems(): if isinstance(k, unicode): k = k.encode('ascii') yield " " + k + "=\"" for element in _flatten(request, v, slotData, renderFactory, True, True): yield element yield "\"" if root.children or tagName not in allowSingleton: yield '>' for element in _flatten(request, root.children, slotData, renderFactory, False, True): yield element yield '</' + tagName + '>' else: yield ' />' else: if isinstance(root.render, directive): rendererName = root.render.name else: rendererName = root.render root = root.clone(False) del root._specials['render'] result = renderFactory.renderer(rendererName)(request, root) yield _flatten(request, result, slotData, renderFactory, None, inXML) slotData.pop() elif isinstance(root, URL): yield escapedData(str(root), inAttribute, inXML) elif isinstance(root, (tuple, list, GeneratorType)): for element in root: yield _flatten(request, element, slotData, renderFactory, inAttribute, inXML) elif isinstance(root, Entity): yield '&#' yield root.num yield ';' elif isinstance(root, xml): if isinstance(root.content, unicode): yield root.content.encode('utf-8') else: yield root.content elif isinstance(root, Deferred): yield root.addCallback( lambda result: (result, _flatten(request, result, slotData, renderFactory, inAttribute, inXML))) else: renderable = IRenderable(root, None) if renderable is not None: # [] for the slotData parameter of this call to _flatten means # slots returned by this renderable's render method won't be filled # with data which has so far accumulated in the slotData stack. # This seems like a reasonable thing to me, since a renderable is a # piece of Python code. It should be isolated from this other # stuff, which is primarily data. -exarkun yield _flatten(request, renderable.render(request), [], renderable, inAttribute, inXML) else: renderer = IRenderer(root, None) if renderer is not None: ctx = _ctxForRequest(request, slotData, None, inAttribute) results = [] synchronous = [] flattened = flattenFactory(renderer, ctx, results.append, lambda ign: None) def cbFlattened(result): synchronous.append(None) return (result, (str(s) for s in results)) flattened.addCallback(cbFlattened) if synchronous: yield ''.join(map(str, results)) else: yield flattened else: flattener = getFlattener(root) if flattener is not None: ctx = _ctxForRequest(request, slotData, renderFactory, inAttribute) yield _flatten(request, flattener(root, ctx), slotData, renderFactory, False, False) else: raise UnsupportedType(root)