File:  [LON-CAPA] / loncom / homework / inputtags.pm
Revision 1.337: download - view: text, annotated - select for diffs
Mon Oct 5 02:35:40 2015 UTC (8 years, 8 months ago) by raeburn
Branches: MAIN
CVS tags: HEAD
- Slots can now require each student to check-in from a unique IP address
  when using a particular slot for a specific resource. The IP address of
  the student's computer is then tied to that student's access to the resource
  thereafter, until the end date of the slot.
  (Intended use case is high stakes online testing where students are using
  computers with statically assigned IP addresses).

    1: # The LearningOnline Network with CAPA
    2: # input  definitons
    3: #
    4: # $Id: inputtags.pm,v 1.337 2015/10/05 02:35:40 raeburn Exp $
    5: #
    6: # Copyright Michigan State University Board of Trustees
    7: #
    8: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
    9: #
   10: # LON-CAPA is free software; you can redistribute it and/or modify
   11: # it under the terms of the GNU General Public License as published by
   12: # the Free Software Foundation; either version 2 of the License, or
   13: # (at your option) any later version.
   14: #
   15: # LON-CAPA is distributed in the hope that it will be useful,
   16: # but WITHOUT ANY WARRANTY; without even the implied warranty of
   17: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   18: # GNU General Public License for more details.
   19: #
   20: # You should have received a copy of the GNU General Public License
   21: # along with LON-CAPA; if not, write to the Free Software
   22: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   23: #
   24: # /home/httpd/html/adm/gpl.txt
   25: #
   26: # http://www.lon-capa.org/
   27: 
   28: =pod
   29: 
   30: =head1 NAME
   31: 
   32: Apache::inputtags
   33: 
   34: =head1 SYNOPSIS
   35: 
   36: 
   37: 
   38: This is part of the LearningOnline Network with CAPA project
   39: described at http://www.lon-capa.org.
   40: 
   41: 
   42: =head1 NOTABLE SUBROUTINES
   43: 
   44: =over
   45: 
   46: =item 
   47: 
   48: =back
   49: 
   50: =cut
   51: 
   52: package Apache::inputtags;
   53: use HTML::Entities();
   54: use strict;
   55: use Apache::loncommon;
   56: use Apache::lonhtmlcommon;
   57: use Apache::lonlocal;
   58: use Apache::lonnet;
   59: use LONCAPA;
   60:  
   61: 
   62: BEGIN {
   63:     &Apache::lonxml::register('Apache::inputtags',('hiddensubmission','hiddenline','textfield','textline'));
   64: }
   65: 
   66: =pod
   67: 
   68: =item initialize_inputtags()
   69: 
   70: Initializes a set of global variables used during the parse of the problem.
   71: 
   72: @Apache::inputtags::input        - List of current input ids.
   73: @Apache::inputtags::inputlist    - List of all input ids seen this problem.
   74: @Apache::inputtags::response     - List of all current resopnse ids.
   75: @Apache::inputtags::responselist - List of all response ids seen this 
   76:                                      problem.
   77: @Apache::inputtags::hint         - List of all hint ids.
   78: @Apache::inputtags::hintlist     - List of all hint ids seen this problem.
   79: @Apache::inputtags::previous     - List describing if specific responseds
   80:                                      have been used
   81: @Apache::inputtags::previous_version - Submission responses were used in.
   82: $Apache::inputtags::part         - Current part id (valid only in 
   83:                                      <problem>)
   84:                                    0 if not in a part.
   85: @Apache::inputtags::partlist     - List of part ids seen in the current
   86:                                      <problem>
   87: @Apache::inputtags::status       - List of problem  statuses. First 
   88:                                    element is the status of the <problem>
   89:                                    the remainder are for individual <part>s.
   90: %Apache::inputtags::params       - Hash of defined parameters for the
   91:                                    current response.
   92: @Apache::inputtags::import       - List of all ids for <import> thes get
   93:                                    join()ed and prepended.
   94: @Apache::inputtags::importlist   - List of all import ids seen.
   95: $Apache::inputtags::response_with_no_part
   96:                                  - Flag set true if we have seen a response
   97:                                    that is not inside a <part>
   98: %Apache::inputtags::answertxt    - <*response> tags store correct
   99:                                    answer strings for display by <textline/>
  100:                                    in this hash.
  101: %Apache::inputtags::submission_display
  102:                                  - <*response> tags store improved display
  103:                                    of submission strings for display by part
  104:                                    end.
  105: 
  106: =cut
  107: 
  108: sub initialize_inputtags {
  109:     @Apache::inputtags::input=();
  110:     @Apache::inputtags::inputlist=();
  111:     @Apache::inputtags::response=();
  112:     @Apache::inputtags::responselist=();
  113:     @Apache::inputtags::hint=();
  114:     @Apache::inputtags::hintlist=();
  115:     @Apache::inputtags::previous=();
  116:     @Apache::inputtags::previous_version=();
  117:     $Apache::inputtags::part='';
  118:     @Apache::inputtags::partlist=();
  119:     @Apache::inputtags::status=();
  120:     %Apache::inputtags::params=();
  121:     @Apache::inputtags::import=();
  122:     @Apache::inputtags::importlist=();
  123:     $Apache::inputtags::response_with_no_part=0;
  124:     %Apache::inputtags::answertxt=();
  125:     %Apache::inputtags::submission_display=();
  126: }
  127: 
  128: #
  129: #  provides the onblur binding for spellchecking.  This could be an
  130: #  empty string if spellchecking was not enabled.
  131: #  Jquery selector binding is done rather than setting an onblur
  132: #  attribute because we'll need to set the element's spellcheck language
  133: #  option dynamically so we need $(this) to be defined.
  134: #
  135: # @param id   - The element id to bind.
  136: # @param lang - Language in which spellchecking is desired.
  137: #               if undef, nothing is generated.  
  138: # @return string - onblur specification to do the requested spellchecking.
  139: #
  140: sub spellcheck_onblur {
  141:     my ($id, $lang) = @_;
  142:     my $result = '';
  143:     if ($lang) {
  144: 
  145: 	$result = <<JAVASCRIPT;
  146: <script type="text/javascript">
  147: \$('\#$id').blur(function() {
  148:     doSpellcheck('\#$id', '$lang');
  149:  });
  150: </script>
  151: 
  152: JAVASCRIPT
  153: 
  154: 
  155:     }
  156:     return $result;
  157: }
  158: 
  159: sub check_for_duplicate_ids {
  160:     my %check;
  161:     foreach my $id (@Apache::inputtags::partlist,
  162: 		    @Apache::inputtags::responselist,
  163: 		    @Apache::inputtags::hintlist,
  164: 		    @Apache::inputtags::importlist) {
  165: 	$check{$id}++;
  166:     }
  167:     my @duplicates;
  168:     foreach my $id (sort(keys(%check))) {
  169: 	if ($check{$id} > 1) {
  170: 	    push(@duplicates,$id);
  171: 	}
  172:     }
  173:     if (@duplicates) {
  174: 	&Apache::lonxml::error("Duplicated ids found, problem will operate incorrectly. Duplicated ids seen: ",join(', ',@duplicates));
  175:     }
  176: }
  177: 
  178: sub start_input {
  179:     my ($parstack,$safeeval)=@_;
  180:     my $id = &Apache::lonxml::get_id($parstack,$safeeval);
  181:     push (@Apache::inputtags::input,$id);
  182:     push (@Apache::inputtags::inputlist,$id);
  183:     return $id;
  184: }
  185: 
  186: sub end_input {
  187:     pop @Apache::inputtags::input;
  188:     return '';
  189: }
  190: 
  191: sub addchars {
  192:     my ($fieldid,$addchars)=@_;
  193:     my $output='';
  194:     foreach (split(/\,/,$addchars)) {
  195: 	$output.='<a href="javascript:void(document.forms.lonhomework.'.
  196: 	    $fieldid.'.value+=\''.$_.'\')">'.$_.'</a> ';
  197:     }
  198:     return $output;
  199: }
  200: 
  201: sub start_textfield {
  202:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
  203:     my $result = "";
  204:     my $id = &start_input($parstack,$safeeval);
  205:     my $resid=$Apache::inputtags::response[-1];
  206:     if ($target eq 'web') {
  207: 	$Apache::lonxml::evaluate--;
  208: 	my $partid=$Apache::inputtags::part;
  209:         my ($oldresponse,$newvariation);
  210:         if ((($Apache::lonhomework::history{"resource.$partid.type"} eq 'randomizetry') ||
  211:              ($Apache::lonhomework::type eq 'randomizetry')) &&
  212:              ($Apache::inputtags::status[-1] eq 'CAN_ANSWER')) {
  213:             if ($env{'form.'.$partid.'.rndseed'} ne
  214:                 $Apache::lonhomework::history{"resource.$partid.rndseed"}) {
  215:                 $newvariation = 1;
  216:             }
  217:         }
  218:         unless ($newvariation) {
  219:             if ((($env{'form.grade_username'} eq '') && ($env{'form.grade_domain'} eq '')) ||
  220:                 (($env{'form.grade_username'} eq $env{'user.name'}) &&
  221:                  ($env{'form.grade_domain'} eq $env{'user.domain'}))) {
  222:                 $oldresponse = $Apache::lonhomework::history{"resource.$partid.$resid.submission"};
  223:             } elsif (($Apache::lonhomework::history{"resource.$partid.type"} eq 'anonsurvey') ||
  224:                     ($Apache::lonhomework::history{"resource.$partid.type"} eq 'anonsurveycred')) {
  225:                 $oldresponse = '* '.&mt('(only shown to submitter)').' *';
  226:             } else {
  227:                 $oldresponse = $Apache::lonhomework::history{"resource.$partid.$resid.submission"};
  228:             }
  229:         }
  230: 	if ($Apache::inputtags::status[-1] eq 'CAN_ANSWER') {
  231: 	    my $cols = &Apache::lonxml::get_param('cols',$parstack,$safeeval);
  232: 	    if ( $cols eq '') { $cols = 80; }
  233: 	    my $rows = &Apache::lonxml::get_param('rows',$parstack,$safeeval);
  234: 	    if ( $rows eq '') { $rows = 16; }
  235: 	    my $addchars=&Apache::lonxml::get_param('addchars',$parstack,$safeeval);
  236: 	    $result='';
  237: 	    my $tagident = 'HWVAL_' . $resid;
  238:             my $itemid = 'HWVAL_'.$partid.'_'.$resid;
  239: 	    if ($addchars) {
  240: 		$result.=&addchars($tagident, $addchars);
  241: 	    }
  242:             my $textareaclass;
  243:             unless (&Apache::londefdef::is_inside_of($tagstack,
  244:                                                     'externalresponse')) {
  245:                 $textareaclass = 'class="LC_richDetectHtml spellchecked"';
  246:             }
  247: 	    $result.= '<textarea wrap="hard" name="'.$tagident.'" id="'.$itemid.'" ' .
  248: 		      'rows="'.$rows.'" cols="'.$cols.'" '.$textareaclass
  249: 		      .'>'.
  250:                       &HTML::Entities::encode($oldresponse,'<>&"');
  251: 	    if ($oldresponse ne '') {
  252: 
  253: 		#get rid of any startup text if the user has already responded
  254: 		&Apache::lonxml::get_all_text("/textfield",$parser,$style);
  255: 	    }
  256: 	} else {
  257: 	    #show past answer in the essayresponse case
  258: 	    if ($oldresponse =~ /\S/
  259: 		&& &Apache::londefdef::is_inside_of($tagstack,
  260: 						    'essayresponse') ) {
  261: 		$result='<table class="LC_pastsubmission"><tr><td>'.
  262: 		    &HTML::Entities::encode($oldresponse,'"<>&').
  263:                     '</td></tr></table>';
  264: 	    }
  265: 	    #get rid of any startup text
  266: 	    &Apache::lonxml::get_all_text("/textfield",$parser,$style);
  267: 	}
  268:     } elsif ($target eq 'grade') {
  269: 	my $seedtext=&Apache::lonxml::get_all_text("/textfield",$parser,
  270: 						   $style);
  271: 	if ($seedtext eq $env{'form.HWVAL_'.$resid}) {
  272: 	    # if the seed text is still there it wasn't a real submission
  273: 	    $env{'form.HWVAL_'.$resid}='';
  274: 	}
  275:     } elsif ($target eq 'edit') {
  276: 	$result.=&Apache::edit::tag_start($target,$token);
  277: 	$result.=&Apache::edit::text_arg('Rows:','rows',$token,4);
  278: 	$result.=&Apache::edit::text_arg('Columns:','cols',$token,4);
  279: 	$result.=&Apache::edit::text_arg
  280: 	    ('Click-On Texts (comma sep):','addchars',$token,10);
  281: 	my $bodytext=&Apache::lonxml::get_all_text("/textfield",$parser,
  282: 						   $style);
  283: 	$result.=&Apache::edit::editfield($token->[1],$bodytext,'Text you want to appear by default:',80,2);
  284:         my $spell_langs = &spelling_languages();
  285: 	$result .= &Apache::edit::select_arg('Spellcheck for:', 'spellcheck',
  286: 					     $spell_langs, $token);
  287:     } elsif ($target eq 'modified') {
  288: 	my $constructtag=&Apache::edit::get_new_args($token,$parstack,
  289: 						     $safeeval,'rows','cols',
  290: 						     'addchars', 'spellcheck');
  291: 	if ($constructtag) {
  292: 	    $result = &Apache::edit::rebuild_tag($token);
  293: 	} else {
  294: 	    $result=$token->[4];
  295: 	}
  296: 	$result.=&Apache::edit::modifiedfield("/textfield",$parser);
  297:     } elsif ($target eq 'tex') {
  298: 	my $number_of_lines = &Apache::lonxml::get_param('rows',$parstack,$safeeval);
  299: 	my $width_of_box = &Apache::lonxml::get_param('cols',$parstack,$safeeval);
  300: 	if ($$tagstack[-2] eq 'essayresponse' and $Apache::lonhomework::type eq 'exam') {
  301: 	    $result = '\fbox{\fbox{\parbox{\textwidth-5mm}{';
  302: 	    for (my $i=0;$i<int $number_of_lines*2;$i++) {$result.='\strut \\\\ ';}
  303: 	    $result.='\strut \\\\\strut \\\\\strut \\\\\strut \\\\}}}';
  304: 	} else {
  305:             if ($env{'form.pdfFormFields'} eq 'yes') {
  306:                 my $fieldname = $env{'request.symb'}.
  307:                                 '&part_'. $Apache::inputtags::part.
  308:                                 '&textresponse'.
  309:                                 '&HWVAL_' . $Apache::inputtags::response['-1'];
  310:                 $result.='\TextField[name='.$fieldname.',multiline=true,height=6\baselineskip,width=270,borderwidth=0,backgroundcolor={.85 .85 .85}]\\';
  311:             } else {
  312:                 my $TeXwidth=$width_of_box/80;
  313:                 $result = '\vskip 1 mm \fbox{\fbox{\parbox{'.$TeXwidth.'\textwidth-5mm}{';
  314:                 for (my $i=0;$i<int $number_of_lines*2;$i++) {$result.='\strut \\\\ ';}
  315:                 $result.='}}}\vskip 2 mm ';
  316:             }
  317: 	}
  318:     }
  319:     return $result;
  320: }
  321: 
  322: sub end_textfield {
  323:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval)=@_;
  324:     my $result;
  325:     if ($target eq 'web') {
  326: 	my $spellcheck = &Apache::lonxml::get_param('spellcheck', $parstack, $safeeval);
  327: 	$Apache::lonxml::evaluate++;
  328: 	if ($Apache::inputtags::status[-1] eq 'CAN_ANSWER') {
  329:             my $partid=$Apache::inputtags::part;
  330: 	    my $resid = $Apache::inputtags::response[-1];
  331: 	    my $itemid = 'HWVAL_' . $partid . '_' . $resid;
  332: 	    my $result =  "</textarea>";
  333: 	    $result .= &spellcheck_onblur($itemid, $spellcheck);
  334: 	    return $result;
  335: 	}
  336:     } elsif ($target eq 'edit') {
  337: 	$result=&Apache::edit::end_table();
  338:     }
  339:     &end_input;
  340:     return $result;
  341: }
  342: 
  343: sub exam_score_line {
  344:     my ($target) = @_;
  345: 
  346:     my $result;
  347:     if ($target eq 'tex') {
  348: 	my $repetition = &Apache::response::repetition();
  349: 	$result.='\begin{enumerate}';
  350: 	if ($env{'request.state'} eq "construct" ) {$result.='\item[\strut]';}
  351: 	foreach my $i (0..$repetition-1) {
  352: 	    $result.='\item[\textbf{'.
  353: 		($Apache::lonxml::counter+$i).
  354: 		'}.]\textit{Leave blank on scoring form}\vskip 0 mm';
  355: 	}
  356: 	$result.= '\end{enumerate}';
  357:     }
  358: 
  359:     return $result;
  360: }
  361: 
  362: sub exam_box {
  363:     my ($target) = @_;
  364:     my $result;
  365: 
  366:     if ($target eq 'tex') {
  367: 	$result .= '\fbox{\fbox{\parbox{\textwidth-5mm}{\strut\\\\\strut\\\\\strut\\\\\strut\\\\}}}';
  368: 	$result .= &exam_score_line($target);
  369:     } elsif ($target eq 'web') {
  370: 	my $id=$Apache::inputtags::response[-1];
  371: 	$result.= '<br /><br />
  372:                    <textarea name="HWVAL_'.$id.'" rows="4" cols="50">
  373:                    </textarea> <br /><br />';
  374:     }
  375:     return $result;
  376: }
  377: 
  378: sub needs_exam_box {
  379:     my ($tagstack) = @_;
  380:     my @tags = ('formularesponse',
  381: 		'stringresponse',
  382: 		'reactionresponse',
  383: 		'organicresponse',
  384: 		);
  385: 
  386:     foreach my $tag (@tags) {
  387: 	if (grep(/\Q$tag\E/,@$tagstack)) {
  388: 	    return 1;
  389: 	}
  390:     }
  391:     return 0;
  392: }
  393: 
  394: sub start_textline {
  395:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval)=@_;
  396:     my $result = "";
  397:     my $input_id = &start_input($parstack,$safeeval);
  398: 
  399:     # The spellcheck attribute 
  400:     # 1. enables spellchecking.
  401:     # 2. Provides the language code in which the spellchecking will be performed.
  402: 
  403:     my $spellcheck = &Apache::lonxml::get_param('spellcheck', $parstack, $safeeval);
  404:     if ($target eq 'web') {
  405: 	$Apache::lonxml::evaluate--;
  406: 	my $partid=$Apache::inputtags::part;
  407: 	my $id=$Apache::inputtags::response[-1];
  408: 	if (!&Apache::response::show_answer()) {
  409: 	    my $size = &Apache::lonxml::get_param('size',$parstack,$safeeval);
  410: 	    my $maxlength;
  411: 	    if ($size eq '') { $size=20; } else {
  412: 		if ($size < 20) {
  413: 		    $maxlength = ' maxlength="'.$size.'"';
  414: 		}
  415: 	    }
  416:             my ($oldresponse,$newvariation);
  417:             if ((($Apache::lonhomework::history{"resource.$partid.type"} eq 'randomizetry') ||
  418:                  ($Apache::lonhomework::type eq 'randomizetry')) &&
  419:                  ($Apache::inputtags::status[-1] eq 'CAN_ANSWER')) {
  420:                 if ($env{'form.'.$partid.'.rndseed'} ne
  421:                     $Apache::lonhomework::history{"resource.$partid.rndseed"}) {
  422:                     $newvariation = 1;
  423:                 }
  424:             }
  425:             unless ($newvariation) {
  426:                 if ((($env{'form.grade_username'} eq '') && ($env{'form.grade_domain'} eq '')) ||
  427:                     (($env{'form.grade_username'} eq $env{'user.name'}) &&
  428:                      ($env{'form.grade_domain'} eq $env{'user.domain'}))) {
  429:                     $oldresponse = $Apache::lonhomework::history{"resource.$partid.$id.submission"};
  430:                 } elsif (($Apache::lonhomework::history{"resource.$partid.type"} eq 'anonsurvey') ||
  431:                         ($Apache::lonhomework::history{"resource.$partid.type"} eq 'anonsurveycred') ||
  432:                         ($Apache::lonhomework::type eq 'anonsurvey') ||
  433:                         ($Apache::lonhomework::type eq 'anonsurveycred')) {
  434:                         $oldresponse = '* '.&mt('(only shown to submitter)').' *';
  435:                 } else {
  436:                     $oldresponse = $Apache::lonhomework::history{"resource.$partid.$id.submission"};
  437:                 }
  438: 	        &Apache::lonxml::debug("oldresponse $oldresponse is ".ref($oldresponse));
  439: 	        if (ref($oldresponse) eq 'ARRAY') {
  440: 		    $oldresponse = $oldresponse->[$#Apache::inputtags::inputlist];
  441: 	        }
  442: 	        $oldresponse = &HTML::Entities::encode($oldresponse,'<>&"');
  443:                 $oldresponse =~ s/^\s+//;
  444:                 $oldresponse =~ s/\s+$//;
  445:                 $oldresponse =~ s/\s+/ /g;
  446:             }
  447: 	    if ($Apache::lonhomework::type ne 'exam') {
  448: 		my $addchars=&Apache::lonxml::get_param('addchars',$parstack,$safeeval);
  449: 		$result='';
  450: 		if ($addchars) {
  451: 		    $result.=&addchars('HWVAL_'.$id,$addchars);
  452: 		}
  453: 		my $readonly=&Apache::lonxml::get_param('readonly',$parstack,
  454: 							$safeeval);
  455: 		if (lc($readonly) eq 'yes' 
  456: 		    || $Apache::inputtags::status[-1] eq 'CANNOT_ANSWER') {
  457: 		    $readonly=' readonly="readonly" ';
  458: 		} else {
  459: 		    $readonly='';
  460: 		}
  461: 		my $name = 'HWVAL_'.$id;
  462:                 my $itemid = 'HWVAL_'.$partid.'_'.$id;
  463: 		if ($Apache::inputtags::status[-1] eq 'CANNOT_ANSWER') {
  464: 		    $name = "none";
  465: 		}
  466: 		$result.= '<input onkeydown="javascript:setSubmittedPart(\''.$partid.'\');"'
  467: 		     . ' onfocus="javascript:disableAutoComplete(\''.$itemid.'\');"'
  468: 		     . ' type="text" '.$readonly.' name="'. $name . '"'
  469: 		     . ' id="' . $itemid . '"'
  470: 		     . ' value="'.  $oldresponse.'"'
  471: 		     . ' class="LC_textline spellchecked"  size="'.$size.'"'.$maxlength.' />';
  472: 
  473: 		$result .= &spellcheck_onblur($itemid, $spellcheck);
  474: 	    }
  475: 	    if ($Apache::lonhomework::type eq 'exam'
  476: 		&& &needs_exam_box($tagstack)) {
  477: 		$result.=&exam_box($target);
  478: 	    }
  479: 	} else {
  480: 	    #right or wrong don't show what was last typed in.
  481: 	    my $count = scalar(@Apache::inputtags::inputlist)-1;
  482: 	    $result='<b>'.$Apache::inputtags::answertxt{$id}[$count].'</b>';
  483: 	    #$result='';
  484: 	}
  485:     } elsif ($target eq 'edit') {
  486: 	$result=&Apache::edit::tag_start($target,$token);
  487: 	$result.=&Apache::edit::text_arg('Size:','size',$token,'5').
  488: 	    &Apache::edit::text_arg('Click-On Texts (comma sep):',
  489: 				    'addchars',$token,10);
  490:         $result.=&Apache::edit::select_arg('Readonly:','readonly',
  491: 					   ['no','yes'],$token);
  492:         my $spell_langs = &spelling_languages();
  493: 	$result.=&Apache::edit::select_arg('Spellcheck for:', 'spellcheck',
  494: 					   $spell_langs, $token);
  495: 	$result.=&Apache::edit::end_row();
  496: 	$result.=&Apache::edit::end_table();
  497:     } elsif ($target eq 'modified') {
  498: 	my $constructtag=&Apache::edit::get_new_args($token,$parstack,
  499: 						     $safeeval,'size',
  500: 						     'addchars','readonly', 'spellcheck');
  501: 	if ($constructtag) { $result = &Apache::edit::rebuild_tag($token); }
  502:     } elsif ($target eq 'tex' 
  503: 	     && $Apache::lonhomework::type ne 'exam') {
  504: 	my $size = &Apache::lonxml::get_param('size',$parstack,$safeeval);
  505: 	if ($size != 0) {$size=$size*2; $size.=' mm';} else {$size='40 mm';}
  506: 	if ($env{'form.pdfFormFields'} eq 'yes'
  507:             && $Apache::inputtags::status[-1] eq 'CAN_ANSWER') {
  508:             my $fieldname = $env{'request.symb'}.
  509:                                  '&part_'. $Apache::inputtags::part.
  510:                                  '&textresponse'.
  511:                                  '&HWVAL_' . $Apache::inputtags::response['-1'];
  512:             $result='\textField{'.$fieldname.'}{'.$size.'}{12 bp}';
  513:         } else {
  514:             $result='\framebox['.$size.'][s]{\tiny\strut}';
  515:         }
  516:     } elsif ($target eq 'tex' 
  517: 	     && $Apache::lonhomework::type eq 'exam'
  518: 	     && &needs_exam_box($tagstack)) {
  519: 	$result.=&exam_box($target);
  520:     }
  521:     return $result;
  522: }
  523: 
  524: sub end_textline {
  525:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval)=@_;
  526:     if    ($target eq 'web') { $Apache::lonxml::evaluate++; }
  527:     elsif ($target eq 'edit') { return ('','no'); }
  528:     &end_input();
  529:     return "";
  530: }
  531: 
  532: sub start_hiddenline {
  533:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval)=@_;
  534:     my $result = "";
  535:     my $input_id = &start_input($parstack,$safeeval);
  536:     if ($target eq 'web') {
  537: 	$Apache::lonxml::evaluate--;
  538: 	if ($Apache::inputtags::status[-1] eq 'CAN_ANSWER') {
  539: 	    my $partid=$Apache::inputtags::part;
  540: 	    my $id=$Apache::inputtags::response[-1];
  541: 	    my $oldresponse = $Apache::lonhomework::history{"resource.$partid.$id.submission"};
  542: 	    if (ref($oldresponse) eq 'ARRAY') {
  543: 		$oldresponse = $oldresponse->[$#Apache::inputtags::inputlist];
  544: 	    }
  545: 	    $oldresponse = &HTML::Entities::encode($oldresponse,'<>&"');
  546: 
  547: 	    if ($Apache::lonhomework::type ne 'exam') {
  548: 		$result= '<input type="hidden" name="HWVAL_'.$id.'" value="'.
  549: 		    $oldresponse.'" />';
  550: 	    }
  551: 	}
  552:     } elsif ($target eq 'edit') {
  553: 	$result=&Apache::edit::tag_start($target,$token);
  554: 	$result.=&Apache::edit::end_table;
  555:     }
  556: 
  557:     if ( ($target eq 'web' || $target eq 'tex')
  558: 	 && $Apache::lonhomework::type eq 'exam'
  559: 	 && &needs_exam_box($tagstack)) {
  560: 	$result.=&exam_box($target);
  561:     }
  562:     return $result;
  563: }
  564: 
  565: sub end_hiddenline {
  566:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval)=@_;
  567:     if    ($target eq 'web') { $Apache::lonxml::evaluate++; }
  568:     elsif ($target eq 'edit') { return ('','no'); }
  569:     &end_input();
  570:     return "";
  571: }
  572: 
  573: 
  574: sub start_hiddensubmission {
  575:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval)=@_;
  576:     my $result = "";
  577:     my $input_id = &start_input($parstack,$safeeval);
  578:     if ($target eq 'web') {
  579:         $Apache::lonxml::evaluate--;
  580:         if ($Apache::inputtags::status[-1] eq 'CAN_ANSWER') {
  581:             my $partid=$Apache::inputtags::part;
  582:             my $id=$Apache::inputtags::response[-1];
  583:             if ($Apache::lonhomework::type ne 'exam') {
  584:                 my $value = &Apache::lonxml::get_param('value',$parstack,$safeeval);
  585:                 $value = &HTML::Entities::encode($value,'<>&"');
  586:                 $result= '<input type="hidden" name="HWVAL_'.$id.'" value="'.$value.'" />';
  587:             }
  588:         }
  589:     } elsif ($target eq 'edit') {
  590:         $result=&Apache::edit::tag_start($target,$token);
  591:         $result.=&Apache::edit::text_arg('Value:','value',$token,'15');
  592:         $result.=&Apache::edit::end_row();
  593:         $result.=&Apache::edit::end_table();
  594:     } elsif ($target eq 'modified') {
  595:         my $constructtag=&Apache::edit::get_new_args($token,$parstack,
  596:                                                      $safeeval,'value');
  597:         if ($constructtag) { $result = &Apache::edit::rebuild_tag($token); }
  598:     }
  599: 
  600:     if ( ($target eq 'web' || $target eq 'tex')
  601:          && $Apache::lonhomework::type eq 'exam'
  602:          && &needs_exam_box($tagstack)) {
  603:         $result.=&exam_box($target);
  604:     }
  605:     return $result;
  606: }
  607: 
  608: sub end_hiddensubmission {
  609:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval)=@_;
  610:     if    ($target eq 'web') { $Apache::lonxml::evaluate++; }
  611:     elsif ($target eq 'edit') { return ('','no'); }
  612:     &end_input();
  613:     return "";
  614: }
  615: 
  616: =pod
  617: 
  618: =item file_selector()
  619: 
  620: $part -> partid
  621: $id -> responseid
  622: $uploadefiletypes -> comma seperated list of extensions allowed or * for any
  623: $which -> 'uploadonly'  -> only newly uploaded files
  624:           'portfolioonly' -> only allow files from portfolio
  625:           'both' -> allow files from either location
  626: $extratext -> additional text to go between the link and the input box
  627: $maxfilesize -> maximum cumulative filesize for submitted files (in MB).
  628: returns a table row <tr> 
  629: 
  630: =cut
  631: 
  632: sub file_selector {
  633:     my ($part,$id,$uploadedfiletypes,$which,$extratext,$maxfilesize)=@_;
  634:     if (!$uploadedfiletypes) { return ''; }
  635: 
  636:     my $jspart=$part;
  637:     $jspart=~s/\./_/g;
  638: 
  639:     my $result;
  640:     my $current_files_display = &current_file_submissions($part,$id);
  641:     my $addfiles;
  642:     if ($current_files_display) {
  643:         $result .= &Apache::lonhtmlcommon::row_title(&mt('Files currently selected for submission')).
  644:                    $current_files_display.
  645:                    &Apache::lonhtmlcommon::row_closure();
  646:         $addfiles = &mt('Submit other file(s)');
  647:     } else {
  648:         $addfiles = &mt('Choose file(s) to submit');
  649:     }
  650:     $result .= &Apache::lonhtmlcommon::row_title($addfiles);
  651:     my $constraints;
  652:     if ($uploadedfiletypes ne '*') {
  653: 	$constraints =
  654: 	    &mt('Allowed filetypes: [_1]','<b>'.$uploadedfiletypes.'</b>').'<br />';
  655:     }
  656:     if ($maxfilesize) {
  657:         $constraints .= &mt('Combined size of all files not to exceed: [_1] MB.',
  658:                         '<b>'.$maxfilesize.'</b>').'<br />';
  659:     }
  660:     if ($constraints) {
  661:         $result .= $constraints.'<br />';
  662:     }
  663:     if ($which eq 'uploadonly' || $which eq 'both') { 
  664:         my $free_space = $maxfilesize * 1048576;
  665:         $result .= &mt('Submit a file: (only one file per submission)').
  666:             ' <br /><input type="file" size="50" name="HWFILE'.$jspart.'_'.$id.
  667:             '" id="HWFILE'.$jspart.'_'.$id.'" class="flUpload" /><br />'.
  668:             '<input type="hidden" id="free_space" value="'.$free_space.'" /><br />'
  669:         }
  670:     if ( $which eq 'both') {
  671: 	$result.='<br />'.'<strong>'.&mt('OR:').'</strong><br />';
  672:     }
  673:     if ($which eq 'portfolioonly' || $which eq 'both') {
  674:         my $symb = $env{'request.symb'};
  675:         (undef,undef,my $res)=&Apache::lonnet::decode_symb($symb);
  676:         my $showsymb;
  677:         # If resource is a .task and URL is unencrypted, include symb in query string
  678:         # for url opened in portfolio file selection window. Can be used to override
  679:         # blocking of portfolio access resulting from an exam event in a different course. 
  680:         if ($res =~ /\.task$/i) {
  681:             my $encsymb = &Apache::lonenc::check_encrypt($symb);
  682:             if ($symb eq $encsymb) {
  683:                 $showsymb = $symb;
  684:             }
  685:         }
  686: 	$result.=$extratext.'<a href='."'".'javascript:void(window.open("/adm/portfolio?mode=selectfile&amp;fieldname='.$env{'form.request.prefix'}.'HWPORT'.$jspart.'_'.$id.'&amp;symb='.$showsymb.'","cat","height=600,width=800,scrollbars=1,resizable=1,menubar=2,location=1"))'."'".'>'.
  687: 	    &mt('Select Portfolio Files: (one or more files per submission)').'</a><br />'.
  688: 	    '<input type="text" size="50" name="HWPORT'.$jspart.'_'.$id.'" value="" />'.
  689: 	    '<br />';
  690: 
  691:     }
  692:     $result.=&Apache::lonhtmlcommon::row_closure(1);
  693:     return $result;
  694: }
  695: 
  696: sub current_file_submissions {
  697:     my ($part,$id) = @_;
  698:     my $jspart=$part;
  699:     $jspart=~s/\./_/g;
  700:     my $uploadedfile=$Apache::lonhomework::history{"resource.$part.$id.uploadedfile"};
  701:     my $portfiles=$Apache::lonhomework::history{"resource.$part.$id.portfiles"};
  702:     return if (($uploadedfile eq '') && ($portfiles !~/[^\s]/));
  703:     my @unversioned;
  704:     foreach my $file (split(/\s*,\s*/,&unescape($portfiles))) {
  705:         my ($path,$name) = ($file =~ m{^(.*/)([^/]+)$});
  706:         my ($origname,$version,$ext) = &Apache::lonnet::file_name_version_ext($name);
  707:         unless ($version) {
  708:             push(@unversioned,$file);
  709:         }    
  710:     }
  711:     return if (!@unversioned);
  712:     my $header = &portpath_popup_js().
  713:                  &Apache::loncommon::start_data_table().
  714:                  &Apache::loncommon::start_data_table_header_row();
  715:     if ($Apache::inputtags::status[-1] eq 'CAN_ANSWER') {
  716:         $header .= '<th>'.&mt('Delete?').'</th>';
  717:     }
  718:     $header .=   '<th>'.&mt('File').'</th>'.
  719:                  '<th>'.&mt('Size (MB)').'</th>'.
  720:                  '<th>'.&mt('Last Modified').'</th>'.
  721:                  &Apache::loncommon::end_data_table_header_row();
  722:     my ($symb,$crsid,$udom,$uname)=&Apache::lonnet::whichuser();
  723:     my ($cdom,$cnum) = ($crsid =~ /^($LONCAPA::match_domain)_($LONCAPA::match_courseid)$/);
  724:     my ($result,$header_shown,%okfiles,%rows,%legacy,@bad_file_list);
  725:     if ($uploadedfile) {
  726:         my $url=$Apache::lonhomework::history{"resource.$part.$id.uploadedurl"};
  727:         my $link = &HTML::Entities::encode($url,'<>&"');
  728:         my ($path,$name) = ($url =~ m{^(/uploaded/\Q$udom\E/\Q$uname\E/essayresponse.*/)([^/]+)$});
  729:         my ($status,$hashref,$error) =
  730:             &current_file_info($url,$link,$name,$path);
  731:         if ($status eq 'ok') {
  732:             push(@{$okfiles{$name}},$url);
  733:             $rows{$url} = $hashref;
  734:             $legacy{$url} = 1;
  735:             &Apache::lonxml::extlink($url);
  736:             &Apache::lonnet::allowuploaded('/adm/essayresponse',$url);
  737:         } else {
  738:             push(@bad_file_list,$error);
  739:         }
  740:     }
  741:     if (@unversioned > 0) {
  742:         my $prefix = "/uploaded/$udom/$uname/portfolio";
  743:         foreach my $file (@unversioned) {
  744:             my ($path,$name) = ($file =~ m{^(.*/)([^/]+)$});
  745:             my $url = $prefix.$path.$name;
  746:             my $uploadedfile = &HTML::Entities::encode($url,'<>&"');
  747:             my ($status,$hashref,$error) =
  748:                 &current_file_info($url,$uploadedfile,$name,$path);
  749:             if ($status eq 'ok') {
  750:                 push(@{$okfiles{$name}},$url);
  751:                 $rows{$url} = $hashref;
  752:             } else {
  753:                 push(@bad_file_list,$error);
  754:             }
  755:         }
  756:     }
  757:     my $num = 0;
  758:     foreach my $name (sort(keys(%okfiles))) {
  759:         if (ref($okfiles{$name}) eq 'ARRAY') {
  760:             foreach my $url (@{$okfiles{$name}}) {
  761:                 if (ref($rows{$url}) eq 'HASH') {
  762:                     my $link = $rows{$url}{link};
  763:                     my $portfile = $rows{$url}{path}.$rows{$url}{name};
  764:                     $portfile = &HTML::Entities::encode($portfile,'<>&"');
  765:                     if ($link) {
  766:                         my $icon=&Apache::loncommon::icon($url);
  767:                         unless ($header_shown) {
  768:                             $result .= $header;
  769:                             $header_shown = 1;
  770:                         }
  771:                         $result.=
  772:                             &Apache::loncommon::start_data_table_row()."\n";
  773:                         if ($Apache::inputtags::status[-1] eq 'CAN_ANSWER') {
  774:                             $result .=
  775:                                  '<td valign="bottom"><input type="checkbox" name="HWFILE'.$jspart.'_'.$id.'_delete"'.
  776:                                  ' value="'.$portfile.'" id="HWFILE'.$jspart.'_'.$id.'_'.$num.'_delete" /></td>'."\n";
  777:                             $num ++;
  778:                         }
  779:                         my $pathid = 'HWFILE'.$jspart.'_'.$id.'_'.$num.'_path';
  780:                         my $pathidtext = $pathid.'text';
  781:                         my ($showname,$showpath);
  782:                         if ($legacy{$url}) {
  783:                             $showname = $name.' '.&mt('not in portfolio');
  784:                         } else {
  785:                             $showname = $name;
  786:                             $showpath = '<br />'. 
  787:                                         '<span id="'.$pathidtext.'" class="LC_cusr_subheading">'.
  788:                                         '<a href="javascript:showPortPath('."'$pathid','$pathidtext'".');" '.
  789:                                         'class="LC_menubuttons_link">'.
  790:                                         &mt('(Show path)').'</a></span>'.
  791:                                         '<div id="'.$pathid.'" class="LC_dccid">'.$rows{$url}{path}.$name.
  792: '</div>';
  793:                         }
  794:                         $result .= 
  795:                             '<td><a href="'.$link.'"><img src="'.$icon.
  796:                             '" border="0" alt="" />'.$showname.'</a>'.$showpath.'</td>'."\n".
  797:                             '<td align="right" valign="bottom">'.$rows{$url}{size}.'</td>'."\n".
  798:                             '<td align="right" valign="bottom">'.$rows{$url}{lastmodified}.'</td>'."\n".
  799:                             &Apache::loncommon::end_data_table_row();
  800:                     }
  801:                 }
  802:             }
  803:         }
  804:     }
  805:     if ($header_shown) {
  806:         $result .= &Apache::loncommon::end_data_table();
  807:         if ($Apache::inputtags::status[-1] eq 'CAN_ANSWER') {
  808:             $result .= '<br /><span class="LC_warning">'.
  809:                        &mt('Exclude existing file(s) from grading by checking the "Delete?" checkbox(es) and clicking "Submit Answer"').'</span>';
  810:         }
  811:     }
  812:     if (@bad_file_list) {
  813:         my $bad_files = '<span class="LC_filename">'.
  814:             join('</span>, <span class="LC_filename">',@bad_file_list).
  815:             '</span>';
  816:         $result.='<p class="LC_error">'.
  817:                  &mt("These file(s) don't exist: [_1]",$bad_files).
  818:                  '</p>';
  819:     }
  820:     return $result;
  821: }
  822: 
  823: sub current_file_info {
  824:     my ($url,$uploadedfile,$name,$path) = @_;
  825:     my ($status,$error,%info);
  826:     my @stat = &Apache::lonnet::stat_file($url);
  827:     if ((@stat) && ($stat[0] ne 'no_such_dir')) {
  828:         my ($lastmod,$size);
  829:         if ($stat[9] =~ /^\d+$/) {
  830:             $lastmod = &Apache::lonlocal::locallocaltime($stat[9]);
  831:         }
  832:         $size = $stat[7]/(1024*1024);
  833:         $size = sprintf("%.3f",$size);
  834:         %info = (
  835:                     link         => $uploadedfile,
  836:                     name         => $name,
  837:                     path         => $path,
  838:                     size         => $size,
  839:                     lastmodified => $lastmod,
  840:                 );
  841:         $status = 'ok';
  842:     } else {
  843:         &Apache::lonnet::logthis("bad file is $url");
  844:         my $icon=&Apache::loncommon::icon($url);
  845:         $error = '<a href="'.$url.'"><img src="'.$icon.
  846:                  '" border="0" />'.$uploadedfile.'</a>';
  847:     }
  848:     return ($status,\%info,$error);
  849: }
  850: 
  851: sub portpath_popup_js {
  852:     my %lt = &Apache::lonlocal::texthash(
  853:                                           show => '(Show path)',
  854:                                           hide => '(Hide)',
  855:                                         );
  856:     return <<"END";
  857: <script type="text/javascript"> 
  858: // <![CDATA[
  859: 
  860: function showPortPath(id,idtext) {
  861:     document.getElementById(id).style.display='block';
  862:     document.getElementById(id).style.textAlign='left';
  863:     document.getElementById(id).style.textFace='normal';
  864:     if (document.getElementById(idtext)) {
  865:         document.getElementById(idtext).innerHTML ='<a href="javascript:hidePortPath(\\''+id+'\\',\\''+idtext+'\\'); '+
  866:                                                    '"class="LC_menubuttons_link">$lt{'hide'}</a>&nbsp;';
  867:     }
  868:     return;
  869: }
  870: 
  871: function hidePortPath(id,idtext) {
  872:     if (document.getElementById(id)) {
  873:         document.getElementById(id).style.display='none';
  874:     }
  875:     if (document.getElementById(idtext)) {
  876:         document.getElementById(idtext).innerHTML ='<a href="javascript:showPortPath(\\''+id+'\\',\\''+idtext+'\\');" '+
  877:                                                    'class="LC_menubuttons_link">$lt{'show'}</a>';
  878:     }
  879:     return;
  880: }
  881: 
  882: // ]]>
  883: </script>
  884: 
  885: END
  886: }
  887: 
  888: sub valid_award {
  889:     my ($award) =@_;
  890:     foreach my $possibleaward ('EXTRA_ANSWER','MISSING_ANSWER', 'ERROR',
  891: 			       'NO_RESPONSE','WRONG_NUMBOXESCHECKED',
  892: 			       'TOO_LONG', 'UNIT_INVALID_INSTRUCTOR',
  893: 			       'UNIT_INVALID_STUDENT', 'UNIT_IRRECONCIBLE',
  894: 			       'UNIT_FAIL', 'NO_UNIT',
  895: 			       'UNIT_NOTNEEDED', 'WANTED_NUMERIC',
  896: 			       'BAD_FORMULA', 'NOT_FUNCTION', 'WRONG_FORMAT', 
  897:                                'INTERNAL_ERROR', 'SIG_FAIL', 'INCORRECT', 
  898: 			       'MISORDERED_RANK', 'INVALID_FILETYPE',
  899:                                'EXCESS_FILESIZE', 'FILENAME_INUSE', 
  900: 			       'DRAFT', 'SUBMITTED', 'SUBMITTED_CREDIT', 
  901:                                'ANONYMOUS', 'ANONYMOUS_CREDIT',
  902:                                'ASSIGNED_SCORE', 'APPROX_ANS',
  903: 			       'EXACT_ANS','COMMA_FAIL') {
  904: 	if ($award eq $possibleaward) { return 1; }
  905:     }
  906:     return 0;
  907: }
  908: 
  909: {
  910:     my @awards = ('EXTRA_ANSWER', 'MISSING_ANSWER', 'ERROR', 'NO_RESPONSE',
  911: 		  'WRONG_NUMBOXESCHECKED','TOO_LONG',
  912: 		  'UNIT_INVALID_INSTRUCTOR', 'UNIT_INVALID_STUDENT',
  913: 		  'UNIT_IRRECONCIBLE', 'UNIT_FAIL', 'NO_UNIT',
  914: 		  'UNIT_NOTNEEDED', 'WANTED_NUMERIC', 'BAD_FORMULA',  'NOT_FUNCTION', 
  915:                   'WRONG_FORMAT', 'INTERNAL_ERROR',
  916: 		  'COMMA_FAIL', 'SIG_FAIL', 'INCORRECT', 'MISORDERED_RANK',
  917: 		  'INVALID_FILETYPE', 'EXCESS_FILESIZE', 'FILENAME_INUSE', 
  918:                   'DRAFT', 'SUBMITTED',
  919:                   'SUBMITTED_CREDIT', 'ANONYMOUS', 'ANONYMOUS_CREDIT',
  920:                   'ASSIGNED_SCORE', 'APPROX_ANS', 'EXACT_ANS');
  921:     my $i=0;
  922:     my %fwd_awards = map { ($_,$i++) } @awards;
  923:     my $max=scalar(@awards);
  924:     @awards=reverse(@awards);
  925:     $i=0;
  926:     my %rev_awards = map { ($_,$i++) } @awards;
  927: 
  928: sub awarddetail_to_awarded {
  929:     my ($awarddetail) = @_;
  930:     if ($awarddetail eq 'EXACT_ANS'
  931: 	|| $awarddetail eq 'APPROX_ANS') {
  932: 	return 1;
  933:     }
  934:     return 0;
  935: }
  936: 
  937: sub hide_award {
  938:     my ($award) = @_;
  939:     if (&Apache::lonhomework::show_no_problem_status()) {
  940: 	return 1;
  941:     }
  942:     if ($award =~
  943: 	/^(?:EXACT_ANS|APPROX_ANS|SUBMITTED|SUBMITTED_CREDIT|ANONYMOUS|ANONYMOUS_CREDIT|ASSIGNED_SCORE|INCORRECT)/) {
  944: 	return 1;
  945:     }
  946:     return 0;
  947: }
  948: 
  949: sub finalizeawards {
  950:     my ($awardref,$msgref,$nameref,$reverse,$final_scantron)=@_;
  951:     my $result;
  952:     if ($#$awardref == -1) { $result = "NO_RESPONSE"; }
  953:     if ($result eq '' ) {
  954: 	my $blankcount;
  955: 	foreach my $award (@$awardref) {
  956: 	    if ($award eq '') {
  957: 		$result='MISSING_ANSWER';
  958: 		$blankcount++;
  959: 	    }
  960: 	}
  961: 	if ($blankcount == ($#$awardref + 1)) {
  962: 	    return ('NO_RESPONSE');
  963: 	}
  964:     }
  965: 
  966:     if ($Apache::lonxml::internal_error) { $result='INTERNAL_ERROR'; }
  967: 
  968:     if (!$final_scantron && defined($result)) { return ($result); }
  969: 
  970:     # if in scantron mode, if the award for any response is 
  971:     # assigned score, then the part gets an assigned score
  972:     if ($final_scantron 
  973: 	&& grep {$_ eq 'ASSIGNED_SCORE'} (@$awardref)) {
  974: 	return ('ASSIGNED_SCORE');
  975:     }
  976: 
  977:     # if in scantron mode, if the award for any response is 
  978:     # correct and there are non-correct responses,
  979:     # then the part gets an assigned score
  980:     if ($final_scantron 
  981: 	&& (grep { $_ eq 'EXACT_ANS' ||
  982: 		   $_ eq 'APPROX_ANS'  } (@$awardref))
  983: 	&& (grep { $_ ne 'EXACT_ANS' &&
  984: 		   $_ ne 'APPROX_ANS'  } (@$awardref))) {
  985: 	return ('ASSIGNED_SCORE');
  986:     }
  987:     # these awards are ordered from most important error through best correct
  988:     my $awards = (!$reverse) ? \%fwd_awards : \%rev_awards ;
  989: 
  990:     my $best = $max;
  991:     my $j=0;
  992:     my $which;
  993:     foreach my $award (@$awardref) {
  994: 	if ($awards->{$award} < $best) {
  995: 	    $best  = $awards->{$award};
  996: 	    $which = $j;
  997: 	}
  998: 	$j++;
  999:     }
 1000: 
 1001:     # if at least one response item is set to include lenient grading
 1002:     # and that item is partially correct then overall award reflects
 1003:     # that, unless an award for one of the other response items does
 1004:     # not fall within the basic awards for correct or incorrect.
 1005:     if ($Apache::inputtags::leniency) {
 1006:         if (($$awardref[$which] eq 'INCORRECT')
 1007:             && (grep { $_ eq 'EXACT_ANS' ||
 1008:                        $_ eq 'APPROX_ANS' ||
 1009:                        $_ eq 'ASSIGNED_SCORE' } (@$awardref))
 1010:             && !((grep { $_ ne 'INCORRECT' &&
 1011:                          $_ ne 'EXACT_ANS' &&
 1012:                          $_ ne 'APPROX_ANS' &&
 1013:                          $_ ne 'ASSIGNED_SCORE' } (@$awardref)))) {
 1014:             return ('ASSIGNED_SCORE');
 1015:         }
 1016:     }
 1017: 
 1018:     if (defined($which)) {
 1019: 	if (ref($nameref)) {
 1020: 	    return ($$awardref[$which],$$msgref[$which],$$nameref[$which]);
 1021: 	} else {
 1022: 	    return ($$awardref[$which],$$msgref[$which]);
 1023: 	}
 1024:     }
 1025:     return ('ERROR',undef);
 1026: }
 1027: }
 1028: 
 1029: sub decideoutput {
 1030:     my ($award,$awarded,$awardmsg,$solved,$previous,$target,$nocorrect,$tdclass)=@_;
 1031: 
 1032:     my $message='';
 1033:     my $button=0;
 1034:     my $previousmsg;
 1035:     my $css_class='orange';
 1036:     my $added_computer_text=0;
 1037:     my %possible_class =
 1038: 	( 'correct'         => 'LC_answer_correct',
 1039: 	  'charged_try'     => 'LC_answer_charged_try',
 1040: 	  'not_charged_try' => 'LC_answer_not_charged_try',
 1041: 	  'no_grade'        => 'LC_answer_no_grade',
 1042: 	  'no_message'      => 'LC_no_message',
 1043: 	  );
 1044: 
 1045:     my $part = $Apache::inputtags::part;
 1046:     my $tohandgrade = &Apache::lonnet::EXT("resource.$part.handgrade");
 1047:     my $handgrade = ('yes' eq lc($tohandgrade)); 
 1048: #
 1049: # Should "Computer's Answer" be displayed?
 1050: # Should not be displayed if still answerable,
 1051: # if the problem is handgraded,
 1052: # or if the problem does not give a correct answer
 1053: #
 1054:     
 1055:     my $computer = ($handgrade || $nocorrect)? ''
 1056: 	                       : " ".&mt("Computer's answer now shown above.");
 1057:     &Apache::lonxml::debug("handgrade has :$handgrade:");
 1058: 
 1059:     if ($previous) { $previousmsg=&mt('You have entered that answer before'); }
 1060:     
 1061:     if ($solved =~ /^correct/) {
 1062:         $css_class=$possible_class{'correct'};
 1063: 	$message=&mt('You are correct.');
 1064: 	if ($awarded < 1 && $awarded > 0) {
 1065: 	    $message=&mt('You are partially correct.');
 1066: 	    $css_class=$possible_class{'not_charged_try'};
 1067: 	} elsif ($awarded < 1) {
 1068: 	    $message=&mt('Incorrect.');
 1069: 	    $css_class=$possible_class{'charged_try'};
 1070: 	}
 1071: 	if ($handgrade || 
 1072:             ($env{'request.filename'}=~/\/res\/lib\/templates\/(examupload|DropBox).problem$/)) {
 1073: 	    $message = &mt("A score has been assigned.");
 1074: 	    $added_computer_text=1;
 1075: 	} else {
 1076: 	    if ($target eq 'tex') {
 1077: 		$message = '\textbf{'.$message.'}';
 1078: 	    } else {
 1079: 		$message = "<b>".$message."</b>";
 1080: 		$message.= $computer;
 1081: 	    }
 1082: 	    $added_computer_text=1;
 1083: 	    if ($awarded > 0) {
 1084: 		my ($symb) = &Apache::lonnet::whichuser();
 1085: 		if (($symb ne '') 
 1086: 		    &&
 1087: 		    ($env{'course.'.$env{'request.course.id'}.
 1088: 			      '.disable_receipt_display'} ne 'yes') &&
 1089:                     ($Apache::lonhomework::type ne 'practice')) { 
 1090: 		    $message.=(($target eq 'web')?'<br />':' ').
 1091: 			&mt('Your receipt no. is [_1]',
 1092: 			    (&Apache::lonnet::receipt($Apache::inputtags::part).
 1093: 			     (($target eq 'web')?&Apache::loncommon::help_open_topic('Receipt'):'')));
 1094: 		}
 1095: 	    }
 1096: 	}
 1097:         if ($awarded >= 1) {
 1098:             $button=0;
 1099:         } elsif (&Apache::lonnet::EXT("resource.$part.retrypartial") !~/^1|on|yes$/i) {
 1100:             $button=0;
 1101:         } else {
 1102:             $button=1;
 1103:         }
 1104: 	$previousmsg='';
 1105:     } elsif ($solved =~ /^excused/) {
 1106: 	if ($target eq 'tex') {
 1107: 	    $message = ' \textbf{'.&mt('You are excused from the problem.').'} ';
 1108: 	} else {
 1109: 	    $message = "<b>".&mt('You are excused from the problem.')."</b>";
 1110: 	}
 1111: 	$css_class=$possible_class{'charged_try'};
 1112: 	$button=0;
 1113: 	$previousmsg='';
 1114:     } elsif ($award eq 'EXACT_ANS' || $award eq 'APPROX_ANS' ) {
 1115: 	if ($solved =~ /^incorrect/ || $solved eq '') {
 1116: 	    $message = &mt("Incorrect").".";
 1117: 	    $css_class=$possible_class{'charged_try'};
 1118: 	    $button=1;
 1119: 	} else {
 1120: 	    if ($target eq 'tex') {
 1121: 		$message = '\textbf{'.&mt('You are correct.').'}';
 1122: 	    } else {
 1123: 		$message = "<b>".&mt('You are correct.')."</b>";
 1124: 		$message.= $computer;
 1125: 	    }
 1126: 	    $added_computer_text=1;
 1127: 	    if  ($awarded > 0 
 1128: 		 && $env{'course.'.
 1129: 			     $env{'request.course.id'}.
 1130: 			     '.disable_receipt_display'} ne 'yes') { 
 1131: 		$message.=(($target eq 'web')?'<br />':' ').
 1132: 		    &mt('Your receipt is [_1]',
 1133: 			(&Apache::lonnet::receipt($Apache::inputtags::part).
 1134: 			 (($target eq 'web')?&Apache::loncommon::help_open_topic('Receipt'):'')));
 1135: 	    }
 1136: 	    $css_class=$possible_class{'correct'};
 1137: 	    $button=0;
 1138: 	    $previousmsg='';
 1139: 	}
 1140:     } elsif ($award eq 'NO_RESPONSE') {
 1141: 	$message = '';
 1142: 	$css_class=$possible_class{'no_feedback'};
 1143: 	$button=1;
 1144:     } elsif ($award eq 'EXTRA_ANSWER') {
 1145: 	$message = &mt('Some extra items were submitted.');
 1146: 	$css_class=$possible_class{'not_charged_try'};
 1147: 	$button = 1;
 1148:     } elsif ($award eq 'MISSING_ANSWER') {
 1149: 	$message = &mt('Some items were not submitted.');
 1150:         if ($target ne 'tex') {
 1151:            $message .= &Apache::loncommon::help_open_topic('Some_Items_Were_Not_Submitted');
 1152:         }
 1153: 	$css_class=$possible_class{'not_charged_try'};
 1154: 	$button = 1;
 1155:     } elsif ($award eq 'WRONG_NUMBOXESCHECKED') {
 1156:         $message = &mt('Number of boxes checked outside permissible range (either too few or too many).');
 1157:         if ($target ne 'tex') {
 1158:            $message .= &Apache::loncommon::help_open_topic('Wrong_Num_Boxes_Checked');
 1159:         }
 1160:         $css_class=$possible_class{'not_charged_try'};
 1161:         $button = 1;
 1162:     } elsif ($award eq 'ERROR') {
 1163: 	$message = &mt('An error occurred while grading your answer.');
 1164: 	$css_class=$possible_class{'not_charged_try'};
 1165: 	$button = 1;
 1166:     } elsif ($award eq 'TOO_LONG') {
 1167: 	$message = &mt("The submitted answer was too long.");
 1168: 	$css_class=$possible_class{'not_charged_try'};
 1169: 	$button=1;
 1170:     } elsif ($award eq 'WANTED_NUMERIC') {
 1171: 	$message = &mt("This question expects a numeric answer.");
 1172: 	$css_class=$possible_class{'not_charged_try'};
 1173: 	$button=1;
 1174:     } elsif ($award eq 'MISORDERED_RANK') {
 1175:         $message = &mt('You have provided an invalid ranking.');
 1176:         if ($target ne 'tex') {
 1177:             $message.=' '.&mt('Please refer to [_1]',&Apache::loncommon::help_open_topic('Ranking_Problems',&mt('help on ranking problems')));
 1178:         }
 1179: 	$css_class=$possible_class{'not_charged_try'};
 1180: 	$button=1;
 1181:     } elsif ($award eq 'EXCESS_FILESIZE') {
 1182:         $message = &mt("Submission won't be graded. The combined size of submitted files exceeded the amount allowed.");
 1183:         $css_class=$possible_class{'not_charged_try'};
 1184:         $button=1;
 1185:     } elsif ($award eq 'FILENAME_INUSE') {
 1186:         $message = &mt('You have already uploaded a file with that filename.');
 1187:         if ($target eq 'tex') {
 1188:             $message.= "\\\\\n";
 1189:         } else {
 1190:             $message .= '<br />';
 1191:         }
 1192:         $message .= &mt('Please use a different filename.');
 1193:         $css_class=$possible_class{'not_charged_try'};
 1194:         $button=1;
 1195:     } elsif ($award eq 'INVALID_FILETYPE') {
 1196: 	$message = &mt("Submission won't be graded. The type of file submitted is not allowed.");
 1197: 	$css_class=$possible_class{'not_charged_try'};
 1198: 	$button=1;
 1199:     } elsif ($award eq 'SIG_FAIL') {
 1200: 	my ($used,$min,$max)=split(':',$awardmsg);
 1201: 	my $word = ($used < $min) ? 'more' : 'fewer';
 1202: 	$message = &mt("Submission not graded. Use $word digits.",$used);
 1203: 	$css_class=$possible_class{'not_charged_try'};
 1204: 	$button=1;
 1205:     } elsif ($award eq 'UNIT_INVALID_INSTRUCTOR') {
 1206: 	$message = &mt('Error in instructor specifed unit. This error has been reported to the instructor.', $awardmsg);
 1207: 	if ($target ne 'tex') {$message.=&Apache::loncommon::help_open_topic('Physical_Units');} 
 1208: 	$css_class=$possible_class{'not_charged_try'};
 1209: 	$button=1;
 1210:     } elsif ($award eq 'UNIT_INVALID_STUDENT') {
 1211: 	$message = &mt('Unable to interpret units. Computer reads units as "[_1]".',&markup_unit($awardmsg,$target));
 1212: 	if ($target ne 'tex') {$message.=&Apache::loncommon::help_open_topic('Physical_Units');} 
 1213: 	$css_class=$possible_class{'not_charged_try'};
 1214: 	$button=1;
 1215:     } elsif ($award eq 'UNIT_FAIL' || $award eq 'UNIT_IRRECONCIBLE') {
 1216: 	$message = &mt('Incompatible units. No conversion found between "[_1]" and the required units.',&markup_unit($awardmsg,$target));
 1217: 	if ($target ne 'tex') {$message.=&Apache::loncommon::help_open_topic('Physical_Units');} 
 1218: 	$css_class=$possible_class{'not_charged_try'};
 1219: 	$button=1;
 1220:     } elsif ($award eq 'UNIT_NOTNEEDED') {
 1221: 	$message = &mt('Only a number required. Computer reads units of "[_1]".',&markup_unit($awardmsg,$target));
 1222: 	$css_class=$possible_class{'not_charged_try'};
 1223: 	$button=1;
 1224:     } elsif ($award eq 'NO_UNIT') {
 1225: 	$message = &mt("Units required").'.';
 1226: 	if ($target ne 'tex') {$message.=&Apache::loncommon::help_open_topic('Physical_Units')};
 1227: 	$css_class=$possible_class{'not_charged_try'};
 1228: 	$button=1;
 1229:     } elsif ($award eq 'COMMA_FAIL') {
 1230: 	$message = &mt("Proper comma separation is required").'.';
 1231: 	$css_class=$possible_class{'not_charged_try'};
 1232: 	$button=1;
 1233:     } elsif ($award eq 'BAD_FORMULA') {
 1234: 	$message = &mt("Unable to understand formula").'.';
 1235:         if ($target ne 'tex') {$message.=&Apache::loncommon::help_open_topic('Formula_Answers')};
 1236: 	$css_class=$possible_class{'not_charged_try'};
 1237: 	$button=1;
 1238:     } elsif ($award eq 'NOT_FUNCTION') {
 1239:         $message = &mt("Not a function").'.';
 1240:         $css_class=$possible_class{'not_charged_try'};
 1241:         $button=1;
 1242:     } elsif ($award eq 'WRONG_FORMAT') {
 1243:         $message = &mt("Wrong format").'.';
 1244:         $css_class=$possible_class{'not_charged_try'};
 1245:         $button=1;
 1246:      } elsif ($award eq 'INTERNAL_ERROR') {
 1247:         $message = &mt("An internal error occurred while processing your answer. Please try again later.");
 1248:         $css_class=$possible_class{'not_charged_try'};
 1249:         $button=1;
 1250:     } elsif ($award eq 'INCORRECT') {
 1251: 	$message = &mt("Incorrect").'.';
 1252: 	$css_class=$possible_class{'charged_try'};
 1253: 	$button=1;
 1254:     } elsif ($award eq 'SUBMITTED') {
 1255: 	$message = &mt("Your submission has been recorded.");
 1256: 	$css_class=$possible_class{'no_grade'};
 1257: 	$button=1;
 1258:     } elsif ($award eq 'SUBMITTED_CREDIT') {
 1259:         $message = &mt("Your submission has been recorded, and credit awarded.");
 1260:         $css_class=$possible_class{'correct'};
 1261:         $button=1;
 1262:     } elsif ($award eq 'ANONYMOUS') {
 1263:         $message = &mt("Your anonymous submission has been recorded.");
 1264:         $css_class=$possible_class{'no_grade'};
 1265:         $button=1;
 1266:     } elsif ($award eq 'ANONYMOUS_CREDIT') {
 1267:         $message = &mt("Your anonymous submission has been recorded, and credit awarded.");
 1268:         $css_class=$possible_class{'correct'};
 1269:         $button=1;
 1270:     } elsif ($award eq 'DRAFT') {
 1271: 	$message = &mt("Copy saved but not submitted.");
 1272: 	$css_class=$possible_class{'not_charged_try'};
 1273: 	$button=1;
 1274:     } elsif ($award eq 'ASSIGNED_SCORE') {
 1275: 	$message = &mt("A score has been assigned.");
 1276: 	$css_class=$possible_class{'correct'};
 1277: 	$button=0;
 1278:     } elsif ($award eq '') {
 1279: 	if ($handgrade && $Apache::inputtags::status[-1] eq 'SHOW_ANSWER') {
 1280: 	    $message = &mt("Nothing submitted.");
 1281: 	    $css_class=$possible_class{'charged_try'};
 1282: 	} else {
 1283: 	    $css_class=$possible_class{'not_charged_try'};
 1284: 	}
 1285: 	$button=1;
 1286:     } else {
 1287: 	$message = &mt("Unknown message").": $award";
 1288: 	$button=1;
 1289:     }
 1290:     my (undef,undef,$domain,$user)=&Apache::lonnet::whichuser();
 1291:     foreach my $resid(@Apache::inputtags::response){
 1292:         if ($Apache::lonhomework::history{"resource.$part.$resid.handback"}) {
 1293:             if ($target eq 'tex') {
 1294:                 $message.= "\\\\\n";
 1295:             } else {
 1296:                 $message.='<br />';
 1297:             }
 1298: 	    my @files = split(/\s*,\s*/,
 1299: 			      $Apache::lonhomework::history{"resource.$part.$resid.handback"});
 1300: 	    my $file_msg;
 1301: 	    foreach my $file (@files) {
 1302:                 if ($target eq 'tex') {
 1303:                     $file_msg.= "\\\\\n".$file;
 1304:                 } else {
 1305:                     $file_msg.= '<br /><a href="/uploaded/'."$domain/$user".'/'.$file.'">'.$file.'</a>';
 1306:                 }
 1307: 	    }
 1308: 	    $message .= &mt('Returned file(s): [_1]',$file_msg);
 1309:             if ($target eq 'tex') {
 1310:                 $message.= "\\\\\n";
 1311:             } else {
 1312:                 $message.='<br />';
 1313:             }
 1314: 	}
 1315:     }
 1316: 
 1317:     if (&Apache::lonhomework::hide_problem_status()
 1318: 	&& $Apache::inputtags::status[-1] ne 'SHOW_ANSWER'
 1319: 	&& &hide_award($award)) {
 1320:         $message = &mt("Answer Submitted: Your final submission will be graded after the due date.");
 1321:         my @interval= &Apache::lonnet::EXT("resource.$part.interval");
 1322:         if ($interval[0] =~ /\d+/) {
 1323:             my $first_access=&Apache::lonnet::get_first_access($interval[1]);
 1324:             if (defined($first_access)) {
 1325:                 my $due_date= &Apache::lonnet::EXT("resource.$part.duedate");
 1326:                 unless (($due_date) && ($due_date < $first_access + $interval[0])) { 
 1327:                     $message = &mt("Answer Submitted: Your final submission will be graded when the time limit is reached.");
 1328:                 }
 1329:             }
 1330:         }
 1331: 	$css_class=$possible_class{'no_grade'};
 1332: 	$button=1;
 1333:     }
 1334:     if ($Apache::inputtags::status[-1] eq 'SHOW_ANSWER' && 
 1335: 	!$added_computer_text && $target ne 'tex') {
 1336: 	$message.= $computer;
 1337: 	$added_computer_text=1;
 1338:     }
 1339:     if ($Apache::lonhomework::type eq 'practice') {
 1340:        if ($target eq 'web') {
 1341:            $message .= '<br />';
 1342:        } else {
 1343:            $message .= ' ';      
 1344:        }
 1345:        $message.=&mt('Submissions to practice problems are not permanently recorded.');
 1346:     }
 1347:     return ($button,$css_class,$message,$previousmsg);
 1348: }
 1349: 
 1350: sub markup_unit {
 1351:     my ($unit,$target)=@_;
 1352:     if ($target eq 'tex') {
 1353: 	return '\texttt{'.&Apache::lonxml::latex_special_symbols($unit).'}'; 
 1354:     } else {
 1355: 	return "<tt>".$unit."</tt>";
 1356:     }
 1357: }
 1358: 
 1359: sub removealldata {
 1360:     my ($id)=@_;
 1361:     foreach my $key (keys(%Apache::lonhomework::results)) {
 1362: 	if (($key =~ /^resource\.\Q$id\E\./) && ($key !~ /\.collaborators$/)) {
 1363: 	    &Apache::lonxml::debug("Removing $key");
 1364: 	    delete($Apache::lonhomework::results{$key});
 1365: 	}
 1366:     }
 1367: }
 1368: 
 1369: sub hidealldata {
 1370:     my ($id)=@_;
 1371:     foreach my $key (keys(%Apache::lonhomework::results)) {
 1372: 	if (($key =~ /^resource\.\Q$id\E\./) && ($key !~ /\.collaborators$/)) {
 1373: 	    &Apache::lonxml::debug("Hidding $key");
 1374: 	    my $newkey=$key;
 1375: 	    $newkey=~s/^(resource\.\Q$id\E\.[^\.]+\.)(.*)$/${1}hidden${2}/;
 1376: 	    $Apache::lonhomework::results{$newkey}=
 1377: 		$Apache::lonhomework::results{$key};
 1378: 	    delete($Apache::lonhomework::results{$key});
 1379: 	}
 1380:     }
 1381: }
 1382: 
 1383: sub setgradedata {
 1384:     my ($award,$msg,$id,$previously_used) = @_;
 1385:     if ($Apache::lonhomework::scantronmode && 
 1386: 	&Apache::lonnet::validCODE($env{'form.CODE'})) {
 1387: 	$Apache::lonhomework::results{"resource.CODE"}=$env{'form.CODE'};
 1388:     } elsif ($Apache::lonhomework::scantronmode && 
 1389: 	     $env{'form.CODE'} eq '' &&
 1390: 	     $Apache::lonhomework::history{"resource.CODE"} ne '') {
 1391: 	$Apache::lonhomework::results{"resource.CODE"}='';
 1392:     }
 1393: 
 1394:     if (!$Apache::lonhomework::scantronmode &&
 1395: 	$Apache::inputtags::status['-1'] ne 'CAN_ANSWER' &&
 1396: 	$Apache::inputtags::status['-1'] ne 'CANNOT_ANSWER') {
 1397: 	$Apache::lonhomework::results{"resource.$id.afterduedate"}=$award;
 1398: 	return '';
 1399:     } elsif ( $Apache::lonhomework::history{"resource.$id.awarded"} < 1
 1400: 	      || $Apache::lonhomework::scantronmode 
 1401: 	      || &Apache::lonhomework::hide_problem_status()  ) {
 1402:         # the student doesn't already have it correct,
 1403: 	# or we are in a mode (scantron orno problem status) where a correct 
 1404:         # can become incorrect
 1405: 	# handle assignment of tries and solved status
 1406: 	my $solvemsg;
 1407: 	if ($Apache::lonhomework::scantronmode) {
 1408: 	    $solvemsg='correct_by_scantron';
 1409: 	} else {
 1410: 	    $solvemsg='correct_by_student';
 1411: 	}
 1412: 	if ($Apache::lonhomework::history{"resource.$id.afterduedate"}) {
 1413: 	    $Apache::lonhomework::results{"resource.$id.afterduedate"}='';
 1414: 	}
 1415: 	if ( $award eq 'ASSIGNED_SCORE') {
 1416: 	    $Apache::lonhomework::results{"resource.$id.tries"} =
 1417: 		$Apache::lonhomework::history{"resource.$id.tries"} + 1;
 1418: 	    $Apache::lonhomework::results{"resource.$id.solved"} =
 1419: 		$solvemsg;
 1420: 	    my $numawards=scalar(@Apache::inputtags::response);
 1421: 	    $Apache::lonhomework::results{"resource.$id.awarded"} = 0;
 1422: 	    foreach my $res (@Apache::inputtags::response) {
 1423: 		if (defined($Apache::lonhomework::results{"resource.$id.$res.awarded"})) {
 1424: 		    $Apache::lonhomework::results{"resource.$id.awarded"}+=
 1425: 			$Apache::lonhomework::results{"resource.$id.$res.awarded"};
 1426: 		} else {
 1427: 		    $Apache::lonhomework::results{"resource.$id.awarded"}+=
 1428: 			&awarddetail_to_awarded($Apache::lonhomework::results{"resource.$id.$res.awarddetail"});
 1429: 		}
 1430: 	    }
 1431: 	    if ($numawards > 0) {
 1432: 		$Apache::lonhomework::results{"resource.$id.awarded"}/=
 1433: 		    $numawards;
 1434: 	    }
 1435: 	} elsif ( $award eq 'APPROX_ANS' || $award eq 'EXACT_ANS' ) {
 1436: 	    $Apache::lonhomework::results{"resource.$id.tries"} =
 1437: 		$Apache::lonhomework::history{"resource.$id.tries"} + 1;
 1438: 	    $Apache::lonhomework::results{"resource.$id.solved"} =
 1439: 		$solvemsg;
 1440: 	    $Apache::lonhomework::results{"resource.$id.awarded"} = '1';
 1441:         } elsif ( $award eq 'SUBMITTED_CREDIT' ) {
 1442:             $Apache::lonhomework::results{"resource.$id.tries"} =
 1443:                 $Apache::lonhomework::history{"resource.$id.tries"} + 1;
 1444:             $Apache::lonhomework::results{"resource.$id.solved"} =
 1445:                 'credit_attempted';
 1446:             $Apache::lonhomework::results{"resource.$id.awarded"} = '1';
 1447:         }  elsif ( $award eq 'ANONYMOUS_CREDIT' ) {
 1448:             $Apache::lonhomework::results{"resource.$id.tries"} =
 1449:                 $Apache::lonhomework::history{"resource.$id.tries"} + 1;
 1450:             $Apache::lonhomework::results{"resource.$id.solved"} =
 1451:                 'credit_attempted';
 1452:             $Apache::lonhomework::results{"resource.$id.awarded"} = '1';
 1453: 	} elsif ( $award eq 'INCORRECT' ) {
 1454: 	    $Apache::lonhomework::results{"resource.$id.tries"} =
 1455: 		$Apache::lonhomework::history{"resource.$id.tries"} + 1;
 1456: 	    if (&Apache::lonhomework::hide_problem_status()
 1457: 		|| $Apache::lonhomework::scantronmode) {
 1458: 		$Apache::lonhomework::results{"resource.$id.awarded"} = 0;
 1459: 	    }
 1460: 	    $Apache::lonhomework::results{"resource.$id.solved"} =
 1461: 		'incorrect_attempted';
 1462: 	} elsif ( $award eq 'SUBMITTED' ) {
 1463: 	    $Apache::lonhomework::results{"resource.$id.tries"} =
 1464: 		$Apache::lonhomework::history{"resource.$id.tries"} + 1;
 1465: 	    $Apache::lonhomework::results{"resource.$id.solved"} =
 1466: 		'ungraded_attempted';
 1467:         }  elsif ( $award eq 'ANONYMOUS' ) {
 1468:             $Apache::lonhomework::results{"resource.$id.tries"} =
 1469:                 $Apache::lonhomework::history{"resource.$id.tries"} + 1;
 1470:             $Apache::lonhomework::results{"resource.$id.solved"} =
 1471:                 'ungraded_attempted';
 1472: 	} elsif ( $award eq 'DRAFT' ) {
 1473: 	    $Apache::lonhomework::results{"resource.$id.solved"} = '';
 1474: 	} elsif ( $award eq 'NO_RESPONSE' ) {
 1475: 	    #no real response so delete any data that got stored
 1476: 	    &removealldata($id);
 1477: 	    return '';
 1478: 	} else {
 1479: 	    $Apache::lonhomework::results{"resource.$id.solved"} =
 1480: 		'incorrect_attempted';
 1481: 	    if (&Apache::lonhomework::show_no_problem_status()
 1482: 		|| $Apache::lonhomework::scantronmode) {
 1483: 		$Apache::lonhomework::results{"resource.$id.tries"} =
 1484: 		    $Apache::lonhomework::history{"resource.$id.tries"} + 1;
 1485: 		$Apache::lonhomework::results{"resource.$id.awarded"} = 0;
 1486: 	    }
 1487: 
 1488: 	    if (&Apache::lonhomework::show_some_problem_status()) {
 1489: 		# clear out the awarded if they had gotten it wrong/right
 1490: 		# and are now in an error mode	
 1491: 		$Apache::lonhomework::results{"resource.$id.awarded"} = '';
 1492: 	    }
 1493: 	}
 1494: 	if (defined($msg)) {
 1495: 	    $Apache::lonhomework::results{"resource.$id.awardmsg"} = $msg;
 1496: 	}
 1497: 	# did either of the overall awards chage? If so ignore the 
 1498: 	# previous check
 1499: 	if (($Apache::lonhomework::results{"resource.$id.awarded"} eq
 1500: 	     $Apache::lonhomework::history{"resource.$id.awarded"}) &&
 1501: 	    ($Apache::lonhomework::results{"resource.$id.solved"} eq
 1502: 	     $Apache::lonhomework::history{"resource.$id.solved"})) {
 1503: 	    # check if this was a previous submission if it was delete the
 1504: 	    # unneeded data and update the previously_used attribute
 1505: 	    if ( $previously_used eq 'PREVIOUSLY_USED') {
 1506: 		if (&Apache::lonhomework::show_problem_status()) {
 1507: 		    delete($Apache::lonhomework::results{"resource.$id.tries"});
 1508: 		    $Apache::lonhomework::results{"resource.$id.previous"} = '1';
 1509: 		}
 1510: 	    } elsif ( $previously_used eq 'PREVIOUSLY_LAST') {
 1511: 		#delete all data as they student didn't do anything, but save
 1512: 		#the list of collaborators.
 1513: 		&removealldata($id);
 1514: 		#and since they didn't do anything we were never here
 1515: 		return '';
 1516: 	    } else {
 1517: 		$Apache::lonhomework::results{"resource.$id.previous"} = '0';
 1518: 	    }
 1519: 	}
 1520:     } elsif ( $Apache::lonhomework::history{"resource.$id.awarded"} == 1 ) {
 1521: 	#delete all data as they student already has it correct
 1522: 	&removealldata($id);
 1523: 	#and since they didn't do anything we were never here
 1524: 	return '';
 1525:     }
 1526:     $Apache::lonhomework::results{"resource.$id.award"} = $award;
 1527:     if ($award eq 'SUBMITTED') {
 1528: 	&Apache::response::add_to_gradingqueue();
 1529:     }
 1530:     $Apache::lonhomework::results{"resource.$id.type"} = $Apache::lonhomework::type;
 1531:     $Apache::lonhomework::results{"resource.$id.duedate"} = &Apache::lonnet::EXT("resource.$id.duedate");
 1532:     $Apache::lonhomework::results{"resource.$id.hinttries"} = &Apache::lonnet::EXT("resource.$id.hinttries");
 1533:     $Apache::lonhomework::results{"resource.$id.version"} = &Apache::lonnet::usedversion();
 1534:     $Apache::lonhomework::results{"resource.$id.maxtries"} = &Apache::lonnet::EXT("resource.$id.maxtries");
 1535: }
 1536: 
 1537: sub find_which_previous {
 1538:     my ($version) = @_;
 1539:     my $part = $Apache::inputtags::part;
 1540:     my (@previous_version);
 1541:     foreach my $resp (@Apache::inputtags::response) {
 1542: 	my $key = "$version:resource.$part.$resp.submission";
 1543: 	my $submission = $Apache::lonhomework::history{$key};
 1544: 	my %previous = &Apache::response::check_for_previous($submission,
 1545: 							     $part,$resp,
 1546: 							     $version);
 1547: 	push(@previous_version,$previous{'version'});
 1548:     }
 1549:     return &previous_match(\@previous_version,
 1550: 			   scalar(@Apache::inputtags::response));
 1551: }
 1552: 
 1553: sub previous_match {
 1554:     my ($previous_array,$count) = @_;
 1555:     my $match = 0;
 1556:     my @matches;
 1557:     foreach my $versionar (@$previous_array) {
 1558: 	foreach my $version (@$versionar) {
 1559: 	    $matches[$version]++;
 1560: 	}
 1561:     }
 1562:     my $which=0;
 1563:     foreach my $elem (@matches) {
 1564: 	if ($elem eq $count) {
 1565: 	    $match=1;
 1566: 	    last;
 1567: 	}
 1568: 	$which++;
 1569:     }
 1570:     return ($match,$which);
 1571: }
 1572: 
 1573: sub grade {
 1574:     my ($target) = @_;
 1575:     my $id = $Apache::inputtags::part;
 1576:     my $response='';
 1577:     if ( defined $env{'form.submitted'}) {
 1578: 	my (@awards,@msgs);
 1579: 	foreach $response (@Apache::inputtags::response) {
 1580: 	    &Apache::lonxml::debug("looking for response.$id.$response.awarddetail");
 1581: 	    my $value=$Apache::lonhomework::results{"resource.$id.$response.awarddetail"};
 1582: 	    &Apache::lonxml::debug("keeping $value from $response for $id");
 1583: 	    push (@awards,$value);
 1584: 	    $value=$Apache::lonhomework::results{"resource.$id.$response.awardmsg"};
 1585: 	    &Apache::lonxml::debug("got message $value from $response for $id");
 1586: 	    push (@msgs,$value);
 1587: 	}
 1588: 	my ($finalaward,$msg) = 
 1589: 	    &finalizeawards(\@awards,\@msgs,undef,undef,
 1590: 			    $Apache::lonhomework::scantronmode);
 1591: 	my $previously_used;
 1592: 	if ( $#Apache::inputtags::previous eq $#awards ) {
 1593: 	    my ($match) =
 1594: 		&previous_match(\@Apache::inputtags::previous_version,
 1595: 				scalar(@Apache::inputtags::response));
 1596: 
 1597: 	    if ($match) {
 1598: 		$previously_used = 'PREVIOUSLY_LAST';
 1599: 		foreach my $value (@Apache::inputtags::previous) {
 1600: 		    if ($value eq 'PREVIOUSLY_USED' ) {
 1601: 			$previously_used = $value;
 1602: 			last;
 1603: 		    }
 1604: 		}
 1605: 	    }
 1606: 	}
 1607: 	&Apache::lonxml::debug("final award $finalaward, $previously_used, message $msg");
 1608: 	&setgradedata($finalaward,$msg,$id,$previously_used);
 1609:     }
 1610:     return '';
 1611: }
 1612: 
 1613: sub get_grade_messages {
 1614:     my ($id,$prefix,$target,$status,$nocorrect,$tdclass) = @_;
 1615: # nocorrect suppresses "Computer's answer now shown above"
 1616:     my ($message,$latemessage,$trystr,$previousmsg);
 1617:     my $showbutton = 1;
 1618: 
 1619:     my $award = $Apache::lonhomework::history{"$prefix.award"};
 1620:     my $awarded = $Apache::lonhomework::history{"$prefix.awarded"};
 1621:     my $solved = $Apache::lonhomework::history{"$prefix.solved"};
 1622:     my $previous = $Apache::lonhomework::history{"$prefix.previous"};
 1623:     my $awardmsg = $Apache::lonhomework::history{"$prefix.awardmsg"};
 1624:     &Apache::lonxml::debug("Found Award |$award|$solved|$awardmsg");
 1625:     if ( $award ne '' || $solved ne '' || $status eq 'SHOW_ANSWER') {
 1626: 	&Apache::lonxml::debug('Getting message');
 1627: 	($showbutton,my $css_class,$message,$previousmsg) =
 1628: 	    &decideoutput($award,$awarded,$awardmsg,$solved,$previous,
 1629: 			  $target,(($status eq 'CAN_ANSWER') || $nocorrect),$tdclass);
 1630: 	if ($target eq 'tex') {
 1631: 	    $message='\vskip 2 mm '.$message.' ';
 1632: 	} else {
 1633: 	    $message="<td class=\"$tdclass $css_class\">$message</td>";
 1634: 	    if ($previousmsg) {
 1635: 		$previousmsg="<td class=\"$tdclass LC_answer_previous\">$previousmsg</td>";
 1636: 	    }
 1637: 	}
 1638:     }
 1639:     my $tries = $Apache::lonhomework::history{"$prefix.tries"};
 1640:     my $maxtries = &Apache::lonnet::EXT("resource.$id.maxtries");
 1641:     &Apache::lonxml::debug("got maxtries of :$maxtries:");
 1642:     #if tries are set to negative turn off the Tries/Button and messages
 1643:     if (defined($maxtries) && $maxtries < 0) { return ''; }
 1644:     if ( $tries eq '' ) { $tries = '0'; }
 1645:     if ( $maxtries eq '' ) { $maxtries = '2'; } 
 1646:     if ( $maxtries eq 'con_lost' ) { $maxtries = '0'; } 
 1647:     my $tries_text= &get_tries_text();
 1648:     if ($showbutton) {
 1649: 	if ($target eq 'tex') {
 1650: 	    if ($env{'request.state'} ne "construct"
 1651: 		&& $Apache::lonhomework::type ne 'exam'
 1652: 		&& $env{'form.suppress_tries'} ne 'yes') {
 1653: 		$trystr ='{\vskip 1 mm \small '
 1654:                         .&mt('[_1]'.$tries_text.'[_2] [_3]'
 1655: 				,'\textit{','}',$tries.'/'.$maxtries ) 
 1656:                         .'} \vskip 2 mm';
 1657: 	    } else {
 1658: 		$trystr = '\vskip 0 mm ';
 1659: 	    }
 1660: 	} else {
 1661: 	    my $trial =$tries;
 1662: 	    if ($Apache::lonhomework::parsing_a_task) {
 1663: 	    } elsif($env{'request.state'} ne 'construct') {
 1664: 		$trial.="/".&Apache::lonhtmlcommon::direct_parm_link($maxtries,$env{'request.symb'},'maxtries',$id,$target);
 1665: 	    } else {
 1666: 		if (defined($Apache::inputtags::params{'maxtries'})) {
 1667: 		    $trial.="/".$Apache::inputtags::params{'maxtries'};
 1668: 		}
 1669: 	    }
 1670: 	    $trystr = '<td class="'.$tdclass.'"><span class="LC_nobreak">'.&mt($tries_text.' [_1]',$trial).'</span></td>';
 1671: 	}
 1672:     }
 1673: 
 1674:     if ($Apache::lonhomework::history{"$prefix.afterduedate"}) {
 1675: 	#last submissions was after due date
 1676: 	$latemessage=&mt(' The last submission was after the Due Date ');;
 1677: 	if ($target eq 'web') {
 1678: 	    $latemessage='<td class="'.$tdclass.' LC_answer_late">'.$latemessage.'</td>';
 1679: 	}
 1680:     }
 1681:     return ($previousmsg,$latemessage,$message,$trystr,$showbutton);
 1682: }
 1683: 
 1684: sub gradestatus {
 1685:     my ($id,$target,$no_previous) = @_;
 1686:     my $showbutton = 1;
 1687:     my $message = '';
 1688:     my $latemessage = '';
 1689:     my $trystr='';
 1690:     my $button='';
 1691:     my $previousmsg='';
 1692:     my $tdclass='';
 1693: 
 1694:     my $status = $Apache::inputtags::status['-1'];
 1695:     &Apache::lonxml::debug("gradestatus has :$status:");
 1696:     if ( $status ne 'CLOSED' 
 1697: 	 && $status ne 'UNAVAILABLE' 
 1698: 	 && $status ne 'INVALID_ACCESS' 
 1699: 	 && $status ne 'NEEDS_CHECKIN' 
 1700: 	 && $status ne 'NOT_IN_A_SLOT'
 1701:          && $status ne 'RESERVABLE'
 1702:          && $status ne 'RESERVABLE_LATER'
 1703:          && $status ne 'NOTRESERVABLE'
 1704:          && $status ne 'NEED_DIFFERENT_IP') {
 1705: 
 1706: 	if ($status eq 'SHOW_ANSWER') {
 1707:             $showbutton = 0;
 1708:         }
 1709: 
 1710:         unless (($status eq 'SHOW_ANSWER') || ($status eq 'CANNOT_ANSWER')) {
 1711:             if ($target ne 'tex') {
 1712:                 $tdclass = 'LC_status_submit_'.$id;
 1713:             }
 1714:         }
 1715: 
 1716: 	($previousmsg,$latemessage,$message,$trystr) =
 1717: 	    &get_grade_messages($id,"resource.$id",$target,$status,
 1718: 				$showbutton,$tdclass);
 1719: 	if ($status eq 'CANNOT_ANSWER') {
 1720: 	    $showbutton = 0;
 1721: 	}
 1722: 	if ( $status eq 'SHOW_ANSWER') {
 1723: 	    undef($previousmsg);
 1724: 	}
 1725: 	if ( $showbutton ) {
 1726: 	    if ($target ne 'tex') {
 1727: 		$button = 
 1728:             '<input onmouseup="javascript:setSubmittedPart(\''.$id.'\');this.form.action+=\'#'.&escape($id).'\';"
 1729:                     type="submit" name="submit_'.$id.'" id="submit_'.$id.'" class="LC_hwk_submit"
 1730:                     value="'.&mt('Submit Answer').'" />&nbsp;'.
 1731:                     '<div id="msg_submit_'.$id.'" style="display:none">'.
 1732:                     &mt('Processing your submission ...').'</div>';
 1733: 	    }
 1734: 	}
 1735: 
 1736:     }
 1737:     my $output= $previousmsg.$latemessage.$message.$trystr;
 1738:     if ($output =~ /^\s*$/) {
 1739: 	return $button;
 1740:     } else {
 1741: 	if ($target eq 'tex') {
 1742: 	    return $button.' \vskip 0 mm '.$output.' ';
 1743: 	} else {
 1744: 	    $output =
 1745: 		'<table><tr><td>'.$button.'</td>'.$output;
 1746: 	    if (!$no_previous) {
 1747: 		$output.='<td class="'.$tdclass.'">'.&previous_tries($id,$target).'</td>';
 1748: 	    }
 1749: 	    $output.= '</tr></table>';
 1750: 	    return $output;
 1751: 	}
 1752:     }
 1753: }
 1754: 
 1755: sub previous_tries {
 1756:     my ($id,$target) = @_;
 1757:     my $output;
 1758:     my $status = $Apache::inputtags::status['-1'];
 1759: 
 1760:     my $count;
 1761:     my %count_lookup;
 1762:     my ($lastrndseed,$lasttype);
 1763:     my $numstamps = 0;
 1764: 
 1765:     foreach my $i (1..$Apache::lonhomework::history{'version'}) {
 1766: 	my $prefix = $i.":resource.$id";
 1767:         my $is_anon;
 1768:         my $curr_type = $Apache::lonhomework::history{"$prefix.type"};    
 1769:         if (defined($env{'form.grade_symb'})) {
 1770:             if (($curr_type eq 'anonsurvey') || ($curr_type eq 'anonsurveycred')) {
 1771:                 $is_anon = 1;
 1772:             }
 1773:         }
 1774: 	next if (!exists($Apache::lonhomework::history{"$prefix.award"}));
 1775: 	$count++;
 1776: 	$count_lookup{$i} = $count;
 1777:         my $curr_rndseed = $Apache::lonhomework::history{"$prefix.rndseed"};
 1778: 	my ($previousmsg,$latemessage,$message,$trystr);
 1779: 
 1780: 	($previousmsg,$latemessage,$message,$trystr) =
 1781: 	    &get_grade_messages($id,"$prefix",$target,$status);
 1782: 
 1783: 	if ($previousmsg ne '') {
 1784: 	    my ($match,$which) = &find_which_previous($i);
 1785: 	    $message=$previousmsg;
 1786: 	    my $previous = $count_lookup{$which};
 1787: 	    $message =~ s{(</td>)}{ as submission \# $previous $1};
 1788: 	} elsif ($Apache::lonhomework::history{"$prefix.tries"}) {
 1789: 	    if (!(&Apache::lonhomework::hide_problem_status()
 1790: 		  && $Apache::inputtags::status[-1] ne 'SHOW_ANSWER')
 1791: 		&& $Apache::lonhomework::history{"$prefix.solved"} =~/^correct/
 1792: 		) {
 1793: 		
 1794:                 my $txt_correct = &mt('Correct');
 1795:                 my $awarded = $Apache::lonhomework::history{"$prefix.awarded"};
 1796:                 if ($awarded < 1 && $awarded > 0) {
 1797:                     $txt_correct=&mt('Partially Correct');
 1798:                 } elsif ($awarded < 1) {
 1799:                     if ($awarded eq '') {
 1800:                         $txt_correct='';
 1801:                     } else {
 1802:                         $txt_correct=&mt('Incorrect');
 1803:                     }
 1804:                 }
 1805: 		$message =~ s{(<td.*?>)(.*?)(</td>)}
 1806:                              {$1 <strong>$txt_correct</strong>. $3}s;
 1807: 	    }
 1808:             my $trystr = "(".&mt('Try [_1]',$Apache::lonhomework::history{"$prefix.tries"}).")";
 1809:             if (($curr_rndseed ne '') &&  ($lastrndseed ne '')) {
 1810:                 if (($curr_rndseed ne $lastrndseed) && 
 1811:                     (($curr_type eq 'randomizetry') || ($lasttype eq 'randomizetry'))) {
 1812:                     $trystr .= '<br /><span style="color: green; white-space: nowrap; font-style: italic; font-weight: bold; font-size: 80%;">'.&mt('New problem variation this try.').'</span>';
 1813:                 }
 1814:             } 
 1815: 	    $message =~ s{(</td>)}{ $trystr $1};
 1816: 	}
 1817: 	my ($class) = ($message =~ m{<td.*class="([^"]*)"}); #"
 1818: 	$message =~ s{(<td.*?>)}{<td>};
 1819: 	
 1820: 
 1821: 	$output .= '<tr class="'.$class.'">'.
 1822: 	           '<td align="center">'.$count.'</td>'.$message;
 1823:         if ((!$is_anon) && ($Apache::lonhomework::history{"$prefix.tries"}) &&
 1824:             ($Apache::lonhomework::history{"$prefix.award"} ne 'ASSIGNED_SCORE') &&
 1825:             ($Apache::lonhomework::history{$i.':timestamp'})) {
 1826:             $output .= '<td>'.&Apache::lonlocal::locallocaltime(
 1827:                              $Apache::lonhomework::history{$i.':timestamp'}).'</td>';
 1828:             $numstamps ++;
 1829:         } else {
 1830:             $output .= '<td></td>';
 1831:         }
 1832: 	foreach my $resid (@Apache::inputtags::response) {
 1833: 	    my $prefix = $prefix.".$resid";
 1834: 	    if (exists($Apache::lonhomework::history{"$prefix.submission"})) {
 1835: 		my $submission =
 1836: 		    $Apache::inputtags::submission_display{"$prefix.submission"};
 1837: 		if (!defined($submission)) {
 1838: 		    $submission = 
 1839: 			$Apache::lonhomework::history{"$prefix.submission"};
 1840: 		}
 1841:                 if ($is_anon) {
 1842:                     $output.='<td>'.&mt('(only shown to submitter)').'</td>';
 1843:                 } else {
 1844: 		    $output.='<td>'.$submission.'</td>';
 1845:                 }
 1846: 	    } else {
 1847: 		$output.='<td></td>';
 1848: 	    }
 1849: 	}
 1850: 	$output.=&Apache::loncommon::end_data_table_row()."\n";
 1851:         $lastrndseed = $curr_rndseed;
 1852:         $lasttype = $curr_type;
 1853:     }
 1854:     return if ($output eq '');
 1855:     my $headers = '<tr>'.
 1856:                   '<th>'.&mt('Submission #').'</th>'.
 1857:                   '<th>'.&mt('Try').'</th><th>';
 1858:     if ($numstamps) {
 1859:         $headers .= &mt('When');
 1860:     }
 1861:     $headers .= '</th>';
 1862:     my $colspan = scalar(@Apache::inputtags::response);
 1863:     if ($colspan > 1) {
 1864:         $headers .= '<th colspan="'.$colspan.'">';
 1865:     } else {
 1866:         $headers .= '<th>';
 1867:     }
 1868:     $headers .= &mt('Submitted Answer').'</th></tr>';
 1869:     $output ='<table class="LC_prior_tries">'.$headers.$output.'</table>';
 1870: 
 1871:     my $tries_text = &get_tries_text('link');
 1872:     my $prefix = $env{'form.request.prefix'};
 1873:     $prefix =~ tr{.}{_};
 1874:     my $function_name = "LONCAPA_previous_tries_".$prefix.
 1875: 	$Apache::lonxml::curdepth.'_'.$env{'form.counter'};
 1876:     my $result = &Apache::loncommon::modal_adhoc_window($function_name,420,410,$output,&mt($tries_text))."<br />";
 1877:     return $result;
 1878: }
 1879: 
 1880: sub get_tries_text {
 1881:     my ($context) = @_;
 1882:     my $tries_text;
 1883:     if ($context eq 'link') {
 1884:         $tries_text = 'Previous Tries';
 1885:     } else {
 1886:         $tries_text = 'Tries';
 1887:     }
 1888:     if ( $Apache::lonhomework::type eq 'survey' ||
 1889:          $Apache::lonhomework::type eq 'surveycred' ||
 1890:          $Apache::lonhomework::type eq 'anonsurvey' ||
 1891:          $Apache::lonhomework::type eq 'anonsurveycred' ||
 1892:          $Apache::lonhomework::parsing_a_task) {
 1893:         if ($context eq 'link') {
 1894:             $tries_text = 'Previous Submissions';
 1895:         } else {
 1896:             $tries_text = 'Submissions';
 1897:         }
 1898:     }
 1899:     return $tries_text;
 1900: }
 1901: 
 1902: sub spelling_languages {
 1903:     my %langchoices;
 1904:     foreach my $id (&Apache::loncommon::languageids()) {
 1905:         my $code = &Apache::loncommon::supportedlanguagecode($id);
 1906:         if ($code ne '') {
 1907:             $langchoices{$code} =  &Apache::loncommon::plainlanguagedescription($id);
 1908:         }
 1909:     }
 1910:     my @spelllangs = ('none');
 1911:     foreach my $code ('en','de','he','es','fr','pt','tr') {
 1912:         push(@spelllangs,[$code,$langchoices{$code}]);
 1913:     }
 1914:     return \@spelllangs;
 1915: }
 1916: 
 1917: 1;
 1918: __END__
 1919: 
 1920: =pod
 1921: 
 1922: =back
 1923: 
 1924: =cut
 1925:  

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>