[Grok-dev] some ideas concerning skins and theming

David Pratt fairwinds at eastlink.ca
Thu Mar 22 10:38:33 EDT 2007


Hi Martin.

You may wish to look at xtemplate, render speed is good as well:

http://zif.sourceforge.net/xtemplate_README.html

Regards,
David


Martijn Faassen wrote:
> Hi there,
> 
> Just now Lennart Regebro and I had a discussion on what Grok's approach 
> to theming might look like. Here I jot down some of the ideas we had. 
> The aims is to allow theming while being template language independent. 
> We do want theming to be in-process, as we don't want to require 
> something like deliverance to be used
> 
> A skin is like a Zope 3 skin, identified by an interface that can 
> inherit other skins. A skin in Grok can however have a post-processing 
> step associated with it. This means that any views that declares it is 
> in this skin will be post-processed by that skin. Let's look at what 
> that looks like:
> 
> class MyPage(grok.View):
>    grok.skin('some_skin')
> 
> using the 'grok.skin' directive we say that this page is in our skin. 
> How we define skins and such we haven't really fleshed out in detail, 
> but one approach is sketched out in a Grok design document:
> 
> http://svn.zope.org/grok/trunk/doc/design/skin-minimal.py
> 
> (this is certainly not set in stone; I think we need to deal with skin 
> inheritance and skins-as-interfaces, for instance. Possibly we just make 
> a special ISkin interface and then just grok anything that subclasses it 
> as a skin?)
> 
> The post-process step can then mess about with the HTML before it 
> finally goes back to the user. This will allow us to do theming.
> 
> What will this look like? We could have a postprocess grokker to 
> identify the postprocessing step (or the skin?) so we can write this:
> 
> class MySiteTheme(grok.Postprocess):
>     # possibly also allow pointing to individual views for this?
>     # do we have a parallel with grok.context going on here, but
>     # then for skins or views?
>     grok.skin('some_skin')
> 
>     def postprocess(self, html):
>         return html.replace('h1', 'h2')
> 
> Now the replace will be done for any HTML generated by any view that's 
> in the skin. This means that any text 'h1' will change into 'h2'.
> 
> Obviously this is a very coarse implementation. Better would be if the 
> template generated a stream of events (along the line of sax) and for 
> the postprocessor to transform the streams instead.
> 
> Genshi happens to have an implementation of this with interesting 
> features, called Markup Streams. This is not the templating language 
> itself, but a library underlying it:
> 
>  http://genshi.edgewall.org/wiki/Documentation/streams.html
> 
> The interesting feature is that they can do xpath on these streams. If 
> we were to generate such a Markup Stream for our templates, we could 
> post-process stuff. We may be able to reuse this bit of Genshi for our 
> purposes, as Genshi is a pure-python library shipped as an egg. Note 
> that I don't mean we use Genshi the template language here (that's 
> another topic), just bits of Genshi's implementation.
> 
> Still, postprocessing a markup stream by hand sucks. We want to make 
> this easier. What if we could use xpath to match bits of the output, and 
> then replace these with something else? That would allow us to do 
> something similar to what you do with macros and slots in Zope, but 
> without actually having to define any slots. You basicaly just have 
> fill-slot. :) What would that look like in grok terms?
> 
> What if we had views that didn't apply to a model, but instead applied 
> to bits of HTML? The context is *not* a model, but instead a HTML tree.
> 
> Let's imagine that that could look like:
> 
> class TitleChanger(grok.MatchView):
>    grok.skin('some_skin')
>    grok.match('head/title') # xpath expression
> 
>    def render(self):
>       return 'ME GROK ' + self.select('text()').upper()
> 
> Now for all views in the skin 'some_skin', we'd change the title of the 
> page (<head><title>hello</title></head>, for instance) from 'hello' (or 
> whatever it may be) to 'ME GROK HELLO'.
> 
> Of course instead of render, we could use Grok's existing view 
> mechanisms to also use a template to render the content instead. Note 
> the existence of a special 'select' method on the grok.MatchView to 
> select some bits (in the current context).
> 
> I imagine a matcher could also be used to insert viewlets and so on. 
> Using a template would be something like this:
> 
> class BoxInserter(grok.MatchView):
>    grok.skin('some_skin')
>    grok.match('some_section')
> 
> and then in boxinserter.pt
> 
> <div name="mybox">
> This is a box with arbitrary stuff, like the weather, or a stock ticker, 
> or whatever.
> </div>
> <tal:block replace="python:view.select('*|text()')" />
> 
> (or should we allow context.select() here?)
> 
> This would insert the box and then put in the original content (this is 
> what select('*|text()') should do. This is modeled after Genshi match 
> templates, but associated with skins instead of included in a document. 
> This is a document describing Genshi match templates:
> 
> http://genshi.edgewall.org/wiki/Documentation/xml-templates.html#id5
> 
> When you write templates while building your application, you make can 
> life easier for those trying to theme your application by using id= or 
> name= on tags in your HTML. Those are easier for a theme writer to match.
> 
> The nice thing about this design however is that you don't really have 
> to: we actually put no requirements on the template writer at all, and 
> it could still be themed (with a bit more of a hassle). This means that 
> we put very little burden on the application developer - a positive 
> compared to the requirement to define the same slots everywhere.
> 
> The standard skin postprocessing logic would apply all the MatchViews 
> associated to that skin. We need to think a bit about application order 
> here - what if multiple expressions match? What if we have a skin that 
> inherits from another one? Is there a way to disable this for a skin and 
> use a different set of matchers altogether?
> 
> If this approach works, there are also some performance aspects we need 
> to think about (how many parsing/serialization steps are necessary in 
> order to make this work? is self.select() efficient enough on a view, or 
> can we
> 
> Note that this mechanism is inspired by Genshi's match templates:
> 
> http://genshi.edgewall.org/wiki/Documentation/xml-templates.html#id5
> 
> I think with a bit of luck we can reuse much of Genshi's implementation 
> to come up with a prototype for all this.
> 
> Regards,
> 
> Martijn
> 
> 
> 
> 
> 
> 
> _______________________________________________
> Grok-dev mailing list
> Grok-dev at zope.org
> http://mail.zope.org/mailman/listinfo/grok-dev
> 


More information about the Grok-dev mailing list