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
62 &GetNormalizedOCLCNumber
72 @EXPORT_OK = qw( GetDailyQuote );
77 C4::Koha - Perl Module containing convenience functions for Koha scripts
85 Koha.pm provides many functions for Koha scripts.
93 $itemtypes = &GetItemTypes( style => $style );
95 Returns information about existing itemtypes.
98 style: either 'array' or 'hash', defaults to 'hash'.
99 'array' returns an arrayref,
100 'hash' return a hashref with the itemtype value as the key
102 build a HTML select with the following code :
104 =head3 in PERL SCRIPT
106 my $itemtypes = GetItemTypes;
108 foreach my $thisitemtype (sort keys %$itemtypes) {
109 my $selected = 1 if $thisitemtype eq $itemtype;
110 my %row =(value => $thisitemtype,
111 selected => $selected,
112 description => $itemtypes->{$thisitemtype}->{'description'},
114 push @itemtypesloop, \%row;
116 $template->param(itemtypeloop => \@itemtypesloop);
120 <form action='<!-- TMPL_VAR name="script_name" -->' method=post>
121 <select name="itemtype">
122 <option value="">Default</option>
123 <!-- TMPL_LOOP name="itemtypeloop" -->
124 <option value="<!-- TMPL_VAR name="value" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="description" --></option>
127 <input type=text name=searchfield value="<!-- TMPL_VAR name="searchfield" -->">
128 <input type="submit" value="OK" class="button">
135 my $style = defined( $params{'style'} ) ? $params{'style'} : 'hash';
137 require C4::Languages;
138 my $language = C4::Languages::getlanguage();
139 # returns a reference to a hash of references to itemtypes...
140 my $dbh = C4::Context->dbh;
144 itemtypes.description,
145 itemtypes.rentalcharge,
146 itemtypes.notforloan,
149 itemtypes.checkinmsg,
150 itemtypes.checkinmsgtype,
151 itemtypes.sip_media_type,
152 itemtypes.hideinopac,
153 itemtypes.searchcategory,
154 COALESCE( localization.translation, itemtypes.description ) AS translated_description
156 LEFT JOIN localization ON itemtypes.itemtype = localization.code
157 AND localization.entity = 'itemtypes'
158 AND localization.lang = ?
161 my $sth = $dbh->prepare($query);
162 $sth->execute( $language );
164 if ( $style eq 'hash' ) {
166 while ( my $IT = $sth->fetchrow_hashref ) {
167 $itemtypes{ $IT->{'itemtype'} } = $IT;
169 return ( \%itemtypes );
171 return [ sort { lc $a->{translated_description} cmp lc $b->{translated_description} } @{ $sth->fetchall_arrayref( {} ) } ];
175 =head2 GetItemTypesCategorized
177 $categories = GetItemTypesCategorized();
179 Returns a hashref containing search categories.
180 A search category will be put in the hash if at least one of its itemtypes is visible in OPAC.
181 The categories must be part of Authorized Values (ITEMTYPECAT)
185 sub GetItemTypesCategorized {
186 my $dbh = C4::Context->dbh;
187 # Order is important, so that partially hidden (some items are not visible in OPAC) search
188 # categories will be visible. hideinopac=0 must be last.
190 SELECT itemtype, description, imageurl, hideinopac, 0 as 'iscat' FROM itemtypes WHERE ISNULL(searchcategory) or length(searchcategory) = 0
192 SELECT DISTINCT searchcategory AS `itemtype`,
193 authorised_values.lib_opac AS description,
194 authorised_values.imageurl AS imageurl,
195 hideinopac, 1 as 'iscat'
197 LEFT JOIN authorised_values ON searchcategory = authorised_value
198 WHERE searchcategory > '' and hideinopac=1
200 SELECT DISTINCT searchcategory AS `itemtype`,
201 authorised_values.lib_opac AS description,
202 authorised_values.imageurl AS imageurl,
203 hideinopac, 1 as 'iscat'
205 LEFT JOIN authorised_values ON searchcategory = authorised_value
206 WHERE searchcategory > '' and hideinopac=0
208 return ($dbh->selectall_hashref($query,'itemtype'));
211 =head2 GetItemTypesByCategory
213 @results = GetItemTypesByCategory( $searchcategory );
215 Returns the itemtype code of all itemtypes included in a searchcategory.
219 sub GetItemTypesByCategory {
223 my $dbh = C4::Context->dbh;
224 my $query = qq|SELECT itemtype FROM itemtypes WHERE searchcategory=?|;
225 my $tmp=$dbh->selectcol_arrayref($query,undef,$category);
231 $frameworks = &getframework();
233 Returns information about existing frameworks
235 build a HTML select with the following code :
237 =head3 in PERL SCRIPT
239 my $frameworks = getframeworks();
241 foreach my $thisframework (keys %$frameworks) {
242 my $selected = 1 if $thisframework eq $frameworkcode;
244 value => $thisframework,
245 selected => $selected,
246 description => $frameworks->{$thisframework}->{'frameworktext'},
248 push @frameworksloop, \%row;
250 $template->param(frameworkloop => \@frameworksloop);
254 <form action="[% script_name %] method=post>
255 <select name="frameworkcode">
256 <option value="">Default</option>
257 [% FOREACH framework IN frameworkloop %]
258 [% IF ( framework.selected ) %]
259 <option value="[% framework.value %]" selected="selected">[% framework.description %]</option>
261 <option value="[% framework.value %]">[% framework.description %]</option>
265 <input type=text name=searchfield value="[% searchfield %]">
266 <input type="submit" value="OK" class="button">
273 # returns a reference to a hash of references to branches...
275 my $dbh = C4::Context->dbh;
276 my $sth = $dbh->prepare("select * from biblio_framework");
278 while ( my $IT = $sth->fetchrow_hashref ) {
279 $itemtypes{ $IT->{'frameworkcode'} } = $IT;
281 return ( \%itemtypes );
284 =head2 GetFrameworksLoop
286 $frameworks = GetFrameworksLoop( $frameworkcode );
288 Returns the loop suggested on getframework(), but ordered by framework description.
290 build a HTML select with the following code :
292 =head3 in PERL SCRIPT
294 $template->param( frameworkloop => GetFrameworksLoop( $frameworkcode ) );
298 Same as getframework()
300 <form action="[% script_name %] method=post>
301 <select name="frameworkcode">
302 <option value="">Default</option>
303 [% FOREACH framework IN frameworkloop %]
304 [% IF ( framework.selected ) %]
305 <option value="[% framework.value %]" selected="selected">[% framework.description %]</option>
307 <option value="[% framework.value %]">[% framework.description %]</option>
311 <input type=text name=searchfield value="[% searchfield %]">
312 <input type="submit" value="OK" class="button">
317 sub GetFrameworksLoop {
318 my $frameworkcode = shift;
319 my $frameworks = getframeworks();
321 foreach my $thisframework (sort { uc($frameworks->{$a}->{'frameworktext'}) cmp uc($frameworks->{$b}->{'frameworktext'}) } keys %$frameworks) {
322 my $selected = ( $thisframework eq $frameworkcode ) ? 1 : undef;
324 value => $thisframework,
325 selected => $selected,
326 description => $frameworks->{$thisframework}->{'frameworktext'},
328 push @frameworkloop, \%row;
330 return \@frameworkloop;
333 =head2 getframeworkinfo
335 $frameworkinfo = &getframeworkinfo($frameworkcode);
337 Returns information about an frameworkcode.
341 sub getframeworkinfo {
342 my ($frameworkcode) = @_;
343 my $dbh = C4::Context->dbh;
345 $dbh->prepare("select * from biblio_framework where frameworkcode=?");
346 $sth->execute($frameworkcode);
347 my $res = $sth->fetchrow_hashref;
351 =head2 getitemtypeinfo
353 $itemtype = &getitemtypeinfo($itemtype, [$interface]);
355 Returns information about an itemtype. The optional $interface argument
356 sets which interface ('opac' or 'intranet') to return the imageurl for.
357 Defaults to intranet.
361 sub getitemtypeinfo {
362 my ($itemtype, $interface) = @_;
363 my $dbh = C4::Context->dbh;
364 require C4::Languages;
365 my $language = C4::Languages::getlanguage();
366 my $it = $dbh->selectrow_hashref(q|
369 itemtypes.description,
370 itemtypes.rentalcharge,
371 itemtypes.notforloan,
374 itemtypes.checkinmsg,
375 itemtypes.checkinmsgtype,
376 itemtypes.sip_media_type,
377 COALESCE( localization.translation, itemtypes.description ) AS translated_description
379 LEFT JOIN localization ON itemtypes.itemtype = localization.code
380 AND localization.entity = 'itemtypes'
381 AND localization.lang = ?
382 WHERE itemtypes.itemtype = ?
383 |, undef, $language, $itemtype );
385 $it->{imageurl} = getitemtypeimagelocation( ( ( defined $interface && $interface eq 'opac' ) ? 'opac' : 'intranet' ), $it->{imageurl} );
390 =head2 getitemtypeimagedir
392 my $directory = getitemtypeimagedir( 'opac' );
394 pass in 'opac' or 'intranet'. Defaults to 'opac'.
396 returns the full path to the appropriate directory containing images.
400 sub getitemtypeimagedir {
401 my $src = shift || 'opac';
402 if ($src eq 'intranet') {
403 return C4::Context->config('intrahtdocs') . '/' .C4::Context->preference('template') . '/img/itemtypeimg';
405 return C4::Context->config('opachtdocs') . '/' . C4::Context->preference('opacthemes') . '/itemtypeimg';
409 sub getitemtypeimagesrc {
410 my $src = shift || 'opac';
411 if ($src eq 'intranet') {
412 return '/intranet-tmpl' . '/' . C4::Context->preference('template') . '/img/itemtypeimg';
414 return '/opac-tmpl' . '/' . C4::Context->preference('opacthemes') . '/itemtypeimg';
418 sub getitemtypeimagelocation {
419 my ( $src, $image ) = @_;
421 return '' if ( !$image );
424 my $scheme = ( URI::Split::uri_split( $image ) )[0];
426 return $image if ( $scheme );
428 return getitemtypeimagesrc( $src ) . '/' . $image;
431 =head3 _getImagesFromDirectory
433 Find all of the image files in a directory in the filesystem
435 parameters: a directory name
437 returns: a list of images in that directory.
439 Notes: this does not traverse into subdirectories. See
440 _getSubdirectoryNames for help with that.
441 Images are assumed to be files with .gif or .png file extensions.
442 The image names returned do not have the directory name on them.
446 sub _getImagesFromDirectory {
447 my $directoryname = shift;
448 return unless defined $directoryname;
449 return unless -d $directoryname;
451 if ( opendir ( my $dh, $directoryname ) ) {
452 my @images = grep { /\.(gif|png)$/i } readdir( $dh );
454 @images = sort(@images);
457 warn "unable to opendir $directoryname: $!";
462 =head3 _getSubdirectoryNames
464 Find all of the directories in a directory in the filesystem
466 parameters: a directory name
468 returns: a list of subdirectories in that directory.
470 Notes: this does not traverse into subdirectories. Only the first
471 level of subdirectories are returned.
472 The directory names returned don't have the parent directory name on them.
476 sub _getSubdirectoryNames {
477 my $directoryname = shift;
478 return unless defined $directoryname;
479 return unless -d $directoryname;
481 if ( opendir ( my $dh, $directoryname ) ) {
482 my @directories = grep { -d File::Spec->catfile( $directoryname, $_ ) && ! ( /^\./ ) } readdir( $dh );
486 warn "unable to opendir $directoryname: $!";
493 returns: a listref of hashrefs. Each hash represents another collection of images.
495 { imagesetname => 'npl', # the name of the image set (npl is the original one)
496 images => listref of image hashrefs
499 each image is represented by a hashref like this:
501 { KohaImage => 'npl/image.gif',
502 StaffImageUrl => '/intranet-tmpl/prog/img/itemtypeimg/npl/image.gif',
503 OpacImageURL => '/opac-tmpl/prog/itemtypeimg/npl/image.gif'
504 checked => 0 or 1: was this the image passed to this method?
505 Note: I'd like to remove this somehow.
512 my $checked = $params{'checked'} || '';
514 my $paths = { staff => { filesystem => getitemtypeimagedir('intranet'),
515 url => getitemtypeimagesrc('intranet'),
517 opac => { filesystem => getitemtypeimagedir('opac'),
518 url => getitemtypeimagesrc('opac'),
522 my @imagesets = (); # list of hasrefs of image set data to pass to template
523 my @subdirectories = _getSubdirectoryNames( $paths->{'staff'}{'filesystem'} );
524 foreach my $imagesubdir ( @subdirectories ) {
525 warn $imagesubdir if $DEBUG;
526 my @imagelist = (); # hashrefs of image info
527 my @imagenames = _getImagesFromDirectory( File::Spec->catfile( $paths->{'staff'}{'filesystem'}, $imagesubdir ) );
528 my $imagesetactive = 0;
529 foreach my $thisimage ( @imagenames ) {
531 { KohaImage => "$imagesubdir/$thisimage",
532 StaffImageUrl => join( '/', $paths->{'staff'}{'url'}, $imagesubdir, $thisimage ),
533 OpacImageUrl => join( '/', $paths->{'opac'}{'url'}, $imagesubdir, $thisimage ),
534 checked => "$imagesubdir/$thisimage" eq $checked ? 1 : 0,
537 $imagesetactive = 1 if "$imagesubdir/$thisimage" eq $checked;
539 push @imagesets, { imagesetname => $imagesubdir,
540 imagesetactive => $imagesetactive,
541 images => \@imagelist };
549 $printers = &GetPrinters();
550 @queues = keys %$printers;
552 Returns information about existing printer queues.
554 C<$printers> is a reference-to-hash whose keys are the print queues
555 defined in the printers table of the Koha database. The values are
556 references-to-hash, whose keys are the fields in the printers table.
562 my $dbh = C4::Context->dbh;
563 my $sth = $dbh->prepare("select * from printers");
565 while ( my $printer = $sth->fetchrow_hashref ) {
566 $printers{ $printer->{'printqueue'} } = $printer;
568 return ( \%printers );
573 $printer = GetPrinter( $query, $printers );
578 my ( $query, $printers ) = @_; # get printer for this query from printers
579 my $printer = $query->param('printer');
580 my %cookie = $query->cookie('userenv');
581 ($printer) || ( $printer = $cookie{'printer'} ) || ( $printer = '' );
582 ( $printers->{$printer} ) || ( $printer = ( keys %$printers )[0] );
588 Returns the number of pages to display in a pagination bar, given the number
589 of items and the number of items per page.
594 my ( $nb_items, $nb_items_per_page ) = @_;
596 return int( ( $nb_items - 1 ) / $nb_items_per_page ) + 1;
601 (@themes) = &getallthemes('opac');
602 (@themes) = &getallthemes('intranet');
604 Returns an array of all available themes.
612 if ( $type eq 'intranet' ) {
613 $htdocs = C4::Context->config('intrahtdocs');
616 $htdocs = C4::Context->config('opachtdocs');
618 opendir D, "$htdocs";
619 my @dirlist = readdir D;
620 foreach my $directory (@dirlist) {
621 next if $directory eq 'lib';
622 -d "$htdocs/$directory/en" and push @themes, $directory;
629 if ( C4::Context->preference("marcflavour") eq "UNIMARC" ) {
634 tags => [ qw/ 600ab 601ab 602a 604at 605a 606ax 610a / ],
640 tags => [ qw/ 607a / ],
646 tags => [ qw/ 500a 501a 503a / ],
652 tags => [ qw/ 700ab 701ab 702ab / ],
653 sep => C4::Context->preference("UNIMARCAuthorsFacetsSeparator"),
658 tags => [ qw/ 225a / ],
664 tags => [ qw/ 995e / ],
668 unless ( Koha::Libraries->search->count == 1 )
670 my $DisplayLibraryFacets = C4::Context->preference('DisplayLibraryFacets');
671 if ( $DisplayLibraryFacets eq 'both'
672 || $DisplayLibraryFacets eq 'holding' )
677 idx => 'holdingbranch',
678 label => 'HoldingLibrary',
679 tags => [qw / 995c /],
684 if ( $DisplayLibraryFacets eq 'both'
685 || $DisplayLibraryFacets eq 'home' )
691 label => 'HomeLibrary',
692 tags => [qw / 995b /],
703 tags => [ qw/ 650a / ],
708 # label => 'People and Organizations',
709 # tags => [ qw/ 600a 610a 611a / ],
715 tags => [ qw/ 651a / ],
721 tags => [ qw/ 630a / ],
727 tags => [ qw/ 100a 110a 700a / ],
733 tags => [ qw/ 440a 490a / ],
738 label => 'ItemTypes',
739 tags => [ qw/ 952y 942c / ],
745 tags => [ qw / 952c / ],
749 unless ( Koha::Libraries->search->count == 1 )
751 my $DisplayLibraryFacets = C4::Context->preference('DisplayLibraryFacets');
752 if ( $DisplayLibraryFacets eq 'both'
753 || $DisplayLibraryFacets eq 'holding' )
758 idx => 'holdingbranch',
759 label => 'HoldingLibrary',
760 tags => [qw / 952b /],
765 if ( $DisplayLibraryFacets eq 'both'
766 || $DisplayLibraryFacets eq 'home' )
772 label => 'HomeLibrary',
773 tags => [qw / 952a /],
784 Return a href where a key is associated to a href. You give a query,
785 the name of the key among the fields returned by the query. If you
786 also give as third argument the name of the value, the function
787 returns a href of scalar. The optional 4th argument is an arrayref of
788 items passed to the C<execute()> call. It is designed to bind
789 parameters to any placeholders in your SQL.
798 # generic href of any information on the item, href of href.
799 my $iteminfos_of = get_infos_of($query, 'itemnumber');
800 print $iteminfos_of->{$itemnumber}{barcode};
802 # specific information, href of scalar
803 my $barcode_of_item = get_infos_of($query, 'itemnumber', 'barcode');
804 print $barcode_of_item->{$itemnumber};
809 my ( $query, $key_name, $value_name, $bind_params ) = @_;
811 my $dbh = C4::Context->dbh;
813 my $sth = $dbh->prepare($query);
814 $sth->execute( @$bind_params );
817 while ( my $row = $sth->fetchrow_hashref ) {
818 if ( defined $value_name ) {
819 $infos_of{ $row->{$key_name} } = $row->{$value_name};
822 $infos_of{ $row->{$key_name} } = $row;
830 =head2 get_notforloan_label_of
832 my $notforloan_label_of = get_notforloan_label_of();
834 Each authorised value of notforloan (information available in items and
835 itemtypes) is link to a single label.
837 Returns a href where keys are authorised values and values are corresponding
840 foreach my $authorised_value (keys %{$notforloan_label_of}) {
842 "authorised_value: %s => %s\n",
844 $notforloan_label_of->{$authorised_value}
850 # FIXME - why not use GetAuthorisedValues ??
852 sub get_notforloan_label_of {
853 my $dbh = C4::Context->dbh;
856 SELECT authorised_value
857 FROM marc_subfield_structure
858 WHERE kohafield = \'items.notforloan\'
861 my $sth = $dbh->prepare($query);
863 my ($statuscode) = $sth->fetchrow_array();
868 FROM authorised_values
871 $sth = $dbh->prepare($query);
872 $sth->execute($statuscode);
873 my %notforloan_label_of;
874 while ( my $row = $sth->fetchrow_hashref ) {
875 $notforloan_label_of{ $row->{authorised_value} } = $row->{lib};
879 return \%notforloan_label_of;
882 =head2 GetAuthorisedValues
884 $authvalues = GetAuthorisedValues([$category]);
886 This function returns all authorised values from the'authorised_value' table in a reference to array of hashrefs.
888 C<$category> returns authorised values for just one category (optional).
890 C<$opac> If set to a true value, displays OPAC descriptions rather than normal ones when they exist.
894 sub GetAuthorisedValues {
895 my ( $category, $opac ) = @_;
897 # Is this cached already?
898 $opac = $opac ? 1 : 0; # normalise to be safe
900 C4::Context->userenv ? C4::Context->userenv->{"branch"} : "";
902 "AuthorisedValues-$category-$opac-$branch_limit";
903 my $cache = Koha::Caches->get_instance();
904 my $result = $cache->get_from_cache($cache_key);
905 return $result if $result;
908 my $dbh = C4::Context->dbh;
911 FROM authorised_values av
914 LEFT JOIN authorised_values_branches ON ( id = av_id )
919 push @where_strings, "category = ?";
920 push @where_args, $category;
923 push @where_strings, "( branchcode = ? OR branchcode IS NULL )";
924 push @where_args, $branch_limit;
926 if(@where_strings > 0) {
927 $query .= " WHERE " . join(" AND ", @where_strings);
929 $query .= ' ORDER BY category, ' . (
930 $opac ? 'COALESCE(lib_opac, lib)'
934 my $sth = $dbh->prepare($query);
936 $sth->execute( @where_args );
937 while (my $data=$sth->fetchrow_hashref) {
938 if ($opac && $data->{lib_opac}) {
939 $data->{lib} = $data->{lib_opac};
941 push @results, $data;
945 $cache->set_in_cache( $cache_key, \@results, { expiry => 5 } );
949 =head2 GetAuthorisedValueCategories
951 $auth_categories = GetAuthorisedValueCategories();
953 Return an arrayref of all of the available authorised
958 sub GetAuthorisedValueCategories {
959 my $dbh = C4::Context->dbh;
960 my $sth = $dbh->prepare("SELECT DISTINCT category FROM authorised_values ORDER BY category");
963 while (defined (my $category = $sth->fetchrow_array) ) {
964 push @results, $category;
969 =head2 GetAuthorisedValueByCode
971 $authorised_value = GetAuthorisedValueByCode( $category, $authvalcode, $opac );
973 Return the lib attribute from authorised_values from the row identified
974 by the passed category and code
978 sub GetAuthorisedValueByCode {
979 my ( $category, $authvalcode, $opac ) = @_;
981 my $field = $opac ? 'lib_opac' : 'lib';
982 my $dbh = C4::Context->dbh;
983 my $sth = $dbh->prepare("SELECT $field FROM authorised_values WHERE category=? AND authorised_value =?");
984 $sth->execute( $category, $authvalcode );
985 while ( my $data = $sth->fetchrow_hashref ) {
986 return $data->{ $field };
990 =head2 GetKohaAuthorisedValues
992 Takes $kohafield, $fwcode as parameters.
994 If $opac parameter is set to a true value, displays OPAC descriptions rather than normal ones when they exist.
996 Returns hashref of Code => description
998 Returns undef if no authorised value category is defined for the kohafield.
1002 sub GetKohaAuthorisedValues {
1003 my ( $kohafield, $fwcode, $opac ) = @_;
1004 $fwcode = '' unless $fwcode;
1006 my $dbh = C4::Context->dbh;
1008 my $avs = Koha::AuthorisedValues->search_by_koha_field( { frameworkcode => $fwcode, kohafield => $kohafield } );
1009 return {} unless $avs->count;
1011 while ( my $av = $avs->next ) {
1012 $values->{ $av->authorised_value } = $opac ? $av->opac_description : $av->lib;
1017 =head2 GetKohaAuthorisedValuesMapping
1019 Takes a hash as a parameter. The interface key indicates the
1020 description to use in the mapping.
1023 "{kohafield},{frameworkcode},{authorised_value}" => "{description}"
1024 for all the kohafields, frameworkcodes, and authorised values.
1026 Returns undef if nothing is found.
1030 sub GetKohaAuthorisedValuesMapping {
1031 my ($parameter) = @_;
1032 my $interface = $parameter->{'interface'} // '';
1034 my $query_mapping = q{
1035 SELECT TA.kohafield,TA.authorised_value AS category,
1036 TA.frameworkcode,TB.authorised_value,
1037 IF(TB.lib_opac>'',TB.lib_opac,TB.lib) AS OPAC,
1038 TB.lib AS Intranet,TB.lib_opac
1039 FROM marc_subfield_structure AS TA JOIN
1040 authorised_values as TB ON
1041 TA.authorised_value=TB.category
1042 WHERE TA.kohafield>'' AND TA.authorised_value>'';
1044 my $dbh = C4::Context->dbh;
1045 my $sth = $dbh->prepare($query_mapping);
1048 if ($interface eq 'opac') {
1049 while (my $row = $sth->fetchrow_hashref) {
1050 $avmapping->{$row->{kohafield}.",".$row->{frameworkcode}.",".$row->{authorised_value}} = $row->{OPAC};
1054 while (my $row = $sth->fetchrow_hashref) {
1055 $avmapping->{$row->{kohafield}.",".$row->{frameworkcode}.",".$row->{authorised_value}} = $row->{Intranet};
1063 my $escaped_string = C4::Koha::xml_escape($string);
1065 Convert &, <, >, ', and " in a string to XML entities
1071 return '' unless defined $str;
1072 $str =~ s/&/&/g;
1075 $str =~ s/'/'/g;
1076 $str =~ s/"/"/g;
1080 =head2 display_marc_indicators
1082 my $display_form = C4::Koha::display_marc_indicators($field);
1084 C<$field> is a MARC::Field object
1086 Generate a display form of the indicators of a variable
1087 MARC field, replacing any blanks with '#'.
1091 sub display_marc_indicators {
1093 my $indicators = '';
1094 if ($field && $field->tag() >= 10) {
1095 $indicators = $field->indicator(1) . $field->indicator(2);
1096 $indicators =~ s/ /#/g;
1101 sub GetNormalizedUPC {
1102 my ($marcrecord,$marcflavour) = @_;
1104 return unless $marcrecord;
1105 if ($marcflavour eq 'UNIMARC') {
1106 my @fields = $marcrecord->field('072');
1107 foreach my $field (@fields) {
1108 my $upc = _normalize_match_point($field->subfield('a'));
1115 else { # assume marc21 if not unimarc
1116 my @fields = $marcrecord->field('024');
1117 foreach my $field (@fields) {
1118 my $indicator = $field->indicator(1);
1119 my $upc = _normalize_match_point($field->subfield('a'));
1120 if ($upc && $indicator == 1 ) {
1127 # Normalizes and returns the first valid ISBN found in the record
1128 # ISBN13 are converted into ISBN10. This is required to get some book cover images.
1129 sub GetNormalizedISBN {
1130 my ($isbn,$marcrecord,$marcflavour) = @_;
1132 # Koha attempts to store multiple ISBNs in biblioitems.isbn, separated by " | "
1133 # anything after " | " should be removed, along with the delimiter
1134 ($isbn) = split(/\|/, $isbn );
1135 return _isbn_cleanup($isbn);
1138 return unless $marcrecord;
1140 if ($marcflavour eq 'UNIMARC') {
1141 my @fields = $marcrecord->field('010');
1142 foreach my $field (@fields) {
1143 my $isbn = $field->subfield('a');
1145 return _isbn_cleanup($isbn);
1149 else { # assume marc21 if not unimarc
1150 my @fields = $marcrecord->field('020');
1151 foreach my $field (@fields) {
1152 $isbn = $field->subfield('a');
1154 return _isbn_cleanup($isbn);
1160 sub GetNormalizedEAN {
1161 my ($marcrecord,$marcflavour) = @_;
1163 return unless $marcrecord;
1165 if ($marcflavour eq 'UNIMARC') {
1166 my @fields = $marcrecord->field('073');
1167 foreach my $field (@fields) {
1168 my $ean = _normalize_match_point($field->subfield('a'));
1174 else { # assume marc21 if not unimarc
1175 my @fields = $marcrecord->field('024');
1176 foreach my $field (@fields) {
1177 my $indicator = $field->indicator(1);
1178 my $ean = _normalize_match_point($field->subfield('a'));
1179 if ( $ean && $indicator == 3 ) {
1186 sub GetNormalizedOCLCNumber {
1187 my ($marcrecord,$marcflavour) = @_;
1188 return unless $marcrecord;
1190 if ($marcflavour ne 'UNIMARC' ) {
1191 my @fields = $marcrecord->field('035');
1192 foreach my $field (@fields) {
1193 my $oclc = $field->subfield('a');
1194 if ($oclc =~ /OCoLC/) {
1195 $oclc =~ s/\(OCoLC\)//;
1205 sub GetAuthvalueDropbox {
1206 my ( $authcat, $default ) = @_;
1207 my $branch_limit = C4::Context->userenv ? C4::Context->userenv->{"branch"} : "";
1208 my $dbh = C4::Context->dbh;
1212 FROM authorised_values
1215 LEFT JOIN authorised_values_branches ON ( id = av_id )
1220 $query .= " AND ( branchcode = ? OR branchcode IS NULL )" if $branch_limit;
1221 $query .= " GROUP BY lib ORDER BY category, lib, lib_opac";
1222 my $sth = $dbh->prepare($query);
1223 $sth->execute( $authcat, $branch_limit ? $branch_limit : () );
1226 my $option_list = [];
1227 my @authorised_values = ( q{} );
1228 while (my $av = $sth->fetchrow_hashref) {
1229 push @{$option_list}, {
1230 value => $av->{authorised_value},
1231 label => $av->{lib},
1232 default => ($default eq $av->{authorised_value}),
1236 if ( @{$option_list} ) {
1237 return $option_list;
1243 =head2 GetDailyQuote($opts)
1245 Takes a hashref of options
1247 Currently supported options are:
1249 'id' An exact quote id
1250 'random' Select a random quote
1251 noop When no option is passed in, this sub will return the quote timestamped for the current day
1253 The function returns an anonymous hash following this format:
1256 'source' => 'source-of-quote',
1257 'timestamp' => 'timestamp-value',
1258 'text' => 'text-of-quote',
1264 # This is definitely a candidate for some sort of caching once we finally settle caching/persistence issues...
1265 # at least for default option
1269 my $dbh = C4::Context->dbh;
1274 $query = 'SELECT * FROM quotes WHERE id = ?';
1275 $sth = $dbh->prepare($query);
1276 $sth->execute($opts{'id'});
1277 $quote = $sth->fetchrow_hashref();
1279 elsif ($opts{'random'}) {
1280 # Fall through... we also return a random quote as a catch-all if all else fails
1283 $query = 'SELECT * FROM quotes WHERE timestamp LIKE CONCAT(CURRENT_DATE,\'%\') ORDER BY timestamp DESC LIMIT 0,1';
1284 $sth = $dbh->prepare($query);
1286 $quote = $sth->fetchrow_hashref();
1288 unless ($quote) { # if there are not matches, choose a random quote
1289 # get a list of all available quote ids
1290 $sth = C4::Context->dbh->prepare('SELECT count(*) FROM quotes;');
1292 my $range = ($sth->fetchrow_array)[0];
1293 # chose a random id within that range if there is more than one quote
1294 my $offset = int(rand($range));
1296 $query = 'SELECT * FROM quotes ORDER BY id LIMIT 1 OFFSET ?';
1297 $sth = C4::Context->dbh->prepare($query);
1298 # see http://www.perlmonks.org/?node_id=837422 for why
1299 # we're being verbose and using bind_param
1300 $sth->bind_param(1, $offset, SQL_INTEGER);
1302 $quote = $sth->fetchrow_hashref();
1303 # update the timestamp for that quote
1304 $query = 'UPDATE quotes SET timestamp = ? WHERE id = ?';
1305 $sth = C4::Context->dbh->prepare($query);
1307 DateTime::Format::MySQL->format_datetime( dt_from_string() ),
1314 sub _normalize_match_point {
1315 my $match_point = shift;
1316 (my $normalized_match_point) = $match_point =~ /([\d-]*[X]*)/;
1317 $normalized_match_point =~ s/-//g;
1319 return $normalized_match_point;
1324 return NormalizeISBN(
1327 format => 'ISBN-10',
1333 =head2 NormalizedISBN
1335 my $isbns = NormalizedISBN({
1337 strip_hyphens => [0,1],
1338 format => ['ISBN-10', 'ISBN-13']
1341 Returns an isbn validated by Business::ISBN.
1342 Optionally strips hyphens and/or forces the isbn
1343 to be of the specified format.
1345 If the string cannot be validated as an isbn,
1353 my $string = $params->{isbn};
1354 my $strip_hyphens = $params->{strip_hyphens};
1355 my $format = $params->{format};
1357 return unless $string;
1359 my $isbn = Business::ISBN->new($string);
1361 if ( $isbn && $isbn->is_valid() ) {
1363 if ( $format eq 'ISBN-10' ) {
1364 $isbn = $isbn->as_isbn10();
1366 elsif ( $format eq 'ISBN-13' ) {
1367 $isbn = $isbn->as_isbn13();
1369 return unless $isbn;
1371 if ($strip_hyphens) {
1372 $string = $isbn->as_string( [] );
1374 $string = $isbn->as_string();
1381 =head2 GetVariationsOfISBN
1383 my @isbns = GetVariationsOfISBN( $isbn );
1385 Returns a list of variations of the given isbn in
1386 both ISBN-10 and ISBN-13 formats, with and without
1389 In a scalar context, the isbns are returned as a
1390 string delimited by ' | '.
1394 sub GetVariationsOfISBN {
1397 return unless $isbn;
1401 push( @isbns, NormalizeISBN({ isbn => $isbn }) );
1402 push( @isbns, NormalizeISBN({ isbn => $isbn, format => 'ISBN-10' }) );
1403 push( @isbns, NormalizeISBN({ isbn => $isbn, format => 'ISBN-13' }) );
1404 push( @isbns, NormalizeISBN({ isbn => $isbn, format => 'ISBN-10', strip_hyphens => 1 }) );
1405 push( @isbns, NormalizeISBN({ isbn => $isbn, format => 'ISBN-13', strip_hyphens => 1 }) );
1407 # Strip out any "empty" strings from the array
1408 @isbns = grep { defined($_) && $_ =~ /\S/ } @isbns;
1410 return wantarray ? @isbns : join( " | ", @isbns );
1413 =head2 GetVariationsOfISBNs
1415 my @isbns = GetVariationsOfISBNs( @isbns );
1417 Returns a list of variations of the given isbns in
1418 both ISBN-10 and ISBN-13 formats, with and without
1421 In a scalar context, the isbns are returned as a
1422 string delimited by ' | '.
1426 sub GetVariationsOfISBNs {
1429 @isbns = map { GetVariationsOfISBN( $_ ) } @isbns;
1431 return wantarray ? @isbns : join( " | ", @isbns );
1434 =head2 IsKohaFieldLinked
1436 my $is_linked = IsKohaFieldLinked({
1437 kohafield => $kohafield,
1438 frameworkcode => $frameworkcode,
1441 Return 1 if the field is linked
1445 sub IsKohaFieldLinked {
1446 my ( $params ) = @_;
1447 my $kohafield = $params->{kohafield};
1448 my $frameworkcode = $params->{frameworkcode} || '';
1449 my $dbh = C4::Context->dbh;
1450 my $is_linked = $dbh->selectcol_arrayref( q|
1452 FROM marc_subfield_structure
1453 WHERE frameworkcode = ?
1455 |,{}, $frameworkcode, $kohafield );
1456 return $is_linked->[0];