Skip to main content

Chameleon page template support for Grok

Project description

megrok.chameleon

megrok.chameleon makes it possible to use chameleon templates in Grok.

Currently support for chameleon genshi templates and chameleon zope page templates is provided.

For more information on Grok and Chameleon templates see:

Requirements

  • Chameleon templates (Chameleon).
  • Chameleon genshi templates (chameleon.genshi).
  • Grok v1.0a1 or later, or five.grok 1.0 or later.

Installation

To use Chameleon page templates with Grok all you need is to install megrok.chameleon as an egg and include its ZCML. The best place to do this is to make megrok.chameleon a dependency of your application by adding it to your install_requires list in setup.cfg. If you used grokproject to create your application setup.py is located in the project root. It should look something like this:

install_requires=['setuptools',
                  'megrok.chameleon',
                  # Add extra requirements here
                  ],

Then include megrok.chameleon in your configure.zcml. If you used grokproject to create your application it’s at src/<projectname>/configure.zcml. Add the include line after the include line for grok, but before the grokking of the current package. It should look something like this:

<include package="grok" />
<include package="megrok.chameleon" />
<grok:grok package="." />

If you use autoInclude in your configure.zcml, you should not have to do this latter step.

Then run bin/buildout again. You should now see buildout saying something like:

Getting distribution for 'megrok.chameleon'.
Got megrok.chameleon 0.5.

That’s all. You can now start using Chameleon page templates in your Grok application.

Usage

megrok.chameleon supports the Grok standard of placing templates in a templates directory, for example app_templates, so you can use Chameleon page templates by simply placing the Chameleon genshi templates or Chameleon Zope page templates in the templates directory, just as you would with regular ZPT templates. Although chameleon templates themselves do not have a standard for the file extensions for templates, Grok needs to have an association between an extension and a type so it knows which type of template each template is. megrok.chameleon defines the following extensions:

  • .cpt (Chameleon page template) for Chameleon page templates
  • .cg (Chameleon genshi template) for chameleon driven genshi templates
  • .cgt (Chameleon genshi text template) for chameleon driven genshi text templates

You can also use Chameleon page templates inline. The syntax for this is:

from megrok.chameleon.components import ChameleonPageTemplate
index = ChameleonPageTemplate('<html>the html code</html>')

Or if you use files:

from megrok.genshi.components import ChameleonPageTemplateFile
index = ChameleonPageTemplateFile(filename='thefilename.html')

Detailed Description

Grok-support for using chameleon driven templates.

With megrok.chameleon you can use templates parsed and rendered by Chameleon. Currently Zope page templates and Genshi templates are supported.

Chameleon Zope page templates

Chameleon provides support for Zope page templates which can be used from grok writing templates with the .cpt (=Chameleon Page Template) filename extension.

Chameleon page templates differ from standard Zope page templates in a few aspects, most notably:

  • Expressions are parsed in Python-mode by default. This means, instead of tal:content="view/value" you must use tal:content="view.value". Every occurence of TAL-expressions starting with python: now can be shortened by skipping this marker.
  • Also genshi-like variable substitutions are supported. For example you can write ${myvar} instead of tal:content="myvar".

Beside this, most rules for regular Zope page templates apply also to chameleon page templates.

See the Chameleon page for more information.

Prerequisites

Before we can see the templates in action, we care for correct registration and set some used variables:

>>> import os
>>> testdir = os.path.join(os.path.dirname(__file__), 'tests')
>>> cpt_fixture = os.path.join(testdir, 'cpt_fixture')
>>> template_dir = os.path.join(cpt_fixture, 'app_templates')

We register everything. Before we can grok our fixture, we have to grok the megrok.chameleon package. This way the new template types are registered with the framework:

>>> import grokcore.view
>>> grokcore.view.testing.grok('megrok.chameleon')
>>> grokcore.view.testing.grok('megrok.chameleon.tests.cpt_fixture')

We create a mammoth, which should provide us a bunch of chameleon page template driven views and put it in the database to setup location info:

>>> from megrok.chameleon.tests.cpt_fixture.app import Mammoth
>>> manfred = Mammoth()
>>> getRootFolder()['manfred'] = manfred

Furthermore we prepare for getting the different views on manfred:

>>> from zope.publisher.browser import TestRequest
>>> from zope.component import getMultiAdapter
>>> request = TestRequest()

Simple templates

We prepared a plain cavepainting view. The template looks like this:

>>> cavepainting_cpt = os.path.join(template_dir, 'cavepainting.cpt')
>>> print open(cavepainting_cpt, 'rb').read()
<html>
  <body>
    A cave painting.
  </body>
</html>

The rendered view looks like this:

>>> view = getMultiAdapter((manfred, request),
...                         name='cavepainting')
>>> print view()
<html>
  <body>
    A cave painting.
  </body>
</html>

Substituting variables

A template can access variables like view, context and its methods and attributes. The food view does exactly this. The template looks like this:

>>> food_cpt = os.path.join(template_dir, 'food.cpt')
>>> print open(food_cpt, 'rb').read()
<html>
<body>
<span tal:define="foo 'a FOO'">
${view.me_do()}
<span tal:replace="structure view.me_do()" />
CSS-URL: ${static['test.css']()}
My context is: ${view.url(context)}
${foo}
<span tal:replace="foo" />
</span>
</body>
</html>

The rendered view looks like this:

>>> view = getMultiAdapter((manfred, request), name='food')
>>> print view()
<html>
<body>
<span>
&lt;ME GROK EAT MAMMOTH!&gt;
<ME GROK EAT MAMMOTH!>
CSS-URL: http://127.0.0.1/@@/megrok.chameleon.tests.cpt_fixture/test.css
My context is: http://127.0.0.1/manfred
a FOO
a FOO
</span>
</body>
</html>

As we can see, there is a difference between Genshi-like substitution and TAL-like substitution: while both expressions:

${view.me_do()}

and:

<span tal:replace="view.me_do()" />

actually render the same string <ME GROK EAT MAMMOTH!>, the former does this straight and plain, while the latter performs additionally HTML-encoding of the string. Therefore the output of both expressions differ. It’s:

<ME GROK EAT MAMMOTH!>

for the former expression and:

&lt;ME GROK EAT MAMMOTH!&gt;

for the latter.

Supported variables

Each template provides at least the following vars:

  • template
    the template instance
  • view
    the associated view
  • context
    the context of the view
  • request
    the current request
  • static
    the static dir of the application

as we can see, when we look at the vars.cpt from our fixture:

>>> cpt_file = os.path.join(template_dir, 'vars.cpt')
>>> print open(cpt_file, 'rb').read()
<html>
<body>
This template knows about the following vars:
<BLANKLINE>
  template (the template instance):
   ${template}
<BLANKLINE>
  view (the associated view):
   ${view}
<BLANKLINE>
  context (the context of the view):
   ${context}
<BLANKLINE>
  request (the current request):
   ${request}
<BLANKLINE>
  static (the static dir of the application):
   ${static}
</body>
</html>

and render it:

>>> view = getMultiAdapter((manfred, request), name='vars')
>>> print view()
<html>
<body>
This template knows about the following vars:
<BLANKLINE>
  template (the template instance):
   &lt;vars template in ...vars.cpt&gt;
<BLANKLINE>
  view (the associated view):
   &lt;megrok.chameleon.tests.cpt_fixture.app.Vars object at 0x...&gt;
<BLANKLINE>
  context (the context of the view):
   &lt;megrok.chameleon.tests.cpt_fixture.app.Mammoth object at 0x...&gt;
<BLANKLINE>
  request (the current request):
   CONTENT_LENGTH:  0
GATEWAY_INTERFACE:  TestFooInterface/1.0
HTTP_HOST:  127.0.0.1
SERVER_URL: http://127.0.0.1
<BLANKLINE>
  static (the static dir of the application):
   &lt;grokcore.view.components.DirectoryResource object at 0x...&gt;
</body>
</html>

Inline Templates

We can also define inline templates. In our app.py we defined an inline template like this:

from megrok.chameleon import components

...

class Inline(grokcore.view.View):
  sometext = 'Some Text'

inline = components.ChameleonPageTemplate(
    "<html><body>ME GROK HAS INLINES! ${view.sometext}</body></html>")

If we render this view we get:

>>> view = getMultiAdapter((manfred, request), name='inline')
>>> print view()
<html><body>ME GROK HAS INLINES! Some Text</body></html>

TAL expressions

Starting with megrok.chameleon 0.5 we deploy the all-in-one Chameleon package.

What TAL/TALES expressions in templates are supported depends mainly from the installed version of Chameleon, while we support some additional, Zope-related TALES expressions.

A list of all supported expressions and statements can be found at the chameleon.zpt documentation. The additional TALES expressions provided by megrok.chameleon are:

  • exists
    Tell whether a name exists in the templates’ namespace.
  • not
    Evaluate the trailing expression to a boolean value and invert it.
  • path
    Handle the trailing expression as a path and not as a Python expression.
  • provider
    Support for viewlet providers.

Warning

z3c.pt support has been dropped with megrok.chameleon 0.5.

Note

Starting with megrok.chameleon 0.5 support for the Python expression exists() has been dropped. The TALES expression exists: path/to/something is still available.

In our app.py we defined a special view for showing some special expressions. This also includes a viewlet:

import grok
from megrok.chameleon import components

class Mammoth(grok.Application, grok.Container):
    pass

...

class Expressions(grok.View):
    pass

class MainArea(grok.ViewletManager):
    grok.name('main')

class MainContent(grok.Viewlet):
    grok.view(Expressions)
    grok.viewletmanager(MainArea)
    def render(self):
        return 'Hello from viewlet'

Now we can make use of the TALES expressions not:, path:, exists: and provider: in the expressions.cpt template of our fixture:

>>> cpt_file = os.path.join(template_dir, 'expressions.cpt')
>>> print open(cpt_file, 'rb').read()
<html>
<body>
  <div tal:define="food 'Yummy Dinoburger'"
       tal:omit-tag="">
    <!-- We support `exists` -->
    <div tal:condition="exists: food">
      ${food}
    </div>
<BLANKLINE>
    <!-- We support `not` -->
    <div tal:content="not: food" />
    <div tal:content="not('food')" />
    <div tal:content="not: 1 in [2,3]" />
    <div tal:content="not: not: food" />
<BLANKLINE>
    <!-- We support `path` -->
    <div tal:content="path: food/upper" />
<BLANKLINE>
    <!-- We support `provider` -->
    <tal:main content="structure provider:main" />
<BLANKLINE>
  </div>
</body>
</html>

and render it:

>>> view = getMultiAdapter((manfred, request), name='expressions')
>>> print view()
<html>
<body>
<BLANKLINE>
    <!-- We support `exists` -->
    <div>
      Yummy Dinoburger
    </div>
<BLANKLINE>
    <!-- We support `not` -->
    <div>False</div>
    <div>False</div>
    <div>True</div>
    <div>True</div>
<BLANKLINE>
    <!-- We support `path` -->
    <div>YUMMY DINOBURGER</div>
<BLANKLINE>
    <!-- We support `provider` -->
    Hello from viewlet
<BLANKLINE>
<BLANKLINE>
</body>
</html>

Macros

With megrok.chameleon we can also use macros, although it is a bit different from regular Zope page templates.

We can define macros like this:

>>> cpt_file = os.path.join(template_dir, 'macromaster.cpt')
>>> print open(cpt_file, 'rb').read()
<p xmlns:metal="http://xml.zope.org/namespaces/metal"
   metal:define-macro="hello">
  Hello from <b metal:define-slot="name">macro master</b>
</p>

The defined macro hello can be rendered in another Chameleon template with the METAL attribute use-macro.

To refer to a local macro, i.e. a macros defined in the same template, you can use something like:

<div metal:use-macro="template.macros['<macro-name>']">
  Replaced by macro
</div>

where <macro-name> must be an existing macro name.

To refer to macros in external templates, you must use the path: expression like this:

<div metal:use-macro="path:
  context/@@<viewname>/template/macros/<macro-name>">
   Replaced by external macro
</div>

where <viewname> refers to an existing view on context and macro-name again refers to an existing macro in the specified template.

Note, that this is different from how you refer to macros in standard Zope page templates. The short notation view/macros/<macro-name> works only with regular Zope page templates.

The following template makes use of both methods:

>>> cpt_file = os.path.join(template_dir, 'macrouser.cpt')
>>> print open(cpt_file, 'rb').read()
<html xmlns:metal="http://xml.zope.org/namespaces/metal">
<body>
  <p metal:define-macro="hello">
    Hi there from macro user!
  </p>
  <div metal:use-macro="template.macros['hello']">
    Fill this
  </div>
<BLANKLINE>
  <div metal:use-macro="path: context/@@macromaster/template/macros/hello">
    <b metal:fill-slot="name">user slot</b>
    Fill this too
  </div>
</body>
</html>

When rendered also the slot defined in the master template is filled by macro user content:

>>> cpt_file = os.path.join(template_dir, 'macrouser.cpt')
>>> view = getMultiAdapter((manfred, request), name='macrouser')
>>> print view()
<html>
<body>
  <p>
    Hi there from macro user!
  </p>
  <p>
    Hi there from macro user!
  </p>
<BLANKLINE>
<BLANKLINE>
  <p>
  Hello from <b>user slot</b>
<BLANKLINE>
</p>
</body>
</html>

Clean up:

>>> del getRootFolder()['manfred']

Differences from regular Zope page templates

  • Macros are referenced differently. See appropriate section above.
  • Expressions are parsed in Python-mode by default. This means, instead of tal:content="view/value" you must use tal:content="view.value". Every occurence of TAL-expressions starting with python: now can be shortened by skipping this marker.

Chameleon Genshi templates

Chameleon provides support for Genshi templates which can be used from grok writing templates with the .cg filename extension.

Genshi text templates can be used with the .cgt filename extension.

Note, that chameleon genshi templates might not cover the full range of functionality offered by native genshi parsers. Use megrok.genshi if you want native genshi support.

See the chameleon.genshi page for more information.

Prerequisites

Before we can see the templates in action, we care for correct registration and set some used variables:

>>> import os
>>> testdir = os.path.join(os.path.dirname(__file__), 'tests')
>>> genshi_fixture = os.path.join(testdir, 'genshi_fixture')
>>> template_dir = os.path.join(genshi_fixture, 'app_templates')

We register everything. Before we can grok our fixture, we have to grok the megrok.chameleon package. This way the new template types are registered with the framework:

>>> grokcore.view.testing.grok('megrok.chameleon')
>>> grokcore.view.testing.grok('megrok.chameleon.tests.genshi_fixture')

We create a mammoth, which should provide us a bunch of Genshi driven views and put it in the database to setup location info:

>>> from megrok.chameleon.tests.genshi_fixture.app import Mammoth
>>> manfred = Mammoth()
>>> getRootFolder()['manfred'] = manfred

Furthermore we prepare for getting the different views on manfred:

>>> from zope.publisher.browser import TestRequest
>>> from zope.component import getMultiAdapter
>>> request = TestRequest()

Simple templates

We prepared a plain cavepainting view. The template looks like this:

>>> cavepainting_cg = os.path.join(template_dir, 'cavepainting.cg')
>>> print open(cavepainting_cg, 'rb').read()
<html>
  <body>
    A cave painting.
  </body>
</html>

The rendered view looks like this:

>>> view = getMultiAdapter((manfred, request),
...                         name='cavepainting')
>>> print view()
<html>
  <body>
    A cave painting.
  </body>
</html>

Substituting variables

A template can access variables like view, context and its methods and attributes. The food view does exactly this. The template looks like this:

>>> food_cg = os.path.join(template_dir, 'food.cg')
>>> print open(food_cg, 'rb').read()
<html>
<body>
${view.me_do()}
CSS-URL: ${static['test.css']()}
My context is: ${view.url(context)}
</body>
</html>

The rendered view looks like this:

>>> view = getMultiAdapter((manfred, request), name='food')
>>> print view()
<html>
<body>
ME GROK EAT MAMMOTH!
CSS-URL: http://127.0.0.1/@@/megrok.chameleon.tests.genshi_fixture/test.css
My context is: http://127.0.0.1/manfred
</body>
</html>

Including other templates

With genshi support we can also include other templates. The gatherer view looks like this:

>>> gatherer_cg = os.path.join(template_dir, 'gatherer.cg')
>>> print open(gatherer_cg, 'rb').read()
<html xmlns:xi="http://www.w3.org/2001/XInclude">
<body>
ME GROK GATHER BERRIES!
<xi:include href="berries.cg"/>
</body>
</html>

Apparently here we include a template called berries.cg. It looks like this:

>>> berries_cg = os.path.join(template_dir, 'berries.cg')
>>> print open(berries_cg, 'rb').read()
<strong>Lovely blueberries!</strong>

When we render the former template, we get:

>>> view = getMultiAdapter((manfred, request), name='gatherer')
>>> print view()
<html>
<body>
ME GROK GATHER BERRIES!
<strong>Lovely blueberries!</strong>
</body>
</html>

Text templates

Also genshi text templates are supported. We have a template that looks like so:

>>> hunter_cgt = os.path.join(template_dir, 'hunter.cgt')
>>> print open(hunter_cgt, 'rb').read()
ME GROK HUNT ${view.game}!

Note, that this template has the .cgt (= cameleon genshi text template) file extension.

If we render it, all expressions are substituted:

>>> view = getMultiAdapter((manfred, request), name='hunter')
>>> print view()
ME GROK HUNT MAMMOTH!!

CHANGES

0.5.2 (2010-07-19)

  • Remove dependency on chameleon.genshi as this is now included into Chameleon itself.

0.5.1 (2010-05-20)

  • Made registering of ITranslatorExpressions conditional: if z3c.pt is installed as well, we don’t register our own ones. Should fix DuplicationError when using megrok.chameleon together with other packages that require z3c.pt like z3c.form and dependent packages from grok-ecosphere.
  • Get rid of zope.testing, zope.app.testing and z3c.testsetup for tests.

0.5 (2010-03-03)

  • Added tests to show usage of macros with megrok.chameleon.

  • Removed dependency from z3c.pt by copying the relevant bits over and registering them locally.

    Drop support for exists('varname') expressions. The regular TALES expression exists: varname/path can still be used.

  • Switch to use Chameleon instead of chameleon.* packages.

0.4 (2010-02-23)

  • Declared megrok as namespace package.
  • Fixed order of includes in ftesting.zcml.
  • Moved pure test-requirements into own setup-section in order to reduce dependencies in regular (non-testing) mode.

0.3 (2010-02-14)

  • Added license file.

0.2 (2009-09-18)

  • Provide macro access from templates.
  • Don’t depend anymore on grok, but only grokcore.view.
  • Added support for path() and exists() in page templates. This was introduced from z3c.pt.
  • Fix ZCML includes.

0.1 (2009-02-22)

  • Initial release

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Files for megrok.chameleon, version 0.5.2
Filename, size File type Python version Upload date Hashes
Filename, size megrok.chameleon-0.5.2.tar.gz (20.7 kB) File type Source Python version None Upload date Hashes View

Supported by

Pingdom Pingdom Monitoring Google Google Object Storage and Download Analytics Sentry Sentry Error logging AWS AWS Cloud computing DataDog DataDog Monitoring Fastly Fastly CDN DigiCert DigiCert EV certificate StatusPage StatusPage Status page