--- Webpacus/lib/Webpacus/Model/WebPAC.pm 2006/01/21 23:27:16 378 +++ Webpacus/lib/Webpacus/Model/WebPAC.pm 2006/01/22 00:44:32 379 @@ -11,6 +11,7 @@ use File::Slurp; use Time::HiRes qw/time/; use Encode qw/encode decode from_to/; +use Template; use Data::Dumper; =head1 NAME @@ -102,7 +103,26 @@ $self->{databases} = $c->config->{databases} || $log->error("can't find databases in config"); - $self->{model_record} = $c->comp('Model::Record') or $log->error("can't find Model::Record"); + # create Template toolkit instance + $self->{'tt'} = Template->new( + INCLUDE_PATH => $template_path, + FILTERS => { + dump_html => sub { + return unless (@_); + my $out; + my $i = 1; + foreach my $v (@_) { + $out .= qq{
} . + Data::HTMLDumper->Dump([ $v ],[ "v$i" ]) . + qq{
}; + $i++; + } + $out =~ s!/]*>!!gis if ($out); + return $out; + } + }, + EVAL_PERL => 1, + ); return $self; @@ -208,18 +228,12 @@ $t = time(); - my $html = '[no output]'; - - if ($self->{model_record}) { - $html = $self->{model_record}->apply( - template => $template_filename, - data => $ds, - record_uri => "${database}/${prefix}/${id}", - config => $self->{databases}->{$database}, - ); - } else { - $log->warn("skipped apply"); - } + my $html = $self->apply( + template => $template_filename, + data => $ds, + record_uri => "${database}/${prefix}/${id}", + config => $self->{databases}->{$database}, + ); $times->{out} += time() - $t; @@ -279,7 +293,7 @@ return; } - my $html = $self->{model_record}->apply( + my $html = $self->apply( template => $args->{template}, data => $ds, record_uri => $args->{record_uri}, @@ -348,6 +362,249 @@ return decode($self->{webpac_encoding}, $content); } + +=head2 apply + +Create output from in-memory data structure using Template Toolkit template. + + my $text = $tt->apply( + template => 'text.tt', + data => $ds, + record_uri => 'database/prefix/mfn', + ); + +It also has follwing template toolikit filter routies defined: + +=cut + +sub apply { + my $self = shift; + + my $args = {@_}; + + my $log = $self->{log} || die "no log?"; + + foreach my $a (qw/template data/) { + $log->logconfess("need $a") unless ($args->{$a}); + } + +=head3 tt_filter_type + +filter to return values of specified from $ds, usage from TT template is in form +C, where C is optional, like this: + + [% d('Title') %] + [% d('Author',', ' %] + +=cut + + sub tt_filter_type { + my ($data,$type) = @_; + + die "no data?" unless ($data); + $type ||= 'display'; + + my $default_delimiter = { + 'display' => '¶
', + 'index' => '\n', + }; + + return sub { + + my ($name,$join) = @_; + + die "no data hash" unless ($data->{'data'} && ref($data->{'data'}) eq 'HASH'); + # Hm? Should we die here? + return unless ($name); + + my $item = $data->{'data'}->{$name} || return; + + my $v = $item->{$type} || return; + + if (ref($v) eq 'ARRAY') { + if ($#{$v} == 0) { + $v = $v->[0]; + } else { + $join = $default_delimiter->{$type} unless defined($join); + $v = join($join, @{$v}); + } + } else { + warn("TT filter $type(): field $name values aren't ARRAY, ignoring"); + } + + return $v; + } + } + + $args->{'d'} = tt_filter_type($args, 'display'); + $args->{'display'} = tt_filter_type($args, 'display'); + +=head3 tt_filter_search + +filter to return links to search, usage in TT: + + [% search('FieldToDisplay','FieldToSearch','optional delimiter', 'optional_template.tt') %] + +=cut + + sub tt_filter_search { + + my ($data) = @_; + + die "no data?" unless ($data); + + return sub { + + my ($display,$search,$delimiter,$template) = @_; + + # default delimiter + $delimiter ||= '¶
', + + die "no data hash" unless ($data->{'data'} && ref($data->{'data'}) eq 'HASH'); + # Hm? Should we die here? + return unless ($display); + + my $item = $data->{'data'}->{$display} || return; + + return unless($item->{'display'}); + if (! $item->{'search'}) { + warn "error in TT template: field $display didn't insert anything into search, use d('$display') and not search('$display'...)"; + return; + } + + my @warn; + foreach my $type (qw/display search/) { + push @warn, "field $display type $type values aren't ARRAY" unless (ref($item->{$type}) eq 'ARRAY'); + } + + if (@warn) { + warn("TT filter search(): " . join(",", @warn) . ", skipping"); + return; + } + my @html; + + my $d_el = $#{ $item->{'display'} }; + my $s_el = $#{ $item->{'search'} }; + + # easy, both fields have same number of elements or there is just + # one search and multiple display + if ( $d_el == $s_el || $s_el == 0 ) { + + foreach my $i ( 0 .. $d_el ) { + + my $s; + if ($s_el > 0) { + $s = $item->{'search'}->[$i] || die "can't find value $i for type search in field $search"; + } else { + $s = $item->{'search'}->[0]; + } + #$s =~ s/([^\w.-])/sprintf("%%%02X",ord($1))/eg; + $s = __quotemeta( $s ); + + my $d = $item->{'display'}->[$i] || die "can't find value $i for type display in field $display"; + + my $template_arg = ''; + $template_arg = qq{,'$template'} if ($template); + + push @html, qq{$d}; + } + + return join($delimiter, @html); + } else { + my $html = qq{
WARNING: we should really support if there is $d_el display elements and $s_el search elements, but currently there is no nice way to do so, so we will just display values
}; + my $v = $item->{'display'}; + + if ($#{$v} == 0) { + $html .= $v->[0]; + } else { + $html .= join($delimiter, @{$v}); + } + return $html; + } + } + } + + $args->{'search'} = tt_filter_search($args); + +=head3 load_rec + +Used mostly for onClick events like this: + + bar + +=cut + + $args->{'load_template'} = sub { + my $template = shift or return "Logger.error('load_template missing template name!'); return false;"; + return "load_template($template); return false;"; + }; + + my $out; + + $self->{'tt'}->process( + $args->{'template'}, + $args, + \$out + ) || $log->error( "apply can't process template: ", $self->{'tt'}->error() ); + + return $out; +} + + +=head2 __quotemeta + +Helper to quote JavaScript-friendly characters + +=cut + +sub __quotemeta { + local $_ = shift; + $_ = decode('iso-8859-2', $_); + + s<([\x{0080}-\x{fffd}]+)>{sprintf '\u%0*v4X', '\u', $1}ge if ( Encode::is_utf8($_) ); + { + use bytes; + s<((?:[^ \x21-\x7E]|(?:\\(?!u)))+)>{sprintf '\x%0*v2X', '\x', $1}ge; + } + + s/\\x09/\\t/g; + s/\\x0A/\\n/g; + s/\\x0D/\\r/g; + s/"/\\"/g; + s/\\x5C/\\\\/g; + + return $_; +} + + + =head1 AUTHOR Dobrica Pavlinusic C<< >>