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 # FIXME - C4::Search uses C4::Reserves2, which uses C4::Search.
29 # So Perl complains that all of the functions here get redefined.
32 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
34 # set the version for version checking
35 $VERSION = do { my @v = '$Revision$' =~ /\d+/g;
36 shift(@v) . "." . join("_", map {sprintf "%03d", $_ } @v); };
40 C4::Search - Functions for searching the Koha catalog and other databases
46 my ($count, @results) = catalogsearch4($env, $type, $search, $num, $offset);
50 This module provides the searching facilities for the Koha catalog and
63 &barcodes &ItemInfo &itemcount
64 &getcoverPhoto &add_query_line
65 &FindDuplicate &ZEBRAsearch_kohafields &convertPQF &sqlsearch &cataloguing_search
66 &getMARCnotes &getMARCsubjects &getMARCurls &parsefields);
67 # make all your functions, whether exported or not;
70 ZEBRAsearchkohafields is the underlying API for searching zebra for KOHA internal use
71 its kept similar to earlier version Koha Marc searches. instead of passing marc tags to the routine
72 you pass named kohafields
73 So you give an array of @kohafieldnames,@values, what relation they have @relations (equal, truncation etc) @and_or and
74 you receive an array of XML records.
75 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
76 search results templates do actually work.
77 This routine will also take CCL,CQL or PQF queries and pass them straight to the server
78 See sub FindDuplicates for an example;
84 sub ZEBRAsearch_kohafields{
85 my ($kohafield,$value, $relation,$sort, $and_or, $fordisplay,$reorder,$startfrom,$number_of_results,$searchfrom,$searchtype)=@_;
86 return (0,undef) unless (@$value[0]);
87 my $server="biblioserver";
94 for ( $i=0; $i<=$#{$value}; $i++){
95 next if (@$value[$i] eq "");
96 my $keyattr=MARCfind_attr_from_kohafield(@$kohafield[$i]) if (@$kohafield[$i]);
97 if (!$keyattr){$keyattr=" \@attr 1=any";}
98 @$value[$i]=~ s/(\.|\?|\;|\=|\/|\\|\||\:|\*|\!|\,|\(|\)|\[|\]|\{|\}|\/)/ /g;
99 my $weighted=weightRank(@$kohafield[$i],@$value[$i],$i) unless($sort || $reorder);
100 $query.=$weighted.@$relation[$i]." ".$keyattr." \"".@$value[$i]."\" " if @$value[$i];
102 for (my $z= 0;$z<=$#{$and_or};$z++){
103 $query=@$and_or[$z]." ".$query if (@$value[$z+1] ne "");
110 ($oConnection[0])=C4::Context->Zconn($server);
113 (@sortpart)=split /,/,$reorder;
115 (@sortpart)=split /,/,$sort;
118 ##sortpart is expected to contain the form "title i<" notation or "title,1" both mean the same thing
120 push @sortpart," "; ##In case multisort variable is coming as a single query
122 if ($sortpart[1]==2){
123 $sortpart[1]=">i"; ##Descending
124 }elsif ($sortpart[1]==1){
125 $sortpart[1]="<i"; ##Ascending
130 $query=convertPQF($searchtype,$oConnection[0],$value);
132 $query=new ZOOM::Query::PQF($query);
134 goto EXITING unless $query;## erronous query coming in
135 $query->sortby($sortpart[0]." ".$sortpart[1]) if @sortpart;
143 $oResult= $oConnection[0]->search($query);
146 while (($i = ZOOM::event(\@oConnection)) != 0) {
147 $event = $oConnection[$i-1]->last_event();
148 last if $event == ZOOM::Event::ZEND;
151 my($error, $errmsg, $addinfo, $diagset) = $oConnection[0]->error_x();
152 if ($error==10007 && $tried<3) {## timeout --another 30 looonng seconds for this update
155 }elsif ($error==2 && $tried<2) {## timeout --temporary zebra error !whatever that means
159 warn "Error-$server /errcode:, $error, /MSG:,$errmsg,$addinfo \n";
161 $oConnection[0]->destroy();
162 return (undef,undef);
164 my $dbh=C4::Context->dbh;
165 $numresults=$oResult->size() ;
171 $ri=$startfrom if $startfrom;
172 for ( $ri; $ri<$numresults ; $ri++){
174 my $xmlrecord=$oResult->record($ri)->raw();
175 $xmlrecord=Encode::decode("utf8",$xmlrecord);
176 $xmlrecord=XML_xml2hash($xmlrecord);
179 push @results,$xmlrecord;
180 last if ($number_of_results && $z>=$number_of_results);
185 my ($facets,@parsed)=parsefields($dbh,$searchfrom,@results);
186 return ($numresults,$facets,@parsed) ;
191 $oConnection[0]->destroy();
193 return ($numresults,@results) ;
197 my ($kohafield,$value,$i)=@_;
198 ### If a multi query is received weighting is reduced from 1st query being highest rank to last query being lowest;
200 my $weight=1000 -($i*100);
201 $weight=100 if $weight==0;
202 return "" if $value eq "";
203 my $keyattr=MARCfind_attr_from_kohafield($kohafield) if ($kohafield);
204 return "" if($keyattr=~/4=109/ || $keyattr=~/4=4/ || $keyattr=~/4=5/); ###ranked sort not valid for numeric fields
205 my $fullfield; ### not all indexes are Complete-field. Use only for title||author
206 if ($kohafield eq "title" || $kohafield eq "" || $kohafield eq "any"){
207 $keyattr=" \@attr 1=title-cover";
208 $fullfield="\@attr 6=3 ";
209 }elsif ($kohafield eq "author"){
210 $fullfield="\@attr 6=3 ";
212 $weighted.="\@attr 2=102 ".$keyattr." \@attr 3=1 $fullfield \@attr 9=$weight \"".$value."\" " ;
213 $weighted=" \@or ".$weighted;
217 # Convert CCL, CQF or PQF to ZEBRA RPN queries,trap errors
218 my ($search_type,$zconn,$query)=@_;
220 if ($search_type eq "pqf"){
222 $pqf_query=new ZOOM::Query::PQF(@$query[0]);
224 }elsif ($search_type eq "ccl"){
226 my $cclfile=C4::Context->config("ccl2rpn");
227 $zconn->option(cclfile=>$cclfile);## CCL conversion file path
229 $pqf_query=new ZOOM::Query::CCL2RPN(@$query[0],$zconn);
231 }elsif ($search_type eq "cql"){
233 $pqf_query=new ZOOM::Query::CQL(@$query[0]);
244 =item add_bold_fields
245 After a search the searched keyword is <b>boldened</b> in the displayed search results if it exists in the title or author
246 It is now depreceated
248 sub add_html_bold_fields {
249 my ($type, $data, $search) = @_;
250 foreach my $key ('title', 'author') {
253 $new_key = 'bold_' . $key;
254 $data->{$new_key} = $data->{$key};
262 if ($type eq 'keyword') {
263 my $newkey=$search->{'keyword'};
265 @keys = split " ", $newkey;
268 for ($i = 0; $i < $count ; $i++) {
270 if (($data->{$new_key} =~ /($keys[$i])/i) && (lc($keys[$i]) ne 'b') ) {
272 $data->{$new_key} =~ s/$word/<b>$word<\/b>/;
281 ## This searches the SQL database only for biblionumber,itemnumber,barcode
282 ### Not very useful on production but as a debug tool useful during system maturing for ZEBRA operations
284 my ($dbh,$search)=@_;
286 if ($search->{'barcode'} ne '') {
287 $sth=$dbh->prepare("SELECT biblionumber from items where barcode=?");
288 $sth->execute($search->{'barcode'});
289 }elsif ($search->{'itemnumber'} ne '') {
290 $sth=$dbh->prepare("SELECT biblionumber from items where itemnumber=?");
291 $sth->execute($search->{'itemnumber'});
292 }elsif ($search->{'biblionumber'} ne '') {
293 $sth=$dbh->prepare("SELECT biblionumber from biblio where biblionumber=?");
294 $sth->execute($search->{'biblionumber'});
296 return (undef,undef);
299 my $result=$sth->fetchrow_hashref;
300 return (1,$result) if $result;
303 sub cataloguing_search{
304 ## This is an SQL based search designed to be used when adding a new biblio incase library sets
305 ## preference zebraorsql to sql when adding a new biblio
306 my ($search,$num,$offset) = @_;
307 my ($count,@results);
308 my $dbh=C4::Context->dbh;
311 my $condition="select SQL_CALC_FOUND_ROWS marcxml from biblio where ";
312 if ($search->{'isbn'} ne''){
313 $search->{'isbn'}=$search->{'isbn'}."%";
314 $query=$search->{'isbn'};
315 $condition.= " isbn like ? ";
317 return (0,undef) unless $search->{title};
318 $query=$search->{'title'};
319 $condition.= " MATCH (title) AGAINST(? in BOOLEAN MODE ) ";
321 my $sth=$dbh->prepare($condition);
322 $sth->execute($query);
323 my $nbresult=$dbh->prepare("SELECT FOUND_ROWS()");
325 my $count=$nbresult->fetchrow;
326 my $limit = $num + $offset;
327 my $startfrom = $offset;
330 while (my $marc=$sth->fetchrow){
331 if (($i >= $startfrom) && ($i < $limit)) {
332 my $record=XML_xml2hash_onerecord($marc);
333 my $data=XMLmarc2koha_onerecord($dbh,$record,"biblios");
339 return ($count,@results);
346 my $dbh=C4::Context->dbh;
347 my ($result) = XMLmarc2koha_onerecord($dbh,$xml,"biblios");
353 # search duplicate on ISBN, easy and fast..
355 if ($result->{isbn}) {
356 push @kohafield,"isbn";
357 ###Temporary fix for ISBN
358 my $isbn=$result->{isbn};
359 $isbn=~ s/(\.|\?|\;|\=|\/|\\|\||\:|\!|\'|,|\-|\"|\*|\(|\)|\[|\]|\{|\}|\/)//g;
362 $result->{title}=~s /\\//g;
363 $result->{title}=~s /\"//g;
364 $result->{title}=~ s/(\.|\?|\;|\=|\/|\\|\||\:|\*|\!|\,|\-|\(|\)|\[|\]|\{|\}|\/)/ /g;
366 push @kohafield,"title";
367 push @value,$result->{title};
368 push @relation,"\@attr 6=3 \@attr 4=1 \@attr 5=1"; ## right truncated,phrase,whole field
371 my ($total,@result)=ZEBRAsearch_kohafields(\@kohafield,\@value,\@relation,"",\@and_or,0,"",0,1);
373 my $title=XML_readline($result[0],"title","biblios") ;
374 my $biblionumber=XML_readline($result[0],"biblionumber","biblios") ;
375 return $biblionumber,$title ;
383 my ($type,$search,$results)=@_;
384 my $dbh = C4::Context->dbh;
387 my $borrowernumber = $search->{'borrowernumber'};
388 my $remote_IP = $search->{'remote_IP'};
389 my $remote_URL= $search->{'remote_URL'};
390 my $searchdesc = $search->{'searchdesc'};
392 my $sth = $dbh->prepare("INSERT INTO phrase_log(phr_phrase,phr_resultcount,phr_ip,user,actual) VALUES(?,?,?,?,?)");
395 $sth->execute($searchdesc,$results,$remote_IP,$borrowernumber,$remote_URL);
403 @results = &ItemInfo($env, $biblionumber, $type);
405 Returns information about books with the given biblionumber.
407 C<$type> may be either C<intra> or anything else. If it is not set to
408 C<intra>, then the search will exclude lost, very overdue, and
413 C<&ItemInfo> returns a list of references-to-hash. Each element
414 contains a number of keys. Most of them are table items from the
415 C<biblio>, C<biblioitems>, C<items>, and C<itemtypes> tables in the
416 Koha database. Other keys include:
420 =item C<$data-E<gt>{branchname}>
422 The name (not the code) of the branch to which the book belongs.
424 =item C<$data-E<gt>{datelastseen}>
426 This is simply C<items.datelastseen>, except that while the date is
427 stored in YYYY-MM-DD format in the database, here it is converted to
428 DD/MM/YYYY format. A NULL date is returned as C<//>.
430 =item C<$data-E<gt>{datedue}>
432 =item C<$data-E<gt>{class}>
434 This is the concatenation of C<biblioitems.classification>, the book's
435 Dewey code, and C<biblioitems.subclass>.
437 =item C<$data-E<gt>{ocount}>
439 I think this is the number of copies of the book available.
441 =item C<$data-E<gt>{order}>
443 If this is set, it is set to C<One Order>.
450 my ($dbh,$data) = @_;
453 my ($date_due, $count_reserves);
455 my $isth=$dbh->prepare("Select issues.*,borrowers.cardnumber from issues,borrowers where itemnumber = ? and returndate is null and issues.borrowernumber=borrowers.borrowernumber");
456 $isth->execute($data->{'itemnumber'});
457 if (my $idata=$isth->fetchrow_hashref){
458 $data->{borrowernumber} = $idata->{borrowernumber};
459 $data->{cardnumber} = $idata->{cardnumber};
460 $datedue = format_date($idata->{'date_due'});
462 if ($datedue eq '' || $datedue eq "0000-00-00"){
464 my ($restype,$reserves)=C4::Reserves2::CheckReserves($data->{'itemnumber'});
466 $count_reserves = $restype;
470 #get branch information.....
471 my $bsth=$dbh->prepare("SELECT * FROM branches WHERE branchcode = ?");
472 $bsth->execute($data->{'holdingbranch'});
473 if (my $bdata=$bsth->fetchrow_hashref){
474 $data->{'branchname'} = $bdata->{'branchname'};
476 my $date=substr($data->{'datelastseen'},0,8);
477 $data->{'datelastseen'}=format_date($date);
478 $data->{'datedue'}=$datedue;
479 $data->{'count_reserves'} = $count_reserves;
480 # get notforloan complete status if applicable
481 my ($tagfield,$tagsub)=MARCfind_marc_from_kohafield("notforloan","holdings");
482 my $sthnflstatus = $dbh->prepare("select authorised_value from holdings_subfield_structure where tagfield='$tagfield' and tagsubfield='$tagsub'");
483 $sthnflstatus->execute;
484 my ($authorised_valuecode) = $sthnflstatus->fetchrow;
485 if ($authorised_valuecode) {
486 $sthnflstatus = $dbh->prepare("select lib from authorised_values where category=? and authorised_value=?");
487 $sthnflstatus->execute($authorised_valuecode,$data->{itemnotforloan});
488 my ($lib) = $sthnflstatus->fetchrow;
489 $data->{notforloan} = $lib;
492 # my shelf procedures
493 my ($tagfield,$tagsubfield)=MARCfind_marc_from_kohafield("shelf","holdings");
495 my $shelfstatus = $dbh->prepare("select authorised_value from holdings_subfield_structure where tagfield='$tagfield' and tagsubfield='$tagsubfield'");
496 $shelfstatus->execute;
497 $authorised_valuecode = $shelfstatus->fetchrow;
498 if ($authorised_valuecode) {
499 $shelfstatus = $dbh->prepare("select lib from authorised_values where category=? and authorised_value=?");
500 $shelfstatus->execute($authorised_valuecode,$data->{shelf});
502 my ($lib) = $shelfstatus->fetchrow;
503 $data->{shelf} = $lib;
517 @barcodes = &barcodes($biblioitemnumber);
519 Given a biblioitemnumber, looks up the corresponding items.
521 Returns an array of references-to-hash; the keys are C<barcode> and
524 The returned items include very overdue items, but not lost ones.
529 #called from request.pl
530 my ($biblionumber)=@_;
532 my $dbh = C4::Context->dbh;
539 push @kohafields, "biblionumber";
540 push @values,$biblionumber;
541 push @relations, " "," \@attr 2=1"; ## selecting wthdrawn less then 1
542 push @and_or, "\@and";
544 my ($count,@results)=ZEBRAsearch_kohafields(\@kohafields,\@values,\@relations,$sort,\@and_or,"","");
545 push @fields,"barcode","itemlost","itemnumber","date_due","wthdrawn","notforloan";
546 my ($biblio,@items)=XMLmarc2koha($dbh,$results[0],"holdings", @fields);
555 ##Requires a MARCXML as $record
556 my ($dbh, $record, $marcflavour) = @_;
558 my ($mintag, $maxtag);
559 if (uc($marcflavour) eq uc"MARC21" || uc($marcflavour) eq "USMARC") {
562 } else { # assume unimarc if not marc21
568 foreach my $field ($mintag..$maxtag) {
570 my @values=XML_readline_asarray($record,"","",$field,"");
571 foreach my $value (@values){
572 $line{MARCNOTE}=$value if $value;
573 push @marcnotes,\%line if $line{MARCNOTE};
577 my $marcnotesarray=\@marcnotes;
578 return $marcnotesarray;
583 sub getMARCsubjects {
585 my ($dbh, $record, $marcflavour) = @_;
586 my ($mintag, $maxtag);
587 if (uc($marcflavour) eq uc"MARC21" || uc($marcflavour) eq "USMARC") {
590 } else { # assume unimarc if not marc21
599 foreach my $field ($mintag..$maxtag) {
600 my @value =XML_readline_asarray($record,"","",$field,"a");
601 foreach my $subject (@value){
602 $marcsubjct = {MARCSUBJCT => $subject,};
603 push @marcsubjcts, $marcsubjct;
607 my $marcsubjctsarray=\@marcsubjcts;
608 return $marcsubjctsarray;
609 } #end getMARCsubjects
613 ### This code is wrong only works with MARC21
614 my ($dbh, $record, $marcflavour) = @_;
615 my ($mintag, $maxtag);
616 if (uc($marcflavour) eq uc"MARC21" || uc($marcflavour) eq "USMARC") {
619 } else { # assume unimarc if not marc21
629 foreach my $field ($mintag..$maxtag) {
630 my @value =XML_readline_asarray($record,"","",$field,"u");
631 foreach my $url (@value){
632 if ( $value ne $url) {
633 $marcurl = {MARCURL => $url,};
634 push @marcurls, $marcurl;
641 my $marcurlsarray=\@marcurls;
642 return $marcurlsarray;
648 #pass this a MARC record and it will parse it for display purposes
649 my ($dbh,$intranet,@marcrecords)=@_;
652 my $retrieve_from=C4::Context->preference('retrieve_from');
653 #Build brancnames hash for displaying in OPAC - more user friendly
655 #get branch information.....
657 my $bsth=$dbh->prepare("SELECT branchcode,branchname FROM branches");
659 while (my $bdata=$bsth->fetchrow_hashref){
660 $branches{$bdata->{'branchcode'}}= $bdata->{'branchname'};
663 #Building shelving hash if library has shelves defined like junior section, non-fiction, audio-visual room etc
666 my ($tagfield,$tagsubfield)=MARCfind_marc_from_kohafield("shelf","holdings");
667 my $shelfstatus = $dbh->prepare("select authorised_value from holdings_subfield_structure where tagfield='$tagfield' and tagsubfield='$tagsubfield'");
668 $shelfstatus->execute;
669 my ($authorised_valuecode) = $shelfstatus->fetchrow;
670 if ($authorised_valuecode) {
671 $shelfstatus = $dbh->prepare("select lib,authorised_value from authorised_values where category=? ");
672 $shelfstatus->execute($authorised_valuecode);
673 while (my $lib = $shelfstatus->fetchrow_hashref){
674 $shelves{$lib->{'authorised_value'}} = $lib->{'lib'};
679 my $facets_counter = ();
680 my $facets_info = ();
681 my @facets_loop; # stores the ref to array of hashes for template
683 foreach my $xml(@marcrecords){
685 if (C4::Context->preference('useFacets')){
686 ($facets_counter,$facets_info)=FillFacets($xml,$facets_counter,$facets_info);
688 my @kohafields; ## just name those necessary for the result page
689 push @kohafields, "biblionumber","title","author","publishercode","classification","itemtype","copyrightdate", "holdingbranch","date_due","location","shelf","itemcallnumber","notforloan","itemlost","wthdrawn";
690 my ($oldbiblio,@itemrecords) = XMLmarc2koha($dbh,$xml,"",@kohafields);
698 ##Loop for each item field
700 foreach my $item (@itemrecords) {
701 $norequests = 0 unless $item->{'itemnotforloan'};
704 #renaming some fields according to templates
705 $item->{'branchname'}=$branches{$item->{'holdingbranch'}};
706 $item->{'shelves'}=$shelves{$item->{'shelf'}};
707 $status="Lost" if ($item->{'itemlost'}>0);
708 $status="Withdrawn" if ($item->{'wthdrawn'}>0);
709 if ($intranet eq "intranet"){ ## we give full itemcallnumber detail in intranet
710 $status="Due:".format_date($item->{'date_due'}) if ($item->{'date_due'} gt "0000-00-00");
711 $status = $item->{'holdingbranch'}."-".$item->{'shelf'}."[".$item->{'itemcallnumber'}."]" unless defined $status;
713 $status="On Loan" if ($item->{'date_due'} gt "0000-00-00");
714 $status = $item->{'branchname'}."[".$item->{'shelves'}."]" unless defined $status;
720 $oldbiblio->{'noitems'} = $noitems;
721 $oldbiblio->{'norequests'} = $norequests;
722 $oldbiblio->{'even'} = $even;
725 $oldbiblio->{'toggle'}="#ffffcc";
727 $oldbiblio->{'toggle'}="white";
728 } ; ## some forms seems to use toggle
730 $oldbiblio->{'itemcount'} = $counts{'total'};
731 my $totalitemcounts = 0;
732 foreach my $key (keys %counts){
733 if ($key ne 'total'){
734 $totalitemcounts+= $counts{$key};
735 $oldbiblio->{'locationhash'}->{$key}=$counts{$key};
739 my ($locationtext, $locationtextonly, $notavailabletext) = ('','','');
740 foreach (sort keys %{$oldbiblio->{'locationhash'}}) {
742 if ($_ eq 'notavailable') {
743 $notavailabletext="Not available";
744 my $c=$oldbiblio->{'locationhash'}->{$_};
745 $oldbiblio->{'not-available-p'}=$c;
748 my $c=$oldbiblio->{'locationhash'}->{$_};
750 $oldbiblio->{'lost-p'} = $c;
751 } elsif ($_ eq 'Withdrawn') {
752 $oldbiblio->{'withdrawn-p'} = $c;
753 } elsif ($_ =~/\^Due:/) {
755 $oldbiblio->{'on-loan-p'} = $c;
757 $locationtextonly.= $_;
758 $locationtextonly.= " ($c)<br> " if $totalitemcounts > 1;
760 if ($totalitemcounts>1) {
761 $locationtext.=" ($c)<br> ";
765 if ($notavailabletext) {
766 $locationtext.= $notavailabletext;
768 $locationtext=~s/, $//;
770 $oldbiblio->{'location'} = $locationtext;
771 $oldbiblio->{'location-only'} = $locationtextonly;
772 $oldbiblio->{'use-location-flags-p'} = 1;
773 push @results,$oldbiblio;
775 }## For each record received
776 @facets_loop=BuildFacets($facets_counter,$facets_info,%branches);
778 return(@facets_loop,@results);
782 my ($facet_record,$facets_counter,$facets_info)=@_;
783 my $facets = C4::Koha::getFacets();
784 for (my $k=0; $k<@$facets;$k++) {
785 my $tags=@$facets->[$k]->{tags};
786 my $subfields=@$facets->[$k]->{subfield};
788 for (my $i=0; $i<@$tags;$i++) {
790 $type="holdings" if @$facets->[$k]->{'link_value'} =~/branch/; ## if using other facets from items add them here
791 if ($type eq "holdings"){
792 ###Read each item record
793 my $holdings=$facet_record->{holdings}->[0]->{record};
794 foreach my $holding(@$holdings){
795 my $data=XML_readline($holding,"","holdings",@$tags[$i],@$subfields[$i]);
796 $facets_counter->{ @$facets->[$k]->{'link_value'} }->{ $data }++ if $data;
799 my $data=XML_readline($facet_record,"","biblios",@$tags[$i],@$subfields[$i]);
800 $facets_counter->{ @$facets->[$k]->{'link_value'} }->{ $data }++ if $data;
803 $facets_info->{ @$facets->[$k]->{'link_value'} }->{ 'label_value' } = @$facets->[$k]->{'label_value'};
804 $facets_info->{ @$facets->[$k]->{'link_value'} }->{ 'expanded' } = @$facets->[$k]->{'expanded'};
806 return ($facets_counter,$facets_info);
810 my ($facets_counter, $facets_info,%branches) = @_;
812 my @facets_loop; # stores the ref to array of hashes for template
814 foreach my $link_value ( sort { $facets_counter->{$b} <=> $facets_counter->{$a} } keys %$facets_counter) {
816 my $number_of_facets;
817 my @this_facets_array;
818 foreach my $one_facet ( sort { $facets_counter->{ $link_value }->{$b} <=> $facets_counter->{ $link_value }->{$a} } keys %{$facets_counter->{$link_value}} ) {
820 if (($number_of_facets < 11) || ($facets_info->{ $link_value }->{ 'expanded'})) {
822 # sanitize the link value ), ( will cause errors with CCL
823 my $facet_link_value = $one_facet;
824 $facet_link_value =~ s/(\(|\))/ /g;
826 # fix the length that will display in the label
827 my $facet_label_value = $one_facet;
828 $facet_label_value = substr($one_facet,0,20)."..." unless length($facet_label_value)<=20;
829 # well, if it's a branch, label by the name, not the code
830 if ($link_value =~/branch/) {
831 $facet_label_value = $branches{$one_facet};
834 # but we're down with the whole label being in the link's title
835 my $facet_title_value = $one_facet;
837 push @this_facets_array ,
838 ( { facet_count => $facets_counter->{ $link_value }->{ $one_facet },
839 facet_label_value => $facet_label_value,
840 facet_title_value => $facet_title_value,
841 facet_link_value => $facet_link_value,
842 type_link_value => $link_value,
845 }## if $number_of_facets
847 unless ($facets_info->{ $link_value }->{ 'expanded'}) {
848 $expandable=1 if ($number_of_facets > 10);
851 { type_link_value => $link_value,
852 type_id => $link_value."_id",
853 type_label => $facets_info->{ $link_value }->{ 'label_value' },
854 facets => \@this_facets_array,
855 expandable => $expandable,
856 expand => $link_value,
861 return \@facets_loop;
866 ## return the address of a cover image if defined otherwise the amazon cover images
869 my $image=XML_readline_onerecord($record,"coverphoto","biblios");
873 # if there is no image put the amazon cover image adress
875 my $isbn=XML_readline_onerecord($record,"isbn","biblios");
876 return "http://images.amazon.com/images/P/".$isbn.".01.MZZZZZZZ.jpg";
881 ($count, $lcount, $nacount, $fcount, $scount, $lostcount,
882 $mending, $transit,$ocount) =
883 &itemcount($env, $biblionumber, $type);
885 Counts the number of items with the given biblionumber, broken down by
890 If C<$type> is not set to C<intra>, lost, very overdue, and withdrawn
891 items will not be counted.
893 C<&itemcount> returns a nine-element list:
895 C<$count> is the total number of items with the given biblionumber.
897 C<$lcount> is the number of items at the Levin branch.
899 C<$nacount> is the number of items that are neither borrowed, lost,
900 nor withdrawn (and are therefore presumably on a shelf somewhere).
902 C<$fcount> is the number of items at the Foxton branch.
904 C<$scount> is the number of items at the Shannon branch.
906 C<$lostcount> is the number of lost and very overdue items.
908 C<$mending> is the number of items at the Mending branch (being
911 C<$transit> is the number of items at the Transit branch (in transit
914 C<$ocount> is the number of items that haven't arrived yet
915 (aqorders.quantity - aqorders.quantityreceived).
923 my ($env,$bibnum,$type)=@_;
924 my $dbh = C4::Context->dbh;
930 my $query="Select * from items where
932 push @kohafield,"biblionumber";
935 my ($total,@result)=ZEBRAsearch_kohafields(\@kohafield,\@value, \@relation,"", \@and_or, 0);## there is only one record no need for $num or $offset
936 my @fields;## extract only the fields required
937 push @fields,"itemnumber","itemlost","wthdrawn","holdingbranch","date_due";
938 my ($biblio,@items)=XMLmarc2koha ($dbh,$result[0],"holdings",\@fields);
948 foreach my $data(@items){
949 if ($type ne "intra"){
950 next if ($data->{itemlost} || $data->{wthdrawn});
951 } ## Probably trying to hide lost item from opac ?
954 ## Now it seems we want to find those which are onloan
957 if ( $data->{date_due} gt "0000-00-00"){
961 ### The rest of this code is hardcoded for Foxtrot Shanon etc. We urgently need a global understanding of these terms--TG
962 if ($data->{'holdingbranch'} eq 'C' || $data->{'holdingbranch'} eq 'LT'){
965 if ($data->{'holdingbranch'} eq 'F' || $data->{'holdingbranch'} eq 'FP'){
968 if ($data->{'holdingbranch'} eq 'S' || $data->{'holdingbranch'} eq 'SP'){
971 if ($data->{'itemlost'} eq '1'){
974 if ($data->{'itemlost'} eq '2'){
977 if ($data->{'holdingbranch'} eq 'FM'){
980 if ($data->{'holdingbranch'} eq 'TR'){
986 my $sth2=$dbh->prepare("Select * from aqorders where biblionumber=?");
987 $sth2->execute($bibnum);
988 if (my $data=$sth2->fetchrow_hashref){
989 $ocount=$data->{'quantity'} - $data->{'quantityreceived'};
993 return ($count,$lcount,$nacount,$fcount,$scount,$lostcount,$mending,$transit,$ocount);
996 END { } # module clean-up code here (global destructor)
1005 Koha Developement team <info@koha.org>
1006 # New functions to comply with ZEBRA search and new KOHA 3 API added 2006 Tumer Garip tgarip@neu.edu.tr