--- loncom/interface/lonfeedback.pm 2012/03/09 15:02:31 1.338 +++ loncom/interface/lonfeedback.pm 2012/03/13 05:37:49 1.339 @@ -1,7 +1,7 @@ # The LearningOnline Network # Feedback # -# $Id: lonfeedback.pm,v 1.338 2012/03/09 15:02:31 raeburn Exp $ +# $Id: lonfeedback.pm,v 1.339 2012/03/13 05:37:49 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -82,6 +82,26 @@ sub discussion_visible { return 1; } +sub discussion_vote_available { + my ($status,$symb)=@_; + my $canvote=&Apache::lonnet::EXT('resource.0.discussvote',$symb); + if ((lc($canvote) eq 'yes') || + ((lc($canvote) eq 'notended') && (&discussion_open($status,$symb)))) { + return 1; + } +} + +sub get_realsymb { + my ($symb) = @_; + my $realsymb = $symb; + if ($symb=~/^bulletin___/) { + my $filename=(&Apache::lonnet::decode_symb($symb))[2]; + $filename=~s{^adm/wrapper/}{}; + $realsymb=&Apache::lonnet::symbread($filename); + } + return $realsymb; +} + sub list_discussion { my ($mode,$status,$ressymb,$imsextras,$group)=@_; unless ($ressymb) { $ressymb=&Apache::lonnet::symbread(); } @@ -265,6 +285,11 @@ sub list_discussion { } else { $seeid=&Apache::lonnet::allowed('rin',$crs); } + +# Is voting on discussions available + my $realsymb = &get_realsymb($ressymb); + my $canvote = &discussion_vote_available($status,$realsymb); + my @discussionitems=(); my %shown = (); my @posteridentity=(); @@ -286,7 +311,7 @@ sub list_discussion { $discinfo{$visitkey} = $visit; &Apache::lonnet::put('nohist_'.$cid.'_discuss',\%discinfo,$env{'user.domain'},$env{'user.name'}); - &build_posting_display(\%usernamesort,\%subjectsort,\%namesort,\%notshown,\%newitem,\%dischash,\%shown,\%alldiscussion,\%imsitems,\%imsfiles,\%roleinfo,\@discussionitems,\@replies,\@depth,\@posters,\$maxdepth,\$visible,\$newpostsflag,\$current,$status,$viewgrades,$seeid,$prevread,$sortposts,$encsymb,$readkey,$showunmark,$showonlyunread,$totposters,\@rolefilter,\@sectionpick,\@grouppick,$classgroups,$statusfilter,$toggkey,$outputtarget,\%anonhash,$anoncnt,$group); + &build_posting_display(\%usernamesort,\%subjectsort,\%namesort,\%notshown,\%newitem,\%dischash,\%shown,\%alldiscussion,\%imsitems,\%imsfiles,\%roleinfo,\@discussionitems,\@replies,\@depth,\@posters,\$maxdepth,\$visible,\$newpostsflag,\$current,$status,$viewgrades,$seeid,$canvote,$prevread,$sortposts,$encsymb,$readkey,$showunmark,$showonlyunread,$totposters,\@rolefilter,\@sectionpick,\@grouppick,$classgroups,$statusfilter,$toggkey,$outputtarget,\%anonhash,$anoncnt,$group); my $discussion=''; my $manifestfile; @@ -744,9 +769,15 @@ END $discussion.= &send_feedback_link($ressymb); if ($env{'request.role.adv'}) { my $close = &Apache::lonnet::EXT('resource.0.discussend',$ressymb); + my $canvote = &Apache::lonnet::EXT('resource.0.discussvote',$ressymb); if (defined($close) && $close ne '' && $close < time) { - $discussion .= ' '.&mt('(Closed for [_1] roles)', - &Apache::lonnet::plaintext('st',$crstype)); + if ($canvote eq 'notended') { + $discussion .= ' '.&mt('(Posting and voting closed for [_1] roles)', + &Apache::lonnet::plaintext('st',$crstype)); + } else { + $discussion .= ' '.&mt('(Closed for [_1] roles)', + &Apache::lonnet::plaintext('st',$crstype)); + } } } } else { @@ -937,7 +968,7 @@ ENDDISCUSS } sub build_posting_display { - my ($usernamesort,$subjectsort,$namesort,$notshown,$newitem,$dischash,$shown,$alldiscussion,$imsitems,$imsfiles,$roleinfo,$discussionitems,$replies,$depth,$posters,$maxdepth,$visible,$newpostsflag,$current,$status,$viewgrades,$seeid,$prevread,$sortposts,$ressymb,$readkey,$showunmark,$showonlyunread,$totposters,$rolefilter,$sectionpick,$grouppick,$classgroups,$statusfilter,$toggkey,$outputtarget,$anonhash,$anoncnt,$group) = @_; + my ($usernamesort,$subjectsort,$namesort,$notshown,$newitem,$dischash,$shown,$alldiscussion,$imsitems,$imsfiles,$roleinfo,$discussionitems,$replies,$depth,$posters,$maxdepth,$visible,$newpostsflag,$current,$status,$viewgrades,$seeid,$canvote,$prevread,$sortposts,$ressymb,$readkey,$showunmark,$showonlyunread,$totposters,$rolefilter,$sectionpick,$grouppick,$classgroups,$statusfilter,$toggkey,$outputtarget,$anonhash,$anoncnt,$group) = @_; my @original=(); my @index=(); my $skip_group_check = 0; @@ -947,17 +978,20 @@ sub build_posting_display { my %contrib=&Apache::lonnet::restore($symb,$env{'request.course.id'}, $env{'course.'.$env{'request.course.id'}.'.domain'}, $env{'course.'.$env{'request.course.id'}.'.num'}); + my (%likes,%userlikes,%userunlikes,@theselikes,$oneplus,$twoplus,$oneminus,$twominus); + my $thisuser=$env{'user.name'}.':'.$env{'user.domain'}; + if ($seeid || $canvote) { # And these are the likes/unlikes - my %likes=&Apache::lonnet::dump('disclikes', + %likes=&Apache::lonnet::dump('disclikes', $env{'course.'.$env{'request.course.id'}.'.domain'}, $env{'course.'.$env{'request.course.id'}.'.num'}, '^'.$symb.':'); - my $thisuser=$env{'user.name'}.':'.$env{'user.domain'}; # Array with likes to figure out averages, etc. - my @theselikes=(); + @theselikes=(); # Hashes containing likes and unlikes for this user. - my %userlikes=(); - my %userunlikes=(); + %userlikes=(); + %userunlikes=(); + } # Is the user allowed to see the real name behind anonymous postings? my $see_anonymous = &Apache::lonnet::allowed('rin',$env{'request.course.id'}.($env{'request.course.sec'}?'/'.$env{'request.course.sec'}:'')); @@ -991,50 +1025,52 @@ sub build_posting_display { &filter_regexp($rolefilter,$sectionpick,$statusfilter); $rolematch = $roleregexp.':'.$secregexp.':'.$statusregexp; } + if ($seeid || $canvote) { # We need to go through this twice, first to get the likes/dislikes, then to actually build the display - for (my $id=1;$id<=$contrib{'version'};$id++) { - my $idx=$id; - next if ($contrib{$idx.':deleted'}); - next if ($contrib{$idx.':hidden'}); - unless ((($hiddens{$idx}) && (!$seeid)) || ($deletions{$idx}) || (!$contrib{$idx.':message'})) { - if ($likes{$symb.':'.$idx.':likes'} ne '') { - push(@theselikes,$likes{$symb.':'.$idx.':likes'}); - if (ref($likes{$symb.':'.$idx.':likers'}) eq 'HASH') { - if (exists($likes{$symb.':'.$idx.':likers'}{$thisuser})) { - $userlikes{$idx} = 1; + for (my $id=1;$id<=$contrib{'version'};$id++) { + my $idx=$id; + next if ($contrib{$idx.':deleted'}); + next if ($contrib{$idx.':hidden'}); + unless ((($hiddens{$idx}) && (!$seeid)) || ($deletions{$idx}) || (!$contrib{$idx.':message'})) { + if ($likes{$symb.':'.$idx.':likes'} ne '') { + push(@theselikes,$likes{$symb.':'.$idx.':likes'}); + if (ref($likes{$symb.':'.$idx.':likers'}) eq 'HASH') { + if (exists($likes{$symb.':'.$idx.':likers'}{$thisuser})) { + $userlikes{$idx} = 1; + } } - } - if (ref($likes{$symb.':'.$idx.':unlikers'}) eq 'HASH') { - if (exists($likes{$symb.':'.$idx.':unlikers'}{$thisuser})) { - $userunlikes{$idx} = 1; + if (ref($likes{$symb.':'.$idx.':unlikers'}) eq 'HASH') { + if (exists($likes{$symb.':'.$idx.':unlikers'}{$thisuser})) { + $userunlikes{$idx} = 1; + } } } } } - } # Figure out average likes and standard deviation if there are enough # discussions to warrant that - my $ave=0; - my $stddev=10000; - if ($#theselikes>1) { - my $sum=0; - my $num=$#theselikes+1; - foreach my $thislike (@theselikes) { - $sum+=$thislike; - } - $ave=$sum/$num; - my $sumsq=0; - foreach my $thislike (@theselikes) { - $sumsq+=($thislike-$ave)*($thislike-$ave); + my $ave=0; + my $stddev=10000; + if ($#theselikes>1) { + my $sum=0; + my $num=$#theselikes+1; + foreach my $thislike (@theselikes) { + $sum+=$thislike; + } + $ave=$sum/$num; + my $sumsq=0; + foreach my $thislike (@theselikes) { + $sumsq+=($thislike-$ave)*($thislike-$ave); + } + $stddev=sqrt($sumsq/$num); } - $stddev=sqrt($sumsq/$num); - } # Now we know the average likes $ave and the standard deviation $stddev # Get the boundaries for markup - my $oneplus=$ave+$stddev; - my $twoplus=$ave+2.*$stddev; - my $oneminus=$ave-$stddev; - my $twominus=$ave-2.*$stddev; + $oneplus=$ave+$stddev; + $twoplus=$ave+2.*$stddev; + $oneminus=$ave-$stddev; + $twominus=$ave-2.*$stddev; + } # # This is now the real loop. Go through all entries, pick up what we need # @@ -1365,40 +1401,47 @@ sub build_posting_display { if ($$dischash{$toggkey}) { $$discussionitems[$idx].='  '.$ctlink; } -# Figure out size based on likes my $thislikes=$likes{$symb.':'.$idx.':likes'}; my $likesize="100"; - if ($thislikes>$twoplus) { - $likesize="200"; - } elsif ($thislikes>$oneplus) { - $likesize="150"; - } - if ($thislikes<$twominus) { - $likesize="50"; - } elsif ($thislikes<$oneminus) { - $likesize="75"; + if ($seeid || $canvote) { +# Figure out size based on likes + my $thislikes=$likes{$symb.':'.$idx.':likes'}; + if ($thislikes>$twoplus) { + $likesize="200"; + } elsif ($thislikes>$oneplus) { + $likesize="150"; + } + if ($thislikes<$twominus) { + $likesize="50"; + } elsif ($thislikes<$oneminus) { + $likesize="75"; + } } # Actually glue in the message itself $$discussionitems[$idx].= '
'. "
". $message. '
'; + if ($canvote) { # Put in the like and unlike buttons - if ($userlikes{$idx}) { - $$discussionitems[$idx].=''.&mt('You like this posting').''; - } else { - $$discussionitems[$idx].=' '.&discussion_link($symb,''.&mt('Like').'','like',$idx,$$newpostsflag,$prevread,&group_args($group),&mt("Like this posting")); - } - if ($userunlikes{$idx}) { - $$discussionitems[$idx].=''.&mt('You unlike this posting').''; - } else { - $$discussionitems[$idx].=' '.&discussion_link($symb,''.&mt('Unlike').'','unlike',$idx,$$newpostsflag,$prevread,&group_args($group),&mt("Unlike this posting")); + if ($userlikes{$idx}) { + $$discussionitems[$idx].=''.&mt('You like this posting').''; + } else { + $$discussionitems[$idx].=' '.&discussion_link($symb,''.&mt('Like').'','like',$idx,$$newpostsflag,$prevread,&group_args($group),&mt("Like this posting")); + } + if ($userunlikes{$idx}) { + $$discussionitems[$idx].=''.&mt('You unlike this posting').''; + } else { + $$discussionitems[$idx].=' '.&discussion_link($symb,''.&mt('Unlike').'','unlike',$idx,$$newpostsflag,$prevread,&group_args($group),&mt("Unlike this posting")); + } } - my $thislikes=$likes{$symb.':'.$idx.':likes'}; - if ($thislikes>0) { - $$discussionitems[$idx].=' ('.&mt("[_1] likes",$thislikes).')'; - } elsif ($thislikes<0) { - $$discussionitems[$idx].=' ('.&mt("[_1] unlikes",abs($thislikes)).')'; + if ($seeid || $canvote) { + my $thislikes=$likes{$symb.':'.$idx.':likes'}; + if ($thislikes>0) { + $$discussionitems[$idx].=' ('.&mt("[_1] likes",$thislikes).')'; + } elsif ($thislikes<0) { + $$discussionitems[$idx].=' ('.&mt("[_1] unlikes",abs($thislikes)).')'; + } } # If there is any history to this post, inform the reader if ($contrib{$idx.':history'}) { @@ -2795,12 +2838,7 @@ sub screen_header { my ($blocked,$blocktext) = &Apache::loncommon::blocking_status('boards'); my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; - my $realsymb = $symb; - if ($symb=~/^bulletin___/) { - my $filename=(&Apache::lonnet::decode_symb($symb))[2]; - $filename=~s|^adm/wrapper/||; - $realsymb=&Apache::lonnet::symbread($filename); - } + my $realsymb = &get_realsymb($symb); if (!$blocked && &discussion_open(undef,$realsymb) && (&Apache::lonnet::allowed('pch', $env{'request.course.id'}. @@ -3003,12 +3041,7 @@ sub storefeedbacklikes { sub adddiscuss { my ($symb,$email,$anon,$attachmenturl,$subject,$group)=@_; my $status=''; - my $realsymb; - if ($symb=~/^bulletin___/) { - my $filename=(&Apache::lonnet::decode_symb($symb))[2]; - $filename=~s|^adm/wrapper/||; - $realsymb=&Apache::lonnet::symbread($filename); - } + my $realsymb = &get_realsymb($symb); my ($cnum,$cdom); if ($env{'request.course.id'}) { $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; @@ -4025,89 +4058,99 @@ ENDREDIR my $entry=$env{'form.like'}?$env{'form.like'}:$env{'form.unlike'}; my ($symb,$idx)=split(/\:\:\:/,$entry); ($symb,my $feedurl)=&get_feedurl_and_clean_symb($symb); + my $status='OPEN'; + if ($Apache::lonhomework::parsing_a_problem || + $Apache::lonhomework::parsing_a_task) { + $status=$Apache::inputtags::status[-1]; + } + my $result; + my $realsymb = &get_realsymb($symb); + if (&discussion_vote_available($status,$realsymb)) { # # Likes and unlikes are in db-file "disclikes" of the course # The prefix is the $symb to identify the resource discussion, # and the $idx to identify the entry # - my $prefix=$symb.':'.$idx.':'; - my %contrib=&Apache::lonnet::dump('disclikes', + my $prefix=$symb.':'.$idx.':'; + my %contrib=&Apache::lonnet::dump('disclikes', $env{'course.'.$env{'request.course.id'}.'.domain'}, $env{'course.'.$env{'request.course.id'}.'.num'}, '^'.$prefix); # Get current like or unlike status for the $idx for this user. - my $thisuser=$env{'user.name'}.':'.$env{'user.domain'}; - my ($userlikes,$userunlikes); - if (ref($contrib{$prefix.'likers'}) eq 'HASH') { - if (exists($contrib{$prefix.'likers'}{$thisuser})) { - $userlikes = 1; + my $thisuser=$env{'user.name'}.':'.$env{'user.domain'}; + my ($userlikes,$userunlikes); + if (ref($contrib{$prefix.'likers'}) eq 'HASH') { + if (exists($contrib{$prefix.'likers'}{$thisuser})) { + $userlikes = 1; + } } - } - if (ref($contrib{$prefix.'unlikers'}) eq 'HASH') { - if (exists($contrib{$prefix.'unlikers'}{$thisuser})) { - $userunlikes = 1; + if (ref($contrib{$prefix.'unlikers'}) eq 'HASH') { + if (exists($contrib{$prefix.'unlikers'}{$thisuser})) { + $userunlikes = 1; + } } - } # Get the current "likes" count - my $likes=$contrib{$prefix.'likes'}; + my $likes=$contrib{$prefix.'likes'}; # Find out if they already voted # Users cannot like a post twice, or unlike it twice. # They can change their mind, though. - my $alreadyflag=0; - my $thisuser=$env{'user.name'}.':'.$env{'user.domain'}; - if ($env{'form.like'}) { - if ($userlikes) { - $alreadyflag=1; - } elsif ($userunlikes) { - delete($contrib{$prefix.'unlikers'}{$thisuser}); - $likes++; - } else { - if (ref($contrib{$prefix.'likers'}) eq 'HASH') { - $contrib{$prefix.'likers'}{$thisuser} = 1; + my $alreadyflag=0; + my $thisuser=$env{'user.name'}.':'.$env{'user.domain'}; + if ($env{'form.like'}) { + if ($userlikes) { + $alreadyflag=1; + } elsif ($userunlikes) { + delete($contrib{$prefix.'unlikers'}{$thisuser}); + $likes++; } else { - $contrib{$prefix.'likers'} = {$thisuser => 1}; + if (ref($contrib{$prefix.'likers'}) eq 'HASH') { + $contrib{$prefix.'likers'}{$thisuser} = 1; + } else { + $contrib{$prefix.'likers'} = {$thisuser => 1}; + } + $likes++; } - $likes++; - } - } else { - if ($userunlikes) { - $alreadyflag=1; - } elsif ($userlikes) { - delete($contrib{$prefix.'likers'}{$thisuser}); - $likes--; } else { - if (ref($contrib{$prefix.'unlikers'}) eq 'HASH') { - $contrib{$prefix.'unlikers'}{$thisuser} = 1; + if ($userunlikes) { + $alreadyflag=1; + } elsif ($userlikes) { + delete($contrib{$prefix.'likers'}{$thisuser}); + $likes--; } else { - $contrib{$prefix.'unlikers'} = {$thisuser => 1}; + if (ref($contrib{$prefix.'unlikers'}) eq 'HASH') { + $contrib{$prefix.'unlikers'}{$thisuser} = 1; + } else { + $contrib{$prefix.'unlikers'} = {$thisuser => 1}; + } + $likes--; } - $likes--; } - } - my $result; # $alreadyflag would be 1 if they tried to double-like or double-unlike - unless ($alreadyflag) { - my %newhash=($prefix.'likes' => $likes, - $prefix.'likers' => $contrib{$prefix.'likers'}, - $prefix.'unlikers' => $contrib{$prefix.'unlikers'}); + unless ($alreadyflag) { + my %newhash=($prefix.'likes' => $likes, + $prefix.'likers' => $contrib{$prefix.'likers'}, + $prefix.'unlikers' => $contrib{$prefix.'unlikers'}); # Store data in db-file "disclikes" - if (&Apache::lonnet::put('disclikes', - \%newhash, - $env{'course.'.$env{'request.course.id'}.'.domain'}, - $env{'course.'.$env{'request.course.id'}.'.num'}) eq 'ok') { + if (&Apache::lonnet::put('disclikes', + \%newhash, + $env{'course.'.$env{'request.course.id'}.'.domain'}, + $env{'course.'.$env{'request.course.id'}.'.num'}) eq 'ok') { # Also store with the person who posted the liked/unliked entry - if ($env{'form.like'}) { - &storediscussionlikes(1,$contrib{$idx.':sendername'},$contrib{$idx.':senderdomain'}); - $result=&mt("Registered 'Like'"); + if ($env{'form.like'}) { + &storediscussionlikes(1,$contrib{$idx.':sendername'},$contrib{$idx.':senderdomain'}); + $result=&mt("Registered 'Like'"); + } else { + &storediscussionlikes(-1,$contrib{$idx.':sendername'},$contrib{$idx.':senderdomain'}); + $result=&mt("Registered 'Unlike'"); + } } else { - &storediscussionlikes(-1,$contrib{$idx.':sendername'},$contrib{$idx.':senderdomain'}); - $result=&mt("Registered 'Unlike'"); - } - } else { # Oops, something went wrong - $result=&mt("Failed to register vote"); + $result=&mt("Failed to register vote"); + } } + } else { + $result=&mt('Voting unavailable for this discussion'); } &redirect_back($r,$feedurl,$result.'
', '0','0','','',$env{'form.previous'},undef,undef,undef, @@ -4562,6 +4605,10 @@ None =item discussion_visible() +=item discussion_vote_available() + +=item get_realsymb() + =item list_discussion() =item send_feedback_link()