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
28 use Koha::DateUtils qw(dt_from_string);
30 use Koha::MarcSubfieldStructures;
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);
41 &GetPrinters &GetPrinter
42 &GetItemTypes &getitemtypeinfo
43 &GetItemTypesCategorized &GetItemTypesByCategory
44 &getframeworks &getframeworkinfo
50 &get_notforloan_label_of
53 &getitemtypeimagelocation
55 &GetAuthorisedValueCategories
56 &GetKohaAuthorisedValues
57 &GetKohaAuthorisedValuesMapping
58 &GetAuthorisedValueByCode
63 &GetNormalizedOCLCNumber
73 @EXPORT_OK = qw( GetDailyQuote );
78 C4::Koha - Perl Module containing convenience functions for Koha scripts
86 Koha.pm provides many functions for Koha scripts.
94 $itemtypes = &GetItemTypes( style => $style );
96 Returns information about existing itemtypes.
99 style: either 'array' or 'hash', defaults to 'hash'.
100 'array' returns an arrayref,
101 'hash' return a hashref with the itemtype value as the key
103 build a HTML select with the following code :
105 =head3 in PERL SCRIPT
107 my $itemtypes = GetItemTypes;
109 foreach my $thisitemtype (sort keys %$itemtypes) {
110 my $selected = 1 if $thisitemtype eq $itemtype;
111 my %row =(value => $thisitemtype,
112 selected => $selected,
113 description => $itemtypes->{$thisitemtype}->{'description'},
115 push @itemtypesloop, \%row;
117 $template->param(itemtypeloop => \@itemtypesloop);
121 <form action='<!-- TMPL_VAR name="script_name" -->' method=post>
122 <select name="itemtype">
123 <option value="">Default</option>
124 <!-- TMPL_LOOP name="itemtypeloop" -->
125 <option value="<!-- TMPL_VAR name="value" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="description" --></option>
128 <input type=text name=searchfield value="<!-- TMPL_VAR name="searchfield" -->">
129 <input type="submit" value="OK" class="button">
136 my $style = defined( $params{'style'} ) ? $params{'style'} : 'hash';
138 require C4::Languages;
139 my $language = C4::Languages::getlanguage();
140 # returns a reference to a hash of references to itemtypes...
141 my $dbh = C4::Context->dbh;
145 itemtypes.description,
146 itemtypes.rentalcharge,
147 itemtypes.notforloan,
150 itemtypes.checkinmsg,
151 itemtypes.checkinmsgtype,
152 itemtypes.sip_media_type,
153 itemtypes.hideinopac,
154 itemtypes.searchcategory,
155 COALESCE( localization.translation, itemtypes.description ) AS translated_description
157 LEFT JOIN localization ON itemtypes.itemtype = localization.code
158 AND localization.entity = 'itemtypes'
159 AND localization.lang = ?
162 my $sth = $dbh->prepare($query);
163 $sth->execute( $language );
165 if ( $style eq 'hash' ) {
167 while ( my $IT = $sth->fetchrow_hashref ) {
168 $itemtypes{ $IT->{'itemtype'} } = $IT;
170 return ( \%itemtypes );
172 return [ sort { lc $a->{translated_description} cmp lc $b->{translated_description} } @{ $sth->fetchall_arrayref( {} ) } ];
176 =head2 GetItemTypesCategorized
178 $categories = GetItemTypesCategorized();
180 Returns a hashref containing search categories.
181 A search category will be put in the hash if at least one of its itemtypes is visible in OPAC.
182 The categories must be part of Authorized Values (ITEMTYPECAT)
186 sub GetItemTypesCategorized {
187 my $dbh = C4::Context->dbh;
188 # Order is important, so that partially hidden (some items are not visible in OPAC) search
189 # categories will be visible. hideinopac=0 must be last.
191 SELECT itemtype, description, imageurl, hideinopac, 0 as 'iscat' FROM itemtypes WHERE ISNULL(searchcategory) or length(searchcategory) = 0
193 SELECT DISTINCT searchcategory AS `itemtype`,
194 authorised_values.lib_opac AS description,
195 authorised_values.imageurl AS imageurl,
196 hideinopac, 1 as 'iscat'
198 LEFT JOIN authorised_values ON searchcategory = authorised_value
199 WHERE searchcategory > '' and hideinopac=1
201 SELECT DISTINCT searchcategory AS `itemtype`,
202 authorised_values.lib_opac AS description,
203 authorised_values.imageurl AS imageurl,
204 hideinopac, 1 as 'iscat'
206 LEFT JOIN authorised_values ON searchcategory = authorised_value
207 WHERE searchcategory > '' and hideinopac=0
209 return ($dbh->selectall_hashref($query,'itemtype'));
212 =head2 GetItemTypesByCategory
214 @results = GetItemTypesByCategory( $searchcategory );
216 Returns the itemtype code of all itemtypes included in a searchcategory.
220 sub GetItemTypesByCategory {
224 my $dbh = C4::Context->dbh;
225 my $query = qq|SELECT itemtype FROM itemtypes WHERE searchcategory=?|;
226 my $tmp=$dbh->selectcol_arrayref($query,undef,$category);
232 $frameworks = &getframework();
234 Returns information about existing frameworks
236 build a HTML select with the following code :
238 =head3 in PERL SCRIPT
240 my $frameworks = getframeworks();
242 foreach my $thisframework (keys %$frameworks) {
243 my $selected = 1 if $thisframework eq $frameworkcode;
245 value => $thisframework,
246 selected => $selected,
247 description => $frameworks->{$thisframework}->{'frameworktext'},
249 push @frameworksloop, \%row;
251 $template->param(frameworkloop => \@frameworksloop);
255 <form action="[% script_name %] method=post>
256 <select name="frameworkcode">
257 <option value="">Default</option>
258 [% FOREACH framework IN frameworkloop %]
259 [% IF ( framework.selected ) %]
260 <option value="[% framework.value %]" selected="selected">[% framework.description %]</option>
262 <option value="[% framework.value %]">[% framework.description %]</option>
266 <input type=text name=searchfield value="[% searchfield %]">
267 <input type="submit" value="OK" class="button">
274 # returns a reference to a hash of references to branches...
276 my $dbh = C4::Context->dbh;
277 my $sth = $dbh->prepare("select * from biblio_framework");
279 while ( my $IT = $sth->fetchrow_hashref ) {
280 $itemtypes{ $IT->{'frameworkcode'} } = $IT;
282 return ( \%itemtypes );
285 =head2 GetFrameworksLoop
287 $frameworks = GetFrameworksLoop( $frameworkcode );
289 Returns the loop suggested on getframework(), but ordered by framework description.
291 build a HTML select with the following code :
293 =head3 in PERL SCRIPT
295 $template->param( frameworkloop => GetFrameworksLoop( $frameworkcode ) );
299 Same as getframework()
301 <form action="[% script_name %] method=post>
302 <select name="frameworkcode">
303 <option value="">Default</option>
304 [% FOREACH framework IN frameworkloop %]
305 [% IF ( framework.selected ) %]
306 <option value="[% framework.value %]" selected="selected">[% framework.description %]</option>
308 <option value="[% framework.value %]">[% framework.description %]</option>
312 <input type=text name=searchfield value="[% searchfield %]">
313 <input type="submit" value="OK" class="button">
318 sub GetFrameworksLoop {
319 my $frameworkcode = shift;
320 my $frameworks = getframeworks();
322 foreach my $thisframework (sort { uc($frameworks->{$a}->{'frameworktext'}) cmp uc($frameworks->{$b}->{'frameworktext'}) } keys %$frameworks) {
323 my $selected = ( $thisframework eq $frameworkcode ) ? 1 : undef;
325 value => $thisframework,
326 selected => $selected,
327 description => $frameworks->{$thisframework}->{'frameworktext'},
329 push @frameworkloop, \%row;
331 return \@frameworkloop;
334 =head2 getframeworkinfo
336 $frameworkinfo = &getframeworkinfo($frameworkcode);
338 Returns information about an frameworkcode.
342 sub getframeworkinfo {
343 my ($frameworkcode) = @_;
344 my $dbh = C4::Context->dbh;
346 $dbh->prepare("select * from biblio_framework where frameworkcode=?");
347 $sth->execute($frameworkcode);
348 my $res = $sth->fetchrow_hashref;
352 =head2 getitemtypeinfo
354 $itemtype = &getitemtypeinfo($itemtype, [$interface]);
356 Returns information about an itemtype. The optional $interface argument
357 sets which interface ('opac' or 'intranet') to return the imageurl for.
358 Defaults to intranet.
362 sub getitemtypeinfo {
363 my ($itemtype, $interface) = @_;
364 my $dbh = C4::Context->dbh;
365 require C4::Languages;
366 my $language = C4::Languages::getlanguage();
367 my $it = $dbh->selectrow_hashref(q|
370 itemtypes.description,
371 itemtypes.rentalcharge,
372 itemtypes.notforloan,
375 itemtypes.checkinmsg,
376 itemtypes.checkinmsgtype,
377 itemtypes.sip_media_type,
378 COALESCE( localization.translation, itemtypes.description ) AS translated_description
380 LEFT JOIN localization ON itemtypes.itemtype = localization.code
381 AND localization.entity = 'itemtypes'
382 AND localization.lang = ?
383 WHERE itemtypes.itemtype = ?
384 |, undef, $language, $itemtype );
386 $it->{imageurl} = getitemtypeimagelocation( ( ( defined $interface && $interface eq 'opac' ) ? 'opac' : 'intranet' ), $it->{imageurl} );
391 =head2 getitemtypeimagedir
393 my $directory = getitemtypeimagedir( 'opac' );
395 pass in 'opac' or 'intranet'. Defaults to 'opac'.
397 returns the full path to the appropriate directory containing images.
401 sub getitemtypeimagedir {
402 my $src = shift || 'opac';
403 if ($src eq 'intranet') {
404 return C4::Context->config('intrahtdocs') . '/' .C4::Context->preference('template') . '/img/itemtypeimg';
406 return C4::Context->config('opachtdocs') . '/' . C4::Context->preference('opacthemes') . '/itemtypeimg';
410 sub getitemtypeimagesrc {
411 my $src = shift || 'opac';
412 if ($src eq 'intranet') {
413 return '/intranet-tmpl' . '/' . C4::Context->preference('template') . '/img/itemtypeimg';
415 return '/opac-tmpl' . '/' . C4::Context->preference('opacthemes') . '/itemtypeimg';
419 sub getitemtypeimagelocation {
420 my ( $src, $image ) = @_;
422 return '' if ( !$image );
425 my $scheme = ( URI::Split::uri_split( $image ) )[0];
427 return $image if ( $scheme );
429 return getitemtypeimagesrc( $src ) . '/' . $image;
432 =head3 _getImagesFromDirectory
434 Find all of the image files in a directory in the filesystem
436 parameters: a directory name
438 returns: a list of images in that directory.
440 Notes: this does not traverse into subdirectories. See
441 _getSubdirectoryNames for help with that.
442 Images are assumed to be files with .gif or .png file extensions.
443 The image names returned do not have the directory name on them.
447 sub _getImagesFromDirectory {
448 my $directoryname = shift;
449 return unless defined $directoryname;
450 return unless -d $directoryname;
452 if ( opendir ( my $dh, $directoryname ) ) {
453 my @images = grep { /\.(gif|png)$/i } readdir( $dh );
455 @images = sort(@images);
458 warn "unable to opendir $directoryname: $!";
463 =head3 _getSubdirectoryNames
465 Find all of the directories in a directory in the filesystem
467 parameters: a directory name
469 returns: a list of subdirectories in that directory.
471 Notes: this does not traverse into subdirectories. Only the first
472 level of subdirectories are returned.
473 The directory names returned don't have the parent directory name on them.
477 sub _getSubdirectoryNames {
478 my $directoryname = shift;
479 return unless defined $directoryname;
480 return unless -d $directoryname;
482 if ( opendir ( my $dh, $directoryname ) ) {
483 my @directories = grep { -d File::Spec->catfile( $directoryname, $_ ) && ! ( /^\./ ) } readdir( $dh );
487 warn "unable to opendir $directoryname: $!";
494 returns: a listref of hashrefs. Each hash represents another collection of images.
496 { imagesetname => 'npl', # the name of the image set (npl is the original one)
497 images => listref of image hashrefs
500 each image is represented by a hashref like this:
502 { KohaImage => 'npl/image.gif',
503 StaffImageUrl => '/intranet-tmpl/prog/img/itemtypeimg/npl/image.gif',
504 OpacImageURL => '/opac-tmpl/prog/itemtypeimg/npl/image.gif'
505 checked => 0 or 1: was this the image passed to this method?
506 Note: I'd like to remove this somehow.
513 my $checked = $params{'checked'} || '';
515 my $paths = { staff => { filesystem => getitemtypeimagedir('intranet'),
516 url => getitemtypeimagesrc('intranet'),
518 opac => { filesystem => getitemtypeimagedir('opac'),
519 url => getitemtypeimagesrc('opac'),
523 my @imagesets = (); # list of hasrefs of image set data to pass to template
524 my @subdirectories = _getSubdirectoryNames( $paths->{'staff'}{'filesystem'} );
525 foreach my $imagesubdir ( @subdirectories ) {
526 warn $imagesubdir if $DEBUG;
527 my @imagelist = (); # hashrefs of image info
528 my @imagenames = _getImagesFromDirectory( File::Spec->catfile( $paths->{'staff'}{'filesystem'}, $imagesubdir ) );
529 my $imagesetactive = 0;
530 foreach my $thisimage ( @imagenames ) {
532 { KohaImage => "$imagesubdir/$thisimage",
533 StaffImageUrl => join( '/', $paths->{'staff'}{'url'}, $imagesubdir, $thisimage ),
534 OpacImageUrl => join( '/', $paths->{'opac'}{'url'}, $imagesubdir, $thisimage ),
535 checked => "$imagesubdir/$thisimage" eq $checked ? 1 : 0,
538 $imagesetactive = 1 if "$imagesubdir/$thisimage" eq $checked;
540 push @imagesets, { imagesetname => $imagesubdir,
541 imagesetactive => $imagesetactive,
542 images => \@imagelist };
550 $printers = &GetPrinters();
551 @queues = keys %$printers;
553 Returns information about existing printer queues.
555 C<$printers> is a reference-to-hash whose keys are the print queues
556 defined in the printers table of the Koha database. The values are
557 references-to-hash, whose keys are the fields in the printers table.
563 my $dbh = C4::Context->dbh;
564 my $sth = $dbh->prepare("select * from printers");
566 while ( my $printer = $sth->fetchrow_hashref ) {
567 $printers{ $printer->{'printqueue'} } = $printer;
569 return ( \%printers );
574 $printer = GetPrinter( $query, $printers );
579 my ( $query, $printers ) = @_; # get printer for this query from printers
580 my $printer = $query->param('printer');
581 my %cookie = $query->cookie('userenv');
582 ($printer) || ( $printer = $cookie{'printer'} ) || ( $printer = '' );
583 ( $printers->{$printer} ) || ( $printer = ( keys %$printers )[0] );
589 Returns the number of pages to display in a pagination bar, given the number
590 of items and the number of items per page.
595 my ( $nb_items, $nb_items_per_page ) = @_;
597 return int( ( $nb_items - 1 ) / $nb_items_per_page ) + 1;
602 (@themes) = &getallthemes('opac');
603 (@themes) = &getallthemes('intranet');
605 Returns an array of all available themes.
613 if ( $type eq 'intranet' ) {
614 $htdocs = C4::Context->config('intrahtdocs');
617 $htdocs = C4::Context->config('opachtdocs');
619 opendir D, "$htdocs";
620 my @dirlist = readdir D;
621 foreach my $directory (@dirlist) {
622 next if $directory eq 'lib';
623 -d "$htdocs/$directory/en" and push @themes, $directory;
630 if ( C4::Context->preference("marcflavour") eq "UNIMARC" ) {
635 tags => [ qw/ 600ab 601ab 602a 604at 605a 606ax 610a / ],
641 tags => [ qw/ 607a / ],
647 tags => [ qw/ 500a 501a 503a / ],
653 tags => [ qw/ 700ab 701ab 702ab / ],
654 sep => C4::Context->preference("UNIMARCAuthorsFacetsSeparator"),
659 tags => [ qw/ 225a / ],
665 tags => [ qw/ 995e / ],
669 unless ( Koha::Libraries->search->count == 1 )
671 my $DisplayLibraryFacets = C4::Context->preference('DisplayLibraryFacets');
672 if ( $DisplayLibraryFacets eq 'both'
673 || $DisplayLibraryFacets eq 'holding' )
678 idx => 'holdingbranch',
679 label => 'HoldingLibrary',
680 tags => [qw / 995c /],
685 if ( $DisplayLibraryFacets eq 'both'
686 || $DisplayLibraryFacets eq 'home' )
692 label => 'HomeLibrary',
693 tags => [qw / 995b /],
704 tags => [ qw/ 650a / ],
709 # label => 'People and Organizations',
710 # tags => [ qw/ 600a 610a 611a / ],
716 tags => [ qw/ 651a / ],
722 tags => [ qw/ 630a / ],
728 tags => [ qw/ 100a 110a 700a / ],
734 tags => [ qw/ 440a 490a / ],
739 label => 'ItemTypes',
740 tags => [ qw/ 952y 942c / ],
746 tags => [ qw / 952c / ],
750 unless ( Koha::Libraries->search->count == 1 )
752 my $DisplayLibraryFacets = C4::Context->preference('DisplayLibraryFacets');
753 if ( $DisplayLibraryFacets eq 'both'
754 || $DisplayLibraryFacets eq 'holding' )
759 idx => 'holdingbranch',
760 label => 'HoldingLibrary',
761 tags => [qw / 952b /],
766 if ( $DisplayLibraryFacets eq 'both'
767 || $DisplayLibraryFacets eq 'home' )
773 label => 'HomeLibrary',
774 tags => [qw / 952a /],
785 Return a href where a key is associated to a href. You give a query,
786 the name of the key among the fields returned by the query. If you
787 also give as third argument the name of the value, the function
788 returns a href of scalar. The optional 4th argument is an arrayref of
789 items passed to the C<execute()> call. It is designed to bind
790 parameters to any placeholders in your SQL.
799 # generic href of any information on the item, href of href.
800 my $iteminfos_of = get_infos_of($query, 'itemnumber');
801 print $iteminfos_of->{$itemnumber}{barcode};
803 # specific information, href of scalar
804 my $barcode_of_item = get_infos_of($query, 'itemnumber', 'barcode');
805 print $barcode_of_item->{$itemnumber};
810 my ( $query, $key_name, $value_name, $bind_params ) = @_;
812 my $dbh = C4::Context->dbh;
814 my $sth = $dbh->prepare($query);
815 $sth->execute( @$bind_params );
818 while ( my $row = $sth->fetchrow_hashref ) {
819 if ( defined $value_name ) {
820 $infos_of{ $row->{$key_name} } = $row->{$value_name};
823 $infos_of{ $row->{$key_name} } = $row;
831 =head2 get_notforloan_label_of
833 my $notforloan_label_of = get_notforloan_label_of();
835 Each authorised value of notforloan (information available in items and
836 itemtypes) is link to a single label.
838 Returns a href where keys are authorised values and values are corresponding
841 foreach my $authorised_value (keys %{$notforloan_label_of}) {
843 "authorised_value: %s => %s\n",
845 $notforloan_label_of->{$authorised_value}
851 # FIXME - why not use GetAuthorisedValues ??
853 sub get_notforloan_label_of {
854 my $dbh = C4::Context->dbh;
857 SELECT authorised_value
858 FROM marc_subfield_structure
859 WHERE kohafield = \'items.notforloan\'
862 my $sth = $dbh->prepare($query);
864 my ($statuscode) = $sth->fetchrow_array();
869 FROM authorised_values
872 $sth = $dbh->prepare($query);
873 $sth->execute($statuscode);
874 my %notforloan_label_of;
875 while ( my $row = $sth->fetchrow_hashref ) {
876 $notforloan_label_of{ $row->{authorised_value} } = $row->{lib};
880 return \%notforloan_label_of;
883 =head2 GetAuthValCode
885 $authvalcode = GetAuthValCode($kohafield,$frameworkcode);
890 my ($kohafield,$fwcode) = @_;
891 my $dbh = C4::Context->dbh;
892 $fwcode='' unless $fwcode;
893 my $sth = $dbh->prepare('select authorised_value from marc_subfield_structure where kohafield=? and frameworkcode=?');
894 $sth->execute($kohafield,$fwcode);
895 my ($authvalcode) = $sth->fetchrow_array;
899 =head2 GetAuthValCodeFromField
901 $authvalcode = GetAuthValCodeFromField($field,$subfield,$frameworkcode);
903 C<$subfield> can be undefined
907 sub GetAuthValCodeFromField {
908 my ($field,$subfield,$fwcode) = @_;
909 my $dbh = C4::Context->dbh;
910 $fwcode='' unless $fwcode;
912 if (defined $subfield) {
913 $sth = $dbh->prepare('select authorised_value from marc_subfield_structure where tagfield=? and tagsubfield=? and frameworkcode=?');
914 $sth->execute($field,$subfield,$fwcode);
916 $sth = $dbh->prepare('select authorised_value from marc_tag_structure where tagfield=? and frameworkcode=?');
917 $sth->execute($field,$fwcode);
919 my ($authvalcode) = $sth->fetchrow_array;
923 =head2 GetAuthorisedValues
925 $authvalues = GetAuthorisedValues([$category]);
927 This function returns all authorised values from the'authorised_value' table in a reference to array of hashrefs.
929 C<$category> returns authorised values for just one category (optional).
931 C<$opac> If set to a true value, displays OPAC descriptions rather than normal ones when they exist.
935 sub GetAuthorisedValues {
936 my ( $category, $opac ) = @_;
938 # Is this cached already?
939 $opac = $opac ? 1 : 0; # normalise to be safe
941 C4::Context->userenv ? C4::Context->userenv->{"branch"} : "";
943 "AuthorisedValues-$category-$opac-$branch_limit";
944 my $cache = Koha::Caches->get_instance();
945 my $result = $cache->get_from_cache($cache_key);
946 return $result if $result;
949 my $dbh = C4::Context->dbh;
952 FROM authorised_values av
955 LEFT JOIN authorised_values_branches ON ( id = av_id )
960 push @where_strings, "category = ?";
961 push @where_args, $category;
964 push @where_strings, "( branchcode = ? OR branchcode IS NULL )";
965 push @where_args, $branch_limit;
967 if(@where_strings > 0) {
968 $query .= " WHERE " . join(" AND ", @where_strings);
970 $query .= ' ORDER BY category, ' . (
971 $opac ? 'COALESCE(lib_opac, lib)'
975 my $sth = $dbh->prepare($query);
977 $sth->execute( @where_args );
978 while (my $data=$sth->fetchrow_hashref) {
979 if ($opac && $data->{lib_opac}) {
980 $data->{lib} = $data->{lib_opac};
982 push @results, $data;
986 $cache->set_in_cache( $cache_key, \@results, { expiry => 5 } );
990 =head2 GetAuthorisedValueCategories
992 $auth_categories = GetAuthorisedValueCategories();
994 Return an arrayref of all of the available authorised
999 sub GetAuthorisedValueCategories {
1000 my $dbh = C4::Context->dbh;
1001 my $sth = $dbh->prepare("SELECT DISTINCT category FROM authorised_values ORDER BY category");
1004 while (defined (my $category = $sth->fetchrow_array) ) {
1005 push @results, $category;
1010 =head2 GetAuthorisedValueByCode
1012 $authorised_value = GetAuthorisedValueByCode( $category, $authvalcode, $opac );
1014 Return the lib attribute from authorised_values from the row identified
1015 by the passed category and code
1019 sub GetAuthorisedValueByCode {
1020 my ( $category, $authvalcode, $opac ) = @_;
1022 my $field = $opac ? 'lib_opac' : 'lib';
1023 my $dbh = C4::Context->dbh;
1024 my $sth = $dbh->prepare("SELECT $field FROM authorised_values WHERE category=? AND authorised_value =?");
1025 $sth->execute( $category, $authvalcode );
1026 while ( my $data = $sth->fetchrow_hashref ) {
1027 return $data->{ $field };
1031 =head2 GetKohaAuthorisedValues
1033 Takes $kohafield, $fwcode as parameters.
1035 If $opac parameter is set to a true value, displays OPAC descriptions rather than normal ones when they exist.
1037 Returns hashref of Code => description
1039 Returns undef if no authorised value category is defined for the kohafield.
1043 sub GetKohaAuthorisedValues {
1044 my ($kohafield,$fwcode,$opac) = @_;
1045 $fwcode='' unless $fwcode;
1047 my $dbh = C4::Context->dbh;
1048 my $avcode = GetAuthValCode($kohafield,$fwcode);
1050 my $sth = $dbh->prepare("select authorised_value, lib, lib_opac from authorised_values where category=? ");
1051 $sth->execute($avcode);
1052 while ( my ($val, $lib, $lib_opac) = $sth->fetchrow_array ) {
1053 $values{$val} = ($opac && $lib_opac) ? $lib_opac : $lib;
1061 =head2 GetKohaAuthorisedValuesMapping
1063 Takes a hash as a parameter. The interface key indicates the
1064 description to use in the mapping.
1067 "{kohafield},{frameworkcode},{authorised_value}" => "{description}"
1068 for all the kohafields, frameworkcodes, and authorised values.
1070 Returns undef if nothing is found.
1074 sub GetKohaAuthorisedValuesMapping {
1075 my ($parameter) = @_;
1076 my $interface = $parameter->{'interface'} // '';
1078 my $query_mapping = q{
1079 SELECT TA.kohafield,TA.authorised_value AS category,
1080 TA.frameworkcode,TB.authorised_value,
1081 IF(TB.lib_opac>'',TB.lib_opac,TB.lib) AS OPAC,
1082 TB.lib AS Intranet,TB.lib_opac
1083 FROM marc_subfield_structure AS TA JOIN
1084 authorised_values as TB ON
1085 TA.authorised_value=TB.category
1086 WHERE TA.kohafield>'' AND TA.authorised_value>'';
1088 my $dbh = C4::Context->dbh;
1089 my $sth = $dbh->prepare($query_mapping);
1092 if ($interface eq 'opac') {
1093 while (my $row = $sth->fetchrow_hashref) {
1094 $avmapping->{$row->{kohafield}.",".$row->{frameworkcode}.",".$row->{authorised_value}} = $row->{OPAC};
1098 while (my $row = $sth->fetchrow_hashref) {
1099 $avmapping->{$row->{kohafield}.",".$row->{frameworkcode}.",".$row->{authorised_value}} = $row->{Intranet};
1107 my $escaped_string = C4::Koha::xml_escape($string);
1109 Convert &, <, >, ', and " in a string to XML entities
1115 return '' unless defined $str;
1116 $str =~ s/&/&/g;
1119 $str =~ s/'/'/g;
1120 $str =~ s/"/"/g;
1124 =head2 display_marc_indicators
1126 my $display_form = C4::Koha::display_marc_indicators($field);
1128 C<$field> is a MARC::Field object
1130 Generate a display form of the indicators of a variable
1131 MARC field, replacing any blanks with '#'.
1135 sub display_marc_indicators {
1137 my $indicators = '';
1138 if ($field && $field->tag() >= 10) {
1139 $indicators = $field->indicator(1) . $field->indicator(2);
1140 $indicators =~ s/ /#/g;
1145 sub GetNormalizedUPC {
1146 my ($marcrecord,$marcflavour) = @_;
1148 return unless $marcrecord;
1149 if ($marcflavour eq 'UNIMARC') {
1150 my @fields = $marcrecord->field('072');
1151 foreach my $field (@fields) {
1152 my $upc = _normalize_match_point($field->subfield('a'));
1159 else { # assume marc21 if not unimarc
1160 my @fields = $marcrecord->field('024');
1161 foreach my $field (@fields) {
1162 my $indicator = $field->indicator(1);
1163 my $upc = _normalize_match_point($field->subfield('a'));
1164 if ($upc && $indicator == 1 ) {
1171 # Normalizes and returns the first valid ISBN found in the record
1172 # ISBN13 are converted into ISBN10. This is required to get some book cover images.
1173 sub GetNormalizedISBN {
1174 my ($isbn,$marcrecord,$marcflavour) = @_;
1176 # Koha attempts to store multiple ISBNs in biblioitems.isbn, separated by " | "
1177 # anything after " | " should be removed, along with the delimiter
1178 ($isbn) = split(/\|/, $isbn );
1179 return _isbn_cleanup($isbn);
1182 return unless $marcrecord;
1184 if ($marcflavour eq 'UNIMARC') {
1185 my @fields = $marcrecord->field('010');
1186 foreach my $field (@fields) {
1187 my $isbn = $field->subfield('a');
1189 return _isbn_cleanup($isbn);
1193 else { # assume marc21 if not unimarc
1194 my @fields = $marcrecord->field('020');
1195 foreach my $field (@fields) {
1196 $isbn = $field->subfield('a');
1198 return _isbn_cleanup($isbn);
1204 sub GetNormalizedEAN {
1205 my ($marcrecord,$marcflavour) = @_;
1207 return unless $marcrecord;
1209 if ($marcflavour eq 'UNIMARC') {
1210 my @fields = $marcrecord->field('073');
1211 foreach my $field (@fields) {
1212 my $ean = _normalize_match_point($field->subfield('a'));
1218 else { # assume marc21 if not unimarc
1219 my @fields = $marcrecord->field('024');
1220 foreach my $field (@fields) {
1221 my $indicator = $field->indicator(1);
1222 my $ean = _normalize_match_point($field->subfield('a'));
1223 if ( $ean && $indicator == 3 ) {
1230 sub GetNormalizedOCLCNumber {
1231 my ($marcrecord,$marcflavour) = @_;
1232 return unless $marcrecord;
1234 if ($marcflavour ne 'UNIMARC' ) {
1235 my @fields = $marcrecord->field('035');
1236 foreach my $field (@fields) {
1237 my $oclc = $field->subfield('a');
1238 if ($oclc =~ /OCoLC/) {
1239 $oclc =~ s/\(OCoLC\)//;
1249 sub GetAuthvalueDropbox {
1250 my ( $authcat, $default ) = @_;
1251 my $branch_limit = C4::Context->userenv ? C4::Context->userenv->{"branch"} : "";
1252 my $dbh = C4::Context->dbh;
1256 FROM authorised_values
1259 LEFT JOIN authorised_values_branches ON ( id = av_id )
1264 $query .= " AND ( branchcode = ? OR branchcode IS NULL )" if $branch_limit;
1265 $query .= " GROUP BY lib ORDER BY category, lib, lib_opac";
1266 my $sth = $dbh->prepare($query);
1267 $sth->execute( $authcat, $branch_limit ? $branch_limit : () );
1270 my $option_list = [];
1271 my @authorised_values = ( q{} );
1272 while (my $av = $sth->fetchrow_hashref) {
1273 push @{$option_list}, {
1274 value => $av->{authorised_value},
1275 label => $av->{lib},
1276 default => ($default eq $av->{authorised_value}),
1280 if ( @{$option_list} ) {
1281 return $option_list;
1287 =head2 GetDailyQuote($opts)
1289 Takes a hashref of options
1291 Currently supported options are:
1293 'id' An exact quote id
1294 'random' Select a random quote
1295 noop When no option is passed in, this sub will return the quote timestamped for the current day
1297 The function returns an anonymous hash following this format:
1300 'source' => 'source-of-quote',
1301 'timestamp' => 'timestamp-value',
1302 'text' => 'text-of-quote',
1308 # This is definitely a candidate for some sort of caching once we finally settle caching/persistence issues...
1309 # at least for default option
1313 my $dbh = C4::Context->dbh;
1318 $query = 'SELECT * FROM quotes WHERE id = ?';
1319 $sth = $dbh->prepare($query);
1320 $sth->execute($opts{'id'});
1321 $quote = $sth->fetchrow_hashref();
1323 elsif ($opts{'random'}) {
1324 # Fall through... we also return a random quote as a catch-all if all else fails
1327 $query = 'SELECT * FROM quotes WHERE timestamp LIKE CONCAT(CURRENT_DATE,\'%\') ORDER BY timestamp DESC LIMIT 0,1';
1328 $sth = $dbh->prepare($query);
1330 $quote = $sth->fetchrow_hashref();
1332 unless ($quote) { # if there are not matches, choose a random quote
1333 # get a list of all available quote ids
1334 $sth = C4::Context->dbh->prepare('SELECT count(*) FROM quotes;');
1336 my $range = ($sth->fetchrow_array)[0];
1337 # chose a random id within that range if there is more than one quote
1338 my $offset = int(rand($range));
1340 $query = 'SELECT * FROM quotes ORDER BY id LIMIT 1 OFFSET ?';
1341 $sth = C4::Context->dbh->prepare($query);
1342 # see http://www.perlmonks.org/?node_id=837422 for why
1343 # we're being verbose and using bind_param
1344 $sth->bind_param(1, $offset, SQL_INTEGER);
1346 $quote = $sth->fetchrow_hashref();
1347 # update the timestamp for that quote
1348 $query = 'UPDATE quotes SET timestamp = ? WHERE id = ?';
1349 $sth = C4::Context->dbh->prepare($query);
1351 DateTime::Format::MySQL->format_datetime( dt_from_string() ),
1358 sub _normalize_match_point {
1359 my $match_point = shift;
1360 (my $normalized_match_point) = $match_point =~ /([\d-]*[X]*)/;
1361 $normalized_match_point =~ s/-//g;
1363 return $normalized_match_point;
1368 return NormalizeISBN(
1371 format => 'ISBN-10',
1377 =head2 NormalizedISBN
1379 my $isbns = NormalizedISBN({
1381 strip_hyphens => [0,1],
1382 format => ['ISBN-10', 'ISBN-13']
1385 Returns an isbn validated by Business::ISBN.
1386 Optionally strips hyphens and/or forces the isbn
1387 to be of the specified format.
1389 If the string cannot be validated as an isbn,
1397 my $string = $params->{isbn};
1398 my $strip_hyphens = $params->{strip_hyphens};
1399 my $format = $params->{format};
1401 return unless $string;
1403 my $isbn = Business::ISBN->new($string);
1405 if ( $isbn && $isbn->is_valid() ) {
1407 if ( $format eq 'ISBN-10' ) {
1408 $isbn = $isbn->as_isbn10();
1410 elsif ( $format eq 'ISBN-13' ) {
1411 $isbn = $isbn->as_isbn13();
1413 return unless $isbn;
1415 if ($strip_hyphens) {
1416 $string = $isbn->as_string( [] );
1418 $string = $isbn->as_string();
1425 =head2 GetVariationsOfISBN
1427 my @isbns = GetVariationsOfISBN( $isbn );
1429 Returns a list of variations of the given isbn in
1430 both ISBN-10 and ISBN-13 formats, with and without
1433 In a scalar context, the isbns are returned as a
1434 string delimited by ' | '.
1438 sub GetVariationsOfISBN {
1441 return unless $isbn;
1445 push( @isbns, NormalizeISBN({ isbn => $isbn }) );
1446 push( @isbns, NormalizeISBN({ isbn => $isbn, format => 'ISBN-10' }) );
1447 push( @isbns, NormalizeISBN({ isbn => $isbn, format => 'ISBN-13' }) );
1448 push( @isbns, NormalizeISBN({ isbn => $isbn, format => 'ISBN-10', strip_hyphens => 1 }) );
1449 push( @isbns, NormalizeISBN({ isbn => $isbn, format => 'ISBN-13', strip_hyphens => 1 }) );
1451 # Strip out any "empty" strings from the array
1452 @isbns = grep { defined($_) && $_ =~ /\S/ } @isbns;
1454 return wantarray ? @isbns : join( " | ", @isbns );
1457 =head2 GetVariationsOfISBNs
1459 my @isbns = GetVariationsOfISBNs( @isbns );
1461 Returns a list of variations of the given isbns in
1462 both ISBN-10 and ISBN-13 formats, with and without
1465 In a scalar context, the isbns are returned as a
1466 string delimited by ' | '.
1470 sub GetVariationsOfISBNs {
1473 @isbns = map { GetVariationsOfISBN( $_ ) } @isbns;
1475 return wantarray ? @isbns : join( " | ", @isbns );
1478 =head2 IsKohaFieldLinked
1480 my $is_linked = IsKohaFieldLinked({
1481 kohafield => $kohafield,
1482 frameworkcode => $frameworkcode,
1485 Return 1 if the field is linked
1489 sub IsKohaFieldLinked {
1490 my ( $params ) = @_;
1491 my $kohafield = $params->{kohafield};
1492 my $frameworkcode = $params->{frameworkcode} || '';
1493 my $dbh = C4::Context->dbh;
1494 my $is_linked = $dbh->selectcol_arrayref( q|
1496 FROM marc_subfield_structure
1497 WHERE frameworkcode = ?
1499 |,{}, $frameworkcode, $kohafield );
1500 return $is_linked->[0];