package FixMyStreet::Template; use parent Template; use strict; use warnings; use FixMyStreet; use mySociety::Locale; use Attribute::Handlers; my %FILTERS; my %SUBS; # HASH is where we want to store the thing, either FILTERS or SUBS. SYMBOL is a # symbol table ref, where NAME then returns its name (perlref has the gory # details). FN is a ref to the function. DATA is an arrayref of any passed in # arguments. sub add_attr { my ($hash, $symbol, $fn, $data) = @_; my $name = $data ? $data->[0] : *{$symbol}{NAME}; $hash->{$name} = $fn; } # Create two attributes, Filter and Fn, which you apply to a function to turn # them into a template filter or function. You can optionally provide an argument # name for what to call the thing in the template if it's not the same as the # function name. They're called at the BEGIN stage rather than the default CHECK # as this code might be imported by an eval. sub Filter : ATTR(CODE,BEGIN) { add_attr(\%FILTERS, $_[1], $_[2], $_[4]); } sub Fn : ATTR(CODE,BEGIN) { add_attr(\%SUBS, $_[1], $_[2], $_[4]); } sub new { my ($class, $config) = @_; $config->{FILTERS}->{$_} = $FILTERS{$_} foreach keys %FILTERS; $config->{ENCODING} = 'utf8'; $class->SUPER::new($config); } sub process { my ($class, $template, $vars, $output, %options) = @_; $vars->{$_} = $SUBS{$_} foreach keys %SUBS; $class->SUPER::process($template, $vars, $output, %options); } =head2 loc [% loc('Some text to localize', 'Optional comment for translator') %] Passes the text to the localisation engine for translations. =cut sub loc : Fn { return _(@_); } =head2 nget [% nget( 'singular', 'plural', $number ) %] Use first or second string depending on the number. =cut sub nget : Fn { return mySociety::Locale::nget(@_); } =head2 file_exists [% file_exists("web/cobrands/$cobrand/image.png") %] Checks to see if a file exists, relative to the codebase root. =cut sub file_exists : Fn { -e FixMyStreet->path_to(@_); } =head2 html_filter Same as Template Toolkit's html_filter, but escapes ' too, as we don't (and shouldn't have to) know whether we'll be used inbetween single or double quotes. =cut sub html_filter : Filter('html') { my $text = shift; for ($text) { s/&/&/g; s//>/g; s/"/"/g; s/'/'/g; } return $text; } =head2 html_paragraph Same as Template Toolkit's html_paragraph, but converts single newlines into
s too. =cut sub html_paragraph : Filter('html_para') { my $text = shift; my @paras = split(/(?:\r?\n){2,}/, $text); s/\r?\n/
\n/ for @paras; $text = "

\n" . join("\n

\n\n

\n", @paras) . "

\n"; return $text; } 1;