--- loncom/interface/lonfeedback.pm 2012/01/06 18:17:44 1.324 +++ loncom/interface/lonfeedback.pm 2012/02/20 11:28:01 1.335 @@ -1,7 +1,7 @@ # The LearningOnline Network # Feedback # -# $Id: lonfeedback.pm,v 1.324 2012/01/06 18:17:44 www Exp $ +# $Id: lonfeedback.pm,v 1.335 2012/02/20 11:28:01 goltermann Exp $ # # Copyright Michigan State University Board of Trustees # @@ -759,7 +759,7 @@ END sub discussion_link { - my ($ressymb,$linktext,$cmd,$item,$flag,$prev,$adds)=@_; + my ($ressymb,$linktext,$cmd,$item,$flag,$prev,$adds,$title)=@_; my $link='/adm/feedback?inhibitmenu=yes&modal=yes&'.$cmd.'='.&escape($ressymb).':::'.$item; if ($flag) { $link .= '&previous='.$prev; } if ($adds) { $link .= $adds; } @@ -769,7 +769,7 @@ sub discussion_link { $width=300; $height=200; } - return &Apache::loncommon::modal_link($link,$linktext,$width,$height); + return &Apache::loncommon::modal_link($link,$linktext,$width,$height,undef,undef,$title); } @@ -787,10 +787,12 @@ sub send_feedback_link { sub send_message_link { my ($ressymb) = @_; my $output = ''. - ' '.&mt('Send Feedback').''; + &discussion_link($ressymb, + ''.&mt('Send Feedback').'', + 'sendmessageonly'). + ''; return $output; } @@ -827,7 +829,17 @@ sub action_links_bar { $discussion .= '&previous='.$prevread; } $discussion .= &group_args($group); - $discussion .= '">'.&mt('Export').''; + $discussion .= '">'.&mt('Export').''; + if (&Apache::lonnet::allowed('rin',$env{'request.course.id'})) { + $discussion .= '  '; + $discussion .=''.&mt('Undelete all deleted entries').''; + } + $discussion.=''; if ($newpostsflag) { if (!$markondisp) { $discussion .=' -
+ $lt{'note'}
-$lt{'title'}: 

+$lt{'title'}: 
ENDDISCUSS if ($env{'form.origpage'}) { @@ -975,7 +987,41 @@ sub build_posting_display { ($skiptest,$roleregexp,$secregexp,$statusregexp) = &filter_regexp($rolefilter,$sectionpick,$statusfilter); $rolematch = $roleregexp.':'.$secregexp.':'.$statusregexp; - } + } +# 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'})) { + push(@theselikes,$likes{$symb.':'.$idx.':likes'}); + } + } +# 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); + } + $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; +# +# This is now the real loop. Go through all entries, pick up what we need +# for (my $id=1;$id<=$contrib{'version'};$id++) { my $idx=$id; next if ($contrib{$idx.':deleted'}); @@ -1109,19 +1155,12 @@ sub build_posting_display { @{$$namesort{$lastname}{$firstname}} = ("$idx"); } if ($outputtarget ne 'tex') { - unless ($likes{$symb.':'.$idx.':likers'}=~/\,\Q$thisuser\E\,/) { - $sender.=' '.&discussion_link($symb,&mt('Like'),'like',$idx,$$newpostsflag,$prevread,&group_args($group)); - } - unless ($likes{$symb.':'.$idx.':unlikers'}=~/\,\Q$thisuser\E\,/) { - $sender.=' '.&discussion_link($symb,&mt('Unlike'),'unlike',$idx,$$newpostsflag,$prevread,&group_args($group)); - } - my $thislikes=$likes{$symb.':'.$idx.':likes'}; - push(@theselikes,$thislikes); - if ($thislikes>0) { - $sender.=' ('.&mt("[_1] likes",$thislikes).')'; - } elsif ($thislikes<0) { - $sender.=' ('.&mt("[_1] unlikes",abs($thislikes)).')'; +# Add karma stars + my $karma=&userkarma($contrib{$idx.':sendername'},$contrib{$idx.':senderdomain'}); + for (my $i=1;$i<=$karma;$i++) { + $sender.=''.&mt('Contributor Kudos').''; } +# Can people edit this? if (&editing_allowed($escsymb.':::'.$idx,$group)) { if (($env{'user.domain'} eq $contrib{$idx.':senderdomain'}) && ($env{'user.name'} eq $contrib{$idx.':sendername'})) { $sender.=' '. @@ -1304,11 +1343,45 @@ 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"; + } +# Actually glue in the message itself $$discussionitems[$idx].= '
'. - $message.'
'; + "
". + $message. + '
'; +# Put in the like and unlike buttons + unless ($likes{$symb.':'.$idx.':likers'}=~/\,\Q$thisuser\E\,/) { + $$discussionitems[$idx].=' '.&discussion_link($symb,''.&mt('Like').'','like',$idx,$$newpostsflag,$prevread,&group_args($group),&mt("Like this posting")); + } else { + $$discussionitems[$idx].=''.&mt('You like this posting').''; + } + unless ($likes{$symb.':'.$idx.':unlikers'}=~/\,\Q$thisuser\E\,/) { + $$discussionitems[$idx].=' '.&discussion_link($symb,''.&mt('Unlike').'',,'unlike',$idx,$$newpostsflag,$prevread,&group_args($group),&mt("Unlike this posting")); + } else { + $$discussionitems[$idx].=''.&mt('You 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 there is any history to this post, inform the reader if ($contrib{$idx.':history'}) { my @postversions = (); - $$discussionitems[$idx] .= &mt('This post has been edited by the author.'); + $$discussionitems[$idx] .= '  '.&mt('This post has been edited by the author.'); if ($seeid) { $$discussionitems[$idx] .= '  '. &discussion_link($symb,&mt('Display all versions'),'allversions',$idx,$$newpostsflag,$prevread,&group_args($group)); @@ -1334,22 +1407,7 @@ sub build_posting_display { } # end of the loop over all discussion entries } -# Figure out average likes and standard deviation if there are enough discussions to warrant that - if ($#theselikes>1) { - my $sum=0; - my $num=$#theselikes+1; - foreach my $thislike (@theselikes) { - $sum.=$thislike; - } - my $ave=$sum/$num; - my $sumsq=0; - foreach my $thislike (@theselikes) { - $sumsq+=($thislike-$ave)*($thislike-$ave); - } - my $stddev=sqrt($sumsq/$num); -# &Apache::lonnet::logthis(join(',',@theselikes)." Ave $ave StdDev $stddev"); - } -# end of "if there actually are any discussion +# end of "if there actually are any discussions } # end of subroutine "build_posting_display" } @@ -2637,7 +2695,7 @@ sub no_redirect_back { 'add_entries' => \%onload,); if ($feedurl !~ m{^/adm/feedback}) { - $body_options{'rediect'} = [2,$feedurl]; + $body_options{'redirect'} = [2,$feedurl]; } my $start_page= &Apache::loncommon::start_page('Feedback not sent',undef, @@ -2679,8 +2737,8 @@ sub screen_header { unless (($env{'form.replydisc'}) || ($env{'form.editdisc'})) { if (($feedurl=~/^\/res\//) && ($feedurl!~/^\/res\/adm/) && ($env{'user.adv'})) { $msgoptions= - '

'; + '
'; } my %optionhash=(); foreach my $type ('question','comment','policy') { @@ -2688,20 +2746,20 @@ sub screen_header { } if (&feedback_available(1)) { $msgoptions.= - '

'; + '
'; } if (&feedback_available(0,1)) { $msgoptions.= - '

'; + '
'; } if (&feedback_available(0,0,1)) { $msgoptions.= - '

'; + '
'; } } if (($env{'request.course.id'}) && (!$env{'form.sendmessageonly'})) { @@ -2889,7 +2947,7 @@ sub storefeedbackpoints { my %record=('grader_user' => $env{'user.name'}, 'grader_domain' => $env{'user.domain'}, 'points' => $points); - return &Apache::lonnet::cstore(\%record,'_feedback'); + return &Apache::lonnet::cstore(\%record,'_feedback',$course,$udom,$uname); } # Store feedback "likes" @@ -2909,7 +2967,7 @@ sub storefeedbacklikes { 'likes_domain' => $env{'user.domain'}, 'likes' => $likes, 'totallikes' => $totallikes); - return &Apache::lonnet::cstore(\%newrecord,'_feedback'); + return &Apache::lonnet::cstore(\%newrecord,'_feedback',$course,$udom,$uname); } @@ -3019,6 +3077,7 @@ sub adddiscuss { $newrecord{'subnumber'}=$record{'subnumber'}+1; $status.='
'.&mt('Registering').': '. &Apache::lonnet::cstore(\%newrecord,'_discussion'); + &updatekarma(); } } else { $status.='Failed.'; @@ -3042,9 +3101,52 @@ sub getdiscussionrecords { sub getdiscussionstats { my %record=&getdiscussionrecords(@_); - return ($record{'subnumber'},$record{'points'},$record{'totallikes'}); + return ($record{'subnumber'},$record{'points'},$record{'totallikes'},$record{'totalvotes'}); +} + +# Calculate discussion karma + +sub calcdiscussionkarma { + my ($subs,$pts,$likes,$votes)=&getdiscussionstats(@_); + my $karma=0; + if ($votes>0) { + $karma=int(.1+5.*(1.-exp(-$subs/10.))*$likes/$votes); + if ($karma<0) { $karma=0; } + if ($karma>5) { $karma=5; } + } + return $karma; +} + +# Update karma + +sub updatekarma { + my ($uname,$udom,$course)=@_; + unless ($uname) { $uname=$env{'user.name'}; } + unless ($udom) { $udom=$env{'user.domain'}; } + unless ($course) { $course=$env{'request.course.id'}; } + my $karma=&calcdiscussionkarma($uname,$udom,$course); + &Apache::lonnet::cstore({ 'karma' => $karma },'_discussion',$course,$udom,$uname); + &Apache::lonnet::do_cache_new('karma',$uname.':'.$udom.':'.$course,$karma,3600); + return $karma; } +# Retrieve karma + +sub userkarma { + my ($uname,$udom,$course)=@_; + unless ($uname) { $uname=$env{'user.name'}; } + unless ($udom) { $udom=$env{'user.domain'}; } + unless ($course) { $course=$env{'request.course.id'}; } + my $hashkey=$uname.':'.$udom.':'.$course; + my ($karma,$cached)=&Apache::lonnet::is_cached_new('karma',$hashkey); + if ($cached) { + return $karma; + } + my %userdisc=&getdiscussionrecords($uname,$udom,$course); + $karma=$userdisc{'karma'}; + &Apache::lonnet::do_cache_new('karma',$hashkey,$karma,3600); + return $karma; +} # Store discussion credit @@ -3057,7 +3159,7 @@ sub storediscussionpoints { my %record=('grader_user' => $env{'user.name'}, 'grader_domain' => $env{'user.domain'}, 'points' => $points); - return &Apache::lonnet::cstore(\%record,'_discussion'); + return &Apache::lonnet::cstore(\%record,'_discussion',$course,$udom,$uname); } # Store discussion "likes" @@ -3072,12 +3174,19 @@ sub storediscussionlikes { unless ($course) { $course=$env{'request.course.id'}; } my %record=&getdiscussionrecords($uname,$udom,$course); my $totallikes=$record{'totallikes'}; + my $totalvotes=$record{'totalvotes'}; $totallikes+=$likes; + $totalvotes++; my %newrecord=('likes_user' => $env{'user.name'}, 'likes_domain' => $env{'user.domain'}, 'likes' => $likes, - 'totallikes' => $totallikes); - return &Apache::lonnet::cstore(\%newrecord,'_discussion'); + 'totallikes' => $totallikes, + 'totalvotes' => $totalvotes); + my $status=&Apache::lonnet::cstore(\%newrecord,'_discussion',$course,$udom,$uname); + if ($status eq 'ok') { + &updatekarma($uname,$udom,$course); + } + return $status; } sub get_discussion_info { @@ -3365,14 +3474,13 @@ sub generate_attachments_button { my ($idx,$attachnum,$ressymb,$now,$currnewattach,$deloldattach, $numoldver,$mode,$blockblog) = @_; my $origpage = $ENV{'REQUEST_URI'}; - my $att=$attachnum.' '.&mt("attachments"); my %lt = &Apache::lonlocal::texthash( - 'clic' => 'Click to add/remove attachments', + 'clic' => 'Add/remove attachments', ); my $response = (<
-$lt{'clic'}: print(&Apache::lonhtmlcommon::confirm_success(&mt("Failed to undelete entries"),1)); + } + $r->print("
".&mt("Return and reload").""); + } + $r->print(&Apache::loncommon::end_page()); + return OK; } else { # ------------------------------------------------------------- Normal feedback my $feedurl=$env{'form.postdata'}; @@ -4056,6 +4182,8 @@ ENDREDIR $symb=(split(/\:\:\:/,$env{'form.editdisc'}))[0]; } elsif ($env{'form.origpage'}) { $symb=""; + } elsif ($env{'form.sendmessageonly'}) { + $symb=(split(/\:\:\:/,$env{'form.sendmessageonly'}))[0]; } else { $symb=&Apache::lonnet::symbread($feedurl); }