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/) {
197 return GetItemTypes( style => 'array' );
199 my $advsearchtypes = GetAuthorisedValues($advanced_search_types);
200 my @results= map {{itemtype=>$$_{authorised_value},description=>$$_{lib},imageurl=>$$_{imageurl}}} @$advsearchtypes;
206 $itemtypes = &GetItemTypes( style => $style );
208 Returns information about existing itemtypes.
211 style: either 'array' or 'hash', defaults to 'hash'.
212 'array' returns an arrayref,
213 'hash' return a hashref with the itemtype value as the key
215 build a HTML select with the following code :
217 =head3 in PERL SCRIPT
219 my $itemtypes = GetItemTypes;
221 foreach my $thisitemtype (sort keys %$itemtypes) {
222 my $selected = 1 if $thisitemtype eq $itemtype;
223 my %row =(value => $thisitemtype,
224 selected => $selected,
225 description => $itemtypes->{$thisitemtype}->{'description'},
227 push @itemtypesloop, \%row;
229 $template->param(itemtypeloop => \@itemtypesloop);
233 <form action='<!-- TMPL_VAR name="script_name" -->' method=post>
234 <select name="itemtype">
235 <option value="">Default</option>
236 <!-- TMPL_LOOP name="itemtypeloop" -->
237 <option value="<!-- TMPL_VAR name="value" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="description" --></option>
240 <input type=text name=searchfield value="<!-- TMPL_VAR name="searchfield" -->">
241 <input type="submit" value="OK" class="button">
248 my $style = defined( $params{'style'} ) ? $params{'style'} : 'hash';
250 my $language = C4::Languages::getlanguage();
251 # returns a reference to a hash of references to itemtypes...
253 my $dbh = C4::Context->dbh;
257 itemtypes.description,
258 itemtypes.rentalcharge,
259 itemtypes.notforloan,
262 itemtypes.checkinmsg,
263 itemtypes.checkinmsgtype,
264 itemtypes.sip_media_type,
265 COALESCE( localization.translation, itemtypes.description ) AS translated_description
267 LEFT JOIN localization ON itemtypes.itemtype = localization.code
268 AND localization.entity = 'itemtypes'
269 AND localization.lang = ?
272 my $sth = $dbh->prepare($query);
273 $sth->execute( $language );
275 if ( $style eq 'hash' ) {
276 while ( my $IT = $sth->fetchrow_hashref ) {
277 $itemtypes{ $IT->{'itemtype'} } = $IT;
279 return ( \%itemtypes );
281 return $sth->fetchall_arrayref({});
285 =head2 GetItemTypesCategorized
287 $categories = GetItemTypesCategorized();
289 Returns a hashref containing search categories.
290 A search category will be put in the hash if at least one of its itemtypes is visible in OPAC.
291 The categories must be part of Authorized Values (ITEMTYPECAT)
295 sub GetItemTypesCategorized {
296 my $dbh = C4::Context->dbh;
297 # Order is important, so that partially hidden (some items are not visible in OPAC) search
298 # categories will be visible. hideinopac=0 must be last.
300 SELECT itemtype, description, imageurl, hideinopac, 0 as 'iscat' FROM itemtypes WHERE ISNULL(searchcategory) or length(searchcategory) = 0
302 SELECT DISTINCT searchcategory AS `itemtype`,
303 authorised_values.lib_opac AS description,
304 authorised_values.imageurl AS imageurl,
305 hideinopac, 1 as 'iscat'
307 LEFT JOIN authorised_values ON searchcategory = authorised_value
308 WHERE searchcategory > '' and hideinopac=1
310 SELECT DISTINCT searchcategory AS `itemtype`,
311 authorised_values.lib_opac AS description,
312 authorised_values.imageurl AS imageurl,
313 hideinopac, 1 as 'iscat'
315 LEFT JOIN authorised_values ON searchcategory = authorised_value
316 WHERE searchcategory > '' and hideinopac=0
318 return ($dbh->selectall_hashref($query,'itemtype'));
321 =head2 GetItemTypesByCategory
323 @results = GetItemTypesByCategory( $searchcategory );
325 Returns the itemtype code of all itemtypes included in a searchcategory.
329 sub GetItemTypesByCategory {
333 my $dbh = C4::Context->dbh;
334 my $query = qq|SELECT itemtype FROM itemtypes WHERE searchcategory=?|;
335 my $tmp=$dbh->selectcol_arrayref($query,undef,$category);
339 sub get_itemtypeinfos_of {
342 my $placeholders = join( ', ', map { '?' } @itemtypes );
343 my $query = <<"END_SQL";
349 WHERE itemtype IN ( $placeholders )
352 return get_infos_of( $query, 'itemtype', undef, \@itemtypes );
357 $authtypes = &getauthtypes();
359 Returns information about existing authtypes.
361 build a HTML select with the following code :
363 =head3 in PERL SCRIPT
365 my $authtypes = getauthtypes;
367 foreach my $thisauthtype (keys %$authtypes) {
368 my $selected = 1 if $thisauthtype eq $authtype;
369 my %row =(value => $thisauthtype,
370 selected => $selected,
371 authtypetext => $authtypes->{$thisauthtype}->{'authtypetext'},
373 push @authtypesloop, \%row;
375 $template->param(itemtypeloop => \@itemtypesloop);
379 <form action='<!-- TMPL_VAR name="script_name" -->' method=post>
380 <select name="authtype">
381 <!-- TMPL_LOOP name="authtypeloop" -->
382 <option value="<!-- TMPL_VAR name="value" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="authtypetext" --></option>
385 <input type=text name=searchfield value="<!-- TMPL_VAR name="searchfield" -->">
386 <input type="submit" value="OK" class="button">
394 # returns a reference to a hash of references to authtypes...
396 my $dbh = C4::Context->dbh;
397 my $sth = $dbh->prepare("select * from auth_types order by authtypetext");
399 while ( my $IT = $sth->fetchrow_hashref ) {
400 $authtypes{ $IT->{'authtypecode'} } = $IT;
402 return ( \%authtypes );
406 my ($authtypecode) = @_;
408 # returns a reference to a hash of references to authtypes...
410 my $dbh = C4::Context->dbh;
411 my $sth = $dbh->prepare("select * from auth_types where authtypecode=?");
412 $sth->execute($authtypecode);
413 my $res = $sth->fetchrow_hashref;
419 $frameworks = &getframework();
421 Returns information about existing frameworks
423 build a HTML select with the following code :
425 =head3 in PERL SCRIPT
427 my $frameworks = getframeworks();
429 foreach my $thisframework (keys %$frameworks) {
430 my $selected = 1 if $thisframework eq $frameworkcode;
432 value => $thisframework,
433 selected => $selected,
434 description => $frameworks->{$thisframework}->{'frameworktext'},
436 push @frameworksloop, \%row;
438 $template->param(frameworkloop => \@frameworksloop);
442 <form action="[% script_name %] method=post>
443 <select name="frameworkcode">
444 <option value="">Default</option>
445 [% FOREACH framework IN frameworkloop %]
446 [% IF ( framework.selected ) %]
447 <option value="[% framework.value %]" selected="selected">[% framework.description %]</option>
449 <option value="[% framework.value %]">[% framework.description %]</option>
453 <input type=text name=searchfield value="[% searchfield %]">
454 <input type="submit" value="OK" class="button">
461 # returns a reference to a hash of references to branches...
463 my $dbh = C4::Context->dbh;
464 my $sth = $dbh->prepare("select * from biblio_framework");
466 while ( my $IT = $sth->fetchrow_hashref ) {
467 $itemtypes{ $IT->{'frameworkcode'} } = $IT;
469 return ( \%itemtypes );
472 =head2 GetFrameworksLoop
474 $frameworks = GetFrameworksLoop( $frameworkcode );
476 Returns the loop suggested on getframework(), but ordered by framework description.
478 build a HTML select with the following code :
480 =head3 in PERL SCRIPT
482 $template->param( frameworkloop => GetFrameworksLoop( $frameworkcode ) );
486 Same as getframework()
488 <form action="[% script_name %] method=post>
489 <select name="frameworkcode">
490 <option value="">Default</option>
491 [% FOREACH framework IN frameworkloop %]
492 [% IF ( framework.selected ) %]
493 <option value="[% framework.value %]" selected="selected">[% framework.description %]</option>
495 <option value="[% framework.value %]">[% framework.description %]</option>
499 <input type=text name=searchfield value="[% searchfield %]">
500 <input type="submit" value="OK" class="button">
505 sub GetFrameworksLoop {
506 my $frameworkcode = shift;
507 my $frameworks = getframeworks();
509 foreach my $thisframework (sort { uc($frameworks->{$a}->{'frameworktext'}) cmp uc($frameworks->{$b}->{'frameworktext'}) } keys %$frameworks) {
510 my $selected = ( $thisframework eq $frameworkcode ) ? 1 : undef;
512 value => $thisframework,
513 selected => $selected,
514 description => $frameworks->{$thisframework}->{'frameworktext'},
516 push @frameworkloop, \%row;
518 return \@frameworkloop;
521 =head2 getframeworkinfo
523 $frameworkinfo = &getframeworkinfo($frameworkcode);
525 Returns information about an frameworkcode.
529 sub getframeworkinfo {
530 my ($frameworkcode) = @_;
531 my $dbh = C4::Context->dbh;
533 $dbh->prepare("select * from biblio_framework where frameworkcode=?");
534 $sth->execute($frameworkcode);
535 my $res = $sth->fetchrow_hashref;
539 =head2 getitemtypeinfo
541 $itemtype = &getitemtypeinfo($itemtype, [$interface]);
543 Returns information about an itemtype. The optional $interface argument
544 sets which interface ('opac' or 'intranet') to return the imageurl for.
545 Defaults to intranet.
549 sub getitemtypeinfo {
550 my ($itemtype, $interface) = @_;
551 my $dbh = C4::Context->dbh;
552 my $language = C4::Languages::getlanguage();
553 my $it = $dbh->selectrow_hashref(q|
556 itemtypes.description,
557 itemtypes.rentalcharge,
558 itemtypes.notforloan,
561 itemtypes.checkinmsg,
562 itemtypes.checkinmsgtype,
563 itemtypes.sip_media_type,
564 COALESCE( localization.translation, itemtypes.description ) AS translated_description
566 LEFT JOIN localization ON itemtypes.itemtype = localization.code
567 AND localization.entity = 'itemtypes'
568 AND localization.lang = ?
569 WHERE itemtypes.itemtype = ?
570 |, undef, $language, $itemtype );
572 $it->{imageurl} = getitemtypeimagelocation( ( ( defined $interface && $interface eq 'opac' ) ? 'opac' : 'intranet' ), $it->{imageurl} );
577 =head2 getitemtypeimagedir
579 my $directory = getitemtypeimagedir( 'opac' );
581 pass in 'opac' or 'intranet'. Defaults to 'opac'.
583 returns the full path to the appropriate directory containing images.
587 sub getitemtypeimagedir {
588 my $src = shift || 'opac';
589 if ($src eq 'intranet') {
590 return C4::Context->config('intrahtdocs') . '/' .C4::Context->preference('template') . '/img/itemtypeimg';
592 return C4::Context->config('opachtdocs') . '/' . C4::Context->preference('opacthemes') . '/itemtypeimg';
596 sub getitemtypeimagesrc {
597 my $src = shift || 'opac';
598 if ($src eq 'intranet') {
599 return '/intranet-tmpl' . '/' . C4::Context->preference('template') . '/img/itemtypeimg';
601 return '/opac-tmpl' . '/' . C4::Context->preference('opacthemes') . '/itemtypeimg';
605 sub getitemtypeimagelocation {
606 my ( $src, $image ) = @_;
608 return '' if ( !$image );
611 my $scheme = ( URI::Split::uri_split( $image ) )[0];
613 return $image if ( $scheme );
615 return getitemtypeimagesrc( $src ) . '/' . $image;
618 =head3 _getImagesFromDirectory
620 Find all of the image files in a directory in the filesystem
622 parameters: a directory name
624 returns: a list of images in that directory.
626 Notes: this does not traverse into subdirectories. See
627 _getSubdirectoryNames for help with that.
628 Images are assumed to be files with .gif or .png file extensions.
629 The image names returned do not have the directory name on them.
633 sub _getImagesFromDirectory {
634 my $directoryname = shift;
635 return unless defined $directoryname;
636 return unless -d $directoryname;
638 if ( opendir ( my $dh, $directoryname ) ) {
639 my @images = grep { /\.(gif|png)$/i } readdir( $dh );
641 @images = sort(@images);
644 warn "unable to opendir $directoryname: $!";
649 =head3 _getSubdirectoryNames
651 Find all of the directories in a directory in the filesystem
653 parameters: a directory name
655 returns: a list of subdirectories in that directory.
657 Notes: this does not traverse into subdirectories. Only the first
658 level of subdirectories are returned.
659 The directory names returned don't have the parent directory name on them.
663 sub _getSubdirectoryNames {
664 my $directoryname = shift;
665 return unless defined $directoryname;
666 return unless -d $directoryname;
668 if ( opendir ( my $dh, $directoryname ) ) {
669 my @directories = grep { -d File::Spec->catfile( $directoryname, $_ ) && ! ( /^\./ ) } readdir( $dh );
673 warn "unable to opendir $directoryname: $!";
680 returns: a listref of hashrefs. Each hash represents another collection of images.
682 { imagesetname => 'npl', # the name of the image set (npl is the original one)
683 images => listref of image hashrefs
686 each image is represented by a hashref like this:
688 { KohaImage => 'npl/image.gif',
689 StaffImageUrl => '/intranet-tmpl/prog/img/itemtypeimg/npl/image.gif',
690 OpacImageURL => '/opac-tmpl/prog/itemtypeimg/npl/image.gif'
691 checked => 0 or 1: was this the image passed to this method?
692 Note: I'd like to remove this somehow.
699 my $checked = $params{'checked'} || '';
701 my $paths = { staff => { filesystem => getitemtypeimagedir('intranet'),
702 url => getitemtypeimagesrc('intranet'),
704 opac => { filesystem => getitemtypeimagedir('opac'),
705 url => getitemtypeimagesrc('opac'),
709 my @imagesets = (); # list of hasrefs of image set data to pass to template
710 my @subdirectories = _getSubdirectoryNames( $paths->{'staff'}{'filesystem'} );
711 foreach my $imagesubdir ( @subdirectories ) {
712 warn $imagesubdir if $DEBUG;
713 my @imagelist = (); # hashrefs of image info
714 my @imagenames = _getImagesFromDirectory( File::Spec->catfile( $paths->{'staff'}{'filesystem'}, $imagesubdir ) );
715 my $imagesetactive = 0;
716 foreach my $thisimage ( @imagenames ) {
718 { KohaImage => "$imagesubdir/$thisimage",
719 StaffImageUrl => join( '/', $paths->{'staff'}{'url'}, $imagesubdir, $thisimage ),
720 OpacImageUrl => join( '/', $paths->{'opac'}{'url'}, $imagesubdir, $thisimage ),
721 checked => "$imagesubdir/$thisimage" eq $checked ? 1 : 0,
724 $imagesetactive = 1 if "$imagesubdir/$thisimage" eq $checked;
726 push @imagesets, { imagesetname => $imagesubdir,
727 imagesetactive => $imagesetactive,
728 images => \@imagelist };
736 $printers = &GetPrinters();
737 @queues = keys %$printers;
739 Returns information about existing printer queues.
741 C<$printers> is a reference-to-hash whose keys are the print queues
742 defined in the printers table of the Koha database. The values are
743 references-to-hash, whose keys are the fields in the printers table.
749 my $dbh = C4::Context->dbh;
750 my $sth = $dbh->prepare("select * from printers");
752 while ( my $printer = $sth->fetchrow_hashref ) {
753 $printers{ $printer->{'printqueue'} } = $printer;
755 return ( \%printers );
760 $printer = GetPrinter( $query, $printers );
765 my ( $query, $printers ) = @_; # get printer for this query from printers
766 my $printer = $query->param('printer');
767 my %cookie = $query->cookie('userenv');
768 ($printer) || ( $printer = $cookie{'printer'} ) || ( $printer = '' );
769 ( $printers->{$printer} ) || ( $printer = ( keys %$printers )[0] );
775 Returns the number of pages to display in a pagination bar, given the number
776 of items and the number of items per page.
781 my ( $nb_items, $nb_items_per_page ) = @_;
783 return int( ( $nb_items - 1 ) / $nb_items_per_page ) + 1;
788 (@themes) = &getallthemes('opac');
789 (@themes) = &getallthemes('intranet');
791 Returns an array of all available themes.
799 if ( $type eq 'intranet' ) {
800 $htdocs = C4::Context->config('intrahtdocs');
803 $htdocs = C4::Context->config('opachtdocs');
805 opendir D, "$htdocs";
806 my @dirlist = readdir D;
807 foreach my $directory (@dirlist) {
808 next if $directory eq 'lib';
809 -d "$htdocs/$directory/en" and push @themes, $directory;
816 if ( C4::Context->preference("marcflavour") eq "UNIMARC" ) {
821 tags => [ qw/ 600ab 601ab 602a 604at 605a 606ax 610a / ],
827 tags => [ qw/ 607a / ],
833 tags => [ qw/ 500a 501a 503a / ],
839 tags => [ qw/ 700ab 701ab 702ab / ],
840 sep => C4::Context->preference("UNIMARCAuthorsFacetsSeparator"),
845 tags => [ qw/ 225a / ],
851 tags => [ qw/ 995e / ],
855 unless ( C4::Context->preference("singleBranchMode")
856 || GetBranchesCount() == 1 )
858 my $DisplayLibraryFacets = C4::Context->preference('DisplayLibraryFacets');
859 if ( $DisplayLibraryFacets eq 'both'
860 || $DisplayLibraryFacets eq 'holding' )
865 idx => 'holdingbranch',
866 label => 'HoldingLibrary',
867 tags => [qw / 995c /],
872 if ( $DisplayLibraryFacets eq 'both'
873 || $DisplayLibraryFacets eq 'home' )
879 label => 'HomeLibrary',
880 tags => [qw / 995b /],
891 tags => [ qw/ 650a / ],
896 # label => 'People and Organizations',
897 # tags => [ qw/ 600a 610a 611a / ],
903 tags => [ qw/ 651a / ],
909 tags => [ qw/ 630a / ],
915 tags => [ qw/ 100a 110a 700a / ],
921 tags => [ qw/ 440a 490a / ],
926 label => 'ItemTypes',
927 tags => [ qw/ 952y 942c / ],
933 tags => [ qw / 952c / ],
937 unless ( C4::Context->preference("singleBranchMode")
938 || GetBranchesCount() == 1 )
940 my $DisplayLibraryFacets = C4::Context->preference('DisplayLibraryFacets');
941 if ( $DisplayLibraryFacets eq 'both'
942 || $DisplayLibraryFacets eq 'holding' )
947 idx => 'holdingbranch',
948 label => 'HoldingLibrary',
949 tags => [qw / 952b /],
954 if ( $DisplayLibraryFacets eq 'both'
955 || $DisplayLibraryFacets eq 'home' )
961 label => 'HomeLibrary',
962 tags => [qw / 952a /],
973 Return a href where a key is associated to a href. You give a query,
974 the name of the key among the fields returned by the query. If you
975 also give as third argument the name of the value, the function
976 returns a href of scalar. The optional 4th argument is an arrayref of
977 items passed to the C<execute()> call. It is designed to bind
978 parameters to any placeholders in your SQL.
987 # generic href of any information on the item, href of href.
988 my $iteminfos_of = get_infos_of($query, 'itemnumber');
989 print $iteminfos_of->{$itemnumber}{barcode};
991 # specific information, href of scalar
992 my $barcode_of_item = get_infos_of($query, 'itemnumber', 'barcode');
993 print $barcode_of_item->{$itemnumber};
998 my ( $query, $key_name, $value_name, $bind_params ) = @_;
1000 my $dbh = C4::Context->dbh;
1002 my $sth = $dbh->prepare($query);
1003 $sth->execute( @$bind_params );
1006 while ( my $row = $sth->fetchrow_hashref ) {
1007 if ( defined $value_name ) {
1008 $infos_of{ $row->{$key_name} } = $row->{$value_name};
1011 $infos_of{ $row->{$key_name} } = $row;
1019 =head2 get_notforloan_label_of
1021 my $notforloan_label_of = get_notforloan_label_of();
1023 Each authorised value of notforloan (information available in items and
1024 itemtypes) is link to a single label.
1026 Returns a href where keys are authorised values and values are corresponding
1029 foreach my $authorised_value (keys %{$notforloan_label_of}) {
1031 "authorised_value: %s => %s\n",
1033 $notforloan_label_of->{$authorised_value}
1039 # FIXME - why not use GetAuthorisedValues ??
1041 sub get_notforloan_label_of {
1042 my $dbh = C4::Context->dbh;
1045 SELECT authorised_value
1046 FROM marc_subfield_structure
1047 WHERE kohafield = \'items.notforloan\'
1050 my $sth = $dbh->prepare($query);
1052 my ($statuscode) = $sth->fetchrow_array();
1057 FROM authorised_values
1060 $sth = $dbh->prepare($query);
1061 $sth->execute($statuscode);
1062 my %notforloan_label_of;
1063 while ( my $row = $sth->fetchrow_hashref ) {
1064 $notforloan_label_of{ $row->{authorised_value} } = $row->{lib};
1068 return \%notforloan_label_of;
1071 =head2 displayServers
1073 my $servers = displayServers();
1074 my $servers = displayServers( $position );
1075 my $servers = displayServers( $position, $type );
1077 displayServers returns a listref of hashrefs, each containing
1078 information about available z3950 servers. Each hashref has a format
1082 'checked' => 'checked',
1083 'encoding' => 'utf8',
1085 'id' => 'LIBRARY OF CONGRESS',
1089 'value' => 'lx2.loc.gov:210/',
1095 sub displayServers {
1096 my ( $position, $type ) = @_;
1097 my $dbh = C4::Context->dbh;
1099 my $strsth = 'SELECT * FROM z3950servers';
1104 push @bind_params, $position;
1105 push @where_clauses, ' position = ? ';
1109 push @bind_params, $type;
1110 push @where_clauses, ' type = ? ';
1113 # reassemble where clause from where clause pieces
1114 if (@where_clauses) {
1115 $strsth .= ' WHERE ' . join( ' AND ', @where_clauses );
1118 my $rq = $dbh->prepare($strsth);
1119 $rq->execute(@bind_params);
1120 my @primaryserverloop;
1122 while ( my $data = $rq->fetchrow_hashref ) {
1123 push @primaryserverloop,
1124 { label => $data->{description},
1125 id => $data->{name},
1127 value => $data->{host} . ":" . $data->{port} . "/" . $data->{database},
1128 encoding => ( $data->{encoding} ? $data->{encoding} : "iso-5426" ),
1129 checked => "checked",
1130 icon => $data->{icon},
1131 zed => $data->{type} eq 'zed',
1132 opensearch => $data->{type} eq 'opensearch'
1135 return \@primaryserverloop;
1139 =head2 GetKohaImageurlFromAuthorisedValues
1141 $authhorised_value = GetKohaImageurlFromAuthorisedValues( $category, $authvalcode );
1143 Return the first url of the authorised value image represented by $lib.
1147 sub GetKohaImageurlFromAuthorisedValues {
1148 my ( $category, $lib ) = @_;
1149 my $dbh = C4::Context->dbh;
1150 my $sth = $dbh->prepare("SELECT imageurl FROM authorised_values WHERE category=? AND lib =?");
1151 $sth->execute( $category, $lib );
1152 while ( my $data = $sth->fetchrow_hashref ) {
1153 return $data->{'imageurl'};
1157 =head2 GetAuthValCode
1159 $authvalcode = GetAuthValCode($kohafield,$frameworkcode);
1163 sub GetAuthValCode {
1164 my ($kohafield,$fwcode) = @_;
1165 my $dbh = C4::Context->dbh;
1166 $fwcode='' unless $fwcode;
1167 my $sth = $dbh->prepare('select authorised_value from marc_subfield_structure where kohafield=? and frameworkcode=?');
1168 $sth->execute($kohafield,$fwcode);
1169 my ($authvalcode) = $sth->fetchrow_array;
1170 return $authvalcode;
1173 =head2 GetAuthValCodeFromField
1175 $authvalcode = GetAuthValCodeFromField($field,$subfield,$frameworkcode);
1177 C<$subfield> can be undefined
1181 sub GetAuthValCodeFromField {
1182 my ($field,$subfield,$fwcode) = @_;
1183 my $dbh = C4::Context->dbh;
1184 $fwcode='' unless $fwcode;
1186 if (defined $subfield) {
1187 $sth = $dbh->prepare('select authorised_value from marc_subfield_structure where tagfield=? and tagsubfield=? and frameworkcode=?');
1188 $sth->execute($field,$subfield,$fwcode);
1190 $sth = $dbh->prepare('select authorised_value from marc_tag_structure where tagfield=? and frameworkcode=?');
1191 $sth->execute($field,$fwcode);
1193 my ($authvalcode) = $sth->fetchrow_array;
1194 return $authvalcode;
1197 =head2 GetAuthorisedValues
1199 $authvalues = GetAuthorisedValues([$category], [$selected]);
1201 This function returns all authorised values from the'authorised_value' table in a reference to array of hashrefs.
1203 C<$category> returns authorised values for just one category (optional).
1205 C<$selected> adds a "selected => 1" entry to the hash if the
1206 authorised_value matches it. B<NOTE:> this feature should be considered
1207 deprecated as it may be removed in the future.
1209 C<$opac> If set to a true value, displays OPAC descriptions rather than normal ones when they exist.
1213 sub GetAuthorisedValues {
1214 my ( $category, $selected, $opac ) = @_;
1216 # TODO: the "selected" feature should be replaced by a utility function
1217 # somewhere else, it doesn't belong in here. For starters it makes
1218 # caching much more complicated. Or just let the UI logic handle it, it's
1221 # Is this cached already?
1222 $opac = $opac ? 1 : 0; # normalise to be safe
1224 C4::Context->userenv ? C4::Context->userenv->{"branch"} : "";
1225 my $selected_key = defined($selected) ? $selected : '';
1227 "AuthorisedValues-$category-$selected_key-$opac-$branch_limit";
1228 my $cache = Koha::Cache->get_instance();
1229 my $result = $cache->get_from_cache($cache_key);
1230 return $result if $result;
1233 my $dbh = C4::Context->dbh;
1236 FROM authorised_values
1239 LEFT JOIN authorised_values_branches ON ( id = av_id )
1244 push @where_strings, "category = ?";
1245 push @where_args, $category;
1248 push @where_strings, "( branchcode = ? OR branchcode IS NULL )";
1249 push @where_args, $branch_limit;
1251 if(@where_strings > 0) {
1252 $query .= " WHERE " . join(" AND ", @where_strings);
1254 $query .= " GROUP BY lib";
1255 $query .= ' ORDER BY category, ' . (
1256 $opac ? 'COALESCE(lib_opac, lib)'
1260 my $sth = $dbh->prepare($query);
1262 $sth->execute( @where_args );
1263 while (my $data=$sth->fetchrow_hashref) {
1264 if ( defined $selected and $selected eq $data->{authorised_value} ) {
1265 $data->{selected} = 1;
1268 $data->{selected} = 0;
1271 if ($opac && $data->{lib_opac}) {
1272 $data->{lib} = $data->{lib_opac};
1274 push @results, $data;
1278 # We can't cache for long because of that "selected" thing which
1279 # makes it impossible to clear the cache without iterating through every
1280 # value, which sucks. This'll cover this request, and not a whole lot more.
1281 $cache->set_in_cache( $cache_key, \@results, { deepcopy => 1, expiry => 5 } );
1285 =head2 GetAuthorisedValueCategories
1287 $auth_categories = GetAuthorisedValueCategories();
1289 Return an arrayref of all of the available authorised
1294 sub GetAuthorisedValueCategories {
1295 my $dbh = C4::Context->dbh;
1296 my $sth = $dbh->prepare("SELECT DISTINCT category FROM authorised_values ORDER BY category");
1299 while (defined (my $category = $sth->fetchrow_array) ) {
1300 push @results, $category;
1305 =head2 IsAuthorisedValueCategory
1307 $is_auth_val_category = IsAuthorisedValueCategory($category);
1309 Returns whether a given category name is a valid one
1313 sub IsAuthorisedValueCategory {
1314 my $category = shift;
1317 FROM authorised_values
1321 my $sth = C4::Context->dbh->prepare($query);
1322 $sth->execute($category);
1323 $sth->fetchrow ? return 1
1327 =head2 GetAuthorisedValueByCode
1329 $authorised_value = GetAuthorisedValueByCode( $category, $authvalcode, $opac );
1331 Return the lib attribute from authorised_values from the row identified
1332 by the passed category and code
1336 sub GetAuthorisedValueByCode {
1337 my ( $category, $authvalcode, $opac ) = @_;
1339 my $field = $opac ? 'lib_opac' : 'lib';
1340 my $dbh = C4::Context->dbh;
1341 my $sth = $dbh->prepare("SELECT $field FROM authorised_values WHERE category=? AND authorised_value =?");
1342 $sth->execute( $category, $authvalcode );
1343 while ( my $data = $sth->fetchrow_hashref ) {
1344 return $data->{ $field };
1348 =head2 GetKohaAuthorisedValues
1350 Takes $kohafield, $fwcode as parameters.
1352 If $opac parameter is set to a true value, displays OPAC descriptions rather than normal ones when they exist.
1354 Returns hashref of Code => description
1356 Returns undef if no authorised value category is defined for the kohafield.
1360 sub GetKohaAuthorisedValues {
1361 my ($kohafield,$fwcode,$opac) = @_;
1362 $fwcode='' unless $fwcode;
1364 my $dbh = C4::Context->dbh;
1365 my $avcode = GetAuthValCode($kohafield,$fwcode);
1367 my $sth = $dbh->prepare("select authorised_value, lib, lib_opac from authorised_values where category=? ");
1368 $sth->execute($avcode);
1369 while ( my ($val, $lib, $lib_opac) = $sth->fetchrow_array ) {
1370 $values{$val} = ($opac && $lib_opac) ? $lib_opac : $lib;
1378 =head2 GetKohaAuthorisedValuesFromField
1380 Takes $field, $subfield, $fwcode as parameters.
1382 If $opac parameter is set to a true value, displays OPAC descriptions rather than normal ones when they exist.
1383 $subfield can be undefined
1385 Returns hashref of Code => description
1387 Returns undef if no authorised value category is defined for the given field and subfield
1391 sub GetKohaAuthorisedValuesFromField {
1392 my ($field, $subfield, $fwcode,$opac) = @_;
1393 $fwcode='' unless $fwcode;
1395 my $dbh = C4::Context->dbh;
1396 my $avcode = GetAuthValCodeFromField($field, $subfield, $fwcode);
1398 my $sth = $dbh->prepare("select authorised_value, lib, lib_opac from authorised_values where category=? ");
1399 $sth->execute($avcode);
1400 while ( my ($val, $lib, $lib_opac) = $sth->fetchrow_array ) {
1401 $values{$val} = ($opac && $lib_opac) ? $lib_opac : $lib;
1409 =head2 GetKohaAuthorisedValuesMapping
1411 Takes a hash as a parameter. The interface key indicates the
1412 description to use in the mapping.
1415 "{kohafield},{frameworkcode},{authorised_value}" => "{description}"
1416 for all the kohafields, frameworkcodes, and authorised values.
1418 Returns undef if nothing is found.
1422 sub GetKohaAuthorisedValuesMapping {
1423 my ($parameter) = @_;
1424 my $interface = $parameter->{'interface'} // '';
1426 my $query_mapping = q{
1427 SELECT TA.kohafield,TA.authorised_value AS category,
1428 TA.frameworkcode,TB.authorised_value,
1429 IF(TB.lib_opac>'',TB.lib_opac,TB.lib) AS OPAC,
1430 TB.lib AS Intranet,TB.lib_opac
1431 FROM marc_subfield_structure AS TA JOIN
1432 authorised_values as TB ON
1433 TA.authorised_value=TB.category
1434 WHERE TA.kohafield>'' AND TA.authorised_value>'';
1436 my $dbh = C4::Context->dbh;
1437 my $sth = $dbh->prepare($query_mapping);
1440 if ($interface eq 'opac') {
1441 while (my $row = $sth->fetchrow_hashref) {
1442 $avmapping->{$row->{kohafield}.",".$row->{frameworkcode}.",".$row->{authorised_value}} = $row->{OPAC};
1446 while (my $row = $sth->fetchrow_hashref) {
1447 $avmapping->{$row->{kohafield}.",".$row->{frameworkcode}.",".$row->{authorised_value}} = $row->{Intranet};
1455 my $escaped_string = C4::Koha::xml_escape($string);
1457 Convert &, <, >, ', and " in a string to XML entities
1463 return '' unless defined $str;
1464 $str =~ s/&/&/g;
1467 $str =~ s/'/'/g;
1468 $str =~ s/"/"/g;
1472 =head2 GetKohaAuthorisedValueLib
1474 Takes $category, $authorised_value as parameters.
1476 If $opac parameter is set to a true value, displays OPAC descriptions rather than normal ones when they exist.
1478 Returns authorised value description
1482 sub GetKohaAuthorisedValueLib {
1483 my ($category,$authorised_value,$opac) = @_;
1485 my $dbh = C4::Context->dbh;
1486 my $sth = $dbh->prepare("select lib, lib_opac from authorised_values where category=? and authorised_value=?");
1487 $sth->execute($category,$authorised_value);
1488 my $data = $sth->fetchrow_hashref;
1489 $value = ($opac && $$data{'lib_opac'}) ? $$data{'lib_opac'} : $$data{'lib'};
1493 =head2 AddAuthorisedValue
1495 AddAuthorisedValue($category, $authorised_value, $lib, $lib_opac, $imageurl);
1497 Create a new authorised value.
1501 sub AddAuthorisedValue {
1502 my ($category, $authorised_value, $lib, $lib_opac, $imageurl) = @_;
1504 my $dbh = C4::Context->dbh;
1506 INSERT INTO authorised_values (category, authorised_value, lib, lib_opac, imageurl)
1509 my $sth = $dbh->prepare($query);
1510 $sth->execute($category, $authorised_value, $lib, $lib_opac, $imageurl);
1513 =head2 display_marc_indicators
1515 my $display_form = C4::Koha::display_marc_indicators($field);
1517 C<$field> is a MARC::Field object
1519 Generate a display form of the indicators of a variable
1520 MARC field, replacing any blanks with '#'.
1524 sub display_marc_indicators {
1526 my $indicators = '';
1527 if ($field->tag() >= 10) {
1528 $indicators = $field->indicator(1) . $field->indicator(2);
1529 $indicators =~ s/ /#/g;
1534 sub GetNormalizedUPC {
1535 my ($record,$marcflavour) = @_;
1538 if ($marcflavour eq 'UNIMARC') {
1539 @fields = $record->field('072');
1540 foreach my $field (@fields) {
1541 my $upc = _normalize_match_point($field->subfield('a'));
1548 else { # assume marc21 if not unimarc
1549 @fields = $record->field('024');
1550 foreach my $field (@fields) {
1551 my $indicator = $field->indicator(1);
1552 my $upc = _normalize_match_point($field->subfield('a'));
1553 if ($indicator == 1 and $upc ne '') {
1560 # Normalizes and returns the first valid ISBN found in the record
1561 # ISBN13 are converted into ISBN10. This is required to get some book cover images.
1562 sub GetNormalizedISBN {
1563 my ($isbn,$record,$marcflavour) = @_;
1566 # Koha attempts to store multiple ISBNs in biblioitems.isbn, separated by " | "
1567 # anything after " | " should be removed, along with the delimiter
1568 ($isbn) = split(/\|/, $isbn );
1569 return _isbn_cleanup($isbn);
1571 return unless $record;
1573 if ($marcflavour eq 'UNIMARC') {
1574 @fields = $record->field('010');
1575 foreach my $field (@fields) {
1576 my $isbn = $field->subfield('a');
1578 return _isbn_cleanup($isbn);
1584 else { # assume marc21 if not unimarc
1585 @fields = $record->field('020');
1586 foreach my $field (@fields) {
1587 $isbn = $field->subfield('a');
1589 return _isbn_cleanup($isbn);
1597 sub GetNormalizedEAN {
1598 my ($record,$marcflavour) = @_;
1601 if ($marcflavour eq 'UNIMARC') {
1602 @fields = $record->field('073');
1603 foreach my $field (@fields) {
1604 $ean = _normalize_match_point($field->subfield('a'));
1610 else { # assume marc21 if not unimarc
1611 @fields = $record->field('024');
1612 foreach my $field (@fields) {
1613 my $indicator = $field->indicator(1);
1614 $ean = _normalize_match_point($field->subfield('a'));
1615 if ($indicator == 3 and $ean ne '') {
1621 sub GetNormalizedOCLCNumber {
1622 my ($record,$marcflavour) = @_;
1625 if ($marcflavour eq 'UNIMARC') {
1626 # TODO: add UNIMARC fields
1628 else { # assume marc21 if not unimarc
1629 @fields = $record->field('035');
1630 foreach my $field (@fields) {
1631 $oclc = $field->subfield('a');
1632 if ($oclc =~ /OCoLC/) {
1633 $oclc =~ s/\(OCoLC\)//;
1642 sub GetAuthvalueDropbox {
1643 my ( $authcat, $default ) = @_;
1644 my $branch_limit = C4::Context->userenv ? C4::Context->userenv->{"branch"} : "";
1645 my $dbh = C4::Context->dbh;
1649 FROM authorised_values
1652 LEFT JOIN authorised_values_branches ON ( id = av_id )
1657 $query .= " AND ( branchcode = ? OR branchcode IS NULL )" if $branch_limit;
1658 $query .= " GROUP BY lib ORDER BY category, lib, lib_opac";
1659 my $sth = $dbh->prepare($query);
1660 $sth->execute( $authcat, $branch_limit ? $branch_limit : () );
1663 my $option_list = [];
1664 my @authorised_values = ( q{} );
1665 while (my $av = $sth->fetchrow_hashref) {
1666 push @{$option_list}, {
1667 value => $av->{authorised_value},
1668 label => $av->{lib},
1669 default => ($default eq $av->{authorised_value}),
1673 if ( @{$option_list} ) {
1674 return $option_list;
1680 =head2 GetDailyQuote($opts)
1682 Takes a hashref of options
1684 Currently supported options are:
1686 'id' An exact quote id
1687 'random' Select a random quote
1688 noop When no option is passed in, this sub will return the quote timestamped for the current day
1690 The function returns an anonymous hash following this format:
1693 'source' => 'source-of-quote',
1694 'timestamp' => 'timestamp-value',
1695 'text' => 'text-of-quote',
1701 # This is definitely a candidate for some sort of caching once we finally settle caching/persistence issues...
1702 # at least for default option
1706 my $dbh = C4::Context->dbh;
1711 $query = 'SELECT * FROM quotes WHERE id = ?';
1712 $sth = $dbh->prepare($query);
1713 $sth->execute($opts{'id'});
1714 $quote = $sth->fetchrow_hashref();
1716 elsif ($opts{'random'}) {
1717 # Fall through... we also return a random quote as a catch-all if all else fails
1720 $query = 'SELECT * FROM quotes WHERE timestamp LIKE CONCAT(CURRENT_DATE,\'%\') ORDER BY timestamp DESC LIMIT 0,1';
1721 $sth = $dbh->prepare($query);
1723 $quote = $sth->fetchrow_hashref();
1725 unless ($quote) { # if there are not matches, choose a random quote
1726 # get a list of all available quote ids
1727 $sth = C4::Context->dbh->prepare('SELECT count(*) FROM quotes;');
1729 my $range = ($sth->fetchrow_array)[0];
1730 # chose a random id within that range if there is more than one quote
1731 my $offset = int(rand($range));
1733 $query = 'SELECT * FROM quotes ORDER BY id LIMIT 1 OFFSET ?';
1734 $sth = C4::Context->dbh->prepare($query);
1735 # see http://www.perlmonks.org/?node_id=837422 for why
1736 # we're being verbose and using bind_param
1737 $sth->bind_param(1, $offset, SQL_INTEGER);
1739 $quote = $sth->fetchrow_hashref();
1740 # update the timestamp for that quote
1741 $query = 'UPDATE quotes SET timestamp = ? WHERE id = ?';
1742 $sth = C4::Context->dbh->prepare($query);
1744 DateTime::Format::MySQL->format_datetime( dt_from_string() ),
1751 sub _normalize_match_point {
1752 my $match_point = shift;
1753 (my $normalized_match_point) = $match_point =~ /([\d-]*[X]*)/;
1754 $normalized_match_point =~ s/-//g;
1756 return $normalized_match_point;
1761 return NormalizeISBN(
1764 format => 'ISBN-10',
1770 =head2 NormalizedISBN
1772 my $isbns = NormalizedISBN({
1774 strip_hyphens => [0,1],
1775 format => ['ISBN-10', 'ISBN-13']
1778 Returns an isbn validated by Business::ISBN.
1779 Optionally strips hyphens and/or forces the isbn
1780 to be of the specified format.
1782 If the string cannot be validated as an isbn,
1790 my $string = $params->{isbn};
1791 my $strip_hyphens = $params->{strip_hyphens};
1792 my $format = $params->{format};
1794 return unless $string;
1796 my $isbn = Business::ISBN->new($string);
1798 if ( $isbn && $isbn->is_valid() ) {
1800 if ( $format eq 'ISBN-10' ) {
1801 $isbn = $isbn->as_isbn10();
1803 elsif ( $format eq 'ISBN-13' ) {
1804 $isbn = $isbn->as_isbn13();
1806 return unless $isbn;
1808 if ($strip_hyphens) {
1809 $string = $isbn->as_string( [] );
1811 $string = $isbn->as_string();
1818 =head2 GetVariationsOfISBN
1820 my @isbns = GetVariationsOfISBN( $isbn );
1822 Returns a list of variations of the given isbn in
1823 both ISBN-10 and ISBN-13 formats, with and without
1826 In a scalar context, the isbns are returned as a
1827 string delimited by ' | '.
1831 sub GetVariationsOfISBN {
1834 return unless $isbn;
1838 push( @isbns, NormalizeISBN({ isbn => $isbn }) );
1839 push( @isbns, NormalizeISBN({ isbn => $isbn, format => 'ISBN-10' }) );
1840 push( @isbns, NormalizeISBN({ isbn => $isbn, format => 'ISBN-13' }) );
1841 push( @isbns, NormalizeISBN({ isbn => $isbn, format => 'ISBN-10', strip_hyphens => 1 }) );
1842 push( @isbns, NormalizeISBN({ isbn => $isbn, format => 'ISBN-13', strip_hyphens => 1 }) );
1844 # Strip out any "empty" strings from the array
1845 @isbns = grep { defined($_) && $_ =~ /\S/ } @isbns;
1847 return wantarray ? @isbns : join( " | ", @isbns );
1850 =head2 GetVariationsOfISBNs
1852 my @isbns = GetVariationsOfISBNs( @isbns );
1854 Returns a list of variations of the given isbns in
1855 both ISBN-10 and ISBN-13 formats, with and without
1858 In a scalar context, the isbns are returned as a
1859 string delimited by ' | '.
1863 sub GetVariationsOfISBNs {
1866 @isbns = map { GetVariationsOfISBN( $_ ) } @isbns;
1868 return wantarray ? @isbns : join( " | ", @isbns );
1871 =head2 IsKohaFieldLinked
1873 my $is_linked = IsKohaFieldLinked({
1874 kohafield => $kohafield,
1875 frameworkcode => $frameworkcode,
1878 Return 1 if the field is linked
1882 sub IsKohaFieldLinked {
1883 my ( $params ) = @_;
1884 my $kohafield = $params->{kohafield};
1885 my $frameworkcode = $params->{frameworkcode} || '';
1886 my $dbh = C4::Context->dbh;
1887 my $is_linked = $dbh->selectcol_arrayref( q|
1889 FROM marc_subfield_structure
1890 WHERE frameworkcode = ?
1892 |,{}, $frameworkcode, $kohafield );
1893 return $is_linked->[0];