Initial Commit

This commit is contained in:
Riley Schneider
2025-12-03 16:38:10 +01:00
parent c5e26bf594
commit b732d8d4b5
17680 changed files with 5977495 additions and 2 deletions

View File

@@ -0,0 +1,155 @@
#============================================================= -*-Perl-*-
#
# Template::Plugin::Assert
#
# DESCRIPTION
# Template Toolkit plugin module which allows you to assert that
# items fetches from the stash are defined.
#
# AUTHOR
# Andy Wardley <abw@wardley.org>
#
# COPYRIGHT
# Copyright (C) 2008 Andy Wardley. All Rights Reserved.
#
# This module is free software; you can redistribute it and/or
# modify it under the same terms as Perl itself.
#
#============================================================================
package Template::Plugin::Assert;
use base 'Template::Plugin';
use strict;
use warnings;
use Template::Exception;
our $VERSION = '3.009';
our $MONAD = 'Template::Monad::Assert';
our $EXCEPTION = 'Template::Exception';
our $AUTOLOAD;
sub load {
my $class = shift;
my $context = shift;
my $stash = $context->stash;
my $vmethod = sub {
$MONAD->new($stash, shift);
};
# define .assert vmethods for hash and list objects
$context->define_vmethod( hash => assert => $vmethod );
$context->define_vmethod( list => assert => $vmethod );
return $class;
}
sub new {
my ($class, $context, @args) = @_;
# create an assert plugin object which will handle simple variable
# lookups.
return bless { _CONTEXT => $context }, $class;
}
sub AUTOLOAD {
my ($self, @args) = @_;
my $item = $AUTOLOAD;
$item =~ s/.*:://;
return if $item eq 'DESTROY';
# lookup the named values
my $stash = $self->{ _CONTEXT }->stash;
my $value = $stash->dotop($stash, $item, \@args);
if (! defined $value) {
die $EXCEPTION->new( assert => "undefined value for $item" );
}
return $value;
}
package Template::Monad::Assert;
our $EXCEPTION = 'Template::Exception';
our $AUTOLOAD;
sub new {
my ($class, $stash, $this) = @_;
bless [$stash, $this], $class;
}
sub AUTOLOAD {
my ($self, @args) = @_;
my ($stash, $this) = @$self;
my $item = $AUTOLOAD;
$item =~ s/.*:://;
return if $item eq 'DESTROY';
my $value = $stash->dotop($stash, $item, \@args);
if (! defined $value) {
die $EXCEPTION->new( assert => "undefined value for $item" );
}
return $value;
}
1;
__END__
=head1 NAME
Template::Plugin::Assert - trap undefined values
=head1 SYNOPSIS
[% USE assert %]
# throws error if any undefined values are returned
[% object.assert.method %]
[% hash.assert.key %]
[% list.assert.item %]
=head1 DESCRIPTION
This plugin defines the C<assert> virtual method that can be used
to automatically throw errors when undefined values are used.
For example, consider this dotop:
[% user.name %]
If C<user.name> is an undefined value then TT will silently ignore the
fact and print nothing. If you C<USE> the C<assert> plugin then you
can add the C<assert> vmethod between the C<user> and C<name> elements,
like so:
[% user.assert.name %]
Now, if C<user.name> is an undefined value, an exception will be thrown:
assert error - undefined value for name
=head1 AUTHOR
Andy Wardley E<lt>abw@wardley.orgE<gt> L<http://wardley.org/>
=head1 COPYRIGHT
Copyright (C) 2008 Andy Wardley. All Rights Reserved.
This module is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.
=head1 SEE ALSO
L<Template::Plugin>
=cut
# Local Variables:
# mode: perl
# perl-indent-level: 4
# indent-tabs-mode: nil
# End:
#
# vim: expandtab shiftwidth=4:

View File

@@ -0,0 +1,135 @@
#============================================================= -*-Perl-*-
#
# Template::Plugin::CGI
#
# DESCRIPTION
# Simple Template Toolkit plugin interfacing to the CGI.pm module.
#
# AUTHOR
# Andy Wardley <abw@wardley.org>
#
# COPYRIGHT
# Copyright (C) 1996-2007 Andy Wardley. All Rights Reserved.
#
# This module is free software; you can redistribute it and/or
# modify it under the same terms as Perl itself.
#
#============================================================================
package Template::Plugin::CGI;
use strict;
use warnings;
use base 'Template::Plugin';
use CGI;
our $VERSION = '3.009';
sub new {
my $class = shift;
my $context = shift;
CGI->new(@_);
}
# monkeypatch CGI::params() method to Do The Right Thing in TT land
sub CGI::params {
my $self = shift;
local $" = ', ';
return $self->{ _TT_PARAMS } ||= do {
# must call Vars() in a list context to receive
# plain list of key/vals rather than a tied hash
my $params = { $self->Vars() };
# convert any null separated values into lists
@$params{ keys %$params } = map {
/\0/ ? [ split /\0/ ] : $_
} values %$params;
$params;
};
}
1;
__END__
=head1 NAME
Template::Plugin::CGI - Interface to the CGI module
=head1 SYNOPSIS
[% USE CGI %]
[% CGI.param('parameter') %]
[% USE things = CGI %]
[% things.param('name') %]
# see CGI docs for other methods provided by the CGI object
=head1 DESCRIPTION
This is a very simple Template Toolkit Plugin interface to the C<CGI> module.
A C<CGI> object will be instantiated via the following directive:
[% USE CGI %]
C<CGI> methods may then be called as follows:
[% CGI.header %]
[% CGI.param('parameter') %]
An alias can be used to provide an alternate name by which the object should
be identified.
[% USE mycgi = CGI %]
[% mycgi.start_form %]
[% mycgi.popup_menu({ Name => 'Color'
Values => [ 'Green' 'Black' 'Brown' ] }) %]
Parenthesised parameters to the C<USE> directive will be passed to the plugin
constructor:
[% USE cgiprm = CGI('uid=abw&name=Andy+Wardley') %]
[% cgiprm.param('uid') %]
=head1 METHODS
In addition to all the methods supported by the C<CGI> module, this
plugin defines the following.
=head2 params()
This method returns a reference to a hash of all the C<CGI> parameters.
Any parameters that have multiple values will be returned as lists.
[% USE CGI('user=abw&item=foo&item=bar') %]
[% CGI.params.user %] # abw
[% CGI.params.item.join(', ') %] # foo, bar
=head1 AUTHOR
Andy Wardley E<lt>abw@wardley.orgE<gt> L<http://wardley.org/>
=head1 COPYRIGHT
Copyright (C) 1996-2007 Andy Wardley. All Rights Reserved.
This module is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.
=head1 SEE ALSO
L<Template::Plugin>, L<CGI>
=cut
# Local Variables:
# mode: perl
# perl-indent-level: 4
# indent-tabs-mode: nil
# End:
#
# vim: expandtab shiftwidth=4:

View File

@@ -0,0 +1,170 @@
#============================================================= -*-Perl-*-
#
# Template::Plugin::Datafile
#
# DESCRIPTION
# Template Toolkit Plugin which reads a datafile and constructs a
# list object containing hashes representing records in the file.
#
# AUTHOR
# Andy Wardley <abw@wardley.org>
#
# COPYRIGHT
# Copyright (C) 1996-2007 Andy Wardley. All Rights Reserved.
#
# This module is free software; you can redistribute it and/or
# modify it under the same terms as Perl itself.
#
#============================================================================
package Template::Plugin::Datafile;
use strict;
use warnings;
use base 'Template::Plugin';
our $VERSION = '3.009';
sub new {
my ($class, $context, $filename, $params) = @_;
my ($delim, $encoding, $line, @fields, @data, @results);
my $self = [ ];
local *FD;
local $/ = "\n";
$params ||= { };
$delim = $params->{'delim'} || ':';
$delim = quotemeta($delim);
$encoding = defined $params->{'encoding'} ? ':encoding('.$params->{'encoding'}.')' : '';
return $class->error("No filename specified")
unless $filename;
open(FD, '<'.$encoding, $filename)
|| return $class->error("$filename: $!");
# first line of file should contain field definitions
while (! $line || $line =~ /^#/) {
$line = <FD>;
chomp $line;
$line =~ s/\r$//;
}
(@fields = split(/\s*$delim\s*/, $line))
|| return $class->error("first line of file must contain field names");
# read each line of the file
while (<FD>) {
chomp;
s/\r$//;
# ignore comments and blank lines
next if /^#/ || /^\s*$/;
# split line into fields
@data = split(/\s*$delim\s*/);
# create hash record to represent data
my %record;
@record{ @fields } = @data;
push(@$self, \%record);
}
# return $self;
bless $self, $class;
}
sub as_list {
return $_[0];
}
1;
__END__
=head1 NAME
Template::Plugin::Datafile - Plugin to construct records from a simple data file
=head1 SYNOPSIS
[% USE mydata = datafile('/path/to/datafile') %]
[% USE mydata = datafile('/path/to/datafile', delim = '|') %]
[% USE mydata = datafile('/path/to/datafile', encoding = 'UTF-8') %]
[% FOREACH record = mydata %]
[% record.this %] [% record.that %]
[% END %]
=head1 DESCRIPTION
This plugin provides a simple facility to construct a list of hash
references, each of which represents a data record of known structure,
from a data file.
[% USE datafile(filename) %]
A absolute filename must be specified (for this initial implementation at
least - in a future version it might also use the C<INCLUDE_PATH>). An
optional C<delim> parameter may also be provided to specify an alternate
delimiter character.
The optional C<encoding> parameter may be used to specify the input file
encoding.
[% USE userlist = datafile('/path/to/file/users') %]
[% USE things = datafile('items', delim = '|') %]
The format of the file is intentionally simple. The first line
defines the field names, delimited by colons with optional surrounding
whitespace. Subsequent lines then defines records containing data
items, also delimited by colons. e.g.
id : name : email : tel
abw : Andy Wardley : abw@tt2.org : 555-1234
sam : Simon Matthews : sam@tt2.org : 555-9876
Each line is read, split into composite fields, and then used to
initialise a hash array containing the field names as relevant keys.
The plugin returns a blessed list reference containing the hash
references in the order as defined in the file.
[% FOREACH user = userlist %]
[% user.id %]: [% user.name %]
[% END %]
The first line of the file B<must> contain the field definitions.
After the first line, blank lines will be ignored, along with comment
line which start with a 'C<#>'.
=head1 BUGS
Should handle file names relative to C<INCLUDE_PATH>.
Doesn't permit use of 'C<:>' in a field. Some escaping mechanism is required.
=head1 AUTHOR
Andy Wardley E<lt>abw@wardley.orgE<gt> L<http://wardley.org/>
=head1 COPYRIGHT
Copyright (C) 1996-2007 Andy Wardley. All Rights Reserved.
This module is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.
=head1 SEE ALSO
L<Template::Plugin>
=cut
# Local Variables:
# mode: perl
# perl-indent-level: 4
# indent-tabs-mode: nil
# End:
#
# vim: expandtab shiftwidth=4:

View File

@@ -0,0 +1,378 @@
#============================================================= -*-Perl-*-
#
# Template::Plugin::Date
#
# DESCRIPTION
#
# Plugin to generate formatted date strings.
#
# AUTHORS
# Thierry-Michel Barral <kktos@electron-libre.com>
# Andy Wardley <abw@wardley.org>
#
# COPYRIGHT
# Copyright (C) 2000-2007 Thierry-Michel Barral, Andy Wardley.
#
# This module is free software; you can redistribute it and/or
# modify it under the same terms as Perl itself.
#
#============================================================================
package Template::Plugin::Date;
use strict;
use warnings;
use base 'Template::Plugin';
use POSIX ();
use Config ();
use constant HAS_SETLOCALE => $Config::Config{d_setlocale};
our $VERSION = '3.009';
our $FORMAT = '%H:%M:%S %d-%b-%Y'; # default strftime() format
our @LOCALE_SUFFIX = qw( .ISO8859-1 .ISO_8859-15 .US-ASCII .UTF-8 );
#------------------------------------------------------------------------
# new(\%options)
#------------------------------------------------------------------------
sub new {
my ($class, $context, $params) = @_;
bless {
$params ? %$params : ()
}, $class;
}
#------------------------------------------------------------------------
# now()
#
# Call time() to return the current system time in seconds since the epoch.
#------------------------------------------------------------------------
sub now {
return time();
}
#------------------------------------------------------------------------
# format()
# format($time)
# format($time, $format)
# format($time, $format, $locale)
# format($time, $format, $locale, $gmt_flag)
# format(\%named_params);
#
# Returns a formatted time/date string for the specified time, $time,
# (or the current system time if unspecified) using the $format, $locale,
# and $gmt values specified as arguments or internal values set defined
# at construction time). Specifying a Perl-true value for $gmt will
# override the local time zone and force the output to be for GMT.
# Any or all of the arguments may be specified as named parameters which
# get passed as a hash array reference as the final argument.
# ------------------------------------------------------------------------
sub format {
my $self = shift;
my $params = ref($_[$#_]) eq 'HASH' ? pop(@_) : { };
my $time = shift(@_);
$time = $params->{ time } || $self->{ time } || $self->now() if !defined $time;
my $format = @_ ? shift(@_)
: ($params->{ format } || $self->{ format } || $FORMAT);
my $locale = @_ ? shift(@_)
: ($params->{ locale } || $self->{ locale });
my $gmt = @_ ? shift(@_)
: ($params->{ gmt } || $self->{ gmt });
my $offset = @_ ? shift(@_)
: ( $params->{ use_offset } || $self->{ use_offset });
my (@date, $datestr);
if ($time =~ /^-?\d+$/) {
# $time is now in seconds since epoch
if ($gmt) {
@date = (gmtime($time))[ 0 .. ( $offset ? 6 : 8 ) ];
}
else {
@date = (localtime($time))[ 0 .. ( $offset ? 6 : 8 ) ];
}
}
else {
# if $time is numeric, then we assume it's seconds since the epoch
# otherwise, we try to parse it as either a 'Y:M:D H:M:S' or a
# 'H:M:S D:M:Y' string
my @parts = (split(/\D/, $time));
if (@parts >= 6) {
if (length($parts[0]) == 4) {
# year is first; assume 'Y:M:D H:M:S'
@date = @parts[reverse 0..5];
}
else {
# year is last; assume 'H:M:S D:M:Y'
@date = @parts[2,1,0,3..5];
}
}
if (!@date) {
return (undef, Template::Exception->new('date',
"bad time/date string: " .
"expects 'h:m:s d:m:y' got: '$time'"));
}
$date[4] -= 1; # correct month number 1-12 to range 0-11
$date[5] -= 1900; # convert absolute year to years since 1900
$time = &POSIX::mktime(@date);
if ($offset) {
push @date, $gmt
? (gmtime($time))[6..8] : (localtime($time))[6..8];
}
}
if ($locale) {
# format the date in a specific locale, saving and subsequently
# restoring the current locale.
my $old_locale = HAS_SETLOCALE
? &POSIX::setlocale(&POSIX::LC_ALL)
: undef;
# some systems expect locales to have a particular suffix
for my $suffix ('', @LOCALE_SUFFIX) {
my $try_locale = $locale.$suffix;
my $setlocale = HAS_SETLOCALE
? &POSIX::setlocale(&POSIX::LC_ALL, $try_locale)
: undef;
if (defined $setlocale && $try_locale eq $setlocale) {
$locale = $try_locale;
last;
}
}
$datestr = &POSIX::strftime($format, @date);
&POSIX::setlocale(&POSIX::LC_ALL, $old_locale) if HAS_SETLOCALE;
}
else {
$datestr = &POSIX::strftime($format, @date);
}
return $datestr;
}
sub calc {
my $self = shift;
eval { require "Date/Calc.pm" };
$self->throw("failed to load Date::Calc: $@") if $@;
return Template::Plugin::Date::Calc->new('no context');
}
sub manip {
my $self = shift;
eval { require "Date/Manip.pm" };
$self->throw("failed to load Date::Manip: $@") if $@;
return Template::Plugin::Date::Manip->new('no context');
}
sub throw {
my $self = shift;
die (Template::Exception->new('date', join(', ', @_)));
}
package Template::Plugin::Date::Calc;
use base qw( Template::Plugin );
our $AUTOLOAD;
*throw = \&Template::Plugin::Date::throw;
sub AUTOLOAD {
my $self = shift;
my $method = $AUTOLOAD;
$method =~ s/.*:://;
return if $method eq 'DESTROY';
my $sub = \&{"Date::Calc::$method"};
$self->throw("no such Date::Calc method: $method")
unless $sub;
&$sub(@_);
}
package Template::Plugin::Date::Manip;
use base qw( Template::Plugin );
our $AUTOLOAD;
*throw = \&Template::Plugin::Date::throw;
sub AUTOLOAD {
my $self = shift;
my $method = $AUTOLOAD;
$method =~ s/.*:://;
return if $method eq 'DESTROY';
my $sub = \&{"Date::Manip::$method"};
$self->throw("no such Date::Manip method: $method")
unless $sub;
&$sub(@_);
}
1;
__END__
=head1 NAME
Template::Plugin::Date - Plugin to generate formatted date strings
=head1 SYNOPSIS
[% USE date %]
# use current time and default format
[% date.format %]
# specify time as seconds since epoch
# or as a 'h:m:s d-m-y' or 'y-m-d h:m:s' string
[% date.format(960973980) %]
[% date.format('4:20:36 21/12/2000') %]
[% date.format('2000/12/21 4:20:36') %]
# specify format
[% date.format(mytime, '%H:%M:%S') %]
# specify locale
[% date.format(date.now, '%a %d %b %y', 'en_GB') %]
# named parameters
[% date.format(mytime, format = '%H:%M:%S') %]
[% date.format(locale = 'en_GB') %]
[% date.format(time = date.now,
format = '%H:%M:%S',
locale = 'en_GB'
use_offset = 1) %]
# specify default format to plugin
[% USE date(format = '%H:%M:%S', locale = 'de_DE') %]
[% date.format %]
...
=head1 DESCRIPTION
The C<Date> plugin provides an easy way to generate formatted time and date
strings by delegating to the C<POSIX> C<strftime()> routine.
The plugin can be loaded via the familiar USE directive.
[% USE date %]
This creates a plugin object with the default name of 'C<date>'. An alternate
name can be specified as such:
[% USE myname = date %]
The plugin provides the C<format()> method which accepts a time value, a
format string and a locale name. All of these parameters are optional
with the current system time, default format ('C<%H:%M:%S %d-%b-%Y>') and
current locale being used respectively, if undefined. Default values
for the time, format and/or locale may be specified as named parameters
in the C<USE> directive.
[% USE date(format = '%a %d-%b-%Y', locale = 'fr_FR') %]
When called without any parameters, the C<format()> method returns a string
representing the current system time, formatted by C<strftime()> according
to the default format and for the default locale (which may not be the
current one, if locale is set in the C<USE> directive).
[% date.format %]
The plugin allows a time/date to be specified as seconds since the epoch,
as is returned by C<time()>.
File last modified: [% date.format(filemod_time) %]
The time/date can also be specified as a string of the form C<h:m:s d/m/y>
or C<y/m/d h:m:s>. Any of the characters : / - or space may be used to
delimit fields.
[% USE day = date(format => '%A', locale => 'en_GB') %]
[% day.format('4:20:00 9-13-2000') %]
Output:
Tuesday
A format string can also be passed to the C<format()> method, and a locale
specification may follow that.
[% date.format(filemod, '%d-%b-%Y') %]
[% date.format(filemod, '%d-%b-%Y', 'en_GB') %]
A fourth parameter allows you to force output in GMT, in the case of
seconds-since-the-epoch input:
[% date.format(filemod, '%d-%b-%Y', 'en_GB', 1) %]
Note that in this case, if the local time is not GMT, then also specifying
'C<%Z>' (time zone) in the format parameter will lead to an extremely
misleading result.
To maintain backwards compatibility, using the C<%z> placeholder in the format
string (to output the UTC offset) currently requires the C<use_offset>
parameter to be set to a true value. This can also be passed as the fifth
parameter to format (but the former will probably be clearer).
Any or all of these parameters may be named. Positional parameters
should always be in the order C<($time, $format, $locale)>.
[% date.format(format => '%H:%M:%S') %]
[% date.format(time => filemod, format => '%H:%M:%S') %]
[% date.format(mytime, format => '%H:%M:%S') %]
[% date.format(mytime, format => '%H:%M:%S', locale => 'fr_FR') %]
[% date.format(mytime, format => '%H:%M:%S', gmt => 1) %]
...etc...
The C<now()> method returns the current system time in seconds since the
epoch.
[% date.format(date.now, '%A') %]
The C<calc()> method can be used to create an interface to the C<Date::Calc>
module (if installed on your system).
[% calc = date.calc %]
[% calc.Monday_of_Week(22, 2001).join('/') %]
The C<manip()> method can be used to create an interface to the C<Date::Manip>
module (if installed on your system).
[% manip = date.manip %]
[% manip.UnixDate("Noon Yesterday","%Y %b %d %H:%M") %]
=head1 AUTHORS
Thierry-Michel Barral wrote the original plugin.
Andy Wardley provided some minor
fixups/enhancements, a test script and documentation.
Mark D. Mills cloned C<Date::Manip> from the C<Date::Calc> sub-plugin.
=head1 COPYRIGHT
Copyright (C) 2000-2007 Thierry-Michel Barral, Andy Wardley.
This module is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.
=head1 SEE ALSO
L<Template::Plugin>, L<POSIX>

View File

@@ -0,0 +1,386 @@
#============================================================= -*-Perl-*-
#
# Template::Plugin::Directory
#
# DESCRIPTION
# Plugin for encapsulating information about a file system directory.
#
# AUTHORS
# Michael Stevens <michael@etla.org>, with some mutilations from
# Andy Wardley <abw@wardley.org>.
#
# COPYRIGHT
# Copyright (C) 2000-2007 Michael Stevens, Andy Wardley.
#
# This module is free software; you can redistribute it and/or
# modify it under the same terms as Perl itself.
#
#============================================================================
package Template::Plugin::Directory;
use strict;
use warnings;
use Cwd;
use File::Spec;
use Template::Plugin::File;
use base 'Template::Plugin::File';
our $VERSION = '3.009';
#------------------------------------------------------------------------
# new(\%config)
#
# Constructor method.
#------------------------------------------------------------------------
sub new {
my $config = ref($_[-1]) eq 'HASH' ? pop(@_) : { };
my ($class, $context, $path) = @_;
return $class->throw('no directory specified')
unless defined $path and length $path;
my $self = $class->SUPER::new($context, $path, $config);
my ($dir, @files, $name, $item, $abs, $rel, $check);
$self->{ files } = [ ];
$self->{ dirs } = [ ];
$self->{ list } = [ ];
$self->{ _dir } = { };
# don't read directory if 'nostat' or 'noscan' set
return $self if $config->{ nostat } || $config->{ noscan };
$self->throw("$path: not a directory")
unless $self->{ isdir };
$self->scan($config);
return $self;
}
#------------------------------------------------------------------------
# scan(\%config)
#
# Scan directory for files and sub-directories.
#------------------------------------------------------------------------
sub scan {
my ($self, $config) = @_;
$config ||= { };
local *DH;
my ($dir, @files, $name, $abs, $rel, $item);
# set 'noscan' in config if recurse isn't set, to ensure Directories
# created don't try to scan deeper
$config->{ noscan } = 1 unless $config->{ recurse };
$dir = $self->{ abs };
opendir(DH, $dir) or return $self->throw("$dir: $!");
@files = readdir DH;
closedir(DH)
or return $self->throw("$dir close: $!");
my ($path, $files, $dirs, $list) = @$self{ qw( path files dirs list ) };
@$files = @$dirs = @$list = ();
foreach $name (sort @files) {
next if $name =~ /^\./;
$abs = File::Spec->catfile($dir, $name);
$rel = File::Spec->catfile($path, $name);
if (-d $abs) {
$item = Template::Plugin::Directory->new(undef, $rel, $config);
push(@$dirs, $item);
}
else {
$item = Template::Plugin::File->new(undef, $rel, $config);
push(@$files, $item);
}
push(@$list, $item);
$self->{ _dir }->{ $name } = $item;
}
return '';
}
#------------------------------------------------------------------------
# file($filename)
#
# Fetch a named file from this directory.
#------------------------------------------------------------------------
sub file {
my ($self, $name) = @_;
return $self->{ _dir }->{ $name };
}
#------------------------------------------------------------------------
# present($view)
#
# Present self to a Template::View
#------------------------------------------------------------------------
sub present {
my ($self, $view) = @_;
$view->view_directory($self);
}
#------------------------------------------------------------------------
# content($view)
#
# Present directory content to a Template::View.
#------------------------------------------------------------------------
sub content {
my ($self, $view) = @_;
return $self->{ list } unless $view;
my $output = '';
foreach my $file (@{ $self->{ list } }) {
$output .= $file->present($view);
}
return $output;
}
#------------------------------------------------------------------------
# throw($msg)
#
# Throw a 'Directory' exception.
#------------------------------------------------------------------------
sub throw {
my ($self, $error) = @_;
die (Template::Exception->new('Directory', $error));
}
1;
__END__
=head1 NAME
Template::Plugin::Directory - Plugin for generating directory listings
=head1 SYNOPSIS
[% USE dir = Directory(dirpath) %]
# files returns list of regular files
[% FOREACH file = dir.files %]
[% file.name %] [% file.path %] ...
[% END %]
# dirs returns list of sub-directories
[% FOREACH subdir = dir.dirs %]
[% subdir.name %] [% subdir.path %] ...
[% END %]
# list returns both interleaved in order
[% FOREACH item = dir.list %]
[% IF item.isdir %]
Directory: [% item.name %]
[% ELSE %]
File: [% item.name %]
[% END %]
[% END %]
# define a VIEW to display dirs/files
[% VIEW myview %]
[% BLOCK file %]
File: [% item.name %]
[% END %]
[% BLOCK directory %]
Directory: [% item.name %]
[% item.content(myview) | indent -%]
[% END %]
[% END %]
# display directory content using view
[% myview.print(dir) %]
=head1 DESCRIPTION
This Template Toolkit plugin provides a simple interface to directory
listings. It is derived from the L<Template::Plugin::File> module and
uses L<Template::Plugin::File> object instances to represent files within
a directory. Sub-directories within a directory are represented by
further C<Template::Plugin::Directory> instances.
The constructor expects a directory name as an argument.
[% USE dir = Directory('/tmp') %]
It then provides access to the files and sub-directories contained within
the directory.
# regular files (not directories)
[% FOREACH file IN dir.files %]
[% file.name %]
[% END %]
# directories only
[% FOREACH file IN dir.dirs %]
[% file.name %]
[% END %]
# files and/or directories
[% FOREACH file IN dir.list %]
[% file.name %] ([% file.isdir ? 'directory' : 'file' %])
[% END %]
The plugin constructor will throw a C<Directory> error if the specified
path does not exist, is not a directory or fails to C<stat()> (see
L<Template::Plugin::File>). Otherwise, it will scan the directory and
create lists named 'C<files>' containing files, 'C<dirs>' containing
directories and 'C<list>' containing both files and directories combined.
The C<nostat> option can be set to disable all file/directory checks
and directory scanning.
Each file in the directory will be represented by a
L<Template::Plugin::File> object instance, and each directory by another
C<Template::Plugin::Directory>. If the C<recurse> flag is set, then those
directories will contain further nested entries, and so on. With the
C<recurse> flag unset, as it is by default, then each is just a place
marker for the directory and does not contain any further content
unless its C<scan()> method is explicitly called. The C<isdir> flag can
be tested against files and/or directories, returning true if the item
is a directory or false if it is a regular file.
[% FOREACH file = dir.list %]
[% IF file.isdir %]
* Directory: [% file.name %]
[% ELSE %]
* File: [% file.name %]
[% END %]
[% END %]
This example shows how you might walk down a directory tree, displaying
content as you go. With the recurse flag disabled, as is the default,
we need to explicitly call the C<scan()> method on each directory, to force
it to lookup files and further sub-directories contained within.
[% USE dir = Directory(dirpath) %]
* [% dir.path %]
[% INCLUDE showdir %]
[% BLOCK showdir -%]
[% FOREACH file = dir.list -%]
[% IF file.isdir -%]
* [% file.name %]
[% file.scan -%]
[% INCLUDE showdir dir=file FILTER indent(4) -%]
[% ELSE -%]
- [% f.name %]
[% END -%]
[% END -%]
[% END %]
This example is adapted (with some re-formatting for clarity) from
a test in F<t/directry.t> which produces the following output:
* test/dir
- file1
- file2
* sub_one
- bar
- foo
* sub_two
- waz.html
- wiz.html
- xyzfile
The C<recurse> flag can be set (disabled by default) to cause the
constructor to automatically recurse down into all sub-directories,
creating a new C<Template::Plugin::Directory> object for each one and
filling it with any further content. In this case there is no need
to explicitly call the C<scan()> method.
[% USE dir = Directory(dirpath, recurse=1) %]
...
[% IF file.isdir -%]
* [% file.name %]
[% INCLUDE showdir dir=file FILTER indent(4) -%]
[% ELSE -%]
...
The directory plugin also provides support for views. A view can be defined as
a C<VIEW ... END> block and should contain C<BLOCK> definitions for files
('C<file>') and directories ('C<directory>').
[% VIEW myview %]
[% BLOCK file %]
- [% item.name %]
[% END %]
[% BLOCK directory %]
* [% item.name %]
[% item.content(myview) FILTER indent %]
[% END %]
[% END %]
The view C<print()> method can then be called, passing the
C<Directory> object as an argument.
[% USE dir = Directory(dirpath, recurse=1) %]
[% myview.print(dir) %]
When a directory is presented to a view, either as C<[% myview.print(dir) %]>
or C<[% dir.present(view) %]>, then the C<directory> C<BLOCK> within the
C<myview> C<VIEW> is processed. The C<item> variable will be set to alias the
C<Directory> object.
[% BLOCK directory %]
* [% item.name %]
[% item.content(myview) FILTER indent %]
[% END %]
In this example, the directory name is first printed and the content(view)
method is then called to present each item within the directory to the view.
Further directories will be mapped to the C<directory> block, and files will be
mapped to the C<file> block.
With the recurse option disabled, as it is by default, the C<directory>
block should explicitly call a C<scan()> on each directory.
[% VIEW myview %]
[% BLOCK file %]
- [% item.name %]
[% END %]
[% BLOCK directory %]
* [% item.name %]
[% item.scan %]
[% item.content(myview) FILTER indent %]
[% END %]
[% END %]
[% USE dir = Directory(dirpath) %]
[% myview.print(dir) %]
=head1 AUTHORS
Michael Stevens wrote the original Directory plugin on which this is based.
Andy Wardley split it into separate L<File|Template::Plugin::File> and
L<Directory|Template::Plugin::Directory> plugins, added some extra code and
documentation for C<VIEW> support, and made a few other minor tweaks.
=head1 COPYRIGHT
Copyright (C) 2000-2007 Michael Stevens, Andy Wardley.
This module is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.
=head1 SEE ALSO
L<Template::Plugin>, L<Template::Plugin::File>, L<Template::View>

View File

@@ -0,0 +1,152 @@
#==============================================================================
#
# Template::Plugin::Dumper
#
# DESCRIPTION
#
# A Template Plugin to provide a Template Interface to Data::Dumper
#
# AUTHOR
# Simon Matthews <sam@tt2.org>
#
# COPYRIGHT
# Copyright (C) 2000 Simon Matthews. All Rights Reserved
#
# This module is free software; you can redistribute it and/or
# modify it under the same terms as Perl itself.
#
#==============================================================================
package Template::Plugin::Dumper;
use strict;
use warnings;
use base 'Template::Plugin';
use Data::Dumper;
our $VERSION = '3.009';
our $DEBUG = 0 unless defined $DEBUG;
our @DUMPER_ARGS = qw( Indent Pad Varname Purity Useqq Terse Freezer
Toaster Deepcopy Quotekeys Bless Maxdepth Sortkeys );
our $AUTOLOAD;
#==============================================================================
# ----- CLASS METHODS -----
#==============================================================================
#------------------------------------------------------------------------
# new($context, \@params)
#------------------------------------------------------------------------
sub new {
my ($class, $context, $params) = @_;
my ($key, $val);
$params ||= { };
foreach my $arg (@DUMPER_ARGS) {
no strict 'refs';
if (defined ($val = $params->{ lc $arg })
or defined ($val = $params->{ $arg })) {
${"Data\::Dumper\::$arg"} = $val;
}
}
bless {
_CONTEXT => $context,
}, $class;
}
sub dump {
my $self = shift;
my $content = Dumper @_;
return $content;
}
sub dump_html {
my $self = shift;
my $content = Dumper @_;
for ($content) {
s/&/&amp;/g;
s/</&lt;/g;
s/>/&gt;/g;
s/\n/<br>\n/g;
}
return $content;
}
1;
__END__
=head1 NAME
Template::Plugin::Dumper - Plugin interface to Data::Dumper
=head1 SYNOPSIS
[% USE Dumper %]
[% Dumper.dump(variable) %]
[% Dumper.dump_html(variable) %]
=head1 DESCRIPTION
This is a very simple Template Toolkit Plugin Interface to the L<Data::Dumper>
module. A C<Dumper> object will be instantiated via the following directive:
[% USE Dumper %]
As a standard plugin, you can also specify its name in lower case:
[% USE dumper %]
The C<Data::Dumper> C<Pad>, C<Indent> and C<Varname> options are supported
as constructor arguments to affect the output generated. See L<Data::Dumper>
for further details.
[% USE dumper(Indent=0, Pad="<br>") %]
These options can also be specified in lower case.
[% USE dumper(indent=0, pad="<br>") %]
=head1 METHODS
There are two methods supported by the C<Dumper> object. Each will
output into the template the contents of the variables passed to the
object method.
=head2 dump()
Generates a raw text dump of the data structure(s) passed
[% USE Dumper %]
[% Dumper.dump(myvar) %]
[% Dumper.dump(myvar, yourvar) %]
=head2 dump_html()
Generates a dump of the data structures, as per L<dump()>, but with the
characters E<lt>, E<gt> and E<amp> converted to their equivalent HTML
entities and newlines converted to E<lt>brE<gt>.
[% USE Dumper %]
[% Dumper.dump_html(myvar) %]
=head1 AUTHOR
Simon Matthews E<lt>sam@tt2.orgE<gt>
=head1 COPYRIGHT
Copyright (C) 2000 Simon Matthews. All Rights Reserved.
This module is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.
=head1 SEE ALSO
L<Template::Plugin>, L<Data::Dumper>

View File

@@ -0,0 +1,391 @@
#============================================================= -*-Perl-*-
#
# Template::Plugin::File
#
# DESCRIPTION
# Plugin for encapsulating information about a system file.
#
# AUTHOR
# Originally written by Michael Stevens <michael@etla.org> as the
# Directory plugin, then mutilated by Andy Wardley <abw@kfs.org>
# into separate File and Directory plugins, with some additional
# code for working with views, etc.
#
# COPYRIGHT
# Copyright 2000-2007 Michael Stevens, Andy Wardley.
#
# This module is free software; you can redistribute it and/or
# modify it under the same terms as Perl itself.
#
#============================================================================
package Template::Plugin::File;
use strict;
use warnings;
use Cwd;
use File::Spec;
use File::Basename;
use base 'Template::Plugin';
our $VERSION = '3.009';
our @STAT_KEYS = qw( dev ino mode nlink uid gid rdev size
atime mtime ctime blksize blocks );
#------------------------------------------------------------------------
# new($context, $file, \%config)
#
# Create a new File object. Takes the pathname of the file as
# the argument following the context and an optional
# hash reference of configuration parameters.
#------------------------------------------------------------------------
sub new {
my $config = ref($_[-1]) eq 'HASH' ? pop(@_) : { };
my ($class, $context, $path) = @_;
my ($root, $home, @stat, $abs);
return $class->throw('no file specified')
unless defined $path and length $path;
# path, dir, name, root, home
if (File::Spec->file_name_is_absolute($path)) {
$root = '';
}
elsif (($root = $config->{ root })) {
# strip any trailing '/' from root
$root =~ s[/$][];
}
else {
$root = '';
}
my ($name, $dir, $ext) = fileparse($path, '\.\w+');
# fixup various items
$dir =~ s[/$][];
$dir = '' if $dir eq '.';
$name = $name . $ext;
$ext =~ s/^\.//g;
my @fields = File::Spec->splitdir($dir);
shift @fields if @fields && ! length $fields[0];
$home = join('/', ('..') x @fields);
$abs = File::Spec->catfile($root ? $root : (), $path);
my $self = {
path => $path,
name => $name,
root => $root,
home => $home,
dir => $dir,
ext => $ext,
abs => $abs,
user => '',
group => '',
isdir => '',
stat => defined $config->{ stat }
? $config->{ stat }
: ! $config->{ nostat },
map { ($_ => '') } @STAT_KEYS,
};
if ($self->{ stat }) {
(@stat = stat( $abs ))
|| return $class->throw("$abs: $!");
@$self{ @STAT_KEYS } = @stat;
unless ($config->{ noid }) {
$self->{ user } = eval { getpwuid( $self->{ uid }) || $self->{ uid } };
$self->{ group } = eval { getgrgid( $self->{ gid }) || $self->{ gid } };
}
$self->{ isdir } = -d $abs;
}
bless $self, $class;
}
#-------------------------------------------------------------------------
# rel($file)
#
# Generate a relative filename for some other file relative to this one.
#------------------------------------------------------------------------
sub rel {
my ($self, $path) = @_;
$path = $path->{ path } if ref $path eq ref $self; # assumes same root
return $path if $path =~ m[^/];
return $path unless $self->{ home };
return $self->{ home } . '/' . $path;
}
#------------------------------------------------------------------------
# present($view)
#
# Present self to a Template::View.
#------------------------------------------------------------------------
sub present {
my ($self, $view) = @_;
$view->view_file($self);
}
sub throw {
my ($self, $error) = @_;
die (Template::Exception->new('File', $error));
}
1;
__END__
=head1 NAME
Template::Plugin::File - Plugin providing information about files
=head1 SYNOPSIS
[% USE File(filepath) %]
[% File.path %] # full path
[% File.name %] # filename
[% File.dir %] # directory
=head1 DESCRIPTION
This plugin provides an abstraction of a file. It can be used to
fetch details about files from the file system, or to represent abstract
files (e.g. when creating an index page) that may or may not exist on
a file system.
A file name or path should be specified as a constructor argument. e.g.
[% USE File('foo.html') %]
[% USE File('foo/bar/baz.html') %]
[% USE File('/foo/bar/baz.html') %]
The file should exist on the current file system (unless C<nostat>
option set, see below) as an absolute file when specified with as
leading 'C</>' as per 'C</foo/bar/baz.html>', or otherwise as one relative
to the current working directory. The constructor performs a C<stat()>
on the file and makes the 13 elements returned available as the plugin
items:
dev ino mode nlink uid gid rdev size
atime mtime ctime blksize blocks
e.g.
[% USE File('/foo/bar/baz.html') %]
[% File.mtime %]
[% File.mode %]
...
In addition, the C<user> and C<group> items are set to contain the user
and group names as returned by calls to C<getpwuid()> and C<getgrgid()> for
the file C<uid> and C<gid> elements, respectively. On Win32 platforms
on which C<getpwuid()> and C<getgrid()> are not available, these values are
undefined.
[% USE File('/tmp/foo.html') %]
[% File.uid %] # e.g. 500
[% File.user %] # e.g. abw
This user/group lookup can be disabled by setting the C<noid> option.
[% USE File('/tmp/foo.html', noid=1) %]
[% File.uid %] # e.g. 500
[% File.user %] # nothing
The C<isdir> flag will be set if the file is a directory.
[% USE File('/tmp') %]
[% File.isdir %] # 1
If the C<stat()> on the file fails (e.g. file doesn't exists, bad
permission, etc) then the constructor will throw a C<File> exception.
This can be caught within a C<TRY...CATCH> block.
[% TRY %]
[% USE File('/tmp/myfile') %]
File exists!
[% CATCH File %]
File error: [% error.info %]
[% END %]
Note the capitalisation of the exception type, 'C<File>', to indicate an
error thrown by the C<File> plugin, to distinguish it from a regular
C<file> exception thrown by the Template Toolkit.
Note that the C<File> plugin can also be referenced by the lower case
name 'C<file>'. However, exceptions are always thrown of the C<File>
type, regardless of the capitalisation of the plugin named used.
[% USE file('foo.html') %]
[% file.mtime %]
As with any other Template Toolkit plugin, an alternate name can be
specified for the object created.
[% USE foo = file('foo.html') %]
[% foo.mtime %]
The C<nostat> option can be specified to prevent the plugin constructor
from performing a C<stat()> on the file specified. In this case, the
file does not have to exist in the file system, no attempt will be made
to verify that it does, and no error will be thrown if it doesn't.
The entries for the items usually returned by C<stat()> will be set
empty.
[% USE file('/some/where/over/the/rainbow.html', nostat=1)
[% file.mtime %] # nothing
=head1 METHODS
All C<File> plugins, regardless of the C<nostat> option, have set a number
of items relating to the original path specified.
=head2 path
The full, original file path specified to the constructor.
[% USE file('/foo/bar.html') %]
[% file.path %] # /foo/bar.html
=head2 name
The name of the file without any leading directories.
[% USE file('/foo/bar.html') %]
[% file.name %] # bar.html
=head2 dir
The directory element of the path with the filename removed.
[% USE file('/foo/bar.html') %]
[% file.name %] # /foo
=head2 ext
The file extension, if any, appearing at the end of the path following
a 'C<.>' (not included in the extension).
[% USE file('/foo/bar.html') %]
[% file.ext %] # html
=head2 home
This contains a string of the form 'C<../..>' to represent the upward path
from a file to its root directory.
[% USE file('bar.html') %]
[% file.home %] # nothing
[% USE file('foo/bar.html') %]
[% file.home %] # ..
[% USE file('foo/bar/baz.html') %]
[% file.home %] # ../..
=head2 root
The C<root> item can be specified as a constructor argument, indicating
a root directory in which the named file resides. This is otherwise
set empty.
[% USE file('foo/bar.html', root='/tmp') %]
[% file.root %] # /tmp
=head2 abs
This returns the absolute file path by constructing a path from the
C<root> and C<path> options.
[% USE file('foo/bar.html', root='/tmp') %]
[% file.path %] # foo/bar.html
[% file.root %] # /tmp
[% file.abs %] # /tmp/foo/bar.html
=head2 rel(path)
This returns a relative path from the current file to another path specified
as an argument. It is constructed by appending the path to the 'C<home>'
item.
[% USE file('foo/bar/baz.html') %]
[% file.rel('wiz/waz.html') %] # ../../wiz/waz.html
=head1 EXAMPLES
[% USE file('/foo/bar/baz.html') %]
[% file.path %] # /foo/bar/baz.html
[% file.dir %] # /foo/bar
[% file.name %] # baz.html
[% file.home %] # ../..
[% file.root %] # ''
[% file.abs %] # /foo/bar/baz.html
[% file.ext %] # html
[% file.mtime %] # 987654321
[% file.atime %] # 987654321
[% file.uid %] # 500
[% file.user %] # abw
[% USE file('foo.html') %]
[% file.path %] # foo.html
[% file.dir %] # ''
[% file.name %] # foo.html
[% file.root %] # ''
[% file.home %] # ''
[% file.abs %] # foo.html
[% USE file('foo/bar/baz.html') %]
[% file.path %] # foo/bar/baz.html
[% file.dir %] # foo/bar
[% file.name %] # baz.html
[% file.root %] # ''
[% file.home %] # ../..
[% file.abs %] # foo/bar/baz.html
[% USE file('foo/bar/baz.html', root='/tmp') %]
[% file.path %] # foo/bar/baz.html
[% file.dir %] # foo/bar
[% file.name %] # baz.html
[% file.root %] # /tmp
[% file.home %] # ../..
[% file.abs %] # /tmp/foo/bar/baz.html
# calculate other file paths relative to this file and its root
[% USE file('foo/bar/baz.html', root => '/tmp/tt2') %]
[% file.path('baz/qux.html') %] # ../../baz/qux.html
[% file.dir('wiz/woz.html') %] # ../../wiz/woz.html
=head1 AUTHORS
Michael Stevens wrote the original C<Directory> plugin on which this is based.
Andy Wardley split it into separate C<File> and C<Directory> plugins, added
some extra code and documentation for C<VIEW> support, and made a few other
minor tweaks.
=head1 COPYRIGHT
Copyright 2000-2007 Michael Stevens, Andy Wardley.
This module is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.
=head1 SEE ALSO
L<Template::Plugin>, L<Template::Plugin::Directory>, L<Template::View>

View File

@@ -0,0 +1,406 @@
#============================================================= -*-Perl-*-
#
# Template::Plugin::Filter
#
# DESCRIPTION
# Template Toolkit module implementing a base class plugin
# object which acts like a filter and can be used with the
# FILTER directive.
#
# AUTHOR
# Andy Wardley <abw@wardley.org>
#
# COPYRIGHT
# Copyright (C) 2001-2019 Andy Wardley. All Rights Reserved.
#
# This module is free software; you can redistribute it and/or
# modify it under the same terms as Perl itself.
#
#============================================================================
package Template::Plugin::Filter;
use strict;
use warnings;
use base 'Template::Plugin';
use Scalar::Util 'weaken', 'isweak';
our $VERSION = '3.009';
our $DYNAMIC = 0 unless defined $DYNAMIC;
sub new {
my ($class, $context, @args) = @_;
my $config = @args && ref $args[-1] eq 'HASH' ? pop(@args) : { };
# look for $DYNAMIC
my $dynamic;
{
no strict 'refs';
$dynamic = ${"$class\::DYNAMIC"};
}
$dynamic = $DYNAMIC unless defined $dynamic;
my $self = bless {
_CONTEXT => $context,
_DYNAMIC => $dynamic,
_ARGS => \@args,
_CONFIG => $config,
}, $class;
return $self->init($config)
|| $class->error($self->error());
}
sub init {
my ($self, $config) = @_;
return $self;
}
sub factory {
my $self = shift;
my $this = $self;
# avoid a memory leak
weaken( $this->{_CONTEXT} ) if ref $this->{_CONTEXT}
&& !isweak $this->{_CONTEXT};
if ($self->{ _DYNAMIC }) {
return [ sub {
my ($context, @args) = @_;
my $config = ref $args[-1] eq 'HASH' ? pop(@args) : { };
return sub {
$this->filter(shift, \@args, $config);
};
}, 1 ];
}
else {
return sub {
$this->filter(shift);
};
}
}
sub filter {
my ($self, $text, $args, $config) = @_;
return $text;
}
sub merge_config {
my ($self, $newcfg) = @_;
my $owncfg = $self->{ _CONFIG };
return $owncfg unless $newcfg;
return { %$owncfg, %$newcfg };
}
sub merge_args {
my ($self, $newargs) = @_;
my $ownargs = $self->{ _ARGS };
return $ownargs unless $newargs;
return [ @$ownargs, @$newargs ];
}
sub install_filter {
my ($self, $name) = @_;
$self->{ _CONTEXT }->define_filter( $name => $self->factory );
return $self;
}
1;
__END__
=head1 NAME
Template::Plugin::Filter - Base class for plugin filters
=head1 SYNOPSIS
package MyOrg::Template::Plugin::MyFilter;
use Template::Plugin::Filter;
use base qw( Template::Plugin::Filter );
sub filter {
my ($self, $text) = @_;
# ...mungify $text...
return $text;
}
# now load it...
[% USE MyFilter %]
# ...and use the returned object as a filter
[% FILTER $MyFilter %]
...
[% END %]
=head1 DESCRIPTION
This module implements a base class for plugin filters. It hides
the underlying complexity involved in creating and using filters
that get defined and made available by loading a plugin.
To use the module, simply create your own plugin module that is
inherited from the C<Template::Plugin::Filter> class.
package MyOrg::Template::Plugin::MyFilter;
use Template::Plugin::Filter;
use base qw( Template::Plugin::Filter );
Then simply define your C<filter()> method. When called, you get
passed a reference to your plugin object (C<$self>) and the text
to be filtered.
sub filter {
my ($self, $text) = @_;
# ...mungify $text...
return $text;
}
To use your custom plugin, you have to make sure that the Template
Toolkit knows about your plugin namespace.
my $tt2 = Template->new({
PLUGIN_BASE => 'MyOrg::Template::Plugin',
});
Or for individual plugins you can do it like this:
my $tt2 = Template->new({
PLUGINS => {
MyFilter => 'MyOrg::Template::Plugin::MyFilter',
},
});
Then you C<USE> your plugin in the normal way.
[% USE MyFilter %]
The object returned is stored in the variable of the same name,
'C<MyFilter>'. When you come to use it as a C<FILTER>, you should add
a dollar prefix. This indicates that you want to use the filter
stored in the variable 'C<MyFilter>' rather than the filter named
'C<MyFilter>', which is an entirely different thing (see later for
information on defining filters by name).
[% FILTER $MyFilter %]
...text to be filtered...
[% END %]
You can, of course, assign it to a different variable.
[% USE blat = MyFilter %]
[% FILTER $blat %]
...text to be filtered...
[% END %]
Any configuration parameters passed to the plugin constructor from the
C<USE> directive are stored internally in the object for inspection by
the C<filter()> method (or indeed any other method). Positional
arguments are stored as a reference to a list in the C<_ARGS> item while
named configuration parameters are stored as a reference to a hash
array in the C<_CONFIG> item.
For example, loading a plugin as shown here:
[% USE blat = MyFilter 'foo' 'bar' baz = 'blam' %]
would allow the C<filter()> method to do something like this:
sub filter {
my ($self, $text) = @_;
my $args = $self->{ _ARGS }; # [ 'foo', 'bar' ]
my $conf = $self->{ _CONFIG }; # { baz => 'blam' }
# ...munge $text...
return $text;
}
By default, plugins derived from this module will create static
filters. A static filter is created once when the plugin gets
loaded via the C<USE> directive and re-used for all subsequent
C<FILTER> operations. That means that any argument specified with
the C<FILTER> directive are ignored.
Dynamic filters, on the other hand, are re-created each time
they are used by a C<FILTER> directive. This allows them to act
on any parameters passed from the C<FILTER> directive and modify
their behaviour accordingly.
There are two ways to create a dynamic filter. The first is to
define a C<$DYNAMIC> class variable set to a true value.
package MyOrg::Template::Plugin::MyFilter;
use base 'Template::Plugin::Filter';
our $DYNAMIC = 1;
The other way is to set the internal C<_DYNAMIC> value within the C<init()>
method which gets called by the C<new()> constructor.
sub init {
my $self = shift;
$self->{ _DYNAMIC } = 1;
return $self;
}
When this is set to a true value, the plugin will automatically
create a dynamic filter. The outcome is that the C<filter()> method
will now also get passed a reference to an array of positional
arguments and a reference to a hash array of named parameters.
So, using a plugin filter like this:
[% FILTER $blat 'foo' 'bar' baz = 'blam' %]
would allow the C<filter()> method to work like this:
sub filter {
my ($self, $text, $args, $conf) = @_;
# $args = [ 'foo', 'bar' ]
# $conf = { baz => 'blam' }
}
In this case can pass parameters to both the USE and FILTER directives,
so your filter() method should probably take that into account.
[% USE MyFilter 'foo' wiz => 'waz' %]
[% FILTER $MyFilter 'bar' biz => 'baz' %]
...
[% END %]
You can use the C<merge_args()> and C<merge_config()> methods to do a quick
and easy job of merging the local (e.g. C<FILTER>) parameters with the
internal (e.g. C<USE>) values and returning new sets of conglomerated
data.
sub filter {
my ($self, $text, $args, $conf) = @_;
$args = $self->merge_args($args);
$conf = $self->merge_config($conf);
# $args = [ 'foo', 'bar' ]
# $conf = { wiz => 'waz', biz => 'baz' }
...
}
You can also have your plugin install itself as a named filter by
calling the C<install_filter()> method from the C<init()> method. You
should provide a name for the filter, something that you might
like to make a configuration option.
sub init {
my $self = shift;
my $name = $self->{ _CONFIG }->{ name } || 'myfilter';
$self->install_filter($name);
return $self;
}
This allows the plugin filter to be used as follows:
[% USE MyFilter %]
[% FILTER myfilter %]
...
[% END %]
or
[% USE MyFilter name = 'swipe' %]
[% FILTER swipe %]
...
[% END %]
Alternately, you can allow a filter name to be specified as the
first positional argument.
sub init {
my $self = shift;
my $name = $self->{ _ARGS }->[0] || 'myfilter';
$self->install_filter($name);
return $self;
}
[% USE MyFilter 'swipe' %]
[% FILTER swipe %]
...
[% END %]
=head1 EXAMPLE
Here's a complete example of a plugin filter module.
package My::Template::Plugin::Change;
use Template::Plugin::Filter;
use base qw( Template::Plugin::Filter );
sub init {
my $self = shift;
$self->{ _DYNAMIC } = 1;
# first arg can specify filter name
$self->install_filter($self->{ _ARGS }->[0] || 'change');
return $self;
}
sub filter {
my ($self, $text, $args, $config) = @_;
$config = $self->merge_config($config);
my $regex = join('|', keys %$config);
$text =~ s/($regex)/$config->{ $1 }/ge;
return $text;
}
1;
=head1 AUTHOR
Andy Wardley E<lt>abw@wardley.orgE<gt> L<http://wardley.org/>
=head1 COPYRIGHT
Copyright (C) 1996-2020 Andy Wardley. All Rights Reserved.
This module is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.
=head1 SEE ALSO
L<Template::Plugin>, L<Template::Filters>, L<Template::Manual::Filters>
=cut
# Local Variables:
# mode: perl
# perl-indent-level: 4
# indent-tabs-mode: nil
# End:
#
# vim: expandtab shiftwidth=4:

View File

@@ -0,0 +1,93 @@
#============================================================= -*-Perl-*-
#
# Template::Plugin::Format
#
# DESCRIPTION
#
# Simple Template Toolkit Plugin which creates formatting functions.
#
# AUTHOR
# Andy Wardley <abw@wardley.org>
#
# COPYRIGHT
# Copyright (C) 1996-2007 Andy Wardley. All Rights Reserved.
#
# This module is free software; you can redistribute it and/or
# modify it under the same terms as Perl itself.
#
#============================================================================
package Template::Plugin::Format;
use strict;
use warnings;
use base 'Template::Plugin';
our $VERSION = '3.009';
sub new {
my ($class, $context, $format) = @_;;
return defined $format
? make_formatter($format)
: \&make_formatter;
}
sub make_formatter {
my $format = shift;
$format = '%s' unless defined $format;
return sub {
my @args = @_;
push(@args, '') unless @args;
return sprintf($format, @args);
}
}
1;
__END__
=head1 NAME
Template::Plugin::Format - Plugin to create formatting functions
=head1 SYNOPSIS
[% USE format %]
[% commented = format('# %s') %]
[% commented('The cat sat on the mat') %]
[% USE bold = format('<b>%s</b>') %]
[% bold('Hello') %]
=head1 DESCRIPTION
The format plugin constructs sub-routines which format text according to
a C<printf()>-like format string.
=head1 AUTHOR
Andy Wardley E<lt>abw@wardley.orgE<gt> L<http://wardley.org/>
=head1 COPYRIGHT
Copyright (C) 1996-2007 Andy Wardley. All Rights Reserved.
This module is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.
=head1 SEE ALSO
L<Template::Plugin>
=cut
# Local Variables:
# mode: perl
# perl-indent-level: 4
# indent-tabs-mode: nil
# End:
#
# vim: expandtab shiftwidth=4:

View File

@@ -0,0 +1,251 @@
#============================================================= -*-Perl-*-
#
# Template::Plugin::HTML
#
# DESCRIPTION
# Template Toolkit plugin providing useful functionality for generating
# HTML.
#
# AUTHOR
# Andy Wardley <abw@wardley.org>
#
# COPYRIGHT
# Copyright (C) 1996-2007 Andy Wardley. All Rights Reserved.
#
# This module is free software; you can redistribute it and/or
# modify it under the same terms as Perl itself.
#
#============================================================================
package Template::Plugin::HTML;
use strict;
use warnings;
use base 'Template::Plugin';
our $VERSION = '3.009';
sub new {
my ($class, $context, @args) = @_;
my $hash = ref $args[-1] eq 'HASH' ? pop @args : { };
bless {
_SORTED => $hash->{ sorted } || 0,
attributes => $hash->{ attributes } || $hash->{ attrs } || { },
}, $class;
}
sub element {
my ($self, $name, $attr) = @_;
($name, $attr) = %$name if ref $name eq 'HASH';
return '' unless defined $name and length $name;
$attr = $self->attributes($attr);
$attr = " $attr" if $attr;
return "<$name$attr>";
}
sub closed_element {
my ($self, $name, $attr) = @_;
($name, $attr) = %$name if ref $name eq 'HASH';
return '' unless defined $name and length $name;
$attr = $self->attributes( $attr );
$attr = " $attr" if $attr;
return "<$name$attr />";
}
sub attributes {
my ($self, $hash) = @_;
$hash ||= $self->{ attributes };
return '' unless ref $hash eq 'HASH';
my @keys = keys %$hash;
@keys = sort @keys if $self->{ _SORTED };
join(' ', map {
"$_=\"" . $self->escape( $hash->{ $_ } ) . '"';
} @keys);
}
sub add_attributes {
my ($self, $attr) = @_;
return unless ref $attr eq 'HASH';
my $cur = $self->{ attributes };
for (keys %{$attr}) {
$cur->{$_} = exists $cur->{$_}
? $cur->{$_} . " $attr->{$_}"
: $attr->{$_};
}
return;
}
*add_attribute = \&add_attributes;
*add = \&add_attributes;
sub replace_attributes {
my ($self, $attr) = @_;
return unless ref $attr eq 'HASH';
my $cur = $self->{ attributes };
for (keys %{$attr}) {
$cur->{$_} = $attr->{$_};
}
return;
}
*replace_attribute = \&replace_attributes;
*replace = \&replace_attributes;
sub clear_attributes {
my $self = shift;
$self->{ attributes } = { };
return;
}
sub escape {
my ($self, $text) = @_;
for ($text) {
s/&/&amp;/g;
s/</&lt;/g;
s/>/&gt;/g;
s/"/&quot;/g;
}
$text;
}
sub url {
my ($self, $text) = @_;
return undef unless defined $text;
$text =~ s/([^a-zA-Z0-9_.-])/uc sprintf("%%%02x",ord($1))/eg;
return $text;
}
1;
__END__
=head1 NAME
Template::Plugin::HTML - Plugin to create HTML elements
=head1 SYNOPSIS
[% USE HTML %]
[% HTML.escape("if (a < b && c > d) ..." %]
[% HTML.element(table => { border => 1, cellpadding => 2 }) %]
[% HTML.attributes(border => 1, cellpadding => 2) %]
=head1 DESCRIPTION
The C<HTML> plugin is a very basic plugin, implementing a few useful
methods for generating HTML.
=head1 METHODS
=head2 escape(text)
Returns the source text with any HTML reserved characters such as
C<E<lt>>, C<E<gt>>, etc., correctly escaped to their entity equivalents.
=head2 attributes(hash)
Returns the elements of the hash array passed by reference correctly
formatted (e.g. values quoted and correctly escaped) as attributes for
an HTML element.
=head2 add_attribute(attributes)
This provides a way to incrementally add attributes to the object.
The values passed in are stored in the object. Calling
L<element> with just a tag or L<attributes> without an parameters
will used the saved attributes.
USE tag = HTML;
tag.add_attributes( { class => 'navbar' } );
tag.add_attributes( { id => 'foo' } );
tag.add_attributes( { class => 'active' } );
tag.element( 'li' ); # <li class="navbar active" id="foo">
This method has two aliases: add_attribute() and add().
=head2 replace_attribute(attributes)
This will replace an attribute value instead of add to existing.
USE tag = HTML;
tag.add_attributes( { class => 'navbar' } );
tag.add_attributes( { id => 'foo' } );
tag.replace_attributes( { class => 'active' } );
tag.element( 'li' ); # <li class="active" id="foo">
This method has two aliases: replace_attribute() and replace().
=head2 clear_attributes
Clears any saved attributes
=head2 element(type, attributes)
Generates an HTML element of the specified type and with the attributes
provided as an optional hash array reference as the second argument or
as named arguments.
[% HTML.element(table => { border => 1, cellpadding => 2 }) %]
[% HTML.element('table', border=1, cellpadding=2) %]
[% HTML.element(table => attribs) %]
=head1 DEBUGGING
The HTML plugin accepts a C<sorted> option as a constructor argument
which, when set to any true value, causes the attributes generated by
the C<attributes()> method (either directly or via C<element()>) to be
returned in sorted order. Order of attributes isn't important in
HTML, but this is provided mainly for the purposes of debugging where
it is useful to have attributes generated in a deterministic order
rather than whatever order the hash happened to feel like returning
the keys in.
[% USE HTML(sorted=1) %]
[% HTML.element( foo => { charlie => 1, bravo => 2, alpha => 3 } ) %]
generates:
<foo alpha="3" bravo="2" charlie="1">
=head1 AUTHOR
Andy Wardley E<lt>abw@wardley.orgE<gt> L<http://wardley.org/>
=head1 COPYRIGHT
Copyright (C) 1996-2007 Andy Wardley. All Rights Reserved.
This module is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.
=head1 SEE ALSO
L<Template::Plugin>
=cut
# Local Variables:
# mode: perl
# perl-indent-level: 4
# indent-tabs-mode: nil
# End:
#
# vim: expandtab shiftwidth=4:

View File

@@ -0,0 +1,436 @@
#============================================================= -*-Perl-*-
#
# Template::Plugin::Image
#
# DESCRIPTION
# Plugin for encapsulating information about an image.
#
# AUTHOR
# Andy Wardley <abw@wardley.org>
#
# COPYRIGHT
# This module is free software; you can redistribute it and/or
# modify it under the same terms as Perl itself.
#
#============================================================================
package Template::Plugin::Image;
use strict;
use warnings;
use base 'Template::Plugin';
use Template::Exception;
use File::Spec;
our $VERSION = '3.009';
our $AUTOLOAD;
BEGIN {
if (eval { require Image::Info; }) {
*img_info = \&Image::Info::image_info;
}
elsif (eval { require Image::Size; }) {
*img_info = sub {
my $file = shift;
my @stuff = Image::Size::imgsize($file);
return { "width" => $stuff[0],
"height" => $stuff[1],
"error" =>
# imgsize returns either a three letter file type
# or an error message as third value
(defined($stuff[2]) && length($stuff[2]) > 3
? $stuff[2]
: undef),
};
}
}
else {
die(Template::Exception->new("image",
"Couldn't load Image::Info or Image::Size: $@"));
}
}
#------------------------------------------------------------------------
# new($context, $name, \%config)
#
# Create a new Image object. Takes the pathname of the file as
# the argument following the context and an optional
# hash reference of configuration parameters.
#------------------------------------------------------------------------
sub new {
my $config = ref($_[-1]) eq 'HASH' ? pop(@_) : { };
my ($class, $context, $name) = @_;
my ($root, $file, $type);
# name can be a positional or named argument
$name = $config->{ name } unless defined $name;
return $class->throw('no image file specified')
unless defined $name and length $name;
# name can be specified as an absolute path or relative
# to a root directory
if ($root = $config->{ root }) {
$file = File::Spec->catfile($root, $name);
}
else {
$file = defined $config->{file} ? $config->{file} : $name;
}
# Make a note of whether we are using Image::Size or
# Image::Info -- at least for the test suite
$type = $INC{"Image/Size.pm"} ? "Image::Size" : "Image::Info";
# set a default (empty) alt attribute for tag()
$config->{ alt } = '' unless defined $config->{ alt };
# do we want to check to see if file exists?
bless {
%$config,
name => $name,
file => $file,
root => $root,
type => $type,
}, $class;
}
#------------------------------------------------------------------------
# init()
#
# Calls image_info on $self->{ file }
#------------------------------------------------------------------------
sub init {
my $self = shift;
return $self if $self->{ size };
my $image = img_info($self->{ file });
return $self->throw($image->{ error }) if defined $image->{ error };
@$self{ keys %$image } = values %$image;
$self->{ size } = [ $image->{ width }, $image->{ height } ];
$self->{ modtime } = (stat $self->{ file })[10];
return $self;
}
#------------------------------------------------------------------------
# attr()
#
# Return the width and height as HTML/XML attributes.
#------------------------------------------------------------------------
sub attr {
my $self = shift;
my $size = $self->size();
return "width=\"$size->[0]\" height=\"$size->[1]\"";
}
#------------------------------------------------------------------------
# modtime()
#
# Return last modification time as a time_t:
#
# [% date.format(image.modtime, "%Y/%m/%d") %]
#------------------------------------------------------------------------
sub modtime {
my $self = shift;
$self->init;
return $self->{ modtime };
}
#------------------------------------------------------------------------
# tag(\%options)
#
# Return an XHTML img tag.
#------------------------------------------------------------------------
sub tag {
my $self = shift;
my $options = ref $_[0] eq 'HASH' ? shift : { @_ };
my $tag = '<img src="' . $self->name() . '" ' . $self->attr();
# XHTML spec says that the alt attribute is mandatory, so who
# are we to argue?
$options->{ alt } = $self->{ alt }
unless defined $options->{ alt };
if (%$options) {
for my $key (sort keys %$options) {
my $escaped = escape( $options->{$key} );
$tag .= qq[ $key="$escaped"];
}
}
$tag .= ' />';
return $tag;
}
sub escape {
my ($text) = @_;
for ($text) {
s/&/&amp;/g;
s/</&lt;/g;
s/>/&gt;/g;
s/"/&quot;/g;
}
$text;
}
sub throw {
my ($self, $error) = @_;
die (Template::Exception->new('Image', $error));
}
sub AUTOLOAD {
my $self = shift;
(my $a = $AUTOLOAD) =~ s/.*:://;
$self->init;
return $self->{ $a };
}
1;
__END__
=head1 NAME
Template::Plugin::Image - Plugin access to image sizes
=head1 SYNOPSIS
[% USE Image(filename) %]
[% Image.width %]
[% Image.height %]
[% Image.size.join(', ') %]
[% Image.attr %]
[% Image.tag %]
=head1 DESCRIPTION
This plugin provides an interface to the L<Image::Info> or L<Image::Size>
modules for determining the size of image files.
You can specify the plugin name as either 'C<Image>' or 'C<image>'. The
plugin object created will then have the same name. The file name of
the image should be specified as a positional or named argument.
[% # all these are valid, take your pick %]
[% USE Image('foo.gif') %]
[% USE image('bar.gif') %]
[% USE Image 'ping.gif' %]
[% USE image(name='baz.gif') %]
[% USE Image name='pong.gif' %]
A C<root> parameter can be used to specify the location of the image file:
[% USE Image(root='/path/to/root', name='images/home.png') %]
# image path: /path/to/root/images/home.png
# img src: images/home.png
In cases where the image path and image url do not match up, specify the
file name directly:
[% USE Image(file='/path/to/home.png', name='/images/home.png') %]
The C<alt> parameter can be used to specify an alternate name for the
image, for use in constructing an XHTML element (see the C<tag()> method
below).
[% USE Image('home.png', alt="Home") %]
You can also provide an alternate name for an C<Image> plugin object.
[% USE img1 = image 'foo.gif' %]
[% USE img2 = image 'bar.gif' %]
The C<name> method returns the image file name.
[% img1.name %] # foo.gif
The C<width> and C<height> methods return the width and height of the
image, respectively. The C<size> method returns a reference to a 2
element list containing the width and height.
[% USE image 'foo.gif' %]
width: [% image.width %]
height: [% image.height %]
size: [% image.size.join(', ') %]
The C<modtime> method returns the modification time of the file in question,
suitable for use with the L<Date|Template::Plugin::Date> plugin, for example:
[% USE image 'foo.gif' %]
[% USE date %]
[% date.format(image.modtime, "%B, %e %Y") %]
The C<attr> method returns the height and width as HTML/XML attributes.
[% USE image 'foo.gif' %]
[% image.attr %]
Typical output:
width="60" height="20"
The C<tag> method returns a complete XHTML tag referencing the image.
[% USE image 'foo.gif' %]
[% image.tag %]
Typical output:
<img src="foo.gif" width="60" height="20" alt="" />
You can provide any additional attributes that should be added to the
XHTML tag.
[% USE image 'foo.gif' %]
[% image.tag(class="logo" alt="Logo") %]
Typical output:
<img src="foo.gif" width="60" height="20" alt="Logo" class="logo" />
Note that the C<alt> attribute is mandatory in a strict XHTML C<img>
element (even if it's empty) so it is always added even if you don't
explicitly provide a value for it. You can do so as an argument to
the C<tag> method, as shown in the previous example, or as an argument
[% USE image('foo.gif', alt='Logo') %]
=head1 CATCHING ERRORS
If the image file cannot be found then the above methods will throw an
C<Image> error. You can enclose calls to these methods in a
C<TRY...CATCH> block to catch any potential errors.
[% TRY;
image.width;
CATCH;
error; # print error
END
%]
=head1 USING Image::Info
At run time, the plugin tries to load L<Image::Info> in preference to
L<Image::Size>. If L<Image::Info> is found, then some additional methods are
available, in addition to C<size>, C<width>, C<height>, C<attr>, and C<tag>.
These additional methods are named after the elements that L<Image::Info>
retrieves from the image itself. The types of methods available depend on the
type of image (see L<Image::Info> for more details). These additional methods
will always include the following:
=head2 file_media_type
This is the MIME type that is appropriate for the given file format.
The corresponding value is a string like: "C<image/png>" or "C<image/jpeg>".
=head2 file_ext
The is the suggested file name extension for a file of the given
file format. The value is a 3 letter, lowercase string like
"C<png>", "C<jpg>".
=head2 color_type
The value is a short string describing what kind of values the pixels
encode. The value can be one of the following:
Gray
GrayA
RGB
RGBA
CMYK
YCbCr
CIELab
These names can also be prefixed by "C<Indexed->" if the image is
composed of indexes into a palette. Of these, only "C<Indexed-RGB>" is
likely to occur.
(It is similar to the TIFF field PhotometricInterpretation, but this
name was found to be too long, so we used the PNG inspired term
instead.)
=head2 resolution
The value of this field normally gives the physical size of the image
on screen or paper. When the unit specifier is missing then this field
denotes the squareness of pixels in the image.
The syntax of this field is:
<res> <unit>
<xres> "/" <yres> <unit>
<xres> "/" <yres>
The C<E<lt>resE<gt>>, C<E<lt>xresE<gt>> and C<E<lt>yresE<gt>> fields are
numbers. The C<E<lt>unitE<gt>> is a string like C<dpi>, C<dpm> or
C<dpcm> (denoting "dots per inch/cm/meter).
=head2 SamplesPerPixel
This says how many channels there are in the image. For some image
formats this number might be higher than the number implied from the
C<color_type>.
=head2 BitsPerSample
This says how many bits are used to encode each of samples. The value
is a reference to an array containing numbers. The number of elements
in the array should be the same as C<SamplesPerPixel>.
=head2 Comment
Textual comments found in the file. The value is a reference to an
array if there are multiple comments found.
=head2 Interlace
If the image is interlaced, then this returns the interlace type.
=head2 Compression
This returns the name of the compression algorithm is used.
=head2 Gamma
A number indicating the gamma curve of the image (e.g. 2.2)
=head1 AUTHOR
Andy Wardley E<lt>abw@wardley.orgE<gt> L<http://wardley.org/>
=head1 COPYRIGHT
Copyright (C) 1996-2007 Andy Wardley. All Rights Reserved.
This module is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.
=head1 SEE ALSO
L<Template::Plugin>, L<Image::Info>
=cut
# Local Variables:
# mode: perl
# perl-indent-level: 4
# indent-tabs-mode: nil
# End:
#
# vim: expandtab shiftwidth=4:

View File

@@ -0,0 +1,88 @@
#============================================================= -*-Perl-*-
#
# Template::Plugin::Iterator
#
# DESCRIPTION
#
# Plugin to create a Template::Iterator from a list of items and optional
# configuration parameters.
#
# AUTHOR
# Andy Wardley <abw@wardley.org>
#
# COPYRIGHT
# Copyright (C) 2000-2007 Andy Wardley. All Rights Reserved.
#
# This module is free software; you can redistribute it and/or
# modify it under the same terms as Perl itself.
#
#============================================================================
package Template::Plugin::Iterator;
use strict;
use warnings;
use base 'Template::Plugin';
use Template::Iterator;
our $VERSION = '3.009';
#------------------------------------------------------------------------
# new($context, \@data, \%args)
#------------------------------------------------------------------------
sub new {
my $class = shift;
my $context = shift;
Template::Iterator->new(@_);
}
1;
__END__
=head1 NAME
Template::Plugin::Iterator - Plugin to create iterators (Template::Iterator)
=head1 SYNOPSIS
[% USE iterator(list, args) %]
[% FOREACH item = iterator %]
[% '<ul>' IF iterator.first %]
<li>[% item %]
[% '</ul>' IF iterator.last %]
[% END %]
=head1 DESCRIPTION
The iterator plugin provides a way to create a L<Template::Iterator> object
to iterate over a data set. An iterator is implicitly automatically by the
L<FOREACH> directive. This plugin allows the iterator to be explicitly created
with a given name.
=head1 AUTHOR
Andy Wardley E<lt>abw@wardley.orgE<gt> L<http://wardley.org/>
=head1 COPYRIGHT
Copyright (C) 1996-2007 Andy Wardley. All Rights Reserved.
This module is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.
=head1 SEE ALSO
L<Template::Plugin>, L<Template::Iterator>
=cut
# Local Variables:
# mode: perl
# perl-indent-level: 4
# indent-tabs-mode: nil
# End:
#
# vim: expandtab shiftwidth=4:

View File

@@ -0,0 +1,242 @@
#============================================================= -*-Perl-*-
#
# Template::Plugin::Math
#
# DESCRIPTION
# Plugin implementing numerous mathematical functions.
#
# AUTHORS
# Andy Wardley <abw@wardley.org>
#
# COPYRIGHT
# Copyright (C) 2002-2007 Andy Wardley. All Rights Reserved.
#
# This module is free software; you can redistribute it and/or
# modify it under the same terms as Perl itself.
#
#============================================================================
package Template::Plugin::Math;
use strict;
use warnings;
use base 'Template::Plugin';
our $VERSION = '3.009';
our $AUTOLOAD;
#------------------------------------------------------------------------
# new($context, \%config)
#
# This constructor method creates a simple, empty object to act as a
# receiver for future object calls. No doubt there are many interesting
# configuration options that might be passed, but I'll leave that for
# someone more knowledgable in these areas to contribute...
#------------------------------------------------------------------------
sub new {
my ($class, $context, $config) = @_;
$config ||= { };
bless {
%$config,
}, $class;
}
sub abs { shift; CORE::abs($_[0]); }
sub atan2 { shift; CORE::atan2($_[0], $_[1]); } # prototyped (ugg)
sub cos { shift; CORE::cos($_[0]); }
sub exp { shift; CORE::exp($_[0]); }
sub hex { shift; CORE::hex($_[0]); }
sub int { shift; CORE::int($_[0]); }
sub log { shift; CORE::log($_[0]); }
sub oct { shift; CORE::oct($_[0]); }
sub rand { shift; @_ ? CORE::rand($_[0]) : CORE::rand(); }
sub sin { shift; CORE::sin($_[0]); }
sub sqrt { shift; CORE::sqrt($_[0]); }
sub srand { shift; @_ ? CORE::srand($_[0]) : CORE::srand(); }
# Use the Math::TrulyRandom module
# XXX This is *sloooooooowwwwwwww*
sub truly_random {
eval { require Math::TrulyRandom; }
or die(Template::Exception->new("plugin",
"Can't load Math::TrulyRandom"));
return Math::TrulyRandom::truly_random_value();
}
eval {
require Math::Trig;
no strict qw(refs);
for my $trig_func (@Math::Trig::EXPORT) {
my $sub = Math::Trig->can($trig_func);
*{$trig_func} = sub { shift; &$sub(@_) };
}
};
# To catch errors from a missing Math::Trig
sub AUTOLOAD { return; }
1;
__END__
=head1 NAME
Template::Plugin::Math - Plugin providing mathematical functions
=head1 SYNOPSIS
[% USE Math %]
[% Math.sqrt(9) %]
=head1 DESCRIPTION
The Math plugin provides numerous mathematical functions for use
within templates.
=head1 METHODS
C<Template::Plugin::Math> makes available the following functions from
the Perl core:
=over 4
=item abs
=item atan2
=item cos
=item exp
=item hex
=item int
=item log
=item oct
=item rand
=item sin
=item sqrt
=item srand
=back
In addition, if the L<Math::Trig> module can be loaded, the following
functions are also available:
=over 4
=item pi
=item tan
=item csc
=item cosec
=item sec
=item cot
=item cotan
=item asin
=item acos
=item atan
=item acsc
=item acosec
=item asec
=item acot
=item acotan
=item sinh
=item cosh
=item tanh
=item csch
=item cosech
=item sech
=item coth
=item cotanh
=item asinh
=item acosh
=item atanh
=item acsch
=item acosech
=item asech
=item acoth
=item acotanh
=item rad2deg
=item rad2grad
=item deg2rad
=item deg2grad
=item grad2rad
=item grad2deg
=back
If the L<Math::TrulyRandom> module is available, and you've got the time
to wait, the C<truly_random_number> method is available:
[% Math.truly_random_number %]
=head1 AUTHOR
Andy Wardley E<lt>abw@wardley.orgE<gt> L<http://wardley.org/>
=head1 COPYRIGHT
Copyright (C) 1996-2007 Andy Wardley. All Rights Reserved.
This module is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.
=head1 SEE ALSO
L<Template::Plugin>
=cut
# Local Variables:
# mode: perl
# perl-indent-level: 4
# indent-tabs-mode: nil
# End:
#
# vim: expandtab shiftwidth=4:

View File

@@ -0,0 +1,87 @@
#==============================================================================
#
# Template::Plugin::Pod
#
# DESCRIPTION
# Pod parser and object model.
#
# AUTHOR
# Andy Wardley <abw@wardley.org>
#
# COPYRIGHT
# Copyright (C) 2000-2007 Andy Wardley. All Rights Reserved.
#
# This module is free software; you can redistribute it and/or
# modify it under the same terms as Perl itself.
#
#============================================================================
package Template::Plugin::Pod;
use strict;
use warnings;
use base 'Template::Plugin';
use Pod::POM;
our $VERSION = '3.009';
#------------------------------------------------------------------------
# new($context, \%config)
#------------------------------------------------------------------------
sub new {
my $class = shift;
my $context = shift;
Pod::POM->new(@_);
}
1;
__END__
=head1 NAME
Template::Plugin::Pod - Plugin interface to Pod::POM (Pod Object Model)
=head1 SYNOPSIS
[% USE Pod(podfile) %]
[% FOREACH head1 = Pod.head1;
FOREACH head2 = head1/head2;
...
END;
END
%]
=head1 DESCRIPTION
This plugin is an interface to the L<Pod::POM> module.
=head1 AUTHOR
Andy Wardley E<lt>abw@wardley.orgE<gt> L<http://wardley.org/>
=head1 COPYRIGHT
Copyright (C) 1996-2007 Andy Wardley. All Rights Reserved.
This module is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.
=head1 SEE ALSO
L<Template::Plugin>, L<Pod::POM>
=cut
# Local Variables:
# mode: perl
# perl-indent-level: 4
# indent-tabs-mode: nil
# End:
#
# vim: expandtab shiftwidth=4:

View File

@@ -0,0 +1,137 @@
#==============================================================================
#
# Template::Plugin::Procedural
#
# DESCRIPTION
# A Template Plugin to provide a Template Interface to Data::Dumper
#
# AUTHOR
# Mark Fowler <mark@twoshortplanks.com>
#
# COPYRIGHT
# Copyright (C) 2002 Mark Fowler. All Rights Reserved
#
# This module is free software; you can redistribute it and/or
# modify it under the same terms as Perl itself.
#
#==============================================================================
package Template::Plugin::Procedural;
use strict;
use warnings;
use base 'Template::Plugin';
our $VERSION = '3.009';
our $DEBUG = 0 unless defined $DEBUG;
our $AUTOLOAD;
#------------------------------------------------------------------------
# load
#------------------------------------------------------------------------
sub load {
my ($class, $context) = @_;
# create a proxy namespace that will be used for objects
my $proxy = "Template::Plugin::" . $class;
# okay, in our proxy create the autoload routine that will
# call the right method in the real class
no strict "refs";
unless( defined( *{ $proxy . "::AUTOLOAD" } ) ) {
*{ $proxy . "::AUTOLOAD" } = sub {
# work out what the method is called
$AUTOLOAD =~ s!^.*::!!;
print STDERR "Calling '$AUTOLOAD' in '$class'\n"
if $DEBUG;
# look up the sub for that method (but in a OO way)
my $uboat = $class->can($AUTOLOAD);
# if it existed call it as a subroutine, not as a method
if ($uboat) {
shift @_;
return $uboat->(@_);
}
print STDERR "Eeek, no such method '$AUTOLOAD'\n"
if $DEBUG;
return "";
};
}
# create a simple new method that simply returns a blessed
# scalar as the object.
unless( defined( *{ $proxy . "::new" } ) ) {
*{ $proxy . "::new" } = sub {
my $this;
return bless \$this, $_[0];
};
}
return $proxy;
}
1;
__END__
=head1 NAME
Template::Plugin::Procedural - Base class for procedural plugins
=head1 SYNOPSIS
package Template::Plugin::LWPSimple;
use base qw(Template::Plugin::Procedural);
use LWP::Simple; # exports 'get'
1;
[% USE LWPSimple %]
[% LWPSimple.get("http://www.tt2.org/") %]
=head1 DESCRIPTION
C<Template::Plugin::Procedural> is a base class for Template Toolkit
plugins that causes defined subroutines to be called directly rather
than as a method. Essentially this means that subroutines will not
receive the class name or object as its first argument.
This is most useful when creating plugins for modules that normally
work by exporting subroutines that do not expect such additional
arguments.
Despite the fact that subroutines will not be called in an OO manner,
inheritance still function as normal. A class that uses
C<Template::Plugin::Procedural> can be subclassed and both subroutines
defined in the subclass and subroutines defined in the original class
will be available to the Template Toolkit and will be called without
the class/object argument.
=head1 AUTHOR
Mark Fowler E<lt>mark@twoshortplanks.comE<gt> L<http://www.twoshortplanks.com>
=head1 COPYRIGHT
Copyright (C) 2002 Mark Fowler E<lt>mark@twoshortplanks.comE<gt>
This module is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.
=head1 SEE ALSO
L<Template>, L<Template::Plugin>
=cut
# Local Variables:
# mode: perl
# perl-indent-level: 4
# indent-tabs-mode: nil
# End:
#
# vim: expandtab shiftwidth=4:

View File

@@ -0,0 +1,163 @@
#============================================================= -*-Perl-*-
#
# Template::Plugin::Scalar
#
# DESCRIPTION
# Template Toolkit plugin module which allows you to call object methods
# in scalar context.
#
# AUTHOR
# Andy Wardley <abw@wardley.org>
#
# COPYRIGHT
# Copyright (C) 2008 Andy Wardley. All Rights Reserved.
#
# This module is free software; you can redistribute it and/or
# modify it under the same terms as Perl itself.
#
#============================================================================
package Template::Plugin::Scalar;
use base 'Template::Plugin';
use strict;
use warnings;
use Template::Exception;
use Scalar::Util qw();
our $VERSION = '3.009';
our $MONAD = 'Template::Monad::Scalar';
our $EXCEPTION = 'Template::Exception';
our $AUTOLOAD;
sub load {
my $class = shift;
my $context = shift;
# define .scalar vmethods for hash and list objects
$context->define_vmethod( hash => scalar => \&scalar_monad );
$context->define_vmethod( list => scalar => \&scalar_monad );
return $class;
}
sub scalar_monad {
# create a .scalar monad which wraps the hash- or list-based object
# and delegates any method calls back to it, calling them in scalar
# context, e.g. foo.scalar.bar becomes $MONAD->new($foo)->bar and
# the monad calls $foo->bar in scalar context
$MONAD->new(shift);
}
sub new {
my ($class, $context, @args) = @_;
# create a scalar plugin object which will lookup a variable subroutine
# and call it. e.g. scalar.foo results in a call to foo() in scalar context
my $self = bless {
_CONTEXT => $context,
}, $class;
return $self;
}
sub AUTOLOAD {
my $self = shift;
my $item = $AUTOLOAD;
$item =~ s/.*:://;
return if $item eq 'DESTROY';
# lookup the named values
my $stash = $self->{ _CONTEXT }->stash;
my $value = $stash->{ $item };
if (! defined $value) {
die $EXCEPTION->new( scalar => "undefined value for scalar call: $item" );
}
elsif (ref $value eq 'CODE') {
$value = $value->(@_);
}
return $value;
}
package Template::Monad::Scalar;
our $EXCEPTION = 'Template::Exception';
our $AUTOLOAD;
sub new {
my ($class, $this) = @_;
bless \$this, $class;
}
sub AUTOLOAD {
my $self = shift;
my $this = $$self;
my $item = $AUTOLOAD;
$item =~ s/.*:://;
return if $item eq 'DESTROY';
my $method;
if (Scalar::Util::blessed($this)) {
# lookup the method...
$method = $this->can($item);
}
else {
die $EXCEPTION->new( scalar => "invalid object method: $item" );
}
# ...and call it in scalar context
my $result = $method->($this, @_);
return $result;
}
1;
__END__
=head1 NAME
Template::Plugin::Scalar - call object methods in scalar context
=head1 SYNOPSIS
[% USE scalar %]
# TT2 calls object methods in array context by default
[% object.method %]
# force it to use scalar context
[% object.scalar.method %]
# also works with subroutine references
[% scalar.my_sub_ref %]
=head1 DESCRIPTION
The Template Toolkit calls user-defined subroutines and object methods
using Perl's array context by default. This plugin module provides a way
for you to call subroutines and methods in scalar context.
=head1 AUTHOR
Andy Wardley E<lt>abw@wardley.orgE<gt> L<http://wardley.org/>
=head1 COPYRIGHT
Copyright (C) 2008 Andy Wardley. All Rights Reserved.
This module is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.
=head1 SEE ALSO
L<Template::Plugin>
=cut
# Local Variables:
# mode: perl
# perl-indent-level: 4
# indent-tabs-mode: nil
# End:
#
# vim: expandtab shiftwidth=4:

View File

@@ -0,0 +1,761 @@
#============================================================= -*-Perl-*-
#
# Template::Plugin::String
#
# DESCRIPTION
# Template Toolkit plugin to implement a basic String object.
#
# AUTHOR
# Andy Wardley <abw@wardley.org>
#
# COPYRIGHT
# Copyright (C) 2001-2007 Andy Wardley. All Rights Reserved.
#
# This module is free software; you can redistribute it and/or
# modify it under the same terms as Perl itself.
#
#============================================================================
package Template::Plugin::String;
use strict;
use warnings;
use base 'Template::Plugin';
use Template::Exception;
use overload q|""| => "text",
fallback => 1;
our $VERSION = '3.009';
our $ERROR = '';
*centre = \*center;
*append = \*push;
*prepend = \*unshift;
#------------------------------------------------------------------------
sub new {
my ($class, @args) = @_;
my $context = ref $class ? undef : shift(@args);
my $config = @args && ref $args[-1] eq 'HASH' ? pop(@args) : { };
$class = ref($class) || $class;
my $text = defined $config->{ text }
? $config->{ text }
: (@args ? shift(@args) : '');
# print STDERR "text: [$text]\n";
# print STDERR "class: [$class]\n";
my $self = bless {
text => $text,
filters => [ ],
_CONTEXT => $context,
}, $class;
my $filter = $config->{ filter } || $config->{ filters };
# install any output filters specified as 'filter' or 'filters' option
$self->output_filter($filter)
if $filter;
return $self;
}
sub text {
my $self = shift;
return $self->{ text } unless @{ $self->{ filters } };
my $text = $self->{ text };
my $context = $self->{ _CONTEXT };
foreach my $dispatch (@{ $self->{ filters } }) {
my ($name, $args) = @$dispatch;
my $code = $context->filter($name, $args)
|| $self->throw($context->error());
$text = &$code($text);
}
return $text;
}
sub copy {
my $self = shift;
$self->new($self->{ text });
}
sub throw {
my $self = shift;
die (Template::Exception->new('String', join('', @_)));
}
#------------------------------------------------------------------------
# output_filter($filter)
#
# Install automatic output filter(s) for the string. $filter can a list:
# [ 'name1', 'name2' => [ ..args.. ], name4 => { ..args.. } ] or a hash
# { name1 => '', name2 => [ args ], name3 => { args } }
#------------------------------------------------------------------------
sub output_filter {
my ($self, $filter) = @_;
my ($name, $args, $dispatch);
my $filters = $self->{ filters };
my $count = 0;
if (ref $filter eq 'HASH') {
$filter = [ %$filter ];
}
elsif (ref $filter ne 'ARRAY') {
$filter = [ split(/\s*\W+\s*/, $filter) ];
}
while (@$filter) {
$name = shift @$filter;
# args may follow as a reference (or empty string, e.g. { foo => '' }
if (@$filter && (ref($filter->[0]) || ! length $filter->[0])) {
$args = shift @$filter;
if ($args) {
$args = [ $args ] unless ref $args eq 'ARRAY';
}
else {
$args = [ ];
}
}
else {
$args = [ ];
}
# $self->DEBUG("adding output filter $name(@$args)\n");
push(@$filters, [ $name, $args ]);
$count++;
}
return '';
}
#------------------------------------------------------------------------
sub push {
my $self = shift;
$self->{ text } .= join('', @_);
return $self;
}
sub unshift {
my $self = shift;
$self->{ text } = join('', @_) . $self->{ text };
return $self;
}
sub pop {
my $self = shift;
my $strip = shift || return $self;
$self->{ text } =~ s/$strip$//;
return $self;
}
sub shift {
my $self = shift;
my $strip = shift || return $self;
$self->{ text } =~ s/^$strip//;
return $self;
}
#------------------------------------------------------------------------
sub center {
my ($self, $width) = @_;
my $text = $self->{ text };
my $len = length $text;
$width ||= 0;
if ($len < $width) {
my $lpad = int(($width - $len) / 2);
my $rpad = $width - $len - $lpad;
$self->{ text } = (' ' x $lpad) . $self->{ text } . (' ' x $rpad);
}
return $self;
}
sub left {
my ($self, $width) = @_;
my $len = length $self->{ text };
$width ||= 0;
$self->{ text } .= (' ' x ($width - $len))
if $width > $len;
return $self;
}
sub right {
my ($self, $width) = @_;
my $len = length $self->{ text };
$width ||= 0;
$self->{ text } = (' ' x ($width - $len)) . $self->{ text }
if $width > $len;
return $self;
}
sub format {
my ($self, $format) = @_;
$format = '%s' unless defined $format;
$self->{ text } = sprintf($format, $self->{ text });
return $self;
}
sub filter {
my ($self, $name, @args) = @_;
my $context = $self->{ _CONTEXT };
my $code = $context->filter($name, \@args)
|| $self->throw($context->error());
return &$code($self->{ text });
}
#------------------------------------------------------------------------
sub upper {
my $self = CORE::shift;
$self->{ text } = uc $self->{ text };
return $self;
}
sub lower {
my $self = CORE::shift;
$self->{ text } = lc $self->{ text };
return $self;
}
sub capital {
my $self = CORE::shift;
$self->{ text } =~ s/^(.)/\U$1/;
return $self;
}
#------------------------------------------------------------------------
sub chop {
my $self = CORE::shift;
chop $self->{ text };
return $self;
}
sub chomp {
my $self = CORE::shift;
chomp $self->{ text };
return $self;
}
sub trim {
my $self = CORE::shift;
for ($self->{ text }) {
s/^\s+//;
s/\s+$//;
}
return $self;
}
sub collapse {
my $self = CORE::shift;
for ($self->{ text }) {
s/^\s+//;
s/\s+$//;
s/\s+/ /g
}
return $self;
}
#------------------------------------------------------------------------
sub length {
my $self = CORE::shift;
return length $self->{ text };
}
sub truncate {
my ($self, $length, $suffix) = @_;
return $self unless defined $length;
$suffix ||= '';
return $self if CORE::length $self->{ text } <= $length;
$self->{ text } = CORE::substr($self->{ text }, 0,
$length - CORE::length($suffix)) . $suffix;
return $self;
}
sub substr {
my ($self, $offset, $length, $replacement) = @_;
$offset ||= 0;
if(defined $length) {
if (defined $replacement) {
my $removed = CORE::substr( $self->{text}, $offset, $length );
CORE::substr( $self->{text}, $offset, $length ) = $replacement;
return $removed;
}
else {
return CORE::substr( $self->{text}, $offset, $length );
}
}
else {
return CORE::substr( $self->{text}, $offset );
}
}
sub repeat {
my ($self, $n) = @_;
return $self unless defined $n;
$self->{ text } = $self->{ text } x $n;
return $self;
}
sub replace {
my ($self, $search, $replace) = @_;
return $self unless defined $search;
$replace = '' unless defined $replace;
$self->{ text } =~ s/$search/$replace/g;
return $self;
}
sub remove {
my ($self, $search) = @_;
$search = '' unless defined $search;
$self->{ text } =~ s/$search//g;
return $self;
}
sub split {
my $self = CORE::shift;
my $split = CORE::shift;
my $limit = CORE::shift || 0;
$split = '\s+' unless defined $split;
return [ split($split, $self->{ text }, $limit) ];
}
sub search {
my ($self, $pattern) = @_;
return $self->{ text } =~ /$pattern/;
}
sub equals {
my ($self, $comparison) = @_;
return $self->{ text } eq $comparison;
}
1;
__END__
=head1 NAME
Template::Plugin::String - Object oriented interface for string manipulation
=head1 SYNOPSIS
# create String objects via USE directive
[% USE String %]
[% USE String 'initial text' %]
[% USE String text => 'initial text' %]
# or from an existing String via new()
[% newstring = String.new %]
[% newstring = String.new('newstring text') %]
[% newstring = String.new( text => 'newstring text' ) %]
# or from an existing String via copy()
[% newstring = String.copy %]
# append text to string
[% String.append('text to append') %]
# format left, right or center/centre padded
[% String.left(20) %]
[% String.right(20) %]
[% String.center(20) %] # American spelling
[% String.centre(20) %] # European spelling
# and various other methods...
=head1 DESCRIPTION
This module implements a C<String> class for doing stringy things to
text in an object-oriented way.
You can create a C<String> object via the C<USE> directive, adding any
initial text value as an argument or as the named parameter C<text>.
[% USE String %]
[% USE String 'initial text' %]
[% USE String text='initial text' %]
The object created will be referenced as C<String> by default, but you
can provide a different variable name for the object to be assigned
to:
[% USE greeting = String 'Hello World' %]
Once you've got a C<String> object, you can use it as a prototype to
create other C<String> objects with the C<new()> method.
[% USE String %]
[% greeting = String.new('Hello World') %]
The C<new()> method also accepts an initial text string as an argument
or the named parameter C<text>.
[% greeting = String.new( text => 'Hello World' ) %]
You can also call C<copy()> to create a new C<String> as a copy of the
original.
[% greet2 = greeting.copy %]
The C<String> object has a C<text()> method to return the content of the
string.
[% greeting.text %]
However, it is sufficient to simply print the string and let the
overloaded stringification operator call the C<text()> method
automatically for you.
[% greeting %]
Thus, you can treat C<String> objects pretty much like any regular piece
of text, interpolating it into other strings, for example:
[% msg = "It printed '$greeting' and then dumped core\n" %]
You also have the benefit of numerous other methods for manipulating
the string.
[% msg.append("PS Don't eat the yellow snow") %]
Note that all methods operate on and mutate the contents of the string
itself. If you want to operate on a copy of the string then simply
take a copy first:
[% msg.copy.append("PS Don't eat the yellow snow") %]
These methods return a reference to the C<String> object itself. This
allows you to chain multiple methods together.
[% msg.copy.append('foo').right(72) %]
It also means that in the above examples, the C<String> is returned which
causes the C<text()> method to be called, which results in the new value of
the string being printed. To suppress printing of the string, you can
use the C<CALL> directive.
[% foo = String.new('foo') %]
[% foo.append('bar') %] # prints "foobar"
[% CALL foo.append('bar') %] # nothing
=head1 CONSTRUCTOR METHODS
These methods are used to create new C<String> objects.
=head2 new()
Creates a new string using an initial value passed as a positional
argument or the named parameter C<text>.
[% USE String %]
[% msg = String.new('Hello World') %]
[% msg = String.new( text => 'Hello World' ) %]
=head2 copy()
Creates a new C<String> object which contains a copy of the original string.
[% msg2 = msg.copy %]
=head1 INSPECTOR METHODS
These methods are used to examine the string.
=head2 text()
Returns the internal text value of the string. The stringification
operator is overloaded to call this method. Thus the following are
equivalent:
[% msg.text %]
[% msg %]
=head2 length()
Returns the length of the string.
[% USE String("foo") %]
[% String.length %] # => 3
=head2 search($pattern)
Searches the string for the regular expression specified in C<$pattern>
returning true if found or false otherwise.
[% item = String.new('foo bar baz wiz waz woz') %]
[% item.search('wiz') ? 'WIZZY! :-)' : 'not wizzy :-(' %]
=head2 split($pattern, $limit)
Splits the string based on the delimiter C<$pattern> and optional C<$limit>.
Delegates to Perl's internal C<split()> so the parameters are exactly the same.
[% FOREACH item.split %]
...
[% END %]
[% FOREACH item.split('baz|waz') %]
...
[% END %]
=head1 MUTATOR METHODS
These methods modify the internal value of the string. For example:
[% USE str=String('foobar') %]
[% str.append('.html') %] # str => 'foobar.html'
The value of C<str> is now 'C<foobar.html>'. If you don't want
to modify the string then simply take a copy first.
[% str.copy.append('.html') %]
These methods all return a reference to the C<String> object itself. This
has two important benefits. The first is that when used as above, the
C<String> object 'C<str>' returned by the C<append()> method will be stringified
with a call to its C<text()> method. This will return the newly modified
string content. In other words, a directive like:
[% str.append('.html') %]
will update the string and also print the new value. If you just want
to update the string but not print the new value then use C<CALL>.
[% CALL str.append('.html') %]
The other benefit of these methods returning a reference to the C<String>
is that you can chain as many different method calls together as you
like. For example:
[% String.append('.html').trim.format(href) %]
Here are the methods:
=head2 push($suffix, ...) / append($suffix, ...)
Appends all arguments to the end of the string. The
C<append()> method is provided as an alias for C<push()>.
[% msg.push('foo', 'bar') %]
[% msg.append('foo', 'bar') %]
=head2 pop($suffix)
Removes the suffix passed as an argument from the end of the String.
[% USE String 'foo bar' %]
[% String.pop(' bar') %] # => 'foo'
=head2 unshift($prefix, ...) / prepend($prefix, ...)
Prepends all arguments to the beginning of the string. The
C<prepend()> method is provided as an alias for C<unshift()>.
[% msg.unshift('foo ', 'bar ') %]
[% msg.prepend('foo ', 'bar ') %]
=head2 shift($prefix)
Removes the prefix passed as an argument from the start of the String.
[% USE String 'foo bar' %]
[% String.shift('foo ') %] # => 'bar'
=head2 left($pad)
If the length of the string is less than C<$pad> then the string is left
formatted and padded with spaces to C<$pad> length.
[% msg.left(20) %]
=head2 right($pad)
As per L<left()> but right padding the C<String> to a length of C<$pad>.
[% msg.right(20) %]
=head2 center($pad) / centre($pad)
As per L<left()> and L<right()> but formatting the C<String> to be centered within
a space padded string of length C<$pad>. The C<centre()> method is provided as
an alias for C<center()>.
[% msg.center(20) %] # American spelling
[% msg.centre(20) %] # European spelling
=head2 format($format)
Apply a format in the style of C<sprintf()> to the string.
[% USE String("world") %]
[% String.format("Hello %s\n") %] # => "Hello World\n"
=head2 upper()
Converts the string to upper case.
[% USE String("foo") %]
[% String.upper %] # => 'FOO'
=head2 lower()
Converts the string to lower case
[% USE String("FOO") %]
[% String.lower %] # => 'foo'
=head2 capital()
Converts the first character of the string to upper case.
[% USE String("foo") %]
[% String.capital %] # => 'Foo'
The remainder of the string is left untouched. To force the string to
be all lower case with only the first letter capitalised, you can do
something like this:
[% USE String("FOO") %]
[% String.lower.capital %] # => 'Foo'
=head2 chop()
Removes the last character from the string.
[% USE String("foop") %]
[% String.chop %] # => 'foo'
=head2 chomp()
Removes the trailing newline from the string.
[% USE String("foo\n") %]
[% String.chomp %] # => 'foo'
=head2 trim()
Removes all leading and trailing whitespace from the string
[% USE String(" foo \n\n ") %]
[% String.trim %] # => 'foo'
=head2 collapse()
Removes all leading and trailing whitespace and collapses any sequences
of multiple whitespace to a single space.
[% USE String(" \n\r \t foo \n \n bar \n") %]
[% String.collapse %] # => "foo bar"
=head2 truncate($length, $suffix)
Truncates the string to C<$length> characters.
[% USE String('long string') %]
[% String.truncate(4) %] # => 'long'
If C<$suffix> is specified then it will be appended to the truncated
string. In this case, the string will be further shortened by the
length of the suffix to ensure that the newly constructed string
complete with suffix is exactly C<$length> characters long.
[% USE msg = String('Hello World') %]
[% msg.truncate(8, '...') %] # => 'Hello...'
=head2 replace($search, $replace)
Replaces all occurrences of C<$search> in the string with C<$replace>.
[% USE String('foo bar foo baz') %]
[% String.replace('foo', 'wiz') %] # => 'wiz bar wiz baz'
=head2 remove($search)
Remove all occurrences of C<$search> in the string.
[% USE String('foo bar foo baz') %]
[% String.remove('foo ') %] # => 'bar baz'
=head2 repeat($count)
Repeats the string C<$count> times.
[% USE String('foo ') %]
[% String.repeat(3) %] # => 'foo foo foo '
=head1 AUTHOR
Andy Wardley E<lt>abw@wardley.orgE<gt> L<http://wardley.org/>
=head1 COPYRIGHT
Copyright (C) 1996-2007 Andy Wardley. All Rights Reserved.
This module is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.
=head1 SEE ALSO
L<Template::Plugin>
=cut
# Local Variables:
# mode: perl
# perl-indent-level: 4
# indent-tabs-mode: nil
# End:
#
# vim: expandtab shiftwidth=4:

View File

@@ -0,0 +1,441 @@
#============================================================= -*-Perl-*-
#
# Template::Plugin::Table
#
# DESCRIPTION
# Plugin to order a linear data set into a virtual 2-dimensional table
# from which row and column permutations can be fetched.
#
# AUTHOR
# Andy Wardley <abw@wardley.org>
#
# COPYRIGHT
# Copyright (C) 2000-2007 Andy Wardley. All Rights Reserved.
#
# This module is free software; you can redistribute it and/or
# modify it under the same terms as Perl itself.
#
#============================================================================
package Template::Plugin::Table;
use strict;
use warnings;
use base 'Template::Plugin';
use Scalar::Util 'blessed';
our $VERSION = '3.009';
our $AUTOLOAD;
#------------------------------------------------------------------------
# new($context, \@data, \%args)
#
# This constructor method initialises the object to iterate through
# the data set passed by reference to a list as the first parameter.
# It calculates the shape of the permutation table based on the ROWS
# or COLS parameters specified in the $args hash reference. The
# OVERLAP parameter may be provided to specify the number of common
# items that should be shared between subsequent columns.
#------------------------------------------------------------------------
sub new {
my ($class, $context, $data, $params) = @_;
my ($size, $rows, $cols, $coloff, $overlap, $error);
# if the data item is a reference to a Template::Iterator object,
# or subclass thereof, we call its get_all() method to extract all
# the data it contains
if (blessed($data) && $data->isa('Template::Iterator')) {
($data, $error) = $data->get_all();
return $class->error("iterator failed to provide data for table: ",
$error)
if $error;
}
return $class->error('invalid table data, expecting a list')
unless ref $data eq 'ARRAY';
$params ||= { };
return $class->error('invalid table parameters, expecting a hash')
unless ref $params eq 'HASH';
# ensure keys are folded to upper case
@$params{ map { uc } keys %$params } = values %$params;
$size = scalar @$data;
$overlap = $params->{ OVERLAP } || 0;
# calculate number of columns based on a specified number of rows
if ($rows = $params->{ ROWS }) {
if ($size < $rows) {
$rows = $size; # pad?
$cols = 1;
$coloff = 0;
}
else {
$coloff = $rows - $overlap;
$cols = int ($size / $coloff)
+ ($size % $coloff > $overlap ? 1 : 0)
}
}
# calculate number of rows based on a specified number of columns
elsif ($cols = $params->{ COLS }) {
if ($size < $cols) {
$cols = $size;
$rows = 1;
$coloff = 1;
}
else {
$coloff = int ($size / $cols)
+ ($size % $cols > $overlap ? 1 : 0);
$rows = $coloff + $overlap;
}
}
else {
$rows = $size;
$cols = 1;
$coloff = 0;
}
bless {
_DATA => $data,
_SIZE => $size,
_NROWS => $rows,
_NCOLS => $cols,
_COLOFF => $coloff,
_OVERLAP => $overlap,
_PAD => defined $params->{ PAD } ? $params->{ PAD } : 1,
}, $class;
}
#------------------------------------------------------------------------
# row($n)
#
# Returns a reference to a list containing the items in the row whose
# number is specified by parameter. If the row number is undefined,
# it calls rows() to return a list of all rows.
#------------------------------------------------------------------------
sub row {
my ($self, $row) = @_;
my ($data, $cols, $offset, $size, $pad)
= @$self{ qw( _DATA _NCOLS _COLOFF _SIZE _PAD) };
my @set;
# return all rows if row number not specified
return $self->rows()
unless defined $row;
return () if $row >= $self->{ _NROWS } || $row < 0;
my $index = $row;
for (my $c = 0; $c < $cols; $c++) {
push(@set, $index < $size
? $data->[$index]
: ($pad ? undef : ()));
$index += $offset;
}
return \@set;
}
#------------------------------------------------------------------------
# col($n)
#
# Returns a reference to a list containing the items in the column whose
# number is specified by parameter. If the column number is undefined,
# it calls cols() to return a list of all columns.
#------------------------------------------------------------------------
sub col {
my ($self, $col) = @_;
my ($data, $size) = @$self{ qw( _DATA _SIZE ) };
my ($start, $end);
my $blanks = 0;
# return all cols if row number not specified
return $self->cols()
unless defined $col;
return () if $col >= $self->{ _NCOLS } || $col < 0;
$start = $self->{ _COLOFF } * $col;
$end = $start + $self->{ _NROWS } - 1;
$end = $start if $end < $start;
if ($end >= $size) {
$blanks = ($end - $size) + 1;
$end = $size - 1;
}
return () if $start >= $size;
return [ @$data[$start..$end],
$self->{ _PAD } ? ((undef) x $blanks) : () ];
}
#------------------------------------------------------------------------
# rows()
#
# Returns all rows as a reference to a list of rows.
#------------------------------------------------------------------------
sub rows {
my $self = shift;
return [ map { $self->row($_) } (0..$self->{ _NROWS }-1) ];
}
#------------------------------------------------------------------------
# cols()
#
# Returns all rows as a reference to a list of rows.
#------------------------------------------------------------------------
sub cols {
my $self = shift;
return [ map { $self->col($_) } (0..$self->{ _NCOLS }-1) ];
}
#------------------------------------------------------------------------
# AUTOLOAD
#
# Provides read access to various internal data members.
#------------------------------------------------------------------------
sub AUTOLOAD {
my $self = shift;
my $item = $AUTOLOAD;
$item =~ s/.*:://;
return if $item eq 'DESTROY';
if ($item =~ /^(?:data|size|nrows|ncols|overlap|pad)$/) {
return $self->{ $item };
}
else {
return (undef, "no such table method: $item");
}
}
1;
__END__
=head1 NAME
Template::Plugin::Table - Plugin to present data in a table
=head1 SYNOPSIS
[% USE table(list, rows=n, cols=n, overlap=n, pad=0) %]
[% FOREACH item IN table.row(n) %]
[% item %]
[% END %]
[% FOREACH item IN table.col(n) %]
[% item %]
[% END %]
[% FOREACH row IN table.rows %]
[% FOREACH item IN row %]
[% item %]
[% END %]
[% END %]
[% FOREACH col IN table.cols %]
[% col.first %] - [% col.last %] ([% col.size %] entries)
[% END %]
=head1 DESCRIPTION
The C<Table> plugin allows you to format a list of data items into a
virtual table. When you create a C<Table> plugin via the C<USE> directive,
simply pass a list reference as the first parameter and then specify
a fixed number of rows or columns.
[% USE Table(list, rows=5) %]
[% USE table(list, cols=5) %]
The C<Table> plugin name can also be specified in lower case as shown
in the second example above. You can also specify an alternative variable
name for the plugin as per regular Template Toolkit syntax.
[% USE mydata = table(list, rows=5) %]
The plugin then presents a table based view on the data set. The data
isn't actually reorganised in any way but is available via the C<row()>,
C<col()>, C<rows()> and C<cols()> as if formatted into a simple two dimensional
table of C<n> rows x C<n> columns.
So if we had a sample C<alphabet> list contained the letters 'C<a>' to 'C<z>',
the above C<USE> directives would create plugins that represented the following
views of the alphabet.
[% USE table(alphabet, ... %]
rows=5 cols=5
a f k p u z a g m s y
b g l q v b h n t z
c h m r w c i o u
d i n s x d j p v
e j o t y e k q w
f l r x
We can request a particular row or column using the C<row()> and C<col()>
methods.
[% USE table(alphabet, rows=5) %]
[% FOREACH item = table.row(0) %]
# [% item %] set to each of [ a f k p u z ] in turn
[% END %]
[% FOREACH item = table.col(2) %]
# [% item %] set to each of [ m n o p q r ] in turn
[% END %]
Data in rows is returned from left to right, columns from top to
bottom. The first row/column is 0. By default, rows or columns that
contain empty values will be padded with the undefined value to fill
it to the same size as all other rows or columns.
For example, the last row (row 4) in the first example would contain the
values C<[ e j o t y undef ]>. The Template Toolkit will safely accept these
undefined values and print a empty string. You can also use the IF directive
to test if the value is set.
[% FOREACH item = table.row(4) %]
[% IF item %]
Item: [% item %]
[% END %]
[% END %]
You can explicitly disable the C<pad> option when creating the plugin to
returned shortened rows/columns where the data is empty.
[% USE table(alphabet, cols=5, pad=0) %]
[% FOREACH item = table.col(4) %]
# [% item %] set to each of 'y z'
[% END %]
The C<rows()> method returns all rows/columns in the table as a reference
to a list of rows (themselves list references). The C<row()> methods
when called without any arguments calls C<rows()> to return all rows in
the table.
Ditto for C<cols()> and C<col()>.
[% USE table(alphabet, cols=5) %]
[% FOREACH row = table.rows %]
[% FOREACH item = row %]
[% item %]
[% END %]
[% END %]
The Template Toolkit provides the C<first>, C<last> and C<size> virtual
methods that can be called on list references to return the first/last entry
or the number of entries in a list. The following example shows how we might
use this to provide an alphabetical index split into 3 even parts.
[% USE table(alphabet, cols=3, pad=0) %]
[% FOREACH group = table.col %]
[ [% group.first %] - [% group.last %] ([% group.size %] letters) ]
[% END %]
This produces the following output:
[ a - i (9 letters) ]
[ j - r (9 letters) ]
[ s - z (8 letters) ]
We can also use the general purpose C<join> virtual method which joins
the items of the list using the connecting string specified.
[% USE table(alphabet, cols=5) %]
[% FOREACH row = table.rows %]
[% row.join(' - ') %]
[% END %]
Data in the table is ordered downwards rather than across but can easily
be transformed on output. For example, to format our data in 5 columns
with data ordered across rather than down, we specify C<rows=5> to order
the data as such:
a f . .
b g .
c h
d i
e j
and then iterate down through each column (a-e, f-j, etc.) printing
the data across.
a b c d e
f g h i j
. .
.
Example code to do so would be much like the following:
[% USE table(alphabet, rows=3) %]
[% FOREACH cols = table.cols %]
[% FOREACH item = cols %]
[% item %]
[% END %]
[% END %]
Output:
a b c
d e f
g h i
j . .
.
In addition to a list reference, the C<Table> plugin constructor may be passed
a reference to a L<Template::Iterator> object or subclass thereof. The
L<Template::Iterator> L<get_all()|Template::Iterator#get_all()> method is
first called on the iterator to return all remaining items. These are then
available via the usual Table interface.
[% USE DBI(dsn,user,pass) -%]
# query() returns an iterator
[% results = DBI.query('SELECT * FROM alphabet ORDER BY letter') %]
# pass into Table plugin
[% USE table(results, rows=8 overlap=1 pad=0) -%]
[% FOREACH row = table.cols -%]
[% row.first.letter %] - [% row.last.letter %]:
[% row.join(', ') %]
[% END %]
=head1 AUTHOR
Andy Wardley E<lt>abw@wardley.orgE<gt> L<http://wardley.org/>
=head1 COPYRIGHT
Copyright (C) 1996-2007 Andy Wardley. All Rights Reserved.
This module is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.
=head1 SEE ALSO
L<Template::Plugin>
=cut
# Local Variables:
# mode: perl
# perl-indent-level: 4
# indent-tabs-mode: nil
# End:
#
# vim: expandtab shiftwidth=4:

View File

@@ -0,0 +1,204 @@
#============================================================= -*-Perl-*-
#
# Template::Plugin::URL
#
# DESCRIPTION
# Template Toolkit Plugin for constructing URL's from a base stem
# and adaptable parameters.
#
# AUTHOR
# Andy Wardley <abw@wardley.org>
#
# COPYRIGHT
# Copyright (C) 2000-2007 Andy Wardley. All Rights Reserved.
#
# This module is free software; you can redistribute it and/or
# modify it under the same terms as Perl itself.
#
#============================================================================
package Template::Plugin::URL;
use strict;
use warnings;
use base 'Template::Plugin';
our $VERSION = '3.009';
our $JOINT = '&amp;';
#------------------------------------------------------------------------
# new($context, $baseurl, \%url_params)
#
# Constructor method which returns a sub-routine closure for constructing
# complex URL's from a base part and hash of additional parameters.
#------------------------------------------------------------------------
sub new {
my ($class, $context, $base, $args) = @_;
$args ||= { };
return sub {
my $newbase = shift unless ref $_[0] eq 'HASH';
my $newargs = shift || { };
my $combo = { %$args, %$newargs };
my $urlargs = join($JOINT,
map { args($_, $combo->{ $_ }) }
grep { defined $combo->{ $_ } && length $combo->{ $_ } }
sort keys %$combo);
my $query = $newbase || $base || '';
$query .= '?' if length $query && length $urlargs;
$query .= $urlargs if length $urlargs;
return $query
}
}
sub args {
my ($key, $val) = @_;
$key = escape($key);
return map {
"$key=" . escape($_);
} ref $val eq 'ARRAY' ? @$val : $val;
}
#------------------------------------------------------------------------
# escape($url)
#
# URL-encode data. Borrowed with minor modifications from CGI.pm.
# Kudos to Lincold Stein.
#------------------------------------------------------------------------
sub escape {
my $toencode = shift;
return undef unless defined($toencode);
utf8::encode($toencode);
$toencode=~s/([^a-zA-Z0-9_.-])/uc sprintf("%%%02x",ord($1))/eg;
return $toencode;
}
1;
__END__
=head1 NAME
Template::Plugin::URL - Plugin to construct complex URLs
=head1 SYNOPSIS
[% USE url('/cgi-bin/foo.pl') %]
[% url(debug = 1, id = 123) %]
# ==> /cgi/bin/foo.pl?debug=1&amp;id=123
[% USE mycgi = url('/cgi-bin/bar.pl', mode='browse', debug=1) %]
[% mycgi %]
# ==> /cgi/bin/bar.pl?mode=browse&amp;debug=1
[% mycgi(mode='submit') %]
# ==> /cgi/bin/bar.pl?mode=submit&amp;debug=1
[% mycgi(debug='d2 p0', id='D4-2k[4]') %]
# ==> /cgi-bin/bar.pl?mode=browse&amp;debug=d2%20p0&amp;id=D4-2k%5B4%5D
=head1 DESCRIPTION
The C<URL> plugin can be used to construct complex URLs from a base stem
and a hash array of additional query parameters.
The constructor should be passed a base URL and optionally, a hash array
reference of default parameters and values. Used from with a template,
it would look something like the following:
[% USE url('http://www.somewhere.com/cgi-bin/foo.pl') %]
[% USE url('/cgi-bin/bar.pl', mode='browse') %]
[% USE url('/cgi-bin/baz.pl', mode='browse', debug=1) %]
When the plugin is then called without any arguments, the default base
and parameters are returned as a formatted query string.
[% url %]
For the above three examples, these will produce the following outputs:
http://www.somewhere.com/cgi-bin/foo.pl
/cgi-bin/bar.pl?mode=browse
/cgi-bin/baz.pl?mode=browse&amp;debug=1
Note that additional parameters are separated by 'C<&amp;>' rather than
simply 'C<&>'. This is the correct behaviour for HTML pages but is,
unfortunately, incorrect when creating URLs that do not need to be
encoded safely for HTML. This is likely to be corrected in a future
version of the plugin (most probably with TT3). In the mean time, you
can set C<$Template::Plugin::URL::JOINT> to C<&> to get the correct
behaviour.
Additional parameters may be also be specified to the URL:
[% url(mode='submit', id='wiz') %]
Which, for the same three examples, produces:
http://www.somewhere.com/cgi-bin/foo.pl?mode=submit&amp;id=wiz
/cgi-bin/bar.pl?mode=browse&amp;id=wiz
/cgi-bin/baz.pl?mode=browse&amp;debug=1&amp;id=wiz
A new base URL may also be specified as the first option:
[% url('/cgi-bin/waz.pl', test=1) %]
producing
/cgi-bin/waz.pl?test=1
/cgi-bin/waz.pl?mode=browse&amp;test=1
/cgi-bin/waz.pl?mode=browse&amp;debug=1&amp;test=1
The ordering of the parameters is non-deterministic due to fact that
Perl's hashes themselves are unordered. This isn't a problem as the
ordering of CGI parameters is insignificant (to the best of my knowledge).
All values will be properly escaped thanks to some code borrowed from
Lincoln Stein's C<CGI> module. e.g.
[% USE url('/cgi-bin/woz.pl') %]
[% url(name="Elrich von Benjy d'Weiro") %]
Here the space and "C<'>" single quote characters are escaped in the output:
/cgi-bin/woz.pl?name=Elrich%20von%20Benjy%20d%27Weiro
An alternate name may be provided for the plugin at construction time
as per regular Template Toolkit syntax.
[% USE mycgi = url('cgi-bin/min.pl') %]
[% mycgi(debug=1) %]
=head1 AUTHOR
Andy Wardley E<lt>abw@wardley.orgE<gt> L<http://wardley.org/>
=head1 COPYRIGHT
Copyright (C) 1996-2007 Andy Wardley. All Rights Reserved.
This module is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.
=head1 SEE ALSO
L<Template::Plugin>
=cut
# Local Variables:
# mode: perl
# perl-indent-level: 4
# indent-tabs-mode: nil
# End:
#
# vim: expandtab shiftwidth=4:

View File

@@ -0,0 +1,97 @@
#============================================================= -*-Perl-*-
#
# Template::Plugin::View
#
# DESCRIPTION
# A user-definable view based on templates. Similar to the concept of
# a "Skin".
#
# AUTHOR
# Andy Wardley <abw@wardley.org>
#
# COPYRIGHT
# Copyright (C) 2000-2007 Andy Wardley. All Rights Reserved.
#
# This module is free software; you can redistribute it and/or
# modify it under the same terms as Perl itself.
#
#============================================================================
package Template::Plugin::View;
use strict;
use warnings;
use base 'Template::Plugin';
our $VERSION = '3.009';
use Template::View;
#------------------------------------------------------------------------
# new($context, \%config)
#------------------------------------------------------------------------
sub new {
my $class = shift;
my $context = shift;
my $view = Template::View->new($context, @_)
|| return $class->error($Template::View::ERROR);
$view->seal();
return $view;
}
1;
__END__
=head1 NAME
Template::Plugin::View - Plugin to create views (Template::View)
=head1 SYNOPSIS
[% USE view(
prefix = 'splash/' # template prefix/suffix
suffix = '.tt2'
bgcol = '#ffffff' # and any other variables you
style = 'Fancy HTML' # care to define as view metadata,
items = [ foo, bar.baz ] # including complex data and
foo = bar ? baz : x.y.z # expressions
%]
[% view.title %] # access view metadata
[% view.header(title = 'Foo!') %] # view "methods" process blocks or
[% view.footer %] # templates with prefix/suffix added
=head1 DESCRIPTION
This plugin module creates L<Template::View> objects. Views are an
experimental feature and are subject to change in the near future.
In the mean time, please consult L<Template::View> for further info.
=head1 AUTHOR
Andy Wardley E<lt>abw@wardley.orgE<gt> L<http://wardley.org/>
=head1 COPYRIGHT
Copyright (C) 1996-2007 Andy Wardley. All Rights Reserved.
This module is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.
=head1 SEE ALSO
L<Template::Plugin>, L<Template::View>, L<Template::Manual::Views>
=cut
# Local Variables:
# mode: perl
# perl-indent-level: 4
# indent-tabs-mode: nil
# End:
#
# vim: expandtab shiftwidth=4:

View File

@@ -0,0 +1,142 @@
#============================================================= -*-Perl-*-
#
# Template::Plugin::Wrap
#
# DESCRIPTION
# Plugin for wrapping text via the Text::Wrap module.
#
# AUTHOR
# Andy Wardley <abw@wardley.org>
#
# COPYRIGHT
# Copyright (C) 1996-2007 Andy Wardley. All Rights Reserved.
#
# This module is free software; you can redistribute it and/or
# modify it under the same terms as Perl itself.
#
#============================================================================
package Template::Plugin::Wrap;
use strict;
use warnings;
use base 'Template::Plugin';
use Text::Wrap;
our $VERSION = '3.009';
sub new {
my ($class, $context, $format) = @_;;
$context->define_filter('wrap', [ \&wrap_filter_factory => 1 ]);
return \&tt_wrap;
}
sub tt_wrap {
my $text = shift;
my $width = shift || 72;
my $itab = shift;
my $ntab = shift;
$itab = '' unless defined $itab;
$ntab = '' unless defined $ntab;
$Text::Wrap::columns = $width;
Text::Wrap::wrap($itab, $ntab, $text);
}
sub wrap_filter_factory {
my ($context, @args) = @_;
return sub {
my $text = shift;
tt_wrap($text, @args);
}
}
1;
__END__
=head1 NAME
Template::Plugin::Wrap - Plugin interface to Text::Wrap
=head1 SYNOPSIS
[% USE wrap %]
# call wrap subroutine
[% wrap(mytext, width, initial_tab, subsequent_tab) %]
# or use wrap FILTER
[% mytext FILTER wrap(width, initital_tab, subsequent_tab) %]
=head1 DESCRIPTION
This plugin provides an interface to the L<Text::Wrap> module which
provides simple paragraph formatting.
It defines a C<wrap> subroutine which can be called, passing the input
text and further optional parameters to specify the page width (default:
72), and tab characters for the first and subsequent lines (no defaults).
[% USE wrap %]
[% text = BLOCK %]
First, attach the transmutex multiplier to the cross-wired
quantum homogeniser.
[% END %]
[% wrap(text, 40, '* ', ' ') %]
Output:
* First, attach the transmutex
multiplier to the cross-wired quantum
homogeniser.
It also registers a C<wrap> filter which accepts the same three optional
arguments but takes the input text directly via the filter input.
Example 1:
[% FILTER bullet = wrap(40, '* ', ' ') -%]
First, attach the transmutex multiplier to the cross-wired quantum
homogeniser.
[%- END %]
Output:
* First, attach the transmutex
multiplier to the cross-wired quantum
homogeniser.
Example 2:
[% FILTER bullet -%]
Then remodulate the shield to match the harmonic frequency, taking
care to correct the phase difference.
[% END %]
Output:
* Then remodulate the shield to match
the harmonic frequency, taking
care to correct the phase difference.
=head1 AUTHOR
Andy Wardley E<lt>abw@wardley.orgE<gt> L<http://wardley.org/>
The L<Text::Wrap> module was written by David Muir Sharnoff
with help from Tim Pierce and many others.
=head1 COPYRIGHT
Copyright (C) 1996-2007 Andy Wardley. All Rights Reserved.
This module is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.
=head1 SEE ALSO
L<Template::Plugin>, L<Text::Wrap>