X-Git-Url: http://wagnertech.de/git?p=kivitendo-erp.git;a=blobdiff_plain;f=SL%2FPresenter%2FEscapedText.pm;fp=SL%2FPresenter%2FEscapedText.pm;h=1c92f6fa9c918cb59ae53e5d09de9cfc3504abc1;hp=c8c78a092f7084b1ed88db8348ab3919e721c8e9;hb=53593baa211863fbf66540cf1bcc36c8fb37257f;hpb=deb4d2dbb676d7d6f69dfe7815d6e0cb09bd4a44 diff --git a/SL/Presenter/EscapedText.pm b/SL/Presenter/EscapedText.pm index c8c78a092..1c92f6fa9 100644 --- a/SL/Presenter/EscapedText.pm +++ b/SL/Presenter/EscapedText.pm @@ -1,29 +1,82 @@ package SL::Presenter::EscapedText; use strict; +use Exporter qw(import); +use Scalar::Util qw(looks_like_number); + +our @EXPORT_OK = qw(escape is_escaped escape_js escape_js_call); +our %EXPORT_TAGS = (ALL => \@EXPORT_OK); use JSON (); -use overload '""' => \&escaped; +use overload '""' => \&escaped_text; + +my %html_entities = ( + '<' => '<', + '>' => '>', + '&' => '&', + '"' => '"', + "'" => ''', +); +# static constructors sub new { my ($class, %params) = @_; return $params{text} if ref($params{text}) eq $class; my $self = bless {}, $class; - $self->{text} = $params{is_escaped} ? $params{text} : $::locale->quote_special_chars('HTML', $params{text}); + $self->{text} = $params{is_escaped} ? $params{text} : quote_html($params{text}); return $self; } -sub escaped { +sub quote_html { + return undef unless defined $_[0]; + (my $x = $_[0]) =~ s/(["'<>&])/$html_entities{$1}/ge; + $x +} + +sub escape { + __PACKAGE__->new(text => $_[0]); +} + +sub is_escaped { + __PACKAGE__->new(text => $_[0], is_escaped => 1); +} + +sub escape_js { + my ($text) = @_; + + $text =~ s|\\|\\\\|g; + $text =~ s|\"|\\\"|g; + $text =~ s|\n|\\n|g; + + __PACKAGE__->new(text => $text, is_escaped => 1); +} + +sub escape_js_call { + my ($func, @args) = @_; + + escape( + sprintf "%s(%s)", + escape_js($func), + join ", ", map { + looks_like_number($_) + ? $_ + : '"' . escape_js($_) . '"' + } @args + ); +} + +# internal magic +sub escaped_text { my ($self) = @_; return $self->{text}; } sub TO_JSON { - goto &escaped; + goto &escaped_text; } 1; @@ -35,15 +88,18 @@ __END__ =head1 NAME -SL::Presenter::EscapedText - Thin proxy object around HTML-escaped strings +SL::Presenter::EscapedText - Thin proxy object to invert the burden of escaping HTML output =head1 SYNOPSIS - use SL::Presenter::EscapedText; + use SL::Presenter::EscapedText qw(escape is_escaped escape_js); sub blackbox { my ($text) = @_; return SL::Presenter::EscapedText->new(text => $text); + + # or shorter: + # return escape($text); } sub build_output { @@ -67,7 +123,7 @@ But higher functions should not have to care if the output is already escaped -- they should be able to simply escape it again. Without producing stuff like '&amp;'. -Stringification is overloaded. It will return the same as L. +Stringification is overloaded. It will return the same as L. This works together with the template plugin L and its C method. @@ -88,7 +144,37 @@ Otherwise C is HTML-escaped and stored in the new instance. This can be overridden by setting C<$params{is_escaped}> to a trueish value. -=item C +=item C + +Static constructor, can be exported. Equivalent to calling C<< new(text => $text) >>. + +=item C + +Static constructor, can be exported. Equivalent to calling C<< new(text => $text, escaped => 1) >>. + +=item C + +Static constructor, can be exported. Like C but also escapes Javascript. + +=item C + +Static constructor, can be exported. Used to construct a javascript call than +can be used for onclick handlers in other Presenter functions. + +For example: + + L.button_tag( + P.escape_js_call("kivi.Package.some_func", arg_one, arg_two, arg_three) + title + ) + +=back + +=head1 METHODS + +=over 4 + +=item C Returns the escaped string (not an instance of C but an actual string).