File:  [LON-CAPA] / loncom / interface / loncommon.pm
Revision 1.34: download - view: text, annotated - select for diffs
Tue Apr 23 21:00:01 2002 UTC (22 years, 2 months ago) by matthew
Branches: MAIN
CVS tags: HEAD
Added &get_domains and &select_dom_form.

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

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