1 package OpenILS::WWW::EGWeb;
2 use strict; use warnings;
7 use Apache2::Const -compile => qw(OK DECLINED HTTP_INTERNAL_SERVER_ERROR);
9 use OpenSRF::EX qw(:try);
10 use OpenILS::Utils::CStoreEditor;
12 use constant OILS_HTTP_COOKIE_SKIN => 'oils:skin';
13 use constant OILS_HTTP_COOKIE_THEME => 'oils:theme';
14 use constant OILS_HTTP_COOKIE_LOCALE => 'oils:locale';
18 my $web_config_edit_time;
22 $web_config_file = shift;
23 unless(-r $web_config_file) {
24 warn "Invalid web config $web_config_file";
33 check_web_config($r); # option to disable this
34 my $ctx = load_context($r);
35 my $base = $ctx->{base_path};
37 $r->content_type('text/html; encoding=utf8');
38 my $stat = run_context_loader($r, $ctx);
39 return $stat unless $stat == Apache2::Const::OK;
41 my($template, $page_args, $as_xml) = find_template($r, $base, $ctx);
42 return Apache2::Const::DECLINED unless $template;
44 $template = $ctx->{skin} . "/$template";
45 $ctx->{page_args} = $page_args;
47 my $tt = Template->new({
48 OUTPUT => ($as_xml) ? sub { parse_as_xml($r, $ctx, @_); } : $r,
49 INCLUDE_PATH => $ctx->{template_paths},
50 DEBUG => $ctx->{debug_template}
53 unless($tt->process($template, {ctx => $ctx})) {
54 $r->log->warn('Template error: ' . $tt->error);
55 return Apache2::Const::HTTP_INTERNAL_SERVER_ERROR;
58 return Apache2::Const::OK;
62 sub run_context_loader {
66 my $stat = Apache2::Const::OK;
68 my $loader = $r->dir_config('OILSWebContextLoader');
69 return $stat unless $loader;
73 $stat = $loader->new($r, $ctx)->load;
77 $r->log->error("Context Loader error: $@");
78 return Apache2::Const::HTTP_INTERNAL_SERVER_ERROR;
81 $r->log->warn("context loader resulted in status $stat");
93 my $doc = XML::LibXML->new->parse_string($data);
94 $data = $doc->documentElement->toStringC14N;
95 $data = $ctx->{final_dtd} . "\n" . $data;
99 my $err = "Invalid XML: $e";
100 $r->log->error($err);
101 $r->content_type('text/plain; encoding=utf8');
102 $r->print("\n$err\n\n$data");
105 $r->print($data) if ($success);
112 my $ctx = {}; # new context for each page load
113 $ctx->{$_} = $web_config->{base_ctx}->{$_} for keys %{$web_config->{base_ctx}};
114 $ctx->{hostname} = $r->hostname;
115 $ctx->{base_url} = $cgi->url(-base => 1);
116 $ctx->{skin} = $cgi->cookie(OILS_HTTP_COOKIE_SKIN) || 'default';
117 $ctx->{theme} = $cgi->cookie(OILS_HTTP_COOKIE_THEME) || 'default';
119 $cgi->cookie(OILS_HTTP_COOKIE_LOCALE) ||
120 parse_accept_lang($r->headers_in->get('Accept-Language')) || 'en-US';
121 $r->log->debug('skin = ' . $ctx->{skin} . ' : theme = ' .
122 $ctx->{theme} . ' : locale = ' . $ctx->{locale});
126 # turn Accept-Language into sometihng EG can understand
127 sub parse_accept_lang {
129 return undef unless $al;
130 my ($locale) = split(/,/, $al);
131 ($locale) = split(/;/, $locale);
132 return undef unless $locale;
133 $locale =~ s/-(.*)/eval '-'.uc("$1")/e;
137 # Given a URI, finds the configured template and any extra page
138 # arguments (trailing path info). Any extra data is returned
139 # as page arguments, in the form of an array, one item per
140 # /-separated URI component
145 my $skin = $ctx->{skin};
147 $path =~ s/$base//og;
148 my @parts = split('/', $path);
151 my $as_xml = $ctx->{force_valid_xml};
152 my $handler = $web_config->{handlers};
155 my $part = shift @parts;
157 my $t = $handler->{$part};
158 if(ref($t) eq 'PathConfig') {
159 $template = $t->{template};
160 $as_xml = ($t->{as_xml} and $t->{as_xml} =~ /true/io) || $as_xml;
161 $page_args = [@parts];
168 unless($template) { # no template configured
170 # see if we can magically find the template based on the path and default extension
171 my $ext = $ctx->{default_template_extension};
173 my @parts = split('/', $path);
174 my $localpath = $path;
177 last unless $localpath;
178 for my $tpath (@{$ctx->{template_paths}}) {
179 my $fpath = "$tpath/$skin/$localpath.$ext";
180 $r->log->debug("looking at possible template $fpath");
182 $template = "$localpath.$ext";
187 push(@args, pop @parts);
188 $localpath = '/'.join('/', @parts);
191 $page_args = [@args];
193 # no template configured or found
195 $r->log->warn("No template configured for path $path");
200 $r->log->debug("template = $template : page args = @$page_args");
201 return ($template, $page_args, $as_xml);
204 # if the web configuration file has never been loaded or has
205 # changed since the last load, reload it
206 sub check_web_config {
208 my $epoch = stat($web_config_file)->mtime;
209 unless($web_config_edit_time and $web_config_edit_time == $epoch) {
210 $r->log->debug("Reloading web config after edit...") if $r;
211 $web_config_edit_time = $epoch;
212 $web_config = parse_config($web_config_file);
217 my $cfg_file = shift;
218 my $data = XML::Simple->new->XMLin($cfg_file);
222 $ctx->{media_prefix} = (ref $data->{media_prefix}) ? '' : $data->{media_prefix};
223 $ctx->{base_path} = (ref $data->{base_path}) ? '' : $data->{base_path};
224 $ctx->{template_paths} = [];
225 $ctx->{force_valid_xml} = ($data->{force_valid_xml} =~ /true/io) ? 1 : 0;
226 $ctx->{debug_template} = ($data->{debug_template} =~ /true/io) ? 1 : 0;
227 $ctx->{default_template_extension} = $data->{default_template_extension} || 'tt2';
228 $ctx->{web_dir} = $data->{web_dir};
230 my $tpaths = $data->{template_paths}->{path};
231 $tpaths = [$tpaths] unless ref $tpaths;
232 push(@{$ctx->{template_paths}}, $_) for @$tpaths;
234 for my $handler (@{$data->{handlers}->{handler}}) {
235 my @parts = split('/', $handler->{path});
237 my $pcount = scalar(@parts);
238 for(my $i = 0; $i < $pcount; $i++) {
240 unless(defined $h->{$p}) {
241 if($i == $pcount - 1) {
242 $h->{$p} = PathConfig->new(%$handler);
252 return {base_ctx => $ctx, handlers => $handlers};
257 my($class, %args) = @_;
258 return bless(\%args, $class);