use C4::Context;
use C4::Biblio; # GetMarcFromKohaField, GetBiblioData
use C4::Koha; # getFacets
+use Koha::DateUtils;
+use Koha::Libraries;
use Lingua::Stem;
use C4::Search::PazPar2;
use XML::Simple;
-use C4::Dates qw(format_date);
-use C4::Members qw(GetHideLostItemsPreference);
use C4::XSLT;
-use C4::Branch;
use C4::Reserves; # GetReserveStatus
use C4::Debug;
use C4::Charset;
+use Koha::AuthorisedValues;
+use Koha::ItemTypes;
+use Koha::Libraries;
+use Koha::Patrons;
use YAML;
use URI::Escape;
use Business::ISBN;
use MARC::Record;
use MARC::Field;
-use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $DEBUG);
+use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $DEBUG);
-# set the version for version checking
BEGIN {
- $VERSION = 3.07.00.049;
$DEBUG = ($ENV{DEBUG}) ? 1 : 0;
}
&buildQuery
&GetDistinctValues
&enabled_staff_search_views
- &PurgeSearchHistory
);
# make all your functions, whether exported or not;
sub FindDuplicate {
my ($record) = @_;
my $dbh = C4::Context->dbh;
- my $result = TransformMarcToKoha( $dbh, $record, '' );
+ my $result = TransformMarcToKoha( $record, '' );
my $sth;
my $query;
my $search;
$possible_duplicate_record
);
- my $result = TransformMarcToKoha( $dbh, $marcrecord, '' );
+ my $result = TransformMarcToKoha( $marcrecord, '' );
# FIXME :: why 2 $biblionumber ?
if ($result) {
=head2 SimpleSearch
-( $error, $results, $total_hits ) = SimpleSearch( $query, $offset, $max_results, [@servers] );
+( $error, $results, $total_hits ) = SimpleSearch( $query, $offset, $max_results, [@servers], [%options] );
This function provides a simple search API on the bibliographic catalog
* @servers is optional. Defaults to biblioserver as found in koha-conf.xml
* $offset - If present, represents the number of records at the beginning to omit. Defaults to 0
* $max_results - if present, determines the maximum number of records to fetch. undef is All. defaults to undef.
+ * %options is optional. (e.g. "skip_normalize" allows you to skip changing : to = )
=item C<Return:>
for my $r ( @{$marcresults} ) {
my $marcrecord = MARC::File::USMARC::decode($r);
- my $biblio = TransformMarcToKoha(C4::Context->dbh,$marcrecord,q{});
+ my $biblio = TransformMarcToKoha($marcrecord,q{});
#build the iarray of hashs for the template.
push @results, {
=cut
sub SimpleSearch {
- my ( $query, $offset, $max_results, $servers ) = @_;
+ my ( $query, $offset, $max_results, $servers, %options ) = @_;
return ( 'No query entered', undef, undef ) unless $query;
# FIXME hardcoded value. See catalog/search.pl & opac-search.pl too.
eval {
$zconns[$i] = C4::Context->Zconn( $servers[$i], 1 );
if ($QParser) {
- $query =~ s/=/:/g;
+ $query =~ s/=/:/g unless $options{skip_normalize};
$QParser->parse( $query );
$query = $QParser->target_syntax($servers[$i]);
$zoom_queries[$i] = new ZOOM::Query::PQF( $query, $zconns[$i]);
} else {
- $query =~ s/:/=/g;
+ $query =~ s/:/=/g unless $options{skip_normalize};
$zoom_queries[$i] = new ZOOM::Query::CCL2RPN( $query, $zconns[$i]);
}
$tmpresults[$i] = $zconns[$i]->search( $zoom_queries[$i] );
my @servers = @$servers_ref;
my @sort_by = @$sort_by_ref;
+ $offset = 0 if $offset < 0;
# Initialize variables for the ZOOM connection and results object
my $zconn;
my @results;
my $results_hashref = ();
+ # TODO simplify this structure ( { branchcode => $branchname } is enought) and remove this parameter
+ $branches ||= { map { $_->branchcode => { branchname => $_->branchname } } Koha::Libraries->search };
+
# Initialize variables for the faceted results objects
my $facets_counter = {};
my $facets_info = {};
{
$facet_label_value =
$itemtypes->{$one_facet}
- ->{'description'};
+ ->{translated_description};
}
}
# also, if it's a location code, use the name instead of the code
if ( $link_value =~ /location/ ) {
- $facet_label_value =
- GetKohaAuthorisedValueLib( 'LOC',
- $one_facet, $opac );
+ # TODO Retrieve all authorised values at once, instead of 1 query per entry
+ my $av = Koha::AuthorisedValues->search({ category => 'LOC', authorised_value => $one_facet });
+ $facet_label_value = $av->count ? $av->next->opac_description : '';
+ }
+
+ # also, if it's a collection code, use the name instead of the code
+ if ( $link_value =~ /ccode/ ) {
+ # TODO Retrieve all authorised values at once, instead of 1 query per entry
+ my $av = Koha::AuthorisedValues->search({ category => 'CCODE', authorised_value => $one_facet });
+ $facet_label_value = $av->count ? $av->next->opac_description : '';
}
# but we're down with the whole label being in the link's title.
$facets_info->{$link_value}->{'label_value'} =~
/Libraries/
)
- and ( C4::Context->preference('singleBranchMode') )
+ and ( Koha::Libraries->search->count == 1 )
);
}
}
}
);
+
+ # This sorts the facets into alphabetical order
+ if (@facets_loop) {
+ foreach my $f (@facets_loop) {
+ $f->{facets} = [ sort { uc($a->{facet_label_value}) cmp uc($b->{facet_label_value}) } @{ $f->{facets} } ];
+ }
+ @facets_loop = sort {$a->{expand} cmp $b->{expand}} @facets_loop;
+ }
+
return ( undef, $results_hashref, \@facets_loop );
}
my $rs = shift;
my $facets;
- my $indexing_mode = C4::Context->config('zebra_bib_index_mode') // 'dom';
my $use_zebra_facets = C4::Context->config('use_zebra_facets') // 0;
- if ( $indexing_mode eq 'dom' &&
- $use_zebra_facets ) {
+ if ( $use_zebra_facets ) {
$facets = _get_facets_from_zebra( $rs );
} else {
$facets = _get_facets_from_records( $rs );
$results_per_page, $offset, $expanded_facet, $branches,
$query_type, $scan
) = @_;
+ #NOTE: Parameter $branches is not used here !
my $paz = C4::Search::PazPar2->new(C4::Context->config('pazpar2url'));
$paz->init();
return ( undef, $results_hashref, \@facets_loop );
}
-# STOPWORDS
-sub _remove_stopwords {
- my ( $operand, $index ) = @_;
- my @stopwords_removed;
-
- # phrase and exact-qualified indexes shouldn't have stopwords removed
- if ( $index !~ m/,(phr|ext)/ ) {
-
-# remove stopwords from operand : parse all stopwords & remove them (case insensitive)
-# we use IsAlpha unicode definition, to deal correctly with diacritics.
-# otherwise, a French word like "leçon" would be split into "le" "çon", "le"
-# is a stopword, we'd get "çon" and wouldn't find anything...
-#
- foreach ( keys %{ C4::Context->stopwords } ) {
- next if ( $_ =~ /(and|or|not)/ ); # don't remove operators
- if ( my ($matched) = ($operand =~
- /([^\X\p{isAlnum}]\Q$_\E[^\X\p{isAlnum}]|[^\X\p{isAlnum}]\Q$_\E$|^\Q$_\E[^\X\p{isAlnum}])/gi))
- {
- $operand =~ s/\Q$matched\E/ /gi;
- push @stopwords_removed, $_;
- }
- }
- }
- return ( $operand, \@stopwords_removed );
-}
-
# TRUNCATION
sub _detect_truncation {
my ( $operand, $index ) = @_;
require Lingua::Stem::Snowball ;
my $stemmed_operand=q{};
+ # Stemmer needs language
+ return $operand unless $lang;
+
# If operand contains a digit, it is almost certainly an identifier, and should
# not be stemmed. This is particularly relevant for ISBNs and ISSNs, which
# can contain the letter "X" - for example, _build_stemmend_operand would reduce
$weighted_query .= "an=\"$operand\"";
}
+ # If the index is numeric, don't autoquote it.
+ elsif ( $index =~ /,st-numeric$/ ) {
+ $weighted_query .= " $index=$operand";
+ }
+
# If the index already has more than one qualifier, wrap the operand
# in quotes and pass it back (assumption is that the user knows what they
# are doing and won't appreciate us mucking up their query
'an',
'Any',
'at',
+ 'arl',
+ 'arp',
'au',
'aub',
'aud',
'date-entered-on-file',
'Date-of-acquisition',
'Date-of-publication',
+ 'Date-time-last-modified',
'Dewey-classification',
'Dissertation-information',
'diss',
+ 'dtlm',
'EAN',
'extent',
'fic',
'Heading-use-subject-added-entry',
'Host-item',
'id-other',
+ 'ident',
+ 'Identifier-standard',
'Illustration-code',
'Index-term-genre',
'Index-term-uncontrolled',
+ 'Interest-age-level',
+ 'Interest-grade-level',
'ISBN',
'isbn',
'ISSN',
'LC-card-number',
'lcn',
'lex',
+ 'lexile-number',
'llength',
'ln',
'ln-audio',
'notes',
'ns',
'nt',
+ 'Other-control-number',
'pb',
'Personal-name',
'Personal-name-heading',
'popularity',
'pubdate',
'Publisher',
+ 'Provider',
+ 'pv',
+ 'Reading-grade-level',
'Record-control-number',
'rcn',
'Record-type',
( $operators, $operands, $indexes, $limits,
$sort_by, $scan, $lang ) =
- buildQuery ( $operators, $operands, $indexes, $limits, $sort_by, $scan, $lang);
+ parseQuery ( $operators, $operands, $indexes, $limits, $sort_by, $scan, $lang);
Shim function to ease the transition from buildQuery to a new QueryParser.
This function is called at the beginning of buildQuery, and modifies
buildQuery's input. If it can handle the input, it returns a query that
buildQuery will not try to parse.
+
=cut
sub parseQuery {
$simple_query, $query_cgi,
$query_desc, $limit,
$limit_cgi, $limit_desc,
-$stopwords_removed, $query_type ) = buildQuery ( $operators, $operands, $indexes, $limits, $sort_by, $scan, $lang);
+$query_type ) = buildQuery ( $operators, $operands, $indexes, $limits, $sort_by, $scan, $lang);
Build queries and limits in CCL, CGI, Human,
-handle truncation, stemming, field weighting, stopwords, fuzziness, etc.
+handle truncation, stemming, field weighting, fuzziness, etc.
See verbose embedded documentation.
my $auto_truncation = C4::Context->preference("QueryAutoTruncate") || 0;
my $weight_fields = C4::Context->preference("QueryWeightFields") || 0;
my $fuzzy_enabled = C4::Context->preference("QueryFuzzy") || 0;
- my $remove_stopwords = C4::Context->preference("QueryRemoveStopwords") || 0;
my $query = $operands[0];
my $simple_query = $operands[0];
my $limit_cgi;
my $limit_desc;
- my $stopwords_removed; # flag to determine if stopwords have been removed
-
my $cclq = 0;
my $cclindexes = getIndexes();
if ( $query !~ /\s*(ccl=|pqf=|cql=)/ ) {
# This is needed otherwise ccl= and &limit won't work together, and
# this happens when selecting a subject on the opac-detail page
@limits = grep {!/^$/} @limits;
+ my $original_q = $q; # without available part
+ unless ( grep { /^available$/ } @limits ) {
+ $q =~ s| and \( \( allrecords,AlwaysMatches:'' not onloan,AlwaysMatches:''\) and \(lost,st-numeric=0\) \)||;
+ $original_q = $q;
+ }
if ( @limits ) {
- $q .= ' and '.join(' and ', @limits);
+ if ( grep { /^available$/ } @limits ) {
+ $q .= q| and ( ( allrecords,AlwaysMatches:'' not onloan,AlwaysMatches:'') and (lost,st-numeric=0) )|;
+ delete $limits['available'];
+ }
+ $q .= ' and '.join(' and ', @limits) if @limits;
}
- return ( undef, $q, $q, "q=ccl=".uri_escape_utf8($q), $q, '', '', '', '', 'ccl' );
+ return ( undef, $q, $q, "q=ccl=".uri_escape_utf8($q), $original_q, '', '', '', 'ccl' );
}
if ( $query =~ /^cql=/ ) {
- return ( undef, $', $', "q=cql=".uri_escape_utf8($'), $', '', '', '', '', 'cql' );
+ return ( undef, $', $', "q=cql=".uri_escape_utf8($'), $', '', '', '', 'cql' );
}
if ( $query =~ /^pqf=/ ) {
if ($query_desc) {
$query_desc = $';
$query_cgi = "q=pqf=".uri_escape_utf8($');
}
- return ( undef, $', $', $query_cgi, $query_desc, '', '', '', '', 'pqf' );
+ return ( undef, $', $', $query_cgi, $query_desc, '', '', '', 'pqf' );
}
# pass nested queries directly
# return (
# undef, $query, $simple_query, $query_cgi,
# $query, $limit, $limit_cgi, $limit_desc,
-# $stopwords_removed, 'ccl'
+# 'ccl'
# );
# }
for ( my $i = 0 ; $i <= @operands ; $i++ ) {
# COMBINE OPERANDS, INDEXES AND OPERATORS
- if ( $operands[$i] ) {
+ if ( ($operands[$i] // '') ne '' ) {
$operands[$i]=~s/^\s+//;
# A flag to determine whether or not to add the index to the query
my $indexes_set;
-# If the user is sophisticated enough to specify an index, turn off field weighting, stemming, and stopword handling
+# If the user is sophisticated enough to specify an index, turn off field weighting, and stemming handling
if ( $operands[$i] =~ /\w(:|=)/ || $scan ) {
$weight_fields = 0;
$stemming = 0;
- $remove_stopwords = 0;
} else {
$operands[$i] =~ s/\?/{?}/g; # need to escape question marks
}
#which is processed higher up in this sub. Other than that, year searches are typically
#handled as limits which are not processed her either.
- # Date of Publication
- if ( $index =~ /yr/ ) {
+ # Search ranges: Date of Publication, st-numeric
+ if ( $index =~ /(yr|st-numeric)/ ) {
#weight_fields/relevance search causes errors with date ranges
#In the case of YYYY-, it will only return records with a 'yr' of YYYY (not the range)
#In the case of YYYY-YYYY, it will return no results
- $stemming = $auto_truncation = $weight_fields = $fuzzy_enabled = $remove_stopwords = 0;
+ $stemming = $auto_truncation = $weight_fields = $fuzzy_enabled = 0;
}
# Date of Acquisition
#top of the results just because they have lots of item records matching that date.
#Fuzzy actually only applies during _build_weighted_query, and is reset there anyway, so
#irrelevant here
- #remove_stopwords doesn't function anymore so is irrelevant
- $stemming = $auto_truncation = $weight_fields = $fuzzy_enabled = $remove_stopwords = 0;
+ $stemming = $auto_truncation = $weight_fields = $fuzzy_enabled = 0;
}
# ISBN,ISSN,Standard Number, don't need special treatment
- elsif ( $index eq 'nb' || $index eq 'ns' ) {
+ elsif ( $index eq 'nb' || $index eq 'ns' || $index eq 'hi' ) {
(
$stemming, $auto_truncation,
- $weight_fields, $fuzzy_enabled,
- $remove_stopwords
- ) = ( 0, 0, 0, 0, 0 );
+ $weight_fields, $fuzzy_enabled
+ ) = ( 0, 0, 0, 0 );
if ( $index eq 'nb' ) {
if ( C4::Context->preference("SearchWithISBNVariations") ) {
my $index_plus = $index . $struct_attr . ':';
my $index_plus_comma = $index . $struct_attr . ',';
- # Remove Stopwords
- if ($remove_stopwords) {
- ( $operand, $stopwords_removed ) =
- _remove_stopwords( $operand, $index );
- warn "OPERAND w/out STOPWORDS: >$operand<" if $DEBUG;
- warn "REMOVED STOPWORDS: @$stopwords_removed"
- if ( $stopwords_removed && $DEBUG );
- }
-
if ($auto_truncation){
unless ( $index =~ /,(st-|phr|ext)/ ) {
#FIXME only valid with LTR scripts
query_desc => $query_desc,
operator => ($operators[ $i - 1 ]) ? $operators[ $i - 1 ] : '',
parsed_operand => $operand,
- original_operand => ($operands[$i]) ? $operands[$i] : '',
+ original_operand => $operands[$i] // '',
index => $index,
index_plus => $index_plus,
indexes_set => $indexes_set,
$limit_cgi .= "&limit=" . uri_escape_utf8($this_limit);
if ($this_limit =~ /^branch:(.+)/) {
my $branchcode = $1;
- my $branchname = GetBranchName($branchcode);
- if (defined $branchname) {
- $limit_desc .= " branch:$branchname";
+ my $library = Koha::Libraries->find( $branchcode );
+ if (defined $library) {
+ $limit_desc .= " branch:" . $library->branchname;
} else {
$limit_desc .= " $this_limit";
}
warn "LIMIT DESC:" . $limit_desc;
warn "---------\nLeave buildQuery\n---------";
}
+
return (
undef, $query, $simple_query, $query_cgi,
$query_desc, $limit, $limit_cgi, $limit_desc,
- $stopwords_removed, $query_type
+ $query_type
);
}
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;
}
#Build branchnames hash
- #find branchname
- #get branch information.....
- my %branches;
- my $bsth =$dbh->prepare("SELECT branchcode,branchname FROM branches"); # FIXME : use C4::Branch::GetBranches
- $bsth->execute();
- while ( my $bdata = $bsth->fetchrow_hashref ) {
- $branches{ $bdata->{'branchcode'} } = $bdata->{'branchname'};
- }
+ my %branches = map { $_->branchcode => $_->branchname } Koha::Libraries->search({}, { order_by => 'branchname' });
+
# FIXME - We build an authorised values hash here, using the default framework
# though it is possible to have different authvals for different fws.
- my $shelflocations =GetKohaAuthorisedValues('items.location','');
+ my $shelflocations =
+ { map { $_->{authorised_value} => $_->{lib} } Koha::AuthorisedValues->get_descriptions_by_koha_field( { frameworkcode => '', kohafield => 'items.location' } ) };
# get notforloan authorised value list (see $shelflocations FIXME)
- my $notforloan_authorised_value = GetAuthValCode('items.notforloan','');
-
- #Build itemtype hash
- #find itemtype & itemtype image
- my %itemtypes;
- $bsth =
- $dbh->prepare(
- "SELECT itemtype,description,imageurl,summary,notforloan FROM itemtypes"
- );
- $bsth->execute();
- while ( my $bdata = $bsth->fetchrow_hashref ) {
- foreach (qw(description imageurl summary notforloan)) {
- $itemtypes{ $bdata->{'itemtype'} }->{$_} = $bdata->{$_};
- }
- }
+ my $av = Koha::MarcSubfieldStructures->search({ frameworkcode => '', kohafield => 'items.notforloan', authorised_value => [ -and => {'!=' => undef }, {'!=' => ''}] });
+ my $notforloan_authorised_value = $av->count ? $av->next->authorised_value : undef;
+
+ #Get itemtype hash
+ my $itemtypes = Koha::ItemTypes->search_with_localization;
+ my %itemtypes = map { $_->{itemtype} => $_ } @{ $itemtypes->unblessed };
#search item field code
my ($itemtag, undef) = &GetMarcFromKohaField( "items.itemnumber", "" );
## find column names of items related to MARC
- my $sth2 = $dbh->prepare("SHOW COLUMNS FROM items");
- $sth2->execute;
my %subfieldstosearch;
- while ( ( my $column ) = $sth2->fetchrow ) {
+ my @columns = Koha::Database->new()->schema()->resultset('Item')->result_source->columns;
+ for my $column ( @columns ) {
my ( $tagfield, $tagsubfield ) =
&GetMarcFromKohaField( "items." . $column, "" );
if ( defined $tagsubfield ) {
# We get the biblionumber position in MARC
my ($bibliotag,$bibliosubf)=GetMarcFromKohaField('biblio.biblionumber','');
+ # set stuff for XSLT processing here once, not later again for every record we retrieved
+ my $interface = $is_opac ? 'OPAC' : '';
+ my $xslsyspref = $interface . "XSLTResultsDisplay";
+ my $xslfile = C4::Context->preference($xslsyspref);
+ my $lang = $xslfile ? C4::Languages::getlanguage() : undef;
+ my $sysxml = $xslfile ? C4::XSLT::get_xslt_sysprefs() : undef;
+
# loop through all of the records we've retrieved
for ( my $i = $offset ; $i <= $times - 1 ; $i++ ) {
: GetFrameworkCode($marcrecord->subfield($bibliotag,$bibliosubf));
SetUTF8Flag($marcrecord);
- my $oldbiblio = TransformMarcToKoha( $dbh, $marcrecord, $fw );
+ my $oldbiblio = TransformMarcToKoha( $marcrecord, $fw );
$oldbiblio->{subtitle} = GetRecordValue('subtitle', $marcrecord, $fw);
$oldbiblio->{result_number} = $i + 1;
# add imageurl to itemtype if there is one
$oldbiblio->{imageurl} = getitemtypeimagelocation( $search_context, $itemtypes{ $oldbiblio->{itemtype} }->{imageurl} );
- $oldbiblio->{'authorised_value_images'} = ($search_context eq 'opac' && C4::Context->preference('AuthorisedValueImages')) || ($search_context eq 'intranet' && C4::Context->preference('StaffAuthorisedValueImages')) ? C4::Items::get_authorised_value_images( C4::Biblio::get_biblio_authorised_values( $oldbiblio->{'biblionumber'}, $marcrecord ) ) : [];
+ $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);
# edition information, if any
$oldbiblio->{edition} = $oldbiblio->{editionstatement};
- $oldbiblio->{description} = $itemtypes{ $oldbiblio->{itemtype} }->{description};
+ $oldbiblio->{description} = $itemtypes{ $oldbiblio->{itemtype} }->{translated_description};
# Build summary if there is one (the summary is defined in the itemtypes table)
# FIXME: is this used anywhere, I think it can be commented out? -- JF
if ( $itemtypes{ $oldbiblio->{itemtype} }->{summary} ) {
# Pull out the items fields
my @fields = $marcrecord->field($itemtag);
my $marcflavor = C4::Context->preference("marcflavour");
+
# adding linked items that belong to host records
- my $analyticsfield = '773';
- if ($marcflavor eq 'MARC21' || $marcflavor eq 'NORMARC') {
- $analyticsfield = '773';
- } elsif ($marcflavor eq 'UNIMARC') {
- $analyticsfield = '461';
- }
- foreach my $hostfield ( $marcrecord->field($analyticsfield)) {
- my $hostbiblionumber = $hostfield->subfield("0");
- my $linkeditemnumber = $hostfield->subfield("9");
- if(!$hostbiblionumber eq undef){
- my $hostbiblio = GetMarcBiblio($hostbiblionumber, 1);
- my ($itemfield, undef) = GetMarcFromKohaField( 'items.itemnumber', GetFrameworkCode($hostbiblionumber) );
- if(!$hostbiblio eq undef){
- my @hostitems = $hostbiblio->field($itemfield);
- foreach my $hostitem (@hostitems){
- if ($hostitem->subfield("9") eq $linkeditemnumber){
- my $linkeditem =$hostitem;
- # append linked items if they exist
- if (!$linkeditem eq undef){
- push (@fields, $linkeditem);}
+ if ( C4::Context->preference('EasyAnalyticalRecords') ) {
+ my $analyticsfield = '773';
+ if ($marcflavor eq 'MARC21' || $marcflavor eq 'NORMARC') {
+ $analyticsfield = '773';
+ } elsif ($marcflavor eq 'UNIMARC') {
+ $analyticsfield = '461';
+ }
+ foreach my $hostfield ( $marcrecord->field($analyticsfield)) {
+ my $hostbiblionumber = $hostfield->subfield("0");
+ my $linkeditemnumber = $hostfield->subfield("9");
+ if( $hostbiblionumber ) {
+ my $hostbiblio = GetMarcBiblio({
+ biblionumber => $hostbiblionumber,
+ embed_items => 1 });
+ my ($itemfield, undef) = GetMarcFromKohaField( 'items.itemnumber', GetFrameworkCode($hostbiblionumber) );
+ if( $hostbiblio ) {
+ my @hostitems = $hostbiblio->field($itemfield);
+ foreach my $hostitem (@hostitems){
+ if ($hostitem->subfield("9") eq $linkeditemnumber){
+ my $linkeditem =$hostitem;
+ # append linked items if they exist
+ push @fields, $linkeditem if $linkeditem;
+ }
}
}
}
foreach my $code ( keys %subfieldstosearch ) {
$item->{$code} = $field->subfield( $subfieldstosearch{$code} );
}
- $item->{description} = $itemtypes{ $item->{itype} }{description};
+ $item->{description} = $itemtypes{ $item->{itype} }{translated_description};
# OPAC hidden items
if ($is_opac) {
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++;
# For each grouping of items (onloan, available, unavailable), we build a key to store relevant info about that item
my $userenv = C4::Context->userenv;
if ( $item->{onloan}
- && !( C4::Members::GetHideLostItemsPreference( $userenv->{'number'} ) && $item->{itemlost} ) )
+ && $userenv
+ && $userenv->{number}
+ && !( Koha::Patrons->find($userenv->{number})->category->hidelostitems && $item->{itemlost} ) )
{
$onloan_count++;
my $key = $prefix . $item->{onloan} . $item->{barcode};
- $onloan_items->{$key}->{due_date} = format_date( $item->{onloan} );
+ $onloan_items->{$key}->{due_date} = $item->{onloan};
$onloan_items->{$key}->{count}++ if $item->{$hbranch};
$onloan_items->{$key}->{branchname} = $item->{branchname};
$onloan_items->{$key}->{location} = $shelflocations->{ $item->{location} };
$onloan_items->{$key}->{itemcallnumber} = $item->{itemcallnumber};
$onloan_items->{$key}->{description} = $item->{description};
$onloan_items->{$key}->{imageurl} =
- getitemtypeimagelocation( $search_context, $itemtypes{ $item->{itype} }->{imageurl} );
+ getitemtypeimagelocation( $search_context->{'interface'}, $itemtypes{ $item->{itype} }->{imageurl} );
# if something's checked out and lost, mark it as 'long overdue'
if ( $item->{itemlost} ) {
# items not on loan, but still unavailable ( lost, withdrawn, damaged )
else {
+ $item->{notforloan}=1 if !$item->{notforloan} && $itemtypes{ C4::Context->preference("item-level_itypes")? $item->{itype}: $oldbiblio->{itemtype} }->{notforloan};
+
# item is on order
if ( $item->{notforloan} < 0 ) {
$ordered_count++;
|| $item->{itemlost}
|| $item->{damaged}
|| $item->{notforloan}
- || $items_count > 20) {
+ || ( C4::Context->preference('MaxSearchResultsItemsPerRecordStatusCheck')
+ && $items_count > C4::Context->preference('MaxSearchResultsItemsPerRecordStatusCheck') ) ) {
# A couple heuristics to limit how many times
# we query the database for item transfer information, sacrificing
$other_items->{$key}->{intransit} = ( $transfertwhen ne '' ) ? 1 : 0;
$other_items->{$key}->{onhold} = ($reservestatus) ? 1 : 0;
$other_items->{$key}->{notforloan} = GetAuthorisedValueDesc('','',$item->{notforloan},'','',$notforloan_authorised_value) if $notforloan_authorised_value and $item->{notforloan};
- $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}->{count}++ if $item->{$hbranch};
+ $other_items->{$key}->{location} = $shelflocations->{ $item->{location} };
+ $other_items->{$key}->{description} = $item->{description};
+ $other_items->{$key}->{imageurl} = getitemtypeimagelocation( $search_context->{'interface'}, $itemtypes{ $item->{itype} }->{imageurl} );
}
# item is available
else {
$can_place_holds = 1;
$available_count++;
- $available_items->{$prefix}->{count}++ if $item->{$hbranch};
- foreach (qw(branchname itemcallnumber description)) {
- $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}->{count}++ if $item->{$hbranch};
+ foreach (qw(branchname itemcallnumber description)) {
+ $available_items->{$prefix}->{$_} = $item->{$_};
+ }
+ $available_items->{$prefix}->{location} = $shelflocations->{ $item->{location} };
+ $available_items->{$prefix}->{imageurl} = getitemtypeimagelocation( $search_context->{'interface'}, $itemtypes{ $item->{itype} }->{imageurl} );
}
}
} # notforloan, item level and biblioitem level
}
# XSLT processing of some stuff
- my $interface = $search_context eq 'opac' ? 'OPAC' : '';
- if (!$scan && C4::Context->preference($interface . "XSLTResultsDisplay")) {
- $oldbiblio->{XSLTResultsRecord} = XSLTParse4Display($oldbiblio->{biblionumber}, $marcrecord, $interface."XSLTResultsDisplay", 1, \@hiddenitems);
- # the last parameter tells Koha to clean up the problematic ampersand entities that Zebra outputs
+ # we fetched the sysprefs already before the loop through all retrieved record!
+ if (!$scan && $xslfile) {
+ $oldbiblio->{XSLTResultsRecord} = XSLTParse4Display($oldbiblio->{biblionumber}, $marcrecord, $xslsyspref, 1, \@hiddenitems, $sysxml, $xslfile, $lang);
}
# if biblio level itypes are used and itemtype is notforloan, it can't be reserved either
}
}
$oldbiblio->{norequests} = 1 unless $can_place_holds;
- $oldbiblio->{itemsplural} = 1 if $items_count > 1;
$oldbiblio->{items_count} = $items_count;
$oldbiblio->{available_items_loop} = \@available_items_loop;
$oldbiblio->{onloan_items_loop} = \@onloan_items_loop;
return @newresults;
}
-=head2 SearchAcquisitions
- Search for acquisitions
-=cut
-
-sub SearchAcquisitions{
- my ($datebegin, $dateend, $itemtypes,$criteria, $orderby) = @_;
-
- my $dbh=C4::Context->dbh;
- # Variable initialization
- my $str=qq|
- SELECT marcxml
- FROM biblio
- LEFT JOIN biblioitems ON biblioitems.biblionumber=biblio.biblionumber
- LEFT JOIN items ON items.biblionumber=biblio.biblionumber
- WHERE dateaccessioned BETWEEN ? AND ?
- |;
-
- my (@params,@loopcriteria);
-
- push @params, $datebegin->output("iso");
- push @params, $dateend->output("iso");
-
- if (scalar(@$itemtypes)>0 and $criteria ne "itemtype" ){
- if(C4::Context->preference("item-level_itypes")){
- $str .= "AND items.itype IN (?".( ',?' x scalar @$itemtypes - 1 ).") ";
- }else{
- $str .= "AND biblioitems.itemtype IN (?".( ',?' x scalar @$itemtypes - 1 ).") ";
- }
- push @params, @$itemtypes;
- }
-
- if ($criteria =~/itemtype/){
- if(C4::Context->preference("item-level_itypes")){
- $str .= "AND items.itype=? ";
- }else{
- $str .= "AND biblioitems.itemtype=? ";
- }
-
- if(scalar(@$itemtypes) == 0){
- my $itypes = GetItemTypes();
- for my $key (keys %$itypes){
- push @$itemtypes, $key;
- }
- }
-
- @loopcriteria= @$itemtypes;
- }elsif ($criteria=~/itemcallnumber/){
- $str .= "AND (items.itemcallnumber LIKE CONCAT(?,'%')
- OR items.itemcallnumber is NULL
- OR items.itemcallnumber = '')";
-
- @loopcriteria = ("AA".."ZZ", "") unless (scalar(@loopcriteria)>0);
- }else {
- $str .= "AND biblio.title LIKE CONCAT(?,'%') ";
- @loopcriteria = ("A".."z") unless (scalar(@loopcriteria)>0);
- }
-
- if ($orderby =~ /date_desc/){
- $str.=" ORDER BY dateaccessioned DESC";
- } else {
- $str.=" ORDER BY title";
- }
-
- my $qdataacquisitions=$dbh->prepare($str);
-
- my @loopacquisitions;
- foreach my $value(@loopcriteria){
- push @params,$value;
- my %cell;
- $cell{"title"}=$value;
- $cell{"titlecode"}=$value;
-
- eval{$qdataacquisitions->execute(@params);};
-
- if ($@){ warn "recentacquisitions Error :$@";}
- else {
- my @loopdata;
- while (my $data=$qdataacquisitions->fetchrow_hashref){
- push @loopdata, {"summary"=>GetBiblioSummary( $data->{'marcxml'} ) };
- }
- $cell{"loopdata"}=\@loopdata;
- }
- push @loopacquisitions,\%cell if (scalar(@{$cell{loopdata}})>0);
- pop @params;
- }
- $qdataacquisitions->finish;
- return \@loopacquisitions;
-}
-
=head2 enabled_staff_search_views
%hash = enabled_staff_search_views()
);
}
-sub PurgeSearchHistory{
- my ($pSearchhistory)=@_;
- my $dbh = C4::Context->dbh;
- my $sth = $dbh->prepare("DELETE FROM search_history WHERE time < DATE_SUB( NOW(), INTERVAL ? DAY )");
- $sth->execute($pSearchhistory) or die $dbh->errstr;
-}
-
=head2 z3950_search_args
$arrayref = z3950_search_args($matchpoints)
=head2 new_record_from_zebra
-Given raw data from a Zebra result set, return a MARC::Record object
+Given raw data from a searchengine result set, return a MARC::Record object
This helper function is needed to take into account all the involved
system preferences and configuration variables to properly create the
If we are using GRS-1, then the raw data we get from Zebra should be USMARC
data. If we are using DOM, then it has to be MARCXML.
+If we are using elasticsearch, it'll already be a MARC::Record and this
+function needs a new name.
+
=cut
sub new_record_from_zebra {
my $server = shift;
my $raw_data = shift;
# Set the default indexing modes
+ my $search_engine = C4::Context->preference("SearchEngine");
+ if ($search_engine eq 'Elasticsearch') {
+ return ref $raw_data eq 'MARC::Record' ? $raw_data : MARC::Record->new_from_xml( $raw_data, 'UTF-8' );
+ }
my $index_mode = ( $server eq 'biblioserver' )
? C4::Context->config('zebra_bib_index_mode') // 'dom'
: C4::Context->config('zebra_auth_index_mode') // 'dom';