Bug 7238 Followup - fix sip_run to require path to SIPconfig.xml
[koha.git] / C4 / VirtualShelves.pm
index 1af3603..57b86da 100644 (file)
@@ -17,32 +17,42 @@ package C4::VirtualShelves;
 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
 #
-# You should have received a copy of the GNU General Public License along with
-# Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
-# Suite 330, Boston, MA  02111-1307 USA
+# You should have received a copy of the GNU General Public License along
+# with Koha; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
 use strict;
+use warnings;
+
 use Carp;
 use C4::Context;
 use C4::Circulation;
-use vars qw($VERSION @ISA @EXPORT);
+use C4::Debug;
+use C4::Members;
+require C4::Auth;
+
+use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
 
 BEGIN {
        # set the version for version checking
-       $VERSION = 3.01;
+       $VERSION = 3.02;
        require Exporter;
        @ISA    = qw(Exporter);
        @EXPORT = qw(
-        &GetShelves &GetShelfContents &GetShelf
-
-        &AddToShelf &AddToShelfFromBiblio &AddShelf
-
-        &ModShelf
-        &ShelfPossibleAction
-        &DelFromShelf &DelShelf
+            &GetShelves &GetShelfContents &GetShelf
+            &AddToShelf &AddShelf
+            &ModShelf
+            &ShelfPossibleAction
+            &DelFromShelf &DelShelf
+            &GetBibliosShelves
        );
+        @EXPORT_OK = qw(
+            &GetShelvesSummary &GetRecentShelves &GetAllShelves
+            &RefreshShelvesSummary &SetShelvesLimit
+        );
 }
 
+
 my $dbh = C4::Context->dbh;
 
 =head1 NAME
@@ -57,26 +67,23 @@ C4::VirtualShelves - Functions for manipulating Koha virtual virtualshelves
 
 This module provides functions for manipulating virtual virtualshelves,
 including creating and deleting virtualshelves, and adding and removing
-items to and from virtualshelves.
+bibs to and from virtualshelves.
 
 =head1 FUNCTIONS
 
-=over 2
+=head2 GetShelves
 
-=item GetShelves
-
-  $shelflist = &GetShelves($owner, $mincategory);
+  ($shelflist, $totshelves) = &GetShelves($mincategory, $row_count, $offset, $owner);
   ($shelfnumber, $shelfhash) = each %{$shelflist};
 
-Looks up the virtual virtualshelves, and returns a summary. C<$shelflist>
-is a reference-to-hash. The keys are the virtualshelves numbers
-(C<$shelfnumber>, above), and the values (C<$shelfhash>, above) are
-themselves references-to-hash, with the following keys:
-
-C<mincategory> : 2 if the list is for "look". 3 if the list is for "Select virtualshelves for adding a virtual".
-virtualshelves of the owner are always selected, whatever the category
+Returns the number of shelves specified by C<$row_count> and C<$offset> as well as the total
+number of shelves that meet the C<$owner> and C<$mincategory> criteria.  C<$mincategory>,
+C<$row_count>, and C<$offset> are required. C<$owner> must be supplied when C<$mincategory> == 1.
+When C<$mincategory> is 2 or 3, supply undef as argument for C<$owner>.
+C<$shelflist>is a reference-to-hash. The keys are the virtualshelves numbers (C<$shelfnumber>, above),
+and the values (C<$shelfhash>, above) are themselves references-to-hash, with the following keys:
 
-=over 4
+=over
 
 =item C<$shelfhash-E<gt>{shelfname}>
 
@@ -90,49 +97,154 @@ The number of virtuals on that virtualshelves.
 
 =cut
 
-#'
-# FIXME - Wouldn't it be more intuitive to return a list, rather than
-# a reference-to-hash? The shelf number can be just another key in the
-# hash.
-
-sub GetShelves {
-    my ( $owner, $mincategory ) = @_;
-
+sub GetShelves ($$$$) {
+    my ($mincategory, $row_count, $offset, $owner) = @_;
+       my @params = ($owner, $mincategory, ($offset ? $offset : 0), $row_count);
+       my @params1 = ($owner, $mincategory);
+       if ($mincategory > 1) {
+               shift @params;
+               shift @params1;
+       }
+       my $total = _shelf_count($owner, $mincategory);
+    # grab only the shelves meeting the row_count/offset spec...
     my $query = qq(
         SELECT virtualshelves.shelfnumber, virtualshelves.shelfname,owner,surname,firstname,virtualshelves.category,virtualshelves.sortfield,
                count(virtualshelfcontents.biblionumber) as count
         FROM   virtualshelves
             LEFT JOIN   virtualshelfcontents ON virtualshelves.shelfnumber = virtualshelfcontents.shelfnumber
-            LEFT JOIN   borrowers ON virtualshelves.owner = borrowers.borrowernumber
-        WHERE  owner=? OR category>=?
+            LEFT JOIN   borrowers ON virtualshelves.owner = borrowers.borrowernumber );
+    $query .= ($mincategory == 1) ? "WHERE  owner=? AND category=?" : "WHERE category>=?";
+       $query .= qq(
         GROUP BY virtualshelves.shelfnumber
-        ORDER BY virtualshelves.category, virtualshelves.shelfname, borrowers.firstname, borrowers.surname
-    );
-    my $sth = $dbh->prepare($query);
-    $sth->execute( $owner, $mincategory );
+        ORDER BY virtualshelves.shelfname
+               LIMIT ?, ?);
+    my $sth2 = $dbh->prepare($query);
+    $sth2->execute(@params);
     my %shelflist;
-    while (
-        my (
-            $shelfnumber, $shelfname, $owner, $surname,
-            $firstname,   $category,  $sortfield, $count
-        )
-        = $sth->fetchrow
-      )
-    {
+    while ( my ( $shelfnumber, $shelfname, $owner, $surname,
+               $firstname,   $category,  $sortfield, $count ) = $sth2->fetchrow ) {
         $shelflist{$shelfnumber}->{'shelfname'} = $shelfname;
         $shelflist{$shelfnumber}->{'count'}     = $count;
-        $shelflist{$shelfnumber}->{'sortfield'}     = $sortfield;
+        if($count eq 1){ $shelflist{$shelfnumber}->{'single'} = 1; }
+        $shelflist{$shelfnumber}->{'sortfield'} = $sortfield;
         $shelflist{$shelfnumber}->{'category'}  = $category;
         $shelflist{$shelfnumber}->{'owner'}     = $owner;
-        $shelflist{$shelfnumber}->{'surname'}     = $surname;
-        $shelflist{$shelfnumber}->{'firstname'}   = $firstname;
+        $shelflist{$shelfnumber}->{'surname'}   = $surname;
+        $shelflist{$shelfnumber}->{'firstname'} = $firstname;
+    }
+    return ( \%shelflist, $total );
+}
+
+=head2 GetShelvesSummary
+
+       ($shelves, $total) = GetShelvesSummary($mincategory, $row_count, $offset, $owner)
+
+Returns the number of shelves specified by C<$row_count> and C<$offset> as well as the total
+number of shelves that meet the C<$owner> and/or C<$mincategory> criteria. C<$mincategory>,
+C<$row_count>, and C<$offset> are required. C<$owner> must be supplied when C<$mincategory> == 1.
+When C<$mincategory> is 2 or 3, supply undef as argument for C<$owner>.
+
+=cut
+
+sub GetShelvesSummary ($$$$) {
+    my ($mincategory, $row_count, $offset, $owner) = @_;
+       my @params = ($owner, $mincategory, ($offset ? $offset : 0), $row_count);
+       my @params1 = ($owner, $mincategory);
+       if ($mincategory > 1) {
+               shift @params;
+               shift @params1;
+       }
+       my $total = _shelf_count($owner, $mincategory);
+    # grab only the shelves meeting the row_count/offset spec...
+       my $query = qq(
+               SELECT
+                       virtualshelves.shelfnumber,
+                       virtualshelves.shelfname,
+                       owner,
+                       CONCAT(firstname, ' ', surname) AS name,
+                       virtualshelves.category,
+                       count(virtualshelfcontents.biblionumber) AS count
+               FROM   virtualshelves
+                       LEFT JOIN  virtualshelfcontents ON virtualshelves.shelfnumber = virtualshelfcontents.shelfnumber
+                       LEFT JOIN             borrowers ON virtualshelves.owner = borrowers.borrowernumber );
+    $query .= ($mincategory == 1) ? "WHERE  owner=? AND category=?" : "WHERE category>=?";
+       $query .= qq(
+               GROUP BY virtualshelves.shelfnumber
+               ORDER BY virtualshelves.category
+               DESC 
+               LIMIT ?, ?);
+       my $sth2 = $dbh->prepare($query);
+       $sth2->execute(@params);
+    my $shelves = $sth2->fetchall_arrayref({});
+    return ($shelves, $total);
+
+       # Probably NOT the final implementation since it is still bulky (repeated hash keys).
+       # might like an array of rows of delimited values:
+       # 1|2||0|blacklist|112
+       # 2|6|Josh Ferraro|51|en_fuego|106
+}
+
+=head2 GetRecentShelves
+
+       ($shelflist, $total) = GetRecentShelves(1, $limit, $owner)
+
+This function returns a reference to an array of hashrefs containing specified shelves sorted
+by the date the shelf was last modified in descending order limited to the number of records
+specified by C<$row_count>. If calling with C<$mincategory> other than 1, use undef as C<$owner>.
+
+This function is intended to return a dataset reflecting the most recently active shelves for
+the submitted parameters.
+
+=cut
+
+sub GetRecentShelves {
+    my ($mincategory, $row_count, $owner) = @_;
+    my $total = _shelf_count($owner, $mincategory);
+    my @params;
+    my $selection;
+    if (defined $owner) {
+        @params = ($owner, $mincategory);
+        $selection = ' WHERE owner = ? AND category = ?';
+    } else {
+        @params = ( $mincategory);
+        $selection = ' WHERE category >= ? ';
+    }
+    my $query = 'SELECT * FROM virtualshelves';
+    $query .= $selection;
+    $query .= ' ORDER BY lastmodified DESC';
+    if ($row_count){
+       $query .= ' LIMIT ?';
+       push @params,$row_count;
     }
-    return ( \%shelflist );
+    my $sth = $dbh->prepare($query);
+    $sth->execute(@params);
+    my $shelflist = $sth->fetchall_arrayref({});
+    return ( $shelflist, $total );
+}
+
+=head2 GetAllShelves
+
+    $shelflist = GetAllShelves($owner)
+
+This function returns a reference to an array of hashrefs containing all shelves sorted
+by the shelf name.
+
+This function is intended to return a dataset reflecting all the shelves for
+the submitted parameters.
+
+=cut
+
+sub GetAllShelves {
+    my ($category,$owner) = @_;
+    my $query = 'SELECT * FROM virtualshelves WHERE category = ? AND owner = ? ORDER BY shelfname ASC';
+    my $sth = $dbh->prepare( $query );
+    $sth->execute( $category, $owner );
+    return $sth->fetchall_arrayref({});
 }
 
-=item GetShelf
+=head2 GetShelf
 
-  (shelfnumber,shelfname,owner,category) = &GetShelf($shelfnumber);
+  (shelfnumber,shelfname,owner,category,sortfield) = &GetShelf($shelfnumber);
 
 Looks up information about the contents of virtual virtualshelves number
 C<$shelfnumber>
@@ -141,10 +253,10 @@ Returns the database's information on 'virtualshelves' table.
 
 =cut
 
-sub GetShelf {
+sub GetShelf ($) {
     my ($shelfnumber) = @_;
     my $query = qq(
-        SELECT shelfnumber,shelfname,owner,category,sortfield
+        SELECT shelfnumber, shelfname, owner, category, sortfield
         FROM   virtualshelves
         WHERE  shelfnumber=?
     );
@@ -153,9 +265,9 @@ sub GetShelf {
     return $sth->fetchrow;
 }
 
-=item GetShelfContents
+=head2 GetShelfContents
 
-  $itemlist = &GetShelfContents($shelfnumber);
+  $biblist = &GetShelfContents($shelfnumber);
 
 Looks up information about the contents of virtual virtualshelves number
 C<$shelfnumber>.  Sorted by a field in the biblio table.  copyrightdate 
@@ -171,29 +283,38 @@ from C4::Circulation.
 
 =cut
 
-sub GetShelfContents {
-    my ( $shelfnumber ,$sortfield) = @_;
+sub GetShelfContents ($;$$$) {
+    my ($shelfnumber, $row_count, $offset, $sortfield) = @_;
     my $dbh=C4::Context->dbh();
+       my $sth1 = $dbh->prepare("SELECT count(*) FROM virtualshelfcontents WHERE shelfnumber = ?");
+       $sth1->execute($shelfnumber);
+       my $total = $sth1->fetchrow;
        if(!$sortfield) {
-               my $sthsort = $dbh->prepare('select sortfield from virtualshelves where shelfnumber=?');
-               $sthsort->execute($shelfnumber);
-               ($sortfield) = $sthsort->fetchrow_array;
+               my $sth2 = $dbh->prepare('SELECT sortfield FROM virtualshelves WHERE shelfnumber=?');
+               $sth2->execute($shelfnumber);
+               ($sortfield) = $sth2->fetchrow_array;
        }
     my $query =
-       " SELECT vc.biblionumber, vc.shelfnumber,
-                               biblio.*, biblioitems.itemtype, itemtypes.*
+       " SELECT vc.biblionumber, vc.shelfnumber, vc.dateadded, itemtypes.*,
+            biblio.*, biblioitems.itemtype, biblioitems.publicationyear as year, biblioitems.publishercode, biblioitems.place, biblioitems.size, biblioitems.pages
          FROM   virtualshelfcontents vc
                 LEFT JOIN biblio      ON      vc.biblionumber =      biblio.biblionumber
                 LEFT JOIN biblioitems ON  biblio.biblionumber = biblioitems.biblionumber
                 LEFT JOIN itemtypes   ON biblioitems.itemtype = itemtypes.itemtype
          WHERE  vc.shelfnumber=? ";
+       my @params = ($shelfnumber);
        if($sortfield) {
-               $query .= " ORDER BY `$sortfield` ";
+               $query .= " ORDER BY " . $sortfield;
                $query .= " DESC " if ($sortfield eq 'copyrightdate');
        }
-    my $sth = $dbh->prepare($query);
-       $sth->execute($shelfnumber);
-       return $sth->fetchall_arrayref({});     
+    if($row_count){
+          $query .= " LIMIT ?, ? ";
+          push (@params, ($offset ? $offset : 0));
+          push (@params, $row_count);
+    }
+    my $sth3 = $dbh->prepare($query);
+       $sth3->execute(@params);
+       return ($sth3->fetchall_arrayref({}), $total);
        # Like the perldoc says,
        # returns reference-to-array, where each element is reference-to-hash of the row:
        #   like [ $sth->fetchrow_hashref(), $sth->fetchrow_hashref() ... ] 
@@ -202,7 +323,7 @@ sub GetShelfContents {
        # or newer, for your version of DBI.
 }
 
-=item AddShelf
+=head2 AddShelf
 
   $shelfnumber = &AddShelf( $shelfname, $owner, $category);
 
@@ -216,7 +337,7 @@ Returns a code to know what's happen.
 =cut
 
 sub AddShelf {
-    my ( $shelfname, $owner, $category ) = @_;
+    my ( $shelfname, $owner, $category, $sortfield ) = @_;
     my $query = qq(
         SELECT *
         FROM   virtualshelves
@@ -227,21 +348,21 @@ sub AddShelf {
     ( $sth->rows ) and return (-1);
     $query = qq(
         INSERT INTO virtualshelves
-            (shelfname,owner,category)
-        VALUES (?,?,?)
+            (shelfname,owner,category,sortfield)
+        VALUES (?,?,?,?)
     );
     $sth = $dbh->prepare($query);
-    $sth->execute( $shelfname, $owner, $category );
+    $sth->execute( $shelfname, $owner, $category, $sortfield );
     my $shelfnumber = $dbh->{'mysql_insertid'};
     return ($shelfnumber);
 }
 
-=item AddToShelf
+=head2 AddToShelf
 
   &AddToShelf($biblionumber, $shelfnumber);
 
-Adds item number C<$biblionumber> to virtual virtualshelves number
-C<$shelfnumber>, unless that item is already on that shelf.
+Adds bib number C<$biblionumber> to virtual virtualshelves number
+C<$shelfnumber>, unless that bib is already on that shelf.
 
 =cut
 
@@ -266,59 +387,63 @@ sub AddToShelf {
        );
        $sth = $dbh->prepare($query);
        $sth->execute( $shelfnumber, $biblionumber );
+       $query = qq(UPDATE virtualshelves
+                               SET lastmodified = CURRENT_TIMESTAMP
+                               WHERE shelfnumber = ?);
+       $sth = $dbh->prepare($query);
+       $sth->execute( $shelfnumber );
 }
 
-=item AddToShelfFromBiblio
-    &AddToShelfFromBiblio($biblionumber, $shelfnumber)
+=head2 ModShelf
+
+ModShelf($shelfnumber, $hashref)
 
-    this function allow to add a virtual into the shelf number $shelfnumber
-    from biblionumber.
+Where $hashref->{column} = param
+
+Modify the value into virtualshelves table with values given 
+from hashref, which each key of the hashref should be
+the name of a column of virtualshelves.
 
 =cut
 
-sub AddToShelfFromBiblio {
-    my ( $biblionumber, $shelfnumber ) = @_;
-    return unless $biblionumber;
-    my $query = qq(
-        SELECT *
-        FROM   virtualshelfcontents
-        WHERE  shelfnumber=? AND biblionumber=?
-    );
-    my $sth = $dbh->prepare($query);
-    $sth->execute( $shelfnumber, $biblionumber );
-    unless ( $sth->rows ) {
-        my $query =qq(
-            INSERT INTO virtualshelfcontents
-                (shelfnumber, biblionumber, flags)
-            VALUES
-                (?, ?, 0)
-        );
-        $sth = $dbh->prepare($query);
-        $sth->execute( $shelfnumber, $biblionumber );
+sub ModShelf {
+    my $shelfnumber = shift;
+    my $shelf = shift;
+
+    if (exists $shelf->{shelfnumber}) {
+        carp "Should not use ModShelf to change shelfnumber";
+        return;
+    }
+    unless (defined $shelfnumber and $shelfnumber =~ /^\d+$/) {
+        carp "Invalid shelfnumber passed to ModShelf: $shelfnumber";
+        return;
     }
-}
 
-=item ModShelf
+       my $query = "UPDATE virtualshelves SET ";
+    my @bind_params = ();
+    my @set_clauses = ();
 
-ModShelf($shelfnumber, $shelfname, $owner, $category )
+       foreach my $column (keys %$shelf) {
+        push @set_clauses, "$column = ?";
+        push @bind_params, $shelf->{$column};
+    }
 
-Modify the value into virtualshelves table with values given on input arg.
+    if ($#set_clauses == -1) {
+        carp "No columns to update passed to ModShelf";
+        return;
+    }
+    $query .= join(", ", @set_clauses);
 
-=cut
+    $query .= " WHERE shelfnumber = ? ";
+    push @bind_params, $shelfnumber;
 
-sub ModShelf {
-    my ( $shelfnumber, $shelfname, $owner, $category, $sortfield ) = @_;
-    my $query = qq(
-        UPDATE virtualshelves
-        SET    shelfname=?,owner=?,category=?,sortfield=?
-        WHERE  shelfnumber=?
-    );
+    $debug and warn "ModShelf query:\n $query\n",
+                       "ModShelf query args: ", join(',', @bind_params), "\n";
        my $sth = $dbh->prepare($query);
-    $sth->execute( $shelfname, $owner, $category, $sortfield, $shelfnumber );
+       $sth->execute( @bind_params );
 }
 
-=item ShelfPossibleAction
+=head2 ShelfPossibleAction
 
 ShelfPossibleAction($loggedinuser, $shelfnumber, $action);
 
@@ -341,18 +466,22 @@ sub ShelfPossibleAction {
     my $sth = $dbh->prepare($query);
     $sth->execute($shelfnumber);
     my ( $owner, $category ) = $sth->fetchrow;
-    return 1 if ($owner eq $user);
-    return 1 if ( $category >= 3);
-    return 1 if (($category >= 2) && $action eq 'view' );
+       my $borrower = GetMemberDetails($user);
+       return 0 if not defined($user);
+       return 1 if ( $category >= 3);                                                  # open list
+    return 1 if (($category >= 2) and
+                               defined($action) and $action eq 'view');        # public list, anybody can view
+    return 1 if (($category >= 2) and defined($user) and ($borrower->{authflags}->{superlibrarian} || $user == 0));    # public list, superlibrarian can edit/delete
+    return 1 if (defined($user)  and $owner  eq $user );       # user owns this list.  Check last.
     return 0;
 }
 
-=item DelFromShelf
+=head2 DelFromShelf
 
   &DelFromShelf( $biblionumber, $shelfnumber);
 
-Removes item number C<$biblionumber> from virtual virtualshelves number
-C<$shelfnumber>. If the item wasn't on that virtualshelves to begin with,
+Removes bib number C<$biblionumber> from virtual virtualshelves number
+C<$shelfnumber>. If the bib wasn't on that virtualshelves to begin with,
 nothing happens.
 
 =cut
@@ -368,7 +497,7 @@ sub DelFromShelf {
     $sth->execute( $shelfnumber, $biblionumber );
 }
 
-=item DelShelf (old version)
+=head2 DelShelf (old version)
 
   ($status, $msg) = &DelShelf($shelfnumber);
 
@@ -379,7 +508,7 @@ Returns a two-element array, where C<$status> is 0 if the operation
 was successful, or non-zero otherwise. C<$msg> is "Done" in case of
 success, or an error message giving the reason for failure.
 
-=item DelShelf (current version)
+=head2 DelShelf (current version)
 
   $Number = DelShelf($shelfnumber);
 
@@ -396,15 +525,104 @@ sub DelShelf {
        return $sth->execute(shift);
 }
 
+=head2 GetBibShelves
+
+This finds all the public lists that this bib record is in.
+
+=cut
+
+sub GetBibliosShelves {
+    my ( $biblionumber )  = @_;
+    my $dbh = C4::Context->dbh;
+    my $sth = $dbh->prepare('
+        SELECT vs.shelfname, vs.shelfnumber 
+        FROM virtualshelves vs 
+        JOIN virtualshelfcontents vc ON (vs.shelfnumber= vc.shelfnumber) 
+        WHERE vs.category != 1 
+        AND vc.biblionumber= ?
+    ');
+    $sth->execute( $biblionumber );
+    return $sth->fetchall_arrayref({});
+}
+
+=head2 RefreshShelvesSummary
+
+       ($total, $pubshelves, $barshelves) = RefreshShelvesSummary($sessionID, $loggedinuser, $row_count);
+
+Updates the current session and userenv with the most recent shelves
+
+Returns the total number of shelves stored in the session/userenv along with two references each to an
+array of hashes, one containing the C<$loggedinuser>'s private shelves and one containing all public/open shelves.
+
+This function is used in conjunction with the 'Lists' button in masthead.inc.
+
+=cut
+
+sub RefreshShelvesSummary ($$$) {
+       
+       my ($sessionID, $loggedinuser, $row_count) = @_;
+       my $session = C4::Auth::get_session($sessionID);
+       my ($total, $totshelves, $barshelves, $pubshelves);
+
+       ($barshelves, $totshelves) = GetRecentShelves(1, $row_count, $loggedinuser);
+       $total->{'bartotal'} = $totshelves;
+       ($pubshelves, $totshelves) = GetRecentShelves(2, $row_count, undef);
+       $total->{'pubtotal'} = $totshelves;
+
+       # Update the current session with the latest shelves...
+       $session->param('barshelves', $barshelves);
+       $session->param('pubshelves', $pubshelves);
+       $session->param('totshelves', $total);
+
+       # likewise the userenv...
+       C4::Context->set_shelves_userenv('bar',$barshelves);
+       C4::Context->set_shelves_userenv('pub',$pubshelves);
+       C4::Context::set_shelves_userenv('tot',$total);
+
+       return ($total, $pubshelves, $barshelves);
+}
+
+# internal subs
+
+sub _shelf_count ($$) {
+       my (@params) = @_;
+       # Find out how many shelves total meet the submitted criteria...
+       my $query = "SELECT count(*) FROM virtualshelves";
+       $query .= ($params[1] > 1) ? " WHERE category >= ?" : " WHERE  owner=? AND category=?";
+       shift @params if $params[1] > 1;
+       my $sth = $dbh->prepare($query);
+       $sth->execute(@params);
+       my $total = $sth->fetchrow;
+       return $total;
+}
+
+sub _biblionumber_sth {
+    my ($shelf) = @_;
+    my $query = 'select biblionumber from virtualshelfcontents where shelfnumber = ?';
+    my $dbh = C4::Context->dbh;
+    my $sth = $dbh->prepare($query)
+       or die $dbh->errstr;
+    $sth->execute( $shelf )
+       or die $sth->errstr;
+    $sth;
+}
+
+sub each_biblionumbers (&$) {
+    my ($code,$shelf) = @_;
+    my $ref =  _biblionumber_sth($shelf)->fetchall_arrayref;
+    map {
+       $_=$$_[0];
+       $code->();
+    } @$ref;
+}
+
 1;
 
 __END__
 
-=back
-
 =head1 AUTHOR
 
-Koha Developement team <info@koha.org>
+Koha Development Team <http://koha-community.org/>
 
 =head1 SEE ALSO