3 # Copyright 2000-2002 Katipo Communications
4 # This file is part of Koha.
6 # Koha is free software; you can redistribute it and/or modify it under the
7 # terms of the GNU General Public License as published by the Free Software
8 # Foundation; either version 2 of the License, or (at your option) any later
11 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
12 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
13 # A PARTICULAR PURPOSE. See the GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License along with
16 # Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
17 # Suite 330, Boston, MA 02111-1307 USA
28 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
30 # set the version for version checking
31 $VERSION = do { my @v = '$Revision$' =~ /\d+/g;
32 shift(@v) . "." . join("_", map {sprintf "%03d", $_ } @v); };
36 C4::Search - Functions for searching the Koha catalog and other databases
42 my ($count, @results) = catalogsearch4($env, $type, $search, $num, $offset);
46 This module provides the searching facilities for the Koha catalog and
59 &barcodes &ItemInfo &itemcount
60 &getcoverPhoto &add_query_line
61 &FindDuplicate &ZEBRAsearch_kohafields &convertPQF &sqlsearch &cataloguing_search
62 &getMARCnotes &getMARCsubjects &getMARCurls &getMARCadditional_authors &parsefields &spellSuggest);
63 # make all your functions, whether exported or not;
66 ZEBRAsearchkohafields is the underlying API for searching zebra for KOHA internal use
67 its kept similar to earlier version Koha Marc searches. instead of passing marc tags to the routine
68 you pass named kohafields
69 So you give an array of @kohafieldnames,@values, what relation they have @relations (equal, truncation etc) @and_or and
70 you receive an array of XML records.
71 The routine also has a flag $fordisplay and if it is set to 1 it will return the @results as an array of Perl hashes so that your previous
72 search results templates do actually work.
73 This routine will also take CCL,CQL or PQF queries and pass them straight to the server
74 See sub FindDuplicates for an example;
80 sub ZEBRAsearch_kohafields{
81 my ($kohafield,$value, $relation,$sort, $and_or, $fordisplay,$reorder,$startfrom,$number_of_results,$searchfrom,$searchtype)=@_;
82 return (0,undef) unless (@$value[0]);
84 my $server="biblioserver";
91 for ( $i=0; $i<=$#{$value}; $i++){
92 next if (@$value[$i] eq "");
93 my $keyattr=MARCfind_attr_from_kohafield(@$kohafield[$i]) if (@$kohafield[$i]);
94 if (!$keyattr){$keyattr=" \@attr 1=any";}
95 @$value[$i]=~ s/(\.|\?|\;|\=|\/|\\|\||\:|\*|\!|\,|\(|\)|\[|\]|\{|\}|\/|\")/ /g;
96 my $weighted=weightRank(@$kohafield[$i],@$value[$i],$i) unless($sort || $reorder);
97 $query.=$weighted.@$relation[$i]." ".$keyattr." \"".@$value[$i]."\" " if @$value[$i];
99 for (my $z= 0;$z<=$#{$and_or};$z++){
100 $query=@$and_or[$z]." ".$query if (@$value[$z+1] ne "");
107 ($oConnection[0])=C4::Context->Zconn($server);
110 (@sortpart)=split /,/,$reorder;
112 (@sortpart)=split /,/,$sort;
115 ##sortpart is expected to contain the form "title i<" notation or "title,1" both mean the same thing
117 push @sortpart," "; ##In case multisort variable is coming as a single query
119 if ($sortpart[1]==2){
120 $sortpart[1]=">i"; ##Descending
121 }elsif ($sortpart[1]==1){
122 $sortpart[1]="<i"; ##Ascending
127 $query=convertPQF($searchtype,$oConnection[0],$value);
129 $query=new ZOOM::Query::PQF($query);
131 goto EXITING unless $query;## erronous query coming in
132 $query->sortby($sortpart[0]." ".$sortpart[1]) if @sortpart;
140 $oResult= $oConnection[0]->search($query);
143 while (($i = ZOOM::event(\@oConnection)) != 0) {
144 $event = $oConnection[$i-1]->last_event();
145 last if $event == ZOOM::Event::ZEND;
148 my($error, $errmsg, $addinfo, $diagset) = $oConnection[0]->error_x();
149 if ($error==10007 && $tried<3) {## timeout --another 30 looonng seconds for this update
152 }elsif ($error==2 && $tried<2) {## timeout --temporary zebra error !whatever that means
156 warn "Error-$server /errcode:, $error, /MSG:,$errmsg,$addinfo \n";
158 $oConnection[0]->destroy();
159 return (undef,undef);
161 my $dbh=C4::Context->dbh;
162 $numresults=$oResult->size() ;
168 $ri=$startfrom if $startfrom;
169 for ( $ri; $ri<$numresults ; $ri++){
171 my $xmlrecord=$oResult->record($ri)->raw();
172 $xmlrecord=Encode::decode("utf8",$xmlrecord);
173 $xmlrecord=XML_xml2hash($xmlrecord);
176 push @results,$xmlrecord;
177 last if ($number_of_results && $z>=$number_of_results);
182 my ($facets,@parsed)=parsefields($dbh,$searchfrom,@results);
183 return ($numresults,$facets,@parsed) ;
188 $oConnection[0]->destroy();
190 return ($numresults,@results) ;
194 my ($kohafield,$value,$i)=@_;
195 ### If a multi query is received weighting is reduced from 1st query being highest rank to last query being lowest;
197 my $weight=1000 -($i*100);
198 $weight=100 if $weight==0;
199 return "" if $value eq "";
200 my $keyattr=MARCfind_attr_from_kohafield($kohafield) if ($kohafield);
201 return "" if($keyattr=~/4=109/ || $keyattr=~/4=4/ || $keyattr=~/4=5/); ###ranked sort not valid for numeric fields
202 my $fullfield; ### not all indexes are Complete-field. Use only for title||author
203 if ($kohafield eq "title" || $kohafield eq "" || $kohafield eq "any"){
204 $keyattr=" \@attr 1=title-cover";
205 $fullfield="\@attr 6=3 ";
206 }elsif ($kohafield eq "author"){
207 $fullfield="\@attr 6=3 ";
209 $weighted.="\@attr 2=102 ".$keyattr." \@attr 3=1 $fullfield \@attr 9=$weight \"".$value."\" " ;
210 $weighted=" \@or ".$weighted;
214 # Convert CCL, CQF or PQF to ZEBRA RPN queries,trap errors
215 my ($search_type,$zconn,$query)=@_;
217 if ($search_type eq "pqf"){
219 $pqf_query=new ZOOM::Query::PQF(@$query[0]);
221 }elsif ($search_type eq "ccl"){
223 my $cclfile=C4::Context->config("ccl2rpn");
224 $zconn->option(cclfile=>$cclfile);## CCL conversion file path
226 $pqf_query=new ZOOM::Query::CCL2RPN(@$query[0],$zconn);
228 }elsif ($search_type eq "cql"){
230 $pqf_query=new ZOOM::Query::CQL(@$query[0]);
241 =item add_bold_fields
242 After a search the searched keyword is <b>boldened</b> in the displayed search results if it exists in the title or author
243 It is now depreceated
245 sub add_html_bold_fields {
246 my ($type, $data, $search) = @_;
247 foreach my $key ('title', 'author') {
250 $new_key = 'bold_' . $key;
251 $data->{$new_key} = $data->{$key};
259 if ($type eq 'keyword') {
260 my $newkey=$search->{'keyword'};
262 @keys = split " ", $newkey;
265 for ($i = 0; $i < $count ; $i++) {
267 if (($data->{$new_key} =~ /($keys[$i])/i) && (lc($keys[$i]) ne 'b') ) {
269 $data->{$new_key} =~ s/$word/<b>$word<\/b>/;
278 ## This searches the SQL database only for biblionumber,itemnumber,barcode
279 ### Not very useful on production but as a debug tool useful during system maturing for ZEBRA operations
281 my ($dbh,$search)=@_;
283 if ($search->{'barcode'} ne '') {
284 $sth=$dbh->prepare("SELECT biblionumber from items where barcode=?");
285 $sth->execute($search->{'barcode'});
286 }elsif ($search->{'itemnumber'} ne '') {
287 $sth=$dbh->prepare("SELECT biblionumber from items where itemnumber=?");
288 $sth->execute($search->{'itemnumber'});
289 }elsif ($search->{'biblionumber'} ne '') {
290 $sth=$dbh->prepare("SELECT biblionumber from biblio where biblionumber=?");
291 $sth->execute($search->{'biblionumber'});
293 return (undef,undef);
296 my $result=$sth->fetchrow_hashref;
297 return (1,$result) if $result;
300 sub cataloguing_search{
301 ## This is an SQL based search designed to be used when adding a new biblio incase library sets
302 ## preference zebraorsql to sql when adding a new biblio
303 my ($search,$num,$offset) = @_;
304 my ($count,@results);
305 my $dbh=C4::Context->dbh;
308 my $condition="select SQL_CALC_FOUND_ROWS marcxml from biblio where ";
309 if ($search->{'isbn'} ne''){
310 $search->{'isbn'}=$search->{'isbn'}."%";
311 $query=$search->{'isbn'};
312 $condition.= " isbn like ? ";
314 return (0,undef) unless $search->{title};
315 $query=$search->{'title'};
316 $condition.= " MATCH (title) AGAINST(? in BOOLEAN MODE ) ";
318 my $sth=$dbh->prepare($condition);
319 $sth->execute($query);
320 my $nbresult=$dbh->prepare("SELECT FOUND_ROWS()");
322 my $count=$nbresult->fetchrow;
323 my $limit = $num + $offset;
324 my $startfrom = $offset;
327 while (my $marc=$sth->fetchrow){
328 if (($i >= $startfrom) && ($i < $limit)) {
329 my $record=XML_xml2hash_onerecord($marc);
330 my $data=XMLmarc2koha_onerecord($dbh,$record,"biblios");
336 return ($count,@results);
343 my $dbh=C4::Context->dbh;
344 my ($result) = XMLmarc2koha_onerecord($dbh,$xml,"biblios");
350 # search duplicate on ISBN, easy and fast..
352 if ($result->{isbn}) {
353 push @kohafield,"isbn";
354 ###Temporary fix for ISBN
355 my $isbn=$result->{isbn};
356 $isbn=~ s/(\.|\?|\;|\=|\/|\\|\||\:|\!|\'|,|\-|\"|\*|\(|\)|\[|\]|\{|\}|\/)//g;
359 $result->{title}=~s /\\//g;
360 $result->{title}=~s /\"//g;
361 $result->{title}=~ s/(\.|\?|\;|\=|\/|\\|\||\:|\*|\!|\,|\-|\(|\)|\[|\]|\{|\}|\/)/ /g;
363 push @kohafield,"title";
364 push @value,$result->{title};
365 push @relation,"\@attr 6=3 \@attr 4=1 \@attr 5=1"; ## right truncated,phrase,whole field
368 my ($total,@result)=ZEBRAsearch_kohafields(\@kohafield,\@value,\@relation,"",\@and_or,0,"",0,1);
370 my $title=XML_readline($result[0],"title","biblios") ;
371 my $biblionumber=XML_readline($result[0],"biblionumber","biblios") ;
372 return $biblionumber,$title ;
380 my ($type,$search,$results)=@_;
381 my $dbh = C4::Context->dbh;
384 my $borrowernumber = $search->{'borrowernumber'};
385 my $remote_IP = $search->{'remote_IP'};
386 my $remote_URL= $search->{'remote_URL'};
387 my $searchdesc = $search->{'searchdesc'};
389 my $sth = $dbh->prepare("INSERT INTO phrase_log(phr_phrase,phr_resultcount,phr_ip,user,actual) VALUES(?,?,?,?,?)");
392 $sth->execute($searchdesc,$results,$remote_IP,$borrowernumber,$remote_URL);
400 @results = &ItemInfo($env, $biblionumber, $type);
402 Returns information about books with the given biblionumber.
404 C<$type> may be either C<intra> or anything else. If it is not set to
405 C<intra>, then the search will exclude lost, very overdue, and
410 C<&ItemInfo> returns a list of references-to-hash. Each element
411 contains a number of keys. Most of them are table items from the
412 C<biblio>, C<biblioitems>, C<items>, and C<itemtypes> tables in the
413 Koha database. Other keys include:
417 =item C<$data-E<gt>{branchname}>
419 The name (not the code) of the branch to which the book belongs.
421 =item C<$data-E<gt>{datelastseen}>
423 This is simply C<items.datelastseen>, except that while the date is
424 stored in YYYY-MM-DD format in the database, here it is converted to
425 DD/MM/YYYY format. A NULL date is returned as C<//>.
427 =item C<$data-E<gt>{datedue}>
429 =item C<$data-E<gt>{class}>
431 This is the concatenation of C<biblioitems.classification>, the book's
432 Dewey code, and C<biblioitems.subclass>.
434 =item C<$data-E<gt>{ocount}>
436 I think this is the number of copies of the book available.
438 =item C<$data-E<gt>{order}>
440 If this is set, it is set to C<One Order>.
447 my ($dbh,$data) = @_;
450 my ($date_due, $count_reserves);
452 my $isth=$dbh->prepare("Select issues.*,borrowers.cardnumber from issues,borrowers where itemnumber = ? and returndate is null and issues.borrowernumber=borrowers.borrowernumber");
453 $isth->execute($data->{'itemnumber'});
454 if (my $idata=$isth->fetchrow_hashref){
455 $data->{borrowernumber} = $idata->{borrowernumber};
456 $data->{cardnumber} = $idata->{cardnumber};
457 $datedue = format_date($idata->{'date_due'});
459 if ($datedue eq '' || $datedue eq "0000-00-00"){
461 my ($restype,$reserves)=C4::Reserves2::CheckReserves($data->{'itemnumber'});
463 $count_reserves = $restype;
467 #get branch information.....
468 my $bsth=$dbh->prepare("SELECT * FROM branches WHERE branchcode = ?");
469 $bsth->execute($data->{'holdingbranch'});
470 if (my $bdata=$bsth->fetchrow_hashref){
471 $data->{'branchname'} = $bdata->{'branchname'};
474 $data->{'datelastseen'}=format_date($data->{'datelastseen'});
475 $data->{'datedue'}=$datedue;
476 $data->{'count_reserves'} = $count_reserves;
477 # get notforloan complete status if applicable
478 my ($tagfield,$tagsub)=MARCfind_marc_from_kohafield("notforloan","holdings");
479 my $sthnflstatus = $dbh->prepare("select authorised_value from holdings_subfield_structure where tagfield='$tagfield' and tagsubfield='$tagsub'");
480 $sthnflstatus->execute;
481 my ($authorised_valuecode) = $sthnflstatus->fetchrow;
482 if ($authorised_valuecode) {
483 $sthnflstatus = $dbh->prepare("select lib from authorised_values where category=? and authorised_value=?");
484 $sthnflstatus->execute($authorised_valuecode,$data->{itemnotforloan});
485 my ($lib) = $sthnflstatus->fetchrow;
486 $data->{notforloan} = $lib;
489 # my shelf procedures
490 my ($tagfield,$tagsubfield)=MARCfind_marc_from_kohafield("shelf","holdings");
492 my $shelfstatus = $dbh->prepare("select authorised_value from holdings_subfield_structure where tagfield='$tagfield' and tagsubfield='$tagsubfield'");
493 $shelfstatus->execute;
494 $authorised_valuecode = $shelfstatus->fetchrow;
495 if ($authorised_valuecode) {
496 $shelfstatus = $dbh->prepare("select lib from authorised_values where category=? and authorised_value=?");
497 $shelfstatus->execute($authorised_valuecode,$data->{shelf});
499 my ($lib) = $shelfstatus->fetchrow;
500 $data->{shelf} = $lib;
514 @barcodes = &barcodes($biblioitemnumber);
516 Given a biblioitemnumber, looks up the corresponding items.
518 Returns an array of references-to-hash; the keys are C<barcode> and
521 The returned items include very overdue items, but not lost ones.
526 #called from request.pl
527 my ($biblionumber)=@_;
529 my $dbh = C4::Context->dbh;
536 push @kohafields, "biblionumber";
537 push @values,$biblionumber;
538 push @relations, " "," \@attr 2=1"; ## selecting wthdrawn less then 1
539 push @and_or, "\@and";
541 my ($count,@results)=ZEBRAsearch_kohafields(\@kohafields,\@values,\@relations,$sort,\@and_or,"","");
542 push @fields,"barcode","itemlost","itemnumber","date_due","wthdrawn","notforloan";
543 my ($biblio,@items)=XMLmarc2koha($dbh,$results[0],"holdings", @fields);
552 ##Requires a MARCXML as $record
553 my ($dbh, $record, $marcflavour) = @_;
555 my ($mintag, $maxtag);
556 if (uc($marcflavour) eq uc"MARC21" || uc($marcflavour) eq "USMARC") {
559 } else { # assume unimarc if not marc21
565 foreach my $field ($mintag..$maxtag) {
567 my @values=XML_readline_asarray($record,"","",$field,"");
568 foreach my $value (@values){
569 $line{MARCNOTE}=$value if $value;
570 push @marcnotes,\%line if $line{MARCNOTE};
574 my $marcnotesarray=\@marcnotes;
575 return $marcnotesarray;
580 sub getMARCsubjects {
582 my ($dbh, $record, $marcflavour) = @_;
583 my ($mintag, $maxtag);
584 if (uc($marcflavour) eq uc"MARC21" || uc($marcflavour) eq "USMARC") {
587 } else { # assume unimarc if not marc21
596 foreach my $field ($mintag..$maxtag) {
597 my @value =XML_readline_asarray($record,"","",$field,"a");
598 foreach my $subject (@value){
599 $marcsubjct = {MARCSUBJCT => $subject,};
600 push @marcsubjcts, $marcsubjct;
604 my $marcsubjctsarray=\@marcsubjcts;
605 return $marcsubjctsarray;
606 } #end getMARCsubjects
610 my ($dbh, $record, $marcflavour) = @_;
611 my ($mintag, $maxtag);
612 if (uc($marcflavour) eq uc"MARC21" || uc($marcflavour) eq "USMARC") {
615 } else { # assume unimarc if not marc21
625 foreach my $field ($mintag..$maxtag) {
626 my @value =XML_readline_asarray($record,"","",$field,"u");
627 foreach my $url (@value){
628 if ( $value ne $url) {
629 $marcurl = {MARCURL => $url,};
630 push @marcurls, $marcurl;
637 my $marcurlsarray=\@marcurls;
638 return $marcurlsarray;
641 sub getMARCadditional_authors {
642 my ($dbh, $record, $marcflavour) = @_;
643 my ($mintag, $maxtag);
644 if (uc($marcflavour) eq uc"MARC21" || uc($marcflavour) eq "USMARC") {
647 } else { # assume unimarc if not marc21
648 ###FIX ME Correct tag to UNIMARC additional authors
658 foreach my $field ($mintag..$maxtag) {
659 my @value =XML_readline_asarray($record,"","",$field,"a");
660 foreach my $author (@value){
661 if ( $value ne $author) {
662 $marcauth = {MARCAUTHOR => $author,};
663 push @marcauthors, $marcauth;
670 my $marcauthsarray=\@marcauthors;
671 return $marcauthsarray;
675 #pass this a MARC record and it will parse it for display purposes
676 my ($dbh,$intranet,@marcrecords)=@_;
679 my $retrieve_from=C4::Context->preference('retrieve_from');
680 #Build brancnames hash for displaying in OPAC - more user friendly
682 #get branch information.....
684 my $bsth=$dbh->prepare("SELECT branchcode,branchname FROM branches");
686 while (my $bdata=$bsth->fetchrow_hashref){
687 $branches{$bdata->{'branchcode'}}= $bdata->{'branchname'};
690 #Building shelving hash if library has shelves defined like junior section, non-fiction, audio-visual room etc
693 my ($tagfield,$tagsubfield)=MARCfind_marc_from_kohafield("shelf","holdings");
694 my $shelfstatus = $dbh->prepare("select authorised_value from holdings_subfield_structure where tagfield='$tagfield' and tagsubfield='$tagsubfield'");
695 $shelfstatus->execute;
696 my ($authorised_valuecode) = $shelfstatus->fetchrow;
697 if ($authorised_valuecode) {
698 $shelfstatus = $dbh->prepare("select lib,authorised_value from authorised_values where category=? ");
699 $shelfstatus->execute($authorised_valuecode);
700 while (my $lib = $shelfstatus->fetchrow_hashref){
701 $shelves{$lib->{'authorised_value'}} = $lib->{'lib'};
706 my $facets_counter = ();
707 my $facets_info = ();
708 my @facets_loop; # stores the ref to array of hashes for template
710 foreach my $xml(@marcrecords){
712 if (C4::Context->preference('useFacets')){
713 ($facets_counter,$facets_info)=FillFacets($xml,$facets_counter,$facets_info);
715 my @kohafields; ## just name those necessary for the result page
716 push @kohafields, "biblionumber","title","author","publishercode","classification","subclass","itemtype","copyrightdate", "holdingbranch","date_due","location","shelf","itemcallnumber","notforloan","itemlost","wthdrawn";
717 my ($oldbiblio,@itemrecords) = XMLmarc2koha($dbh,$xml,"",@kohafields);
725 ##Loop for each item field
727 foreach my $item (@itemrecords) {
728 $norequests = 0 unless $item->{'itemnotforloan'};
731 #renaming some fields according to templates
732 $item->{'branchname'}=$branches{$item->{'holdingbranch'}};
733 $item->{'shelves'}=$shelves{$item->{'shelf'}};
734 $status="Lost" if ($item->{'itemlost'}>0);
735 $status="Withdrawn" if ($item->{'wthdrawn'}>0);
736 if ($intranet eq "intranet"){ ## we give full itemcallnumber detail in intranet
737 $status="Due:".format_date($item->{'date_due'}) if ($item->{'date_due'} gt "0000-00-00");
738 $status = $item->{'holdingbranch'}."-".$item->{'shelf'}."[".$item->{'itemcallnumber'}."]" unless defined $status;
740 $status="On Loan" if ($item->{'date_due'} gt "0000-00-00");
741 $status = $item->{'branchname'}."[".$item->{'shelves'}."]" unless defined $status;
747 $oldbiblio->{'noitems'} = $noitems;
748 $oldbiblio->{'norequests'} = $norequests;
749 $oldbiblio->{'even'} = $even;
752 $oldbiblio->{'toggle'}="#ffffcc";
754 $oldbiblio->{'toggle'}="white";
755 } ; ## some forms seems to use toggle
757 $oldbiblio->{'itemcount'} = $counts{'total'};
758 my $totalitemcounts = 0;
759 foreach my $key (keys %counts){
760 if ($key ne 'total'){
761 $totalitemcounts+= $counts{$key};
762 $oldbiblio->{'locationhash'}->{$key}=$counts{$key};
766 my ($locationtext, $locationtextonly, $notavailabletext) = ('','','');
767 foreach (sort keys %{$oldbiblio->{'locationhash'}}) {
769 if ($_ eq 'notavailable') {
770 $notavailabletext="Not available";
771 my $c=$oldbiblio->{'locationhash'}->{$_};
772 $oldbiblio->{'not-available-p'}=$c;
775 my $c=$oldbiblio->{'locationhash'}->{$_};
777 $oldbiblio->{'lost-p'} = $c;
778 } elsif ($_ eq 'Withdrawn') {
779 $oldbiblio->{'withdrawn-p'} = $c;
780 } elsif ($_ =~/\^Due:/) {
782 $oldbiblio->{'on-loan-p'} = $c;
784 $locationtextonly.= $_;
785 $locationtextonly.= " ($c)<br> " if $totalitemcounts > 1;
787 if ($totalitemcounts>1) {
788 $locationtext.=" ($c)<br> ";
792 if ($notavailabletext) {
793 $locationtext.= $notavailabletext;
795 $locationtext=~s/, $//;
797 $oldbiblio->{'location'} = $locationtext;
798 $oldbiblio->{'location-only'} = $locationtextonly;
799 $oldbiblio->{'use-location-flags-p'} = 1;
800 push @results,$oldbiblio;
802 }## For each record received
803 @facets_loop=BuildFacets($facets_counter,$facets_info,%branches);
805 return(@facets_loop,@results);
809 my ($facet_record,$facets_counter,$facets_info)=@_;
810 my $facets = C4::Koha::getFacets();
811 for (my $k=0; $k<@$facets;$k++) {
812 my $tags=@$facets->[$k]->{tags};
813 my $subfields=@$facets->[$k]->{subfield};
815 for (my $i=0; $i<@$tags;$i++) {
817 $type="holdings" if @$facets->[$k]->{'link_value'} =~/branch/; ## if using other facets from items add them here
818 if ($type eq "holdings"){
819 ###Read each item record
820 my $holdings=$facet_record->{holdings}->[0]->{record};
821 foreach my $holding(@$holdings){
822 for (my $z=0; $z<@$subfields;$z++) {
823 my $data=XML_readline_onerecord($holding,"","holdings",@$tags[$i],@$subfields[$z]);
824 $facets_counter->{ @$facets->[$k]->{'link_value'} }->{ $data }++ if $data;
828 for (my $z=0; $z<@$subfields;$z++) {
829 my $data=XML_readline($facet_record,"","biblios",@$tags[$i],@$subfields[$z]);
830 $facets_counter->{ @$facets->[$k]->{'link_value'} }->{ $data }++ if $data;
834 $facets_info->{ @$facets->[$k]->{'link_value'} }->{ 'label_value' } = @$facets->[$k]->{'label_value'};
835 $facets_info->{ @$facets->[$k]->{'link_value'} }->{ 'expanded' } = @$facets->[$k]->{'expanded'};
837 return ($facets_counter,$facets_info);
841 my ($facets_counter, $facets_info,%branches) = @_;
843 my @facets_loop; # stores the ref to array of hashes for template
845 foreach my $link_value ( sort { $facets_counter->{$b} <=> $facets_counter->{$a} } keys %$facets_counter) {
847 my $number_of_facets;
848 my @this_facets_array;
849 foreach my $one_facet ( sort { $facets_counter->{ $link_value }->{$b} <=> $facets_counter->{ $link_value }->{$a} } keys %{$facets_counter->{$link_value}} ) {
851 if (($number_of_facets < 11) || ($facets_info->{ $link_value }->{ 'expanded'})) {
853 # sanitize the link value ), ( will cause errors with CCL
854 my $facet_link_value = $one_facet;
855 $facet_link_value =~ s/(\(|\))/ /g;
857 # fix the length that will display in the label
858 my $facet_label_value = $one_facet;
859 $facet_label_value = substr($one_facet,0,20)."..." unless length($facet_label_value)<=20;
860 # well, if it's a branch, label by the name, not the code
861 if ($link_value =~/branch/) {
862 $facet_label_value = $branches{$one_facet};
865 # but we're down with the whole label being in the link's title
866 my $facet_title_value = $one_facet;
868 push @this_facets_array ,
869 ( { facet_count => $facets_counter->{ $link_value }->{ $one_facet },
870 facet_label_value => $facet_label_value,
871 facet_title_value => $facet_title_value,
872 facet_link_value => $facet_link_value,
873 type_link_value => $link_value,
876 }## if $number_of_facets
878 unless ($facets_info->{ $link_value }->{ 'expanded'}) {
879 $expandable=1 if ($number_of_facets > 10);
882 { type_link_value => $link_value,
883 type_id => $link_value."_id",
884 type_label => $facets_info->{ $link_value }->{ 'label_value' },
885 facets => \@this_facets_array,
886 expandable => $expandable,
887 expand => $link_value,
892 return \@facets_loop;
897 ## return the address of a cover image if defined otherwise the amazon cover images
900 my $image=XML_readline_onerecord($record,"coverphoto","biblios");
904 # if there is no image put the amazon cover image adress
906 my $isbn=XML_readline_onerecord($record,"isbn","biblios");
907 return "http://images.amazon.com/images/P/".$isbn.".01.MZZZZZZZ.jpg";
912 ($count, $lcount, $nacount, $fcount, $scount, $lostcount,
913 $mending, $transit,$ocount) =
914 &itemcount($env, $biblionumber, $type);
916 Counts the number of items with the given biblionumber, broken down by
921 If C<$type> is not set to C<intra>, lost, very overdue, and withdrawn
922 items will not be counted.
924 C<&itemcount> returns a nine-element list:
926 C<$count> is the total number of items with the given biblionumber.
928 C<$lcount> is the number of items at the Levin branch.
930 C<$nacount> is the number of items that are neither borrowed, lost,
931 nor withdrawn (and are therefore presumably on a shelf somewhere).
933 C<$fcount> is the number of items at the Foxton branch.
935 C<$scount> is the number of items at the Shannon branch.
937 C<$lostcount> is the number of lost and very overdue items.
939 C<$mending> is the number of items at the Mending branch (being
942 C<$transit> is the number of items at the Transit branch (in transit
945 C<$ocount> is the number of items that haven't arrived yet
946 (aqorders.quantity - aqorders.quantityreceived).
954 my ($env,$bibnum,$type)=@_;
955 my $dbh = C4::Context->dbh;
961 my $query="Select * from items where
963 push @kohafield,"biblionumber";
966 my ($total,@result)=ZEBRAsearch_kohafields(\@kohafield,\@value, \@relation,"", \@and_or, 0);## there is only one record no need for $num or $offset
967 my @fields;## extract only the fields required
968 push @fields,"itemnumber","itemlost","wthdrawn","holdingbranch","date_due";
969 my ($biblio,@items)=XMLmarc2koha ($dbh,$result[0],"holdings",\@fields);
979 foreach my $data(@items){
980 if ($type ne "intra"){
981 next if ($data->{itemlost} || $data->{wthdrawn});
982 } ## Probably trying to hide lost item from opac ?
985 ## Now it seems we want to find those which are onloan
988 if ( $data->{date_due} gt "0000-00-00"){
992 ### The rest of this code is hardcoded for Foxtrot Shanon etc. We urgently need a global understanding of these terms--TG
993 if ($data->{'holdingbranch'} eq 'C' || $data->{'holdingbranch'} eq 'LT'){
996 if ($data->{'holdingbranch'} eq 'F' || $data->{'holdingbranch'} eq 'FP'){
999 if ($data->{'holdingbranch'} eq 'S' || $data->{'holdingbranch'} eq 'SP'){
1002 if ($data->{'itemlost'} eq '1'){
1005 if ($data->{'itemlost'} eq '2'){
1008 if ($data->{'holdingbranch'} eq 'FM'){
1011 if ($data->{'holdingbranch'} eq 'TR'){
1017 my $sth2=$dbh->prepare("Select * from aqorders where biblionumber=?");
1018 $sth2->execute($bibnum);
1019 if (my $data=$sth2->fetchrow_hashref){
1020 $ocount=$data->{'quantity'} - $data->{'quantityreceived'};
1024 return ($count,$lcount,$nacount,$fcount,$scount,$lostcount,$mending,$transit,$ocount);
1028 my ($kohafield,$value)=@_;
1029 if (@$kohafield[0] eq "title" || @$kohafield[0] eq "author" || @$kohafield eq "subject"){
1030 ## pass them through
1032 @$kohafield[0]="any";
1034 my $kohaattr=MARCfind_attr_from_kohafield(@$kohafield[0]);
1035 @$value[0]=~ s/(\.|\?|\;|\=|\/|\\|\||\:|\*|\!|\,|\(|\)|\[|\]|\{|\}|\/)/ /g;
1036 my $query= $kohaattr." \@attr 6=3 \"".@$value[0]."\"";
1038 $zconn[0]=C4::Context->Zconn("biblioserver");
1039 $zconn[0]->option(number=>5);
1040 my $result=$zconn[0]->scan_pqf($query);
1043 while (($i = ZOOM::event(\@zconn)) != 0) {
1044 $event = $zconn[$i-1]->last_event();
1045 last if $event == ZOOM::Event::ZEND;
1048 my $n=$result->size();
1051 for (my $i=0; $i<$n; $i++){
1052 my ($term,$occ)=$result->term($i);
1053 push @suggestion, {kohafield=>@$kohafield[0], value=>$term,occ=>$occ} unless $term=~/\@/;
1055 $zconn[0]->destroy();
1058 END { } # module clean-up code here (global destructor)
1067 Koha Developement team <info@koha.org>
1068 # New functions to comply with ZEBRA search and new KOHA 3 XML API added 2006 Tumer Garip tgarip@neu.edu.tr