1 package OpenILS::WWW::EGCatLoader;
2 use strict; use warnings;
5 use Digest::MD5 qw(md5_hex);
6 use Apache2::Const -compile => qw(OK DECLINED HTTP_INTERNAL_SERVER_ERROR REDIRECT HTTP_BAD_REQUEST);
7 use OpenSRF::AppSession;
8 use OpenSRF::EX qw/:try/;
9 use OpenSRF::Utils::Logger qw/$logger/;
10 use OpenILS::Application::AppUtils;
11 use OpenILS::Utils::CStoreEditor qw/:funcs/;
12 use OpenILS::Utils::Fieldmapper;
13 my $U = 'OpenILS::Application::AppUtils';
15 my %cache; # proc-level cache
18 my($class, $apache, $ctx) = @_;
20 my $self = bless({}, ref($class) || $class);
22 $self->apache($apache);
26 OpenILS::Utils::CStoreEditor->init; # just in case
27 $self->editor(new_editor());
33 # current Apache2::RequestRec;
35 my($self, $apache) = @_;
36 $self->{apache} = $apache if $apache;
37 return $self->{apache};
40 # runtime / template context
43 $self->{ctx} = $ctx if $ctx;
49 my($self, $editor) = @_;
50 $self->{editor} = $editor if $editor;
51 return $self->{editor};
57 $self->{cgi} = $cgi if $cgi;
62 # load common data, then load page data
67 my $stat = $self->load_common;
68 return $stat unless $stat == Apache2::Const::OK;
70 my $path = $self->apache->path_info;
71 return $self->load_home if $path =~ /opac\/home/;
72 return $self->load_login if $path =~ /opac\/login/;
73 return $self->load_logout if $path =~ /opac\/logout/;
74 return $self->load_rresults if $path =~ /opac\/results/;
75 return $self->load_record if $path =~ /opac\/record/;
76 return $self->load_place_hold if $path =~ /opac\/place_hold/;
78 return $self->load_myopac_holds if $path =~ /opac\/myopac\/holds/;
79 return $self->load_myopac if $path =~ /opac\/myopac/;
81 return Apache2::Const::OK;
84 # general purpose utility functions added to the environment
87 my $e = $self->editor;
90 $cache{map} = {}; # public object maps
91 $cache{list} = {}; # public object lists
93 # fetch-on-demand-and-cache subs for commonly used public data
94 my @public_classes = qw/ccs aout/;
96 for my $hint (@public_classes) {
99 $Fieldmapper::fieldmap->{$_}->{hint} eq $hint
100 } keys %{ $Fieldmapper::fieldmap };
102 $class =~ s/Fieldmapper:://o;
106 my $list_key = $hint . '_list';
107 my $find_key = "find_$hint";
109 $ctx->{$list_key} = sub {
110 my $method = "retrieve_all_$class";
111 $cache{list}{$hint} = $e->$method() unless $cache{list}{$hint};
112 return $cache{list}{$hint};
115 $cache{map}{$hint} = {};
117 $ctx->{$find_key} = sub {
119 return $cache{map}{$hint}{$id} if $cache{map}{$hint}{$id};
120 ($cache{map}{$hint}{$id}) = grep { $_->id == $id } @{$ctx->{$list_key}->()};
121 return $cache{map}{$hint}{$id};
126 $ctx->{aou_tree} = sub {
128 # fetch the org unit tree
129 unless($cache{aou_tree}) {
130 my $tree = $e->search_actor_org_unit([
131 { parent_ou => undef},
133 flesh_fields => {aou => ['children']},
134 order_by => {aou => 'name'}
138 # flesh the org unit type for each org unit
139 # and simultaneously set the id => aou map cache
143 $node->ou_type( $ctx->{find_aout}->($node->ou_type) );
144 $cache{map}{aou}{$node->id} = $node;
145 flesh_aout($_, $ctx) foreach @{$node->children};
147 flesh_aout($tree, $ctx);
149 $cache{aou_tree} = $tree;
152 return $cache{aou_tree};
155 # Add a special handler for the tree-shaped org unit cache
156 $cache{map}{aou} = {};
157 $ctx->{find_aou} = sub {
159 $ctx->{aou_tree}->(); # force the org tree to load
160 return $cache{map}{aou}{$org_id};
167 # user_status : hash of user circ numbers
171 my $e = $self->editor;
172 my $ctx = $self->ctx;
174 if($e->authtoken($self->cgi->cookie('ses'))) {
178 $ctx->{authtoken} = $e->authtoken;
179 $ctx->{user} = $e->requestor;
180 $ctx->{user_stats} = $U->simplereq(
182 'open-ils.actor.user.opac.vital_stats',
183 $e->authtoken, $e->requestor->id);
187 return $self->load_logout;
191 return Apache2::Const::OK;
196 $self->ctx->{page} = 'home';
197 return Apache2::Const::OK;
203 my $cgi = $self->cgi;
205 $self->ctx->{page} = 'login';
207 my $username = $cgi->param('username');
208 my $password = $cgi->param('password');
210 return Apache2::Const::OK unless $username and $password;
212 my $seed = $U->simplereq(
214 'open-ils.auth.authenticate.init',
217 my $response = $U->simplereq(
219 'open-ils.auth.authenticate.complete',
220 { username => $username,
221 password => md5_hex($seed . md5_hex($password)),
226 # XXX check event, redirect as necessary
228 my $home = $self->apache->unparsed_uri;
229 $home =~ s/\/login/\/home/;
231 $self->apache->print(
233 -url => $cgi->param('origin') || $home,
234 -cookie => $cgi->cookie(
238 -value => $response->{payload}->{authtoken},
239 -expires => CORE::time + $response->{payload}->{authtime}
244 return Apache2::Const::REDIRECT;
250 my $path = $self->apache->uri;
251 $path =~ s/(\/[^\/]+$)/\/home/;
252 my $url = 'http://' . $self->apache->hostname . "$path";
254 $self->apache->print(
255 $self->cgi->redirect(
257 -cookie => $self->cgi->cookie(
266 return Apache2::Const::REDIRECT;
272 # records : list of bre's and copy-count objects
275 my $cgi = $self->cgi;
276 my $ctx = $self->ctx;
277 my $e = $self->editor;
279 $ctx->{page} = 'rresult';
280 my $page = $cgi->param('page') || 0;
281 my $facet = $cgi->param('facet');
282 my $query = $cgi->param('query');
283 my $limit = $cgi->param('limit') || 10; # XXX user settings
284 my $args = {limit => $limit, offset => $page * $limit};
285 $query = "$query $facet" if $facet;
289 $results = $U->simplereq(
291 'open-ils.search.biblio.multiclass.query.staff',
296 $logger->error("multiclass search error: $err");
297 $results = {count => 0, ids => []};
300 my $rec_ids = [map { $_->[0] } @{$results->{ids}}];
302 $ctx->{records} = [];
303 $ctx->{search_facets} = {};
304 $ctx->{page_size} = $limit;
305 $ctx->{hit_count} = $results->{count};
307 return Apache2::Const::OK if @$rec_ids == 0;
309 my $cstore1 = OpenSRF::AppSession->create('open-ils.cstore');
310 my $bre_req = $cstore1->request(
311 'open-ils.cstore.direct.biblio.record_entry.search', {id => $rec_ids});
313 my $search = OpenSRF::AppSession->create('open-ils.search');
314 my $facet_req = $search->request('open-ils.search.facet_cache.retrieve', $results->{facet_key}, 10);
316 unless($cache{cmf}) {
317 $cache{cmf} = $e->search_config_metabib_field({id => {'!=' => undef}});
318 $ctx->{metabib_field} = $cache{cmf};
319 #$cache{cmc} = $e->search_config_metabib_class({name => {'!=' => undef}});
320 #$ctx->{metabib_class} = $cache{cmc};
324 while(my $resp = $bre_req->recv) {
325 my $bre = $resp->content;
327 # XXX farm out to multiple cstore sessions before loop, then collect after
328 my $copy_counts = $e->json_query(
329 {from => ['asset.record_copy_count', 1, $bre->id, 0]})->[0];
334 marc_xml => XML::LibXML->new->parse_string($bre->marc),
335 copy_counts => $copy_counts
342 # shove recs into context in search results order
343 for my $rec_id (@$rec_ids) {
346 grep { $_->{bre}->id == $rec_id } @data
350 my $facets = $facet_req->gather(1);
352 for my $cmf_id (keys %$facets) { # quick-n-dirty
353 my ($cmf) = grep { $_->id eq $cmf_id } @{$cache{cmf}};
354 $facets->{$cmf_id} = {cmf => $cmf, data => $facets->{$cmf_id}};
356 $ctx->{search_facets} = $facets;
358 return Apache2::Const::OK;
362 # record : bre object
365 $self->ctx->{page} = 'record';
367 my $rec_id = $self->ctx->{page_args}->[0]
368 or return Apache2::Const::HTTP_BAD_REQUEST;
370 $self->ctx->{record} = $self->editor->retrieve_biblio_record_entry([
375 bre => ['call_numbers'],
376 acn => ['copies'] # limit, paging, etc.
381 $self->ctx->{marc_xml} = XML::LibXML->new->parse_string($self->ctx->{record}->marc);
383 return Apache2::Const::OK;
387 # user : au object, fleshed
390 $self->ctx->{page} = 'myopac';
392 $self->ctx->{user} = $self->editor->retrieve_actor_user([
393 $self->ctx->{user}->id,
403 return Apache2::Const::OK;
406 sub load_myopac_holds {
408 my $e = $self->editor;
409 my $ctx = $self->ctx;
411 my $circ = OpenSRF::AppSession->create('open-ils.circ');
412 my $hold_ids = $circ->request(
413 'open-ils.circ.holds.id_list.retrieve',
418 my $req = $circ->request(
419 'open-ils.circ.hold.details.batch.retrieve',
424 # any requests we can fire off here?
425 # XXX use marc attrs instead of the mvr's returned by hold.details
428 while(my $resp = $req->recv) {
429 my $hold = $resp->content;
430 # need to fetch anything else?
431 push(@{$ctx->{holds}}, $hold);
435 return Apache2::Const::OK;
439 sub load_place_hold {
441 my $ctx = $self->ctx;
442 my $e = $self->editor;
443 $self->ctx->{page} = 'place_hold';
445 $ctx->{hold_target} = $self->cgi->param('hold_target');
446 $ctx->{hold_type} = $self->cgi->param('hold_type');
447 $ctx->{default_pickup_lib} = $e->requestor->home_ou; # XXX staff
449 if($ctx->{hold_type} eq 'T') {
450 $ctx->{record} = $e->retrieve_biblio_record_entry($ctx->{hold_target});
454 $ctx->{marc_xml} = XML::LibXML->new->parse_string($ctx->{record}->marc);
456 if(my $pickup_lib = $self->cgi->param('pickup_lib')) {
459 patronid => $e->requestor->id,
460 titleid => $ctx->{hold_target}, # XXX
461 pickup_lib => $pickup_lib,
465 my $allowed = $U->simplereq(
467 'open-ils.circ.title_hold.is_possible',
471 if($allowed->{success} == 1) {
472 my $hold = Fieldmapper::action::hold_request->new;
474 $hold->pickup_lib($pickup_lib);
475 $hold->requestor($e->requestor->id);
476 $hold->usr($e->requestor->id); # XXX staff
477 $hold->target($ctx->{hold_target});
478 $hold->hold_type($ctx->{hold_type});
479 # frozen, expired, etc..
481 my $stat = $U->simplereq(
483 'open-ils.circ.holds.create',
487 if($stat and $stat > 0) {
488 $ctx->{hold_success} = 1;
490 $ctx->{hold_failed} = 1; # XXX process the events, etc
494 # place the hold and deliver results
497 return Apache2::Const::OK;