--- loncom/homework/grades.pm	2010/12/20 04:23:43	1.596.2.2
+++ loncom/homework/grades.pm	2011/10/10 18:27:34	1.596.2.5
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # The LON-CAPA Grading handler
 #
-# $Id: grades.pm,v 1.596.2.2 2010/12/20 04:23:43 raeburn Exp $
+# $Id: grades.pm,v 1.596.2.5 2011/10/10 18:27:34 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -40,9 +40,10 @@ use Apache::lonhomework;
 use Apache::lonpickcode;
 use Apache::loncoursedata;
 use Apache::lonmsg();
-use Apache::Constants qw(:common);
+use Apache::Constants qw(:common :http);
 use Apache::lonlocal;
 use Apache::lonenc;
+use Apache::bridgetask();
 use String::Similarity;
 use LONCAPA;
 
@@ -127,7 +128,7 @@ sub get_symb {
     my $symb=($env{'form.symb'} ne '' ? $env{'form.symb'} : (&Apache::lonnet::symbread($url)));
     if ($symb eq '') { 
 	if (!$silent) {
-	    $request->print("Unable to handle ambiguous references:$url:.");
+            $request->print(&mt("Unable to handle ambiguous references: [_1].",$url));
 	    return ();
 	}
     }
@@ -436,7 +437,7 @@ sub cleanRecord {
 	return '<blockquote><table border="1">'.
 	    '<tr valign="top"><td>'.&mt('Answer').'</td>'.$toprow.'</tr>'.
 	    '<tr valign="top"><td>'.$grayFont.&mt('Option ID').'</span></td>'.
-	    $grayFont.$bottomrow.'</tr>'.'</table></blockquote>';
+	    $bottomrow.'</tr></table></blockquote>';
     } elsif ($response eq 'essay') {
 	if (! exists ($env{'form.'.$symb})) {
 	    my (%keyhash) = &Apache::lonnet::dump('nohist_handgrade',
@@ -745,7 +746,7 @@ sub most_similar {
 
 # ignore empty submissions (occuring when only files are sent)
 
-    unless ($uessay=~/\w+/) { return ''; }
+    unless ($uessay=~/\w+/s) { return ''; }
 
 # these will be returned. Do not care if not at least 50 percent similar
     my $limit=0.6;
@@ -1488,13 +1489,29 @@ INNERJS
 
     my $docopen=&Apache::lonhtmlcommon::javascript_docopen();
     $docopen=~s/^document\.//;
-    my $alertmsg = &mt('Please select a word or group of words from document and then click this link.');
+    my %lt = &Apache::lonlocal::texthash(
+                keyw => 'Keywords list, separated by a space. Add/delete to list if desired.',
+                plse => 'Please select a word or group of words from document and then click this link.',
+                adds => 'Add selection to keyword list? Edit if desired.',
+                comp => 'Compose Message for: ',
+                incl => 'Include',
+                type => 'Type',
+                subj => 'Subject',
+                mesa => 'Message',
+                new  => 'New',
+                save => 'Save',
+                canc => 'Cancel',
+                kehi => 'Keyword Highlight Options',
+                txtc => 'Text Color',
+                font => 'Font Size',
+                fnst => 'Font Style',
+             );
     $request->print(<<SUBJAVASCRIPT);
 <script type="text/javascript" language="javascript">
 
 //===================== Show list of keywords ====================
   function keywords(formname) {
-    var nret = prompt("Keywords list, separated by a space. Add/delete to list if desired.",formname.keywords.value);
+    var nret = prompt("$lt{'keyw'}",formname.keywords.value);
     if (nret==null) return;
     formname.keywords.value = nret;
 
@@ -1521,10 +1538,10 @@ INNERJS
     else return;
     var cleantxt = txt.replace(new RegExp('([\\f\\n\\r\\t\\v ])+', 'g')," ");
     if (cleantxt=="") {
-	alert("$alertmsg");
+	alert("$lt{'plse'}");
 	return;
     }
-    var nret = prompt("Add selection to keyword list? Edit if desired.",cleantxt);
+    var nret = prompt("$lt{'adds'}",cleantxt);
     if (nret==null) return;
     document.SCORE.keywords.value = document.SCORE.keywords.value+" "+nret;
     if (document.SCORE.keywords.value != "") {
@@ -1598,7 +1615,7 @@ INNERJS
     var ypos = (screen.height-height)/2-30;
     ypos = (ypos < 0) ? '0' : ypos;
 
-    pWin = window.open('', 'MessageCenter', 'resizable=yes,toolbar=no,location=no,scrollbars='+scrollbar+',screenx='+xpos+',screeny='+ypos+',width=600,height='+height);
+    pWin = window.open('', 'MessageCenter', 'resizable=yes,toolbar=no,location=no,scrollbars='+scrollbar+',screenx='+xpos+',screeny='+ypos+',width=700,height='+height);
     pWin.focus();
     pDoc = pWin.document;
     pDoc.$docopen;
@@ -1606,16 +1623,16 @@ INNERJS
 
     pDoc.write("<form action=\\"inactive\\" name=\\"msgcenter\\">");
     pDoc.write("<input value=\\""+usrctr+"\\" name=\\"usrctr\\" type=\\"hidden\\">");
-    pDoc.write("<h3><span class=\\"LC_info\\">&nbsp;Compose Message for \"+fullname+\"<\\/span><\\/h3><br /><br />");
+    pDoc.write("<h3><span class=\\"LC_info\\">&nbsp;$lt{'comp'}\"+fullname+\"<\\/span><\\/h3><br /><br />");
 
     pDoc.write('<table border="0" width="100%"><tr><td bgcolor="#777777">');
     pDoc.write('<table border="0" width="100%"><tr bgcolor="#DDFFFF">');
-    pDoc.write("<td><b>Type<\\/b><\\/td><td><b>Include<\\/b><\\/td><td><b>Message<\\/td><\\/tr>");
+    pDoc.write("<td><b>$lt{'type'}<\\/b><\\/td><td><b>$lt{'incl'}<\\/b><\\/td><td><b>$lt{'mesa'}<\\/td><\\/tr>");
 }
     function displaySubject(msg,shwsel) {
     pDoc = pWin.document;
     pDoc.write("<tr bgcolor=\\"#ffffdd\\">");
-    pDoc.write("<td>Subject<\\/td>");
+    pDoc.write("<td>$lt{'subj'}<\\/td>");
     pDoc.write("<td align=\\"center\\"><input name=\\"subchk\\" type=\\"checkbox\\"" +shwsel+"><\\/td>");
     pDoc.write("<td><input name=\\"msgsub\\" type=\\"text\\" value=\\""+msg+"\\"size=\\"60\\" maxlength=\\"80\\"><\\/td><\\/tr>");
 }
@@ -1631,7 +1648,7 @@ INNERJS
   function newMsg(newmsg,shwsel) {
     pDoc = pWin.document;
     pDoc.write("<tr bgcolor=\\"#ffffdd\\">");
-    pDoc.write("<td align=\\"center\\">New<\\/td>");
+    pDoc.write("<td align=\\"center\\">$lt{'new'}<\\/td>");
     pDoc.write("<td align=\\"center\\"><input name=\\"newmsgchk\\" type=\\"checkbox\\"" +shwsel+"><\\/td>");
     pDoc.write("<td><textarea name=\\"newmsg\\" cols=\\"60\\" rows=\\"3\\" onchange=\\"javascript:this.form.newmsgchk.checked=true\\" >"+newmsg+"<\\/textarea><\\/td><\\/tr>");
 }
@@ -1640,8 +1657,8 @@ INNERJS
     pDoc = pWin.document;
     pDoc.write("<\\/table>");
     pDoc.write("<\\/td><\\/tr><\\/table>&nbsp;");
-    pDoc.write("<input type=\\"button\\" value=\\"Save\\" onclick=\\"javascript:checkInput()\\">&nbsp;&nbsp;");
-    pDoc.write("<input type=\\"button\\" value=\\"Cancel\\" onclick=\\"self.close()\\"><br /><br />");
+    pDoc.write("<input type=\\"button\\" value=\\"$lt{'save'}\\" onclick=\\"javascript:checkInput()\\">&nbsp;&nbsp;");
+    pDoc.write("<input type=\\"button\\" value=\\"$lt{'canc'}\\" onclick=\\"self.close()\\"><br /><br />");
     pDoc.write("<\\/form>");
     pDoc.write('$end_page_msg_central');
     pDoc.close();
@@ -1691,11 +1708,11 @@ INNERJS
     hDoc.$docopen;
     hDoc.write('$start_page_highlight_central');
     hDoc.write("<form action=\\"inactive\\" name=\\"hlCenter\\">");
-    hDoc.write("<h3><span class=\\"LC_info\\">&nbsp;Keyword Highlight Options<\\/span><\\/h3><br /><br />");
+    hDoc.write("<h3><span class=\\"LC_info\\">&nbsp;$lt{'kehi'}<\\/span><\\/h3><br /><br />");
 
     hDoc.write('<table border="0" width="100%"><tr><td bgcolor="#777777">');
     hDoc.write('<table border="0" width="100%"><tr bgcolor="#DDFFFF">');
-    hDoc.write("<td><b>Text Color<\\/b><\\/td><td><b>Font Size<\\/b><\\/td><td><b>Font Style<\\/td><\\/tr>");
+    hDoc.write("<td><b>$lt{'txtc'}<\\/b><\\/td><td><b>$lt{'font'}<\\/b><\\/td><td><b>$lt{'fnst'}<\\/td><\\/tr>");
   }
 
   function highlightbody(clrval,clrtxt,clrsel,szval,sztxt,szsel,syval,sytxt,sysel) { 
@@ -1714,8 +1731,8 @@ INNERJS
     var hDoc = hwdWin.document;
     hDoc.write("<\\/table>");
     hDoc.write("<\\/td><\\/tr><\\/table>&nbsp;");
-    hDoc.write("<input type=\\"button\\" value=\\"Save\\" onclick=\\"javascript:updateChoice(1)\\">&nbsp;&nbsp;");
-    hDoc.write("<input type=\\"button\\" value=\\"Cancel\\" onclick=\\"self.close()\\"><br /><br />");
+    hDoc.write("<input type=\\"button\\" value=\\"$lt{'save'}\\" onclick=\\"javascript:updateChoice(1)\\">&nbsp;&nbsp;");
+    hDoc.write("<input type=\\"button\\" value=\\"$lt{'canc'}\\" onclick=\\"self.close()\\"><br /><br />");
     hDoc.write("<\\/form>");
     hDoc.write('$end_page_highlight_central');
     hDoc.close();
@@ -1832,7 +1849,7 @@ sub handback_box {
     my ($symb,$uname,$udom,$counter,$partid,$record,$res_error) = @_;
     my ($partlist,$handgrade,$responseType) = &response_type($symb,$res_error);
     my (@respids);
-     my @part_response_id = &flatten_responseType($responseType);
+    my @part_response_id = &flatten_responseType($responseType);
     foreach my $part_response_id (@part_response_id) {
     	my ($part,$resp) = @{ $part_response_id };
         if ($part eq $partid) {
@@ -1844,9 +1861,10 @@ sub handback_box {
 	my $prefix = $counter.'_'.$partid.'_'.$respid.'_';
 	my $files=&get_submitted_files($udom,$uname,$partid,$respid,$record);
 	next if (!@$files);
-	my $file_counter = 1;
+	my $file_counter = 0;
 	foreach my $file (@$files) {
 	    if ($file =~ /\/portfolio\//) {
+                $file_counter++;
     	        my ($file_path, $file_disp) = ($file =~ m|(.+/)(.+)$|);
     	        my ($name,$version,$ext) = &file_name_version_ext($file_disp);
     	        $file_disp = "$name.$ext";
@@ -1854,11 +1872,14 @@ sub handback_box {
     	        $result.=&mt('Return commented version of [_1] to student.',
     			 '<span class="LC_filename">'.$file_disp.'</span>');
     	        $result.='<input type="file"   name="'.$prefix.'returndoc'.$file_counter.'" />'."\n";
-    	        $result.='<input type="hidden" name="'.$prefix.'origdoc'.$file_counter.'" value="'.$file.'" /><br />';
-    	        $result.='('.&mt('File will be uploaded when you click on Save &amp; Next below.').')<br />';
-    	        $file_counter++;
+    	        $result.='<input type="hidden" name="'.$prefix.'origdoc'.$file_counter.'" value="'.$file.'" /><br />'."\n";
 	    }
 	}
+        if ($file_counter) {
+            $result .= '<input type="hidden" name="'.$prefix.'countreturndoc" value="'.$file_counter.'" />'."\n".
+                       '<span class="LC_info">'.
+                       '('.&mt('File(s) will be uploaded when you click on Save &amp; Next below.',$file_counter).')</span><br /><br />';
+        }
     }
     return $result;    
 }
@@ -2080,15 +2101,22 @@ sub submission {
 	$request->print($prnmsg);
 
 	if ($env{'form.handgrade'} eq 'yes' && $env{'form.showgrading'} eq 'yes') {
+
+            my %lt = &Apache::lonlocal::texthash(
+                          keyw => 'Keyword Options',
+                          list => 'List',
+                          past => 'Paste Selection to List',
+                          high => 'Hightlight Attribute',
+                     );
 #
 # Print out the keyword options line
 #
 	    $request->print(<<KEYWORDS);
-&nbsp;<b>Keyword Options:</b>&nbsp;
-<a href="javascript:keywords(document.SCORE);" target="_self">List</a>&nbsp; &nbsp;
+&nbsp;<b>$lt{'keyw'}:</b>&nbsp;
+<a href="javascript:keywords(document.SCORE);" target="_self">$lt{'list'}</a>&nbsp; &nbsp;
 <a href="#" onmousedown="javascript:getSel(); return false"
- CLASS="page">Paste Selection to List</a>&nbsp; &nbsp;
-<a href="javascript:kwhighlight();" target="_self">Highlight Attribute</a><br /><br />
+ CLASS="page">$lt{'past'}</a>&nbsp; &nbsp;
+<a href="javascript:kwhighlight();" target="_self">$lt{'high'}</a><br /><br />
 KEYWORDS
 #
 # Load the other essays for similarity check
@@ -2215,7 +2243,7 @@ KEYWORDS
                     $lastsubonly.="\n".'<div class="LC_grade_submission_part">'.
                         '<b>'.&mt('Part: [_1]',$display_part).'</b>'.
                         ' <span class="LC_internal_info">'.
-                        '('.&mt('Part ID: [_1]',$respid).')</b>'.
+                        '('.&mt('Response ID: [_1]',$respid).')'.
                         '</span>&nbsp; &nbsp;'.
 			'<span class="LC_warning">'.&mt('Nothing submitted - no attempts.').'</span><br /><br /></div>';
 		    next;
@@ -2267,8 +2295,8 @@ KEYWORDS
                         $lastsubonly.='<div class="LC_grade_submission_part">'.
                             '<b>'.&mt('Part: [_1]',$display_part).'</b>'.
                             ' <span class="LC_internal_info">'.
-                            '('.&mt('Part ID: [_1]',$respid).')'.
-                            '</b></span>&nbsp; &nbsp;';
+                            '('.&mt('Response ID: [_1]',$respid).')'.
+                            '</span>&nbsp; &nbsp;';
 			my $files=&get_submitted_files($udom,$uname,$partid,$respid,\%record);
 			if (@$files) {
                             if ($hide eq 'anon') {
@@ -2437,7 +2465,7 @@ sub check_collaborators {
 	next if ($record->{'resource.'.$part.'.collaborators'} eq '');
 	my (@good_collaborators, @bad_collaborators);
 	foreach my $possible_collaborator
-	    (split(/,?\s+/,$record->{'resource.'.$part.'.collaborators'})) { 
+	    (split(/[,;\s]+/,$record->{'resource.'.$part.'.collaborators'})) { 
 	    $possible_collaborator =~ s/[\$\^\(\)]//g;
 	    next if ($possible_collaborator eq '');
 	    my ($co_name,$co_dom) = split(/\@|:/,$possible_collaborator);
@@ -2453,13 +2481,13 @@ sub check_collaborators {
 	    }
 	}
 	if (scalar(@good_collaborators) != 0) {
-	    $result.='<br />'.&mt('Collaborators: ');
+	    $result.='<br />'.&mt('Collaborators: ').'<ol>';
 	    foreach my $name (@good_collaborators) {
 		my ($lastname,$givenn) = split(/,/,$$fullname{$name});
 		push(@col_fullnames, $givenn.' '.$lastname);
-		$result.=$fullname->{$name}.'&nbsp; &nbsp; &nbsp;';
+		$result.='<li>'.$fullname->{$name}.'</li>';
 	    }
-	    $result.='<br />'."\n";
+	    $result.='</ol><br />'."\n";
 	    my ($part)=split(/\./,$part);
 	    $result.='<input type="hidden" name="collaborator'.$counter.
 		'" value="'.$part.':'.(join ':',@good_collaborators).'" />'.
@@ -2505,12 +2533,11 @@ sub get_last_submission {
                     ($lasthash{$key} eq 'randomizetry')) {
                     my ($ign,@parts) = split(/\./,$key);
                     pop(@parts);
+                    my $id = join('.',@parts);
                     if ($lasthash{$key} eq 'randomizetry') {
-                        my $id = join(',',@parts);
                         $randombytry{$ign.'.'.$id} = $lasthash{$key};
                     } else {
                         unless ($showsurv) {
-                            my $id = join(',',@parts);
                             $typeparts{$ign.'.'.$id} = $lasthash{$key};
                         }
                     }
@@ -2619,7 +2646,7 @@ sub processHandGrade {
                                                      undef,undef,$showsymb,
                                                      $restitle);
 		$request->print('<br />'.&mt('Sending message to [_1]',$uname.':'.$udom).': '.
-				$msgstatus);
+				$msgstatus.'<br />');
 	    }
 	    if ($env{'form.collaborator'.$ctr}) {
 		my @collabstrs=&Apache::loncommon::get_env_multiple("form.collaborator$ctr");
@@ -2805,7 +2832,7 @@ sub processHandGrade {
     }
     if ($total < 0) {
 	my $the_end = '<h3><span class="LC_info">'.&mt('LON-CAPA User Message').'</span></h3><br />'."\n";
-	$the_end.=&mt('<b>Message: </b> No more students for this section or class.').'<br /><br />'."\n";
+	$the_end.='<p>'.&mt('[_1]Message:[_2] No more students for this section or class.','<b>','</b>').'</p>'."\n";
 	$the_end.=&mt('Click on the button below to return to the grading menu.').'<br /><br />'."\n";
 	$the_end.=&show_grading_menu_form($symb);
 	$request->print($the_end);
@@ -2958,18 +2985,19 @@ sub handback_files {
         $request->print('<br />'.&navmap_errormsg().'<br />');
         return;
     }
+    my @handedback;
+    my $file_msg;
     my @part_response_id = &flatten_responseType($responseType);
     foreach my $part_response_id (@part_response_id) {
     	my ($part_id,$resp_id) = @{ $part_response_id };
 	my $part_resp = join('_',@{ $part_response_id });
-            if (($env{'form.'.$newflg.'_'.$part_resp.'_returndoc1'}) && ($new_part == $part_id)) {
+        if (($env{'form.'.$newflg.'_'.$part_resp.'_countreturndoc'} =~ /^\d+$/) & ($new_part eq $part_id)) {
+            for (my $counter=1; $counter<=$env{'form.'.$newflg.'_'.$part_resp.'_countreturndoc'}; $counter++) {
                 # if multiple files are uploaded names will be 'returndoc2','returndoc3'
-                my $file_counter = 1;
-		my $file_msg;
-                while ($env{'form.'.$newflg.'_'.$part_resp.'_returndoc'.$file_counter}) {
-                    my $fname=$env{'form.'.$newflg.'_'.$part_resp.'_returndoc'.$file_counter.'.filename'};
+		if ($env{'form.'.$newflg.'_'.$part_resp.'_returndoc'.$counter}) {
+                    my $fname=$env{'form.'.$newflg.'_'.$part_resp.'_returndoc'.$counter.'.filename'};
                     my ($directory,$answer_file) = 
-                        ($env{'form.'.$newflg.'_'.$part_resp.'_origdoc'.$file_counter} =~ /^(.*?)([^\/]*)$/);
+                        ($env{'form.'.$newflg.'_'.$part_resp.'_origdoc'.$counter} =~ /^(.*?)([^\/]*)$/);
                     my ($answer_name,$answer_ver,$answer_ext) =
 		        &file_name_version_ext($answer_file);
 		    my ($portfolio_path) = ($directory =~ /^.+$stuname\/portfolio(.*)/);
@@ -2979,43 +3007,55 @@ sub handback_files {
                     # fix file name
                     my ($save_file_name) = (($directory.$answer_name.".$version.".$answer_ext) =~ /^.+\/${stuname}\/(.*)/);
                     my $result=&Apache::lonnet::finishuserfileupload($stuname,$domain,
-            	                                $newflg.'_'.$part_resp.'_returndoc'.$file_counter,
+            	                                $newflg.'_'.$part_resp.'_returndoc'.$counter,
             	                                $save_file_name);
                     if ($result !~ m|^/uploaded/|) {
                         $request->print('<br /><span class="LC_error">'.
                             &mt('An error occurred ([_1]) while trying to upload [_2].',
-                                $result,$newflg.'_'.$part_resp.'_returndoc'.$file_counter).
+                                $result,$newflg.'_'.$part_resp.'_returndoc'.$counter).
                                         '</span>');
                     } else {
                         # mark the file as read only
-                        my @files = ($save_file_name);
-                        my @what = ($symb,$env{'request.course.id'},'handback');
-                        &Apache::lonnet::mark_as_readonly($domain,$stuname,\@files,\@what);
+                        push(@handedback,$save_file_name);
 			if (exists($$newrecord{"resource.$new_part.$resp_id.handback"})) {
 			    $$newrecord{"resource.$new_part.$resp_id.handback"}.=',';
 			}
                         $$newrecord{"resource.$new_part.$resp_id.handback"} .= $save_file_name;
-			$file_msg.= "\n".'<br /><span class="LC_filename"><a href="/uploaded/'."$domain/$stuname/".$save_file_name.'">'.$save_file_name."</a></span><br />";
+			$file_msg.='<span class="LC_filename"><a href="/uploaded/'."$domain/$stuname/".$save_file_name.'">'.$save_file_name."</a></span> <br />";
 
                     }
-                    $request->print("<br />".$fname." will be the uploaded file name");
-                    $request->print(" ".$env{'form.'.$newflg.'_'.$part_resp.'_origdoc'.$file_counter});
-                    $file_counter++;
+                    $request->print('<br />'.&mt('[_1] will be the uploaded file name [_2]','<span class="LC_info">'.$fname.'</span>','<span class="LC_filename">'.$env{'form.'.$newflg.'_'.$part_resp.'_origdoc'.$counter}.'</span>'));
                 }
-		my $subject = "File Handed Back by Instructor ";
-		my $message = "A file has been returned that was originally submitted in reponse to: <br />";
-		$message .= "<strong>".&Apache::lonnet::gettitle($symb)."</strong><br />";
-		$message .= ' The returned file(s) are named: '. $file_msg;
-		$message .= " and can be found in your portfolio space.";
-		my ($feedurl,$showsymb) = 
-		    &get_feedurl_and_symb($symb,$domain,$stuname);
-                my $restitle = &Apache::lonnet::gettitle($symb);
-		my $msgstatus = 
-                   &Apache::lonmsg::user_normal_msg($stuname,$domain,$subject.
-			 ' (File Returned) ['.$restitle.']',$message,undef,
-                         $feedurl,undef,undef,undef,$showsymb,$restitle);
             }
         }
+    }
+    if (@handedback > 0) {
+        $request->print('<br />');
+        my @what = ($symb,$env{'request.course.id'},'handback');
+        &Apache::lonnet::mark_as_readonly($domain,$stuname,\@handedback,\@what);
+        my $user_lh = &Apache::loncommon::user_lang($stuname,$domain,$env{'request.course.id'});
+        my ($subject,$message);
+        if (scalar(@handedback) == 1) {
+            $subject = &mt_user($user_lh,'File Handed Back by Instructor');
+        } else {
+            $subject = &mt_user($user_lh,'Files Handed Back by Instructor');
+            $message = &mt_user($user_lh,'Files have been returned that were originally submitted in response to: ');
+        }
+        $message .= "<p><strong>".&Apache::lonnet::gettitle($symb)." </strong></p>";
+        $message .= &mt_user($user_lh,'The returned file(s) are named: [_1]',"<br />$file_msg <br />").
+                    &mt_user($user_lh,'The file(s) can be found in your [_1]portfolio[_2].','<a href="/adm/portfolio">','</a>');
+        my ($feedurl,$showsymb) =
+            &get_feedurl_and_symb($symb,$domain,$stuname);
+        my $restitle = &Apache::lonnet::gettitle($symb);
+        $subject .= ' '.&mt_user($user_lh,'(File Returned)').' ['.$restitle.']';
+        my $msgstatus =
+             &Apache::lonmsg::user_normal_msg($stuname,$domain,$subject,
+                 $message,undef,$feedurl,undef,undef,undef,$showsymb,
+                 $restitle);
+        if ($msgstatus) {
+            $request->print(&mt('Notification message status: [_1]','<span class="LC_info">'.$msgstatus.'</span>').'<br />');
+        }
+    }
     return;
 }
 
@@ -4044,7 +4084,7 @@ sub upcsvScores_form {
     $result.='<table width="100%" border="0"><tr bgcolor="#e6ffff"><td>'."\n";
     $result.='&nbsp;<b>'.&mt('Specify a file containing the class scores for current resource.').
 	'</b></td></tr>'."\n";
-    $result.='<tr bgcolor=#ffffe6><td>'."\n";
+    $result.='<tr bgcolor="#ffffe6"><td>'."\n";
     my $upload=&mt("Upload Scores");
     my $upfile_select=&Apache::loncommon::upfile_select_html();
     my $ignore=&mt('Ignore First Line');
@@ -4192,6 +4232,7 @@ sub csvuploadassign {
     my ($classlist) = &getclasslist('all',0);
     my @notallowed;
     my @skipped;
+    my @warnings;
     my $countdone=0;
     foreach my $grade (@gradedata) {
 	my %entries=&Apache::loncommon::record_sep($grade);
@@ -4240,6 +4281,9 @@ sub csvuploadassign {
                     my $pcr=$entries{$fields{$dest}} / $wgt;
                     my $award=($pcr == 0) ? 'incorrect_by_override'
                                           : 'correct_by_override';
+                    if ($pcr>1) {
+                        push(@warnings,&mt("[_1]: point value larger than weight","$username:$domain"));
+                    }
                     $grades{"resource.$part.awarded"}=$pcr;
                     $grades{"resource.$part.solved"}=$award;
                     $points{$part}=1;
@@ -4268,6 +4312,11 @@ sub csvuploadassign {
 					   $domain,$username);
 	   if ($result eq 'ok') {
 	      $request->print('.');
+# Remove from grading queue
+              &Apache::bridgetask::remove_from_queue('gradingqueue',$symb,
+                                             $env{'course.'.$env{'request.course.id'}.'.domain'},
+                                             $env{'course.'.$env{'request.course.id'}.'.num'},
+                                             $domain,$username);
 	   } else {
 	      $request->print("<p><span class=\"LC_error\">".
                               &mt("Failed to save data for student [_1]. Message when trying to save was: [_2]",
@@ -4278,6 +4327,10 @@ sub csvuploadassign {
         }
     }
     $request->print('<br />'.&Apache::lonhtmlcommon::confirm_success(&mt("Saved scores for [quant,_1,student]",$countdone),$countdone==0));
+    if (@warnings) {
+        $request->print('<br />'.&Apache::lonhtmlcommon::confirm_success(&mt('Warnings generated for the following saved scores:'),1).'<br />');
+        $request->print(join(', ',@warnings));
+    }
     if (@skipped) {
 	$request->print('<br />'.&Apache::lonhtmlcommon::confirm_success(&mt('No scores stored for the following username(s):'),1).'<br />');
         $request->print(join(', ',@skipped));
@@ -4683,7 +4736,7 @@ sub displaySubByDates {
                     $displaySub[0].='<span class="LC_nobreak"';
                     $displaySub[0].='<b>'.&mt('Part: [_1]',$display_part).'</b>'
                                    .' <span class="LC_internal_info">'
-                                   .'('.&mt('Part ID: [_1]',$responseId).')'
+                                   .'('.&mt('Response ID: [_1]',$responseId).')'
                                    .'</span>'
                                    .' <b>';
                     if ($hidden) {
@@ -5298,6 +5351,54 @@ sub scantron_selectphase {
 
     $ssi_error = 0;
 
+    if (&Apache::lonnet::allowed('usc',$env{'request.role.domain'}) ||
+        &Apache::lonnet::allowed('usc',$env{'request.course.id'})) {
+
+        # Chunk of form to prompt for a scantron file upload.
+
+        $r->print('
+    <br />
+    '.&Apache::loncommon::start_data_table('LC_scantron_action').'
+       '.&Apache::loncommon::start_data_table_header_row().'
+            <th>
+              &nbsp;'.&mt('Specify a bubblesheet data file to upload.').'
+            </th>
+       '.&Apache::loncommon::end_data_table_header_row().'
+       '.&Apache::loncommon::start_data_table_row().'
+            <td>
+');
+    my $default_form_data=&defaultFormData(&get_symb($r,1));
+    my $cdom= $env{'course.'.$env{'request.course.id'}.'.domain'};
+    my $cnum= $env{'course.'.$env{'request.course.id'}.'.num'};
+    $r->print('
+              <script type="text/javascript" language="javascript">
+    function checkUpload(formname) {
+        if (formname.upfile.value == "") {
+            alert("'.&mt('Please use the browse button to select a file from your local directory.').'");
+            return false;
+        }
+        formname.submit();
+    }
+              </script>
+
+              <form enctype="multipart/form-data" action="/adm/grades" name="rules" method="post">
+                '.$default_form_data.'
+                <input name="courseid" type="hidden" value="'.$cnum.'" />
+                <input name="domainid" type="hidden" value="'.$cdom.'" />
+                <input name="command" value="scantronupload_save" type="hidden" />
+                '.&mt('File to upload: [_1]','<input type="file" name="upfile" size="50" />').'
+                <br />
+                <input type="button" onclick="javascript:checkUpload(this.form);" value="'.&mt('Upload Bubblesheet Data').'" />
+              </form>
+');
+
+        $r->print('
+            </td>
+       '.&Apache::loncommon::end_data_table_row().'
+       '.&Apache::loncommon::end_data_table().'
+');
+    }
+
     # Chunk of form to prompt for a file to grade and how:
 
     $result.= '
@@ -5345,54 +5446,6 @@ sub scantron_selectphase {
    
     $r->print($result);
 
-    if (&Apache::lonnet::allowed('usc',$env{'request.role.domain'}) ||
-        &Apache::lonnet::allowed('usc',$env{'request.course.id'})) {
-
-	# Chunk of form to prompt for a scantron file upload.
-
-        $r->print('
-    <br />
-    '.&Apache::loncommon::start_data_table('LC_scantron_action').'
-       '.&Apache::loncommon::start_data_table_header_row().'
-            <th>
-              &nbsp;'.&mt('Specify a bubblesheet data file to upload.').'
-            </th>
-       '.&Apache::loncommon::end_data_table_header_row().'
-       '.&Apache::loncommon::start_data_table_row().'
-            <td>
-');
-    my $default_form_data=&defaultFormData(&get_symb($r,1));
-    my $cdom= $env{'course.'.$env{'request.course.id'}.'.domain'};
-    my $cnum= $env{'course.'.$env{'request.course.id'}.'.num'};
-    $r->print('
-              <script type="text/javascript" language="javascript">
-    function checkUpload(formname) {
-	if (formname.upfile.value == "") {
-	    alert("'.&mt('Please use the browse button to select a file from your local directory.').'");
-	    return false;
-	}
-	formname.submit();
-    }
-              </script>
-
-              <form enctype="multipart/form-data" action="/adm/grades" name="rules" method="post">
-                '.$default_form_data.'
-                <input name="courseid" type="hidden" value="'.$cnum.'" />
-                <input name="domainid" type="hidden" value="'.$cdom.'" />
-                <input name="command" value="scantronupload_save" type="hidden" />
-                '.&mt('File to upload: [_1]','<input type="file" name="upfile" size="50" />').'
-                <br />
-                <input type="button" onclick="javascript:checkUpload(this.form);" value="'.&mt('Upload Bubblesheet Data').'" />
-              </form>
-');
-
-        $r->print('
-            </td>
-       '.&Apache::loncommon::end_data_table_row().'
-       '.&Apache::loncommon::end_data_table().'
-');
-    }
-
     # Chunk of the form that prompts to view a scoring office file,
     # corrected file, skipped records in a file.
 
@@ -6359,15 +6412,15 @@ sub scantron_do_warning {
     if ( $env{'form.selectpage'} eq '' ||
 	 $env{'form.scantron_selectfile'} eq '' ||
 	 $env{'form.scantron_format'} eq '' ) {
-	$r->print("<p>".&mt('You have forgetten to specify some information. Please go Back and try again.')."</p>");
+	$r->print("<p>".&mt('You have forgotten to specify some information. Please go Back and try again.')."</p>");
 	if ( $env{'form.selectpage'} eq '') {
 	    $r->print('<p><span class="LC_error">'.&mt('You have not selected a Sequence to grade').'</span></p>');
 	} 
 	if ( $env{'form.scantron_selectfile'} eq '') {
-	    $r->print('<p><span class="LC_error">'.&mt('You have not selected a file that contains the student\'s response data.').'</span></p>');
+	    $r->print('<p><span class="LC_error">'.&mt("You have not selected a file that contains the student's response data.").'</span></p>');
 	} 
 	if ( $env{'form.scantron_format'} eq '') {
-	    $r->print('<p><span class="LC_error">'.&mt('You have not selected a the format of the student\'s response data.').'</span></p>');
+	    $r->print('<p><span class="LC_error">'.&mt("You have not selected the format of the student's response data.").'</span></p>');
 	} 
     } else {
 	my $warning=&scantron_warning_screen('Grading: Validate Records');
@@ -8055,7 +8108,7 @@ sub scantron_upload_scantron_data {
 
 </script>
 
-<h3>'.&mt('Send scanned bubblesheet data to a course').'</h3>
+<h3>'.&mt('Send bubblesheet data to a course').'</h3>
 
 <form enctype="multipart/form-data" action="/adm/grades" name="rules" method="post">
 '.$default_form_data.
@@ -8400,7 +8453,14 @@ sub checkscantron_results {
             }
         }
     }
-    $r->print('<p>'.&mt('Comparison of bubblesheet data (including corrections) with corresponding submission records (most recent submission) for <b>[quant,_1,student]</b>  ([_2] scantron lines/student).',$numstudents,$env{'form.scantron_maxbubble'}).'</p>');
+    $r->print('<p>'.
+              &mt('Comparison of bubblesheet data (including corrections) with corresponding submission records (most recent submission) for [_1][quant,_2,student][_3]  ([_4] bubblesheet lines/student).',
+                  '<b>',
+                  $numstudents,
+                  '</b>',
+                  $env{'form.scantron_maxbubble'}).
+              '</p>'
+    );
     $r->print('<p>'.&mt('Exact matches for <b>[quant,_1,student]</b>.',$passed).'<br />'.&mt('Discrepancies detected for <b>[quant,_1,student]</b>.',$failed).'</p>');
     if ($passed) {
         $r->print(&mt('Students with exact correspondence between bubblesheet data and submissions are as follows:').'<br /><br />');
@@ -8638,7 +8698,7 @@ sub grading_menu {
                     		url => $url4,
                     		permission => 'F',
                     		icon => 'stat.png',
-                    		linktitle => 'Grade scantron exams, upload/download scantron data files, and review previously graded scantron exams.'
+                    		linktitle => 'Grade bubblesheet exams, upload/download bubblesheet data files, and review previously graded bubblesheet exams.'
                 	    }
                     ]
             });
@@ -8967,7 +9027,7 @@ sub process_clicker {
     $result.='<table width="100%" border="0"><tr bgcolor="#e6ffff"><td>'."\n";
     $result.='&nbsp;<b>'.&mt('Specify a file containing the clicker information for this resource.').
         '</b></td></tr>'."\n";
-    $result.='<tr bgcolor=#ffffe6><td>'."\n";
+    $result.='<tr bgcolor="#ffffe6"><td>'."\n";
 # Attempt to restore parameters from last session, set defaults if not present
     my %Saveable_Parameters=&clicker_grading_parameters();
     &Apache::loncommon::restore_course_settings('grades_clicker',
@@ -9085,7 +9145,7 @@ sub process_clicker_file {
     if ($env{'form.gradingmechanism'} eq 'given') {
         $env{'form.givenanswer'}=~s/^\s*//gs;
         $env{'form.givenanswer'}=~s/\s*$//gs;
-        $env{'form.givenanswer'}=~s/[^a-zA-Z0-9\.\*\-]+/\,/g;
+        $env{'form.givenanswer'}=~s/[^a-zA-Z0-9\.\*\-\+]+/\,/g;
         $env{'form.givenanswer'}=uc($env{'form.givenanswer'});
         my @answers=split(/\,/,$env{'form.givenanswer'});
         $foundgiven=$#answers+1;
@@ -9145,7 +9205,7 @@ sub process_clicker_file {
     $result.=(<<ENDHEADER);
 <br /><table width="100%" border="0"><tr><td bgcolor="#777777">
 <table width="100%" border="0"><tr bgcolor="#e6ffff"><td>
-<b>$heading</b></td></tr><tr bgcolor=#ffffe6><td>
+<b>$heading</b></td></tr><tr bgcolor="#ffffe6"><td>
 <form method="post" action="/adm/grades" name="clickeranalysis">
 <input type="hidden" name="symb" value="$symb" />
 <input type="hidden" name="command" value="assignclickergrades" />
@@ -9214,7 +9274,7 @@ ENDHEADER
                    "\n".&mt("Username").": <input type='text' name='uname".$id."' />&nbsp;".
                    "\n".&mt("Domain").": ".
                    &Apache::loncommon::select_dom_form($env{'course.'.$env{'request.course.id'}.'.domain'},'udom'.$id).'&nbsp;'.
-                   &Apache::loncommon::selectstudent_link('clickeranalysis','uname'.$id,'udom'.$id);
+                   &Apache::loncommon::selectstudent_link('clickeranalysis','uname'.$id,'udom'.$id,0,$id);
           $unknown_count++;
        }
     }
@@ -9259,6 +9319,7 @@ sub iclicker_eval {
 	    $id=~s/^[\#0]+//;
 	    for (my $i=0;$i<$number;$i++) {
 		my $idx=3+$i*6;
+                $entries[$idx]=~s/[^a-zA-Z0-9\.\*\-\+]+//g;
 		push(@idresponses,$entries[$idx]);
 	    }
 	    $$responses{$id}=join(',',@idresponses);
@@ -9311,14 +9372,14 @@ sub assign_clicker_grades {
 # FIXME: This should probably look for the first handgradeable part
     my $part=$$partlist[0];
 # Start screen output
-    my ($result) = &showResourceInfo($symb,$env{'form.probTitle'});
+    my ($result) = &showResourceInfo($symb,$env{'form.probTitle'}).'<br />';
+
+    $result .= &Apache::loncommon::start_data_table().
+               &Apache::loncommon::start_data_table_header_row().
+               '<th>'.&mt('Assigning grades based on clicker file').'</th>'.
+               &Apache::loncommon::end_data_table_header_row().
+               &Apache::loncommon::start_data_table_row().'<td>';
 
-    my $heading=&mt('Assigning grades based on clicker file');
-    $result.=(<<ENDHEADER);
-<br /><table width="100%" border="0"><tr><td bgcolor="#777777">
-<table width="100%" border="0"><tr bgcolor="#e6ffff"><td>
-<b>$heading</b></td></tr><tr bgcolor=#ffffe6><td>
-ENDHEADER
 # Get correct result
 # FIXME: Possibly need delimiter other than ":"
     my @correct=();
@@ -9334,25 +9395,26 @@ ENDHEADER
                     $result.='<br /><span class="LC_warning">'.
                              &mt('More than one correct result given for question "[_1]": [_2] versus [_3].',
                                  $env{'form.question:'.$i},$correct[$i],$input[$i]).'</span>';
-                 } elsif ($input[$i]) {
+                 } elsif (($input[$i]) || ($input[$i] eq '0')) {
                     $correct[$i]=$input[$i];
                  }
              }
           }
        }
        for (my $i=0;$i<$number;$i++) {
-          if (!$correct[$i]) {
+          if ((!$correct[$i]) && ($correct[$i] ne '0')) {
              $result.='<br /><span class="LC_error">'.
                       &mt('No correct result given for question "[_1]"!',
                           $env{'form.question:'.$i}).'</span>';
           }
        }
-       $result.='<br />'.&mt("Correct answer: [_1]",join(', ',map { ($_?$_:'-') } @correct));
+       $result.='<br />'.&mt("Correct answer: [_1]",join(', ',map { ((($_) || ($_ eq '0'))?$_:'-') } @correct));
     }
 # Start grading
     my $pcorrect=$env{'form.pcorrect'};
     my $pincorrect=$env{'form.pincorrect'};
     my $storecount=0;
+    my %users=();
     foreach my $key (keys(%env)) {
        my $user='';
        if ($key=~/^form\.student\:(.*)$/) {
@@ -9366,7 +9428,13 @@ ENDHEADER
              $user=$env{'form.multi'.$id};
           }
        }
-       if ($user) { 
+       if ($user) {
+          if ($users{$user}) {
+             $result.='<br /><span class="LC_warning">'.
+                      &mt("More than one entry found for <tt>[_1]</tt>!",$user).
+                      '</span><br />';
+          }
+          $users{$user}=1;
           my @answer=split(/\,/,$env{$key});
           my $sum=0;
           my $realnumber=$number;
@@ -9379,11 +9447,23 @@ ENDHEADER
                 } elsif ($correct[$i] eq '*') {
                    $sum+=$pcorrect;
                 } else {
-                   if ($answer[$i] eq $correct[$i]) {
-                      $sum+=$pcorrect;
-                   } else {
-                      $sum+=$pincorrect;
+# We actually grade if correct or not
+                   my $increment=$pincorrect;
+# Special case: numerical answer "0"
+                   if ($correct[$i] eq '0') {
+                      if ($answer[$i]=~/^[0\.]+$/) {
+                         $increment=$pcorrect;
+                      }
+# General numerical answer, both evaluate to something non-zero
+                   } elsif ((1.0*$correct[$i]!=0) && (1.0*$answer[$i]!=0)) {
+                      if (1.0*$correct[$i]==1.0*$answer[$i]) {
+                         $increment=$pcorrect;
+                      }
+# Must be just alphanumeric
+                   } elsif ($answer[$i] eq $correct[$i]) {
+                      $increment=$pcorrect;
                    }
+                   $sum+=$increment;
                 }
              }
           }
@@ -9406,8 +9486,9 @@ ENDHEADER
     }
 # We are done
     $result.='<br />'.&mt('Successfully stored grades for [quant,_1,student].',$storecount).
-             '</td></tr></table>'."\n".
-             '</td></tr></table><br /><br />'."\n";
+             '</td>'.
+             &Apache::loncommon::end_data_table_row().
+             &Apache::loncommon::end_data_table()."<br /><br />\n";
     return $result.&show_grading_menu_form($symb);
 }
 
@@ -9421,14 +9502,13 @@ sub navmap_errormsg {
 sub handler {
     my $request=$_[0];
     &reset_caches();
-    if ($env{'browser.mathml'}) {
-	&Apache::loncommon::content_type($request,'text/xml');
-    } else {
-	&Apache::loncommon::content_type($request,'text/html');
+    if ($request->header_only) {
+        &Apache::loncommon::content_type($request,'text/html');
+        $request->send_http_header;
+        return OK;
     }
-    $request->send_http_header;
-    return '' if $request->header_only;
     &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'});
+
     my $symb=&get_symb($request,1);
     my @commands=&Apache::loncommon::get_env_multiple('form.command');
     my $command=$commands[0];
@@ -9439,10 +9519,13 @@ sub handler {
 
     $ssi_error = 0;
     my $brcrum = [{href=>"/adm/grades",text=>"Grading"}];
-    $request->print(&Apache::loncommon::start_page('Grading',undef,
-                                          {'bread_crumbs' => $brcrum}));
+    my $start_page = &Apache::loncommon::start_page('Grading',undef,
+                                          {'bread_crumbs' => $brcrum});
     if ($symb eq '' && $command eq '') {
 	if ($env{'user.adv'}) {
+            &Apache::loncommon::content_type($request,'text/html');
+            $request->send_http_header;
+            $request->print($start_page);
 	    if (($env{'form.codeone'}) && ($env{'form.codetwo'}) &&
 		($env{'form.codethree'})) {
 		my $token=$env{'form.codeone'}.'*'.$env{'form.codetwo'}.'*'.
@@ -9466,9 +9549,28 @@ sub handler {
 	    } else {
 		$request->print(&Apache::lonxml::tokeninputfield());
 	    }
-	}
+        } elsif ($env{'request.course.id'}) {
+            &init_perm(); 
+            if (!%perm) {
+                $request->internal_redirect('/adm/quickgrades');
+            } else {
+                &Apache::loncommon::content_type($request,'text/html');
+                $request->send_http_header;
+                $request->print($start_page);
+            }
+        }
     } else {
-	&init_perm();
+        &init_perm();
+        if (!$env{'request.course.id'}) {
+            # Not in a course.
+            $env{'user.error.msg'}="/adm/grades::vgr:0:0:Cannot display grades page outside course context";
+            return HTTP_NOT_ACCEPTABLE;
+        } elsif (!%perm) {
+            $request->internal_redirect('/adm/quickgrades');
+        }
+        &Apache::loncommon::content_type($request,'text/html');
+        $request->send_http_header;
+        $request->print($start_page);
 	if ($command eq 'submission' && $perm{'vgr'}) {
 	    ($env{'form.student'} eq '' ? &listStudents($request) : &submission($request,0,0));
 	} elsif ($command eq 'pickStudentPage' && $perm{'vgr'}) {
@@ -9546,7 +9648,7 @@ sub handler {
     }
     $request->print(&Apache::loncommon::end_page());
     &reset_caches();
-    return '';
+    return OK;
 }
 
 1;