Revert "Bug 8567: Set output directory for fines.pl in cron config created by the...
[koha.git] / C4 / Koha.pm
blob6f98ac6b096d2ea8e02264ffed4f58fd1c2e7cbb
1 package C4::Koha;
3 # Copyright 2000-2002 Katipo Communications
4 # Parts Copyright 2010 Nelsonville Public Library
5 # Parts copyright 2010 BibLibre
7 # This file is part of Koha.
9 # Koha is free software; you can redistribute it and/or modify it under the
10 # terms of the GNU General Public License as published by the Free Software
11 # Foundation; either version 2 of the License, or (at your option) any later
12 # version.
14 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
15 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 # A PARTICULAR PURPOSE. See the GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License along
19 # with Koha; if not, write to the Free Software Foundation, Inc.,
20 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 use strict;
24 #use warnings; FIXME - Bug 2505
26 use C4::Context;
27 use C4::Branch qw(GetBranchesCount);
28 use Koha::DateUtils qw(dt_from_string);
29 use Memoize;
30 use DateTime::Format::MySQL;
31 use Business::ISBN;
32 use autouse 'Data::Dumper' => qw(Dumper);
33 use DBI qw(:sql_types);
35 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK $DEBUG);
37 BEGIN {
38 $VERSION = 3.07.00.049;
39 require Exporter;
40 @ISA = qw(Exporter);
41 @EXPORT = qw(
42 &slashifyDate
43 &subfield_is_koha_internal_p
44 &GetPrinters &GetPrinter
45 &GetItemTypes &getitemtypeinfo
46 &GetSupportName &GetSupportList
47 &get_itemtypeinfos_of
48 &getframeworks &getframeworkinfo
49 &getauthtypes &getauthtype
50 &getallthemes
51 &getFacets
52 &displayServers
53 &getnbpages
54 &get_infos_of
55 &get_notforloan_label_of
56 &getitemtypeimagedir
57 &getitemtypeimagesrc
58 &getitemtypeimagelocation
59 &GetAuthorisedValues
60 &GetAuthorisedValueCategories
61 &IsAuthorisedValueCategory
62 &GetKohaAuthorisedValues
63 &GetKohaAuthorisedValuesFromField
64 &GetKohaAuthorisedValueLib
65 &GetAuthorisedValueByCode
66 &GetKohaImageurlFromAuthorisedValues
67 &GetAuthValCode
68 &AddAuthorisedValue
69 &GetNormalizedUPC
70 &GetNormalizedISBN
71 &GetNormalizedEAN
72 &GetNormalizedOCLCNumber
73 &xml_escape
75 &GetVariationsOfISBN
76 &GetVariationsOfISBNs
77 &NormalizeISBN
79 $DEBUG
81 $DEBUG = 0;
82 @EXPORT_OK = qw( GetDailyQuote );
85 # expensive functions
86 memoize('GetAuthorisedValues');
88 =head1 NAME
90 C4::Koha - Perl Module containing convenience functions for Koha scripts
92 =head1 SYNOPSIS
94 use C4::Koha;
96 =head1 DESCRIPTION
98 Koha.pm provides many functions for Koha scripts.
100 =head1 FUNCTIONS
102 =cut
104 =head2 slashifyDate
106 $slash_date = &slashifyDate($dash_date);
108 Takes a string of the form "DD-MM-YYYY" (or anything separated by
109 dashes), converts it to the form "YYYY/MM/DD", and returns the result.
111 =cut
113 sub slashifyDate {
115 # accepts a date of the form xx-xx-xx[xx] and returns it in the
116 # form xx/xx/xx[xx]
117 my @dateOut = split( '-', shift );
118 return ("$dateOut[2]/$dateOut[1]/$dateOut[0]");
121 # FIXME.. this should be moved to a MARC-specific module
122 sub subfield_is_koha_internal_p {
123 my ($subfield) = @_;
125 # We could match on 'lib' and 'tab' (and 'mandatory', & more to come!)
126 # But real MARC subfields are always single-character
127 # so it really is safer just to check the length
129 return length $subfield != 1;
132 =head2 GetSupportName
134 $itemtypename = &GetSupportName($codestring);
136 Returns a string with the name of the itemtype.
138 =cut
140 sub GetSupportName{
141 my ($codestring)=@_;
142 return if (! $codestring);
143 my $resultstring;
144 my $advanced_search_types = C4::Context->preference("AdvancedSearchTypes");
145 if (!$advanced_search_types or $advanced_search_types eq 'itemtypes') {
146 my $query = qq|
147 SELECT description
148 FROM itemtypes
149 WHERE itemtype=?
150 order by description
152 my $sth = C4::Context->dbh->prepare($query);
153 $sth->execute($codestring);
154 ($resultstring)=$sth->fetchrow;
155 return $resultstring;
156 } else {
157 my $sth =
158 C4::Context->dbh->prepare(
159 "SELECT lib FROM authorised_values WHERE category = ? AND authorised_value = ?"
161 $sth->execute( $advanced_search_types, $codestring );
162 my $data = $sth->fetchrow_hashref;
163 return $$data{'lib'};
167 =head2 GetSupportList
169 $itemtypes = &GetSupportList();
171 Returns an array ref containing informations about Support (since itemtype is rather a circulation code when item-level-itypes is used).
173 build a HTML select with the following code :
175 =head3 in PERL SCRIPT
177 my $itemtypes = GetSupportList();
178 $template->param(itemtypeloop => $itemtypes);
180 =head3 in TEMPLATE
182 <select name="itemtype" id="itemtype">
183 <option value=""></option>
184 [% FOREACH itemtypeloo IN itemtypeloop %]
185 [% IF ( itemtypeloo.selected ) %]
186 <option value="[% itemtypeloo.itemtype %]" selected="selected">[% itemtypeloo.description %]</option>
187 [% ELSE %]
188 <option value="[% itemtypeloo.itemtype %]">[% itemtypeloo.description %]</option>
189 [% END %]
190 [% END %]
191 </select>
193 =cut
195 sub GetSupportList{
196 my $advanced_search_types = C4::Context->preference("AdvancedSearchTypes");
197 if (!$advanced_search_types or $advanced_search_types =~ /itemtypes/) {
198 my $query = qq|
199 SELECT *
200 FROM itemtypes
201 order by description
203 my $sth = C4::Context->dbh->prepare($query);
204 $sth->execute;
205 return $sth->fetchall_arrayref({});
206 } else {
207 my $advsearchtypes = GetAuthorisedValues($advanced_search_types);
208 my @results= map {{itemtype=>$$_{authorised_value},description=>$$_{lib},imageurl=>$$_{imageurl}}} @$advsearchtypes;
209 return \@results;
212 =head2 GetItemTypes
214 $itemtypes = &GetItemTypes( style => $style );
216 Returns information about existing itemtypes.
218 Params:
219 style: either 'array' or 'hash', defaults to 'hash'.
220 'array' returns an arrayref,
221 'hash' return a hashref with the itemtype value as the key
223 build a HTML select with the following code :
225 =head3 in PERL SCRIPT
227 my $itemtypes = GetItemTypes;
228 my @itemtypesloop;
229 foreach my $thisitemtype (sort keys %$itemtypes) {
230 my $selected = 1 if $thisitemtype eq $itemtype;
231 my %row =(value => $thisitemtype,
232 selected => $selected,
233 description => $itemtypes->{$thisitemtype}->{'description'},
235 push @itemtypesloop, \%row;
237 $template->param(itemtypeloop => \@itemtypesloop);
239 =head3 in TEMPLATE
241 <form action='<!-- TMPL_VAR name="script_name" -->' method=post>
242 <select name="itemtype">
243 <option value="">Default</option>
244 <!-- TMPL_LOOP name="itemtypeloop" -->
245 <option value="<!-- TMPL_VAR name="value" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="description" --></option>
246 <!-- /TMPL_LOOP -->
247 </select>
248 <input type=text name=searchfield value="<!-- TMPL_VAR name="searchfield" -->">
249 <input type="submit" value="OK" class="button">
250 </form>
252 =cut
254 sub GetItemTypes {
255 my ( %params ) = @_;
256 my $style = defined( $params{'style'} ) ? $params{'style'} : 'hash';
258 # returns a reference to a hash of references to itemtypes...
259 my %itemtypes;
260 my $dbh = C4::Context->dbh;
261 my $query = qq|
262 SELECT *
263 FROM itemtypes
265 my $sth = $dbh->prepare($query);
266 $sth->execute;
268 if ( $style eq 'hash' ) {
269 while ( my $IT = $sth->fetchrow_hashref ) {
270 $itemtypes{ $IT->{'itemtype'} } = $IT;
272 return ( \%itemtypes );
273 } else {
274 return $sth->fetchall_arrayref({});
278 sub get_itemtypeinfos_of {
279 my @itemtypes = @_;
281 my $placeholders = join( ', ', map { '?' } @itemtypes );
282 my $query = <<"END_SQL";
283 SELECT itemtype,
284 description,
285 imageurl,
286 notforloan
287 FROM itemtypes
288 WHERE itemtype IN ( $placeholders )
289 END_SQL
291 return get_infos_of( $query, 'itemtype', undef, \@itemtypes );
294 =head2 getauthtypes
296 $authtypes = &getauthtypes();
298 Returns information about existing authtypes.
300 build a HTML select with the following code :
302 =head3 in PERL SCRIPT
304 my $authtypes = getauthtypes;
305 my @authtypesloop;
306 foreach my $thisauthtype (keys %$authtypes) {
307 my $selected = 1 if $thisauthtype eq $authtype;
308 my %row =(value => $thisauthtype,
309 selected => $selected,
310 authtypetext => $authtypes->{$thisauthtype}->{'authtypetext'},
312 push @authtypesloop, \%row;
314 $template->param(itemtypeloop => \@itemtypesloop);
316 =head3 in TEMPLATE
318 <form action='<!-- TMPL_VAR name="script_name" -->' method=post>
319 <select name="authtype">
320 <!-- TMPL_LOOP name="authtypeloop" -->
321 <option value="<!-- TMPL_VAR name="value" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="authtypetext" --></option>
322 <!-- /TMPL_LOOP -->
323 </select>
324 <input type=text name=searchfield value="<!-- TMPL_VAR name="searchfield" -->">
325 <input type="submit" value="OK" class="button">
326 </form>
329 =cut
331 sub getauthtypes {
333 # returns a reference to a hash of references to authtypes...
334 my %authtypes;
335 my $dbh = C4::Context->dbh;
336 my $sth = $dbh->prepare("select * from auth_types order by authtypetext");
337 $sth->execute;
338 while ( my $IT = $sth->fetchrow_hashref ) {
339 $authtypes{ $IT->{'authtypecode'} } = $IT;
341 return ( \%authtypes );
344 sub getauthtype {
345 my ($authtypecode) = @_;
347 # returns a reference to a hash of references to authtypes...
348 my %authtypes;
349 my $dbh = C4::Context->dbh;
350 my $sth = $dbh->prepare("select * from auth_types where authtypecode=?");
351 $sth->execute($authtypecode);
352 my $res = $sth->fetchrow_hashref;
353 return $res;
356 =head2 getframework
358 $frameworks = &getframework();
360 Returns information about existing frameworks
362 build a HTML select with the following code :
364 =head3 in PERL SCRIPT
366 my $frameworks = frameworks();
367 my @frameworkloop;
368 foreach my $thisframework (keys %$frameworks) {
369 my $selected = 1 if $thisframework eq $frameworkcode;
370 my %row =(value => $thisframework,
371 selected => $selected,
372 description => $frameworks->{$thisframework}->{'frameworktext'},
374 push @frameworksloop, \%row;
376 $template->param(frameworkloop => \@frameworksloop);
378 =head3 in TEMPLATE
380 <form action='<!-- TMPL_VAR name="script_name" -->' method=post>
381 <select name="frameworkcode">
382 <option value="">Default</option>
383 <!-- TMPL_LOOP name="frameworkloop" -->
384 <option value="<!-- TMPL_VAR name="value" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="frameworktext" --></option>
385 <!-- /TMPL_LOOP -->
386 </select>
387 <input type=text name=searchfield value="<!-- TMPL_VAR name="searchfield" -->">
388 <input type="submit" value="OK" class="button">
389 </form>
391 =cut
393 sub getframeworks {
395 # returns a reference to a hash of references to branches...
396 my %itemtypes;
397 my $dbh = C4::Context->dbh;
398 my $sth = $dbh->prepare("select * from biblio_framework");
399 $sth->execute;
400 while ( my $IT = $sth->fetchrow_hashref ) {
401 $itemtypes{ $IT->{'frameworkcode'} } = $IT;
403 return ( \%itemtypes );
406 =head2 getframeworkinfo
408 $frameworkinfo = &getframeworkinfo($frameworkcode);
410 Returns information about an frameworkcode.
412 =cut
414 sub getframeworkinfo {
415 my ($frameworkcode) = @_;
416 my $dbh = C4::Context->dbh;
417 my $sth =
418 $dbh->prepare("select * from biblio_framework where frameworkcode=?");
419 $sth->execute($frameworkcode);
420 my $res = $sth->fetchrow_hashref;
421 return $res;
424 =head2 getitemtypeinfo
426 $itemtype = &getitemtypeinfo($itemtype, [$interface]);
428 Returns information about an itemtype. The optional $interface argument
429 sets which interface ('opac' or 'intranet') to return the imageurl for.
430 Defaults to intranet.
432 =cut
434 sub getitemtypeinfo {
435 my ($itemtype, $interface) = @_;
436 my $dbh = C4::Context->dbh;
437 my $sth = $dbh->prepare("select * from itemtypes where itemtype=?");
438 $sth->execute($itemtype);
439 my $res = $sth->fetchrow_hashref;
441 $res->{imageurl} = getitemtypeimagelocation( ( ( defined $interface && $interface eq 'opac' ) ? 'opac' : 'intranet' ), $res->{imageurl} );
443 return $res;
446 =head2 getitemtypeimagedir
448 my $directory = getitemtypeimagedir( 'opac' );
450 pass in 'opac' or 'intranet'. Defaults to 'opac'.
452 returns the full path to the appropriate directory containing images.
454 =cut
456 sub getitemtypeimagedir {
457 my $src = shift || 'opac';
458 if ($src eq 'intranet') {
459 return C4::Context->config('intrahtdocs') . '/' .C4::Context->preference('template') . '/img/itemtypeimg';
460 } else {
461 return C4::Context->config('opachtdocs') . '/' . C4::Context->preference('opacthemes') . '/itemtypeimg';
465 sub getitemtypeimagesrc {
466 my $src = shift || 'opac';
467 if ($src eq 'intranet') {
468 return '/intranet-tmpl' . '/' . C4::Context->preference('template') . '/img/itemtypeimg';
469 } else {
470 return '/opac-tmpl' . '/' . C4::Context->preference('opacthemes') . '/itemtypeimg';
474 sub getitemtypeimagelocation {
475 my ( $src, $image ) = @_;
477 return '' if ( !$image );
478 require URI::Split;
480 my $scheme = ( URI::Split::uri_split( $image ) )[0];
482 return $image if ( $scheme );
484 return getitemtypeimagesrc( $src ) . '/' . $image;
487 =head3 _getImagesFromDirectory
489 Find all of the image files in a directory in the filesystem
491 parameters: a directory name
493 returns: a list of images in that directory.
495 Notes: this does not traverse into subdirectories. See
496 _getSubdirectoryNames for help with that.
497 Images are assumed to be files with .gif or .png file extensions.
498 The image names returned do not have the directory name on them.
500 =cut
502 sub _getImagesFromDirectory {
503 my $directoryname = shift;
504 return unless defined $directoryname;
505 return unless -d $directoryname;
507 if ( opendir ( my $dh, $directoryname ) ) {
508 my @images = grep { /\.(gif|png)$/i } readdir( $dh );
509 closedir $dh;
510 @images = sort(@images);
511 return @images;
512 } else {
513 warn "unable to opendir $directoryname: $!";
514 return;
518 =head3 _getSubdirectoryNames
520 Find all of the directories in a directory in the filesystem
522 parameters: a directory name
524 returns: a list of subdirectories in that directory.
526 Notes: this does not traverse into subdirectories. Only the first
527 level of subdirectories are returned.
528 The directory names returned don't have the parent directory name on them.
530 =cut
532 sub _getSubdirectoryNames {
533 my $directoryname = shift;
534 return unless defined $directoryname;
535 return unless -d $directoryname;
537 if ( opendir ( my $dh, $directoryname ) ) {
538 my @directories = grep { -d File::Spec->catfile( $directoryname, $_ ) && ! ( /^\./ ) } readdir( $dh );
539 closedir $dh;
540 return @directories;
541 } else {
542 warn "unable to opendir $directoryname: $!";
543 return;
547 =head3 getImageSets
549 returns: a listref of hashrefs. Each hash represents another collection of images.
551 { imagesetname => 'npl', # the name of the image set (npl is the original one)
552 images => listref of image hashrefs
555 each image is represented by a hashref like this:
557 { KohaImage => 'npl/image.gif',
558 StaffImageUrl => '/intranet-tmpl/prog/img/itemtypeimg/npl/image.gif',
559 OpacImageURL => '/opac-tmpl/prog/itemtypeimg/npl/image.gif'
560 checked => 0 or 1: was this the image passed to this method?
561 Note: I'd like to remove this somehow.
564 =cut
566 sub getImageSets {
567 my %params = @_;
568 my $checked = $params{'checked'} || '';
570 my $paths = { staff => { filesystem => getitemtypeimagedir('intranet'),
571 url => getitemtypeimagesrc('intranet'),
573 opac => { filesystem => getitemtypeimagedir('opac'),
574 url => getitemtypeimagesrc('opac'),
578 my @imagesets = (); # list of hasrefs of image set data to pass to template
579 my @subdirectories = _getSubdirectoryNames( $paths->{'staff'}{'filesystem'} );
580 foreach my $imagesubdir ( @subdirectories ) {
581 warn $imagesubdir if $DEBUG;
582 my @imagelist = (); # hashrefs of image info
583 my @imagenames = _getImagesFromDirectory( File::Spec->catfile( $paths->{'staff'}{'filesystem'}, $imagesubdir ) );
584 my $imagesetactive = 0;
585 foreach my $thisimage ( @imagenames ) {
586 push( @imagelist,
587 { KohaImage => "$imagesubdir/$thisimage",
588 StaffImageUrl => join( '/', $paths->{'staff'}{'url'}, $imagesubdir, $thisimage ),
589 OpacImageUrl => join( '/', $paths->{'opac'}{'url'}, $imagesubdir, $thisimage ),
590 checked => "$imagesubdir/$thisimage" eq $checked ? 1 : 0,
593 $imagesetactive = 1 if "$imagesubdir/$thisimage" eq $checked;
595 push @imagesets, { imagesetname => $imagesubdir,
596 imagesetactive => $imagesetactive,
597 images => \@imagelist };
600 return \@imagesets;
603 =head2 GetPrinters
605 $printers = &GetPrinters();
606 @queues = keys %$printers;
608 Returns information about existing printer queues.
610 C<$printers> is a reference-to-hash whose keys are the print queues
611 defined in the printers table of the Koha database. The values are
612 references-to-hash, whose keys are the fields in the printers table.
614 =cut
616 sub GetPrinters {
617 my %printers;
618 my $dbh = C4::Context->dbh;
619 my $sth = $dbh->prepare("select * from printers");
620 $sth->execute;
621 while ( my $printer = $sth->fetchrow_hashref ) {
622 $printers{ $printer->{'printqueue'} } = $printer;
624 return ( \%printers );
627 =head2 GetPrinter
629 $printer = GetPrinter( $query, $printers );
631 =cut
633 sub GetPrinter {
634 my ( $query, $printers ) = @_; # get printer for this query from printers
635 my $printer = $query->param('printer');
636 my %cookie = $query->cookie('userenv');
637 ($printer) || ( $printer = $cookie{'printer'} ) || ( $printer = '' );
638 ( $printers->{$printer} ) || ( $printer = ( keys %$printers )[0] );
639 return $printer;
642 =head2 getnbpages
644 Returns the number of pages to display in a pagination bar, given the number
645 of items and the number of items per page.
647 =cut
649 sub getnbpages {
650 my ( $nb_items, $nb_items_per_page ) = @_;
652 return int( ( $nb_items - 1 ) / $nb_items_per_page ) + 1;
655 =head2 getallthemes
657 (@themes) = &getallthemes('opac');
658 (@themes) = &getallthemes('intranet');
660 Returns an array of all available themes.
662 =cut
664 sub getallthemes {
665 my $type = shift;
666 my $htdocs;
667 my @themes;
668 if ( $type eq 'intranet' ) {
669 $htdocs = C4::Context->config('intrahtdocs');
671 else {
672 $htdocs = C4::Context->config('opachtdocs');
674 opendir D, "$htdocs";
675 my @dirlist = readdir D;
676 foreach my $directory (@dirlist) {
677 next if $directory eq 'lib';
678 -d "$htdocs/$directory/en" and push @themes, $directory;
680 return @themes;
683 sub getFacets {
684 my $facets;
685 if ( C4::Context->preference("marcflavour") eq "UNIMARC" ) {
686 $facets = [
688 idx => 'su-to',
689 label => 'Topics',
690 tags => [ qw/ 600ab 601ab 602a 604at 605a 606ax 610a / ],
691 sep => ' - ',
694 idx => 'su-geo',
695 label => 'Places',
696 tags => [ qw/ 607a / ],
697 sep => ' - ',
700 idx => 'su-ut',
701 label => 'Titles',
702 tags => [ qw/ 500a 501a 503a / ],
703 sep => ', ',
706 idx => 'au',
707 label => 'Authors',
708 tags => [ qw/ 700ab 701ab 702ab / ],
709 sep => C4::Context->preference("UNIMARCAuthorsFacetsSeparator"),
712 idx => 'se',
713 label => 'Series',
714 tags => [ qw/ 225a / ],
715 sep => ', ',
718 idx => 'location',
719 label => 'Location',
720 tags => [ qw/ 995e / ],
724 unless ( C4::Context->preference("singleBranchMode")
725 || GetBranchesCount() == 1 )
727 my $DisplayLibraryFacets = C4::Context->preference('DisplayLibraryFacets');
728 if ( $DisplayLibraryFacets eq 'both'
729 || $DisplayLibraryFacets eq 'holding' )
731 push(
732 @$facets,
734 idx => 'holdingbranch',
735 label => 'HoldingLibrary',
736 tags => [qw / 995b /],
741 if ( $DisplayLibraryFacets eq 'both'
742 || $DisplayLibraryFacets eq 'home' )
744 push(
745 @$facets,
747 idx => 'homebranch',
748 label => 'HomeLibrary',
749 tags => [qw / 995a /],
755 else {
756 $facets = [
758 idx => 'su-to',
759 label => 'Topics',
760 tags => [ qw/ 650a / ],
761 sep => '--',
764 # idx => 'su-na',
765 # label => 'People and Organizations',
766 # tags => [ qw/ 600a 610a 611a / ],
767 # sep => 'a',
768 # },
770 idx => 'su-geo',
771 label => 'Places',
772 tags => [ qw/ 651a / ],
773 sep => '--',
776 idx => 'su-ut',
777 label => 'Titles',
778 tags => [ qw/ 630a / ],
779 sep => '--',
782 idx => 'au',
783 label => 'Authors',
784 tags => [ qw/ 100a 110a 700a / ],
785 sep => ', ',
788 idx => 'se',
789 label => 'Series',
790 tags => [ qw/ 440a 490a / ],
791 sep => ', ',
794 idx => 'itype',
795 label => 'ItemTypes',
796 tags => [ qw/ 952y 942c / ],
797 sep => ', ',
800 idx => 'location',
801 label => 'Location',
802 tags => [ qw / 952c / ],
806 unless ( C4::Context->preference("singleBranchMode")
807 || GetBranchesCount() == 1 )
809 my $DisplayLibraryFacets = C4::Context->preference('DisplayLibraryFacets');
810 if ( $DisplayLibraryFacets eq 'both'
811 || $DisplayLibraryFacets eq 'holding' )
813 push(
814 @$facets,
816 idx => 'holdingbranch',
817 label => 'HoldingLibrary',
818 tags => [qw / 952b /],
823 if ( $DisplayLibraryFacets eq 'both'
824 || $DisplayLibraryFacets eq 'home' )
826 push(
827 @$facets,
829 idx => 'homebranch',
830 label => 'HomeLibrary',
831 tags => [qw / 952a /],
837 return $facets;
840 =head2 get_infos_of
842 Return a href where a key is associated to a href. You give a query,
843 the name of the key among the fields returned by the query. If you
844 also give as third argument the name of the value, the function
845 returns a href of scalar. The optional 4th argument is an arrayref of
846 items passed to the C<execute()> call. It is designed to bind
847 parameters to any placeholders in your SQL.
849 my $query = '
850 SELECT itemnumber,
851 notforloan,
852 barcode
853 FROM items
856 # generic href of any information on the item, href of href.
857 my $iteminfos_of = get_infos_of($query, 'itemnumber');
858 print $iteminfos_of->{$itemnumber}{barcode};
860 # specific information, href of scalar
861 my $barcode_of_item = get_infos_of($query, 'itemnumber', 'barcode');
862 print $barcode_of_item->{$itemnumber};
864 =cut
866 sub get_infos_of {
867 my ( $query, $key_name, $value_name, $bind_params ) = @_;
869 my $dbh = C4::Context->dbh;
871 my $sth = $dbh->prepare($query);
872 $sth->execute( @$bind_params );
874 my %infos_of;
875 while ( my $row = $sth->fetchrow_hashref ) {
876 if ( defined $value_name ) {
877 $infos_of{ $row->{$key_name} } = $row->{$value_name};
879 else {
880 $infos_of{ $row->{$key_name} } = $row;
883 $sth->finish;
885 return \%infos_of;
888 =head2 get_notforloan_label_of
890 my $notforloan_label_of = get_notforloan_label_of();
892 Each authorised value of notforloan (information available in items and
893 itemtypes) is link to a single label.
895 Returns a href where keys are authorised values and values are corresponding
896 labels.
898 foreach my $authorised_value (keys %{$notforloan_label_of}) {
899 printf(
900 "authorised_value: %s => %s\n",
901 $authorised_value,
902 $notforloan_label_of->{$authorised_value}
906 =cut
908 # FIXME - why not use GetAuthorisedValues ??
910 sub get_notforloan_label_of {
911 my $dbh = C4::Context->dbh;
913 my $query = '
914 SELECT authorised_value
915 FROM marc_subfield_structure
916 WHERE kohafield = \'items.notforloan\'
917 LIMIT 0, 1
919 my $sth = $dbh->prepare($query);
920 $sth->execute();
921 my ($statuscode) = $sth->fetchrow_array();
923 $query = '
924 SELECT lib,
925 authorised_value
926 FROM authorised_values
927 WHERE category = ?
929 $sth = $dbh->prepare($query);
930 $sth->execute($statuscode);
931 my %notforloan_label_of;
932 while ( my $row = $sth->fetchrow_hashref ) {
933 $notforloan_label_of{ $row->{authorised_value} } = $row->{lib};
935 $sth->finish;
937 return \%notforloan_label_of;
940 =head2 displayServers
942 my $servers = displayServers();
943 my $servers = displayServers( $position );
944 my $servers = displayServers( $position, $type );
946 displayServers returns a listref of hashrefs, each containing
947 information about available z3950 servers. Each hashref has a format
948 like:
951 'checked' => 'checked',
952 'encoding' => 'utf8',
953 'icon' => undef,
954 'id' => 'LIBRARY OF CONGRESS',
955 'label' => '',
956 'name' => 'server',
957 'opensearch' => '',
958 'value' => 'lx2.loc.gov:210/',
959 'zed' => 1,
962 =cut
964 sub displayServers {
965 my ( $position, $type ) = @_;
966 my $dbh = C4::Context->dbh;
968 my $strsth = 'SELECT * FROM z3950servers';
969 my @where_clauses;
970 my @bind_params;
972 if ($position) {
973 push @bind_params, $position;
974 push @where_clauses, ' position = ? ';
977 if ($type) {
978 push @bind_params, $type;
979 push @where_clauses, ' type = ? ';
982 # reassemble where clause from where clause pieces
983 if (@where_clauses) {
984 $strsth .= ' WHERE ' . join( ' AND ', @where_clauses );
987 my $rq = $dbh->prepare($strsth);
988 $rq->execute(@bind_params);
989 my @primaryserverloop;
991 while ( my $data = $rq->fetchrow_hashref ) {
992 push @primaryserverloop,
993 { label => $data->{description},
994 id => $data->{name},
995 name => "server",
996 value => $data->{host} . ":" . $data->{port} . "/" . $data->{database},
997 encoding => ( $data->{encoding} ? $data->{encoding} : "iso-5426" ),
998 checked => "checked",
999 icon => $data->{icon},
1000 zed => $data->{type} eq 'zed',
1001 opensearch => $data->{type} eq 'opensearch'
1004 return \@primaryserverloop;
1008 =head2 GetKohaImageurlFromAuthorisedValues
1010 $authhorised_value = GetKohaImageurlFromAuthorisedValues( $category, $authvalcode );
1012 Return the first url of the authorised value image represented by $lib.
1014 =cut
1016 sub GetKohaImageurlFromAuthorisedValues {
1017 my ( $category, $lib ) = @_;
1018 my $dbh = C4::Context->dbh;
1019 my $sth = $dbh->prepare("SELECT imageurl FROM authorised_values WHERE category=? AND lib =?");
1020 $sth->execute( $category, $lib );
1021 while ( my $data = $sth->fetchrow_hashref ) {
1022 return $data->{'imageurl'};
1026 =head2 GetAuthValCode
1028 $authvalcode = GetAuthValCode($kohafield,$frameworkcode);
1030 =cut
1032 sub GetAuthValCode {
1033 my ($kohafield,$fwcode) = @_;
1034 my $dbh = C4::Context->dbh;
1035 $fwcode='' unless $fwcode;
1036 my $sth = $dbh->prepare('select authorised_value from marc_subfield_structure where kohafield=? and frameworkcode=?');
1037 $sth->execute($kohafield,$fwcode);
1038 my ($authvalcode) = $sth->fetchrow_array;
1039 return $authvalcode;
1042 =head2 GetAuthValCodeFromField
1044 $authvalcode = GetAuthValCodeFromField($field,$subfield,$frameworkcode);
1046 C<$subfield> can be undefined
1048 =cut
1050 sub GetAuthValCodeFromField {
1051 my ($field,$subfield,$fwcode) = @_;
1052 my $dbh = C4::Context->dbh;
1053 $fwcode='' unless $fwcode;
1054 my $sth;
1055 if (defined $subfield) {
1056 $sth = $dbh->prepare('select authorised_value from marc_subfield_structure where tagfield=? and tagsubfield=? and frameworkcode=?');
1057 $sth->execute($field,$subfield,$fwcode);
1058 } else {
1059 $sth = $dbh->prepare('select authorised_value from marc_tag_structure where tagfield=? and frameworkcode=?');
1060 $sth->execute($field,$fwcode);
1062 my ($authvalcode) = $sth->fetchrow_array;
1063 return $authvalcode;
1066 =head2 GetAuthorisedValues
1068 $authvalues = GetAuthorisedValues([$category], [$selected]);
1070 This function returns all authorised values from the'authorised_value' table in a reference to array of hashrefs.
1072 C<$category> returns authorised values for just one category (optional).
1074 C<$opac> If set to a true value, displays OPAC descriptions rather than normal ones when they exist.
1076 =cut
1078 sub GetAuthorisedValues {
1079 my ( $category, $selected, $opac ) = @_;
1080 my $branch_limit = C4::Context->userenv ? C4::Context->userenv->{"branch"} : "";
1081 my @results;
1082 my $dbh = C4::Context->dbh;
1083 my $query = qq{
1084 SELECT *
1085 FROM authorised_values
1087 $query .= qq{
1088 LEFT JOIN authorised_values_branches ON ( id = av_id )
1089 } if $branch_limit;
1090 my @where_strings;
1091 my @where_args;
1092 if($category) {
1093 push @where_strings, "category = ?";
1094 push @where_args, $category;
1096 if($branch_limit) {
1097 push @where_strings, "( branchcode = ? OR branchcode IS NULL )";
1098 push @where_args, $branch_limit;
1100 if(@where_strings > 0) {
1101 $query .= " WHERE " . join(" AND ", @where_strings);
1103 $query .= " GROUP BY lib";
1104 $query .= ' ORDER BY category, ' . (
1105 $opac ? 'COALESCE(lib_opac, lib)'
1106 : 'lib, lib_opac'
1109 my $sth = $dbh->prepare($query);
1111 $sth->execute( @where_args );
1112 while (my $data=$sth->fetchrow_hashref) {
1113 if ( defined $selected and $selected eq $data->{authorised_value} ) {
1114 $data->{selected} = 1;
1116 else {
1117 $data->{selected} = 0;
1120 if ($opac && $data->{lib_opac}) {
1121 $data->{lib} = $data->{lib_opac};
1123 push @results, $data;
1125 $sth->finish;
1126 return \@results;
1129 =head2 GetAuthorisedValueCategories
1131 $auth_categories = GetAuthorisedValueCategories();
1133 Return an arrayref of all of the available authorised
1134 value categories.
1136 =cut
1138 sub GetAuthorisedValueCategories {
1139 my $dbh = C4::Context->dbh;
1140 my $sth = $dbh->prepare("SELECT DISTINCT category FROM authorised_values ORDER BY category");
1141 $sth->execute;
1142 my @results;
1143 while (defined (my $category = $sth->fetchrow_array) ) {
1144 push @results, $category;
1146 return \@results;
1149 =head2 IsAuthorisedValueCategory
1151 $is_auth_val_category = IsAuthorisedValueCategory($category);
1153 Returns whether a given category name is a valid one
1155 =cut
1157 sub IsAuthorisedValueCategory {
1158 my $category = shift;
1159 my $query = '
1160 SELECT category
1161 FROM authorised_values
1162 WHERE BINARY category=?
1163 LIMIT 1
1165 my $sth = C4::Context->dbh->prepare($query);
1166 $sth->execute($category);
1167 $sth->fetchrow ? return 1
1168 : return 0;
1171 =head2 GetAuthorisedValueByCode
1173 $authorised_value = GetAuthorisedValueByCode( $category, $authvalcode, $opac );
1175 Return the lib attribute from authorised_values from the row identified
1176 by the passed category and code
1178 =cut
1180 sub GetAuthorisedValueByCode {
1181 my ( $category, $authvalcode, $opac ) = @_;
1183 my $field = $opac ? 'lib_opac' : 'lib';
1184 my $dbh = C4::Context->dbh;
1185 my $sth = $dbh->prepare("SELECT $field FROM authorised_values WHERE category=? AND authorised_value =?");
1186 $sth->execute( $category, $authvalcode );
1187 while ( my $data = $sth->fetchrow_hashref ) {
1188 return $data->{ $field };
1192 =head2 GetKohaAuthorisedValues
1194 Takes $kohafield, $fwcode as parameters.
1196 If $opac parameter is set to a true value, displays OPAC descriptions rather than normal ones when they exist.
1198 Returns hashref of Code => description
1200 Returns undef if no authorised value category is defined for the kohafield.
1202 =cut
1204 sub GetKohaAuthorisedValues {
1205 my ($kohafield,$fwcode,$opac) = @_;
1206 $fwcode='' unless $fwcode;
1207 my %values;
1208 my $dbh = C4::Context->dbh;
1209 my $avcode = GetAuthValCode($kohafield,$fwcode);
1210 if ($avcode) {
1211 my $sth = $dbh->prepare("select authorised_value, lib, lib_opac from authorised_values where category=? ");
1212 $sth->execute($avcode);
1213 while ( my ($val, $lib, $lib_opac) = $sth->fetchrow_array ) {
1214 $values{$val} = ($opac && $lib_opac) ? $lib_opac : $lib;
1216 return \%values;
1217 } else {
1218 return;
1222 =head2 GetKohaAuthorisedValuesFromField
1224 Takes $field, $subfield, $fwcode as parameters.
1226 If $opac parameter is set to a true value, displays OPAC descriptions rather than normal ones when they exist.
1227 $subfield can be undefined
1229 Returns hashref of Code => description
1231 Returns undef if no authorised value category is defined for the given field and subfield
1233 =cut
1235 sub GetKohaAuthorisedValuesFromField {
1236 my ($field, $subfield, $fwcode,$opac) = @_;
1237 $fwcode='' unless $fwcode;
1238 my %values;
1239 my $dbh = C4::Context->dbh;
1240 my $avcode = GetAuthValCodeFromField($field, $subfield, $fwcode);
1241 if ($avcode) {
1242 my $sth = $dbh->prepare("select authorised_value, lib, lib_opac from authorised_values where category=? ");
1243 $sth->execute($avcode);
1244 while ( my ($val, $lib, $lib_opac) = $sth->fetchrow_array ) {
1245 $values{$val} = ($opac && $lib_opac) ? $lib_opac : $lib;
1247 return \%values;
1248 } else {
1249 return;
1253 =head2 xml_escape
1255 my $escaped_string = C4::Koha::xml_escape($string);
1257 Convert &, <, >, ', and " in a string to XML entities
1259 =cut
1261 sub xml_escape {
1262 my $str = shift;
1263 return '' unless defined $str;
1264 $str =~ s/&/&amp;/g;
1265 $str =~ s/</&lt;/g;
1266 $str =~ s/>/&gt;/g;
1267 $str =~ s/'/&apos;/g;
1268 $str =~ s/"/&quot;/g;
1269 return $str;
1272 =head2 GetKohaAuthorisedValueLib
1274 Takes $category, $authorised_value as parameters.
1276 If $opac parameter is set to a true value, displays OPAC descriptions rather than normal ones when they exist.
1278 Returns authorised value description
1280 =cut
1282 sub GetKohaAuthorisedValueLib {
1283 my ($category,$authorised_value,$opac) = @_;
1284 my $value;
1285 my $dbh = C4::Context->dbh;
1286 my $sth = $dbh->prepare("select lib, lib_opac from authorised_values where category=? and authorised_value=?");
1287 $sth->execute($category,$authorised_value);
1288 my $data = $sth->fetchrow_hashref;
1289 $value = ($opac && $$data{'lib_opac'}) ? $$data{'lib_opac'} : $$data{'lib'};
1290 return $value;
1293 =head2 AddAuthorisedValue
1295 AddAuthorisedValue($category, $authorised_value, $lib, $lib_opac, $imageurl);
1297 Create a new authorised value.
1299 =cut
1301 sub AddAuthorisedValue {
1302 my ($category, $authorised_value, $lib, $lib_opac, $imageurl) = @_;
1304 my $dbh = C4::Context->dbh;
1305 my $query = qq{
1306 INSERT INTO authorised_values (category, authorised_value, lib, lib_opac, imageurl)
1307 VALUES (?,?,?,?,?)
1309 my $sth = $dbh->prepare($query);
1310 $sth->execute($category, $authorised_value, $lib, $lib_opac, $imageurl);
1313 =head2 display_marc_indicators
1315 my $display_form = C4::Koha::display_marc_indicators($field);
1317 C<$field> is a MARC::Field object
1319 Generate a display form of the indicators of a variable
1320 MARC field, replacing any blanks with '#'.
1322 =cut
1324 sub display_marc_indicators {
1325 my $field = shift;
1326 my $indicators = '';
1327 if ($field->tag() >= 10) {
1328 $indicators = $field->indicator(1) . $field->indicator(2);
1329 $indicators =~ s/ /#/g;
1331 return $indicators;
1334 sub GetNormalizedUPC {
1335 my ($record,$marcflavour) = @_;
1336 my (@fields,$upc);
1338 if ($marcflavour eq 'UNIMARC') {
1339 @fields = $record->field('072');
1340 foreach my $field (@fields) {
1341 my $upc = _normalize_match_point($field->subfield('a'));
1342 if ($upc ne '') {
1343 return $upc;
1348 else { # assume marc21 if not unimarc
1349 @fields = $record->field('024');
1350 foreach my $field (@fields) {
1351 my $indicator = $field->indicator(1);
1352 my $upc = _normalize_match_point($field->subfield('a'));
1353 if ($indicator == 1 and $upc ne '') {
1354 return $upc;
1360 # Normalizes and returns the first valid ISBN found in the record
1361 # ISBN13 are converted into ISBN10. This is required to get some book cover images.
1362 sub GetNormalizedISBN {
1363 my ($isbn,$record,$marcflavour) = @_;
1364 my @fields;
1365 if ($isbn) {
1366 # Koha attempts to store multiple ISBNs in biblioitems.isbn, separated by " | "
1367 # anything after " | " should be removed, along with the delimiter
1368 $isbn =~ s/(.*)( \| )(.*)/$1/;
1369 return _isbn_cleanup($isbn);
1371 return unless $record;
1373 if ($marcflavour eq 'UNIMARC') {
1374 @fields = $record->field('010');
1375 foreach my $field (@fields) {
1376 my $isbn = $field->subfield('a');
1377 if ($isbn) {
1378 return _isbn_cleanup($isbn);
1379 } else {
1380 return;
1384 else { # assume marc21 if not unimarc
1385 @fields = $record->field('020');
1386 foreach my $field (@fields) {
1387 $isbn = $field->subfield('a');
1388 if ($isbn) {
1389 return _isbn_cleanup($isbn);
1390 } else {
1391 return;
1397 sub GetNormalizedEAN {
1398 my ($record,$marcflavour) = @_;
1399 my (@fields,$ean);
1401 if ($marcflavour eq 'UNIMARC') {
1402 @fields = $record->field('073');
1403 foreach my $field (@fields) {
1404 $ean = _normalize_match_point($field->subfield('a'));
1405 if ($ean ne '') {
1406 return $ean;
1410 else { # assume marc21 if not unimarc
1411 @fields = $record->field('024');
1412 foreach my $field (@fields) {
1413 my $indicator = $field->indicator(1);
1414 $ean = _normalize_match_point($field->subfield('a'));
1415 if ($indicator == 3 and $ean ne '') {
1416 return $ean;
1421 sub GetNormalizedOCLCNumber {
1422 my ($record,$marcflavour) = @_;
1423 my (@fields,$oclc);
1425 if ($marcflavour eq 'UNIMARC') {
1426 # TODO: add UNIMARC fields
1428 else { # assume marc21 if not unimarc
1429 @fields = $record->field('035');
1430 foreach my $field (@fields) {
1431 $oclc = $field->subfield('a');
1432 if ($oclc =~ /OCoLC/) {
1433 $oclc =~ s/\(OCoLC\)//;
1434 return $oclc;
1435 } else {
1436 return;
1442 sub GetAuthvalueDropbox {
1443 my ( $authcat, $default ) = @_;
1444 my $branch_limit = C4::Context->userenv ? C4::Context->userenv->{"branch"} : "";
1445 my $dbh = C4::Context->dbh;
1447 my $query = qq{
1448 SELECT *
1449 FROM authorised_values
1451 $query .= qq{
1452 LEFT JOIN authorised_values_branches ON ( id = av_id )
1453 } if $branch_limit;
1454 $query .= qq{
1455 WHERE category = ?
1457 $query .= " AND ( branchcode = ? OR branchcode IS NULL )" if $branch_limit;
1458 $query .= " GROUP BY lib ORDER BY category, lib, lib_opac";
1459 my $sth = $dbh->prepare($query);
1460 $sth->execute( $authcat, $branch_limit ? $branch_limit : () );
1463 my $option_list = [];
1464 my @authorised_values = ( q{} );
1465 while (my $av = $sth->fetchrow_hashref) {
1466 push @{$option_list}, {
1467 value => $av->{authorised_value},
1468 label => $av->{lib},
1469 default => ($default eq $av->{authorised_value}),
1473 if ( @{$option_list} ) {
1474 return $option_list;
1476 return;
1480 =head2 GetDailyQuote($opts)
1482 Takes a hashref of options
1484 Currently supported options are:
1486 'id' An exact quote id
1487 'random' Select a random quote
1488 noop When no option is passed in, this sub will return the quote timestamped for the current day
1490 The function returns an anonymous hash following this format:
1493 'source' => 'source-of-quote',
1494 'timestamp' => 'timestamp-value',
1495 'text' => 'text-of-quote',
1496 'id' => 'quote-id'
1499 =cut
1501 # This is definitely a candidate for some sort of caching once we finally settle caching/persistence issues...
1502 # at least for default option
1504 sub GetDailyQuote {
1505 my %opts = @_;
1506 my $dbh = C4::Context->dbh;
1507 my $query = '';
1508 my $sth = undef;
1509 my $quote = undef;
1510 if ($opts{'id'}) {
1511 $query = 'SELECT * FROM quotes WHERE id = ?';
1512 $sth = $dbh->prepare($query);
1513 $sth->execute($opts{'id'});
1514 $quote = $sth->fetchrow_hashref();
1516 elsif ($opts{'random'}) {
1517 # Fall through... we also return a random quote as a catch-all if all else fails
1519 else {
1520 $query = 'SELECT * FROM quotes WHERE timestamp LIKE CONCAT(CURRENT_DATE,\'%\') ORDER BY timestamp DESC LIMIT 0,1';
1521 $sth = $dbh->prepare($query);
1522 $sth->execute();
1523 $quote = $sth->fetchrow_hashref();
1525 unless ($quote) { # if there are not matches, choose a random quote
1526 # get a list of all available quote ids
1527 $sth = C4::Context->dbh->prepare('SELECT count(*) FROM quotes;');
1528 $sth->execute;
1529 my $range = ($sth->fetchrow_array)[0];
1530 # chose a random id within that range if there is more than one quote
1531 my $offset = int(rand($range));
1532 # grab it
1533 $query = 'SELECT * FROM quotes ORDER BY id LIMIT 1 OFFSET ?';
1534 $sth = C4::Context->dbh->prepare($query);
1535 # see http://www.perlmonks.org/?node_id=837422 for why
1536 # we're being verbose and using bind_param
1537 $sth->bind_param(1, $offset, SQL_INTEGER);
1538 $sth->execute();
1539 $quote = $sth->fetchrow_hashref();
1540 # update the timestamp for that quote
1541 $query = 'UPDATE quotes SET timestamp = ? WHERE id = ?';
1542 $sth = C4::Context->dbh->prepare($query);
1543 $sth->execute(
1544 DateTime::Format::MySQL->format_datetime( dt_from_string() ),
1545 $quote->{'id'}
1548 return $quote;
1551 sub _normalize_match_point {
1552 my $match_point = shift;
1553 (my $normalized_match_point) = $match_point =~ /([\d-]*[X]*)/;
1554 $normalized_match_point =~ s/-//g;
1556 return $normalized_match_point;
1559 sub _isbn_cleanup {
1560 my ($isbn) = @_;
1561 return NormalizeISBN(
1563 isbn => $isbn,
1564 format => 'ISBN-10',
1565 strip_hyphens => 1,
1567 ) if $isbn;
1570 =head2 NormalizedISBN
1572 my $isbns = NormalizedISBN({
1573 isbn => $isbn,
1574 strip_hyphens => [0,1],
1575 format => ['ISBN-10', 'ISBN-13']
1578 Returns an isbn validated by Business::ISBN.
1579 Optionally strips hyphens and/or forces the isbn
1580 to be of the specified format.
1582 If the string cannot be validated as an isbn,
1583 it returns nothing.
1585 =cut
1587 sub NormalizeISBN {
1588 my ($params) = @_;
1590 my $string = $params->{isbn};
1591 my $strip_hyphens = $params->{strip_hyphens};
1592 my $format = $params->{format};
1594 return unless $string;
1596 my $isbn = Business::ISBN->new($string);
1598 if ( $isbn && $isbn->is_valid() ) {
1600 if ( $format eq 'ISBN-10' ) {
1601 $isbn = $isbn->as_isbn10();
1603 elsif ( $format eq 'ISBN-13' ) {
1604 $isbn = $isbn->as_isbn13();
1607 if ($strip_hyphens) {
1608 $string = $isbn->as_string( [] );
1609 } else {
1610 $string = $isbn->as_string();
1613 return $string;
1617 =head2 GetVariationsOfISBN
1619 my @isbns = GetVariationsOfISBN( $isbn );
1621 Returns a list of varations of the given isbn in
1622 both ISBN-10 and ISBN-13 formats, with and without
1623 hyphens.
1625 In a scalar context, the isbns are returned as a
1626 string delimited by ' | '.
1628 =cut
1630 sub GetVariationsOfISBN {
1631 my ($isbn) = @_;
1633 return unless $isbn;
1635 my @isbns;
1637 push( @isbns, NormalizeISBN({ isbn => $isbn }) );
1638 push( @isbns, NormalizeISBN({ isbn => $isbn, format => 'ISBN-10' }) );
1639 push( @isbns, NormalizeISBN({ isbn => $isbn, format => 'ISBN-13' }) );
1640 push( @isbns, NormalizeISBN({ isbn => $isbn, format => 'ISBN-10', strip_hyphens => 1 }) );
1641 push( @isbns, NormalizeISBN({ isbn => $isbn, format => 'ISBN-13', strip_hyphens => 1 }) );
1643 # Strip out any "empty" strings from the array
1644 @isbns = grep { defined($_) && $_ =~ /\S/ } @isbns;
1646 return wantarray ? @isbns : join( " | ", @isbns );
1649 =head2 GetVariationsOfISBNs
1651 my @isbns = GetVariationsOfISBNs( @isbns );
1653 Returns a list of varations of the given isbns in
1654 both ISBN-10 and ISBN-13 formats, with and without
1655 hyphens.
1657 In a scalar context, the isbns are returned as a
1658 string delimited by ' | '.
1660 =cut
1662 sub GetVariationsOfISBNs {
1663 my (@isbns) = @_;
1665 @isbns = map { GetVariationsOfISBN( $_ ) } @isbns;
1667 return wantarray ? @isbns : join( " | ", @isbns );
1672 __END__
1674 =head1 AUTHOR
1676 Koha Team
1678 =cut