[Subversion] / PEAK / src / peak / web / templates.py  

Diff of /PEAK/src/peak/web/templates.py

Parent Directory | Revision Log

version 1865, Sat Oct 16 23:34:31 2004 UTC version 2003, Thu Feb 3 16:19:55 2005 UTC
Line 8 
Line 8 
   
  - Phase out old PWT syntax   - Phase out old PWT syntax
   
  - implement interaction wrapper for "/skin", "/request", etc. data paths  
   
  - implement sub-template support (convert doc->DOMlet in another doc)   - implement sub-template support (convert doc->DOMlet in another doc)
   
  - add hooks for DOMlets to validate the list of supplied parameters   - add hooks for DOMlets to validate the list of supplied parameters
Line 26 
Line 24 
 from interfaces import *  from interfaces import *
 from xml.sax.saxutils import quoteattr, escape  from xml.sax.saxutils import quoteattr, escape
 from publish import TraversalPath  from publish import TraversalPath
 from peak.util import SOX  from peak.util import SOX, imports
   from places import Decorator
   from environ import traverseItem, traverseDefault
   from errors import NotFound
   
 __all__ = [  __all__ = [
     'TEMPLATE_NS', 'DOMLETS_PROPERTY', 'TemplateDocument'      'TEMPLATE_NS', 'DOMLETS_PROPERTY', 'TemplateDocument'
Line 38 
Line 39 
 unicodeJoin = u''.join  unicodeJoin = u''.join
   
   
   
 def infiniter(sequence):  def infiniter(sequence):
     while 1:      while 1:
         for item in sequence:          for item in sequence:
             yield item              yield item
   
   
 class DOMletState(binding.Component):  class DOMletVars(Decorator):
   
       state = None
   
       def traverseTo(self, name, ctx, default=NOT_GIVEN):
           loc = traverseItem(ctx, self.state, 'item', name, name, NOT_FOUND)
           if loc is not NOT_FOUND:
               return loc
   
           # attribute is absent or private, fall through to underlying object
           return traverseDefault(ctx, self.ob, 'attr', name, name, default)
   
     """Execution state for a DOMlet"""  
   class DOMletMethod(object):
       """Bind an 'IDOMletRenderable' to a specific context"""
   
     protocols.advise(      protocols.advise(
         instancesProvide = [IDOMletState],          instancesProvide = [IDOMletRenderable]
     )      )
   
     write = binding.Require("Unicode output stream write() method")      __slots__ = 'template','ctx'
   
   
     def findState(self, iface):      def __init__(self,ctx,template):
           self.ctx = ctx
           self.template = template
   
         """Find nearest DOMletState implementing 'iface'"""      def renderFor(self,ctx,state):
           return self.template.renderFor(self.ctx,state)
   
         for c in binding.iterParents(self):     # XXX not covered by tests!  
             state = adapt(c,iface,None)  
             if state is not None:  
                 return state  
   
   
   
   
   
   
   class Parameters:
       """'params' object for templates"""
   
       protocols.advise( instancesProvide = [IWebTraversable] )
   
       def __init__(self,ctx,data):
           self.ctx = ctx
           self.data = data
           self.cache = {}
   
       def traverseTo(self, name, ctx, default=NOT_GIVEN):
           try:
               item = self.cache[name]
           except KeyError:
               try:
                   item = self.data[name]
               except KeyError:
                   if default is not NOT_GIVEN:
                       return default
                   raise NotFound(ctx,name,self)
               else:
                   tmpl = IDOMletRenderable(item,None)
                   if tmpl is not None:
                       item = self.cache[name] = DOMletMethod(self.ctx,tmpl)
                   else:
                       path = adapt(item,TraversalPath,None)
                       if path is not None:
                           self.data[name] = path
                           return path.traverse(self.ctx)
           return ctx.childContext(name,item)
   
   
       def beforeHTTP(self, ctx):
           return ctx
   
       def getURL(self,ctx):
           return ctx.traversedURL
   
   
   
   
 class DOMletAsHTTP(binding.Component):  class DOMletState(binding.Component):
   
     """Render a template component"""      """Execution state for a DOMlet"""
   
     protocols.advise(      protocols.advise(
         instancesProvide = [IHTTPHandler],          instancesProvide = [IDOMletState],
         asAdapterForProtocols = [IDOMletNode],  
         factoryMethod = 'fromNode'  
     )      )
   
     templateNode = binding.Require("""Node to render""")      write = binding.Require("Unicode output stream write() method")
   
     def fromNode(klass, subject):  
         return klass(templateNode = subject)  
   
     fromNode = classmethod(fromNode)  
   
     def handle_http(self, ctx):      data = binding.Make(dict)
         ctx = ctx.parentContext()  
         data = []  
         self.templateNode.renderFor(  
             ctx,  
             DOMletState(ctx.current, write=data.append)  
         )  
         return '200 OK', [], [str(unicodeJoin(data))]    # XXX content-type  
   
       def __getitem__(self,key):
           return self.data[key]
   
       def withData(self,**kw):
           data = self.data.copy()
           data.update(kw)
           return self.__class__(self,data=data,write=self.write)
   
       def wrapContext(self,ctx):
           return DOMletVars(ob=ctx, state=self)
   
       def findState(self, iface):
   
           """Find nearest DOMletState implementing 'iface'"""
   
           for c in binding.iterParents(self):     # XXX not covered by tests!
               state = adapt(c,iface,None)
               if state is not None:
                   return state
   
   
   
Line 134 
Line 175 
         paramName = data.get('this.is'),          paramName = data.get('this.is'),
     )      )
   
     inner = data.get('content.factory')      inner = data.get('content.factory') or ('content.register' in data and parent.tagFactory)
     if inner:      if inner:
         data['pwt.this'] = outer          data['pwt.this'] = outer
         data['pwt.content'] = inner(outer,          data['pwt.content'] = inner(outer,
Line 157 
Line 198 
         this = content          this = content
     for f in data.get('this.register',()):      for f in data.get('this.register',()):
         f(this)          f(this)
       if 'previous' in data:
           data['previous']['pwt.content'].addChild(this)
     return this      return this
   
   
   
   
 def negotiateDomlet(parser, data, name, value):  def negotiateDomlet(parser, data, name, value):
     data['attributes'].remove((name,value))      data['attributes'].remove((name,value))
     if ':' in value:      if ':' in value:
Line 197 
Line 238 
 def nodeIs(mode, parser, data, name, value):  def nodeIs(mode, parser, data, name, value):
     data['attributes'].remove((name,value))      data['attributes'].remove((name,value))
     data[mode+'.is'] = value      data[mode+'.is'] = value
     parent = data['previous']['pwt.content']  
     data.setdefault(mode+'.register',[]).append(      data.setdefault(mode+'.register',[]).append(
         lambda ob: parent.addParameter(value,ob)          lambda ob: binding.getParentComponent(ob).addParameter(value,ob)
     )      )
   
   
   
 def setupElement(parser,data):  def setupElement(parser,data):
   
     d = dict(data.get('attributes',()))      d = dict(data.get('attributes',()))
   
     if 'domlet' in d:      if 'domlet' in d:
         negotiateDomlet(parser,data,'domlet',d['domlet'])          negotiateDomlet(parser,data,'domlet',d['domlet'])
   
     if 'define' in d:      if 'define' in d:
         negotiateDefine(parser,data,'define',d['define'])          negotiateDefine(parser,data,'define',d['define'])
   
     def child(result):  
         data['pwt.content'].addChild(result)  
   
     def text(xml):      def text(xml):
         top = data['pwt.content']          top = data['pwt.content']
         top.addChild(top.textFactory(top,xml=escape(xml)))          top.addChild(top.textFactory(top,xml=escape(xml)))
Line 223 
Line 264 
   
     data['start'] = startElement      data['start'] = startElement
     data['finish'] = finishElement      data['finish'] = finishElement
     data['child'] = child  
     data['text'] = text      data['text'] = text
     data['literal'] = literal      data['literal'] = literal
   
   
 def setupDocument(parser,data):  def setupDocument(parser,data):
     setupElement(parser,data)      setupElement(parser,data)
     data['pwt.content'] = TemplateDocument(data['parent'])      data['pwt.content'] = data['pwt_document']
   
   
   
   
   
   
   def withParam(parser,data,name,value):
       data['attributes'].remove((name,value))
       data.setdefault('content.register',[]).append(
           lambda ob: ob.addParameter(name.split(':',1)[-1],value)
       )
   
   
   
Line 301 
Line 342 
     domletProperty = None      domletProperty = None
     dataSpec       = binding.Make(lambda: '', adaptTo=TraversalPath)      dataSpec       = binding.Make(lambda: '', adaptTo=TraversalPath)
     paramName      = None      paramName      = None
     acceptParams   = binding.Obtain('domletProperty')      acceptParams   = ()
       multiParams    = ()
   
     # IDOMletNode      # IDOMletNode
   
Line 325 
Line 367 
   
   
   
   
     def optimizedChildren(self):      def optimizedChildren(self):
   
         """Child nodes with as many separate text nodes combined as possible"""          """Child nodes with as many separate text nodes combined as possible"""
Line 355 
Line 396 
   
   
     def _traverse(self, data, state):      def _traverse(self, data, state):
           return self.dataSpec.traverse(data,state.wrapContext), state
   
   
   
         return self.dataSpec.traverse(  
             data, lambda ctx: self._wrapInteraction(ctx)  
         ), state  
   
   
   
Line 399 
Line 440 
         self.children.append(node)          self.children.append(node)
   
   
   
   
   
   
   
   
   
   
   
     def addParameter(self, name, element):      def addParameter(self, name, element):
         """Declare 'element' as part of parameter 'name'"""          """Declare 'element' as part of parameter 'name'"""
         if self.acceptParams:  
             self.params.setdefault(name,[]).append(element)          if not self.acceptParams:
               return self.getParentComponent().addParameter(name,element)
   
           if name not in self.acceptParams and '*' not in self.acceptParams:
               # XXX need line info
               raise SyntaxError("Unrecognized parameter: %r" % name)
   
           is_multi = (
               name in self.multiParams or
               name not in self.acceptParams and '*' in self.multiParams
           )
   
           if name in self.params:
   
               if not is_multi:
                   raise SyntaxError(
                       "Multiple definitions for parameter: %r" % name
                   )   # XXX need line info
   
               self.params[name].append(element)
   
           elif is_multi:
               self.params[name] = [element]
   
         else:          else:
             self.getParentComponent().addParameter(name,element)              self.params[name] = element
   
   
   
   
   
   
   
   
   
     # Override in subclasses  
   
     def _wrapInteraction(self,interaction):  
         # XXX This should wrap the interaction in an IWebTraversable simulator,  
         # XXX which should include access to this element's parameters as well  
         # XXX as interaction variables.  
         raise NotImplementedError  
   
   
       # Override in subclasses
   
     _emptyTag = binding.Make(      _emptyTag = binding.Make(
         lambda self: self.tagName and self._openTag[:-1]+u' />' or ''          lambda self: self.tagName and self._openTag[:-1]+u' />' or ''
     )      )
Line 433 
Line 508 
         ) or ''          ) or ''
     )      )
   
   
     tagFactory     = None # real value is set below      tagFactory     = None # real value is set below
     textFactory    = Literal      textFactory    = Literal
     literalFactory = Literal      literalFactory = Literal
Line 449 
Line 525 
   
   
   
   
   
   
   
   
   
 class TaglessElement(Element):  class TaglessElement(Element):
   
     """Element w/out tags"""      """Element w/out tags"""
Line 456 
Line 538 
     _openTag = _closeTag = _emptyTag = ''      _openTag = _closeTag = _emptyTag = ''
   
   
   class Uses(Element):
   
       """Render child elements with target data, or skip element altogether"""
   
       staticText = None
       render_if = True
   
       def renderFor(self, data, state):
           try:
               if self.dataSpec:
                   data, state = self._traverse(data, state)
           except (web.NotFound,web.NotAllowed):
               if self.render_if:
                   return
           else:
               if not self.render_if:
                   return
   
           state.write(self._openTag)
   
           for child in self.optimizedChildren:
               child.renderFor(data,state)
   
           state.write(self._closeTag)
   
   
   class Unless(Uses):
   
       """Skip child elements if target data is available"""
   
       render_if = False
   
   
   
 class TemplateDocument(TaglessElement):  class TemplateDocument(TaglessElement):
   
     """Document-level template element"""      """Document-level template element"""
   
     acceptParams = True     # handle any top-level parameters      protocols.advise(
           instancesProvide = [IHTTPHandler],
           classProvides = [naming.IObjectFactory],
       )
   
       acceptParams = '*',     # handle any top-level parameters
   
       def renderFor(self, ctx, state):
           if not self.fragment:
               raise TypeError("Can't be used as a fragment")
           return self.fragment.renderFor(ctx.parentContext(),state)
   
       def handle_http(self, ctx):
           name = ctx.shift()
           if name is not None:
               raise web.NotFound(ctx,name,self)   # No traversal to subobjects!
           if not self.page:
               raise web.UnsupportedMethod(ctx)    # We're not a page!
           data = []
           self.page.renderFor(
               ctx.parentContext(), DOMletState(self, write=data.append)
           )
           h = []
           if self.content_type:
               h.append(('Content-type',self.content_type))
           return '200 OK', h, [str(unicodeJoin(data))]    # XXX encoding
   
   
       def getObjectInstance(klass, context, refInfo, name, attrs=None):
           url, = refInfo.addresses
           return config.processXML(
               web.TEMPLATE_SCHEMA(context),str(url),pwt_document=klass(context),
           )
   
       getObjectInstance = classmethod(getObjectInstance)
   
   
       content_type = binding.Make(lambda self:
           str(self.params.get('content-type'))
       )
   
       def layoutDOMlet(self,d,attrName):
   
           if attrName+'-layout' in self.params:
               path = self.params[attrName+'-layout'] + ''  # ensure stringness
               if path=='/nothing':
                   return None
               elif path=='/default':
                   return super(TemplateDocument,self)
               else:
                   return Replace(self, dataSpec=path, params=self.params.copy())
   
           if attrName in self.params:
               return IDOMletRenderable(self.params[attrName])
   
           if attrName=='fragment':
               # It's okay to be a fragment by default
               return super(TemplateDocument,self)
   
       fragment = page = binding.Make(layoutDOMlet)
   
   
   
   
   
   
Line 476 
Line 654 
   
   
   
   class Replace(Element):
   
       staticText = None
       acceptParams = '*',
       escaped = True
   
       def renderFor(self,data,state):
   
           if self.dataSpec:
               ctx, state = self._traverse(data, state)
   
           if self.params:
               state = state.withData(params=Parameters(data,self.params))
   
           current = ctx.current
   
           domlet = IDOMletRenderable(current,None)
           if domlet is not None:
               return domlet.renderFor(ctx,state)
   
           # XXX dyn var comp goes here
           # XXX if NOT_FOUND -> return
           # XXX if NOT_GIVEN -> render original content
   
           current = unicode(current)
           if self.escaped:
               current = escape(current)
   
           state.write(current)
   
   
   class ReplaceXML(Replace):
       escaped = False
   
   
   
Line 545 
Line 750 
     _openTag = _closeTag = _emptyTag = ''      _openTag = _closeTag = _emptyTag = ''
   
   
   class Expects(Element):
   
       """Render child elements with target data, or skip element altogether"""
   
       staticText = None
   
       dataSpec = ''   # to disable conversion to path
   
       protocol = binding.Make(
           lambda self: imports.importString(self.dataSpec),uponAssembly=True
       )
   
       def renderFor(self, data, state):
   
           data = data.clone(current=adapt(data.current,self.protocol))
   
           state.write(self._openTag)
           for child in self.optimizedChildren:
               child.renderFor(data,state)
           state.write(self._closeTag)
   
   
   
   
   
   
   
   
   
   
   
   
Line 583 
Line 788 
         if self.dataSpec:          if self.dataSpec:
             data, state = self._traverse(data, state)              data, state = self._traverse(data, state)
   
         url = unicode(data.absoluteURL)          url = unicode(data.url)
   
         if not self.optimizedChildren and not self.nonEmpty:          if not self.optimizedChildren and not self.nonEmpty:
             state.write(self._emptyTag % locals())              state.write(self._emptyTag % locals())
Line 607 
Line 812 
         write = state.write          write = state.write
   
         write(self._openTag)          write(self._openTag)
         write(unicode(data.absoluteURL))          write(unicode(data.url))
         write(self._closeTag)          write(self._closeTag)
   
 class TaglessURLText(URLText):  class TaglessURLText(URLText):
Line 656 
Line 861 
   
 class List(ContentReplacer):  class List(ContentReplacer):
   
       acceptParams = 'listItem','header','emptyList','footer'
       multiParams = 'listItem',
   
     def renderFor(self, data, state):      def renderFor(self, data, state):
   
         if self.dataSpec:          if self.dataSpec:
Line 675 
Line 883 
                 continue                  continue
   
             if not ct:              if not ct:
                 for child in self.params.get('header',()):                  if 'header' in self.params:
                     child.renderFor(data,state)                      self.params['header'].renderFor(data,state)
   
             loc = data.childContext(str(ct), item)              loc = data.childContext(str(ct), item)
             nextPattern().renderFor(loc, state)              nextPattern().renderFor(loc, state)
Line 684 
Line 892 
   
         if not ct:          if not ct:
             # Handle list being empty              # Handle list being empty
             for child in self.params.get('emptyList',()):              if 'emptyList' in self.params:
                 child.renderFor(data, state)                  self.params['emptyList'].renderFor(data,state)
         else:          else:
             for child in self.params.get('footer',()):              if 'footer' in self.params:
                 child.renderFor(data,state)                  self.params['footer'].renderFor(data,state)
   
         state.write(self._closeTag)          state.write(self._closeTag)
   
 class TaglessList(List):  class TaglessList(List):
     _openTag = _closeTag = _emptyTag = ''      _openTag = _closeTag = _emptyTag = ''
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   


Generate output suitable for use with a patch program
Legend:
Removed from v.1865  
changed lines
  Added in v.2003

cvs-admin@eby-sarna.com

Powered by ViewCVS 1.0-dev

ViewCVS and CVS Help