From 6b5ef59a11cc73af72aa779951abfd5d2c5ab5cc Mon Sep 17 00:00:00 2001 From: Kyle M Hall Date: Mon, 6 May 2013 09:44:01 -0400 Subject: [PATCH] Bug 10278 - Add ability to hide items and records from search results for Independent Branches For the staff intranet, enabling IndependentBranchesRecordsAndItems will automatically add a branch limit filter to the search results thus hiding all records without one or more items owned by the logged in library. In add addition, all items whose homebranch is not that libraries will be filtered and hidden from the search results and record details. This system preference will not affect the OPAC, unless the environment variable BRANCHCODE is defined in the koha-httpd.conf file. If it is defined, the same filters are applied to the OPAC, but based on the branchcode value of the environment variable BRANCHCODE, rather than the logged in branch. Test Plan: 1) Apply patch 2) Run updatedatabase.pl 3) Perform a search that will give results for both records which have items owned by the logged in library, and records which have no items owned by the logged in library. 4) Enable the new system preference IndependentBranchesRecordesAndItems 5) Perform the same search again, any records without items owned by the currently logged in library should not appear. --- C4/Auth.pm | 2 +- C4/Branch.pm | 73 +++++++++++++++---- C4/Context.pm | 36 ++++++---- C4/Items.pm | 10 +++- C4/Koha.pm | 74 ++++++++++---------- C4/Search.pm | 34 +++++++++ catalogue/search.pl | 52 ++++++++++---- cataloguing/additem.pl | 38 +++++----- installer/data/mysql/updatedatabase.pl | 21 ++++++ .../intranet-tmpl/prog/en/includes/cat-search.inc | 64 ++++++++++-------- .../prog/en/modules/admin/preferences/admin.pref | 7 ++ .../prog/en/modules/catalogue/advsearch.tt | 59 +++++++++------- 12 files changed, 315 insertions(+), 155 deletions(-) diff --git a/C4/Auth.pm b/C4/Auth.pm index 551a951..a18d7fd 100644 --- a/C4/Auth.pm +++ b/C4/Auth.pm @@ -665,7 +665,7 @@ sub checkauth { $session->param('surname'), $session->param('branch'), $session->param('branchname'), $session->param('flags'), $session->param('emailaddress'), $session->param('branchprinter'), - $session->param('persona') + $session->param('persona'), $type ); C4::Context::set_shelves_userenv('bar',$session->param('barshelves')); C4::Context::set_shelves_userenv('pub',$session->param('pubshelves')); diff --git a/C4/Branch.pm b/C4/Branch.pm index e2399f8..6626a3c 100644 --- a/C4/Branch.pm +++ b/C4/Branch.pm @@ -42,6 +42,7 @@ BEGIN { &GetCategoryTypes &GetBranchCategories &GetBranchesInCategory + &GetCategoriesForBranch &ModBranchCategoryInfo &GetIndependentGroupModificationRights &DelBranch @@ -445,6 +446,39 @@ sub GetBranchesInCategory { return( \@branches ); } +=head2 GetCategoriesForBranch + + my @categories = GetCategoriesForBranch({ + branchcode => $branchcode, + categorytype => 'independent_group' + }); + + Called in a list context, returns an array of branch category codes + that branch is part of. + + Called in a scalar context, returns an array ref. + +=cut + +sub GetCategoriesForBranch { + my ( $params ) = @_; + my $branchcode = $params->{branchcode}; + my $categorytype = $params->{categorytype} || '%'; + + carp("Missing branchcode parameter!") unless ( $branchcode ); + + my $sql = q{ + SELECT categorycode FROM branchrelations + JOIN branchcategories USING ( categorycode ) + WHERE branchcode = ? + AND categorytype = ? + }; + + my $categories = C4::Context->dbh->selectcol_arrayref( $sql, {}, ( $branchcode, $categorytype ) ); + + return wantarray() ? $categories : @$categories; +} + =head2 GetIndependentGroupModificationRights GetIndependentGroupModificationRights( @@ -491,25 +525,34 @@ sub GetIndependentGroupModificationRights { my $stringify = $params->{stringify}; $this_branch ||= C4::Context->userenv->{branch}; + $this_branch ||= $ENV{BRANCHCODE}; - carp("No branch found!") unless ($this_branch); + return 1 if ( $this_branch && $this_branch eq $other_branch ); - return 1 if ( $this_branch eq $other_branch ); - - my $sql = q{ - SELECT DISTINCT(branchcode) - FROM branchrelations - JOIN branchcategories USING ( categorycode ) - WHERE categorycode IN ( - SELECT categorycode - FROM branchrelations - WHERE branchcode = ? - ) - AND branchcategories.categorytype = 'independent_group' - }; + my $allow_all = 0; + $allow_all = 1 if C4::Context->IsSuperLibrarian(); + $allow_all = 1 if C4::Context->userenv->{type} eq 'opac' && !$ENV{BRANCHCODE}; + my $sql; my @params; - push( @params, $this_branch ); + if ( $allow_all ) { + $sql = q{ + SELECT branchcode FROM branches WHERE 1 + } + } else { + $sql = q{ + SELECT DISTINCT(branchcode) + FROM branchrelations + JOIN branchcategories USING ( categorycode ) + WHERE categorycode IN ( + SELECT categorycode + FROM branchrelations + WHERE branchcode = ? + ) + AND branchcategories.categorytype = 'independent_group' + }; + push( @params, $this_branch ); + } if ($other_branch) { $sql .= q{ AND branchcode = ? }; diff --git a/C4/Context.pm b/C4/Context.pm index db0d498..fc3bd94 100644 --- a/C4/Context.pm +++ b/C4/Context.pm @@ -1104,23 +1104,31 @@ set_userenv is called in Auth.pm #' sub set_userenv { - my ($usernum, $userid, $usercnum, $userfirstname, $usersurname, $userbranch, $branchname, $userflags, $emailaddress, $branchprinter, $persona)= @_; - my $var=$context->{"activeuser"} || ''; + my ( + $usernum, $userid, $usercnum, $userfirstname, + $usersurname, $userbranch, $branchname, $userflags, + $emailaddress, $branchprinter, $persona, $type + ) = @_; + + my $var = $context->{"activeuser"} || ''; + my $cell = { - "number" => $usernum, - "id" => $userid, - "cardnumber" => $usercnum, - "firstname" => $userfirstname, - "surname" => $usersurname, - #possibly a law problem - "branch" => $userbranch, - "branchname" => $branchname, - "flags" => $userflags, - "emailaddress" => $emailaddress, - "branchprinter" => $branchprinter, - "persona" => $persona, + "number" => $usernum, + "id" => $userid, + "cardnumber" => $usercnum, + "firstname" => $userfirstname, + "surname" => $usersurname, + "branch" => $userbranch, + "branchname" => $branchname, + "flags" => $userflags, + "emailaddress" => $emailaddress, + "branchprinter" => $branchprinter, + "persona" => $persona, + "type" => $type, }; + $context->{userenv}->{$var} = $cell; + return $cell; } diff --git a/C4/Items.pm b/C4/Items.pm index c1adeae..735e7d6 100644 --- a/C4/Items.pm +++ b/C4/Items.pm @@ -1202,6 +1202,11 @@ If this is set, it is set to C. sub GetItemsInfo { my ( $biblionumber ) = @_; + + my $IndependentBranchesRecordsAndItems = + C4::Context->preference('IndependentBranchesRecordsAndItems') + && !C4::Context->IsSuperLibrarian(); + my $dbh = C4::Context->dbh; # note biblioitems.* must be avoided to prevent large marc and marcxml fields from killing performance. my $query = " @@ -1231,7 +1236,10 @@ sub GetItemsInfo { LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber LEFT JOIN itemtypes ON itemtypes.itemtype = " . (C4::Context->preference('item-level_itypes') ? 'items.itype' : 'biblioitems.itemtype'); - $query .= " WHERE items.biblionumber = ? ORDER BY home.branchname, items.enumchron, LPAD( items.copynumber, 8, '0' ), items.dateaccessioned DESC" ; + $query .= " WHERE items.biblionumber = ? "; + $query .= " AND items.homebranch IN ( " . GetIndependentGroupModificationRights({ stringify => 1}) . " ) " if ( $IndependentBranchesRecordsAndItems ); + $query .= " ORDER BY home.branchname, items.enumchron, LPAD( items.copynumber, 8, '0' ), items.dateaccessioned DESC" ; + my $sth = $dbh->prepare($query); $sth->execute($biblionumber); my $i = 0; diff --git a/C4/Koha.pm b/C4/Koha.pm index 3648dcc..d817b82 100644 --- a/C4/Koha.pm +++ b/C4/Koha.pm @@ -36,43 +36,43 @@ BEGIN { $VERSION = 3.07.00.049; require Exporter; @ISA = qw(Exporter); - @EXPORT = qw( - &slashifyDate - &subfield_is_koha_internal_p - &GetPrinters &GetPrinter - &GetItemTypes &getitemtypeinfo - &GetCcodes - &GetSupportName &GetSupportList - &get_itemtypeinfos_of - &getframeworks &getframeworkinfo - &getauthtypes &getauthtype - &getallthemes - &getFacets - &displayServers - &getnbpages - &get_infos_of - &get_notforloan_label_of - &getitemtypeimagedir - &getitemtypeimagesrc - &getitemtypeimagelocation - &GetAuthorisedValues - &GetAuthorisedValueCategories - &IsAuthorisedValueCategory - &GetKohaAuthorisedValues - &GetKohaAuthorisedValuesFromField - &GetKohaAuthorisedValueLib - &GetAuthorisedValueByCode - &GetKohaImageurlFromAuthorisedValues - &GetAuthValCode - &AddAuthorisedValue - &GetNormalizedUPC - &GetNormalizedISBN - &GetNormalizedEAN - &GetNormalizedOCLCNumber - &xml_escape - - $DEBUG - ); + @EXPORT = qw( + &slashifyDate + &subfield_is_koha_internal_p + &GetPrinters &GetPrinter + &GetItemTypes &getitemtypeinfo + &GetCcodes + &GetSupportName &GetSupportList + &get_itemtypeinfos_of + &getframeworks &getframeworkinfo + &getauthtypes &getauthtype + &getallthemes + &getFacets + &displayServers + &getnbpages + &get_infos_of + &get_notforloan_label_of + &getitemtypeimagedir + &getitemtypeimagesrc + &getitemtypeimagelocation + &GetAuthorisedValues + &GetAuthorisedValueCategories + &IsAuthorisedValueCategory + &GetKohaAuthorisedValues + &GetKohaAuthorisedValuesFromField + &GetKohaAuthorisedValueLib + &GetAuthorisedValueByCode + &GetKohaImageurlFromAuthorisedValues + &GetAuthValCode + &AddAuthorisedValue + &GetNormalizedUPC + &GetNormalizedISBN + &GetNormalizedEAN + &GetNormalizedOCLCNumber + &xml_escape + + $DEBUG + ); $DEBUG = 0; @EXPORT_OK = qw( GetDailyQuote ); } diff --git a/C4/Search.pm b/C4/Search.pm index aec024b..fc4fc86 100644 --- a/C4/Search.pm +++ b/C4/Search.pm @@ -36,6 +36,7 @@ use URI::Escape; use Business::ISBN; use MARC::Record; use MARC::Field; +use List::MoreUtils qw(none); use utf8; use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $DEBUG); @@ -1828,14 +1829,47 @@ sub searchResults { my $maxitems = $maxitems_pref ? $maxitems_pref - 1 : 1; my @hiddenitems; # hidden itemnumbers based on OpacHiddenItems syspref + my $IndependentBranchesRecordsAndItems; + if ( $search_context eq 'opac' ) { + # For the OPAC, if IndependentBranchesRecordsAndItems is enabled, + # and BRANCHCODE has been set in the httpd conf, + # we need to filter the items + $IndependentBranchesRecordsAndItems = + C4::Context->preference('IndependentBranchesRecordsAndItems') + && $ENV{BRANCHCODE}; + } + else { + # For the intranet, if IndependentBranchesRecordsAndItems is enabled, + # and the user is not a superlibrarian, + # we need to filter the items + $IndependentBranchesRecordsAndItems = + C4::Context->preference('IndependentBranchesRecordsAndItems') + && !C4::Context->IsSuperLibrarian(); + } + my @allowed_branches = $IndependentBranchesRecordsAndItems ? GetIndependentGroupModificationRights() : undef; + # loop through every item + my $index = -1; foreach my $field (@fields) { + $index++; my $item; # populate the items hash foreach my $code ( keys %subfieldstosearch ) { $item->{$code} = $field->subfield( $subfieldstosearch{$code} ); } + + # if IndependentBranchesRecordsAndItems is enabled, and this record + # isn't allowed to be viewed, remove it from the items list and go + # right to the next item. + if ( $IndependentBranchesRecordsAndItems ) { + if ( none { $_ eq $item->{homebranch} } @allowed_branches ) { + splice(@fields, $index, 1); + $items_count--; + next; + } + } + $item->{description} = $itemtypes{ $item->{itype} }{description}; # OPAC hidden items diff --git a/catalogue/search.pl b/catalogue/search.pl index 8624b3b..57f3c7c 100755 --- a/catalogue/search.pl +++ b/catalogue/search.pl @@ -135,6 +135,8 @@ Not yet completed... use strict; # always use #use warnings; FIXME - Bug 2505 +use List::MoreUtils qw(any); + ## STEP 1. Load things that are used in both search page and # results page and decide which template to load, operations # to perform, etc. @@ -179,9 +181,11 @@ else { flagsrequired => { catalogue => 1 }, } ); + if (C4::Context->preference("marcflavour") eq "UNIMARC" ) { $template->param('UNIMARC' => 1); } + if (C4::Context->preference("IntranetNumbersPreferPhrase")) { $template->param('numbersphr' => 1); } @@ -226,20 +230,10 @@ my $branches = GetBranches(); # Populate branch_loop with all branches sorted by their name. If # independantbranches is activated, set the default branch to the borrower # branch, except for superlibrarian who need to search all libraries. -my $user = C4::Context->userenv; -my @branch_loop = map { - { - value => $_, - branchname => $branches->{$_}->{branchname}, - selected => $user->{branch} eq $_ && C4::Branch::onlymine(), - } -} sort { - $branches->{$a}->{branchname} cmp $branches->{$b}->{branchname} -} keys %$branches; - -my $categories = GetBranchCategories(undef,'searchdomain'); - -$template->param(branchloop => \@branch_loop, searchdomainloop => $categories); +$template->param( + branchloop => GetBranchesLoop(), + searchdomainloop => GetBranchCategories( undef, 'searchdomain' ), +); # load the Type stuff my $itemtypes = GetItemTypes; @@ -411,7 +405,35 @@ my @operands = $cgi->param('q'); # limits are use to limit to results to a pre-defined category such as branch or language my @limits = $cgi->param('limit'); -if($params->{'multibranchlimit'}) { +if ( C4::Context->preference('IndependentBranchesRecordsAndItems') ) { + # Get list of branches this branch can access + my @branches = GetIndependentGroupModificationRights(); + + # Strip out any branch search limits that are not in the list of allowable branches + my $has_valid_branch_limits; # If at least one allowable branch limit is passed, + # use the valid ones, otherwise make it an "all branches" + # search with the allowed branches + + my @new_limits; + foreach my $limit ( @limits ) { + if ( $limit =~ /^branch:/ ) { + my ( undef, $branch ) = split(':', $limit); + push( @new_limits, $limit ) if any { $_ eq $branch } @branches; + $has_valid_branch_limits = 1; + } else { + push( @new_limits, $limit ); + } + } + @limits = @new_limits; + + # If the limits contain any branch limits, if not, do a search on all allowable branches + unless ($has_valid_branch_limits) { + my $new_branch_limit = + '(' . join( " or ", map { "branch: $_ " } @branches ) . ')'; + push( @limits, $new_branch_limit ); + } +} +elsif( $params->{'multibranchlimit'} ) { push @limits, '('.join( " or ", map { "branch: $_ " } @{ GetBranchesInCategory( $params->{'multibranchlimit'} ) } ).')'; } diff --git a/cataloguing/additem.pl b/cataloguing/additem.pl index 9cebd67..315faf8 100755 --- a/cataloguing/additem.pl +++ b/cataloguing/additem.pl @@ -666,6 +666,10 @@ if ( C4::Context->preference('EasyAnalyticalRecords') ) { } } +my $IndependentBranches = !C4::Context->IsSuperLibrarian() + && C4::Context->preference('IndependentBranches'); +my $IndependentBranchesRecordsAndItems = $IndependentBranches + && C4::Context->preference('IndependentBranchesRecordsAndItems'); foreach my $field (@fields) { next if ( $field->tag() < 10 ); @@ -691,13 +695,11 @@ foreach my $field (@fields) { if ( $field->tag eq $branchtagfield && $subfieldcode eq $branchtagsubfield - && C4::Context->preference("IndependentBranches") ) + && $IndependentBranches ) { #verifying rights - my $userenv = C4::Context->userenv(); unless ( - $userenv->{'flags'} % 2 == 1 - || GetIndependentGroupModificationRights( + GetIndependentGroupModificationRights( { for => $subfieldvalue } ) ) @@ -705,24 +707,24 @@ foreach my $field (@fields) { $this_row{'nomod'} = 1; } } - $this_row{itemnumber} = $subfieldvalue if ($field->tag() eq $itemtagfield && $subfieldcode eq $itemtagsubfield); - if ( C4::Context->preference('EasyAnalyticalRecords') ) { - foreach my $hostitemnumber (@hostitemnumbers){ - if ($this_row{itemnumber} eq $hostitemnumber){ - $this_row{hostitemflag} = 1; - $this_row{hostbiblionumber}= GetBiblionumberFromItemnumber($hostitemnumber); - last; - } - } + $this_row{itemnumber} = $subfieldvalue if ($field->tag() eq $itemtagfield && $subfieldcode eq $itemtagsubfield); -# my $countanalytics=GetAnalyticsCount($this_row{itemnumber}); -# if ($countanalytics > 0){ -# $this_row{countanalytics} = $countanalytics; -# } - } + if ( C4::Context->preference('EasyAnalyticalRecords') ) { + foreach my $hostitemnumber (@hostitemnumbers) { + if ( $this_row{itemnumber} eq $hostitemnumber ) { + $this_row{hostitemflag} = 1; + $this_row{hostbiblionumber} = + GetBiblionumberFromItemnumber($hostitemnumber); + last; + } + } + } } + + next if ( $this_row{'nomod'} && $IndependentBranchesRecordsAndItems ); + if (%this_row) { push(@big_array, \%this_row); } diff --git a/installer/data/mysql/updatedatabase.pl b/installer/data/mysql/updatedatabase.pl index 19d2c35..3f681e8 100755 --- a/installer/data/mysql/updatedatabase.pl +++ b/installer/data/mysql/updatedatabase.pl @@ -6853,6 +6853,27 @@ if ( CheckVersion($DBversion) ) { SetVersion ($DBversion); } +$DBversion = "3.11.00.XXX"; +if ( CheckVersion($DBversion) ) { + print "Upgrade to $DBversion done (IndependentBranches)\n"; + $dbh->do(" + INSERT INTO systempreferences ( + variable, + value, + options, + explanation, + type + ) VALUES ( + 'IndependentBranchesRecordsAndItems', + '0', + '', + 'If on, the staff interface search will hide all records that do not contain an item owned by the logged in branch, and hide the items themselves.', + 'YesNo' + ) + "); + SetVersion ($DBversion); +} + =head1 FUNCTIONS =head2 TableExists($table) diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/cat-search.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/cat-search.inc index 186f1d1..4cf3e71 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/includes/cat-search.inc +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/cat-search.inc @@ -1,33 +1,41 @@
-

[% LibraryName %]

-
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/admin.pref b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/admin.pref index 1215ac1..7433a53 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/admin.pref +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/admin.pref @@ -70,6 +70,13 @@ Administration: yes: Prevent no: "Don't prevent" - staff (but not superlibrarians) from modifying objects (holds, items, patrons, etc.) belonging to other libraries. + - + - pref: IndependentBranchesRecordsAndItems + default: 0 + choices: + yes: Prevent + no: "Don't prevent" + - staff from seeing items owned by other libraries, and records without any items the library. CAS Authentication: - - pref: casAuthentication diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/advsearch.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/advsearch.tt index c982eb4..1dac415 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/advsearch.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/advsearch.tt @@ -1,3 +1,4 @@ +[% USE Koha %] [% INCLUDE 'doc-head-open.inc' %] Koha › Catalog › Advanced search [% INCLUDE 'doc-head-close.inc' %] @@ -229,34 +230,40 @@ [% END %] -
Location and availability -
-

-
+
Location and availability +
+

+
-
-

- - [% IF ( searchdomainloop ) %] -

OR

-

- [% END %] -
+
+

+ + +

+ + [% IF searchdomainloop && !Koha.Preference('IndependentBranchesRecordsAndItems') %] +

OR

+

+ + +

+ [% END %]
+
-- 1.7.2.5