--- loncom/interface/lonfeedback.pm 2007/01/04 20:35:15 1.239 +++ loncom/interface/lonfeedback.pm 2008/11/21 20:17:11 1.257 @@ -1,7 +1,7 @@ # The LearningOnline Network # Feedback # -# $Id: lonfeedback.pm,v 1.239 2007/01/04 20:35:15 albertel Exp $ +# $Id: lonfeedback.pm,v 1.257 2008/11/21 20:17:11 jms Exp $ # # Copyright Michigan State University Board of Trustees # @@ -91,7 +91,7 @@ sub list_discussion { if (not &discussion_visible($status)) { if ($mode ne 'board') { &Apache::lonenc::check_encrypt(\$ressymb); - return &send_message_link($ressymb); + return '
'.&send_message_link($ressymb); } } if ($group ne '' && $mode eq 'board') { @@ -637,7 +637,7 @@ END } } if ($dischash{$toggkey}) { - my $storebutton = &mt('Store read/unread changes'); + my $storebutton = &mt('Save read/unread changes'); $discussion.=''. ''."\n". ''; + $discussion.= &send_feedback_link($ressymb,$target); } } if ($outputtarget ne 'tex') { $discussion.= &send_message_link($ressymb); } - $discussion.=''; + $discussion.=''; } return $discussion; } @@ -763,7 +762,7 @@ sub send_feedback_link { sub send_message_link { my ($ressymb) = @_; - my $output = ''. + my $output = ''. ' '. $screenname; } - + if ($see_anonymous) { + $sender.=&Apache::loncommon::student_image_tag($contrib{$idx.':senderdomain'},$contrib{$idx.':sendername'}); + } # Set up for sorting by domain, then username unless (defined($$usernamesort{$contrib{$idx.':senderdomain'}})) { %{$$usernamesort{$contrib{$idx.':senderdomain'}}} = (); @@ -1236,7 +1240,7 @@ sub build_posting_display { $$newitem{$idx} = 1; $$discussionitems[$idx] .= '

- '; + '; } else { $$newitem{$idx} = 0; $$discussionitems[$idx] .= ' @@ -1468,7 +1472,7 @@ sub replicate_attachments { } sub mail_screen { - my ($r,$feedurl,$options,$caller_symb) = @_; + my ($r,$feedurl,$options,$caller_symb,$attachmaxtext) = @_; if (exists($env{'form.origpage'})) { &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},['subject','comment','currnewattach','addnewattach','deloldattach','delnewattach','timestamp','idx','anondiscuss','discuss','blog','group','ref']); } @@ -1478,7 +1482,7 @@ sub mail_screen { 'myqu' => 'My question/comment/feedback:', 'title' => 'Title', 'reta' => 'Retained attachments', - 'atta' => 'Attachment (128 KB max size)', + 'atta' => 'Attachment', ); my $restitle = &get_resource_title($caller_symb,$feedurl); my $quote=''; @@ -1717,7 +1721,7 @@ END } } else { $r->print(< +$lt{'atta'} $attachmaxtext:

END } @@ -1798,7 +1802,7 @@ sub print_display_options { 'unread' => 'New posts only', 'unmark' => 'Posts not marked read', 'ondisp' => 'Once displayed', - 'onmark' => 'Once marked not NEW ', + 'onmark' => 'Once marked not NEW', 'toggon' => 'Shown', 'toggoff' => 'Not shown', 'disa' => 'Posts displayed?', @@ -1956,6 +1960,7 @@ END
END + my $save = &mt('Save Changes'); $r->print(&Apache::loncommon::end_data_table_row()); $r->print(&Apache::loncommon::end_data_table()); $r->print(< - + END if (exists($env{'form.group'})) { $r->print(''); @@ -2055,7 +2060,7 @@ sub print_sortfilter_options { 'spgr' => 'Specific groups', 'psub' => 'Pick specific users (by name)', 'shal' => 'Show a list of current posters', - 'stor' => 'Store changes', + 'stor' => 'Save changes', ); my %sort_types = (); @@ -2386,7 +2391,7 @@ ENDFAILREDIR } sub redirect_back { - my ($r,$feedurl,$typestyle,$sendsomething,$sendposts,$blog,$status,$previous,$sort,$rolefilter,$statusfilter,$sectionpick,$grouppick,$numpicks,$group) = @_; + my ($r,$feedurl,$typestyle,$sendsomething,$sendposts,$blog,$status,$previous,$sort,$rolefilter,$statusfilter,$sectionpick,$grouppick,$numpicks,$group,$toolarge) = @_; my $sorttag = ''; my $roletag = ''; my $statustag = ''; @@ -2500,6 +2505,7 @@ $start_page $typestyle Sent $sendsomething message(s), and $sendposts post(s). $blog +$toolarge $status
$prevtag @@ -2559,26 +2565,31 @@ sub screen_header { '

'; } + my %optionhash=(); + foreach my $type ('question','comment','policy') { + $optionhash{$type}=$env{'course.'.$env{'request.course.id'}.'.'.$type.'.email.text'}; + } if (&feedback_available(1)) { $msgoptions.= '

'; + ($optionhash{'question'}?$optionhash{'question'}:&mt('Question about resource content')).'

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

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

'; } } if (($env{'request.course.id'}) && (!$env{'form.sendmessageonly'})) { - if (&discussion_open(undef,$symb) && + my ($blocked,$blocktext) = &Apache::loncommon::blocking_status('boards'); + if (!$blocked && &discussion_open(undef,$symb) && &Apache::lonnet::allowed('pch', $env{'request.course.id'}. ($env{'request.course.sec'}?'/'.$env{'request.course.sec'}:''))) { @@ -2588,10 +2599,10 @@ sub screen_header { &mt('Anonymous contribution to course discussion of resource'). ' ('.&mt('name only visible to course faculty').') '. ''.&mt('Change Screenname').''; - } - my $blockblog = &Apache::loncommon::blocking_status('blogs'); - if (!$blockblog) { - $discussoptions.= &add_blog_checkbox(); + my $blockblog = &Apache::loncommon::blocking_status('blogs'); + if (!$blockblog) { + $discussoptions.= &add_blog_checkbox(); + } } } if ($msgoptions) { $msgoptions='

'.&mt('Sending Messages').'

'.$msgoptions; } @@ -2661,142 +2672,53 @@ ENDCITE return ($email,$citations); } -sub secapply { - my $rec=shift; - my $defaultflag=shift; - $rec=~s/\s+//g; - $rec=~s/\@/\:/g; - my ($adr,$sections_or_groups)=($rec=~/^([^\(]+)\(([^\)]+)\)/); - if ($sections_or_groups) { - foreach my $item (split(/\;/,$sections_or_groups)) { - if (($item eq $env{'request.course.sec'}) || - ($defaultflag && ($item eq '*'))) { - return $adr; - } elsif ($env{'request.course.groups'}) { - my @usersgroups = split(/:/,$env{'request.course.groups'}); - if (grep(/^\Q$item\E$/,@usersgroups)) { - return $adr; - } - } - } - } else { - return $rec; - } - return ''; -} - -=pod - -=over 4 - -=item * - -decide_receiver($feedurl,$author,$question,$course,$policy,$defaultflag); - -Arguments - $feedurl - /res/ url of resource (only need if $author is true) - $author,$question,$course,$policy - all true/false parameters - if true will attempt to find the addresses of user that should receive - this type of feedback (author - feedback to author of resource $feedurl, - $question 'Resource Content Questions', $course 'Course Content Question', - $policy 'Course Policy') - (Additionally it also checks $env for whether the corresponding form. - element exists, for ease of use in a html response context) - - $defaultflag - (internal should be left blank) if true gather addresses - that aren't for a section even if I have a section - (used for reccursion internally, first we look for - addresses for our specific section then we recurse - and look for non section addresses) - -Returns - $typestyle - string of html text, describing what addresses were found - %to - a hash, which keys are addresses of users to send messages to - the keys will look like name:domain - -=cut - -sub decide_receiver { - my ($feedurl,$author,$question,$course,$policy,$defaultflag) = @_; - &Apache::lonenc::check_decrypt(\$feedurl); - my $typestyle=''; - my %to=(); - if ($env{'form.discuss'} eq 'author' ||$author) { - $typestyle.='Submitting as Author Feedback
'; - $feedurl=~ m{^/res/($LONCAPA::domain_re)/($LONCAPA::username_re)/}; - $to{$2.':'.$1}=1; - } - if ($env{'form.discuss'} eq 'question' ||$question) { - $typestyle.=&mt('Submitting as Question').'
'; - foreach my $item (split(/\,/, - $env{'course.'.$env{'request.course.id'}.'.question.email'}) - ) { - my $rec=&secapply($item,$defaultflag); - if ($rec) { $to{$rec}=1; } - } - } - if ($env{'form.discuss'} eq 'course' ||$course) { - $typestyle.=&mt('Submitting as Comment').'
'; - foreach my $item (split(/\,/, - $env{'course.'.$env{'request.course.id'}.'.comment.email'}) - ) { - my $rec=&secapply($item,$defaultflag); - if ($rec) { $to{$rec}=1; } - } - } - if ($env{'form.discuss'} eq 'policy' ||$policy) { - $typestyle.=&mt('Submitting as Policy Feedback').'
'; - foreach my $item (split(/\,/, - $env{'course.'.$env{'request.course.id'}.'.policy.email'}) - ) { - my $rec=&secapply($item,$defaultflag); - if ($rec) { $to{$rec}=1; } - } - } - if ((scalar(%to) eq '0') && (!$defaultflag)) { - ($typestyle,%to)= - &decide_receiver($feedurl,$author,$question,$course,$policy,1); - } - return ($typestyle,%to); -} sub feedback_available { my ($question,$course,$policy)=@_; - my ($typestyle,%to)=&decide_receiver('',0,$question,$course,$policy); + my ($typestyle,%to)=&Apache::lonmsg::decide_receiver('',0,$question, + $course,$policy); return scalar(%to); } sub send_msg { - my ($title,$feedurl,$email,$citations,$attachmenturl,$symb,%to)=@_; - my $status=''; - my $sendsomething=0; - my $restitle = &get_resource_title($symb,$feedurl); - if ($title=~/^Error/) { $title=&mt('Feedback').': '.$title; } - unless ($title=~/\w/) { $title=&mt('Feedback'); } - foreach my $key (keys(%to)) { - if ($key) { - unless (&Apache::lonmsg::user_normal_msg(split(/\:/,$key), - $title.' ['.$restitle.']',$email,$citations,$feedurl, - $attachmenturl,undef,undef,$symb,$restitle)=~/ok/) { - $status.='
'.&mt('Error sending message to').' '.$key.'
'; - } else { - $sendsomething++; - } + my ($title,$feedurl,$email,$citations,$attachmenturl,$symb,%to)=@_; + my $status=''; + my $sendsomething=0; + my $restitle = &get_resource_title($symb,$feedurl); + if ($title=~/^Error/) { $title=&mt('Feedback').': '.$title; } + unless ($title=~/\w/) { $title=&mt('Feedback'); } + foreach my $key (keys(%to)) { + if ($key) { + my ($user,$domain) = split(/\:/,$key,2); + if (!defined($user)) { + $status.='
'.&mt('Error sending message to [_1], no user specified.',$key); + } elsif (!defined($domain)) { + $status.='
'.&mt('Error sending message to [_1], no domain specified.',$key); + } else { + unless (&Apache::lonmsg::user_normal_msg($user,$domain, + $title.' ['.$restitle.']',$email,$citations,$feedurl, + $attachmenturl,undef,undef,$symb,$restitle)=~/ok/) { + $status.='
'.&mt('Error sending message to').' '.$key.'
'; + } else { + $sendsomething++; + } + } + } } - } + my %record=&Apache::lonnet::restore('_feedback'); my ($temp)=keys(%record); unless ($temp=~/^error\:/) { - my %newrecord=(); - $newrecord{'resource'}=$feedurl; - $newrecord{'subnumber'}=$record{'subnumber'}+1; - unless (&Apache::lonnet::cstore(\%newrecord,'_feedback') eq 'ok') { - $status.='
'.&mt('Not registered').'
'; - } + my %newrecord=(); + $newrecord{'resource'}=$feedurl; + $newrecord{'subnumber'}=$record{'subnumber'}+1; + unless (&Apache::lonnet::cstore(\%newrecord,'_feedback') eq 'ok') { + $status.='
'.&mt('Not registered').'
'; + } } - - return ($status,$sendsomething); + + return ($status,$sendsomething); } sub adddiscuss { @@ -3011,7 +2933,8 @@ ENDPREVIEW } sub modify_attachments { - my ($r,$currnewattach,$currdelold,$symb,$idx,$attachmenturls)=@_; + my ($r,$currnewattach,$currdelold,$symb,$idx,$attachmenturls, + $attachmaxtext,$toolarge)=@_; my %lt = &Apache::lonlocal::texthash( 'subj' => 'Subject', @@ -3019,7 +2942,7 @@ sub modify_attachments { 'chth' => 'Check the checkboxes for any you wish to remove.', 'thef' => 'The following attachments have been uploaded for inclusion with this posting.', 'adda' => 'Add a new attachment to this post.', - 'stch' => 'Store Changes', + 'stch' => 'Save Changes', ); my $js = < @@ -3053,10 +2976,12 @@ END $r->print(< -
NEW
'.&mt('NEW').'$lt{$disctogg}
+
+
- + + + + +
+ Subject: $subject

END if ($idx) { @@ -3082,9 +3007,15 @@ END $r->print("
"); } $r->print(< +
+ $lt{'adda'}
$attachmaxtext
@@ -3367,6 +3298,10 @@ sub handler { &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'}, ['hide','unhide','deldisc','postdata','preview','replydisc','editdisc','cmd','symb','onlyunread','allposts','onlyunmark','previous','markread','markonread','markondisp','toggoff','toggon','modifydisp','changes','navtime','navmaps','navurl','sortposts','applysort','rolefilter','statusfilter','sectionpick','groupick','posterlist','userpick','attach','origpage','currnewattach','deloldattach','keepold','allversions','export','sendmessageonly','group','ref']); my $group = $env{'form.group'}; + my %attachmax = ( + text => '(128 KB max size)', + num => 131072, + ); if ($env{'form.editdisc'}) { if (!(&editing_allowed($env{'form.editdisc'},$env{'form.group'}))) { my $symb=(split(/\:\:\:/,$env{'form.editdisc'}))[0]; @@ -3637,7 +3572,7 @@ ENDREDIR my ($symb,$feedurl)=&get_feedurl_and_clean_symb($env{'form.symb'}); if ($env{'form.cmd'} eq 'threadedon') { &Apache::lonnet::put('environment',{'threadeddiscussion' => 'on'}); - &Apache::lonnet::appenv('environment.threadeddiscussion' => 'on'); + &Apache::lonnet::appenv({'environment.threadeddiscussion' => 'on'}); } else { &Apache::lonnet::del('environment',['threadeddiscussion']); &Apache::lonnet::delenv('environment\.threadeddiscussion'); @@ -3675,13 +3610,15 @@ ENDREDIR &Apache::loncommon::content_type($r,'text/html'); $r->send_http_header; &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},['subject','comment','addnewattach','delnewattach','timestamp','numoldver','idx','discuss','blog']); - my (@currnewattach,@currdelold,@keepold); + my (@currnewattach,@currdelold,@keepold,$toolarge); &process_attachments(\@currnewattach,\@currdelold,\@keepold); if (exists($env{'form.addnewattach.filename'})) { - unless (length($env{'form.addnewattach'})>131072) { + if (length($env{'form.addnewattach'})<=$attachmax{'num'}) { my $subdir = 'feedback/'.$env{'form.timestamp'}; my $newattachment=&Apache::lonnet::userfileupload('addnewattach',undef,$subdir); push(@currnewattach, $newattachment); + } else { + $toolarge = '

'.&mt('Attachment not included - exceeded permitted length').'

'; } } my $attachmenturls; @@ -3694,7 +3631,7 @@ ENDREDIR $attachmenturls = $contrib{$idx.':attachmenturl'}; } &modify_attachments($r,\@currnewattach,\@currdelold,$symb,$idx, - $attachmenturls); + $attachmenturls,$attachmax{'text'},$toolarge); return OK; } elsif ($env{'form.export'}) { &Apache::loncommon::content_type($r,'text/html'); @@ -3783,7 +3720,7 @@ ENDREDIR } my $options=&screen_header($feedurl,$symb); if ($options) { - &mail_screen($r,$feedurl,$options,$symb); + &mail_screen($r,$feedurl,$options,$symb,$attachmax{'text'}); } else { &fail_redirect($r,$feedurl); } @@ -3800,7 +3737,7 @@ ENDREDIR my $usersaw=&resource_output($feedurl); # Get resource answer (need to allow student to view grades for this to work) - &Apache::lonnet::appenv(('allowed.vgr'=>'F')); + &Apache::lonnet::appenv({'allowed.vgr'=>'F'}); my $usersymb = &Apache::lonenc::check_encrypt($symb); my $useranswer= &Apache::loncommon::get_student_answers( @@ -3809,6 +3746,7 @@ ENDREDIR &Apache::lonnet::delenv('allowed.vgr'); # Get attachments, if any, and not too large my $attachmenturl=''; + my $toolarge=''; if (($env{'form.origpage'}) || ($env{'form.editdisc'}) || ($env{'form.replydisc'})) { my ($symb,$idx); @@ -3827,9 +3765,13 @@ ENDREDIR $symb=~s|(bulletin___\d+___)adm/wrapper/|$1|; $attachmenturl=&construct_attachmenturl(\@currnewattach,\@keepold,$symb,$idx); } elsif ($env{'form.attachment.filename'}) { - unless (length($env{'form.attachment'})>131072) { - $attachmenturl=&Apache::lonnet::userfileupload('attachment',undef,'feedback'); - } + if (length($env{'form.attachment'})<=$attachmax{'num'}) { + my $now = time; + my $subdir = 'feedback/'.$now; + $attachmenturl=&Apache::lonnet::userfileupload('attachment',undef,$subdir); + } else { + $toolarge = '

'.&mt('Attachment not included - exceeded permitted length').'

'; + } } # Filter HTML out of message (could be nasty) my $message=&clear_out_html($env{'form.comment'}); @@ -3839,13 +3781,13 @@ ENDREDIR $usersaw,$useranswer); # Who gets this? - my ($typestyle,%to) = &decide_receiver($feedurl); + my ($typestyle,%to) = &Apache::lonmsg::decide_receiver($feedurl); # Actually send mail my ($status,$numsent)=&send_msg(&clear_out_html($env{'form.subject'}, undef,1), $feedurl,$email,$citations, - $attachmenturl,$symb,%to); + $attachmenturl,$usersymb,%to); # Discussion? Store that. my $numpost=0; @@ -3872,7 +3814,7 @@ ENDREDIR } # Receipt screen and redirect back to where came from - &redirect_back($r,$feedurl,$typestyle,$numsent,$numpost,$blog,$status,$env{'form.previous'},undef,undef,undef,undef,undef,undef,$group); + &redirect_back($r,$feedurl,$typestyle,$numsent,$numpost,$blog,$status,$env{'form.previous'},undef,undef,undef,undef,undef,undef,$group,$toolarge); } return OK; } @@ -4017,3 +3959,131 @@ sub get_resource_title { 1; __END__ + + +=pod + +=head1 NAME + +Apache::lonfeedback.pm + +=head1 SYNOPSIS + +Handles feedback from students to instructors and system administrators. + +Provides a screenshot of the current resource, as well as previous attempts if the resource was a homework. + +Used by lonmsg.pm. + +This is part of the LearningOnline Network with CAPA project +described at http://www.lon-capa.org. + +=head1 OVERVIEW + +None + +=head1 SUBROUTINES + +=over + +=item discussion_open() + +=item discussion_visible() + +=item list_discussion() + +=item send_feedback_link() + +=item send_message_link() + +=item action_links_bar() + +=item postingform_display() + +=item build_posting_display + +=item filter_regexp() + +=item get_post_contents() + +=item replicate_attachments() + +=item mail_screen() + +=item print_display_options() + +=item print_sortfilter_options() + +=item print_showposters() + +=item get_post_versions() + +=item get_post_attachments() + +=item fail_redirect() + +=item redirect_back() + +=item no_redirect_back() + +=item screen_header() + +=item resource_output() + +=item clear_out_html() + +=item assemble_email() + +=item feedback_available() + +=item send_msg() + +=item adddiscuss() + +=item get_discussion_info() + +=item show_preview() + +=item newline_to_br() + +=item generate_preview_button() + +=item modify_attachments() + +=item process_attachments() + +=item generate_attachments_button() + +=item extract_attachments() + +=item construct_attachmenturl() + +=item add_blog_checkbox() + +=item has_discussion() + +=item sort_filter_names() + +=item handler() + +=item blocked_reply_or_edit() + +=item wrap_symb() + +=item dewrapper() + +=item get_feedurl() + +=item get_feedurl_and_clean_symb() + +=item editing_allowed() + +=item check_group_priv() + +=item group_args() + +=item get_resource_title() + +=back + +=cut \ No newline at end of file