3 # Copyright 2000-2002 Katipo Communications
4 # New functions added 22-09-2005 Tumer Garip tgarip@neu.edu.tr
5 # This file is part of Koha.
7 # Koha is free software; you can redistribute it and/or modify it under the
8 # terms of the GNU General Public License as published by the Free Software
9 # Foundation; either version 2 of the License, or (at your option) any later
12 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14 # A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License along with
17 # Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
18 # Suite 330, Boston, MA 02111-1307 USA
29 use MARC::File::USMARC;
32 # FIXME - C4::Search uses C4::Reserves2, which uses C4::Search.
33 # So Perl complains that all of the functions here get redefined.
36 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
38 # set the version for version checking
39 $VERSION = do { my @v = '$Revision$' =~ /\d+/g;
40 shift(@v) . "." . join("_", map {sprintf "%03d", $_ } @v); };
44 C4::Search - Functions for searching the Koha catalog and other databases
50 my ($count, @results) = catalogsearch($env, $type, $search, $num, $offset);
54 This module provides the searching facilities for the Koha catalog and
57 C<&catalogsearch> is a front end to all the other searches. Depending
58 on what is passed to it, it calls the appropriate search function.
69 &CatSearch &BornameSearch &ItemInfo &KeywordSearch &subsearch
70 &itemdata &bibdata &GetItems &borrdata &itemnodata &itemcount
71 &borrdata2 &borrdata3 &NewBorrowerNumber &bibitemdata &borrissues
72 &getboracctrecord &ItemType &itemissues &subject &subtitle
73 &addauthor &bibitems &barcodes &findguarantees &allissues
74 &findseealso &findguarantor &getwebsites &getwebbiblioitems &itemcount2 &FindDuplicate
75 &isbnsearch &getbranchname &getborrowercategory &getborrowercategoryinfo
77 &searchZOOM &catalogsearch &catalogsearch3 &CatSearch3 &catalogsearch4 &searchResults
79 &getRecords &buildQuery
81 &getMARCnotes &getMARCsubjects &getMARCurls);
82 # make all your functions, whether exported or not;
84 =head1 findseealso($dbh,$fields);
86 =head2 $dbh is a link to the DB handler.
89 my $dbh =C4::Context->dbh;
91 =head2 $fields is a reference to the fields array
93 This function modify the @$fields array and add related fields to search on.
98 my ($dbh, $fields) = @_;
99 my $tagslib = MARCgettagslib ($dbh,1);
100 for (my $i=0;$i<=$#{$fields};$i++) {
101 my ($tag) =substr(@$fields[$i],1,3);
102 my ($subfield) =substr(@$fields[$i],4,1);
103 @$fields[$i].=','.$tagslib->{$tag}->{$subfield}->{seealso} if ($tagslib->{$tag}->{$subfield}->{seealso});
109 ($num_children, $children_arrayref) = &findguarantees($parent_borrno);
110 $child0_cardno = $children_arrayref->[0]{"cardnumber"};
111 $child0_borrno = $children_arrayref->[0]{"borrowernumber"};
113 C<&findguarantees> takes a borrower number (e.g., that of a patron
114 with children) and looks up the borrowers who are guaranteed by that
115 borrower (i.e., the patron's children).
117 C<&findguarantees> returns two values: an integer giving the number of
118 borrowers guaranteed by C<$parent_borrno>, and a reference to an array
119 of references to hash, which gives the actual results.
125 my $dbh = C4::Context->dbh;
126 my $sth=$dbh->prepare("select cardnumber,borrowernumber, firstname, surname from borrowers where guarantor=?");
127 $sth->execute($bornum);
130 while (my $data = $sth->fetchrow_hashref)
135 return (scalar(@dat), \@dat);
140 $guarantor = &findguarantor($borrower_no);
141 $guarantor_cardno = $guarantor->{"cardnumber"};
142 $guarantor_surname = $guarantor->{"surname"};
145 C<&findguarantor> takes a borrower number (presumably that of a child
146 patron), finds the guarantor for C<$borrower_no> (the child's parent),
147 and returns the record for the guarantor.
149 C<&findguarantor> returns a reference-to-hash. Its keys are the fields
150 from the C<borrowers> database table;
156 my $dbh = C4::Context->dbh;
157 my $sth=$dbh->prepare("select guarantor from borrowers where borrowernumber=?");
158 $sth->execute($bornum);
159 my $data=$sth->fetchrow_hashref;
161 $sth=$dbh->prepare("Select * from borrowers where borrowernumber=?");
162 $sth->execute($data->{'guarantor'});
163 $data=$sth->fetchrow_hashref;
168 =item NewBorrowerNumber
170 $num = &NewBorrowerNumber();
172 Allocates a new, unused borrower number, and returns it.
176 # FIXME - This is identical to C4::Circulation::Borrower::NewBorrowerNumber.
177 # Pick one and stick with it. Preferably use the other one. This function
178 # doesn't belong in C4::Search.
179 sub NewBorrowerNumber {
180 my $dbh = C4::Context->dbh;
181 my $sth=$dbh->prepare("Select max(borrowernumber) from borrowers");
183 my $data=$sth->fetchrow_hashref;
185 $data->{'max(borrowernumber)'}++;
186 return($data->{'max(borrowernumber)'});
191 ($count, @results) = &catalogsearch($env, $type, $search, $num, $offset);
193 This is primarily a front-end to other, more specialized catalog
194 search functions: if C<$search-E<gt>{itemnumber}> or
195 C<$search-E<gt>{isbn}> is given, C<&catalogsearch> uses a precise
196 C<&CatSearch>. If $search->{subject} is given, it runs a subject
197 C<&CatSearch>. If C<$search-E<gt>{keyword}> is given, it runs a
198 C<&KeywordSearch>. Otherwise, it runs a loose C<&CatSearch>.
200 If C<$env-E<gt>{itemcount}> is 1, then C<&catalogsearch> also counts
201 the items for each result, and adds several keys:
207 The total number of copies of this book.
209 =item C<locationhash>
211 This is a reference-to-hash; the keys are the names of branches where
212 this book may be found, and the values are the number of copies at
217 A descriptive string saying where the book is located, and how many
218 copies there are, if greater than 1.
222 The book's subject, with spaces replaced with C<%20>, presumably for
230 my ($dbh, $tags, $and_or, $excluding, $operator, $value, $offset,$length,$orderby,$desc_or_asc) = @_;
232 # used for the new API
233 my ($search_or_scan,$type,$query,$num,$startfrom,$then_sort_by);
235 $search_or_scan = 'search';
237 my $number_of_results = $length; # num of results to return
238 $startfrom = $offset; # offset
240 for (my $i = 0 ; $i <= $#{$value} ; $i++) {
241 $ccl_query.= @$value[$i];
243 my ($error,$count,$facets,@results) = searchZOOM('search','ccl',$ccl_query,$number_of_results,$startfrom,$then_sort_by);
246 my $subtitle; # Added by JF for Subtitles
248 # find bibids from results
250 foreach my $rec (@results) {
251 my $record = MARC::Record->new_from_usmarc($rec);
252 my $oldbiblio = MARCmarc2koha($dbh,$record,'');
253 push @result, $oldbiblio->{'biblionumber'}; #FIXME bibid?
255 # we have bibid list. Now, loads title and author from [offset] to [offset]+[length]
256 my $counter = $offset;
257 # HINT : biblionumber as bn is important. The hash is fills biblionumber with items.biblionumber.
258 # so if you dont' has an item, you get a not nice empty value.
259 my $sth = $dbh->prepare("SELECT biblio.biblionumber as bn,biblioitems.*,biblio.*, itemtypes.notforloan,itemtypes.description
261 LEFT JOIN biblioitems on biblio.biblionumber = biblioitems.biblionumber
262 LEFT JOIN itemtypes on itemtypes.itemtype=biblioitems.itemtype
263 WHERE biblio.biblionumber = ?"); #marc_biblio.biblionumber AND bibid = ?");
264 my $sth_subtitle = $dbh->prepare("SELECT subtitle FROM bibliosubtitle WHERE biblionumber=?"); # Added BY JF for Subtitles
265 my @finalresult = ();
269 my ($oldbibid, $oldauthor, $oldtitle);
271 if (C4::Context->preference('hidelostitems')) {
272 $sth_itemCN = $dbh->prepare("select items.* from items where biblionumber=? and (itemlost = 0 or itemlost is NULL) order by homebranch");
274 $sth_itemCN = $dbh->prepare("select items.* from items where biblionumber=? order by homebranch");
276 my $sth_issue = $dbh->prepare("select date_due,returndate from issues where itemnumber=?");
277 # parse all biblios between start & end.
278 #while (($counter <= $#result) && ($counter <= ($offset + $length))) { #FIXME, do all of them
279 while ($counter <= $#result) {
280 # search & parse all items & note itemcallnumber
281 #warn $result[$counter];
282 $sth->execute($result[$counter]);
284 my $line = $sth->fetchrow_hashref;
285 my $biblionumber=$line->{bn};
286 # Return subtitles first ADDED BY JF
287 $sth_subtitle->execute($biblionumber);
288 my $subtitle_here.= $sth_subtitle->fetchrow." ";
290 $subtitle = $subtitle_here;
291 # warn "Here's the Biblionumber ".$biblionumber;
292 # warn "and here's the subtitle: ".$subtitle_here;
296 # $continue=0 unless $line->{bn};
297 # my $lastitemnumber;
298 $sth_itemCN->execute($biblionumber);
300 my $notforloan=1; # to see if there is at least 1 item that can be issued
301 while (my $item = $sth_itemCN->fetchrow_hashref) {
302 # parse the result, putting holdingbranch & itemcallnumber in separate array
303 # then all other fields in the main array
305 # search if item is on loan
307 $sth_issue->execute($item->{itemnumber});
308 while (my $loan = $sth_issue->fetchrow_hashref) {
309 if ($loan->{date_due} and !$loan->{returndate}) {
310 $date_due = $loan->{date_due};
315 $lineCN{holdingbranch} = $item->{holdingbranch};
316 $lineCN{itemcallnumber} = $item->{itemcallnumber};
317 $lineCN{location} = $item->{location};
318 $lineCN{date_due} = format_date($date_due);
319 #$lineCN{notforloan} = $notforloanstatus{$line->{notforloan}} if ($line->{notforloan}); # setting not forloan if itemtype is not for loan
320 #$lineCN{notforloan} = $notforloanstatus{$item->{notforloan}} if ($item->{notforloan}); # setting not forloan it this item is not for loan
321 $notforloan=0 unless ($item->{notforloan} or $item->{wthdrawn} or $item->{itemlost});
322 push @CNresults,\%lineCN;
325 # save the biblio in the final array, with item and item issue status
328 $newline{totitem} = $totalitems;
329 # if $totalitems == 0, check if it's being ordered.
330 if ($totalitems == 0) {
331 my $sth = $dbh->prepare("select count(*) from aqorders where biblionumber=? and datecancellationprinted is NULL");
332 $sth->execute($biblionumber);
333 my ($ordered) = $sth->fetchrow;
334 $newline{onorder} = 1 if $ordered;
336 $newline{biblionumber} = $biblionumber;
337 $newline{norequests} = 0;
338 $newline{norequests} = 1 if ($line->{notforloan}); # itemtype not issuable
339 $newline{norequests} = 1 if (!$line->{notforloan} && $notforloan); # itemtype issuable but all items not issuable for instance
340 $newline{subtitle} = $subtitle; # put the subtitle in ADDED BY JF
342 my @CNresults2= @CNresults;
343 $newline{CN} = \@CNresults2;
344 $newline{'even'} = 1 if $#finalresult % 2 == 0;
345 $newline{'odd'} = 1 if $#finalresult % 2 == 1;
346 $newline{'timestamp'} = format_date($newline{timestamp});
348 push @finalresult, \%newline;
352 my $nbresults = $#result+1;
353 return (\@finalresult, $nbresults);
357 sub add_html_bold_fields {
358 my ($type, $data, $search) = @_;
360 my %reference = ('additionalauthors' => 'author',
361 'publishercode' => 'publisher',
362 'subtitle' => 'title'
365 foreach my $key ('title', 'author', 'additionalauthors', 'publishercode', 'publicationyear', 'subject', 'subtitle') {
367 if ($key eq 'additionalauthors') {
368 $new_key = 'additionalauthors';
370 $new_key = 'bold_' . $key;
371 $data->{$new_key} = $data->{$key};
375 if ($reference{$key}) {
376 $key1 = $reference{$key};
383 if ($type eq 'keyword') {
384 my $newkey=$search->{'keyword'};
386 @keys = split " ", $newkey;
388 while ($search->{"field_value$i"}) {
389 my $newkey=$search->{"field_value$i"};
396 for ($i = 0; $i < $count ; $i++) {
397 if ($key eq 'additionalauthors') {
399 foreach (@{$data->{$new_key}}) {
400 if (!$data->{$new_key}->[$j]->{'bold_value'}) {
401 $data->{$new_key}->[$j]->{'bold_value'} = $data->{$new_key}->[$j]->{'value'};
403 if ( ($data->{$new_key}->[$j]->{'value'} =~ /($keys[$i])/i) && (lc($keys[$i]) ne 'b') ) {
405 $data->{$new_key}->[$j]->{'bold_value'} =~ s/$word/<b>$word<\/b>/;
410 if (($data->{$new_key} =~ /($keys[$i])/i) && (lc($keys[$i]) ne 'b') ) {
412 $data->{$new_key} =~ s/$word/<b>$word<\/b>/;
422 my ($search,$num,$offset) = @_;
423 my $dbh = C4::Context->dbh;
424 my ($count,@results);
426 if ($search->{'itemnumber'} ne '' || $search->{'isbn'} ne ''|| $search->{'biblionumber'} ne ''){
427 ($count,@results) = CatSearch3('precise',$search,$num,$offset);
428 } elsif ($search->{'keyword'} ne ''){
429 ($count,@results) = CatSearch3('keyword',$search,$num,$offset);
430 } elsif ($search->{'recently_items'} ne '') {
431 ($count,@results) = CatSearch3('recently_items',$search,$num,$offset);
433 ($count,@results) = CatSearch3('loose',$search,$num,$offset);
437 return ($count,@results);
442 my ($type,$search,$num,$offset)=@_;
443 my $dbh = C4::Context->dbh;
444 my $query = ''; #to make the query statement
445 my $count_query = ''; #to count total results
446 my @params = (); #to collect the params
447 my @results; #to retrieve the results
449 # 1) do a search by barcode or isbn
450 if ($type eq 'precise') {
452 if ($search->{'itemnumber'} ne ''){
453 $query = "SELECT biblionumber FROM items WHERE (barcode = ?)";
454 push @params, $search->{'itemnumber'};
456 } elsif ($search->{'isbn'} ne '') {
457 $query = "SELECT biblionumber FROM biblioitems WHERE (isbn like ?)";
458 push @params, $search->{'isbn'};
460 $query = "SELECT biblionumber FROM biblioitems WHERE (biblionumber = ?)";
461 push @params, $search->{'biblionumber'};
464 #add branch condition
465 if ($search->{'branch'} ne '') {
466 $query.= " AND ( holdingbranch like ? ) ";
467 my $keys = $search->{'branch'};
471 # 2) do a search by keyword
472 } elsif ($type eq 'keyword') {
473 my $keys = $search->{'keyword'};
474 my @words = split / /, $keys;
478 if ($search->{'ttype'} eq 'exact') {
479 for (my $i = 0; $i < @words ;$i++) {
480 if ($i + 1 == @words) {
481 $words[$i] = '+' . $words[$i] . '*';
483 $words[$i] = '+' . $words[$i];
487 for (my $i = 0; $i < @words ;$i++) {
488 $words[$i] = $words[$i] . '*';
491 $keyword = join " ", @words;
494 $query = "(SELECT DISTINCT B.biblionumber AS biblionumber ,( MATCH (title,seriestitle,unititle,B.author,subject,publishercode,itemcallnumber) AGAINST(? in BOOLEAN MODE) ) as Relevance
496 LEFT JOIN biblioitems AS BI ON (B.biblionumber = BI.biblionumber)
497 LEFT JOIN items AS I ON (BI.biblionumber = I.biblionumber)
498 LEFT JOIN additionalauthors AA1 ON (B.biblionumber = AA1.biblionumber)
499 LEFT JOIN bibliosubject AS BS1 ON (B.biblionumber = BS1.biblionumber)
500 LEFT JOIN bibliosubtitle AS BSU1 ON (B.biblionumber = BSU1.biblionumber)
501 where MATCH (title,seriestitle,unititle,B.author,subject,publishercode,itemcallnumber) AGAINST (? IN BOOLEAN MODE) ";
503 push @params,$keyword;
504 push @params,$keyword;
506 if ($search->{'class'} ne '') {
507 $query .= " AND ( itemtype = ? ) ";
508 push @params, $search->{'class'};
511 if ($search->{'branch'} ne '') {
512 $query .= " AND ( items.holdingbranch like ? ) ";
513 push @params, $search->{'branch'};
515 if ($search->{'stack'} ne '') {
516 $query .= " AND ( items.stack = ? ) ";
517 push @params, $search->{'stack'};
519 #search by publication year
520 if ($search->{'date_from'} ne '') {
521 $query .= " AND ( biblioitems.publicationyear >= ?) ";
522 push @params, $search->{'date_from'};
523 if ($search->{'date_to'} ne '') {
524 $query .= " AND ( biblioitems.publicationyear <= ?) ";
525 push @params, $search->{'date_to'};
534 # 3) search the items acquired recently (in the last $search->{'range'} days)
535 } elsif ($type eq 'recently_items') {
537 if ($search->{'range'}) {
538 $keys = $search->{'range'};
542 $query = "SELECT B.biblionumber FROM biblio AS B
543 LEFT JOIN biblioitems AS BI ON (B.biblionumber = BI.biblionumber)
546 (TO_DAYS( NOW( ) ) - TO_DAYS( B.timestamp ))<?";
549 if ($search->{'class'} ne '') {
550 $query .= " AND ( BI.itemtype = ? ) ";
551 push @params, $search->{'class'};
553 $query.= " ORDER BY title ";
555 # 4) do a loose search
558 my ($condition1, $condition2, $condition3) = ('','','');
559 my $count_params = 0;
563 if ($search->{'field_name1'} eq 'all') {
564 $condition1.= " ( MATCH (title,seriestitle,unititle,B.author,subject,publishercode,itemcallnumber) AGAINST(? in BOOLEAN MODE) ) ";
567 } elsif ($search->{'field_name1'} eq 'author') {
568 $condition1.= " ( MATCH (B.author) AGAINST(? in BOOLEAN MODE) ) ";
570 } elsif ($search->{'field_name1'} eq 'title') {
571 $condition1.= " ( MATCH (title,seriestitle,unititle) AGAINST(? in BOOLEAN MODE ) ) ";
573 } elsif ($search->{'field_name1'} eq 'subject') {
574 $condition1.= " ( ( MATCH (subject) AGAINST(? in BOOLEAN MODE) ) ) ";
576 } elsif ($search->{'field_name1'} eq 'publisher') {
577 $condition1.= " ( MATCH (publishercode) AGAINST(? in BOOLEAN MODE )) ";
579 } elsif ($search->{'field_name1'} eq 'publicationyear') {
580 $condition1.= " ( MATCH (publicationyear) AGAINST(? in BOOLEAN MODE )) ";
582 } elsif ($search->{'field_name1'} eq 'callno') {
583 $condition1.= " ( MATCH (itemcallnumber) AGAINST(? in BOOLEAN MODE )) ";
587 if ($search->{'ttype1'} eq 'exact') {
588 push @params,"\"".$search->{'field_value1'}."\"";
589 push @params, "\"".$search->{'field_value1'}."\"";
591 my $keys = $search->{'field_value1'};
592 my @words = split / /, $keys;
595 for (my $i = 0; $i < @words ;$i++) {
596 $words[$i] = '+'. $words[$i] . '*';
598 $keyword = join " ", @words;
599 push @params, $keyword;
600 push @params, $keyword;
604 $query = " SELECT DISTINCT B.biblionumber AS biblionumber ,$condition1 as Relevance
606 LEFT JOIN biblioitems AS BI ON (B.biblionumber = BI.biblionumber)
607 LEFT JOIN items AS I ON (BI.biblionumber = I.biblionumber)
608 LEFT JOIN additionalauthors AA1 ON (B.biblionumber = AA1.biblionumber)
609 LEFT JOIN bibliosubject AS BS1 ON (B.biblionumber = BS1.biblionumber)
610 LEFT JOIN bibliosubtitle AS BSU1 ON (B.biblionumber = BSU1.biblionumber) ";
614 if ( ($search->{'field_value1'}) && ($search->{'field_value2'}) ) {
615 if ($search->{'field_name2'} eq 'all') {
616 $condition2.= " MATCH (title,seriestitle,unititle,B.author,subject,publishercode,itemcallnumber) AGAINST( ? in BOOLEAN MODE) ) ";
619 } elsif ($search->{'field_name2'} eq 'author') {
620 $condition2.= " MATCH (B.author,AA1.author) AGAINST( ? in BOOLEAN MODE) ) ";
622 } elsif ($search->{'field_name2'} eq 'title') {
623 $condition2.= " MATCH (title,seriestitle,unititle) AGAINST( ? in BOOLEAN MODE ) ) ";
625 } elsif ($search->{'field_name2'} eq 'subject') {
626 $condition2.= " MATCH (subject) AGAINST(? in BOOLEAN MODE) ) ";
628 } elsif ($search->{'field_name2'} eq 'publisher') {
629 $condition2.= " MATCH (publishercode) AGAINST(? in BOOLEAN MODE )) ";
631 } elsif ($search->{'field_name2'} eq 'publicationyear') {
632 $condition2.= " MATCH (publicationyear) AGAINST(? in BOOLEAN MODE )) ";
634 } elsif ($search->{'field_name2'} eq 'callno') {
635 $condition2.= " MATCH (itemcallnumber) AGAINST(? in BOOLEAN MODE )) ";
638 if ($search->{'op1'} eq "not"){
639 $search->{'op1'}="and (not ";
641 $search->{'op1'}.=" (";
644 if ($search->{'ttype2'} eq 'exact') {
645 push @params, "\"".$search->{'field_value2'}."\"";
647 my $keys = $search->{'field_value2'};
648 my @words = split / /, $keys;
651 for (my $i = 0; $i < @words ;$i++) {
652 $words[$i] = "+". $words[$i] . '*';
654 $keyword = join " ", @words;
655 push @params, $keyword;
661 if ( ($search->{'field_value2'}) && ($search->{'field_value3'}) ) {
663 if ($search->{'field_name3'} eq 'all') {
664 $condition3.= " MATCH (title,seriestitle,unititle,B.author,subject,publishercode,itemcallnumber) AGAINST(? in BOOLEAN MODE ) ) ";
667 } elsif ($search->{'field_name3'} eq 'author') {
668 $condition3.= " MATCH (B.author,AA1.author) AGAINST(? in BOOLEAN MODE) ) ";
670 } elsif ($search->{'field_name3'} eq 'title') {
671 $condition3.= " MATCH (title,seriestitle,unititle) AGAINST(? in BOOLEAN MODE) ) ";
673 } elsif ($search->{'field_name3'} eq 'subject') {
674 $condition3.= " MATCH (subject) AGAINST(? in BOOLEAN MODE ) ) ";
676 } elsif ($search->{'field_name3'} eq 'publisher') {
677 $condition3.= " MATCH (publishercode) AGAINST(? in BOOLEAN MODE )) ";
679 } elsif ($search->{'field_name3'} eq 'publicationyear') {
680 $condition3.= " MATCH (publicationyear) AGAINST(? in BOOLEAN MODE )) ";
682 } elsif ($search->{'field_name3'} eq 'callno') {
683 $condition3.= " MATCH (itemcallnumber) AGAINST(? in BOOLEAN MODE )) ";
686 if ($search->{'op2'} eq "not"){
687 $search->{'op2'}="and (not ";
689 $search->{'op2'}.=" (";
691 if ($search->{'ttype3'} eq 'exact') {
692 push @params, "\"".$search->{'field_value3'}."\"";
694 my $keys = $search->{'field_value3'};
695 my @words = split / /, $keys;
699 for (my $i = 0; $i < @words ;$i++) {
700 $words[$i] = "+". $words[$i] . '*';
702 $keyword = join " ", @words;
703 push @params, $keyword;
708 if (($condition1 ne '') && ($condition2 ne '') && ($condition3 ne '')) {
709 if ($search->{'op1'} eq $search->{'op2'}) {
710 $query.= " ( $condition1 $search->{'op1'} $condition2 $search->{'op2'} $condition3 ) ";
711 } elsif ( $search->{'op1'} eq "and (" ) {
712 $query.= " ( $condition1 $search->{'op1'} ( $condition2 $search->{'op2'} $condition3 ) ) ";
714 $query.= " ( ( $condition1 $search->{'op1'} $condition2 ) $search->{'op2'} $condition3 ) ";
716 } elsif ( ($condition1 ne '') && ($condition2 ne '') ) {
717 $query.= " ( $condition1 $search->{'op1'} $condition2 ) ";
719 $query.= " ( $condition1 ) ";
723 if ($search->{'class'} ne ''){
724 $query.= " AND ( itemtype = ? ) ";
725 my $keys = $search->{'class'};
726 push @params, $search->{'class'};
729 if ($search->{'branch'} ne '') {
730 $query.= " AND I.holdingbranch like ? ";
731 my $keys = $search->{'branch'};
732 push @params, $keys, $keys;
734 #search by publication year
735 if ($search->{'date_from'} ne '') {
736 $query .= " AND ( BI.publicationyear >= ?) ";
737 push @params, $search->{'date_from'};
738 if ($search->{'date_to'} ne '') {
739 $query .= " AND ( BI.publicationyear <= ?) ";
740 push @params, $search->{'date_to'};
744 if ($search->{'order'} eq "1=1003 i<"){
745 $query.= " ORDER BY b.author ";
746 }elsif ($search->{'order'} ge "1=9 i<"){
747 $query.= " ORDER BY lcsort ";
748 }elsif ($search->{'order'} eq "1=4 i<"){
749 $query.= " ORDER BY title ";
751 $query.=" ORDER BY Relevance DESC";
755 #warn "$query,@params,";
756 $count_query = $query;
757 warn "QUERY:".$count_query;
758 #execute the query and returns just the results between $num and $num + $offset
759 my $limit = $num + $offset;
760 my $startfrom = $offset;
761 my $sth = $dbh->prepare($query);
763 $sth->execute(@params);
766 #Build brancnames hash
768 #get branch information.....
770 my $bsth=$dbh->prepare("SELECT branchcode,branchname FROM branches");
772 while (my $bdata=$bsth->fetchrow_hashref){
773 $branches{$bdata->{'branchcode'}}= $bdata->{'branchname'};
777 #Building shelving hash
780 my $stackstatus = $dbh->prepare('select authorised_value from marc_subfield_structure where kohafield="items.stack"');
781 $stackstatus->execute;
783 my ($authorised_valuecode) = $stackstatus->fetchrow;
784 if ($authorised_valuecode) {
785 $stackstatus = $dbh->prepare("select lib,authorised_value from authorised_values where category=? ");
786 $stackstatus->execute($authorised_valuecode);
788 while (my $lib = $stackstatus->fetchrow_hashref){
789 $shelves{$lib->{'authorised_value'}} = $lib->{'lib'};
793 #search item field code
796 "select tagfield from marc_subfield_structure where kohafield like 'items.itemnumber'"
799 my ($itemtag) = $sth3->fetchrow;
800 ## find column names of items related to MARC
801 my $sth2=$dbh->prepare("SHOW COLUMNS from items");
803 my %subfieldstosearch;
804 while ((my $column)=$sth2->fetchrow){
805 my ($tagfield,$tagsubfield) = &MARCfind_marc_from_kohafield($dbh,"items.".$column,"");
806 $subfieldstosearch{$column}=$tagsubfield;
810 #proccess just the results to show
811 while (my( $data,$rel) = $sth->fetchrow) {
812 if (($i >= $startfrom) && ($i < $limit)) {
814 my $marcrecord=MARCgetbiblio($dbh,$data);
815 my $oldbiblio=MARCmarc2koha($dbh,$marcrecord,'');
818 &add_html_bold_fields($type, $oldbiblio, $search);
824 $oldbiblio->{'toggle'}=$toggle;
828 my @fields = $marcrecord->field($itemtag);
835 ##Loop for each item field
836 foreach my $field (@fields) {
837 foreach my $code ( keys %subfieldstosearch ) {
839 $item->{$code}=$field->subfield($subfieldstosearch{$code});
844 $item->{'branchname'}=$branches{$item->{'holdingbranch'}};
845 $item->{'shelves'}=$shelves{$item->{stack}};
846 $status="Lost" if ($item->{'itemlost'}>0);
847 $status="Withdrawn" if ($item->{'wthdrawn'}>0) ;
848 if ($search->{'from'} eq "intranet"){
849 $search->{'avoidquerylog'}=1;
850 $status="Due:".format_date($item->{'onloan'}) if ($item->{'onloan'}>0);
851 $status = $item->{'holdingbranch'}."-".$item->{'stack'}."[".$item->{'itemcallnumber'}."]" unless defined $status;
853 $status="On Loan" if ($item->{'onloan'}>0);
854 $status = $item->{'branchname'}."[".$item->{'shelves'}."]" unless defined $status;
866 foreach my $itm (@items) {
867 $norequests = 0 unless $itm->{'itemnotforloan'};
870 $oldbiblio->{'noitems'} = $noitems;
871 $oldbiblio->{'norequests'} = $norequests;
872 $oldbiblio->{'even'} = $even = not $even;
873 $oldbiblio->{'itemcount'} = $counts{'total'};
874 my $totalitemcounts = 0;
875 foreach my $key (keys %counts){
876 if ($key ne 'total'){
877 $totalitemcounts+= $counts{$key};
878 $oldbiblio->{'locationhash'}->{$key}=$counts{$key};
882 my ($locationtext, $locationtextonly, $notavailabletext) = ('','','');
883 foreach (sort keys %{$oldbiblio->{'locationhash'}}) {
884 if ($_ eq 'notavailable') {
885 $notavailabletext="Not available";
886 my $c=$oldbiblio->{'locationhash'}->{$_};
887 $oldbiblio->{'not-available-p'}=$c;
890 my $c=$oldbiblio->{'locationhash'}->{$_};
891 if ($_ eq 'Item Lost') {
892 $oldbiblio->{'lost-p'} = $c;
893 } elsif ($_ eq 'Withdrawn') {
894 $oldbiblio->{'withdrawn-p'} = $c;
895 } elsif ($_ eq 'On Loan') {
896 $oldbiblio->{'on-loan-p'} = $c;
898 $locationtextonly.= $_;
899 $locationtextonly.= " ($c)<br> " if $totalitemcounts > 1;
901 if ($totalitemcounts>1) {
902 $locationtext.=" ($c)<br> ";
906 if ($notavailabletext) {
907 $locationtext.= $notavailabletext;
909 $locationtext=~s/, $//;
911 $oldbiblio->{'location'} = $locationtext;
912 $oldbiblio->{'location-only'} = $locationtextonly;
913 $oldbiblio->{'use-location-flags-p'} = 1;
914 push @results, $oldbiblio;
921 unless ($search->{'avoidquerylog'}) {
922 add_query_line($type, $search, $count);}
923 return($count,@results);
927 my ($search,$num,$offset) = @_;
928 my ($count,@results);
930 if ($search->{'itemnumber'} ne '' || $search->{'isbn'} ne ''|| $search->{'biblionumber'} ne ''|| $search->{'authnumber'} ne ''){
931 ($count,@results) = CatSearch4('precise',$search,$num,$offset);
932 } elsif ($search->{'cql'} ne ''){
933 if ($search->{'rpn'} ne '') {
935 ($count,@results) = CatSearch4('rpn',$search,$num,$offset);
937 warn "RPN".$search->{'rpn'};
938 ($count,@results) = CatSearch4('cql',$search,$num,$offset);
940 } elsif ($search->{'keyword'} ne ''){
941 ($count,@results) = CatSearch4('keyword',$search,$num,$offset);
942 } elsif ($search->{'recently_items'} ne '') {
943 ($count,@results) = CatSearch4('recently_items',$search,$num,$offset);
945 ($count,@results) = CatSearch4('loose',$search,$num,$offset);
947 return ($count,@results);
952 my ($type,$search,$num,$offset)=@_;
953 my $dbh = C4::Context->dbh;
954 my $query = ''; #to make the query statement
955 my $count_query = ''; #to count total results
956 my @params = (); #to collect the params
957 my @results; #to retrieve the results
968 # 1) do a search by barcode or isbn
969 if ($type eq 'cql') {
971 $cql_query = $search->{'cql'};
972 while( my ($k, $v) = each %$search ) {
973 warn "key: $k, value: $v.\n";
975 warn "QUERY:".$query;
977 if ($type eq 'rpn') {
980 $cql_query = $search->{'cql'}; #but it's really a rpn query FIXME
982 if ($type eq 'precise') {
984 if ($search->{'itemnumber'} ne '') {
986 $query = " \@attr 1=1028 ". $search->{'itemnumber'};
989 }elsif ($search->{'isbn'} ne ''){
990 $query = " \@attr 1=7 \@attr 4=1 \@attr 5=1 "."\"".$search->{'isbn'}."\"";
992 }elsif ($search->{'biblionumber'} ne ''){
993 $query = " \@attr 1=1007 ".$search->{'biblionumber'};
995 }elsif ($search->{'authnumber'} ne ''){
997 my @ids=split / /,$search->{'authnumber'} ;
998 foreach my $id (@ids){
999 $query .= " \@attr GILS 1=2057 ".$id;
1003 $query= "\@or ".$query;
1007 #add branch condition
1008 if ($search->{'branch'} ne '') {
1009 $query= "\@and ".$query;
1010 $query .= " \@attr 1=1033 \"".$search->{'branch'}."\"";
1013 # 2) do a search by keyword
1014 }elsif ($type eq 'keyword') {
1015 $search->{'keyword'}=~ s/(\\|\|)//g;;
1020 if ($search->{'ttype'} eq 'exact') {
1021 $attr="\@attr 4=1 \@attr 5=1 \@attr 2=102 ";
1023 $attr=" \@attr 4=6 \@attr 5=103 \@attr 2=102 ";
1028 $query = " \@attr 1=1016 ".$attr."\"".$search->{'keyword'}."\"";
1031 #search by itemtypes
1032 if ($search->{'class'} ne '') {
1033 $query= "\@and ".$query;
1034 $query .= " \@attr 1=1031 \"".$search->{'class'}."\"";
1035 push @params, $search->{'class'};
1037 #search by callnumber
1038 if ($search->{'callno'} ne '') {
1039 $query= "\@and ".$query;
1040 $query .= " \@attr 1=20 \@attr 4=1 \@attr 5=1 \"".$search->{'callno'}."\"";
1044 if ($search->{'branch'} ne '') {
1045 $query= "\@and ".$query;
1046 $query .= " \@attr 1=1033 \"".$search->{'branch'}."\"";
1049 if ($search->{'stack'} ne '') {
1050 $query= "\@and ".$query;
1051 $query .= " \@attr 1=1019 \"".$search->{'stack'}."\"";
1052 push @params, $search->{'stack'};
1054 if ($search->{'date_from'} ne '') {
1055 $query= "\@and ".$query;
1056 $query .= " \@attr 1=30 \@attr 2=4 \@attr 4=4 ".$search->{'date_from'};
1057 push @params, $search->{'date_from'};
1059 if ($search->{'date_to'} ne '') {
1060 $query= "\@and ".$query;
1061 $query .= " \@attr 1=30 \@attr 2=2 \@attr 4=4 ".$search->{'date_to'};
1062 push @params, $search->{'date_to'};
1066 # 3) search the items acquired recently (in the last $search->{'range'} days)
1067 } elsif ($type eq 'recently_items') {
1069 if ($search->{'range'}) {
1070 $keys = $search->{'range'}*(-1);
1074 my @datearr = localtime();
1075 my $dateduef = (1900+$datearr[5])."-".sprintf ("%0.2d", ($datearr[4]+1))."-".$datearr[3];
1078 my ($year, $month, $day) = split /-/, $dateduef;
1079 ($year, $month, $day) = &Date::Calc::Add_Delta_Days($year, $month, $day, ($keys - 1));
1080 $dateduef = "$year-$month-$day";
1081 $query .= " \@attr 1=32 \@attr 2=4 \@attr 4=5 ".$dateduef;
1083 push @params, $keys;
1084 if ($search->{'class'} ne '') {
1085 $query= "\@and ".$query;
1086 $query .= " \@attr 1=1031 \"".$search->{'class'}."\"";
1093 # 4) do a loose search
1096 my ($condition1, $condition2, $condition3) = ('','','');
1097 my $count_params = 0;
1099 if ($search->{'ttype1'} eq 'exact') {
1100 $attr="\@attr 4=1 ";
1101 if ($search->{'atype1'} eq 'start'){
1102 $attr.=" \@attr 3=1 \@attr 6=3 \@attr 5=1 \@attr 2=102 ";
1104 $attr.=" \@attr 5=1 \@attr 3=3 \@attr 6=1 \@attr 2=102 ";
1107 $attr=" \@attr 4=6 \@attr 5=103 ";
1111 $search->{'field_value1'}=~ s/(\.|\?|\;|\=|\/|\\|\||\:|\!|,|\-|\"|\(|\)|\[|\]|\{|\}|\/)//g;
1112 if ($search->{'field_name1'} eq 'all') {
1113 $condition1.= " \@attr 1=1016 ".$attr." \"".$search->{'field_value1'}."\" ";
1115 } elsif ($search->{'field_name1'} eq 'author') {
1116 $condition1.=" \@attr 1=1003 ".$attr." \"".$search->{'field_value1'}."\" ";
1118 } elsif ($search->{'field_name1'} eq 'title') {
1119 $condition1.= " \@attr 1=4 ".$attr." \"".$search->{'field_value1'}."\" ";
1121 } elsif ($search->{'field_name1'} eq 'subject') {
1122 $condition1.=" \@attr 1=21 ".$attr." \"".$search->{'field_value1'}."\" ";
1123 } elsif ($search->{'field_name1'} eq 'series') {
1124 $condition1.=" \@attr 1=5 ".$attr." \"".$search->{'field_value1'}."\" ";
1126 } elsif ($search->{'field_name1'} eq 'publisher') {
1127 $condition1.= " \@attr 1=1018 ".$attr." \"".$search->{'field_value1'}."\" ";
1128 } elsif ($search->{'field_name1'} eq 'callno') {
1129 $condition1.= " \@attr 1=20 \@attr 3=2 ".$attr." \"".$search->{'field_value1'}."\" ";
1131 $query = $condition1;
1133 if ($search->{'field_value2'}) {
1134 $search->{'field_value2'}=~ s/(\.|\?|\;|\=|\/|\\|\||\:|\!|,|\-|\"|\(|\)|\[|\]|\{|\}|\/)//g;
1135 if ($search->{'ttype2'} eq 'exact') {
1137 $attr2="\@attr 4=1 ";
1138 if ($search->{'atype1'} eq 'start'){
1139 $attr.=" \@attr 3=1 \@attr 6=3 \@attr 5=1 \@attr 2=102 ";
1141 $attr.=" \@attr 5=1 \@attr 3=3 \@attr 6=1 \@attr 2=102 ";
1144 $attr2=" \@attr 4=6 \@attr 5=103 ";
1147 if ($search->{'field_name2'} eq 'all') {
1148 if ($search->{'op1'} eq 'and') {
1149 $query = " \@and ".$query;
1150 $condition2.= " \@attr 1=1016 ".$attr2." \"".$search->{'field_value2'}."\" ";
1152 } elsif ($search->{'op1'} eq 'or') {
1153 $query = " \@or ".$query;
1154 $condition2.= " \@attr 1=1016 ".$attr2." \"".$search->{'field_value2'}."\" ";
1156 $query = " \@not ".$query;
1157 $condition2.= " \@attr 1=1016 ".$attr2." \"".$search->{'field_value2'}."\" ";
1160 } elsif ($search->{'field_name2'} eq 'author') {
1161 if ($search->{'op1'} eq 'and') {
1162 $query = " \@and ".$query;
1163 $condition2.= " \@attr 1=1003 ".$attr2." \"".$search->{'field_value2'}."\" ";
1165 } elsif ($search->{'op1'} eq 'or'){
1166 $query = " \@or ".$query;
1167 $condition2.= " \@attr 1=1003 ".$attr2." \"".$search->{'field_value2'}."\" ";
1169 $query = " \@not ".$query;
1170 $condition2.= " \@attr 1=1003 ".$attr2." \"".$search->{'field_value2'}."\" ";
1174 } elsif ($search->{'field_name2'} eq 'title') {
1175 if ($search->{'op1'} eq 'and') {
1176 $query = " \@and ".$query;
1177 $condition2.= " \@attr 1=4 ".$attr2." \"".$search->{'field_value2'}."\" ";
1179 } elsif ($search->{'op1'} eq 'or'){
1180 $query = " \@or ".$query;
1181 $condition2.= " \@attr 1=4 ".$attr2." \"".$search->{'field_value2'}."\" ";
1183 $query = " \@not ".$query;
1184 $condition2.= " \@attr 1=4 ".$attr2." \"".$search->{'field_value2'}."\" ";
1187 } elsif ($search->{'field_name2'} eq 'subject') {
1188 if ($search->{'op1'} eq 'and') {
1189 $query = " \@and ".$query;
1190 $condition2.= " \@attr 1=21 ".$attr2." \"".$search->{'field_value2'}."\" ";
1192 } elsif ($search->{'op1'} eq 'or') {
1193 $query = " \@or ".$query;
1194 $condition2.= " \@attr 1=21 ".$attr2." \"".$search->{'field_value2'}."\" ";
1196 $query = " \@not ".$query;
1197 $condition2.= " \@attr 1=21 ".$attr2." \"".$search->{'field_value2'}."\" ";
1199 } elsif ($search->{'field_name2'} eq 'series') {
1200 if ($search->{'op1'} eq 'and') {
1201 $query = " \@and ".$query;
1202 $condition2.= " \@attr 1=5 ".$attr2." \"".$search->{'field_value2'}."\" ";
1204 } elsif ($search->{'op1'} eq 'or') {
1205 $query = " \@or ".$query;
1206 $condition2.= " \@attr 1=5 ".$attr2." \"".$search->{'field_value2'}."\" ";
1208 $query = " \@not ".$query;
1209 $condition2.= " \@attr 1=5 ".$attr2." \"".$search->{'field_value2'}."\" ";
1211 } elsif ($search->{'field_name2'} eq 'callno') {
1212 if ($search->{'op1'} eq 'and') {
1213 $query = " \@and ".$query;
1214 $condition2.= " \@attr 1=20 \@attr 3=2 ".$attr2." \"".$search->{'field_value2'}."\" ";
1216 } elsif ($search->{'op1'} eq 'or'){
1217 $query = " \@or ".$query;
1218 $condition2.= " \@attr 1=20 \@attr 3=2 ".$attr2." \"".$search->{'field_value2'}."\" ";
1220 $query = " \@not ".$query;
1221 $condition2.= " \@attr 1=20 \@attr 3=2 ".$attr2." \"".$search->{'field_value2'}."\" ";
1223 } elsif ($search->{'field_name2'} eq 'publisher') {
1224 $query = " \@and ".$query;
1225 $condition2.= " \@attr 1=1018 ".$attr2." \"".$search->{'field_value2'}."\" ";
1226 } elsif ($search->{'field_name2'} eq 'publicationyear') {
1227 $query = " \@and ".$query;
1228 $condition2.= " \@attr 1=30 ".$search->{'field_value2'};
1230 $query .=$condition2;
1236 if ($search->{'field_value3'}) {
1237 $search->{'field_value3'}=~ s/(\.|\?|\;|\=|\/|\\|\||\:|\!|,|\-|\"|\(|\)|\[|\]|\{|\}|\/)//g;
1238 if ($search->{'ttype3'} eq 'exact') {
1239 $attr3="\@attr 4=1 ";
1240 if ($search->{'atype1'} eq 'start'){
1241 $attr.=" \@attr 3=1 \@attr 6=3 \@attr 5=1 \@attr 2=102 ";
1243 $attr.=" \@attr 5=1 \@attr 3=3 \@attr 6=1 \@attr 2=102 ";
1246 $attr3=" \@attr 4=6 \@attr 5=103 ";
1249 if ($search->{'field_name3'} eq 'all') {
1250 if ($search->{'op2'} eq 'and') {
1251 $query = " \@and ".$query;
1252 $condition3.= " \@attr 1=1016 ".$attr3." \"".$search->{'field_value3'}."\" ";
1254 } elsif ($search->{'op2'} eq 'or') {
1255 $query = " \@or ".$query;
1256 $condition3.= " \@attr 1=1016 ".$attr3." \"".$search->{'field_value3'}."\" ";
1258 $query = " \@not ".$query;
1259 $condition3.= " \@attr 1=1016 ".$attr3." \"".$search->{'field_value3'}."\" ";
1261 } elsif ($search->{'field_name3'} eq 'author') {
1262 if ($search->{'op2'} eq 'and') {
1263 $query = " \@and ".$query;
1264 $condition3.= " \@attr 1=1003 ".$attr3." \"".$search->{'field_value3'}."\" ";
1266 } elsif ($search->{'op2'} eq 'or') {
1267 $query = " \@or ".$query;
1268 $condition3.= " \@attr 1=1003 ".$attr3." \"".$search->{'field_value3'}."\" ";
1270 $query = " \@not ".$query;
1271 $condition3.= " \@attr 1=1003 ".$attr3." \"".$search->{'field_value3'}."\" ";
1274 } elsif ($search->{'field_name3'} eq 'title') {
1275 if ($search->{'op2'} eq 'and') {
1276 $query = " \@and ".$query;
1277 $condition3.= " \@attr 1=4 ".$attr3." \"".$search->{'field_value3'}."\" ";
1279 } elsif ($search->{'op2'} eq 'or') {
1280 $query = " \@or ".$query;
1281 $condition3.= " \@attr 1=4 ".$attr3." \"".$search->{'field_value3'}."\" ";
1283 $query = " \@not ".$query;
1284 $condition3.= " \@attr 1=4 ".$attr3." \"".$search->{'field_value3'}."\" ";
1287 } elsif ($search->{'field_name3'} eq 'subject') {
1288 if ($search->{'op2'} eq 'and') {
1289 $query = " \@and ".$query;
1290 $condition3.= " \@attr 1=21 ".$attr3." \"".$search->{'field_value3'}."\" ";
1292 } elsif ($search->{'op2'} eq 'or') {
1293 $query = " \@or ".$query;
1294 $condition3.= " \@attr 1=21 ".$attr3." \"".$search->{'field_value3'}."\" ";
1296 $query = " \@not ".$query;
1297 $condition3.= " \@attr 1=21 ".$attr3." \"".$search->{'field_value3'}."\" ";
1299 } elsif ($search->{'field_name3'} eq 'series') {
1300 if ($search->{'op2'} eq 'and') {
1301 $query = " \@and ".$query;
1302 $condition3.= " \@attr 1=5 ".$attr3." \"".$search->{'field_value3'}."\" ";
1304 } elsif ($search->{'op2'} eq 'or') {
1305 $query = " \@or ".$query;
1306 $condition3.= " \@attr 1=5 ".$attr3." \"".$search->{'field_value3'}."\" ";
1308 $query = " \@not ".$query;
1309 $condition3.= " \@attr 1=5 ".$attr3." \"".$search->{'field_value3'}."\" ";
1311 } elsif ($search->{'field_name3'} eq 'callno') {
1312 if ($search->{'op2'} eq 'and') {
1313 $query = " \@and ".$query;
1314 $condition3.= " \@attr 1=20 \@attr 3=2 ".$attr3." \"".$search->{'field_value3'}."\" ";
1316 } elsif ($search->{'op2'} eq 'or') {
1317 $query = " \@or ".$query;
1318 $condition3.= " \@attr 1=20 \@attr 3=2 ".$attr3." \"".$search->{'field_value3'}."\" ";
1321 $query = " \@not ".$query;
1322 $condition3.= " \@attr 1=20 \@attr 3=2 ".$attr3." \"".$search->{'field_value3'}."\" ";
1326 } elsif ($search->{'field_name3'} eq 'publisher') {
1327 $query = " \@and ".$query;
1328 $condition3.= " \@attr 1=1018 ".$attr3." \"".$search->{'field_value3'}."\" ";
1329 } elsif ($search->{'field_name2'} eq 'publicationyear') {
1330 $query = " \@and ".$query;
1331 $condition3.= " \@attr 1=30 ".$search->{'field_value3'};
1333 $query .=$condition3;
1341 if ($search->{'class'} ne '') {
1342 $query= "\@and ".$query;
1343 $query .= " \@attr 1=1031 \"".$search->{'class'}."\"";
1344 push @params, $search->{'class'};
1347 if ($search->{'branch'} ne '') {
1348 $query= "\@and ".$query;
1349 $query .= " \@attr 1=1033 \"".$search->{'branch'}."\"";
1352 if ($search->{'stack'} ne '') {
1353 $query= "\@and ".$query;
1354 $query .= " \@attr 1=1019 \"".$search->{'stack'}."\"";
1357 if ($search->{'date_from'} ne '') {
1358 $query= "\@and ".$query;
1359 $query .= " \@attr 1=30 \@attr 2=4 \@attr 4=4 ".$search->{'date_from'};
1361 if ($search->{'date_to'} ne '') {
1362 $query= "\@and ".$query;
1363 $query .= " \@attr 1=30 \@attr 2=2 \@attr 4=4 ".$search->{'date_to'};
1371 $count_query = $cql_query;
1374 $count_query = $query;
1376 warn "QUERY_AFTER".$count_query;
1377 if ($search->{'order'}) {
1378 $query.=" ".$search->{'order'};
1379 $query=" \@or \@or ".$query;
1382 #execute the query and returns just the results between $num and $num + $offset
1383 my $limit = $num + $offset;
1384 my $startfrom = $offset;
1385 return unless $query; ##Somebody hit the search button with no query. Prevent a system crash
1386 my $oConnection=C4::Context->Zconn("biblioserver");
1387 if ($oConnection eq "error"){
1388 return("error",undef);
1390 #$oConnection->option(preferredRecordSyntax => "XML");
1394 warn "CQLISH:".$cql_query;
1396 $newq= new ZOOM::Query::PQF($cql_query);
1398 $newq = new ZOOM::Query::CQL($cql_query,$oConnection);
1401 $newq= new ZOOM::Query::PQF($query);
1403 #my $order=$search->{'order'};
1405 #$newq->sortby("$order");
1408 $oResult= $oConnection->search($newq);
1411 return("error",undef);
1416 $numresults=$oResult->size() if ($oResult);
1420 #proccess just the results to show
1421 if ($numresults>0) {
1422 #Build brancnames hash
1424 #get branch information.....
1426 my $bsth=$dbh->prepare("SELECT branchcode,branchname FROM branches");
1428 while (my $bdata=$bsth->fetchrow_hashref){
1429 $branches{$bdata->{'branchcode'}}= $bdata->{'branchname'};
1433 #Building shelving hash
1436 my $stackstatus = $dbh->prepare('select authorised_value from marc_subfield_structure where kohafield="items.stack"');
1437 $stackstatus->execute;
1439 my ($authorised_valuecode) = $stackstatus->fetchrow;
1440 if ($authorised_valuecode) {
1441 $stackstatus = $dbh->prepare("select lib,authorised_value from authorised_values where category=? ");
1442 $stackstatus->execute($authorised_valuecode);
1444 while (my $lib = $stackstatus->fetchrow_hashref){
1445 $shelves{$lib->{'authorised_value'}} = $lib->{'lib'};
1449 #search item field code
1452 "select tagfield from marc_subfield_structure where kohafield like 'items.itemnumber'"
1455 my ($itemtag) = $sth->fetchrow;
1456 ## find column names of items related to MARC
1457 my $sth2=$dbh->prepare("SHOW COLUMNS from items");
1459 my %subfieldstosearch;
1460 while ((my $column)=$sth2->fetchrow){
1461 my ($tagfield,$tagsubfield) = &MARCfind_marc_from_kohafield($dbh,"items.".$column,"");
1462 $subfieldstosearch{$column}=$tagsubfield;
1465 for ($i=$startfrom; $i<(($startfrom+$num<=$numresults) ? ($startfrom+$num):$numresults) ; $i++){
1467 my $rec=$oResult->record($i);
1469 $marcdata = $rec->raw();
1471 $marcrecord = MARC::File::USMARC::decode($marcdata);
1472 # $marcrecord=MARC::Record->new_from_xml( $marcdata,'UTF-8' );
1473 # $marcrecord->encoding( 'UTF-8' );
1474 my $oldbiblio = MARCmarc2koha($dbh,$marcrecord,'');
1476 &add_html_bold_fields($type,$oldbiblio,$search);
1482 $oldbiblio->{'toggle'}=$toggle;
1486 my @fields = $marcrecord->field($itemtag);
1493 ##Loop for each item field
1494 foreach my $field (@fields) {
1495 foreach my $code ( keys %subfieldstosearch ) {
1497 $item->{$code}=$field->subfield($subfieldstosearch{$code});
1502 $item->{'branchname'}=$branches{$item->{'holdingbranch'}};
1503 $item->{'shelves'}=$shelves{$item->{stack}};
1504 $status="Lost" if ($item->{'itemlost'}>0);
1505 $status="Withdrawn" if ($item->{'wthdrawn'}>0);
1506 if ($search->{'from'} eq "intranet"){
1507 $search->{'avoidquerylog'}=1;
1508 $status="Due:".format_date($item->{'onloan'}) if ($item->{'onloan'}>0);
1509 $status = $item->{'holdingbranch'}."-".$item->{'stack'}."[".$item->{'itemcallnumber'}."]" unless defined $status;
1511 $status="On Loan" if ($item->{'onloan'}>0);
1512 $status = $item->{'branchname'}."[".$item->{'shelves'}."]" unless defined $status;
1517 #$oldbiblio->{'itemcount'}++;
1524 foreach my $itm (@items) {
1525 $norequests = 0 unless $itm->{'itemnotforloan'};
1528 $oldbiblio->{'noitems'} = $noitems;
1529 $oldbiblio->{'norequests'} = $norequests;
1530 $oldbiblio->{'even'} = $even = not $even;
1531 $oldbiblio->{'itemcount'} = $counts{'total'};
1533 my $totalitemcounts = 0;
1534 foreach my $key (keys %counts){
1535 if ($key ne 'total'){
1536 $totalitemcounts+= $counts{$key};
1537 $oldbiblio->{'locationhash'}->{$key}=$counts{$key};
1541 my ($locationtext, $locationtextonly, $notavailabletext) = ('','','');
1542 foreach (sort keys %{$oldbiblio->{'locationhash'}}) {
1543 if ($_ eq 'notavailable') {
1544 $notavailabletext="Not available";
1545 my $c=$oldbiblio->{'locationhash'}->{$_};
1546 $oldbiblio->{'not-available-p'}=$c;
1548 $locationtext.="$_";
1549 my $c=$oldbiblio->{'locationhash'}->{$_};
1550 if ($_ eq 'Item Lost') {
1551 $oldbiblio->{'lost-p'} = $c;
1552 } elsif ($_ eq 'Withdrawn') {
1553 $oldbiblio->{'withdrawn-p'} = $c;
1554 } elsif ($_ eq 'On Loan') {
1555 $oldbiblio->{'on-loan-p'} = $c;
1557 $locationtextonly.= $_;
1558 $locationtextonly.= " ($c)<br> " if $totalitemcounts > 1;
1560 if ($totalitemcounts>1) {
1561 $locationtext.=" ($c)<br> ";
1565 if ($notavailabletext) {
1566 $locationtext.= $notavailabletext;
1568 $locationtext=~s/, $//;
1570 $oldbiblio->{'location'} = $locationtext;
1571 $oldbiblio->{'location-only'} = $locationtextonly;
1572 $oldbiblio->{'use-location-flags-p'} = 1;
1574 push (@results, $oldbiblio);
1579 #$oConnection->destroy();
1580 my $count = $numresults;
1582 unless ($search->{'avoidquerylog'}) {
1583 add_query_line($type, $search, $count);}
1584 return($count,@results);
1590 my $dbh=C4::Context->dbh;
1591 my $result = MARCmarc2koha($dbh,$record,'');
1596 my ($biblionumber,$bibid,$title);
1597 # search duplicate on ISBN, easy and fast..
1598 $search->{'avoidquerylog'}=1;
1599 if ($result->{isbn}) {
1601 ###Temporary fix for ISBN
1602 my $isbn=$result->{isbn};
1603 $isbn=~ s/(\.|\?|\;|\=|\/|\\|\||\:|\!|\'|,|\-|\"|\(|\)|\[|\]|\{|\}|\/)//g;
1604 $search->{'isbn'}=$isbn;
1606 $result->{title}=~s /\\//g;
1607 $result->{title}=~s /\"//g;
1609 $search->{'field_name1'}="title";
1610 $search->{'field_value1'}=$result->{title};
1611 $search->{'ttype1'}="exact";
1612 $search->{'atype1'}="start";
1614 my ($total,@result)=CatSearch4($type,$search,1,0);
1615 return $result[0]->{'biblionumber'}, $result[0]->{'biblionumber'},$result[0]->{'title'} if ($total);
1620 $search = { "keyword" => "One or more keywords",
1621 "class" => "VID|CD", # Limit search to fiction and CDs
1624 ($count, @results) = &KeywordSearch($env, $type, $search, $num, $offset);
1626 C<&KeywordSearch> searches the catalog by keyword: given a string
1627 (C<$search-E<gt>{"keyword"}> consisting of a space-separated list of
1628 keywords, it looks for books that contain any of those keywords in any
1629 of a number of places.
1631 C<&KeywordSearch> looks for keywords in the book title (and subtitle),
1632 series name, notes (both C<biblio.notes> and C<biblioitems.notes>),
1635 C<$search-E<gt>{"class"}> can be set to a C<|> (pipe)-separated list of
1636 item class codes (e.g., "F" for fiction, "JNF" for junior nonfiction,
1637 etc.). In this case, the search will be restricted to just those
1640 If C<$search-E<gt>{"class"}> is not specified, you may specify
1641 C<$search-E<gt>{"dewey"}>. This will restrict the search to that
1642 particular Dewey Decimal Classification category. Setting
1643 C<$search-E<gt>{"dewey"}> to "513" will return books about arithmetic,
1644 whereas setting it to "5" will return all books with Dewey code 5I<xx>
1645 (Science and Mathematics).
1647 C<$env> and C<$type> are ignored.
1649 C<$offset> and C<$num> specify the subset of results to return.
1650 C<$num> specifies the number of results to return, and C<$offset> is
1651 the number of the first result. Thus, setting C<$offset> to 100 and
1652 C<$num> to 5 will return results 100 through 104 inclusive.
1657 my ($env,$type,$search,$num,$offset)=@_;
1658 my $dbh = C4::Context->dbh;
1659 $search->{'keyword'}=~ s/ +$//;
1660 my @key=split(' ',$search->{'keyword'});
1661 # FIXME - Naive users might enter comma-separated
1662 # words, e.g., "training, animal". Ought to cope with
1666 my %biblionumbers; # Set of biblionumbers returned by the
1669 # FIXME - Ought to filter the stopwords out of the list of keywords.
1670 # @key = map { !defined($stopwords{$_}) } @key;
1672 # FIXME - The way this code is currently set up, it looks for all of
1673 # the keywords first in (title, notes, seriestitle), then in the
1674 # subtitle, then in the subject. Thus, if you look for keywords
1675 # "science fiction", this search won't find a book with
1676 # title = "How to write fiction"
1677 # subtitle = "A science-based approach"
1678 # Is this the desired effect? If not, then the first SQL query
1679 # should look in the biblio, subtitle, and subject tables all at
1680 # once. The way the first query is built can accomodate this easily.
1682 # Look for keywords in table 'biblio'.
1684 # Build an SQL query that finds each of the keywords in any of the
1685 # title, biblio.notes, or seriestitle. To do this, we'll build up an
1686 # array of clauses, one for each keyword.
1687 my $query; # The SQL query
1688 my @clauses = (); # The search clauses
1689 my @bind = (); # The term bindings
1691 $query = <<EOT; # Beginning of the query
1696 foreach my $keyword (@key)
1698 my @subclauses = (); # Subclauses, one for each field we're
1701 # For each field we're searching on, create a subclause that'll
1702 # match the current keyword in the current field.
1703 foreach my $field (qw(title notes seriestitle author))
1706 "$field LIKE ? OR $field LIKE ?";
1707 push(@bind,"\Q$keyword\E%","% \Q$keyword\E%");
1709 # (Yes, this could have been done as
1710 # @subclauses = map {...} qw(field1 field2 ...)
1711 # )but I think this way is more readable.
1713 # Construct the current clause by joining the subclauses.
1714 push @clauses, "(" . join(")\n\tOR (", @subclauses) . ")";
1716 # Now join all of the clauses together and append to the query.
1717 $query .= "(" . join(")\nAND (", @clauses) . ")";
1719 # FIXME - Perhaps use $sth->bind_columns() ? Documented as the most
1720 # efficient way to fetch data.
1721 my $sth=$dbh->prepare($query);
1722 $sth->execute(@bind);
1723 while (my @res = $sth->fetchrow_array) {
1726 $biblionumbers{$_} = 1; # Add these results to the set
1731 # Now look for keywords in the 'bibliosubtitle' table.
1733 # Again, we build a list of clauses from the keywords.
1736 $query = "SELECT biblionumber FROM bibliosubtitle WHERE ";
1737 foreach my $keyword (@key)
1740 "subtitle LIKE ? OR subtitle like ?";
1741 push(@bind,"\Q$keyword\E%","% \Q$keyword\E%");
1743 $query .= "(" . join(") AND (", @clauses) . ")";
1745 $sth=$dbh->prepare($query);
1746 $sth->execute(@bind);
1747 while (my @res = $sth->fetchrow_array) {
1750 $biblionumbers{$_} = 1; # Add these results to the set
1755 # Look for the keywords in the notes for individual items
1756 # ('biblioitems.notes')
1758 # Again, we build a list of clauses from the keywords.
1761 $query = "SELECT biblionumber FROM biblioitems WHERE ";
1762 foreach my $keyword (@key)
1765 "notes LIKE ? OR notes like ?";
1766 push(@bind,"\Q$keyword\E%","% \Q$keyword\E%");
1768 $query .= "(" . join(") AND (", @clauses) . ")";
1770 $sth=$dbh->prepare($query);
1771 $sth->execute(@bind);
1772 while (my @res = $sth->fetchrow_array) {
1775 $biblionumbers{$_} = 1; # Add these results to the set
1780 # Look for keywords in the 'bibliosubject' table.
1782 # FIXME - The other queries look for words in the desired field that
1783 # begin with the individual keywords the user entered. This one
1784 # searches for the literal string the user entered. Is this the
1786 # Note in particular that spaces are retained: if the user typed
1788 # (with two spaces), this won't find the subject "science fiction"
1789 # (one space). Likewise, a search for "%" will return absolutely
1791 # If this isn't the desired effect, see the previous searches for
1794 $sth=$dbh->prepare("Select biblionumber from bibliosubject where subject
1795 like ? group by biblionumber");
1796 $sth->execute("%$search->{'keyword'}%");
1798 while (my @res = $sth->fetchrow_array) {
1801 $biblionumbers{$_} = 1; # Add these results to the set
1811 my @res = keys %biblionumbers;
1815 # print "count $count";
1816 if ($search->{'class'} ne ''){
1817 while ($i2 <$count){
1818 my $query="select * from biblio,biblioitems where
1819 biblio.biblionumber=? and
1820 biblio.biblionumber=biblioitems.biblionumber ";
1821 my @bind = ($res[$i2]);
1822 if ($search->{'class'} ne ''){ # FIXME - Redundant
1823 my @temp=split(/\|/,$search->{'class'});
1825 $query.= "and ( itemtype=?";
1826 push(@bind,$temp[0]);
1827 for (my $i=1;$i<$count;$i++){
1828 $query.=" or itemtype=?";
1829 push(@bind,$temp[$i]);
1833 my $sth=$dbh->prepare($query);
1835 $sth->execute(@bind);
1836 if (my $data2=$sth->fetchrow_hashref){
1837 my $dewey= $data2->{'dewey'};
1838 my $subclass=$data2->{'subclass'};
1839 # FIXME - This next bit is bogus, because it assumes that the
1840 # Dewey code is a floating-point number. It isn't. It's
1841 # actually a string that mainly consists of numbers. In
1842 # particular, "4" is not a valid Dewey code, although "004"
1843 # is ("Data processing; Computer science"). Likewise, zeros
1844 # after the decimal are significant ("575" is not the same as
1845 # "575.0"; the latter is more specific). And "000" is a
1846 # perfectly good Dewey code ("General works; computer
1847 # science") and should not be interpreted to mean "this
1848 # database entry does not have a Dewey code". That's what
1851 ($dewey == 0) && ($dewey='');
1852 ($dewey) && ($dewey.=" $subclass") ;
1854 my $end=$offset +$num;
1855 if ($i4 <= $offset){
1859 if ($i4 <=$end && $i4 > $offset){
1860 $data2->{'dewey'}=$dewey;
1863 # $res2[$i3]="$data2->{'author'}\t$data2->{'title'}\t$data2->{'biblionumber'}\t$data2->{'copyrightdate'}\t$dewey";
1866 # print "in here $i3<br>";
1877 # $search->{'class'} was not specified
1879 # FIXME - This is bogus: it makes a separate query for each
1880 # biblioitem, and returns results in apparently random order. It'd
1881 # be much better to combine all of the previous queries into one big
1882 # one (building it up a little at a time, of course), and have that
1883 # big query select all of the desired fields, instead of just
1886 while ($i2 < $num && $i2 < $count){
1887 my $query="select * from biblio,biblioitems where
1888 biblio.biblionumber=? and
1889 biblio.biblionumber=biblioitems.biblionumber ";
1890 my @bind=($res[$i2+$offset]);
1892 if ($search->{'dewey'} ne ''){
1893 $query.= "and (dewey like ?)";
1894 push(@bind,"$search->{'dewey'}%");
1897 my $sth=$dbh->prepare($query);
1899 $sth->execute(@bind);
1900 if (my $data2=$sth->fetchrow_hashref){
1901 my $dewey= $data2->{'dewey'};
1902 my $subclass=$data2->{'subclass'};
1904 ($dewey == 0) && ($dewey='');
1905 ($dewey) && ($dewey.=" $subclass") ;
1907 $data2->{'dewey'}=$dewey;
1910 # $res2[$i]="$data2->{'author'}\t$data2->{'title'}\t$data2->{'biblionumber'}\t$data2->{'copyrightdate'}\t$dewey";
1919 return($count,@res2);
1922 sub KeywordSearch2 {
1923 my ($env,$type,$search,$num,$offset)=@_;
1924 my $dbh = C4::Context->dbh;
1925 $search->{'keyword'}=~ s/ +$//;
1926 my @key=split(' ',$search->{'keyword'});
1930 my $query ="Select * from biblio,bibliosubtitle,biblioitems where
1931 biblio.biblionumber=biblioitems.biblionumber and
1932 biblio.biblionumber=bibliosubtitle.biblionumber and
1933 (((title like ? or title like ?)";
1934 my @bind=("$key[0]%","% $key[0]%");
1935 while ($i < $count){
1936 $query .= " and (title like ? or title like ?)";
1937 push(@bind,"$key[$i]%","% $key[$i]%");
1940 $query.= ") or ((subtitle like ? or subtitle like ?)";
1941 push(@bind,"$key[0]%","% $key[0]%");
1942 for ($i=1;$i<$count;$i++){
1943 $query.= " and (subtitle like ? or subtitle like ?)";
1944 push(@bind,"$key[$i]%","% $key[$i]%");
1946 $query.= ") or ((seriestitle like ? or seriestitle like ?)";
1947 push(@bind,"$key[0]%","% $key[0]%");
1948 for ($i=1;$i<$count;$i++){
1949 $query.=" and (seriestitle like ? or seriestitle like ?)";
1950 push(@bind,"$key[$i]%","% $key[$i]%");
1952 $query.= ") or ((biblio.notes like ? or biblio.notes like ?)";
1953 push(@bind,"$key[0]%","% $key[0]%");
1954 for ($i=1;$i<$count;$i++){
1955 $query.=" and (biblio.notes like ? or biblio.notes like ?)";
1956 push(@bind,"$key[$i]%","% $key[$i]%");
1958 $query.= ") or ((biblioitems.notes like ? or biblioitems.notes like ?)";
1959 push(@bind,"$key[0]%","% $key[0]%");
1960 for ($i=1;$i<$count;$i++){
1961 $query.=" and (biblioitems.notes like ? or biblioitems.notes like ?)";
1962 push(@bind,"$key[$i]%","% $key[$i]%");
1964 if ($search->{'keyword'} =~ /new zealand/i){
1965 $query.= "or (title like 'nz%' or title like '% nz %' or title like '% nz' or subtitle like 'nz%'
1966 or subtitle like '% nz %' or subtitle like '% nz' or author like 'nz %'
1967 or author like '% nz %' or author like '% nz')"
1969 if ($search->{'keyword'} eq 'nz' || $search->{'keyword'} eq 'NZ' ||
1970 $search->{'keyword'} =~ /nz /i || $search->{'keyword'} =~ / nz /i ||
1971 $search->{'keyword'} =~ / nz/i){
1972 $query.= "or (title like 'new zealand%' or title like '% new zealand %'
1973 or title like '% new zealand' or subtitle like 'new zealand%' or
1974 subtitle like '% new zealand %'
1975 or subtitle like '% new zealand' or author like 'new zealand%'
1976 or author like '% new zealand %' or author like '% new zealand' or
1977 seriestitle like 'new zealand%' or seriestitle like '% new zealand %'
1978 or seriestitle like '% new zealand')"
1981 if ($search->{'class'} ne ''){
1982 my @temp=split(/\|/,$search->{'class'});
1984 $query.= "and ( itemtype=?";
1985 push(@bind,"$temp[0]");
1986 for (my $i=1;$i<$count;$i++){
1987 $query.=" or itemtype=?";
1988 push(@bind,"$temp[$i]");
1992 if ($search->{'dewey'} ne ''){
1993 $query.= "and (dewey like '$search->{'dewey'}%') ";
1995 $query.="group by biblio.biblionumber";
1996 #$query.=" order by author,title";
1998 my $sth=$dbh->prepare($query);
1999 $sth->execute(@bind);
2001 while (my $data=$sth->fetchrow_hashref){
2002 #FIXME: rewrite to use ? before uncomment
2003 # my $sti=$dbh->prepare("select dewey,subclass from biblioitems where biblionumber=$data->{'biblionumber'}
2006 # my ($dewey, $subclass) = $sti->fetchrow;
2007 my $dewey=$data->{'dewey'};
2008 my $subclass=$data->{'subclass'};
2010 ($dewey == 0) && ($dewey='');
2011 ($dewey) && ($dewey.=" $subclass");
2013 $results[$i]="$data->{'author'}\t$data->{'title'}\t$data->{'biblionumber'}\t$data->{'copyrightdate'}\t$dewey";
2014 # print $results[$i];
2018 $sth=$dbh->prepare("Select biblionumber from bibliosubject where subject
2019 like ? group by biblionumber");
2020 $sth->execute("%".$search->{'keyword'}."%");
2021 while (my $data=$sth->fetchrow_hashref){
2022 $query="Select * from biblio,biblioitems where
2023 biblio.biblionumber=? and
2024 biblio.biblionumber=biblioitems.biblionumber ";
2025 @bind=($data->{'biblionumber'});
2026 if ($search->{'class'} ne ''){
2027 my @temp=split(/\|/,$search->{'class'});
2029 $query.= " and ( itemtype=?";
2030 push(@bind,$temp[0]);
2031 for (my $i=1;$i<$count;$i++){
2032 $query.=" or itemtype=?";
2033 push(@bind,$temp[$i]);
2038 if ($search->{'dewey'} ne ''){
2039 $query.= "and (dewey like ?)";
2040 push(@bind,"$search->{'dewey'}%");
2042 my $sth2=$dbh->prepare($query);
2043 $sth2->execute(@bind);
2045 while (my $data2=$sth2->fetchrow_hashref){
2046 my $dewey= $data2->{'dewey'};
2047 my $subclass=$data2->{'subclass'};
2049 ($dewey == 0) && ($dewey='');
2050 ($dewey) && ($dewey.=" $subclass") ;
2052 $results[$i]="$data2->{'author'}\t$data2->{'title'}\t$data2->{'biblionumber'}\t$data2->{'copyrightdate'}\t$dewey";
2053 # print $results[$i];
2059 @results=sort @results;
2064 $res[0]=$results[0];
2066 while ($i2 < $count){
2067 if ($results[$i2] ne $res[$i-1]){
2068 $res[$i]=$results[$i2];
2076 while ($i2 < $num && $i2 < $count){
2077 $res2[$i2]=$res[$i2+$offset];
2088 sub add_query_line {
2090 my ($type,$search,$results)=@_;
2091 my $dbh = C4::Context->dbh;
2092 my $searchdesc = '';
2094 my $borrowernumber = $search->{'borrowernumber'};
2095 my $remote_IP = $search->{'remote_IP'};
2096 my $remote_URL= $search->{'remote_URL'};
2097 my $searchmode = '';
2098 my $searchlinkdesc = '';
2100 if ($search->{'from'}) {
2101 $from = $search->{'from'};
2106 if ($type eq 'keyword') {
2107 $searchdesc = $search->{'keyword'};
2108 if ($search->{'ttype'} eq 'exact') {
2109 $searchmode = 'phrase';
2111 $searchmode = 'any word';
2113 $searchlinkdesc.= "search_type=keyword&keyword=$search->{'keyword'}&ttype=$search->{'ttype'}";
2115 } elsif ($type eq 'precise') {
2116 if ($search->{'itemnumber'}) {
2117 $searchdesc = "barcode = $search->{'itemnumber'}";
2118 $searchlinkdesc.= "search_type=precise&itemnumber=$search->{'itemnumber'}";
2120 $searchdesc = "isbn = $search->{'itemnumber'}";
2121 $searchlinkdesc.= "search_type=precise&itemnumber=$search->{'isbn'}";
2124 } elsif ($type eq 'recently_items') {
2125 $searchdesc = "$search->{'range'}";
2126 $searchlinkdesc.= "recently_items=1&search=$search->{'range'}";
2128 $searchlinkdesc.= "search_type=loose";
2129 if ( ($search->{"field_name1"}) && ($search->{"field_value1"}) ) {
2130 if ($search->{"ttype1"} eq 'exact') {
2131 $searchmode.= ' starting with ';
2133 $searchmode.= ' containing ';
2135 $searchdesc.= " | " . $search->{"field_name1"} . " = " . $search->{"field_value1"} . " | ";
2136 $searchlinkdesc.= "&ttype=$search->{'ttype1'}&field_name1=$search->{'field_name1'}&field_value1=$search->{'field_value1'}";
2139 if ( ($search->{"field_name2"}) && ($search->{"field_value2"}) ) {
2140 if ($search->{"ttype2"} eq 'exact') {
2141 $searchmode.= ' | starting with ';
2143 $searchmode.= ' | containing ';
2145 $searchdesc.= uc($search->{"op1"});
2146 $searchdesc.= " | " . $search->{"field_name2"} . " = " . $search->{"field_value2"} . " | ";
2147 $searchlinkdesc.= "&op1=$search->{'op1'}&ttype=$search->{'ttype2'}&field_name2=$search->{'field_name2'}&field_value2=$search->{'field_value2'}";
2150 if ( ($search->{"field_name3"}) && ($search->{"field_value3"}) ) {
2151 if ($search->{"ttype3"} eq 'exact') {
2152 $searchmode.= ' | starting with ';
2154 $searchmode.= ' | containing ';
2156 $searchdesc.= uc($search->{"op2"});
2157 $searchdesc.= " | " . $search->{"field_name3"} . " = " . $search->{"field_value3"} . " | ";
2158 $searchlinkdesc.= "&op2=$search->{'op2'}&ttype=$search->{'ttype3'}&field_name3=$search->{'field_name3'}&field_value3=$search->{'field_value3'}";
2162 if ($search->{'branch'}) {
2163 $searchdesc.= " AND branch = $search->{'branch'}";
2164 $searchlinkdesc.= "&branch=$search->{'branch'}";
2166 if ($search->{'class'}) {
2167 $searchdesc.= " AND itemtype = $search->{'class'}";
2168 $searchlinkdesc.= "&class=$search->{'class'}";
2171 # my $sth = $dbh->prepare("INSERT INTO querys_log (searchtype, searchdesc, searchmode, borrowernumber, number_of_results, date, execute_from, remote_IP, linkdesc) VALUES (?,?,?,?,?,NOW(),?,?,?)");
2172 # $sth->execute($type, $searchdesc, $searchmode, $borrowernumber, $results, $from, $remote_IP, $searchlinkdesc);
2174 my $sth = $dbh->prepare("INSERT INTO phrase_log(phr_phrase,phr_resultcount,phr_ip,user,actual) VALUES(?,?,?,?,?)");
2177 $sth->execute($searchdesc,$results,$remote_IP,$borrowernumber,$remote_URL);
2185 ($count, @results) = &CatSearch($env, $type, $search, $num, $offset);
2187 C<&CatSearch> searches the Koha catalog. It returns a list whose first
2188 element is the number of returned results, and whose subsequent
2189 elements are the results themselves.
2191 Each returned element is a reference-to-hash. Most of the keys are
2192 simply the fields from the C<biblio> table in the Koha database, but
2193 the following keys may also be present:
2197 =item C<illustrator>
2199 The book's illustrator.
2209 C<$type> may be C<subject>, C<loose>, or C<precise>. This controls the
2210 high-level behavior of C<&CatSearch>, as described below.
2212 In many cases, the description below says that a certain field in the
2213 database must match the search string. In these cases, it means that
2214 the beginning of some word in the field must match the search string.
2215 Thus, an author search for "sm" will return books whose author is
2216 "John Smith" or "Mike Smalls", but not "Paul Grossman", since the "sm"
2217 does not occur at the beginning of a word.
2219 Note that within each search mode, the criteria are and-ed together.
2220 That is, if you perform a loose search on the author "Jerome" and the
2221 title "Boat", the search will only return books by Jerome containing
2222 "Boat" in the title.
2224 It is not possible to cross modes, e.g., set the author to "Asimov"
2225 and the subject to "Math" in hopes of finding books on math by Asimov.
2229 If C<$type> is set to C<loose>, the following search criteria may be
2234 =item C<$search-E<gt>{author}>
2236 The search string is a space-separated list of words. Each word must
2237 match either the C<author> or C<additionalauthors> field.
2239 =item C<$search-E<gt>{title}>
2241 Each word in the search string must match the book title. If no author
2242 is specified, the book subtitle will also be searched.
2244 =item C<$search-E<gt>{abstract}>
2246 Searches for the given search string in the book's abstract.
2248 =item C<$search-E<gt>{'date-before'}>
2250 Searches for books whose copyright date matches the search string.
2251 That is, setting C<$search-E<gt>{'date-before'}> to "1985" will find
2252 books written in 1985, and setting it to "198" will find books written
2253 between 1980 and 1989.
2255 =item C<$search-E<gt>{title}>
2257 Searches by title are also affected by the value of
2258 C<$search-E<gt>{"ttype"}>; if it is set to C<exact>, then the book
2259 title, (one of) the series titleZ<>(s), or (one of) the unititleZ<>(s) must
2260 match the search string exactly (the subtitle is not searched).
2262 If C<$search-E<gt>{"ttype"}> is set to anything other than C<exact>,
2263 each word in the search string must match the title, subtitle,
2264 unititle, or series title.
2266 =item C<$search-E<gt>{class}>
2268 Restricts the search to certain item classes. The value of
2269 C<$search-E<gt>{"class"}> is a | (pipe)-separated list of item types.
2270 Thus, setting it to "F" restricts the search to fiction, and setting
2271 it to "CD|CAS" will only look in compact disks and cassettes.
2273 =item C<$search-E<gt>{dewey}>
2275 Searches for books whose Dewey Decimal Classification code matches the
2276 search string. That is, setting C<$search-E<gt>{"dewey"}> to "5" will
2277 search for all books in 5I<xx> (Science and mathematics), setting it
2278 to "54" will search for all books in 54I<x> (Chemistry), and setting
2279 it to "546" will search for books on inorganic chemistry.
2281 =item C<$search-E<gt>{publisher}>
2283 Searches for books whose publisher contains the search string (unlike
2284 other search criteria, C<$search-E<gt>{publisher}> is a string, not a
2289 =head2 Subject search
2291 If C<$type> is set to C<subject>, the following search criterion may
2296 =item C<$search-E<gt>{subject}>
2298 The search string is a space-separated list of words, each of which
2299 must match the book's subject.
2301 Special case: if C<$search-E<gt>{subject}> is set to C<nz>,
2302 C<&CatSearch> will search for books whose subject is "New Zealand".
2303 However, setting C<$search-E<gt>{subject}> to C<"nz football"> will
2304 search for books on "nz" and "football", not books on "New Zealand"
2309 =head2 Precise search
2311 If C<$type> is set to C<precise>, the following search criteria may be
2316 =item C<$search-E<gt>{item}>
2318 Searches for books whose barcode exactly matches the search string.
2320 =item C<$search-E<gt>{isbn}>
2322 Searches for books whose ISBN exactly matches the search string.
2326 For a loose search, if an author was specified, the results are
2327 ordered by author and title. If no author was specified, the results
2328 are ordered by title.
2330 For other (non-loose) searches, if a subject was specified, the
2331 results are ordered alphabetically by subject.
2333 In all other cases (e.g., loose search by keyword), the results are
2339 my ($env,$type,$search,$num,$offset)=@_;
2340 my $dbh = C4::Context->dbh;
2345 my $title = lc($search->{'title'});
2347 if ($type eq 'loose') {
2348 if ($search->{'author'} ne ''){
2349 my @key=split(' ',$search->{'author'});
2352 $query="select *,biblio.author,biblio.biblionumber from
2354 left join additionalauthors
2355 on additionalauthors.biblionumber =biblio.biblionumber
2357 ((biblio.author like ? or biblio.author like ? or
2358 additionalauthors.author like ? or additionalauthors.author
2361 @bind=("$key[0]%","% $key[0]%","$key[0]%","% $key[0]%");
2362 while ($i < $count){
2364 biblio.author like ? or biblio.author like ? or
2365 additionalauthors.author like ? or additionalauthors.author like ?
2367 push(@bind,"$key[$i]%","% $key[$i]%","$key[$i]%","% $key[$i]%");
2371 if ($search->{'title'} ne ''){
2372 my @key=split(' ',$search->{'title'});
2375 $query.= " and (((title like ? or title like ?)";
2376 push(@bind,"$key[0]%","% $key[0]%");
2378 $query .= " and (title like ? or title like ?)";
2379 push(@bind,"$key[$i]%","% $key[$i]%");
2382 $query.=") or ((seriestitle like ? or seriestitle like ?)";
2383 push(@bind,"$key[0]%","% $key[0]%");
2384 for ($i=1;$i<$count;$i++){
2385 $query.=" and (seriestitle like ? or seriestitle like ?)";
2386 push(@bind,"$key[$i]%","% $key[$i]%");
2388 $query.=") or ((unititle like ? or unititle like ?)";
2389 push(@bind,"$key[0]%","% $key[0]%");
2390 for ($i=1;$i<$count;$i++){
2391 $query.=" and (unititle like ? or unititle like ?)";
2392 push(@bind,"$key[$i]%","% $key[$i]%");
2396 if ($search->{'abstract'} ne ''){
2397 $query.= " and (abstract like ?)";
2398 push(@bind,"%$search->{'abstract'}%");
2400 if ($search->{'date-before'} ne ''){
2401 $query.= " and (copyrightdate like ?)";
2402 push(@bind,"%$search->{'date-before'}%");
2404 $query.=" group by biblio.biblionumber";
2406 if ($search->{'title'} ne '') {
2407 if ($search->{'ttype'} eq 'exact'){
2408 $query="select * from biblio
2410 (biblio.title=? or (biblio.unititle = ?
2411 or biblio.unititle like ? or
2412 biblio.unititle like ? or
2413 biblio.unititle like ?) or
2414 (biblio.seriestitle = ? or
2415 biblio.seriestitle like ? or
2416 biblio.seriestitle like ? or
2417 biblio.seriestitle like ?)
2419 @bind=($search->{'title'},$search->{'title'},"$search->{'title'} |%","%| $search->{'title'} |%","%| $search->{'title'}",$search->{'title'},"$search->{'title'} |%","%| $search->{'title'} |%","%| $search->{'title'}");
2421 my @key=split(' ',$search->{'title'});
2424 $query="select biblio.biblionumber,author,title,unititle,notes,abstract,serial,seriestitle,copyrightdate,timestamp,subtitle from biblio
2425 left join bibliosubtitle on
2426 biblio.biblionumber=bibliosubtitle.biblionumber
2428 (((title like ? or title like ?)";
2429 @bind=("$key[0]%","% $key[0]%");
2431 $query .= " and (title like ? or title like ?)";
2432 push(@bind,"$key[$i]%","% $key[$i]%");
2435 $query.=") or ((subtitle like ? or subtitle like ?)";
2436 push(@bind,"$key[0]%","% $key[0]%");
2437 for ($i=1;$i<$count;$i++){
2438 $query.=" and (subtitle like ? or subtitle like ?)";
2439 push(@bind,"$key[$i]%","% $key[$i]%");
2441 $query.=") or ((seriestitle like ? or seriestitle like ?)";
2442 push(@bind,"$key[0]%","% $key[0]%");
2443 for ($i=1;$i<$count;$i++){
2444 $query.=" and (seriestitle like ? or seriestitle like ?)";
2445 push(@bind,"$key[$i]%","% $key[$i]%");
2447 $query.=") or ((unititle like ? or unititle like ?)";
2448 push(@bind,"$key[0]%","% $key[0]%");
2449 for ($i=1;$i<$count;$i++){
2450 $query.=" and (unititle like ? or unititle like ?)";
2451 push(@bind,"$key[$i]%","% $key[$i]%");
2455 if ($search->{'abstract'} ne ''){
2456 $query.= " and (abstract like ?)";
2457 push(@bind,"%$search->{'abstract'}%");
2459 if ($search->{'date-before'} ne ''){
2460 $query.= " and (copyrightdate like ?)";
2461 push(@bind,"%$search->{'date-before'}%");
2464 } elsif ($search->{'dewey'} ne ''){
2465 $query="select * from biblioitems,biblio
2466 where biblio.biblionumber=biblioitems.biblionumber
2467 and biblioitems.dewey like ?";
2468 @bind=("$search->{'dewey'}%");
2469 } elsif ($search->{'illustrator'} ne '') {
2470 $query="select * from biblioitems,biblio
2471 where biblio.biblionumber=biblioitems.biblionumber
2472 and biblioitems.illus like ?";
2473 @bind=("%".$search->{'illustrator'}."%");
2474 } elsif ($search->{'publisher'} ne ''){
2475 $query = "Select * from biblio,biblioitems where biblio.biblionumber
2476 =biblioitems.biblionumber and (publishercode like ?)";
2477 @bind=("%$search->{'publisher'}%");
2478 } elsif ($search->{'abstract'} ne ''){
2479 $query = "Select * from biblio where abstract like ?";
2480 @bind=("%$search->{'abstract'}%");
2481 } elsif ($search->{'date-before'} ne ''){
2482 $query = "Select * from biblio where copyrightdate like ?";
2483 @bind=("%$search->{'date-before'}%");
2484 }elsif ($search->{'branch'} ne ''){
2485 $query = "Select * from biblio,items where biblio.biblionumber
2486 =items.biblionumber and holdingbranch like ?";
2487 @bind=("$search->{'branch'}");
2488 }elsif ($search->{'class'} ne ''){
2489 $query="select * from biblioitems,biblio where biblio.biblionumber=biblioitems.biblionumber";
2491 $query.= " where itemtype= ?";
2492 @bind=("$search->{'class'}");
2494 $query .=" group by biblio.biblionumber";
2497 if ($type eq 'subject'){
2498 my @key=split(' ',$search->{'subject'});
2501 $query="select * from bibliosubject, biblioitems where
2502 (bibliosubject.biblionumber = biblioitems.biblionumber) and ( subject like ? or subject like ? or subject like ?)";
2503 @bind=("$key[0]%","% $key[0]%","%($key[0])%");
2505 $query.=" and (subject like ? or subject like ? or subject like ?)";
2506 push(@bind,"$key[$i]%","% $key[$i]%","%($key[$i])%");
2510 # FIXME - Wouldn't it be better to fix the database so that if a
2511 # book has a subject "NZ", then it also gets added the subject
2513 # This can also be generalized by adding a table of subject
2514 # synonyms to the database: just declare "NZ" to be a synonym for
2515 # "New Zealand", "SF" a synonym for both "Science fiction" and
2516 # "Fantastic fiction", etc.
2518 if (lc($search->{'subject'}) eq 'nz'){
2519 $query.= " or (subject like 'NEW ZEALAND %' or subject like '% NEW ZEALAND %'
2520 or subject like '% NEW ZEALAND' or subject like '%(NEW ZEALAND)%' ) ";
2521 } elsif ( $search->{'subject'} =~ /^nz /i || $search->{'subject'} =~ / nz /i || $search->{'subject'} =~ / nz$/i){
2522 $query=~ s/ nz/ NEW ZEALAND/ig;
2523 $query=~ s/nz /NEW ZEALAND /ig;
2524 $query=~ s/\(nz\)/\(NEW ZEALAND\)/gi;
2527 if ($type eq 'precise'){
2528 if ($search->{'itemnumber'} ne ''){
2529 $query="select * from items,biblio ";
2530 my $search2=uc $search->{'itemnumber'};
2531 $query=$query." where
2532 items.biblionumber=biblio.biblionumber
2537 if ($search->{'isbn'} ne ''){
2538 $query = "Select * from biblio,biblioitems where biblio.biblionumber
2539 =biblioitems.biblionumber and (isbn like ?)";
2540 @bind=("$search->{'isbn'}%");
2543 if ($type ne 'precise' && $type ne 'subject'){
2544 if ($search->{'author'} ne ''){
2545 $query .= " order by biblio.author,title";
2547 $query .= " order by title";
2550 if ($type eq 'subject'){
2551 $query .= " group by subject ";
2554 my $sth=$dbh->prepare($query);
2555 $sth->execute(@bind);
2558 my $limit= $num+$offset;
2559 while (my $data=$sth->fetchrow_hashref){
2560 my $query="select classification,dewey,subclass,publishercode from biblioitems where biblionumber=?";
2561 my @bind=($data->{'biblionumber'});
2562 if ($search->{'class'} ne ''){
2563 my @temp=split(/\|/,$search->{'class'});
2565 $query.= " and ( itemtype= ?";
2566 push(@bind,$temp[0]);
2567 for (my $i=1;$i<$count;$i++){
2568 $query.=" or itemtype=?";
2569 push(@bind,$temp[$i]);
2573 if ($search->{'dewey'} ne ''){
2574 $query.=" and dewey=? ";
2575 push(@bind,$search->{'dewey'});
2577 if ($search->{'illustrator'} ne ''){
2578 $query.=" and illus like ?";
2579 push(@bind,"%$search->{'illustrator'}%");
2581 if ($search->{'publisher'} ne ''){
2582 $query.= " and (publishercode like ?)";
2583 push(@bind,"%$search->{'publisher'}%");
2585 my $sti=$dbh->prepare($query);
2586 $sti->execute(@bind);
2593 if ($bibitemdata = $sti->fetchrow_hashref()){
2595 $classification=$bibitemdata->{'classification'};
2596 $dewey=$bibitemdata->{'dewey'};
2597 $subclass=$bibitemdata->{'subclass'};
2598 $publishercode=$bibitemdata->{'publishercode'};
2600 # print STDERR "$dewey $subclass $publishercode\n";
2601 # FIXME - The Dewey code is a string, not a number.
2603 ($dewey == 0) && ($dewey='');
2604 ($dewey) && ($dewey.=" $subclass");
2605 $data->{'classification'}=$classification;
2606 $data->{'dewey'}=$dewey;
2607 $data->{'publishercode'}=$publishercode;
2610 if ($count > $offset && $count <= $limit){
2619 return($count,@results);
2622 sub updatesearchstats{
2623 my ($dbh,$query)=@_;
2629 @results = &subsearch($env, $subject);
2631 Searches for books that have a subject that exactly matches
2634 C<&subsearch> returns an array of results. Each element of this array
2635 is a string, containing the book's title, author, and biblionumber,
2643 my ($env,$subject)=@_;
2644 my $dbh = C4::Context->dbh;
2645 my $sth=$dbh->prepare("Select * from biblio,bibliosubject where
2646 biblio.biblionumber=bibliosubject.biblionumber and
2647 bibliosubject.subject=? group by biblio.biblionumber
2648 order by biblio.title");
2649 $sth->execute($subject);
2652 while (my $data=$sth->fetchrow_hashref){
2653 push @results, $data;
2662 @results = &ItemInfo($env, $biblionumber, $type);
2664 Returns information about books with the given biblionumber.
2666 C<$type> may be either C<intra> or anything else. If it is not set to
2667 C<intra>, then the search will exclude lost, very overdue, and
2672 C<&ItemInfo> returns a list of references-to-hash. Each element
2673 contains a number of keys. Most of them are table items from the
2674 C<biblio>, C<biblioitems>, C<items>, and C<itemtypes> tables in the
2675 Koha database. Other keys include:
2679 =item C<$data-E<gt>{branchname}>
2681 The name (not the code) of the branch to which the book belongs.
2683 =item C<$data-E<gt>{datelastseen}>
2685 This is simply C<items.datelastseen>, except that while the date is
2686 stored in YYYY-MM-DD format in the database, here it is converted to
2687 DD/MM/YYYY format. A NULL date is returned as C<//>.
2689 =item C<$data-E<gt>{datedue}>
2691 =item C<$data-E<gt>{class}>
2693 This is the concatenation of C<biblioitems.classification>, the book's
2694 Dewey code, and C<biblioitems.subclass>.
2696 =item C<$data-E<gt>{ocount}>
2698 I think this is the number of copies of the book available.
2700 =item C<$data-E<gt>{order}>
2702 If this is set, it is set to C<One Order>.
2709 my ($env,$biblionumber,$type) = @_;
2710 my $dbh = C4::Context->dbh;
2711 my $query = "SELECT *,items.notforloan as itemnotforloan FROM items, biblio, biblioitems
2712 left join itemtypes on biblioitems.itemtype = itemtypes.itemtype
2713 WHERE items.biblionumber = ?
2714 AND biblioitems.biblioitemnumber = items.biblioitemnumber
2715 AND biblio.biblionumber = items.biblionumber";
2716 $query .= " order by items.dateaccessioned desc";
2717 my $sth=$dbh->prepare($query);
2718 $sth->execute($biblionumber);
2721 my ($date_due, $count_reserves);
2722 while (my $data=$sth->fetchrow_hashref){
2724 my $isth=$dbh->prepare("Select issues.*,borrowers.cardnumber from issues,borrowers where itemnumber = ? and returndate is null and issues.borrowernumber=borrowers.borrowernumber");
2725 $isth->execute($data->{'itemnumber'});
2726 if (my $idata=$isth->fetchrow_hashref){
2727 $data->{borrowernumber} = $idata->{borrowernumber};
2728 $data->{cardnumber} = $idata->{cardnumber};
2729 $datedue = format_date($idata->{'date_due'});
2731 if ($datedue eq ''){
2732 # $datedue="Available";
2733 my ($restype,$reserves)=C4::Reserves2::CheckReserves($data->{'itemnumber'});
2735 # $datedue=$restype;
2736 $count_reserves = $restype;
2740 #get branch information.....
2741 my $bsth=$dbh->prepare("SELECT * FROM branches WHERE branchcode = ?");
2742 $bsth->execute($data->{'holdingbranch'});
2743 if (my $bdata=$bsth->fetchrow_hashref){
2744 $data->{'branchname'} = $bdata->{'branchname'};
2746 my $date=format_date($data->{'datelastseen'});
2747 $data->{'datelastseen'}=$date;
2748 $data->{'datedue'}=$datedue;
2749 $data->{'count_reserves'} = $count_reserves;
2750 # get notforloan complete status if applicable
2751 my $sthnflstatus = $dbh->prepare('select authorised_value from marc_subfield_structure where kohafield="items.notforloan"');
2752 $sthnflstatus->execute;
2753 my ($authorised_valuecode) = $sthnflstatus->fetchrow;
2754 if ($authorised_valuecode) {
2755 $sthnflstatus = $dbh->prepare("select lib from authorised_values where category=? and authorised_value=?");
2756 $sthnflstatus->execute($authorised_valuecode,$data->{itemnotforloan});
2757 my ($lib) = $sthnflstatus->fetchrow;
2758 $data->{notforloan} = $lib;
2761 # my stack procedures
2763 my $stackstatus = $dbh->prepare('select authorised_value from marc_subfield_structure where kohafield="items.stack"');
2764 $stackstatus->execute;
2766 ($authorised_valuecode) = $stackstatus->fetchrow;
2767 if ($authorised_valuecode) {
2768 $stackstatus = $dbh->prepare("select lib from authorised_values where category=? and authorised_value=?");
2769 $stackstatus->execute($authorised_valuecode,$data->{stack});
2771 my ($lib) = $stackstatus->fetchrow;
2772 $data->{stack} = $lib;
2784 @results = &GetItems($env, $biblionumber);
2786 Returns information about books with the given biblionumber.
2790 C<&GetItems> returns an array of strings. Each element is a
2791 tab-separated list of values: biblioitemnumber, itemtype,
2792 classification, Dewey number, subclass, ISBN, volume, number, and
2795 Itemdata, in turn, is a string of the form
2796 "I<barcode>C<[>I<holdingbranch>C<[>I<flags>" where I<flags> contains
2797 the string C<NFL> if the item is not for loan, and C<LOST> if the item
2803 my ($env,$biblionumber)=@_;
2804 #debug_msg($env,"GetItems");
2805 my $dbh = C4::Context->dbh;
2806 my $sth=$dbh->prepare("Select * from biblioitems where (biblionumber = ?)");
2807 $sth->execute($biblionumber);
2808 #debug_msg($env,"executed query");
2811 while (my $data=$sth->fetchrow_hashref) {
2812 print ($env,$data->{'biblioitemnumber'});
2813 my $dewey = $data->{'dewey'};
2815 my $isbn= $data->{'isbn'};
2818 my $line = $data->{'biblioitemnumber'}."\t".$data->{'itemtype'};
2819 $line .= "\t$data->{'classification'}\t$dewey";
2820 $line .= "\t$data->{'subclass'}\t$data->{'isbn'}";
2821 $line .= "\t$data->{'volume'}\t$data->{number}";
2822 my $isth= $dbh->prepare("select * from items where biblioitemnumber = ?");
2823 $isth->execute($data->{'biblioitemnumber'});
2824 while (my $idata = $isth->fetchrow_hashref) {
2825 my $iline = $idata->{'barcode'}."[".$idata->{'holdingbranch'}."[";
2826 if ($idata->{'notforloan'} == 1) {
2829 if ($idata->{'itemlost'} == 1) {
2832 $line .= "\t$iline";
2835 $results[$i] = $line;
2844 $item = &itemdata($barcode);
2846 Looks up the item with the given barcode, and returns a
2847 reference-to-hash containing information about that item. The keys of
2848 the hash are the fields from the C<items> and C<biblioitems> tables in
2855 my $dbh = C4::Context->dbh;
2856 my $sth=$dbh->prepare("Select * from items,biblioitems where barcode=?
2857 and items.biblioitemnumber=biblioitems.biblioitemnumber");
2858 $sth->execute($barcode);
2859 my $data=$sth->fetchrow_hashref;
2866 $data = &bibdata($biblionumber, $type);
2868 Returns information about the book with the given biblionumber.
2870 C<$type> is ignored.
2872 C<&bibdata> returns a reference-to-hash. The keys are the fields in
2873 the C<biblio>, C<biblioitems>, and C<bibliosubtitle> tables in the
2876 In addition, C<$data-E<gt>{subject}> is the list of the book's
2877 subjects, separated by C<" , "> (space, comma, space).
2879 If there are multiple biblioitems with the given biblionumber, only
2880 the first one is considered.
2885 my ($bibnum, $type) = @_;
2886 my $dbh = C4::Context->dbh;
2887 my $sth = $dbh->prepare("Select *, biblioitems.notes AS bnotes, biblio.notes
2888 from biblio, biblioitems
2889 left join bibliosubtitle on
2890 biblio.biblionumber = bibliosubtitle.biblionumber
2891 left join itemtypes on biblioitems.itemtype=itemtypes.itemtype
2892 where biblio.biblionumber = ?
2893 and biblioitems.biblionumber = biblio.biblionumber");
2894 $sth->execute($bibnum);
2896 $data = $sth->fetchrow_hashref;
2898 # handle management of repeated subtitle
2899 $sth = $dbh->prepare("Select * from bibliosubtitle where biblionumber = ?");
2900 $sth->execute($bibnum);
2902 while (my $dat = $sth->fetchrow_hashref){
2904 $line{subtitle} = $dat->{subtitle};
2905 push @subtitles, \%line;
2907 $data->{subtitles} = \@subtitles;
2909 $sth = $dbh->prepare("Select * from bibliosubject where biblionumber = ?");
2910 $sth->execute($bibnum);
2912 while (my $dat = $sth->fetchrow_hashref){
2914 $line{subject} = $dat->{'subject'};
2915 push @subjects, \%line;
2917 $data->{subjects} = \@subjects;
2919 $sth = $dbh->prepare("Select * from additionalauthors where biblionumber = ?");
2920 $sth->execute($bibnum);
2921 while (my $dat = $sth->fetchrow_hashref){
2922 $data->{'additionalauthors'} .= "$dat->{'author'} - ";
2924 chop $data->{'additionalauthors'};
2925 chop $data->{'additionalauthors'};
2926 chop $data->{'additionalauthors'};
2933 $itemdata = &bibitemdata($biblioitemnumber);
2935 Looks up the biblioitem with the given biblioitemnumber. Returns a
2936 reference-to-hash. The keys are the fields from the C<biblio>,
2937 C<biblioitems>, and C<itemtypes> tables in the Koha database, except
2938 that C<biblioitems.notes> is given as C<$itemdata-E<gt>{bnotes}>.
2944 my $dbh = C4::Context->dbh;
2945 my $sth = $dbh->prepare("Select *,biblioitems.notes as bnotes from biblio, biblioitems,itemtypes where biblio.biblionumber = biblioitems.biblionumber and biblioitemnumber = ? and biblioitems.itemtype = itemtypes.itemtype");
2948 $sth->execute($bibitem);
2950 $data = $sth->fetchrow_hashref;
2958 ($count, $subjects) = &subject($biblionumber);
2960 Looks up the subjects of the book with the given biblionumber. Returns
2961 a two-element list. C<$subjects> is a reference-to-array, where each
2962 element is a subject of the book, and C<$count> is the number of
2963 elements in C<$subjects>.
2969 my $dbh = C4::Context->dbh;
2970 my $sth=$dbh->prepare("Select * from bibliosubject where biblionumber=?");
2971 $sth->execute($bibnum);
2974 while (my $data=$sth->fetchrow_hashref){
2979 return($i,\@results);
2984 ($count, $authors) = &addauthors($biblionumber);
2986 Looks up the additional authors for the book with the given
2989 Returns a two-element list. C<$authors> is a reference-to-array, where
2990 each element is an additional author, and C<$count> is the number of
2991 elements in C<$authors>.
2997 my $dbh = C4::Context->dbh;
2998 my $sth=$dbh->prepare("Select * from additionalauthors where biblionumber=?");
2999 $sth->execute($bibnum);
3002 while (my $data=$sth->fetchrow_hashref){
3007 return($i,\@results);
3012 ($count, $subtitles) = &subtitle($biblionumber);
3014 Looks up the subtitles for the book with the given biblionumber.
3016 Returns a two-element list. C<$subtitles> is a reference-to-array,
3017 where each element is a subtitle, and C<$count> is the number of
3018 elements in C<$subtitles>.
3024 my $dbh = C4::Context->dbh;
3025 my $sth=$dbh->prepare("Select * from bibliosubtitle where biblionumber=?");
3026 $sth->execute($bibnum);
3029 while (my $data=$sth->fetchrow_hashref){
3034 return($i,\@results);
3039 @issues = &itemissues($biblioitemnumber, $biblio);
3041 Looks up information about who has borrowed the bookZ<>(s) with the
3042 given biblioitemnumber.
3044 C<$biblio> is ignored.
3046 C<&itemissues> returns an array of references-to-hash. The keys
3047 include the fields from the C<items> table in the Koha database.
3048 Additional keys include:
3054 If the item is currently on loan, this gives the due date.
3056 If the item is not on loan, then this is either "Available" or
3057 "Cancelled", if the item has been withdrawn.
3061 If the item is currently on loan, this gives the card number of the
3062 patron who currently has the item.
3064 =item C<timestamp0>, C<timestamp1>, C<timestamp2>
3066 These give the timestamp for the last three times the item was
3069 =item C<card0>, C<card1>, C<card2>
3071 The card number of the last three patrons who borrowed this item.
3073 =item C<borrower0>, C<borrower1>, C<borrower2>
3075 The borrower number of the last three patrons who borrowed this item.
3082 my ($bibitem, $biblio)=@_;
3083 my $dbh = C4::Context->dbh;
3084 # FIXME - If this function die()s, the script will abort, and the
3085 # user won't get anything; depending on how far the script has
3086 # gotten, the user might get a blank page. It would be much better
3087 # to at least print an error message. The easiest way to do this
3088 # is to set $SIG{__DIE__}.
3089 my $sth = $dbh->prepare("Select * from items where
3090 items.biblioitemnumber = ?")
3091 || die $dbh->errstr;
3095 $sth->execute($bibitem)
3096 || die $sth->errstr;
3098 while (my $data = $sth->fetchrow_hashref) {
3099 # Find out who currently has this item.
3100 # FIXME - Wouldn't it be better to do this as a left join of
3101 # some sort? Currently, this code assumes that if
3102 # fetchrow_hashref() fails, then the book is on the shelf.
3103 # fetchrow_hashref() can fail for any number of reasons (e.g.,
3104 # database server crash), not just because no items match the
3106 my $sth2 = $dbh->prepare("select * from issues,borrowers
3107 where itemnumber = ?
3108 and returndate is NULL
3109 and issues.borrowernumber = borrowers.borrowernumber");
3111 $sth2->execute($data->{'itemnumber'});
3112 if (my $data2 = $sth2->fetchrow_hashref) {
3113 $data->{'date_due'} = $data2->{'date_due'};
3114 $data->{'card'} = $data2->{'cardnumber'};
3115 $data->{'borrower'} = $data2->{'borrowernumber'};
3117 if ($data->{'wthdrawn'} eq '1') {
3118 $data->{'date_due'} = 'Cancelled';
3120 $data->{'date_due'} = 'Available';
3126 # Find the last 3 people who borrowed this item.
3127 $sth2 = $dbh->prepare("select * from issues, borrowers
3128 where itemnumber = ?
3129 and issues.borrowernumber = borrowers.borrowernumber
3130 and returndate is not NULL
3131 order by returndate desc,timestamp desc") ;
3132 $sth2->execute($data->{'itemnumber'}) ;
3133 for (my $i2 = 0; $i2 < 2; $i2++) { # FIXME : error if there is less than 3 pple borrowing this item
3134 if (my $data2 = $sth2->fetchrow_hashref) {
3135 $data->{"timestamp$i2"} = $data2->{'timestamp'};
3136 $data->{"card$i2"} = $data2->{'cardnumber'};
3137 $data->{"borrower$i2"} = $data2->{'borrowernumber'};
3142 $results[$i] = $data;
3152 $item = &itemnodata($env, $dbh, $biblioitemnumber);
3154 Looks up the item with the given biblioitemnumber.
3156 C<$env> and C<$dbh> are ignored.
3158 C<&itemnodata> returns a reference-to-hash whose keys are the fields
3159 from the C<biblio>, C<biblioitems>, and C<items> tables in the Koha
3165 my ($env,$dbh,$itemnumber) = @_;
3166 $dbh = C4::Context->dbh;
3167 my $sth=$dbh->prepare("Select * from biblio,items,biblioitems
3168 where items.itemnumber = ?
3169 and biblio.biblionumber = items.biblionumber
3170 and biblioitems.biblioitemnumber = items.biblioitemnumber");
3172 $sth->execute($itemnumber);
3173 my $data=$sth->fetchrow_hashref;
3180 ($count, $borrowers) = &BornameSearch($env, $searchstring, $type);
3182 Looks up patrons (borrowers) by name.
3186 BUGFIX 499: C<$type> is now used to determine type of search.
3187 if $type is "simple", search is performed on the first letter of the
3190 C<$searchstring> is a space-separated list of search terms. Each term
3191 must match the beginning a borrower's surname, first name, or other
3194 C<&BornameSearch> returns a two-element list. C<$borrowers> is a
3195 reference-to-array; each element is a reference-to-hash, whose keys
3196 are the fields of the C<borrowers> table in the Koha database.
3197 C<$count> is the number of elements in C<$borrowers>.
3201 #used by member enquiries from the intranet
3202 #called by member.pl
3204 my ($env,$searchstring,$orderby,$type)=@_;
3205 my $dbh = C4::Context->dbh;
3206 my $query = ""; my $count; my @data;
3209 if($type eq "simple") # simple search for one letter only
3211 $query="Select * from borrowers where surname like '$searchstring%' order by $orderby";
3212 # @bind=("$searchstring%");
3214 else # advanced search looking in surname, firstname and othernames
3216 ### Try to determine whether numeric like cardnumber
3217 if ($searchstring+1>1) {
3218 $query="Select * from borrowers where cardnumber like '$searchstring%' ";
3222 my @words=split / /,$searchstring;
3223 foreach my $word(@words){
3227 $searchstring=join " ",@words;
3229 $query="Select * from borrowers where MATCH(surname,firstname,othernames) AGAINST('$searchstring' in boolean mode)";
3232 $query=$query." order by $orderby";
3235 my $sth=$dbh->prepare($query);
3236 # warn "Q $orderby : $query";
3240 while (my $data=$sth->fetchrow_hashref){
3241 push(@results,$data);
3245 return ($cnt,\@results);
3250 $borrower = &borrdata($cardnumber, $borrowernumber);
3252 Looks up information about a patron (borrower) by either card number
3253 or borrower number. If $borrowernumber is specified, C<&borrdata>
3254 searches by borrower number; otherwise, it searches by card number.
3256 C<&borrdata> returns a reference-to-hash whose keys are the fields of
3257 the C<borrowers> table in the Koha database.
3262 my ($cardnumber,$bornum)=@_;
3263 $cardnumber = uc $cardnumber;
3264 my $dbh = C4::Context->dbh;
3266 if ($bornum eq ''&& $cardnumber eq ''){ return undef; }
3268 $sth=$dbh->prepare("Select * from borrowers where cardnumber=?");
3269 $sth->execute($cardnumber);
3271 $sth=$dbh->prepare("Select * from borrowers where borrowernumber=?");
3272 $sth->execute($bornum);
3274 my $data=$sth->fetchrow_hashref;
3278 } else { # try with firstname
3280 my $sth=$dbh->prepare("select * from borrowers where firstname=?");
3281 $sth->execute($cardnumber);
3282 my $data=$sth->fetchrow_hashref;
3292 ($count, $issues) = &borrissues($borrowernumber);
3294 Looks up what the patron with the given borrowernumber has borrowed.
3296 C<&borrissues> returns a two-element array. C<$issues> is a
3297 reference-to-array, where each element is a reference-to-hash; the
3298 keys are the fields from the C<issues>, C<biblio>, and C<items> tables
3299 in the Koha database. C<$count> is the number of elements in
3306 my $dbh = C4::Context->dbh;
3307 my $sth=$dbh->prepare("Select * from issues,biblio,items where borrowernumber=?
3308 and items.itemnumber=issues.itemnumber
3309 and items.biblionumber=biblio.biblionumber
3310 and issues.returndate is NULL order by date_due");
3311 $sth->execute($bornum);
3313 while (my $data = $sth->fetchrow_hashref) {
3314 push @result, $data;
3317 return(scalar(@result), \@result);
3322 ($count, $issues) = &allissues($borrowernumber, $sortkey, $limit);
3324 Looks up what the patron with the given borrowernumber has borrowed,
3325 and sorts the results.
3327 C<$sortkey> is the name of a field on which to sort the results. This
3328 should be the name of a field in the C<issues>, C<biblio>,
3329 C<biblioitems>, or C<items> table in the Koha database.
3331 C<$limit> is the maximum number of results to return.
3333 C<&allissues> returns a two-element array. C<$issues> is a
3334 reference-to-array, where each element is a reference-to-hash; the
3335 keys are the fields from the C<issues>, C<biblio>, C<biblioitems>, and
3336 C<items> tables of the Koha database. C<$count> is the number of
3337 elements in C<$issues>
3342 my ($bornum,$order,$limit)=@_;
3343 #FIXME: sanity-check order and limit
3344 my $dbh = C4::Context->dbh;
3345 my $query="Select * from issues,biblio,items,biblioitems
3346 where borrowernumber=? and
3347 items.biblioitemnumber=biblioitems.biblioitemnumber and
3348 items.itemnumber=issues.itemnumber and
3349 items.biblionumber=biblio.biblionumber order by $order";
3351 $query.=" limit $limit";
3354 my $sth=$dbh->prepare($query);
3355 $sth->execute($bornum);
3358 while (my $data=$sth->fetchrow_hashref){
3363 return($i,\@result);
3368 ($borrowed, $due, $fine) = &borrdata2($env, $borrowernumber);
3370 Returns aggregate data about items borrowed by the patron with the
3371 given borrowernumber.
3375 C<&borrdata2> returns a three-element array. C<$borrowed> is the
3376 number of books the patron currently has borrowed. C<$due> is the
3377 number of overdue items the patron currently has borrowed. C<$fine> is
3378 the total fine currently due by the borrower.
3383 my ($env,$bornum)=@_;
3384 my $dbh = C4::Context->dbh;
3385 my $query="Select count(*) from issues where borrowernumber='$bornum' and
3386 returndate is NULL";
3388 my $sth=$dbh->prepare($query);
3390 my $data=$sth->fetchrow_hashref;
3392 $sth=$dbh->prepare("Select count(*) from issues where
3393 borrowernumber='$bornum' and date_due < now() and returndate is NULL");
3395 my $data2=$sth->fetchrow_hashref;
3397 $sth=$dbh->prepare("Select sum(amountoutstanding) from accountlines where
3398 borrowernumber='$bornum'");
3400 my $data3=$sth->fetchrow_hashref;
3403 return($data2->{'count(*)'},$data->{'count(*)'},$data3->{'sum(amountoutstanding)'});
3407 my ($env,$bornum)=@_;
3408 my $dbh = C4::Context->dbh;
3409 my $query="Select count(*) from reserveissue as r where r.borrowernumber='$bornum'
3410 and rettime is null";
3412 my $sth=$dbh->prepare($query);
3414 my $data=$sth->fetchrow_hashref;
3416 $sth=$dbh->prepare("Select count(*),timediff(now(), duetime ) as elapsed, hour(timediff(now(), duetime )) as hours, MINUTE(timediff(now(), duetime )) as min from
3417 reserveissue as r where r.borrowernumber='$bornum' and rettime is null and duetime< now() group by r.borrowernumber");
3420 my $data2=$sth->fetchrow_hashref;
3422 my $rescharge=C4::Context->preference('resmaterialcharge');
3426 if ($data2->{'elapsed'}>0){
3427 $resfine=($data2->{'hours'}+$data2->{'min'}/60)*$rescharge;
3428 $resfine=sprintf ("%.1f",$resfine);
3431 $sth=$dbh->prepare("Select sum(amountoutstanding) from accountlines where
3432 borrowernumber='$bornum'");
3434 my $data3=$sth->fetchrow_hashref;
3438 return($data2->{'count(*)'},$data->{'count(*)'},$data3->{'sum(amountoutstanding)'},$resfine);
3440 =item getboracctrecord
3442 ($count, $acctlines, $total) = &getboracctrecord($env, $borrowernumber);
3444 Looks up accounting data for the patron with the given borrowernumber.
3448 (FIXME - I'm not at all sure what this is about.)
3450 C<&getboracctrecord> returns a three-element array. C<$acctlines> is a
3451 reference-to-array, where each element is a reference-to-hash; the
3452 keys are the fields of the C<accountlines> table in the Koha database.
3453 C<$count> is the number of elements in C<$acctlines>. C<$total> is the
3454 total amount outstanding for all of the account lines.
3458 sub getboracctrecord {
3459 my ($env,$params) = @_;
3460 my $dbh = C4::Context->dbh;
3463 my $sth=$dbh->prepare("Select * from accountlines where
3464 borrowernumber=? order by date desc,timestamp desc");
3466 $sth->execute($params->{'borrowernumber'});
3468 while (my $data=$sth->fetchrow_hashref){
3469 #FIXME before reinstating: insecure?
3470 # if ($data->{'itemnumber'} ne ''){
3471 # $query="Select * from items,biblio where items.itemnumber=
3472 # '$data->{'itemnumber'}' and biblio.biblionumber=items.biblionumber";
3473 # my $sth2=$dbh->prepare($query);
3475 # my $data2=$sth2->fetchrow_hashref;
3479 $acctlines[$numlines] = $data;
3481 $total += $data->{'amountoutstanding'};
3484 return ($numlines,\@acctlines,$total);
3489 ($count, $lcount, $nacount, $fcount, $scount, $lostcount,
3490 $mending, $transit,$ocount) =
3491 &itemcount($env, $biblionumber, $type);
3493 Counts the number of items with the given biblionumber, broken down by
3498 If C<$type> is not set to C<intra>, lost, very overdue, and withdrawn
3499 items will not be counted.
3501 C<&itemcount> returns a nine-element list:
3503 C<$count> is the total number of items with the given biblionumber.
3505 C<$lcount> is the number of items at the Levin branch.
3507 C<$nacount> is the number of items that are neither borrowed, lost,
3508 nor withdrawn (and are therefore presumably on a shelf somewhere).
3510 C<$fcount> is the number of items at the Foxton branch.
3512 C<$scount> is the number of items at the Shannon branch.
3514 C<$lostcount> is the number of lost and very overdue items.
3516 C<$mending> is the number of items at the Mending branch (being
3519 C<$transit> is the number of items at the Transit branch (in transit
3522 C<$ocount> is the number of items that haven't arrived yet
3523 (aqorders.quantity - aqorders.quantityreceived).
3528 # FIXME - There's also a &C4::Biblio::itemcount.
3529 # Since they're all exported, acqui/acquire.pl doesn't compile with -w.
3531 my ($env,$bibnum,$type)=@_;
3532 my $dbh = C4::Context->dbh;
3533 my $query="Select * from items where
3535 if ($type ne 'intra'){
3536 $query.=" and ((itemlost <>1 and itemlost <> 2) or itemlost is NULL) and
3537 (wthdrawn <> 1 or wthdrawn is NULL)";
3539 my $sth=$dbh->prepare($query);
3541 $sth->execute($bibnum);
3551 while (my $data=$sth->fetchrow_hashref){
3554 my $sth2=$dbh->prepare("select * from issues,items where issues.itemnumber=
3555 ? and returndate is NULL
3556 and items.itemnumber=issues.itemnumber and ((items.itemlost <>1 and
3557 items.itemlost <> 2) or items.itemlost is NULL)
3558 and (wthdrawn <> 1 or wthdrawn is NULL)");
3559 $sth2->execute($data->{'itemnumber'});
3560 if (my $data2=$sth2->fetchrow_hashref){
3563 if ($data->{'holdingbranch'} eq 'C' || $data->{'holdingbranch'} eq 'LT'){
3566 if ($data->{'holdingbranch'} eq 'F' || $data->{'holdingbranch'} eq 'FP'){
3569 if ($data->{'holdingbranch'} eq 'S' || $data->{'holdingbranch'} eq 'SP'){
3572 if ($data->{'itemlost'} eq '1'){
3575 if ($data->{'itemlost'} eq '2'){
3578 if ($data->{'holdingbranch'} eq 'FM'){
3581 if ($data->{'holdingbranch'} eq 'TR'){
3588 my $sth2=$dbh->prepare("Select * from aqorders where biblionumber=?");
3589 $sth2->execute($bibnum);
3590 if (my $data=$sth2->fetchrow_hashref){
3591 $ocount=$data->{'quantity'} - $data->{'quantityreceived'};
3596 return ($count,$lcount,$nacount,$fcount,$scount,$lostcount,$mending,$transit,$ocount);
3601 $counts = &itemcount2($env, $biblionumber, $type);
3603 Counts the number of items with the given biblionumber, broken down by
3608 C<$type> may be either C<intra> or anything else. If it is not set to
3609 C<intra>, then the search will exclude lost, very overdue, and
3612 C<$&itemcount2> returns a reference-to-hash, with the following fields:
3618 The total number of items with this biblionumber.
3622 The number of items on order (aqorders.quantity -
3623 aqorders.quantityreceived).
3627 For each branch that has at least one copy of the book, C<$counts>
3628 will have a key with the branch name, giving the number of copies at
3636 my ($env,$bibnum,$type)=@_;
3637 my $dbh = C4::Context->dbh;
3638 my $query="Select * from items,branches where
3639 biblionumber=? and items.holdingbranch=branches.branchcode";
3640 if ($type ne 'intra'){
3641 $query.=" and ((itemlost <>1 and itemlost <> 2) or itemlost is NULL) and
3642 (wthdrawn <> 1 or wthdrawn is NULL)";
3644 my $sth=$dbh->prepare($query);
3646 $sth->execute($bibnum);
3649 while (my $data=$sth->fetchrow_hashref){
3655 'select * from items
3657 and not ((items.itemlost <>1 and items.itemlost <> 2)
3658 or items.itemlost is NULL)'
3661 'select * from items
3662 where itemnumber=? and not (wthdrawn <> 1 or wthdrawn is NULL)'
3664 'On Loan', "select * from issues,items
3665 where issues.itemnumber=? and returndate is NULL
3666 and items.itemnumber=issues.itemnumber"
3669 my($testlabel, $query2) = @$test;
3671 my $sth2=$dbh->prepare($query2);
3672 $sth2->execute($data->{'itemnumber'});
3674 # FIXME - fetchrow_hashref() can fail for any number of reasons
3675 # (e.g., a database server crash). Perhaps use a left join of some
3677 $status = $testlabel if $sth2->fetchrow_hashref;
3679 last if defined $status;
3681 ## find the shelving name from stack
3682 my $stackstatus = $dbh->prepare('select authorised_value from marc_subfield_structure where kohafield="items.stack"');
3683 $stackstatus->execute;
3685 my ($authorised_valuecode) = $stackstatus->fetchrow;
3686 if ($authorised_valuecode) {
3687 $stackstatus = $dbh->prepare("select lib from authorised_values where category=? and authorised_value=?");
3688 $stackstatus->execute($authorised_valuecode,$data->{stack});
3690 my ($lib) = $stackstatus->fetchrow;
3691 $data->{stack} = $lib;
3695 $status = $data->{'branchname'}."[".$data->{'stack'}."]" unless defined $status;
3699 my $sth2=$dbh->prepare("Select * from aqorders where biblionumber=? and
3700 datecancellationprinted is NULL and quantity > quantityreceived");
3701 $sth2->execute($bibnum);
3702 if (my $data=$sth2->fetchrow_hashref){
3703 $counts{'order'}=$data->{'quantity'} - $data->{'quantityreceived'};
3712 $description = &ItemType($itemtype);
3714 Given an item type code, returns the description for that type.
3719 # FIXME - I'm pretty sure that after the initial setup, the list of
3720 # item types doesn't change very often. Hence, it seems slow and
3721 # inefficient to make yet another database call to look up information
3722 # that'll only change every few months or years.
3724 # Much better, I think, to automatically build a Perl file that can be
3725 # included in those scripts that require it, e.g.:
3726 # @itemtypes = qw( ART BCD CAS CD F ... );
3728 # ART => "Art Prints",
3729 # BCD => "CD-ROM from book",
3730 # CD => "Compact disc (WN)",
3731 # F => "Free Fiction",
3734 # The web server can then run a cron job to rebuild this file from the
3735 # database every hour or so.
3737 # The same thing goes for branches, book funds, book sellers, currency
3738 # rates, printers, stopwords, and perhaps others.
3741 my $dbh = C4::Context->dbh;
3742 my $sth=$dbh->prepare("select description from itemtypes where itemtype=?");
3743 $sth->execute($type);
3744 my $dat=$sth->fetchrow_hashref;
3746 return ($dat->{'description'});
3751 ($count, @results) = &bibitems($biblionumber);
3753 Given the biblionumber for a book, C<&bibitems> looks up that book's
3754 biblioitems (different publications of the same book, the audio book
3755 and film versions, etc.).
3757 C<$count> is the number of elements in C<@results>.
3759 C<@results> is an array of references-to-hash; the keys are the fields
3760 of the C<biblioitems> and C<itemtypes> tables of the Koha database. In
3761 addition, C<itemlost> indicates the availability of the item: if it is
3762 "2", then all copies of the item are long overdue; if it is "1", then
3763 all copies are lost; otherwise, there is at least one copy available.
3769 my $dbh = C4::Context->dbh;
3770 my $sth = $dbh->prepare("SELECT biblioitems.*,
3772 MIN(items.itemlost) as itemlost,
3773 MIN(items.dateaccessioned) as dateaccessioned
3774 FROM biblioitems, itemtypes, items
3775 WHERE biblioitems.biblionumber = ?
3776 AND biblioitems.itemtype = itemtypes.itemtype
3777 AND biblioitems.biblioitemnumber = items.biblioitemnumber
3778 GROUP BY items.biblioitemnumber");
3781 $sth->execute($bibnum);
3782 while (my $data = $sth->fetchrow_hashref) {
3783 $results[$count] = $data;
3787 return($count, @results);
3792 @barcodes = &barcodes($biblioitemnumber);
3794 Given a biblioitemnumber, looks up the corresponding items.
3796 Returns an array of references-to-hash; the keys are C<barcode> and
3799 The returned items include very overdue items, but not lost ones.
3804 #called from request.pl
3805 my ($biblioitemnumber)=@_;
3806 my $dbh = C4::Context->dbh;
3807 my $sth=$dbh->prepare("SELECT barcode, itemlost, holdingbranch,onloan,itemnumber FROM items
3808 WHERE biblioitemnumber = ?
3809 AND (wthdrawn <> 1 OR wthdrawn IS NULL)");
3810 $sth->execute($biblioitemnumber);
3813 while (my $data=$sth->fetchrow_hashref){
3814 $barcodes[$i]=$data;
3823 ($count, @websites) = &getwebsites($biblionumber);
3825 Looks up the web sites pertaining to the book with the given
3828 C<$count> is the number of elements in C<@websites>.
3830 C<@websites> is an array of references-to-hash; the keys are the
3831 fields from the C<websites> table in the Koha database.
3836 my ($biblionumber) = @_;
3837 my $dbh = C4::Context->dbh;
3838 my $sth = $dbh->prepare("Select * from websites where biblionumber = ?");
3842 $sth->execute($biblionumber);
3843 while (my $data = $sth->fetchrow_hashref) {
3844 # FIXME - The URL scheme shouldn't be stripped off, at least
3845 # not here, since it's part of the URL, and will be useful in
3846 # constructing a link to the site. If you don't want the user
3847 # to see the "http://" part, strip that off when building the
3849 $data->{'url'} =~ s/^http:\/\///; # FIXME - Leaning toothpick
3851 $results[$count] = $data;
3856 return($count, @results);
3859 =item getwebbiblioitems
3861 ($count, @results) = &getwebbiblioitems($biblionumber);
3863 Given a book's biblionumber, looks up the web versions of the book
3864 (biblioitems with itemtype C<WEB>).
3866 C<$count> is the number of items in C<@results>. C<@results> is an
3867 array of references-to-hash; the keys are the items from the
3868 C<biblioitems> table of the Koha database.
3872 sub getwebbiblioitems {
3873 my ($biblionumber) = @_;
3874 my $dbh = C4::Context->dbh;
3875 my $sth = $dbh->prepare("Select * from biblioitems where biblionumber = ?
3876 and itemtype = 'WEB'");
3880 $sth->execute($biblionumber);
3881 while (my $data = $sth->fetchrow_hashref) {
3882 $data->{'url'} =~ s/^http:\/\///;
3883 $results[$count] = $data;
3888 return($count, @results);
3889 } # sub getwebbiblioitems
3895 ($count, @results) = &isbnsearch($isbn,$title);
3897 Given an isbn and/or a title, returns the biblios having it.
3898 Used in acqui.simple, isbnsearch.pl only
3900 C<$count> is the number of items in C<@results>. C<@results> is an
3901 array of references-to-hash; the keys are the items from the
3902 C<biblioitems> table of the Koha database.
3907 my ($isbn,$title) = @_;
3908 my $dbh = C4::Context->dbh;
3914 $query = "Select distinct biblio.*, biblioitems.classification from biblio, biblioitems where
3915 biblio.biblionumber = biblioitems.biblionumber";
3918 $query .= " and isbn like ?";
3919 @bind=(uc($isbn)."%");
3922 $query .= " and title like ?";
3925 $sth = $dbh->prepare($query);
3927 $sth->execute(@bind);
3928 while (my $data = $sth->fetchrow_hashref) {
3929 $results[$count] = $data;
3934 return($count, @results);
3939 $branchname = &getbranchname($branchcode);
3941 Given the branch code, the function returns the corresponding
3942 branch name for a comprehensive information display
3948 my ($branchcode) = @_;
3949 my $dbh = C4::Context->dbh;
3950 my $sth = $dbh->prepare("SELECT branchname FROM branches WHERE branchcode = ?");
3951 $sth->execute($branchcode);
3952 my $branchname = $sth->fetchrow();
3955 } # sub getbranchname
3957 =item getborrowercategory
3959 $description = &getborrowercategory($categorycode);
3961 Given the borrower's category code, the function returns the corresponding
3962 description for a comprehensive information display.
3966 sub getborrowercategory
3969 my $dbh = C4::Context->dbh;
3970 my $sth = $dbh->prepare("SELECT description FROM categories WHERE categorycode = ?");
3971 $sth->execute($catcode);
3972 my $description = $sth->fetchrow();
3974 return $description;
3975 } # sub getborrowercategory
3977 sub getborrowercategoryinfo
3980 my $dbh = C4::Context->dbh;
3981 my $sth = $dbh->prepare("SELECT * FROM categories WHERE categorycode = ?");
3982 $sth->execute($catcode);
3983 my $category = $sth->fetchrow_hashref;
3986 } # sub getborrowercategoryinfo
3989 my ($dbh, $bibid, $marcflavour) = @_;
3990 my ($mintag, $maxtag);
3991 if ($marcflavour eq "MARC21") {
3994 } else { # assume unimarc if not marc21
4001 my $record=MARCgetbiblio($dbh,$bibid);
4008 foreach my $field ($record->field('5..')) {
4009 my $value = $field->as_string();
4011 $marcnote = {marcnote => $note,};
4012 push @marcnotes, $marcnote;
4015 if ($note ne $value) {
4016 $note = $note." ".$value;
4021 $marcnote = {marcnote => $note};
4022 push @marcnotes, $marcnote; #load last tag into array
4027 my $marcnotesarray=\@marcnotes;
4028 return $marcnotesarray;
4029 } # end getMARCnotes
4032 sub getMARCsubjects {
4033 my ($dbh, $bibid, $marcflavour) = @_;
4034 my ($mintag, $maxtag);
4035 if ($marcflavour eq "MARC21") {
4038 } else { # assume unimarc if not marc21
4042 my $record=MARCgetbiblio($dbh,$bibid);
4048 foreach my $field ($record->field('6..')) {
4049 #my $value = $field->subfield('a');
4050 #$marcsubjct = {MARCSUBJCT => $value,};
4051 $marcsubjct = {MARCSUBJCT => $field->as_string(),};
4052 push @marcsubjcts, $marcsubjct;
4056 my $marcsubjctsarray=\@marcsubjcts;
4057 return $marcsubjctsarray;
4058 } #end getMARCsubjects
4062 my ($dbh, $bibid, $marcflavour) = @_;
4063 my ($mintag, $maxtag);
4064 if ($marcflavour eq "MARC21") {
4067 } else { # assume unimarc if not marc21
4072 my $record=MARCgetbiblio($dbh,$bibid);
4078 foreach my $field ($record->field('856')) {
4081 my $value = $field->subfield('u');
4082 # my $subfil = $data->[1];
4083 if ( $value ne $url) {
4084 $marcurl = {MARCURLS => $value,};
4085 push @marcurls, $marcurl;
4091 my $marcurlsarray=\@marcurls;
4092 return $marcurlsarray;
4097 my ($search_or_scan,$type,$query,$num,$startfrom,$then_sort_by,$expanded_facet) = @_;
4098 # establish database connections
4099 my $dbh = C4::Context->dbh;
4100 my $zconn=C4::Context->Zconn("biblioserver");
4101 my $branches = GetBranches();
4102 # make sure all is well with the connection
4103 if ($zconn eq "error") {
4104 return("error with connection",undef); #FIXME: better error handling
4109 # prepare the query depending on the type
4110 if ($type eq 'ccl') {
4111 #$query =~ s/(\(|\))//g;
4113 $zoom_query_obj = new ZOOM::Query::CCL2RPN($query,$zconn);
4116 return ("error: Sorry, there was a problem with your query: $@",undef); #FIXME: better error handling
4118 } elsif ($type eq 'cql') {
4120 $zoom_query_obj = new ZOOM::Query::CQL2RPN($query,$zconn);
4123 return ("error: Sorry, there was a problem with your query: $@",undef); #FIXME: better error handling
4127 $zoom_query_obj = new ZOOM::Query::PQF($query);
4130 return("error with search: $@",undef); #FIXME: better error handling
4134 # PERFORM THE SEARCH OR SCAN
4138 if ($search_or_scan =~ /scan/) {
4140 $result = $zconn->scan($zoom_query_obj);
4143 return ("error with scan: $@",undef);
4147 $result = $zconn->search($zoom_query_obj);
4150 return("error with search: $@",undef); #FIXME: better error handling
4154 #### RESORT RESULT SET
4155 if ($then_sort_by) {
4156 $result->sort("yaz", "$then_sort_by")
4158 ### New Facets Stuff
4159 my $facets_counter = ();
4160 my $facets_info = ();
4162 link_value => 'su-t',
4163 label_value => 'Subject - Topic',
4164 tags => ['650', '651',],
4169 label_value => 'Authors',
4170 tags => ['100','700',],
4175 label_value => 'Series',
4176 tags => ['440','490',],
4180 link_value => 'branch',
4181 label_value => 'Branches',
4188 #### INITIALIZE SOME VARS USED CREATE THE FACETED RESULTS
4189 my @facets_loop; # stores the ref to array of hashes for template
4190 #### LOOP THROUGH THE RESULTS
4191 $numresults = 0 | $result->size() if ($result);
4192 for ( my $i=$startfrom; $i<(($startfrom+$num<=$numresults) ? ($startfrom+$num):$numresults) ; $i++){
4193 ## This is just an index scan
4194 if ($search_or_scan =~ /scan/) {
4195 my ($term,$occ) = $result->term($i);
4196 # here we create a minimal MARC record and hand it off to the
4197 # template just like a normal result ... perhaps not ideal, but
4198 # it works for now FIXME: distinguish between MARC21 and UNIMARC
4200 my $tmprecord = MARC::Record->new();
4201 $tmprecord->encoding('UTF-8');
4202 my $tmptitle = MARC::Field->new( '245',' ',' ',
4205 $tmprecord->append_fields($tmptitle);
4206 push @results, $tmprecord->as_usmarc();
4207 ## This is a real search
4209 my $rec = $result->record($i);
4210 push(@results,$rec->raw()) if $rec; #FIXME: sometimes this fails
4212 ##### BUILD FACETS AND LIMITS ####
4213 my $facet_record = MARC::Record->new_from_usmarc($rec->raw());
4215 for (my $i=0;$i<=@$facets;$i++) {
4216 if ($facets->[$i]) {
4218 for my $tag (@{$facets->[$i]->{'tags'}}) {
4219 push @fields, $facet_record->field($tag);
4221 for my $field (@fields) {
4222 my @subfields = $field->subfields();
4223 for my $subfield (@subfields) {
4224 my ($code,$data) = @$subfield;
4225 if ($code eq $facets->[$i]->{'subfield'}) {
4226 $facets_counter->{ $facets->[$i]->{'link_value'} }->{ $data }++;
4230 $facets_info->{ $facets->[$i]->{'link_value'} }->{ 'label_value' } = $facets->[$i]->{'label_value'};
4231 $facets_info->{ $facets->[$i]->{'link_value'} }->{ 'expanded' } = $facets->[$i]->{'expanded'};
4238 for my $link_value ( sort { $facets_counter->{$b} <=> $facets_counter->{$a} } keys %$facets_counter) {
4240 my $number_of_facets;
4241 my @this_facets_array;
4242 for my $one_facet (sort { $facets_counter->{ $link_value }->{$b} <=> $facets_counter->{ $link_value }->{$a} } keys %{$facets_counter->{ $link_value }} ) {
4243 $number_of_facets++;
4244 if (($number_of_facets < 6) || ($expanded_facet eq $link_value) || ($facets_info->{ $link_value }->{ 'expanded'})) {
4246 # sanitize the link value ), ( will cause errors with CCL
4247 my $facet_link_value = $one_facet;
4248 $facet_link_value =~ s/(\(|\))/ /g;
4250 # fix the length that will display in the label
4251 my $facet_label_value = $one_facet;
4252 $facet_label_value = substr($one_facet,0,20)."..." unless length($facet_label_value)<=20;
4253 # well, if it's a branch, label by the name, not the code
4254 if ($link_value =~/branch/) {
4256 $facet_label_value = $branches->{$one_facet}->{'branchname'};
4259 # but we're down with the whole label being in the link's title
4260 my $facet_title_value = $one_facet;
4262 push @this_facets_array ,
4263 ( { facet_count => $facets_counter->{ $link_value }->{ $one_facet },
4264 facet_label_value => $facet_label_value,
4265 facet_title_value => $facet_title_value,
4266 facet_link_value => $facet_link_value,
4267 type_link_value => $link_value,
4272 unless ($facets_info->{ $link_value }->{ 'expanded'}) {
4273 $expandable=1 if (($number_of_facets > 6) && ($expanded_facet ne $link_value));
4276 ( { type_link_value => $link_value,
4277 type_id => $link_value."_id",
4278 type_label => $facets_info->{ $link_value }->{ 'label_value' },
4279 facets => \@this_facets_array,
4280 expandable => $expandable,
4281 expand => $link_value,
4286 return(undef,$numresults,\@facets_loop,@results);
4290 my ($zoom_query_ref,$sort_by_ref,$servers_ref,$count,$offset) = @_;
4291 my @zoom_query = @$zoom_query_ref;
4292 my @servers = @$servers_ref;
4293 my @sort_by = @$sort_by_ref;
4295 # build the query string
4297 foreach my $query (@zoom_query) {
4298 $zoom_query.="$query " if $query;
4301 # create the zoom connection and query object
4305 my @results_array; # stores the final array of hashes of arrays
4306 for (my $i = 0; $i < @servers; $i++) {
4307 $zconns[$i] = new ZOOM::Connection($servers[$i], 0,
4308 async => 1, # asynchronous mode
4309 count => 1, # piggyback retrieval count
4310 preferredRecordSyntax => "usmarc");
4311 $zconns[$i]->option( cclfile=> "/koha/etc/ccl.properties");
4312 # perform the search, create the results objects
4313 $results[$i] = $zconns[$i]->search(new ZOOM::Query::CCL2RPN($zoom_query,$zconns[$i]));
4315 # concatenate the sort_by limits and pass them to the results object
4317 foreach my $sort (@sort_by) {
4318 $sort_by.=$sort." "; # used to be $sort,
4320 $results[$i]->sort("yaz", $sort_by) if $sort_by;
4322 while ((my $i = ZOOM::event(\@zconns)) != 0) {
4323 my $ev = $zconns[$i-1]->last_event();
4324 #print("<td><tr>connection ", $i-1, ": ", ZOOM::event_str($ev), "</tr></td>\n");
4325 if ($ev == ZOOM::Event::ZEND) {
4326 my $size = $results[$i-1]->size();
4329 $results_hash->{'server'} = $servers[$i-1];
4330 $results_hash->{'hits'} = $size;
4331 for ( my $j=$offset; $j<(($offset+$count<=$size) ? ($offset+$count):$size) ; $j++){
4333 my $record = $results[$i-1]->record($j)->raw();
4335 my ($error,$final_record) = changeEncoding($record,'MARC','MARC21','UTF-8');
4336 $records_hash->{'record'} = $final_record;
4337 $results_hash->{'RECORDS'}[$j] = $records_hash;
4338 my $dbh = C4::Context->dbh;
4340 my $record_obj = MARC::Record->new_from_usmarc($final_record);
4341 my $oldbiblio = MARCmarc2koha($dbh,$record_obj,'');
4342 $results_hash->{'BIBLIOS'}[$j] = $oldbiblio;
4345 push @results_array, $results_hash;
4347 #print "connection ", $i-1, ": $size hits";
4348 #print $results[$i-1]->record(0)->render() if $size > 0;
4351 return (undef, @results_array);
4356 my ($operators,$operands,$limits,$sort_by) = @_;
4357 my @operators = @$operators if $operators;
4358 my @operands = @$operands if $operands;
4359 my @limits = @$limits if $limits;
4360 my @sort_by = @$sort_by if $sort_by;
4361 my $previous_operand; # a flag used to keep track if there was a previous query
4362 # if there was, we can apply the current operator
4365 # construct the query with operators
4366 for (my $i=0; $i<=@operands; $i++) {
4367 if ($operands[$i]) {
4369 # only add an operator if there is a previous operand
4370 if ($previous_operand) {
4371 if ($operators[$i]) {
4372 push @ccl,( {operator => $operators[$i], operand => $operands[$i]} );
4375 # the default operator is and
4377 push @ccl,( {operator => 'and', operand => $operands[$i]} );
4381 push @ccl, ( {operand => $operands[$i]} );
4382 $previous_operand = 1;
4388 foreach my $limit (@limits) {
4389 push @ccl, ( {limit => $limit} ) if $limit;
4392 return (undef,@ccl);
4395 my ($searchdesc,$num,$count,@marcresults)=@_;
4398 my $dbh= C4::Context->dbh;
4402 my @span_terms = split (/ /, $searchdesc);
4403 #Build brancnames hash
4405 #get branch information.....
4407 my $bsth=$dbh->prepare("SELECT branchcode,branchname FROM branches");
4409 while (my $bdata=$bsth->fetchrow_hashref){
4410 $branches{$bdata->{'branchcode'}}= $bdata->{'branchname'};
4413 #search item field code
4414 my $sth = $dbh->prepare(
4415 "select tagfield from marc_subfield_structure where kohafield like 'items.itemnumber'"
4418 my ($itemtag) = $sth->fetchrow;
4420 ## find column names of items related to MARC
4421 my $sth2=$dbh->prepare("SHOW COLUMNS from items");
4423 my %subfieldstosearch;
4424 while ((my $column)=$sth2->fetchrow){
4425 my ($tagfield,$tagsubfield) = &MARCfind_marc_from_kohafield($dbh,"items.".$column,"");
4426 $subfieldstosearch{$column}=$tagsubfield;
4431 for ( my $i=0; $i<$num ; $i++){
4433 $marcrecord = MARC::File::USMARC::decode($marcresults[$i]);
4434 my $oldbiblio = MARCmarc2koha($dbh,$marcrecord,'');
4435 # add spans to search term in results
4436 foreach my $term (@span_terms) {
4437 if (length($term) > 3) {
4438 $term =~ s/(.*=|\)|\))//g;
4439 $oldbiblio->{'title'} =~ s/$term/<span class=term>$term<\/span>/gi;
4440 $oldbiblio->{'subtitle'} =~ s/$term/<span class=term>$term<\/span>/gi;
4441 $oldbiblio->{'author'} =~ s/$term/<span class=term>$term<\/span>/gi;
4442 $oldbiblio->{'publishercode'} =~ s/$term/<span class=term>$term<\/span>/gi;
4443 $oldbiblio->{'place'} =~ s/$term/<span class=term>$term<\/span>/gi;
4444 $oldbiblio->{'pages'} =~ s/$term/<span class=term>$term<\/span>/gi;
4445 $oldbiblio->{'notes'} =~ s/$term/<span class=term>$term<\/span>/gi;
4446 $oldbiblio->{'size'} =~ s/$term/<span class=term>$term<\/span>/gi;
4455 $oldbiblio->{'toggle'}=$toggle;
4456 my @fields = $marcrecord->field($itemtag);
4463 ##Loop for each item field
4464 foreach my $field (@fields) {
4465 foreach my $code ( keys %subfieldstosearch ) {
4467 $item->{$code}=$field->subfield($subfieldstosearch{$code});
4471 $item->{'branchname'}=$branches{$item->{'homebranch'}};
4472 $item->{'date_due'}=$item->{onloan};
4473 $status="Lost" if ($item->{itemlost});
4474 $status="Withdrawn" if ($item->{wthdrawn});
4475 $status =" On loan" if ($item->{onloan});
4476 #$status="Due:".format_date($item->{onloan}) if ($item->{onloan}>0 );
4477 # $status="On Loan" if ($item->{onloan} );
4478 if ($item->{'location'}){
4479 $status = $item->{'branchname'}."[".$item->{'location'}."]" unless defined $status;
4481 $status = $item->{'branchname'} unless defined $status;
4491 foreach my $itm (@items) {
4492 $norequests = 0 unless $itm->{'itemnotforloan'};
4495 $oldbiblio->{'noitems'} = $noitems;
4496 $oldbiblio->{'norequests'} = $norequests;
4497 $oldbiblio->{'even'} = $even = not $even;
4498 $oldbiblio->{'itemcount'} = $counts{'total'};
4499 my $totalitemcounts = 0;
4500 foreach my $key (keys %counts){
4501 if ($key ne 'total'){
4502 $totalitemcounts+= $counts{$key};
4503 $oldbiblio->{'locationhash'}->{$key}=$counts{$key};
4506 my ($locationtext, $locationtextonly, $notavailabletext) = ('','','');
4507 foreach (sort keys %{$oldbiblio->{'locationhash'}}) {
4508 if ($_ eq 'notavailable') {
4509 $notavailabletext="Not available";
4510 my $c=$oldbiblio->{'locationhash'}->{$_};
4511 $oldbiblio->{'not-available-p'}=$c;
4513 $locationtext.="$_";
4514 my $c=$oldbiblio->{'locationhash'}->{$_};
4515 if ($_ eq 'Item Lost') {
4516 $oldbiblio->{'lost-p'} = $c;
4517 } elsif ($_ eq 'Withdrawn') {
4518 $oldbiblio->{'withdrawn-p'} = $c;
4519 } elsif ($_ eq 'On Loan') {
4520 $oldbiblio->{'on-loan-p'} = $c;
4522 $locationtextonly.= $_;
4523 $locationtextonly.= " ($c)<br/> " if $totalitemcounts > 1;
4525 if ($totalitemcounts>1) {
4526 $locationtext.=" ($c)<br/> ";
4530 if ($notavailabletext) {
4531 $locationtext.= $notavailabletext;
4533 $locationtext=~s/, $//;
4535 $oldbiblio->{'location'} = $locationtext;
4536 $oldbiblio->{'location-only'} = $locationtextonly;
4537 $oldbiblio->{'use-location-flags-p'} = 1;
4538 push (@newresults, $oldbiblio);
4544 END { } # module clean-up code here (global destructor)
4553 Koha Developement team <info@koha.org>