#============================================================= -*-perl-*- # # Apache::Template # # DESCRIPTION # Apache/mod_perl handler for the Template Toolkit. # # AUTHOR # Andy Wardley # # COPYRIGHT # Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved. # Copyright (C) 1998-2002 Canon Research Centre Europe Ltd. # # This module is free software; you can redistribute it and/or # modify it under the same terms as Perl itself. # # REVISION # $Id: Template.pm,v 1.5 2004/04/27 09:11:31 abw Exp $ # #======================================================================== package Apache::Template; use strict; use vars qw( $VERSION $DEBUG $ERROR $SERVICE ); use DynaLoader (); use Apache::ModuleConfig (); use Apache::Constants qw( :common ); use Template::Service::Apache; use Template::Config; $VERSION = '0.09'; $ERROR = ''; $DEBUG = 0 unless defined $DEBUG; $Template::Config::SERVICE = 'Template::Service::Apache'; if ($ENV{ MOD_PERL }) { no strict; @ISA = qw( DynaLoader ); __PACKAGE__->bootstrap($VERSION); } #------------------------------------------------------------------------ # handler($request) # # Main Apache/mod_perl content handler which delegates to an # underlying Template::Service::Apache object. A service is created that # is unique to the hostname (e.g. to support multiple configurations for # virtual hosts). This is created and stored in the $SERVICE hash and # then reused across requests to the same hostname. This allows compiled # templates to be cached and re-used without requiring re-compilation. # The service implements 4 methods for different phases of the request: # # template($request) # fetch a compiled template # params($request) # build parameter set (template vars) # process($template, $params) # process template # headers($request, $template, \$content) # # set and send http headers #------------------------------------------------------------------------ sub handler { my $r = shift; # create and cache a service for each hostname my $service = $SERVICE->{ $r->hostname() } ||= do { my $cfg = Apache::ModuleConfig->get($r) || { }; # warn "setup service for hostname: ", $r->hostname, " ($cfg):\n", # dump_hash($cfg), "\n"; Template::Config->service($cfg) || do { $r->log_reason(Template::Config->error(), $r->filename()); return SERVER_ERROR; }; }; my $template = $service->template($r); return $template unless ref $template; my $params = $service->params($r); return $params unless ref $params; my $content = $service->process($template, $params); unless (defined $content) { $r->log_reason($service->error(), $r->filename()); return SERVER_ERROR; } $service->headers($r, $template, \$content); $r->print($content); return OK; } #======================================================================== # Configuration Handlers #======================================================================== #------------------------------------------------------------------------ # TT2Tags html # specify TAG_STYLE # TT2Tags [* *] # specify START_TAG and END_TAG #------------------------------------------------------------------------ sub TT2Tags($$$$) { my ($cfg, $parms, $start, $end) = @_; if (defined $end and length $end) { $cfg->{ START_TAG } = quotemeta($start); $cfg->{ END_TAG } = quotemeta($end); } else { $cfg->{ TAG_STYLE } = $start; } } #------------------------------------------------------------------------ # TT2PreChomp On # enable PRE_CHOMP #------------------------------------------------------------------------ sub TT2PreChomp($$$) { my ($cfg, $parms, $on) = @_; $cfg->{ PRE_CHOMP } = $on; } #------------------------------------------------------------------------ # TT2PostChomp On # enable POST_CHOMP #------------------------------------------------------------------------ sub TT2PostChomp($$$) { my ($cfg, $parms, $on) = @_; $cfg->{ POST_CHOMP } = $on; } #------------------------------------------------------------------------ # TT2Trim On # enable TRIM #------------------------------------------------------------------------ sub TT2Trim($$$) { my ($cfg, $parms, $on) = @_; $cfg->{ TRIM } = $on; } #------------------------------------------------------------------------ # TT2AnyCase On # enable ANYCASE #------------------------------------------------------------------------ sub TT2AnyCase($$$) { my ($cfg, $parms, $on) = @_; $cfg->{ ANYCASE } = $on; } #------------------------------------------------------------------------ # TT2Interpolate On # enable INTERPOLATE #------------------------------------------------------------------------ sub TT2Interpolate($$$) { my ($cfg, $parms, $on) = @_; $cfg->{ INTERPOLATE } = $on; } #------------------------------------------------------------------------ # TT2Tolerant On # enable TOLERANT #------------------------------------------------------------------------ sub TT2Tolerant($$$) { my ($cfg, $parms, $on) = @_; $cfg->{ TOLERANT } = $on; } #------------------------------------------------------------------------ # TT2IncludePath /here /there # define INCLUDE_PATH directories # TT2IncludePath /elsewhere # additional INCLUDE_PATH directories #------------------------------------------------------------------------ sub TT2IncludePath($$@) { my ($cfg, $parms, $path) = @_; my $incpath = $cfg->{ INCLUDE_PATH } ||= [ ]; push(@$incpath, $path); } #------------------------------------------------------------------------ # TT2Absolute On # enable ABSOLUTE file paths #------------------------------------------------------------------------ sub TT2Absolute($$$) { my ($cfg, $parms, $on) = @_; $cfg->{ ABSOLUTE } = $on; } #------------------------------------------------------------------------ # TT2Relative On # enable RELATIVE file paths #------------------------------------------------------------------------ sub TT2Relative($$$) { my ($cfg, $parms, $on) = @_; $cfg->{ RELATIVE } = $on; } #------------------------------------------------------------------------ # TT2Delimiter , # set alternate directory delimiter #------------------------------------------------------------------------ sub TT2Delimiter($$$) { my ($cfg, $parms, $delim) = @_; $cfg->{ DELIMITER } = $delim; } #------------------------------------------------------------------------ # TT2PreProcess config header # define PRE_PROCESS templates # TT2PreProcess menu # additional PRE_PROCESS templates #------------------------------------------------------------------------ sub TT2PreProcess($$@) { my ($cfg, $parms, $file) = @_; my $preproc = $cfg->{ PRE_PROCESS } ||= [ ]; push(@$preproc, $file); } #------------------------------------------------------------------------ # TT2Process main1 main2 # define PROCESS templates # TT2Process main3 # additional PROCESS template #------------------------------------------------------------------------ sub TT2Process($$@) { my ($cfg, $parms, $file) = @_; my $process = $cfg->{ PROCESS } ||= [ ]; push(@$process, $file); } #------------------------------------------------------------------------ # TT2Wrapper main1 main2 # define WRAPPER templates # TT2Wrapper main3 # additional WRAPPER template #------------------------------------------------------------------------ sub TT2Wrapper($$@) { my ($cfg, $parms, $file) = @_; my $wrapper = $cfg->{ WRAPPER } ||= [ ]; push(@$wrapper, $file); } #------------------------------------------------------------------------ # TT2PostProcess menu copyright # define POST_PROCESS templates # TT2PostProcess footer # additional POST_PROCESS templates #------------------------------------------------------------------------ sub TT2PostProcess($$@) { my ($cfg, $parms, $file) = @_; my $postproc = $cfg->{ POST_PROCESS } ||= [ ]; push(@$postproc, $file); } #------------------------------------------------------------------------ # TT2Default notfound # define DEFAULT template #------------------------------------------------------------------------ sub TT2Default($$$) { my ($cfg, $parms, $file) = @_; $cfg->{ DEFAULT } = $file; } #------------------------------------------------------------------------ # TT2Error error # define ERROR template #------------------------------------------------------------------------ sub TT2Error($$$) { my ($cfg, $parms, $file) = @_; $cfg->{ ERROR } = $file; } #------------------------------------------------------------------------ # TT2EvalPerl On # enable EVAL_PERL #------------------------------------------------------------------------ sub TT2EvalPerl($$$) { my ($cfg, $parms, $on) = @_; $cfg->{ EVAL_PERL } = $on; } #------------------------------------------------------------------------ # TT2LoadPerl On # enable LOAD_PERL #------------------------------------------------------------------------ sub TT2LoadPerl($$$) { my ($cfg, $parms, $on) = @_; $cfg->{ LOAD_PERL } = $on; } #------------------------------------------------------------------------ # TT2Recursion On # enable RECURSION #------------------------------------------------------------------------ sub TT2Recursion($$$) { my ($cfg, $parms, $on) = @_; $cfg->{ RECURSION } = $on; } #------------------------------------------------------------------------ # TT2PluginBase My::Plugins # define PLUGIN_BASE package(s) # TT2PluginBase Your::Plugin # additional PLUGIN_BASE package(s) #------------------------------------------------------------------------ sub TT2PluginBase($$@) { my ($cfg, $parms, $base) = @_; my $pbases = $cfg->{ PLUGIN_BASE } ||= [ ]; push(@$pbases, $base); } #------------------------------------------------------------------------ # TT2AutoReset Off # disable AUTO_RESET #------------------------------------------------------------------------ sub TT2AutoReset($$$) { my ($cfg, $parms, $on) = @_; $cfg->{ AUTO_RESET } = $on; } #------------------------------------------------------------------------ # TT2CacheSize 128 # define CACHE_SIZE #------------------------------------------------------------------------ sub TT2CacheSize($$$) { my ($cfg, $parms, $size) = @_; $cfg->{ CACHE_SIZE } = $size; } #------------------------------------------------------------------------ # TT2CompileExt .tt2 # define COMPILE_EXT #------------------------------------------------------------------------ sub TT2CompileExt($$$) { my ($cfg, $parms, $ext) = @_; $cfg->{ COMPILE_EXT } = $ext; } #------------------------------------------------------------------------ # TT2CompileDir /var/tt2/cache # define COMPILE_DIR #------------------------------------------------------------------------ sub TT2CompileDir($$$) { my ($cfg, $parms, $dir) = @_; $cfg->{ COMPILE_DIR } = $dir; } #------------------------------------------------------------------------ # TT2Debug On # enable DEBUG #------------------------------------------------------------------------ sub TT2Debug($$$) { my ($cfg, $parms, $on) = @_; $cfg->{ DEBUG } = $DEBUG = $on; } #------------------------------------------------------------------------ # TT2Headers length etag # add certain HTTP headers #------------------------------------------------------------------------ sub TT2Headers($$@) { my ($cfg, $parms, $item) = @_; my $headers = $cfg->{ SERVICE_HEADERS } ||= [ ]; push(@$headers, $item); } #------------------------------------------------------------------------ # TT2Params uri env pnotes uploads request # add template vars #------------------------------------------------------------------------ sub TT2Params($$@) { my ($cfg, $parms, $item) = @_; my $params = $cfg->{ SERVICE_PARAMS } ||= [ ]; push(@$params, $item); } #------------------------------------------------------------------------ # TT2ContentType text/xml # custom content type #------------------------------------------------------------------------ sub TT2ContentType($$$) { my ($cfg, $parms, $type) = @_; $cfg->{ CONTENT_TYPE } = $type; } #------------------------------------------------------------------------ # TT2ServiceModule My::Service::Class # custom service module #------------------------------------------------------------------------ sub TT2ServiceModule($$$) { my ($cfg, $parms, $module) = @_; $Template::Config::SERVICE = $module; } #------------------------------------------------------------------------ # TT2Variable name value # define template variable #------------------------------------------------------------------------ sub TT2Variable($$$$) { my ($cfg, $parms, $name, $value) = @_; $cfg->{ VARIABLES }->{ $name } = $value; } #------------------------------------------------------------------------ # TT2Constant foo bar #------------------------------------------------------------------------ sub TT2Constant($$@@) { my ($cfg, $parms, $name, $value) = @_; my $constants = $cfg->{ CONSTANTS } ||= { }; $constants->{ $name } = $value; } #------------------------------------------------------------------------ # TT2ConstantsNamespace const #------------------------------------------------------------------------ sub TT2ConstantsNamespace($$$) { my ($cfg, $parms, $namespace) = @_; $cfg->{ CONSTANTS_NAMESPACE } = $namespace; } #======================================================================== # Configuration creators/mergers #======================================================================== my $dir_counter = 1; # used for debugging/testing of problems my $srv_counter = 1; # with SERVER_MERGE and DIR_MERGE sub SERVER_CREATE { my $class = shift; my $config = bless { }, $class; warn "SERVER_CREATE($class) => $config\n" if $DEBUG; return $config; } sub SERVER_MERGE { my ($parent, $config) = @_; my $merged = _merge($parent, $config); if ($DEBUG) { $merged->{ counter } = $srv_counter; warn "\nSERVER_MERGE #" . $srv_counter++ . "\n" . "$parent\n" . dump_hash($parent) . "\n+\n" . "$config\n" . dump_hash($config) . "\n=\n" . "$merged\n" . dump_hash($merged) . "\n"; } return $merged; } sub DIR_CREATE { my $class = shift; my $config = bless { }, $class; warn "DIR_CREATE($class) => $config\n" if $DEBUG; return $config; } sub DIR_MERGE { my ($parent, $config) = @_; my $merged = _merge($parent, $config); if ($DEBUG) { $merged->{ counter } = $dir_counter; warn "\nDIR_MERGE #" . $dir_counter++ . "\n" . "$parent\n" . dump_hash($parent) . "\n+\n" . "$config\n" . dump_hash($config) . "\n=\n" . "$merged\n" . dump_hash($merged) . "\n"; } return $merged; } sub _merge { my ($parent, $config) = @_; # let's not merge with ourselves. # it's not.. umm.. natural. return $config if $parent eq $config; my $merged = bless { }, ref($parent); foreach my $key (keys %$parent) { if(!ref $parent->{$key}) { $merged->{$key} = $parent->{$key}; } elsif (ref $parent->{$key} eq 'ARRAY') { $merged->{$key} = [ @{$parent->{$key}} ]; } elsif (ref $parent->{$key} eq 'HASH') { $merged->{$key} = { %{$parent->{$key}} }; } elsif (ref $parent->{$key} eq 'SCALAR') { $merged->{$key} = \${$parent->{$key}}; } } foreach my $key (keys %$config) { if(!ref $config->{$key}) { $merged->{$key} = $config->{$key}; } elsif (ref $config->{$key} eq 'ARRAY') { push @{$merged->{$key} ||= []}, @{$config->{$key}}; } elsif (ref $config->{$key} eq 'HASH') { $merged->{$key} = { %{$merged->{$key}}, %{$config->{$key}} }; } elsif (ref $config->{$key} eq 'SCALAR') { $merged->{$key} = \${$config->{$key}}; } } return $merged; } # debug methods for testing problems with DIR_MERGE, etc. sub dump_hash { my $hash = shift; my $out = " {\n"; while (my($key, $value) = (each %$hash)) { $value = "[ @$value ]" if ref $value eq 'ARRAY'; $out .= " $key => $value\n"; } $out .= " }"; } sub dump_hash_html { my $hash = dump_hash(shift); for ($hash) { s/>/>/g; s/\n/
/g; s/ / /g; } return $hash; } 1; __END__ =head1 NAME Apache::Template - Apache/mod_perl interface to the Template Toolkit =head1 SYNOPSIS # add the following to your httpd.conf PerlModule Apache::Template # set various configuration options, e.g. TT2Trim On TT2PostChomp On TT2EvalPerl On TT2IncludePath /usr/local/tt2/templates TT2IncludePath /home/abw/tt2/lib TT2PreProcess config header TT2PostProcess footer TT2Error error # now define Apache::Template as a PerlHandler, e.g. SetHandler perl-script PerlHandler Apache::Template SetHandler perl-script PerlHandler Apache::Template =head1 DESCRIPTION The Apache::Template module provides a simple interface to the Template Toolkit from Apache/mod_perl. The Template Toolkit is a fast, powerful and extensible template processing system written in Perl. It implements a general purpose template language which allows you to clearly separate application logic, data and presentation elements. It boasts numerous features to facilitate in the generation of web content both online and offline in "batch mode". This documentation describes the Apache::Template module, concerning itself primarily with the Apache/mod_perl configuration options (e.g. the httpd.conf side of things) and not going into any great depth about the Template Toolkit itself. The Template Toolkit includes copious documentation which already covers these things in great detail. See L