remove incomplete bib bulk editing code
[koha.git] / C4 / Koha.pm
1 package C4::Koha;
2
3 # Copyright 2000-2002 Katipo Communications
4 #
5 # This file is part of Koha.
6 #
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
10 # version.
11 #
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.
15 #
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
19
20
21 use strict;
22 use C4::Context;
23 use C4::Output;
24 use URI::Split qw(uri_split);
25
26 use vars qw($VERSION @ISA @EXPORT $DEBUG);
27
28 BEGIN {
29         $VERSION = 3.01;
30         require Exporter;
31         @ISA    = qw(Exporter);
32         @EXPORT = qw(
33                 &slashifyDate
34                 &DisplayISBN
35                 &subfield_is_koha_internal_p
36                 &GetPrinters &GetPrinter
37                 &GetItemTypes &getitemtypeinfo
38                 &GetCcodes
39                 &get_itemtypeinfos_of
40                 &getframeworks &getframeworkinfo
41                 &getauthtypes &getauthtype
42                 &getallthemes
43                 &getFacets
44                 &displayServers
45                 &getnbpages
46                 &get_infos_of
47                 &get_notforloan_label_of
48                 &getitemtypeimagedir
49                 &getitemtypeimagesrc
50                 &getitemtypeimagelocation
51                 &GetAuthorisedValues
52                 &GetAuthorisedValueCategories
53                 &GetKohaAuthorisedValues
54                 &GetAuthValCode
55                 &GetNormalizedUPC
56                 &GetNormalizedISBN
57                 &GetNormalizedEAN
58                 &GetNormalizedOCLCNumber
59
60                 $DEBUG
61         );
62         $DEBUG = 0;
63 }
64
65 =head1 NAME
66
67     C4::Koha - Perl Module containing convenience functions for Koha scripts
68
69 =head1 SYNOPSIS
70
71   use C4::Koha;
72
73
74 =head1 DESCRIPTION
75
76     Koha.pm provides many functions for Koha scripts.
77
78 =head1 FUNCTIONS
79
80 =cut
81
82 =head2 slashifyDate
83
84   $slash_date = &slashifyDate($dash_date);
85
86     Takes a string of the form "DD-MM-YYYY" (or anything separated by
87     dashes), converts it to the form "YYYY/MM/DD", and returns the result.
88
89 =cut
90
91 sub slashifyDate {
92
93     # accepts a date of the form xx-xx-xx[xx] and returns it in the
94     # form xx/xx/xx[xx]
95     my @dateOut = split( '-', shift );
96     return ("$dateOut[2]/$dateOut[1]/$dateOut[0]");
97 }
98
99
100 =head2 DisplayISBN
101
102     my $string = DisplayISBN( $isbn );
103
104 =cut
105
106 sub DisplayISBN {
107     my ($isbn) = @_;
108     if (length ($isbn)<13){
109     my $seg1;
110     if ( substr( $isbn, 0, 1 ) <= 7 ) {
111         $seg1 = substr( $isbn, 0, 1 );
112     }
113     elsif ( substr( $isbn, 0, 2 ) <= 94 ) {
114         $seg1 = substr( $isbn, 0, 2 );
115     }
116     elsif ( substr( $isbn, 0, 3 ) <= 995 ) {
117         $seg1 = substr( $isbn, 0, 3 );
118     }
119     elsif ( substr( $isbn, 0, 4 ) <= 9989 ) {
120         $seg1 = substr( $isbn, 0, 4 );
121     }
122     else {
123         $seg1 = substr( $isbn, 0, 5 );
124     }
125     my $x = substr( $isbn, length($seg1) );
126     my $seg2;
127     if ( substr( $x, 0, 2 ) <= 19 ) {
128
129         # if(sTmp2 < 10) sTmp2 = "0" sTmp2;
130         $seg2 = substr( $x, 0, 2 );
131     }
132     elsif ( substr( $x, 0, 3 ) <= 699 ) {
133         $seg2 = substr( $x, 0, 3 );
134     }
135     elsif ( substr( $x, 0, 4 ) <= 8399 ) {
136         $seg2 = substr( $x, 0, 4 );
137     }
138     elsif ( substr( $x, 0, 5 ) <= 89999 ) {
139         $seg2 = substr( $x, 0, 5 );
140     }
141     elsif ( substr( $x, 0, 6 ) <= 9499999 ) {
142         $seg2 = substr( $x, 0, 6 );
143     }
144     else {
145         $seg2 = substr( $x, 0, 7 );
146     }
147     my $seg3 = substr( $x, length($seg2) );
148     $seg3 = substr( $seg3, 0, length($seg3) - 1 );
149     my $seg4 = substr( $x, -1, 1 );
150     return "$seg1-$seg2-$seg3-$seg4";
151     } else {
152       my $seg1;
153       $seg1 = substr( $isbn, 0, 3 );
154       my $seg2;
155       if ( substr( $isbn, 3, 1 ) <= 7 ) {
156           $seg2 = substr( $isbn, 3, 1 );
157       }
158       elsif ( substr( $isbn, 3, 2 ) <= 94 ) {
159           $seg2 = substr( $isbn, 3, 2 );
160       }
161       elsif ( substr( $isbn, 3, 3 ) <= 995 ) {
162           $seg2 = substr( $isbn, 3, 3 );
163       }
164       elsif ( substr( $isbn, 3, 4 ) <= 9989 ) {
165           $seg2 = substr( $isbn, 3, 4 );
166       }
167       else {
168           $seg2 = substr( $isbn, 3, 5 );
169       }
170       my $x = substr( $isbn, length($seg2) +3);
171       my $seg3;
172       if ( substr( $x, 0, 2 ) <= 19 ) {
173   
174           # if(sTmp2 < 10) sTmp2 = "0" sTmp2;
175           $seg3 = substr( $x, 0, 2 );
176       }
177       elsif ( substr( $x, 0, 3 ) <= 699 ) {
178           $seg3 = substr( $x, 0, 3 );
179       }
180       elsif ( substr( $x, 0, 4 ) <= 8399 ) {
181           $seg3 = substr( $x, 0, 4 );
182       }
183       elsif ( substr( $x, 0, 5 ) <= 89999 ) {
184           $seg3 = substr( $x, 0, 5 );
185       }
186       elsif ( substr( $x, 0, 6 ) <= 9499999 ) {
187           $seg3 = substr( $x, 0, 6 );
188       }
189       else {
190           $seg3 = substr( $x, 0, 7 );
191       }
192       my $seg4 = substr( $x, length($seg3) );
193       $seg4 = substr( $seg4, 0, length($seg4) - 1 );
194       my $seg5 = substr( $x, -1, 1 );
195       return "$seg1-$seg2-$seg3-$seg4-$seg5";       
196     }    
197 }
198
199 # FIXME.. this should be moved to a MARC-specific module
200 sub subfield_is_koha_internal_p ($) {
201     my ($subfield) = @_;
202
203     # We could match on 'lib' and 'tab' (and 'mandatory', & more to come!)
204     # But real MARC subfields are always single-character
205     # so it really is safer just to check the length
206
207     return length $subfield != 1;
208 }
209
210 =head2 GetItemTypes
211
212   $itemtypes = &GetItemTypes();
213
214 Returns information about existing itemtypes.
215
216 build a HTML select with the following code :
217
218 =head3 in PERL SCRIPT
219
220     my $itemtypes = GetItemTypes;
221     my @itemtypesloop;
222     foreach my $thisitemtype (sort keys %$itemtypes) {
223         my $selected = 1 if $thisitemtype eq $itemtype;
224         my %row =(value => $thisitemtype,
225                     selected => $selected,
226                     description => $itemtypes->{$thisitemtype}->{'description'},
227                 );
228         push @itemtypesloop, \%row;
229     }
230     $template->param(itemtypeloop => \@itemtypesloop);
231
232 =head3 in TEMPLATE
233
234     <form action='<!-- TMPL_VAR name="script_name" -->' method=post>
235         <select name="itemtype">
236             <option value="">Default</option>
237         <!-- TMPL_LOOP name="itemtypeloop" -->
238             <option value="<!-- TMPL_VAR name="value" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="description" --></option>
239         <!-- /TMPL_LOOP -->
240         </select>
241         <input type=text name=searchfield value="<!-- TMPL_VAR name="searchfield" -->">
242         <input type="submit" value="OK" class="button">
243     </form>
244
245 =cut
246
247 sub GetItemTypes {
248
249     # returns a reference to a hash of references to itemtypes...
250     my %itemtypes;
251     my $dbh   = C4::Context->dbh;
252     my $query = qq|
253         SELECT *
254         FROM   itemtypes
255     |;
256     my $sth = $dbh->prepare($query);
257     $sth->execute;
258     while ( my $IT = $sth->fetchrow_hashref ) {
259         $itemtypes{ $IT->{'itemtype'} } = $IT;
260     }
261     return ( \%itemtypes );
262 }
263
264 sub get_itemtypeinfos_of {
265     my @itemtypes = @_;
266
267     my $placeholders = join( ', ', map { '?' } @itemtypes );
268     my $query = <<"END_SQL";
269 SELECT itemtype,
270        description,
271        imageurl,
272        notforloan
273   FROM itemtypes
274   WHERE itemtype IN ( $placeholders )
275 END_SQL
276
277     return get_infos_of( $query, 'itemtype', undef, \@itemtypes );
278 }
279
280 # this is temporary until we separate collection codes and item types
281 sub GetCcodes {
282     my $count = 0;
283     my @results;
284     my $dbh = C4::Context->dbh;
285     my $sth =
286       $dbh->prepare(
287         "SELECT * FROM authorised_values ORDER BY authorised_value");
288     $sth->execute;
289     while ( my $data = $sth->fetchrow_hashref ) {
290         if ( $data->{category} eq "CCODE" ) {
291             $count++;
292             $results[$count] = $data;
293
294             #warn "data: $data";
295         }
296     }
297     $sth->finish;
298     return ( $count, @results );
299 }
300
301 =head2 getauthtypes
302
303   $authtypes = &getauthtypes();
304
305 Returns information about existing authtypes.
306
307 build a HTML select with the following code :
308
309 =head3 in PERL SCRIPT
310
311 my $authtypes = getauthtypes;
312 my @authtypesloop;
313 foreach my $thisauthtype (keys %$authtypes) {
314     my $selected = 1 if $thisauthtype eq $authtype;
315     my %row =(value => $thisauthtype,
316                 selected => $selected,
317                 authtypetext => $authtypes->{$thisauthtype}->{'authtypetext'},
318             );
319     push @authtypesloop, \%row;
320 }
321 $template->param(itemtypeloop => \@itemtypesloop);
322
323 =head3 in TEMPLATE
324
325 <form action='<!-- TMPL_VAR name="script_name" -->' method=post>
326     <select name="authtype">
327     <!-- TMPL_LOOP name="authtypeloop" -->
328         <option value="<!-- TMPL_VAR name="value" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="authtypetext" --></option>
329     <!-- /TMPL_LOOP -->
330     </select>
331     <input type=text name=searchfield value="<!-- TMPL_VAR name="searchfield" -->">
332     <input type="submit" value="OK" class="button">
333 </form>
334
335
336 =cut
337
338 sub getauthtypes {
339
340     # returns a reference to a hash of references to authtypes...
341     my %authtypes;
342     my $dbh = C4::Context->dbh;
343     my $sth = $dbh->prepare("select * from auth_types order by authtypetext");
344     $sth->execute;
345     while ( my $IT = $sth->fetchrow_hashref ) {
346         $authtypes{ $IT->{'authtypecode'} } = $IT;
347     }
348     return ( \%authtypes );
349 }
350
351 sub getauthtype {
352     my ($authtypecode) = @_;
353
354     # returns a reference to a hash of references to authtypes...
355     my %authtypes;
356     my $dbh = C4::Context->dbh;
357     my $sth = $dbh->prepare("select * from auth_types where authtypecode=?");
358     $sth->execute($authtypecode);
359     my $res = $sth->fetchrow_hashref;
360     return $res;
361 }
362
363 =head2 getframework
364
365   $frameworks = &getframework();
366
367 Returns information about existing frameworks
368
369 build a HTML select with the following code :
370
371 =head3 in PERL SCRIPT
372
373 my $frameworks = frameworks();
374 my @frameworkloop;
375 foreach my $thisframework (keys %$frameworks) {
376     my $selected = 1 if $thisframework eq $frameworkcode;
377     my %row =(value => $thisframework,
378                 selected => $selected,
379                 description => $frameworks->{$thisframework}->{'frameworktext'},
380             );
381     push @frameworksloop, \%row;
382 }
383 $template->param(frameworkloop => \@frameworksloop);
384
385 =head3 in TEMPLATE
386
387 <form action='<!-- TMPL_VAR name="script_name" -->' method=post>
388     <select name="frameworkcode">
389         <option value="">Default</option>
390     <!-- TMPL_LOOP name="frameworkloop" -->
391         <option value="<!-- TMPL_VAR name="value" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="frameworktext" --></option>
392     <!-- /TMPL_LOOP -->
393     </select>
394     <input type=text name=searchfield value="<!-- TMPL_VAR name="searchfield" -->">
395     <input type="submit" value="OK" class="button">
396 </form>
397
398
399 =cut
400
401 sub getframeworks {
402
403     # returns a reference to a hash of references to branches...
404     my %itemtypes;
405     my $dbh = C4::Context->dbh;
406     my $sth = $dbh->prepare("select * from biblio_framework");
407     $sth->execute;
408     while ( my $IT = $sth->fetchrow_hashref ) {
409         $itemtypes{ $IT->{'frameworkcode'} } = $IT;
410     }
411     return ( \%itemtypes );
412 }
413
414 =head2 getframeworkinfo
415
416   $frameworkinfo = &getframeworkinfo($frameworkcode);
417
418 Returns information about an frameworkcode.
419
420 =cut
421
422 sub getframeworkinfo {
423     my ($frameworkcode) = @_;
424     my $dbh             = C4::Context->dbh;
425     my $sth             =
426       $dbh->prepare("select * from biblio_framework where frameworkcode=?");
427     $sth->execute($frameworkcode);
428     my $res = $sth->fetchrow_hashref;
429     return $res;
430 }
431
432 =head2 getitemtypeinfo
433
434   $itemtype = &getitemtype($itemtype);
435
436 Returns information about an itemtype.
437
438 =cut
439
440 sub getitemtypeinfo {
441     my ($itemtype) = @_;
442     my $dbh        = C4::Context->dbh;
443     my $sth        = $dbh->prepare("select * from itemtypes where itemtype=?");
444     $sth->execute($itemtype);
445     my $res = $sth->fetchrow_hashref;
446
447     $res->{imageurl} = getitemtypeimagelocation( 'intranet', $res->{imageurl} );
448
449     return $res;
450 }
451
452 =head2 getitemtypeimagedir
453
454 =over
455
456 =item 4
457
458   my $directory = getitemtypeimagedir( 'opac' );
459
460 pass in 'opac' or 'intranet'. Defaults to 'opac'.
461
462 returns the full path to the appropriate directory containing images.
463
464 =back
465
466 =cut
467
468 sub getitemtypeimagedir {
469         my $src = shift || 'opac';
470         if ($src eq 'intranet') {
471                 return C4::Context->config('intrahtdocs') . '/' .C4::Context->preference('template') . '/img/itemtypeimg';
472         } else {
473                 return C4::Context->config('opachtdocs') . '/' . C4::Context->preference('template') . '/itemtypeimg';
474         }
475 }
476
477 sub getitemtypeimagesrc {
478         my $src = shift || 'opac';
479         if ($src eq 'intranet') {
480                 return '/intranet-tmpl' . '/' . C4::Context->preference('template') . '/img/itemtypeimg';
481         } else {
482                 return '/opac-tmpl' . '/' . C4::Context->preference('template') . '/itemtypeimg';
483         }
484 }
485
486 sub getitemtypeimagelocation($$) {
487         my ( $src, $image ) = @_;
488
489         return '' if ( !$image );
490
491         my $scheme = ( uri_split( $image ) )[0];
492
493         return $image if ( $scheme );
494
495         return getitemtypeimagesrc( $src ) . '/' . $image;
496 }
497
498 =head3 _getImagesFromDirectory
499
500   Find all of the image files in a directory in the filesystem
501
502   parameters:
503     a directory name
504
505   returns: a list of images in that directory.
506
507   Notes: this does not traverse into subdirectories. See
508       _getSubdirectoryNames for help with that.
509     Images are assumed to be files with .gif or .png file extensions.
510     The image names returned do not have the directory name on them.
511
512 =cut
513
514 sub _getImagesFromDirectory {
515     my $directoryname = shift;
516     return unless defined $directoryname;
517     return unless -d $directoryname;
518
519     if ( opendir ( my $dh, $directoryname ) ) {
520         my @images = grep { /\.(gif|png)$/i } readdir( $dh );
521         closedir $dh;
522         return @images;
523     } else {
524         warn "unable to opendir $directoryname: $!";
525         return;
526     }
527 }
528
529 =head3 _getSubdirectoryNames
530
531   Find all of the directories in a directory in the filesystem
532
533   parameters:
534     a directory name
535
536   returns: a list of subdirectories in that directory.
537
538   Notes: this does not traverse into subdirectories. Only the first
539       level of subdirectories are returned.
540     The directory names returned don't have the parent directory name
541       on them.
542
543 =cut
544
545 sub _getSubdirectoryNames {
546     my $directoryname = shift;
547     return unless defined $directoryname;
548     return unless -d $directoryname;
549
550     if ( opendir ( my $dh, $directoryname ) ) {
551         my @directories = grep { -d File::Spec->catfile( $directoryname, $_ ) && ! ( /^\./ ) } readdir( $dh );
552         closedir $dh;
553         return @directories;
554     } else {
555         warn "unable to opendir $directoryname: $!";
556         return;
557     }
558 }
559
560 =head3 getImageSets
561
562   returns: a listref of hashrefs. Each hash represents another collection of images.
563            { imagesetname => 'npl', # the name of the image set (npl is the original one)
564              images => listref of image hashrefs
565            }
566
567     each image is represented by a hashref like this:
568       { KohaImage     => 'npl/image.gif',
569         StaffImageUrl => '/intranet-tmpl/prog/img/itemtypeimg/npl/image.gif',
570         OpacImageURL  => '/opac-tmpl/prog/itemtypeimg/npl/image.gif'
571         checked       => 0 or 1: was this the image passed to this method?
572                          Note: I'd like to remove this somehow.
573       }
574
575 =cut
576
577 sub getImageSets {
578     my %params = @_;
579     my $checked = $params{'checked'} || '';
580
581     my $paths = { staff => { filesystem => getitemtypeimagedir('intranet'),
582                              url        => getitemtypeimagesrc('intranet'),
583                         },
584                   opac => { filesystem => getitemtypeimagedir('opac'),
585                              url       => getitemtypeimagesrc('opac'),
586                         }
587                   };
588
589     my @imagesets = (); # list of hasrefs of image set data to pass to template
590     my @subdirectories = _getSubdirectoryNames( $paths->{'staff'}{'filesystem'} );
591
592     foreach my $imagesubdir ( @subdirectories ) {
593         my @imagelist     = (); # hashrefs of image info
594         my @imagenames = _getImagesFromDirectory( File::Spec->catfile( $paths->{'staff'}{'filesystem'}, $imagesubdir ) );
595         foreach my $thisimage ( @imagenames ) {
596             push( @imagelist,
597                   { KohaImage     => "$imagesubdir/$thisimage",
598                     StaffImageUrl => join( '/', $paths->{'staff'}{'url'}, $imagesubdir, $thisimage ),
599                     OpacImageUrl  => join( '/', $paths->{'opac'}{'url'}, $imagesubdir, $thisimage ),
600                     checked       => "$imagesubdir/$thisimage" eq $checked ? 1 : 0,
601                }
602              );
603         }
604         push @imagesets, { imagesetname => $imagesubdir,
605                            images       => \@imagelist };
606         
607     }
608     return \@imagesets;
609 }
610
611 =head2 GetPrinters
612
613   $printers = &GetPrinters();
614   @queues = keys %$printers;
615
616 Returns information about existing printer queues.
617
618 C<$printers> is a reference-to-hash whose keys are the print queues
619 defined in the printers table of the Koha database. The values are
620 references-to-hash, whose keys are the fields in the printers table.
621
622 =cut
623
624 sub GetPrinters {
625     my %printers;
626     my $dbh = C4::Context->dbh;
627     my $sth = $dbh->prepare("select * from printers");
628     $sth->execute;
629     while ( my $printer = $sth->fetchrow_hashref ) {
630         $printers{ $printer->{'printqueue'} } = $printer;
631     }
632     return ( \%printers );
633 }
634
635 =head2 GetPrinter
636
637 $printer = GetPrinter( $query, $printers );
638
639 =cut
640
641 sub GetPrinter ($$) {
642     my ( $query, $printers ) = @_;    # get printer for this query from printers
643     my $printer = $query->param('printer');
644     my %cookie = $query->cookie('userenv');
645     ($printer) || ( $printer = $cookie{'printer'} ) || ( $printer = '' );
646     ( $printers->{$printer} ) || ( $printer = ( keys %$printers )[0] );
647     return $printer;
648 }
649
650 =head2 getnbpages
651
652 Returns the number of pages to display in a pagination bar, given the number
653 of items and the number of items per page.
654
655 =cut
656
657 sub getnbpages {
658     my ( $nb_items, $nb_items_per_page ) = @_;
659
660     return int( ( $nb_items - 1 ) / $nb_items_per_page ) + 1;
661 }
662
663 =head2 getallthemes
664
665   (@themes) = &getallthemes('opac');
666   (@themes) = &getallthemes('intranet');
667
668 Returns an array of all available themes.
669
670 =cut
671
672 sub getallthemes {
673     my $type = shift;
674     my $htdocs;
675     my @themes;
676     if ( $type eq 'intranet' ) {
677         $htdocs = C4::Context->config('intrahtdocs');
678     }
679     else {
680         $htdocs = C4::Context->config('opachtdocs');
681     }
682     opendir D, "$htdocs";
683     my @dirlist = readdir D;
684     foreach my $directory (@dirlist) {
685         -d "$htdocs/$directory/en" and push @themes, $directory;
686     }
687     return @themes;
688 }
689
690 sub getFacets {
691     my $facets;
692     if ( C4::Context->preference("marcflavour") eq "UNIMARC" ) {
693         $facets = [
694             {
695                 link_value  => 'su-to',
696                 label_value => 'Topics',
697                 tags        =>
698                   [ '600', '601', '602', '603', '604', '605', '606', '610' ],
699                 subfield => 'a',
700             },
701             {
702                 link_value  => 'su-geo',
703                 label_value => 'Places',
704                 tags        => ['651'],
705                 subfield    => 'a',
706             },
707             {
708                 link_value  => 'su-ut',
709                 label_value => 'Titles',
710                 tags        => [ '500', '501', '502', '503', '504', ],
711                 subfield    => 'a',
712             },
713             {
714                 link_value  => 'au',
715                 label_value => 'Authors',
716                 tags        => [ '700', '701', '702', ],
717                 subfield    => 'a',
718             },
719             {
720                 link_value  => 'se',
721                 label_value => 'Series',
722                 tags        => ['225'],
723                 subfield    => 'a',
724             },
725             ];
726
727             my $library_facet;
728
729             $library_facet = {
730                 link_value  => 'branch',
731                 label_value => 'Libraries',
732                 tags        => [ '995', ],
733                 subfield    => 'b',
734                 expanded    => '1',
735             };
736             push @$facets, $library_facet unless C4::Context->preference("singleBranchMode");
737     }
738     else {
739         $facets = [
740             {
741                 link_value  => 'su-to',
742                 label_value => 'Topics',
743                 tags        => ['650'],
744                 subfield    => 'a',
745             },
746
747             #        {
748             #        link_value => 'su-na',
749             #        label_value => 'People and Organizations',
750             #        tags => ['600', '610', '611'],
751             #        subfield => 'a',
752             #        },
753             {
754                 link_value  => 'su-geo',
755                 label_value => 'Places',
756                 tags        => ['651'],
757                 subfield    => 'a',
758             },
759             {
760                 link_value  => 'su-ut',
761                 label_value => 'Titles',
762                 tags        => ['630'],
763                 subfield    => 'a',
764             },
765             {
766                 link_value  => 'au',
767                 label_value => 'Authors',
768                 tags        => [ '100', '110', '700', ],
769                 subfield    => 'a',
770             },
771             {
772                 link_value  => 'se',
773                 label_value => 'Series',
774                 tags        => [ '440', '490', ],
775                 subfield    => 'a',
776             },
777             ];
778             my $library_facet;
779             $library_facet = {
780                 link_value  => 'branch',
781                 label_value => 'Libraries',
782                 tags        => [ '952', ],
783                 subfield    => 'b',
784                 expanded    => '1',
785             };
786             push @$facets, $library_facet unless C4::Context->preference("singleBranchMode");
787     }
788     return $facets;
789 }
790
791 =head2 get_infos_of
792
793 Return a href where a key is associated to a href. You give a query,
794 the name of the key among the fields returned by the query. If you
795 also give as third argument the name of the value, the function
796 returns a href of scalar. The optional 4th argument is an arrayref of
797 items passed to the C<execute()> call. It is designed to bind
798 parameters to any placeholders in your SQL.
799
800   my $query = '
801 SELECT itemnumber,
802        notforloan,
803        barcode
804   FROM items
805 ';
806
807   # generic href of any information on the item, href of href.
808   my $iteminfos_of = get_infos_of($query, 'itemnumber');
809   print $iteminfos_of->{$itemnumber}{barcode};
810
811   # specific information, href of scalar
812   my $barcode_of_item = get_infos_of($query, 'itemnumber', 'barcode');
813   print $barcode_of_item->{$itemnumber};
814
815 =cut
816
817 sub get_infos_of {
818     my ( $query, $key_name, $value_name, $bind_params ) = @_;
819
820     my $dbh = C4::Context->dbh;
821
822     my $sth = $dbh->prepare($query);
823     $sth->execute( @$bind_params );
824
825     my %infos_of;
826     while ( my $row = $sth->fetchrow_hashref ) {
827         if ( defined $value_name ) {
828             $infos_of{ $row->{$key_name} } = $row->{$value_name};
829         }
830         else {
831             $infos_of{ $row->{$key_name} } = $row;
832         }
833     }
834     $sth->finish;
835
836     return \%infos_of;
837 }
838
839 =head2 get_notforloan_label_of
840
841   my $notforloan_label_of = get_notforloan_label_of();
842
843 Each authorised value of notforloan (information available in items and
844 itemtypes) is link to a single label.
845
846 Returns a href where keys are authorised values and values are corresponding
847 labels.
848
849   foreach my $authorised_value (keys %{$notforloan_label_of}) {
850     printf(
851         "authorised_value: %s => %s\n",
852         $authorised_value,
853         $notforloan_label_of->{$authorised_value}
854     );
855   }
856
857 =cut
858
859 # FIXME - why not use GetAuthorisedValues ??
860 #
861 sub get_notforloan_label_of {
862     my $dbh = C4::Context->dbh;
863
864     my $query = '
865 SELECT authorised_value
866   FROM marc_subfield_structure
867   WHERE kohafield = \'items.notforloan\'
868   LIMIT 0, 1
869 ';
870     my $sth = $dbh->prepare($query);
871     $sth->execute();
872     my ($statuscode) = $sth->fetchrow_array();
873
874     $query = '
875 SELECT lib,
876        authorised_value
877   FROM authorised_values
878   WHERE category = ?
879 ';
880     $sth = $dbh->prepare($query);
881     $sth->execute($statuscode);
882     my %notforloan_label_of;
883     while ( my $row = $sth->fetchrow_hashref ) {
884         $notforloan_label_of{ $row->{authorised_value} } = $row->{lib};
885     }
886     $sth->finish;
887
888     return \%notforloan_label_of;
889 }
890
891 =head2 displayServers
892
893 =over 4
894
895 my $servers = displayServers();
896
897 my $servers = displayServers( $position );
898
899 my $servers = displayServers( $position, $type );
900
901 =back
902
903 displayServers returns a listref of hashrefs, each containing
904 information about available z3950 servers. Each hashref has a format
905 like:
906
907     {
908       'checked'    => 'checked',
909       'encoding'   => 'MARC-8'
910       'icon'       => undef,
911       'id'         => 'LIBRARY OF CONGRESS',
912       'label'      => '',
913       'name'       => 'server',
914       'opensearch' => '',
915       'value'      => 'z3950.loc.gov:7090/',
916       'zed'        => 1,
917     },
918
919
920 =cut
921
922 sub displayServers {
923     my ( $position, $type ) = @_;
924     my $dbh = C4::Context->dbh;
925
926     my $strsth = 'SELECT * FROM z3950servers';
927     my @where_clauses;
928     my @bind_params;
929
930     if ($position) {
931         push @bind_params,   $position;
932         push @where_clauses, ' position = ? ';
933     }
934
935     if ($type) {
936         push @bind_params,   $type;
937         push @where_clauses, ' type = ? ';
938     }
939
940     # reassemble where clause from where clause pieces
941     if (@where_clauses) {
942         $strsth .= ' WHERE ' . join( ' AND ', @where_clauses );
943     }
944
945     my $rq = $dbh->prepare($strsth);
946     $rq->execute(@bind_params);
947     my @primaryserverloop;
948
949     while ( my $data = $rq->fetchrow_hashref ) {
950         push @primaryserverloop,
951           { label    => $data->{description},
952             id       => $data->{name},
953             name     => "server",
954             value    => $data->{host} . ":" . $data->{port} . "/" . $data->{database},
955             encoding => ( $data->{encoding} ? $data->{encoding} : "iso-5426" ),
956             checked  => "checked",
957             icon     => $data->{icon},
958             zed        => $data->{type} eq 'zed',
959             opensearch => $data->{type} eq 'opensearch'
960           };
961     }
962     return \@primaryserverloop;
963 }
964
965 sub displaySecondaryServers {
966
967 #       my $secondary_servers_loop = [
968 #               { inner_sup_servers_loop => [
969 #               {label => "Google", id=>"GOOG", value=>"google",icon => "google.ico",opensearch => "1"},
970 #               {label => "Yahoo", id=>"YAH", value=>"yahoo", icon =>"yahoo.ico", zed => "1"},
971 #               {label => "Worldcat", id=>"WCT", value=>"worldcat", icon => "worldcat.gif", zed => "1"},
972 #               {label => "Library of Congress", id=>"LOC", name=> "server", value=>"z3950.loc.gov:7090/Voyager", icon =>"loc.ico", zed => "1"},
973 #       ],
974 #       },
975 #       ];
976     return;    #$secondary_servers_loop;
977 }
978
979 =head2 GetAuthValCode
980
981 $authvalcode = GetAuthValCode($kohafield,$frameworkcode);
982
983 =cut
984
985 sub GetAuthValCode {
986         my ($kohafield,$fwcode) = @_;
987         my $dbh = C4::Context->dbh;
988         $fwcode='' unless $fwcode;
989         my $sth = $dbh->prepare('select authorised_value from marc_subfield_structure where kohafield=? and frameworkcode=?');
990         $sth->execute($kohafield,$fwcode);
991         my ($authvalcode) = $sth->fetchrow_array;
992         return $authvalcode;
993 }
994
995 =head2 GetAuthorisedValues
996
997 $authvalues = GetAuthorisedValues([$category], [$selected]);
998
999 This function returns all authorised values from the'authosied_value' table in a reference to array of hashrefs.
1000
1001 C<$category> returns authorised values for just one category (optional).
1002
1003 =cut
1004
1005 sub GetAuthorisedValues {
1006     my ($category,$selected) = @_;
1007         my @results;
1008     my $dbh      = C4::Context->dbh;
1009     my $query    = "SELECT * FROM authorised_values";
1010     $query .= " WHERE category = '" . $category . "'" if $category;
1011
1012     my $sth = $dbh->prepare($query);
1013     $sth->execute;
1014         while (my $data=$sth->fetchrow_hashref) {
1015                 if ($selected eq $data->{'authorised_value'} ) {
1016                         $data->{'selected'} = 1;
1017                 }
1018         push @results, $data;
1019         }
1020     #my $data = $sth->fetchall_arrayref({});
1021     return \@results; #$data;
1022 }
1023
1024 =head2 GetAuthorisedValueCategories
1025
1026 $auth_categories = GetAuthorisedValueCategories();
1027
1028 Return an arrayref of all of the available authorised
1029 value categories.
1030
1031 =cut
1032
1033 sub GetAuthorisedValueCategories {
1034     my $dbh = C4::Context->dbh;
1035     my $sth = $dbh->prepare("SELECT DISTINCT category FROM authorised_values ORDER BY category");
1036     $sth->execute;
1037     my @results;
1038     while (my $category = $sth->fetchrow_array) {
1039         push @results, $category;
1040     }
1041     return \@results;
1042 }
1043
1044 =head2 GetKohaAuthorisedValues
1045         
1046         Takes $kohafield, $fwcode as parameters.
1047         Returns hashref of Code => description
1048         Returns undef 
1049           if no authorised value category is defined for the kohafield.
1050
1051 =cut
1052
1053 sub GetKohaAuthorisedValues {
1054   my ($kohafield,$fwcode,$codedvalue) = @_;
1055   $fwcode='' unless $fwcode;
1056   my %values;
1057   my $dbh = C4::Context->dbh;
1058   my $avcode = GetAuthValCode($kohafield,$fwcode);
1059   if ($avcode) {  
1060         my $sth = $dbh->prepare("select authorised_value, lib from authorised_values where category=? ");
1061         $sth->execute($avcode);
1062         while ( my ($val, $lib) = $sth->fetchrow_array ) { 
1063                 $values{$val}= $lib;
1064         }
1065         return \%values;
1066   } else {
1067         return undef;
1068   }
1069 }
1070
1071 =head2 display_marc_indicators
1072
1073 =over 4
1074
1075 # field is a MARC::Field object
1076 my $display_form = C4::Koha::display_marc_indicators($field);
1077
1078 =back
1079
1080 Generate a display form of the indicators of a variable
1081 MARC field, replacing any blanks with '#'.
1082
1083 =cut
1084
1085 sub display_marc_indicators {
1086     my $field = shift;
1087     my $indicators = '';
1088     if ($field->tag() >= 10) {
1089         $indicators = $field->indicator(1) . $field->indicator(2);
1090         $indicators =~ s/ /#/g;
1091     }
1092     return $indicators;
1093 }
1094
1095 sub GetNormalizedUPC {
1096  my ($record,$marcflavour) = @_;
1097     my (@fields,$upc);
1098
1099     if ($marcflavour eq 'MARC21') {
1100         @fields = $record->field('024');
1101         foreach my $field (@fields) {
1102             my $indicator = $field->indicator(1);
1103             my $upc = _normalize_match_point($field->subfield('a'));
1104             if ($indicator == 1 and $upc ne '') {
1105                 return $upc;
1106             }
1107         }
1108     }
1109     else { # assume unimarc if not marc21
1110         @fields = $record->field('072');
1111         foreach my $field (@fields) {
1112             my $upc = _normalize_match_point($field->subfield('a'));
1113             if ($upc ne '') {
1114                 return $upc;
1115             }
1116         }
1117     }
1118 }
1119
1120 # Normalizes and returns the first valid ISBN found in the record
1121 sub GetNormalizedISBN {
1122     my ($isbn,$record,$marcflavour) = @_;
1123     my @fields;
1124     if ($isbn) {
1125         return _isbn_cleanup($isbn);
1126     }
1127     return undef unless $record;
1128
1129     if ($marcflavour eq 'MARC21') {
1130         @fields = $record->field('020');
1131         foreach my $field (@fields) {
1132             $isbn = $field->subfield('a');
1133             if ($isbn) {
1134                 return _isbn_cleanup($isbn);
1135             } else {
1136                 return undef;
1137             }
1138         }
1139     }
1140     else { # assume unimarc if not marc21
1141         @fields = $record->field('010');
1142         foreach my $field (@fields) {
1143             my $isbn = $field->subfield('a');
1144             if ($isbn) {
1145                 return _isbn_cleanup($isbn);
1146             } else {
1147                 return undef;
1148             }
1149         }
1150     }
1151
1152 }
1153
1154 sub GetNormalizedEAN {
1155     my ($record,$marcflavour) = @_;
1156     my (@fields,$ean);
1157
1158     if ($marcflavour eq 'MARC21') {
1159         @fields = $record->field('024');
1160         foreach my $field (@fields) {
1161             my $indicator = $field->indicator(1);
1162             $ean = _normalize_match_point($field->subfield('a'));
1163             if ($indicator == 3 and $ean ne '') {
1164                 return $ean;
1165             }
1166         }
1167     }
1168     else { # assume unimarc if not marc21
1169         @fields = $record->field('073');
1170         foreach my $field (@fields) {
1171             $ean = _normalize_match_point($field->subfield('a'));
1172             if ($ean ne '') {
1173                 return $ean;
1174             }
1175         }
1176     }
1177 }
1178 sub GetNormalizedOCLCNumber {
1179     my ($record,$marcflavour) = @_;
1180     my (@fields,$oclc);
1181
1182     if ($marcflavour eq 'MARC21') {
1183         @fields = $record->field('035');
1184         foreach my $field (@fields) {
1185             $oclc = $field->subfield('a');
1186             if ($oclc =~ /OCoLC/) {
1187                 $oclc =~ s/\(OCoLC\)//;
1188                 return $oclc;
1189             } else {
1190                 return undef;
1191             }
1192         }
1193     }
1194     else { # TODO: add UNIMARC fields
1195     }
1196 }
1197
1198 sub _normalize_match_point {
1199     my $match_point = shift;
1200     (my $normalized_match_point) = $match_point =~ /([\d-]*[X]*)/;
1201     $normalized_match_point =~ s/-//g;
1202
1203     return $normalized_match_point;
1204 }
1205
1206 sub _isbn_cleanup ($) {
1207     my $normalized_isbn = shift;
1208     $normalized_isbn =~ s/-//g;
1209     $normalized_isbn =~/([0-9]{1,})/;
1210     $normalized_isbn = $1;
1211     if (
1212         $normalized_isbn =~ /\b(\d{13})\b/ or
1213         $normalized_isbn =~ /\b(\d{10})\b/ or
1214         $normalized_isbn =~ /\b(\d{9}X)\b/i
1215     ) { 
1216         return $1;
1217     }
1218     return undef;
1219 }
1220
1221 1;
1222
1223 __END__
1224
1225 =head1 AUTHOR
1226
1227 Koha Team
1228
1229 =cut