From 95375e0787838d2083e8cc588b7d2ea32dab4eef Mon Sep 17 00:00:00 2001 From: Nick Clemens Date: Thu, 30 May 2024 17:57:14 +0000 Subject: [PATCH] Bug 36996: Add z3950Status system preference This bug adds a new system preference 'z3950Status' which takes a yaml block and marks any items matching the conditions as unavailable via z3950 results It obeys the existing z3950 extra fields, adding item ststus to field $k To test: 0 - Apply patch, update database 1 - Setup your koha to use itself as a z39 source in Administration/Z3950/SRU servers: host: localhost port 2100 database: biblios syntax: MARC21 (or UNIMARC if applicable) 2 - Uncomment the config line in /etc/koha/sites/kohadev/z3950/config.xml Also make sure to remove the backslashes escaping the hyphens 3 - Restart all 4 - Cataloging - New from Z3950 5 - Search your records, view the marc to confirm status subfield $k is included 6 - Add items to a record with various statuses (lost,damaged,withdrawn) and itemtypes 7 - Edit syspref AdditionalFieldsInZ3950ResultSearch o add 952$k 8 - Search for record above - confirm statuses show as expected 9 - Edit new syspref z3950Status - confirm the language of the pref makes sense itype: [BK] ccode: [REF] 10 - Search z39 again and confirm items and collection codes are marked 'SYSPREF' in $k 11 - Edit Authorized values - add category Z3950_STATUS 12 - Add authorized vlaue: SYSPREF with Description: System preferenced 13 - Restart all 14 - Search z39 again and confirm statuses show new description Sponsored by: Northeast Kansas Library System Sponsored by: South East Kansas Library System --- Koha/Item.pm | 60 +++++++++++++ Koha/Z3950Responder/Session.pm | 35 +------- .../atomicupdate/add_z3950status_syspref.pl | 21 +++++ installer/data/mysql/mandatory/sysprefs.sql | 3 +- .../modules/admin/preferences/searching.pref | 10 +++ t/db_dependent/Koha/Item.t | 86 ++++++++++++++++++- 6 files changed, 181 insertions(+), 34 deletions(-) create mode 100755 installer/data/mysql/atomicupdate/add_z3950status_syspref.pl diff --git a/Koha/Item.pm b/Koha/Item.pm index 91c15b22d55..371ca7786ca 100644 --- a/Koha/Item.pm +++ b/Koha/Item.pm @@ -2602,6 +2602,66 @@ sub location_update_trigger { return $messages; } + +=head3 z3950_status + + my $statuses = $item->z3950_statuses( $status_strings ); + +Returns an array of statuses for use in z3950 results. Takes a hashref listing the display strings for the +various statuses. Availability is determined by item statuses and the system preference z3950Status. + +Status strings are defined in authorised values in the Z3950_STATUS category. + +=cut + +sub z3950_status { + my ( $self, $status_strings ) = @_; + + my @statuses; + + if ( $self->onloan() ) { + push @statuses, $status_strings->{CHECKED_OUT} // "CHECKED_OUT"; + } + + if ( $self->itemlost() ) { + push @statuses, $status_strings->{LOST} // "LOST"; + } + + if ( $self->is_notforloan() ) { + push @statuses, $status_strings->{NOT_FOR_LOAN} // "NOT_FOR_LOAN"; + } + + if ( $self->damaged() ) { + push @statuses, $status_strings->{DAMAGED} // "DAMAGED"; + } + + if ( $self->withdrawn() ) { + push @statuses, $status_strings->{WITHDRAWN} // "WITHDRAWN"; + } + + if ( my $transfer = $self->get_transfer ) { + push @statuses, $status_strings->{IN_TRANSIT} // "IN_TRANSIT" if $transfer->in_transit; + } + + if ( C4::Reserves::GetReserveStatus( $self->itemnumber ) ne '' ) { + push @statuses, $status_strings->{ON_HOLD} // "ON_HOLD"; + } + my $rules = C4::Context->yaml_preference('Z3950Status'); + if ($rules) { + foreach my $field ( keys %$rules ) { + foreach my $value ( @{ $rules->{$field} } ) { + if ( defined $self->$field && $self->$field eq $value ) { + push @statuses, $status_strings->{"SYSPREF"} // "SYSPREF"; + last; + } + } + } + } + + return \@statuses; + +} + =head3 _type =cut diff --git a/Koha/Z3950Responder/Session.pm b/Koha/Z3950Responder/Session.pm index f84bcba77b8..8cefe1e1d53 100644 --- a/Koha/Z3950Responder/Session.pm +++ b/Koha/Z3950Responder/Session.pm @@ -20,7 +20,6 @@ package Koha::Z3950Responder::Session; use Modern::Perl; use C4::Context; -use C4::Reserves qw( GetReserveStatus ); use C4::Search qw( new_record_from_zebra ); use Koha::Items; @@ -263,40 +262,12 @@ sub add_item_status { my $item = Koha::Items->find( $itemnumber ); return unless $item; - my @statuses; - - if ( $item->onloan() ) { - push @statuses, $status_strings->{CHECKED_OUT}; - } - - if ( $item->itemlost() ) { - push @statuses, $status_strings->{LOST}; - } - - if ( $item->notforloan() ) { - push @statuses, $status_strings->{NOT_FOR_LOAN}; - } - - if ( $item->damaged() ) { - push @statuses, $status_strings->{DAMAGED}; - } - - if ( $item->withdrawn() ) { - push @statuses, $status_strings->{WITHDRAWN}; - } - - if ( my $transfer = $item->get_transfer ) { - push @statuses, $status_strings->{IN_TRANSIT} if $transfer->in_transit; - } - - if ( GetReserveStatus( $itemnumber ) ne '' ) { - push @statuses, $status_strings->{ON_HOLD}; - } + my $statuses = $item->z3950_status($status_strings); if ( $server->{add_status_multi_subfield} ) { - $field->add_subfields( map { ( $add_subfield, $_ ) } ( @statuses ? @statuses : $status_strings->{AVAILABLE} ) ); + $field->add_subfields( map { ( $add_subfield, $_ ) } ( @$statuses ? @$statuses : $status_strings->{AVAILABLE} ) ); } else { - $field->add_subfields( $add_subfield, @statuses ? join( ', ', @statuses ) : $status_strings->{AVAILABLE} ); + $field->add_subfields( $add_subfield, @$statuses ? join( ', ', @$statuses ) : $status_strings->{AVAILABLE} ); } } diff --git a/installer/data/mysql/atomicupdate/add_z3950status_syspref.pl b/installer/data/mysql/atomicupdate/add_z3950status_syspref.pl new file mode 100755 index 00000000000..220bb766a83 --- /dev/null +++ b/installer/data/mysql/atomicupdate/add_z3950status_syspref.pl @@ -0,0 +1,21 @@ +use Modern::Perl; +use Koha::Installer::Output qw(say_warning say_failure say_success say_info); + +return { + bug_number => "36996", + description => "Add z3950Status system preference", + up => sub { + my ($args) = @_; + my ( $dbh, $out ) = @$args{qw(dbh out)}; + + # Do you stuffs here + $dbh->do( + q{ + INSERT IGNORE INTO systempreferences ( `variable`, `value`, `options`, `explanation`, `type` ) VALUES + ('z3950Status','','','This syspref allows to define custom YAML based rules for marking items unavailable in z3950 results.','Textarea') + } + ); + + say $out "Added new system preference 'Z3950Status'"; + }, +}; diff --git a/installer/data/mysql/mandatory/sysprefs.sql b/installer/data/mysql/mandatory/sysprefs.sql index df70c69def8..aa5623cf281 100644 --- a/installer/data/mysql/mandatory/sysprefs.sql +++ b/installer/data/mysql/mandatory/sysprefs.sql @@ -854,5 +854,6 @@ INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` ('XSLTListsDisplay','default','','Enable XSLT stylesheet control over lists pages display on intranet','Free'), ('XSLTResultsDisplay','default','','Enable XSL stylesheet control over results page display on intranet','Free'), ('z3950AuthorAuthFields','701,702,700',NULL,'Define the MARC biblio fields for Personal Name Authorities to fill biblio.author','free'), -('z3950NormalizeAuthor','0','','If ON, Personal Name Authorities will replace authors in biblio.author','YesNo') +('z3950NormalizeAuthor','0','','If ON, Personal Name Authorities will replace authors in biblio.author','YesNo'), +('z3950Status','','','This syspref allows to define custom YAML based rules for marking items unavailable in z3950 results.','Textarea') ; diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/searching.pref b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/searching.pref index da202d7e1ef..a99a7ea4f0c 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/searching.pref +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/searching.pref @@ -99,6 +99,16 @@ Searching: - "List of search fields (separated by | or ,) that should not be autotruncated by Elasticsearch even if QueryAutoTruncate is set to 'Yes':" - pref: ESPreventAutoTruncate class: long + - + - pref: z3950Status + type: textarea + syntax: text/x-yaml + class: code + - "Define custom rules to show specific items as not available in Z3950. Rules are defined with item database fields as keys and an array of values.
" + - "Examples:

" + - "
ccode: [REF,ARC]

- would mark all REF and ARC collections (Reference and Archives) items as unavailable in Z3950 results.

" + - "You can define multiple keys and values, items meeting ANY of the criteria will be marked unavailable:

" + - "
itype: [REF]
holdingbranch: [REPAIR]

- would mark all reference items AND items at the REPAIR branch as unavailable." Search form: - - pref: LoadSearchHistoryToTheFirstLoggedUser diff --git a/t/db_dependent/Koha/Item.t b/t/db_dependent/Koha/Item.t index 5df382edf50..76c0f115fa5 100755 --- a/t/db_dependent/Koha/Item.t +++ b/t/db_dependent/Koha/Item.t @@ -20,7 +20,7 @@ use Modern::Perl; use utf8; -use Test::More tests => 34; +use Test::More tests => 35; use Test::Exception; use Test::MockModule; @@ -44,6 +44,90 @@ use t::lib::Dates; my $schema = Koha::Database->new->schema; my $builder = t::lib::TestBuilder->new; +subtest 'z3950_status' => sub { + plan tests => 9; + + $schema->storage->txn_begin; + t::lib::Mocks::mock_preference( 'z3950Status', '' ); + + my $itemtype = $builder->build_object( { class => "Koha::ItemTypes" } ); + my $item = $builder->build_sample_item( + { + itype => $itemtype->itemtype, + } + ); + + my $statuses = $item->z3950_status(); + is( scalar @{$statuses}, 0, "No statuses set when pref blank and item has no status" ); + + $item->onloan('2001-01-01')->store(); + $statuses = $item->z3950_status(); + is_deeply( $statuses, ['CHECKED_OUT'], "Item status is checked out when onloan is set" ); + + $item->damaged(1)->withdrawn(1)->itemlost(1)->store(); + $statuses = $item->z3950_status(); + is_deeply( + $statuses, [ 'CHECKED_OUT', 'LOST', 'DAMAGED', 'WITHDRAWN' ], + "Multiple item statuses set from other fields" + ); + + $itemtype->notforloan(1)->store(); + $statuses = $item->z3950_status(); + is_deeply( + $statuses, [ 'CHECKED_OUT', 'LOST', 'NOT_FOR_LOAN', 'DAMAGED', 'WITHDRAWN' ], + "Not for loan status correctly added from itemtype" + ); + + $statuses = $item->z3950_status( { LOST => 'Gone', 'WITHDRAWN' => 'Weeded' } ); + is_deeply( + $statuses, [ 'CHECKED_OUT', 'Gone', 'NOT_FOR_LOAN', 'DAMAGED', 'Weeded' ], + "Lost items correctly substituted when values passed" + ); + + $builder->build_object( + { class => 'Koha::Item::Transfers', value => { itemnumber => $item->itemnumber, datesent => '1999-12-31' } } ); + $builder->build_object( { class => 'Koha::Holds', value => { itemnumber => $item->itemnumber, found => 'W' } } ); + + $statuses = $item->z3950_status( { LOST => 'Gone', 'WITHDRAWN' => 'Weeded', 'ON_HOLD' => 'Patron awaits' } ); + is_deeply( + $statuses, [ 'CHECKED_OUT', 'Gone', 'NOT_FOR_LOAN', 'DAMAGED', 'Weeded', 'IN_TRANSIT', 'Patron awaits' ], + "Hold and transit statuses applied correctly" + ); + + t::lib::Mocks::mock_preference( 'z3950Status', "homebranch: [" . $item->homebranch . "]" ); + $statuses = $item->z3950_status( { LOST => 'Gone', 'WITHDRAWN' => 'Weeded', 'ON_HOLD' => 'Patron awaits' } ); + is_deeply( + $statuses, + [ 'CHECKED_OUT', 'Gone', 'NOT_FOR_LOAN', 'DAMAGED', 'Weeded', 'IN_TRANSIT', 'Patron awaits', 'SYSPREF' ], + "System preference statuses applied correctly" + ); + + my $item_2 = $builder->build_sample_item( + { + homebranch => $item->homebranch, + } + ); + + $statuses = $item_2->z3950_status( + { + LOST => 'Gone', 'WITHDRAWN' => 'Weeded', 'ON_HOLD' => 'Patron awaits', + 'SYSPREF' => 'Library policy forbids' + } + ); + is_deeply( $statuses, ['Library policy forbids'], "system preference statuses substituted correctly" ); + + t::lib::Mocks::mock_preference( 'z3950Status', "ccode: [FAKE]\r\n\nhomebranch: [" . $item->homebranch . "]" ); + $statuses = $item_2->z3950_status( + { + LOST => 'Gone', 'WITHDRAWN' => 'Weeded', 'ON_HOLD' => 'Patron awaits', + 'SYSPREF' => 'Library policy forbids' + } + ); + is_deeply( $statuses, ['Library policy forbids'], "Status applied when any field matches" ); + + $schema->storage->txn_rollback; +}; + subtest 'return_claims relationship' => sub { plan tests => 3; -- 2.39.2