[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