]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Record.pm
TPAC: Fix filtering of org units in record details
[Evergreen.git] / Open-ILS / src / perlmods / lib / OpenILS / WWW / EGCatLoader / Record.pm
1 package OpenILS::WWW::EGCatLoader;
2 use strict; use warnings;
3 use Apache2::Const -compile => qw(OK DECLINED FORBIDDEN HTTP_INTERNAL_SERVER_ERROR REDIRECT HTTP_BAD_REQUEST);
4 use OpenSRF::Utils::Logger qw/$logger/;
5 use OpenILS::Utils::CStoreEditor qw/:funcs/;
6 use OpenILS::Utils::Fieldmapper;
7 use OpenILS::Application::AppUtils;
8 my $U = 'OpenILS::Application::AppUtils';
9
10 # context additions: 
11 #   record : bre object
12 sub load_record {
13     my $self = shift;
14     my $ctx = $self->ctx;
15     $ctx->{page} = 'record';  
16
17     my $org = $self->_get_search_lib();
18     my $org_name = $ctx->{get_aou}->($org)->shortname;
19     my $pref_ou = $self->_get_pref_lib();
20     my $depth = $self->cgi->param('depth');
21     $depth = $ctx->{get_aou}->($org)->ou_type->depth 
22         unless defined $depth; # can be 0
23
24     my $copy_depth = $self->cgi->param('copy_depth');
25     $copy_depth = $depth unless defined $copy_depth; # can be 0
26     $self->ctx->{copy_depth} = $copy_depth;
27
28     my $copy_limit = int($self->cgi->param('copy_limit') || 10);
29     my $copy_offset = int($self->cgi->param('copy_offset') || 0);
30
31     my $rec_id = $ctx->{page_args}->[0]
32         or return Apache2::Const::HTTP_BAD_REQUEST;
33
34     $self->get_staff_search_settings;
35     if ($ctx->{staff_saved_search_size}) {
36         $ctx->{saved_searches} = ($self->staff_load_searches)[1];
37     }
38
39     $self->fetch_related_search_info($rec_id);
40
41     # run copy retrieval in parallel to bib retrieval
42     # XXX unapi
43     my $cstore = OpenSRF::AppSession->create('open-ils.cstore');
44     my $copy_rec = $cstore->request(
45         'open-ils.cstore.json_query.atomic', 
46         $self->mk_copy_query($rec_id, $org, $copy_depth, $copy_limit, $copy_offset, $pref_ou)
47     );
48
49     my (undef, @rec_data) = $self->get_records_and_facets([$rec_id], undef, {
50         flesh => '{holdings_xml,bmp,mra,acp,acnp,acns}',
51         site => $org_name,
52         depth => $depth,
53         pref_lib => $pref_ou
54     });
55     $ctx->{bre_id} = $rec_data[0]->{id};
56     $ctx->{marc_xml} = $rec_data[0]->{marc_xml};
57
58     $ctx->{copies} = $copy_rec->gather(1);
59     $ctx->{copy_limit} = $copy_limit;
60     $ctx->{copy_offset} = $copy_offset;
61
62     $ctx->{have_holdings_to_show} = 0;
63     $ctx->{have_mfhd_to_show} = 0;
64
65     $self->get_hold_copy_summary($rec_id, $org);
66
67     $cstore->kill_me;
68
69     if (
70         $ctx->{get_org_setting}->
71             ($org, "opac.fully_compressed_serial_holdings")
72     ) {
73         $ctx->{holding_summaries} =
74             $self->get_holding_summaries($rec_id, $org, $copy_depth);
75
76         $ctx->{have_holdings_to_show} =
77             scalar(@{$ctx->{holding_summaries}->{basic}}) ||
78             scalar(@{$ctx->{holding_summaries}->{index}}) ||
79             scalar(@{$ctx->{holding_summaries}->{supplement}});
80     } else {
81         $ctx->{mfhd_summaries} =
82             $self->get_mfhd_summaries($rec_id, $org, $copy_depth);
83
84         if ($ctx->{mfhd_summaries} && scalar(@{$ctx->{mfhd_summaries}})
85         ) {
86             $ctx->{have_mfhd_to_show} = 1;
87         };
88     }
89
90     my %expandies = (
91         marchtml => sub {
92             $ctx->{marchtml} = $self->mk_marc_html($rec_id);
93         },
94         issues => sub {
95             $ctx->{expanded_holdings} =
96                 $self->get_expanded_holdings($rec_id, $org, $copy_depth)
97                 if $ctx->{have_holdings_to_show};
98         },
99         cnbrowse => sub {
100             $self->prepare_browse_call_numbers();
101         }
102     );
103
104     my @expand = $self->cgi->param('expand');
105     if (grep {$_ eq 'all'} @expand) {
106         $ctx->{expand_all} = 1;
107         $expandies{$_}->() for keys %expandies;
108
109     } else {
110         for my $exp (@expand) {
111             $ctx->{"expand_$exp"} = 1;
112             $expandies{$exp}->() if exists $expandies{$exp};
113         }
114     }
115
116     return Apache2::Const::OK;
117 }
118
119 # collect IDs and info on the search that lead to this details page
120 # If no search query, etc is present, we leave ctx.search_result_index == -1
121 sub fetch_related_search_info {
122     my $self = shift;
123     my $rec_id = shift;
124     my $ctx = $self->ctx;
125     $ctx->{search_result_index} = -1;
126
127     $self->load_rresults(internal => 1);
128
129     my @search_ids = @{$ctx->{ids}};
130     return unless @search_ids;
131
132     for my $idx (0..$#search_ids) {
133         if ($search_ids[$idx] == $rec_id) {
134             $ctx->{prev_search_record} = $search_ids[$idx - 1] if $idx > 0;
135             $ctx->{next_search_record} = $search_ids[$idx + 1];
136             $ctx->{search_result_index} = $idx;
137             last;
138         }
139     }
140
141     $ctx->{first_search_record} = $search_ids[0];
142     $ctx->{last_search_record} = $search_ids[-1];
143 }
144
145
146 sub mk_copy_query {
147     my $self = shift;
148     my $rec_id = shift;
149     my $org = shift;
150     my $depth = shift;
151     my $copy_limit = shift;
152     my $copy_offset = shift;
153     my $pref_ou = shift;
154
155     my $query = {
156         select => {
157             acp => ['id', 'barcode', 'circ_lib', 'create_date', 'age_protect', 'holdable'],
158             acpl => [
159                 {column => 'name', alias => 'copy_location'},
160                 {column => 'holdable', alias => 'location_holdable'}
161             ],
162             ccs => [
163                 {column => 'name', alias => 'copy_status'},
164                 {column => 'holdable', alias => 'status_holdable'}
165             ],
166             acn => [
167                 {column => 'label', alias => 'call_number_label'},
168                 {column => 'id', alias => 'call_number'}
169             ],
170             circ => ['due_date'],
171             acnp => [
172                 {column => 'label', alias => 'call_number_prefix_label'},
173                 {column => 'id', alias => 'call_number_prefix'}
174             ],
175             acns => [
176                 {column => 'label', alias => 'call_number_suffix_label'},
177                 {column => 'id', alias => 'call_number_suffix'}
178             ],
179             bmp => [
180                 {column => 'label', alias => 'part_label'},
181             ]
182         },
183
184         from => {
185             acp => {
186                 acn => { 
187                     join => { 
188                         acnp => { fkey => 'prefix' },
189                         acns => { fkey => 'suffix' }
190                     },
191                     filter => [{deleted => 'f'}, {record => $rec_id}],
192                 },
193                 circ => { # If the copy is circulating, retrieve the open circ
194                     type => 'left',
195                     filter => {checkin_time => undef}
196                 },
197                 acpl => {},
198                 ccs => {},
199                 aou => {},
200                 acpm => {
201                     type => 'left',
202                     join => {
203                         bmp => { type => 'left' }
204                     }
205                 }
206             }
207         },
208
209         where => {
210             '+acp' => {deleted => 'f' }
211         },
212
213         order_by => [
214             { class => "aou", field => 'id', 
215               transform => 'evergreen.rank_ou', params => [$org]
216             },
217             {class => 'aou', field => 'name'}, 
218             {class => 'acn', field => 'label'},
219             { class => "acp", field => 'status',
220               transform => 'evergreen.rank_cp_status'
221             }
222         ],
223
224         limit => $copy_limit,
225         offset => $copy_offset
226     };
227
228     if($org != $self->ctx->{aou_tree}->()->id) { 
229         # no need to add the org join filter if we're not actually filtering
230         $query->{from}->{acp}->{aou} = {
231             fkey => 'circ_lib',
232             field => 'id',
233             filter => {
234                 id => {
235                     in => {
236                         select => {aou => [{
237                             column => 'id', 
238                             transform => 'actor.org_unit_descendants', 
239                             result_field => 'id', 
240                             params => [$depth]
241                         }]},
242                         from => 'aou',
243                         where => {id => $org}
244                     }
245                 }
246             }
247         }
248     };
249
250     # Filter hidden items if this is the public catalog
251     unless($self->ctx->{is_staff}) { 
252         $query->{where}->{'+acp'}->{opac_visible} = 't';
253         $query->{from}->{'acp'}->{'acpl'}->{filter} = {opac_visible => 't'};
254         $query->{from}->{'acp'}->{'ccs'}->{filter} = {opac_visible => 't'};
255         $query->{where}->{'+aou'}->{opac_visible} = 't';
256     }
257
258     return $query;
259 }
260
261 sub mk_marc_html {
262     my($self, $rec_id) = @_;
263
264     # could be optimized considerably by performing the xslt on the already fetched record
265     return $U->simplereq(
266         'open-ils.search', 
267         'open-ils.search.biblio.record.html', $rec_id, 1);
268 }
269
270 sub get_holding_summaries {
271     my ($self, $rec_id, $org, $depth) = @_;
272
273     my $serial = create OpenSRF::AppSession("open-ils.serial");
274     my $result = $serial->request(
275         "open-ils.serial.bib.summary_statements",
276         $rec_id, {"org_id" => $org, "depth" => $depth}
277     )->gather(1);
278
279     $serial->kill_me;
280     return $result;
281 }
282
283 sub get_mfhd_summaries {
284     my ($self, $rec_id, $org, $depth) = @_;
285
286     my $serial = create OpenSRF::AppSession("open-ils.search");
287     my $result = $serial->request(
288         "open-ils.search.serial.record.bib.retrieve",
289         $rec_id, $org, $depth
290     )->gather(1);
291
292     $serial->kill_me;
293     return $result;
294 }
295
296 sub get_expanded_holdings {
297     my ($self, $rec_id, $org, $depth) = @_;
298
299     my $holding_limit = int($self->cgi->param("holding_limit") || 10);
300     my $holding_offset = int($self->cgi->param("holding_offset") || 0);
301     my $type = $self->cgi->param("expand_holding_type");
302
303     my $serial =  create OpenSRF::AppSession("open-ils.serial");
304     my $result = $serial->request(
305         "open-ils.serial.received_siss.retrieve.by_bib.atomic",
306         $rec_id, {
307             "ou" => $org, "depth" => $depth,
308             "limit" => $holding_limit, "offset" => $holding_offset,
309             "type" => $type
310         }
311     )->gather(1);
312
313     $serial->kill_me;
314     return $result;
315 }
316
317 sub any_call_number_label {
318     my ($self) = @_;
319
320     if ($self->ctx->{copies} and @{$self->ctx->{copies}}) {
321         return $self->ctx->{copies}->[0]->{call_number_label};
322     } else {
323         return;
324     }
325 }
326
327 sub prepare_browse_call_numbers {
328     my ($self) = @_;
329
330     my $cn = ($self->cgi->param("cn") || $self->any_call_number_label) or
331         return [];
332
333     my $org_unit = $self->ctx->{get_aou}->($self->cgi->param('loc')) ||
334         $self->ctx->{aou_tree}->();
335
336     my $supercat = create OpenSRF::AppSession("open-ils.supercat");
337     my $results = $supercat->request(
338         "open-ils.supercat.call_number.browse", 
339         $cn, $org_unit->shortname, 9, $self->cgi->param("cnoffset")
340     )->gather(1) || [];
341
342     $supercat->kill_me;
343
344     $self->ctx->{browsed_call_numbers} = [
345         map {
346             $_->record->marc(
347                 (new XML::LibXML)->parse_string($_->record->marc)
348             );
349             $_;
350         } @$results
351     ];
352     $self->ctx->{browsing_ou} = $org_unit;
353 }
354
355 sub get_hold_copy_summary {
356     my ($self, $rec_id, $org) = @_;
357     
358     my $search = OpenSRF::AppSession->create('open-ils.search');
359     my $req1 = $search->request(
360         'open-ils.search.biblio.record.copy_count', $org, $rec_id); 
361
362     $self->ctx->{record_hold_count} = $U->simplereq(
363         'open-ils.circ', 'open-ils.circ.bre.holds.count', $rec_id);
364
365     $self->ctx->{copy_summary} = $req1->recv->content;
366
367     $search->kill_me;
368 }
369
370 sub load_print_record {
371     my $self = shift;
372
373     my $rec_id = $self->ctx->{page_args}->[0] 
374         or return Apache2::Const::HTTP_BAD_REQUEST;
375
376     $self->{ctx}->{bre_id} = $rec_id;
377     $self->{ctx}->{printable_record} = $U->simplereq(
378         'open-ils.search',
379         'open-ils.search.biblio.record.print', $rec_id);
380
381     return Apache2::Const::OK;
382 }
383
384 sub load_email_record {
385     my $self = shift;
386
387     my $rec_id = $self->ctx->{page_args}->[0] 
388         or return Apache2::Const::HTTP_BAD_REQUEST;
389
390     $self->{ctx}->{bre_id} = $rec_id;
391     $U->simplereq(
392         'open-ils.search',
393         'open-ils.search.biblio.record.email', 
394         $self->ctx->{authtoken}, $rec_id);
395
396     return Apache2::Const::OK;
397 }
398
399 1;