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
10 # under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; either version 3 of the License, or
12 # (at your option) any later version.
14 # Koha is distributed in the hope that it will be useful, but
15 # WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
19 # You should have received a copy of the GNU General Public License
20 # along with Koha; if not, see <http://www.gnu.org/licenses>.
24 #use warnings; FIXME - Bug 2505
27 use C4::Branch qw(GetBranchesCount);
29 use Koha::DateUtils qw(dt_from_string);
30 use DateTime::Format::MySQL;
32 use autouse 'Data::cselectall_arrayref' => qw(Dumper);
33 use DBI qw(:sql_types);
34 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK $DEBUG);
37 $VERSION = 3.07.00.049;
42 &subfield_is_koha_internal_p
43 &GetPrinters &GetPrinter
44 &GetItemTypes &getitemtypeinfo
45 &GetItemTypesCategorized &GetItemTypesByCategory
46 &GetSupportName &GetSupportList
48 &getframeworks &getframeworkinfo
50 &getauthtypes &getauthtype
56 &get_notforloan_label_of
59 &getitemtypeimagelocation
61 &GetAuthorisedValueCategories
62 &IsAuthorisedValueCategory
63 &GetKohaAuthorisedValues
64 &GetKohaAuthorisedValuesFromField
65 &GetKohaAuthorisedValuesMapping
66 &GetKohaAuthorisedValueLib
67 &GetAuthorisedValueByCode
68 &GetKohaImageurlFromAuthorisedValues
74 &GetNormalizedOCLCNumber
84 @EXPORT_OK = qw( GetDailyQuote );
89 C4::Koha - Perl Module containing convenience functions for Koha scripts
97 Koha.pm provides many functions for Koha scripts.
105 $slash_date = &slashifyDate($dash_date);
107 Takes a string of the form "DD-MM-YYYY" (or anything separated by
108 dashes), converts it to the form "YYYY/MM/DD", and returns the result.
114 # accepts a date of the form xx-xx-xx[xx] and returns it in the
116 my @dateOut = split( '-', shift );
117 return ("$dateOut[2]/$dateOut[1]/$dateOut[0]");
120 # FIXME.. this should be moved to a MARC-specific module
121 sub subfield_is_koha_internal_p {
124 # We could match on 'lib' and 'tab' (and 'mandatory', & more to come!)
125 # But real MARC subfields are always single-character
126 # so it really is safer just to check the length
128 return length $subfield != 1;
131 =head2 GetSupportName
133 $itemtypename = &GetSupportName($codestring);
135 Returns a string with the name of the itemtype.
141 return if (! $codestring);
143 my $advanced_search_types = C4::Context->preference("AdvancedSearchTypes");
144 if (!$advanced_search_types or $advanced_search_types eq 'itemtypes') {
151 my $sth = C4::Context->dbh->prepare($query);
152 $sth->execute($codestring);
153 ($resultstring)=$sth->fetchrow;
154 return $resultstring;
157 C4::Context->dbh->prepare(
158 "SELECT lib FROM authorised_values WHERE category = ? AND authorised_value = ?"
160 $sth->execute( $advanced_search_types, $codestring );
161 my $data = $sth->fetchrow_hashref;
162 return $$data{'lib'};
166 =head2 GetSupportList
168 $itemtypes = &GetSupportList();
170 Returns an array ref containing informations about Support (since itemtype is rather a circulation code when item-level-itypes is used).
172 build a HTML select with the following code :
174 =head3 in PERL SCRIPT
176 my $itemtypes = GetSupportList();
177 $template->param(itemtypeloop => $itemtypes);
181 <select name="itemtype" id="itemtype">
182 <option value=""></option>
183 [% FOREACH itemtypeloo IN itemtypeloop %]
184 [% IF ( itemtypeloo.selected ) %]
185 <option value="[% itemtypeloo.itemtype %]" selected="selected">[% itemtypeloo.description %]</option>
187 <option value="[% itemtypeloo.itemtype %]">[% itemtypeloo.description %]</option>
195 my $advanced_search_types = C4::Context->preference("AdvancedSearchTypes");
196 if (!$advanced_search_types or $advanced_search_types =~ /itemtypes/) {
202 my $sth = C4::Context->dbh->prepare($query);
204 return $sth->fetchall_arrayref({});
206 my $advsearchtypes = GetAuthorisedValues($advanced_search_types);
207 my @results= map {{itemtype=>$$_{authorised_value},description=>$$_{lib},imageurl=>$$_{imageurl}}} @$advsearchtypes;
213 $itemtypes = &GetItemTypes( style => $style );
215 Returns information about existing itemtypes.
218 style: either 'array' or 'hash', defaults to 'hash'.
219 'array' returns an arrayref,
220 'hash' return a hashref with the itemtype value as the key
222 build a HTML select with the following code :
224 =head3 in PERL SCRIPT
226 my $itemtypes = GetItemTypes;
228 foreach my $thisitemtype (sort keys %$itemtypes) {
229 my $selected = 1 if $thisitemtype eq $itemtype;
230 my %row =(value => $thisitemtype,
231 selected => $selected,
232 description => $itemtypes->{$thisitemtype}->{'description'},
234 push @itemtypesloop, \%row;
236 $template->param(itemtypeloop => \@itemtypesloop);
240 <form action='<!-- TMPL_VAR name="script_name" -->' method=post>
241 <select name="itemtype">
242 <option value="">Default</option>
243 <!-- TMPL_LOOP name="itemtypeloop" -->
244 <option value="<!-- TMPL_VAR name="value" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="description" --></option>
247 <input type=text name=searchfield value="<!-- TMPL_VAR name="searchfield" -->">
248 <input type="submit" value="OK" class="button">
255 my $style = defined( $params{'style'} ) ? $params{'style'} : 'hash';
257 my $language = C4::Languages::getlanguage();
258 # returns a reference to a hash of references to itemtypes...
260 my $dbh = C4::Context->dbh;
264 itemtypes.description,
265 itemtypes.rentalcharge,
266 itemtypes.notforloan,
269 itemtypes.checkinmsg,
270 itemtypes.checkinmsgtype,
271 itemtypes.sip_media_type,
272 COALESCE( localization.translation, itemtypes.description ) AS translated_description
274 LEFT JOIN localization ON itemtypes.itemtype = localization.code
275 AND localization.entity = 'itemtypes'
276 AND localization.lang = ?
279 my $sth = $dbh->prepare($query);
280 $sth->execute( $language );
282 if ( $style eq 'hash' ) {
283 while ( my $IT = $sth->fetchrow_hashref ) {
284 $itemtypes{ $IT->{'itemtype'} } = $IT;
286 return ( \%itemtypes );
288 return $sth->fetchall_arrayref({});
292 =head2 GetItemTypesCategorized
294 $categories = GetItemTypesCategorized();
296 Returns a hashref containing search categories.
297 A search category will be put in the hash if at least one of its itemtypes is visible in OPAC.
298 The categories must be part of Authorized Values (ITEMTYPECAT)
302 sub GetItemTypesCategorized {
303 my $dbh = C4::Context->dbh;
304 # Order is important, so that partially hidden (some items are not visible in OPAC) search
305 # categories will be visible. hideinopac=0 must be last.
307 SELECT itemtype, description, imageurl, hideinopac, 0 as 'iscat' FROM itemtypes WHERE ISNULL(searchcategory) or length(searchcategory) = 0
309 SELECT DISTINCT searchcategory AS `itemtype`,
310 authorised_values.lib_opac AS description,
311 authorised_values.imageurl AS imageurl,
312 hideinopac, 1 as 'iscat'
314 LEFT JOIN authorised_values ON searchcategory = authorised_value
315 WHERE searchcategory > '' and hideinopac=1
317 SELECT DISTINCT searchcategory AS `itemtype`,
318 authorised_values.lib_opac AS description,
319 authorised_values.imageurl AS imageurl,
320 hideinopac, 1 as 'iscat'
322 LEFT JOIN authorised_values ON searchcategory = authorised_value
323 WHERE searchcategory > '' and hideinopac=0
325 return ($dbh->selectall_hashref($query,'itemtype'));
328 =head2 GetItemTypesByCategory
330 @results = GetItemTypesByCategory( $searchcategory );
332 Returns the itemtype code of all itemtypes included in a searchcategory.
336 sub GetItemTypesByCategory {
340 my $dbh = C4::Context->dbh;
341 my $query = qq|SELECT itemtype FROM itemtypes WHERE searchcategory=?|;
342 my $tmp=$dbh->selectcol_arrayref($query,undef,$category);
346 sub get_itemtypeinfos_of {
349 my $placeholders = join( ', ', map { '?' } @itemtypes );
350 my $query = <<"END_SQL";
356 WHERE itemtype IN ( $placeholders )
359 return get_infos_of( $query, 'itemtype', undef, \@itemtypes );
364 $authtypes = &getauthtypes();
366 Returns information about existing authtypes.
368 build a HTML select with the following code :
370 =head3 in PERL SCRIPT
372 my $authtypes = getauthtypes;
374 foreach my $thisauthtype (keys %$authtypes) {
375 my $selected = 1 if $thisauthtype eq $authtype;
376 my %row =(value => $thisauthtype,
377 selected => $selected,
378 authtypetext => $authtypes->{$thisauthtype}->{'authtypetext'},
380 push @authtypesloop, \%row;
382 $template->param(itemtypeloop => \@itemtypesloop);
386 <form action='<!-- TMPL_VAR name="script_name" -->' method=post>
387 <select name="authtype">
388 <!-- TMPL_LOOP name="authtypeloop" -->
389 <option value="<!-- TMPL_VAR name="value" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="authtypetext" --></option>
392 <input type=text name=searchfield value="<!-- TMPL_VAR name="searchfield" -->">
393 <input type="submit" value="OK" class="button">
401 # returns a reference to a hash of references to authtypes...
403 my $dbh = C4::Context->dbh;
404 my $sth = $dbh->prepare("select * from auth_types order by authtypetext");
406 while ( my $IT = $sth->fetchrow_hashref ) {
407 $authtypes{ $IT->{'authtypecode'} } = $IT;
409 return ( \%authtypes );
413 my ($authtypecode) = @_;
415 # returns a reference to a hash of references to authtypes...
417 my $dbh = C4::Context->dbh;
418 my $sth = $dbh->prepare("select * from auth_types where authtypecode=?");
419 $sth->execute($authtypecode);
420 my $res = $sth->fetchrow_hashref;
426 $frameworks = &getframework();
428 Returns information about existing frameworks
430 build a HTML select with the following code :
432 =head3 in PERL SCRIPT
434 my $frameworks = getframeworks();
436 foreach my $thisframework (keys %$frameworks) {
437 my $selected = 1 if $thisframework eq $frameworkcode;
439 value => $thisframework,
440 selected => $selected,
441 description => $frameworks->{$thisframework}->{'frameworktext'},
443 push @frameworksloop, \%row;
445 $template->param(frameworkloop => \@frameworksloop);
449 <form action="[% script_name %] method=post>
450 <select name="frameworkcode">
451 <option value="">Default</option>
452 [% FOREACH framework IN frameworkloop %]
453 [% IF ( framework.selected ) %]
454 <option value="[% framework.value %]" selected="selected">[% framework.description %]</option>
456 <option value="[% framework.value %]">[% framework.description %]</option>
460 <input type=text name=searchfield value="[% searchfield %]">
461 <input type="submit" value="OK" class="button">
468 # returns a reference to a hash of references to branches...
470 my $dbh = C4::Context->dbh;
471 my $sth = $dbh->prepare("select * from biblio_framework");
473 while ( my $IT = $sth->fetchrow_hashref ) {
474 $itemtypes{ $IT->{'frameworkcode'} } = $IT;
476 return ( \%itemtypes );
479 =head2 GetFrameworksLoop
481 $frameworks = GetFrameworksLoop( $frameworkcode );
483 Returns the loop suggested on getframework(), but ordered by framework description.
485 build a HTML select with the following code :
487 =head3 in PERL SCRIPT
489 $template->param( frameworkloop => GetFrameworksLoop( $frameworkcode ) );
493 Same as getframework()
495 <form action="[% script_name %] method=post>
496 <select name="frameworkcode">
497 <option value="">Default</option>
498 [% FOREACH framework IN frameworkloop %]
499 [% IF ( framework.selected ) %]
500 <option value="[% framework.value %]" selected="selected">[% framework.description %]</option>
502 <option value="[% framework.value %]">[% framework.description %]</option>
506 <input type=text name=searchfield value="[% searchfield %]">
507 <input type="submit" value="OK" class="button">
512 sub GetFrameworksLoop {
513 my $frameworkcode = shift;
514 my $frameworks = getframeworks();
516 foreach my $thisframework (sort { uc($frameworks->{$a}->{'frameworktext'}) cmp uc($frameworks->{$b}->{'frameworktext'}) } keys %$frameworks) {
517 my $selected = ( $thisframework eq $frameworkcode ) ? 1 : undef;
519 value => $thisframework,
520 selected => $selected,
521 description => $frameworks->{$thisframework}->{'frameworktext'},
523 push @frameworkloop, \%row;
525 return \@frameworkloop;
528 =head2 getframeworkinfo
530 $frameworkinfo = &getframeworkinfo($frameworkcode);
532 Returns information about an frameworkcode.
536 sub getframeworkinfo {
537 my ($frameworkcode) = @_;
538 my $dbh = C4::Context->dbh;
540 $dbh->prepare("select * from biblio_framework where frameworkcode=?");
541 $sth->execute($frameworkcode);
542 my $res = $sth->fetchrow_hashref;
546 =head2 getitemtypeinfo
548 $itemtype = &getitemtypeinfo($itemtype, [$interface]);
550 Returns information about an itemtype. The optional $interface argument
551 sets which interface ('opac' or 'intranet') to return the imageurl for.
552 Defaults to intranet.
556 sub getitemtypeinfo {
557 my ($itemtype, $interface) = @_;
558 my $dbh = C4::Context->dbh;
559 my $language = C4::Languages::getlanguage();
560 my $it = $dbh->selectrow_hashref(q|
563 itemtypes.description,
564 itemtypes.rentalcharge,
565 itemtypes.notforloan,
568 itemtypes.checkinmsg,
569 itemtypes.checkinmsgtype,
570 itemtypes.sip_media_type,
571 COALESCE( localization.translation, itemtypes.description ) AS translated_description
573 LEFT JOIN localization ON itemtypes.itemtype = localization.code
574 AND localization.entity = 'itemtypes'
575 AND localization.lang = ?
576 WHERE itemtypes.itemtype = ?
577 |, undef, $language, $itemtype );
579 $it->{imageurl} = getitemtypeimagelocation( ( ( defined $interface && $interface eq 'opac' ) ? 'opac' : 'intranet' ), $it->{imageurl} );
584 =head2 getitemtypeimagedir
586 my $directory = getitemtypeimagedir( 'opac' );
588 pass in 'opac' or 'intranet'. Defaults to 'opac'.
590 returns the full path to the appropriate directory containing images.
594 sub getitemtypeimagedir {
595 my $src = shift || 'opac';
596 if ($src eq 'intranet') {
597 return C4::Context->config('intrahtdocs') . '/' .C4::Context->preference('template') . '/img/itemtypeimg';
599 return C4::Context->config('opachtdocs') . '/' . C4::Context->preference('opacthemes') . '/itemtypeimg';
603 sub getitemtypeimagesrc {
604 my $src = shift || 'opac';
605 if ($src eq 'intranet') {
606 return '/intranet-tmpl' . '/' . C4::Context->preference('template') . '/img/itemtypeimg';
608 return '/opac-tmpl' . '/' . C4::Context->preference('opacthemes') . '/itemtypeimg';
612 sub getitemtypeimagelocation {
613 my ( $src, $image ) = @_;
615 return '' if ( !$image );
618 my $scheme = ( URI::Split::uri_split( $image ) )[0];
620 return $image if ( $scheme );
622 return getitemtypeimagesrc( $src ) . '/' . $image;
625 =head3 _getImagesFromDirectory
627 Find all of the image files in a directory in the filesystem
629 parameters: a directory name
631 returns: a list of images in that directory.
633 Notes: this does not traverse into subdirectories. See
634 _getSubdirectoryNames for help with that.
635 Images are assumed to be files with .gif or .png file extensions.
636 The image names returned do not have the directory name on them.
640 sub _getImagesFromDirectory {
641 my $directoryname = shift;
642 return unless defined $directoryname;
643 return unless -d $directoryname;
645 if ( opendir ( my $dh, $directoryname ) ) {
646 my @images = grep { /\.(gif|png)$/i } readdir( $dh );
648 @images = sort(@images);
651 warn "unable to opendir $directoryname: $!";
656 =head3 _getSubdirectoryNames
658 Find all of the directories in a directory in the filesystem
660 parameters: a directory name
662 returns: a list of subdirectories in that directory.
664 Notes: this does not traverse into subdirectories. Only the first
665 level of subdirectories are returned.
666 The directory names returned don't have the parent directory name on them.
670 sub _getSubdirectoryNames {
671 my $directoryname = shift;
672 return unless defined $directoryname;
673 return unless -d $directoryname;
675 if ( opendir ( my $dh, $directoryname ) ) {
676 my @directories = grep { -d File::Spec->catfile( $directoryname, $_ ) && ! ( /^\./ ) } readdir( $dh );
680 warn "unable to opendir $directoryname: $!";
687 returns: a listref of hashrefs. Each hash represents another collection of images.
689 { imagesetname => 'npl', # the name of the image set (npl is the original one)
690 images => listref of image hashrefs
693 each image is represented by a hashref like this:
695 { KohaImage => 'npl/image.gif',
696 StaffImageUrl => '/intranet-tmpl/prog/img/itemtypeimg/npl/image.gif',
697 OpacImageURL => '/opac-tmpl/prog/itemtypeimg/npl/image.gif'
698 checked => 0 or 1: was this the image passed to this method?
699 Note: I'd like to remove this somehow.
706 my $checked = $params{'checked'} || '';
708 my $paths = { staff => { filesystem => getitemtypeimagedir('intranet'),
709 url => getitemtypeimagesrc('intranet'),
711 opac => { filesystem => getitemtypeimagedir('opac'),
712 url => getitemtypeimagesrc('opac'),
716 my @imagesets = (); # list of hasrefs of image set data to pass to template
717 my @subdirectories = _getSubdirectoryNames( $paths->{'staff'}{'filesystem'} );
718 foreach my $imagesubdir ( @subdirectories ) {
719 warn $imagesubdir if $DEBUG;
720 my @imagelist = (); # hashrefs of image info
721 my @imagenames = _getImagesFromDirectory( File::Spec->catfile( $paths->{'staff'}{'filesystem'}, $imagesubdir ) );
722 my $imagesetactive = 0;
723 foreach my $thisimage ( @imagenames ) {
725 { KohaImage => "$imagesubdir/$thisimage",
726 StaffImageUrl => join( '/', $paths->{'staff'}{'url'}, $imagesubdir, $thisimage ),
727 OpacImageUrl => join( '/', $paths->{'opac'}{'url'}, $imagesubdir, $thisimage ),
728 checked => "$imagesubdir/$thisimage" eq $checked ? 1 : 0,
731 $imagesetactive = 1 if "$imagesubdir/$thisimage" eq $checked;
733 push @imagesets, { imagesetname => $imagesubdir,
734 imagesetactive => $imagesetactive,
735 images => \@imagelist };
743 $printers = &GetPrinters();
744 @queues = keys %$printers;
746 Returns information about existing printer queues.
748 C<$printers> is a reference-to-hash whose keys are the print queues
749 defined in the printers table of the Koha database. The values are
750 references-to-hash, whose keys are the fields in the printers table.
756 my $dbh = C4::Context->dbh;
757 my $sth = $dbh->prepare("select * from printers");
759 while ( my $printer = $sth->fetchrow_hashref ) {
760 $printers{ $printer->{'printqueue'} } = $printer;
762 return ( \%printers );
767 $printer = GetPrinter( $query, $printers );
772 my ( $query, $printers ) = @_; # get printer for this query from printers
773 my $printer = $query->param('printer');
774 my %cookie = $query->cookie('userenv');
775 ($printer) || ( $printer = $cookie{'printer'} ) || ( $printer = '' );
776 ( $printers->{$printer} ) || ( $printer = ( keys %$printers )[0] );
782 Returns the number of pages to display in a pagination bar, given the number
783 of items and the number of items per page.
788 my ( $nb_items, $nb_items_per_page ) = @_;
790 return int( ( $nb_items - 1 ) / $nb_items_per_page ) + 1;
795 (@themes) = &getallthemes('opac');
796 (@themes) = &getallthemes('intranet');
798 Returns an array of all available themes.
806 if ( $type eq 'intranet' ) {
807 $htdocs = C4::Context->config('intrahtdocs');
810 $htdocs = C4::Context->config('opachtdocs');
812 opendir D, "$htdocs";
813 my @dirlist = readdir D;
814 foreach my $directory (@dirlist) {
815 next if $directory eq 'lib';
816 -d "$htdocs/$directory/en" and push @themes, $directory;
823 if ( C4::Context->preference("marcflavour") eq "UNIMARC" ) {
828 tags => [ qw/ 600ab 601ab 602a 604at 605a 606ax 610a / ],
834 tags => [ qw/ 607a / ],
840 tags => [ qw/ 500a 501a 503a / ],
846 tags => [ qw/ 700ab 701ab 702ab / ],
847 sep => C4::Context->preference("UNIMARCAuthorsFacetsSeparator"),
852 tags => [ qw/ 225a / ],
858 tags => [ qw/ 995e / ],
862 unless ( C4::Context->preference("singleBranchMode")
863 || GetBranchesCount() == 1 )
865 my $DisplayLibraryFacets = C4::Context->preference('DisplayLibraryFacets');
866 if ( $DisplayLibraryFacets eq 'both'
867 || $DisplayLibraryFacets eq 'holding' )
872 idx => 'holdingbranch',
873 label => 'HoldingLibrary',
874 tags => [qw / 995c /],
879 if ( $DisplayLibraryFacets eq 'both'
880 || $DisplayLibraryFacets eq 'home' )
886 label => 'HomeLibrary',
887 tags => [qw / 995b /],
898 tags => [ qw/ 650a / ],
903 # label => 'People and Organizations',
904 # tags => [ qw/ 600a 610a 611a / ],
910 tags => [ qw/ 651a / ],
916 tags => [ qw/ 630a / ],
922 tags => [ qw/ 100a 110a 700a / ],
928 tags => [ qw/ 440a 490a / ],
933 label => 'ItemTypes',
934 tags => [ qw/ 952y 942c / ],
940 tags => [ qw / 952c / ],
944 unless ( C4::Context->preference("singleBranchMode")
945 || GetBranchesCount() == 1 )
947 my $DisplayLibraryFacets = C4::Context->preference('DisplayLibraryFacets');
948 if ( $DisplayLibraryFacets eq 'both'
949 || $DisplayLibraryFacets eq 'holding' )
954 idx => 'holdingbranch',
955 label => 'HoldingLibrary',
956 tags => [qw / 952b /],
961 if ( $DisplayLibraryFacets eq 'both'
962 || $DisplayLibraryFacets eq 'home' )
968 label => 'HomeLibrary',
969 tags => [qw / 952a /],
980 Return a href where a key is associated to a href. You give a query,
981 the name of the key among the fields returned by the query. If you
982 also give as third argument the name of the value, the function
983 returns a href of scalar. The optional 4th argument is an arrayref of
984 items passed to the C<execute()> call. It is designed to bind
985 parameters to any placeholders in your SQL.
994 # generic href of any information on the item, href of href.
995 my $iteminfos_of = get_infos_of($query, 'itemnumber');
996 print $iteminfos_of->{$itemnumber}{barcode};
998 # specific information, href of scalar
999 my $barcode_of_item = get_infos_of($query, 'itemnumber', 'barcode');
1000 print $barcode_of_item->{$itemnumber};
1005 my ( $query, $key_name, $value_name, $bind_params ) = @_;
1007 my $dbh = C4::Context->dbh;
1009 my $sth = $dbh->prepare($query);
1010 $sth->execute( @$bind_params );
1013 while ( my $row = $sth->fetchrow_hashref ) {
1014 if ( defined $value_name ) {
1015 $infos_of{ $row->{$key_name} } = $row->{$value_name};
1018 $infos_of{ $row->{$key_name} } = $row;
1026 =head2 get_notforloan_label_of
1028 my $notforloan_label_of = get_notforloan_label_of();
1030 Each authorised value of notforloan (information available in items and
1031 itemtypes) is link to a single label.
1033 Returns a href where keys are authorised values and values are corresponding
1036 foreach my $authorised_value (keys %{$notforloan_label_of}) {
1038 "authorised_value: %s => %s\n",
1040 $notforloan_label_of->{$authorised_value}
1046 # FIXME - why not use GetAuthorisedValues ??
1048 sub get_notforloan_label_of {
1049 my $dbh = C4::Context->dbh;
1052 SELECT authorised_value
1053 FROM marc_subfield_structure
1054 WHERE kohafield = \'items.notforloan\'
1057 my $sth = $dbh->prepare($query);
1059 my ($statuscode) = $sth->fetchrow_array();
1064 FROM authorised_values
1067 $sth = $dbh->prepare($query);
1068 $sth->execute($statuscode);
1069 my %notforloan_label_of;
1070 while ( my $row = $sth->fetchrow_hashref ) {
1071 $notforloan_label_of{ $row->{authorised_value} } = $row->{lib};
1075 return \%notforloan_label_of;
1078 =head2 displayServers
1080 my $servers = displayServers();
1081 my $servers = displayServers( $position );
1082 my $servers = displayServers( $position, $type );
1084 displayServers returns a listref of hashrefs, each containing
1085 information about available z3950 servers. Each hashref has a format
1089 'checked' => 'checked',
1090 'encoding' => 'utf8',
1092 'id' => 'LIBRARY OF CONGRESS',
1096 'value' => 'lx2.loc.gov:210/',
1102 sub displayServers {
1103 my ( $position, $type ) = @_;
1104 my $dbh = C4::Context->dbh;
1106 my $strsth = 'SELECT * FROM z3950servers';
1111 push @bind_params, $position;
1112 push @where_clauses, ' position = ? ';
1116 push @bind_params, $type;
1117 push @where_clauses, ' type = ? ';
1120 # reassemble where clause from where clause pieces
1121 if (@where_clauses) {
1122 $strsth .= ' WHERE ' . join( ' AND ', @where_clauses );
1125 my $rq = $dbh->prepare($strsth);
1126 $rq->execute(@bind_params);
1127 my @primaryserverloop;
1129 while ( my $data = $rq->fetchrow_hashref ) {
1130 push @primaryserverloop,
1131 { label => $data->{description},
1132 id => $data->{name},
1134 value => $data->{host} . ":" . $data->{port} . "/" . $data->{database},
1135 encoding => ( $data->{encoding} ? $data->{encoding} : "iso-5426" ),
1136 checked => "checked",
1137 icon => $data->{icon},
1138 zed => $data->{type} eq 'zed',
1139 opensearch => $data->{type} eq 'opensearch'
1142 return \@primaryserverloop;
1146 =head2 GetKohaImageurlFromAuthorisedValues
1148 $authhorised_value = GetKohaImageurlFromAuthorisedValues( $category, $authvalcode );
1150 Return the first url of the authorised value image represented by $lib.
1154 sub GetKohaImageurlFromAuthorisedValues {
1155 my ( $category, $lib ) = @_;
1156 my $dbh = C4::Context->dbh;
1157 my $sth = $dbh->prepare("SELECT imageurl FROM authorised_values WHERE category=? AND lib =?");
1158 $sth->execute( $category, $lib );
1159 while ( my $data = $sth->fetchrow_hashref ) {
1160 return $data->{'imageurl'};
1164 =head2 GetAuthValCode
1166 $authvalcode = GetAuthValCode($kohafield,$frameworkcode);
1170 sub GetAuthValCode {
1171 my ($kohafield,$fwcode) = @_;
1172 my $dbh = C4::Context->dbh;
1173 $fwcode='' unless $fwcode;
1174 my $sth = $dbh->prepare('select authorised_value from marc_subfield_structure where kohafield=? and frameworkcode=?');
1175 $sth->execute($kohafield,$fwcode);
1176 my ($authvalcode) = $sth->fetchrow_array;
1177 return $authvalcode;
1180 =head2 GetAuthValCodeFromField
1182 $authvalcode = GetAuthValCodeFromField($field,$subfield,$frameworkcode);
1184 C<$subfield> can be undefined
1188 sub GetAuthValCodeFromField {
1189 my ($field,$subfield,$fwcode) = @_;
1190 my $dbh = C4::Context->dbh;
1191 $fwcode='' unless $fwcode;
1193 if (defined $subfield) {
1194 $sth = $dbh->prepare('select authorised_value from marc_subfield_structure where tagfield=? and tagsubfield=? and frameworkcode=?');
1195 $sth->execute($field,$subfield,$fwcode);
1197 $sth = $dbh->prepare('select authorised_value from marc_tag_structure where tagfield=? and frameworkcode=?');
1198 $sth->execute($field,$fwcode);
1200 my ($authvalcode) = $sth->fetchrow_array;
1201 return $authvalcode;
1204 =head2 GetAuthorisedValues
1206 $authvalues = GetAuthorisedValues([$category], [$selected]);
1208 This function returns all authorised values from the'authorised_value' table in a reference to array of hashrefs.
1210 C<$category> returns authorised values for just one category (optional).
1212 C<$selected> adds a "selected => 1" entry to the hash if the
1213 authorised_value matches it. B<NOTE:> this feature should be considered
1214 deprecated as it may be removed in the future.
1216 C<$opac> If set to a true value, displays OPAC descriptions rather than normal ones when they exist.
1220 sub GetAuthorisedValues {
1221 my ( $category, $selected, $opac ) = @_;
1223 # TODO: the "selected" feature should be replaced by a utility function
1224 # somewhere else, it doesn't belong in here. For starters it makes
1225 # caching much more complicated. Or just let the UI logic handle it, it's
1228 # Is this cached already?
1229 $opac = $opac ? 1 : 0; # normalise to be safe
1231 C4::Context->userenv ? C4::Context->userenv->{"branch"} : "";
1232 my $selected_key = defined($selected) ? $selected : '';
1234 "AuthorisedValues-$category-$selected_key-$opac-$branch_limit";
1235 my $cache = Koha::Cache->get_instance();
1236 my $result = $cache->get_from_cache($cache_key);
1237 return $result if $result;
1240 my $dbh = C4::Context->dbh;
1243 FROM authorised_values
1246 LEFT JOIN authorised_values_branches ON ( id = av_id )
1251 push @where_strings, "category = ?";
1252 push @where_args, $category;
1255 push @where_strings, "( branchcode = ? OR branchcode IS NULL )";
1256 push @where_args, $branch_limit;
1258 if(@where_strings > 0) {
1259 $query .= " WHERE " . join(" AND ", @where_strings);
1261 $query .= " GROUP BY lib";
1262 $query .= ' ORDER BY category, ' . (
1263 $opac ? 'COALESCE(lib_opac, lib)'
1267 my $sth = $dbh->prepare($query);
1269 $sth->execute( @where_args );
1270 while (my $data=$sth->fetchrow_hashref) {
1271 if ( defined $selected and $selected eq $data->{authorised_value} ) {
1272 $data->{selected} = 1;
1275 $data->{selected} = 0;
1278 if ($opac && $data->{lib_opac}) {
1279 $data->{lib} = $data->{lib_opac};
1281 push @results, $data;
1285 # We can't cache for long because of that "selected" thing which
1286 # makes it impossible to clear the cache without iterating through every
1287 # value, which sucks. This'll cover this request, and not a whole lot more.
1288 $cache->set_in_cache( $cache_key, \@results, { deepcopy => 1, expiry => 5 } );
1292 =head2 GetAuthorisedValueCategories
1294 $auth_categories = GetAuthorisedValueCategories();
1296 Return an arrayref of all of the available authorised
1301 sub GetAuthorisedValueCategories {
1302 my $dbh = C4::Context->dbh;
1303 my $sth = $dbh->prepare("SELECT DISTINCT category FROM authorised_values ORDER BY category");
1306 while (defined (my $category = $sth->fetchrow_array) ) {
1307 push @results, $category;
1312 =head2 IsAuthorisedValueCategory
1314 $is_auth_val_category = IsAuthorisedValueCategory($category);
1316 Returns whether a given category name is a valid one
1320 sub IsAuthorisedValueCategory {
1321 my $category = shift;
1324 FROM authorised_values
1328 my $sth = C4::Context->dbh->prepare($query);
1329 $sth->execute($category);
1330 $sth->fetchrow ? return 1
1334 =head2 GetAuthorisedValueByCode
1336 $authorised_value = GetAuthorisedValueByCode( $category, $authvalcode, $opac );
1338 Return the lib attribute from authorised_values from the row identified
1339 by the passed category and code
1343 sub GetAuthorisedValueByCode {
1344 my ( $category, $authvalcode, $opac ) = @_;
1346 my $field = $opac ? 'lib_opac' : 'lib';
1347 my $dbh = C4::Context->dbh;
1348 my $sth = $dbh->prepare("SELECT $field FROM authorised_values WHERE category=? AND authorised_value =?");
1349 $sth->execute( $category, $authvalcode );
1350 while ( my $data = $sth->fetchrow_hashref ) {
1351 return $data->{ $field };
1355 =head2 GetKohaAuthorisedValues
1357 Takes $kohafield, $fwcode as parameters.
1359 If $opac parameter is set to a true value, displays OPAC descriptions rather than normal ones when they exist.
1361 Returns hashref of Code => description
1363 Returns undef if no authorised value category is defined for the kohafield.
1367 sub GetKohaAuthorisedValues {
1368 my ($kohafield,$fwcode,$opac) = @_;
1369 $fwcode='' unless $fwcode;
1371 my $dbh = C4::Context->dbh;
1372 my $avcode = GetAuthValCode($kohafield,$fwcode);
1374 my $sth = $dbh->prepare("select authorised_value, lib, lib_opac from authorised_values where category=? ");
1375 $sth->execute($avcode);
1376 while ( my ($val, $lib, $lib_opac) = $sth->fetchrow_array ) {
1377 $values{$val} = ($opac && $lib_opac) ? $lib_opac : $lib;
1385 =head2 GetKohaAuthorisedValuesFromField
1387 Takes $field, $subfield, $fwcode as parameters.
1389 If $opac parameter is set to a true value, displays OPAC descriptions rather than normal ones when they exist.
1390 $subfield can be undefined
1392 Returns hashref of Code => description
1394 Returns undef if no authorised value category is defined for the given field and subfield
1398 sub GetKohaAuthorisedValuesFromField {
1399 my ($field, $subfield, $fwcode,$opac) = @_;
1400 $fwcode='' unless $fwcode;
1402 my $dbh = C4::Context->dbh;
1403 my $avcode = GetAuthValCodeFromField($field, $subfield, $fwcode);
1405 my $sth = $dbh->prepare("select authorised_value, lib, lib_opac from authorised_values where category=? ");
1406 $sth->execute($avcode);
1407 while ( my ($val, $lib, $lib_opac) = $sth->fetchrow_array ) {
1408 $values{$val} = ($opac && $lib_opac) ? $lib_opac : $lib;
1416 =head2 GetKohaAuthorisedValuesMapping
1418 Takes a hash as a parameter. The interface key indicates the
1419 description to use in the mapping.
1422 "{kohafield},{frameworkcode},{authorised_value}" => "{description}"
1423 for all the kohafields, frameworkcodes, and authorised values.
1425 Returns undef if nothing is found.
1429 sub GetKohaAuthorisedValuesMapping {
1430 my ($parameter) = @_;
1431 my $interface = $parameter->{'interface'} // '';
1433 my $query_mapping = q{
1434 SELECT TA.kohafield,TA.authorised_value AS category,
1435 TA.frameworkcode,TB.authorised_value,
1436 IF(TB.lib_opac>'',TB.lib_opac,TB.lib) AS OPAC,
1437 TB.lib AS Intranet,TB.lib_opac
1438 FROM marc_subfield_structure AS TA JOIN
1439 authorised_values as TB ON
1440 TA.authorised_value=TB.category
1441 WHERE TA.kohafield>'' AND TA.authorised_value>'';
1443 my $dbh = C4::Context->dbh;
1444 my $sth = $dbh->prepare($query_mapping);
1447 if ($interface eq 'opac') {
1448 while (my $row = $sth->fetchrow_hashref) {
1449 $avmapping->{$row->{kohafield}.",".$row->{frameworkcode}.",".$row->{authorised_value}} = $row->{OPAC};
1453 while (my $row = $sth->fetchrow_hashref) {
1454 $avmapping->{$row->{kohafield}.",".$row->{frameworkcode}.",".$row->{authorised_value}} = $row->{Intranet};
1462 my $escaped_string = C4::Koha::xml_escape($string);
1464 Convert &, <, >, ', and " in a string to XML entities
1470 return '' unless defined $str;
1471 $str =~ s/&/&/g;
1474 $str =~ s/'/'/g;
1475 $str =~ s/"/"/g;
1479 =head2 GetKohaAuthorisedValueLib
1481 Takes $category, $authorised_value as parameters.
1483 If $opac parameter is set to a true value, displays OPAC descriptions rather than normal ones when they exist.
1485 Returns authorised value description
1489 sub GetKohaAuthorisedValueLib {
1490 my ($category,$authorised_value,$opac) = @_;
1492 my $dbh = C4::Context->dbh;
1493 my $sth = $dbh->prepare("select lib, lib_opac from authorised_values where category=? and authorised_value=?");
1494 $sth->execute($category,$authorised_value);
1495 my $data = $sth->fetchrow_hashref;
1496 $value = ($opac && $$data{'lib_opac'}) ? $$data{'lib_opac'} : $$data{'lib'};
1500 =head2 AddAuthorisedValue
1502 AddAuthorisedValue($category, $authorised_value, $lib, $lib_opac, $imageurl);
1504 Create a new authorised value.
1508 sub AddAuthorisedValue {
1509 my ($category, $authorised_value, $lib, $lib_opac, $imageurl) = @_;
1511 my $dbh = C4::Context->dbh;
1513 INSERT INTO authorised_values (category, authorised_value, lib, lib_opac, imageurl)
1516 my $sth = $dbh->prepare($query);
1517 $sth->execute($category, $authorised_value, $lib, $lib_opac, $imageurl);
1520 =head2 display_marc_indicators
1522 my $display_form = C4::Koha::display_marc_indicators($field);
1524 C<$field> is a MARC::Field object
1526 Generate a display form of the indicators of a variable
1527 MARC field, replacing any blanks with '#'.
1531 sub display_marc_indicators {
1533 my $indicators = '';
1534 if ($field->tag() >= 10) {
1535 $indicators = $field->indicator(1) . $field->indicator(2);
1536 $indicators =~ s/ /#/g;
1541 sub GetNormalizedUPC {
1542 my ($record,$marcflavour) = @_;
1545 if ($marcflavour eq 'UNIMARC') {
1546 @fields = $record->field('072');
1547 foreach my $field (@fields) {
1548 my $upc = _normalize_match_point($field->subfield('a'));
1555 else { # assume marc21 if not unimarc
1556 @fields = $record->field('024');
1557 foreach my $field (@fields) {
1558 my $indicator = $field->indicator(1);
1559 my $upc = _normalize_match_point($field->subfield('a'));
1560 if ($indicator == 1 and $upc ne '') {
1567 # Normalizes and returns the first valid ISBN found in the record
1568 # ISBN13 are converted into ISBN10. This is required to get some book cover images.
1569 sub GetNormalizedISBN {
1570 my ($isbn,$record,$marcflavour) = @_;
1573 # Koha attempts to store multiple ISBNs in biblioitems.isbn, separated by " | "
1574 # anything after " | " should be removed, along with the delimiter
1575 ($isbn) = split(/\|/, $isbn );
1576 return _isbn_cleanup($isbn);
1578 return unless $record;
1580 if ($marcflavour eq 'UNIMARC') {
1581 @fields = $record->field('010');
1582 foreach my $field (@fields) {
1583 my $isbn = $field->subfield('a');
1585 return _isbn_cleanup($isbn);
1591 else { # assume marc21 if not unimarc
1592 @fields = $record->field('020');
1593 foreach my $field (@fields) {
1594 $isbn = $field->subfield('a');
1596 return _isbn_cleanup($isbn);
1604 sub GetNormalizedEAN {
1605 my ($record,$marcflavour) = @_;
1608 if ($marcflavour eq 'UNIMARC') {
1609 @fields = $record->field('073');
1610 foreach my $field (@fields) {
1611 $ean = _normalize_match_point($field->subfield('a'));
1617 else { # assume marc21 if not unimarc
1618 @fields = $record->field('024');
1619 foreach my $field (@fields) {
1620 my $indicator = $field->indicator(1);
1621 $ean = _normalize_match_point($field->subfield('a'));
1622 if ($indicator == 3 and $ean ne '') {
1628 sub GetNormalizedOCLCNumber {
1629 my ($record,$marcflavour) = @_;
1632 if ($marcflavour eq 'UNIMARC') {
1633 # TODO: add UNIMARC fields
1635 else { # assume marc21 if not unimarc
1636 @fields = $record->field('035');
1637 foreach my $field (@fields) {
1638 $oclc = $field->subfield('a');
1639 if ($oclc =~ /OCoLC/) {
1640 $oclc =~ s/\(OCoLC\)//;
1649 sub GetAuthvalueDropbox {
1650 my ( $authcat, $default ) = @_;
1651 my $branch_limit = C4::Context->userenv ? C4::Context->userenv->{"branch"} : "";
1652 my $dbh = C4::Context->dbh;
1656 FROM authorised_values
1659 LEFT JOIN authorised_values_branches ON ( id = av_id )
1664 $query .= " AND ( branchcode = ? OR branchcode IS NULL )" if $branch_limit;
1665 $query .= " GROUP BY lib ORDER BY category, lib, lib_opac";
1666 my $sth = $dbh->prepare($query);
1667 $sth->execute( $authcat, $branch_limit ? $branch_limit : () );
1670 my $option_list = [];
1671 my @authorised_values = ( q{} );
1672 while (my $av = $sth->fetchrow_hashref) {
1673 push @{$option_list}, {
1674 value => $av->{authorised_value},
1675 label => $av->{lib},
1676 default => ($default eq $av->{authorised_value}),
1680 if ( @{$option_list} ) {
1681 return $option_list;
1687 =head2 GetDailyQuote($opts)
1689 Takes a hashref of options
1691 Currently supported options are:
1693 'id' An exact quote id
1694 'random' Select a random quote
1695 noop When no option is passed in, this sub will return the quote timestamped for the current day
1697 The function returns an anonymous hash following this format:
1700 'source' => 'source-of-quote',
1701 'timestamp' => 'timestamp-value',
1702 'text' => 'text-of-quote',
1708 # This is definitely a candidate for some sort of caching once we finally settle caching/persistence issues...
1709 # at least for default option
1713 my $dbh = C4::Context->dbh;
1718 $query = 'SELECT * FROM quotes WHERE id = ?';
1719 $sth = $dbh->prepare($query);
1720 $sth->execute($opts{'id'});
1721 $quote = $sth->fetchrow_hashref();
1723 elsif ($opts{'random'}) {
1724 # Fall through... we also return a random quote as a catch-all if all else fails
1727 $query = 'SELECT * FROM quotes WHERE timestamp LIKE CONCAT(CURRENT_DATE,\'%\') ORDER BY timestamp DESC LIMIT 0,1';
1728 $sth = $dbh->prepare($query);
1730 $quote = $sth->fetchrow_hashref();
1732 unless ($quote) { # if there are not matches, choose a random quote
1733 # get a list of all available quote ids
1734 $sth = C4::Context->dbh->prepare('SELECT count(*) FROM quotes;');
1736 my $range = ($sth->fetchrow_array)[0];
1737 # chose a random id within that range if there is more than one quote
1738 my $offset = int(rand($range));
1740 $query = 'SELECT * FROM quotes ORDER BY id LIMIT 1 OFFSET ?';
1741 $sth = C4::Context->dbh->prepare($query);
1742 # see http://www.perlmonks.org/?node_id=837422 for why
1743 # we're being verbose and using bind_param
1744 $sth->bind_param(1, $offset, SQL_INTEGER);
1746 $quote = $sth->fetchrow_hashref();
1747 # update the timestamp for that quote
1748 $query = 'UPDATE quotes SET timestamp = ? WHERE id = ?';
1749 $sth = C4::Context->dbh->prepare($query);
1751 DateTime::Format::MySQL->format_datetime( dt_from_string() ),
1758 sub _normalize_match_point {
1759 my $match_point = shift;
1760 (my $normalized_match_point) = $match_point =~ /([\d-]*[X]*)/;
1761 $normalized_match_point =~ s/-//g;
1763 return $normalized_match_point;
1768 return NormalizeISBN(
1771 format => 'ISBN-10',
1777 =head2 NormalizedISBN
1779 my $isbns = NormalizedISBN({
1781 strip_hyphens => [0,1],
1782 format => ['ISBN-10', 'ISBN-13']
1785 Returns an isbn validated by Business::ISBN.
1786 Optionally strips hyphens and/or forces the isbn
1787 to be of the specified format.
1789 If the string cannot be validated as an isbn,
1797 my $string = $params->{isbn};
1798 my $strip_hyphens = $params->{strip_hyphens};
1799 my $format = $params->{format};
1801 return unless $string;
1803 my $isbn = Business::ISBN->new($string);
1805 if ( $isbn && $isbn->is_valid() ) {
1807 if ( $format eq 'ISBN-10' ) {
1808 $isbn = $isbn->as_isbn10();
1810 elsif ( $format eq 'ISBN-13' ) {
1811 $isbn = $isbn->as_isbn13();
1813 return unless $isbn;
1815 if ($strip_hyphens) {
1816 $string = $isbn->as_string( [] );
1818 $string = $isbn->as_string();
1825 =head2 GetVariationsOfISBN
1827 my @isbns = GetVariationsOfISBN( $isbn );
1829 Returns a list of variations of the given isbn in
1830 both ISBN-10 and ISBN-13 formats, with and without
1833 In a scalar context, the isbns are returned as a
1834 string delimited by ' | '.
1838 sub GetVariationsOfISBN {
1841 return unless $isbn;
1845 push( @isbns, NormalizeISBN({ isbn => $isbn }) );
1846 push( @isbns, NormalizeISBN({ isbn => $isbn, format => 'ISBN-10' }) );
1847 push( @isbns, NormalizeISBN({ isbn => $isbn, format => 'ISBN-13' }) );
1848 push( @isbns, NormalizeISBN({ isbn => $isbn, format => 'ISBN-10', strip_hyphens => 1 }) );
1849 push( @isbns, NormalizeISBN({ isbn => $isbn, format => 'ISBN-13', strip_hyphens => 1 }) );
1851 # Strip out any "empty" strings from the array
1852 @isbns = grep { defined($_) && $_ =~ /\S/ } @isbns;
1854 return wantarray ? @isbns : join( " | ", @isbns );
1857 =head2 GetVariationsOfISBNs
1859 my @isbns = GetVariationsOfISBNs( @isbns );
1861 Returns a list of variations of the given isbns in
1862 both ISBN-10 and ISBN-13 formats, with and without
1865 In a scalar context, the isbns are returned as a
1866 string delimited by ' | '.
1870 sub GetVariationsOfISBNs {
1873 @isbns = map { GetVariationsOfISBN( $_ ) } @isbns;
1875 return wantarray ? @isbns : join( " | ", @isbns );
1878 =head2 IsKohaFieldLinked
1880 my $is_linked = IsKohaFieldLinked({
1881 kohafield => $kohafield,
1882 frameworkcode => $frameworkcode,
1885 Return 1 if the field is linked
1889 sub IsKohaFieldLinked {
1890 my ( $params ) = @_;
1891 my $kohafield = $params->{kohafield};
1892 my $frameworkcode = $params->{frameworkcode} || '';
1893 my $dbh = C4::Context->dbh;
1894 my $is_linked = $dbh->selectcol_arrayref( q|
1896 FROM marc_subfield_structure
1897 WHERE frameworkcode = ?
1899 |,{}, $frameworkcode, $kohafield );
1900 return $is_linked->[0];