From 9e5ef5e0814a78fe8d8ce914dcb563a7a5e27a79 Mon Sep 17 00:00:00 2001 From: Chris Cormack Date: Thu, 6 Nov 2014 10:34:18 +1300 Subject: [PATCH] Bug 14385: Extend OpacHiddenItems to allow specifying exempt borrower categories Edit: Fixing merge conflicts in - t/db_dependent/Items.t - t/db_dependent/Search.t - C4/Search.pm Changes the API for calling GetHiddenItems and all the places in the code that call it. This is to allow borrower categories to be passed in. Adds an OpacHiddenItemsExceptions syspref to allow certain borrower categories to be able to see items, even if they are marked hidden by OpacHiddenItems To test: 1) Make two borrowers, one in a category that should see everything (ie Adult), and another in a category that should only see certain things (ie Adult - exceptions) 2) Add the borrower that can see everything (the Adult) to OpacHiddenItemsExceptions 3) To the OpacHiddenItems syspref, add an item type (ensure that you have some records that fall under this type in your library). 4) Log in as the borrower that should only see certain things (Adult - exception) 5) Do a search, filtered to show records which are the item type that you specified in the OpacHiddenItems syspref. No records should show for this borrower as this item type is hidden to them. 6) Log in as the borrower that should see everything (Adult) 7) Do the same search. There should be results from this search, as this borrower category has been specified as an exception to the hidden items Signed-off-by: Claire Gravely Signed-off-by: Mark Tompsett Signed-off-by: Aleisha Amohia --- C4/Items.pm | 17 +++++++++++++---- C4/Search.pm | 13 +++++++------ catalogue/search.pl | 2 +- cataloguing/addbooks.pl | 5 ++++- .../prog/en/modules/admin/preferences/opac.pref | 3 +++ opac/opac-ISBDdetail.pl | 10 +++++++++- opac/opac-MARCdetail.pl | 9 ++++++++- opac/opac-detail.pl | 15 ++++++++++++--- opac/opac-search.pl | 13 +++++++++++-- t/db_dependent/Items.t | 17 +++++++++-------- t/db_dependent/Search.t | 12 ++++++------ 11 files changed, 83 insertions(+), 33 deletions(-) diff --git a/C4/Items.pm b/C4/Items.pm index 3f68375..e46737d 100644 --- a/C4/Items.pm +++ b/C4/Items.pm @@ -1287,16 +1287,25 @@ sub get_hostitemnumbers_of { =head2 GetHiddenItemnumbers - my @itemnumbers_to_hide = GetHiddenItemnumbers(@items); + my @itemnumbers_to_hide = GetHiddenItemnumbers({ items => \@items, borcat => $category }); Given a list of items it checks which should be hidden from the OPAC given the current configuration. Returns a list of itemnumbers corresponding to -those that should be hidden. +those that should be hidden. Optionally takes a borcat parameter for certain borrower types +to be excluded =cut sub GetHiddenItemnumbers { - my (@items) = @_; + my $params = shift; + my $items = $params->{items}; + if (my $exceptions = C4::Context->preference('OpacHiddenItemsExceptions') and $params->{'borcat'}){ + foreach my $except (split(/\|/, $exceptions)){ + if ($params->{'borcat'} eq $except){ + return; # we dont hide anything for this borrower category + } + } + } my @resultitems; my $yaml = C4::Context->preference('OpacHiddenItems'); @@ -1313,7 +1322,7 @@ sub GetHiddenItemnumbers { my $dbh = C4::Context->dbh; # For each item - foreach my $item (@items) { + foreach my $item (@$items) { # We check each rule foreach my $field (keys %$hidingrules) { diff --git a/C4/Search.pm b/C4/Search.pm index 06e837d..c01eb6a 100644 --- a/C4/Search.pm +++ b/C4/Search.pm @@ -1853,9 +1853,9 @@ sub searchResults { require C4::Items; - $search_context = 'opac' if !$search_context || $search_context ne 'intranet'; + $search_context->{'interface'} = 'opac' if !$search_context->{'interface'} || $search_context->{'interface'} ne 'intranet'; my ($is_opac, $hidelostitems); - if ($search_context eq 'opac') { + if ($search_context->{'interface'} eq 'opac') { $hidelostitems = C4::Context->preference('hidelostitems'); $is_opac = 1; } @@ -1945,6 +1945,7 @@ sub searchResults { # add imageurl to itemtype if there is one $oldbiblio->{imageurl} = getitemtypeimagelocation( $search_context, $itemtypes{ $oldbiblio->{itemtype} }->{imageurl} ); + $oldbiblio->{'authorised_value_images'} = ($search_context->{'interface'} eq 'opac' && C4::Context->preference('AuthorisedValueImages')) || ($search_context->{'interface'} eq 'intranet' && C4::Context->preference('StaffAuthorisedValueImages')) ? C4::Items::get_authorised_value_images( C4::Biblio::get_biblio_authorised_values( $oldbiblio->{'biblionumber'}, $marcrecord ) ) : []; $oldbiblio->{normalized_upc} = GetNormalizedUPC( $marcrecord,$marcflavour); $oldbiblio->{normalized_ean} = GetNormalizedEAN( $marcrecord,$marcflavour); $oldbiblio->{normalized_oclc} = GetNormalizedOCLCNumber($marcrecord,$marcflavour); @@ -2076,7 +2077,7 @@ sub searchResults { next; } # hidden based on OpacHiddenItems syspref - my @hi = C4::Items::GetHiddenItemnumbers($item); + my @hi = C4::Items::GetHiddenItemnumbers({ items=> [ $item ], borcat => $search_context->{category} }); if (scalar @hi) { push @hiddenitems, @hi; $hideatopac_count++; @@ -2205,7 +2206,7 @@ sub searchResults { $other_items->{$key}->{count}++ if $item->{$hbranch}; $other_items->{$key}->{location} = $shelflocations->{ $item->{location} }; $other_items->{$key}->{description} = $item->{description}; - $other_items->{$key}->{imageurl} = getitemtypeimagelocation( $search_context, $itemtypes{ $item->{itype} }->{imageurl} ); + $other_items->{$key}->{imageurl} = getitemtypeimagelocation( $search_context->{'interface'}, $itemtypes{ $item->{itype} }->{imageurl} ); } # item is available else { @@ -2216,7 +2217,7 @@ sub searchResults { $available_items->{$prefix}->{$_} = $item->{$_}; } $available_items->{$prefix}->{location} = $shelflocations->{ $item->{location} }; - $available_items->{$prefix}->{imageurl} = getitemtypeimagelocation( $search_context, $itemtypes{ $item->{itype} }->{imageurl} ); + $available_items->{$prefix}->{imageurl} = getitemtypeimagelocation( $search_context->{'interface'}, $itemtypes{ $item->{itype} }->{imageurl} ); } } } # notforloan, item level and biblioitem level @@ -2242,9 +2243,9 @@ sub searchResults { # XSLT processing of some stuff # we fetched the sysprefs already before the loop through all retrieved record! + my $interface = $search_context->{'interface'} eq 'opac' ? 'OPAC' : ''; if (!$scan && $xslfile) { $oldbiblio->{XSLTResultsRecord} = XSLTParse4Display($oldbiblio->{biblionumber}, $marcrecord, $xslsyspref, 1, \@hiddenitems, $sysxml, $xslfile, $lang); - # the last parameter tells Koha to clean up the problematic ampersand entities that Zebra outputs } # if biblio level itypes are used and itemtype is notforloan, it can't be reserved either diff --git a/catalogue/search.pl b/catalogue/search.pl index 842ada5..d9961a0 100755 --- a/catalogue/search.pl +++ b/catalogue/search.pl @@ -541,7 +541,7 @@ for (my $i=0;$i<@servers;$i++) { if ($server =~/biblioserver/) { # this is the local bibliographic server my $hits = $results_hashref->{$server}->{"hits"} // 0; my $page = $cgi->param('page') || 0; - my @newresults = searchResults('intranet', $query_desc, $hits, $results_per_page, $offset, $scan, + my @newresults = searchResults({ 'interface' => 'intranet' }, $query_desc, $hits, $results_per_page, $offset, $scan, $results_hashref->{$server}->{"RECORDS"}); $total = $total + $hits; diff --git a/cataloguing/addbooks.pl b/cataloguing/addbooks.pl index 253683d..9db7c89 100755 --- a/cataloguing/addbooks.pl +++ b/cataloguing/addbooks.pl @@ -90,7 +90,10 @@ if ($query) { # format output # SimpleSearch() give the results per page we want, so 0 offet here my $total = @{$marcresults}; - my @newresults = searchResults( 'intranet', $query, $total, $results_per_page, 0, 0, $marcresults ); + my @newresults = searchResults( {'interface' => 'intranet'}, $query, $total, $results_per_page, 0, 0, $marcresults ); + foreach my $line (@newresults) { + if ( not exists $line->{'size'} ) { $line->{'size'} = "" } + } $template->param( total => $total_hits, query => $query, diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/opac.pref b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/opac.pref index f46dda3..bfb8ef7 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/opac.pref +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/opac.pref @@ -590,6 +590,9 @@ OPAC: class: code - Define custom rules to hide specific items from search and view on the OPAC. How to write these rules is documented on the Koha wiki. - + - pref: OpacHiddenItemsExceptions + - List of borrower categories, separated by |, that can see items otherwise hidden by OpacHiddenItems + - - pref: OpacAllowPublicListCreation default: 1 choices: diff --git a/opac/opac-ISBDdetail.pl b/opac/opac-ISBDdetail.pl index fe69564..d6aba31 100755 --- a/opac/opac-ISBDdetail.pl +++ b/opac/opac-ISBDdetail.pl @@ -85,7 +85,15 @@ my $marcflavour = C4::Context->preference("marcflavour"); my @items = GetItemsInfo($biblionumber); if (scalar @items >= 1) { - my @hiddenitems = GetHiddenItemnumbers(@items); + my $borcat; + if ( C4::Context->preference('OpacHiddenItemsExceptions') ) { + + # we need to fetch the borrower info here, so we can pass the category + my $borrower = GetMember( borrowernumber => $borrowernumber ); + $borcat = $borrower->{categorycode}; + } + + my @hiddenitems = GetHiddenItemnumbers( { items => \@items, borcat => $borcat }); if (scalar @hiddenitems == scalar @items ) { print $query->redirect("/cgi-bin/koha/errors/404.pl"); # escape early diff --git a/opac/opac-MARCdetail.pl b/opac/opac-MARCdetail.pl index d438036..533c9a5 100755 --- a/opac/opac-MARCdetail.pl +++ b/opac/opac-MARCdetail.pl @@ -85,7 +85,14 @@ if ( ! $record ) { my @all_items = GetItemsInfo($biblionumber); my @items2hide; if (scalar @all_items >= 1) { - push @items2hide, GetHiddenItemnumbers(@all_items); + my $borcat; + if ( C4::Context->preference('OpacHiddenItemsExceptions') ) { + + # we need to fetch the borrower info here, so we can pass the category + my $borrower = GetMember( borrowernumber => $borrowernumber ); + $borcat = $borrower->{categorycode}; + } + push @items2hide, GetHiddenItemnumbers({ items => \@all_items, borcat => $botcat }); if (scalar @items2hide == scalar @all_items ) { print $query->redirect("/cgi-bin/koha/errors/404.pl"); diff --git a/opac/opac-detail.pl b/opac/opac-detail.pl index 6f10c6b..460cc39 100755 --- a/opac/opac-detail.pl +++ b/opac/opac-detail.pl @@ -83,8 +83,17 @@ $biblionumber = int($biblionumber); my @all_items = GetItemsInfo($biblionumber); my @hiddenitems; -if (scalar @all_items >= 1) { - push @hiddenitems, GetHiddenItemnumbers(@all_items); +if ( scalar @all_items >= 1 ) { + my $borcat; + if ( C4::Context->preference('OpacHiddenItemsExceptions') ) { + + # we need to fetch the borrower info here, so we can pass the category + my $borrower = GetMember( borrowernumber => $borrowernumber ); + $borcat = $borrower->{categorycode}; + } + + push @hiddenitems, + GetHiddenItemnumbers( { items => \@all_items, borcat => $borcat } ); if (scalar @hiddenitems == scalar @all_items ) { print $query->redirect("/cgi-bin/koha/errors/404.pl"); # escape early @@ -233,7 +242,7 @@ if ($session->param('busc')) { for (my $i=0;$i<@servers;$i++) { my $server = $servers[$i]; $hits = $results_hashref->{$server}->{"hits"}; - @newresults = searchResults('opac', '', $hits, $results_per_page, $offset, $arrParamsBusc->{'scan'}, $results_hashref->{$server}->{"RECORDS"}); + @newresults = searchResults({ 'interface' => 'opac' }, '', $hits, $results_per_page, $offset, $arrParamsBusc->{'scan'}, $results_hashref->{$server}->{"RECORDS"}); } return \@newresults; }#searchAgain diff --git a/opac/opac-search.pl b/opac/opac-search.pl index ce2cefc..6033fbd 100755 --- a/opac/opac-search.pl +++ b/opac/opac-search.pl @@ -51,6 +51,7 @@ use C4::Koha; use C4::Tags qw(get_tags); use C4::SocialData; use C4::External::OverDrive; +use C4::Borrowers qw(GetMember); use Koha::ItemTypes; use Koha::Ratings; @@ -621,6 +622,14 @@ if (not $tag and ( $@ || $error)) { # At this point, each server has given us a result set # now we build that set for template display my @sup_results_array; +my $search_context = {}; +$search_context->{'interface'} = 'opac'; +if (C4::Context->preference('OpacHiddenItemsExceptions')){ + # we need to fetch the borrower info here, so we can pass the category + my $borrower = GetMember( borrowernumber => $borrowernumber ); + $search_context->{'category'} = $borrower->{'categorycode'}; +} + for (my $i=0;$i<@servers;$i++) { my $server = $servers[$i]; if ($server && $server =~/biblioserver/) { # this is the local bibliographic server @@ -632,12 +641,12 @@ for (my $i=0;$i<@servers;$i++) { # because pazGetRecords handles retieving only the records # we want as specified by $offset and $results_per_page, # we need to set the offset parameter of searchResults to 0 - my @group_results = searchResults( 'opac', $query_desc, $group->{'group_count'},$results_per_page, 0, $scan, + my @group_results = searchResults( $search_context, $query_desc, $group->{'group_count'},$results_per_page, 0, $scan, $group->{"RECORDS"}); push @newresults, { group_label => $group->{'group_label'}, GROUP_RESULTS => \@group_results }; } } else { - @newresults = searchResults('opac', $query_desc, $hits, $results_per_page, $offset, $scan, + @newresults = searchResults( $search_context, $query_desc, $hits, $results_per_page, $offset, $scan, $results_hashref->{$server}->{"RECORDS"}); } $hits = 0 unless @newresults; diff --git a/t/db_dependent/Items.t b/t/db_dependent/Items.t index 92151d6..b5d69ae 100755 --- a/t/db_dependent/Items.t +++ b/t/db_dependent/Items.t @@ -192,20 +192,20 @@ subtest 'GetHiddenItemnumbers tests' => sub { push @items, GetItem( $item2_itemnumber ); # Empty OpacHiddenItems - t::lib::Mocks::mock_preference('OpacHiddenItems',''); - ok( !defined( GetHiddenItemnumbers( @items ) ), + C4::Context->set_preference('OpacHiddenItems',''); + ok( !defined( GetHiddenItemnumbers( { items => \@items } ) ), "Hidden items list undef if OpacHiddenItems empty"); # Blank spaces - t::lib::Mocks::mock_preference('OpacHiddenItems',' '); - ok( scalar GetHiddenItemnumbers( @items ) == 0, + C4::Context->set_preference('OpacHiddenItems',' '); + ok( scalar GetHiddenItemnumbers( { items => \@items } ) == 0, "Hidden items list empty if OpacHiddenItems only contains blanks"); # One variable / value $opachiddenitems = " withdrawn: [1]"; t::lib::Mocks::mock_preference( 'OpacHiddenItems', $opachiddenitems ); - @hidden = GetHiddenItemnumbers( @items ); + @hidden = GetHiddenItemnumbers( { items => \@items } ); ok( scalar @hidden == 1, "Only one hidden item"); is( $hidden[0], $item1_itemnumber, "withdrawn=1 is hidden"); @@ -213,7 +213,8 @@ subtest 'GetHiddenItemnumbers tests' => sub { $opachiddenitems = " withdrawn: [1,0]"; t::lib::Mocks::mock_preference( 'OpacHiddenItems', $opachiddenitems ); - @hidden = GetHiddenItemnumbers( @items ); + C4::Context->set_preference( 'OpacHiddenItems', $opachiddenitems ); + @hidden = GetHiddenItemnumbers( { items => \@items } ); ok( scalar @hidden == 2, "Two items hidden"); is_deeply( \@hidden, \@itemnumbers, "withdrawn=1 and withdrawn=0 hidden"); @@ -223,13 +224,13 @@ subtest 'GetHiddenItemnumbers tests' => sub { homebranch: [$library2->{branchcode}] "; t::lib::Mocks::mock_preference( 'OpacHiddenItems', $opachiddenitems ); - @hidden = GetHiddenItemnumbers( @items ); + @hidden = GetHiddenItemnumbers( { items => \@items } ); ok( scalar @hidden == 2, "Two items hidden"); is_deeply( \@hidden, \@itemnumbers, "withdrawn=1 and homebranch library2 hidden"); # Valid OpacHiddenItems, empty list @items = (); - @hidden = GetHiddenItemnumbers( @items ); + @hidden = GetHiddenItemnumbers( { items => \@items } ); ok( scalar @hidden == 0, "Empty items list, no item hidden"); $schema->storage->txn_rollback; diff --git a/t/db_dependent/Search.t b/t/db_dependent/Search.t index 67a4348..93e4875 100644 --- a/t/db_dependent/Search.t +++ b/t/db_dependent/Search.t @@ -373,7 +373,7 @@ ok(MARC::Record::new_from_xml($results_hashref->{biblioserver}->{RECORDS}->[0],' ($error, $results_hashref, $facets_loop) = getRecords($query,$simple_query,[ ], [ 'biblioserver' ],20,0,undef,\%branches,\%itemtypes,$query_type,0); is($results_hashref->{biblioserver}->{hits}, 19, "getRecords generated keyword search for 'salud' matched right number of records"); - my @newresults = searchResults('opac', $query_desc, $results_hashref->{'biblioserver'}->{'hits'}, 18, 0, 0, + my @newresults = searchResults({'interface' => 'opac'}, $query_desc, $results_hashref->{'biblioserver'}->{'hits'}, 18, 0, 0, $results_hashref->{'biblioserver'}->{"RECORDS"}); is(scalar @newresults,18, "searchResults returns requested number of hits"); @@ -455,7 +455,7 @@ ok(MARC::Record::new_from_xml($results_hashref->{biblioserver}->{RECORDS}->[0],' ($error, $results_hashref, $facets_loop) = getRecords($query,$simple_query,[ ], [ 'biblioserver' ],20,0,undef,\%branches,\%itemtypes,$query_type,0); is($results_hashref->{biblioserver}->{hits}, 26, "getRecords generated availability-limited search matched right number of records"); - @newresults = searchResults('opac', $query_desc, $results_hashref->{'biblioserver'}->{'hits'}, 17, 0, 0, + @newresults = searchResults({'interface'=>'opac'}, $query_desc, $results_hashref->{'biblioserver'}->{'hits'}, 17, 0, 0, $results_hashref->{'biblioserver'}->{"RECORDS"}); my $allavailable = 'true'; foreach my $result (@newresults) { @@ -593,7 +593,7 @@ ok(MARC::Record::new_from_xml($results_hashref->{biblioserver}->{RECORDS}->[0],' # Let's just test a few other bits and bobs, just for fun ($error, $results_hashref, $facets_loop) = getRecords("Godzina pąsowej róży","Godzina pąsowej róży",[ ], [ 'biblioserver' ],20,0,undef,\%branches,\%itemtypes,$query_type,0); - @newresults = searchResults('intranet', $query_desc, $results_hashref->{'biblioserver'}->{'hits'}, 17, 0, 0, + @newresults = searchResults({'interface'=>'intranet'}, $query_desc, $results_hashref->{'biblioserver'}->{'hits'}, 17, 0, 0, $results_hashref->{'biblioserver'}->{"RECORDS"}); is($newresults[0]->{'alternateholdings_count'}, 1, 'Alternate holdings filled in correctly'); @@ -612,7 +612,7 @@ ok(MARC::Record::new_from_xml($results_hashref->{biblioserver}->{RECORDS}->[0],' }); ($error, $results_hashref, $facets_loop) = getRecords("TEST12121212","TEST12121212",[ ], [ 'biblioserver' ],20,0,undef,\%branches,\%itemtypes,$query_type,0); - @newresults = searchResults('intranet', $query_desc, $results_hashref->{'biblioserver'}->{'hits'}, 17, 0, 0, + @newresults = searchResults({'interface'=>'intranet'}, $query_desc, $results_hashref->{'biblioserver'}->{'hits'}, 17, 0, 0, $results_hashref->{'biblioserver'}->{"RECORDS"}); ok(!exists($newresults[0]->{norequests}), 'presence of a transit does not block hold request action (bug 10741)'); @@ -620,7 +620,7 @@ ok(MARC::Record::new_from_xml($results_hashref->{biblioserver}->{RECORDS}->[0],' ( undef, $results_hashref, $facets_loop ) = getRecords('ti:punctuation', 'punctuation', [], [ 'biblioserver' ], '19', 0, undef, \%branches, \%itemtypes, 'ccl', undef); is($results_hashref->{biblioserver}->{hits}, 1, "search for ti:punctuation returned expected number of records"); - warning_like { @newresults = searchResults('intranet', $query_desc, + warning_like { @newresults = searchResults({'intranet' => 'intranet'}, $query_desc, $results_hashref->{'biblioserver'}->{'hits'}, 20, 0, 0, $results_hashref->{'biblioserver'}->{"RECORDS"}) } qr/^ERROR DECODING RECORD - Tag "50%" is not a valid tag/, @@ -758,7 +758,7 @@ ok(MARC::Record::new_from_xml($results_hashref->{biblioserver}->{RECORDS}->[0],' ( undef, $results_hashref, $facets_loop ) = getRecords('ti:marc the large record', '', [], [ 'biblioserver' ], '20', 0, undef, \%branches, \%itemtypes, 'ccl', undef); is($results_hashref->{biblioserver}->{hits}, 1, "Can do a search that retrieves an over-large bib record (bug 11096)"); - @newresults = searchResults('opac', $query_desc, $results_hashref->{'biblioserver'}->{'hits'}, 10, 0, 0, + @newresults = searchResults({'interface' =>'opac'}, $query_desc, $results_hashref->{'biblioserver'}->{'hits'}, 10, 0, 0, $results_hashref->{'biblioserver'}->{"RECORDS"}); is($newresults[0]->{title}, 'Marc the Large Record', 'Able to render the title for over-large bib record (bug 11096)'); is($newresults[0]->{biblionumber}, '300', 'Over-large bib record has the correct biblionumber (bug 11096)'); -- 2.1.4