X-Git-Url: http://git.rot13.org/?a=blobdiff_plain;f=C4%2FSearch.pm;h=56468b5098ca6f94e5a13a23374bfbdc81b65669;hb=81d5f9dcdae68037a43c7953be1e91a2fcf92b04;hp=fdb34790a37b13fe623c2ba018c6159ec71d1e0c;hpb=32d8abbb43729aedfe20d30a9acc21d4f7268ec9;p=koha.git diff --git a/C4/Search.pm b/C4/Search.pm index fdb34790a3..56468b5098 100644 --- a/C4/Search.pm +++ b/C4/Search.pm @@ -31,8 +31,10 @@ use C4::Branch; use C4::Reserves; # CheckReserves use C4::Debug; use C4::Items; +use C4::Charset; use YAML; use URI::Escape; +use Business::ISBN; use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $DEBUG); @@ -68,9 +70,9 @@ This module provides searching functions for Koha's bibliographic databases &NZgetRecords &AddSearchHistory &GetDistinctValues - &BiblioAddAuthorities + &enabled_staff_search_views + &SimpleSearch ); -#FIXME: i had to add BiblioAddAuthorities here because in Biblios.pm it caused circular dependencies (C4::Search uses C4::Biblio, and BiblioAddAuthorities uses SimpleSearch from C4::Search) # make all your functions, whether exported or not; @@ -468,46 +470,39 @@ sub getRecords { if ( !$scan && $servers[ $i - 1 ] =~ /biblioserver/ ) { my $jmax = $size>$facets_maxrecs? $facets_maxrecs: $size; - - for ( my $k = 0 ; $k <= @$facets ; $k++ ) { - ($facets->[$k]) or next; - my @fcodes = @{$facets->[$k]->{'tags'}}; - my $sfcode = $facets->[$k]->{'subfield'}; - + for my $facet ( @$facets ) { for ( my $j = 0 ; $j < $jmax ; $j++ ) { my $render_record = $results[ $i - 1 ]->record($j)->render(); my @used_datas = (); - - foreach my $fcode (@fcodes) { - + foreach my $tag ( @{$facet->{tags}} ) { # avoid first line - my $field_pattern = '\n'.$fcode.' ([^\n]+)'; + my $tag_num = substr($tag, 0, 3); + my $letters = substr($tag, 3); + my $field_pattern = '\n' . $tag_num . ' ([^\n]+)'; my @field_tokens = ( $render_record =~ /$field_pattern/g ) ; - foreach my $field_token (@field_tokens) { - my $subfield_pattern = '\$'.$sfcode.' ([^\$]+)'; - my @subfield_values = ( $field_token =~ /$subfield_pattern/g ); - - foreach my $subfield_value (@subfield_values) { - - my $data = $subfield_value; - $data =~ s/^\s+//; # trim left - $data =~ s/\s+$//; # trim right - - unless ( $data ~~ @used_datas ) { - $facets_counter->{ $facets->[$k]->{'link_value'} }->{$data}++; - push @used_datas, $data; + my @subf = ( $field_token =~ /\$([a-zA-Z0-9]) ([^\$]+)/g ); + my @values; + for (my $i = 0; $i < @subf; $i += 2) { + if ( $letters =~ $subf[$i] ) { + my $value = $subf[$i+1]; + $value =~ s/^ *//; + $value =~ s/ *$//; + push @values, $value; } - } # subfields + } + my $data = join($facet->{sep}, @values); + unless ( $data ~~ @used_datas ) { + $facets_counter->{ $facet->{idx} }->{$data}++; + push @used_datas, $data; + } } # fields } # field codes } # records - - $facets_info->{ $facets->[$k]->{'link_value'} }->{'label_value'} = $facets->[$k]->{'label_value'}; - $facets_info->{ $facets->[$k]->{'link_value'} }->{'expanded'} = $facets->[$k]->{'expanded'}; + $facets_info->{ $facet->{idx} }->{label_value} = $facet->{label}; + $facets_info->{ $facet->{idx} }->{expanded} = $facet->{expanded}; } # facets } - # End PROGILONE } # warn "connection ", $i-1, ": $size hits"; @@ -1302,7 +1297,7 @@ sub buildQuery { warn "QUERY BEFORE LIMITS: >$query<" if $DEBUG; # add limits - my $group_OR_limits; + my %group_OR_limits; my $availability_limit; foreach my $this_limit (@limits) { if ( $this_limit =~ /available/ ) { @@ -1319,17 +1314,16 @@ sub buildQuery { # group_OR_limits, prefixed by mc- # OR every member of the group elsif ( $this_limit =~ /mc/ ) { - - if ( $this_limit =~ /mc-ccode:/ ) { + my ($k,$v) = split(/:/, $this_limit,2); + if ( $k !~ /mc-i(tem)?type/ ) { # in case the mc-ccode value has complicating chars like ()'s inside it we wrap in quotes $this_limit =~ tr/"//d; - my ($k,$v) = split(/:/, $this_limit,2); $this_limit = $k.":\"".$v."\""; } - $group_OR_limits .= " or " if $group_OR_limits; - $limit_desc .= " or " if $group_OR_limits; - $group_OR_limits .= "$this_limit"; + $group_OR_limits{$k} .= " or " if $group_OR_limits{$k}; + $limit_desc .= " or " if $group_OR_limits{$k}; + $group_OR_limits{$k} .= "$this_limit"; $limit_cgi .= "&limit=$this_limit"; $limit_desc .= " $this_limit"; } @@ -1352,9 +1346,9 @@ sub buildQuery { } } } - if ($group_OR_limits) { + foreach my $k (keys (%group_OR_limits)) { $limit .= " and " if ( $query || $limit ); - $limit .= "($group_OR_limits)"; + $limit .= "($group_OR_limits{$k})"; } if ($availability_limit) { $limit .= " and " if ( $query || $limit ); @@ -1413,11 +1407,16 @@ Format results in a form suitable for passing to the template # IMO this subroutine is pretty messy still -- it's responsible for # building the HTML output for the template sub searchResults { - my ( $search_context, $searchdesc, $hits, $results_per_page, $offset, $scan, @marcresults, $hidelostitems ) = @_; + my ( $search_context, $searchdesc, $hits, $results_per_page, $offset, $scan, $marcresults ) = @_; my $dbh = C4::Context->dbh; my @newresults; - $search_context = 'opac' unless $search_context eq 'opac' or $search_context eq 'intranet'; + $search_context = 'opac' if !$search_context || $search_context ne 'intranet'; + my ($is_opac, $hidelostitems); + if ($search_context eq 'opac') { + $hidelostitems = C4::Context->preference('hidelostitems'); + $is_opac = 1; + } #Build branchnames hash #find branchname @@ -1480,12 +1479,11 @@ sub searchResults { my $marcflavour = C4::Context->preference("marcflavour"); # We get the biblionumber position in MARC my ($bibliotag,$bibliosubf)=GetMarcFromKohaField('biblio.biblionumber',''); - my $fw; # loop through all of the records we've retrieved for ( my $i = $offset ; $i <= $times - 1 ; $i++ ) { - my $marcrecord = MARC::File::USMARC::decode( $marcresults[$i] ); - $fw = $scan + my $marcrecord = MARC::File::USMARC::decode( $marcresults->[$i] ); + my $fw = $scan ? undef : $bibliotag < 10 ? GetFrameworkCode($marcrecord->field($bibliotag)->data) @@ -1603,18 +1601,18 @@ sub searchResults { my $other_count = 0; my $wthdrawn_count = 0; my $itemlost_count = 0; + my $hideatopac_count = 0; my $itembinding_count = 0; my $itemdamaged_count = 0; my $item_in_transit_count = 0; my $can_place_holds = 0; - my $item_onhold_count = 0; + my $item_onhold_count = 0; my $items_count = scalar(@fields); - my $maxitems = - ( C4::Context->preference('maxItemsinSearchResults') ) - ? C4::Context->preference('maxItemsinSearchResults') - 1 - : 1; + my $maxitems_pref = C4::Context->preference('maxItemsinSearchResults'); + my $maxitems = $maxitems_pref ? $maxitems_pref - 1 : 1; # loop through every item + my @hiddenitems; foreach my $field (@fields) { my $item; @@ -1622,11 +1620,14 @@ sub searchResults { foreach my $code ( keys %subfieldstosearch ) { $item->{$code} = $field->subfield( $subfieldstosearch{$code} ); } + $item->{description} = $itemtypes{ $item->{itype} }{description}; # Hidden items - my @items = ($item); - my (@hiddenitems) = GetHiddenItemnumbers(@items); - $item->{'hideatopac'} = 1 if (@hiddenitems); + if ($is_opac) { + my @hi = GetHiddenItemnumbers($item); + $item->{'hideatopac'} = @hi; + push @hiddenitems, @hi; + } my $hbranch = C4::Context->preference('HomeOrHoldingBranch') eq 'homebranch' ? 'homebranch' : 'holdingbranch'; my $otherbranch = C4::Context->preference('HomeOrHoldingBranch') eq 'homebranch' ? 'holdingbranch' : 'homebranch'; @@ -1650,6 +1651,7 @@ sub searchResults { $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} ); # if something's checked out and lost, mark it as 'long overdue' if ( $item->{itemlost} ) { @@ -1673,7 +1675,7 @@ sub searchResults { my ($transfertfrom, $transfertto); # is item on the reserve shelf? - my $reservestatus = 0; + my $reservestatus = ''; my $reserveitem; unless ($item->{wthdrawn} @@ -1695,7 +1697,7 @@ sub searchResults { # should map transit status to record indexed in Zebra. # ($transfertwhen, $transfertfrom, $transfertto) = C4::Circulation::GetTransfers($item->{itemnumber}); - ($reservestatus, $reserveitem) = C4::Reserves::CheckReserves($item->{itemnumber}); + ($reservestatus, $reserveitem, undef) = C4::Reserves::CheckReserves($item->{itemnumber}); } # item is withdrawn, lost, damaged, not for loan, reserved or in transit @@ -1710,10 +1712,11 @@ sub searchResults { $wthdrawn_count++ if $item->{wthdrawn}; $itemlost_count++ if $item->{itemlost}; $itemdamaged_count++ if $item->{damaged}; + $hideatopac_count++ if $item->{hideatopac}; $item_in_transit_count++ if $transfertwhen ne ''; $item_onhold_count++ if $reservestatus eq 'Waiting'; $item->{status} = $item->{wthdrawn} . "-" . $item->{itemlost} . "-" . $item->{damaged} . "-" . $item->{notforloan}; - + # can place hold on item ? if ((!$item->{damaged} || C4::Context->preference('AllowHoldsOnDamagedItems')) && !$item->{itemlost} @@ -1733,6 +1736,7 @@ sub searchResults { $other_items->{$key}->{notforloan} = GetAuthorisedValueDesc('','',$item->{notforloan},'','',$notforloan_authorised_value) if $notforloan_authorised_value; $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} ); } # item is available @@ -1740,7 +1744,7 @@ sub searchResults { $can_place_holds = 1; $available_count++; $available_items->{$prefix}->{count}++ if $item->{$hbranch}; - foreach (qw(branchname itemcallnumber hideatopac)) { + foreach (qw(branchname itemcallnumber hideatopac description)) { $available_items->{$prefix}->{$_} = $item->{$_}; } $available_items->{$prefix}->{location} = $shelflocations->{ $item->{location} }; @@ -1748,11 +1752,11 @@ sub searchResults { } } } # notforloan, item level and biblioitem level + if ($items_count > 0) { + next if $is_opac && $hideatopac_count >= $items_count; + next if $hidelostitems && $itemlost_count >= $items_count; + } my ( $availableitemscount, $onloanitemscount, $otheritemscount ); - $maxitems = - ( C4::Context->preference('maxItemsinSearchResults') ) - ? C4::Context->preference('maxItemsinSearchResults') - 1 - : 1; for my $key ( sort keys %$onloan_items ) { (++$onloanitemscount > $maxitems) and last; push @onloan_items_loop, $onloan_items->{$key}; @@ -1770,13 +1774,10 @@ sub searchResults { use C4::Charset; SetUTF8Flag($marcrecord); $debug && warn $marcrecord->as_formatted; - if (!$scan && $search_context eq 'opac' && C4::Context->preference("OPACXSLTResultsDisplay")) { - # FIXME note that XSLTResultsDisplay (use of XSLT to format staff interface bib search results) - # is not implemented yet - $oldbiblio->{XSLTResultsRecord} = XSLTParse4Display($oldbiblio->{biblionumber}, $marcrecord, 'Results', - $search_context, 1); - # the last parameter tells Koha to clean up the problematic ampersand entities that Zebra outputs - + 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 } # if biblio level itypes are used and itemtype is notforloan, it can't be reserved either @@ -1803,8 +1804,8 @@ sub searchResults { $oldbiblio->{intransitcount} = $item_in_transit_count; $oldbiblio->{onholdcount} = $item_onhold_count; $oldbiblio->{orderedcount} = $ordered_count; - $oldbiblio->{isbn} =~ - s/-//g; # deleting - in isbn to enable amazon content + # deleting - in isbn to enable amazon content + $oldbiblio->{isbn} =~ s/-//g; if (C4::Context->preference("AlternateHoldingsField") && $items_count == 0) { my $fieldspec = C4::Context->preference("AlternateHoldingsField"); @@ -1834,10 +1835,7 @@ sub searchResults { $oldbiblio->{'alternateholdings_count'} = $alternateholdingscount; } - push( @newresults, $oldbiblio ) - if(not $hidelostitems - or (($items_count > $itemlost_count ) - && $hidelostitems)); + push( @newresults, $oldbiblio ); } return @newresults; @@ -2628,7 +2626,15 @@ $template->param ( MYLOOP => C4::Search::z3950_search_args($searchscalar) ) sub z3950_search_args { my $bibrec = shift; - $bibrec = { title => $bibrec } if !ref $bibrec; + my $isbn = Business::ISBN->new($bibrec); + + if (defined $isbn && $isbn->is_valid) + { + $bibrec = { isbn => $bibrec } if !ref $bibrec; + } + else { + $bibrec = { title => $bibrec } if !ref $bibrec; + } my $array = []; for my $field (qw/ lccn isbn issn title author dewey subject /) { @@ -2638,105 +2644,6 @@ sub z3950_search_args { return $array; } -=head2 BiblioAddAuthorities - -( $countlinked, $countcreated ) = BiblioAddAuthorities($record, $frameworkcode); - -this function finds the authorities linked to the biblio - * search in the authority DB for the same authid (in $9 of the biblio) - * search in the authority DB for the same 001 (in $3 of the biblio in UNIMARC) - * search in the authority DB for the same values (exactly) (in all subfields of the biblio) -OR adds a new authority record - -=over 2 - -=item C - - * $record is the MARC record in question (marc blob) - * $frameworkcode is the bibliographic framework to use (if it is "" it uses the default framework) - -=item C - - * $countlinked is the number of authorities records that are linked to this authority - * $countcreated - -=item C - * I had to add this to Search.pm (instead of the logical Biblio.pm) because of a circular dependency (this sub uses SimpleSearch, and Search.pm uses Biblio.pm) - -=back - -=cut - - -sub BiblioAddAuthorities{ - my ( $record, $frameworkcode ) = @_; - my $dbh=C4::Context->dbh; - my $query=$dbh->prepare(qq| -SELECT authtypecode,tagfield -FROM marc_subfield_structure -WHERE frameworkcode=? -AND (authtypecode IS NOT NULL AND authtypecode<>\"\")|); -# SELECT authtypecode,tagfield -# FROM marc_subfield_structure -# WHERE frameworkcode=? -# AND (authtypecode IS NOT NULL OR authtypecode<>\"\")|); - $query->execute($frameworkcode); - my ($countcreated,$countlinked); - while (my $data=$query->fetchrow_hashref){ - foreach my $field ($record->field($data->{tagfield})){ - next if ($field->subfield('3')||$field->subfield('9')); - # No authorities id in the tag. - # Search if there is any authorities to link to. - my $query='at='.$data->{authtypecode}.' '; - map {$query.= ' and he,ext="'.$_->[1].'"' if ($_->[0]=~/[A-z]/)} $field->subfields(); - my ($error, $results, $total_hits)=SimpleSearch( $query, undef, undef, [ "authorityserver" ] ); - # there is only 1 result - if ( $error ) { - warn "BIBLIOADDSAUTHORITIES: $error"; - return (0,0) ; - } - if ( @{$results} == 1 ) { - my $marcrecord = MARC::File::USMARC::decode($results->[0]); - $field->add_subfields('9'=>$marcrecord->field('001')->data); - $countlinked++; - } elsif ( @{$results} > 1 ) { - #More than One result - #This can comes out of a lack of a subfield. -# my $marcrecord = MARC::File::USMARC::decode($results->[0]); -# $record->field($data->{tagfield})->add_subfields('9'=>$marcrecord->field('001')->data); - $countlinked++; - } else { - #There are no results, build authority record, add it to Authorities, get authid and add it to 9 - ###NOTICE : This is only valid if a subfield is linked to one and only one authtypecode - ###NOTICE : This can be a problem. We should also look into other types and rejected forms. - my $authtypedata=C4::AuthoritiesMarc::GetAuthType($data->{authtypecode}); - next unless $authtypedata; - my $marcrecordauth=MARC::Record->new(); - my $authfield=MARC::Field->new($authtypedata->{auth_tag_to_report},'','',"a"=>"".$field->subfield('a')); - map { $authfield->add_subfields($_->[0]=>$_->[1]) if ($_->[0]=~/[A-z]/ && $_->[0] ne "a" )} $field->subfields(); - $marcrecordauth->insert_fields_ordered($authfield); - - # bug 2317: ensure new authority knows it's using UTF-8; currently - # only need to do this for MARC21, as MARC::Record->as_xml_record() handles - # automatically for UNIMARC (by not transcoding) - # FIXME: AddAuthority() instead should simply explicitly require that the MARC::Record - # use UTF-8, but as of 2008-08-05, did not want to introduce that kind - # of change to a core API just before the 3.0 release. - if (C4::Context->preference('marcflavour') eq 'MARC21') { - SetMarcUnicodeFlag($marcrecordauth, 'MARC21'); - } - -# warn "AUTH RECORD ADDED : ".$marcrecordauth->as_formatted; - - my $authid=AddAuthority($marcrecordauth,'',$data->{authtypecode}); - $countcreated++; - $field->add_subfields('9'=>$authid); - } - } - } - return ($countlinked,$countcreated); -} - =head2 GetDistinctValues($field); C<$field> is a reference to the fields array