@@ -, +, @@ --- C4/Auth.pm | 61 +-- C4/VirtualShelves.pm | 577 +++++++++++--------- C4/VirtualShelves/Page.pm | 143 +++-- catalogue/search.pl | 26 +- .../en/modules/virtualshelves/addbybiblionumber.tt | 36 +- .../prog/en/modules/virtualshelves/shelves.tt | 28 +- koha-tmpl/opac-tmpl/prog/en/includes/masthead.inc | 28 +- .../prog/en/modules/opac-addbybiblionumber.tt | 11 +- .../opac-tmpl/prog/en/modules/opac-shelves.tt | 26 +- opac/opac-addbybiblionumber.pl | 186 ++++--- opac/opac-search.pl | 27 +- opac/opac-shelves.pl | 8 +- virtualshelves/addbybiblionumber.pl | 243 ++++----- 13 files changed, 728 insertions(+), 672 deletions(-) --- a/C4/Auth.pm +++ a/C4/Auth.pm @@ -162,21 +162,15 @@ sub get_template_and_user { $template->param( loggedinusername => $user ); $template->param( sessionID => $sessionID ); - my ($total, $pubshelves, $barshelves) = C4::Context->get_shelves_userenv(); - if (defined($pubshelves)) { - $template->param( pubshelves => scalar @{$pubshelves}, - pubshelvesloop => $pubshelves, - ); - $template->param( pubtotal => $total->{'pubtotal'}, ) if ($total->{'pubtotal'} > scalar @{$pubshelves}); - } - if (defined($barshelves)) { - $template->param( barshelves => scalar @{$barshelves}, - barshelvesloop => $barshelves, - ); - $template->param( bartotal => $total->{'bartotal'}, ) if ($total->{'bartotal'} > scalar @{$barshelves}); - } - $borrowernumber = getborrowernumber($user) if defined($user); + my ($total, $pubshelves, $barshelves) = C4::VirtualShelves::GetSomeShelfNames($borrowernumber, 'MASTHEAD'); + $template->param( + pubshelves => $total->{pubtotal}, + pubshelvesloop => $pubshelves, + barshelves => $total->{bartotal}, + barshelvesloop => $barshelves, + ); + require C4::Members; my ( $borr ) = C4::Members::GetMemberDetails( $borrowernumber ); my @bordat; @@ -294,14 +288,11 @@ sub get_template_and_user { $template->param( sessionID => $sessionID ); - my ($total, $pubshelves) = C4::Context->get_shelves_userenv(); # an anonymous user has no 'barshelves'... - if (defined $pubshelves) { - $template->param( pubshelves => scalar @{$pubshelves}, - pubshelvesloop => $pubshelves, - ); - $template->param( pubtotal => $total->{'pubtotal'}, ) if ($total->{'pubtotal'} > scalar @{$pubshelves}); - } - + my ($total, $pubshelves) = C4::VirtualShelves::GetSomeShelfNames(undef, 'MASTHEAD'); + $template->param( + pubshelves => $total->{pubtotal}, + pubshelvesloop => $pubshelves, + ); } # Anonymous opac search history # If opac search history is enabled and at least one search has already been performed @@ -870,22 +861,6 @@ sub checkauth { $session->param('emailaddress'), $session->param('branchprinter') ); - # Grab borrower's shelves and public shelves and add them to the session - # $row_count determines how many records are returned from the db query - # and the number of lists to be displayed of each type in the 'Lists' button drop down - my $row_count = 10; # FIXME:This probably should be a syspref - my ($total, $totshelves, $barshelves, $pubshelves); - ($barshelves, $totshelves) = C4::VirtualShelves::GetRecentShelves(1, $row_count, $borrowernumber); - $total->{'bartotal'} = $totshelves; - ($pubshelves, $totshelves) = C4::VirtualShelves::GetRecentShelves(2, $row_count, undef); - $total->{'pubtotal'} = $totshelves; - $session->param('barshelves', $barshelves); - $session->param('pubshelves', $pubshelves); - $session->param('totshelves', $total); - - C4::Context::set_shelves_userenv('bar',$barshelves); - C4::Context::set_shelves_userenv('pub',$pubshelves); - C4::Context::set_shelves_userenv('tot',$total); } else { if ($userid) { @@ -899,16 +874,6 @@ sub checkauth { # anonymous sessions are created only for the OPAC $debug and warn "Initiating an anonymous session..."; - # Grab the public shelves and add to the session... - my $row_count = 20; # FIXME:This probably should be a syspref - my ($total, $totshelves, $pubshelves); - ($pubshelves, $totshelves) = C4::VirtualShelves::GetRecentShelves(2, $row_count, undef); - $total->{'pubtotal'} = $totshelves; - $session->param('pubshelves', $pubshelves); - $session->param('totshelves', $total); - C4::Context::set_shelves_userenv('pub',$pubshelves); - C4::Context::set_shelves_userenv('tot',$total); - # setting a couple of other session vars... $session->param('ip',$session->remote_addr()); $session->param('lasttime',time()); --- a/C4/VirtualShelves.pm +++ a/C4/VirtualShelves.pm @@ -28,6 +28,11 @@ use Carp; use C4::Context; use C4::Debug; +use constant SHELVES_MASTHEAD_MAX => 10; #number under Lists button in masthead +use constant SHELVES_COMBO_MAX => 10; #add to combo in search +use constant SHELVES_MGRPAGE_MAX => 20; #managing page +use constant SHELVES_POPUP_MAX => 40; #addbybiblio popup + use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); BEGIN { @@ -44,8 +49,7 @@ BEGIN { &GetBibliosShelves ); @EXPORT_OK = qw( - &GetShelvesSummary &GetRecentShelves &GetAllShelves - &RefreshShelvesSummary &SetShelvesLimit + &GetAllShelves &ShelvesMax ); } @@ -54,7 +58,7 @@ my $dbh = C4::Context->dbh; =head1 NAME -C4::VirtualShelves - Functions for manipulating Koha virtual virtualshelves +C4::VirtualShelves - Functions for manipulating Koha virtual shelves =head1 SYNOPSIS @@ -62,21 +66,24 @@ C4::VirtualShelves - Functions for manipulating Koha virtual virtualshelves =head1 DESCRIPTION -This module provides functions for manipulating virtual virtualshelves, -including creating and deleting virtualshelves, and adding and removing -bibs to and from virtualshelves. +This module provides functions for manipulating virtual shelves, +including creating and deleting virtual shelves, and adding and removing +bibs to and from virtual shelves. =head1 FUNCTIONS =head2 GetShelves - ($shelflist, $totshelves) = &GetShelves($mincategory, $row_count, $offset, $owner); + ($shelflist, $totshelves) = &GetShelves($category, $row_count, $offset, $owner); ($shelfnumber, $shelfhash) = each %{$shelflist}; 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>. +number of shelves that meet the C<$owner> and C<$category> criteria. C<$category>, +C<$row_count>, and C<$offset> are required. C<$owner> must be supplied when C<$category> == 1. +When C<$category> is 2, supply undef as argument for C<$owner>. + +This function is used by shelfpage in VirtualShelves/Page.pm when listing all shelves for lists management in opac or staff client. Order is by shelfname. + 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: @@ -94,35 +101,40 @@ The number of virtuals on that virtualshelves. =cut -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 ); - $query .= ($mincategory == 1) ? "WHERE owner=? AND category=?" : "WHERE category>=?"; - $query .= qq( - GROUP BY virtualshelves.shelfnumber - ORDER BY virtualshelves.shelfname - LIMIT ?, ?); +sub GetShelves { + my ($category, $row_count, $offset, $owner) = @_; + my @params; + my $total = _shelf_count($owner, $category); + my $query = qq{ + SELECT vs.shelfnumber, vs.shelfname,vs.owner, + bo.surname,bo.firstname,vs.category,vs.sortfield, + count(vc.biblionumber) as count + FROM virtualshelves vs + LEFT JOIN borrowers bo ON vs.owner=bo.borrowernumber + LEFT JOIN virtualshelfcontents vc USING (shelfnumber) }; + if($category==1) { + $query.= qq{ + LEFT JOIN virtualshelfshares sh ON sh.shelfnumber=vs.shelfnumber + AND sh.borrowernumber=? + WHERE category=1 AND (vs.owner=? OR sh.borrowernumber=?) }; + @params= ($owner, $owner, $owner, $offset||0, $row_count); + } + else { + $query.= 'WHERE category=2 '; + @params= ($offset||0, $row_count); + } + $query.= qq{ + GROUP BY vs.shelfnumber + ORDER BY vs.shelfname + LIMIT ?, ?}; + my $sth2 = $dbh->prepare($query); $sth2->execute(@params); my %shelflist; - while ( my ( $shelfnumber, $shelfname, $owner, $surname, - $firstname, $category, $sortfield, $count ) = $sth2->fetchrow ) { + while( my ($shelfnumber, $shelfname, $owner, $surname, $firstname, $category, $sortfield, $count)= $sth2->fetchrow) { $shelflist{$shelfnumber}->{'shelfname'} = $shelfname; $shelflist{$shelfnumber}->{'count'} = $count; - if($count eq 1){ $shelflist{$shelfnumber}->{'single'} = 1; } + $shelflist{$shelfnumber}->{'single'} = $count==1; $shelflist{$shelfnumber}->{'sortfield'} = $sortfield; $shelflist{$shelfnumber}->{'category'} = $category; $shelflist{$shelfnumber}->{'owner'} = $owner; @@ -132,111 +144,77 @@ sub GetShelves ($$$$) { 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 +=head2 GetAllShelves - ($shelflist, $total) = GetRecentShelves(1, $limit, $owner) + $shelflist = GetAllShelves($category, $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 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 the most recently active shelves for +This function is intended to return a dataset reflecting all the shelves for the submitted parameters. =cut -sub GetRecentShelves { - my ($mincategory, $row_count, $owner) = @_; - my $total = _shelf_count($owner, $mincategory); +sub GetAllShelves { + my ($category,$owner,$adding_allowed) = @_; my @params; - my $selection; - if (defined $owner) { - @params = ($owner, $mincategory); - $selection = ' WHERE owner = ? AND category = ?'; - } else { - @params = ( $mincategory); - $selection = ' WHERE category >= ? '; + my $query = 'SELECT vs.* FROM virtualshelves vs '; + if($category==1) { + $query.= qq{ + LEFT JOIN virtualshelfshares sh ON sh.shelfnumber=vs.shelfnumber + AND sh.borrowernumber=? + WHERE category=1 AND (vs.owner=? OR sh.borrowernumber=?) }; + @params = ($owner, $owner, $owner); } - my $query = 'SELECT * FROM virtualshelves'; - $query .= $selection; - $query .= ' ORDER BY lastmodified DESC'; - if ($row_count){ - $query .= ' LIMIT ?'; - push @params,$row_count; + else { + $query.='WHERE category=2 '; + @params = (); } - my $sth = $dbh->prepare($query); + $query.='AND (allow_add=1 OR owner=?) ' if $adding_allowed; + push @params, $owner if $adding_allowed; + $query.= 'ORDER BY shelfname ASC'; + my $sth = $dbh->prepare( $query ); $sth->execute(@params); - my $shelflist = $sth->fetchall_arrayref({}); - return ( $shelflist, $total ); + return $sth->fetchall_arrayref({}); } -=head2 GetAllShelves +=head2 GetSomeShelfNames - $shelflist = GetAllShelves($owner) +Returns shelf names and numbers for Add to combo of search results and Lists button of OPAC header. -This function returns a reference to an array of hashrefs containing all shelves sorted -by the shelf name. +=cut -This function is intended to return a dataset reflecting all the shelves for -the submitted parameters. +sub GetSomeShelfNames { + my ($owner, $purpose, $adding_allowed)= @_; + my ($bar, $pub, @params); -=cut + my $bquery = 'SELECT vs.shelfnumber, vs.shelfname FROM virtualshelves vs '; + my $limit= ShelvesMax($purpose); -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({}); + my $qry1= $bquery."WHERE vs.category=2 "; + $qry1.= "AND (allow_add=1 OR owner=?) " if $adding_allowed; + push @params, $owner||0 if $adding_allowed; + $qry1.= "ORDER BY vs.lastmodified DESC LIMIT $limit"; + + unless($adding_allowed && (!defined($owner) || $owner<=0)) { + #if adding items, user should be known + $pub= $dbh->selectall_arrayref($qry1,{Slice=>{}},@params); + } + + if($owner) { + my $qry2= $bquery. qq{ + LEFT JOIN virtualshelfshares sh ON sh.shelfnumber=vs.shelfnumber AND sh.borrowernumber=? + WHERE vs.category=1 AND (vs.owner=? OR sh.borrowernumber=?) }; + @params=($owner,$owner,$owner); + $qry2.= "AND (allow_add=1 OR owner=?) " if $adding_allowed; + push @params, $owner if $adding_allowed; + $qry2.= "ORDER BY vs.lastmodified DESC "; + $qry2.= "LIMIT $limit"; + $bar= $dbh->selectall_arrayref($qry2,{Slice=>{}},@params); + } + + return ( { bartotal => $bar? scalar @$bar: 0, pubtotal => $pub? scalar @$pub: 0}, $pub, $bar); } =head2 GetShelf @@ -322,50 +300,54 @@ sub GetShelfContents ($;$$$) { =head2 AddShelf - $shelfnumber = &AddShelf( $shelfname, $owner, $category); + $shelfnumber = &AddShelf($hashref, $owner); -Creates a new virtual virtualshelves with name C<$shelfname>, owner C<$owner> and category -C<$category>. +Creates a new virtual shelf. Params passed in a hash like ModShelf. Returns a code to know what's happen. - * -1 : if this virtualshelves already exist. + * -1 : if this virtualshelves already exists. * $shelfnumber : if success. =cut sub AddShelf { - my ( $shelfname, $owner, $category, $sortfield ) = @_; - my $query = qq( - SELECT * - FROM virtualshelves - WHERE shelfname=? AND owner=? - ); + my ($hashref, $owner)= @_; + + #initialize missing hash values to silence warnings + foreach('shelfname','category', 'sortfield', 'allow_add', 'allow_delete_own', 'allow_delete_other' ) { + $hashref->{$_}= exists $hashref->{$_}? $hashref->{$_}||'': ''; + } + + return -1 unless _CheckShelfName($hashref->{shelfname}, $hashref->{category}, $owner, 0); + + my $query = qq(INSERT INTO virtualshelves + (shelfname,owner,category,sortfield,allow_add,allow_delete_own,allow_delete_other) + VALUES (?,?,?,?,?,?,?)); + my $sth = $dbh->prepare($query); - $sth->execute($shelfname,$owner); - ( $sth->rows ) and return (-1); - $query = qq( - INSERT INTO virtualshelves - (shelfname,owner,category,sortfield) - VALUES (?,?,?,?) - ); - $sth = $dbh->prepare($query); - $sth->execute( $shelfname, $owner, $category, $sortfield ); + $sth->execute( + $hashref->{shelfname}, + $owner, + $hashref->{category}, + $hashref->{sortfield}, + $hashref->{allow_add}||0, + $hashref->{allow_delete_own}||1, + $hashref->{allow_delete_other}||0 ); my $shelfnumber = $dbh->{'mysql_insertid'}; - return ($shelfnumber); + return $shelfnumber; } =head2 AddToShelf - &AddToShelf($biblionumber, $shelfnumber); + &AddToShelf($biblionumber, $shelfnumber, $borrower); Adds bib number C<$biblionumber> to virtual virtualshelves number C<$shelfnumber>, unless that bib is already on that shelf. =cut -#' sub AddToShelf { - my ( $biblionumber, $shelfnumber ) = @_; + my ($biblionumber, $shelfnumber, $borrowernumber) = @_; return unless $biblionumber; my $query = qq( SELECT * @@ -378,12 +360,10 @@ sub AddToShelf { ($sth->rows) and return undef; # already on shelf $query = qq( INSERT INTO virtualshelfcontents - (shelfnumber, biblionumber, flags) - VALUES - (?, ?, 0) - ); + (shelfnumber, biblionumber, flags, borrowernumber) + VALUES (?, ?, 0, ?)); $sth = $dbh->prepare($query); - $sth->execute( $shelfnumber, $biblionumber ); + $sth->execute( $shelfnumber, $biblionumber, $borrowernumber); $query = qq(UPDATE virtualshelves SET lastmodified = CURRENT_TIMESTAMP WHERE shelfnumber = ?); @@ -393,51 +373,55 @@ sub AddToShelf { =head2 ModShelf -ModShelf($shelfnumber, $hashref) +my $result= ModShelf($shelfnumber, $hashref) 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. +Fields like shelfnumber or owner cannot be changed. + +Returns 1 if the action seemed to be successful. =cut 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; - } + my ($shelfnumber,$hashref) = @_; - my $query = "UPDATE virtualshelves SET "; - my @bind_params = (); - my @set_clauses = (); + my $query= "SELECT * FROM virtualshelves WHERE shelfnumber=?"; + my $sth = $dbh->prepare($query); + $sth->execute($shelfnumber); + my $oldrecord= $sth->fetchrow_hashref; + return 0 unless $oldrecord; #not found? - foreach my $column (keys %$shelf) { - push @set_clauses, "$column = ?"; - push @bind_params, $shelf->{$column}; + #initialize missing hash values to silence warnings + foreach('shelfname','category', 'sortfield', 'allow_add', 'allow_delete_own', 'allow_delete_other' ) { + $hashref->{$_}= exists $hashref->{$_}? $hashref->{$_}||'': ''; } - if ($#set_clauses == -1) { - carp "No columns to update passed to ModShelf"; - return; + #if name or category changes, the name should be tested + if($hashref->{shelfname} || $hashref->{category}) { + unless(_CheckShelfName( + $hashref->{shelfname}||$oldrecord->{shelfname}, + $hashref->{category}||$oldrecord->{category}, + $oldrecord->{owner}, $shelfnumber )) { + return 0; #name check failed + } } - $query .= join(", ", @set_clauses); - - $query .= " WHERE shelfnumber = ? "; - push @bind_params, $shelfnumber; - $debug and warn "ModShelf query:\n $query\n", - "ModShelf query args: ", join(',', @bind_params), "\n"; - my $sth = $dbh->prepare($query); - $sth->execute( @bind_params ); + #only the following fields from the hash may be changed + $query= "UPDATE virtualshelves SET shelfname=?, category=?, sortfield=?, allow_add=?, allow_delete_own=?, allow_delete_other=? WHERE shelfnumber=?"; + $sth = $dbh->prepare($query); + $sth->execute( + $hashref->{shelfname}||$oldrecord->{shelfname}, + $hashref->{category}||$oldrecord->{category}, + $hashref->{sortfield}||$oldrecord->{sortfield}, + $hashref->{allow_add}||$oldrecord->{allow_add}, + $hashref->{allow_delete_own}||$oldrecord->{allow_delete_own}, + $hashref->{allow_delete_other}||$oldrecord->{allow_delete_other}, + $shelfnumber ); + return $@? 0: 1; } =head2 ShelfPossibleAction @@ -446,7 +430,11 @@ ShelfPossibleAction($loggedinuser, $shelfnumber, $action); C<$loggedinuser,$shelfnumber,$action> -$action can be "view" or "manage". +$action can be "view", "add", "delete", "manage", "new_public", "new_private". +Note that add/delete here refers to adding/deleting entries from the list. Deleting the list itself falls under manage. +new_public and new_private refers to creating a new public or private list. +The distinction between deleting your own entries from the list or entries from +others is made in DelFromShelf. Returns 1 if the user can do the $action in the $shelfnumber shelf. Returns 0 otherwise. @@ -455,75 +443,115 @@ Returns 0 otherwise. sub ShelfPossibleAction { my ( $user, $shelfnumber, $action ) = @_; - my $query = qq( - SELECT owner,category - FROM virtualshelves - WHERE shelfnumber=? - ); + $action= 'view' unless $action; + $user=0 unless $user; + + if($action =~ /^new/) { #no shelfnumber needed + if($action eq 'new_private') { + return $user>0; + } + elsif($action eq 'new_public') { + return $user>0 && C4::Context->preference('OpacAllowPublicListCreation'); + } + return 0; + } + + return 0 unless defined($shelfnumber); + + my $query = qq/ + SELECT IFNULL(owner,0) AS owner, category, allow_add, allow_delete_own, allow_delete_other, IFNULL(sh.borrowernumber,0) AS borrowernumber + FROM virtualshelves vs + LEFT JOIN virtualshelfshares sh ON sh.shelfnumber=vs.shelfnumber + AND sh.borrowernumber=? + WHERE vs.shelfnumber=? + /; my $sth = $dbh->prepare($query); - $sth->execute($shelfnumber); - my ( $owner, $category ) = $sth->fetchrow; - require C4::Members; - my $borrower = C4::Members::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. + $sth->execute($user, $shelfnumber); + my $shelf= $sth->fetchrow_hashref; + + return 0 unless $shelf && ($shelf->{category}==2 || $shelf->{owner}==$user || $shelf->{borrowernumber}==$user); + if($action eq 'view') { + #already handled in the above condition + return 1; + } + elsif($action eq 'add') { + return 0 if $user<=0; #should be logged in + return 1 if $shelf->{allow_add}==1 || $shelf->{owner}==$user; + #owner may always add + } + elsif($action eq 'delete') { + #this answer is just diplomatic: it says that you may be able to delete + #some items from that shelf + #it does not answer the question about a specific biblio + #DelFromShelf checks the situation per biblio + return 1 if $user>0 && ($shelf->{allow_delete_own}==1 || $shelf->{allow_delete_other}==1); + } + elsif($action eq 'manage') { + return 1 if $user && $shelf->{owner}==$user; + } return 0; } =head2 DelFromShelf - &DelFromShelf( $biblionumber, $shelfnumber); + $result= &DelFromShelf( $bibref, $shelfnumber, $user); -Removes bib number C<$biblionumber> from virtual virtualshelves number -C<$shelfnumber>. If the bib wasn't on that virtualshelves to begin with, -nothing happens. +Removes biblionumbers in passed arrayref from shelf C<$shelfnumber>. +If the bib wasn't on that virtualshelves to begin with, nothing happens. + +Returns 0 if no items have been deleted. =cut -#' sub DelFromShelf { - my ( $biblionumber, $shelfnumber ) = @_; - my $query = qq( - DELETE FROM virtualshelfcontents - WHERE shelfnumber=? AND biblionumber=? - ); - my $sth = $dbh->prepare($query); - $sth->execute( $shelfnumber, $biblionumber ); + my ($bibref, $shelfnumber, $user) = @_; + my $query = qq(SELECT allow_delete_own, allow_delete_other FROM virtualshelves WHERE shelfnumber=?); + my $sth= $dbh->prepare($query); + $sth->execute($shelfnumber); + my ($del_own, $del_oth)= $sth->fetchrow; + my $r; my $t=0; + + if($del_own) { + $query = qq(DELETE FROM virtualshelfcontents + WHERE shelfnumber=? AND biblionumber=? AND borrowernumber=?); + $sth= $dbh->prepare($query); + foreach my $biblionumber (@$bibref) { + $sth->execute($shelfnumber, $biblionumber, $user); + $r= $sth->rows; #Expect -1, 0 or 1 (-1 means Don't know; count as 1) + $t+= ($r==-1)? 1: $r; + } + } + if($del_oth) { + $query = qq(DELETE FROM virtualshelfcontents + WHERE shelfnumber=? AND biblionumber=? AND borrowernumber<>?); + $sth= $dbh->prepare($query); + foreach my $biblionumber (@$bibref) { + $sth->execute($shelfnumber, $biblionumber, $user); + $r= $sth->rows; + $t+= ($r==-1)? 1: $r; + } + } + return $t; } -=head2 DelShelf (old version) - - ($status, $msg) = &DelShelf($shelfnumber); - -Deletes virtual virtualshelves number C<$shelfnumber>. The virtualshelves must -be empty. - -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. - -=head2 DelShelf (current version) +=head2 DelShelf $Number = DelShelf($shelfnumber); This function deletes the shelf number, and all of it's content. +Authorization to do so MUST have been checked before calling, while using +ShelfPossibleAction with manage parameter. =cut sub DelShelf { - unless (@_) { - carp "DelShelf called without valid argument (shelfnumber)"; - return undef; - } - my $sth = $dbh->prepare("DELETE FROM virtualshelves WHERE shelfnumber=?"); - return $sth->execute(shift); + my ($shelfnumber)= @_; + return unless $shelfnumber && $shelfnumber =~ /^\d+$/; + my $sth = $dbh->prepare("DELETE FROM virtualshelves WHERE shelfnumber=?"); + return $sth->execute($shelfnumber); } -=head2 GetBibShelves +=head2 GetBibliosShelves This finds all the public lists that this bib record is in. @@ -536,65 +564,59 @@ sub GetBibliosShelves { SELECT vs.shelfname, vs.shelfnumber FROM virtualshelves vs JOIN virtualshelfcontents vc ON (vs.shelfnumber= vc.shelfnumber) - WHERE vs.category != 1 + WHERE vs.category=2 AND vc.biblionumber= ? '); $sth->execute( $biblionumber ); return $sth->fetchall_arrayref({}); } -=head2 RefreshShelvesSummary +=head2 ShelvesMax - ($total, $pubshelves, $barshelves) = RefreshShelvesSummary($sessionID, $loggedinuser, $row_count); + $howmany= ShelvesMax($context); -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. +Tells how much shelves are shown in which context. +POPUP refers to addbybiblionumber popup, MGRPAGE is managing page (in opac or +staff), COMBO refers to the Add to-combo of search results. MASTHEAD is the +main Koha toolbar with Lists button. =cut -sub RefreshShelvesSummary ($$$) { - require C4::Auth; - 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); +sub ShelvesMax { + my $which= shift; + return SHELVES_POPUP_MAX if $which eq 'POPUP'; + return SHELVES_MGRPAGE_MAX if $which eq 'MGRPAGE'; + return SHELVES_COMBO_MAX if $which eq 'COMBO'; + return SHELVES_MASTHEAD_MAX if $which eq 'MASTHEAD'; + return SHELVES_MASTHEAD_MAX; } # 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 _shelf_count { + my ($owner, $category) = @_; + my @params; + # Find out how many shelves total meet the submitted criteria... + + my $query = "SELECT count(*) FROM virtualshelves vs "; + if($category==1) { + $query.= qq{ + LEFT JOIN virtualshelfshares sh ON sh.shelfnumber=vs.shelfnumber + AND sh.borrowernumber=? + WHERE category=1 AND (vs.owner=? OR sh.borrowernumber=?) }; + @params= ($owner, $owner, $owner); + } + else { + $query.='WHERE category=2'; + @params= (); + } + my $sth = $dbh->prepare($query); + $sth->execute(@params); + my ($total)= $sth->fetchrow; + return $total; } -sub _biblionumber_sth { +sub _biblionumber_sth { #only used in obsolete sub below my ($shelf) = @_; my $query = 'select biblionumber from virtualshelfcontents where shelfnumber = ?'; my $dbh = C4::Context->dbh; @@ -605,7 +627,7 @@ sub _biblionumber_sth { $sth; } -sub each_biblionumbers (&$) { +sub each_biblionumbers (&$) { #OBSOLETE my ($code,$shelf) = @_; my $ref = _biblionumber_sth($shelf)->fetchall_arrayref; map { @@ -614,6 +636,25 @@ sub each_biblionumbers (&$) { } @$ref; } +sub _CheckShelfName { + my ($name, $cat, $owner, $number)= @_; + + my $query = qq( + SELECT DISTINCT shelfnumber + FROM virtualshelves + LEFT JOIN virtualshelfshares sh USING (shelfnumber) + WHERE shelfname=? AND shelfnumber<>?); + if($cat==1) { + $query.= ' AND (sh.borrowernumber=? OR owner=?) AND category=1'; + } + else { + $query.= ' AND category=2'; + } + my $sth = $dbh->prepare($query); + $sth->execute($cat==1? ($name, $number, $owner, $owner): ($name, $number)); + return $sth->rows>0? 0: 1; +} + 1; __END__ --- a/C4/VirtualShelves/Page.pm +++ a/C4/VirtualShelves/Page.pm @@ -22,8 +22,12 @@ package C4::VirtualShelves::Page; use strict; use warnings; + use CGI; -use C4::VirtualShelves qw/:DEFAULT RefreshShelvesSummary/; +use Exporter; +use Data::Dumper; + +use C4::VirtualShelves qw/:DEFAULT ShelvesMax/; use C4::Biblio; use C4::Items; use C4::Koha; @@ -32,11 +36,11 @@ use C4::Members; use C4::Output; use C4::Dates qw/format_date/; use C4::Tags qw(get_tags); -use Exporter; -use Data::Dumper; use C4::Csv; use C4::XSLT; +use constant VIRTUALSHELVES_COUNT => 20; + use vars qw($debug @EXPORT @ISA $VERSION); BEGIN { @@ -51,12 +55,15 @@ our %pages = ( opac => { redirect => '/cgi-bin/koha/opac-shelves.pl', }, ); -sub shelfpage ($$$$$) { +sub shelfpage { my ( $type, $query, $template, $loggedinuser, $cookie ) = @_; ( $pages{$type} ) or $type = 'opac'; $query or die "No query"; $template or die "No template"; - $template->param( loggedinuser => $loggedinuser ); + $template->param( + loggedinuser => $loggedinuser, + OpacAllowPublicListCreation => C4::Context->preference('OpacAllowPublicListCreation'), + ); my $edit; my $shelves; my @paramsloop; @@ -69,7 +76,7 @@ sub shelfpage ($$$$$) { my $marcflavour = C4::Context->preference("marcflavour"); $shelflimit = ( $type eq 'opac' ? C4::Context->preference('OPACnumSearchResults') : C4::Context->preference('numSearchResults') ); - $shelflimit = $shelflimit || 20; + $shelflimit = $shelflimit || ShelvesMax('MGRPAGE'); $shelfoffset = ( $itemoff - 1 ) * $shelflimit; # Sets the offset to begin retrieving items at $shelveslimit = $shelflimit; # Limits number of shelves returned for a given query (row_count) $shelvesoffset = ( $shelfoff - 1 ) * $shelflimit; # Sets the offset to begin retrieving shelves at (offset) @@ -84,36 +91,51 @@ sub shelfpage ($$$$$) { } my $op = $query->param('op'); - # my $imgdir = getitemtypeimagesrc(); - # my $itemtypes = GetItemTypes(); - # the format of this is unindented for ease of diff comparison to the old script # Note: do not mistake the assignment statements below for comparisons! if ( $query->param('modifyshelfcontents') ) { my ( $shelfnumber, $barcode, $item, $biblio ); if ( $shelfnumber = $query->param('viewshelf') ) { - if ( ShelfPossibleAction( $loggedinuser, $shelfnumber, 'manage' ) ) { - if ( $barcode = $query->param('addbarcode') ) { - $item = GetItem( 0, $barcode ); - if (defined $item && $item->{'itemnumber'}){ + #add to shelf + if($barcode = $query->param('addbarcode') ) { + if(ShelfPossibleAction( $loggedinuser, $shelfnumber, 'add')) { + $item = GetItem( 0, $barcode); + if (defined $item && $item->{'itemnumber'}) { $biblio = GetBiblioFromItemNumber( $item->{'itemnumber'} ); - AddToShelf( $biblio->{'biblionumber'}, $shelfnumber ) + AddToShelf( $biblio->{'biblionumber'}, $shelfnumber, $loggedinuser) or push @paramsloop, { duplicatebiblio => $barcode }; - } else { + } + else { push @paramsloop, { failgetitem => $barcode }; } - } else { - ( grep { /REM-(\d+)/ } $query->param ) or push @paramsloop, { nobarcode => 1 }; - foreach ( $query->param ) { + } + else { + push @paramsloop, { nopermission => $shelfnumber }; + } + } + elsif(grep { /REM-(\d+)/ } $query->param) { + #remove item(s) from shelf + if(ShelfPossibleAction($loggedinuser, $shelfnumber, 'delete')) { + #This is just a general okay; DelFromShelf checks further + my @bib; + foreach($query->param) { /REM-(\d+)/ or next; - $debug and warn "SHELVES: user $loggedinuser removing item $1 from shelf $shelfnumber"; - DelFromShelf( $1, $shelfnumber ); # $1 is biblionumber + push @bib, $1; #$1 is biblionumber + } + my $t= DelFromShelf(\@bib, $shelfnumber, $loggedinuser); + if($t==0) { + push @paramsloop, {nothingdeleted => $shelfnumber}; + } + elsif($t<@bib) { + push @paramsloop, {somedeleted => $shelfnumber}; } } - } else { - push @paramsloop, { nopermission => $shelfnumber }; + else { + push @paramsloop, { nopermission => $shelfnumber }; + } } - } else { + } + else { push @paramsloop, { noshelfnumber => 1 }; } } @@ -148,24 +170,31 @@ sub shelfpage ($$$$$) { SWITCH: { if ($op) { - unless ($okmanage) { - push @paramsloop, { nopermission => $shelfnumber }; - last SWITCH; - } + #Saving modified shelf if ( $op eq 'modifsave' ) { + unless ($okmanage) { + push @paramsloop, { nopermission => $shelfnumber }; + last SWITCH; + } my $shelf = { 'shelfname' => $query->param('shelfname'), - 'category' => $query->param('category'), 'sortfield' => $query->param('sortfield'), }; - - ModShelf( $shelfnumber, $shelf ); - - } elsif ( $op eq 'modif' ) { + if($query->param('category')) { #optional + $shelf->{category}= $query->param('category'); + } + unless(ModShelf($shelfnumber, $shelf )) { + push @paramsloop, {modifyfailure => $shelf->{shelfname}}; + last SWITCH; + } + } + #Editing a shelf + elsif ( $op eq 'modif' ) { my ( $shelfnumber2, $shelfname, $owner, $category, $sortfield ) = GetShelf($shelfnumber); my $member = GetMember( 'borrowernumber' => $owner ); my $ownername = defined($member) ? $member->{firstname} . " " . $member->{surname} : ''; $edit = 1; + $sortfield='' unless $sortfield; $template->param( edit => 1, shelfnumber => $shelfnumber2, @@ -179,6 +208,8 @@ sub shelfpage ($$$$$) { } last SWITCH; } + + #View a shelf if ( $shelfnumber = $query->param('viewshelf') ) { # explicitly fetch this shelf my ($shelfnumber2,$shelfname,$owner,$category,$sorton) = GetShelf($shelfnumber); @@ -247,21 +278,31 @@ sub shelfpage ($$$$$) { authorsort => $authorsort, yearsort => $yearsort, manageshelf => $manageshelf, + allowremovingitems => ShelfPossibleAction( $loggedinuser, $shelfnumber, 'delete'), + allowaddingitem => ShelfPossibleAction( $loggedinuser, $shelfnumber, 'add'), "category$category" => 1, category => $category, itemsloop => $items, + showprivateshelves => $category==1, ); } else { push @paramsloop, { nopermission => $shelfnumber }; } last SWITCH; } + if ( $query->param('shelves') ) { my $stay = 1; + + #Add a shelf if ( my $newshelf = $query->param('addshelf') ) { # note: a user can always add a new shelf - my $shelfnumber = AddShelf( $newshelf, $query->param('owner'), $query->param('category'), $query->param('sortfield') ); + my $shelfnumber = AddShelf( { + shelfname => $newshelf, + sortfield => $query->param('sortfield'), + category => $query->param('category') }, + $query->param('owner') ); $stay = 1; if ( $shelfnumber == -1 ) { #shelf already exists. $showadd = 1; @@ -272,6 +313,8 @@ sub shelfpage ($$$$$) { exit; } } + + #Deleting a shelf (asking for confirmation if it has entries) foreach ( $query->param() ) { /DEL-(\d+)/ or next; $delflag = 1; @@ -313,7 +356,6 @@ sub shelfpage ($$$$$) { } push( @paramsloop, { delete_ok => $name } ); - # print $query->redirect($pages{$type}->{redirect}); exit; $stay = 0; } $showadd = 1; @@ -323,7 +365,7 @@ sub shelfpage ($$$$$) { } last SWITCH; } - } + } # end of SWITCH block (@paramsloop) and $template->param( paramsloop => \@paramsloop ); $showadd and $template->param( showadd => 1 ); @@ -337,7 +379,7 @@ sub shelfpage ($$$$$) { my %line; $shelflist->{$element}->{shelf} = $element; my $category = $shelflist->{$element}->{'category'}; - my $owner = $shelflist->{$element}->{'owner'}; + my $owner = $shelflist->{$element}->{'owner'}||0; my $canmanage = ShelfPossibleAction( $loggedinuser, $element, 'manage' ); my $sortfield = $shelflist->{$element}->{'sortfield'}; if ( $sortfield ){ @@ -349,7 +391,7 @@ sub shelfpage ($$$$$) { } $shelflist->{$element}->{"viewcategory$category"} = 1; $shelflist->{$element}->{manageshelf} = $canmanage; - if ( $owner eq $loggedinuser or $canmanage ) { + if($canmanage || ($loggedinuser && $owner==$loggedinuser)) { $shelflist->{$element}->{'mine'} = 1; } my $member = GetMember( 'borrowernumber' => $owner ); @@ -393,28 +435,15 @@ sub shelfpage ($$$$$) { $template->param( seflag => 1 ); } - #FIXME: This refresh really only needs to happen when there is a modification of some sort - # to the shelves, but the above code is so convoluted in its handling of the various - # options, it is easier to do this refresh every time C4::VirtualShelves::Page.pm is - # called - - my ( $total, $pubshelves, $barshelves ) = RefreshShelvesSummary( $query->cookie("CGISESSID"), $loggedinuser, ( $loggedinuser == -1 ? 20 : 10 ) ); - - if ( defined $barshelves ) { - $template->param( - barshelves => scalar( @{ $barshelves } ), +#Next call updates the shelves for the Lists button. +#May not always be needed (when nothing changed), but doesn't take much. + my ($total, $pubshelves, $barshelves) = C4::VirtualShelves::GetSomeShelfNames($loggedinuser, 'MASTHEAD'); + $template->param( + barshelves => $total->{bartotal}, barshelvesloop => $barshelves, - ); - $template->param( bartotal => $total->{'bartotal'}, ) if ( $total->{'bartotal'} > scalar( @{ $barshelves } ) ); - } - - if ( defined $pubshelves ) { - $template->param( - pubshelves => scalar( @{ $pubshelves } ), + pubshelves => $total->{pubtotal}, pubshelvesloop => $pubshelves, - ); - $template->param( pubtotal => $total->{'pubtotal'}, ) if ( $total->{'pubtotal'} > scalar( @{ $pubshelves } ) ); - } + ); output_html_with_http_headers $query, $cookie, $template->output; } --- a/catalogue/search.pl +++ a/catalogue/search.pl @@ -147,7 +147,7 @@ use C4::Search; use C4::Languages qw(getAllLanguages); use C4::Koha; use C4::Members qw(GetMember); -use C4::VirtualShelves qw(GetRecentShelves); +use C4::VirtualShelves; use POSIX qw(ceil floor); use C4::Branch; # GetBranches @@ -695,21 +695,13 @@ if ($query_desc || $limit_desc) { # VI. BUILD THE TEMPLATE # Build drop-down list for 'Add To:' menu... - -my $row_count = 10; # FIXME:This probably should be a syspref -my ($pubshelves, $total) = GetRecentShelves(2, $row_count, undef); -my ($barshelves, $total) = GetRecentShelves(1, $row_count, $borrowernumber); - -if (@{$pubshelves}) { - $template->param( addpubshelves => scalar @{$pubshelves}); - $template->param( addpubshelvesloop => $pubshelves); -} - -if (@{$barshelves}) { - $template->param( addbarshelves => scalar @{$barshelves}); - $template->param( addbarshelvesloop => $barshelves); -} - - +my ($totalref, $pubshelves, $barshelves)= + C4::VirtualShelves::GetSomeShelfNames($borrowernumber,'COMBO',1); +$template->param( + addbarshelves => $totalref->{bartotal}, + addbarshelvesloop => $barshelves, + addpubshelves => $totalref->{pubtotal}, + addpubshelvesloop => $pubshelves, + ); output_html_with_http_headers $cgi, $cookie, $template->output; --- a/koha-tmpl/intranet-tmpl/prog/en/modules/virtualshelves/addbybiblionumber.tt +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/virtualshelves/addbybiblionumber.tt @@ -7,6 +7,13 @@
+[% UNLESS (authorized) %] +
+ [% IF (errcode==1) %]Could not make new shelf. Please check the name.
[% END %]
+ [% IF (errcode==2) %]Sorry, you do not have permission to add items to this list.
[% END %]
+ Close this window.
+
... or...
[% END %] - - [% END %] +... or...
+[% END %] @@ -85,3 +108,4 @@- This is not an open list. - Items cannot be added to it. + [% IF (errcode==1) %] + Could not create new list. Please check if the name is unique. + [% ELSE %] + Sorry, you cannot add items to this list. + [% END %]
[% END %]--- a/koha-tmpl/opac-tmpl/prog/en/modules/opac-shelves.tt +++ a/koha-tmpl/opac-tmpl/prog/en/modules/opac-shelves.tt @@ -175,6 +175,15 @@ $(function() { [% IF ( paramsloo.unrecognized ) %]
[% END %] + [% IF ( paramsloo.modifyfailure) %] + + [% END %] + [% IF ( paramsloo.nothingdeleted) %] + + [% END %] + [% IF ( paramsloo.somedeleted) %] + + [% END %] [% END %] @@ -351,21 +360,20 @@ $(function() { [% END %] - [% IF ( manageshelf ) %] - - [% IF ( itemsloop ) %] + [% IF ( itemsloop && allowremovingitems ) %] + + [% ELSIF ( !itemsloop && manageshelf ) %] + [% END %] [% IF ( edit ) %] @@ -386,11 +394,13 @@ $(function() {