Annotation of rat/lonuserstate.pm, revision 1.22

1.1       www         1: # The LearningOnline Network with CAPA
                      2: # Construct and maintain state and binary representation of course for user
                      3: #
                      4: # (Server for RAT Maps
                      5: #
                      6: # (Edit Handler for RAT Maps
                      7: # (TeX Content Handler
                      8: #
                      9: # 05/29/00,05/30 Gerd Kortemeyer)
                     10: # 7/1 Gerd Kortemeyer)
                     11: # 7/1,7/3,7/4,7/7,7/8,7/10 Gerd Kortemeyer)
                     12: #
1.9       www        13: # 7/15,7/17,7/18,8/1,8/2,8/4,8/5,8/21,8/22,8/23,8/30,
1.19      www        14: # 9/2,9/4,9/29,9/30,10/2,10/11,10/30,10/31,
1.22    ! www        15: # 11/1,11/2,11/14,11/16,11/22,12/28,
        !            16: # 07/05/01 Gerd Kortemeyer
1.1       www        17: 
                     18: package Apache::lonuserstate;
                     19: 
                     20: use strict;
                     21: use Apache::Constants qw(:common :http);
                     22: use Apache::File;
                     23: use HTML::TokeParser;
                     24: use Apache::lonnet();
                     25: use GDBM_File;
1.12      www        26: use Apache::lonmsg;
1.15      www        27: use Safe;
1.21      www        28: use Safe::Hole;
1.15      www        29: use Opcode;
                     30: 
1.1       www        31: # ---------------------------------------------------- Globals for this package
                     32: 
                     33: my $pc;      # Package counter
                     34: my %hash;    # The big tied hash
1.19      www        35: my %parmhash;# The hash with the parameters
1.1       www        36: my @cond;    # Array with all of the conditions
                     37: my $errtext; # variable with all errors
1.21      www        38: my $retfurl; # variable with the very first URL in the course
1.1       www        39: 
                     40: # --------------------------------------------------------- Loads map from disk
                     41: 
                     42: sub loadmap { 
                     43:     my $uri=shift;
                     44:     if ($hash{'map_pc_'.$uri}) { return OK; }
                     45: 
                     46:     $pc++;
                     47:     my $lpc=$pc;
                     48:     $hash{'map_pc_'.$uri}=$lpc;
                     49:     $hash{'map_id_'.$lpc}=$uri;
                     50: 
                     51:     my $fn='/home/httpd/html'.$uri;
                     52: 
1.10      www        53:     unless (($fn=~/\.sequence$/) ||
1.1       www        54:             ($fn=~/\.page$/)) { 
                     55:        $errtext.="Invalid map: $fn\n";
                     56:        return OK; 
                     57:     }
                     58: 
1.22    ! www        59:     my $ispage=($fn=~/\.page$/);
        !            60: 
1.1       www        61:     unless (-e $fn) {
                     62: 	my $returned=Apache::lonnet::repcopy($fn);
                     63:         unless ($returned eq OK) {
                     64:            $errtext.="Could not import: $fn - ";
                     65:            if ($returned eq HTTP_SERVICE_UNAVAILABLE) {
                     66: 	      $errtext.="Server unavailable\n";
                     67:            }
                     68:            if ($returned eq HTTP_NOT_FOUND) {
                     69: 	      $errtext.="File not found\n";
                     70:            }
                     71:            if ($returned eq FORBIDDEN) {
                     72: 	      $errtext.="Access forbidden\n";
                     73:            }
                     74:            return OK;
                     75:        }
                     76:     }
                     77: 
                     78:     if (-e $fn) {
                     79:         my @content;
                     80:         {
                     81: 	    my $fh=Apache::File->new($fn);
                     82:             @content=<$fh>;
                     83:         }
                     84:         my $instr=join('',@content);
                     85:         my $parser = HTML::TokeParser->new(\$instr);
                     86:         my $token;
                     87: 
                     88:         my $linkpc=0;
                     89: 
                     90:         $fn=~/\.(\w+)$/;
                     91: 
                     92:         $hash{'map_type_'.$lpc}=$1;
                     93: 
                     94:         while ($token = $parser->get_token) {
                     95: 	    if ($token->[0] eq 'S') {
                     96:                 if ($token->[1] eq 'resource') {
                     97: # -------------------------------------------------------------------- Resource
                     98: 
                     99:                     my $rid=$lpc.'.'.$token->[2]->{'id'};
                    100: 
                    101:                     $hash{'kind_'.$rid}='res';
                    102:                     $hash{'title_'.$rid}=$token->[2]->{'title'};
                    103:                     my $turi=$token->[2]->{'src'};
1.22    ! www       104:                     unless ($ispage) {
        !           105:                         $turi=~/\.(\w+)$/;
        !           106:                         my $embstyle=&Apache::lonnet::fileembstyle($1);
        !           107:                         if ($token->[2]->{'external'} eq 'true') {
        !           108:                             $turi=~s/^http\:\/\//\/adm\/wrapper\/ext\//;
        !           109:                         } else {
        !           110:                            my $embstyle=&Apache::lonnet::fileembstyle($1);
        !           111:                            if (($embstyle eq 'img') || ($embstyle eq 'emb')) {
        !           112: 			       $turi='/adm/wrapper'.$turi;
        !           113:                            }
        !           114:                         }
        !           115: 		    }
1.1       www       116:                     $hash{'src_'.$rid}=$turi;
                    117: 
                    118:                     if (defined($hash{'ids_'.$turi})) {
                    119:                         $hash{'ids_'.$turi}.=','.$rid;
                    120:                     } else {
                    121:                         $hash{'ids_'.$turi}=''.$rid;
                    122:                     }
                    123: 
1.22    ! www       124:                     if ($token->[2]->{'external'} eq 'true') {
1.1       www       125:                         $hash{'ext_'.$rid}='true:';
                    126:                     } else {
                    127:                         $hash{'ext_'.$rid}='false:';
                    128:                     }
                    129:                     if ($token->[2]->{'type'}) {
                    130: 			$hash{'type_'.$rid}=$token->[2]->{'type'};
1.2       www       131:                         if ($token->[2]->{'type'} eq 'start') {
                    132: 			    $hash{'map_start_'.$uri}="$rid";
                    133:                         }
                    134:                         if ($token->[2]->{'type'} eq 'finish') {
                    135: 			    $hash{'map_finish_'.$uri}="$rid";
                    136:                         }
1.1       www       137:                     }  else {
                    138:                         $hash{'type_'.$rid}='normal';
                    139:                     }
                    140: 
1.10      www       141:                     if (($turi=~/\.sequence$/) ||
1.1       www       142:                         ($turi=~/\.page$/)) {
1.2       www       143:                         $hash{'is_map_'.$rid}=1;
1.1       www       144:                         &loadmap($turi);
                    145:                     } 
                    146:                     
                    147:                 } elsif ($token->[1] eq 'condition') {
                    148: # ------------------------------------------------------------------- Condition
                    149: 
                    150:                     my $rid=$lpc.'.'.$token->[2]->{'id'};
                    151: 
                    152:                     $hash{'kind_'.$rid}='cond';
1.2       www       153:                     $cond[$#cond+1]=$token->[2]->{'value'};
                    154:                     $hash{'condid_'.$rid}=$#cond;
1.1       www       155:                     if ($token->[2]->{'type'}) {
1.2       www       156:                         $cond[$#cond].=':'.$token->[2]->{'type'};
1.1       www       157:                     }  else {
1.2       www       158:                         $cond[$#cond].=':normal';
1.1       www       159:                     }
                    160: 
                    161:                 } elsif ($token->[1] eq 'link') {
                    162: # ----------------------------------------------------------------------- Links
                    163: 
                    164:                     $linkpc++;
                    165:                     my $linkid=$lpc.'.'.$linkpc;
                    166: 
                    167:                     my $goesto=$lpc.'.'.$token->[2]->{'to'};
                    168:                     my $comesfrom=$lpc.'.'.$token->[2]->{'from'};
                    169:                     my $undercond=0;
                    170: 
                    171:                     if ($token->[2]->{'condition'}) {
                    172: 			$undercond=$lpc.'.'.$token->[2]->{'condition'};
                    173:                     }
                    174: 
                    175:                     $hash{'goesto_'.$linkid}=$goesto;
                    176:                     $hash{'comesfrom_'.$linkid}=$comesfrom;
                    177:                     $hash{'undercond_'.$linkid}=$undercond;
                    178: 
                    179:                     if (defined($hash{'to_'.$comesfrom})) {
                    180:                         $hash{'to_'.$comesfrom}.=','.$linkid;
                    181:                     } else {
                    182:                         $hash{'to_'.$comesfrom}=''.$linkid;
                    183:                     }
                    184:                     if (defined($hash{'from_'.$goesto})) {
                    185:                         $hash{'from_'.$goesto}.=','.$linkid;
                    186:                     } else {
                    187:                         $hash{'from_'.$goesto}=''.$linkid;
                    188:                     }
1.18      www       189:                 } elsif ($token->[1] eq 'param') {
                    190: # ------------------------------------------------------------------- Parameter
                    191: 
                    192:                     my $referid=$lpc.'.'.$token->[2]->{'to'};
1.20      www       193:                     my $part=$token->[2]->{'part'};
                    194:                     unless ($part) { $part=0; }
1.18      www       195:                     my $newparam=
                    196: 			&Apache::lonnet::escape($token->[2]->{'type'}).':'.
1.20      www       197: 			&Apache::lonnet::escape($part.'.'.
                    198:                          $token->[2]->{'name'}).'='.
1.18      www       199: 			&Apache::lonnet::escape($token->[2]->{'value'});
                    200:                     if (defined($hash{'param_'.$referid})) {
                    201:                         $hash{'param_'.$referid}.='&'.$newparam;
                    202:                     } else {
                    203:                         $hash{'param_'.$referid}=''.$newparam;
                    204:                     }
                    205: 
1.1       www       206:                 } 
                    207: 
                    208:             }
                    209:         }
                    210: 
                    211:     } else {
                    212:         $errtext.='Map not loaded: The file does not exist. ';
                    213:     }
                    214: }
                    215: 
1.3       www       216: # --------------------------------------------------------- Simplify expression
                    217: 
                    218: sub simplify {
                    219:    my $expression=shift;
                    220: # (8)=8
                    221:    $expression=~s/\((\d+)\)/$1/g;
                    222: # 8&8=8
1.7       www       223:    $expression=~s/(\D)(\d+)\&\2(\D)/$1$2$3/g;
1.3       www       224: # 8|8=8
1.7       www       225:    $expression=~s/(\D)(\d+)\|\2(\D)/$1$2$3/g;
1.3       www       226: # (5&3)&4=5&3&4
1.7       www       227:    $expression=~s/\((\d+)((?:\&\d+)+)\)\&(\d+\D)/$1$2\&$3/g;
1.3       www       228: # (((5&3)|(4&6)))=((5&3)|(4&6))
                    229:    $expression=~
                    230:        s/\((\(\(\d+(?:\&\d+)*\)(?:\|\(\d+(?:\&\d+)*\))+\))\)/$1/g;
                    231: # ((5&3)|(4&6))|(1&2)=(5&3)|(4&6)|(1&2)
                    232:    $expression=~
                    233:        s/\((\(\d+(?:\&\d+)*\))((?:\|\(\d+(?:\&\d+)*\))+)\)\|(\(\d+(?:\&\d+)*\))/\($1$2\|$3\)/g;
                    234:    return $expression;
                    235: }
                    236: 
1.2       www       237: # -------------------------------------------------------- Build condition hash
                    238: 
                    239: sub traceroute {
1.3       www       240:     my ($sofar,$rid,$beenhere)=@_;
                    241:     $sofar=simplify($sofar);
1.2       www       242:     unless ($beenhere=~/\&$rid\&/) {
                    243:        $beenhere.=$rid.'&';  
1.21      www       244:        if ($retfurl eq '') {
                    245:            $retfurl=$hash{'src_'.$rid};
                    246:        }
1.2       www       247:        if (defined($hash{'conditions_'.$rid})) {
1.3       www       248: 	   $hash{'conditions_'.$rid}=simplify(
                    249:            '('.$hash{'conditions_'.$rid}.')|('.$sofar.')');
1.2       www       250:        } else {
                    251:            $hash{'conditions_'.$rid}=$sofar;
                    252:        }
                    253:        if (defined($hash{'is_map_'.$rid})) {
1.3       www       254:            if (defined($hash{'map_start_'.$hash{'src_'.$rid}})) {
                    255: 	       &traceroute($sofar,$hash{'map_start_'.$hash{'src_'.$rid}},'&');
                    256:                if (defined($hash{'map_finish_'.$hash{'src_'.$rid}})) {
                    257: 		   $sofar=
                    258:                   $hash{'conditions_'.$hash{'map_finish_'.$hash{'src_'.$rid}}};
                    259:                }
1.2       www       260:            }
                    261:        }
                    262:        if (defined($hash{'to_'.$rid})) {
                    263:           map {
                    264: 		my $further=$sofar;
                    265:                 if ($hash{'undercond_'.$_}) {
                    266: 		   if (defined($hash{'condid_'.$hash{'undercond_'.$_}})) {
1.3       www       267:   		       $further=simplify('('.$further.')&('.
                    268:                               $hash{'condid_'.$hash{'undercond_'.$_}}.')');
1.2       www       269: 		   } else {
                    270:                        $errtext.='Undefined condition ID: '
                    271:                                  .$hash{'undercond_'.$_}.'. ';
                    272:                    }
                    273:                 }
                    274:                 &traceroute($further,$hash{'goesto_'.$_},$beenhere);
                    275:           } split(/\,/,$hash{'to_'.$rid});
                    276:        }
                    277:     }
                    278: }
1.1       www       279: 
1.19      www       280: # ------------------------------ Cascading conditions, quick access, parameters
1.4       www       281: 
                    282: sub accinit {
                    283:     my ($uri,$short,$fn)=@_;
                    284:     my %acchash=();
                    285:     my %captured=();
                    286:     my $condcounter=0;
1.5       www       287:     $acchash{'acc.cond.'.$short.'.0'}=0;
1.4       www       288:     map {
                    289:        if ($_=~/^conditions/) {
                    290: 	  my $expr=$hash{$_};
                    291:           map {
                    292:              my $sub=$_;
                    293:              my $orig=$_;
1.13      www       294:       $sub=~/\(\((\d+\&(:?\d+\&)*)(?:\d+\&*)+\)(?:\|\(\1(?:\d+\&*)+\))+\)/;
1.4       www       295:              my $factor=$1;
1.7       www       296:              $sub=~s/$factor//g;
                    297:              $sub=~s/^\(/\($factor\(/;
1.4       www       298: 	     $sub.=')';
                    299:              $sub=simplify($sub);
                    300:              $orig=~s/(\W)/\\$1/g;
1.7       www       301:  	     $expr=~s/$orig/$sub/;
1.4       www       302: 	  } ($expr=~m/(\(\(\d+(?:\&\d+)+\)(?:\|\(\d+(?:\&\d+)+\))+\))/g);
                    303:           $hash{$_}=$expr;
                    304:           unless (defined($captured{$expr})) {
                    305: 	      $condcounter++;
                    306:               $captured{$expr}=$condcounter;
1.5       www       307:               $acchash{'acc.cond.'.$short.'.'.$condcounter}=$expr;
1.4       www       308:           } 
1.19      www       309:        } elsif ($_=~/^param_(\d+)\.(\d+)/) {
                    310:           my $prefix=&Apache::lonnet::declutter($hash{'map_id_'.$1}).
                    311:       '___'.$2.'___'.&Apache::lonnet::declutter($hash{'src_'.$1.'.'.$2});
                    312:           map {
                    313: 	     my ($typename,$value)=split(/\=/,$_);
                    314:              my ($type,$name)=split(/\:/,$typename);
                    315:              $parmhash{$prefix.'.'.&Apache::lonnet::unescape($name)}=
                    316:                                    &Apache::lonnet::unescape($value);
                    317: 	     $parmhash{$prefix.'.'.&Apache::lonnet::unescape($name).'.type'}=
                    318:                                    &Apache::lonnet::unescape($type);
                    319:           } split(/\&/,$hash{$_});
                    320:        }
1.4       www       321:     } keys %hash;
                    322:     map {
                    323: 	if ($_=~/^ids/) {
1.13      www       324: 	  map {
                    325: 	    my $resid=$_;
1.4       www       326:             my $uri=$hash{'src_'.$resid};
1.22    ! www       327:             $uri=~s/^\/adm\/wrapper//;
1.4       www       328:             my @uriparts=split(/\//,$uri);
                    329:             my $urifile=$uriparts[$#uriparts];
                    330:             $#uriparts--;
                    331:             my $uripath=join('/',@uriparts);
1.8       www       332:             $uripath=~s/^\/res\///;
1.13      www       333:             my $uricond='0';
1.4       www       334:             if (defined($hash{'conditions_'.$resid})) {
1.13      www       335:  		$uricond=$captured{$hash{'conditions_'.$resid}};
1.4       www       336:             }
1.5       www       337:             if (defined($acchash{'acc.res.'.$short.'.'.$uripath})) {
1.13      www       338:                 if ($acchash{'acc.res.'.$short.'.'.$uripath}=~
                    339:                    /(\&$urifile\:[^\&]*)/) {
                    340: 		    my $replace=$1;
                    341:                     $acchash{'acc.res.'.$short.'.'.$uripath}
                    342:                      =~s/$replace/$replace\|$uricond/;
                    343:                 } else {
                    344: 		   $acchash{'acc.res.'.$short.'.'.$uripath}.=
                    345:                      $urifile.':'.$uricond.'&';
                    346: 	        }
1.4       www       347:             } else {
1.13      www       348:                 $acchash{'acc.res.'.$short.'.'.$uripath}=
                    349:                  '&'.$urifile.':'.$uricond.'&';
                    350:             } 
                    351:          } split(/\,/,$hash{$_});
                    352:       }
1.4       www       353:     } keys %hash;
1.8       www       354:     my $courseuri=$uri;
                    355:     $courseuri=~s/^\/res\///;
1.19      www       356:     &Apache::lonnet::delenv('(acc\.|httpref\.)');
1.4       www       357:     &Apache::lonnet::appenv(%acchash,
1.9       www       358:                             "request.course.id"  => $short,
1.8       www       359:                             "request.course.fn"  => $fn,
                    360:                             "request.course.uri" => $courseuri); 
1.4       www       361: }
                    362: 
1.1       www       363: # ---------------------------------------------------- Read map and all submaps
                    364: 
                    365: sub readmap {
1.9       www       366:    my $short=shift;
                    367:    $short=~s/^\///;
                    368:    my %cenv=&Apache::lonnet::coursedescription($short);
                    369:    my $fn=$cenv{'fn'};
                    370:    my $uri;
                    371:    $short=~s/\//\_/g;
                    372:    unless ($uri=$cenv{'url'}) { 
                    373:       &Apache::lonnet::logthis("<font color=blue>WARNING: ".
                    374:                        "Could not load course $short.</font>"); 
                    375:       return 'No course data available.';
                    376:    }
1.3       www       377:    @cond=('true:normal');
1.11      www       378:    unlink($fn.'.db');
                    379:    unlink($fn.'_symb.db');
                    380:    unlink($fn.'.state');
1.19      www       381:    unlink($fn.'parms.db');
1.21      www       382:    $retfurl='';
1.19      www       383:    if ((tie(%hash,'GDBM_File',"$fn.db",&GDBM_WRCREAT,0640)) &&
                    384:        (tie(%parmhash,'GDBM_File',$fn.'_parms.db',&GDBM_WRCREAT,0640))) {
1.4       www       385:     %hash=();
1.19      www       386:     %parmhash=();
1.4       www       387:     $errtext='';
                    388:     $pc=0;
                    389:     loadmap($uri);
                    390:     if (defined($hash{'map_start_'.$uri})) {
                    391:         &traceroute('0',$hash{'map_start_'.$uri},'&');
                    392:         &accinit($uri,$short,$fn);
1.2       www       393:     }
1.19      www       394:     unless ((untie(%hash)) && (untie(%parmhash))) {
1.4       www       395:       &Apache::lonnet::logthis("<font color=blue>WARNING: ".
                    396:                        "Could not untie coursemap $fn for $uri.</font>"); 
1.1       www       397:     }
1.4       www       398:     {
                    399:      my $cfh;
                    400:      if ($cfh=Apache::File->new(">$fn.state")) {
                    401:         print $cfh join("\n",@cond);
                    402:      } else {
1.6       www       403:       &Apache::lonnet::logthis("<font color=blue>WARNING: ".
1.4       www       404:                        "Could not write statemap $fn for $uri.</font>"); 
                    405:      }
                    406:     }  
                    407:    } else {
1.6       www       408:       &Apache::lonnet::logthis("<font color=blue>WARNING: ".
1.4       www       409:                        "Could not tie coursemap $fn for $uri.</font>"); 
                    410:    }
1.12      www       411:    &Apache::lonmsg::author_res_msg($ENV{'request.course.uri'},$errtext);
1.21      www       412:    return ($retfurl,$errtext);
1.1       www       413: }
1.15      www       414: 
                    415: # ------------------------------------------------------- Evaluate state string
                    416: 
                    417: sub evalstate {
1.21      www       418: 
1.15      www       419:     my $fn=$ENV{'request.course.fn'}.'.state';
                    420:     my $state='2';
                    421:     if (-e $fn) {
                    422:        my @conditions=();
                    423:        {
                    424:         my $fh=Apache::File->new($fn);
                    425:         @conditions=<$fh>;
                    426:        }  
1.21      www       427:        my $safeeval = new Safe;
                    428:        my $safehole = new Safe::Hole;
1.15      www       429:        $safeeval->permit("entereval");
                    430:        $safeeval->permit(":base_math");
                    431:        $safeeval->deny(":base_io");
1.21      www       432:        $safehole->wrap(\&Apache::lonnet::EXT,$safeeval,'&EXT');
1.15      www       433:        map {
                    434: 	   my $line=$_;
                    435:            chomp($line);
                    436: 	   my ($condition,$weight)=split(/\:/,$_);
                    437:            if ($safeeval->reval($condition)) {
                    438: 	       if ($weight eq 'force') {
                    439: 		   $state.='3';
                    440:                } else {
                    441:                    $state.='2';
                    442:                }
                    443:            } else {
                    444:                if ($weight eq 'stop') {
                    445: 		   $state.='0';
                    446:                } else {
                    447:                    $state.='1';
                    448:                }
                    449:            }
                    450:        } @conditions;
                    451:     }
                    452:     &Apache::lonnet::appenv('user.state.'.$ENV{'request.course.id'} => $state);
                    453:     return $state;
                    454: }
                    455: 
1.1       www       456: 1;
                    457: __END__
                    458: 
                    459: 
                    460: 
                    461: 
                    462: 
                    463: 
                    464: 

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>
500 Internal Server Error

Internal Server Error

The server encountered an internal error or misconfiguration and was unable to complete your request.

Please contact the server administrator at root@localhost to inform them of the time this error occurred, and the actions you performed just before this error.

More information about this error may be available in the server error log.