Update fines cronjob: remove redundant scripts, remove some superfluous code in remai...
[koha_fer] / C4 / Biblio.pm
index b89e32a..e4e2dc2 100755 (executable)
@@ -30,6 +30,7 @@ use C4::Branch;
 use C4::Dates qw/format_date/;
 use C4::Log; # logaction
 use C4::ClassSource;
+use C4::Charset;
 
 use vars qw($VERSION @ISA @EXPORT);
 
@@ -40,6 +41,7 @@ BEGIN {
        @ISA = qw( Exporter );
 
        # to add biblios
+# EXPORTED FUNCTIONS.
        push @EXPORT, qw( 
                &AddBiblio
        );
@@ -60,7 +62,6 @@ BEGIN {
                &GetMarcSeries
                GetMarcUrls
                &GetUsedMarcStructure
-
                &GetXmlBiblio
 
                &GetAuthorisedValueDesc
@@ -77,12 +78,17 @@ BEGIN {
                &ModBiblioframework
                &ModZebra
        );
-
        # To delete something
        push @EXPORT, qw(
                &DelBiblio
        );
 
+    # To link headings in a bib record
+    # to authority records.
+    push @EXPORT, qw(
+        &LinkBibHeadingsToAuthorities
+    );
+
        # Internal functions
        # those functions are exported but should not be used
        # they are usefull is few circumstances, so are exported.
@@ -90,7 +96,6 @@ BEGIN {
        push @EXPORT, qw(
                &ModBiblioMarc
        );
-
        # Others functions
        push @EXPORT, qw(
                &TransformMarcToKoha
@@ -98,11 +103,16 @@ BEGIN {
                &TransformHtmlToMarc
                &TransformHtmlToXml
                &PrepareItemrecordDisplay
-               &char_decode
                &GetNoZebraIndexes
        );
 }
 
+# because of interdependencies between
+# C4::Search, C4::Heading, and C4::Biblio,
+# 'use C4::Heading' must occur after
+# the exports have been defined.
+use C4::Heading;
+
 =head1 NAME
 
 C4::Biblio - cataloging management functions
@@ -223,19 +233,38 @@ sub AddBiblio {
 
     _koha_marc_update_bib_ids($record, $frameworkcode, $biblionumber, $biblioitemnumber);
 
+    # update MARC subfield that stores biblioitems.cn_sort
+    _koha_marc_update_biblioitem_cn_sort($record, $olddata, $frameworkcode);
+    
     # now add the record
     $biblionumber = ModBiblioMarc( $record, $biblionumber, $frameworkcode ) unless $defer_marc_save;
       
-    &logaction(C4::Context->userenv->{'number'},"CATALOGUING","ADD",$biblionumber,"biblio") 
-        if C4::Context->preference("CataloguingLog");
+    logaction("CATALOGUING", "ADD", $biblionumber, "biblio") if C4::Context->preference("CataloguingLog");
 
     return ( $biblionumber, $biblioitemnumber );
 }
 
 =head2 ModBiblio
 
+=over 4
+
     ModBiblio( $record,$biblionumber,$frameworkcode);
-    Exported function (core API) to modify a biblio
+
+=back
+
+Replace an existing bib record identified by C<$biblionumber>
+with one supplied by the MARC::Record object C<$record>.  The embedded
+item, biblioitem, and biblionumber fields from the previous
+version of the bib record replace any such fields of those tags that
+are present in C<$record>.  Consequently, ModBiblio() is not
+to be used to try to modify item records.
+
+C<$frameworkcode> specifies the MARC framework to use
+when storing the modified bib record; among other things,
+this controls how MARC fields get mapped to display columns
+in the C<biblio> and C<biblioitems> tables, as well as
+which fields are used to store embedded item, biblioitem,
+and biblionumber data for indexing.
 
 =cut
 
@@ -243,7 +272,7 @@ sub ModBiblio {
     my ( $record, $biblionumber, $frameworkcode ) = @_;
     if (C4::Context->preference("CataloguingLog")) {
         my $newrecord = GetMarcBiblio($biblionumber);
-        &logaction(C4::Context->userenv->{'number'},"CATALOGUING","MODIFY",$biblionumber,"BEFORE=>".$newrecord->as_formatted);
+        logaction("CATALOGUING", "MODIFY", $biblionumber, "BEFORE=>".$newrecord->as_formatted);
     }
     
     my $dbh = C4::Context->dbh;
@@ -253,6 +282,13 @@ sub ModBiblio {
     # get the items before and append them to the biblio before updating the record, atm we just have the biblio
     my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField("items.itemnumber",$frameworkcode);
     my $oldRecord = GetMarcBiblio( $biblionumber );
+
+    # delete any item fields from incoming record to avoid
+    # duplication or incorrect data - use AddItem() or ModItem()
+    # to change items
+    foreach my $field ($record->field($itemtag)) {
+        $record->delete_field($field);
+    }
     
     # parse each item, and, for an unknown reason, re-encode each subfield 
     # if you don't do that, the record will have encoding mixed
@@ -281,12 +317,15 @@ sub ModBiblio {
     $sth->finish();
     _koha_marc_update_bib_ids($record, $frameworkcode, $biblionumber, $biblioitemnumber);
 
-    # update the MARC record (that now contains biblio and items) with the new record data
-    &ModBiblioMarc( $record, $biblionumber, $frameworkcode );
-    
     # load the koha-table data object
     my $oldbiblio = TransformMarcToKoha( $dbh, $record, $frameworkcode );
 
+    # update MARC subfield that stores biblioitems.cn_sort
+    _koha_marc_update_biblioitem_cn_sort($record, $oldbiblio, $frameworkcode);
+
+    # update the MARC record (that now contains biblio and items) with the new record data
+    &ModBiblioMarc( $record, $biblionumber, $frameworkcode );
+    
     # modify the other koha tables
     _koha_modify_biblio( $dbh, $oldbiblio, $frameworkcode );
     _koha_modify_biblioitem_nonmarc( $dbh, $oldbiblio );
@@ -346,7 +385,13 @@ sub DelBiblio {
     # - we need to read the biblio if NoZebra is set (to remove it from the indexes
     # - if something goes wrong, the biblio may be deleted from Koha but not from zebra
     #   and we would have no way to remove it (except manually in zebra, but I bet it would be very hard to handle the problem)
-    ModZebra($biblionumber, "recordDelete", "biblioserver", undef);
+    my $oldRecord;
+    if (C4::Context->preference("NoZebra")) {
+        # only NoZebra indexing needs to have
+        # the previous version of the record
+        $oldRecord = GetMarcBiblio($biblionumber);
+    }
+    ModZebra($biblionumber, "recordDelete", "biblioserver", $oldRecord, undef);
 
     # delete biblioitems and items from Koha tables and save in deletedbiblioitems,deleteditems
     $sth =
@@ -366,11 +411,68 @@ sub DelBiblio {
     # from being generated by _koha_delete_biblioitems
     $error = _koha_delete_biblio( $dbh, $biblionumber );
 
-    &logaction(C4::Context->userenv->{'number'},"CATALOGUING","DELETE",$biblionumber,"") 
-        if C4::Context->preference("CataloguingLog");
+    logaction("CATALOGUING", "DELETE", $biblionumber, "") if C4::Context->preference("CataloguingLog");
+
     return;
 }
 
+=head2 LinkBibHeadingsToAuthorities
+
+=over 4
+
+my $headings_linked = LinkBibHeadingsToAuthorities($marc);
+
+=back
+
+Links bib headings to authority records by checking
+each authority-controlled field in the C<MARC::Record>
+object C<$marc>, looking for a matching authority record,
+and setting the linking subfield $9 to the ID of that
+authority record.  
+
+If no matching authority exists, or if multiple
+authorities match, no $9 will be added, and any 
+existing one inthe field will be deleted.
+
+Returns the number of heading links changed in the
+MARC record.
+
+=cut
+
+sub LinkBibHeadingsToAuthorities {
+    my $bib = shift;
+
+    my $num_headings_changed = 0;
+    foreach my $field ($bib->fields()) {
+        my $heading = C4::Heading->new_from_bib_field($field);    
+        next unless defined $heading;
+
+        # check existing $9
+        my $current_link = $field->subfield('9');
+
+        # look for matching authorities
+        my $authorities = $heading->authorities();
+
+        # want only one exact match
+        if ($#{ $authorities } == 0) {
+            my $authority = MARC::Record->new_from_usmarc($authorities->[0]);
+            my $authid = $authority->field('001')->data();
+            next if defined $current_link and $current_link eq $authid;
+
+            $field->delete_subfield(code => '9') if defined $current_link;
+            $field->add_subfields('9', $authid);
+            $num_headings_changed++;
+        } else {
+            if (defined $current_link) {
+                $field->delete_subfield(code => '9');
+                $num_headings_changed++;
+            }
+        }
+
+    }
+    return $num_headings_changed;
+}
+
 =head2 GetBiblioData
 
 =over 4
@@ -437,12 +539,11 @@ sub GetBiblioItemData {
     my ($biblioitemnumber) = @_;
     my $dbh       = C4::Context->dbh;
     my $query = "SELECT *,biblioitems.notes AS bnotes
-        FROM biblio, biblioitems ";
+        FROM biblio LEFT JOIN biblioitems on biblio.biblionumber=biblioitems.biblioitemnumber ";
     unless(C4::Context->preference('item-level_itypes')) { 
         $query .= "LEFT JOIN itemtypes on biblioitems.itemtype=itemtypes.itemtype ";
     }    
-    $query .= " WHERE biblio.biblionumber = biblioitems.biblionumber 
-        AND biblioitemnumber = ? ";
+    $query .= " WHERE biblioitemnumber = ? ";
     my $sth       =  $dbh->prepare($query);
     my $data;
     $sth->execute($biblioitemnumber);
@@ -581,10 +682,19 @@ $frameworkcode : the framework code to read
 
 =cut
 
+# cache for results of GetMarcStructure -- needed
+# for batch jobs
+our $marc_structure_cache;
+
 sub GetMarcStructure {
     my ( $forlibrarian, $frameworkcode ) = @_;
     my $dbh=C4::Context->dbh;
     $frameworkcode = "" unless $frameworkcode;
+
+    if (defined $marc_structure_cache and exists $marc_structure_cache->{$forlibrarian}->{$frameworkcode}) {
+        return $marc_structure_cache->{$forlibrarian}->{$frameworkcode};
+    }
+
     my $sth;
     my $libfield = ( $forlibrarian eq 1 ) ? 'liblibrarian' : 'libopac';
 
@@ -610,7 +720,7 @@ sub GetMarcStructure {
     {
         $res->{$tag}->{lib} =
           ( $forlibrarian or !$libopac ) ? $liblibrarian : $libopac;
-        $res->{$tab}->{tab}        = "";
+        $res->{$tag}->{tab}        = "";
         $res->{$tag}->{mandatory}  = $mandatory;
         $res->{$tag}->{repeatable} = $repeatable;
     }
@@ -664,6 +774,9 @@ sub GetMarcStructure {
         $res->{$tag}->{$subfield}->{'link'}           = $link;
         $res->{$tag}->{$subfield}->{defaultvalue}     = $defaultvalue;
     }
+
+    $marc_structure_cache->{$forlibrarian}->{$frameworkcode} = $res;
+
     return $res;
 }
 
@@ -725,11 +838,14 @@ sub GetMarcFromKohaField {
 
 =over 4
 
-Returns MARC::Record of the biblionumber passed in parameter.
-the marc record contains both biblio & item datas
+my $record = GetMarcBiblio($biblionumber);
 
 =back
 
+Returns MARC::Record representing bib identified by
+C<$biblionumber>.  If no bib exists, returns undef.
+The MARC record contains both biblio & item data.
+
 =cut
 
 sub GetMarcBiblio {
@@ -738,18 +854,13 @@ sub GetMarcBiblio {
     my $sth          =
       $dbh->prepare("SELECT marcxml FROM biblioitems WHERE biblionumber=? ");
     $sth->execute($biblionumber);
-     my ($marcxml) = $sth->fetchrow;
+    my $row = $sth->fetchrow_hashref;
+    my $marcxml = StripNonXmlChars($row->{'marcxml'});
      MARC::File::XML->default_record_format(C4::Context->preference('marcflavour'));
-     $marcxml =~ s/\x1e//g;
-     $marcxml =~ s/\x1f//g;
-     $marcxml =~ s/\x1d//g;
-     $marcxml =~ s/\x0f//g;
-     $marcxml =~ s/\x0c//g;  
-#   warn $marcxml;
     my $record = MARC::Record->new();
     if ($marcxml) {
         $record = eval {MARC::Record::new_from_xml( $marcxml, "utf8", C4::Context->preference('marcflavour'))};
-        if ($@) {warn $@;}
+        if ($@) {warn " problem with :$biblionumber : $@ \n$marcxml";}
 #      $record = MARC::Record::new_from_usmarc( $marc) if $marc;
         return $record;
     } else {
@@ -912,7 +1023,7 @@ sub GetMarcSubjects {
         my $subfield9 = $field->subfield('9');
         for my $subject_subfield (@subfields ) {
             # don't load unimarc subfields 3,4,5
-            next if (($marcflavour eq "UNIMARC") and ($subject_subfield->[0] =~ (3|4|5) ) );
+            next if (($marcflavour eq "UNIMARC") and ($subject_subfield->[0] =~ /3|4|5/ ) );
             my $code = $subject_subfield->[0];
             my $value = $subject_subfield->[1];
             my $linkvalue = $value;
@@ -926,7 +1037,7 @@ sub GetMarcSubjects {
             my $separator = C4::Context->preference("authoritysep") unless $counter==0;
             # ignore $9
             my @this_link_loop = @link_loop;
-            push @subfields_loop, {code => $code, value => $value, link_loop => \@this_link_loop, separator => $separator} unless ($subject_subfield->[0] == 9 );
+            push @subfields_loop, {code => $code, value => $value, link_loop => \@this_link_loop, separator => $separator} unless ($subject_subfield->[0] eq 9 );
             $counter++;
         }
                 
@@ -976,7 +1087,7 @@ sub GetMarcAuthors {
         my $subfield9 = $field->subfield('9');
         for my $authors_subfield (@subfields) {
             # don't load unimarc subfields 3, 5
-            next if ($marcflavour eq 'UNIMARC' and ($authors_subfield->[0] =~ (3|5) ) );
+            next if ($marcflavour eq 'UNIMARC' and ($authors_subfield->[0] =~ /3|5/ ) );
             my $subfieldcode = $authors_subfield->[0];
             my $value = $authors_subfield->[1];
             my $linkvalue = $value;
@@ -984,15 +1095,16 @@ sub GetMarcAuthors {
             my $operator = " and " unless $count_auth==0;
             # if we have an authority link, use that as the link, otherwise use standard searching
             if ($subfield9) {
-                @link_loop = ({'limit' => 'Koha-Auth-Number' ,link => "$subfield9" });
+                @link_loop = ({'limit' => 'an' ,link => "$subfield9" });
             }
             else {
                 # reset $linkvalue if UNIMARC author responsibility
-                if ( $marcflavour eq 'UNIMARC' and ($authors_subfield->[0] eq '4')) {
+                if ( $marcflavour eq 'UNIMARC' and ($authors_subfield->[0] eq "4")) {
                     $linkvalue = "(".GetAuthorisedValueDesc( $field->tag(), $authors_subfield->[0], $authors_subfield->[1], '', $tagslib ).")";
                 }
                 push @link_loop, {'limit' => 'au', link => $linkvalue, operator => $operator };
             }
+            $value = GetAuthorisedValueDesc( $field->tag(), $authors_subfield->[0], $authors_subfield->[1], '', $tagslib ) if ( $marcflavour eq 'UNIMARC' and ($authors_subfield->[0] =~/4/));
             my @this_link_loop = @link_loop;
             my $separator = C4::Context->preference("authoritysep") unless $count_auth==0;
             push @subfields_loop, {code => $subfieldcode, value => $value, link_loop => \@this_link_loop, separator => $separator} unless ($authors_subfield->[0] == 9 );
@@ -1025,17 +1137,29 @@ sub GetMarcUrls {
         for my $note ( $field->subfield('z')) {
             push @notes , {note => $note};
         }        
-        $marcurl = {  MARCURL => $url,
-                      notes => \@notes,
-                    };
         if($marcflavour eq 'MARC21') {
             my $s3 = $field->subfield('3');
             my $link = $field->subfield('y');
-            $marcurl->{'linktext'} = $link || $s3 || $url ;;
+                       unless($url =~ /^\w+:/) {
+                               if($field->indicator(1) eq '7') {
+                                       $url = $field->subfield('2') . "://" . $url;
+                               } elsif ($field->indicator(1) eq '1') {
+                                       $url = 'ftp://' . $url;
+                               } else {  
+                                       #  properly, this should be if ind1=4,
+                                       #  however we will assume http protocol since we're building a link.
+                                       $url = 'http://' . $url;
+                               }
+                       }
+                       # TODO handle ind 2 (relationship)
+               $marcurl = {  MARCURL => $url,
+                      notes => \@notes,
+            };
+            $marcurl->{'linktext'} = $link || $s3 || C4::Context->preference('URLLinkText') || $url ;;
             $marcurl->{'part'} = $s3 if($link);
             $marcurl->{'toc'} = 1 if($s3 =~ /^[Tt]able/) ;
         } else {
-            $marcurl->{'linktext'} = $url;
+            $marcurl->{'linktext'} = $url || C4::Context->preference('URLLinkText') ;
         }
         push @marcurls, $marcurl;    
     }
@@ -1366,10 +1490,12 @@ sub TransformHtmlToXml {
     L<$record> = TransformHtmlToMarc(L<$params>,L<$cgi>)
     L<$params> is a ref to an array as below:
     {
-        'tag_010_indicator_531951' ,
+        'tag_010_indicator1_531951' ,
+        'tag_010_indicator2_531951' ,
         'tag_010_code_a_531951_145735' ,
         'tag_010_subfield_a_531951_145735' ,
-        'tag_200_indicator_873510' ,
+        'tag_200_indicator1_873510' ,
+        'tag_200_indicator2_873510' ,
         'tag_200_code_a_873510_673465' ,
         'tag_200_subfield_a_873510_673465' ,
         'tag_200_code_b_873510_704318' ,
@@ -1387,7 +1513,21 @@ sub TransformHtmlToXml {
 sub TransformHtmlToMarc {
     my $params = shift;
     my $cgi    = shift;
-    
+   
+    # explicitly turn on the UTF-8 flag for all
+    # 'tag_' parameters to avoid incorrect character
+    # conversion later on
+    my $cgi_params = $cgi->Vars;
+    foreach my $param_name (keys %$cgi_params) {
+        if ($param_name =~ /^tag_/) {
+            my $param_value = $cgi_params->{$param_name};
+            if (utf8::decode($param_value)) {
+                $cgi_params->{$param_name} = $param_value;
+            } 
+            # FIXME - need to do something if string is not valid UTF-8
+        }
+    }
+   
     # creating a new record
     my $record  = MARC::Record->new();
     my $i=0;
@@ -1414,20 +1554,20 @@ sub TransformHtmlToMarc {
             }
             push @fields,$newfield if($newfield);
         } 
-        elsif ($param =~ /^tag_(\d*)_indicator_/){ # new field start when having 'input name="..._indicator_..."
+        elsif ($param =~ /^tag_(\d*)_indicator1_/){ # new field start when having 'input name="..._indicator1_..."
             my $tag  = $1;
             
             my $ind1 = substr($cgi->param($param),0,1);
-            my $ind2 = substr($cgi->param($param),1,1);
+            my $ind2 = substr($cgi->param($params->[$i+1]),0,1);
             $newfield=0;
-            my $j=$i+1;
+            my $j=$i+2;
             
             if($tag < 10){ # no code for theses fields
     # in MARC editor, 000 contains the leader.
                 if ($tag eq '000' ) {
                     $record->leader($cgi->param($params->[$j+1])) if length($cgi->param($params->[$j+1]))==24;
     # between 001 and 009 (included)
-                } else {
+                } elsif ($cgi->param($params->[$j+1]) ne '') {
                     $newfield = MARC::Field->new(
                         $tag,
                         $cgi->param($params->[$j+1]),
@@ -1438,13 +1578,13 @@ sub TransformHtmlToMarc {
                 while($params->[$j] =~ /_code_/){ # browse all it's subfield
                     my $inner_param = $params->[$j];
                     if ($newfield){
-                        if($cgi->param($params->[$j+1])){  # only if there is a value (code => value)
+                        if($cgi->param($params->[$j+1]) ne ''){  # only if there is a value (code => value)
                             $newfield->add_subfields(
                                 $cgi->param($inner_param) => $cgi->param($params->[$j+1])
                             );
                         }
                     } else {
-                        if ( $cgi->param($params->[$j+1]) ) { # creating only if there is a value (code => value)
+                        if ( $cgi->param($params->[$j+1]) ne '' ) { # creating only if there is a value (code => value)
                             $newfield = MARC::Field->new(
                                 $tag,
                                 ''.$ind1,
@@ -1725,202 +1865,6 @@ sub TransformMarcToKohaOneField {
 
 =head1  OTHER FUNCTIONS
 
-=head2 char_decode
-
-=over 4
-
-my $string = char_decode( $string, $encoding );
-
-converts ISO 5426 coded string to UTF-8
-sloppy code : should be improved in next issue
-
-=back
-
-=cut
-
-sub char_decode {
-    my ( $string, $encoding ) = @_;
-    $_ = $string;
-
-    $encoding = C4::Context->preference("marcflavour") unless $encoding;
-    if ( $encoding eq "UNIMARC" ) {
-
-        #         s/\xe1/Æ/gm;
-        s/\xe2/Ğ/gm;
-        s/\xe9/Ø/gm;
-        s/\xec/ş/gm;
-        s/\xf1/æ/gm;
-        s/\xf3/ğ/gm;
-        s/\xf9/ø/gm;
-        s/\xfb/ß/gm;
-        s/\xc1\x61/à/gm;
-        s/\xc1\x65/è/gm;
-        s/\xc1\x69/ì/gm;
-        s/\xc1\x6f/ò/gm;
-        s/\xc1\x75/ù/gm;
-        s/\xc1\x41/À/gm;
-        s/\xc1\x45/È/gm;
-        s/\xc1\x49/Ì/gm;
-        s/\xc1\x4f/Ò/gm;
-        s/\xc1\x55/Ù/gm;
-        s/\xc2\x41/Á/gm;
-        s/\xc2\x45/É/gm;
-        s/\xc2\x49/Í/gm;
-        s/\xc2\x4f/Ó/gm;
-        s/\xc2\x55/Ú/gm;
-        s/\xc2\x59/İ/gm;
-        s/\xc2\x61/á/gm;
-        s/\xc2\x65/é/gm;
-        s/\xc2\x69/í/gm;
-        s/\xc2\x6f/ó/gm;
-        s/\xc2\x75/ú/gm;
-        s/\xc2\x79/ı/gm;
-        s/\xc3\x41/Â/gm;
-        s/\xc3\x45/Ê/gm;
-        s/\xc3\x49/Î/gm;
-        s/\xc3\x4f/Ô/gm;
-        s/\xc3\x55/Û/gm;
-        s/\xc3\x61/â/gm;
-        s/\xc3\x65/ê/gm;
-        s/\xc3\x69/î/gm;
-        s/\xc3\x6f/ô/gm;
-        s/\xc3\x75/û/gm;
-        s/\xc4\x41/Ã/gm;
-        s/\xc4\x4e/Ñ/gm;
-        s/\xc4\x4f/Õ/gm;
-        s/\xc4\x61/ã/gm;
-        s/\xc4\x6e/ñ/gm;
-        s/\xc4\x6f/õ/gm;
-        s/\xc8\x41/Ä/gm;
-        s/\xc8\x45/Ë/gm;
-        s/\xc8\x49/Ï/gm;
-        s/\xc8\x61/ä/gm;
-        s/\xc8\x65/ë/gm;
-        s/\xc8\x69/ï/gm;
-        s/\xc8\x6F/ö/gm;
-        s/\xc8\x75/ü/gm;
-        s/\xc8\x76/ÿ/gm;
-        s/\xc9\x41/Ä/gm;
-        s/\xc9\x45/Ë/gm;
-        s/\xc9\x49/Ï/gm;
-        s/\xc9\x4f/Ö/gm;
-        s/\xc9\x55/Ü/gm;
-        s/\xc9\x61/ä/gm;
-        s/\xc9\x6f/ö/gm;
-        s/\xc9\x75/ü/gm;
-        s/\xca\x41/Å/gm;
-        s/\xca\x61/å/gm;
-        s/\xd0\x43/Ç/gm;
-        s/\xd0\x63/ç/gm;
-
-        # this handles non-sorting blocks (if implementation requires this)
-        $string = nsb_clean($_);
-    }
-    elsif ( $encoding eq "USMARC" || $encoding eq "MARC21" ) {
-        ##MARC-8 to UTF-8
-
-        s/\xe1\x61/à/gm;
-        s/\xe1\x65/è/gm;
-        s/\xe1\x69/ì/gm;
-        s/\xe1\x6f/ò/gm;
-        s/\xe1\x75/ù/gm;
-        s/\xe1\x41/À/gm;
-        s/\xe1\x45/È/gm;
-        s/\xe1\x49/Ì/gm;
-        s/\xe1\x4f/Ò/gm;
-        s/\xe1\x55/Ù/gm;
-        s/\xe2\x41/Á/gm;
-        s/\xe2\x45/É/gm;
-        s/\xe2\x49/Í/gm;
-        s/\xe2\x4f/Ó/gm;
-        s/\xe2\x55/Ú/gm;
-        s/\xe2\x59/İ/gm;
-        s/\xe2\x61/á/gm;
-        s/\xe2\x65/é/gm;
-        s/\xe2\x69/í/gm;
-        s/\xe2\x6f/ó/gm;
-        s/\xe2\x75/ú/gm;
-        s/\xe2\x79/ı/gm;
-        s/\xe3\x41/Â/gm;
-        s/\xe3\x45/Ê/gm;
-        s/\xe3\x49/Î/gm;
-        s/\xe3\x4f/Ô/gm;
-        s/\xe3\x55/Û/gm;
-        s/\xe3\x61/â/gm;
-        s/\xe3\x65/ê/gm;
-        s/\xe3\x69/î/gm;
-        s/\xe3\x6f/ô/gm;
-        s/\xe3\x75/û/gm;
-        s/\xe4\x41/Ã/gm;
-        s/\xe4\x4e/Ñ/gm;
-        s/\xe4\x4f/Õ/gm;
-        s/\xe4\x61/ã/gm;
-        s/\xe4\x6e/ñ/gm;
-        s/\xe4\x6f/õ/gm;
-        s/\xe6\x41/Ă/gm;
-        s/\xe6\x45/Ĕ/gm;
-        s/\xe6\x65/ĕ/gm;
-        s/\xe6\x61/ă/gm;
-        s/\xe8\x45/Ë/gm;
-        s/\xe8\x49/Ï/gm;
-        s/\xe8\x65/ë/gm;
-        s/\xe8\x69/ï/gm;
-        s/\xe8\x76/ÿ/gm;
-        s/\xe9\x41/A/gm;
-        s/\xe9\x4f/O/gm;
-        s/\xe9\x55/U/gm;
-        s/\xe9\x61/a/gm;
-        s/\xe9\x6f/o/gm;
-        s/\xe9\x75/u/gm;
-        s/\xea\x41/A/gm;
-        s/\xea\x61/a/gm;
-
-        #Additional Turkish characters
-        s/\x1b//gm;
-        s/\x1e//gm;
-        s/(\xf0)s/\xc5\x9f/gm;
-        s/(\xf0)S/\xc5\x9e/gm;
-        s/(\xf0)c/ç/gm;
-        s/(\xf0)C/Ç/gm;
-        s/\xe7\x49/\\xc4\xb0/gm;
-        s/(\xe6)G/\xc4\x9e/gm;
-        s/(\xe6)g/ğ\xc4\x9f/gm;
-        s/\xB8/ı/gm;
-        s/\xB9/£/gm;
-        s/(\xe8|\xc8)o/ö/gm;
-        s/(\xe8|\xc8)O/Ö/gm;
-        s/(\xe8|\xc8)u/ü/gm;
-        s/(\xe8|\xc8)U/Ü/gm;
-        s/\xc2\xb8/\xc4\xb1/gm;
-        s/¸/\xc4\xb1/gm;
-
-        # this handles non-sorting blocks (if implementation requires this)
-        $string = nsb_clean($_);
-    }
-    return ($string);
-}
-
-=head2 nsb_clean
-
-=over 4
-
-my $string = nsb_clean( $string, $encoding );
-
-=back
-
-=cut
-
-sub nsb_clean {
-    my $NSB      = '\x88';    # NSB : begin Non Sorting Block
-    my $NSE      = '\x89';    # NSE : Non Sorting Block end
-                              # handles non sorting blocks
-    my ($string) = @_;
-    $_ = $string;
-    s/$NSB/(/gm;
-    s/[ ]{0,1}$NSE/) /gm;
-    $string = $_;
-    return ($string);
-}
 
 =head2 PrepareItemrecordDisplay
 
@@ -1936,14 +1880,14 @@ Returns a hash with all the fields for Display a given item data in a template
 
 sub PrepareItemrecordDisplay {
 
-    my ( $bibnum, $itemnum ) = @_;
+    my ( $bibnum, $itemnum, $defaultvalues ) = @_;
 
     my $dbh = C4::Context->dbh;
     my $frameworkcode = &GetFrameworkCode( $bibnum );
     my ( $itemtagfield, $itemtagsubfield ) =
       &GetMarcFromKohaField( "items.itemnumber", $frameworkcode );
     my $tagslib = &GetMarcStructure( 1, $frameworkcode );
-    my $itemrecord = GetMarcItem( $bibnum, $itemnum) if ($itemnum);
+    my $itemrecord = C4::Items::GetMarcItem( $bibnum, $itemnum) if ($itemnum);
     my @loop_data;
     my $authorised_values_sth =
       $dbh->prepare(
@@ -1965,11 +1909,7 @@ sub PrepareItemrecordDisplay {
                   $tagslib->{$tag}->{$subfield}->{'kohafield'};
 
          #        $subfield_data{marc_lib}=$tagslib->{$tag}->{$subfield}->{lib};
-                $subfield_data{marc_lib} =
-                    "<span id=\"error\" title=\""
-                  . $tagslib->{$tag}->{$subfield}->{lib} . "\">"
-                  . substr( $tagslib->{$tag}->{$subfield}->{lib}, 0, 12 )
-                  . "</span>";
+                $subfield_data{marc_lib} = $tagslib->{$tag}->{$subfield}->{lib};
                 $subfield_data{mandatory} =
                   $tagslib->{$tag}->{$subfield}->{mandatory};
                 $subfield_data{repeatable} =
@@ -1995,6 +1935,26 @@ sub PrepareItemrecordDisplay {
                         $value = $temp->subfield($CNsubfield);
                     }
                 }
+                if ( $tagslib->{$tag}->{$subfield}->{kohafield} eq
+                    'items.itemcallnumber'
+                    && $defaultvalues->{'callnumber'} )
+                {
+                    my $temp = $itemrecord->field($subfield) if ($itemrecord);
+                    unless ($temp) {
+                        $value = $defaultvalues->{'callnumber'};
+                    }
+                }
+                if ( ($tagslib->{$tag}->{$subfield}->{kohafield} eq
+                    'items.holdingbranch' ||
+                    $tagslib->{$tag}->{$subfield}->{kohafield} eq
+                    'items.homebranch')          
+                    && $defaultvalues->{'branchcode'} )
+                {
+                    my $temp = $itemrecord->field($subfield) if ($itemrecord);
+                    unless ($temp) {
+                        $value = $defaultvalues->{branchcode};
+                    }
+                }
                 if ( $tagslib->{$tag}->{$subfield}->{authorised_value} ) {
                     my @authorised_values;
                     my %authorised_lib;
@@ -2084,7 +2044,7 @@ sub PrepareItemrecordDisplay {
                 }
                 elsif ( $tagslib->{$tag}->{$subfield}->{thesaurus_category} ) {
                     $subfield_data{marc_value} =
-"<input type=\"text\" name=\"field_value\"  size=47 maxlength=255> <a href=\"javascript:Dopop('cataloguing/thesaurus_popup.pl?category=$tagslib->{$tag}->{$subfield}->{thesaurus_category}&index=',)\">...</a>";
+"<input type=\"text\" name=\"field_value\"  size=\"47\" maxlength=\"255\" /> <a href=\"javascript:Dopop('cataloguing/thesaurus_popup.pl?category=$tagslib->{$tag}->{$subfield}->{thesaurus_category}&index=',)\">...</a>";
 
 #"
 # COMMENTED OUT because No $i is provided with this API.
@@ -2099,7 +2059,7 @@ sub PrepareItemrecordDisplay {
                 }
                 else {
                     $subfield_data{marc_value} =
-"<input type=\"text\" name=\"field_value\" value=\"$value\" size=50 maxlength=255>";
+"<input type=\"text\" name=\"field_value\" value=\"$value\" size=\"50\" maxlength=\"255\" />";
                 }
                 push( @loop_data, \%subfield_data );
             }
@@ -2152,11 +2112,14 @@ sub PrepareItemrecordDisplay {
 
 =over 4
 
-ModZebra( $biblionumber, $op, $server, $newRecord );
+ModZebra( $biblionumber, $op, $server, $oldRecord, $newRecord );
 
     $biblionumber is the biblionumber we want to index
     $op is specialUpdate or delete, and is used to know what we want to do
     $server is the server that we want to update
+    $oldRecord is the MARC::Record containing the previous version of the record.  This is used only when 
+      NoZebra=1, as NoZebra indexing needs to know the previous version of a record in order to
+      do an update.
     $newRecord is the MARC::Record containing the new record. It is usefull only when NoZebra=1, and is used to know what to add to the nozebra database. (the record in mySQL being, if it exist, the previous record, the one just before the modif. We need both : the previous and the new one.
     
 =back
@@ -2165,7 +2128,7 @@ ModZebra( $biblionumber, $op, $server, $newRecord );
 
 sub ModZebra {
 ###Accepts a $server variable thus we can use it for biblios authorities or other zebra dbs
-    my ( $biblionumber, $op, $server, $newRecord ) = @_;
+    my ( $biblionumber, $op, $server, $oldRecord, $newRecord ) = @_;
     my $dbh=C4::Context->dbh;
 
     # true ModZebra commented until indexdata fixes zebraDB crashes (it seems they occur on multiple updates
@@ -2177,26 +2140,20 @@ sub ModZebra {
         # lock the nozebra table : we will read index lines, update them in Perl process
         # and write everything in 1 transaction.
         # lock the table to avoid someone else overwriting what we are doing
-        $dbh->do('LOCK TABLES nozebra WRITE,biblio WRITE,biblioitems WRITE, systempreferences WRITE, auth_types WRITE, auth_header WRITE');
-        my %result; # the result hash that will be builded by deletion / add, and written on mySQL at the end, to improve speed
-        my $record;
-        if ($server eq 'biblioserver') {
-            $record= GetMarcBiblio($biblionumber);
-        } else {
-            $record= C4::AuthoritiesMarc::GetAuthority($biblionumber);
-        }
+        $dbh->do('LOCK TABLES nozebra WRITE,biblio WRITE,biblioitems WRITE, systempreferences WRITE, auth_types WRITE, auth_header WRITE, auth_subfield_structure READ');
+        my %result; # the result hash that will be built by deletion / add, and written on mySQL at the end, to improve speed
         if ($op eq 'specialUpdate') {
             # OK, we have to add or update the record
             # 1st delete (virtually, in indexes), if record actually exists
-            if ($record) { 
-                %result = _DelBiblioNoZebra($biblionumber,$record,$server);
+            if ($oldRecord) { 
+                %result = _DelBiblioNoZebra($biblionumber,$oldRecord,$server);
             }
             # ... add the record
             %result=_AddBiblioNoZebra($biblionumber,$newRecord, $server, %result);
         } else {
             # it's a deletion, delete the record...
             # warn "DELETE the record $biblionumber on $server".$record->as_formatted;
-            %result=_DelBiblioNoZebra($biblionumber,$record,$server);
+            %result=_DelBiblioNoZebra($biblionumber,$oldRecord,$server);
         }
         # ok, now update the database...
         my $sth = $dbh->prepare("UPDATE nozebra SET biblionumbers=? WHERE server=? AND indexname=? AND value=?");
@@ -2206,14 +2163,24 @@ sub ModZebra {
             }
         }
         $dbh->do('UNLOCK TABLES');
-
     } else {
         #
         # we use zebra, just fill zebraqueue table
         #
-        my $sth=$dbh->prepare("INSERT INTO zebraqueue  (biblio_auth_number,server,operation) VALUES(?,?,?)");
-        $sth->execute($biblionumber,$server,$op);
-        $sth->finish;
+        my $check_sql = "SELECT COUNT(*) FROM zebraqueue 
+                         WHERE server = ?
+                         AND   biblio_auth_number = ?
+                         AND   operation = ?
+                         AND   done = 0";
+        my $check_sth = $dbh->prepare_cached($check_sql);
+        $check_sth->execute($server, $biblionumber, $op);
+        my ($count) = $check_sth->fetchrow_array;
+        $check_sth->finish();
+        if ($count == 0) {
+            my $sth=$dbh->prepare("INSERT INTO zebraqueue  (biblio_auth_number,server,operation) VALUES(?,?,?)");
+            $sth->execute($biblionumber,$server,$op);
+            $sth->finish;
+        }
     }
 }
 
@@ -2271,12 +2238,13 @@ sub _DelBiblioNoZebra {
         $title = lc($record->subfield($titletag,$titlesubfield));
     } else {
         # for authorities, the "title" is the $a mainentry
-        my $authref = C4::AuthoritiesMarc::GetAuthType($record->subfield(152,'b'));
+        my ($auth_type_tag, $auth_type_sf) = C4::AuthoritiesMarc::get_auth_type_location();
+        my $authref = C4::AuthoritiesMarc::GetAuthType($record->subfield($auth_type_tag, $auth_type_sf));
         warn "ERROR : authtype undefined for ".$record->as_formatted unless $authref;
         $title = $record->subfield($authref->{auth_tag_to_report},'a');
         $index{'mainmainentry'}= $authref->{'auth_tag_to_report'}.'a';
         $index{'mainentry'}    = $authref->{'auth_tag_to_report'}.'*';
-        $index{'auth_type'}    = '152b';
+        $index{'auth_type'}    = "${auth_type_tag}${auth_type_sf}";
     }
     
     my %result;
@@ -2365,16 +2333,17 @@ sub _AddBiblioNoZebra {
     } else {
         # warn "server : $server";
         # for authorities, the "title" is the $a mainentry
-        my $authref = C4::AuthoritiesMarc::GetAuthType($record->subfield(152,'b'));
+        my ($auth_type_tag, $auth_type_sf) = C4::AuthoritiesMarc::get_auth_type_location();
+        my $authref = C4::AuthoritiesMarc::GetAuthType($record->subfield($auth_type_tag, $auth_type_sf));
         warn "ERROR : authtype undefined for ".$record->as_formatted unless $authref;
         $title = $record->subfield($authref->{auth_tag_to_report},'a');
         $index{'mainmainentry'} = $authref->{auth_tag_to_report}.'a';
         $index{'mainentry'}     = $authref->{auth_tag_to_report}.'*';
-        $index{'auth_type'}     = '152b';
+        $index{'auth_type'}    = "${auth_type_tag}${auth_type_sf}";
     }
 
     # remove blancks comma (that could cause problem when decoding the string for CQL retrieval) and regexp specific values
-    $title =~ s/ |,|;|\[|\]|\(|\)|\*|-|'|=//g;
+    $title =~ s/ |\.|,|;|\[|\]|\(|\)|\*|-|'|:|=|\r|\n//g;
     # limit to 10 char, should be enough, and limit the DB size
     $title = substr($title,0,10);
     #parse each field
@@ -2386,6 +2355,7 @@ sub _AddBiblioNoZebra {
             my $tag = $field->tag();
             my $subfieldcode = $subfield->[0];
             my $indexed=0;
+#             warn "INDEXING :".$subfield->[1];
             # check each index to see if the subfield is stored somewhere
             # otherwise, store it in __RAW__ index
             foreach my $key (keys %index) {
@@ -2394,15 +2364,15 @@ sub _AddBiblioNoZebra {
                     $indexed=1;
                     my $line= lc $subfield->[1];
                     # remove meaningless value in the field...
-                    $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:/ /g;
+                    $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:|\r|\n/ /g;
                     # ... and split in words
                     foreach (split / /,$line) {
                         next unless $_; # skip  empty values (multiple spaces)
                         # if the entry is already here, improve weight
 #                         warn "managing $_";
-                        if ($result{$key}->{"$_"} =~ /$biblionumber,$title\-(\d);/) {
+                        if ($result{$key}->{"$_"} =~ /$biblionumber,\Q$title\E\-(\d);/) { 
                             my $weight=$1+1;
-                            $result{$key}->{"$_"} =~ s/$biblionumber,$title\-(\d);//;
+                            $result{$key}->{"$_"} =~ s/$biblionumber,\Q$title\E\-(\d);//;
                             $result{$key}->{"$_"} .= "$biblionumber,$title-$weight;";
                         } else {
                             # get the value if it exist in the nozebra table, otherwise, create it
@@ -2412,7 +2382,7 @@ sub _AddBiblioNoZebra {
                             if ($existing_biblionumbers) {
                                 $result{$key}->{"$_"} =$existing_biblionumbers;
                                 my $weight=$1+1;
-                                $result{$key}->{"$_"} =~ s/$biblionumber,$title\-(\d);//;
+                                $result{$key}->{"$_"} =~ s/$biblionumber,\Q$title\E\-(\d);//;
                                 $result{$key}->{"$_"} .= "$biblionumber,$title-$weight;";
                             # create a new ligne for this entry
                             } else {
@@ -2427,14 +2397,14 @@ sub _AddBiblioNoZebra {
             # the subfield is not indexed, store it in __RAW__ index anyway
             unless ($indexed) {
                 my $line= lc $subfield->[1];
-                $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:/ /g;
+                $line =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|<|>|&|\+|\*|\/|=|:|\r|\n/ /g;
                 # ... and split in words
                 foreach (split / /,$line) {
                     next unless $_; # skip  empty values (multiple spaces)
                     # if the entry is already here, improve weight
-                    if ($result{'__RAW__'}->{"$_"} =~ /$biblionumber,$title\-(\d);/) {
+                    if ($result{'__RAW__'}->{"$_"} =~ /$biblionumber,\Q$title\E\-(\d);/) { 
                         my $weight=$1+1;
-                        $result{'__RAW__'}->{"$_"} =~ s/$biblionumber,$title\-(\d);//;
+                        $result{'__RAW__'}->{"$_"} =~ s/$biblionumber,\Q$title\E\-(\d);//;
                         $result{'__RAW__'}->{"$_"} .= "$biblionumber,$title-$weight;";
                     } else {
                         # get the value if it exist in the nozebra table, otherwise, create it
@@ -2444,7 +2414,7 @@ sub _AddBiblioNoZebra {
                         if ($existing_biblionumbers) {
                             $result{'__RAW__'}->{"$_"} =$existing_biblionumbers;
                             my $weight=$1+1;
-                            $result{'__RAW__'}->{"$_"} =~ s/$biblionumber,$title\-(\d);//;
+                            $result{'__RAW__'}->{"$_"} =~ s/$biblionumber,\Q$title\E\-(\d);//;
                             $result{'__RAW__'}->{"$_"} .= "$biblionumber,$title-$weight;";
                         # create a new ligne for this entry
                         } else {
@@ -2543,7 +2513,7 @@ sub _koha_marc_update_bib_ids {
 
         # drop old field and create new one...
         $old_field = $record->field($biblio_tag);
-        $record->delete_field($old_field);
+        $record->delete_field($old_field) if $old_field;
         $record->append_fields($new_field);
 
         # deal with biblioitemnumber
@@ -2556,7 +2526,7 @@ sub _koha_marc_update_bib_ids {
         }
         # drop old field and create new one...
         $old_field = $record->field($biblioitem_tag);
-        $record->delete_field($old_field);
+        $record->delete_field($old_field) if $old_field;
         $record->insert_fields_ordered($new_field);
 
     } else {
@@ -2569,11 +2539,49 @@ sub _koha_marc_update_bib_ids {
 
         # drop old field and create new one...
         my $old_field = $record->field($biblio_tag);
-        $record->delete_field($old_field);
+        $record->delete_field($old_field) if $old_field;
         $record->insert_fields_ordered($new_field);
     }
 }
 
+=head2 _koha_marc_update_biblioitem_cn_sort
+
+=over 4
+
+_koha_marc_update_biblioitem_cn_sort($marc, $biblioitem, $frameworkcode);
+
+=back
+
+Given a MARC bib record and the biblioitem hash, update the
+subfield that contains a copy of the value of biblioitems.cn_sort.
+
+=cut
+
+sub _koha_marc_update_biblioitem_cn_sort {
+    my $marc = shift;
+    my $biblioitem = shift;
+    my $frameworkcode= shift;
+
+    my ($biblioitem_tag, $biblioitem_subfield ) = GetMarcFromKohaField("biblioitems.cn_sort",$frameworkcode);
+    return unless $biblioitem_tag;
+
+    my ($cn_sort) = GetClassSort($biblioitem->{'biblioitems.cn_source'}, $biblioitem->{'cn_class'}, $biblioitem->{'cn_item'} );
+
+    if (my $field = $marc->field($biblioitem_tag)) {
+        $field->delete_subfield(code => $biblioitem_subfield);
+        if ($cn_sort ne '') {
+            $field->add_subfields($biblioitem_subfield => $cn_sort);
+        }
+    } else {
+        # if we get here, no biblioitem tag is present in the MARC record, so
+        # we'll create it if $cn_sort is not empty -- this would be
+        # an odd combination of events, however
+        if ($cn_sort) {
+            $marc->insert_grouped_field(MARC::Field->new($biblioitem_tag, ' ', ' ', $biblioitem_subfield => $cn_sort));
+        }
+    }
+}
+
 =head2 _koha_add_biblio
 
 =over 4
@@ -3013,13 +3021,19 @@ sub ModBiblioMarc {
                 MARC::Field->new( 100, "", "", "a" => $string ) );
         }
     }
-    ModZebra($biblionumber,"specialUpdate","biblioserver",$record);
+    my $oldRecord;
+    if (C4::Context->preference("NoZebra")) {
+        # only NoZebra indexing needs to have
+        # the previous version of the record
+        $oldRecord = GetMarcBiblio($biblionumber);
+    }
     $sth =
       $dbh->prepare(
         "UPDATE biblioitems SET marc=?,marcxml=? WHERE biblionumber=?");
     $sth->execute( $record->as_usmarc(), $record->as_xml_record($encoding),
         $biblionumber );
     $sth->finish;
+    ModZebra($biblionumber,"specialUpdate","biblioserver",$oldRecord,$record);
     return $biblionumber;
 }
 
@@ -3131,6 +3145,68 @@ sub set_service_options {
     return $serviceOptions;
 }
 
+=head3 get_biblio_authorised_values
+
+  find the types and values for all authorised values assigned to this biblio.
+
+  parameters:
+    biblionumber
+
+  returns: a hashref malling the authorised value to the value set for this biblionumber
+
+      $authorised_values = {
+                             'Scent'     => 'flowery',
+                             'Audience'  => 'Young Adult',
+                             'itemtypes' => 'SER',
+                           };
+
+  Notes: forlibrarian should probably be passed in, and called something different.
+
+
+=cut
+
+sub get_biblio_authorised_values {
+    my $biblionumber = shift;
+    
+    my $forlibrarian = 1; # are we in staff or opac?
+    my $frameworkcode = GetFrameworkCode( $biblionumber );
+
+    my $authorised_values;
+
+    my $record  = GetMarcBiblio( $biblionumber )
+      or return $authorised_values;
+    my $tagslib = GetMarcStructure( $forlibrarian, $frameworkcode )
+      or return $authorised_values;
+
+    # assume that these entries in the authorised_value table are bibliolevel.
+    # ones that start with 'item%' are item level.
+    my $query = q(SELECT distinct authorised_value, kohafield
+                    FROM marc_subfield_structure
+                    WHERE authorised_value !=''
+                      AND (kohafield like 'biblio%'
+                       OR  kohafield like '') );
+    my $bibliolevel_authorised_values = C4::Context->dbh->selectall_hashref( $query, 'authorised_value' );
+    
+    foreach my $tag ( keys( %$tagslib ) ) {
+        foreach my $subfield ( keys( %{$tagslib->{ $tag }} ) ) {
+            # warn "checking $subfield. type is: " . ref $tagslib->{ $tag }{ $subfield };
+            if ( 'HASH' eq ref $tagslib->{ $tag }{ $subfield } ) {
+                if ( exists $tagslib->{ $tag }{ $subfield }{'authorised_value'} && exists $bibliolevel_authorised_values->{ $tagslib->{ $tag }{ $subfield }{'authorised_value'} } ) {
+                    if ( defined $record->field( $tag ) ) {
+                        my $this_subfield_value = $record->field( $tag )->subfield( $subfield );
+                        if ( defined $this_subfield_value ) {
+                            $authorised_values->{ $tagslib->{ $tag }{ $subfield }{'authorised_value'} } = $this_subfield_value;
+                        }
+                    }
+                }
+            }
+        }
+    }
+    # warn ( Data::Dumper->Dump( [ $authorised_values ], [ 'authorised_values' ] ) );
+    return $authorised_values;
+}
+
+
 1;
 
 __END__