changing haspermission() to require that user has ALL requiredflags, not ANYOF requir...
[koha_fer] / C4 / Biblio.pm
old mode 100644 (file)
new mode 100755 (executable)
index 558b228..9169fd7
@@ -257,8 +257,8 @@ sub AddItem {
     ( $item->{'biblioitemnumber'}, $itemtype ) = $sth->fetchrow;
     $sth =
       $dbh->prepare(
-        "SELECT notforloan FROM itemtypes WHERE itemtype='$itemtype'");
-    $sth->execute();
+        "SELECT notforloan FROM itemtypes WHERE itemtype=?");
+    $sth->execute( C4::Context->preference('item-level_itypes') ? $item->{'itype'} : $itemtype );
     my $notforloan = $sth->fetchrow;
     ##Change the notforloan field if $notforloan found
     if ( $notforloan > 0 ) {
@@ -465,14 +465,22 @@ sub ModItemInMarconefield {
 
     my $record = GetMarcItem( $biblionumber, $itemnumber );
     my ($tagfield, $tagsubfield) = GetMarcFromKohaField( $itemfield,'');
-    if ($tagfield && $tagsubfield) {
+    # FIXME - the condition is done this way because GetMarcFromKohaField
+    # returns (0, 0) if it can't field a MARC tag for the kohafield.  However,
+    # some fields like items.wthdrawn are mapped to subfield $0, making the
+    # customary test of "if ($tagfield && $tagsubfield)" incorrect.
+    # GetMarcFromKohaField should probably be returning (undef, undef), making
+    # the correct test "if (defined $tagfield && defined $tagsubfield)", but
+    # this would be a large change and consequently deferred for after 3.0.
+    if (not(int($tagfield) == 0 && int($tagsubfield) == 0)) { 
         my $tag = $record->field($tagfield);
         if ($tag) {
 #             my $tagsubs = $record->field($tagfield)->subfield($tagsubfield);
             $tag->update( $tagsubfield => $newvalue );
             $record->delete_field($tag);
             $record->insert_fields_ordered($tag);
-            &ModItemInMarc( $record, $biblionumber, $itemnumber, 0 );
+            my $frameworkcode = GetFrameworkCode( $biblionumber );
+            &ModItemInMarc( $record, $biblionumber, $itemnumber, $frameworkcode );
         }
     }
 }
@@ -481,7 +489,7 @@ sub ModItemInMarconefield {
 
 =over
 
-&ModItemInMarc( $record, $biblionumber, $itemnumber )
+&ModItemInMarc( $record, $biblionumber, $itemnumber, $frameworkcode )
 
 =back
 
@@ -563,10 +571,7 @@ 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, "delete_record", "biblioserver", undef);
-
-    # delete biblio from Koha tables and save in deletedbiblio
-    $error = &_koha_delete_biblio( $dbh, $biblionumber );
+    ModZebra($biblionumber, "recordDelete", "biblioserver", undef);
 
     # delete biblioitems and items from Koha tables and save in deletedbiblioitems,deleteditems
     $sth =
@@ -576,9 +581,16 @@ sub DelBiblio {
     while ( my $biblioitemnumber = $sth->fetchrow ) {
 
         # delete this biblioitem
-        $error = &_koha_delete_biblioitems( $dbh, $biblioitemnumber );
+        $error = _koha_delete_biblioitems( $dbh, $biblioitemnumber );
         return $error if $error;
     }
+
+    # delete biblio from Koha tables and save in deletedbiblio
+    # must do this *after* _koha_delete_biblioitems, otherwise
+    # delete cascade will prevent deletedbiblioitems rows
+    # 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");
     return;
@@ -597,7 +609,6 @@ Exported function (core API) for deleting an item record in Koha.
 
 sub DelItem {
     my ( $dbh, $biblionumber, $itemnumber ) = @_;
-    my $dbh = C4::Context->dbh;
        
        # check the item has no current issues
        
@@ -649,14 +660,21 @@ sub GetBiblioData {
     my ( $bibnum ) = @_;
     my $dbh = C4::Context->dbh;
 
-    my $query = "
-        SELECT * , biblioitems.notes AS bnotes, itemtypes.notforloan as bi_notforloan, biblio.notes
-        FROM biblio
+  #  my $query =  C4::Context->preference('item-level_itypes') ? 
+       #       " SELECT * , biblioitems.notes AS bnotes, biblio.notes
+    #          FROM biblio
+    #        LEFT JOIN biblioitems ON biblio.biblionumber = biblioitems.biblionumber
+    #          WHERE biblio.biblionumber = ?
+    #        AND biblioitems.biblionumber = biblio.biblionumber
+    #";
+       
+       my $query = " SELECT * , biblioitems.notes AS bnotes, itemtypes.notforloan as bi_notforloan, biblio.notes
+               FROM biblio
             LEFT JOIN biblioitems ON biblio.biblionumber = biblioitems.biblionumber
             LEFT JOIN itemtypes ON biblioitems.itemtype = itemtypes.itemtype
-        WHERE biblio.biblionumber = ?
-            AND biblioitems.biblionumber = biblio.biblionumber
-    ";
+               WHERE biblio.biblionumber = ?
+            AND biblioitems.biblionumber = biblio.biblionumber ";
+                
     my $sth = $dbh->prepare($query);
     $sth->execute($bibnum);
     my $data;
@@ -723,25 +741,25 @@ sub GetItemsInfo {
     my $query = "SELECT *,items.notforloan as itemnotforloan
                  FROM items 
                  LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber
-                 LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber
-                 LEFT JOIN itemtypes on biblioitems.itemtype = itemtypes.itemtype
-                WHERE items.biblionumber = ?
-                ORDER BY items.dateaccessioned desc
-                 ";
+                 LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber";
+       $query .=  (C4::Context->preference('item-level_itypes')) ?
+                                        " LEFT JOIN itemtypes on items.itype = itemtypes.itemtype "
+                                       : " LEFT JOIN itemtypes on biblioitems.itemtype = itemtypes.itemtype ";
+       $query .= "WHERE items.biblionumber = ? ORDER BY items.dateaccessioned desc" ;
     my $sth = $dbh->prepare($query);
     $sth->execute($biblionumber);
     my $i = 0;
     my @results;
     my ( $date_due, $count_reserves );
 
+    my $isth    = $dbh->prepare(
+        "SELECT issues.*,borrowers.cardnumber,borrowers.surname,borrowers.firstname,borrowers.branchcode as bcode
+        FROM   issues LEFT JOIN borrowers ON issues.borrowernumber=borrowers.borrowernumber
+        WHERE  itemnumber = ?
+            AND returndate IS NULL"
+       );
     while ( my $data = $sth->fetchrow_hashref ) {
         my $datedue = '';
-        my $isth    = $dbh->prepare(
-            "SELECT issues.*,borrowers.cardnumber,borrowers.surname,borrowers.firstname
-            FROM   issues LEFT JOIN borrowers ON issues.borrowernumber=borrowers.borrowernumber
-            WHERE  itemnumber = ?
-                AND returndate IS NULL"
-        );
         $isth->execute( $data->{'itemnumber'} );
         if ( my $idata = $isth->fetchrow_hashref ) {
             $data->{borrowernumber} = $idata->{borrowernumber};
@@ -749,13 +767,18 @@ sub GetItemsInfo {
             $data->{surname}     = $idata->{surname};
             $data->{firstname}     = $idata->{firstname};
             $datedue                = format_date( $idata->{'date_due'} );
+           if (C4::Context->preference("IndependantBranches")){
+               my $userenv = C4::Context->userenv;
+               if ( ($userenv) && ( $userenv->{flags} != 1 ) ) { 
+                   $data->{'NOTSAMEBRANCH'} = 1 if ($idata->{'bcode'} ne $userenv->{branch});
+               }
+           }
         }
         if ( $datedue eq '' ) {
             #$datedue="Available";
             my ( $restype, $reserves ) =
               C4::Reserves::CheckReserves( $data->{'itemnumber'} );
             if ($restype) {
-
                 #$datedue=$restype;
                 $count_reserves = $restype;
             }
@@ -1132,13 +1155,14 @@ that C<biblioitems.notes> is given as C<$itemdata-E<gt>{bnotes}>.
 sub GetBiblioItemData {
     my ($biblioitemnumber) = @_;
     my $dbh       = C4::Context->dbh;
-    my $sth       =
-      $dbh->prepare(
-       "SELECT *,biblioitems.notes AS bnotes
-               FROM biblioitems,biblio,itemtypes 
-       WHERE biblio.biblionumber = biblioitems.biblionumber 
-               AND biblioitemnumber = ? "
-      );
+       my $query = "SELECT *,biblioitems.notes AS bnotes
+               FROM biblio, biblioitems ";
+       unless(C4::Context->preference('item-level_itypes')) { 
+               $query .= "LEFT JOIN itemtypes on biblioitems.itemtype=itemtypes.itemtype ";
+       }        
+       $query .= " WHERE biblio.biblionumber = biblioitems.biblionumber 
+               AND biblioitemnumber = ? ";
+    my $sth       =  $dbh->prepare($query);
     my $data;
     $sth->execute($biblioitemnumber);
     $data = $sth->fetchrow_hashref;
@@ -1198,9 +1222,9 @@ sub GetBiblioItemByBiblioNumber {
 
 =over 4
 
-$item = &GetBiblioFromItemNumber($itemnumber);
+$item = &GetBiblioFromItemNumber($itemnumber,$barcode);
 
-Looks up the item with the given itemnumber.
+Looks up the item with the given itemnumber. if undef, try the barcode.
 
 C<&itemnodata> returns a reference-to-hash whose keys are the fields
 from the C<biblio>, C<biblioitems>, and C<items> tables in the Koha
@@ -1212,16 +1236,22 @@ database.
 
 #'
 sub GetBiblioFromItemNumber {
-    my ( $itemnumber ) = @_;
+    my ( $itemnumber, $barcode ) = @_;
     my $dbh = C4::Context->dbh;
-    my $sth = $dbh->prepare(
-        "SELECT * FROM items 
-        LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber
-        LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber
-         WHERE items.itemnumber = ?"
-    );
-
-    $sth->execute($itemnumber);
+    my $sth;
+    if($itemnumber) {
+               $sth=$dbh->prepare(  "SELECT * FROM items 
+           LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber
+           LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber
+                WHERE items.itemnumber = ?") ; 
+       $sth->execute($itemnumber);
+       } else {
+               $sth=$dbh->prepare(  "SELECT * FROM items 
+           LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber
+           LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber
+                WHERE items.barcode = ?") ; 
+       $sth->execute($barcode);
+       }
     my $data = $sth->fetchrow_hashref;
     $sth->finish;
     return ($data);
@@ -1612,11 +1642,14 @@ sub GetMarcBiblio {
      $marcxml =~ s/\x0c//g;  
 #   warn $marcxml;
     my $record = MARC::Record->new();
-     
-      $record = eval {MARC::Record::new_from_xml( $marcxml, "utf8",C4::Context->preference('marcflavour'))} if ($marcxml);
-     if ($@) {warn $@;}
+    if ($marcxml) {
+        $record = eval {MARC::Record::new_from_xml( $marcxml, "utf8", C4::Context->preference('marcflavour'))};
+        if ($@) {warn $@;}
 #      $record = MARC::Record::new_from_usmarc( $marc) if $marc;
-    return $record;
+        return $record;
+    } else {
+        return undef;
+    }
 }
 
 =head2 GetXmlBiblio
@@ -1696,23 +1729,49 @@ Returns MARC::Record of the item passed in parameter.
 
 sub GetMarcItem {
     my ( $biblionumber, $itemnumber ) = @_;
-    my $dbh = C4::Context->dbh;
-    my $newrecord = MARC::Record->new();
-    my $marcflavour = C4::Context->preference('marcflavour');
-    
-    my $marcxml = GetXmlBiblio($biblionumber);
-    my $record = MARC::Record->new();
-    $record = MARC::Record::new_from_xml( $marcxml, "utf8", $marcflavour );
-    # now, find where the itemnumber is stored & extract only the item
-    my ( $itemnumberfield, $itemnumbersubfield ) =
-      GetMarcFromKohaField( 'items.itemnumber', '' );
-    my @fields = $record->field($itemnumberfield);
-    foreach my $field (@fields) {
-        if ( $field->subfield($itemnumbersubfield) eq $itemnumber ) {
-            $newrecord->insert_fields_ordered($field);
-        }
-    }
-    return $newrecord;
+
+    # GetMarcItem has been revised so that it does the following:
+    #  1. Gets the item information from the items table.
+    #  2. Converts it to a MARC field for storage in the bib record.
+    #
+    # The previous behavior was:
+    #  1. Get the bib record.
+    #  2. Return the MARC tag corresponding to the item record.
+    #
+    # The difference is that one treats the items row as authoritative,
+    # while the other treats the MARC representation as authoritative
+    # under certain circumstances.
+    #
+    # FIXME - a big one
+    #
+    # As of 2007-11-27, this change hopefully does not introduce
+    # any bugs.  However, it does mean that for code that uses
+    # ModItemInMarconefield to update one subfield (corresponding to
+    # an items column) is now less efficient.
+    #
+    # The API needs to be shifted to the following:
+    #  1. User updates items record.
+    #  2. Linked bib is sent for indexing.
+    # 
+    # The missing step 1.5 is updating the item tag in the bib MARC record
+    # so that the indexes are updated.  Depending on performance considerations,
+    # this may ultimately mean of of the following:
+    #  a. MARC field for item is updated right away.
+    #  b. MARC field for item is updated only as part of indexing.
+    #  c. MARC field for item is never actually stored in bib record; instead
+    #     it is generated only when needed for indexing, item export, and
+    #     (maybe) OPAC display.
+    #
+
+    my $itemrecord = GetItem($itemnumber);
+
+    # Tack on 'items.' prefix to column names so that TransformKohaToMarc will work.
+    # Also, don't emit a subfield if the underlying field is blank.
+    my $mungeditem = { map {  $itemrecord->{$_} ne '' ? ("items.$_" => $itemrecord->{$_}) : ()  } keys %{ $itemrecord } };
+
+    my $itemmarc = TransformKohaToMarc($mungeditem);
+    return $itemmarc;
+
 }
 
 
@@ -1784,48 +1843,49 @@ sub GetMarcSubjects {
         $mintag = "600";
         $maxtag = "611";
     }
+       
+    my @marcsubjects;
+       my $subject = "";
+       my $subfield = "";
+       my $marcsubject;
 
-    my @marcsubjcts;
-
-    foreach my $field ( $record->fields ) {
+    foreach my $field ( $record->field('6..' )) {
         next unless $field->tag() >= $mintag && $field->tag() <= $maxtag;
+               my @subfields_loop;
         my @subfields = $field->subfields();
-        my $link = "su:";
-        my $label;
-        my $flag = 0;
-        my $authoritysep=C4::Context->preference("authoritysep");
-        for my $subject_subfield ( @subfields ) {
-            if (
-                $marcflavour ne 'MARC21'
-                and (
-                    ($subject_subfield->[0] eq '3') or
-                    ($subject_subfield->[0] eq '4') or
-                    ($subject_subfield->[0] eq '5')
-                )
-            )
-            {
-                next;
-            }
-            my $code = $subject_subfield->[0];
-            $label .= $subject_subfield->[1].$authoritysep unless ( $code == 9 );
-            $link  .= " and su-to:".$subject_subfield->[1]  unless ( $code == 9 );
-            if ( $code == 9 ) {
-                $link = "an:".$subject_subfield->[1];
-                $flag = 1;
-            }
-            elsif ( ! $flag ) {
-                $link =~ s/ and\ssu-to:$//;
+               my $counter = 0;
+               my @link_loop;
+               # if there is an authority link, build the link with an= subfield9
+               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) ) );
+                       my $code = $subject_subfield->[0];
+                       my $value = $subject_subfield->[1];
+                       my $linkvalue = $value;
+                       $linkvalue =~ s/(\(|\))//g;
+                       my $operator = " and " unless $counter==0;
+                       if ($subfield9) {
+                @link_loop = ({'limit' => 'an' ,link => "$subfield9" });
+            } else {
+                push @link_loop, {'limit' => 'su', link => $linkvalue, operator => $operator };
             }
-        }
-         $label =~ s/$authoritysep$//;
-        push @marcsubjcts,
-          {
-            label => $label,
-            link  => $link
-          }
-    }
-    return \@marcsubjcts;
-}    #end GetMarcSubjects
+                       my $separator = C4::Context->preference("authoritysep") unless $counter==0;
+                       # ignore $9
+                       push @subfields_loop, {code => $code, value => $value, link_loop => \@link_loop, separator => $separator} unless ($subject_subfield->[0] == 9 );
+                       # this needs to be added back in in a way that the template can expose it properly
+                       #if ( $code == 9 ) {
+            #    $link = "an:".$subject_subfield->[1];
+            #    $flag = 1;
+            #}
+                       $counter++;
+               }
+                
+               push @marcsubjects, { MARCSUBJECT_SUBFIELDS_LOOP => \@subfields_loop };
+        
+       }
+        return \@marcsubjects;
+}  #end getMARCsubjects
 
 =head2 GetMarcAuthors
 
@@ -1849,7 +1909,7 @@ sub GetMarcAuthors {
         $maxtag = "720"; 
     }
     elsif ( $marcflavour eq "UNIMARC" ) {    # assume unimarc if not marc21
-        $mintag = "701";
+        $mintag = "700";
         $maxtag = "712";
     }
        else {
@@ -3065,10 +3125,13 @@ sub GetNoZebraIndexes {
     my %indexes;
     foreach my $line (split /('|"),/,$index) {
         $line =~ /(.*)=>(.*)/;
+warn $line;
         my $index = substr($1,1); # get the index, don't forget to remove initial ' or "
         my $fields = $2;
-        $index =~ s/'|"| //g;
-        $fields =~ s/'|"| //g;
+        $index =~ s/'|"|\s//g;
+
+
+        $fields =~ s/'|"|\s//g;
         $indexes{$index}=$fields;
     }
     return %indexes;
@@ -3454,20 +3517,13 @@ sub _koha_add_biblio {
 
        my $error;
 
-       # get the next biblionumber
-    my $sth = $dbh->prepare("SELECT MAX(biblionumber) FROM biblio");
-    $sth->execute();
-    my $data = $sth->fetchrow_arrayref();
-    my $biblionumber = $$data[0] + 1;
        # set the series flag
     my $serial = 0;
     if ( $biblio->{'seriestitle'} ) { $serial = 1 };
 
-    $sth->finish();
        my $query = 
         "INSERT INTO biblio
-               SET biblionumber  = ?, 
-                       frameworkcode = ?,
+               SET frameworkcode = ?,
                        author = ?,
                        title = ?,
                        unititle =?,
@@ -3478,9 +3534,8 @@ sub _koha_add_biblio {
                        datecreated=NOW(),
                        abstract = ?
                ";
-    $sth = $dbh->prepare($query);
+    my $sth = $dbh->prepare($query);
     $sth->execute(
-        $biblionumber,
                $frameworkcode,
         $biblio->{'author'},
         $biblio->{'title'},
@@ -3492,6 +3547,7 @@ sub _koha_add_biblio {
         $biblio->{'abstract'}
     );
 
+    my $biblionumber = $dbh->{'mysql_insertid'};
        if ( $dbh->errstr ) {
                $error.="ERROR in _koha_add_biblio $query".$dbh->errstr;
         warn $error;
@@ -3660,16 +3716,10 @@ Internal function to add a biblioitem
 sub _koha_add_biblioitem {
     my ( $dbh, $biblioitem ) = @_;
        my $error;
-    my $sth = $dbh->prepare("SELECT MAX(biblioitemnumber) FROM biblioitems");
-    $sth->execute();
-    my $data       = $sth->fetchrow_arrayref;
-    my $bibitemnum = $$data[0] + 1;
-    $sth->finish();
 
        my ($cn_sort) = GetClassSort($biblioitem->{'biblioitems.cn_source'}, $biblioitem->{'cn_class'}, $biblioitem->{'cn_item'} );
     my $query =
     "INSERT INTO biblioitems SET
-               biblioitemnumber = ?,
         biblionumber    = ?,
         volume          = ?,
         number          = ?,
@@ -3702,7 +3752,6 @@ sub _koha_add_biblioitem {
         ";
        my $sth = $dbh->prepare($query);
     $sth->execute(
-               $bibitemnum,
         $biblioitem->{'biblionumber'},
         $biblioitem->{'volume'},
         $biblioitem->{'number'},
@@ -3733,6 +3782,7 @@ sub _koha_add_biblioitem {
         $cn_sort,
         $biblioitem->{'totalissues'}
     );
+    my $bibitemnum = $dbh->{'mysql_insertid'};
     if ( $dbh->errstr ) {
                $error.="ERROR in _koha_add_biblioitem $query".$dbh->errstr;
                warn $error;
@@ -3755,12 +3805,6 @@ sub _koha_new_items {
     my ( $dbh, $item, $barcode ) = @_;
        my $error;
 
-    my $sth = $dbh->prepare("SELECT MAX(itemnumber) FROM items");
-    $sth->execute();
-    my $data       = $sth->fetchrow_hashref;
-    my $itemnumber = $data->{'MAX(itemnumber)'} + 1;
-    $sth->finish;
-
     my ($items_cn_sort) = GetClassSort($item->{'items.cn_source'}, $item->{'itemcallnumber'}, "");
 
     # if dateaccessioned is provided, use it. Otherwise, set to NOW()
@@ -3770,7 +3814,6 @@ sub _koha_new_items {
        }
        my $query = 
            "INSERT INTO items SET
-            itemnumber                 = ?,
                        biblionumber            = ?,
             biblioitemnumber    = ?,
                        barcode                 = ?,
@@ -3797,12 +3840,12 @@ sub _koha_new_items {
                        cn_source                       = ?,
                        cn_sort                         = ?,
                        ccode                           = ?,
+                       itype                           = ?,
                        materials                       = ?,
                        uri                             = ?
           ";
     my $sth = $dbh->prepare($query);
        $sth->execute(
-            $itemnumber,
                        $item->{'biblionumber'},
                        $item->{'biblioitemnumber'},
             $barcode,
@@ -3827,9 +3870,11 @@ sub _koha_new_items {
                        $item->{'items.cn_source'},
                        $items_cn_sort,
                        $item->{'ccode'},
+                       $item->{'itype'},
                        $item->{'materials'},
                        $item->{'uri'},
     );
+    my $itemnumber = $dbh->{'mysql_insertid'};
     if ( defined $sth->errstr ) {
         $error.="ERROR in _koha_new_items $query".$sth->errstr;
     }