—###############################################################################
#html_template.pm
#Last Change: 2009-01-21
#Copyright (c) 2009 Marc-Seabstian "Maluku" Lucksch
#Version 0.4
####################
#This file is an addon to the Dotiac::DTL project.
#
#html_template.pm is published under the terms of the MIT license, which
#basically means "Do with it whatever you want". For more information, see the
#license.txt file that should be enclosed with the distributions. A copy of
#the license is (at the time of writing) also available at
###############################################################################
package
Dotiac::DTL::Addon::html_template;
use
strict;
use
warnings;
use
Dotiac::DTL::Core;
require
Dotiac::DTL::Tag;
require
Dotiac::DTL::Addon;
require
Dotiac::DTL::Tag::importloop;
our
$VERSION
= 0.4;
my
@oldparser
;
my
$first
;
sub
import
{
push
@oldparser
,
$Dotiac::DTL::PARSER
;
$Dotiac::DTL::PARSER
=
"Dotiac::DTL::Addon::html_template"
;
$Dotiac::DTL::Addon::NOCOMPILE
{
'Dotiac::DTL::Addon::html_template'
}=1;
my
$class
=
shift
;
while
(
my
$a
=
shift
@_
) {
my
$o
=
shift
@_
;
if
(
defined
$o
and
exists
$Dotiac::DTL::Addon::html_template_pure::OPTIONS
{
$a
}) {
$Dotiac::DTL::Addon::html_template_pure::OPTIONS
{
$a
}=
$o
;
}
}
$first
=1;
}
sub
unimport {
$Dotiac::DTL::PARSER
=
pop
@oldparser
;
$first
=0;
}
sub
new {
my
$class
=
shift
;
my
$self
={};
bless
$self
,
$class
;
return
$self
;
}
sub
parse {
my
$self
=
shift
;
my
$template
=
shift
;
my
$pos
=
shift
;
my
$start
=
$$pos
;
my
@end
=
@_
;
my
$found
;
$found
=
shift
@end
if
@end
;
local
$_
;
while
(
$Dotiac::DTL::PARSER
eq __PACKAGE__) {
pos
(
$$template
) =
$$pos
;
if
(
$$template
=~m/[^<\{]*([<\{])/g) {
my
$dtag
=$1;
#Decisive tag :)
my
$pre
=
substr
$$template
,
$start
,
pos
(
$$template
)-
$start
-1;
if
(
$dtag
eq
"{"
) {
my
$n
=
substr
$$template
,
pos
(
$$template
),1;
$$pos
=
pos
(
$$template
)+1;
if
(
$n
eq
"%"
) {
my
$npos
=
index
(
$$template
,
"%}"
,
$$pos
);
die
"Missing closing %} at char $$pos"
if
$npos
< 0;
my
$cont
=
substr
$$template
,
$$pos
,
$npos
-
$$pos
;
$$pos
=
$npos
+2;
$cont
=~s/^\s+//;
$cont
=~s/\s+$//;
my
(
$tagname
,
$param
) =
split
/\s+/,
$cont
,2;
$tagname
=
lc
$tagname
;
$$found
=
$tagname
and
return
Dotiac::DTL::Tag->new(
$pre
)
if
$found
and
grep
{
$_
eq
$tagname
}
@end
;
my
$r
;
eval
{
$r
=
"Dotiac::DTL::Tag::$tagname"
->new(
$pre
,
$param
,
$self
,
$template
,
$pos
);};
if
($@) {
die
"Error while loading Tag '$tagname' from Dotiac::DTL::Tag::$tagname. If this is an endtag (like endif) then your template is unbalanced\n$@"
;
}
if
(
$$pos
>=
length
$$template
) {
$r
->
next
(Dotiac::DTL::Tag->new(
""
));
}
else
{
$r
->
next
(
$self
->parse(
$template
,
$pos
,
@_
));
}
if
(not
$start
or
$first
) {
$first
=0;
return
$r
if
lc
(
$Dotiac::DTL::Addon::html_template_pure::OPTIONS
{default_escape}) eq
"html"
;
return
bless
{
p
=>
""
,
n
=>Dotiac::DTL::Tag->new(
""
),
escape
=>0,
content
=>
$r
},
"Dotiac::DTL::Tag::autoescape"
;
#Autoescape off unless default_escape == html
}
return
$r
;
}
elsif
(
$n
eq
"{"
) {
my
$npos
=
index
(
$$template
,
"}}"
,
$$pos
);
die
"Missing closing }} at char $$pos"
if
$npos
< 0;
my
$cont
=
substr
$$template
,
$$pos
,
$npos
-
$$pos
;
$$pos
=
$npos
+2;
return
Dotiac::DTL::Variable->new(
$pre
,
$cont
,
$self
->parse(
$template
,
$pos
,
@_
));
}
elsif
(
$n
eq
"#"
) {
my
$npos
=
index
(
$$template
,
"#}"
,
$$pos
);
die
"Missing closing #} at char $$pos"
if
$npos
< 0;
my
$cont
=
substr
$$template
,
$$pos
,
$npos
-
$$pos
;
$$pos
=
$npos
+2;
return
Dotiac::DTL::Comment->new(
$pre
,
$cont
,
$self
->parse(
$template
,
$pos
,
@_
));
}
}
else
{
#my $p=
if
(
$$template
=~m/\G(?:!--\s*)?
([\/]?)\s*
[Tt][Mm][Pp][Ll]_((?:[Vv][Aa][Rr])|(?:[Ii][Ff])|(?:[Ee][Ll][Ss][Ee])|(?:[Uu][Nn][Ll][Ee][Ss][Ss])|(?:[Ll][Oo][Oo][Pp])|(?:[Ii][Nn][Cc][Ll][Uu][Dd][Ee]))
\s*(
(?:
(?:
(?:(?:[Dd][Ee][Ff][Aa][Uu][Ll][Tt])|(?:[Ee][Ss][Cc][Aa][Pp][Ee])|(?:[Nn][Aa][Mm][Ee]))
\s*=\s*
)?
(?!-->)(?:(?:
"[^"
>]*")|(?:
'[^'
>]*')|(?:[^\
s
=>]*))\s*
)*
)
(?:--)?>/sgx) {
my
$end
=$1;
my
$tag
=
lc
($2);
my
$content
=$3;
$$pos
=
pos
(
$$template
);
my
$tagname
=
$tag
;
$tagname
=
"end$tag"
if
$end
;
$tagname
=
"endimportloop"
if
$end
and
$tag
eq
"loop"
;
$tagname
=
"endif"
if
$end
and
$tag
eq
"unless"
;
$$found
=
$tagname
and
return
Dotiac::DTL::Tag->new(
$pre
)
if
$found
and
grep
{
$_
eq
$tagname
}
@end
;
my
$t
=
$self
->maketag(
$tag
,
$content
,
$pre
,
$template
,
$pos
);
if
(
$t
) {
if
(
$$pos
>=
length
$$template
) {
$t
->
next
(Dotiac::DTL::Tag->new(
""
));
}
else
{
$t
->
next
(
$self
->parse(
$template
,
$pos
,
@_
));
}
if
(not
$start
or
$first
) {
$first
=0;
return
$t
if
lc
(
$Dotiac::DTL::Addon::html_template_pure::OPTIONS
{default_escape}) eq
"html"
;
return
bless
{
p
=>
""
,
n
=>Dotiac::DTL::Tag->new(
""
),
escape
=>0,
content
=>
$t
},
"Dotiac::DTL::Tag::autoescape"
;
#Autoescape off unless default_escape == html
}
return
$t
;
}
else
{
warn
"Couldn't make anything with $tag,$tagname, maybe your template is unbalanced"
;
}
}
else
{
#die;# $p;
$$pos
++;
}
}
}
else
{
$$pos
=
length
$$template
;
return
Dotiac::DTL::Tag->new(
substr
$$template
,
$start
);
}
}
my
$parser
=
$Dotiac::DTL::PARSER
->new();
my
@args
=(
$template
,
$pos
);
push
@args
,
$found
if
$found
;
push
@args
,
@end
if
@end
;
return
$parser
->parse(
@args
);
}
1;
__END__
=head1 NAME
Dotiac::DTL::Addon::html_template - Render combined Django and HTML::Template templates in Dotiac::DTL
=head1 SYNOPSIS
Load in Perl file for all templates:
use Dotiac::DTL::Addon::html_template;
Unload again:
no Dotiac::DTL::Addon::html_template;
Load from a Dotiac::DTL-template (only Dotiac::DTL 0.8 and up)
{% load html_template %}<TMPL_VAR NaME=Foo>....
You also might want make the whole thing case insensitive if the L<HTML::Template> template's need it.
use Dotiac::DTL::Addon::html_template;
use Dotiac::DTL::Addon::case_insensitive;
or in the template ( > Dotiac::DTL 0.8 ):
{% load html_template case_insensitive %}<TMPL_VAR NaME=Foo>....
=head1 INSTALLATION
via CPAN:
perl -MCPAN -e "install Dotiac::DTL::Addon::html_template"
or get it from L<https://sourceforge.net/project/showfiles.php?group_id=249411&package_id=306751>, extract it and then run in the extracted folder:
perl Makefile.PL
make test
make install
=head1 DESCRIPTION
This makes L<Dotiac::DTL> render templates written for L<HTML::Template>. There are four ways to do this:
=head2 Dotiac::DTL::Addon::html_template_pure
This exchanges the parser of Dotiac::DTL with one that can read HTML::Template templates.
It can't render Django Templates anymore, so those will not work.
=head2 Dotiac::DTL::Addon::html_template
This also exchanges the parser, but with one that can read both Django and HTML::Template templates.
This way HTML::Template templates can import/extend Django templates and be imported/extended from them.
B<This means currently working HTML::Template templates can be extended with some Django/Dotiac tags and it will still work like expected>
<!-- Large web project -->
....
<TMPL_IF time><div id="time">{# <TMPL_VAR time> Now Django #}{{ time|date:"Y-m-d H:M" }}</TMPL_IF>
....
But there will be a problem if the HTML::Template template contains not Django {{, {% or {# tags, but this is rarely the case.
=head2 Dotiac::DTL::Addon::html_template::Convert
This replaces HTML::Template and converts the templates before giving them to Dotiac::DTL. It can work with both pure and combined Django/HTML::Template templates.
B<So even here Django and HTML::Template tags can be mixed, and there is just one different line in the script>
# use HTML::Template # Not anymore
use Dotiac::DTL::Addon::html_template::Convert qw/combine/ #Now using Dotiac
See L<Dotiac::DTL::Addon::html_template::Convert>
=head2 Dotiac::DTL::Addon::html_template::Replace
Same as Convert, behaves like HTML::Template to the script, but uses Dotiac::DTL internally. Also supports mixed templates.
This is faster than C<Convert> when using scalarrefs and such as template data.
C<Convert> on the other hand is more stable with filenames, C<Replace> might get confused when using different options on the same template.
See L<Dotiac::DTL::Addon::html_template::Replace>
=head1 OPTIONS
Since Django has no concept of options to a template, there are a few L<HTML::Template> options that can't be ignored:
=head2 filter
This won't work at all, the templates will need to have the filter applied beforehand. There are a lot of things that HTML::Template requires a filter for, but Django supports in a different way, for example: Includes from variables, n-sstage templating (L<Dotiac::DTL::Addon::unparsed>).
=head2 associate
There is also no corresponding thing in Dotiac, but there is an easy solution that almost does the same thing (at least for CGI):
#Perl
my $template=Dotiac::DTL->new(...);
$cgi=new CGI;
$template->param(cgi=>$cgi->Vars);
#And then in the template:
Hello, <TMPL_VAR cgi.name>
It this won't work you need (because of existsing templates), do this:
#Perl
# $obj is the associate object.
foreach my $p ($obj->param()) {
$template->param($p,$obj->param($p));
}
# In the template:
Hello, <TMPL_VAR name>
=head2 case_sensitive
This option defaults to off in HTML::Template, but in Django it defaults to on.
This is quite bad for most templates, so you can use the case_insensitive addon from CPAN for this. (It should already be installed when this module is installed):
In the perl script that calls it:
use Dotiac::DTL::Addon::case_insensitive;
In the template (before any {% load html_template_pure %}) with Dotiac::DTL 0.8 and up:
{% load case_insensitive %}
But remember, this makes Dotiac::DTL slower, so this should be avoided and all variables should be in the right case.
=head2 loop_context_vars
This one defaults to off in HTML::Template, but is set to on here, because it probably won't disrupt any templates.
It can be set to off if there are some problems:
use Dotiac::DTL::Addon::html_template loop_context_vars=>0;
=head2 global_vars
This one defaults to off in HTML::Template, but it is also set to on here, because it probably won't disrupt any templates.
It can be set to off if there are some problems:
use Dotiac::DTL::Addon::html_template global_vars=>0;
=head2 default_escape
This is set to off in HTML::Template (which is not that good), but set to HTML in Django. Therefore this parser has to fiddle about with it a lot.
use Dotiac::DTL::Addon::html_template default_escape=>"HTML";
use Dotiac::DTL::Addon::html_template default_escape=>"JS";
use Dotiac::DTL::Addon::html_template default_escape=>"URL";
=head2 Combine options
use Dotiac::DTL::Addon::html_template global_vars=>0, loop_context_vars=>0, default_escape=>"HTML";
=head2 Setting options during runtime:
Options are save in %Dotiac::DTL::Addon::html_template_pure::OPTIONS (no matter if you use Dotiac::DTL::Addon::html_template_pure or Dotiac::DTL::Addon::html_template).
Changes are only applied to the following new() calls or included templates during print().
$Dotiac::DTL::Addon::html_template_pure::OPTIONS{default_escape}="html"
$Dotiac::DTL::Addon::html_template_pure::OPTIONS{default_escape}="js"
$Dotiac::DTL::Addon::html_template_pure::OPTIONS{default_escape}="url"
$Dotiac::DTL::Addon::html_template_pure::OPTIONS{default_escape}="" #off
$Dotiac::DTL::Addon::html_template_pure::OPTIONS{global_var}=0
$Dotiac::DTL::Addon::html_template_pure::OPTIONS{global_var}=1
$Dotiac::DTL::Addon::html_template_pure::OPTIONS{loop_context_vars}=1
$Dotiac::DTL::Addon::html_template_pure::OPTIONS{loop_context_vars}=0
B<Note:> Changing options for the same template might not work because of the caching routines.
It works fine for different templates.
B<Also note:> Once a template is compiled, these options are ignored. In fact the whole module wouldn't be needed anymore. (Unless there is a {% load html_template(_pure) %} in there)
=head1 A NOTE ON COMBINED TEMPLATES
It is possible for Django tags close Html::Template tags and reverse, but it is not very pretty:
<h1>My posts</h1>
<TMPL_LOOP posts>
<h2>
{% if title %}
{{ title }}
<!-- TMPL_ELSE -->
A post
</TMPL_IF>
</h2>
{{ text|linebreaksbr}}
{% endimportloop %}
But sometimes it might be useful to add an {% empty %} tag to an existing template:
Updated on :
<TMPL_LOOP updated>
{{ time|date }}
{% empty %}
<b>Never</b>
</TMPL_LOOP>
=head1 BUGS
Please report any bugs or feature requests to L<https://sourceforge.net/tracker2/?group_id=249411&atid=1126445>
=head1 SEE ALSO
L<Dotiac::DTL>, L<Dotiac::DTL::Addon>, L<http://www.dotiac.com>, L<http://www.djangoproject.com>
=head1 AUTHOR
Marc-Sebastian Lucksch
perl@marc-s.de
=cut