The drupal 7 implementation of TWIG template language.
Updated 22-11-2012 for version 7.0.2.0
This project adds TWIG as a second theme engine to drupal 7. This project is mostly intended for those who want to use twig in D7 and are not afraid to move files into the correct place in their drupal installation. Don't worry to much, it's only folder and one file.
Beware this version is not 100% downards compatible with the previous version.
Changes
- Compiled templates are now stored in a private folder. Be sure to set one at
admin/config/media/file-system - Removed the old custom function call node as the functionallity is now
available native in TWIG - Rebuild the way templates are discovered and internally stored.
- Added drupal_static construction to the disk expensive calls.
- Moved the autoloader into the project, patching of the config is not needed.
General information and installation
For more information about Twig, visit http://twig-project.org
To use this engine you will need to clone Twig from git
(https://github.com/fabpot/Twig.git) and manually copy the contents of /lib/Twig
into sites/all/libraries in a folder called Twig
The contents of TFD from this module needs to be moved into libraries as well.
So you end up with a folder structure like this.
sites/all/libraries/Twig/
sites/all/libraries/Twig/Error/
sites/all/libraries/Twig/Extension/ ... etc
sites/all/libraries/TFD/Autoloader.php
sites/all/libraries/TFD/Loader/ ... etc
Then move the twig.engine file into ./themes/engines/twig/ so that is on the
same level as the phptemplate engine drupal comes with.
Usage
To create a theme for twig, simply set engine = twig in your theme.info and
start creating templates
Twig for Drupal 7
The implementation of this Twig engine is purely for Drupal 7. The implementation of Drupal 8 most likely will be different from this!
However most extensions should be compatible with Drupal 8 as they are done as an extension of Twig instead of Drupal.
This is a 100% compatible drop in replacement for the default drupal phptemplate system, with one restriction due to a core bug that is unsolvable at this moment because of the backportability and structural changes between Drupal 7 and 8. You can NOT extend a phptemplate based theme with a twig theme. See http://drupal.org/node/1545964 for more information. However drupal modules that do not provide twig based templates will work as long as the template is within the module folder. If you want to extend or change the modules default template you MUST convert it to a twig template within your theme.
By convention twig for drupal templates are postfixed with .tpl.twig
Installation.
TODO
Usage
My first twig theme.
Declaring a theme as a twig theme works exactly the same as with a phptemplate based theme. Start with creating a folder in your themes structure, add a .info file and a tempate.php if needed. The only difference is, instead of engine = phptemplate you use engine = twig
For examplae
name = Twiggy
description = a cool twig based theme
engine = twig
core = 7.xThere is nothing more needed to use twig as engine.
Twig for Drupal specific extensions
Besides the default twig tags, filters and functions this version comes with a few extra bells and whistles that can make taming the drupal theming layer a bit easier.
Including and extending templates
One of the cool features of twig is the extendability of templates. The behaviour is adapted to drupal using a symfony2 like aproach of resolving templates within the current theme and the base theme if needed. Please note, that loading templates from other themes then the current enabled theme and the base theme for that theme is not supported!
The golden rule to remember is, every '/' in the path from 'templates' is replaced by a ':'
And the base theme is referred by it's machine name seperated by '::'
Asume you have a theme called twiggy that extends mothertwig and all templates are in the /template folder of that theme.
Loading a template from the current theme 'template' folder.
{% include 'node-default.tpl.twig' %}Loading a template from a subfolder called pages as seen from the current theme 'template' folder
{% include 'pages:node-default.tpl.twig' %}Loading a template from the base theme 'template' folder
{% include 'mothertwig::node-default.tpl.twig' %}Loading a template from a subfolder called pages as seen from the base theme.
{% include 'mothertwig::pages:node-default.tpl.twig' %}The template locations are cached, so if you add a new template, the normal routine of clearing the theme cache in drupal is required!
Tags
with
The 'with' tag allows a scope-shift into a defined array. The format is as follows:
The with construct sets the argument as the current context. If an 'as name' is defined,the variable is defined as that local name.
The two flags sandboxed or merged define an additional behaviour, allowing to sandbox the contents of the construct from the current scope (which results in having only the defined variables in the current scope), or merging them with the current scope respectively.
If none of the flags is defined, the current context is stacked in the context as _parent. After the end of the with construct, thecontext is restored.
Usage :
{% with expr [as localName] [, expr2 [as localName2], [....]] {sandboxed|merged} %}
content
{% endwith %}Assume the following context:
array(
'foo' => array(
'name' => 'Foo',
'id' => 1
),
'bar' => array(
'name' => 'Bar',
'id' => 2
)
)
{% with foo %}
{{ id }}: {{ name }} {# would output: "1: Foo" #}
{% endwith %}
{% with foo as baz %}
{{ baz.id }}: {{ baz.name }} {# would output: "1: Foo" #}
{% endwith %}
{% with foo as bar, bar as foo %}
{{ bar.id }}: {{ bar.name }} {# would output: "1: Foo" #}
{{ foo.id }}: {{ foo.name }} {# would output: "2: Bar" #}
{% endwith %}This tag originally was contributed by Gerard van Helden
switch
The 'switch' tag allows a switch case construction in your template. By default the case constructs are not fallthrough, which means that there is no need to break from a switch like you normally would in PHP. You can use the fallthrough parameter at a case to overrule the default breaking behaviour. Default is the non obligatory catch all constructor. The case constructor is case sensitive!
{% switch bar %}
{% case 'beer' %}
Here is a cold beer.
{% case 'water' %}
The tap is in the toilet
{% case 'howdie' fallthrough %}
Hello stranger,
{% case 'hello' %}
What do you want to drink?
{% default %}
say what?
{% endswitch %}'beer','water' and 'hello' produce a single line, yet 'howdie' produces both the outcome of the 'howdie' and 'hello'
And if bar is empty or none of the other case, it will produce 'say what?'
This tag originally was contributed by Gerard van Helden
unset
Unset removes a variable from the current context, it is the opposite of the twig set tag.
Usefull if you assigned a local scope set and want to remove that for later use in the template
{% set foo = 'bar' %}
do something with foo
{% unset(foo) %}Filters
defaults
Returns a merged array of defaults upon the variable the filter is called.
{{ foo|defaults({'bar','baz'}) }}dump()
dumps the variable in a smart way. The dump filter checks if the 'devel' module is loaded, and if so by default does a kpr($variable); else it uses the PHP var_dump($variable)
However you can specify the method to use by a single parameter.
Default PHP:
- dump('v') maps to var_dump();
- dump('p') maps to print_r();
Devel module powered:
- dump('k') maps to kpr();
- dump('r') maps to dpr();
- dump('d') maps to dpm();
Please note, by using a parameter the method is forced, yet checked if callable. So using dump('d') when the devel module is not enabled will not fallback and will print nothing.
url
Maps a variable to the drupal url() and checkurl() methods. When the variable is numeric, it is asumed to be a node and prepended by /node/ before passing it to the checkurl() method.
<a href="{{node.nid|url}}">click me</a>will produce a fully path_auto extended url to the given node.
<a href="{{'<front>'|url}}">home</a>will produce a link to the frontpage.
See the drupal documentation for the options you can use as parameters.
t
A one to one mapping to the drupal t() method.
ucfirst
A one to one mapping to the php ucfirst() method.
children
Removes al the '#meta' data from the first level of a 'render array of doom'.
date_format
A strongly discouraged in favour of a proper field fieldformatter unix timestamp to date filter.
Usage: |date_format('format','method').
Where method is either date or strftime and the format is a apropriate format for the used function.
Defaults to strftime('%d-%m-%Y %H:%M')
Functions
render
A 'smarter' render function
If you want to render just the contents of a single value field, you normally would do
something like this {{ r(field.foobar[0]) }} because of the field wrappers.
But typing the extra [0] can be a bit a over-verbose.
This render method checks if the array you pass has a single [0] key, and if so pass that to the render() method.
in any other case the whole array is passed trough.
For extra RSI preventing convenience a shortcut version {{ r() }} is available to.
hide
A 100% mapped version of the drupal hide() method. Unlike the above mentioned render this function does nothing smart.
For extra RSI preventing convenience a shortcut version {{ h() }} is available to.
dump
The function equivalent of the above documented dump filter. Parameter and usage are the same.
{{ dump(var,'method') }}flatten
Flattens a typical drupal select field array into a more usable format.
input = array( 0 => array ('value' => 1),
1 => array ('value' => 4)
)
output = array(0 => 1 ,1 => 4)theme_get_setting
A one on one mapping to the drupal theme_get_setting(); method.
module_exists
A one on one mapping to the drupal module_exists(); method.
Tests
Test are mostly used in {% if constraints %}
property
Checks if the given property excists in a 'render array of doom'.
$var = array("#theme" => 'aaaa')
{% if var is property('theme') %}
do something
{% endif %}numeric
checks if the given value is a number.
convenience mapping
For developers convenience, the twig default binary or & and are mapped to || & && however the original style of using them in a if function is strongly encouraged.
HOOKS (API)
All drupal hooks are called upon rebuilding of theme registry and thus on the 'cache clear all' call in Drupal
hook_twig_token
Add your own tokens (tags) to the tokenparser. These new tokens should extend the Twig_TokenParser.
In most circumstances you will actually never extend the tokenparser as this is a language construct rather then extending display logic.
Also
See twig documentation for more information on creating your own tokenparser.
module_twig_token($token){
$token[] = new My_Twig_Token();}