--- loncom/homework/grades.pm	2014/01/30 19:11:05	1.717
+++ loncom/homework/grades.pm	2015/02/10 04:02:17	1.731
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # The LON-CAPA Grading handler
 #
-# $Id: grades.pm,v 1.717 2014/01/30 19:11:05 bisitz Exp $
+# $Id: grades.pm,v 1.731 2015/02/10 04:02:17 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -330,6 +330,8 @@ sub cleanRecord {
     my $grayFont = '<span class="LC_internal_info">';
     if ($response =~ /^(option|rank)$/) {
 	my %answer=&Apache::lonnet::str2hash($answer);
+        my @answer = %answer;
+        %answer = map {&HTML::Entities::encode($_, '"<>&')}  @answer;
 	my %grading=&Apache::lonnet::str2hash($record->{$version."resource.$partid.$respid.submissiongrading"});
 	my ($toprow,$bottomrow);
 	foreach my $foil (@$order) {
@@ -346,6 +348,8 @@ sub cleanRecord {
 	    $bottomrow.'</tr></table></blockquote>';
     } elsif ($response eq 'match') {
 	my %answer=&Apache::lonnet::str2hash($answer);
+        my @answer = %answer;
+        %answer = map {&HTML::Entities::encode($_, '"<>&')}  @answer;
 	my %grading=&Apache::lonnet::str2hash($record->{$version."resource.$partid.$respid.submissiongrading"});
 	my @items=&Apache::lonnet::str2array($record->{$version."resource.$partid.$respid.submissionitems"});
 	my ($toprow,$middlerow,$bottomrow);
@@ -368,6 +372,8 @@ sub cleanRecord {
 	    $bottomrow.'</tr></table></blockquote>';
     } elsif ($response eq 'radiobutton') {
 	my %answer=&Apache::lonnet::str2hash($answer);
+        my @answer = %answer;
+        %answer = map {&HTML::Entities::encode($_, '"<>&')}  @answer;
 	my ($toprow,$bottomrow);
 	my $correct = 
 	    &get_radiobutton_correct_foil($partid,$respid,$symb,$uname,$udom,$type,$trial,$rndseed);
@@ -400,10 +406,11 @@ sub cleanRecord {
 	    $env{'form.kwstyle'}  = $keyhash{$loginuser.'_kwstyle'} ne '' ? $keyhash{$loginuser.'_kwstyle'} : '';
 	    $env{'form.'.$symb} = 1; # so that we don't have to read it from disk for multiple sub of the same prob.
 	}
-	$answer =~ s-\n-<br />-g;
 	return '<br /><br /><blockquote><tt>'.&keywords_highlight($answer).'</tt></blockquote>';
+
     } elsif ( $response eq 'organic') {
-	my $result='Smile representation: "<tt>'.$answer.'</tt>"';
+        my $result=&mt('Smile representation: [_1]',
+                           '"<tt>'.&HTML::Entities::encode($answer, '"<>&').'</tt>"');
 	my $jme=$record->{$version."resource.$partid.$respid.molecule"};
 	$result.=&Apache::chemresponse::jme_img($jme,$answer,400);
 	return $result;
@@ -442,8 +449,9 @@ sub cleanRecord {
 	$answer = 
 	    &Apache::loncommon::format_previous_attempt_value('submission',
 							      $answer);
+	return $answer;
     }
-    return $answer;
+    return &HTML::Entities::encode($answer, '"<>&');
 }
 
 #-- A couple of common js functions
@@ -1287,10 +1295,8 @@ sub sub_page_js {
 			    }
 			}
 		    }
-		    
 		}
 	    }
-	    
 	}
 	formname.submit();
     }
@@ -1432,6 +1438,15 @@ INNERJS
                 txtc => 'Text Color',
                 font => 'Font Size',
                 fnst => 'Font Style',
+                col1 => 'red',
+                col2 => 'green',
+                col3 => 'blue',
+                siz1 => 'normal',
+                siz2 => '+1',
+                siz3 => '+2',
+                sty1 => 'normal',
+                sty2 => 'italic',
+                sty3 => 'bold',
              );
     $request->print(&Apache::lonhtmlcommon::scripttag(<<SUBJAVASCRIPT));
 
@@ -1595,25 +1610,34 @@ INNERJS
     var redsel = "";
     var grnsel = "";
     var blusel = "";
-    if (kwclr=="red")   {var redsel="checked"};
-    if (kwclr=="green") {var grnsel="checked"};
-    if (kwclr=="blue")  {var blusel="checked"};
+    var txtcol1 = "$lt{'col1'}";
+    var txtcol2 = "$lt{'col2'}";
+    var txtcol3 = "$lt{'col3'}";
+    var txtsiz1 = "$lt{'siz1'}";
+    var txtsiz2 = "$lt{'siz2'}";
+    var txtsiz3 = "$lt{'siz3'}";
+    var txtsty1 = "$lt{'sty1'}";
+    var txtsty2 = "$lt{'sty2'}";
+    var txtsty3 = "$lt{'sty3'}";
+    if (kwclr=="red")   {var redsel="checked='checked'"};
+    if (kwclr=="green") {var grnsel="checked='checked'"};
+    if (kwclr=="blue")  {var blusel="checked='checked'"};
     var sznsel = "";
     var sz1sel = "";
     var sz2sel = "";
-    if (kwsize=="0")  {var sznsel="checked"};
-    if (kwsize=="+1") {var sz1sel="checked"};
-    if (kwsize=="+2") {var sz2sel="checked"};
+    if (kwsize=="0")  {var sznsel="checked='checked'"};
+    if (kwsize=="+1") {var sz1sel="checked='checked'"};
+    if (kwsize=="+2") {var sz2sel="checked='checked'"};
     var synsel = "";
     var syisel = "";
     var sybsel = "";
-    if (kwstyle=="")    {var synsel="checked"};
-    if (kwstyle=="<i>") {var syisel="checked"};
-    if (kwstyle=="<b>") {var sybsel="checked"};
+    if (kwstyle=="")    {var synsel="checked='checked'"};
+    if (kwstyle=="<i>") {var syisel="checked='checked'"};
+    if (kwstyle=="<b>") {var sybsel="checked='checked'"};
     highlightCentral();
-    highlightbody('red','red',redsel,'0','normal',sznsel,'','normal',synsel);
-    highlightbody('green','green',grnsel,'+1','+1',sz1sel,'<i>','italic',syisel);
-    highlightbody('blue','blue',blusel,'+2','+2',sz2sel,'<b>','bold',sybsel);
+    highlightbody('red',txtcol1,redsel,'0',txtsiz1,sznsel,'',txtsty1,synsel);
+    highlightbody('green',txtcol2,grnsel,'+1',txtsiz2,sz1sel,'<i>',txtsty2,syisel);
+    highlightbody('blue',txtcol3,blusel,'+2',txtsiz3,sz2sel,'<b>',txtsty3,sybsel);
     highlightend();
     return;
   }
@@ -1631,31 +1655,29 @@ INNERJS
     hDoc.$docopen;
     hDoc.write('$start_page_highlight_central');
     hDoc.write("<form action=\\"inactive\\" name=\\"hlCenter\\">");
-    hDoc.write("<h3><span class=\\"LC_info\\">&nbsp;$lt{'kehi'}<\\/span><\\/h3><br /><br />");
+    hDoc.write("<h1>$lt{'kehi'}<\\/h1>");
 
-    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>$lt{'txtc'}<\\/b><\\/td><td><b>$lt{'font'}<\\/b><\\/td><td><b>$lt{'fnst'}<\\/td><\\/tr>");
+    hDoc.write('<table border="0" width="100%"><tr style="background-color:#A1D676">');
+    hDoc.write("<th>$lt{'txtc'}<\\/th><th>$lt{'font'}<\\/th><th>$lt{'fnst'}<\\/th><\\/tr>");
   }
 
   function highlightbody(clrval,clrtxt,clrsel,szval,sztxt,szsel,syval,sytxt,sysel) { 
     var hDoc = hwdWin.document;
-    hDoc.write("<tr bgcolor=\\"#ffffdd\\">");
+    hDoc.write("<tr>");
     hDoc.write("<td align=\\"left\\">");
-    hDoc.write("<input name=\\"kwdclr\\" type=\\"radio\\" value=\\""+clrval+"\\" "+clrsel+">&nbsp;"+clrtxt+"<\\/td>");
+    hDoc.write("<input name=\\"kwdclr\\" type=\\"radio\\" value=\\""+clrval+"\\" "+clrsel+" \\/>&nbsp;"+clrtxt+"<\\/td>");
     hDoc.write("<td align=\\"left\\">");
-    hDoc.write("<input name=\\"kwdsize\\" type=\\"radio\\" value=\\""+szval+"\\" "+szsel+">&nbsp;"+sztxt+"<\\/td>");
+    hDoc.write("<input name=\\"kwdsize\\" type=\\"radio\\" value=\\""+szval+"\\" "+szsel+" \\/>&nbsp;"+sztxt+"<\\/td>");
     hDoc.write("<td align=\\"left\\">");
-    hDoc.write("<input name=\\"kwdstyle\\" type=\\"radio\\" value=\\""+syval+"\\" "+sysel+">&nbsp;"+sytxt+"<\\/td>");
+    hDoc.write("<input name=\\"kwdstyle\\" type=\\"radio\\" value=\\""+syval+"\\" "+sysel+" \\/>&nbsp;"+sytxt+"<\\/td>");
     hDoc.write("<\\/tr>");
   }
 
   function highlightend() { 
     var hDoc = hwdWin.document;
-    hDoc.write("<\\/table>");
-    hDoc.write("<\\/td><\\/tr><\\/table>&nbsp;");
-    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("<\\/table><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();
@@ -1789,7 +1811,7 @@ sub handback_box {
 	    if ($file =~ /\/portfolio\//) {
                 $file_counter++;
     	        my ($file_path, $file_disp) = ($file =~ m|(.+/)(.+)$|);
-    	        my ($name,$version,$ext) = &file_name_version_ext($file_disp);
+    	        my ($name,$version,$ext) = &Apache::lonnet::file_name_version_ext($file_disp);
     	        $file_disp = "$name.$ext";
     	        $file = $file_path.$file_disp;
     	        $result.=&mt('Return commented version of [_1] to student.',
@@ -2045,6 +2067,7 @@ sub submission {
         if (1) {
 
             my %lt = &Apache::lonlocal::texthash(
+                          keyh => 'Keyword Highlighting for Essays',
                           keyw => 'Keyword Options',
                           list => 'List',
                           past => 'Paste Selection to List',
@@ -2053,13 +2076,18 @@ sub submission {
 #
 # Print out the keyword options line
 #
-	    $request->print(<<KEYWORDS);
-<br /><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">$lt{'past'}</a>&nbsp; &nbsp;
-<a href="javascript:kwhighlight();" target="_self">$lt{'high'}</a><br /><br />
-KEYWORDS
+	    $request->print(
+                '<div class="LC_columnSection">'
+               .'<fieldset><legend>'.$lt{'keyh'}.'</legend>'
+               .&Apache::lonhtmlcommon::funclist_from_array(
+                    ['<a href="javascript:keywords(document.SCORE);" target="_self">'.$lt{'list'}.'</a>',
+                     '<a href="#" onmousedown="javascript:getSel(); return false"
+ class="page">'.$lt{'past'}.'</a>',
+                     '<a href="javascript:kwhighlight();" target="_self">'.$lt{'high'}.'</a>'],
+                    {legend => $lt{'keyw'}})
+               .'</fieldset></div>'
+            );
+
 #
 # Load the other essays for similarity check
 #
@@ -2196,7 +2224,7 @@ KEYWORDS
 	    foreach my $submission (@$string) {
 		my ($partid,$respid) = ($submission =~ /^resource\.([^\.]*)\.([^\.]*)\.submission/);
 		if (join('_',@{$part}) ne ($partid.'_'.$respid)) { next; }
-		my ($ressub,$hide,$subval) = split(/:/,$submission,3);
+		my ($ressub,$hide,$draft,$subval) = split(/:/,$submission,4);
 		# Similarity check
                 my $similar='';
                 my ($type,$trial,$rndseed);
@@ -2265,9 +2293,17 @@ KEYWORDS
                     if ($hide eq 'anon') {
                         $lastsubonly.='<br /><b>'.&mt('Anonymous Survey').'</b>'; 
                     } else {
-             	        $lastsubonly.='<br /><b>'.&mt('Submitted Answer:').' </b>'.
+             	        $lastsubonly.='<br /><b>'.&mt('Submitted Answer:').' </b>';
+                        if ($draft) {
+                            $lastsubonly.= ' <span class="LC_warning">'.&mt('Draft Copy').'</span>';
+                        }
+                        $subval =
 			    &cleanRecord($subval,$responsetype,$symb,$partid,
 					 $respid,\%record,$order,undef,$uname,$udom,$type,$trial,$rndseed);
+                        if ($responsetype eq 'essay') {
+                            $subval =~ s{\n}{<br />}g;
+                        }
+                        $lastsubonly.=$subval."\n";
                     }
 	            if ($similar) {$lastsubonly.="<br /><br />$similar\n";}
 		    $lastsubonly.='</div>';
@@ -2280,12 +2316,15 @@ KEYWORDS
     if ($env{'form.lastSub'} eq 'datesub') {
         my ($parts,$handgrade,$responseType) = &response_type($symb,\$res_error);
 	$request->print(&displaySubByDates($symb,\%record,$parts,$responseType,$checkIcon,$uname,$udom));
+  
     } 
     if ($env{'form.lastSub'} =~ /^(last|all)$/) {
+        my $identifier = (&canmodify($usec)? $counter : '');
         $request->print(&Apache::loncommon::get_previous_attempt($symb,$uname,$udom,
 								 $env{'request.course.id'},
 								 $last,'.submission',
-								 'Apache::grades::keywords_highlight'));
+								 'Apache::grades::keywords_highlight',
+                                                                 $usec,$identifier));
     }
     $request->print('<input type="hidden" name="unamedom'.$counter.'" value="'.$uname.':'
 	.$udom.'" />'."\n");
@@ -2513,10 +2552,8 @@ sub get_last_submission {
                 }
             }
 	    my ($partid,$foo) = split(/submission$/,$key);
-	    my $draft  = $lasthash{$partid.'awarddetail'} eq 'DRAFT' ?
-		'<span class="LC_warning">'.&mt('Draft Copy').'</span> ' : '';
-	    #push(@string, join(':', $key, $hide, $draft.$lasthash{$key}));
-            push(@string, join(':', $key, $hide, $draft.(
+	    my $draft  = $lasthash{$partid.'awarddetail'} eq 'DRAFT' ? 1 : 0;
+            push(@string, join(':', $key, $hide, $draft, (
                 ref($lasthash{$key}) eq 'ARRAY' ?
                     join(',', @{$lasthash{$key}}) : $lasthash{$key}) ));
 	}
@@ -2737,16 +2774,26 @@ sub processHandGrade {
 	my $ctr = 0;
 	while ($ctr < $ngrade) {
 	    my ($uname,$udom) = split(/:/,$env{'form.unamedom'.$ctr});
-	    my ($errorflag,$pts,$wgt) = &saveHandGrade($request,$symb,$uname,$udom,$ctr);
+	    my ($errorflag,$pts,$wgt,$numhidden) = 
+                &saveHandGrade($request,$symb,$uname,$udom,$ctr);
 	    if ($errorflag eq 'no_score') {
 		$ctr++;
 		next;
 	    }
 	    if ($errorflag eq 'not_allowed') {
-		$request->print("<span class=\"LC_warning\">Not allowed to modify grades for $uname:$udom</span>");
+		$request->print(
+                    '<span class="LC_error">'
+                   .&mt('Not allowed to modify grades for [_1]',"$uname:$udom")
+                   .'</span>');
 		$ctr++;
 		next;
 	    }
+            if ($numhidden) {
+                $request->print(
+                    '<span class="LC_info">'
+                   .&mt('For [_1]: [quant,_2,transaction] hidden',"$uname:$udom",$numhidden)
+                   .'</span><br />');
+            }
 	    my $includemsg = $env{'form.includemsg'.$ctr};
 	    my ($subject,$message,$msgstatus) = ('','','');
 	    my $restitle = &Apache::lonnet::gettitle($symb);
@@ -2968,9 +3015,14 @@ sub saveHandGrade {
     my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'},$domain,$stuname);
     my @parts_graded;
     my %newrecord  = ();
-    my ($pts,$wgt) = ('','');
+    my ($pts,$wgt,$totchg) = ('','',0);
     my %aggregate = ();
     my $aggregateflag = 0;
+    if ($env{'form.HIDE'.$newflg}) {
+        my ($version,$parts) = split(/:/,$env{'form.HIDE'.$newflg},2);
+        my $numchgs = &makehidden($version,$parts,\%record,$symb,$domain,$stuname,1);
+        $totchg += $numchgs;
+    }
     my @parts = split(/:/,$env{'form.partlist'.$newflg});
     foreach my $new_part (@parts) {
 	#collaborator ($submi may vary for different parts
@@ -3073,7 +3125,37 @@ sub saveHandGrade {
         &Apache::lonnet::cinc('nohist_resourcetracker',\%aggregate,
 			      $cdom,$cnum);
     }
-    return ('',$pts,$wgt);
+    return ('',$pts,$wgt,$totchg);
+}
+
+sub makehidden {
+    my ($version,$parts,$record,$symb,$domain,$stuname,$tolog) = @_;
+    return unless (ref($record) eq 'HASH');
+    my %modified;
+    my $numchanged = 0;
+    if (exists($record->{$version.':keys'})) {
+        my $partsregexp = $parts;
+        $partsregexp =~ s/,/|/g;
+        foreach my $key (split(/\:/,$record->{$version.':keys'})) {
+            if ($key =~ /^resource\.(?:$partsregexp)\.([^\.]+)$/) {
+                 my $item = $1;
+                 unless (($item eq 'solved') || ($item =~ /^award(|msg|ed)$/)) {
+                     $modified{$key} = $record->{$version.':'.$key};
+                 }
+            } elsif ($key =~ m{^(resource\.(?:$partsregexp)\.[^\.]+\.)(.+)$}) {
+                $modified{$1.'hidden'.$2} = $record->{$version.':'.$key};
+            } elsif ($key =~ /^(ip|timestamp|host)$/) {
+                $modified{$key} = $record->{$version.':'.$key};
+            }
+        }
+        if (keys(%modified)) {
+            if (&Apache::lonnet::putstore($env{'request.course.id'},$symb,$version,\%modified,
+                                          $domain,$stuname,$tolog) eq 'ok') {
+                $numchanged ++;
+            }
+        }
+    }
+    return $numchanged;
 }
 
 sub check_and_remove_from_queue {
@@ -3117,13 +3199,13 @@ sub handback_files {
                     my ($directory,$answer_file) = 
                         ($env{'form.'.$newflg.'_'.$part_resp.'_origdoc'.$counter} =~ /^(.*?)([^\/]*)$/);
                     my ($answer_name,$answer_ver,$answer_ext) =
-		        &file_name_version_ext($answer_file);
+		        &Apache::lonnet::file_name_version_ext($answer_file);
 		    my ($portfolio_path) = ($directory =~ /^.+$stuname\/portfolio(.*)/);
                     my $getpropath = 1;
                     my ($dir_list,$listerror) = 
                         &Apache::lonnet::dirlist($portfolio_root.$portfolio_path,
                                                  $domain,$stuname,$getpropath);
-		    my $version = &get_next_version($answer_name,$answer_ext,$dir_list);
+		    my $version = &Apache::lonnet::get_next_version($answer_name,$answer_ext,$dir_list);
                     # fix filename
                     my ($save_file_name) = (($directory.$answer_name.".$version.".$answer_ext) =~ /^.+\/${stuname}\/(.*)/);
                     my $result=&Apache::lonnet::finishuserfileupload($stuname,$domain,
@@ -3272,29 +3354,14 @@ sub version_portfiles {
     my $version_parts = join('|',@$v_flag);
     my @returned_keys;
     my $parts = join('|', @$parts_graded);
-    my $portfolio_root = '/userfiles/portfolio';
     foreach my $key (keys(%$record)) {
         my $new_portfiles;
         if ($key =~ /^resource\.($version_parts)\./ && $key =~ /\.portfiles$/ ) {
             my @versioned_portfiles;
             my @portfiles = split(/\s*,\s*/,$$record{$key});
-            foreach my $file (@portfiles) {
-                &Apache::lonnet::unmark_as_readonly($domain,$stu_name,[$symb,$env{'request.course.id'}],$file);
-                my ($directory,$answer_file) =($file =~ /^(.*?)([^\/]*)$/);
-		my ($answer_name,$answer_ver,$answer_ext) =
-		    &file_name_version_ext($answer_file);
-                my $getpropath = 1;    
-                my ($dir_list,$listerror) = 
-                    &Apache::lonnet::dirlist($portfolio_root.$directory,$domain,
-                                             $stu_name,$getpropath);
-                my $version = &get_next_version($answer_name,$answer_ext,$dir_list);
-                my $new_answer = &version_selected_portfile($domain, $stu_name, $directory, $answer_file, $version);
-                if ($new_answer ne 'problem getting file') {
-                    push(@versioned_portfiles, $directory.$new_answer);
-                    &Apache::lonnet::mark_as_readonly($domain,$stu_name,
-                        [$directory.$new_answer],
-                        [$symb,$env{'request.course.id'},'graded']);
-                }
+            if (@portfiles) {
+                &Apache::lonnet::portfiles_versioning($symb,$domain,$stu_name,\@portfiles,
+                                                      \@versioned_portfiles);
             }
             $$record{$key} = join(',',@versioned_portfiles);
             push(@returned_keys,$key);
@@ -3303,64 +3370,6 @@ sub version_portfiles {
     return (@returned_keys);   
 }
 
-sub get_next_version {
-    my ($answer_name, $answer_ext, $dir_list) = @_;
-    my $version;
-    if (ref($dir_list) eq 'ARRAY') {
-        foreach my $row (@{$dir_list}) {
-            my ($file) = split(/\&/,$row,2);
-            my ($file_name,$file_version,$file_ext) =
-	        &file_name_version_ext($file);
-            if (($file_name eq $answer_name) && 
-	        ($file_ext eq $answer_ext)) {
-                     # gets here if filename and extension match, 
-                     # regardless of version
-                if ($file_version ne '') {
-                    # a versioned file is found  so save it for later
-                    if ($file_version > $version) {
-		        $version = $file_version;
-	            }
-                }
-            }
-        }
-    }
-    $version ++;
-    return($version);
-}
-
-sub version_selected_portfile {
-    my ($domain,$stu_name,$directory,$file_name,$version) = @_;
-    my ($answer_name,$answer_ver,$answer_ext) =
-        &file_name_version_ext($file_name);
-    my $new_answer;
-    $env{'form.copy'} = &Apache::lonnet::getfile("/uploaded/$domain/$stu_name/portfolio$directory$file_name");
-    if($env{'form.copy'} eq '-1') {
-        $new_answer = 'problem getting file';
-    } else {
-        $new_answer = $answer_name.'.'.$version.'.'.$answer_ext;
-        my $copy_result = &Apache::lonnet::finishuserfileupload(
-                            $stu_name,$domain,'copy',
-		        '/portfolio'.$directory.$new_answer);
-    }    
-    return ($new_answer);
-}
-
-sub file_name_version_ext {
-    my ($file)=@_;
-    my @file_parts = split(/\./, $file);
-    my ($name,$version,$ext);
-    if (@file_parts > 1) {
-	$ext=pop(@file_parts);
-	if (@file_parts > 1 && $file_parts[-1] =~ /^\d+$/) {
-	    $version=pop(@file_parts);
-	}
-	$name=join('.',@file_parts);
-    } else {
-	$name=join('.',@file_parts);
-    }
-    return($name,$version,$ext);
-}
-
 #--------------------------------------------------------------------------------------
 #
 #-------------------------- Next few routines handles grading by section or whole class
@@ -4741,9 +4750,11 @@ sub displayPage {
 		}
 	    } elsif ($env{'form.lastSub'} eq 'all') {
 		my $last = ($env{'form.lastSub'} eq 'last' ? 'last' : '');
+                my $identifier = (&canmodify($usec)? $prob : ''); 
 		$studentTable.=&Apache::loncommon::get_previous_attempt($symbx,$uname,$udom,
 									$env{'request.course.id'},
-									'','.submission');
+									'','.submission',undef,
+                                                                        $usec,$identifier);
  
 	    }
 	    if (&canmodify($usec)) {
@@ -4956,7 +4967,7 @@ sub updateGradeByPage {
 
     $iterator->next(); # skip the first BEGIN_MAP
     my $curRes = $iterator->next(); # for "current resource"
-    my ($depth,$question,$prob,$changeflag)= (1,1,1,0);
+    my ($depth,$question,$prob,$changeflag,$hideflag)= (1,1,1,0,0);
     while ($depth > 0) {
         if($curRes == $iterator->BEGIN_MAP) { $depth++; }
         if($curRes == $iterator->END_MAP) { $depth--; }
@@ -4977,6 +4988,12 @@ sub updateGradeByPage {
 	    my @displayPts=();
             my %aggregate = ();
             my $aggregateflag = 0;
+            if ($env{'form.HIDE'.$prob}) {
+                my %record = &Apache::lonnet::restore($symbx,$env{'request.course.id'},$udom,$uname);
+                my ($version,$parts) = split(/:/,$env{'form.HIDE'.$prob},2);
+                my $numchgs = &makehidden($version,$parts,\%record,$symbx,$udom,$uname,1);
+                $hideflag += $numchgs;
+            }
 	    foreach my $partid (@{$parts}) {
 		my $newpts = $env{'form.GD_BOX'.$question.'_'.$partid};
 		my $oldpts = $env{'form.oldpts'.$question.'_'.$partid};
@@ -5067,8 +5084,11 @@ sub updateGradeByPage {
     $studentTable.=&Apache::loncommon::end_data_table();
     my $grademsg=($changeflag == 0 ? &mt('No score was changed or updated.') :
 		  &mt('The scores were changed for [quant,_1,problem].',
-		  $changeflag));
-    $request->print($grademsg.$studentTable);
+		  $changeflag).'<br />');
+    my $hidemsg=($hideflag == 0 ? '' :
+                 &mt('Submissions were marked "hidden" for [quant,_1,transaction].',
+                     $hideflag).'<br />');
+    $request->print($hidemsg.$grademsg.$studentTable);
 
     return '';
 }
@@ -5694,7 +5714,9 @@ sub get_scantron_config {
 =item username_to_idmap
 
     creates a hash keyed by student/employee ID with values of the corresponding
-    student username:domain.
+    student username:domain. If a single ID occurs for more than one student,
+    the status of the student is checked, and if Active, the value in the hash
+    will be set to the Active student.
 
   Arguments:
 
@@ -5712,8 +5734,17 @@ sub username_to_idmap {
     my ($classlist)= @_;
     my %idmap;
     foreach my $student (keys(%$classlist)) {
-	$idmap{$classlist->{$student}->[&Apache::loncoursedata::CL_ID]}=
-	    $student;
+        my $id = $classlist->{$student}->[&Apache::loncoursedata::CL_ID];
+        unless ($id eq '') {
+            if (!exists($idmap{$id})) {
+                $idmap{$id} = $student;
+            } else {
+                my $status = $classlist->{$student}->[&Apache::loncoursedata::CL_STATUS];
+                if ($status eq 'Active') {
+                    $idmap{$id} = $student;
+                }
+            }
+        }
     }
     return %idmap;
 }
@@ -6583,7 +6614,7 @@ sub scantron_warning_screen {
 	$scantron_config{'CODEstart'} &&
 	$scantron_config{'CODElength'}) {
 	$CODElist=$env{'form.scantron_CODElist'};
-	if ($env{'form.scantron_CODElist'} eq '') { $CODElist='<span class="LC_warning">None</span>'; }
+	if ($env{'form.scantron_CODElist'} eq '') { $CODElist='<span class="LC_warning">'.&mt('None').'</span>'; }
 	$CODElist=
 	    '<tr><td><b>'.&mt('List of CODES to validate against:').'</b></td><td><tt>'.
 	    $env{'form.scantron_CODElist'}.'</tt></td></tr>';
@@ -8216,7 +8247,7 @@ sub hand_bubble_option {
         return &mt('The sequence to be graded contains response types which are handgraded.').'<p>'.
                &mt('If you have already graded these by bubbling sheets to indicate points awarded, [_1]what point value is assigned to a filled last bubble in each row?','<br />').
                '<label><input type="radio" name="scantron_lastbubblepoints" value="'.$bubbles_per_row.'" checked="checked" />'.&mt('[quant,_1,point]',$bubbles_per_row).'</label>&nbsp;'.&mt('or').'&nbsp;'.
-               '<label><input type="radio" name="scantron_lastbubblepoints" value="0"/>0 points</label></p>';
+               '<label><input type="radio" name="scantron_lastbubblepoints" value="0" />'.&mt('0 points').'</label></p>';
     }
     return;
 }
@@ -8879,7 +8910,7 @@ sub scantron_download_scantron_data {
     &Apache::lonnet::allowuploaded('/adm/grades',$skipped);
     $r->print('
     <p>
-	'.&mt('[_1]Original[_2] file as uploaded by the bubblesheet office.',
+	'.&mt('[_1]Original[_2] file as uploaded by the bubblesheet scanning office.',
 	      '<a href="'.$orig.'">','</a>').'
     </p>
     <p>
@@ -9050,14 +9081,14 @@ sub checkscantron_results {
 '<td>'.&mt('Bubblesheet').'</td><td>'.$showscandata.'</td><td rowspan="2">'.$last.'</td><td rowspan="2">'.$pid.'</td>'."\n".
 '</tr>'."\n".
 '<tr class="'.$css_class.'">'."\n".
-'<td>Submissions</td><td>'.$showrecord.'</td></tr>'."\n";
+'<td>'.&mt('Submissions').'</td><td>'.$showrecord.'</td></tr>'."\n";
                     $passed ++;
                 } else {
                     my $css_class = ($failed % 2)?'LC_odd_row':'LC_even_row';
                     $badstudents .= '<tr class="'.$css_class.'"><td>'.&mt('Bubblesheet').'</td><td><span class="LC_nobreak">'.$scandata{$pid}.'</span></td><td rowspan="2">'.$last.'</td><td rowspan="2">'.$pid.'</td>'."\n".
 '</tr>'."\n".
 '<tr class="'.$css_class.'">'."\n".
-'<td>Submissions</td><td><span class="LC_nobreak">'.$record{$pid}.'</span></td>'."\n".
+'<td>'.&mt('Submissions').'</td><td><span class="LC_nobreak">'.$record{$pid}.'</span></td>'."\n".
 '</tr>'."\n";
                     $failed ++;
                 }