From fffbc8c08566f6130e75891c7b55e479d181c4c9 Mon Sep 17 00:00:00 2001 From: Jared Camins-Esakov Date: Sat, 2 Jun 2012 13:32:25 -0400 Subject: [PATCH] [SIGNED-OFF] Bug 7417: Include see from references in bibliographic searches This patch adds the Koha::Indexer::RecordNormalizer and Koha::Indexer::MARC::RecordNormalizer::EmbedSeeFromHeadings packages to enable the inclusion of alternate forms of headings in bibliographic searches. When the new syspref IncludeSeeFromInSearches is turned on (default is off) rebuild_zebra.pl will insert see from headings from authority records into bibliographic records when indexing, so that a search on an obsolete term will turn up relevant records. To test: 1) Enable IncludeSeeFromInSearches 2) Add a heading that has an alternate form to a record (for example, "Cooking" has the alternate form "Cookery," if you have authority records from LC) 3) Index the zebraqueue (or reindex if you haven't indexed your system yet) 4) Confirm that if you search for "Cookery" you get the record you just modified Signed-off-by: Jared Camins-Esakov Rebased on master 5 August 2012 Signed-off-by: Katrin Fischer Also checked: - Verified database update works correctly - Checked system preference and its description - Checked staff/opac detail pages with feature on/off - Checked staff/opac search facets - Downloaded and tested records in various formats - Tried different searches for 'see from' entries of authorities - Ran all unit tests No problems found. --- C4/AuthoritiesMarc.pm | 16 +- C4/Search.pm | 3 +- Koha/Authority.pm | 92 ++++++++++ Koha/Filter/MARC/EmbedSeeFromHeadings.pm | 102 +++++++++++ Koha/Filter/MARC/Null.pm | 55 ++++++ Koha/RecordProcessor.pm | 183 ++++++++++++++++++++ Koha/RecordProcessor/Base.pm | 133 ++++++++++++++ Koha/SearchEngine/Solr/Index.pm | 6 + installer/data/mysql/sysprefs.sql | 1 + installer/data/mysql/updatedatabase.pl | 9 + .../en/modules/admin/preferences/searching.pref | 7 + .../prog/en/xslt/MARC21slim2intranetResults.xsl | 6 +- .../prog/en/xslt/NORMARCslim2intranetResults.xsl | 6 +- .../prog/en/xslt/MARC21slim2OPACResults.xsl | 6 +- .../prog/en/xslt/NORMARCslim2OPACResults.xsl | 6 +- misc/maintenance/process_record_through_filter.pl | 18 ++ misc/migration_tools/rebuild_zebra.pl | 4 + t/RecordProcessor.t | 80 +++++++++ t/db_dependent/Koha_Authority.t | 66 +++++++ .../RecordProcessor_EmbedSeeFromHeadings.t | 66 +++++++ 20 files changed, 839 insertions(+), 26 deletions(-) create mode 100644 Koha/Authority.pm create mode 100644 Koha/Filter/MARC/EmbedSeeFromHeadings.pm create mode 100644 Koha/Filter/MARC/Null.pm create mode 100644 Koha/RecordProcessor.pm create mode 100644 Koha/RecordProcessor/Base.pm create mode 100755 misc/maintenance/process_record_through_filter.pl create mode 100755 t/RecordProcessor.t create mode 100755 t/db_dependent/Koha_Authority.t create mode 100755 t/db_dependent/RecordProcessor_EmbedSeeFromHeadings.t diff --git a/C4/AuthoritiesMarc.pm b/C4/AuthoritiesMarc.pm index 21ebd94..c533ad0 100644 --- a/C4/AuthoritiesMarc.pm +++ b/C4/AuthoritiesMarc.pm @@ -26,6 +26,7 @@ use C4::AuthoritiesMarc::MARC21; use C4::AuthoritiesMarc::UNIMARC; use C4::Charset; use C4::Log; +use Koha::Authority; use vars qw($VERSION @ISA @EXPORT); @@ -848,19 +849,8 @@ Returns MARC::Record of the authority passed in parameter. sub GetAuthority { my ($authid)=@_; - my $dbh=C4::Context->dbh; - my $sth=$dbh->prepare("select authtypecode, marcxml from auth_header where authid=?"); - $sth->execute($authid); - my ($authtypecode, $marcxml) = $sth->fetchrow; - my $record=eval {MARC::Record->new_from_xml(StripNonXmlChars($marcxml),'UTF-8', - (C4::Context->preference("marcflavour") eq "UNIMARC"?"UNIMARCAUTH":C4::Context->preference("marcflavour")))}; - return undef if ($@); - $record->encoding('UTF-8'); - if (C4::Context->preference("marcflavour") eq "MARC21") { - my ($auth_type_tag, $auth_type_subfield) = get_auth_type_location($authtypecode); - C4::AuthoritiesMarc::MARC21::fix_marc21_auth_type_location($record, $auth_type_tag, $auth_type_subfield); - } - return ($record); + my $authority = Koha::Authority->get_from_authid($authid); + return ($authority->record); } =head2 GetAuthType diff --git a/C4/Search.pm b/C4/Search.pm index fd81610..968d7bc 100644 --- a/C4/Search.pm +++ b/C4/Search.pm @@ -477,7 +477,8 @@ sub getRecords { # avoid first line my $tag_num = substr($tag, 0, 3); my $letters = substr($tag, 3); - my $field_pattern = '\n' . $tag_num . ' ([^\n]+)'; + my $field_pattern = '\n' . $tag_num . ' ([^z][^\n]+)'; + $field_pattern = '\n' . $tag_num . ' ([^\n]+)' if (int($tag_num) < 10); my @field_tokens = ( $render_record =~ /$field_pattern/g ) ; foreach my $field_token (@field_tokens) { my @subf = ( $field_token =~ /\$([a-zA-Z0-9]) ([^\$]+)/g ); diff --git a/Koha/Authority.pm b/Koha/Authority.pm new file mode 100644 index 0000000..b64f8de --- /dev/null +++ b/Koha/Authority.pm @@ -0,0 +1,92 @@ +package Koha::Authority; + +# Copyright 2012 C & P Bibliography Services +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +=head1 NAME + +Koha::Authority - class to encapsulate authority records in Koha + +=head1 SYNOPSIS + +Object-oriented class that encapsulates authority records in Koha. + +=head1 DESCRIPTION + +Authority data. + +=cut + +use strict; +use warnings; +use C4::Context; +use MARC::Record; +use MARC::File::XML; +use C4::Charset; + +use base qw(Class::Accessor); + +__PACKAGE__->mk_accessors(qw( authid authtype record marcflavour )); + +=head2 new + + my $auth = Koha::Authority->new($record); + +Create a new Koha::Authority object based on the provided record. + +=cut +sub new { + my $class = shift; + my $record = shift; + + my $self = $class->SUPER::new( { record => $record }); + + bless $self, $class; + return $self; +} + +=head2 get_from_authid + + my $auth = Koha::Authority->get_from_authid($authid); + +Create the Koha::Authority object associated with the provided authid. + +=cut +sub get_from_authid { + my $class = shift; + my $authid = shift; + my $marcflavour = C4::Context->preference("marcflavour"); + + my $dbh=C4::Context->dbh; + my $sth=$dbh->prepare("select authtypecode, marcxml from auth_header where authid=?"); + $sth->execute($authid); + my ($authtypecode, $marcxml) = $sth->fetchrow; + my $record=eval {MARC::Record->new_from_xml(StripNonXmlChars($marcxml),'UTF-8', + (C4::Context->preference("marcflavour") eq "UNIMARC"?"UNIMARCAUTH":C4::Context->preference("marcflavour")))}; + return undef if ($@); + $record->encoding('UTF-8'); + + my $self = $class->SUPER::new( { authid => $authid, + marcflavour => $marcflavour, + authtype => $authtypecode, + record => $record }); + + bless $self, $class; + return $self; +} + +1; diff --git a/Koha/Filter/MARC/EmbedSeeFromHeadings.pm b/Koha/Filter/MARC/EmbedSeeFromHeadings.pm new file mode 100644 index 0000000..ea7e38b --- /dev/null +++ b/Koha/Filter/MARC/EmbedSeeFromHeadings.pm @@ -0,0 +1,102 @@ +package Koha::Filter::MARC::EmbedSeeFromHeadings; + +# Copyright 2012 C & P Bibliography Services +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +=head1 NAME + +Koha::Filter::MARC::EmbedSeeFromHeadings - embeds see from headings into MARC for indexing + +=head1 SYNOPSIS + + +=head1 DESCRIPTION + +Filter to embed see from headings into MARC records. + +=cut + +use strict; +use warnings; +use Carp; +use Koha::Authority; + +use base qw(Koha::RecordProcessor::Base); +our $NAME = 'EmbedSeeFromHeadings'; +our $VERSION = '1.0'; + +=head2 filter + + my $newrecord = $filter->filter($record); + my $newrecords = $filter->filter(\@records); + +Embed see from headings into the specified record(s) and return the result. +In order to differentiate added headings from actual headings, a 'z' is +put in the first indicator. + +=cut +sub filter { + my $self = shift; + my $record = shift; + my $newrecord; + + return undef unless defined $record; + + if (ref $record eq 'ARRAY') { + my @recarray; + foreach my $thisrec (@$record) { + push @recarray, _processrecord($thisrec); + } + $newrecord = \@recarray; + } elsif (ref $record eq 'MARC::Record') { + $newrecord = _processrecord($record); + } + + return $newrecord; +} + +sub _processrecord { + my $record = shift; + + foreach my $field ( $record->fields() ) { + next if $field->is_control_field(); + my $authid = $field->subfield('9'); + + next unless $authid; + + my $authority = Koha::Authority->get_from_authid($authid); + next unless $authority; + my $auth_marc = $authority->record; + my @seefrom = $auth_marc->field('4..'); + my @newfields; + foreach my $authfield (@seefrom) { + my $tag = substr($field->tag(), 0, 1) . substr($authfield->tag(), 1, 2); + my $newfield = MARC::Field->new($tag, + 'z', + $authfield->indicator(2) || ' ', + '9' => '1'); + foreach my $sub ($authfield->subfields()) { + my ($code,$val) = @$sub; + $newfield->add_subfields( $code => $val ); + } + $newfield->delete_subfield( code => '9' ); + push @newfields, $newfield if (scalar($newfield->subfields()) > 0); + } + $record->append_fields(@newfields); + } + return $record; +} diff --git a/Koha/Filter/MARC/Null.pm b/Koha/Filter/MARC/Null.pm new file mode 100644 index 0000000..578781c --- /dev/null +++ b/Koha/Filter/MARC/Null.pm @@ -0,0 +1,55 @@ +package Koha::Filter::MARC::Null; + +# Copyright 2012 C & P Bibliography Services +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +=head1 NAME + +Koha::Filter::MARC::Null - an example filter that does nothing but allow us to run tests + +=head1 SYNOPSIS + + +=head1 DESCRIPTION + +Filter to allow us to run unit tests and regression tests against the +RecordProcessor. + +=cut + +use strict; +use warnings; +use Carp; + +use base qw(Koha::RecordProcessor::Base); +our $NAME = 'Null'; +our $VERSION = '1.0'; + +=head2 filter + + my $newrecord = $filter->filter($record); + my $newrecords = $filter->filter(\@records); + +Return the original record. + +=cut +sub filter { + my $self = shift; + my $record = shift; + + return $record; +} diff --git a/Koha/RecordProcessor.pm b/Koha/RecordProcessor.pm new file mode 100644 index 0000000..3d4e1bf --- /dev/null +++ b/Koha/RecordProcessor.pm @@ -0,0 +1,183 @@ +package Koha::RecordProcessor; + +# Copyright 2012 C & P Bibliography Services +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +=head1 NAME + +Koha::RecordProcessor - Dispatcher class for record normalization + +=head1 SYNOPSIS + + use Koha::RecordProcessor; + my $normalizer = Koha::RecordProcessor(%params); + $normalizer->process($record) + +=head1 DESCRIPTION + +Dispatcher class for record normalization. RecordProcessors must +extend Koha::RecordProcessor::Base, be in the Koha::Filter namespace, +and provide the following methods: + +B - apply the filter and return the result. $record +may be either a scalar or an arrayref, and the return result will be +the same type. + +These methods may be overriden: + +B - initialize the filter + +B - destroy the filter + +These methods should not be overridden unless you are very sure of what +you are doing: + +B - create a new filter object + +Note that the RecordProcessor will not clone the record that is +passed in. If you do not want to change the original MARC::Record +object (or whatever type of object you are passing in), you must +clone it I to passing it off to the RecordProcessor. + +=head1 FUNCTIONS + +=cut + +use strict; +use warnings; +use Module::Load::Conditional qw(can_load); +use Module::Pluggable::Object; + +use base qw(Class::Accessor); + +__PACKAGE__->mk_accessors(qw( schema filters options record )); + +=head2 new + + my $normalizer = Koha::RecordProcessor->new(%params); + +Create a new normalizer. Available parameters are: + +=over 8 + +=item B + +Which metadata schema is in use. At the moment the only supported schema +is 'MARC'. + +=item B + +What filter(s) to use. This must be an arrayref to a list of filters. Filters +can be specified either with a complete class path, or, if they are in the +Koha::Filter::${schema} namespace, as only the filter name, and +"Koha::Filter::${schema}" will be prepended to it before the filter is loaded. + +=back + +=cut +sub new { + my $class = shift; + my $param = shift; + + + my $schema = $param->{schema} || 'MARC'; + my $options = $param->{options} || ''; + my @filters = ( ); + + foreach my $filter ($param->{filters}) { + next unless $filter; + my $filter_module = $filter =~ m/:/ ? $filter : "Koha::Filter::${schema}::${filter}"; + if (can_load( modules => { $filter_module => undef } )) { + my $object = $filter_module->new(); + $filter_module->initialize($param); + push @filters, $object; + } + } + + my $self = $class->SUPER::new( { schema => $schema, + filters => \@filters, + options => $options }); + bless $self, $class; + return $self; +} + +=head2 bind + + $normalizer->bind($record) + +Bind a normalizer to a particular record. + +=cut +sub bind { + my $self = shift; + my $record = shift; + + $self->{record} = $record; + return; +} + +=head2 process + + my $newrecord = $normalizer->process([$record]) + +Run the record(s) through the normalization pipeline. If $record is +not specified, process the record the normalizer is bound to. +Note that $record may be either a scalar or an arrayref, and the +return value will be of the same type. + +=cut +sub process { + my $self = shift; + my $record = shift || $self->record; + + return unless defined $record; + + my $newrecord = $record; + + foreach my $filterobj (@{$self->filters}) { + next unless $filterobj; + $newrecord = $filterobj->filter($newrecord); + } + + return $newrecord; +} + +sub DESTROY { + my $self = shift; + + foreach my $filterobj (@{$self->filters}) { + $filterobj->destroy(); + } +} + +=head2 AvailableFilters + + my @available_filters = Koha::RecordProcessor::AvailableFilters([$schema]); + +Get a list of available filters. Optionally specify the metadata schema. +At present only MARC is supported as a schema. + +=cut +sub AvailableFilters { + my $schema = pop || ''; + my $path = 'Koha::Filter'; + $path .= "::$schema" if ($schema eq 'MARC'); + my $finder = Module::Pluggable::Object->new(search_path => $path); + return $finder->plugins; +} + +1; diff --git a/Koha/RecordProcessor/Base.pm b/Koha/RecordProcessor/Base.pm new file mode 100644 index 0000000..00bc623 --- /dev/null +++ b/Koha/RecordProcessor/Base.pm @@ -0,0 +1,133 @@ +package Koha::RecordProcessor::Base; + +# Copyright 2012 C & P Bibliography Services +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +=head1 NAME + +Koha::RecordProcessor::Base - Base class for RecordProcessor filters + +=head1 SYNOPSIS + + use base qw(Koha::RecordProcessor::Base); + +=head1 DESCRIPTION + +Base class for record normalizer filters. RecordProcessors must +provide the following methods: + +B - apply the filter and return the result. $record +may be either a scalar or an arrayref, and the return result will be +the same type. + +The following variables must be defined in each filter: + our $NAME ='Filter'; + our $VERSION = '1.0'; + +These methods may be overriden: + +B - initialize the filter + +B - destroy the filter + +These methods should not be overridden unless you are very sure of what +you are doing: + +B - create a new filter object + +Note that the RecordProcessor will not clone the record that is +passed in. If you do not want to change the original MARC::Record +object (or whatever type of object you are passing in), you must +clone it I to passing it off to the RecordProcessor. + +=head1 FUNCTIONS + +=cut + +use strict; +use warnings; + +use base qw(Class::Accessor); + +__PACKAGE__->mk_ro_accessors(qw( name version )); +__PACKAGE__->mk_accessors(qw( params )); +our $NAME = 'Base'; +our $VERSION = '1.0'; + + +=head2 new + + my $filter = Koha::RecordProcessor::Base->new; + +Create a new filter; + +=cut +sub new { + my $class = shift; + + my $self = $class->SUPER::new( { });#name => $class->NAME, + #version => $class->VERSION }); + + bless $self, $class; + return $self; +} + + +=head2 initialize + + $filter->initalize(%params); + +Initialize a filter using the specified parameters. + +=cut +sub initialize { + my $self = shift; + my $params = shift; + + #$self->params = $params; + + return $self; +} + + +=head2 destroy + + $filter->destroy(); + +Destroy the filter. + +=cut +sub destroy { + my $self = shift; + return; +} + +=head2 filter + + my $newrecord = $filter->filter($record); + my $newrecords = $filter->filter(\@records); + +Filter the specified record(s) and return the result. + +=cut +sub filter { + my $self = shift; + my $record = shift; + return $record; +} + +1; diff --git a/Koha/SearchEngine/Solr/Index.pm b/Koha/SearchEngine/Solr/Index.pm index fd7c3d7..6fa3242 100644 --- a/Koha/SearchEngine/Solr/Index.pm +++ b/Koha/SearchEngine/Solr/Index.pm @@ -9,6 +9,7 @@ use List::MoreUtils qw(uniq); use Koha::SearchEngine::Solr; use C4::AuthoritiesMarc; use C4::Biblio; +use Koha::RecordProcessor; has searchengine => ( is => 'rw', @@ -39,6 +40,11 @@ sub index_record { $record = GetAuthority( $id ) if $recordtype eq "authority"; $record = GetMarcBiblio( $id ) if $recordtype eq "biblio"; + if ($record_type eq 'biblio' && C4::Context->preference('IncludeSeeFromInSearches')) { + my $normalizer = Koha::RecordProcessor->new( { filters => 'EmbedSeeFromHeadings' } ); + $record = $normalizer->process($record); + } + next unless ( $record ); my $index_values = { diff --git a/installer/data/mysql/sysprefs.sql b/installer/data/mysql/sysprefs.sql index f5a18b2..54981b3 100644 --- a/installer/data/mysql/sysprefs.sql +++ b/installer/data/mysql/sysprefs.sql @@ -374,3 +374,4 @@ INSERT INTO systempreferences (variable,value,options,explanation,type) VALUES ( INSERT INTO systempreferences (variable,value,explanation,type) VALUES('EnableBorrowerFiles','0','If enabled, allows librarians to upload and attach arbitrary files to a borrower record.','YesNo'); INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('UpdateTotalIssuesOnCirc','0','Whether to update the totalissues field in the biblio on each circ.',NULL,'YesNo'); INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('IntranetSlipPrinterJS','','Use this JavaScript for printing slips. Define at least function printThenClose(). For use e.g. with Firefox PlugIn jsPrintSetup, see http://jsprintsetup.mozdev.org/','','Free'); +INSERT INTO systempreferences (variable,value,options,explanation,type) VALUES ('IncludeSeeFromInSearches','0','','Include see-from references in searches.','YesNo'); diff --git a/installer/data/mysql/updatedatabase.pl b/installer/data/mysql/updatedatabase.pl index ed0c674..39108be 100755 --- a/installer/data/mysql/updatedatabase.pl +++ b/installer/data/mysql/updatedatabase.pl @@ -5635,6 +5635,15 @@ if(C4::Context->preference("Version") < TransformToNum($DBversion) ) { SetVersion($DBversion); } + +$DBversion = "3.09.00.XXX"; +if (C4::Context->preference("Version") < TransformToNum($DBversion)) { + $dbh->do("INSERT INTO systempreferences (variable,value,options,explanation,type) VALUES ('IncludeSeeFromInSearches','0','','Include see-from references in searches.','YesNo');"); + print "Upgrade to $DBversion done (Add IncludeSeeFromInSearches system preference)\n"; + SetVersion ($DBversion); +} + + =head1 FUNCTIONS =head2 TableExists($table) 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 e324680..e35159f 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 @@ -76,6 +76,13 @@ Searching: yes: Using no: "Not using" - 'ICU Zebra indexing. Please note: This setting will not affect Zebra indexing, it should only be used to tell Koha that you have activated ICU indexing if you have actually done so, since there is no way for Koha to figure this out on its own.' + - + - pref: IncludeSeeFromInSearches + default: 0 + choices: + yes: Include + no: "Don't include" + - "see from (non-preferred form) headings in bibliographic searches. Please note: you will need to reindex your bibliographic database when changing this preference." Search Form: - - Show tabs in OPAC and staff-side advanced search for limiting searches on the diff --git a/koha-tmpl/intranet-tmpl/prog/en/xslt/MARC21slim2intranetResults.xsl b/koha-tmpl/intranet-tmpl/prog/en/xslt/MARC21slim2intranetResults.xsl index b4c4cde..e5683cb 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/xslt/MARC21slim2intranetResults.xsl +++ b/koha-tmpl/intranet-tmpl/prog/en/xslt/MARC21slim2intranetResults.xsl @@ -336,7 +336,7 @@

by - + @@ -351,7 +351,7 @@ . ; - + @@ -365,7 +365,7 @@ ; - + diff --git a/koha-tmpl/intranet-tmpl/prog/en/xslt/NORMARCslim2intranetResults.xsl b/koha-tmpl/intranet-tmpl/prog/en/xslt/NORMARCslim2intranetResults.xsl index 5721e49..ed33e1b 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/xslt/NORMARCslim2intranetResults.xsl +++ b/koha-tmpl/intranet-tmpl/prog/en/xslt/NORMARCslim2intranetResults.xsl @@ -283,7 +283,7 @@

av - + @@ -298,7 +298,7 @@ . ; - + @@ -312,7 +312,7 @@ ; - + diff --git a/koha-tmpl/opac-tmpl/prog/en/xslt/MARC21slim2OPACResults.xsl b/koha-tmpl/opac-tmpl/prog/en/xslt/MARC21slim2OPACResults.xsl index 2b5aacc..c23a987 100644 --- a/koha-tmpl/opac-tmpl/prog/en/xslt/MARC21slim2OPACResults.xsl +++ b/koha-tmpl/opac-tmpl/prog/en/xslt/MARC21slim2OPACResults.xsl @@ -449,7 +449,7 @@ by - + . @@ -460,7 +460,7 @@ - + -- @@ -476,7 +476,7 @@ - + -- diff --git a/koha-tmpl/opac-tmpl/prog/en/xslt/NORMARCslim2OPACResults.xsl b/koha-tmpl/opac-tmpl/prog/en/xslt/NORMARCslim2OPACResults.xsl index c6b700a..8f17aff 100644 --- a/koha-tmpl/opac-tmpl/prog/en/xslt/NORMARCslim2OPACResults.xsl +++ b/koha-tmpl/opac-tmpl/prog/en/xslt/NORMARCslim2OPACResults.xsl @@ -324,7 +324,7 @@ av - + . @@ -335,7 +335,7 @@ - + . @@ -346,7 +346,7 @@ - + . diff --git a/misc/maintenance/process_record_through_filter.pl b/misc/maintenance/process_record_through_filter.pl new file mode 100755 index 0000000..3b3e537 --- /dev/null +++ b/misc/maintenance/process_record_through_filter.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl + +# This script is intended for testing RecordProcessor filters. To use it +# run the script like so: +# > perl process_record_through_filter.pl ${BIBLIONUMBER} ${FILTER} + +use strict; +use warnings; +use Koha::RecordProcessor; +use Data::Dumper; +use C4::Biblio; + +my $record = GetMarcBiblio($ARGV[0]); + +print "Before: " . $record->as_formatted() . "\n"; +my $processor = Koha::RecordProcessor->new( { filters => ( $ARGV[1] ) }); +$record = $processor->process($record); +print "After : " . $record->as_formatted() . "\n"; diff --git a/misc/migration_tools/rebuild_zebra.pl b/misc/migration_tools/rebuild_zebra.pl index 1621e84..e11817d 100755 --- a/misc/migration_tools/rebuild_zebra.pl +++ b/misc/migration_tools/rebuild_zebra.pl @@ -10,6 +10,7 @@ use File::Path; use C4::Biblio; use C4::AuthoritiesMarc; use C4::Items; +use Koha::RecordProcessor; # # script that checks zebradir structure & create directories & mandatory files if needed @@ -497,6 +498,9 @@ sub get_corrected_marc_record { fix_leader($marc); if ($record_type eq 'authority') { fix_authority_id($marc, $record_number); + } elsif ($record_type eq 'biblio' && C4::Context->preference('IncludeSeeFromInSearches')) { + my $normalizer = Koha::RecordProcessor->new( { filters => 'EmbedSeeFromHeadings' } ); + $marc = $normalizer->process($marc); } if (C4::Context->preference("marcflavour") eq "UNIMARC") { fix_unimarc_100($marc); diff --git a/t/RecordProcessor.t b/t/RecordProcessor.t new file mode 100755 index 0000000..32acc6b --- /dev/null +++ b/t/RecordProcessor.t @@ -0,0 +1,80 @@ +#!/usr/bin/perl + +# Copyright 2012 C & P Bibliography Services +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use strict; +use warnings; +use File::Spec; +use MARC::Record; + +use Test::More; + +BEGIN { + use_ok('Koha::RecordProcessor'); +} + +my $isbn = '0590353403'; +my $title = 'Foundation'; +my $marc_record=MARC::Record->new; +my $field = MARC::Field->new('020','','','a' => $isbn); +$marc_record->append_fields($field); +$field = MARC::Field->new('245','','','a' => $title); +$marc_record->append_fields($field); + + +my $filterdir = File::Spec->rel2abs('Koha/Filter') . '/MARC'; + +opendir(my $dh, $filterdir); +my @installed_filters = map { ( /\.pm$/ && -f "$filterdir/$_" && s/\.pm$// ) ? "Koha::Filters::MARC::$_" : () } readdir($dh); +my @available_filters = Koha::RecordProcessor::AvailableFilters(); + +foreach my $filter (@installed_filters) { + ok(grep($filter, @available_filters), "Found filter $filter"); +} + +my $marc_filters = grep (/MARC/, @available_filters); +is(scalar Koha::RecordProcessor::AvailableFilters('MARC'), $marc_filters, 'Retrieved list of MARC filters'); + +my $processor = Koha::RecordProcessor->new( { filters => ( 'ABCD::EFGH::IJKL' ) } ); + +is(ref($processor), 'Koha::RecordProcessor', 'Created record processor with invalid filter'); + +is($processor->process($marc_record), $marc_record, 'Process record with empty processor'); + +$processor = Koha::RecordProcessor->new( { filters => ( 'Null' ) } ); +is(ref($processor->filters->[0]), 'Koha::Filter::MARC::Null', 'Created record processor with implicitly scoped Null filter'); + +$processor = Koha::RecordProcessor->new( { filters => ( 'Koha::Filter::MARC::Null' ) } ); +is(ref($processor->filters->[0]), 'Koha::Filter::MARC::Null', 'Created record processor with explicitly scoped Null filter'); + +is($processor->process($marc_record), $marc_record, 'Process record'); + +$processor->bind($marc_record); + +is($processor->record, $marc_record, 'Bound record to processor'); + +is($processor->process(), $marc_record, 'Filter bound record'); + +eval { + $processor = Koha::RecordProcessor->new( { filters => ( 'Koha::Filter::MARC::Null' ) } ); + undef $processor; +}; + +ok(!$@, 'Destroyed processor successfully'); + +done_testing(); diff --git a/t/db_dependent/Koha_Authority.t b/t/db_dependent/Koha_Authority.t new file mode 100755 index 0000000..dc57774 --- /dev/null +++ b/t/db_dependent/Koha_Authority.t @@ -0,0 +1,66 @@ +#!/usr/bin/perl + +# Copyright 2012 C & P Bibliography Services +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use strict; +use warnings; + +use C4::Context; +use Test::More; + +BEGIN { + use_ok('Koha::Authority'); +} + +my $record = MARC::Record->new; + +$record->add_fields( + [ '001', '1234' ], + [ '150', ' ', ' ', a => 'Cooking' ], + [ '450', ' ', ' ', a => 'Cookery' ], + ); +my $authority = Koha::Authority->new($record); + +is(ref($authority), 'Koha::Authority', 'Created valid Koha::Authority object'); + +is_deeply($authority->record, $record, 'Saved record'); + +SKIP: +{ + my $dbh = C4::Context->dbh; + my $sth = $dbh->prepare("SELECT authid FROM auth_header LIMIT 1;"); + $sth->execute(); + + my $authid; + for my $row ($sth->fetchrow_hashref) { + $authid = $row->{'authid'}; + } + skip 'No authorities', 3 unless $authid; + $authority = Koha::Authority->get_from_authid($authid); + + is(ref($authority), 'Koha::Authority', 'Retrieved valid Koha::Authority object'); + + is($authority->authid, $authid, 'Object authid is correct'); + + is($authority->record->field('001')->data(), $authid, 'Retrieved correct record'); + + $authority = Koha::Authority->get_from_authid('alphabetsoup'); + is($authority, undef, 'No invalid record is retrieved'); +} + +done_testing(); diff --git a/t/db_dependent/RecordProcessor_EmbedSeeFromHeadings.t b/t/db_dependent/RecordProcessor_EmbedSeeFromHeadings.t new file mode 100755 index 0000000..4742983 --- /dev/null +++ b/t/db_dependent/RecordProcessor_EmbedSeeFromHeadings.t @@ -0,0 +1,66 @@ +#!/usr/bin/perl + +# Copyright 2012 C & P Bibliography Services +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use strict; +use warnings; +use File::Spec; +use MARC::Record; +use Koha::Authority; + +use Test::More; +use Test::MockModule; + +BEGIN { + use_ok('Koha::RecordProcessor'); +} + +my $module = new Test::MockModule('MARC::Record'); +$module->mock('new_from_xml', sub { + my $record = MARC::Record->new; + + $record->add_fields( + [ '001', '1234' ], + [ '150', ' ', ' ', a => 'Cooking' ], + [ '450', ' ', ' ', a => 'Cookery' ], + ); + + return $record; +}); + +my $bib = MARC::Record->new; +$bib->add_fields( + [ '245', '0', '4', a => 'The Ifrane cookbook' ], + [ '650', ' ', ' ', a => 'Cooking', 9 => '1234' ] + ); + +my $resultbib = MARC::Record->new; +$resultbib->add_fields( + [ '245', '0', '4', a => 'The Ifrane cookbook' ], + [ '650', ' ', ' ', a => 'Cooking', 9 => '1234' ], + [ '650', 'z', ' ', a => 'Cookery' ] + ); + +my $processor = Koha::RecordProcessor->new( { filters => ( 'EmbedSeeFromHeadings' ) } ); +is(ref($processor), 'Koha::RecordProcessor', 'Created record processor'); + +my $result = $processor->process($bib); + +is_deeply($result, $resultbib, 'Inserted see-from heading to record'); + +done_testing(); -- 1.7.9.5