FreeMarker Plugin
FreeMarker template parser.
FreeMarker is a "template engine"; a generic tool to generate text output (anything from HTML to autogenerated source code) based on templates.
http://freemarker.sourceforge.net/
Why another template language?
FreeMarker can be used next to Foswiki templates:
- FreeMarker has powerful features like if/else conditionals, list functions, scalar, array and hash variables, calculations and string manipulations. Especially when combined with Foswiki macros these are very powerful.
- The template markup can be used in plain topics without using template files or template topics.
- Developers can use the parser to create application frontends. A good example is the configure interface of Foswiki 1.1 that uses FreeMarker templates.
FreeMarker Plugin uses a parser written in Perl using CPAN:Parse::Yapp. The Yapp module is compiled with the parser code so you don't need to install anything else.
Feed the plugin some template text, optionally pass data, and get the combined result:
Plugin syntax
%STARTFREEMARKER{ params }%
template
%ENDFREEMARKER%
-
params
contains the data object to pass to the template. See Passing data to the template.
-
template
is the FreeMarker template text that contains FreeMarker syntax
FreeMarker syntax
FreeMarker plugin understands the most usable FreeMarker syntax -
see below for a list.
FreeMarker syntax is extensively documented at
http://freemarker.sourceforge.net/docs/
If you are new to FreeMarker, these examples may give you an idea.
Conditionally write a headline with variable size:
<#if headline??>
<h${depth}>${headline}</h${depth}>
</#if>
Combining lists:
<#list users + admins + children as person>
* ${person}
</#list>
Using data in the template:
<#assign names=["John", "Ashley"]>
${names[0]}, ${names[1]}
Conditionally writing blocks of text:
<#assign localNoUpdate>
<h2>Nothing to save - you haven't made any changes yet</h2>
</#assign>
<#assign localUpdate>
<h2>You are about to update
<#if modifiedCount == 1>
one setting
<#else>
${modifiedCount} settings
</#if>
</h2>
</#assign>
<#if modifiedCount == 0>
${localNoUpdate}
<#else>
${localUpdate}
</#if>
Passing data to the parser
Any data you pass as macro parameters is converted to a Perl object. For example, with:
%STARTFREEMARKER{ x="[one,two,three]" }%
x
is now an array with 3 strings.
You can pass strings, numbers, arrays and hashes. But note: data keys cannot be one of the reserved keywords. All words under string and array operations are reserved. For example,
${size}
is illegal syntax; use
${headersize}
instead.
In a template, strings must always be quoted; in macro parameters, quotes are optional.
Passing an array:
%STARTFREEMARKER{users="[Ashton, Bonny, Zeta]"}%
Passing a hash:
%STARTFREEMARKER{ x="{name:Muriel, age:24}" }%
Passing an array of hashes:
%STARTFREEMARKER{vars="[{color:red, value:255},{color:green, value:100}]"}%
Passing hashes that contain arrays:
%STARTFREEMARKER{
vars="{
users : [ Joe, Fred ],
admins : [ Julia, Kate ],
children : [ Mickey, Rooney ]
}"}%
Using data in templates
Iterating over an array:
%STARTFREEMARKER{users="[Ashton, Bonny, Zeta]"}%
<#list users as x>
- ${x}
</#list>
%ENDFREEMARKER%
Iterating over an array that has been created by
SEARCH
(the search macro has been escaped to postpone it's rendering):
%STARTFREEMARKER{
topics="[$percntSEARCH{$quot*$quot web=$quotSystem$quot format=$quot$topic$quot nonoise=$quoton$quot separator=$quot,$quot limit=$quot20$quot}$percnt]"
}%
<noautolink>
<#list topics as topic>
- ${topic}
</#list>
</noautolink>
%ENDFREEMARKER%
Combining arrays:
%STARTFREEMARKER{
vars="{
users : [ Joe, Fred ],
admins : [ Julia, Kate ],
children : [ Mickey, Rooney ]
}"}%
<#list vars.users + vars.admins + vars.children as person>
- ${person}
</#list>
%ENDFREEMARKER%
Retrieving a hash value:
%STARTFREEMARKER{
vars="[{color:red, value:255},{color:green, value:100}]"}%
${vars[1].color}
%ENDFREEMARKER%
Elaborate example
See
FreeMarkerPluginExamples for working examples.
Implemented syntax
- Directives:
- If-statements
<#if condition > ...<#elseif condition > ... <#else> ... </#if>
- Assignments:
<#assign var=value>
and <#assign var> value </#assign>
- List iterator:
<#list sequence as item> ... </#list>
- Comments:
<#-- ... -->
- Variable substitution:
${variable_name}
- String operations:
cap_first
, capitalize
, eval
, html
, length
, lower_case
, replace
, string
, substring
, uncap_first
, upper_case
, word_list
, xhtml
- Array operations:
first
, join
, last
, reverse
, seq_contains
, seq_index_of
, size
, sort
, sort_by
- Function calls
Not implemented
- Dates, date operations
- User-defined directives
- Nodes, node operations
- switch, case, default, break
- break
- include
- import
- noparse
- compress
- escape, noescape
- global
- local
- setting
- nested, return
- function, return
- flush
- stop
- t, lt, rt
- nt
- attempt, recover
- visit, recurse, fallback
Test
Assignment
%STARTFREEMARKER{}%
<verbatim>
<#assign names=["John", "Ashley"]>
${names[0]}, ${names[1]}
</verbatim>
%ENDFREEMARKER%
If installed, results in:
%STARTFREEMARKER{}%
<#assign names=["John", "Ashley"]>
${names[0]}, ${names[1]}
%ENDFREEMARKER%
Condition
%STARTFREEMARKER{headline="Welcome!" depth="4" class="foswikiHelp"}%
<#if headline??>
<h${depth} class="${class}">${headline}</h${depth}>
</#if>
%ENDFREEMARKER%
If installed, results in:
%STARTFREEMARKER{headline="Welcome!" depth="4" class="foswikiHelp"}%
<#if headline??>
<h${depth} class="${class}">${headline}</h${depth}>
</#if>
%ENDFREEMARKER%
Replace
%STARTFREEMARKER{}%
${"Good morning, mister Vain"?replace("Good morning","Guten Morgen")}
%ENDFREEMARKER%
If installed, results in:
%STARTFREEMARKER{}%
${"Good morning, mister Vain"?replace("Good morning","Guten Morgen")}
%ENDFREEMARKER%
Troubleshooting
If nothing gets rendered between
STARTFREEMARKER
and
ENDFREEMARKER
, look for any syntax errors.
- Check for
<#/if>
that should be </#if>
.
- Check for unclosed or non-matching closing tags
- Turn on
FREEMARKERPLUGIN_DEBUG
- the parser output is written to the webserver error log, the plugin output to Foswiki's debug log
- Perhaps you have used one of the reserved keywords (words listed in string and array operations cannot be used for data keys)
Background
For developers
Cautionary note
This plugin is an approximation of the results that the FreeMarker Java code produces. I haven't found a specification for the language, and the documentation leaves room for interpretation. So some things will be different, some things may not be right. Please visit
http://foswiki.org/Support/FreeMarkerPlugin if you think this plugin can be improved.
Parsing text
Create a parser object and feed it some text:
my $template = 'some text with FreeMarker syntax: ${10 + 2 * 3}';
my $parser = new Foswiki::Plugins::FreeMarkerPlugin::FreeMarkerParser();
my $parsed = $parser->parse( $template );
Parsing text and data
Create a parser object and feed it text plus a data object. FreeMarker understands scalars, arrays and hashes, and this plugin assumes you pass the objects by reference.
my $vars = {
users => [ 'Joe', 'Fred' ],
admins => [ 'Julia', 'Kate' ],
children => [ 'Mickey', 'Rooney' ]
};
my $template = '<#list users + admins + children as person>
* ${person}
</#list>';
my $parser = new Foswiki::Plugins::FreeMarkerPlugin::FreeMarkerParser();
my $parsed = $parser->parse( $template, $vars );
my $data = $parser->{data};
The
$parser->{data}
object contains the input data transformed by the parser.
Calling functions from FreeMarker
sub func {
my ($str) = @_;
# do something
}
my $template = '${doFunc("hello!")}';
my $parser = new Foswiki::Plugins::FreeMarkerPlugin::FreeMarkerParser();
my $parsed = $parser->parse($template, {
doFunc => \&func,
});
Working with the parsers
You need to install
CPAN:Parse::Yapp. See the source code for compile instructions.
Debugging
Turn on Parse::Yapp debugging with
debugLevel
:
my $parser = new Foswiki::Plugins::FreeMarkerPlugin::FreeMarkerParser();
$parser->{debugLevel} = 0x1F;
Error level values:
Bit Value Outputs
0x01 Token reading (useful for Lexer debugging)
0x02 States information
0x04 Driver actions (shifts, reduces, accept...)
0x08 Parse Stack dump
0x10 Error Recovery tracing
Additional debugging statements can be turned on with
debug
:
my $parser = new Foswiki::Plugins::FreeMarkerPlugin::FreeMarkerParser();
$parser->{debug} = 0x1F;
Plugin Preferences
- To enable debugging, set DEBUG to 1 (not in this topic, use Main.SitePreferences for example):
- Set FREEMARKERPLUGIN_DEBUG =
Installation
You do not need to install anything in the browser to use this extension. The following instructions are for the administrator who installs the extension on the server.
Open configure, and open the "Extensions" section. Use "Find More Extensions" to get a list of available extensions. Select "Install".
If you have any problems, or if the extension isn't available in
configure
, then you can still install manually from the command-line. See
http://foswiki.org/Support/ManuallyInstallingExtensions for more help.
Plugin Info