TIP FreeMarkerPlugin is not installed on Foswiki.org.

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:

basic scheme.png

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.

Data formats

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

Author: Foswiki:Main.ArthurClemens
Copyright: © 2010, Arthur Clemens
License: GPL (GNU General Public License)
Release: 1.1.1
Version: 10302 (2010-12-13)
Change History:  
04 Dec 2010 1.1.0: Many improvements to the parser. Support for macro tag.
03 Oct 2010 1.0.1: Fixed bug where a variable name that contained a string operation command resulted in a syntax error.
12 May 2010 1.0.0: First release
Home: http://foswiki.org/Extensions/FreeMarkerPlugin
Support: http://foswiki.org/Support/FreeMarkerPlugin

Topic revision: r5 - 06 Mar 2011, KipLubliner
The copyright of the content on this website is held by the contributing authors, except where stated elsewhere. See Copyright Statement. Creative Commons License    Legal Imprint    Privacy Policy