diff options
Diffstat (limited to 'perllib/FixMyStreet')
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Contact.pm | 6 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Report/New.pm | 3 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Report/Update.pm | 3 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/View/Web.pm | 20 | ||||
-rw-r--r-- | perllib/FixMyStreet/DB/Result/Comment.pm | 6 | ||||
-rw-r--r-- | perllib/FixMyStreet/DB/Result/ModerationOriginalData.pm | 6 | ||||
-rw-r--r-- | perllib/FixMyStreet/DB/Result/Problem.pm | 17 | ||||
-rw-r--r-- | perllib/FixMyStreet/DB/Result/User.pm | 4 | ||||
-rw-r--r-- | perllib/FixMyStreet/Template.pm | 19 | ||||
-rw-r--r-- | perllib/FixMyStreet/Template/Context.pm | 67 | ||||
-rw-r--r-- | perllib/FixMyStreet/Template/SafeString.pm | 106 | ||||
-rw-r--r-- | perllib/FixMyStreet/Template/Stash.pm | 75 | ||||
-rw-r--r-- | perllib/FixMyStreet/Template/Variable.pm | 177 |
13 files changed, 485 insertions, 24 deletions
diff --git a/perllib/FixMyStreet/App/Controller/Contact.pm b/perllib/FixMyStreet/App/Controller/Contact.pm index 8477dd694..9ce89a9e2 100644 --- a/perllib/FixMyStreet/App/Controller/Contact.pm +++ b/perllib/FixMyStreet/App/Controller/Contact.pm @@ -7,6 +7,7 @@ BEGIN { extends 'Catalyst::Controller'; } use MIME::Base64; use mySociety::EmailUtil; use FixMyStreet::Email; +use FixMyStreet::Template::SafeString; =head1 NAME @@ -253,8 +254,9 @@ generally required to stash sub setup_request : Private { my ( $self, $c ) = @_; - $c->stash->{contact_email} = $c->cobrand->contact_email; - $c->stash->{contact_email} =~ s/\@/@/; + my $email = $c->cobrand->contact_email; + $email =~ s/\@/@/; + $c->stash->{contact_email} = FixMyStreet::Template::SafeString->new($email); for my $param (qw/em subject message/) { $c->stash->{$param} = $c->get_param($param); diff --git a/perllib/FixMyStreet/App/Controller/Report/New.pm b/perllib/FixMyStreet/App/Controller/Report/New.pm index 270ad2ddb..899028ee9 100644 --- a/perllib/FixMyStreet/App/Controller/Report/New.pm +++ b/perllib/FixMyStreet/App/Controller/Report/New.pm @@ -4,6 +4,7 @@ use Moose; use namespace::autoclean; BEGIN { extends 'Catalyst::Controller'; } +use utf8; use Encode; use List::MoreUtils qw(uniq); use List::Util 'first'; @@ -895,7 +896,7 @@ sub process_user : Private { oauth_report => { $report->get_inflated_columns } }; unless ( $c->forward( '/auth/sign_in', [ $params{username} ] ) ) { - $c->stash->{field_errors}->{password} = _('There was a problem with your login information. If you cannot remember your password, or do not have one, please fill in the ‘No’ section of the form.'); + $c->stash->{field_errors}->{password} = _('There was a problem with your login information. If you cannot remember your password, or do not have one, please fill in the ‘No’ section of the form.'); return 1; } my $user = $c->user->obj; diff --git a/perllib/FixMyStreet/App/Controller/Report/Update.pm b/perllib/FixMyStreet/App/Controller/Report/Update.pm index 1dc337c48..610f0f4eb 100644 --- a/perllib/FixMyStreet/App/Controller/Report/Update.pm +++ b/perllib/FixMyStreet/App/Controller/Report/Update.pm @@ -4,6 +4,7 @@ use Moose; use namespace::autoclean; BEGIN { extends 'Catalyst::Controller'; } +use utf8; use Path::Class; use List::Util 'first'; use Utils; @@ -143,7 +144,7 @@ sub process_user : Private { oauth_update => { $update->get_inflated_columns } }; unless ( $c->forward( '/auth/sign_in', [ $params{username} ] ) ) { - $c->stash->{field_errors}->{password} = _('There was a problem with your login information. If you cannot remember your password, or do not have one, please fill in the ‘No’ section of the form.'); + $c->stash->{field_errors}->{password} = _('There was a problem with your login information. If you cannot remember your password, or do not have one, please fill in the ‘No’ section of the form.'); return 1; } my $user = $c->user->obj; diff --git a/perllib/FixMyStreet/App/View/Web.pm b/perllib/FixMyStreet/App/View/Web.pm index 93aa0e2fb..1e1b50094 100644 --- a/perllib/FixMyStreet/App/View/Web.pm +++ b/perllib/FixMyStreet/App/View/Web.pm @@ -6,6 +6,7 @@ use warnings; use FixMyStreet; use FixMyStreet::Template; +use FixMyStreet::Template::SafeString; use Utils; __PACKAGE__->config( @@ -19,6 +20,7 @@ __PACKAGE__->config( 'tprintf', 'prettify_dt', 'version', 'decode', 'prettify_state', + 'mark_safe', ], FILTERS => { add_links => \&add_links, @@ -59,7 +61,15 @@ sprintf (different name to avoid clash) sub tprintf { my ( $self, $c, $format, @args ) = @_; @args = @{$args[0]} if ref $args[0] eq 'ARRAY'; - return sprintf $format, @args; + #$format = $format->plain if UNIVERSAL::isa($format, 'Template::HTML::Variable'); + my $s = sprintf $format, @args; + return FixMyStreet::Template::SafeString->new($s); +} + +sub mark_safe { + my ($self, $c, $s) = @_; + $s = $s->plain if UNIVERSAL::isa($s, 'FixMyStreet::Template::Variable'); + return FixMyStreet::Template::SafeString->new($s); } =head2 Utils::prettify_dt @@ -82,16 +92,16 @@ sub prettify_dt { [% text | add_links | html_para %] -Add some links to some text (and thus HTML-escapes the other text. +Add some links to some text (and thus HTML-escapes the other text). =cut sub add_links { my $text = shift; + $text = FixMyStreet::Template::conditional_escape($text); $text =~ s/\r//g; - $text = FixMyStreet::Template::html_filter($text); $text =~ s{(https?://)([^\s]+)}{"<a href=\"$1$2\">$1" . _space_slash($2) . '</a>'}ge; - return $text; + return FixMyStreet::Template::SafeString->new($text); } sub _space_slash { @@ -113,7 +123,7 @@ sub markup_factory { my $text = shift; return $text unless $user && ($user->from_body || $user->is_superuser); $text =~ s{\*(\S.*?\S)\*}{<i>$1</i>}; - $text; + FixMyStreet::Template::SafeString->new($text); } } diff --git a/perllib/FixMyStreet/DB/Result/Comment.pm b/perllib/FixMyStreet/DB/Result/Comment.pm index 1ffcc7b40..99c5b6dab 100644 --- a/perllib/FixMyStreet/DB/Result/Comment.pm +++ b/perllib/FixMyStreet/DB/Result/Comment.pm @@ -101,6 +101,7 @@ __PACKAGE__->load_components("+FixMyStreet::DB::RABXColumn"); __PACKAGE__->rabx_column('extra'); use Moo; +use FixMyStreet::Template::SafeString; use namespace::clean -except => [ 'meta' ]; use FixMyStreet::Template; @@ -201,7 +202,7 @@ sub moderation_filter { =head2 meta_line Returns a string to be used on a report update, describing some of the metadata -about an update +about an update. Can include HTML. =cut @@ -225,6 +226,7 @@ sub meta_line { } else { $body = $self->user->body; } + $body = FixMyStreet::Template::html_filter($body); if ($body eq 'Bromley Council') { $body = "$body <img src='/cobrands/bromley/favicon.png' alt=''>"; } elsif ($body eq 'Royal Borough of Greenwich') { @@ -259,7 +261,7 @@ sub meta_line { $meta .= ', ' . _( 'and a defect raised' ); } - return $meta; + return FixMyStreet::Template::SafeString->new($meta); }; sub problem_state_processed { diff --git a/perllib/FixMyStreet/DB/Result/ModerationOriginalData.pm b/perllib/FixMyStreet/DB/Result/ModerationOriginalData.pm index 18d2a7683..1805e1fd2 100644 --- a/perllib/FixMyStreet/DB/Result/ModerationOriginalData.pm +++ b/perllib/FixMyStreet/DB/Result/ModerationOriginalData.pm @@ -74,6 +74,7 @@ __PACKAGE__->belongs_to( # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:FLKiZELcfBcc9VwHU2MZYQ use Moo; +use FixMyStreet::Template::SafeString; use Text::Diff; use Data::Dumper; @@ -147,11 +148,12 @@ sub compare_photo { push @deleted, $diff->Items(1); push @added, $diff->Items(2); } - return (join ', ', map { + my $s = (join ', ', map { "<del style='background-color:#fcc'>$_</del>"; } @deleted) . (join ', ', map { "<ins style='background-color:#cfc'>$_</ins>"; } @added); + return FixMyStreet::Template::SafeString->new($s); } sub compare_extra { @@ -212,7 +214,7 @@ sub string_diff { $string .= $inserted; } } - return $string; + return FixMyStreet::Template::SafeString->new($string); } 1; diff --git a/perllib/FixMyStreet/DB/Result/Problem.pm b/perllib/FixMyStreet/DB/Result/Problem.pm index 97f0666e0..4b52cd11d 100644 --- a/perllib/FixMyStreet/DB/Result/Problem.pm +++ b/perllib/FixMyStreet/DB/Result/Problem.pm @@ -201,6 +201,8 @@ use Moo; use namespace::clean -except => [ 'meta' ]; use Utils; use FixMyStreet::Map::FMS; +use FixMyStreet::Template; +use FixMyStreet::Template::SafeString; use LWP::Simple qw($ua); use RABX; use URI; @@ -669,16 +671,16 @@ sub body { my $cache = $problem->result_source->schema->cache; return $cache->{bodies}{$problem->external_body} //= $c->model('DB::Body')->find({ id => $problem->external_body }); } else { - $body = $problem->external_body; + $body = FixMyStreet::Template::html_filter($problem->external_body); } } else { my $bodies = $problem->bodies; my @body_names = sort map { my $name = $_->name; if ($c and FixMyStreet->config('AREA_LINKS_FROM_PROBLEMS')) { - '<a href="' . $_->url . '">' . $name . '</a>'; + '<a href="' . $_->url . '">' . FixMyStreet::Template::html_filter($name) . '</a>'; } else { - $name; + FixMyStreet::Template::html_filter($name); } } values %$bodies; if ( scalar @body_names > 2 ) { @@ -688,7 +690,7 @@ sub body { $body = join( _(' and '), @body_names); } } - return $body; + return FixMyStreet::Template::SafeString->new($body); } @@ -778,17 +780,20 @@ sub can_display_external_id { return 0; } +# This can return HTML and is safe, so returns a FixMyStreet::Template::SafeString sub duration_string { my ( $problem, $c ) = @_; my $body = $c->cobrand->call_hook(link_to_council_cobrand => $problem) || $problem->body($c); my $handler = $c->cobrand->call_hook(get_body_handler_for_problem => $problem); if ( $handler && $handler->call_hook('is_council_with_case_management') ) { - return sprintf(_('Received by %s moments later'), $body); + my $s = sprintf(_('Received by %s moments later'), $body); + return FixMyStreet::Template::SafeString->new($s); } return unless $problem->whensent; - return sprintf(_('Sent to %s %s later'), $body, + my $s = sprintf(_('Sent to %s %s later'), $body, Utils::prettify_duration($problem->whensent->epoch - $problem->confirmed->epoch, 'minute') ); + return FixMyStreet::Template::SafeString->new($s); } sub local_coords { diff --git a/perllib/FixMyStreet/DB/Result/User.pm b/perllib/FixMyStreet/DB/Result/User.pm index 9554bbe7e..4f46fcfe2 100644 --- a/perllib/FixMyStreet/DB/Result/User.pm +++ b/perllib/FixMyStreet/DB/Result/User.pm @@ -449,8 +449,8 @@ sub has_permission_to { return 0 unless $available{$permission_type}; return 1 if $self->is_superuser; - return 0 if !$body_ids || (ref $body_ids && !@$body_ids); - $body_ids = [ $body_ids ] unless ref $body_ids; + return 0 if !$body_ids || (ref $body_ids eq 'ARRAY' && !@$body_ids); + $body_ids = [ $body_ids ] unless ref $body_ids eq 'ARRAY'; my %body_ids = map { $_ => 1 } @$body_ids; foreach (@{$self->body_permissions}) { diff --git a/perllib/FixMyStreet/Template.pm b/perllib/FixMyStreet/Template.pm index ba4376959..354b6c911 100644 --- a/perllib/FixMyStreet/Template.pm +++ b/perllib/FixMyStreet/Template.pm @@ -6,6 +6,9 @@ use warnings; use FixMyStreet; use mySociety::Locale; use Attribute::Handlers; +use FixMyStreet::Template::SafeString; +use FixMyStreet::Template::Context; +use FixMyStreet::Template::Stash; my %FILTERS; my %SUBS; @@ -39,6 +42,8 @@ sub new { my ($class, $config) = @_; $config->{FILTERS}->{$_} = $FILTERS{$_} foreach keys %FILTERS; $config->{ENCODING} = 'utf8'; + $config->{STASH} = FixMyStreet::Template::Stash->new($config); + $config->{CONTEXT} = FixMyStreet::Template::Context->new($config); $class->SUPER::new($config); } @@ -57,7 +62,8 @@ Passes the text to the localisation engine for translations. =cut sub loc : Fn { - return _(@_); + my $s = _(@_); + return FixMyStreet::Template::SafeString->new($s); } =head2 nget @@ -69,7 +75,7 @@ Use first or second string depending on the number. =cut sub nget : Fn { - return mySociety::Locale::nget(@_); + return FixMyStreet::Template::SafeString->new(mySociety::Locale::nget(@_)); } =head2 file_exists @@ -104,6 +110,12 @@ sub html_filter : Filter('html') { return $text; } +sub conditional_escape { + my $text = shift; + $text = html_filter($text) unless UNIVERSAL::isa($text, 'FixMyStreet::Template::SafeString'); + return $text; +} + =head2 html_paragraph Same as Template Toolkit's html_paragraph, but converts single newlines @@ -113,10 +125,11 @@ into <br>s too. sub html_paragraph : Filter('html_para') { my $text = shift; + $text = conditional_escape($text); my @paras = grep { $_ } split(/(?:\r?\n){2,}/, $text); s/\r?\n/<br>\n/g for @paras; $text = "<p>\n" . join("\n</p>\n\n<p>\n", @paras) . "</p>\n"; - return $text; + return FixMyStreet::Template::SafeString->new($text); } 1; diff --git a/perllib/FixMyStreet/Template/Context.pm b/perllib/FixMyStreet/Template/Context.pm new file mode 100644 index 000000000..de3212095 --- /dev/null +++ b/perllib/FixMyStreet/Template/Context.pm @@ -0,0 +1,67 @@ +package FixMyStreet::Template::Context; + +use strict; +use warnings; +use base qw(Template::Context); + +sub filter { + my $self = shift; + my ($name, $args, $alias) = @_; + + # If we're passing through the safe filter, then unwrap + # from a Template::HTML::Variable if we are one. + if ( $name eq 'safe' ) { + return sub { + my $value = shift; + return $value->plain if UNIVERSAL::isa($value, 'FixMyStreet::Template::Variable'); + return $value; + }; + } + + my $filter = $self->SUPER::filter(@_); + + # If we are already going to auto-encode, we don't want to do it again. + # This makes the html filter a no-op on auto-encoded variables. + if ( $name eq 'html' ) { + return sub { + my $value = shift; + return $value if UNIVERSAL::isa($value, 'FixMyStreet::Template::Variable'); + return $filter->($value); + }; + } + + return sub { + my $value = shift; + + if ( UNIVERSAL::isa($value, 'FixMyStreet::Template::Variable') ) { + my $result = $filter->($value->plain); + return $result if UNIVERSAL::isa($result, 'FixMyStreet::Template::SafeString'); + return ref($value)->new($result); + } + + return $filter->($value); + }; +} + +1; +__END__ + +=head1 NAME + +FixMyStreet::Template::Context - Similar to Template::HTML::Context but use +'safe' rather than 'none' to be clear, also prevents html filter double-encoding, +and doesn't rewrap a FixMyStreet::Template::SafeString. + +=head1 AUTHORS + +Martyn Smith, E<lt>msmith@cpan.orgE<gt> + +Matthew Somerville, E<lt>matthew@mysociety.orgE<gt> + +=head1 LICENSE + +This library is free software; you can redistribute it and/or modify +it under the same terms as Perl itself, either Perl version 5.8.8 or, +at your option, any later version of Perl 5 you may have available. + +=cut diff --git a/perllib/FixMyStreet/Template/SafeString.pm b/perllib/FixMyStreet/Template/SafeString.pm new file mode 100644 index 000000000..619bee048 --- /dev/null +++ b/perllib/FixMyStreet/Template/SafeString.pm @@ -0,0 +1,106 @@ +package FixMyStreet::Template::SafeString; + +use strict; +use warnings; + +=head1 NAME + +FixMyStreet::Template::SafeString - a string that won't be escaped on output in a template + +=cut + +use overload + '""' => sub { ${$_[0]} }, + '.' => \&concat, + '.=' => \&concatequals, + '=' => \&clone, + 'cmp' => \&cmp, +; + +sub new { + my ($class, $value) = @_; + + my $self = bless \$value, $class; + + return $self; +} + +sub cmp { + my ($self, $str) = @_; + + if (ref $str eq __PACKAGE__) { + return $$self cmp $$str; + } else { + return $$self cmp $str; + } +} + +sub concat { + my ($self, $str, $prefix) = @_; + + return $self->clone() if not defined $str or $str eq ''; + + if ( $prefix ) { + return $str . $$self; + } else { + return $$self . $str; + } +} + +sub concatequals { + my ($self, $str, $prefix) = @_; + + if ( ref $str eq __PACKAGE__) { + $$self .= $$str; + return $self; + } else { + return $self->clone() if $str eq ''; + $$self .= $str; + return $$self; + } +} + +sub clone { + my $self = shift; + + my $val = $$self; + my $clone = bless \$val, ref $self; + + return $clone; +} + +1; +__END__ + +=head1 SYNOPSIS + + use FixMyStreet::Template; + use FixMyStreet::Template::SafeString; + + my $s1 = "< test & stuff >"; + my $s2 = FixMyStreet::Template::SafeString->new($s1); + + my $tt = FixMyStreet::Template->new(); + $tt->process(\"[% s1 %] * [% s2 %]\n", { s1 => $s1, s2 => $s2 }); + + # Produces output "< test & stuff > * < test & stuff >" + +=head1 DESCRIPTION + +This object provides a safe string to use as part of the FixMyStreet::Template +extension. It will not be automatically escaped when used, so can be used to +pass HTML to a template by a function that is safely creating some. + +=head1 AUTHOR + +Matthew Somerville, E<lt>matthew@mysociety.orgE<gt> + +Martyn Smith, E<lt>msmith@cpan.orgE<gt> + +=head1 LICENSE + +This library is free software; you can redistribute it and/or modify +it under the same terms as Perl itself, either Perl version 5.8.8 or, +at your option, any later version of Perl 5 you may have available. + +=cut diff --git a/perllib/FixMyStreet/Template/Stash.pm b/perllib/FixMyStreet/Template/Stash.pm new file mode 100644 index 000000000..dd027400e --- /dev/null +++ b/perllib/FixMyStreet/Template/Stash.pm @@ -0,0 +1,75 @@ +package FixMyStreet::Template::Stash; + +use strict; +use warnings; +use base qw(Template::Stash); +use FixMyStreet::Template::Variable; +use Scalar::Util qw(blessed); + +sub get { + my $self = shift; + + my $value = $self->SUPER::get(@_); + + $value = FixMyStreet::Template::Variable->new($value) unless ref $value; + + return $value; +} + +# To deal with being able to call var.upper or var.match +sub _dotop { + my $self = shift; + my ($root, $item, $args, $lvalue) = @_; + + $args ||= [ ]; + $lvalue ||= 0; + + return undef unless defined($root) and defined($item); + return undef if $item =~ /^[_.]/; + + if (blessed($root) && $root->isa('FixMyStreet::Template::Variable')) { + if ((my $value = $Template::Stash::SCALAR_OPS->{ $item }) && ! $lvalue) { + my @result = &$value($root->{value}, @$args); + if (defined $result[0]) { + return scalar @result > 1 ? [ @result ] : $result[0]; + } + return undef; + } + } + + return $self->SUPER::_dotop(@_); +} + +1; +__END__ + +=head1 NAME + +FixMyStreet::Template::Stash - The same as Template::HTML::Stash, but +additionally copes with scalar operations on stash items. + +=head1 FUNCTIONS + +=head2 get() + +An overridden function from Template::Stash that calls the parent class's get +method, and returns a FixMyStreet::Template::Variable instead of a raw string. + +=head2 _dotop() + +An overridden function from Template::Stash so that scalar operations on +wrapped FixMyStreet::Template::Variable strings still function correctly. + +=head1 AUTHOR + +Martyn Smith, E<lt>msmith@cpan.orgE<gt> + +Matthew Somerville, E<lt>matthew@mysociety.orgE<gt> + +=head1 LICENSE + +This library is free software; you can redistribute it and/or modify +it under the same terms as Perl itself, either Perl version 5.8.8 or, +at your option, any later version of Perl 5 you may have available. + +=cut diff --git a/perllib/FixMyStreet/Template/Variable.pm b/perllib/FixMyStreet/Template/Variable.pm new file mode 100644 index 000000000..9b5a0fcc4 --- /dev/null +++ b/perllib/FixMyStreet/Template/Variable.pm @@ -0,0 +1,177 @@ +package FixMyStreet::Template::Variable; + +use strict; +use warnings; +use FixMyStreet::Template; + +sub op_factory { + my ($op) = @_; + + return eval q|sub { + my ($self, $str) = @_; + + if ( ref $str eq __PACKAGE__) { + return $self->{value} | . $op . q| $str->{value}; + } + else { + return $self->{value} | . $op . q| $str; + } + }|; +} + +use overload + '""' => \&html_encoded, + '.' => \&concat, + '.=' => \&concatequals, + '=' => \&clone, + + 'cmp' => op_factory('cmp'), + 'eq' => op_factory('eq'), + '<=>' => op_factory('<=>'), + '==' => op_factory('=='), + '%' => op_factory('%'), + '+' => op_factory('+'), + '-' => op_factory('-'), + '*' => op_factory('*'), + '/' => op_factory('/'), + '**' => op_factory('**'), + '>>' => op_factory('>>'), + '<<' => op_factory('<<'), +; + +sub new { + my ($class, $value) = @_; + + my $self = bless { value => $value }, $class; + + return $self; +} + +sub plain { + my $self = shift; + + return $self->{value}; +} + +sub html_encoded { + my $self = shift; + return FixMyStreet::Template::html_filter($self->{value}); +} + +sub concat { + my ($self, $str, $prefix) = @_; + + # Special case where we're _not_ going to html_encode now now + return $self->clone() if not defined $str or $str eq ''; + + if ( $prefix ) { + return $str . $self->html_encoded(); + } + else { + return $self->html_encoded() . $str; + } +} + +sub concatequals { + my ($self, $str, $prefix) = @_; + + if ( ref $str eq __PACKAGE__) { + $self->{value} .= $str->{value}; + return $self; + } + else { + # Special case where we're _not_ going to html_encode now now + return $self->clone() if $str eq ''; + + # Fix Template::HTML::Variable issue with double output + my $ret = $self->html_encoded . $str; + $self->{value} .= $str; + return $ret; + } +} + +sub clone { + my $self = shift; + + my $clone = bless { %$self }, ref $self; + + return $clone; +} + +1; +__END__ + +=head1 NAME + +FixMyStreet::Template::Variable - A "pretend" string that auto HTML encodes; +a copy of Template::HTML::Variable with a bugfix. + +=head1 SYNOPSIS + + use FixMyStreet::Template::Variable; + + my $string = FixMyStreet::Template::Variable->new('< test & stuff >'); + + print $string, "\n"; + + # Produces output "< test & stuff >" + +=head1 DESCRIPTION + +This object provides a "pretend" string to use as part of the +FixMyStreet::Template extension. + +It automatically stringifies to an HTML encoded version of what it was created +with, all the while trying to keep a sane state through string concatinations +etc. + +=head1 FUNCTIONS + +=head2 new() + +Takes a single argument which is the string to set this variable to + +=head2 plain() + +Returns a non HTML-encoded version of the string (i.e. exactly what was passed +to the new() function + +=head2 html_encoded() + +Returns an HTML encoded version of the string (used by the stringify +overloads) + +=head2 concat() + +Implementation of overloaded . operator + +=head2 concatequals() + +Implementation of overloaded .= operator. + +The original Template::HTML::Variable has a bug here, whereby it adds the new +string to its internal value, then returns the HTML encoded version of the +whole string with the new string concatenated again (unescaped). + +=head2 clone() + +Returns a clone of this variable. (used for the implementation of the +overloaded = operator). + +=head2 op_factory() + +Factory for generating operator overloading subs + +=head1 AUTHOR + +Martyn Smith, E<lt>msmith@cpan.orgE<gt> + +Matthew Somerville, E<lt>matthew@mysociety.orgE<gt> + +=head1 LICENSE + +This library is free software; you can redistribute it and/or modify +it under the same terms as Perl itself, either Perl version 5.8.8 or, +at your option, any later version of Perl 5 you may have available. + +=cut |