From fc99f4f2230061922f8bea0e061f04d240a1ddfa Mon Sep 17 00:00:00 2001 From: Bill Erickson Date: Mon, 10 Feb 2014 14:01:47 -0500 Subject: [PATCH] LP#1053397 TPAC metarecord search and holds UI API, TPAC backend, and UI bits for TPAC metarecord searching and holds. * Group Formats and Editions options in advanced search / searchbar * MR holds placement form, allowing selected formats and languages * MR holds targeting updated to work w/ new holdable formats composite definitions Signed-off-by: Bill Erickson Signed-off-by: Mike Rylander Signed-off-by: Dan Wells --- .../lib/OpenILS/Application/Circ/Holds.pm | 176 +++++++++++++++--- .../Application/Storage/Publisher/action.pm | 116 ++++++------ .../lib/OpenILS/WWW/EGCatLoader/Account.pm | 138 +++++++++++++- .../lib/OpenILS/WWW/EGCatLoader/Search.pm | 53 +++++- .../lib/OpenILS/WWW/EGCatLoader/Util.pm | 58 ++++-- .../src/perlmods/lib/OpenILS/WWW/EGWeb.pm | 5 +- Open-ILS/src/templates/opac/css/style.css.tt2 | 1 + Open-ILS/src/templates/opac/myopac/holds.tt2 | 6 +- .../src/templates/opac/myopac/holds/edit.tt2 | 9 + .../templates/opac/parts/advanced/search.tt2 | 16 +- Open-ILS/src/templates/opac/parts/config.tt2 | 7 + .../opac/parts/metarecord_hold_filters.tt2 | 89 +++++++++ .../src/templates/opac/parts/misc_util.tt2 | 41 ++-- .../src/templates/opac/parts/place_hold.tt2 | 11 ++ .../src/templates/opac/parts/result/table.tt2 | 43 ++++- Open-ILS/src/templates/opac/results.tt2 | 24 ++- Open-ILS/web/js/ui/default/opac/simple.js | 5 +- 17 files changed, 651 insertions(+), 147 deletions(-) create mode 100644 Open-ILS/src/templates/opac/parts/metarecord_hold_filters.tt2 diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Holds.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Holds.pm index 8ba48ab600..4f56877277 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Holds.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Holds.pm @@ -92,8 +92,15 @@ sub test_and_create_hold_batch { elsif ($$params{'hold_type'} eq 'P') { $target_field = 'partid'; } else { return undef; } + my $formats_map = delete $$params{holdable_formats_map}; + foreach (@$target_list) { $$params{$target_field} = $_; + + # copy the requested formats from the target->formats map + # into the top-level formats attr for each hold + $$params{holdable_formats} = $formats_map->{$_}; + my $res; ($res) = $self->method_lookup( 'open-ils.circ.title_hold.is_possible')->run($auth, $params, $override ? $oargs : {}); @@ -2470,10 +2477,9 @@ sub do_possibility_checks { } elsif( $hold_type eq OILS_HOLD_TYPE_METARECORD ) { - my $maps = $e->search_metabib_metarecord_source_map({metarecord=>$mrid}); - my @recs = map { $_->source } @$maps; + my ($recs) = __PACKAGE__->method_lookup('open-ils.circ.holds.metarecord.filterd_records')->run($mrid, $holdable_formats); my @status = (); - for my $rec (@recs) { + for my $rec (@$recs) { @status = _check_title_hold_is_possible( $rec, $depth, $request_lib, $patron, $e->requestor, $pickup_lib, $selection_ou, $holdable_formats, $oargs ); @@ -2484,6 +2490,15 @@ sub do_possibility_checks { # else { Unrecognized hold_type ! } # FIXME: return error? or 0? } +sub MR_filter_records { + return $U->storagereq('open-ils.storage.metarecord.filtered_records.atomic', $_[2], $_[3]); +} +__PACKAGE__->register_method( + method => 'MR_filter_records', + api_name => 'open-ils.circ.holds.metarecord.filterd_records', +); + + my %prox_cache; sub create_ranged_org_filter { my($e, $selection_ou, $depth) = @_; @@ -2511,11 +2526,7 @@ sub create_ranged_org_filter { sub _check_title_hold_is_possible { my( $titleid, $depth, $request_lib, $patron, $requestor, $pickup_lib, $selection_ou, $holdable_formats, $oargs ) = @_; - - my ($types, $formats, $lang); - if (defined($holdable_formats)) { - ($types, $formats, $lang) = split '-', $holdable_formats; - } + # $holdable_formats is now unused. We pre-filter the MR's records. my $e = new_editor(); my %org_filter = create_ranged_org_filter($e, $selection_ou, $depth); @@ -2529,23 +2540,7 @@ sub _check_title_hold_is_possible { acn => { field => 'id', fkey => 'call_number', - 'join' => { - bre => { - field => 'id', - filter => { id => $titleid }, - fkey => 'record' - }, - mrd => { - field => 'record', - fkey => 'record', - filter => { - record => $titleid, - ( $types ? (item_type => [split '', $types]) : () ), - ( $formats ? (item_form => [split '', $formats]) : () ), - ( $lang ? (item_lang => $lang) : () ) - } - } - } + filter => { record => $titleid } }, acpl => { field => 'id', filter => { holdable => 't'}, fkey => 'location' }, ccs => { field => 'id', filter => { holdable => 't'}, fkey => 'status' }, @@ -4205,4 +4200,135 @@ sub calculate_expire_time return undef; } + +__PACKAGE__->register_method( + method => 'mr_hold_filter_attrs', + api_name => 'open-ils.circ.mmr.holds.filters', + authoritative => 1, + stream => 1, + signature => { + desc => q/ + Returns the set of available formats and languages for the + constituent records of the provided metarcord. + If an array of hold IDs is also provided, information about + each is returned as well. This information includes: + 1. a slightly easier to read version of holdable_formats + 2. attributes describing the set of format icons included + in the set of desired, constituent records. + /, + params => [ + {desc => 'Metarecord ID', type => 'number'}, + {desc => 'Hold ID List', type => 'array'}, + ], + return => { + desc => q/ + Stream of objects. The first will have a 'metarecord' key + containing non-hold-specific metarecord information, subsequent + responses will contain a 'hold' key containing hold-specific + information + /, + type => 'object' + } + } +); + +sub mr_hold_filter_attrs { + my ($self, $client, $mr_id, $hold_ids) = @_; + my $e = new_editor(); + + + my $mr = $e->retrieve_metabib_metarecord($mr_id) or return $e->event; + my $bre_ids = $e->json_query({ + select => {mmrsm => ['source']}, + from => 'mmrsm', + where => {'+mmrsm' => {metarecord => $mr_id}} + }); + $bre_ids = [map {$_->{source}} @$bre_ids]; + + my $item_lang_attr = 'item_lang'; # configurable? + my $format_attr = $e->retrieve_config_global_flag( + 'opac.metarecord.holds.format_attr')->value; + + # helper sub for fetching ccvms for a batch of record IDs + sub get_batch_ccvms { + my ($e, $attr, $bre_ids) = @_; + return [] unless $bre_ids and @$bre_ids; + my $vals = $e->search_metabib_record_attr_flat({ + attr => $attr, + id => $bre_ids + }); + return [] unless @$vals; + return $e->search_config_coded_value_map({ + ctype => $attr, + code => [map {$_->value} @$vals] + }); + } + + my $langs = get_batch_ccvms($e, $item_lang_attr, $bre_ids); + my $formats = get_batch_ccvms($e, $format_attr, $bre_ids); + + $client->respond({ + metarecord => { + id => $mr_id, + formats => $formats, + langs => $langs + } + }); + + return unless $hold_ids; + my $icon_attr = $e->retrieve_config_global_flag('opac.icon_attr'); + $icon_attr = $icon_attr ? $icon_attr->value : ''; + + for my $hold_id (@$hold_ids) { + my $hold = $e->retrieve_action_hold_request($hold_id) + or return $e->event; + + next unless $hold->hold_type eq 'M'; + + my $resp = { + hold => { + id => $hold_id, + formats => [], + langs => [] + } + }; + + # collect the ccvm's for the selected formats / language ( + # (i.e. the holdable formats) on the MR. + # this assumes a two-key structure for format / language, + # though assumption is made about the keys themselves. + my $hformats = OpenSRF::Utils::JSON->JSON2perl($hold->holdable_formats); + my $lang_vals = []; + my $format_vals = []; + for my $val (values %$hformats) { + # val is either a single ccvm or an array of them + $val = [$val] unless ref $val eq 'ARRAY'; + for my $node (@$val) { + push (@$lang_vals, $node->{_val}) + if $node->{_attr} eq $item_lang_attr; + push (@$format_vals, $node->{_val}) + if $node->{_attr} eq $format_attr; + } + } + + # fetch the ccvm's for consistency with the {metarecord} blob + $resp->{hold}{formats} = $e->search_config_coded_value_map({ + ctype => $format_attr, code => $format_vals}); + $resp->{hold}{langs} = $e->search_config_coded_value_map({ + ctype => $item_lang_attr, code => $lang_vals}); + + # find all of the bib records within this metarcord whose + # format / language match the holdable formats on the hold + my ($bre_ids) = $self->method_lookup( + 'open-ils.circ.holds.metarecord.filterd_records')->run( + $hold->target, $hold->holdable_formats); + + # now find all of the 'icon' attributes for the records + $resp->{hold}{icons} = get_batch_ccvms($e, $icon_attr, $bre_ids); + $client->respond($resp); + } + + return; +} + 1; diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher/action.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher/action.pm index b81e8470a5..2f98fa5532 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher/action.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher/action.pm @@ -1283,6 +1283,47 @@ __PACKAGE__->register_method( ); +sub MR_records_matching_format { + my $self = shift; + my $client = shift; + my $MR = shift; + my $filter = shift; + + # find filters for MR holds + my $mr_filter; + if (defined($filter)) { + ($mr_filter) = @{action::hold_request->db_Main->selectcol_arrayref( + 'SELECT metabib.compile_composite_attr(?)', + {}, + $filter + )}; + } + + my $records = [metabib::metarecord->retrieve($MR)->source_records]; + + if (!$mr_filter) { + $client->respond( $_->id ) for @$records; + } else { + for my $r ( map { isTrue($_->deleted) ? () : ($_->id) } @$records ) { + $client->respond($r) if + @{action::hold_request->db_Main->selectcol_arrayref( + 'SELECT source FROM metabib.record_attr_vector_list WHERE source = ? AND vlist @@ ?', + {}, + $r, + $mr_filter + )}; + } + } + return; # discard final l-val +} +__PACKAGE__->register_method( + api_name => 'open-ils.storage.metarecord.filtered_records', + api_level => 1, + stream => 1, + argc => 2, + method => 'MR_records_matching_format', +); + sub new_hold_copy_targeter { my $self = shift; @@ -1432,41 +1473,23 @@ sub new_hold_copy_targeter { my $all_copies = []; - # find filters for MR holds - my ($types, $formats, $lang); - if (defined($hold->holdable_formats)) { - ($types, $formats, $lang) = split '-', $hold->holdable_formats; - } - # find all the potential copies if ($hold->hold_type eq 'M') { - my $records = [ - map { - isTrue($_->deleted) ? () : ($_->id) - } metabib::metarecord->retrieve($hold->target)->source_records - ]; - if(@$records > 0) { - for my $r ( map - {$_->record} - metabib::record_descriptor - ->search( - record => $records, - ( $types ? (item_type => [split '', $types]) : () ), - ( $formats ? (item_form => [split '', $formats]) : () ), - ( $lang ? (item_lang => $lang) : () ), - ) - ) { - my ($rtree) = $self - ->method_lookup( 'open-ils.storage.biblio.record_entry.ranged_tree') - ->run( $r->id, $hold->selection_ou, $hold->selection_depth ); - - for my $cn ( @{ $rtree->call_numbers } ) { - push @$all_copies, - asset::copy->search_where( - { id => [map {$_->id} @{ $cn->copies }], - deleted => 'f' } - ) if ($cn && @{ $cn->copies }); - } + for my $r_id ( + $self->method_lookup( + 'open-ils.storage.metarecord.filtered_records' + )->run( $hold->target, $hold->holdable_formats ) + ) { + my ($rtree) = $self + ->method_lookup( 'open-ils.storage.biblio.record_entry.ranged_tree') + ->run( $r_id, $hold->selection_ou, $hold->selection_depth ); + + for my $cn ( @{ $rtree->call_numbers } ) { + push @$all_copies, + asset::copy->search_where( + { id => [map {$_->id} @{ $cn->copies }], + deleted => 'f' } + ) if ($cn && @{ $cn->copies }); } } } elsif ($hold->hold_type eq 'T') { @@ -2323,32 +2346,5 @@ sub title_hold_capture { $self->volume_hold_capture($hold,$cn_list) if (ref $cn_list and @$cn_list); } -sub metarecord_hold_capture { - my $self = shift; - my $hold = shift; - - my $titles; - try { - $titles = [ metabib::metarecord_source_map->search( metarecord => $hold->target) ]; - - } catch Error with { - my $e = shift; - die "Could not retrieve initial title list:\n\n$e\n"; - }; - - try { - my @recs = map {$_->record} metabib::record_descriptor->search( record => $titles, item_type => [split '', $hold->holdable_formats] ); - - $titles = [ biblio::record_entry->search( id => \@recs ) ]; - - } catch Error with { - my $e = shift; - die "Could not retrieve format-pruned title list:\n\n$e\n"; - }; - - - $cache{titles}{$_->id} = $_ for (@$titles); - $self->title_hold_capture($hold,$titles) if (ref $titles and @$titles); -} 1; diff --git a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Account.pm b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Account.pm index ac11d61fae..3961984cac 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Account.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Account.pm @@ -571,9 +571,35 @@ sub fetch_user_holds { if(@collected) { while(my $blob = pop(@collected)) { - my (undef, @data) = $self->get_records_and_facets( - [$blob->{hold}->{bre_id}], undef, {flesh => '{mra}'} - ); + my @data; + + # in the holds edit UI, we need to know what formats and + # languages the user selected for this hold, plus what + # formats/langs are available on the MR as a whole. + if ($blob->{hold}{hold}->hold_type eq 'M') { + my $hold = $blob->{hold}->{hold}; + + # for MR, fetch the combined MR unapi blob + (undef, @data) = $self->get_records_and_facets( + [$hold->target], undef, {flesh => '{mra}', metarecord => 1}); + + my $filter_data = $U->simplereq( + 'open-ils.circ', + 'open-ils.circ.mmr.holds.filters.authoritative.atomic', + $hold->target, [$hold->id] + ); + + $blob->{metarecord_filters} = + $filter_data->[0]->{metarecord}; + $blob->{metarecord_selected_filters} = + $filter_data->[1]->{hold}; + } else { + + (undef, @data) = $self->get_records_and_facets( + [$blob->{hold}->{bre_id}], undef, {flesh => '{mra}'} + ); + } + $blob->{marc_xml} = $data[0]->{marc_xml}; push(@holds, $blob); } @@ -652,6 +678,10 @@ sub handle_hold_update { m:^(\d{2})/(\d{2})/(\d{4})$:; $val->{$field} = "$3-$1-$2"; } + + $val->{holdable_formats} = # no-op for non-MR holds + $self->compile_holdable_formats(undef, $_); + $val; } @hold_ids; @@ -777,8 +807,37 @@ sub load_place_hold { }; my $type_dispatch = { + M => sub { + # target metarecords + my $mrecs = $e->batch_retrieve_metabib_metarecord([ + \@targets, + {flesh => 1, flesh_fields => {mmr => ['master_record']}}], + {substream => 1} + ); + + for my $id (@targets) { + my ($mr) = grep {$_->id eq $id} @$mrecs; + + my $filter_data = $U->simplereq( + 'open-ils.circ', + 'open-ils.circ.mmr.holds.filters.authoritative', $mr->id); + + my $holdable_formats = + $self->compile_holdable_formats($mr->id); + + push(@hold_data, $data_filler->({ + target => $mr, + record => $mr->master_record, + holdable_formats => $holdable_formats, + metarecord_filters => $filter_data->{metarecord} + })); + } + }, T => sub { - my $recs = $e->batch_retrieve_biblio_record_entry(\@targets, {substream => 1}); + my $recs = $e->batch_retrieve_biblio_record_entry( + [\@targets, {flesh => 1, flesh_fields => {bre => ['metarecord']}}], + {substream => 1} + ); for my $id (@targets) { # force back into the correct order my ($rec) = grep {$_->id eq $id} @$recs; @@ -984,13 +1043,23 @@ sub attempt_hold_placement { if(@create_targets) { + # holdable formats may be different for each MR hold. + # map each set to the ID of the target. + my $holdable_formats = {}; + if ($hold_type eq 'M') { + $holdable_formats->{$_->{target_id}} = + $_->{holdable_formats} for @hold_data; + } + my $bses = OpenSRF::AppSession->create('open-ils.circ'); my $breq = $bses->request( $method, $e->authtoken, - $data_filler->({ patronid => $usr, + $data_filler->({ + patronid => $usr, pickup_lib => $pickup_lib, - hold_type => $hold_type + hold_type => $hold_type, + holdable_formats_map => $holdable_formats }), \@create_targets ); @@ -1053,6 +1122,63 @@ sub attempt_hold_placement { } } +# pull the selected formats and languages for metarecord holds +# from the CGI params and map them into the JSON holdable +# formats...er, format. +# if no metarecord is provided, we'll pull it from the target +# of the provided hold. +sub compile_holdable_formats { + my ($self, $mr_id, $hold_id) = @_; + my $e = $self->editor; + my $cgi = $self->cgi; + + # exit early if not needed + return "" unless + grep /metarecord_formats_|metarecord_langs_/, + $cgi->param; + + # CGI params are based on the MR id, since during hold placement + # we have no old ID. During hold edit, map the hold ID back to + # the metarecod target. + $mr_id = + $e->retrieve_action_hold_request($hold_id)->target + unless $mr_id; + + my $format_attr = $self->ctx->{get_cgf}->( + 'opac.metarecord.holds.format_attr'); + + if (!$format_attr) { + $logger->error("Missing config.global_flag: ". + "opac.metarecord.holds.format_attr!"); + return ""; + } + + $format_attr = $format_attr->value; + + # during hold placement or edit submission, the user selects + # which of the available formats/langs are acceptable. + # Capture those here as the holdable_formats for the MR hold. + my @selected_formats = $cgi->param("metarecord_formats_$mr_id"); + my @selected_langs = $cgi->param("metarecord_langs_$mr_id"); + + # map the selected attrs into the JSON holdable_formats structure + my $blob = {}; + if (@selected_formats) { + $blob->{0} = [ + map { {_attr => $format_attr, _val => $_} } + @selected_formats + ]; + } + if (@selected_langs) { + $blob->{1} = [ + map { {_attr => 'item_lang', _val => $_} } + @selected_langs + ]; + } + + return OpenSRF::Utils::JSON->perl2JSON($blob); +} + sub fetch_user_circs { my $self = shift; my $flesh = shift; # flesh bib data, etc. diff --git a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Search.pm b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Search.pm index 00c4c49c83..26df556347 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Search.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Search.pm @@ -67,7 +67,8 @@ sub _prepare_biblio_search { foreach ($cgi->param('modifier')) { # The unless bit is to avoid stacking modifiers. - $query = ('#' . $_ . ' ' . $query) unless $query =~ qr/\#\Q$_/; + $query = ('#' . $_ . ' ' . $query) unless + $query =~ qr/\#\Q$_/ or $_ eq 'metabib'; } # filters @@ -307,6 +308,13 @@ sub load_rresults { my $ctx = $self->ctx; my $e = $self->editor; + # 1. param->metarecord : view constituent bib records for a metarecord + # 2. param->modifier=metabib : perform a metarecord search + my $metarecord = $ctx->{metarecord} = $cgi->param('metarecord'); + my @mods = $cgi->param('modifier'); + my $is_meta = (@mods and grep {$_ eq 'metabib'} @mods and !$metarecord); + my $id_key = $is_meta ? 'mmr_id' : 'bre_id'; + # find the last record in the set, then redirect my $find_last = $cgi->param('find_last'); @@ -343,7 +351,6 @@ sub load_rresults { $ctx->{search_ou} = $self->_get_search_lib(); $ctx->{pref_ou} = $self->_get_pref_lib() || $ctx->{search_ou}; my $offset = $page * $limit; - my $metarecord = $cgi->param('metarecord'); my $results; my $tag_circs = $self->tag_circed_items; $self->timelog("Got search parameters"); @@ -408,12 +415,14 @@ sub load_rresults { $query = "$_ $query" for @facets; - $logger->activity("EGWeb: [search] $query"); + my $ltag = $is_meta ? '[mmr search]' : '[bre search]'; + $logger->activity("EGWeb: $ltag $query"); try { my $method = 'open-ils.search.biblio.multiclass.query'; $method .= '.staff' if $ctx->{is_staff}; + $method =~ s/biblio/metabib/ if $is_meta; my $ses = OpenSRF::AppSession->create('open-ils.search'); @@ -440,6 +449,7 @@ sub load_rresults { my $rec_id = pop @$rec_ids; $cgi->delete('find_last'); my $url = $cgi->url(-full => 1, -path => 1, -query => 1); + # TODO: metarecord => /rresults?metarecord=$mmr_id $url =~ s|/results|/record/$rec_id|; return $self->generic_redirect($url); } @@ -448,12 +458,27 @@ sub load_rresults { $self->load_rresults_bookbag_item_notes($rec_ids) if $ctx->{bookbag}; + my $fetch_recs = $rec_ids; + + my $metarecord_master; + if ($metarecord) { + # when listing the contents of a metarecord, be sure to fetch + # the lead record for summary display. Adding the ID to + # $fetch_recs lets us grab the record (if necessary) w/o it + # unintentially becoming a member of the result set. + my $mr = $e->retrieve_metabib_metarecord($metarecord); + push(@$fetch_recs, $mr->master_record) + unless grep {$_ eq $mr->master_record} @$fetch_recs; + $metarecord_master = $mr->master_record; + } + $self->timelog("Calling get_records_and_facets()"); my ($facets, @data) = $self->get_records_and_facets( - $rec_ids, $results->{facet_key}, + $fetch_recs, $results->{facet_key}, { flesh => '{holdings_xml,mra,acp,acnp,acns,bmp}', site => $site, + metarecord => $is_meta, depth => $depth, pref_lib => $ctx->{pref_ou}, } @@ -461,6 +486,7 @@ sub load_rresults { $self->timelog("Returned from get_records_and_facets()"); if ($page == 0) { + # TODO: handle metarecords my $stat = $self->check_1hit_redirect($rec_ids); return $stat if $stat; } @@ -470,19 +496,26 @@ sub load_rresults { # shove recs into context in search results order for my $rec_id (@$rec_ids) { - push( - @{$ctx->{records}}, - grep { $_->{id} == $rec_id } @data - ); + my ($rec) = grep { $_->{$id_key} == $rec_id } @data; + push(@{$ctx->{records}}, $rec); + + $ctx->{metarecord_master} = $rec + if $metarecord_master and $metarecord_master eq $rec_id; + + # MR's with multiple constituent records will have a + # null value in position 2 of the result set. + my ($res_rec) = grep { $_->[0] == $rec_id} @{$results->{ids}}; + $rec->{mr_has_multi} = !$res_rec->[2]; } if ($tag_circs) { for my $rec (@{$ctx->{records}}) { - my ($res_rec) = grep { $_->[0] == $rec->{id} } @{$results->{ids}}; + my ($res_rec) = grep { $_->[0] == $rec->{$id_key} } @{$results->{ids}}; # index 1 in the per-record result array is a boolean which # indicates whether the record in question is in the users # accessible circ history list - $rec->{user_circulated} = 1 if $res_rec->[1]; + my $index = $is_meta ? 3 : 1; + $rec->{user_circulated} = 1 if $res_rec->[$index]; } } diff --git a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Util.pm b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Util.pm index ce009dd4ca..2bc2c16b4b 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Util.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Util.pm @@ -277,9 +277,13 @@ sub get_records_and_facets { $unapi_args->{depth} ||= $self->ctx->{aou_tree}->()->ou_type->depth; $unapi_args->{flesh_depth} ||= 5; + my $is_meta = delete $unapi_args->{metarecord}; + my $unapi_type = $is_meta ? 'unapi.mmr' : 'unapi.bre'; + $unapi_cache ||= OpenSRF::Utils::Cache->new('global'); my $unapi_cache_key_suffix = join( '_', + $is_meta || 0, $unapi_args->{site}, $unapi_args->{depth}, $unapi_args->{flesh_depth}, @@ -299,36 +303,59 @@ sub get_records_and_facets { $outer_self->timelog("get_records_and_facets(): got response content"); # Protect against requests for non-existent records - return unless $data->{'unapi.bre'}; + return unless $data->{$unapi_type}; - my $xml = XML::LibXML->new->parse_string($data->{'unapi.bre'})->documentElement; + my $xml = XML::LibXML->new->parse_string($data->{$unapi_type})->documentElement; $outer_self->timelog("get_records_and_facets(): parsed xml"); # Protect against legacy invalid MARCXML that might not have a 901c my $bre_id; + my $mmr_id; my $bre_id_nodes = $xml->find('*[@tag="901"]/*[@code="c"]'); if ($bre_id_nodes) { $bre_id = $bre_id_nodes->[0]->textContent; } else { $logger->warn("Missing 901 subfield 'c' in " . $xml->toString()); } - $tmp_data{$bre_id} = {id => $bre_id, marc_xml => $xml}; - if ($bre_id) { + if ($is_meta) { + # extract metarecord ID from mmr.unapi tag + for my $node ($xml->getElementsByTagName('abbr')) { + my $title = $node->getAttribute('title'); + ($mmr_id = $title) =~ + s/tag:open-ils.org:U2\@mmr\/(\d+)\/.*/$1/g; + last if $mmr_id; + } + } + + my $rec_id = $mmr_id ? $mmr_id : $bre_id; + $tmp_data{$rec_id} = { + id => $rec_id, + bre_id => $bre_id, + mmr_id => $mmr_id, + marc_xml => $xml + }; + + if ($rec_id) { # Let other backends grab our data now that we're done. - my $key = 'TPAC_unapi_cache_'.$bre_id.'_'.$unapi_cache_key_suffix; + my $key = 'TPAC_unapi_cache_'.$rec_id.'_'.$unapi_cache_key_suffix; my $cache_data = $unapi_cache->get_cache($key); if ($$cache_data{running}) { - $unapi_cache->put_cache($key, { id => $bre_id, marc_xml => $data->{'unapi.bre'} }, 10); + $unapi_cache->put_cache($key, { + bre_id => $bre_id, + mmr_id => $mmr_id, + id => $rec_id, + marc_xml => $data->{$unapi_type} + }, 10); } } - $outer_self->timelog("get_records_and_facets(): end of success handler"); } ); - $self->timelog("get_records_and_facets(): about to call unapi.bre via json_query (rec_ids has " . scalar(@$rec_ids)); + $self->timelog("get_records_and_facets(): about to call ". + "$unapi_type via json_query (rec_ids has " . scalar(@$rec_ids)); my @loop_recs = @$rec_ids; my %rec_timeout; @@ -359,19 +386,26 @@ sub get_records_and_facets { $tmp_data{$unapi_data->{id}} = $unapi_data; } else { # we're the first or we timed out. success_handler will populate the real value $unapi_cache->put_cache($unapi_cache_key, { running => $$ }, 10); + + my $sdepth = $unapi_args->{flesh_depth}; + my $slimit = "acn=>$sdepth,acp=>$sdepth"; + $slimit .= ",bre=>$sdepth" if $is_meta; + my $flesh = $unapi_args->{flesh} || ''; + + # tag the record with the MR id + $flesh =~ s/}$/,mmr.unapi}/g if $is_meta; + $ses->request( 'open-ils.cstore.json_query', {from => [ - 'unapi.bre', $bid, 'marcxml','record', - $unapi_args->{flesh}, + $unapi_type, $bid, 'marcxml','record', $flesh, $unapi_args->{site}, $unapi_args->{depth}, - 'acn=>' . $unapi_args->{flesh_depth} . ',acp=>' . $unapi_args->{flesh_depth}, + $slimit, undef, undef, $unapi_args->{pref_lib} ]} ); } - } diff --git a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGWeb.pm b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGWeb.pm index 57cb02b4dd..8b72e537eb 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGWeb.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGWeb.pm @@ -186,7 +186,10 @@ sub load_context { parse_accept_lang($r->headers_in->get('Accept-Language')); # set the editor default locale for each page load - OpenSRF::AppSession->default_locale(parse_eg_locale($ctx->{locale})); + my $ses_locale = parse_eg_locale($ctx->{locale}); + OpenSRF::AppSession->default_locale($ses_locale); + # give templates access to the en-US style locale + $ctx->{eg_locale} = $ses_locale; my $mprefix = $ctx->{media_prefix}; if($mprefix and $mprefix !~ /^http/ and $mprefix !~ /^\//) { diff --git a/Open-ILS/src/templates/opac/css/style.css.tt2 b/Open-ILS/src/templates/opac/css/style.css.tt2 index e57741fdb0..0c5e71b992 100644 --- a/Open-ILS/src/templates/opac/css/style.css.tt2 +++ b/Open-ILS/src/templates/opac/css/style.css.tt2 @@ -982,6 +982,7 @@ table.acct_notes th { div.adv_search_available { margin-top: 1em; } + #myopac_loading { width:100%; text-align:center; diff --git a/Open-ILS/src/templates/opac/myopac/holds.tt2 b/Open-ILS/src/templates/opac/myopac/holds.tt2 index 81ebc34a54..0a7b04a2c8 100644 --- a/Open-ILS/src/templates/opac/myopac/holds.tt2 +++ b/Open-ILS/src/templates/opac/myopac/holds.tt2 @@ -150,8 +150,10 @@
- [% IF attrs.format_icon %] - [% attrs.format_label | html %] + [% IF attrs.all_formats.size %] + [% FOR format IN attrs.all_formats %] + [% format.label | html %] + [% END %] [% END %]
diff --git a/Open-ILS/src/templates/opac/myopac/holds/edit.tt2 b/Open-ILS/src/templates/opac/myopac/holds/edit.tt2 index 2fd2e92ebb..e0981bc308 100644 --- a/Open-ILS/src/templates/opac/myopac/holds/edit.tt2 +++ b/Open-ILS/src/templates/opac/myopac/holds/edit.tt2 @@ -2,6 +2,7 @@ PROCESS "opac/parts/misc_util.tt2"; PROCESS "opac/parts/hold_status.tt2"; PROCESS "opac/parts/org_selector.tt2"; + PROCESS "opac/parts/metarecord_hold_filters.tt2"; WRAPPER "opac/parts/myopac/base.tt2"; myopac_page = "holds"; # in this case, just for tab coloring. @@ -100,6 +101,14 @@ [% END %] + + + [% IF hold.metarecord_filters.formats.size OR + hold.metarecord_filters.langs.size > 1; + PROCESS metarecord_hold_filters_selector + hold_data=hold; END %] + +