Annotation of loncom/interface/loncommon.pm, revision 1.36

1.10      albertel    1: # The LearningOnline Network with CAPA
1.1       albertel    2: # a pile of common routines
1.10      albertel    3: #
1.36    ! matthew     4: # $Id: loncommon.pm,v 1.35 2002/04/23 21:42:01 matthew Exp $
1.10      albertel    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: #
1.12      harris41   28: # YEAR=2001
                     29: # 2/13-12/7 Guy Albertelli
1.17      harris41   30: # 12/11,12/12,12/17 Scott Harrison
1.18      www        31: # 12/21 Gerd Kortemeyer
1.20      www        32: # 12/21 Scott Harrison
1.22      www        33: # 12/25,12/28 Gerd Kortemeyer
1.23      www        34: # YEAR=2002
                     35: # 1/4 Gerd Kortemeyer
1.1       albertel   36: 
                     37: # Makes a table out of the previous attempts
1.2       albertel   38: # Inputs result_from_symbread, user, domain, course_id
1.16      harris41   39: # Reads in non-network-related .tab files
1.1       albertel   40: 
1.35      matthew    41: # POD header:
                     42: 
                     43: =head1 NAME
                     44: 
                     45: Apache::loncommon - pile of common routines
                     46: 
                     47: =head1 SYNOPSIS
                     48: 
                     49: Referenced by other mod_perl Apache modules.
                     50: 
                     51: Invocation:
                     52:  &Apache::loncommon::SUBROUTINENAME(ARGUMENTS);
                     53: 
                     54: =head1 INTRODUCTION
                     55: 
                     56: Common collection of used subroutines.  This collection helps remove
                     57: redundancy from other modules and increase efficiency of memory usage.
                     58: 
                     59: Current things done:
                     60: 
                     61:  Makes a table out of the previous homework attempts
                     62:  Inputs result_from_symbread, user, domain, course_id
                     63:  Reads in non-network-related .tab files
                     64: 
                     65: This is part of the LearningOnline Network with CAPA project
                     66: described at http://www.lon-capa.org.
                     67: 
                     68: =head2 Subroutines
                     69: 
                     70: =over 4
                     71: 
                     72: =cut 
                     73: 
                     74: # End of POD header
1.1       albertel   75: package Apache::loncommon;
                     76: 
                     77: use strict;
1.22      www        78: use Apache::lonnet();
1.8       albertel   79: use POSIX qw(strftime);
1.1       albertel   80: use Apache::Constants qw(:common);
                     81: use Apache::lonmsg();
1.12      harris41   82: 
1.22      www        83: my $readit;
                     84: 
1.20      www        85: # ----------------------------------------------- Filetypes/Languages/Copyright
1.12      harris41   86: my %language;
                     87: my %cprtag;
                     88: my %fe; my %fd;
1.15      harris41   89: my %fc;
1.12      harris41   90: 
1.20      www        91: # -------------------------------------------------------------- Thesaurus data
1.21      www        92: my @therelated;
                     93: my @theword;
                     94: my @thecount;
                     95: my %theindex;
                     96: my $thetotalcount;
1.20      www        97: my $thefuzzy=2;
                     98: my $thethreshold=0.1/$thefuzzy;
                     99: my $theavecount;
                    100: 
1.12      harris41  101: # ----------------------------------------------------------------------- BEGIN
1.35      matthew   102: =item BEGIN() 
                    103: 
                    104: Initialize values from language.tab, copyright.tab, filetypes.tab,
                    105: and filecategories.tab.
                    106: 
                    107: =cut
                    108: # ----------------------------------------------------------------------- BEGIN
                    109: 
1.18      www       110: BEGIN {
1.22      www       111: 
                    112:     unless ($readit) {
1.12      harris41  113: # ------------------------------------------------------------------- languages
                    114:     {
                    115: 	my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonTabDir'}.
                    116: 				 '/language.tab');
1.16      harris41  117: 	if ($fh) {
                    118: 	    while (<$fh>) {
                    119: 		next if /^\#/;
                    120: 		chomp;
                    121: 		my ($key,$val)=(split(/\s+/,$_,2));
                    122: 		$language{$key}=$val;
                    123: 	    }
1.12      harris41  124: 	}
                    125:     }
                    126: # ------------------------------------------------------------------ copyrights
                    127:     {
1.16      harris41  128: 	my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonIncludes'}.
                    129: 				  '/copyright.tab');
                    130: 	if ($fh) {
                    131: 	    while (<$fh>) {
                    132: 		next if /^\#/;
                    133: 		chomp;
                    134: 		my ($key,$val)=(split(/\s+/,$_,2));
                    135: 		$cprtag{$key}=$val;
                    136: 	    }
1.12      harris41  137: 	}
                    138:     }
1.15      harris41  139: # ------------------------------------------------------------- file categories
                    140:     {
                    141: 	my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonTabDir'}.
1.16      harris41  142: 				  '/filecategories.tab');
                    143: 	if ($fh) {
                    144: 	    while (<$fh>) {
                    145: 		next if /^\#/;
                    146: 		chomp;
                    147: 		my ($key,$val)=(split(/\s+/,$_,2));
                    148: 		push @{$fc{$key}},$val;
                    149: 	    }
1.15      harris41  150: 	}
                    151:     }
1.12      harris41  152: # ------------------------------------------------------------------ file types
                    153:     {
1.16      harris41  154: 	my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonTabDir'}.
                    155: 	       '/filetypes.tab');
                    156: 	if ($fh) {
                    157:             while (<$fh>) {
                    158: 		next if (/^\#/);
                    159: 		chomp;
                    160: 		my ($ending,$emb,$descr)=split(/\s+/,$_,3);
                    161: 		if ($descr ne '') { 
                    162: 		    $fe{$ending}=lc($emb);
                    163: 		    $fd{$ending}=$descr;
                    164: 		}
1.12      harris41  165: 	    }
                    166: 	}
                    167:     }
1.20      www       168: # -------------------------------------------------------------- Thesaurus data
                    169:     {
                    170: 	my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonTabDir'}.
                    171: 	       '/thesaurus.dat');
                    172: 	if ($fh) {
                    173:             while (<$fh>) {
                    174:                my ($tword,$tindex,$tcount,$trelated)=split(/\@/,$_);
                    175:                $theindex{$tword}=$tindex;
                    176:                $theword[$tindex]=$tword;
                    177:                $thecount[$tindex]=$tcount;
                    178:                $thetotalcount+=$tcount;
                    179:                $therelated[$tindex]=$trelated;
                    180: 	   }
                    181:         }
                    182:         $theavecount=$thetotalcount/$#thecount;
                    183:     }
1.22      www       184:     &Apache::lonnet::logthis(
                    185:               "<font color=yellow>INFO: Read file types and thesaurus</font>");
                    186:     $readit=1;
                    187: }
1.32      matthew   188:     
                    189: }
                    190: # ============================================================= END BEGIN BLOCK
1.36    ! matthew   191: 
        !           192: =item linked_select_forms(...)
        !           193: 
        !           194: linked_select_forms returns a string containing a <script></script> block
        !           195: and html for two <select> menus.  The select menus will be linked in that
        !           196: changing the value of the first menu will result in new values being placed
        !           197: in the second menu.  The values in the select menu will appear in alphabetical
        !           198: order.
        !           199: 
        !           200: linked_select_forms takes the following ordered inputs:
        !           201: 
        !           202: =over 4
        !           203: 
        !           204: =item $formname, the name of the <form> tag
        !           205: 
        !           206: =item $middletext, the text which appears between the <select> tags
        !           207: 
        !           208: =item $firstdefault, the default value for the first menu
        !           209: 
        !           210: =item $firstselectname, the name of the first <select> tag
        !           211: 
        !           212: =item $secondselectname, the name of the second <select> tag
        !           213: 
        !           214: =item $hashref, a reference to a hash containing the data for the menus.
        !           215: 
        !           216: Below is an example of such a hash.  Only the 'text', 'default', and 
        !           217: 'select2' keys must appear as stated.  keys(%menu) are the possible 
        !           218: values for the first select menu.  The text that coincides with the 
        !           219: first menu values is given in $menu{$choice1}->{'text'}.  The values 
        !           220: and text for the second menu are given in the hash pointed to by 
        !           221: $menu{$choice1}->{'select2'}.  
        !           222: 
        !           223:  my %menu = ( A1 => { text =>"Choice A1" ,
        !           224:                       default => "B3",
        !           225:                       select2 => { 
        !           226:                           B1 => "Choice B1",
        !           227:                           B2 => "Choice B2",
        !           228:                           B3 => "Choice B3",
        !           229:                           B4 => "Choice B4"
        !           230:                           }
        !           231:                   },
        !           232:               A2 => { text =>"Choice A2" ,
        !           233:                       default => "C2",
        !           234:                       select2 => { 
        !           235:                           C1 => "Choice C1",
        !           236:                           C2 => "Choice C2",
        !           237:                           C3 => "Choice C3"
        !           238:                           }
        !           239:                   },
        !           240:               A3 => { text =>"Choice A3" ,
        !           241:                       default => "D6",
        !           242:                       select2 => { 
        !           243:                           D1 => "Choice D1",
        !           244:                           D2 => "Choice D2",
        !           245:                           D3 => "Choice D3",
        !           246:                           D4 => "Choice D4",
        !           247:                           D5 => "Choice D5",
        !           248:                           D6 => "Choice D6",
        !           249:                           D7 => "Choice D7"
        !           250:                           }
        !           251:                   }
        !           252:               );
        !           253: 
        !           254: =back
        !           255: 
        !           256: =cut
        !           257: 
        !           258: # ------------------------------------------------
        !           259: 
        !           260: sub linked_select_forms {
        !           261:     my ($formname,
        !           262:         $middletext,
        !           263:         $firstdefault,
        !           264:         $firstselectname,
        !           265:         $secondselectname, 
        !           266:         $hashref
        !           267:         ) = @_;
        !           268:     my $second = "document.$formname.$secondselectname";
        !           269:     my $first = "document.$formname.$firstselectname";
        !           270:     # output the javascript to do the changing
        !           271:     my $result = '';
        !           272:     $result.="<script>\n";
        !           273:     $result.="var select2data = new Object();\n";
        !           274:     $" = '","';
        !           275:     my $debug = '';
        !           276:     foreach my $s1 (sort(keys(%$hashref))) {
        !           277:         $result.="select2data.d_$s1 = new Object();\n";        
        !           278:         $result.="select2data.d_$s1.def = new String('".
        !           279:             $hashref->{$s1}->{'default'}."');\n";
        !           280:         $result.="select2data.d_$s1.values = new Array(";        
        !           281:         my @s2values = sort(keys( %{ $hashref->{$s1}->{'select2'} } ));
        !           282:         $result.="\"@s2values\");\n";
        !           283:         $result.="select2data.d_$s1.texts = new Array(";        
        !           284:         my @s2texts;
        !           285:         foreach my $value (@s2values) {
        !           286:             push @s2texts, $hashref->{$s1}->{'select2'}->{$value};
        !           287:         }
        !           288:         $result.="\"@s2texts\");\n";
        !           289:     }
        !           290:     $"=' ';
        !           291:     $result.= <<"END";
        !           292: 
        !           293: function select1_changed() {
        !           294:     // Determine new choice
        !           295:     var newvalue = "d_" + $first.value;
        !           296:     // update select2
        !           297:     var values     = select2data[newvalue].values;
        !           298:     var texts      = select2data[newvalue].texts;
        !           299:     var select2def = select2data[newvalue].def;
        !           300:     var i;
        !           301:     // out with the old
        !           302:     for (i = 0; i < $second.options.length; i++) {
        !           303:         $second.options[i] = null;
        !           304:     }
        !           305:     // in with the nuclear
        !           306:     for (i=0;i<values.length; i++) {
        !           307:         $second.options[i] = new Option(values[i]);
        !           308:         $second.options[i].text = texts[i];
        !           309:         if (values[i] == select2def) {
        !           310:             $second.options[i].selected = true;
        !           311:         }
        !           312:     }
        !           313: }
        !           314: </script>
        !           315: END
        !           316:     # output the initial values for the selection lists
        !           317:     $result .= "<select size=\"1\" name=\"$firstselectname\" onchange=\"select1_changed()\">\n";
        !           318:     foreach my $value (sort(keys(%$hashref))) {
        !           319:         $result.="    <option value=\"$value\" ";
        !           320:         $result.=" selected=\"true\" " if ($value eq $firstdefault);
        !           321:         $result.=">$hashref->{$value}->{'text'}</option>\n";
        !           322:     }
        !           323:     $result .= "</select>\n";
        !           324:     my %select2 = %{$hashref->{$firstdefault}->{'select2'}};
        !           325:     $result .= $middletext;
        !           326:     $result .= "<select size=\"1\" name=\"$secondselectname\">\n";
        !           327:     my $seconddefault = $hashref->{$firstdefault}->{'default'};
        !           328:     foreach my $value (sort(keys(%select2))) {
        !           329:         $result.="    <option value=\"$value\" ";        
        !           330:         $result.=" selected=\"true\" " if ($value eq $seconddefault);
        !           331:         $result.=">$select2{$value}</option>\n";
        !           332:     }
        !           333:     $result .= "</select>\n";
        !           334:     #    return $debug;
        !           335:     return $result;
        !           336: }   #  end of sub linked_select_forms {
        !           337: 
1.33      matthew   338: ###############################################################
                    339: ##        Home server <option> list generating code          ##
                    340: ###############################################################
1.35      matthew   341: #-------------------------------------------
                    342: 
                    343: =item get_domains()
                    344: 
                    345: Returns an array containing each of the domains listed in the hosts.tab
                    346: file.
                    347: 
                    348: =cut
                    349: 
                    350: #-------------------------------------------
1.34      matthew   351: sub get_domains {
                    352:     # The code below was stolen from "The Perl Cookbook", p 102, 1st ed.
                    353:     my @domains;
                    354:     my %seen;
                    355:     foreach (sort values(%Apache::lonnet::hostdom)) {
                    356:         push (@domains,$_) unless $seen{$_}++;
                    357:     }
                    358:     return @domains;
                    359: }
                    360: 
1.35      matthew   361: #-------------------------------------------
                    362: 
                    363: =item select_dom_form($defdom,$name)
                    364: 
                    365: Returns a string containing a <select name='$name' size='1'> form to 
                    366: allow a user to select the domain to preform an operation in.  
                    367: See loncreateuser.pm for an example invocation and use.
                    368: 
                    369: =cut
                    370: 
                    371: #-------------------------------------------
1.34      matthew   372: sub select_dom_form {
                    373:     my ($defdom,$name) = @_;
                    374:     my @domains = get_domains();
                    375:     my $selectdomain = "<select name=\"$name\" size=\"1\">\n";
                    376:     foreach (@domains) {
                    377:         $selectdomain.="<option value=\"$_\" ".
                    378:             ($_ eq $defdom ? 'selected' : '').
                    379:                 ">$_</option>\n";
                    380:     }
                    381:     $selectdomain.="</select>";
                    382:     return $selectdomain;
                    383: }
                    384: 
1.35      matthew   385: #-------------------------------------------
                    386: 
                    387: =item get_home_servers($domain)
                    388: 
                    389: Returns a hash which contains keys like '103l3' and values like 
                    390: 'kirk.lite.msu.edu'.  All of the keys will be for machines in the
                    391: given $domain.
                    392: 
                    393: =cut
                    394: 
                    395: #-------------------------------------------
1.33      matthew   396: sub get_home_servers {
                    397:     my $domain = shift;
                    398:     my %home_servers;
                    399:     foreach (keys(%Apache::lonnet::libserv)) {
                    400:         if ($Apache::lonnet::hostdom{$_} eq $domain) {
                    401:             $home_servers{$_} = $Apache::lonnet::hostname{$_};
                    402:         }
                    403:     }
                    404:     return %home_servers;
                    405: }
                    406: 
1.35      matthew   407: #-------------------------------------------
                    408: 
                    409: =item home_server_option_list($domain)
                    410: 
                    411: returns a string which contains an <option> list to be used in a 
                    412: <select> form input.  See loncreateuser.pm for an example.
                    413: 
                    414: =cut
                    415: 
                    416: #-------------------------------------------
1.33      matthew   417: sub home_server_option_list {
                    418:     my $domain = shift;
                    419:     my %servers = &get_home_servers($domain);
                    420:     my $result = '';
                    421:     foreach (sort keys(%servers)) {
                    422:         $result.=
                    423:             '<option value="'.$_.'">'.$_.' '.$servers{$_}."</option>\n";
                    424:     }
                    425:     return $result;
                    426: }
                    427: ###############################################################
                    428: ##    End of home server <option> list generating code       ##
                    429: ###############################################################
1.32      matthew   430: 
                    431: ###############################################################
                    432: ##    Authentication changing form generation subroutines    ##
                    433: ###############################################################
                    434: ##
                    435: ## All of the authform_xxxxxxx subroutines take their inputs in a
                    436: ## hash, and have reasonable default values.
                    437: ##
                    438: ##    formname = the name given in the <form> tag.
1.35      matthew   439: #-------------------------------------------
                    440: 
                    441: =item authform_xxxxxx
                    442: 
                    443: The authform_xxxxxx subroutines provide javascript and html forms which 
                    444: handle some of the conveniences required for authentication forms.  
                    445: This is not an optimal method, but it works.  
                    446: 
                    447: See loncreateuser.pm for invocation and use examples.
                    448: 
                    449: =over 4
                    450: 
                    451: =item authform_header
                    452: 
                    453: =item authform_authorwarning
                    454: 
                    455: =item authform_nochange
                    456: 
                    457: =item authform_kerberos
                    458: 
                    459: =item authform_internal
                    460: 
                    461: =item authform_filesystem
                    462: 
                    463: =back
                    464: 
                    465: =cut
                    466: 
                    467: #-------------------------------------------
1.32      matthew   468: sub authform_header{  
                    469:     my %in = (
                    470:         formname => 'cu',
                    471:         kerb_def_dom => 'MSU.EDU',
                    472:         @_,
                    473:     );
                    474:     $in{'formname'} = 'document.' . $in{'formname'};
                    475:     my $result='';
                    476:     $result.=<<"END";
                    477: var current = new Object();
                    478: current.radiovalue = 'nochange';
                    479: current.argfield = null;
                    480: 
                    481: function changed_radio(choice,currentform) {
                    482:     var choicearg = choice + 'arg';
                    483:     // If a radio button in changed, we need to change the argfield
                    484:     if (current.radiovalue != choice) {
                    485:         current.radiovalue = choice;
                    486:         if (current.argfield != null) {
                    487:             currentform.elements[current.argfield].value = '';
                    488:         }
                    489:         if (choice == 'nochange') {
                    490:             current.argfield = null;
                    491:         } else {
                    492:             current.argfield = choicearg;
                    493:             switch(choice) {
                    494:                 case 'krb': 
                    495:                     currentform.elements[current.argfield].value = 
                    496:                         "$in{'kerb_def_dom'}";
                    497:                 break;
                    498:               default:
                    499:                 break;
                    500:             }
                    501:         }
                    502:     }
                    503:     return;
                    504: }
1.22      www       505: 
1.32      matthew   506: function changed_text(choice,currentform) {
                    507:     var choicearg = choice + 'arg';
                    508:     if (currentform.elements[choicearg].value !='') {
                    509:         switch (choice) {
                    510:             case 'krb': currentform.elements[choicearg].value =
                    511:                 currentform.elements[choicearg].value.toUpperCase();
                    512:                 break;
                    513:             default:
                    514:         }
                    515:         // clear old field
                    516:         if ((current.argfield != choicearg) && (current.argfield != null)) {
                    517:             currentform.elements[current.argfield].value = '';
                    518:         }
                    519:         current.argfield = choicearg;
                    520:     }
                    521:     set_auth_radio_buttons(choice,currentform);
                    522:     return;
1.20      www       523: }
1.32      matthew   524: 
                    525: function set_auth_radio_buttons(newvalue,currentform) {
                    526:     var i=0;
                    527:     while (i < currentform.login.length) {
                    528:         if (currentform.login[i].value == newvalue) { break; }
                    529:         i++;
                    530:     }
                    531:     if (i == currentform.login.length) {
                    532:         return;
                    533:     }
                    534:     current.radiovalue = newvalue;
                    535:     currentform.login[i].checked = true;
                    536:     return;
                    537: }
                    538: END
                    539:     return $result;
                    540: }
                    541: 
                    542: sub authform_authorwarning{
                    543:     my $result='';
                    544:     $result=<<"END";
                    545: <i>As a general rule, only authors or co-authors should be filesystem
                    546: authenticated (which allows access to the server filesystem).</i>
                    547: END
                    548:     return $result;
                    549: }
                    550: 
                    551: sub authform_nochange{  
                    552:     my %in = (
                    553:               formname => 'document.cu',
                    554:               kerb_def_dom => 'MSU.EDU',
                    555:               @_,
                    556:           );
                    557:     my $result='';
                    558:     $result.=<<"END";
                    559: <input type="radio" name="login" value="nochange" checked="checked"
                    560:        onclick="javascript:changed_radio('nochange',$in{'formname'});">
                    561: Do not change login data
                    562: END
                    563:     return $result;
                    564: }
                    565: 
                    566: sub authform_kerberos{  
                    567:     my %in = (
                    568:               formname => 'document.cu',
                    569:               kerb_def_dom => 'MSU.EDU',
                    570:               @_,
                    571:               );
                    572:     my $result='';
                    573:     $result.=<<"END";
                    574: <input type="radio" name="login" value="krb" 
                    575:        onclick="javascript:changed_radio('krb',$in{'formname'});"
                    576:        onchange="javascript:changed_radio('krb',$in{'formname'});">
                    577: Kerberos authenticated with domain
                    578: <input type="text" size="10" name="krbarg" value=""
                    579:        onchange="javascript:changed_text('krb',$in{'formname'});">
                    580: END
                    581:     return $result;
                    582: }
                    583: 
                    584: sub authform_internal{  
                    585:     my %args = (
                    586:                 formname => 'document.cu',
                    587:                 kerb_def_dom => 'MSU.EDU',
                    588:                 @_,
                    589:                 );
                    590:     my $result='';
                    591:     $result.=<<"END";
                    592: <input type="radio" name="login" value="int"
                    593:        onchange="javascript:changed_radio('int',$args{'formname'});"
                    594:        onclick="javascript:changed_radio('int',$args{'formname'});">
                    595: Internally authenticated (with initial password 
                    596: <input type="text" size="10" name="intarg" value=""
                    597:        onchange="javascript:changed_text('int',$args{'formname'});">
                    598: END
                    599:     return $result;
                    600: }
                    601: 
                    602: sub authform_local{  
                    603:     my %in = (
                    604:               formname => 'document.cu',
                    605:               kerb_def_dom => 'MSU.EDU',
                    606:               @_,
                    607:               );
                    608:     my $result='';
                    609:     $result.=<<"END";
                    610: <input type="radio" name="login" value="loc"
                    611:        onchange="javascript:changed_radio('loc',$in{'formname'});"
                    612:        onclick="javascript:changed_radio('loc',$in{'formname'});"> 
                    613: Local Authentication with argument
                    614: <input type="text" size="10" name="locarg" value=""
                    615:        onchange="javascript:changed_text('loc',$in{'formname'});">
                    616: END
                    617:     return $result;
                    618: }
                    619: 
                    620: sub authform_filesystem{  
                    621:     my %in = (
                    622:               formname => 'document.cu',
                    623:               kerb_def_dom => 'MSU.EDU',
                    624:               @_,
                    625:               );
                    626:     my $result='';
                    627:     $result.=<<"END";
                    628: <input type="radio" name="login" value="fsys" 
                    629:        onchange="javascript:changed_radio('fsys',$in{'formname'});"
                    630:        onclick="javascript:changed_radio('fsys',$in{'formname'});"> 
                    631: Filesystem authenticated (with initial password 
                    632: <input type="text" size="10" name="fsysarg" value=""
                    633:        onchange="javascript:changed_text('fsys',$in{'formname'});">
                    634: END
                    635:     return $result;
                    636: }
                    637: 
                    638: ###############################################################
                    639: ##   End Authentication changing form generation functions   ##
                    640: ###############################################################
                    641: 
1.20      www       642: 
                    643: 
                    644: # ---------------------------------------------------------- Is this a keyword?
                    645: 
                    646: sub keyword {
                    647:     my $newword=shift;
                    648:     $newword=~s/\W//g;
                    649:     $newword=~tr/A-Z/a-z/;
                    650:     my $tindex=$theindex{$newword};
                    651:     if ($tindex) {
                    652:         if ($thecount[$tindex]>$theavecount) {
                    653:            return 1;
                    654:         }
                    655:     }
                    656:     return 0;
                    657: }
                    658: # -------------------------------------------------------- Return related words
                    659: 
                    660: sub related {
                    661:     my $newword=shift;
                    662:     $newword=~s/\W//g;
                    663:     $newword=~tr/A-Z/a-z/;
                    664:     my $tindex=$theindex{$newword};
                    665:     if ($tindex) {
                    666:         my %found=();
                    667:         foreach (split(/\,/,$therelated[$tindex])) {
                    668: # - Related word found
                    669:             my ($ridx,$rcount)=split(/\:/,$_);
                    670: # - Direct relation index
                    671:             my $directrel=$rcount/$thecount[$tindex];
                    672:             if ($directrel>$thethreshold) {
                    673:                foreach (split(/\,/,$therelated[$ridx])) {
                    674:                   my ($rridx,$rrcount)=split(/\:/,$_);
                    675:                   if ($rridx==$tindex) {
                    676: # - Determine reverse relation index
                    677:                      my $revrel=$rrcount/$thecount[$ridx];
                    678: # - Calculate full index
                    679:                      $found{$ridx}=$directrel*$revrel;
                    680:                      if ($found{$ridx}>$thethreshold) {
                    681:                         foreach (split(/\,/,$therelated[$ridx])) {
                    682:                             my ($rrridx,$rrrcount)=split(/\:/,$_);
                    683:                             unless ($found{$rrridx}) {
                    684:                                my $revrevrel=$rrrcount/$thecount[$ridx];
                    685:                                if (
                    686:                           $directrel*$revrel*$revrevrel>$thethreshold
                    687:                                ) {
                    688:                                   $found{$rrridx}=
                    689:                                        $directrel*$revrel*$revrevrel;
                    690:                                }
                    691:                             }
                    692:                         }
                    693:                      }
                    694:                   }
                    695:                }
                    696:             }
                    697:         }
                    698:     }
                    699:     return ();
1.14      harris41  700: }
                    701: 
                    702: # ---------------------------------------------------------------- Language IDs
                    703: sub languageids {
1.16      harris41  704:     return sort(keys(%language));
1.14      harris41  705: }
                    706: 
                    707: # -------------------------------------------------------- Language Description
                    708: sub languagedescription {
1.16      harris41  709:     return $language{shift(@_)};
1.14      harris41  710: }
                    711: 
                    712: # --------------------------------------------------------------- Copyright IDs
                    713: sub copyrightids {
1.16      harris41  714:     return sort(keys(%cprtag));
1.14      harris41  715: }
                    716: 
                    717: # ------------------------------------------------------- Copyright Description
                    718: sub copyrightdescription {
1.16      harris41  719:     return $cprtag{shift(@_)};
1.14      harris41  720: }
                    721: 
                    722: # ------------------------------------------------------------- File Categories
                    723: sub filecategories {
1.16      harris41  724:     return sort(keys(%fc));
1.15      harris41  725: }
1.14      harris41  726: 
1.17      harris41  727: # -------------------------------------- File Types within a specified category
1.15      harris41  728: sub filecategorytypes {
1.16      harris41  729:     return @{$fc{lc(shift(@_))}};
1.14      harris41  730: }
                    731: 
                    732: # ------------------------------------------------------------------ File Types
                    733: sub fileextensions {
1.16      harris41  734:     return sort(keys(%fe));
1.14      harris41  735: }
                    736: 
                    737: # ------------------------------------------------------------- Embedding Style
                    738: sub fileembstyle {
1.16      harris41  739:     return $fe{lc(shift(@_))};
1.14      harris41  740: }
                    741: 
                    742: # ------------------------------------------------------------ Description Text
                    743: sub filedescription {
1.16      harris41  744:     return $fd{lc(shift(@_))};
                    745: }
                    746: 
                    747: # ------------------------------------------------------------ Description Text
                    748: sub filedescriptionex {
                    749:     my $ex=shift;
                    750:     return '.'.$ex.' '.$fd{lc($ex)};
1.12      harris41  751: }
1.1       albertel  752: 
                    753: sub get_previous_attempt {
1.2       albertel  754:   my ($symb,$username,$domain,$course)=@_;
1.1       albertel  755:   my $prevattempts='';
                    756:   if ($symb) {
1.3       albertel  757:     my (%returnhash)=
                    758:       &Apache::lonnet::restore($symb,$course,$domain,$username);
1.1       albertel  759:     if ($returnhash{'version'}) {
                    760:       my %lasthash=();
                    761:       my $version;
                    762:       for ($version=1;$version<=$returnhash{'version'};$version++) {
1.19      harris41  763:         foreach (sort(split(/\:/,$returnhash{$version.':keys'}))) {
1.1       albertel  764: 	  $lasthash{$_}=$returnhash{$version.':'.$_};
1.19      harris41  765:         }
1.1       albertel  766:       }
                    767:       $prevattempts='<table border=2></tr><th>History</th>';
1.16      harris41  768:       foreach (sort(keys %lasthash)) {
1.31      albertel  769: 	my ($ign,@parts) = split(/\./,$_);
                    770: 	if (@parts) {
                    771: 	  my $data=$parts[-1];
                    772: 	  pop(@parts);
                    773: 	  $prevattempts.='<th>Part '.join('.',@parts).'<br />'.$data.'</th>';
                    774: 	} else {
                    775: 	  $prevattempts.='<th>'.$ign.'</th>';
                    776: 	}
1.16      harris41  777:       }
1.1       albertel  778:       for ($version=1;$version<=$returnhash{'version'};$version++) {
                    779:         $prevattempts.='</tr><tr><th>Attempt '.$version.'</th>';
1.16      harris41  780:         foreach (sort(keys %lasthash)) {
1.5       albertel  781: 	  my $value;
                    782: 	  if ($_ =~ /timestamp/) {
                    783: 	    $value=scalar(localtime($returnhash{$version.':'.$_}));
                    784: 	  } else {
                    785: 	    $value=$returnhash{$version.':'.$_};
                    786: 	  }
                    787: 	  $prevattempts.='<td>'.$value.'</td>';   
1.16      harris41  788:         }
1.1       albertel  789:       }
                    790:       $prevattempts.='</tr><tr><th>Current</th>';
1.16      harris41  791:       foreach (sort(keys %lasthash)) {
1.5       albertel  792: 	my $value;
                    793: 	if ($_ =~ /timestamp/) {
                    794: 	  $value=scalar(localtime($lasthash{$_}));
                    795: 	} else {
                    796: 	  $value=$lasthash{$_};
                    797: 	}
                    798: 	$prevattempts.='<td>'.$value.'</td>';
1.16      harris41  799:       }
1.1       albertel  800:       $prevattempts.='</tr></table>';
                    801:     } else {
                    802:       $prevattempts='Nothing submitted - no attempts.';
                    803:     }
                    804:   } else {
                    805:     $prevattempts='No data.';
                    806:   }
1.10      albertel  807: }
                    808: 
                    809: sub get_student_view {
                    810:   my ($symb,$username,$domain,$courseid) = @_;
                    811:   my ($map,$id,$feedurl) = split(/___/,$symb);
                    812:   my (%old,%moreenv);
                    813:   my @elements=('symb','courseid','domain','username');
                    814:   foreach my $element (@elements) {
                    815:     $old{$element}=$ENV{'form.grade_'.$element};
                    816:     $moreenv{'form.grade_'.$element}=eval '$'.$element #'
                    817:   }
1.11      albertel  818:   &Apache::lonnet::appenv(%moreenv);
                    819:   my $userview=&Apache::lonnet::ssi('/res/'.$feedurl);
                    820:   &Apache::lonnet::delenv('form.grade_');
                    821:   foreach my $element (@elements) {
                    822:     $ENV{'form.grade_'.$element}=$old{$element};
                    823:   }
                    824:   $userview=~s/\<body[^\>]*\>//gi;
                    825:   $userview=~s/\<\/body\>//gi;
                    826:   $userview=~s/\<html\>//gi;
                    827:   $userview=~s/\<\/html\>//gi;
                    828:   $userview=~s/\<head\>//gi;
                    829:   $userview=~s/\<\/head\>//gi;
                    830:   $userview=~s/action\s*\=/would_be_action\=/gi;
                    831:   return $userview;
                    832: }
                    833: 
                    834: sub get_student_answers {
                    835:   my ($symb,$username,$domain,$courseid) = @_;
                    836:   my ($map,$id,$feedurl) = split(/___/,$symb);
                    837:   my (%old,%moreenv);
                    838:   my @elements=('symb','courseid','domain','username');
                    839:   foreach my $element (@elements) {
                    840:     $old{$element}=$ENV{'form.grade_'.$element};
                    841:     $moreenv{'form.grade_'.$element}=eval '$'.$element #'
                    842:   }
                    843:   $moreenv{'form.grade_target'}='answer';
1.10      albertel  844:   &Apache::lonnet::appenv(%moreenv);
                    845:   my $userview=&Apache::lonnet::ssi('/res/'.$feedurl);
                    846:   &Apache::lonnet::delenv('form.grade_');
                    847:   foreach my $element (@elements) {
                    848:     $ENV{'form.grade_'.$element}=$old{$element};
                    849:   }
                    850:   $userview=~s/\<body[^\>]*\>//gi;
                    851:   $userview=~s/\<\/body\>//gi;
                    852:   $userview=~s/\<html\>//gi;
                    853:   $userview=~s/\<\/html\>//gi;
                    854:   $userview=~s/\<head\>//gi;
                    855:   $userview=~s/\<\/head\>//gi;
                    856:   $userview=~s/action\s*\=/would_be_action\=/gi;
                    857:   return $userview;
1.1       albertel  858: }
                    859: 
1.6       albertel  860: sub get_unprocessed_cgi {
1.25      albertel  861:   my ($query,$possible_names)= @_;
1.26      matthew   862:   # $Apache::lonxml::debug=1;
1.16      harris41  863:   foreach (split(/&/,$query)) {
1.6       albertel  864:     my ($name, $value) = split(/=/,$_);
1.25      albertel  865:     $name = &Apache::lonnet::unescape($name);
                    866:     if (!defined($possible_names) || (grep {$_ eq $name} @$possible_names)) {
                    867:       $value =~ tr/+/ /;
                    868:       $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;
                    869:       &Apache::lonxml::debug("Seting :$name: to :$value:");
1.30      albertel  870:       unless (defined($ENV{'form.'.$name})) { &add_to_env('form.'.$name,$value) };
1.25      albertel  871:     }
1.16      harris41  872:   }
1.6       albertel  873: }
                    874: 
1.7       albertel  875: sub cacheheader {
1.23      www       876:   unless ($ENV{'request.method'} eq 'GET') { return ''; }
1.8       albertel  877:   my $date=strftime("%a, %d %b %Y %H:%M:%S GMT",gmtime);
1.7       albertel  878:   my $output .='<meta HTTP-EQUIV="Expires" CONTENT="'.$date.'" />
                    879:                 <meta HTTP-EQUIV="Cache-control" CONTENT="no-cache" />
                    880:                 <meta HTTP-EQUIV="Pragma" CONTENT="no-cache" />';
                    881:   return $output;
                    882: }
                    883: 
1.9       albertel  884: sub no_cache {
                    885:   my ($r) = @_;
1.23      www       886:   unless ($ENV{'request.method'} eq 'GET') { return ''; }
1.24      albertel  887:   #my $date=strftime("%a, %d %b %Y %H:%M:%S GMT",gmtime);
1.9       albertel  888:   $r->no_cache(1);
                    889:   $r->header_out("Pragma" => "no-cache");
1.24      albertel  890:   #$r->header_out("Expires" => $date);
1.9       albertel  891: }
1.25      albertel  892: 
                    893: sub add_to_env {
                    894:   my ($name,$value)=@_;
1.28      albertel  895:   if (defined($ENV{$name})) {
1.27      albertel  896:     if (ref($ENV{$name})) {
1.25      albertel  897:       #already have multiple values
                    898:       push(@{ $ENV{$name} },$value);
                    899:     } else {
                    900:       #first time seeing multiple values, convert hash entry to an arrayref
                    901:       my $first=$ENV{$name};
                    902:       undef($ENV{$name});
                    903:       push(@{ $ENV{$name} },$first,$value);
                    904:     }
                    905:   } else {
                    906:     $ENV{$name}=$value;
                    907:   }
1.31      albertel  908: }
                    909: 
                    910: #---CSV Upload/Handling functions
                    911: 
                    912: # ========================================================= Store uploaded file
                    913: # needs $ENV{'form.upfile'}
                    914: # return $datatoken to be put into hidden field
                    915: 
                    916: sub upfile_store {
                    917:     my $r=shift;
                    918:     $ENV{'form.upfile'}=~s/\r/\n/gs;
                    919:     $ENV{'form.upfile'}=~s/\f/\n/gs;
                    920:     $ENV{'form.upfile'}=~s/\n+/\n/gs;
                    921:     $ENV{'form.upfile'}=~s/\n+$//gs;
                    922: 
                    923:     my $datatoken=$ENV{'user.name'}.'_'.$ENV{'user.domain'}.
                    924: 	'_enroll_'.$ENV{'request.course.id'}.'_'.time.'_'.$$;
                    925:     {
                    926: 	my $fh=Apache::File->new('>'.$r->dir_config('lonDaemons').
                    927: 				 '/tmp/'.$datatoken.'.tmp');
                    928: 	print $fh $ENV{'form.upfile'};
                    929:     }
                    930:     return $datatoken;
                    931: }
                    932: 
                    933: # ================================================= Load uploaded file from tmp
                    934: # needs $ENV{'form.datatoken'}
                    935: # sets $ENV{'form.upfile'} to the contents of the file
                    936: 
                    937: sub load_tmp_file {
                    938:     my $r=shift;
                    939:     my @studentdata=();
                    940:     {
                    941: 	my $fh;
                    942: 	if ($fh=Apache::File->new($r->dir_config('lonDaemons').
                    943: 				  '/tmp/'.$ENV{'form.datatoken'}.'.tmp')) {
                    944: 	    @studentdata=<$fh>;
                    945: 	}
                    946:     }
                    947:     $ENV{'form.upfile'}=join('',@studentdata);
                    948: }
                    949: 
                    950: # ========================================= Separate uploaded file into records
                    951: # returns array of records
                    952: # needs $ENV{'form.upfile'}
                    953: # needs $ENV{'form.upfiletype'}
                    954: 
                    955: sub upfile_record_sep {
                    956:     if ($ENV{'form.upfiletype'} eq 'xml') {
                    957:     } else {
                    958: 	return split(/\n/,$ENV{'form.upfile'});
                    959:     }
                    960: }
                    961: 
                    962: # =============================================== Separate a record into fields
                    963: # needs $ENV{'form.upfiletype'}
                    964: # takes $record as arg
                    965: sub record_sep {
                    966:     my $record=shift;
                    967:     my %components=();
                    968:     if ($ENV{'form.upfiletype'} eq 'xml') {
                    969:     } elsif ($ENV{'form.upfiletype'} eq 'space') {
                    970:         my $i=0;
                    971:         foreach (split(/\s+/,$record)) {
                    972:             my $field=$_;
                    973:             $field=~s/^(\"|\')//;
                    974:             $field=~s/(\"|\')$//;
                    975:             $components{$i}=$field;
                    976:             $i++;
                    977:         }
                    978:     } elsif ($ENV{'form.upfiletype'} eq 'tab') {
                    979:         my $i=0;
                    980:         foreach (split(/\t+/,$record)) {
                    981:             my $field=$_;
                    982:             $field=~s/^(\"|\')//;
                    983:             $field=~s/(\"|\')$//;
                    984:             $components{$i}=$field;
                    985:             $i++;
                    986:         }
                    987:     } else {
                    988:         my @allfields=split(/\,/,$record);
                    989:         my $i=0;
                    990:         my $j;
                    991:         for ($j=0;$j<=$#allfields;$j++) {
                    992:             my $field=$allfields[$j];
                    993:             if ($field=~/^\s*(\"|\')/) {
                    994: 		my $delimiter=$1;
                    995:                 while (($field!~/$delimiter$/) && ($j<$#allfields)) {
                    996: 		    $j++;
                    997: 		    $field.=','.$allfields[$j];
                    998: 		}
                    999:                 $field=~s/^\s*$delimiter//;
                   1000:                 $field=~s/$delimiter\s*$//;
                   1001:             }
                   1002:             $components{$i}=$field;
                   1003: 	    $i++;
                   1004:         }
                   1005:     }
                   1006:     return %components;
                   1007: }
                   1008: 
                   1009: # =============================== HTML code to select file and specify its type
                   1010: sub upfile_select_html {
                   1011:     return (<<'ENDUPFORM');
                   1012: <input type="file" name="upfile" size="50">
                   1013: <br />Type: <select name="upfiletype">
                   1014: <option value="csv">CSV (comma separated values, spreadsheet)</option>
                   1015: <option value="space">Space separated</option>
                   1016: <option value="tab">Tabulator separated</option>
                   1017: <option value="xml">HTML/XML</option>
                   1018: </select>
                   1019: ENDUPFORM
                   1020: }
                   1021: 
                   1022: # ===================Prints a table of sample values from each column uploaded
                   1023: # $r is an Apache Request ref
                   1024: # $records is an arrayref from &Apache::loncommon::upfile_record_sep
                   1025: sub csv_print_samples {
                   1026:     my ($r,$records) = @_;
                   1027:     my (%sone,%stwo,%sthree);
                   1028:     %sone=&record_sep($$records[0]);
                   1029:     if (defined($$records[1])) {%stwo=&record_sep($$records[1]);}
                   1030:     if (defined($$records[2])) {%sthree=&record_sep($$records[2]);}
                   1031: 
                   1032:     $r->print('Samples<br /><table border="2"><tr>');
                   1033:     foreach (sort({$a <=> $b} keys(%sone))) { $r->print('<th>Column&nbsp;'.($_+1).'</th>'); }
                   1034:     $r->print('</tr>');
                   1035:     foreach my $hash (\%sone,\%stwo,\%sthree) {
                   1036: 	$r->print('<tr>');
                   1037: 	foreach (sort({$a <=> $b} keys(%sone))) {
                   1038: 	    $r->print('<td>');
                   1039: 	    if (defined($$hash{$_})) { $r->print($$hash{$_}); }
                   1040: 	    $r->print('</td>');
                   1041: 	}
                   1042: 	$r->print('</tr>');
                   1043:     }
                   1044:     $r->print('</tr></table><br />'."\n");
                   1045: }
                   1046: 
                   1047: # ======Prints a table to create associations between values and table columns
                   1048: # $r is an Apache Request ref
                   1049: # $records is an arrayref from &Apache::loncommon::upfile_record_sep
                   1050: # $d is an array of 2 element arrays (internal name, displayed name)
                   1051: sub csv_print_select_table {
                   1052:     my ($r,$records,$d) = @_;
                   1053:     my $i=0;my %sone;
                   1054:     %sone=&record_sep($$records[0]);
                   1055:     $r->print('Associate columns with student attributes.'."\n".
                   1056: 	     '<table border="2"><tr><th>Attribute</th><th>Column</th></tr>'."\n");
                   1057:     foreach (@$d) {
                   1058: 	my ($value,$display)=@{ $_ };
                   1059: 	$r->print('<tr><td>'.$display.'</td>');
                   1060: 
                   1061: 	$r->print('<td><select name=f'.$i.
1.32      matthew  1062: 		  ' onchange="javascript:flip(this.form,'.$i.');">');
1.31      albertel 1063: 	$r->print('<option value="none"></option>');
                   1064: 	foreach (sort({$a <=> $b} keys(%sone))) {
                   1065: 	    $r->print('<option value="'.$_.'">Column '.($_+1).'</option>');
                   1066: 	}
                   1067: 	$r->print('</select></td></tr>'."\n");
                   1068: 	$i++;
                   1069:     }
                   1070:     $i--;
                   1071:     return $i;
                   1072: }
                   1073: 
                   1074: # ===================Prints a table of sample values from the upload and
                   1075: #                      can make associate samples to internal names
                   1076: # $r is an Apache Request ref
                   1077: # $records is an arrayref from &Apache::loncommon::upfile_record_sep
                   1078: # $d is an array of 2 element arrays (internal name, displayed name)
                   1079: sub csv_samples_select_table {
                   1080:     my ($r,$records,$d) = @_;
                   1081:     my %sone; my %stwo; my %sthree;
                   1082:     my $i=0;
                   1083: 
                   1084:     $r->print('<table border=2><tr><th>Field</th><th>Samples</th></tr>');
                   1085:     %sone=&record_sep($$records[0]);
                   1086:     if (defined($$records[1])) {%stwo=&record_sep($$records[1]);}
                   1087:     if (defined($$records[2])) {%sthree=&record_sep($$records[2]);}
                   1088: 
                   1089:     foreach (sort keys %sone) {
                   1090: 	$r->print('<tr><td><select name=f'.$i.
1.32      matthew  1091: 		  ' onchange="javascript:flip(this.form,'.$i.');">');
1.31      albertel 1092: 	foreach (@$d) {
                   1093: 	    my ($value,$display)=@{ $_ };
                   1094: 	    $r->print('<option value='.$value.'>'.$display.'</option>');
                   1095: 	}
                   1096: 	$r->print('</select></td><td>');
                   1097: 	if (defined($sone{$_})) { $r->print($sone{$_}."</br>\n"); }
                   1098: 	if (defined($stwo{$_})) { $r->print($stwo{$_}."</br>\n"); }
                   1099: 	if (defined($sthree{$_})) { $r->print($sthree{$_}."</br>\n"); }
                   1100: 	$r->print('</td></tr>');
                   1101: 	$i++;
                   1102:     }
                   1103:     $i--;
                   1104:     return($i);
1.25      albertel 1105: }
1.1       albertel 1106: 1;
                   1107: __END__;
1.17      harris41 1108: 
1.35      matthew  1109: =item languageids() 
1.17      harris41 1110: 
1.35      matthew  1111: returns list of all language ids
1.17      harris41 1112: 
1.35      matthew  1113: =item languagedescription() 
1.17      harris41 1114: 
1.35      matthew  1115: returns description of a specified language id
1.17      harris41 1116: 
1.35      matthew  1117: =item copyrightids() 
1.17      harris41 1118: 
1.35      matthew  1119: returns list of all copyrights
1.17      harris41 1120: 
1.35      matthew  1121: =item copyrightdescription() 
1.17      harris41 1122: 
1.35      matthew  1123: returns description of a specified copyright id
1.17      harris41 1124: 
1.35      matthew  1125: =item filecategories() 
1.17      harris41 1126: 
1.35      matthew  1127: returns list of all file categories
1.17      harris41 1128: 
1.35      matthew  1129: =item filecategorytypes() 
1.17      harris41 1130: 
1.35      matthew  1131: returns list of file types belonging to a given file
1.17      harris41 1132: category
                   1133: 
1.35      matthew  1134: =item fileembstyle() 
1.17      harris41 1135: 
1.35      matthew  1136: returns embedding style for a specified file type
1.17      harris41 1137: 
1.35      matthew  1138: =item filedescription() 
1.17      harris41 1139: 
1.35      matthew  1140: returns description for a specified file type
1.17      harris41 1141: 
1.35      matthew  1142: =item filedescriptionex() 
1.17      harris41 1143: 
1.35      matthew  1144: returns description for a specified file type with
1.17      harris41 1145: extra formatting
                   1146: 
1.35      matthew  1147: =item get_previous_attempt() 
1.17      harris41 1148: 
1.35      matthew  1149: return string with previous attempt on problem
1.17      harris41 1150: 
1.35      matthew  1151: =item get_student_view() 
1.17      harris41 1152: 
1.35      matthew  1153: show a snapshot of what student was looking at
1.17      harris41 1154: 
1.35      matthew  1155: =item get_student_answers() 
1.17      harris41 1156: 
1.35      matthew  1157: show a snapshot of how student was answering problem
1.17      harris41 1158: 
1.35      matthew  1159: =item get_unprocessed_cgi() 
1.17      harris41 1160: 
1.35      matthew  1161: get unparsed CGI parameters
1.17      harris41 1162: 
1.35      matthew  1163: =item cacheheader() 
1.17      harris41 1164: 
1.35      matthew  1165: returns cache-controlling header code
1.17      harris41 1166: 
1.35      matthew  1167: =item nocache() 
1.17      harris41 1168: 
1.35      matthew  1169: specifies header code to not have cache
1.25      albertel 1170: 
1.35      matthew  1171: =item add_to_env($name,$value) 
1.25      albertel 1172: 
1.35      matthew  1173: adds $name to the %ENV hash with value
1.25      albertel 1174: $value, if $name already exists, the entry is converted to an array
                   1175: reference and $value is added to the array.
1.17      harris41 1176: 
                   1177: =back
                   1178: 
                   1179: =cut

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