Bug 16458: Update library when a guarantor is set
[koha.git] / C4 / Koha.pm
blobb2a009a06d5d918ca6b2a59c74f1fb64576ef2e3
1 package C4::Koha;
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>.
23 use strict;
24 #use warnings; FIXME - Bug 2505
26 use C4::Context;
27 use C4::Branch qw(GetBranchesCount);
28 use Koha::Cache;
29 use Koha::DateUtils qw(dt_from_string);
30 use DateTime::Format::MySQL;
31 use Business::ISBN;
32 use autouse 'Data::cselectall_arrayref' => qw(Dumper);
33 use DBI qw(:sql_types);
34 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK $DEBUG);
36 BEGIN {
37 $VERSION = 3.07.00.049;
38 require Exporter;
39 @ISA = qw(Exporter);
40 @EXPORT = qw(
41 &slashifyDate
42 &subfield_is_koha_internal_p
43 &GetPrinters &GetPrinter
44 &GetItemTypes &getitemtypeinfo
45 &GetItemTypesCategorized &GetItemTypesByCategory
46 &GetSupportName &GetSupportList
47 &get_itemtypeinfos_of
48 &getframeworks &getframeworkinfo
49 &GetFrameworksLoop
50 &getauthtypes &getauthtype
51 &getallthemes
52 &getFacets
53 &displayServers
54 &getnbpages
55 &get_infos_of
56 &get_notforloan_label_of
57 &getitemtypeimagedir
58 &getitemtypeimagesrc
59 &getitemtypeimagelocation
60 &GetAuthorisedValues
61 &GetAuthorisedValueCategories
62 &IsAuthorisedValueCategory
63 &GetKohaAuthorisedValues
64 &GetKohaAuthorisedValuesFromField
65 &GetKohaAuthorisedValuesMapping
66 &GetKohaAuthorisedValueLib
67 &GetAuthorisedValueByCode
68 &GetKohaImageurlFromAuthorisedValues
69 &GetAuthValCode
70 &AddAuthorisedValue
71 &GetNormalizedUPC
72 &GetNormalizedISBN
73 &GetNormalizedEAN
74 &GetNormalizedOCLCNumber
75 &xml_escape
77 &GetVariationsOfISBN
78 &GetVariationsOfISBNs
79 &NormalizeISBN
81 $DEBUG
83 $DEBUG = 0;
84 @EXPORT_OK = qw( GetDailyQuote );
87 =head1 NAME
89 C4::Koha - Perl Module containing convenience functions for Koha scripts
91 =head1 SYNOPSIS
93 use C4::Koha;
95 =head1 DESCRIPTION
97 Koha.pm provides many functions for Koha scripts.
99 =head1 FUNCTIONS
101 =cut
103 =head2 slashifyDate
105 $slash_date = &slashifyDate($dash_date);
107 Takes a string of the form "DD-MM-YYYY" (or anything separated by
108 dashes), converts it to the form "YYYY/MM/DD", and returns the result.
110 =cut
112 sub slashifyDate {
114 # accepts a date of the form xx-xx-xx[xx] and returns it in the
115 # form xx/xx/xx[xx]
116 my @dateOut = split( '-', shift );
117 return ("$dateOut[2]/$dateOut[1]/$dateOut[0]");
120 # FIXME.. this should be moved to a MARC-specific module
121 sub subfield_is_koha_internal_p {
122 my ($subfield) = @_;
124 # We could match on 'lib' and 'tab' (and 'mandatory', & more to come!)
125 # But real MARC subfields are always single-character
126 # so it really is safer just to check the length
128 return length $subfield != 1;
131 =head2 GetSupportName
133 $itemtypename = &GetSupportName($codestring);
135 Returns a string with the name of the itemtype.
137 =cut
139 sub GetSupportName{
140 my ($codestring)=@_;
141 return if (! $codestring);
142 my $resultstring;
143 my $advanced_search_types = C4::Context->preference("AdvancedSearchTypes");
144 if (!$advanced_search_types or $advanced_search_types eq 'itemtypes') {
145 my $query = qq|
146 SELECT description
147 FROM itemtypes
148 WHERE itemtype=?
149 order by description
151 my $sth = C4::Context->dbh->prepare($query);
152 $sth->execute($codestring);
153 ($resultstring)=$sth->fetchrow;
154 return $resultstring;
155 } else {
156 my $sth =
157 C4::Context->dbh->prepare(
158 "SELECT lib FROM authorised_values WHERE category = ? AND authorised_value = ?"
160 $sth->execute( $advanced_search_types, $codestring );
161 my $data = $sth->fetchrow_hashref;
162 return $$data{'lib'};
166 =head2 GetSupportList
168 $itemtypes = &GetSupportList();
170 Returns an array ref containing informations about Support (since itemtype is rather a circulation code when item-level-itypes is used).
172 build a HTML select with the following code :
174 =head3 in PERL SCRIPT
176 my $itemtypes = GetSupportList();
177 $template->param(itemtypeloop => $itemtypes);
179 =head3 in TEMPLATE
181 <select name="itemtype" id="itemtype">
182 <option value=""></option>
183 [% FOREACH itemtypeloo IN itemtypeloop %]
184 [% IF ( itemtypeloo.selected ) %]
185 <option value="[% itemtypeloo.itemtype %]" selected="selected">[% itemtypeloo.description %]</option>
186 [% ELSE %]
187 <option value="[% itemtypeloo.itemtype %]">[% itemtypeloo.description %]</option>
188 [% END %]
189 [% END %]
190 </select>
192 =cut
194 sub GetSupportList{
195 my $advanced_search_types = C4::Context->preference("AdvancedSearchTypes");
196 if (!$advanced_search_types or $advanced_search_types =~ /itemtypes/) {
197 return GetItemTypes( style => 'array' );
198 } else {
199 my $advsearchtypes = GetAuthorisedValues($advanced_search_types);
200 my @results= map {{itemtype=>$$_{authorised_value},description=>$$_{lib},imageurl=>$$_{imageurl}}} @$advsearchtypes;
201 return \@results;
204 =head2 GetItemTypes
206 $itemtypes = &GetItemTypes( style => $style );
208 Returns information about existing itemtypes.
210 Params:
211 style: either 'array' or 'hash', defaults to 'hash'.
212 'array' returns an arrayref,
213 'hash' return a hashref with the itemtype value as the key
215 build a HTML select with the following code :
217 =head3 in PERL SCRIPT
219 my $itemtypes = GetItemTypes;
220 my @itemtypesloop;
221 foreach my $thisitemtype (sort keys %$itemtypes) {
222 my $selected = 1 if $thisitemtype eq $itemtype;
223 my %row =(value => $thisitemtype,
224 selected => $selected,
225 description => $itemtypes->{$thisitemtype}->{'description'},
227 push @itemtypesloop, \%row;
229 $template->param(itemtypeloop => \@itemtypesloop);
231 =head3 in TEMPLATE
233 <form action='<!-- TMPL_VAR name="script_name" -->' method=post>
234 <select name="itemtype">
235 <option value="">Default</option>
236 <!-- TMPL_LOOP name="itemtypeloop" -->
237 <option value="<!-- TMPL_VAR name="value" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="description" --></option>
238 <!-- /TMPL_LOOP -->
239 </select>
240 <input type=text name=searchfield value="<!-- TMPL_VAR name="searchfield" -->">
241 <input type="submit" value="OK" class="button">
242 </form>
244 =cut
246 sub GetItemTypes {
247 my ( %params ) = @_;
248 my $style = defined( $params{'style'} ) ? $params{'style'} : 'hash';
250 require C4::Languages;
251 my $language = C4::Languages::getlanguage();
252 # returns a reference to a hash of references to itemtypes...
253 my $dbh = C4::Context->dbh;
254 my $query = q|
255 SELECT
256 itemtypes.itemtype,
257 itemtypes.description,
258 itemtypes.rentalcharge,
259 itemtypes.notforloan,
260 itemtypes.imageurl,
261 itemtypes.summary,
262 itemtypes.checkinmsg,
263 itemtypes.checkinmsgtype,
264 itemtypes.sip_media_type,
265 itemtypes.hideinopac,
266 itemtypes.searchcategory,
267 COALESCE( localization.translation, itemtypes.description ) AS translated_description
268 FROM itemtypes
269 LEFT JOIN localization ON itemtypes.itemtype = localization.code
270 AND localization.entity = 'itemtypes'
271 AND localization.lang = ?
272 ORDER BY itemtype
274 my $sth = $dbh->prepare($query);
275 $sth->execute( $language );
277 if ( $style eq 'hash' ) {
278 my %itemtypes;
279 while ( my $IT = $sth->fetchrow_hashref ) {
280 $itemtypes{ $IT->{'itemtype'} } = $IT;
282 return ( \%itemtypes );
283 } else {
284 return [ sort { lc $a->{translated_description} cmp lc $b->{translated_description} } @{ $sth->fetchall_arrayref( {} ) } ];
288 =head2 GetItemTypesCategorized
290 $categories = GetItemTypesCategorized();
292 Returns a hashref containing search categories.
293 A search category will be put in the hash if at least one of its itemtypes is visible in OPAC.
294 The categories must be part of Authorized Values (ITEMTYPECAT)
296 =cut
298 sub GetItemTypesCategorized {
299 my $dbh = C4::Context->dbh;
300 # Order is important, so that partially hidden (some items are not visible in OPAC) search
301 # categories will be visible. hideinopac=0 must be last.
302 my $query = q|
303 SELECT itemtype, description, imageurl, hideinopac, 0 as 'iscat' FROM itemtypes WHERE ISNULL(searchcategory) or length(searchcategory) = 0
304 UNION
305 SELECT DISTINCT searchcategory AS `itemtype`,
306 authorised_values.lib_opac AS description,
307 authorised_values.imageurl AS imageurl,
308 hideinopac, 1 as 'iscat'
309 FROM itemtypes
310 LEFT JOIN authorised_values ON searchcategory = authorised_value
311 WHERE searchcategory > '' and hideinopac=1
312 UNION
313 SELECT DISTINCT searchcategory AS `itemtype`,
314 authorised_values.lib_opac AS description,
315 authorised_values.imageurl AS imageurl,
316 hideinopac, 1 as 'iscat'
317 FROM itemtypes
318 LEFT JOIN authorised_values ON searchcategory = authorised_value
319 WHERE searchcategory > '' and hideinopac=0
321 return ($dbh->selectall_hashref($query,'itemtype'));
324 =head2 GetItemTypesByCategory
326 @results = GetItemTypesByCategory( $searchcategory );
328 Returns the itemtype code of all itemtypes included in a searchcategory.
330 =cut
332 sub GetItemTypesByCategory {
333 my ($category) = @_;
334 my $count = 0;
335 my @results;
336 my $dbh = C4::Context->dbh;
337 my $query = qq|SELECT itemtype FROM itemtypes WHERE searchcategory=?|;
338 my $tmp=$dbh->selectcol_arrayref($query,undef,$category);
339 return @$tmp;
342 sub get_itemtypeinfos_of {
343 my @itemtypes = @_;
345 my $placeholders = join( ', ', map { '?' } @itemtypes );
346 my $query = <<"END_SQL";
347 SELECT itemtype,
348 description,
349 imageurl,
350 notforloan
351 FROM itemtypes
352 WHERE itemtype IN ( $placeholders )
353 END_SQL
355 return get_infos_of( $query, 'itemtype', undef, \@itemtypes );
358 =head2 getauthtypes
360 $authtypes = &getauthtypes();
362 Returns information about existing authtypes.
364 build a HTML select with the following code :
366 =head3 in PERL SCRIPT
368 my $authtypes = getauthtypes;
369 my @authtypesloop;
370 foreach my $thisauthtype (keys %$authtypes) {
371 my $selected = 1 if $thisauthtype eq $authtype;
372 my %row =(value => $thisauthtype,
373 selected => $selected,
374 authtypetext => $authtypes->{$thisauthtype}->{'authtypetext'},
376 push @authtypesloop, \%row;
378 $template->param(itemtypeloop => \@itemtypesloop);
380 =head3 in TEMPLATE
382 <form action='<!-- TMPL_VAR name="script_name" -->' method=post>
383 <select name="authtype">
384 <!-- TMPL_LOOP name="authtypeloop" -->
385 <option value="<!-- TMPL_VAR name="value" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="authtypetext" --></option>
386 <!-- /TMPL_LOOP -->
387 </select>
388 <input type=text name=searchfield value="<!-- TMPL_VAR name="searchfield" -->">
389 <input type="submit" value="OK" class="button">
390 </form>
393 =cut
395 sub getauthtypes {
397 # returns a reference to a hash of references to authtypes...
398 my %authtypes;
399 my $dbh = C4::Context->dbh;
400 my $sth = $dbh->prepare("select * from auth_types order by authtypetext");
401 $sth->execute;
402 while ( my $IT = $sth->fetchrow_hashref ) {
403 $authtypes{ $IT->{'authtypecode'} } = $IT;
405 return ( \%authtypes );
408 sub getauthtype {
409 my ($authtypecode) = @_;
411 # returns a reference to a hash of references to authtypes...
412 my %authtypes;
413 my $dbh = C4::Context->dbh;
414 my $sth = $dbh->prepare("select * from auth_types where authtypecode=?");
415 $sth->execute($authtypecode);
416 my $res = $sth->fetchrow_hashref;
417 return $res;
420 =head2 getframework
422 $frameworks = &getframework();
424 Returns information about existing frameworks
426 build a HTML select with the following code :
428 =head3 in PERL SCRIPT
430 my $frameworks = getframeworks();
431 my @frameworkloop;
432 foreach my $thisframework (keys %$frameworks) {
433 my $selected = 1 if $thisframework eq $frameworkcode;
434 my %row =(
435 value => $thisframework,
436 selected => $selected,
437 description => $frameworks->{$thisframework}->{'frameworktext'},
439 push @frameworksloop, \%row;
441 $template->param(frameworkloop => \@frameworksloop);
443 =head3 in TEMPLATE
445 <form action="[% script_name %] method=post>
446 <select name="frameworkcode">
447 <option value="">Default</option>
448 [% FOREACH framework IN frameworkloop %]
449 [% IF ( framework.selected ) %]
450 <option value="[% framework.value %]" selected="selected">[% framework.description %]</option>
451 [% ELSE %]
452 <option value="[% framework.value %]">[% framework.description %]</option>
453 [% END %]
454 [% END %]
455 </select>
456 <input type=text name=searchfield value="[% searchfield %]">
457 <input type="submit" value="OK" class="button">
458 </form>
460 =cut
462 sub getframeworks {
464 # returns a reference to a hash of references to branches...
465 my %itemtypes;
466 my $dbh = C4::Context->dbh;
467 my $sth = $dbh->prepare("select * from biblio_framework");
468 $sth->execute;
469 while ( my $IT = $sth->fetchrow_hashref ) {
470 $itemtypes{ $IT->{'frameworkcode'} } = $IT;
472 return ( \%itemtypes );
475 =head2 GetFrameworksLoop
477 $frameworks = GetFrameworksLoop( $frameworkcode );
479 Returns the loop suggested on getframework(), but ordered by framework description.
481 build a HTML select with the following code :
483 =head3 in PERL SCRIPT
485 $template->param( frameworkloop => GetFrameworksLoop( $frameworkcode ) );
487 =head3 in TEMPLATE
489 Same as getframework()
491 <form action="[% script_name %] method=post>
492 <select name="frameworkcode">
493 <option value="">Default</option>
494 [% FOREACH framework IN frameworkloop %]
495 [% IF ( framework.selected ) %]
496 <option value="[% framework.value %]" selected="selected">[% framework.description %]</option>
497 [% ELSE %]
498 <option value="[% framework.value %]">[% framework.description %]</option>
499 [% END %]
500 [% END %]
501 </select>
502 <input type=text name=searchfield value="[% searchfield %]">
503 <input type="submit" value="OK" class="button">
504 </form>
506 =cut
508 sub GetFrameworksLoop {
509 my $frameworkcode = shift;
510 my $frameworks = getframeworks();
511 my @frameworkloop;
512 foreach my $thisframework (sort { uc($frameworks->{$a}->{'frameworktext'}) cmp uc($frameworks->{$b}->{'frameworktext'}) } keys %$frameworks) {
513 my $selected = ( $thisframework eq $frameworkcode ) ? 1 : undef;
514 my %row = (
515 value => $thisframework,
516 selected => $selected,
517 description => $frameworks->{$thisframework}->{'frameworktext'},
519 push @frameworkloop, \%row;
521 return \@frameworkloop;
524 =head2 getframeworkinfo
526 $frameworkinfo = &getframeworkinfo($frameworkcode);
528 Returns information about an frameworkcode.
530 =cut
532 sub getframeworkinfo {
533 my ($frameworkcode) = @_;
534 my $dbh = C4::Context->dbh;
535 my $sth =
536 $dbh->prepare("select * from biblio_framework where frameworkcode=?");
537 $sth->execute($frameworkcode);
538 my $res = $sth->fetchrow_hashref;
539 return $res;
542 =head2 getitemtypeinfo
544 $itemtype = &getitemtypeinfo($itemtype, [$interface]);
546 Returns information about an itemtype. The optional $interface argument
547 sets which interface ('opac' or 'intranet') to return the imageurl for.
548 Defaults to intranet.
550 =cut
552 sub getitemtypeinfo {
553 my ($itemtype, $interface) = @_;
554 my $dbh = C4::Context->dbh;
555 require C4::Languages;
556 my $language = C4::Languages::getlanguage();
557 my $it = $dbh->selectrow_hashref(q|
558 SELECT
559 itemtypes.itemtype,
560 itemtypes.description,
561 itemtypes.rentalcharge,
562 itemtypes.notforloan,
563 itemtypes.imageurl,
564 itemtypes.summary,
565 itemtypes.checkinmsg,
566 itemtypes.checkinmsgtype,
567 itemtypes.sip_media_type,
568 COALESCE( localization.translation, itemtypes.description ) AS translated_description
569 FROM itemtypes
570 LEFT JOIN localization ON itemtypes.itemtype = localization.code
571 AND localization.entity = 'itemtypes'
572 AND localization.lang = ?
573 WHERE itemtypes.itemtype = ?
574 |, undef, $language, $itemtype );
576 $it->{imageurl} = getitemtypeimagelocation( ( ( defined $interface && $interface eq 'opac' ) ? 'opac' : 'intranet' ), $it->{imageurl} );
578 return $it;
581 =head2 getitemtypeimagedir
583 my $directory = getitemtypeimagedir( 'opac' );
585 pass in 'opac' or 'intranet'. Defaults to 'opac'.
587 returns the full path to the appropriate directory containing images.
589 =cut
591 sub getitemtypeimagedir {
592 my $src = shift || 'opac';
593 if ($src eq 'intranet') {
594 return C4::Context->config('intrahtdocs') . '/' .C4::Context->preference('template') . '/img/itemtypeimg';
595 } else {
596 return C4::Context->config('opachtdocs') . '/' . C4::Context->preference('opacthemes') . '/itemtypeimg';
600 sub getitemtypeimagesrc {
601 my $src = shift || 'opac';
602 if ($src eq 'intranet') {
603 return '/intranet-tmpl' . '/' . C4::Context->preference('template') . '/img/itemtypeimg';
604 } else {
605 return '/opac-tmpl' . '/' . C4::Context->preference('opacthemes') . '/itemtypeimg';
609 sub getitemtypeimagelocation {
610 my ( $src, $image ) = @_;
612 return '' if ( !$image );
613 require URI::Split;
615 my $scheme = ( URI::Split::uri_split( $image ) )[0];
617 return $image if ( $scheme );
619 return getitemtypeimagesrc( $src ) . '/' . $image;
622 =head3 _getImagesFromDirectory
624 Find all of the image files in a directory in the filesystem
626 parameters: a directory name
628 returns: a list of images in that directory.
630 Notes: this does not traverse into subdirectories. See
631 _getSubdirectoryNames for help with that.
632 Images are assumed to be files with .gif or .png file extensions.
633 The image names returned do not have the directory name on them.
635 =cut
637 sub _getImagesFromDirectory {
638 my $directoryname = shift;
639 return unless defined $directoryname;
640 return unless -d $directoryname;
642 if ( opendir ( my $dh, $directoryname ) ) {
643 my @images = grep { /\.(gif|png)$/i } readdir( $dh );
644 closedir $dh;
645 @images = sort(@images);
646 return @images;
647 } else {
648 warn "unable to opendir $directoryname: $!";
649 return;
653 =head3 _getSubdirectoryNames
655 Find all of the directories in a directory in the filesystem
657 parameters: a directory name
659 returns: a list of subdirectories in that directory.
661 Notes: this does not traverse into subdirectories. Only the first
662 level of subdirectories are returned.
663 The directory names returned don't have the parent directory name on them.
665 =cut
667 sub _getSubdirectoryNames {
668 my $directoryname = shift;
669 return unless defined $directoryname;
670 return unless -d $directoryname;
672 if ( opendir ( my $dh, $directoryname ) ) {
673 my @directories = grep { -d File::Spec->catfile( $directoryname, $_ ) && ! ( /^\./ ) } readdir( $dh );
674 closedir $dh;
675 return @directories;
676 } else {
677 warn "unable to opendir $directoryname: $!";
678 return;
682 =head3 getImageSets
684 returns: a listref of hashrefs. Each hash represents another collection of images.
686 { imagesetname => 'npl', # the name of the image set (npl is the original one)
687 images => listref of image hashrefs
690 each image is represented by a hashref like this:
692 { KohaImage => 'npl/image.gif',
693 StaffImageUrl => '/intranet-tmpl/prog/img/itemtypeimg/npl/image.gif',
694 OpacImageURL => '/opac-tmpl/prog/itemtypeimg/npl/image.gif'
695 checked => 0 or 1: was this the image passed to this method?
696 Note: I'd like to remove this somehow.
699 =cut
701 sub getImageSets {
702 my %params = @_;
703 my $checked = $params{'checked'} || '';
705 my $paths = { staff => { filesystem => getitemtypeimagedir('intranet'),
706 url => getitemtypeimagesrc('intranet'),
708 opac => { filesystem => getitemtypeimagedir('opac'),
709 url => getitemtypeimagesrc('opac'),
713 my @imagesets = (); # list of hasrefs of image set data to pass to template
714 my @subdirectories = _getSubdirectoryNames( $paths->{'staff'}{'filesystem'} );
715 foreach my $imagesubdir ( @subdirectories ) {
716 warn $imagesubdir if $DEBUG;
717 my @imagelist = (); # hashrefs of image info
718 my @imagenames = _getImagesFromDirectory( File::Spec->catfile( $paths->{'staff'}{'filesystem'}, $imagesubdir ) );
719 my $imagesetactive = 0;
720 foreach my $thisimage ( @imagenames ) {
721 push( @imagelist,
722 { KohaImage => "$imagesubdir/$thisimage",
723 StaffImageUrl => join( '/', $paths->{'staff'}{'url'}, $imagesubdir, $thisimage ),
724 OpacImageUrl => join( '/', $paths->{'opac'}{'url'}, $imagesubdir, $thisimage ),
725 checked => "$imagesubdir/$thisimage" eq $checked ? 1 : 0,
728 $imagesetactive = 1 if "$imagesubdir/$thisimage" eq $checked;
730 push @imagesets, { imagesetname => $imagesubdir,
731 imagesetactive => $imagesetactive,
732 images => \@imagelist };
735 return \@imagesets;
738 =head2 GetPrinters
740 $printers = &GetPrinters();
741 @queues = keys %$printers;
743 Returns information about existing printer queues.
745 C<$printers> is a reference-to-hash whose keys are the print queues
746 defined in the printers table of the Koha database. The values are
747 references-to-hash, whose keys are the fields in the printers table.
749 =cut
751 sub GetPrinters {
752 my %printers;
753 my $dbh = C4::Context->dbh;
754 my $sth = $dbh->prepare("select * from printers");
755 $sth->execute;
756 while ( my $printer = $sth->fetchrow_hashref ) {
757 $printers{ $printer->{'printqueue'} } = $printer;
759 return ( \%printers );
762 =head2 GetPrinter
764 $printer = GetPrinter( $query, $printers );
766 =cut
768 sub GetPrinter {
769 my ( $query, $printers ) = @_; # get printer for this query from printers
770 my $printer = $query->param('printer');
771 my %cookie = $query->cookie('userenv');
772 ($printer) || ( $printer = $cookie{'printer'} ) || ( $printer = '' );
773 ( $printers->{$printer} ) || ( $printer = ( keys %$printers )[0] );
774 return $printer;
777 =head2 getnbpages
779 Returns the number of pages to display in a pagination bar, given the number
780 of items and the number of items per page.
782 =cut
784 sub getnbpages {
785 my ( $nb_items, $nb_items_per_page ) = @_;
787 return int( ( $nb_items - 1 ) / $nb_items_per_page ) + 1;
790 =head2 getallthemes
792 (@themes) = &getallthemes('opac');
793 (@themes) = &getallthemes('intranet');
795 Returns an array of all available themes.
797 =cut
799 sub getallthemes {
800 my $type = shift;
801 my $htdocs;
802 my @themes;
803 if ( $type eq 'intranet' ) {
804 $htdocs = C4::Context->config('intrahtdocs');
806 else {
807 $htdocs = C4::Context->config('opachtdocs');
809 opendir D, "$htdocs";
810 my @dirlist = readdir D;
811 foreach my $directory (@dirlist) {
812 next if $directory eq 'lib';
813 -d "$htdocs/$directory/en" and push @themes, $directory;
815 return @themes;
818 sub getFacets {
819 my $facets;
820 if ( C4::Context->preference("marcflavour") eq "UNIMARC" ) {
821 $facets = [
823 idx => 'su-to',
824 label => 'Topics',
825 tags => [ qw/ 600ab 601ab 602a 604at 605a 606ax 610a / ],
826 sep => ' - ',
829 idx => 'su-geo',
830 label => 'Places',
831 tags => [ qw/ 607a / ],
832 sep => ' - ',
835 idx => 'su-ut',
836 label => 'Titles',
837 tags => [ qw/ 500a 501a 503a / ],
838 sep => ', ',
841 idx => 'au',
842 label => 'Authors',
843 tags => [ qw/ 700ab 701ab 702ab / ],
844 sep => C4::Context->preference("UNIMARCAuthorsFacetsSeparator"),
847 idx => 'se',
848 label => 'Series',
849 tags => [ qw/ 225a / ],
850 sep => ', ',
853 idx => 'location',
854 label => 'Location',
855 tags => [ qw/ 995e / ],
859 unless ( C4::Context->preference("singleBranchMode")
860 || GetBranchesCount() == 1 )
862 my $DisplayLibraryFacets = C4::Context->preference('DisplayLibraryFacets');
863 if ( $DisplayLibraryFacets eq 'both'
864 || $DisplayLibraryFacets eq 'holding' )
866 push(
867 @$facets,
869 idx => 'holdingbranch',
870 label => 'HoldingLibrary',
871 tags => [qw / 995c /],
876 if ( $DisplayLibraryFacets eq 'both'
877 || $DisplayLibraryFacets eq 'home' )
879 push(
880 @$facets,
882 idx => 'homebranch',
883 label => 'HomeLibrary',
884 tags => [qw / 995b /],
890 else {
891 $facets = [
893 idx => 'su-to',
894 label => 'Topics',
895 tags => [ qw/ 650a / ],
896 sep => '--',
899 # idx => 'su-na',
900 # label => 'People and Organizations',
901 # tags => [ qw/ 600a 610a 611a / ],
902 # sep => 'a',
903 # },
905 idx => 'su-geo',
906 label => 'Places',
907 tags => [ qw/ 651a / ],
908 sep => '--',
911 idx => 'su-ut',
912 label => 'Titles',
913 tags => [ qw/ 630a / ],
914 sep => '--',
917 idx => 'au',
918 label => 'Authors',
919 tags => [ qw/ 100a 110a 700a / ],
920 sep => ', ',
923 idx => 'se',
924 label => 'Series',
925 tags => [ qw/ 440a 490a / ],
926 sep => ', ',
929 idx => 'itype',
930 label => 'ItemTypes',
931 tags => [ qw/ 952y 942c / ],
932 sep => ', ',
935 idx => 'location',
936 label => 'Location',
937 tags => [ qw / 952c / ],
941 unless ( C4::Context->preference("singleBranchMode")
942 || GetBranchesCount() == 1 )
944 my $DisplayLibraryFacets = C4::Context->preference('DisplayLibraryFacets');
945 if ( $DisplayLibraryFacets eq 'both'
946 || $DisplayLibraryFacets eq 'holding' )
948 push(
949 @$facets,
951 idx => 'holdingbranch',
952 label => 'HoldingLibrary',
953 tags => [qw / 952b /],
958 if ( $DisplayLibraryFacets eq 'both'
959 || $DisplayLibraryFacets eq 'home' )
961 push(
962 @$facets,
964 idx => 'homebranch',
965 label => 'HomeLibrary',
966 tags => [qw / 952a /],
972 return $facets;
975 =head2 get_infos_of
977 Return a href where a key is associated to a href. You give a query,
978 the name of the key among the fields returned by the query. If you
979 also give as third argument the name of the value, the function
980 returns a href of scalar. The optional 4th argument is an arrayref of
981 items passed to the C<execute()> call. It is designed to bind
982 parameters to any placeholders in your SQL.
984 my $query = '
985 SELECT itemnumber,
986 notforloan,
987 barcode
988 FROM items
991 # generic href of any information on the item, href of href.
992 my $iteminfos_of = get_infos_of($query, 'itemnumber');
993 print $iteminfos_of->{$itemnumber}{barcode};
995 # specific information, href of scalar
996 my $barcode_of_item = get_infos_of($query, 'itemnumber', 'barcode');
997 print $barcode_of_item->{$itemnumber};
999 =cut
1001 sub get_infos_of {
1002 my ( $query, $key_name, $value_name, $bind_params ) = @_;
1004 my $dbh = C4::Context->dbh;
1006 my $sth = $dbh->prepare($query);
1007 $sth->execute( @$bind_params );
1009 my %infos_of;
1010 while ( my $row = $sth->fetchrow_hashref ) {
1011 if ( defined $value_name ) {
1012 $infos_of{ $row->{$key_name} } = $row->{$value_name};
1014 else {
1015 $infos_of{ $row->{$key_name} } = $row;
1018 $sth->finish;
1020 return \%infos_of;
1023 =head2 get_notforloan_label_of
1025 my $notforloan_label_of = get_notforloan_label_of();
1027 Each authorised value of notforloan (information available in items and
1028 itemtypes) is link to a single label.
1030 Returns a href where keys are authorised values and values are corresponding
1031 labels.
1033 foreach my $authorised_value (keys %{$notforloan_label_of}) {
1034 printf(
1035 "authorised_value: %s => %s\n",
1036 $authorised_value,
1037 $notforloan_label_of->{$authorised_value}
1041 =cut
1043 # FIXME - why not use GetAuthorisedValues ??
1045 sub get_notforloan_label_of {
1046 my $dbh = C4::Context->dbh;
1048 my $query = '
1049 SELECT authorised_value
1050 FROM marc_subfield_structure
1051 WHERE kohafield = \'items.notforloan\'
1052 LIMIT 0, 1
1054 my $sth = $dbh->prepare($query);
1055 $sth->execute();
1056 my ($statuscode) = $sth->fetchrow_array();
1058 $query = '
1059 SELECT lib,
1060 authorised_value
1061 FROM authorised_values
1062 WHERE category = ?
1064 $sth = $dbh->prepare($query);
1065 $sth->execute($statuscode);
1066 my %notforloan_label_of;
1067 while ( my $row = $sth->fetchrow_hashref ) {
1068 $notforloan_label_of{ $row->{authorised_value} } = $row->{lib};
1070 $sth->finish;
1072 return \%notforloan_label_of;
1075 =head2 displayServers
1077 my $servers = displayServers();
1078 my $servers = displayServers( $position );
1079 my $servers = displayServers( $position, $type );
1081 displayServers returns a listref of hashrefs, each containing
1082 information about available z3950 servers. Each hashref has a format
1083 like:
1086 'checked' => 'checked',
1087 'encoding' => 'utf8',
1088 'icon' => undef,
1089 'id' => 'LIBRARY OF CONGRESS',
1090 'label' => '',
1091 'name' => 'server',
1092 'opensearch' => '',
1093 'value' => 'lx2.loc.gov:210/',
1094 'zed' => 1,
1097 =cut
1099 sub displayServers {
1100 my ( $position, $type ) = @_;
1101 my $dbh = C4::Context->dbh;
1103 my $strsth = 'SELECT * FROM z3950servers';
1104 my @where_clauses;
1105 my @bind_params;
1107 if ($position) {
1108 push @bind_params, $position;
1109 push @where_clauses, ' position = ? ';
1112 if ($type) {
1113 push @bind_params, $type;
1114 push @where_clauses, ' type = ? ';
1117 # reassemble where clause from where clause pieces
1118 if (@where_clauses) {
1119 $strsth .= ' WHERE ' . join( ' AND ', @where_clauses );
1122 my $rq = $dbh->prepare($strsth);
1123 $rq->execute(@bind_params);
1124 my @primaryserverloop;
1126 while ( my $data = $rq->fetchrow_hashref ) {
1127 push @primaryserverloop,
1128 { label => $data->{description},
1129 id => $data->{name},
1130 name => "server",
1131 value => $data->{host} . ":" . $data->{port} . "/" . $data->{database},
1132 encoding => ( $data->{encoding} ? $data->{encoding} : "iso-5426" ),
1133 checked => "checked",
1134 icon => $data->{icon},
1135 zed => $data->{type} eq 'zed',
1136 opensearch => $data->{type} eq 'opensearch'
1139 return \@primaryserverloop;
1143 =head2 GetKohaImageurlFromAuthorisedValues
1145 $authhorised_value = GetKohaImageurlFromAuthorisedValues( $category, $authvalcode );
1147 Return the first url of the authorised value image represented by $lib.
1149 =cut
1151 sub GetKohaImageurlFromAuthorisedValues {
1152 my ( $category, $lib ) = @_;
1153 my $dbh = C4::Context->dbh;
1154 my $sth = $dbh->prepare("SELECT imageurl FROM authorised_values WHERE category=? AND lib =?");
1155 $sth->execute( $category, $lib );
1156 while ( my $data = $sth->fetchrow_hashref ) {
1157 return $data->{'imageurl'};
1161 =head2 GetAuthValCode
1163 $authvalcode = GetAuthValCode($kohafield,$frameworkcode);
1165 =cut
1167 sub GetAuthValCode {
1168 my ($kohafield,$fwcode) = @_;
1169 my $dbh = C4::Context->dbh;
1170 $fwcode='' unless $fwcode;
1171 my $sth = $dbh->prepare('select authorised_value from marc_subfield_structure where kohafield=? and frameworkcode=?');
1172 $sth->execute($kohafield,$fwcode);
1173 my ($authvalcode) = $sth->fetchrow_array;
1174 return $authvalcode;
1177 =head2 GetAuthValCodeFromField
1179 $authvalcode = GetAuthValCodeFromField($field,$subfield,$frameworkcode);
1181 C<$subfield> can be undefined
1183 =cut
1185 sub GetAuthValCodeFromField {
1186 my ($field,$subfield,$fwcode) = @_;
1187 my $dbh = C4::Context->dbh;
1188 $fwcode='' unless $fwcode;
1189 my $sth;
1190 if (defined $subfield) {
1191 $sth = $dbh->prepare('select authorised_value from marc_subfield_structure where tagfield=? and tagsubfield=? and frameworkcode=?');
1192 $sth->execute($field,$subfield,$fwcode);
1193 } else {
1194 $sth = $dbh->prepare('select authorised_value from marc_tag_structure where tagfield=? and frameworkcode=?');
1195 $sth->execute($field,$fwcode);
1197 my ($authvalcode) = $sth->fetchrow_array;
1198 return $authvalcode;
1201 =head2 GetAuthorisedValues
1203 $authvalues = GetAuthorisedValues([$category], [$selected]);
1205 This function returns all authorised values from the'authorised_value' table in a reference to array of hashrefs.
1207 C<$category> returns authorised values for just one category (optional).
1209 C<$selected> adds a "selected => 1" entry to the hash if the
1210 authorised_value matches it. B<NOTE:> this feature should be considered
1211 deprecated as it may be removed in the future.
1213 C<$opac> If set to a true value, displays OPAC descriptions rather than normal ones when they exist.
1215 =cut
1217 sub GetAuthorisedValues {
1218 my ( $category, $selected, $opac ) = @_;
1220 # TODO: the "selected" feature should be replaced by a utility function
1221 # somewhere else, it doesn't belong in here. For starters it makes
1222 # caching much more complicated. Or just let the UI logic handle it, it's
1223 # what it's for.
1225 # Is this cached already?
1226 $opac = $opac ? 1 : 0; # normalise to be safe
1227 my $branch_limit =
1228 C4::Context->userenv ? C4::Context->userenv->{"branch"} : "";
1229 my $selected_key = defined($selected) ? $selected : '';
1230 my $cache_key =
1231 "AuthorisedValues-$category-$selected_key-$opac-$branch_limit";
1232 my $cache = Koha::Cache->get_instance();
1233 my $result = $cache->get_from_cache($cache_key);
1234 return $result if $result;
1236 my @results;
1237 my $dbh = C4::Context->dbh;
1238 my $query = qq{
1239 SELECT *
1240 FROM authorised_values
1242 $query .= qq{
1243 LEFT JOIN authorised_values_branches ON ( id = av_id )
1244 } if $branch_limit;
1245 my @where_strings;
1246 my @where_args;
1247 if($category) {
1248 push @where_strings, "category = ?";
1249 push @where_args, $category;
1251 if($branch_limit) {
1252 push @where_strings, "( branchcode = ? OR branchcode IS NULL )";
1253 push @where_args, $branch_limit;
1255 if(@where_strings > 0) {
1256 $query .= " WHERE " . join(" AND ", @where_strings);
1258 $query .= " GROUP BY lib";
1259 $query .= ' ORDER BY category, ' . (
1260 $opac ? 'COALESCE(lib_opac, lib)'
1261 : 'lib, lib_opac'
1264 my $sth = $dbh->prepare($query);
1266 $sth->execute( @where_args );
1267 while (my $data=$sth->fetchrow_hashref) {
1268 if ( defined $selected and $selected eq $data->{authorised_value} ) {
1269 $data->{selected} = 1;
1271 else {
1272 $data->{selected} = 0;
1275 if ($opac && $data->{lib_opac}) {
1276 $data->{lib} = $data->{lib_opac};
1278 push @results, $data;
1280 $sth->finish;
1282 # We can't cache for long because of that "selected" thing which
1283 # makes it impossible to clear the cache without iterating through every
1284 # value, which sucks. This'll cover this request, and not a whole lot more.
1285 $cache->set_in_cache( $cache_key, \@results, { deepcopy => 1, expiry => 5 } );
1286 return \@results;
1289 =head2 GetAuthorisedValueCategories
1291 $auth_categories = GetAuthorisedValueCategories();
1293 Return an arrayref of all of the available authorised
1294 value categories.
1296 =cut
1298 sub GetAuthorisedValueCategories {
1299 my $dbh = C4::Context->dbh;
1300 my $sth = $dbh->prepare("SELECT DISTINCT category FROM authorised_values ORDER BY category");
1301 $sth->execute;
1302 my @results;
1303 while (defined (my $category = $sth->fetchrow_array) ) {
1304 push @results, $category;
1306 return \@results;
1309 =head2 IsAuthorisedValueCategory
1311 $is_auth_val_category = IsAuthorisedValueCategory($category);
1313 Returns whether a given category name is a valid one
1315 =cut
1317 sub IsAuthorisedValueCategory {
1318 my $category = shift;
1319 my $query = '
1320 SELECT category
1321 FROM authorised_values
1322 WHERE category=?
1323 LIMIT 1
1325 my $sth = C4::Context->dbh->prepare($query);
1326 $sth->execute($category);
1327 $sth->fetchrow ? return 1
1328 : return 0;
1331 =head2 GetAuthorisedValueByCode
1333 $authorised_value = GetAuthorisedValueByCode( $category, $authvalcode, $opac );
1335 Return the lib attribute from authorised_values from the row identified
1336 by the passed category and code
1338 =cut
1340 sub GetAuthorisedValueByCode {
1341 my ( $category, $authvalcode, $opac ) = @_;
1343 my $field = $opac ? 'lib_opac' : 'lib';
1344 my $dbh = C4::Context->dbh;
1345 my $sth = $dbh->prepare("SELECT $field FROM authorised_values WHERE category=? AND authorised_value =?");
1346 $sth->execute( $category, $authvalcode );
1347 while ( my $data = $sth->fetchrow_hashref ) {
1348 return $data->{ $field };
1352 =head2 GetKohaAuthorisedValues
1354 Takes $kohafield, $fwcode as parameters.
1356 If $opac parameter is set to a true value, displays OPAC descriptions rather than normal ones when they exist.
1358 Returns hashref of Code => description
1360 Returns undef if no authorised value category is defined for the kohafield.
1362 =cut
1364 sub GetKohaAuthorisedValues {
1365 my ($kohafield,$fwcode,$opac) = @_;
1366 $fwcode='' unless $fwcode;
1367 my %values;
1368 my $dbh = C4::Context->dbh;
1369 my $avcode = GetAuthValCode($kohafield,$fwcode);
1370 if ($avcode) {
1371 my $sth = $dbh->prepare("select authorised_value, lib, lib_opac from authorised_values where category=? ");
1372 $sth->execute($avcode);
1373 while ( my ($val, $lib, $lib_opac) = $sth->fetchrow_array ) {
1374 $values{$val} = ($opac && $lib_opac) ? $lib_opac : $lib;
1376 return \%values;
1377 } else {
1378 return;
1382 =head2 GetKohaAuthorisedValuesFromField
1384 Takes $field, $subfield, $fwcode as parameters.
1386 If $opac parameter is set to a true value, displays OPAC descriptions rather than normal ones when they exist.
1387 $subfield can be undefined
1389 Returns hashref of Code => description
1391 Returns undef if no authorised value category is defined for the given field and subfield
1393 =cut
1395 sub GetKohaAuthorisedValuesFromField {
1396 my ($field, $subfield, $fwcode,$opac) = @_;
1397 $fwcode='' unless $fwcode;
1398 my %values;
1399 my $dbh = C4::Context->dbh;
1400 my $avcode = GetAuthValCodeFromField($field, $subfield, $fwcode);
1401 if ($avcode) {
1402 my $sth = $dbh->prepare("select authorised_value, lib, lib_opac from authorised_values where category=? ");
1403 $sth->execute($avcode);
1404 while ( my ($val, $lib, $lib_opac) = $sth->fetchrow_array ) {
1405 $values{$val} = ($opac && $lib_opac) ? $lib_opac : $lib;
1407 return \%values;
1408 } else {
1409 return;
1413 =head2 GetKohaAuthorisedValuesMapping
1415 Takes a hash as a parameter. The interface key indicates the
1416 description to use in the mapping.
1418 Returns hashref of:
1419 "{kohafield},{frameworkcode},{authorised_value}" => "{description}"
1420 for all the kohafields, frameworkcodes, and authorised values.
1422 Returns undef if nothing is found.
1424 =cut
1426 sub GetKohaAuthorisedValuesMapping {
1427 my ($parameter) = @_;
1428 my $interface = $parameter->{'interface'} // '';
1430 my $query_mapping = q{
1431 SELECT TA.kohafield,TA.authorised_value AS category,
1432 TA.frameworkcode,TB.authorised_value,
1433 IF(TB.lib_opac>'',TB.lib_opac,TB.lib) AS OPAC,
1434 TB.lib AS Intranet,TB.lib_opac
1435 FROM marc_subfield_structure AS TA JOIN
1436 authorised_values as TB ON
1437 TA.authorised_value=TB.category
1438 WHERE TA.kohafield>'' AND TA.authorised_value>'';
1440 my $dbh = C4::Context->dbh;
1441 my $sth = $dbh->prepare($query_mapping);
1442 $sth->execute();
1443 my $avmapping;
1444 if ($interface eq 'opac') {
1445 while (my $row = $sth->fetchrow_hashref) {
1446 $avmapping->{$row->{kohafield}.",".$row->{frameworkcode}.",".$row->{authorised_value}} = $row->{OPAC};
1449 else {
1450 while (my $row = $sth->fetchrow_hashref) {
1451 $avmapping->{$row->{kohafield}.",".$row->{frameworkcode}.",".$row->{authorised_value}} = $row->{Intranet};
1454 return $avmapping;
1457 =head2 xml_escape
1459 my $escaped_string = C4::Koha::xml_escape($string);
1461 Convert &, <, >, ', and " in a string to XML entities
1463 =cut
1465 sub xml_escape {
1466 my $str = shift;
1467 return '' unless defined $str;
1468 $str =~ s/&/&amp;/g;
1469 $str =~ s/</&lt;/g;
1470 $str =~ s/>/&gt;/g;
1471 $str =~ s/'/&apos;/g;
1472 $str =~ s/"/&quot;/g;
1473 return $str;
1476 =head2 GetKohaAuthorisedValueLib
1478 Takes $category, $authorised_value as parameters.
1480 If $opac parameter is set to a true value, displays OPAC descriptions rather than normal ones when they exist.
1482 Returns authorised value description
1484 =cut
1486 sub GetKohaAuthorisedValueLib {
1487 my ($category,$authorised_value,$opac) = @_;
1488 my $value;
1489 my $dbh = C4::Context->dbh;
1490 my $sth = $dbh->prepare("select lib, lib_opac from authorised_values where category=? and authorised_value=?");
1491 $sth->execute($category,$authorised_value);
1492 my $data = $sth->fetchrow_hashref;
1493 $value = ($opac && $$data{'lib_opac'}) ? $$data{'lib_opac'} : $$data{'lib'};
1494 return $value;
1497 =head2 AddAuthorisedValue
1499 AddAuthorisedValue($category, $authorised_value, $lib, $lib_opac, $imageurl);
1501 Create a new authorised value.
1503 =cut
1505 sub AddAuthorisedValue {
1506 my ($category, $authorised_value, $lib, $lib_opac, $imageurl) = @_;
1508 my $dbh = C4::Context->dbh;
1509 my $query = qq{
1510 INSERT INTO authorised_values (category, authorised_value, lib, lib_opac, imageurl)
1511 VALUES (?,?,?,?,?)
1513 my $sth = $dbh->prepare($query);
1514 $sth->execute($category, $authorised_value, $lib, $lib_opac, $imageurl);
1517 =head2 display_marc_indicators
1519 my $display_form = C4::Koha::display_marc_indicators($field);
1521 C<$field> is a MARC::Field object
1523 Generate a display form of the indicators of a variable
1524 MARC field, replacing any blanks with '#'.
1526 =cut
1528 sub display_marc_indicators {
1529 my $field = shift;
1530 my $indicators = '';
1531 if ($field && $field->tag() >= 10) {
1532 $indicators = $field->indicator(1) . $field->indicator(2);
1533 $indicators =~ s/ /#/g;
1535 return $indicators;
1538 sub GetNormalizedUPC {
1539 my ($marcrecord,$marcflavour) = @_;
1541 return unless $marcrecord;
1542 if ($marcflavour eq 'UNIMARC') {
1543 my @fields = $marcrecord->field('072');
1544 foreach my $field (@fields) {
1545 my $upc = _normalize_match_point($field->subfield('a'));
1546 if ($upc) {
1547 return $upc;
1552 else { # assume marc21 if not unimarc
1553 my @fields = $marcrecord->field('024');
1554 foreach my $field (@fields) {
1555 my $indicator = $field->indicator(1);
1556 my $upc = _normalize_match_point($field->subfield('a'));
1557 if ($upc && $indicator == 1 ) {
1558 return $upc;
1564 # Normalizes and returns the first valid ISBN found in the record
1565 # ISBN13 are converted into ISBN10. This is required to get some book cover images.
1566 sub GetNormalizedISBN {
1567 my ($isbn,$marcrecord,$marcflavour) = @_;
1568 if ($isbn) {
1569 # Koha attempts to store multiple ISBNs in biblioitems.isbn, separated by " | "
1570 # anything after " | " should be removed, along with the delimiter
1571 ($isbn) = split(/\|/, $isbn );
1572 return _isbn_cleanup($isbn);
1575 return unless $marcrecord;
1577 if ($marcflavour eq 'UNIMARC') {
1578 my @fields = $marcrecord->field('010');
1579 foreach my $field (@fields) {
1580 my $isbn = $field->subfield('a');
1581 if ($isbn) {
1582 return _isbn_cleanup($isbn);
1586 else { # assume marc21 if not unimarc
1587 my @fields = $marcrecord->field('020');
1588 foreach my $field (@fields) {
1589 $isbn = $field->subfield('a');
1590 if ($isbn) {
1591 return _isbn_cleanup($isbn);
1597 sub GetNormalizedEAN {
1598 my ($marcrecord,$marcflavour) = @_;
1600 return unless $marcrecord;
1602 if ($marcflavour eq 'UNIMARC') {
1603 my @fields = $marcrecord->field('073');
1604 foreach my $field (@fields) {
1605 my $ean = _normalize_match_point($field->subfield('a'));
1606 if ( $ean ) {
1607 return $ean;
1611 else { # assume marc21 if not unimarc
1612 my @fields = $marcrecord->field('024');
1613 foreach my $field (@fields) {
1614 my $indicator = $field->indicator(1);
1615 my $ean = _normalize_match_point($field->subfield('a'));
1616 if ( $ean && $indicator == 3 ) {
1617 return $ean;
1623 sub GetNormalizedOCLCNumber {
1624 my ($marcrecord,$marcflavour) = @_;
1625 return unless $marcrecord;
1627 if ($marcflavour ne 'UNIMARC' ) {
1628 my @fields = $marcrecord->field('035');
1629 foreach my $field (@fields) {
1630 my $oclc = $field->subfield('a');
1631 if ($oclc =~ /OCoLC/) {
1632 $oclc =~ s/\(OCoLC\)//;
1633 return $oclc;
1636 } else {
1637 # TODO for UNIMARC
1639 return
1642 sub GetAuthvalueDropbox {
1643 my ( $authcat, $default ) = @_;
1644 my $branch_limit = C4::Context->userenv ? C4::Context->userenv->{"branch"} : "";
1645 my $dbh = C4::Context->dbh;
1647 my $query = qq{
1648 SELECT *
1649 FROM authorised_values
1651 $query .= qq{
1652 LEFT JOIN authorised_values_branches ON ( id = av_id )
1653 } if $branch_limit;
1654 $query .= qq{
1655 WHERE category = ?
1657 $query .= " AND ( branchcode = ? OR branchcode IS NULL )" if $branch_limit;
1658 $query .= " GROUP BY lib ORDER BY category, lib, lib_opac";
1659 my $sth = $dbh->prepare($query);
1660 $sth->execute( $authcat, $branch_limit ? $branch_limit : () );
1663 my $option_list = [];
1664 my @authorised_values = ( q{} );
1665 while (my $av = $sth->fetchrow_hashref) {
1666 push @{$option_list}, {
1667 value => $av->{authorised_value},
1668 label => $av->{lib},
1669 default => ($default eq $av->{authorised_value}),
1673 if ( @{$option_list} ) {
1674 return $option_list;
1676 return;
1680 =head2 GetDailyQuote($opts)
1682 Takes a hashref of options
1684 Currently supported options are:
1686 'id' An exact quote id
1687 'random' Select a random quote
1688 noop When no option is passed in, this sub will return the quote timestamped for the current day
1690 The function returns an anonymous hash following this format:
1693 'source' => 'source-of-quote',
1694 'timestamp' => 'timestamp-value',
1695 'text' => 'text-of-quote',
1696 'id' => 'quote-id'
1699 =cut
1701 # This is definitely a candidate for some sort of caching once we finally settle caching/persistence issues...
1702 # at least for default option
1704 sub GetDailyQuote {
1705 my %opts = @_;
1706 my $dbh = C4::Context->dbh;
1707 my $query = '';
1708 my $sth = undef;
1709 my $quote = undef;
1710 if ($opts{'id'}) {
1711 $query = 'SELECT * FROM quotes WHERE id = ?';
1712 $sth = $dbh->prepare($query);
1713 $sth->execute($opts{'id'});
1714 $quote = $sth->fetchrow_hashref();
1716 elsif ($opts{'random'}) {
1717 # Fall through... we also return a random quote as a catch-all if all else fails
1719 else {
1720 $query = 'SELECT * FROM quotes WHERE timestamp LIKE CONCAT(CURRENT_DATE,\'%\') ORDER BY timestamp DESC LIMIT 0,1';
1721 $sth = $dbh->prepare($query);
1722 $sth->execute();
1723 $quote = $sth->fetchrow_hashref();
1725 unless ($quote) { # if there are not matches, choose a random quote
1726 # get a list of all available quote ids
1727 $sth = C4::Context->dbh->prepare('SELECT count(*) FROM quotes;');
1728 $sth->execute;
1729 my $range = ($sth->fetchrow_array)[0];
1730 # chose a random id within that range if there is more than one quote
1731 my $offset = int(rand($range));
1732 # grab it
1733 $query = 'SELECT * FROM quotes ORDER BY id LIMIT 1 OFFSET ?';
1734 $sth = C4::Context->dbh->prepare($query);
1735 # see http://www.perlmonks.org/?node_id=837422 for why
1736 # we're being verbose and using bind_param
1737 $sth->bind_param(1, $offset, SQL_INTEGER);
1738 $sth->execute();
1739 $quote = $sth->fetchrow_hashref();
1740 # update the timestamp for that quote
1741 $query = 'UPDATE quotes SET timestamp = ? WHERE id = ?';
1742 $sth = C4::Context->dbh->prepare($query);
1743 $sth->execute(
1744 DateTime::Format::MySQL->format_datetime( dt_from_string() ),
1745 $quote->{'id'}
1748 return $quote;
1751 sub _normalize_match_point {
1752 my $match_point = shift;
1753 (my $normalized_match_point) = $match_point =~ /([\d-]*[X]*)/;
1754 $normalized_match_point =~ s/-//g;
1756 return $normalized_match_point;
1759 sub _isbn_cleanup {
1760 my ($isbn) = @_;
1761 return NormalizeISBN(
1763 isbn => $isbn,
1764 format => 'ISBN-10',
1765 strip_hyphens => 1,
1767 ) if $isbn;
1770 =head2 NormalizedISBN
1772 my $isbns = NormalizedISBN({
1773 isbn => $isbn,
1774 strip_hyphens => [0,1],
1775 format => ['ISBN-10', 'ISBN-13']
1778 Returns an isbn validated by Business::ISBN.
1779 Optionally strips hyphens and/or forces the isbn
1780 to be of the specified format.
1782 If the string cannot be validated as an isbn,
1783 it returns nothing.
1785 =cut
1787 sub NormalizeISBN {
1788 my ($params) = @_;
1790 my $string = $params->{isbn};
1791 my $strip_hyphens = $params->{strip_hyphens};
1792 my $format = $params->{format};
1794 return unless $string;
1796 my $isbn = Business::ISBN->new($string);
1798 if ( $isbn && $isbn->is_valid() ) {
1800 if ( $format eq 'ISBN-10' ) {
1801 $isbn = $isbn->as_isbn10();
1803 elsif ( $format eq 'ISBN-13' ) {
1804 $isbn = $isbn->as_isbn13();
1806 return unless $isbn;
1808 if ($strip_hyphens) {
1809 $string = $isbn->as_string( [] );
1810 } else {
1811 $string = $isbn->as_string();
1814 return $string;
1818 =head2 GetVariationsOfISBN
1820 my @isbns = GetVariationsOfISBN( $isbn );
1822 Returns a list of variations of the given isbn in
1823 both ISBN-10 and ISBN-13 formats, with and without
1824 hyphens.
1826 In a scalar context, the isbns are returned as a
1827 string delimited by ' | '.
1829 =cut
1831 sub GetVariationsOfISBN {
1832 my ($isbn) = @_;
1834 return unless $isbn;
1836 my @isbns;
1838 push( @isbns, NormalizeISBN({ isbn => $isbn }) );
1839 push( @isbns, NormalizeISBN({ isbn => $isbn, format => 'ISBN-10' }) );
1840 push( @isbns, NormalizeISBN({ isbn => $isbn, format => 'ISBN-13' }) );
1841 push( @isbns, NormalizeISBN({ isbn => $isbn, format => 'ISBN-10', strip_hyphens => 1 }) );
1842 push( @isbns, NormalizeISBN({ isbn => $isbn, format => 'ISBN-13', strip_hyphens => 1 }) );
1844 # Strip out any "empty" strings from the array
1845 @isbns = grep { defined($_) && $_ =~ /\S/ } @isbns;
1847 return wantarray ? @isbns : join( " | ", @isbns );
1850 =head2 GetVariationsOfISBNs
1852 my @isbns = GetVariationsOfISBNs( @isbns );
1854 Returns a list of variations of the given isbns in
1855 both ISBN-10 and ISBN-13 formats, with and without
1856 hyphens.
1858 In a scalar context, the isbns are returned as a
1859 string delimited by ' | '.
1861 =cut
1863 sub GetVariationsOfISBNs {
1864 my (@isbns) = @_;
1866 @isbns = map { GetVariationsOfISBN( $_ ) } @isbns;
1868 return wantarray ? @isbns : join( " | ", @isbns );
1871 =head2 IsKohaFieldLinked
1873 my $is_linked = IsKohaFieldLinked({
1874 kohafield => $kohafield,
1875 frameworkcode => $frameworkcode,
1878 Return 1 if the field is linked
1880 =cut
1882 sub IsKohaFieldLinked {
1883 my ( $params ) = @_;
1884 my $kohafield = $params->{kohafield};
1885 my $frameworkcode = $params->{frameworkcode} || '';
1886 my $dbh = C4::Context->dbh;
1887 my $is_linked = $dbh->selectcol_arrayref( q|
1888 SELECT COUNT(*)
1889 FROM marc_subfield_structure
1890 WHERE frameworkcode = ?
1891 AND kohafield = ?
1892 |,{}, $frameworkcode, $kohafield );
1893 return $is_linked->[0];
1898 __END__
1900 =head1 AUTHOR
1902 Koha Team
1904 =cut