Annotation of rat/lonuserstate.pm, revision 1.111

1.1       www         1: # The LearningOnline Network with CAPA
                      2: # Construct and maintain state and binary representation of course for user
                      3: #
1.111   ! albertel    4: # $Id: lonuserstate.pm,v 1.110 2006/05/11 23:54:06 albertel Exp $
1.25      www         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.26      harris41   28: ###
1.1       www        29: 
                     30: package Apache::lonuserstate;
                     31: 
1.26      harris41   32: # ------------------------------------------------- modules used by this module
1.1       www        33: use strict;
                     34: use HTML::TokeParser;
1.89      albertel   35: use Apache::lonnet;
1.26      harris41   36: use Apache::loncommon();
1.1       www        37: use GDBM_File;
1.12      www        38: use Apache::lonmsg;
1.15      www        39: use Safe;
1.21      www        40: use Safe::Hole;
1.15      www        41: use Opcode;
1.73      www        42: use Apache::lonenc;
1.96      albertel   43: use Fcntl qw(:flock);
1.15      www        44: 
1.1       www        45: # ---------------------------------------------------- Globals for this package
                     46: 
                     47: my $pc;      # Package counter
                     48: my %hash;    # The big tied hash
1.19      www        49: my %parmhash;# The hash with the parameters
1.1       www        50: my @cond;    # Array with all of the conditions
                     51: my $errtext; # variable with all errors
1.21      www        52: my $retfurl; # variable with the very first URL in the course
1.29      www        53: my %randompick; # randomly picked resources
1.51      www        54: my %randompickseed; # optional seed for randomly picking resources
1.73      www        55: my %encurl; # URLs in this folder are supposed to be encrypted
                     56: my %hiddenurl; # this URL (or complete folder) is supposed to be hidden
1.61      www        57: 
                     58: # ----------------------------------- Remove version from URL and store in hash
                     59: 
                     60: sub versiontrack {
                     61:     my $uri=shift;
                     62:     if ($uri=~/\.(\d+)\.\w+$/) {
                     63: 	my $version=$1;
                     64: 	$uri=~s/\.\d+\.(\w+)$/\.$1/;
1.62      www        65:         unless ($hash{'version_'.$uri}) {
                     66: 	    $hash{'version_'.$uri}=$version;
                     67: 	}
1.61      www        68:     }
                     69:     return $uri;
                     70: }
                     71: 
                     72: # -------------------------------------------------------------- Put in version
                     73: 
                     74: sub putinversion {
                     75:     my $uri=shift;
1.93      www        76:     my $key=$env{'request.course.id'}.'_'.&Apache::lonnet::clutter($uri);
1.61      www        77:     if ($hash{'version_'.$uri}) {
                     78: 	my $version=$hash{'version_'.$uri};
1.65      www        79: 	if ($version eq 'mostrecent') { return $uri; }
1.66      www        80: 	if ($version eq &Apache::lonnet::getversion(
                     81: 			&Apache::lonnet::filelocation('',$uri))) 
                     82: 	             { return $uri; }
1.61      www        83: 	$uri=~s/\.(\w+)$/\.$version\.$1/;
                     84:     }
1.93      www        85:     &Apache::lonnet::do_cache_new('courseresversion',$key,&Apache::lonnet::declutter($uri),600);
1.61      www        86:     return $uri;
                     87: }
                     88: 
                     89: # ----------------------------------------- Processing versions file for course
                     90: 
                     91: sub processversionfile {
1.64      www        92:     my %cenv=@_;
1.61      www        93:     my %versions=&Apache::lonnet::dump('resourceversions',
                     94: 				       $cenv{'domain'},
                     95: 				       $cenv{'num'});
1.106     albertel   96:     foreach my $ver (keys(%versions)) {
                     97: 	if ($ver=~/^error\:/) { return; }
                     98: 	$hash{'version_'.$ver}=$versions{$ver};
1.61      www        99:     }
                    100: }
1.45      www       101: 
1.1       www       102: # --------------------------------------------------------- Loads map from disk
                    103: 
                    104: sub loadmap { 
                    105:     my $uri=shift;
1.98      albertel  106:     if ($hash{'map_pc_'.$uri}) { return; }
1.1       www       107: 
                    108:     $pc++;
                    109:     my $lpc=$pc;
                    110:     $hash{'map_pc_'.$uri}=$lpc;
                    111:     $hash{'map_id_'.$lpc}=$uri;
                    112: 
1.37      www       113: # Determine and check filename
1.62      www       114:     my $fn=&Apache::lonnet::filelocation('',&putinversion($uri));
1.37      www       115: 
                    116:     my $ispage=($fn=~/\.page$/);
1.1       www       117: 
1.10      www       118:     unless (($fn=~/\.sequence$/) ||
1.1       www       119:             ($fn=~/\.page$/)) { 
1.85      albertel  120: 	$errtext.="Invalid map: $fn\n";
1.98      albertel  121: 	return; 
1.1       www       122:     }
                    123: 
1.37      www       124:     my $instr=&Apache::lonnet::getfile($fn);
                    125: 
1.57      albertel  126:     unless ($instr eq -1) {
1.22      www       127: 
1.37      www       128: # Successfully got file, parse it
1.1       www       129: 
                    130:         my $parser = HTML::TokeParser->new(\$instr);
1.95      www       131: 	$parser->attr_encoded(1);
1.1       www       132:         my $token;
                    133: 
                    134:         my $linkpc=0;
                    135: 
                    136:         $fn=~/\.(\w+)$/;
                    137: 
                    138:         $hash{'map_type_'.$lpc}=$1;
                    139: 
                    140:         while ($token = $parser->get_token) {
                    141: 	    if ($token->[0] eq 'S') {
                    142:                 if ($token->[1] eq 'resource') {
                    143: # -------------------------------------------------------------------- Resource
1.92      www       144:                     if ($token->[2]->{'type'} eq 'zombie') { next; }
1.1       www       145:                     my $rid=$lpc.'.'.$token->[2]->{'id'};
                    146: 
                    147:                     $hash{'kind_'.$rid}='res';
                    148:                     $hash{'title_'.$rid}=$token->[2]->{'title'};
1.61      www       149:                     my $turi=&versiontrack($token->[2]->{'src'});
                    150:                     if ($token->[2]->{'version'}) {
1.62      www       151: 			unless ($hash{'version_'.$turi}) {
                    152: 			    $hash{'version_'.$turi}=$1;
                    153: 			}
1.61      www       154: 		    }
1.83      albertel  155: 		    my $title=$token->[2]->{'title'};
                    156: 		    $title=~s/\&colon\;/\:/gs;
1.84      albertel  157: #		    my $symb=&Apache::lonnet::encode_symb($uri,
                    158: #							  $token->[2]->{'id'},
                    159: #							  $turi);
                    160: #		    &Apache::lonnet::do_cache_new('title',$symb,$title);
1.22      www       161:                     unless ($ispage) {
                    162:                         $turi=~/\.(\w+)$/;
1.26      harris41  163:                         my $embstyle=&Apache::loncommon::fileembstyle($1);
1.40      www       164:                         if ($token->[2]->{'external'} eq 'true') { # external
1.22      www       165:                             $turi=~s/^http\:\/\//\/adm\/wrapper\/ext\//;
1.40      www       166:                         } elsif ($turi=~/^\/*uploaded\//) { # uploaded
1.97      www       167: 			    if (($embstyle eq 'img') 
                    168: 				|| ($embstyle eq 'emb')
1.99      albertel  169: 				|| ($embstyle eq 'wrp')) {
1.76      albertel  170:                                 $turi='/adm/wrapper'.$turi;
1.78      albertel  171: 			    } elsif ($embstyle eq 'ssi') {
                    172: 				#do nothing with these
                    173: 			    } elsif ($turi!~/\.(sequence|page)$/) {
1.42      www       174: 				$turi='/adm/coursedocs/showdoc'.$turi;
1.40      www       175:                             }
1.70      www       176:                         } elsif ($turi=~/\S/) { # normal non-empty internal resource
1.68      www       177: 			    my $mapdir=$uri;
                    178: 			    $mapdir=~s/[^\/]+$//;
                    179: 			    $turi=&Apache::lonnet::hreflocation($mapdir,$turi);
1.99      albertel  180: 			    if (($embstyle eq 'img') 
                    181: 				|| ($embstyle eq 'emb')
                    182: 				|| ($embstyle eq 'wrp')) {
1.68      www       183: 				$turi='/adm/wrapper'.$turi;
                    184: 			    }
1.22      www       185:                         }
                    186: 		    }
1.71      www       187: # Store reverse lookup, remove query string
                    188: 		    my $idsuri=$turi;
                    189: 		    $idsuri=~s/\?.+$//;
                    190:                     if (defined($hash{'ids_'.$idsuri})) {
                    191:                         $hash{'ids_'.$idsuri}.=','.$rid;
1.1       www       192:                     } else {
1.71      www       193:                         $hash{'ids_'.$idsuri}=''.$rid;
1.1       www       194:                     }
1.53      www       195:                
1.85      albertel  196:                     if ($turi=~/\/(syllabus|aboutme|navmaps|smppg|bulletinboard)$/) {
1.53      www       197: 			$turi.='?register=1';
                    198: 		    }
                    199: 
                    200:                     $hash{'src_'.$rid}=$turi;
1.1       www       201: 
1.22      www       202:                     if ($token->[2]->{'external'} eq 'true') {
1.1       www       203:                         $hash{'ext_'.$rid}='true:';
                    204:                     } else {
                    205:                         $hash{'ext_'.$rid}='false:';
                    206:                     }
                    207:                     if ($token->[2]->{'type'}) {
                    208: 			$hash{'type_'.$rid}=$token->[2]->{'type'};
1.2       www       209:                         if ($token->[2]->{'type'} eq 'start') {
                    210: 			    $hash{'map_start_'.$uri}="$rid";
                    211:                         }
                    212:                         if ($token->[2]->{'type'} eq 'finish') {
                    213: 			    $hash{'map_finish_'.$uri}="$rid";
                    214:                         }
1.1       www       215:                     }  else {
                    216:                         $hash{'type_'.$rid}='normal';
                    217:                     }
                    218: 
1.10      www       219:                     if (($turi=~/\.sequence$/) ||
1.1       www       220:                         ($turi=~/\.page$/)) {
1.2       www       221:                         $hash{'is_map_'.$rid}=1;
1.1       www       222:                         &loadmap($turi);
                    223:                     } 
                    224:                     
                    225:                 } elsif ($token->[1] eq 'condition') {
                    226: # ------------------------------------------------------------------- Condition
                    227: 
                    228:                     my $rid=$lpc.'.'.$token->[2]->{'id'};
                    229: 
                    230:                     $hash{'kind_'.$rid}='cond';
1.2       www       231:                     $cond[$#cond+1]=$token->[2]->{'value'};
                    232:                     $hash{'condid_'.$rid}=$#cond;
1.1       www       233:                     if ($token->[2]->{'type'}) {
1.2       www       234:                         $cond[$#cond].=':'.$token->[2]->{'type'};
1.1       www       235:                     }  else {
1.2       www       236:                         $cond[$#cond].=':normal';
1.1       www       237:                     }
                    238: 
                    239:                 } elsif ($token->[1] eq 'link') {
                    240: # ----------------------------------------------------------------------- Links
                    241: 
                    242:                     $linkpc++;
                    243:                     my $linkid=$lpc.'.'.$linkpc;
                    244: 
                    245:                     my $goesto=$lpc.'.'.$token->[2]->{'to'};
                    246:                     my $comesfrom=$lpc.'.'.$token->[2]->{'from'};
                    247:                     my $undercond=0;
                    248: 
                    249:                     if ($token->[2]->{'condition'}) {
                    250: 			$undercond=$lpc.'.'.$token->[2]->{'condition'};
                    251:                     }
                    252: 
                    253:                     $hash{'goesto_'.$linkid}=$goesto;
                    254:                     $hash{'comesfrom_'.$linkid}=$comesfrom;
                    255:                     $hash{'undercond_'.$linkid}=$undercond;
                    256: 
                    257:                     if (defined($hash{'to_'.$comesfrom})) {
                    258:                         $hash{'to_'.$comesfrom}.=','.$linkid;
                    259:                     } else {
                    260:                         $hash{'to_'.$comesfrom}=''.$linkid;
                    261:                     }
                    262:                     if (defined($hash{'from_'.$goesto})) {
                    263:                         $hash{'from_'.$goesto}.=','.$linkid;
                    264:                     } else {
                    265:                         $hash{'from_'.$goesto}=''.$linkid;
                    266:                     }
1.18      www       267:                 } elsif ($token->[1] eq 'param') {
                    268: # ------------------------------------------------------------------- Parameter
                    269: 
                    270:                     my $referid=$lpc.'.'.$token->[2]->{'to'};
1.63      albertel  271: 		    my $name=$token->[2]->{'name'};
                    272: 		    my $part;
                    273: 		    if ($name=~/^parameter_(.*)_/) {
                    274: 			$part=$1;
                    275: 		    } else {
                    276: 			$part=0;
                    277: 		    }
                    278: 		    $name=~s/^.*_([^_]*)$/$1/;
1.18      www       279:                     my $newparam=
                    280: 			&Apache::lonnet::escape($token->[2]->{'type'}).':'.
1.63      albertel  281: 			&Apache::lonnet::escape($part.'.'.$name).'='.
1.18      www       282: 			&Apache::lonnet::escape($token->[2]->{'value'});
                    283:                     if (defined($hash{'param_'.$referid})) {
                    284:                         $hash{'param_'.$referid}.='&'.$newparam;
                    285:                     } else {
                    286:                         $hash{'param_'.$referid}=''.$newparam;
                    287:                     }
1.75      www       288:                     if ($token->[2]->{'name'}=~/^parameter_(0_)*mapalias$/) {
1.28      www       289: 			$hash{'mapalias_'.$token->[2]->{'value'}}=$referid;
                    290:                     }
1.75      www       291:                     if ($token->[2]->{'name'}=~/^parameter_(0_)*randompick$/) {
1.29      www       292: 			$randompick{$referid}=$token->[2]->{'value'};
                    293:                     }
1.75      www       294:                     if ($token->[2]->{'name'}=~/^parameter_(0_)*randompickseed$/) {
1.51      www       295: 			$randompick{$referid}=$token->[2]->{'value'};
                    296:                     }
1.75      www       297:                     if ($token->[2]->{'name'}=~/^parameter_(0_)*encrypturl$/) {
1.74      www       298: 			if ($token->[2]->{'value'}=~/^yes$/i) {
                    299: 			    $encurl{$referid}=1;
                    300: 			}
1.73      www       301:                     }
1.75      www       302:                     if ($token->[2]->{'name'}=~/^parameter_(0_)*hiddenresource$/) {
1.74      www       303: 			if ($token->[2]->{'value'}=~/^yes$/i) {
                    304: 			    $hiddenurl{$referid}=1;
                    305: 			}
1.73      www       306:                     }
1.1       www       307:                 } 
1.85      albertel  308: 		
1.1       www       309:             }
                    310:         }
                    311: 
                    312:     } else {
1.84      albertel  313:         $errtext.='Map not loaded: The file ('.$fn.') does not exist. ';
1.1       www       314:     }
                    315: }
                    316: 
1.3       www       317: # --------------------------------------------------------- Simplify expression
                    318: 
                    319: sub simplify {
1.85      albertel  320:     my $expression=shift;
1.101     albertel  321: # (0&1) = 1
1.105     albertel  322:     $expression=~s/\(0\&([_\.\d]+)\)/$1/g;
1.3       www       323: # (8)=8
1.105     albertel  324:     $expression=~s/\(([_\.\d]+)\)/$1/g;
1.3       www       325: # 8&8=8
1.105     albertel  326:     $expression=~s/([^_\.\d])([_\.\d]+)\&\2([^_\.\d])/$1$2$3/g;
1.3       www       327: # 8|8=8
1.105     albertel  328:     $expression=~s/([^_\.\d])([_\.\d]+)\|\2([^_\.\d])/$1$2$3/g;
1.3       www       329: # (5&3)&4=5&3&4
1.105     albertel  330:     $expression=~s/\(([_\.\d]+)((?:\&[_\.\d]+)+)\)\&([_\.\d]+[^_\.\d])/$1$2\&$3/g;
1.3       www       331: # (((5&3)|(4&6)))=((5&3)|(4&6))
1.105     albertel  332:     $expression=~
                    333: 	s/\((\(\([_\.\d]+(?:\&[_\.\d]+)*\)(?:\|\([_\.\d]+(?:\&[_\.\d]+)*\))+\))\)/$1/g;
1.3       www       334: # ((5&3)|(4&6))|(1&2)=(5&3)|(4&6)|(1&2)
1.85      albertel  335:     $expression=~
1.105     albertel  336: 	s/\((\([_\.\d]+(?:\&[_\.\d]+)*\))((?:\|\([_\.\d]+(?:\&[_\.\d]+)*\))+)\)\|(\([_\.\d]+(?:\&[_\.\d]+)*\))/\($1$2\|$3\)/g;
1.85      albertel  337:     return $expression;
1.3       www       338: }
                    339: 
1.2       www       340: # -------------------------------------------------------- Build condition hash
                    341: 
                    342: sub traceroute {
1.77      www       343:     my ($sofar,$rid,$beenhere,$encflag,$hdnflag)=@_;
1.81      albertel  344:     my $newsofar=$sofar=simplify($sofar);
1.2       www       345:     unless ($beenhere=~/\&$rid\&/) {
1.85      albertel  346: 	$beenhere.=$rid.'&';  
                    347: 	my ($mapid,$resid)=split(/\./,$rid);
                    348: 	my $symb=&Apache::lonnet::encode_symb($hash{'map_id_'.$mapid},$resid,$hash{'src_'.$rid});
                    349: 	my $hidden=&Apache::lonnet::EXT('resource.0.hiddenresource',$symb);
1.91      albertel  350: 
1.90      albertel  351: 	if ($hdnflag || lc($hidden) eq 'yes') {
                    352: 	    $hiddenurl{$rid}=1;
1.91      albertel  353: 	}
                    354: 	if (!$hdnflag && lc($hidden) eq 'no') {
1.90      albertel  355: 	    delete($hiddenurl{$rid});
                    356: 	}
1.91      albertel  357: 
1.85      albertel  358: 	my $encrypt=&Apache::lonnet::EXT('resource.0.encrypturl',$symb);
                    359: 	if ($encflag || lc($encrypt) eq 'yes') { $encurl{$rid}=1; }
                    360: 	if (($retfurl eq '') && ($hash{'src_'.$rid})
                    361: 	    && ($hash{'src_'.$rid}!~/\.sequence$/)) {
                    362: 	    $retfurl=$hash{'src_'.$rid}.(($hash{'src_'.$rid}=~/\?/)?'&':'?').
                    363: 		'symb='.$symb;
                    364: 	}
                    365: 	if (defined($hash{'conditions_'.$rid})) {
                    366: 	    $hash{'conditions_'.$rid}=simplify(
1.103     albertel  367:            '('.$hash{'conditions_'.$rid}.')|('.$sofar.')');
1.85      albertel  368: 	} else {
                    369: 	    $hash{'conditions_'.$rid}=$sofar;
                    370: 	}
1.107     albertel  371: 
                    372: 	# if the expression is just the 0th condition keep it
                    373: 	# otherwise leave a pointer to this condition expression
                    374: 	$newsofar = ($sofar eq '0') ? $sofar : '_'.$rid;
                    375: 
1.85      albertel  376: 	if (defined($hash{'is_map_'.$rid})) {
                    377: 	    if (defined($hash{'map_start_'.$hash{'src_'.$rid}})) {
                    378: 		$sofar=$newsofar=
                    379: 		    &traceroute($sofar,
                    380: 				$hash{'map_start_'.$hash{'src_'.$rid}},'&',
                    381: 				$encflag || $encurl{$rid},
                    382: 				$hdnflag || $hiddenurl{$rid});
                    383: 	    }
                    384: 	}
                    385: 	if (defined($hash{'to_'.$rid})) {
1.106     albertel  386: 	    foreach my $id (split(/\,/,$hash{'to_'.$rid})) {
1.2       www       387: 		my $further=$sofar;
1.106     albertel  388:                 if ($hash{'undercond_'.$id}) {
                    389: 		    if (defined($hash{'condid_'.$hash{'undercond_'.$id}})) {
1.105     albertel  390: 			$further=simplify('('.'_'.$rid.')&('.
1.106     albertel  391: 					  $hash{'condid_'.$hash{'undercond_'.$id}}.')');
1.85      albertel  392: 		    } else {
                    393: 			$errtext.='Undefined condition ID: '
1.106     albertel  394: 			    .$hash{'undercond_'.$id}.'. ';
1.85      albertel  395: 		    }
1.2       www       396:                 }
1.106     albertel  397:                 $newsofar=&traceroute($further,$hash{'goesto_'.$id},$beenhere,
1.81      albertel  398: 				      $encflag,$hdnflag);
1.85      albertel  399: 	    }
                    400: 	}
1.2       www       401:     }
1.81      albertel  402:     return $newsofar;
1.2       www       403: }
1.1       www       404: 
1.19      www       405: # ------------------------------ Cascading conditions, quick access, parameters
1.4       www       406: 
                    407: sub accinit {
                    408:     my ($uri,$short,$fn)=@_;
                    409:     my %acchash=();
                    410:     my %captured=();
                    411:     my $condcounter=0;
1.5       www       412:     $acchash{'acc.cond.'.$short.'.0'}=0;
1.104     albertel  413:     foreach my $key (keys(%hash)) {
                    414: 	if ($key=~/^conditions/) {
                    415: 	    my $expr=$hash{$key};
1.109     albertel  416: 	    # try to find and factor out common sub-expressions
1.105     albertel  417: 	    foreach my $sub ($expr=~m/(\(\([_\.\d]+(?:\&[_\.\d]+)+\)(?:\|\([_\.\d]+(?:\&[_\.\d]+)+\))+\))/g) {
1.104     albertel  418: 		my $orig=$sub;
1.109     albertel  419: 
                    420: 		my ($factor) = ($sub=~/\(\(([_\.\d]+\&(:?[_\.\d]+\&)*)(?:[_\.\d]+\&*)+\)(?:\|\(\1(?:[_\.\d]+\&*)+\))+\)/);
                    421: 		next if (!defined($factor));
                    422: 
                    423: 		$sub=~s/\Q$factor\E//g;
1.85      albertel  424: 		$sub=~s/^\(/\($factor\(/;
                    425: 		$sub.=')';
                    426: 		$sub=simplify($sub);
1.109     albertel  427: 		$expr=~s/\Q$orig\E/$sub/;
1.85      albertel  428: 	    }
1.104     albertel  429: 	    $hash{$key}=$expr;
1.85      albertel  430: 	    unless (defined($captured{$expr})) {
                    431: 		$condcounter++;
                    432: 		$captured{$expr}=$condcounter;
                    433: 		$acchash{'acc.cond.'.$short.'.'.$condcounter}=$expr;
                    434: 	    } 
1.104     albertel  435: 	} elsif ($key=~/^param_(\d+)\.(\d+)/) {
1.86      albertel  436: 	    my $prefix=&Apache::lonnet::encode_symb($hash{'map_id_'.$1},$2,
                    437: 						    $hash{'src_'.$1.'.'.$2});
1.104     albertel  438: 	    foreach my $param (split(/\&/,$hash{$key})) {
                    439: 		my ($typename,$value)=split(/\=/,$param);
1.85      albertel  440: 		my ($type,$name)=split(/\:/,$typename);
                    441: 		$parmhash{$prefix.'.'.&Apache::lonnet::unescape($name)}=
                    442: 		    &Apache::lonnet::unescape($value);
                    443: 		$parmhash{$prefix.'.'.&Apache::lonnet::unescape($name).'.type'}=
                    444: 		    &Apache::lonnet::unescape($type);
                    445: 	    }
                    446: 	}
1.26      harris41  447:     }
1.104     albertel  448:     foreach my $key (keys(%hash)) {
                    449: 	if ($key=~/^ids/) {
                    450: 	    foreach my $resid (split(/\,/,$hash{$key})) {
1.85      albertel  451: 		my $uri=$hash{'src_'.$resid};
1.100     albertel  452: 		my ($uripath,$urifile) =
                    453: 		    &Apache::lonnet::split_uri_for_cond($uri);
1.85      albertel  454: 		if ($uripath) {
                    455: 		    my $uricond='0';
                    456: 		    if (defined($hash{'conditions_'.$resid})) {
                    457: 			$uricond=$captured{$hash{'conditions_'.$resid}};
                    458: 		    }
                    459: 		    if (defined($acchash{'acc.res.'.$short.'.'.$uripath})) {
                    460: 			if ($acchash{'acc.res.'.$short.'.'.$uripath}=~
                    461: 			    /(\&\Q$urifile\E\:[^\&]*)/) {
                    462: 			    my $replace=$1;
                    463: 			    my $regexp=$replace;
                    464: 			    #$regexp=~s/\|/\\\|/g;
1.105     albertel  465: 			    $acchash{'acc.res.'.$short.'.'.$uripath} =~
1.104     albertel  466: 				s/\Q$regexp\E/$replace\|$uricond/;
1.85      albertel  467: 			} else {
                    468: 			    $acchash{'acc.res.'.$short.'.'.$uripath}.=
                    469: 				$urifile.':'.$uricond.'&';
                    470: 			}
                    471: 		    } else {
                    472: 			$acchash{'acc.res.'.$short.'.'.$uripath}=
                    473: 			    '&'.$urifile.':'.$uricond.'&';
                    474: 		    }
                    475: 		} 
                    476: 	    }
                    477: 	}
1.26      harris41  478:     }
1.24      www       479:     $acchash{'acc.res.'.$short.'.'}='&:0&';
1.8       www       480:     my $courseuri=$uri;
                    481:     $courseuri=~s/^\/res\///;
1.19      www       482:     &Apache::lonnet::delenv('(acc\.|httpref\.)');
1.79      albertel  483:     &Apache::lonnet::appenv(%acchash);
1.4       www       484: }
                    485: 
1.73      www       486: # ---------------- Selectively delete from randompick maps and hidden url parms
1.29      www       487: 
1.73      www       488: sub hiddenurls {
1.31      www       489:     my $randomoutentry='';
1.29      www       490:     foreach my $rid (keys %randompick) {
                    491:         my $rndpick=$randompick{$rid};
                    492:         my $mpc=$hash{'map_pc_'.$hash{'src_'.$rid}};
                    493: # ------------------------------------------- put existing resources into array
                    494:         my @currentrids=();
1.106     albertel  495:         foreach my $key (sort(keys(%hash))) {
                    496: 	    if ($key=~/^src_($mpc\.\d+)/) {
1.29      www       497: 		if ($hash{'src_'.$1}) { push @currentrids, $1; }
                    498:             }
                    499:         }
1.50      albertel  500: 	# rids are number.number and we want to numercially sort on 
                    501:         # the second number
                    502: 	@currentrids=sort {
                    503: 	    my (undef,$aid)=split(/\./,$a);
                    504: 	    my (undef,$bid)=split(/\./,$b);
                    505: 	    $aid <=> $bid;
                    506: 	} @currentrids;
1.29      www       507:         next if ($#currentrids<$rndpick);
                    508: # -------------------------------- randomly eliminate the ones that should stay
1.50      albertel  509: 	my (undef,$id)=split(/\./,$rid);
1.51      www       510:         if ($randompickseed{$rid}) { $id=$randompickseed{$rid}; }
1.50      albertel  511: 	my $rndseed=&Apache::lonnet::rndseed($id); # use id instead of symb
1.58      albertel  512: 	&Apache::lonnet::setup_random_from_rndseed($rndseed);
1.50      albertel  513: 	my @whichids=&Math::Random::random_permuted_index($#currentrids+1);
                    514:         for (my $i=1;$i<=$rndpick;$i++) { $currentrids[$whichids[$i]]=''; }
                    515: 	#&Apache::lonnet::logthis("$id,$rndseed,".join(':',@whichids));
1.29      www       516: # -------------------------------------------------------- delete the leftovers
                    517:         for (my $k=0; $k<=$#currentrids; $k++) {
                    518:             if ($currentrids[$k]) {
                    519: 		$hash{'randomout_'.$currentrids[$k]}=1;
1.32      www       520:                 my ($mapid,$resid)=split(/\./,$currentrids[$k]);
                    521:                 $randomoutentry.='&'.
1.86      albertel  522: 		    &Apache::lonnet::encode_symb($hash{'map_id_'.$mapid},
                    523: 						 $resid,
                    524: 						 $hash{'src_'.$currentrids[$k]}
                    525: 						 ).'&';
1.29      www       526:             }
                    527:         }
1.31      www       528:     }
1.73      www       529: # ------------------------------ take care of explicitly hidden urls or folders
                    530:     foreach my $rid (keys %hiddenurl) {
                    531: 	$hash{'randomout_'.$rid}=1;
                    532: 	my ($mapid,$resid)=split(/\./,$rid);
                    533: 	$randomoutentry.='&'.
1.86      albertel  534: 	    &Apache::lonnet::encode_symb($hash{'map_id_'.$mapid},$resid,
                    535: 					 $hash{'src_'.$rid}).'&';
1.73      www       536:     }
                    537: # --------------------------------------- append randomout entry to environment
1.31      www       538:     if ($randomoutentry) {
                    539: 	&Apache::lonnet::appenv('acc.randomout' => $randomoutentry);
1.29      www       540:     }
                    541: }
                    542: 
1.1       www       543: # ---------------------------------------------------- Read map and all submaps
                    544: 
                    545: sub readmap {
1.85      albertel  546:     my $short=shift;
                    547:     $short=~s/^\///;
1.108     albertel  548:     my %cenv=&Apache::lonnet::coursedescription($short,{'freshen_cache'=>1});
1.85      albertel  549:     my $fn=$cenv{'fn'};
                    550:     my $uri;
                    551:     $short=~s/\//\_/g;
                    552:     unless ($uri=$cenv{'url'}) { 
                    553: 	&Apache::lonnet::logthis("<font color=blue>WARNING: ".
                    554: 				 "Could not load course $short.</font>"); 
                    555: 	return 'No course data available.';
                    556:     }
                    557:     @cond=('true:normal');
1.96      albertel  558: 
                    559:     open(LOCKFILE,">$fn.db.lock");
                    560:     my $lock=0;
                    561:     if (flock(LOCKFILE,LOCK_EX|LOCK_NB)) {
                    562: 	$lock=1;
                    563: 	unlink($fn.'.db');
                    564: 	unlink($fn.'_symb.db');
                    565: 	unlink($fn.'.state');
                    566: 	unlink($fn.'parms.db');
                    567:     }
1.85      albertel  568:     undef %randompick;
                    569:     undef %hiddenurl;
                    570:     undef %encurl;
                    571:     $retfurl='';
1.96      albertel  572:     if ($lock && (tie(%hash,'GDBM_File',"$fn.db",&GDBM_WRCREAT(),0640)) &&
1.85      albertel  573: 	(tie(%parmhash,'GDBM_File',$fn.'_parms.db',&GDBM_WRCREAT(),0640))) {
                    574: 	%hash=();
                    575: 	%parmhash=();
                    576: 	$errtext='';
                    577: 	$pc=0;
                    578: 	&processversionfile(%cenv);
                    579: 	my $furi=&Apache::lonnet::clutter($uri);
                    580: 	$hash{'src_0.0'}=&versiontrack($furi);
                    581: 	$hash{'title_0.0'}=&Apache::lonnet::metadata($uri,'title');
                    582: 	$hash{'ids_'.$furi}='0.0';
                    583: 	$hash{'is_map_0.0'}=1;
                    584: 	loadmap($uri);
                    585: 	if (defined($hash{'map_start_'.$uri})) {
                    586: 	    &Apache::lonnet::appenv("request.course.id"  => $short,
                    587: 				    "request.course.fn"  => $fn,
                    588: 				    "request.course.uri" => $uri);
                    589: 	    &traceroute('0',$hash{'map_start_'.$uri},'&');
                    590: 	    &accinit($uri,$short,$fn);
                    591: 	    &hiddenurls();
                    592: 	}
1.62      www       593: # ------------------------------------------------------- Put versions into src
1.106     albertel  594: 	foreach my $key (keys(%hash)) {
1.110     albertel  595: 	    if ($key=~/^src_/) {
1.106     albertel  596: 		$hash{$key}=&putinversion($hash{$key});
1.110     albertel  597: 	    } elsif ($key =~ /^(map_(?:start|finish|pc)_)(.*)/) {
                    598: 		my ($type, $url) = ($1,$2);
                    599: 		my $value = $hash{$key};
                    600: 		delete($hash{$key});
                    601: 		$hash{$type.&putinversion($url)}=$value;
1.85      albertel  602: 	    }
1.61      www       603: 	}
1.74      www       604: # ---------------------------------------------------------------- Encrypt URLs
1.106     albertel  605: 	foreach my $id (keys(%encurl)) {
                    606: #	    $hash{'src_'.$id}=&Apache::lonenc::encrypted($hash{'src_'.$id});
                    607: 	    $hash{'encrypted_'.$id}=1;
1.85      albertel  608: 	}
1.74      www       609: # ----------------------------------------------- Close hashes to finally store
                    610: # --------------------------------- Routine must pass this point, no early outs
1.94      albertel  611: 	$hash{'first_url'}=$retfurl;
1.85      albertel  612: 	unless ((untie(%hash)) && (untie(%parmhash))) {
                    613: 	    &Apache::lonnet::logthis("<font color=blue>WARNING: ".
                    614: 				     "Could not untie coursemap $fn for $uri.</font>"); 
                    615: 	}
                    616: # ---------------------------------------------------- Store away initial state
                    617: 	{
                    618: 	    my $cfh;
1.88      raeburn   619: 	    if (open($cfh,">$fn.state")) {
1.85      albertel  620: 		print $cfh join("\n",@cond);
                    621: 	    } else {
                    622: 		&Apache::lonnet::logthis("<font color=blue>WARNING: ".
                    623: 					 "Could not write statemap $fn for $uri.</font>"); 
                    624: 	    }
1.96      albertel  625: 	}
                    626: 	flock(LOCKFILE,LOCK_UN);
                    627: 	close(LOCKFILE);
1.85      albertel  628:     } else {
1.87      albertel  629: 	# if we are here it is likely because we are already trying to 
                    630: 	# initialize the course in another child, busy wait trying to 
                    631: 	# tie the hashes for the next 90 seconds, if we succeed forward 
                    632: 	# them on to navmaps, if we fail, throw up the Could not init 
                    633: 	# course screen
1.96      albertel  634: 	if ($lock) {
                    635: 	    # Got the lock but not the DB files
                    636: 	    flock(LOCKFILE,LOCK_UN);
                    637: 	}
1.87      albertel  638: 	untie(%hash);
                    639: 	untie(%parmhash);
1.85      albertel  640: 	&Apache::lonnet::logthis("<font color=blue>WARNING: ".
                    641: 				 "Could not tie coursemap $fn for $uri.</font>"); 
1.87      albertel  642: 	my $i=0;
                    643: 	while($i<90) {
                    644: 	    $i++;
                    645: 	    sleep(1);
1.96      albertel  646: 	    if (flock(LOCKFILE,LOCK_EX|LOCK_NB) &&
                    647: 		(tie(%hash,'GDBM_File',"$fn.db",&GDBM_READER(),0640))) {
1.87      albertel  648: 		if (tie(%parmhash,'GDBM_File',$fn.'_parms.db',&GDBM_READER(),0640)) {
                    649: 		    $retfurl='/adm/navmaps';
                    650: 		    &Apache::lonnet::appenv("request.course.id"  => $short,
                    651: 					    "request.course.fn"  => $fn,
                    652: 					    "request.course.uri" => $uri);
                    653: 		    untie(%hash);
                    654: 		    untie(%parmhash);
                    655: 		    last;
                    656: 		}
                    657: 	    }
                    658: 	    untie(%hash);
                    659: 	    untie(%parmhash);
                    660: 	}
1.96      albertel  661: 	flock(LOCKFILE,LOCK_UN);
                    662: 	close(LOCKFILE);
1.1       www       663:     }
1.89      albertel  664:     &Apache::lonmsg::author_res_msg($env{'request.course.uri'},$errtext);
1.46      www       665: # ------------------------------------------------- Check for critical messages
                    666: 
1.89      albertel  667:     my @what=&Apache::lonnet::dump('critical',$env{'user.domain'},
                    668: 				   $env{'user.name'});
1.46      www       669:     if ($what[0]) {
                    670: 	if (($what[0] ne 'con_lost') && ($what[0]!~/^error\:/)) {
                    671: 	    $retfurl='/adm/email?critical=display';
                    672:         }
                    673:     }
1.85      albertel  674:     return ($retfurl,$errtext);
1.1       www       675: }
1.15      www       676: 
                    677: # ------------------------------------------------------- Evaluate state string
                    678: 
                    679: sub evalstate {
1.89      albertel  680:     my $fn=$env{'request.course.fn'}.'.state';
1.80      albertel  681:     my $state='';
1.15      www       682:     if (-e $fn) {
1.80      albertel  683: 	my @conditions=();
                    684: 	{
                    685: 	    my $fh=Apache::File->new($fn);
                    686: 	    @conditions=<$fh>;
                    687: 	}  
                    688: 	my $safeeval = new Safe;
                    689: 	my $safehole = new Safe::Hole;
                    690: 	$safeeval->permit("entereval");
                    691: 	$safeeval->permit(":base_math");
                    692: 	$safeeval->deny(":base_io");
                    693: 	$safehole->wrap(\&Apache::lonnet::EXT,$safeeval,'&EXT');
                    694: 	foreach my $line (@conditions) {
                    695: 	    chomp($line);
                    696: 	    my ($condition,$weight)=split(/\:/,$line);
                    697: 	    if ($safeeval->reval($condition)) {
                    698: 		if ($weight eq 'force') {
                    699: 		    $state.='3';
                    700: 		} else {
                    701: 		    $state.='2';
                    702: 		}
                    703: 	    } else {
                    704: 		if ($weight eq 'stop') {
                    705: 		    $state.='0';
                    706: 		} else {
                    707: 		    $state.='1';
                    708: 		}
                    709: 	    }
                    710: 	}
1.15      www       711:     }
1.89      albertel  712:     &Apache::lonnet::appenv('user.state.'.$env{'request.course.id'} => $state);
1.15      www       713:     return $state;
                    714: }
                    715: 
1.1       www       716: 1;
                    717: __END__
                    718: 
1.26      harris41  719: =head1 NAME
                    720: 
                    721: Apache::lonuserstate - Construct and maintain state and binary representation
                    722: of course for user
                    723: 
                    724: =head1 SYNOPSIS
                    725: 
                    726: Invoked by lonroles.pm.
                    727: 
                    728: &Apache::lonuserstate::readmap($cdom.'/'.$cnum);
                    729: 
                    730: =head1 INTRODUCTION
                    731: 
                    732: This module constructs and maintains state and binary representation
                    733: of course for user.
                    734: 
                    735: This is part of the LearningOnline Network with CAPA project
                    736: described at http://www.lon-capa.org.
                    737: 
                    738: =head1 HANDLER SUBROUTINE
                    739: 
                    740: There is no handler subroutine.
                    741: 
                    742: =head1 OTHER SUBROUTINES
                    743: 
                    744: =over 4
                    745: 
                    746: =item *
                    747: 
                    748: loadmap() : Loads map from disk
                    749: 
                    750: =item *
                    751: 
                    752: simplify() : Simplify expression
                    753: 
                    754: =item *
                    755: 
                    756: traceroute() : Build condition hash
                    757: 
                    758: =item *
                    759: 
                    760: accinit() : Cascading conditions, quick access, parameters
1.1       www       761: 
1.26      harris41  762: =item *
1.1       www       763: 
1.26      harris41  764: readmap() : Read map and all submaps
1.1       www       765: 
1.26      harris41  766: =item *
1.1       www       767: 
1.26      harris41  768: evalstate() : Evaluate state string
1.1       www       769: 
1.26      harris41  770: =back
1.1       www       771: 
1.26      harris41  772: =cut

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.