LP1171875 Add locale support to IDL2js
authorBill Erickson <berick@esilibrary.com>
Fri, 26 Apr 2013 17:20:24 +0000 (13:20 -0400)
committerMike Rylander <mrylander@gmail.com>
Fri, 2 Aug 2013 20:05:49 +0000 (16:05 -0400)
/IDL2js now reads locale information from either locale= CGI parameter
or Accept-Language HTTP headers.  The locale-aware IDL is loaded from
/reports/fm_IDL.xml via Apache subrequest.  Each full copy of the IDL is
cached within the Apache processes to avoid the need to re-parse the IDL ad
infinitum for full IDL retrieval.  Partial IDL retrieval is also supported
(but not cached).

No attempt is made to cleanse the locale -- invalid locale strings are
discarded -- so it's the callers responsibility to pass a valid locale.

Signed-off-by: Bill Erickson <berick@esilibrary.com>
Signed-off-by: Pasi Kallinen <pasi.kallinen@pttk.fi>
Signed-off-by: Mike Rylander <mrylander@gmail.com>
Open-ILS/src/perlmods/lib/OpenILS/WWW/IDL2js.pm

index c55f137..497b0db 100644 (file)
@@ -3,14 +3,18 @@ use strict; use warnings;
 use XML::LibXML;
 use XML::LibXSLT;
 use Apache2::Const -compile => qw(OK DECLINED HTTP_INTERNAL_SERVER_ERROR);
+use Apache2::RequestRec;
+use Apache2::SubRequest;
+use Apache2::Filter;
+use APR::Brigade;
+use APR::Bucket;
 use Error qw/:try/;
 use OpenSRF::System;
 use OpenSRF::Utils::SettingsClient;
+use CGI;
 
 my $bs_config;
 my $stylesheet;
-my $idl_doc;
-
 
 # load and parse the stylesheet
 sub import {
@@ -48,21 +52,82 @@ sub child_init {
         warn "Invalid XSL File: $xsl_file: $e\n";
     };
 
-    $idl_doc = XML::LibXML->load_xml(location => $idl_file);
     return Apache2::Const::OK;
 }
 
 
+my %idl_cache;
 sub handler {
     my $r = shift;
     my $args = $r->args || '';
     child_init() unless $__initted;
 
-    return Apache2::Const::HTTP_INTERNAL_SERVER_ERROR unless $stylesheet and $idl_doc;
-    return Apache2::Const::DECLINED if $args and $args !~ /^[a-zA-Z,]*$/;
+    return Apache2::Const::HTTP_INTERNAL_SERVER_ERROR unless $stylesheet;
+
+    # pull the locale from the query string if present
+    (my $locale = $args) =~ s/.*locale=([a-z]{2}-[A-Z]{2}).*/$1/g;
+
+    # remove the locale argument from the query 
+    # string, regardless of whether it was valid
+    $args =~ s/([&;]?locale=[^&;]*)[&;]?//g; 
+
+    # if no valid locale is present in the query 
+    # string, pull it from the headers
+    $locale = $r->headers_in->get('Accept-Language') unless $locale;
+
+    if (!$locale or $locale !~ /^[a-z]{2}-[A-Z]{2}$/) {
+        $r->log->debug("Invalid IDL2js locale '$locale'; using en-US");
+        $locale = 'en-US';
+    }
+
+    $r->log->debug("IDL2js using locale '$locale'");
+
+    my $output = '';
+    my $stat = load_IDL($r, $locale, $args, \$output);
+    return $stat unless $stat == Apache2::Const::OK;
+
+    $r->content_type('application/x-javascript; encoding=utf8');
+    $r->print($output);
+    return Apache2::Const::OK;
+}
+
+# loads the IDL for the provided locale.
+# when possible, use a cached version of the IDL.
+sub load_IDL {
+    my ($r, $locale, $args, $output_ref) = @_;
+
+    # do we already have a cached copy of the IDL for this locale?
+    if (!$args and $idl_cache{$locale}) {
+        $$output_ref = $idl_cache{$locale};
+        return Apache2::Const::OK;
+    }
+
+    # Fetch the locale-aware fm_IDL.xml via Apache subrequest.
+    my $subr = $r->lookup_uri("/reports/fm_IDL.xml?locale=$locale");
+
+    # filter allows us to capture the output of the subrequest locally
+    # http://www.gossamer-threads.com/lists/modperl/modperl/97649#97649
+    my $xml = ''; 
+    $subr->add_output_filter(sub {
+        my ($f, $bb) = @_; 
+        while (my $e = $bb->first) { 
+            $e->read(my $buf); 
+            $xml .= $buf; 
+            $e->delete; 
+        } 
+        return Apache2::Const::OK; 
+    }); 
+
+    $subr->run;
+
+    if (!$xml) {
+        $r->log->error("No IDL XML found");
+        return Apache2::Const::HTTP_INTERNAL_SERVER_ERROR;
+    }
 
     my $output;
     try {
+        my $idl_doc = XML::LibXML->load_xml(string => $xml);
         my $results = $stylesheet->transform($idl_doc, class_list => "'$args'");
         $output = $stylesheet->output_as_bytes($results);
     } catch Error with {
@@ -72,9 +137,13 @@ sub handler {
 
     return Apache2::Const::HTTP_INTERNAL_SERVER_ERROR unless $output;
 
-    $r->content_type('application/x-javascript; encoding=utf8');
-    $r->print($output);
-    return Apache2::Const::OK;
+    # only cache full versions of the IDL
+    $idl_cache{$locale} = $output unless $args;
+
+    # pass output back to the caller
+    $$output_ref = $output;
+
+    return Apache2::Const::OK; 
 }
 
 1;