]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/WWW/EGWeb.pm
840dfaaba814ca61336aea3294a726f1b9a7f7df
[working/Evergreen.git] / Open-ILS / src / perlmods / OpenILS / WWW / EGWeb.pm
1 package OpenILS::WWW::EGWeb;
2 use strict; use warnings;
3 use Template;
4 use XML::Simple;
5 use File::stat;
6 use Apache2::Const -compile => qw(OK DECLINED HTTP_INTERNAL_SERVER_ERROR);
7 use Apache2::Log;
8
9 use constant OILS_HTTP_COOKIE_SKIN => 'oils:skin';
10 use constant OILS_HTTP_COOKIE_THEME => 'oils:theme';
11 use constant OILS_HTTP_COOKIE_LOCALE => 'oils:locale';
12
13 my $web_config;
14 my $web_config_file;
15 my $web_config_edit_time;
16
17 sub import {
18     my $self = shift;
19     $web_config_file = shift;
20     unless(-r $web_config_file) {
21         warn "Invalid web config $web_config_file";
22         return;
23     }
24     check_web_config();
25 }
26
27
28 sub handler {
29     my $r = shift;
30     check_web_config($r); # option to disable this
31     my $ctx = load_context($r);
32     my $base = $ctx->{base_uri};
33     my($template, $page_args) = find_template($r, $base, $ctx);
34     return Apache2::Const::DECLINED unless $template;
35
36     $template = $ctx->{skin} . "/$template";
37     $ctx->{page_args} = $page_args;
38     $r->content_type('text/html; encoding=utf8');
39
40     my $tt = Template->new({
41         OUTPUT => ($ctx->{force_valid_xml}) ? sub { validate_as_xml($r, @_); } : $r,
42         INCLUDE_PATH => $ctx->{template_paths},
43     });
44
45     unless($tt->process($template, {ctx => $ctx})) {
46         $r->log->warn('Template error: ' . $tt->error);
47         return Apache2::Const::HTTP_INTERNAL_SERVER_ERROR;
48     }
49
50     return Apache2::Const::OK;
51 }
52
53 sub validate_as_xml {
54     my $r = shift;
55     my $data = shift;
56     eval { XML::Simple->new->XMLin($data); };
57     if($@) {
58         my $err = "Invalid XML: $@";
59         $r->log->error($err);
60         $r->content_type('text/plain; encoding=utf8');
61         $r->print("\n$err\n\n$data");
62     } else {
63         $r->print($data);
64     }
65 }
66
67
68 sub load_context {
69     my $r = shift;
70     my $cgi = CGI->new;
71     my $ctx = $web_config->{ctx};
72     $ctx->{skin} = $cgi->cookie(OILS_HTTP_COOKIE_SKIN) || 'default';
73     $ctx->{theme} = $cgi->cookie(OILS_HTTP_COOKIE_THEME) || 'default';
74     $ctx->{locale} = 
75         $cgi->cookie(OILS_HTTP_COOKIE_LOCALE) || 
76         parse_accept_lang($r->headers_in->get('Accept-Language')) || 'en-US'; #  this will need some trimming
77     $r->log->debug('skin = ' . $ctx->{skin} . ' : theme = ' . 
78         $ctx->{theme} . ' : locale = ' . $ctx->{locale});
79     return $ctx;
80 }
81
82 # turn Accept-Language into sometihng EG can understand
83 sub parse_accept_lang {
84     my $al = shift;
85     return undef unless $al;
86     my ($locale) = split(/,/, $al);
87     ($locale) = split(/;/, $locale);
88     return undef unless $locale;
89     $locale =~ s/-(.*)/eval '-'.uc("$1")/e;
90     return $locale;
91 }
92
93 # Given a URI, finds the configured template and any extra page 
94 # arguments (trailing path info).  Any extra data is returned
95 # as page arguments, in the form of an array, one item per 
96 # /-separated URI component
97 sub find_template {
98     my $r = shift;
99     my $base = shift;
100     my $ctx = shift;
101     my $skin = $ctx->{skin};
102     my $path = $r->uri;
103     $path =~ s/$base//og;
104     my @parts = split('/', $path);
105     my $template = '';
106     my $page_args = [];
107     my $handler = $web_config->{handlers};
108     while(@parts) {
109         my $part = shift @parts;
110         next unless $part;
111         my $t = $handler->{$part};
112         if(ref $t) {
113             $handler = $t;
114         } else {
115             $template = $t;
116             $page_args = [@parts];
117             last;
118         }
119     }
120
121     unless($template) { # no template configured
122
123         # see if we can magically find the template based on the path and default extension
124         my $ext = $ctx->{default_template_extension};
125
126         my @parts = split('/', $path);
127         my $localpath = $path;
128         my @args;
129         while(@parts) {
130             last unless $localpath;
131             for my $tpath (@{$ctx->{template_paths}}) {
132                 my $fpath = "$tpath/$skin/$localpath.$ext";
133                 $r->log->debug("looking at possible template $fpath");
134                 if(-r $fpath) {
135                     $template = "$localpath.$ext";
136                     last;
137                 }
138             }
139             last if $template;
140             push(@args, pop @parts);
141             $localpath = '/'.join('/', @parts);
142         } 
143
144         $page_args = [@args];
145
146         # no template configured or found
147         unless($template) {
148             $r->log->warn("No template configured for path $path");
149             return ();
150         }
151     }
152
153     $r->log->debug("template = $template : page args = @$page_args");
154     return ($template, $page_args);
155 }
156
157 # if the web configuration file has never been loaded or has
158 # changed since the last load, reload it
159 sub check_web_config {
160     my $r = shift;
161     my $epoch = stat($web_config_file)->mtime;
162     unless($web_config_edit_time and $web_config_edit_time == $epoch) {
163         $r->log->debug("Reloading web config after edit...") if $r;
164         $web_config_edit_time = $epoch;
165         $web_config = parse_config($web_config_file);
166     }
167 }
168
169 sub parse_config {
170     my $cfg_file = shift;
171     my $data = XML::Simple->new->XMLin($cfg_file);
172     my $ctx = {};
173     my $handlers = {};
174
175     $ctx->{media_prefix} = (ref $data->{media_prefix}) ? '' : $data->{media_prefix};
176     $ctx->{base_uri} = (ref $data->{base_uri}) ? '' : $data->{base_uri};
177     $ctx->{template_paths} = [];
178     $ctx->{force_valid_xml} = ($data->{force_valid_xml} =~ /true/io) ? 1 : 0;
179     $ctx->{default_template_extension} = $data->{default_template_extension} || 'tt2';
180
181     my $tpaths = $data->{template_paths}->{path};
182     $tpaths = [$tpaths] unless ref $tpaths;
183     push(@{$ctx->{template_paths}}, $_) for @$tpaths;
184
185     for my $handler (@{$data->{handlers}->{handler}}) {
186         my @parts = split('/', $handler->{path});
187         my $h = $handlers;
188         my $pcount = scalar(@parts);
189         for(my $i = 0; $i < $pcount; $i++) {
190             my $p = $parts[$i];
191             unless(defined $h->{$p}) {
192                 if($i == $pcount - 1) {
193                     $h->{$p} = $handler->{template};
194                     last;
195                 } else {
196                     $h->{$p} = {};
197                 }
198             }
199             $h = $h->{$p};
200         }
201     }
202
203     return {ctx => $ctx, handlers => $handlers};
204 }
205
206
207 1;