Annotation of rat/lonuserstate.pm, revision 1.129

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