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; # Can be removed?
29 use Koha::DateUtils qw(dt_from_string);
31 use DateTime::Format::MySQL;
33 use autouse 'Data::cselectall_arrayref' => qw(Dumper);
34 use DBI qw(:sql_types);
35 use vars qw(@ISA @EXPORT @EXPORT_OK $DEBUG);
38 $VERSION = 3.07.00.049;
42 &GetPrinters &GetPrinter
43 &GetItemTypes &getitemtypeinfo
44 &GetItemTypesCategorized &GetItemTypesByCategory
45 &GetSupportName &GetSupportList
46 &getframeworks &getframeworkinfo
52 &get_notforloan_label_of
55 &getitemtypeimagelocation
57 &GetAuthorisedValueCategories
58 &GetKohaAuthorisedValues
59 &GetKohaAuthorisedValuesFromField
60 &GetKohaAuthorisedValuesMapping
61 &GetKohaAuthorisedValueLib
62 &GetAuthorisedValueByCode
67 &GetNormalizedOCLCNumber
77 @EXPORT_OK = qw( GetDailyQuote );
82 C4::Koha - Perl Module containing convenience functions for Koha scripts
90 Koha.pm provides many functions for Koha scripts.
98 $itemtypename = &GetSupportName($codestring);
100 Returns a string with the name of the itemtype.
106 return if (! $codestring);
108 my $advanced_search_types = C4::Context->preference("AdvancedSearchTypes");
109 if (!$advanced_search_types or $advanced_search_types eq 'itemtypes') {
116 my $sth = C4::Context->dbh->prepare($query);
117 $sth->execute($codestring);
118 ($resultstring)=$sth->fetchrow;
119 return $resultstring;
122 C4::Context->dbh->prepare(
123 "SELECT lib FROM authorised_values WHERE category = ? AND authorised_value = ?"
125 $sth->execute( $advanced_search_types, $codestring );
126 my $data = $sth->fetchrow_hashref;
127 return $$data{'lib'};
131 =head2 GetSupportList
133 $itemtypes = &GetSupportList();
135 Returns an array ref containing informations about Support (since itemtype is rather a circulation code when item-level-itypes is used).
137 build a HTML select with the following code :
139 =head3 in PERL SCRIPT
141 my $itemtypes = GetSupportList();
142 $template->param(itemtypeloop => $itemtypes);
146 <select name="itemtype" id="itemtype">
147 <option value=""></option>
148 [% FOREACH itemtypeloo IN itemtypeloop %]
149 [% IF ( itemtypeloo.selected ) %]
150 <option value="[% itemtypeloo.itemtype %]" selected="selected">[% itemtypeloo.description %]</option>
152 <option value="[% itemtypeloo.itemtype %]">[% itemtypeloo.description %]</option>
160 my $advanced_search_types = C4::Context->preference("AdvancedSearchTypes");
161 if (!$advanced_search_types or $advanced_search_types =~ /itemtypes/) {
162 return GetItemTypes( style => 'array' );
164 my $advsearchtypes = GetAuthorisedValues($advanced_search_types);
165 my @results= map {{itemtype=>$$_{authorised_value},description=>$$_{lib},imageurl=>$$_{imageurl}}} @$advsearchtypes;
171 $itemtypes = &GetItemTypes( style => $style );
173 Returns information about existing itemtypes.
176 style: either 'array' or 'hash', defaults to 'hash'.
177 'array' returns an arrayref,
178 'hash' return a hashref with the itemtype value as the key
180 build a HTML select with the following code :
182 =head3 in PERL SCRIPT
184 my $itemtypes = GetItemTypes;
186 foreach my $thisitemtype (sort keys %$itemtypes) {
187 my $selected = 1 if $thisitemtype eq $itemtype;
188 my %row =(value => $thisitemtype,
189 selected => $selected,
190 description => $itemtypes->{$thisitemtype}->{'description'},
192 push @itemtypesloop, \%row;
194 $template->param(itemtypeloop => \@itemtypesloop);
198 <form action='<!-- TMPL_VAR name="script_name" -->' method=post>
199 <select name="itemtype">
200 <option value="">Default</option>
201 <!-- TMPL_LOOP name="itemtypeloop" -->
202 <option value="<!-- TMPL_VAR name="value" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="description" --></option>
205 <input type=text name=searchfield value="<!-- TMPL_VAR name="searchfield" -->">
206 <input type="submit" value="OK" class="button">
213 my $style = defined( $params{'style'} ) ? $params{'style'} : 'hash';
215 require C4::Languages;
216 my $language = C4::Languages::getlanguage();
217 # returns a reference to a hash of references to itemtypes...
218 my $dbh = C4::Context->dbh;
222 itemtypes.description,
223 itemtypes.rentalcharge,
224 itemtypes.notforloan,
227 itemtypes.checkinmsg,
228 itemtypes.checkinmsgtype,
229 itemtypes.sip_media_type,
230 itemtypes.hideinopac,
231 itemtypes.searchcategory,
232 COALESCE( localization.translation, itemtypes.description ) AS translated_description
234 LEFT JOIN localization ON itemtypes.itemtype = localization.code
235 AND localization.entity = 'itemtypes'
236 AND localization.lang = ?
239 my $sth = $dbh->prepare($query);
240 $sth->execute( $language );
242 if ( $style eq 'hash' ) {
244 while ( my $IT = $sth->fetchrow_hashref ) {
245 $itemtypes{ $IT->{'itemtype'} } = $IT;
247 return ( \%itemtypes );
249 return [ sort { lc $a->{translated_description} cmp lc $b->{translated_description} } @{ $sth->fetchall_arrayref( {} ) } ];
253 =head2 GetItemTypesCategorized
255 $categories = GetItemTypesCategorized();
257 Returns a hashref containing search categories.
258 A search category will be put in the hash if at least one of its itemtypes is visible in OPAC.
259 The categories must be part of Authorized Values (ITEMTYPECAT)
263 sub GetItemTypesCategorized {
264 my $dbh = C4::Context->dbh;
265 # Order is important, so that partially hidden (some items are not visible in OPAC) search
266 # categories will be visible. hideinopac=0 must be last.
268 SELECT itemtype, description, imageurl, hideinopac, 0 as 'iscat' FROM itemtypes WHERE ISNULL(searchcategory) or length(searchcategory) = 0
270 SELECT DISTINCT searchcategory AS `itemtype`,
271 authorised_values.lib_opac AS description,
272 authorised_values.imageurl AS imageurl,
273 hideinopac, 1 as 'iscat'
275 LEFT JOIN authorised_values ON searchcategory = authorised_value
276 WHERE searchcategory > '' and hideinopac=1
278 SELECT DISTINCT searchcategory AS `itemtype`,
279 authorised_values.lib_opac AS description,
280 authorised_values.imageurl AS imageurl,
281 hideinopac, 1 as 'iscat'
283 LEFT JOIN authorised_values ON searchcategory = authorised_value
284 WHERE searchcategory > '' and hideinopac=0
286 return ($dbh->selectall_hashref($query,'itemtype'));
289 =head2 GetItemTypesByCategory
291 @results = GetItemTypesByCategory( $searchcategory );
293 Returns the itemtype code of all itemtypes included in a searchcategory.
297 sub GetItemTypesByCategory {
301 my $dbh = C4::Context->dbh;
302 my $query = qq|SELECT itemtype FROM itemtypes WHERE searchcategory=?|;
303 my $tmp=$dbh->selectcol_arrayref($query,undef,$category);
309 $frameworks = &getframework();
311 Returns information about existing frameworks
313 build a HTML select with the following code :
315 =head3 in PERL SCRIPT
317 my $frameworks = getframeworks();
319 foreach my $thisframework (keys %$frameworks) {
320 my $selected = 1 if $thisframework eq $frameworkcode;
322 value => $thisframework,
323 selected => $selected,
324 description => $frameworks->{$thisframework}->{'frameworktext'},
326 push @frameworksloop, \%row;
328 $template->param(frameworkloop => \@frameworksloop);
332 <form action="[% script_name %] method=post>
333 <select name="frameworkcode">
334 <option value="">Default</option>
335 [% FOREACH framework IN frameworkloop %]
336 [% IF ( framework.selected ) %]
337 <option value="[% framework.value %]" selected="selected">[% framework.description %]</option>
339 <option value="[% framework.value %]">[% framework.description %]</option>
343 <input type=text name=searchfield value="[% searchfield %]">
344 <input type="submit" value="OK" class="button">
351 # returns a reference to a hash of references to branches...
353 my $dbh = C4::Context->dbh;
354 my $sth = $dbh->prepare("select * from biblio_framework");
356 while ( my $IT = $sth->fetchrow_hashref ) {
357 $itemtypes{ $IT->{'frameworkcode'} } = $IT;
359 return ( \%itemtypes );
362 =head2 GetFrameworksLoop
364 $frameworks = GetFrameworksLoop( $frameworkcode );
366 Returns the loop suggested on getframework(), but ordered by framework description.
368 build a HTML select with the following code :
370 =head3 in PERL SCRIPT
372 $template->param( frameworkloop => GetFrameworksLoop( $frameworkcode ) );
376 Same as getframework()
378 <form action="[% script_name %] method=post>
379 <select name="frameworkcode">
380 <option value="">Default</option>
381 [% FOREACH framework IN frameworkloop %]
382 [% IF ( framework.selected ) %]
383 <option value="[% framework.value %]" selected="selected">[% framework.description %]</option>
385 <option value="[% framework.value %]">[% framework.description %]</option>
389 <input type=text name=searchfield value="[% searchfield %]">
390 <input type="submit" value="OK" class="button">
395 sub GetFrameworksLoop {
396 my $frameworkcode = shift;
397 my $frameworks = getframeworks();
399 foreach my $thisframework (sort { uc($frameworks->{$a}->{'frameworktext'}) cmp uc($frameworks->{$b}->{'frameworktext'}) } keys %$frameworks) {
400 my $selected = ( $thisframework eq $frameworkcode ) ? 1 : undef;
402 value => $thisframework,
403 selected => $selected,
404 description => $frameworks->{$thisframework}->{'frameworktext'},
406 push @frameworkloop, \%row;
408 return \@frameworkloop;
411 =head2 getframeworkinfo
413 $frameworkinfo = &getframeworkinfo($frameworkcode);
415 Returns information about an frameworkcode.
419 sub getframeworkinfo {
420 my ($frameworkcode) = @_;
421 my $dbh = C4::Context->dbh;
423 $dbh->prepare("select * from biblio_framework where frameworkcode=?");
424 $sth->execute($frameworkcode);
425 my $res = $sth->fetchrow_hashref;
429 =head2 getitemtypeinfo
431 $itemtype = &getitemtypeinfo($itemtype, [$interface]);
433 Returns information about an itemtype. The optional $interface argument
434 sets which interface ('opac' or 'intranet') to return the imageurl for.
435 Defaults to intranet.
439 sub getitemtypeinfo {
440 my ($itemtype, $interface) = @_;
441 my $dbh = C4::Context->dbh;
442 require C4::Languages;
443 my $language = C4::Languages::getlanguage();
444 my $it = $dbh->selectrow_hashref(q|
447 itemtypes.description,
448 itemtypes.rentalcharge,
449 itemtypes.notforloan,
452 itemtypes.checkinmsg,
453 itemtypes.checkinmsgtype,
454 itemtypes.sip_media_type,
455 COALESCE( localization.translation, itemtypes.description ) AS translated_description
457 LEFT JOIN localization ON itemtypes.itemtype = localization.code
458 AND localization.entity = 'itemtypes'
459 AND localization.lang = ?
460 WHERE itemtypes.itemtype = ?
461 |, undef, $language, $itemtype );
463 $it->{imageurl} = getitemtypeimagelocation( ( ( defined $interface && $interface eq 'opac' ) ? 'opac' : 'intranet' ), $it->{imageurl} );
468 =head2 getitemtypeimagedir
470 my $directory = getitemtypeimagedir( 'opac' );
472 pass in 'opac' or 'intranet'. Defaults to 'opac'.
474 returns the full path to the appropriate directory containing images.
478 sub getitemtypeimagedir {
479 my $src = shift || 'opac';
480 if ($src eq 'intranet') {
481 return C4::Context->config('intrahtdocs') . '/' .C4::Context->preference('template') . '/img/itemtypeimg';
483 return C4::Context->config('opachtdocs') . '/' . C4::Context->preference('opacthemes') . '/itemtypeimg';
487 sub getitemtypeimagesrc {
488 my $src = shift || 'opac';
489 if ($src eq 'intranet') {
490 return '/intranet-tmpl' . '/' . C4::Context->preference('template') . '/img/itemtypeimg';
492 return '/opac-tmpl' . '/' . C4::Context->preference('opacthemes') . '/itemtypeimg';
496 sub getitemtypeimagelocation {
497 my ( $src, $image ) = @_;
499 return '' if ( !$image );
502 my $scheme = ( URI::Split::uri_split( $image ) )[0];
504 return $image if ( $scheme );
506 return getitemtypeimagesrc( $src ) . '/' . $image;
509 =head3 _getImagesFromDirectory
511 Find all of the image files in a directory in the filesystem
513 parameters: a directory name
515 returns: a list of images in that directory.
517 Notes: this does not traverse into subdirectories. See
518 _getSubdirectoryNames for help with that.
519 Images are assumed to be files with .gif or .png file extensions.
520 The image names returned do not have the directory name on them.
524 sub _getImagesFromDirectory {
525 my $directoryname = shift;
526 return unless defined $directoryname;
527 return unless -d $directoryname;
529 if ( opendir ( my $dh, $directoryname ) ) {
530 my @images = grep { /\.(gif|png)$/i } readdir( $dh );
532 @images = sort(@images);
535 warn "unable to opendir $directoryname: $!";
540 =head3 _getSubdirectoryNames
542 Find all of the directories in a directory in the filesystem
544 parameters: a directory name
546 returns: a list of subdirectories in that directory.
548 Notes: this does not traverse into subdirectories. Only the first
549 level of subdirectories are returned.
550 The directory names returned don't have the parent directory name on them.
554 sub _getSubdirectoryNames {
555 my $directoryname = shift;
556 return unless defined $directoryname;
557 return unless -d $directoryname;
559 if ( opendir ( my $dh, $directoryname ) ) {
560 my @directories = grep { -d File::Spec->catfile( $directoryname, $_ ) && ! ( /^\./ ) } readdir( $dh );
564 warn "unable to opendir $directoryname: $!";
571 returns: a listref of hashrefs. Each hash represents another collection of images.
573 { imagesetname => 'npl', # the name of the image set (npl is the original one)
574 images => listref of image hashrefs
577 each image is represented by a hashref like this:
579 { KohaImage => 'npl/image.gif',
580 StaffImageUrl => '/intranet-tmpl/prog/img/itemtypeimg/npl/image.gif',
581 OpacImageURL => '/opac-tmpl/prog/itemtypeimg/npl/image.gif'
582 checked => 0 or 1: was this the image passed to this method?
583 Note: I'd like to remove this somehow.
590 my $checked = $params{'checked'} || '';
592 my $paths = { staff => { filesystem => getitemtypeimagedir('intranet'),
593 url => getitemtypeimagesrc('intranet'),
595 opac => { filesystem => getitemtypeimagedir('opac'),
596 url => getitemtypeimagesrc('opac'),
600 my @imagesets = (); # list of hasrefs of image set data to pass to template
601 my @subdirectories = _getSubdirectoryNames( $paths->{'staff'}{'filesystem'} );
602 foreach my $imagesubdir ( @subdirectories ) {
603 warn $imagesubdir if $DEBUG;
604 my @imagelist = (); # hashrefs of image info
605 my @imagenames = _getImagesFromDirectory( File::Spec->catfile( $paths->{'staff'}{'filesystem'}, $imagesubdir ) );
606 my $imagesetactive = 0;
607 foreach my $thisimage ( @imagenames ) {
609 { KohaImage => "$imagesubdir/$thisimage",
610 StaffImageUrl => join( '/', $paths->{'staff'}{'url'}, $imagesubdir, $thisimage ),
611 OpacImageUrl => join( '/', $paths->{'opac'}{'url'}, $imagesubdir, $thisimage ),
612 checked => "$imagesubdir/$thisimage" eq $checked ? 1 : 0,
615 $imagesetactive = 1 if "$imagesubdir/$thisimage" eq $checked;
617 push @imagesets, { imagesetname => $imagesubdir,
618 imagesetactive => $imagesetactive,
619 images => \@imagelist };
627 $printers = &GetPrinters();
628 @queues = keys %$printers;
630 Returns information about existing printer queues.
632 C<$printers> is a reference-to-hash whose keys are the print queues
633 defined in the printers table of the Koha database. The values are
634 references-to-hash, whose keys are the fields in the printers table.
640 my $dbh = C4::Context->dbh;
641 my $sth = $dbh->prepare("select * from printers");
643 while ( my $printer = $sth->fetchrow_hashref ) {
644 $printers{ $printer->{'printqueue'} } = $printer;
646 return ( \%printers );
651 $printer = GetPrinter( $query, $printers );
656 my ( $query, $printers ) = @_; # get printer for this query from printers
657 my $printer = $query->param('printer');
658 my %cookie = $query->cookie('userenv');
659 ($printer) || ( $printer = $cookie{'printer'} ) || ( $printer = '' );
660 ( $printers->{$printer} ) || ( $printer = ( keys %$printers )[0] );
666 Returns the number of pages to display in a pagination bar, given the number
667 of items and the number of items per page.
672 my ( $nb_items, $nb_items_per_page ) = @_;
674 return int( ( $nb_items - 1 ) / $nb_items_per_page ) + 1;
679 (@themes) = &getallthemes('opac');
680 (@themes) = &getallthemes('intranet');
682 Returns an array of all available themes.
690 if ( $type eq 'intranet' ) {
691 $htdocs = C4::Context->config('intrahtdocs');
694 $htdocs = C4::Context->config('opachtdocs');
696 opendir D, "$htdocs";
697 my @dirlist = readdir D;
698 foreach my $directory (@dirlist) {
699 next if $directory eq 'lib';
700 -d "$htdocs/$directory/en" and push @themes, $directory;
707 if ( C4::Context->preference("marcflavour") eq "UNIMARC" ) {
712 tags => [ qw/ 600ab 601ab 602a 604at 605a 606ax 610a / ],
718 tags => [ qw/ 607a / ],
724 tags => [ qw/ 500a 501a 503a / ],
730 tags => [ qw/ 700ab 701ab 702ab / ],
731 sep => C4::Context->preference("UNIMARCAuthorsFacetsSeparator"),
736 tags => [ qw/ 225a / ],
742 tags => [ qw/ 995e / ],
746 unless ( Koha::Libraries->search->count == 1 )
748 my $DisplayLibraryFacets = C4::Context->preference('DisplayLibraryFacets');
749 if ( $DisplayLibraryFacets eq 'both'
750 || $DisplayLibraryFacets eq 'holding' )
755 idx => 'holdingbranch',
756 label => 'HoldingLibrary',
757 tags => [qw / 995c /],
762 if ( $DisplayLibraryFacets eq 'both'
763 || $DisplayLibraryFacets eq 'home' )
769 label => 'HomeLibrary',
770 tags => [qw / 995b /],
781 tags => [ qw/ 650a / ],
786 # label => 'People and Organizations',
787 # tags => [ qw/ 600a 610a 611a / ],
793 tags => [ qw/ 651a / ],
799 tags => [ qw/ 630a / ],
805 tags => [ qw/ 100a 110a 700a / ],
811 tags => [ qw/ 440a 490a / ],
816 label => 'ItemTypes',
817 tags => [ qw/ 952y 942c / ],
823 tags => [ qw / 952c / ],
827 unless ( Koha::Libraries->search->count == 1 )
829 my $DisplayLibraryFacets = C4::Context->preference('DisplayLibraryFacets');
830 if ( $DisplayLibraryFacets eq 'both'
831 || $DisplayLibraryFacets eq 'holding' )
836 idx => 'holdingbranch',
837 label => 'HoldingLibrary',
838 tags => [qw / 952b /],
843 if ( $DisplayLibraryFacets eq 'both'
844 || $DisplayLibraryFacets eq 'home' )
850 label => 'HomeLibrary',
851 tags => [qw / 952a /],
862 Return a href where a key is associated to a href. You give a query,
863 the name of the key among the fields returned by the query. If you
864 also give as third argument the name of the value, the function
865 returns a href of scalar. The optional 4th argument is an arrayref of
866 items passed to the C<execute()> call. It is designed to bind
867 parameters to any placeholders in your SQL.
876 # generic href of any information on the item, href of href.
877 my $iteminfos_of = get_infos_of($query, 'itemnumber');
878 print $iteminfos_of->{$itemnumber}{barcode};
880 # specific information, href of scalar
881 my $barcode_of_item = get_infos_of($query, 'itemnumber', 'barcode');
882 print $barcode_of_item->{$itemnumber};
887 my ( $query, $key_name, $value_name, $bind_params ) = @_;
889 my $dbh = C4::Context->dbh;
891 my $sth = $dbh->prepare($query);
892 $sth->execute( @$bind_params );
895 while ( my $row = $sth->fetchrow_hashref ) {
896 if ( defined $value_name ) {
897 $infos_of{ $row->{$key_name} } = $row->{$value_name};
900 $infos_of{ $row->{$key_name} } = $row;
908 =head2 get_notforloan_label_of
910 my $notforloan_label_of = get_notforloan_label_of();
912 Each authorised value of notforloan (information available in items and
913 itemtypes) is link to a single label.
915 Returns a href where keys are authorised values and values are corresponding
918 foreach my $authorised_value (keys %{$notforloan_label_of}) {
920 "authorised_value: %s => %s\n",
922 $notforloan_label_of->{$authorised_value}
928 # FIXME - why not use GetAuthorisedValues ??
930 sub get_notforloan_label_of {
931 my $dbh = C4::Context->dbh;
934 SELECT authorised_value
935 FROM marc_subfield_structure
936 WHERE kohafield = \'items.notforloan\'
939 my $sth = $dbh->prepare($query);
941 my ($statuscode) = $sth->fetchrow_array();
946 FROM authorised_values
949 $sth = $dbh->prepare($query);
950 $sth->execute($statuscode);
951 my %notforloan_label_of;
952 while ( my $row = $sth->fetchrow_hashref ) {
953 $notforloan_label_of{ $row->{authorised_value} } = $row->{lib};
957 return \%notforloan_label_of;
960 =head2 GetAuthValCode
962 $authvalcode = GetAuthValCode($kohafield,$frameworkcode);
967 my ($kohafield,$fwcode) = @_;
968 my $dbh = C4::Context->dbh;
969 $fwcode='' unless $fwcode;
970 my $sth = $dbh->prepare('select authorised_value from marc_subfield_structure where kohafield=? and frameworkcode=?');
971 $sth->execute($kohafield,$fwcode);
972 my ($authvalcode) = $sth->fetchrow_array;
976 =head2 GetAuthValCodeFromField
978 $authvalcode = GetAuthValCodeFromField($field,$subfield,$frameworkcode);
980 C<$subfield> can be undefined
984 sub GetAuthValCodeFromField {
985 my ($field,$subfield,$fwcode) = @_;
986 my $dbh = C4::Context->dbh;
987 $fwcode='' unless $fwcode;
989 if (defined $subfield) {
990 $sth = $dbh->prepare('select authorised_value from marc_subfield_structure where tagfield=? and tagsubfield=? and frameworkcode=?');
991 $sth->execute($field,$subfield,$fwcode);
993 $sth = $dbh->prepare('select authorised_value from marc_tag_structure where tagfield=? and frameworkcode=?');
994 $sth->execute($field,$fwcode);
996 my ($authvalcode) = $sth->fetchrow_array;
1000 =head2 GetAuthorisedValues
1002 $authvalues = GetAuthorisedValues([$category], [$selected]);
1004 This function returns all authorised values from the'authorised_value' table in a reference to array of hashrefs.
1006 C<$category> returns authorised values for just one category (optional).
1008 C<$selected> adds a "selected => 1" entry to the hash if the
1009 authorised_value matches it. B<NOTE:> this feature should be considered
1010 deprecated as it may be removed in the future.
1012 C<$opac> If set to a true value, displays OPAC descriptions rather than normal ones when they exist.
1016 sub GetAuthorisedValues {
1017 my ( $category, $selected, $opac ) = @_;
1019 # TODO: the "selected" feature should be replaced by a utility function
1020 # somewhere else, it doesn't belong in here. For starters it makes
1021 # caching much more complicated. Or just let the UI logic handle it, it's
1024 # Is this cached already?
1025 $opac = $opac ? 1 : 0; # normalise to be safe
1027 C4::Context->userenv ? C4::Context->userenv->{"branch"} : "";
1028 my $selected_key = defined($selected) ? $selected : '';
1030 "AuthorisedValues-$category-$selected_key-$opac-$branch_limit";
1031 my $cache = Koha::Cache->get_instance();
1032 my $result = $cache->get_from_cache($cache_key);
1033 return $result if $result;
1036 my $dbh = C4::Context->dbh;
1039 FROM authorised_values
1042 LEFT JOIN authorised_values_branches ON ( id = av_id )
1047 push @where_strings, "category = ?";
1048 push @where_args, $category;
1051 push @where_strings, "( branchcode = ? OR branchcode IS NULL )";
1052 push @where_args, $branch_limit;
1054 if(@where_strings > 0) {
1055 $query .= " WHERE " . join(" AND ", @where_strings);
1057 $query .= " GROUP BY lib";
1058 $query .= ' ORDER BY category, ' . (
1059 $opac ? 'COALESCE(lib_opac, lib)'
1063 my $sth = $dbh->prepare($query);
1065 $sth->execute( @where_args );
1066 while (my $data=$sth->fetchrow_hashref) {
1067 if ( defined $selected and $selected eq $data->{authorised_value} ) {
1068 $data->{selected} = 1;
1071 $data->{selected} = 0;
1074 if ($opac && $data->{lib_opac}) {
1075 $data->{lib} = $data->{lib_opac};
1077 push @results, $data;
1081 # We can't cache for long because of that "selected" thing which
1082 # makes it impossible to clear the cache without iterating through every
1083 # value, which sucks. This'll cover this request, and not a whole lot more.
1084 $cache->set_in_cache( $cache_key, \@results, { deepcopy => 1, expiry => 5 } );
1088 =head2 GetAuthorisedValueCategories
1090 $auth_categories = GetAuthorisedValueCategories();
1092 Return an arrayref of all of the available authorised
1097 sub GetAuthorisedValueCategories {
1098 my $dbh = C4::Context->dbh;
1099 my $sth = $dbh->prepare("SELECT DISTINCT category FROM authorised_values ORDER BY category");
1102 while (defined (my $category = $sth->fetchrow_array) ) {
1103 push @results, $category;
1108 =head2 GetAuthorisedValueByCode
1110 $authorised_value = GetAuthorisedValueByCode( $category, $authvalcode, $opac );
1112 Return the lib attribute from authorised_values from the row identified
1113 by the passed category and code
1117 sub GetAuthorisedValueByCode {
1118 my ( $category, $authvalcode, $opac ) = @_;
1120 my $field = $opac ? 'lib_opac' : 'lib';
1121 my $dbh = C4::Context->dbh;
1122 my $sth = $dbh->prepare("SELECT $field FROM authorised_values WHERE category=? AND authorised_value =?");
1123 $sth->execute( $category, $authvalcode );
1124 while ( my $data = $sth->fetchrow_hashref ) {
1125 return $data->{ $field };
1129 =head2 GetKohaAuthorisedValues
1131 Takes $kohafield, $fwcode as parameters.
1133 If $opac parameter is set to a true value, displays OPAC descriptions rather than normal ones when they exist.
1135 Returns hashref of Code => description
1137 Returns undef if no authorised value category is defined for the kohafield.
1141 sub GetKohaAuthorisedValues {
1142 my ($kohafield,$fwcode,$opac) = @_;
1143 $fwcode='' unless $fwcode;
1145 my $dbh = C4::Context->dbh;
1146 my $avcode = GetAuthValCode($kohafield,$fwcode);
1148 my $sth = $dbh->prepare("select authorised_value, lib, lib_opac from authorised_values where category=? ");
1149 $sth->execute($avcode);
1150 while ( my ($val, $lib, $lib_opac) = $sth->fetchrow_array ) {
1151 $values{$val} = ($opac && $lib_opac) ? $lib_opac : $lib;
1159 =head2 GetKohaAuthorisedValuesFromField
1161 Takes $field, $subfield, $fwcode as parameters.
1163 If $opac parameter is set to a true value, displays OPAC descriptions rather than normal ones when they exist.
1164 $subfield can be undefined
1166 Returns hashref of Code => description
1168 Returns undef if no authorised value category is defined for the given field and subfield
1172 sub GetKohaAuthorisedValuesFromField {
1173 my ($field, $subfield, $fwcode,$opac) = @_;
1174 $fwcode='' unless $fwcode;
1176 my $dbh = C4::Context->dbh;
1177 my $avcode = GetAuthValCodeFromField($field, $subfield, $fwcode);
1179 my $sth = $dbh->prepare("select authorised_value, lib, lib_opac from authorised_values where category=? ");
1180 $sth->execute($avcode);
1181 while ( my ($val, $lib, $lib_opac) = $sth->fetchrow_array ) {
1182 $values{$val} = ($opac && $lib_opac) ? $lib_opac : $lib;
1190 =head2 GetKohaAuthorisedValuesMapping
1192 Takes a hash as a parameter. The interface key indicates the
1193 description to use in the mapping.
1196 "{kohafield},{frameworkcode},{authorised_value}" => "{description}"
1197 for all the kohafields, frameworkcodes, and authorised values.
1199 Returns undef if nothing is found.
1203 sub GetKohaAuthorisedValuesMapping {
1204 my ($parameter) = @_;
1205 my $interface = $parameter->{'interface'} // '';
1207 my $query_mapping = q{
1208 SELECT TA.kohafield,TA.authorised_value AS category,
1209 TA.frameworkcode,TB.authorised_value,
1210 IF(TB.lib_opac>'',TB.lib_opac,TB.lib) AS OPAC,
1211 TB.lib AS Intranet,TB.lib_opac
1212 FROM marc_subfield_structure AS TA JOIN
1213 authorised_values as TB ON
1214 TA.authorised_value=TB.category
1215 WHERE TA.kohafield>'' AND TA.authorised_value>'';
1217 my $dbh = C4::Context->dbh;
1218 my $sth = $dbh->prepare($query_mapping);
1221 if ($interface eq 'opac') {
1222 while (my $row = $sth->fetchrow_hashref) {
1223 $avmapping->{$row->{kohafield}.",".$row->{frameworkcode}.",".$row->{authorised_value}} = $row->{OPAC};
1227 while (my $row = $sth->fetchrow_hashref) {
1228 $avmapping->{$row->{kohafield}.",".$row->{frameworkcode}.",".$row->{authorised_value}} = $row->{Intranet};
1236 my $escaped_string = C4::Koha::xml_escape($string);
1238 Convert &, <, >, ', and " in a string to XML entities
1244 return '' unless defined $str;
1245 $str =~ s/&/&/g;
1248 $str =~ s/'/'/g;
1249 $str =~ s/"/"/g;
1253 =head2 GetKohaAuthorisedValueLib
1255 Takes $category, $authorised_value as parameters.
1257 If $opac parameter is set to a true value, displays OPAC descriptions rather than normal ones when they exist.
1259 Returns authorised value description
1263 sub GetKohaAuthorisedValueLib {
1264 my ($category,$authorised_value,$opac) = @_;
1266 my $dbh = C4::Context->dbh;
1267 my $sth = $dbh->prepare("select lib, lib_opac from authorised_values where category=? and authorised_value=?");
1268 $sth->execute($category,$authorised_value);
1269 my $data = $sth->fetchrow_hashref;
1270 $value = ($opac && $$data{'lib_opac'}) ? $$data{'lib_opac'} : $$data{'lib'};
1274 =head2 display_marc_indicators
1276 my $display_form = C4::Koha::display_marc_indicators($field);
1278 C<$field> is a MARC::Field object
1280 Generate a display form of the indicators of a variable
1281 MARC field, replacing any blanks with '#'.
1285 sub display_marc_indicators {
1287 my $indicators = '';
1288 if ($field && $field->tag() >= 10) {
1289 $indicators = $field->indicator(1) . $field->indicator(2);
1290 $indicators =~ s/ /#/g;
1295 sub GetNormalizedUPC {
1296 my ($marcrecord,$marcflavour) = @_;
1298 return unless $marcrecord;
1299 if ($marcflavour eq 'UNIMARC') {
1300 my @fields = $marcrecord->field('072');
1301 foreach my $field (@fields) {
1302 my $upc = _normalize_match_point($field->subfield('a'));
1309 else { # assume marc21 if not unimarc
1310 my @fields = $marcrecord->field('024');
1311 foreach my $field (@fields) {
1312 my $indicator = $field->indicator(1);
1313 my $upc = _normalize_match_point($field->subfield('a'));
1314 if ($upc && $indicator == 1 ) {
1321 # Normalizes and returns the first valid ISBN found in the record
1322 # ISBN13 are converted into ISBN10. This is required to get some book cover images.
1323 sub GetNormalizedISBN {
1324 my ($isbn,$marcrecord,$marcflavour) = @_;
1326 # Koha attempts to store multiple ISBNs in biblioitems.isbn, separated by " | "
1327 # anything after " | " should be removed, along with the delimiter
1328 ($isbn) = split(/\|/, $isbn );
1329 return _isbn_cleanup($isbn);
1332 return unless $marcrecord;
1334 if ($marcflavour eq 'UNIMARC') {
1335 my @fields = $marcrecord->field('010');
1336 foreach my $field (@fields) {
1337 my $isbn = $field->subfield('a');
1339 return _isbn_cleanup($isbn);
1343 else { # assume marc21 if not unimarc
1344 my @fields = $marcrecord->field('020');
1345 foreach my $field (@fields) {
1346 $isbn = $field->subfield('a');
1348 return _isbn_cleanup($isbn);
1354 sub GetNormalizedEAN {
1355 my ($marcrecord,$marcflavour) = @_;
1357 return unless $marcrecord;
1359 if ($marcflavour eq 'UNIMARC') {
1360 my @fields = $marcrecord->field('073');
1361 foreach my $field (@fields) {
1362 my $ean = _normalize_match_point($field->subfield('a'));
1368 else { # assume marc21 if not unimarc
1369 my @fields = $marcrecord->field('024');
1370 foreach my $field (@fields) {
1371 my $indicator = $field->indicator(1);
1372 my $ean = _normalize_match_point($field->subfield('a'));
1373 if ( $ean && $indicator == 3 ) {
1380 sub GetNormalizedOCLCNumber {
1381 my ($marcrecord,$marcflavour) = @_;
1382 return unless $marcrecord;
1384 if ($marcflavour ne 'UNIMARC' ) {
1385 my @fields = $marcrecord->field('035');
1386 foreach my $field (@fields) {
1387 my $oclc = $field->subfield('a');
1388 if ($oclc =~ /OCoLC/) {
1389 $oclc =~ s/\(OCoLC\)//;
1399 sub GetAuthvalueDropbox {
1400 my ( $authcat, $default ) = @_;
1401 my $branch_limit = C4::Context->userenv ? C4::Context->userenv->{"branch"} : "";
1402 my $dbh = C4::Context->dbh;
1406 FROM authorised_values
1409 LEFT JOIN authorised_values_branches ON ( id = av_id )
1414 $query .= " AND ( branchcode = ? OR branchcode IS NULL )" if $branch_limit;
1415 $query .= " GROUP BY lib ORDER BY category, lib, lib_opac";
1416 my $sth = $dbh->prepare($query);
1417 $sth->execute( $authcat, $branch_limit ? $branch_limit : () );
1420 my $option_list = [];
1421 my @authorised_values = ( q{} );
1422 while (my $av = $sth->fetchrow_hashref) {
1423 push @{$option_list}, {
1424 value => $av->{authorised_value},
1425 label => $av->{lib},
1426 default => ($default eq $av->{authorised_value}),
1430 if ( @{$option_list} ) {
1431 return $option_list;
1437 =head2 GetDailyQuote($opts)
1439 Takes a hashref of options
1441 Currently supported options are:
1443 'id' An exact quote id
1444 'random' Select a random quote
1445 noop When no option is passed in, this sub will return the quote timestamped for the current day
1447 The function returns an anonymous hash following this format:
1450 'source' => 'source-of-quote',
1451 'timestamp' => 'timestamp-value',
1452 'text' => 'text-of-quote',
1458 # This is definitely a candidate for some sort of caching once we finally settle caching/persistence issues...
1459 # at least for default option
1463 my $dbh = C4::Context->dbh;
1468 $query = 'SELECT * FROM quotes WHERE id = ?';
1469 $sth = $dbh->prepare($query);
1470 $sth->execute($opts{'id'});
1471 $quote = $sth->fetchrow_hashref();
1473 elsif ($opts{'random'}) {
1474 # Fall through... we also return a random quote as a catch-all if all else fails
1477 $query = 'SELECT * FROM quotes WHERE timestamp LIKE CONCAT(CURRENT_DATE,\'%\') ORDER BY timestamp DESC LIMIT 0,1';
1478 $sth = $dbh->prepare($query);
1480 $quote = $sth->fetchrow_hashref();
1482 unless ($quote) { # if there are not matches, choose a random quote
1483 # get a list of all available quote ids
1484 $sth = C4::Context->dbh->prepare('SELECT count(*) FROM quotes;');
1486 my $range = ($sth->fetchrow_array)[0];
1487 # chose a random id within that range if there is more than one quote
1488 my $offset = int(rand($range));
1490 $query = 'SELECT * FROM quotes ORDER BY id LIMIT 1 OFFSET ?';
1491 $sth = C4::Context->dbh->prepare($query);
1492 # see http://www.perlmonks.org/?node_id=837422 for why
1493 # we're being verbose and using bind_param
1494 $sth->bind_param(1, $offset, SQL_INTEGER);
1496 $quote = $sth->fetchrow_hashref();
1497 # update the timestamp for that quote
1498 $query = 'UPDATE quotes SET timestamp = ? WHERE id = ?';
1499 $sth = C4::Context->dbh->prepare($query);
1501 DateTime::Format::MySQL->format_datetime( dt_from_string() ),
1508 sub _normalize_match_point {
1509 my $match_point = shift;
1510 (my $normalized_match_point) = $match_point =~ /([\d-]*[X]*)/;
1511 $normalized_match_point =~ s/-//g;
1513 return $normalized_match_point;
1518 return NormalizeISBN(
1521 format => 'ISBN-10',
1527 =head2 NormalizedISBN
1529 my $isbns = NormalizedISBN({
1531 strip_hyphens => [0,1],
1532 format => ['ISBN-10', 'ISBN-13']
1535 Returns an isbn validated by Business::ISBN.
1536 Optionally strips hyphens and/or forces the isbn
1537 to be of the specified format.
1539 If the string cannot be validated as an isbn,
1547 my $string = $params->{isbn};
1548 my $strip_hyphens = $params->{strip_hyphens};
1549 my $format = $params->{format};
1551 return unless $string;
1553 my $isbn = Business::ISBN->new($string);
1555 if ( $isbn && $isbn->is_valid() ) {
1557 if ( $format eq 'ISBN-10' ) {
1558 $isbn = $isbn->as_isbn10();
1560 elsif ( $format eq 'ISBN-13' ) {
1561 $isbn = $isbn->as_isbn13();
1563 return unless $isbn;
1565 if ($strip_hyphens) {
1566 $string = $isbn->as_string( [] );
1568 $string = $isbn->as_string();
1575 =head2 GetVariationsOfISBN
1577 my @isbns = GetVariationsOfISBN( $isbn );
1579 Returns a list of variations of the given isbn in
1580 both ISBN-10 and ISBN-13 formats, with and without
1583 In a scalar context, the isbns are returned as a
1584 string delimited by ' | '.
1588 sub GetVariationsOfISBN {
1591 return unless $isbn;
1595 push( @isbns, NormalizeISBN({ isbn => $isbn }) );
1596 push( @isbns, NormalizeISBN({ isbn => $isbn, format => 'ISBN-10' }) );
1597 push( @isbns, NormalizeISBN({ isbn => $isbn, format => 'ISBN-13' }) );
1598 push( @isbns, NormalizeISBN({ isbn => $isbn, format => 'ISBN-10', strip_hyphens => 1 }) );
1599 push( @isbns, NormalizeISBN({ isbn => $isbn, format => 'ISBN-13', strip_hyphens => 1 }) );
1601 # Strip out any "empty" strings from the array
1602 @isbns = grep { defined($_) && $_ =~ /\S/ } @isbns;
1604 return wantarray ? @isbns : join( " | ", @isbns );
1607 =head2 GetVariationsOfISBNs
1609 my @isbns = GetVariationsOfISBNs( @isbns );
1611 Returns a list of variations of the given isbns in
1612 both ISBN-10 and ISBN-13 formats, with and without
1615 In a scalar context, the isbns are returned as a
1616 string delimited by ' | '.
1620 sub GetVariationsOfISBNs {
1623 @isbns = map { GetVariationsOfISBN( $_ ) } @isbns;
1625 return wantarray ? @isbns : join( " | ", @isbns );
1628 =head2 IsKohaFieldLinked
1630 my $is_linked = IsKohaFieldLinked({
1631 kohafield => $kohafield,
1632 frameworkcode => $frameworkcode,
1635 Return 1 if the field is linked
1639 sub IsKohaFieldLinked {
1640 my ( $params ) = @_;
1641 my $kohafield = $params->{kohafield};
1642 my $frameworkcode = $params->{frameworkcode} || '';
1643 my $dbh = C4::Context->dbh;
1644 my $is_linked = $dbh->selectcol_arrayref( q|
1646 FROM marc_subfield_structure
1647 WHERE frameworkcode = ?
1649 |,{}, $frameworkcode, $kohafield );
1650 return $is_linked->[0];