From deb4d39674989eb70b757637d8464e2b37537a32 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Thu, 11 Jul 2013 17:20:18 +0300 Subject: [PATCH] First test of creating a wrapper for Template Toolkit that handles l() and other stuff automatically. TODO: remember to rm EGWeb/EGI18N.pm and EGWeb/CGI_utf8.pm Signed-off-by: Pasi Kallinen --- Open-ILS/src/perlmods/lib/OpenILS/Utils/Templar.pm | 162 +++++++++++ .../perlmods/lib/OpenILS/Utils/Templar/CGI_utf8.pm | 44 +++ .../lib/OpenILS/Utils/Templar/I18NFilter.pm | 12 + Open-ILS/src/perlmods/lib/OpenILS/WWW/EGWeb.pm | 304 ++++++++++++--------- Open-ILS/src/sql/Pg/950.data.seed-values.sql | 18 +- 5 files changed, 406 insertions(+), 134 deletions(-) create mode 100644 Open-ILS/src/perlmods/lib/OpenILS/Utils/Templar.pm create mode 100644 Open-ILS/src/perlmods/lib/OpenILS/Utils/Templar/CGI_utf8.pm create mode 100644 Open-ILS/src/perlmods/lib/OpenILS/Utils/Templar/I18NFilter.pm diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Utils/Templar.pm b/Open-ILS/src/perlmods/lib/OpenILS/Utils/Templar.pm new file mode 100644 index 0000000000..ac608a4fc7 --- /dev/null +++ b/Open-ILS/src/perlmods/lib/OpenILS/Utils/Templar.pm @@ -0,0 +1,162 @@ +package OpenILS::Utils::Templar; + +use strict; use warnings; +use diagnostics; + +use Encode; +use Template; + +use OpenSRF::Utils::Logger qw(:logger); +use OpenILS::Utils::CStoreEditor q/:funcs/; + + +use Data::Dumper; + +# cache string bundles +my %registered_locales; + +sub set_text_handler { + my $locale = shift; + return sub { + my $lh = OpenILS::Utils::Templar::I18N->get_handle($locale); + return $lh->maketext(@_) if ($lh); + return '['.join(',', @_).']'; + }; +} + +sub get_registered_locales { my $self=shift; return \%registered_locales; } + +sub process { + my $self = shift; + my $parm = shift || {}; + my $ttc = shift || {}; + my $ttparams = shift || {}; # params for TT + + my $template = $parm->{template} || ''; + + my $output = $parm->{output} || undef; + + my $locale = $parm->{locale} || 'en_us'; + my $text_handler = set_text_handler($locale); + + $ttc->{encode_utf8} = sub {return encode_utf8(shift())}; + $ttc->{ctx}->{encode_utf8} = sub {return encode_utf8(shift())} if (!exists($ttc->{ctx}->{encode_utf8})); + $ttc->{ENV} = \%ENV; + + if (!exists($ttparams->{FILTERS}->{l}) && !exists($ttc->{l})) { + $ttc->{l} = $text_handler; + $ttparams->{FILTERS}->{l} = [ + sub { + my ($ttc, @args) = @_; + return sub { $text_handler->(shift(), @args); } + }, 1 + ]; + } + + $ttparams->{PLUGINS}->{CGI_utf8} = 'OpenILS::Utils::Templar::CGI_utf8' if (!exists($ttparams->{PLUGINS}->{CGI_utf8})); + + my $tt = Template->new($ttparams); + + if (!$tt) { + #warn "Templar: Error creating template processor: $@"; + return 1; + } + if (!$tt->process($template, $ttc, $output)) { + my $error; + ($error = $tt->error()) =~ s/\n/ /og; + $logger->error("Error processing template: $error"); + #print "
Templar: Error processing: " . $tt->error() . "\n";
+	#print Dumper($template)."\n";
+	#print Dumper($ttc)."\n";
+	return 2;
+    }
+    return 0;
+}
+
+
+
+# Create an I18N sub-module for each supported locale
+# Each module creates its own MakeText lexicon by parsing .po/.mo files
+sub load_locale_handlers {
+    my $self = shift;
+    my %locales = @_;
+
+    #print 'load_locale_handlers:locales='.Dumper(%locales)."
"; + + my $editor = new_editor(); + my @locale_tags = sort { length($a) <=> length($b) } keys %locales; + + # always fall back to en_us, the assumed template language + push(@locale_tags, 'en_us'); + + for my $idx (0..$#locale_tags) { + + my $tag = $locale_tags[$idx]; + next if grep { $_ eq $tag } keys %registered_locales; + + my $res = $editor->json_query({ + "from" => [ + "evergreen.get_locale_name", + $tag + ] + }); + + my $locale_name = $res->[0]->{"name"} if exists $res->[0]->{"name"}; + next unless $locale_name; + + #print "locale_name=$locale_name, $tag
"; + + my $parent_tag = ''; + my $sub_idx = $idx; + + # find the parent locale if possible. It will be + # longest left-anchored substring of the current tag + while( --$sub_idx >= 0 ) { + my $ptag = $locale_tags[$sub_idx]; + if( substr($tag, 0, length($ptag)) eq $ptag ) { + $parent_tag = "::$ptag"; + last; + } + } + + my $messages = $locales{$tag} || ''; + + #print "load_locale_handlers: $tag, $parent_tag, $locale_name, $messages
"; + + # TODO Can we do this without eval? + my $eval = <<" EVAL"; + package OpenILS::Utils::Templar::I18N::$tag; + use base 'OpenILS::Utils::Templar::I18N$parent_tag'; + if(\$messages) { + use Locale::Maketext::Lexicon { + _decode => 1 + }; + use Locale::Maketext::Lexicon::Gettext; + if(open F, '$messages') { + our %Lexicon = (%Lexicon, %{ Locale::Maketext::Lexicon::Gettext->parse() }); + close F; + } else { + #print "EGWeb: unable to open messages file: $messages
"; + warn "EGWeb: unable to open messages file: $messages"; + } + } + EVAL + eval $eval; + + if ($@) { + warn "$@\n" if $@; + } else { + $registered_locales{"$tag"} = $locale_name; + } + } + return 0; +} + + + +# base class for all supported locales +package OpenILS::Utils::Templar::I18N; +use base 'Locale::Maketext'; +our %Lexicon = (_AUTO => 1); + +1; diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Utils/Templar/CGI_utf8.pm b/Open-ILS/src/perlmods/lib/OpenILS/Utils/Templar/CGI_utf8.pm new file mode 100644 index 0000000000..3dfc6c1deb --- /dev/null +++ b/Open-ILS/src/perlmods/lib/OpenILS/Utils/Templar/CGI_utf8.pm @@ -0,0 +1,44 @@ +package OpenILS::Utils::Templar::CGI_utf8; + +# The code in this module is copied from (except for a tiny modification) +# Template::Plugin::CGI, which is written by: +# +# Andy Wardley Eabw@wardley.orgE L +# +# 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. + +use strict; +use warnings; +use base 'Template::Plugin'; +use CGI qw(:all -utf8); + +sub new { + my $class = shift; + my $context = shift; + new CGI(@_); +} + +# 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; diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Utils/Templar/I18NFilter.pm b/Open-ILS/src/perlmods/lib/OpenILS/Utils/Templar/I18NFilter.pm new file mode 100644 index 0000000000..d9f86a0632 --- /dev/null +++ b/Open-ILS/src/perlmods/lib/OpenILS/Utils/Templar/I18NFilter.pm @@ -0,0 +1,12 @@ +package OpenILS::Utils::Templar::I18NFilter; +use Template::Plugin::Filter; +use base qw(Template::Plugin::Filter); +our $DYNAMIC = 1; + +sub filter { + my ($self, $text, $args) = @_; + return $maketext->($text, @$args); +} + +1; + diff --git a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGWeb.pm b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGWeb.pm index e5d5bb280a..de9031f64b 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGWeb.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGWeb.pm @@ -1,5 +1,6 @@ package OpenILS::WWW::EGWeb; use strict; use warnings; +use diagnostics; use Template; use XML::Simple; use XML::LibXML; @@ -11,12 +12,16 @@ use OpenSRF::EX qw(:try); use OpenILS::Utils::CStoreEditor q/:funcs/; use List::MoreUtils qw/uniq/; +use OpenILS::Utils::Templar; + +use Data::Dumper; + use constant OILS_HTTP_COOKIE_SKIN => 'eg_skin'; use constant OILS_HTTP_COOKIE_THEME => 'eg_theme'; use constant OILS_HTTP_COOKIE_LOCALE => 'eg_locale'; # cache string bundles -my %registered_locales; +#my %registered_locales; sub handler { my $r = shift; @@ -31,56 +36,118 @@ sub handler { return $stat unless $stat == Apache2::Const::OK; return Apache2::Const::DECLINED unless $template; - my $text_handler = set_text_handler($ctx, $r); +# my $text_handler = set_text_handler($ctx, $r); - my $tt = Template->new({ + my %ttparams = ( ENCODING => 'utf-8', OUTPUT => ($as_xml) ? sub { parse_as_xml($r, $ctx, @_); } : $r, INCLUDE_PATH => $ctx->{template_paths}, - DEBUG => $ctx->{debug_template}, - PLUGINS => { - EGI18N => 'OpenILS::WWW::EGWeb::I18NFilter', - CGI_utf8 => 'OpenILS::WWW::EGWeb::CGI_utf8' - }, - FILTERS => { - # Register a dynamic filter factory for our locale::maketext generator - l => [ - sub { - my($ctx, @args) = @_; - return sub { $text_handler->(shift(), @args); } - }, 1 - ] - } - }); + DEBUG => $ctx->{debug_template} + ); - if (!$tt) { - $r->log->error("Error creating template processor: $@"); - return Apache2::Const::HTTP_INTERNAL_SERVER_ERROR; - } + my $TR = 'OpenILS::Utils::Templar'; - $ctx->{encode_utf8} = sub {return encode_utf8(shift())}; + if ($TR->load_locale_handlers(%{$ctx->{oils_locales}})) { + #print "error at load_locale_handlers\n"; + } else { + my $cgi = CGI->new; + # Set a locale cookie if the requested locale is valid + my $set_locale = $cgi->param('set_eg_locale') || ''; - unless($tt->process($template, {ctx => $ctx, ENV => \%ENV, l => $text_handler})) { - $r->log->warn('egweb: template error: ' . $tt->error); - return Apache2::Const::HTTP_INTERNAL_SERVER_ERROR; + $ctx->{locales} = $TR->get_registered_locales(); + #print "locales: ".Dumper($ctx->{locales})."
"; + + #print "set_locale = $set_locale
"; + + #print "locales keys: ".Dumper(keys $ctx->{locales})."
"; + + if (!(grep {$_ eq $set_locale} keys $ctx->{locales})) { + $set_locale = ''; + } else { + my $slc = $cgi->cookie({ + '-name' => OILS_HTTP_COOKIE_LOCALE, + '-value' => $set_locale, + '-expires' => '+10y' + }); + $r->headers_out->add('Set-Cookie' => $slc); + } + + $ctx->{locale} = $set_locale || + $cgi->cookie(OILS_HTTP_COOKIE_LOCALE) || $ctx->{oils_default_locale} || + parse_accept_lang($r->headers_in->get('Accept-Language')); + + #print "set_locale = $set_locale
"; + + #print "ctx.locale=".Dumper($ctx->{locale})."
"; + + # set the editor default locale for each page load + $OpenILS::Utils::CStoreEditor::default_locale = parse_eg_locale($ctx->{locale}); + + my %parm = ( + template => $template, + locale => $ctx->{locale} + ); + + my %ttc = ( + ctx => $ctx + ); + my $xx = $TR->process(\%parm, \%ttc, \%ttparams); + #print "XX = $xx
"; } + + #return Apache2::Const::HTTP_INTERNAL_SERVER_ERROR if ($xx); + + # my $tt = Template->new({ + # ENCODING => 'utf-8', + # OUTPUT => ($as_xml) ? sub { parse_as_xml($r, $ctx, @_); } : $r, + # INCLUDE_PATH => $ctx->{template_paths}, + # DEBUG => $ctx->{debug_template}, + # PLUGINS => { + # EGI18N => 'OpenILS::WWW::EGWeb::I18NFilter', + # CGI_utf8 => 'OpenILS::WWW::EGWeb::CGI_utf8' + # }, + # FILTERS => { + # # Register a dynamic filter factory for our locale::maketext generator + # l => [ + # sub { + # my($ctx, @args) = @_; + # return sub { $text_handler->(shift(), @args); } + # }, 1 + # ] + # } + # }); + + # if (!$tt) { + # $r->log->error("Error creating template processor: $@"); + # return Apache2::Const::HTTP_INTERNAL_SERVER_ERROR; + # } + + # $ctx->{encode_utf8} = sub {return encode_utf8(shift())}; + + # unless($tt->process($template, {ctx => $ctx, ENV => \%ENV, l => $text_handler})) { + # $r->log->warn('egweb: template error: ' . $tt->error); + # return Apache2::Const::HTTP_INTERNAL_SERVER_ERROR; + # } + return Apache2::Const::OK; } -sub set_text_handler { - my $ctx = shift; - my $r = shift; - my $locale = $ctx->{locale}; - $r->log->debug("egweb: messages locale = $locale"); +# sub set_text_handler { +# my $ctx = shift; +# my $r = shift; - return sub { - my $lh = OpenILS::WWW::EGWeb::I18N->get_handle($locale); - return $lh->maketext(@_); - }; -} +# my $locale = $ctx->{locale}; + +# $r->log->debug("egweb: messages locale = $locale"); + +# return sub { +# my $lh = OpenILS::WWW::EGWeb::I18N->get_handle($locale); +# return $lh->maketext(@_); +# }; +# } @@ -151,29 +218,12 @@ sub load_context { $ctx->{template_paths} = [ reverse @template_paths ]; my %locales = $r->dir_config->get('OILSWebLocale'); - load_locale_handlers($ctx, %locales); + #load_locale_handlers($ctx, %locales); - $ctx->{locales} = \%registered_locales; + $ctx->{oils_locales} = \%locales; + $ctx->{oils_default_locale} = $default_locale; - # Set a locale cookie if the requested locale is valid - my $set_locale = $cgi->param('set_eg_locale') || ''; - if (!(grep {$_ eq $set_locale} keys %registered_locales)) { - $set_locale = ''; - } else { - my $slc = $cgi->cookie({ - '-name' => OILS_HTTP_COOKIE_LOCALE, - '-value' => $set_locale, - '-expires' => '+10y' - }); - $r->headers_out->add('Set-Cookie' => $slc); - } - - $ctx->{locale} = $set_locale || - $cgi->cookie(OILS_HTTP_COOKIE_LOCALE) || $default_locale || - parse_accept_lang($r->headers_in->get('Accept-Language')); - - # set the editor default locale for each page load - $OpenILS::Utils::CStoreEditor::default_locale = parse_eg_locale($ctx->{locale}); + #print "oils_locales: ".Dumper(%locales)."
"; my $mprefix = $ctx->{media_prefix}; if($mprefix and $mprefix !~ /^http/ and $mprefix !~ /^\//) { @@ -261,77 +311,79 @@ sub find_template { # Create an I18N sub-module for each supported locale # Each module creates its own MakeText lexicon by parsing .po/.mo files -sub load_locale_handlers { - my $ctx = shift; - my %locales = @_; - - my $editor = new_editor(); - my @locale_tags = sort { length($a) <=> length($b) } keys %locales; - - # always fall back to en_us, the assumed template language - push(@locale_tags, 'en_us'); - - for my $idx (0..$#locale_tags) { - - my $tag = $locale_tags[$idx]; - next if grep { $_ eq $tag } keys %registered_locales; - - my $res = $editor->json_query({ - "from" => [ - "evergreen.get_locale_name", - $tag - ] - }); - - my $locale_name = $res->[0]->{"name"} if exists $res->[0]->{"name"}; - next unless $locale_name; - - my $parent_tag = ''; - my $sub_idx = $idx; - - # find the parent locale if possible. It will be - # longest left-anchored substring of the current tag - while( --$sub_idx >= 0 ) { - my $ptag = $locale_tags[$sub_idx]; - if( substr($tag, 0, length($ptag)) eq $ptag ) { - $parent_tag = "::$ptag"; - last; - } - } - - my $messages = $locales{$tag} || ''; - - # TODO Can we do this without eval? - my $eval = <<" EVAL"; - package OpenILS::WWW::EGWeb::I18N::$tag; - use base 'OpenILS::WWW::EGWeb::I18N$parent_tag'; - if(\$messages) { - use Locale::Maketext::Lexicon { - _decode => 1 - }; - use Locale::Maketext::Lexicon::Gettext; - if(open F, '$messages') { - our %Lexicon = (%Lexicon, %{ Locale::Maketext::Lexicon::Gettext->parse() }); - close F; - } else { - warn "EGWeb: unable to open messages file: $messages"; - } - } - EVAL - eval $eval; - - if ($@) { - warn "$@\n" if $@; - } else { - $registered_locales{"$tag"} = $locale_name; - } - } -} +# sub load_locale_handlers { +# my $ctx = shift; +# my %locales = @_; + +# my $editor = new_editor(); +# my @locale_tags = sort { length($a) <=> length($b) } keys %locales; + +# # always fall back to en_us, the assumed template language +# push(@locale_tags, 'en_us'); + +# for my $idx (0..$#locale_tags) { + +# my $tag = $locale_tags[$idx]; +# next if grep { $_ eq $tag } keys %registered_locales; + +# my $res = $editor->json_query({ +# "from" => [ +# "evergreen.get_locale_name", +# $tag +# ] +# }); + +# my $locale_name = $res->[0]->{"name"} if exists $res->[0]->{"name"}; +# next unless $locale_name; + +# warn "locale_name=$locale_name, $tag\n"; + +# my $parent_tag = ''; +# my $sub_idx = $idx; + +# # find the parent locale if possible. It will be +# # longest left-anchored substring of the current tag +# while( --$sub_idx >= 0 ) { +# my $ptag = $locale_tags[$sub_idx]; +# if( substr($tag, 0, length($ptag)) eq $ptag ) { +# $parent_tag = "::$ptag"; +# last; +# } +# } + +# my $messages = $locales{$tag} || ''; + +# # TODO Can we do this without eval? +# my $eval = <<" EVAL"; +# package OpenILS::WWW::EGWeb::I18N::$tag; +# use base 'OpenILS::WWW::EGWeb::I18N$parent_tag'; +# if(\$messages) { +# use Locale::Maketext::Lexicon { +# _decode => 1 +# }; +# use Locale::Maketext::Lexicon::Gettext; +# if(open F, '$messages') { +# our %Lexicon = (%Lexicon, %{ Locale::Maketext::Lexicon::Gettext->parse() }); +# close F; +# } else { +# warn "EGWeb: unable to open messages file: $messages"; +# } +# } +# EVAL +# eval $eval; + +# if ($@) { +# warn "$@\n" if $@; +# } else { +# $registered_locales{"$tag"} = $locale_name; +# } +# } +# } # base class for all supported locales -package OpenILS::WWW::EGWeb::I18N; -use base 'Locale::Maketext'; -our %Lexicon = (_AUTO => 1); +#package OpenILS::WWW::EGWeb::I18N; +#use base 'Locale::Maketext'; +#our %Lexicon = (_AUTO => 1); 1; diff --git a/Open-ILS/src/sql/Pg/950.data.seed-values.sql b/Open-ILS/src/sql/Pg/950.data.seed-values.sql index 433fbfd352..bc1145ec9f 100644 --- a/Open-ILS/src/sql/Pg/950.data.seed-values.sql +++ b/Open-ILS/src/sql/Pg/950.data.seed-values.sql @@ -8785,11 +8785,11 @@ $$ [% END %] [% FOR xact_id IN xact_mp_hash.keys.sort %] [% SET xact = xact_mp_hash.$xact_id.xact %] -
  • Transaction ID: [% xact_id %] +
  • [% l('Transaction ID: [_1]', xact_id) %] [% IF xact.circulation %][% helpers.get_copy_bib_basics(xact.circulation.target_copy).title %] - [% ELSE %]Miscellaneous + [% ELSE %][% l('Miscellaneous') %] [% END %] - Line item billings:
      + [% l('Line item billings:') %]
        [% SET mb_type_hash = {} %] [% FOR mb IN xact.billings %][%# Group billings by their btype %] [% IF mb.voided == 'f' %] @@ -8803,18 +8803,20 @@ $$ [% END %] [% FOR mb_type IN mb_type_hash.keys.sort %]
      1. [% IF mb_type == 1 %][%# Consolidated view of overdue billings %] - $[% mb_type_hash.$mb_type.sum %] for [% mb_type_hash.$mb_type.billings.0.btype.name %] - on [% mb_type_hash.$mb_type.first_ts %] through [% mb_type_hash.$mb_type.last_ts %] + [% l('$[_1] for [_2] on [_3] through [_4]', + mb_type_hash.$mb_type.sum, mb_type_hash.$mb_type.billings.0.btype.name, + mb_type_hash.$mb_type.first_ts, mb_type_hash.$mb_type.last_ts) %] [% ELSE %][%# all other billings show individually %] [% FOR mb IN mb_type_hash.$mb_type.billings %] - $[% mb.amount %] for [% mb.btype.name %] on [% mb.billing_ts %] [% mb.note %] + [% l('$[_1] for [_2] on [_3] [_4]', + mb.amount, mb.btype.name, mb.billing_ts, mb.note) %] [% END %] [% END %]
      2. [% END %]
      - Line item payments:
        + [% l('Line item payments:') %]
          [% FOR mp IN xact_mp_hash.$xact_id.payments %] -
        1. Payment ID: [% mp.id %] +
        2. [% l('Payment ID: [_1]', mp.id) %] Paid [% mp.amount %] via [% SWITCH mp.payment_type -%] [% CASE "cash_payment" %]cash [% CASE "check_payment" %]check -- 2.11.0