Merge remote branch 'kc/new/bug_5058' into kcmaster
[koha.git] / C4 / Koha.pm
bloba63a3a551a4601cdf6dfb586e08f9a5224c2189a
1 package C4::Koha;
3 # Copyright 2000-2002 Katipo Communications
4 # Parts Copyright 2010 Nelsonville Public Library
6 # This file is part of Koha.
8 # Koha is free software; you can redistribute it and/or modify it under the
9 # terms of the GNU General Public License as published by the Free Software
10 # Foundation; either version 2 of the License, or (at your option) any later
11 # version.
13 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
14 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15 # A PARTICULAR PURPOSE. See the GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License along
18 # with Koha; if not, write to the Free Software Foundation, Inc.,
19 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 use strict;
23 #use warnings; FIXME - Bug 2505
24 use C4::Context;
25 use C4::Output;
26 use URI::Split qw(uri_split);
27 use Memoize;
28 use Business::ISBN;
30 use vars qw($VERSION @ISA @EXPORT $DEBUG);
32 BEGIN {
33 $VERSION = 3.01;
34 require Exporter;
35 @ISA = qw(Exporter);
36 @EXPORT = qw(
37 &slashifyDate
38 &DisplayISBN
39 &subfield_is_koha_internal_p
40 &GetPrinters &GetPrinter
41 &GetItemTypes &getitemtypeinfo
42 &GetCcodes
43 &GetSupportName &GetSupportList
44 &get_itemtypeinfos_of
45 &getframeworks &getframeworkinfo
46 &getauthtypes &getauthtype
47 &getallthemes
48 &getFacets
49 &displayServers
50 &getnbpages
51 &get_infos_of
52 &get_notforloan_label_of
53 &getitemtypeimagedir
54 &getitemtypeimagesrc
55 &getitemtypeimagelocation
56 &GetAuthorisedValues
57 &GetAuthorisedValueCategories
58 &GetKohaAuthorisedValues
59 &GetKohaAuthorisedValuesFromField
60 &GetAuthValCode
61 &GetNormalizedUPC
62 &GetNormalizedISBN
63 &GetNormalizedEAN
64 &GetNormalizedOCLCNumber
65 &xml_escape
67 $DEBUG
69 $DEBUG = 0;
72 # expensive functions
73 memoize('GetAuthorisedValues');
75 =head1 NAME
77 C4::Koha - Perl Module containing convenience functions for Koha scripts
79 =head1 SYNOPSIS
81 use C4::Koha;
83 =head1 DESCRIPTION
85 Koha.pm provides many functions for Koha scripts.
87 =head1 FUNCTIONS
89 =cut
91 =head2 slashifyDate
93 $slash_date = &slashifyDate($dash_date);
95 Takes a string of the form "DD-MM-YYYY" (or anything separated by
96 dashes), converts it to the form "YYYY/MM/DD", and returns the result.
98 =cut
100 sub slashifyDate {
102 # accepts a date of the form xx-xx-xx[xx] and returns it in the
103 # form xx/xx/xx[xx]
104 my @dateOut = split( '-', shift );
105 return ("$dateOut[2]/$dateOut[1]/$dateOut[0]");
109 =head2 DisplayISBN
111 my $string = DisplayISBN( $isbn );
113 =cut
115 sub DisplayISBN {
116 my ($isbn) = @_;
117 if (length ($isbn)<13){
118 my $seg1;
119 if ( substr( $isbn, 0, 1 ) <= 7 ) {
120 $seg1 = substr( $isbn, 0, 1 );
122 elsif ( substr( $isbn, 0, 2 ) <= 94 ) {
123 $seg1 = substr( $isbn, 0, 2 );
125 elsif ( substr( $isbn, 0, 3 ) <= 995 ) {
126 $seg1 = substr( $isbn, 0, 3 );
128 elsif ( substr( $isbn, 0, 4 ) <= 9989 ) {
129 $seg1 = substr( $isbn, 0, 4 );
131 else {
132 $seg1 = substr( $isbn, 0, 5 );
134 my $x = substr( $isbn, length($seg1) );
135 my $seg2;
136 if ( substr( $x, 0, 2 ) <= 19 ) {
138 # if(sTmp2 < 10) sTmp2 = "0" sTmp2;
139 $seg2 = substr( $x, 0, 2 );
141 elsif ( substr( $x, 0, 3 ) <= 699 ) {
142 $seg2 = substr( $x, 0, 3 );
144 elsif ( substr( $x, 0, 4 ) <= 8399 ) {
145 $seg2 = substr( $x, 0, 4 );
147 elsif ( substr( $x, 0, 5 ) <= 89999 ) {
148 $seg2 = substr( $x, 0, 5 );
150 elsif ( substr( $x, 0, 6 ) <= 9499999 ) {
151 $seg2 = substr( $x, 0, 6 );
153 else {
154 $seg2 = substr( $x, 0, 7 );
156 my $seg3 = substr( $x, length($seg2) );
157 $seg3 = substr( $seg3, 0, length($seg3) - 1 );
158 my $seg4 = substr( $x, -1, 1 );
159 return "$seg1-$seg2-$seg3-$seg4";
160 } else {
161 my $seg1;
162 $seg1 = substr( $isbn, 0, 3 );
163 my $seg2;
164 if ( substr( $isbn, 3, 1 ) <= 7 ) {
165 $seg2 = substr( $isbn, 3, 1 );
167 elsif ( substr( $isbn, 3, 2 ) <= 94 ) {
168 $seg2 = substr( $isbn, 3, 2 );
170 elsif ( substr( $isbn, 3, 3 ) <= 995 ) {
171 $seg2 = substr( $isbn, 3, 3 );
173 elsif ( substr( $isbn, 3, 4 ) <= 9989 ) {
174 $seg2 = substr( $isbn, 3, 4 );
176 else {
177 $seg2 = substr( $isbn, 3, 5 );
179 my $x = substr( $isbn, length($seg2) +3);
180 my $seg3;
181 if ( substr( $x, 0, 2 ) <= 19 ) {
183 # if(sTmp2 < 10) sTmp2 = "0" sTmp2;
184 $seg3 = substr( $x, 0, 2 );
186 elsif ( substr( $x, 0, 3 ) <= 699 ) {
187 $seg3 = substr( $x, 0, 3 );
189 elsif ( substr( $x, 0, 4 ) <= 8399 ) {
190 $seg3 = substr( $x, 0, 4 );
192 elsif ( substr( $x, 0, 5 ) <= 89999 ) {
193 $seg3 = substr( $x, 0, 5 );
195 elsif ( substr( $x, 0, 6 ) <= 9499999 ) {
196 $seg3 = substr( $x, 0, 6 );
198 else {
199 $seg3 = substr( $x, 0, 7 );
201 my $seg4 = substr( $x, length($seg3) );
202 $seg4 = substr( $seg4, 0, length($seg4) - 1 );
203 my $seg5 = substr( $x, -1, 1 );
204 return "$seg1-$seg2-$seg3-$seg4-$seg5";
208 # FIXME.. this should be moved to a MARC-specific module
209 sub subfield_is_koha_internal_p ($) {
210 my ($subfield) = @_;
212 # We could match on 'lib' and 'tab' (and 'mandatory', & more to come!)
213 # But real MARC subfields are always single-character
214 # so it really is safer just to check the length
216 return length $subfield != 1;
219 =head2 GetSupportName
221 $itemtypename = &GetSupportName($codestring);
223 Returns a string with the name of the itemtype.
225 =cut
227 sub GetSupportName{
228 my ($codestring)=@_;
229 return if (! $codestring);
230 my $resultstring;
231 my $advanced_search_types = C4::Context->preference("AdvancedSearchTypes");
232 if (!$advanced_search_types or $advanced_search_types eq 'itemtypes') {
233 my $query = qq|
234 SELECT description
235 FROM itemtypes
236 WHERE itemtype=?
237 order by description
239 my $sth = C4::Context->dbh->prepare($query);
240 $sth->execute($codestring);
241 ($resultstring)=$sth->fetchrow;
242 return $resultstring;
243 } else {
244 my $sth =
245 C4::Context->dbh->prepare(
246 "SELECT lib FROM authorised_values WHERE category = ? AND authorised_value = ?"
248 $sth->execute( $advanced_search_types, $codestring );
249 my $data = $sth->fetchrow_hashref;
250 return $$data{'lib'};
254 =head2 GetSupportList
256 $itemtypes = &GetSupportList();
258 Returns an array ref containing informations about Support (since itemtype is rather a circulation code when item-level-itypes is used).
260 build a HTML select with the following code :
262 =head3 in PERL SCRIPT
264 my $itemtypes = GetSupportList();
265 $template->param(itemtypeloop => $itemtypes);
267 =head3 in TEMPLATE
269 <form action='<!-- TMPL_VAR name="script_name" -->' method=post>
270 <select name="itemtype">
271 <option value="">Default</option>
272 <!-- TMPL_LOOP name="itemtypeloop" -->
273 <option value="<!-- TMPL_VAR name="itemtype" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->> <!--TMPL_IF Name="imageurl"--><img alt="<!-- TMPL_VAR name="description" -->" src="<!--TMPL_VAR Name="imageurl"-->><!--TMPL_ELSE-->"<!-- TMPL_VAR name="description" --><!--/TMPL_IF--></option>
274 <!-- /TMPL_LOOP -->
275 </select>
276 <input type=text name=searchfield value="<!-- TMPL_VAR name="searchfield" -->">
277 <input type="submit" value="OK" class="button">
278 </form>
280 =cut
282 sub GetSupportList{
283 my $advanced_search_types = C4::Context->preference("AdvancedSearchTypes");
284 if (!$advanced_search_types or $advanced_search_types eq 'itemtypes') {
285 my $query = qq|
286 SELECT *
287 FROM itemtypes
288 order by description
290 my $sth = C4::Context->dbh->prepare($query);
291 $sth->execute;
292 return $sth->fetchall_arrayref({});
293 } else {
294 my $advsearchtypes = GetAuthorisedValues($advanced_search_types);
295 my @results= map {{itemtype=>$$_{authorised_value},description=>$$_{lib},imageurl=>$$_{imageurl}}} @$advsearchtypes;
296 return \@results;
299 =head2 GetItemTypes
301 $itemtypes = &GetItemTypes();
303 Returns information about existing itemtypes.
305 build a HTML select with the following code :
307 =head3 in PERL SCRIPT
309 my $itemtypes = GetItemTypes;
310 my @itemtypesloop;
311 foreach my $thisitemtype (sort keys %$itemtypes) {
312 my $selected = 1 if $thisitemtype eq $itemtype;
313 my %row =(value => $thisitemtype,
314 selected => $selected,
315 description => $itemtypes->{$thisitemtype}->{'description'},
317 push @itemtypesloop, \%row;
319 $template->param(itemtypeloop => \@itemtypesloop);
321 =head3 in TEMPLATE
323 <form action='<!-- TMPL_VAR name="script_name" -->' method=post>
324 <select name="itemtype">
325 <option value="">Default</option>
326 <!-- TMPL_LOOP name="itemtypeloop" -->
327 <option value="<!-- TMPL_VAR name="value" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="description" --></option>
328 <!-- /TMPL_LOOP -->
329 </select>
330 <input type=text name=searchfield value="<!-- TMPL_VAR name="searchfield" -->">
331 <input type="submit" value="OK" class="button">
332 </form>
334 =cut
336 sub GetItemTypes {
338 # returns a reference to a hash of references to itemtypes...
339 my %itemtypes;
340 my $dbh = C4::Context->dbh;
341 my $query = qq|
342 SELECT *
343 FROM itemtypes
345 my $sth = $dbh->prepare($query);
346 $sth->execute;
347 while ( my $IT = $sth->fetchrow_hashref ) {
348 $itemtypes{ $IT->{'itemtype'} } = $IT;
350 return ( \%itemtypes );
353 sub get_itemtypeinfos_of {
354 my @itemtypes = @_;
356 my $placeholders = join( ', ', map { '?' } @itemtypes );
357 my $query = <<"END_SQL";
358 SELECT itemtype,
359 description,
360 imageurl,
361 notforloan
362 FROM itemtypes
363 WHERE itemtype IN ( $placeholders )
364 END_SQL
366 return get_infos_of( $query, 'itemtype', undef, \@itemtypes );
369 # this is temporary until we separate collection codes and item types
370 sub GetCcodes {
371 my $count = 0;
372 my @results;
373 my $dbh = C4::Context->dbh;
374 my $sth =
375 $dbh->prepare(
376 "SELECT * FROM authorised_values ORDER BY authorised_value");
377 $sth->execute;
378 while ( my $data = $sth->fetchrow_hashref ) {
379 if ( $data->{category} eq "CCODE" ) {
380 $count++;
381 $results[$count] = $data;
383 #warn "data: $data";
386 $sth->finish;
387 return ( $count, @results );
390 =head2 getauthtypes
392 $authtypes = &getauthtypes();
394 Returns information about existing authtypes.
396 build a HTML select with the following code :
398 =head3 in PERL SCRIPT
400 my $authtypes = getauthtypes;
401 my @authtypesloop;
402 foreach my $thisauthtype (keys %$authtypes) {
403 my $selected = 1 if $thisauthtype eq $authtype;
404 my %row =(value => $thisauthtype,
405 selected => $selected,
406 authtypetext => $authtypes->{$thisauthtype}->{'authtypetext'},
408 push @authtypesloop, \%row;
410 $template->param(itemtypeloop => \@itemtypesloop);
412 =head3 in TEMPLATE
414 <form action='<!-- TMPL_VAR name="script_name" -->' method=post>
415 <select name="authtype">
416 <!-- TMPL_LOOP name="authtypeloop" -->
417 <option value="<!-- TMPL_VAR name="value" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="authtypetext" --></option>
418 <!-- /TMPL_LOOP -->
419 </select>
420 <input type=text name=searchfield value="<!-- TMPL_VAR name="searchfield" -->">
421 <input type="submit" value="OK" class="button">
422 </form>
425 =cut
427 sub getauthtypes {
429 # returns a reference to a hash of references to authtypes...
430 my %authtypes;
431 my $dbh = C4::Context->dbh;
432 my $sth = $dbh->prepare("select * from auth_types order by authtypetext");
433 $sth->execute;
434 while ( my $IT = $sth->fetchrow_hashref ) {
435 $authtypes{ $IT->{'authtypecode'} } = $IT;
437 return ( \%authtypes );
440 sub getauthtype {
441 my ($authtypecode) = @_;
443 # returns a reference to a hash of references to authtypes...
444 my %authtypes;
445 my $dbh = C4::Context->dbh;
446 my $sth = $dbh->prepare("select * from auth_types where authtypecode=?");
447 $sth->execute($authtypecode);
448 my $res = $sth->fetchrow_hashref;
449 return $res;
452 =head2 getframework
454 $frameworks = &getframework();
456 Returns information about existing frameworks
458 build a HTML select with the following code :
460 =head3 in PERL SCRIPT
462 my $frameworks = frameworks();
463 my @frameworkloop;
464 foreach my $thisframework (keys %$frameworks) {
465 my $selected = 1 if $thisframework eq $frameworkcode;
466 my %row =(value => $thisframework,
467 selected => $selected,
468 description => $frameworks->{$thisframework}->{'frameworktext'},
470 push @frameworksloop, \%row;
472 $template->param(frameworkloop => \@frameworksloop);
474 =head3 in TEMPLATE
476 <form action='<!-- TMPL_VAR name="script_name" -->' method=post>
477 <select name="frameworkcode">
478 <option value="">Default</option>
479 <!-- TMPL_LOOP name="frameworkloop" -->
480 <option value="<!-- TMPL_VAR name="value" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="frameworktext" --></option>
481 <!-- /TMPL_LOOP -->
482 </select>
483 <input type=text name=searchfield value="<!-- TMPL_VAR name="searchfield" -->">
484 <input type="submit" value="OK" class="button">
485 </form>
487 =cut
489 sub getframeworks {
491 # returns a reference to a hash of references to branches...
492 my %itemtypes;
493 my $dbh = C4::Context->dbh;
494 my $sth = $dbh->prepare("select * from biblio_framework");
495 $sth->execute;
496 while ( my $IT = $sth->fetchrow_hashref ) {
497 $itemtypes{ $IT->{'frameworkcode'} } = $IT;
499 return ( \%itemtypes );
502 =head2 getframeworkinfo
504 $frameworkinfo = &getframeworkinfo($frameworkcode);
506 Returns information about an frameworkcode.
508 =cut
510 sub getframeworkinfo {
511 my ($frameworkcode) = @_;
512 my $dbh = C4::Context->dbh;
513 my $sth =
514 $dbh->prepare("select * from biblio_framework where frameworkcode=?");
515 $sth->execute($frameworkcode);
516 my $res = $sth->fetchrow_hashref;
517 return $res;
520 =head2 getitemtypeinfo
522 $itemtype = &getitemtype($itemtype);
524 Returns information about an itemtype.
526 =cut
528 sub getitemtypeinfo {
529 my ($itemtype) = @_;
530 my $dbh = C4::Context->dbh;
531 my $sth = $dbh->prepare("select * from itemtypes where itemtype=?");
532 $sth->execute($itemtype);
533 my $res = $sth->fetchrow_hashref;
535 $res->{imageurl} = getitemtypeimagelocation( 'intranet', $res->{imageurl} );
537 return $res;
540 =head2 getitemtypeimagedir
542 my $directory = getitemtypeimagedir( 'opac' );
544 pass in 'opac' or 'intranet'. Defaults to 'opac'.
546 returns the full path to the appropriate directory containing images.
548 =cut
550 sub getitemtypeimagedir {
551 my $src = shift || 'opac';
552 if ($src eq 'intranet') {
553 return C4::Context->config('intrahtdocs') . '/' .C4::Context->preference('template') . '/img/itemtypeimg';
554 } else {
555 return C4::Context->config('opachtdocs') . '/' . C4::Context->preference('template') . '/itemtypeimg';
559 sub getitemtypeimagesrc {
560 my $src = shift || 'opac';
561 if ($src eq 'intranet') {
562 return '/intranet-tmpl' . '/' . C4::Context->preference('template') . '/img/itemtypeimg';
563 } else {
564 return '/opac-tmpl' . '/' . C4::Context->preference('template') . '/itemtypeimg';
568 sub getitemtypeimagelocation($$) {
569 my ( $src, $image ) = @_;
571 return '' if ( !$image );
573 my $scheme = ( uri_split( $image ) )[0];
575 return $image if ( $scheme );
577 return getitemtypeimagesrc( $src ) . '/' . $image;
580 =head3 _getImagesFromDirectory
582 Find all of the image files in a directory in the filesystem
584 parameters: a directory name
586 returns: a list of images in that directory.
588 Notes: this does not traverse into subdirectories. See
589 _getSubdirectoryNames for help with that.
590 Images are assumed to be files with .gif or .png file extensions.
591 The image names returned do not have the directory name on them.
593 =cut
595 sub _getImagesFromDirectory {
596 my $directoryname = shift;
597 return unless defined $directoryname;
598 return unless -d $directoryname;
600 if ( opendir ( my $dh, $directoryname ) ) {
601 my @images = grep { /\.(gif|png)$/i } readdir( $dh );
602 closedir $dh;
603 @images = sort(@images);
604 return @images;
605 } else {
606 warn "unable to opendir $directoryname: $!";
607 return;
611 =head3 _getSubdirectoryNames
613 Find all of the directories in a directory in the filesystem
615 parameters: a directory name
617 returns: a list of subdirectories in that directory.
619 Notes: this does not traverse into subdirectories. Only the first
620 level of subdirectories are returned.
621 The directory names returned don't have the parent directory name on them.
623 =cut
625 sub _getSubdirectoryNames {
626 my $directoryname = shift;
627 return unless defined $directoryname;
628 return unless -d $directoryname;
630 if ( opendir ( my $dh, $directoryname ) ) {
631 my @directories = grep { -d File::Spec->catfile( $directoryname, $_ ) && ! ( /^\./ ) } readdir( $dh );
632 closedir $dh;
633 return @directories;
634 } else {
635 warn "unable to opendir $directoryname: $!";
636 return;
640 =head3 getImageSets
642 returns: a listref of hashrefs. Each hash represents another collection of images.
644 { imagesetname => 'npl', # the name of the image set (npl is the original one)
645 images => listref of image hashrefs
648 each image is represented by a hashref like this:
650 { KohaImage => 'npl/image.gif',
651 StaffImageUrl => '/intranet-tmpl/prog/img/itemtypeimg/npl/image.gif',
652 OpacImageURL => '/opac-tmpl/prog/itemtypeimg/npl/image.gif'
653 checked => 0 or 1: was this the image passed to this method?
654 Note: I'd like to remove this somehow.
657 =cut
659 sub getImageSets {
660 my %params = @_;
661 my $checked = $params{'checked'} || '';
663 my $paths = { staff => { filesystem => getitemtypeimagedir('intranet'),
664 url => getitemtypeimagesrc('intranet'),
666 opac => { filesystem => getitemtypeimagedir('opac'),
667 url => getitemtypeimagesrc('opac'),
671 my @imagesets = (); # list of hasrefs of image set data to pass to template
672 my @subdirectories = _getSubdirectoryNames( $paths->{'staff'}{'filesystem'} );
674 foreach my $imagesubdir ( @subdirectories ) {
675 my @imagelist = (); # hashrefs of image info
676 my @imagenames = _getImagesFromDirectory( File::Spec->catfile( $paths->{'staff'}{'filesystem'}, $imagesubdir ) );
677 my $imagesetactive = 0;
678 foreach my $thisimage ( @imagenames ) {
679 push( @imagelist,
680 { KohaImage => "$imagesubdir/$thisimage",
681 StaffImageUrl => join( '/', $paths->{'staff'}{'url'}, $imagesubdir, $thisimage ),
682 OpacImageUrl => join( '/', $paths->{'opac'}{'url'}, $imagesubdir, $thisimage ),
683 checked => "$imagesubdir/$thisimage" eq $checked ? 1 : 0,
686 $imagesetactive = 1 if "$imagesubdir/$thisimage" eq $checked;
688 push @imagesets, { imagesetname => $imagesubdir,
689 imagesetactive => $imagesetactive,
690 images => \@imagelist };
693 return \@imagesets;
696 =head2 GetPrinters
698 $printers = &GetPrinters();
699 @queues = keys %$printers;
701 Returns information about existing printer queues.
703 C<$printers> is a reference-to-hash whose keys are the print queues
704 defined in the printers table of the Koha database. The values are
705 references-to-hash, whose keys are the fields in the printers table.
707 =cut
709 sub GetPrinters {
710 my %printers;
711 my $dbh = C4::Context->dbh;
712 my $sth = $dbh->prepare("select * from printers");
713 $sth->execute;
714 while ( my $printer = $sth->fetchrow_hashref ) {
715 $printers{ $printer->{'printqueue'} } = $printer;
717 return ( \%printers );
720 =head2 GetPrinter
722 $printer = GetPrinter( $query, $printers );
724 =cut
726 sub GetPrinter ($$) {
727 my ( $query, $printers ) = @_; # get printer for this query from printers
728 my $printer = $query->param('printer');
729 my %cookie = $query->cookie('userenv');
730 ($printer) || ( $printer = $cookie{'printer'} ) || ( $printer = '' );
731 ( $printers->{$printer} ) || ( $printer = ( keys %$printers )[0] );
732 return $printer;
735 =head2 getnbpages
737 Returns the number of pages to display in a pagination bar, given the number
738 of items and the number of items per page.
740 =cut
742 sub getnbpages {
743 my ( $nb_items, $nb_items_per_page ) = @_;
745 return int( ( $nb_items - 1 ) / $nb_items_per_page ) + 1;
748 =head2 getallthemes
750 (@themes) = &getallthemes('opac');
751 (@themes) = &getallthemes('intranet');
753 Returns an array of all available themes.
755 =cut
757 sub getallthemes {
758 my $type = shift;
759 my $htdocs;
760 my @themes;
761 if ( $type eq 'intranet' ) {
762 $htdocs = C4::Context->config('intrahtdocs');
764 else {
765 $htdocs = C4::Context->config('opachtdocs');
767 opendir D, "$htdocs";
768 my @dirlist = readdir D;
769 foreach my $directory (@dirlist) {
770 -d "$htdocs/$directory/en" and push @themes, $directory;
772 return @themes;
775 sub getFacets {
776 my $facets;
777 if ( C4::Context->preference("marcflavour") eq "UNIMARC" ) {
778 $facets = [
780 link_value => 'su-to',
781 label_value => 'Topics',
782 tags =>
783 [ '600', '601', '602', '603', '604', '605', '606', '610' ],
784 subfield => 'a',
787 link_value => 'su-geo',
788 label_value => 'Places',
789 tags => ['651'],
790 subfield => 'a',
793 link_value => 'su-ut',
794 label_value => 'Titles',
795 tags => [ '500', '501', '502', '503', '504', ],
796 subfield => 'a',
799 link_value => 'au',
800 label_value => 'Authors',
801 tags => [ '700', '701', '702', ],
802 subfield => 'a',
805 link_value => 'se',
806 label_value => 'Series',
807 tags => ['225'],
808 subfield => 'a',
812 my $library_facet;
814 $library_facet = {
815 link_value => 'branch',
816 label_value => 'Libraries',
817 tags => [ '995', ],
818 subfield => 'b',
819 expanded => '1',
821 push @$facets, $library_facet unless C4::Context->preference("singleBranchMode");
823 else {
824 $facets = [
826 link_value => 'su-to',
827 label_value => 'Topics',
828 tags => ['650'],
829 subfield => 'a',
833 # link_value => 'su-na',
834 # label_value => 'People and Organizations',
835 # tags => ['600', '610', '611'],
836 # subfield => 'a',
837 # },
839 link_value => 'su-geo',
840 label_value => 'Places',
841 tags => ['651'],
842 subfield => 'a',
845 link_value => 'su-ut',
846 label_value => 'Titles',
847 tags => ['630'],
848 subfield => 'a',
851 link_value => 'au',
852 label_value => 'Authors',
853 tags => [ '100', '110', '700', ],
854 subfield => 'a',
857 link_value => 'se',
858 label_value => 'Series',
859 tags => [ '440', '490', ],
860 subfield => 'a',
863 my $library_facet;
864 $library_facet = {
865 link_value => 'branch',
866 label_value => 'Libraries',
867 tags => [ '952', ],
868 subfield => 'b',
869 expanded => '1',
871 push @$facets, $library_facet unless C4::Context->preference("singleBranchMode");
873 return $facets;
876 =head2 get_infos_of
878 Return a href where a key is associated to a href. You give a query,
879 the name of the key among the fields returned by the query. If you
880 also give as third argument the name of the value, the function
881 returns a href of scalar. The optional 4th argument is an arrayref of
882 items passed to the C<execute()> call. It is designed to bind
883 parameters to any placeholders in your SQL.
885 my $query = '
886 SELECT itemnumber,
887 notforloan,
888 barcode
889 FROM items
892 # generic href of any information on the item, href of href.
893 my $iteminfos_of = get_infos_of($query, 'itemnumber');
894 print $iteminfos_of->{$itemnumber}{barcode};
896 # specific information, href of scalar
897 my $barcode_of_item = get_infos_of($query, 'itemnumber', 'barcode');
898 print $barcode_of_item->{$itemnumber};
900 =cut
902 sub get_infos_of {
903 my ( $query, $key_name, $value_name, $bind_params ) = @_;
905 my $dbh = C4::Context->dbh;
907 my $sth = $dbh->prepare($query);
908 $sth->execute( @$bind_params );
910 my %infos_of;
911 while ( my $row = $sth->fetchrow_hashref ) {
912 if ( defined $value_name ) {
913 $infos_of{ $row->{$key_name} } = $row->{$value_name};
915 else {
916 $infos_of{ $row->{$key_name} } = $row;
919 $sth->finish;
921 return \%infos_of;
924 =head2 get_notforloan_label_of
926 my $notforloan_label_of = get_notforloan_label_of();
928 Each authorised value of notforloan (information available in items and
929 itemtypes) is link to a single label.
931 Returns a href where keys are authorised values and values are corresponding
932 labels.
934 foreach my $authorised_value (keys %{$notforloan_label_of}) {
935 printf(
936 "authorised_value: %s => %s\n",
937 $authorised_value,
938 $notforloan_label_of->{$authorised_value}
942 =cut
944 # FIXME - why not use GetAuthorisedValues ??
946 sub get_notforloan_label_of {
947 my $dbh = C4::Context->dbh;
949 my $query = '
950 SELECT authorised_value
951 FROM marc_subfield_structure
952 WHERE kohafield = \'items.notforloan\'
953 LIMIT 0, 1
955 my $sth = $dbh->prepare($query);
956 $sth->execute();
957 my ($statuscode) = $sth->fetchrow_array();
959 $query = '
960 SELECT lib,
961 authorised_value
962 FROM authorised_values
963 WHERE category = ?
965 $sth = $dbh->prepare($query);
966 $sth->execute($statuscode);
967 my %notforloan_label_of;
968 while ( my $row = $sth->fetchrow_hashref ) {
969 $notforloan_label_of{ $row->{authorised_value} } = $row->{lib};
971 $sth->finish;
973 return \%notforloan_label_of;
976 =head2 displayServers
978 my $servers = displayServers();
979 my $servers = displayServers( $position );
980 my $servers = displayServers( $position, $type );
982 displayServers returns a listref of hashrefs, each containing
983 information about available z3950 servers. Each hashref has a format
984 like:
987 'checked' => 'checked',
988 'encoding' => 'MARC-8'
989 'icon' => undef,
990 'id' => 'LIBRARY OF CONGRESS',
991 'label' => '',
992 'name' => 'server',
993 'opensearch' => '',
994 'value' => 'z3950.loc.gov:7090/',
995 'zed' => 1,
998 =cut
1000 sub displayServers {
1001 my ( $position, $type ) = @_;
1002 my $dbh = C4::Context->dbh;
1004 my $strsth = 'SELECT * FROM z3950servers';
1005 my @where_clauses;
1006 my @bind_params;
1008 if ($position) {
1009 push @bind_params, $position;
1010 push @where_clauses, ' position = ? ';
1013 if ($type) {
1014 push @bind_params, $type;
1015 push @where_clauses, ' type = ? ';
1018 # reassemble where clause from where clause pieces
1019 if (@where_clauses) {
1020 $strsth .= ' WHERE ' . join( ' AND ', @where_clauses );
1023 my $rq = $dbh->prepare($strsth);
1024 $rq->execute(@bind_params);
1025 my @primaryserverloop;
1027 while ( my $data = $rq->fetchrow_hashref ) {
1028 push @primaryserverloop,
1029 { label => $data->{description},
1030 id => $data->{name},
1031 name => "server",
1032 value => $data->{host} . ":" . $data->{port} . "/" . $data->{database},
1033 encoding => ( $data->{encoding} ? $data->{encoding} : "iso-5426" ),
1034 checked => "checked",
1035 icon => $data->{icon},
1036 zed => $data->{type} eq 'zed',
1037 opensearch => $data->{type} eq 'opensearch'
1040 return \@primaryserverloop;
1043 =head2 GetAuthValCode
1045 $authvalcode = GetAuthValCode($kohafield,$frameworkcode);
1047 =cut
1049 sub GetAuthValCode {
1050 my ($kohafield,$fwcode) = @_;
1051 my $dbh = C4::Context->dbh;
1052 $fwcode='' unless $fwcode;
1053 my $sth = $dbh->prepare('select authorised_value from marc_subfield_structure where kohafield=? and frameworkcode=?');
1054 $sth->execute($kohafield,$fwcode);
1055 my ($authvalcode) = $sth->fetchrow_array;
1056 return $authvalcode;
1059 =head2 GetAuthValCodeFromField
1061 $authvalcode = GetAuthValCodeFromField($field,$subfield,$frameworkcode);
1063 C<$subfield> can be undefined
1065 =cut
1067 sub GetAuthValCodeFromField {
1068 my ($field,$subfield,$fwcode) = @_;
1069 my $dbh = C4::Context->dbh;
1070 $fwcode='' unless $fwcode;
1071 my $sth;
1072 if (defined $subfield) {
1073 $sth = $dbh->prepare('select authorised_value from marc_subfield_structure where tagfield=? and tagsubfield=? and frameworkcode=?');
1074 $sth->execute($field,$subfield,$fwcode);
1075 } else {
1076 $sth = $dbh->prepare('select authorised_value from marc_tag_structure where tagfield=? and frameworkcode=?');
1077 $sth->execute($field,$fwcode);
1079 my ($authvalcode) = $sth->fetchrow_array;
1080 return $authvalcode;
1083 =head2 GetAuthorisedValues
1085 $authvalues = GetAuthorisedValues([$category], [$selected]);
1087 This function returns all authorised values from the'authorised_value' table in a reference to array of hashrefs.
1089 C<$category> returns authorised values for just one category (optional).
1091 C<$opac> If set to a true value, displays OPAC descriptions rather than normal ones when they exist.
1093 =cut
1095 sub GetAuthorisedValues {
1096 my ($category,$selected,$opac) = @_;
1097 my @results;
1098 my $dbh = C4::Context->dbh;
1099 my $query = "SELECT * FROM authorised_values";
1100 $query .= " WHERE category = '" . $category . "'" if $category;
1101 $query .= " ORDER BY category, lib, lib_opac";
1102 my $sth = $dbh->prepare($query);
1103 $sth->execute;
1104 while (my $data=$sth->fetchrow_hashref) {
1105 if ($selected && $selected eq $data->{'authorised_value'} ) {
1106 $data->{'selected'} = 1;
1108 if ($opac && $data->{'lib_opac'}) {
1109 $data->{'lib'} = $data->{'lib_opac'};
1111 push @results, $data;
1113 #my $data = $sth->fetchall_arrayref({});
1114 return \@results; #$data;
1117 =head2 GetAuthorisedValueCategories
1119 $auth_categories = GetAuthorisedValueCategories();
1121 Return an arrayref of all of the available authorised
1122 value categories.
1124 =cut
1126 sub GetAuthorisedValueCategories {
1127 my $dbh = C4::Context->dbh;
1128 my $sth = $dbh->prepare("SELECT DISTINCT category FROM authorised_values ORDER BY category");
1129 $sth->execute;
1130 my @results;
1131 while (my $category = $sth->fetchrow_array) {
1132 push @results, $category;
1134 return \@results;
1137 =head2 GetKohaAuthorisedValues
1139 Takes $kohafield, $fwcode as parameters.
1141 If $opac parameter is set to a true value, displays OPAC descriptions rather than normal ones when they exist.
1143 Returns hashref of Code => description
1145 Returns undef if no authorised value category is defined for the kohafield.
1147 =cut
1149 sub GetKohaAuthorisedValues {
1150 my ($kohafield,$fwcode,$opac) = @_;
1151 $fwcode='' unless $fwcode;
1152 my %values;
1153 my $dbh = C4::Context->dbh;
1154 my $avcode = GetAuthValCode($kohafield,$fwcode);
1155 if ($avcode) {
1156 my $sth = $dbh->prepare("select authorised_value, lib, lib_opac from authorised_values where category=? ");
1157 $sth->execute($avcode);
1158 while ( my ($val, $lib, $lib_opac) = $sth->fetchrow_array ) {
1159 $values{$val} = ($opac && $lib_opac) ? $lib_opac : $lib;
1161 return \%values;
1162 } else {
1163 return undef;
1167 =head2 GetKohaAuthorisedValuesFromField
1169 Takes $field, $subfield, $fwcode as parameters.
1171 If $opac parameter is set to a true value, displays OPAC descriptions rather than normal ones when they exist.
1172 $subfield can be undefined
1174 Returns hashref of Code => description
1176 Returns undef if no authorised value category is defined for the given field and subfield
1178 =cut
1180 sub GetKohaAuthorisedValuesFromField {
1181 my ($field, $subfield, $fwcode,$opac) = @_;
1182 $fwcode='' unless $fwcode;
1183 my %values;
1184 my $dbh = C4::Context->dbh;
1185 my $avcode = GetAuthValCodeFromField($field, $subfield, $fwcode);
1186 if ($avcode) {
1187 my $sth = $dbh->prepare("select authorised_value, lib, lib_opac from authorised_values where category=? ");
1188 $sth->execute($avcode);
1189 while ( my ($val, $lib, $lib_opac) = $sth->fetchrow_array ) {
1190 $values{$val} = ($opac && $lib_opac) ? $lib_opac : $lib;
1192 return \%values;
1193 } else {
1194 return undef;
1198 =head2 xml_escape
1200 my $escaped_string = C4::Koha::xml_escape($string);
1202 Convert &, <, >, ', and " in a string to XML entities
1204 =cut
1206 sub xml_escape {
1207 my $str = shift;
1208 return '' unless defined $str;
1209 $str =~ s/&/&amp;/g;
1210 $str =~ s/</&lt;/g;
1211 $str =~ s/>/&gt;/g;
1212 $str =~ s/'/&apos;/g;
1213 $str =~ s/"/&quot;/g;
1214 return $str;
1217 =head2 display_marc_indicators
1219 my $display_form = C4::Koha::display_marc_indicators($field);
1221 C<$field> is a MARC::Field object
1223 Generate a display form of the indicators of a variable
1224 MARC field, replacing any blanks with '#'.
1226 =cut
1228 sub display_marc_indicators {
1229 my $field = shift;
1230 my $indicators = '';
1231 if ($field->tag() >= 10) {
1232 $indicators = $field->indicator(1) . $field->indicator(2);
1233 $indicators =~ s/ /#/g;
1235 return $indicators;
1238 sub GetNormalizedUPC {
1239 my ($record,$marcflavour) = @_;
1240 my (@fields,$upc);
1242 if ($marcflavour eq 'MARC21') {
1243 @fields = $record->field('024');
1244 foreach my $field (@fields) {
1245 my $indicator = $field->indicator(1);
1246 my $upc = _normalize_match_point($field->subfield('a'));
1247 if ($indicator == 1 and $upc ne '') {
1248 return $upc;
1252 else { # assume unimarc if not marc21
1253 @fields = $record->field('072');
1254 foreach my $field (@fields) {
1255 my $upc = _normalize_match_point($field->subfield('a'));
1256 if ($upc ne '') {
1257 return $upc;
1263 # Normalizes and returns the first valid ISBN found in the record
1264 # ISBN13 are converted into ISBN10. This is required to get Amazon cover book.
1265 sub GetNormalizedISBN {
1266 my ($isbn,$record,$marcflavour) = @_;
1267 my @fields;
1268 if ($isbn) {
1269 # Koha attempts to store multiple ISBNs in biblioitems.isbn, separated by " | "
1270 # anything after " | " should be removed, along with the delimiter
1271 $isbn =~ s/(.*)( \| )(.*)/$1/;
1272 return _isbn_cleanup($isbn);
1274 return undef unless $record;
1276 if ($marcflavour eq 'MARC21') {
1277 @fields = $record->field('020');
1278 foreach my $field (@fields) {
1279 $isbn = $field->subfield('a');
1280 if ($isbn) {
1281 return _isbn_cleanup($isbn);
1282 } else {
1283 return undef;
1287 else { # assume unimarc if not marc21
1288 @fields = $record->field('010');
1289 foreach my $field (@fields) {
1290 my $isbn = $field->subfield('a');
1291 if ($isbn) {
1292 return _isbn_cleanup($isbn);
1293 } else {
1294 return undef;
1301 sub GetNormalizedEAN {
1302 my ($record,$marcflavour) = @_;
1303 my (@fields,$ean);
1305 if ($marcflavour eq 'MARC21') {
1306 @fields = $record->field('024');
1307 foreach my $field (@fields) {
1308 my $indicator = $field->indicator(1);
1309 $ean = _normalize_match_point($field->subfield('a'));
1310 if ($indicator == 3 and $ean ne '') {
1311 return $ean;
1315 else { # assume unimarc if not marc21
1316 @fields = $record->field('073');
1317 foreach my $field (@fields) {
1318 $ean = _normalize_match_point($field->subfield('a'));
1319 if ($ean ne '') {
1320 return $ean;
1325 sub GetNormalizedOCLCNumber {
1326 my ($record,$marcflavour) = @_;
1327 my (@fields,$oclc);
1329 if ($marcflavour eq 'MARC21') {
1330 @fields = $record->field('035');
1331 foreach my $field (@fields) {
1332 $oclc = $field->subfield('a');
1333 if ($oclc =~ /OCoLC/) {
1334 $oclc =~ s/\(OCoLC\)//;
1335 return $oclc;
1336 } else {
1337 return undef;
1341 else { # TODO: add UNIMARC fields
1345 sub _normalize_match_point {
1346 my $match_point = shift;
1347 (my $normalized_match_point) = $match_point =~ /([\d-]*[X]*)/;
1348 $normalized_match_point =~ s/-//g;
1350 return $normalized_match_point;
1353 sub _isbn_cleanup {
1354 my $isbn = Business::ISBN->new( $_[0] );
1355 if ( $isbn ) {
1356 $isbn = $isbn->as_isbn10 if $isbn->type eq 'ISBN13';
1357 if (defined $isbn) {
1358 return $isbn->as_string([]);
1361 return;
1366 __END__
1368 =head1 AUTHOR
1370 Koha Team
1372 =cut