# The LearningOnline Network # Feedback # # $Id: lonfeedback.pm,v 1.50 2003/08/06 17:00:30 albertel Exp $ # # Copyright Michigan State University Board of Trustees # # This file is part of the LearningOnline Network with CAPA (LON-CAPA). # # LON-CAPA is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # LON-CAPA is distributed in the hope that it will be useful, # but WITHOUT ANY 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 LON-CAPA; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # /home/httpd/html/adm/gpl.txt # # http://www.lon-capa.org/ # # (Internal Server Error Handler # # (Login Screen # 5/21/99,5/22,5/25,5/26,5/31,6/2,6/10,7/12,7/14, # 1/14/00,5/29,5/30,6/1,6/29,7/1,11/9 Gerd Kortemeyer) # # 3/1/1 Gerd Kortemeyer) # # 3/1,2/3,2/5,2/6,2/8 Gerd Kortemeyer # 2/9 Guy Albertelli # 2/10 Gerd Kortemeyer # 2/13 Guy Albertelli # 7/25 Gerd Kortemeyer # 7/26 Guy Albertelli # 7/26,8/10,10/1,11/5,11/6,12/27,12/29 Gerd Kortemeyer # YEAR=2002 # 1/1,1/16 Gerd Kortemeyer # package Apache::lonfeedback; use strict; use Apache::Constants qw(:common); use Apache::lonmsg(); use Apache::loncommon(); use Apache::lontexconvert(); sub mail_screen { my ($r,$feedurl,$options) = @_; my $bodytag=&Apache::loncommon::bodytag('Resource Feedback and Discussion', '','onLoad="window.focus();"'); my $latexHelp = Apache::loncommon::helpLatexCheatsheet(); $r->print(< The LearningOnline Network with CAPA $bodytag

$feedurl

Please check at least one of the following feedback types: $options
My question/comment/feedback:

$latexHelp

Attachment (128 KB max size):

ENDDOCUMENT $r->print(&generate_preview_button().''); } sub fail_redirect { my ($r,$feedurl) = @_; $r->print (<Feedback not sent Sorry, no recipients ... ENDFAILREDIR } sub redirect_back { my ($r,$feedurl,$typestyle,$sendsomething,$sendposts,$status) = @_; $r->print (< Feedback sent $typestyle Sent $sendsomething message(s), and $sendposts post(s). $status
ENDREDIR } sub no_redirect_back { my ($r,$feedurl) = @_; $r->print (<Feedback not sent ENDNOREDIR if ($feedurl!~/^\/adm\/feedback/) { $r->print(''); } $r->print (< Sorry, no feedback possible on this resource ... ENDNOREDIRTWO } sub screen_header { my ($feedurl) = @_; my $msgoptions=''; my $discussoptions=''; if (($feedurl=~/^\/res\//) && ($feedurl!~/^\/res\/adm/)) { $msgoptions= '

Feedback to resource author'; } if (&feedback_available(1)) { $msgoptions.= '
Question about resource content'; } if (&feedback_available(0,1)) { $msgoptions.= '
'. 'Question/Comment/Feedback about course content'; } if (&feedback_available(0,0,1)) { $msgoptions.= '
'. 'Question/Comment/Feedback about course policy'; } if ($ENV{'request.course.id'}) { if (&Apache::lonnet::allowed('pch', $ENV{'request.course.id'}. ($ENV{'request.course.sec'}?'/'.$ENV{'request.course.sec'}:''))) { $discussoptions=' '. 'Contribution to course discussion of resource'; $discussoptions.='
'. 'Anonymous contribution to course discussion of resource'. ' (name only visible to course faculty)'; } } if ($msgoptions) { $msgoptions='

Sending Messages

'.$msgoptions; } if ($discussoptions) { $discussoptions='

Discussion Contributions

'.$discussoptions; } return $msgoptions.$discussoptions; } sub resource_output { my ($feedurl) = @_; my $usersaw=&Apache::lonnet::ssi_body($feedurl); $usersaw=~s/\]*\>//gi; $usersaw=~s/\<\/body\>//gi; $usersaw=~s/\//gi; $usersaw=~s/\<\/html\>//gi; $usersaw=~s/\//gi; $usersaw=~s/\<\/head\>//gi; $usersaw=~s/action\s*\=/would_be_action\=/gi; return $usersaw; } sub clear_out_html { my ($message,$override)=@_; my $cid=$ENV{'request.course.id'}; if (($ENV{"course.$cid.allow_limited_html_in_feedback"} =~ m/yes/i) || ($override)) { # allows


      • #
        my %html=(B=>1, I=>1, P=>1, A=>1, LI=>1, OL=>1, UL=>1, EM=>1, BR=>1, TT=>1, STRONG=>1, BLOCKQUOTE=>1, DIV=>1, IMG=>1); $message =~ s/\<(\/?\s*(\w+)[^\>\<]*)/ {($html{uc($2)}&&(length($1)<1000))?"\<$1":"\<$1"}/ge; $message =~ s/(\]*)\>/ {($html{uc($2)}&&(length($1)<1000))?"$1\>":"$1\>"}/ge; } else { $message=~s/\<\/*m\s*\>//g; $message=~s/\/\>\;/g; } return $message; } sub assemble_email { my ($feedurl,$message,$prevattempts,$usersaw,$useranswer)=@_; my $email=<<"ENDEMAIL"; Refers to $feedurl $message ENDEMAIL my $citations=<<"ENDCITE";

        Previous attempts of student (if applicable)

        $prevattempts


        Original screen output (if applicable)

        $usersaw

        Correct Answer(s) (if applicable)

        $useranswer ENDCITE return ($email,$citations); } sub secapply { my $rec=shift; my $defaultflag=shift; $rec=~s/\s+//g; $rec=~s/\@/\:/g; my ($adr,$sections)=($rec=~/^([^\(]+)\(([^\)]+)\)/); if ($sections) { foreach (split(/\;/,$sections)) { if (($_ eq $ENV{'request.course.sec'}) || ($defaultflag && ($_ eq '*'))) { return $adr; } } } else { return $rec; } return ''; } sub decide_receiver { my ($feedurl,$author,$question,$course,$policy,$defaultflag) = @_; my $typestyle=''; my %to=(); if ($ENV{'form.author'}||$author) { $typestyle.='Submitting as Author Feedback
        '; $feedurl=~/^\/res\/(\w+)\/(\w+)\//; $to{$2.':'.$1}=1; } if ($ENV{'form.question'}||$question) { $typestyle.='Submitting as Question
        '; foreach (split(/\,/, $ENV{'course.'.$ENV{'request.course.id'}.'.question.email'}) ) { my $rec=&secapply($_,$defaultflag); if ($rec) { $to{$rec}=1; } } } if ($ENV{'form.course'}||$course) { $typestyle.='Submitting as Comment
        '; foreach (split(/\,/, $ENV{'course.'.$ENV{'request.course.id'}.'.comment.email'}) ) { my $rec=&secapply($_,$defaultflag); if ($rec) { $to{$rec}=1; } } } if ($ENV{'form.policy'}||$policy) { $typestyle.='Submitting as Policy Feedback
        '; foreach (split(/\,/, $ENV{'course.'.$ENV{'request.course.id'}.'.policy.email'}) ) { my $rec=&secapply($_,$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); return scalar(%to); } sub send_msg { my ($feedurl,$email,$citations,$attachmenturl,%to)=@_; my $status=''; my $sendsomething=0; foreach (keys %to) { if ($_) { my $declutter=&Apache::lonnet::declutter($feedurl); unless (&Apache::lonmsg::user_normal_msg(split(/\:/,$_), 'Feedback ['.$declutter.']',$email,$citations,$feedurl, $attachmenturl)=~/ok/) { $status.='
        Error sending message to '.$_.'
        '; } 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.='
        Not registered
        '; } } return ($status,$sendsomething); } sub adddiscuss { my ($symb,$email,$anon,$attachmenturl)=@_; my $status=''; if (&Apache::lonnet::allowed('pch',$ENV{'request.course.id'}. ($ENV{'request.course.sec'}?'/'.$ENV{'request.course.sec'}:''))) { my %contrib=('message' => $email, 'sendername' => $ENV{'user.name'}, 'senderdomain' => $ENV{'user.domain'}, 'screenname' => $ENV{'environment.screenname'}, 'plainname' => $ENV{'environment.firstname'}.' '. $ENV{'environment.middlename'}.' '. $ENV{'environment.lastname'}.' '. $ENV{'enrironment.generation'}, 'attachmenturl'=> $attachmenturl); if ($anon) { $contrib{'anonymous'}='true'; } if (($symb) && ($email)) { $status='Adding to class discussion'.($anon?' (anonymous)':'').': '. &Apache::lonnet::store(\%contrib,$symb,$ENV{'request.course.id'}, $ENV{'course.'.$ENV{'request.course.id'}.'.domain'}, $ENV{'course.'.$ENV{'request.course.id'}.'.num'}); my %storenewentry=($symb => time); $status.='
        Updating discussion time: '. &Apache::lonnet::put('discussiontimes',\%storenewentry, $ENV{'course.'.$ENV{'request.course.id'}.'.domain'}, $ENV{'course.'.$ENV{'request.course.id'}.'.num'}); } my %record=&Apache::lonnet::restore('_discussion'); my ($temp)=keys %record; unless ($temp=~/^error\:/) { my %newrecord=(); $newrecord{'resource'}=$symb; $newrecord{'subnumber'}=$record{'subnumber'}+1; $status.='
        Registering: '. &Apache::lonnet::cstore(\%newrecord,'_discussion'); } } else { $status.='Failed.'; } return $status.'
        '; } # ----------------------------------------------------------- Preview function sub show_preview { my $r=shift; my $message=&clear_out_html($ENV{'form.comment'}); $message=~s/\n/\
        /g; $message=&Apache::lontexconvert::msgtexconverted($message); $r->print('
        '. $message.'
        '); } sub generate_preview_button { return(< ENDPREVIEW } sub handler { my $r = shift; if ($r->header_only) { $r->content_type('text/html'); $r->send_http_header; return OK; } # --------------------------- Get query string for limited number of parameters &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'}, ['hide','unhide','deldisc','postdata','preview']); if (($ENV{'form.hide'}) || ($ENV{'form.unhide'})) { # ----------------------------------------------------------------- Hide/unhide $r->content_type('text/html'); $r->send_http_header; my $entry=$ENV{'form.hide'}?$ENV{'form.hide'}:$ENV{'form.unhide'}; my ($symb,$idx)=split(/\:\:\:/,$entry); my ($map,$ind,$url)=split(/\_\_\_/,$symb); 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 $currenthidden=$contrib{'hidden'}; if ($ENV{'form.hide'}) { $currenthidden.='.'.$idx.'.'; } else { $currenthidden=~s/\.$idx\.//g; } my %newhash=('hidden' => $currenthidden); &Apache::lonnet::store(\%newhash,$symb,$ENV{'request.course.id'}, $ENV{'course.'.$ENV{'request.course.id'}.'.domain'}, $ENV{'course.'.$ENV{'request.course.id'}.'.num'}); &redirect_back($r,&Apache::lonnet::clutter($url), 'Changed discussion status

        ','0','0'); } elsif ($ENV{'form.deldisc'}) { # --------------------------------------------------------------- Hide for good $r->content_type('text/html'); $r->send_http_header; my $entry=$ENV{'form.deldisc'}; my ($symb,$idx)=split(/\:\:\:/,$entry); my ($map,$ind,$url)=split(/\_\_\_/,$symb); 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 $currentdeleted=$contrib{'deleted'}; $currentdeleted.='.'.$idx.'.'; my %newhash=('deleted' => $currentdeleted); &Apache::lonnet::store(\%newhash,$symb,$ENV{'request.course.id'}, $ENV{'course.'.$ENV{'request.course.id'}.'.domain'}, $ENV{'course.'.$ENV{'request.course.id'}.'.num'}); &redirect_back($r,&Apache::lonnet::clutter($url), 'Changed discussion status

        ','0','0'); } elsif ($ENV{'form.preview'}) { # -------------------------------------------------------- User wants a preview &show_preview($r); } else { # ------------------------------------------------------------- Normal feedback my $feedurl=$ENV{'form.postdata'}; $feedurl=~s/^http\:\/\///; $feedurl=~s/^$ENV{'SERVER_NAME'}//; $feedurl=~s/^$ENV{'HTTP_HOST'}//; my $symb=&Apache::lonnet::symbread($feedurl); unless ($symb) { $symb=$ENV{'form.symb'}; if ($symb) { my ($map,$id,$url)=split(/\_\_\_/,$symb); $feedurl=&Apache::lonnet::clutter($url); } } my $goahead=1; if ($feedurl=~/\.(problem|exam|quiz|assess|survey|form)$/) { unless ($symb) { $goahead=0; } } if ($goahead) { # Go ahead with feedback, no ambiguous reference $r->content_type('text/html'); $r->send_http_header; if ( ( ($feedurl=~m:^/res:) && ($feedurl!~m:^/res/adm:) ) || ($ENV{'request.course.id'} && ($feedurl!~m:^/adm:)) || ($ENV{'request.course.id'} && ($symb=~/^bulletin\_\_\_/)) ) { # --------------------------------------------------- Print login screen header unless ($ENV{'form.sendit'}) { my $options=&screen_header($feedurl); if ($options) { &mail_screen($r,$feedurl,$options); } else { &fail_redirect($r,$feedurl); } } else { # Get previous user input my $prevattempts=&Apache::loncommon::get_previous_attempt( $symb,$ENV{'user.name'},$ENV{'user.domain'}, $ENV{'request.course.id'}); # Get output from resource 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')); my $useranswer=&Apache::loncommon::get_student_answers( $symb,$ENV{'user.name'},$ENV{'user.domain'}, $ENV{'request.course.id'}); &Apache::lonnet::delenv('allowed.vgr'); # Get attachments, if any, and not too large my $attachmenturl=''; if ($ENV{'form.attachment.filename'}) { unless (length($ENV{'form.attachment'})>131072) { $attachmenturl=&Apache::lonnet::userfileupload('attachment'); } } # Filter HTML out of message (could be nasty) my $message=&clear_out_html($ENV{'form.comment'}); # Assemble email my ($email,$citations)=&assemble_email($feedurl,$message,$prevattempts, $usersaw,$useranswer); # Who gets this? my ($typestyle,%to) = &decide_receiver($feedurl); # Actually send mail my ($status,$numsent)=&send_msg($feedurl,$email,$citations, $attachmenturl,%to); # Discussion? Store that. my $numpost=0; if ($ENV{'form.discuss'}) { $typestyle.=&adddiscuss($symb,$message,0,$attachmenturl); $numpost++; } if ($ENV{'form.anondiscuss'}) { $typestyle.=&adddiscuss($symb,$message,1,$attachmenturl); $numpost++; } # Receipt screen and redirect back to where came from &redirect_back($r,$feedurl,$typestyle,$numsent,$numpost,$status); } } else { # Unable to give feedback &no_redirect_back($r,$feedurl); } } else { # Ambiguous Problem Resource $r->internal_redirect('/adm/ambiguous'); } } return OK; } 1; __END__