From 85f0b6635615034351859b751b2a37d0a8703494 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 a3ecd87..9d4c709 100644 --- a/C4/Items.pm +++ b/C4/Items.pm @@ -1393,16 +1393,25 @@ sub GetBarcodeFromItemnumber { =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'); @@ -1419,7 +1428,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 c11a61e..7cc32df 100644 --- a/C4/Search.pm +++ b/C4/Search.pm @@ -1848,9 +1848,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; } @@ -1940,6 +1940,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); @@ -2072,7 +2073,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++; @@ -2201,7 +2202,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 { @@ -2212,7 +2213,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 @@ -2238,9 +2239,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 79ba83f..48f8f89 100755 --- a/catalogue/search.pl +++ b/catalogue/search.pl @@ -536,7 +536,7 @@ for (my $i=0;$i<@servers;$i++) { if ($server =~/biblioserver/) { # this is the local bibliographic server $hits = $results_hashref->{$server}->{"hits"}; 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 + $results_hashref->{$server}->{"hits"}; diff --git a/cataloguing/addbooks.pl b/cataloguing/addbooks.pl index ae4d7ac..a4760cc 100755 --- a/cataloguing/addbooks.pl +++ b/cataloguing/addbooks.pl @@ -89,7 +89,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 c44b109..fa0269b 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 40e9b71..ecd9a63 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 ebb78a2..91e4d0e 100755 --- a/opac/opac-MARCdetail.pl +++ b/opac/opac-MARCdetail.pl @@ -84,7 +84,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 e7653d9..361806b 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 f06e40a..7894343 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; @@ -623,6 +624,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 @@ -634,12 +643,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 f6cc891..8a4b7e0 100755 --- a/t/db_dependent/Items.t +++ b/t/db_dependent/Items.t @@ -196,20 +196,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"); @@ -217,7 +217,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"); @@ -227,13 +228,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 a1ec226..af4d46e 100644 --- a/t/db_dependent/Search.t +++ b/t/db_dependent/Search.t @@ -417,7 +417,7 @@ if ( $indexing_mode eq 'dom' ) { ($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"); @@ -499,7 +499,7 @@ if ( $indexing_mode eq 'dom' ) { ($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) { @@ -637,7 +637,7 @@ if ( $indexing_mode eq 'dom' ) { # 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'); @@ -656,7 +656,7 @@ if ( $indexing_mode eq 'dom' ) { }); ($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)'); @@ -664,7 +664,7 @@ if ( $indexing_mode eq 'dom' ) { ( 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/, @@ -802,7 +802,7 @@ if ( $indexing_mode eq 'dom' ) { ( 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