Diff for /rat/lonuserstate.pm between versions 1.128.2.1 and 1.138

version 1.128.2.1, 2008/12/21 04:46:53 version 1.138, 2011/07/26 10:40:23
Line 43  use Opcode; Line 43  use Opcode;
 use Apache::lonenc;  use Apache::lonenc;
 use Fcntl qw(:flock);  use Fcntl qw(:flock);
 use LONCAPA;  use LONCAPA;
   use File::Basename;
   
     
   
 # ---------------------------------------------------- Globals for this package  # ---------------------------------------------------- Globals for this package
   
 my $pc;      # Package counter  my $pc;      # Package counter is this what 'Guts' calls the map counter?
 my %hash;    # The big tied hash  my %hash;    # The big tied hash
 my %parmhash;# The hash with the parameters  my %parmhash;# The hash with the parameters
 my @cond;    # Array with all of the conditions  my @cond;    # Array with all of the conditions
Line 62  my %hiddenurl; # this URL (or complete f Line 64  my %hiddenurl; # this URL (or complete f
   
 # ----------------------------------- Remove version from URL and store in hash  # ----------------------------------- Remove version from URL and store in hash
   
   sub versionerror {
       my ($uri,$usedversion,$unusedversion)=@_;
       return '<br />'.&mt('Version discrepancy: resource [_1] included in both version [_2] and version [_3]. Using version [_2].',
                       $uri,$usedversion,$unusedversion).'<br />';
   }
   
 sub versiontrack {  sub versiontrack {
     my $uri=shift;      my $uri=shift;
     if ($uri=~/\.(\d+)\.\w+$/) {      if ($uri=~/\.(\d+)\.\w+$/) {
Line 69  sub versiontrack { Line 77  sub versiontrack {
  $uri=~s/\.\d+\.(\w+)$/\.$1/;   $uri=~s/\.\d+\.(\w+)$/\.$1/;
         unless ($hash{'version_'.$uri}) {          unless ($hash{'version_'.$uri}) {
     $hash{'version_'.$uri}=$version;      $hash{'version_'.$uri}=$version;
  }   } elsif ($version!=$hash{'version_'.$uri}) {
               $errtext.=&versionerror($uri,$hash{'version_'.$uri},$version);
           }
     }      }
     return $uri;      return $uri;
 }  }
Line 104  sub processversionfile { Line 114  sub processversionfile {
     }      }
 }  }
   
 # --------------------------------------------------------- Loads map from disk  # --------------------------------------------------------- Loads from disk
   
 sub loadmap {   sub loadmap { 
     my ($uri,$parent_rid)=@_;      my ($uri,$parent_rid)=@_;
   
       # Is the map already included?
   
     if ($hash{'map_pc_'.$uri}) {       if ($hash{'map_pc_'.$uri}) { 
  $errtext.='<p class="LC_error">'.   $errtext.='<p class="LC_error">'.
     &mt('Multiple use of sequence/page [_1]! The course will not function properly.','<tt>'.$uri.'</tt>').      &mt('Multiple use of sequence/page [_1]! The course will not function properly.','<tt>'.$uri.'</tt>').
     '</p>';      '</p>';
  return;    return; 
     }      }
       # Register the resource in it's map_pc_ [for the URL]
       # map_id.nnn is the nesting level -> to the URI.
   
     $pc++;      $pc++;
     my $lpc=$pc;      my $lpc=$pc;
     $hash{'map_pc_'.$uri}=$lpc;      $hash{'map_pc_'.$uri}=$lpc;
     $hash{'map_id_'.$lpc}=$uri;      $hash{'map_id_'.$lpc}=$uri;
   
 # Determine and check filename      # If the parent is of the form n.m hang this map underneath it in the
       # map hierarchy.
   
       if ($parent_rid =~ /^(\d+)\.\d+$/) {
           my $parent_pc = $1;
           if (defined($hash{'map_hierarchy_'.$parent_pc})) {
               $hash{'map_hierarchy_'.$lpc}=$hash{'map_hierarchy_'.$parent_pc}.','.
                                            $parent_pc;
           } else {
               $hash{'map_hierarchy_'.$lpc}=$parent_pc;
           }
       }
   
   # Determine and check filename of the sequence we need to read:
   
     my $fn=&Apache::lonnet::filelocation('',&putinversion($uri));      my $fn=&Apache::lonnet::filelocation('',&putinversion($uri));
   
     my $ispage=($fn=~/\.page$/);      my $ispage=($fn=~/\.page$/);
   
     unless (($fn=~/\.sequence$/) ||      # We can only nest sequences or pages.  Anything else is an illegal nest.
             ($fn=~/\.page$/)) {   
       unless (($fn=~/\.sequence$/) || $ispage) { 
  $errtext.=&mt("<br />Invalid map: <tt>[_1]</tt>",$fn);   $errtext.=&mt("<br />Invalid map: <tt>[_1]</tt>",$fn);
  return;    return; 
     }      }
   
       # Read the XML that constitutes the file.
   
     my $instr=&Apache::lonnet::getfile($fn);      my $instr=&Apache::lonnet::getfile($fn);
   
     if ($instr eq -1) {      if ($instr eq -1) {
Line 137  sub loadmap { Line 170  sub loadmap {
  return;   return;
     }      }
   
 # Successfully got file, parse it      # Successfully got file, parse it
   
       # parse for parameter processing.
       # Note that these are <param... / > tags
       # so we only care about 'S' (tag start) nodes.
   
   
     my $parser = HTML::TokeParser->new(\$instr);      my $parser = HTML::TokeParser->new(\$instr);
     $parser->attr_encoded(1);      $parser->attr_encoded(1);
   
     # first get all parameters      # first get all parameters
   
   
     while (my $token = $parser->get_token) {      while (my $token = $parser->get_token) {
  next if ($token->[0] ne 'S');   next if ($token->[0] ne 'S');
  if ($token->[1] eq 'param') {   if ($token->[1] eq 'param') {
     &parse_param($token,$lpc);      &parse_param($token,$lpc);
  }    } 
     }      }
     #reset parser  
       # Get set to take another pass through the XML:
       # for resources and links.
   
     $parser = HTML::TokeParser->new(\$instr);      $parser = HTML::TokeParser->new(\$instr);
     $parser->attr_encoded(1);      $parser->attr_encoded(1);
   
Line 160  sub loadmap { Line 204  sub loadmap {
   
     my $randomize = ($randomorder{$parent_rid} =~ /^yes$/i);      my $randomize = ($randomorder{$parent_rid} =~ /^yes$/i);
   
       # Parse the resources, link and condition tags.
       # Note that if randomorder or random select is chosen the links and
       # conditions are meaningless but are determined by the randomization.
       # This is handled in the next chunk of code.
   
     my @map_ids;      my @map_ids;
     while (my $token = $parser->get_token) {      while (my $token = $parser->get_token) {
  next if ($token->[0] ne 'S');   next if ($token->[0] ne 'S');
   
    # Resource
   
  if ($token->[1] eq 'resource') {   if ($token->[1] eq 'resource') {
     push(@map_ids,&parse_resource($token,$lpc,$ispage,$uri));      my $resource_id = &parse_resource($token,$lpc,$ispage,$uri);
       if (defined $resource_id) {
    push(@map_ids, $resource_id);
       }
   
          # Link
   
  } elsif ($token->[1] eq 'link' && !$randomize) {   } elsif ($token->[1] eq 'link' && !$randomize) {
 # ----------------------------------------------------------------------- Links  
     &make_link(++$linkpc,$lpc,$token->[2]->{'to'},      &make_link(++$linkpc,$lpc,$token->[2]->{'to'},
        $token->[2]->{'from'},         $token->[2]->{'from'},
        $token->[2]->{'condition'});         $token->[2]->{'condition'});
   
    # condition
   
  } elsif ($token->[1] eq 'condition' && !$randomize) {   } elsif ($token->[1] eq 'condition' && !$randomize) {
     &parse_condition($token,$lpc);      &parse_condition($token,$lpc);
  }   }
     }      }
   
   
       # Handle randomization and random selection
   
     if ($randomize) {      if ($randomize) {
  if (!$env{'request.role.adv'}) {   if (!$env{'request.role.adv'}) {
     my $seed;      my $seed;
Line 188  sub loadmap { Line 251  sub loadmap {
   
  $seed = $symb;   $seed = $symb;
     }      }
   
       # Here for sure we need to pass along the username/domain
       # so that we can impersonate users in lonprintout e.g.
   
     my $rndseed=&Apache::lonnet::rndseed($seed);      my $rndseed=&Apache::lonnet::rndseed($seed);
     &Apache::lonnet::setup_random_from_rndseed($rndseed);      &Apache::lonnet::setup_random_from_rndseed($rndseed);
     @map_ids=&Math::Random::random_permutation(@map_ids);      @map_ids=&Math::Random::random_permutation(@map_ids);
Line 224  sub loadmap { Line 290  sub loadmap {
   
   
 # -------------------------------------------------------------------- Resource  # -------------------------------------------------------------------- Resource
   #
   #  Parses a resource tag to produce the value to push into the
   #  map_ids array.
   # 
   #
   #  Information about the actual type of resource is provided by the file extension
   #  of the uri (e.g. .problem, .sequence etc. etc.).
   #
   #  Parameters:
   #    $token   - A token from HTML::TokeParser
   #               This is an array that describes the most recently parsed HTML item.
   #    $lpc     - Map nesting level (?)
   #    $ispage  - True if this resource is encapsulated in a .page (assembled resourcde).
   #    $uri     - URI of the enclosing resource.
   # Returns:
   #
   # Note:
   #   The token is an array that contains the following elements:
   #   [0]   => 'S' indicating this is a start token
   #   [1]   => 'resource'  indicating this tag is a <resource> tag.
   #   [2]   => Hash of attribute =>value pairs.
   #   [3]   => @(keys [2]).
   #   [4]   => unused.
   #
   #   The attributes of the resourcde tag include:
   #
   #   id     - The resource id.
   #   src    - The URI of the resource.
   #   type   - The resource type (e.g. start and finish).
   #   title  - The resource title.
   
   
 sub parse_resource {  sub parse_resource {
     my ($token,$lpc,$ispage,$uri) = @_;      my ($token,$lpc,$ispage,$uri) = @_;
     if ($token->[2]->{'type'} eq 'zombie') { next; }      
       # I refuse to coutenance code like this that has 
       # such a dirty side effect (and forcing this sub to be called within a loop).
       #
       #  if ($token->[2]->{'type'} eq 'zombie') { next; }
   
       # Zombie resources don't produce anything useful.
   
       if ($token->[2]->{'type'} eq 'zombie') {
    return undef;
       }
   
     my $rid=$lpc.'.'.$token->[2]->{'id'};      my $rid=$lpc.'.'.$token->[2]->{'id'};
           
     $hash{'kind_'.$rid}='res';      $hash{'kind_'.$rid}='res';
Line 278  sub parse_resource { Line 387  sub parse_resource {
  $hash{'ids_'.$idsuri}=''.$rid;   $hash{'ids_'.$idsuri}=''.$rid;
     }      }
           
     if ($turi=~/\/(syllabus|aboutme|navmaps|smppg|bulletinboard)$/) {      if ($turi=~/\/(syllabus|aboutme|navmaps|smppg|bulletinboard|viewclasslist)$/) {
  $turi.='?register=1';   $turi.='?register=1';
     }      }
           
Line 356  sub parse_condition { Line 465  sub parse_condition {
 }  }
   
 # ------------------------------------------------------------------- Parameter  # ------------------------------------------------------------------- Parameter
   # Parse a <parameter> tag in the map.
   # Parmameters:
   #    $token Token array for a start tag from HTML::TokeParser
   #           [0] = 'S'
   #           [1] = tagname ("param")
   #           [2] = Hash of {attribute} = values.
   #           [3] = Array of the keys in [2].
   #           [4] = unused.
   #    $lpc   Current map nesting level.a
   #
   #  Typical attributes:
   #     to=n      - Number of the resource the parameter applies to.
   #     type=xx   - Type of parameter value (e.g. string_yesno or int_pos).
   #     name=xxx  - Name ofr parameter (e.g. parameter_randompick or parameter_randomorder).
   #     value=xxx - value of the parameter.
   
 sub parse_param {  sub parse_param {
     my ($token,$lpc) = @_;      my ($token,$lpc) = @_;
     my $referid=$lpc.'.'.$token->[2]->{'to'};      my $referid=$lpc.'.'.$token->[2]->{'to'}; # Resource param applies to.
     my $name=$token->[2]->{'name'};      my $name=$token->[2]->{'name'};      # Name of parameter
     my $part;      my $part;
     if ($name=~/^parameter_(.*)_/) {  
   
       if ($name=~/^parameter_(.*)_/) { 
  $part=$1;   $part=$1;
     } else {      } else {
  $part=0;   $part=0;
     }      }
   
       # Peel the parameter_ off the parameter name.
   
     $name=~s/^.*_([^_]*)$/$1/;      $name=~s/^.*_([^_]*)$/$1/;
   
       # The value is:
       #   type.part.name.value
   
     my $newparam=      my $newparam=
  &escape($token->[2]->{'type'}).':'.   &escape($token->[2]->{'type'}).':'.
  &escape($part.'.'.$name).'='.   &escape($part.'.'.$name).'='.
  &escape($token->[2]->{'value'});   &escape($token->[2]->{'value'});
   
       # The hash key is param_resourceid.
       # Multiple parameters for a single resource are & separated in the hash.
   
   
     if (defined($hash{'param_'.$referid})) {      if (defined($hash{'param_'.$referid})) {
  $hash{'param_'.$referid}.='&'.$newparam;   $hash{'param_'.$referid}.='&'.$newparam;
     } else {      } else {
  $hash{'param_'.$referid}=''.$newparam;   $hash{'param_'.$referid}=''.$newparam;
     }      }
     if ($token->[2]->{'name'}=~/^parameter_(0_)*randompick$/) {      #
       #  These parameters have to do with randomly selecting
       # resources, therefore a separate hash is also created to 
       # make it easy to locate them when actually computing the resource set later on
       # See the code conditionalized by ($randomize) in loadmap().
   
       if ($token->[2]->{'name'}=~/^parameter_(0_)*randompick$/) { # Random selection turned on
  $randompick{$referid}=$token->[2]->{'value'};   $randompick{$referid}=$token->[2]->{'value'};
     }      }
     if ($token->[2]->{'name'}=~/^parameter_(0_)*randompickseed$/) {      if ($token->[2]->{'name'}=~/^parameter_(0_)*randompickseed$/) { # Randomseed provided.
  $randompickseed{$referid}=$token->[2]->{'value'};   $randompickseed{$referid}=$token->[2]->{'value'};
     }      }
     if ($token->[2]->{'name'}=~/^parameter_(0_)*randomorder$/) {      if ($token->[2]->{'name'}=~/^parameter_(0_)*randomorder$/) { # Random order turned on.
  $randomorder{$referid}=$token->[2]->{'value'};   $randomorder{$referid}=$token->[2]->{'value'};
     }      }
   
       # These parameters have to do with how the URLs of resources are presented to
       # course members(?).  encrypturl presents encypted url's while
       # hiddenresource hides the URL.
       #
   
     if ($token->[2]->{'name'}=~/^parameter_(0_)*encrypturl$/) {      if ($token->[2]->{'name'}=~/^parameter_(0_)*encrypturl$/) {
  if ($token->[2]->{'value'}=~/^yes$/i) {   if ($token->[2]->{'value'}=~/^yes$/i) {
     $encurl{$referid}=1;      $encurl{$referid}=1;
Line 573  sub accinit { Line 723  sub accinit {
     $acchash{'acc.res.'.$short.'.'}='&:0&';      $acchash{'acc.res.'.$short.'.'}='&:0&';
     my $courseuri=$uri;      my $courseuri=$uri;
     $courseuri=~s/^\/res\///;      $courseuri=~s/^\/res\///;
     &Apache::lonnet::delenv('(acc\.|httpref\.)');      my $regexp = 1;
       &Apache::lonnet::delenv('(acc\.|httpref\.)',$regexp);
     &Apache::lonnet::appenv(\%acchash);      &Apache::lonnet::appenv(\%acchash);
 }  }
   
Line 639  sub hiddenurls { Line 790  sub hiddenurls {
 sub readmap {  sub readmap {
     my $short=shift;      my $short=shift;
     $short=~s/^\///;      $short=~s/^\///;
     my %cenv=&Apache::lonnet::coursedescription($short,{'freshen_cache'=>1});  
       # TODO:  Hidden dependency on current user:
   
       my %cenv=&Apache::lonnet::coursedescription($short,{'freshen_cache'=>1}); 
   
     my $fn=$cenv{'fn'};      my $fn=$cenv{'fn'};
     my $uri;      my $uri;
     $short=~s/\//\_/g;      $short=~s/\//\_/g;
     unless ($uri=$cenv{'url'}) {       unless ($uri=$cenv{'url'}) { 
  &Apache::lonnet::logthis("<font color=blue>WARNING: ".   &Apache::lonnet::logthis('<font color="blue">WARNING: '.
  "Could not load course $short.</font>");    "Could not load course $short.</font>"); 
  return ('',&mt('No course data available.'));;   return ('',&mt('No course data available.'));;
     }      }
     @cond=('true:normal');      @cond=('true:normal');
   
     open(LOCKFILE,">$fn.db.lock");      unless (open(LOCKFILE,">$fn.db.lock")) {
    # 
    # Most likely a permissions problem on the lockfile or its directory.
    #
           $errtext.='<br />'.&mt('Map not loaded - Lock file could not be opened when reading map:').' <tt>'.$fn.'</tt>.';
           $retfurl = '';
           return ($retfurl,$errtext);
       }
     my $lock=0;      my $lock=0;
     if (flock(LOCKFILE,LOCK_EX|LOCK_NB)) {      my $gotstate=0;
  $lock=1;      
  unlink($fn.'.db');      # If we can get the lock without delay any files there are idle
  unlink($fn.'_symb.db');      # and from some prior request.  We'll kill them off and regenerate them:
  unlink($fn.'.state');  
  unlink($fn.'parms.db');      if (flock(LOCKFILE,LOCK_EX|LOCK_NB)) {
    $lock=1; # Remember that we hold the lock.
           &unlink_tmpfiles($fn);
     }      }
     undef %randompick;      undef %randompick;
     undef %hiddenurl;      undef %hiddenurl;
     undef %encurl;      undef %encurl;
     $retfrid='';      $retfrid='';
     if ($lock && (tie(%hash,'GDBM_File',"$fn.db",&GDBM_WRCREAT(),0640)) &&      my ($untiedhash,$untiedparmhash,$tiedhash,$tiedparmhash); # More state flags.
  (tie(%parmhash,'GDBM_File',$fn.'_parms.db',&GDBM_WRCREAT(),0640))) {  
  %hash=();      # if we got the lock, regenerate course regnerate empty files and tie them.
  %parmhash=();  
  $errtext='';      if ($lock) {
  $pc=0;          if (tie(%hash,'GDBM_File',"$fn.db",&GDBM_WRCREAT(),0640)) {
  &clear_mapalias_count();              $tiedhash = 1;
  &processversionfile(%cenv);              if (tie(%parmhash,'GDBM_File',$fn.'_parms.db',&GDBM_WRCREAT(),0640)) {
  my $furi=&Apache::lonnet::clutter($uri);                  $tiedparmhash = 1;
  $hash{'src_0.0'}=&versiontrack($furi);                  $gotstate = &build_tmp_hashes($uri,
  $hash{'title_0.0'}=&Apache::lonnet::metadata($uri,'title');        $fn,
  $hash{'ids_'.$furi}='0.0';        $short,
  $hash{'is_map_0.0'}=1;        \%cenv); # TODO: Need to provide requested user@dom
  loadmap($uri,'0.0');                  unless ($gotstate) {
  if (defined($hash{'map_start_'.$uri})) {                      &Apache::lonnet::logthis('Failed to write statemap at first attempt '.$fn.' for '.$uri.'.</font>');
     &Apache::lonnet::appenv({"request.course.id"  => $short,                  }
      "request.course.fn"  => $fn,                  $untiedparmhash = untie(%parmhash);
      "request.course.uri" => $uri});                  unless ($untiedparmhash) {
     $env{'request.course.id'}=$short;                      &Apache::lonnet::logthis('<font color="blue">WARNING: '.
     &traceroute('0',$hash{'map_start_'.$uri},'&');                          'Could not untie coursemap parmhash '.$fn.' for '.$uri.'.</font>');
     &accinit($uri,$short,$fn);                  }
     &hiddenurls();              }
  }              $untiedhash = untie(%hash);
  $errtext .= &get_mapalias_errors();              unless ($untiedhash) {
 # ------------------------------------------------------- Put versions into src                  &Apache::lonnet::logthis('<font color="blue">WARNING: '.
  foreach my $key (keys(%hash)) {                      'Could not untie coursemap hash '.$fn.' for '.$uri.'.</font>');
     if ($key=~/^src_/) {              }
  $hash{$key}=&putinversion($hash{$key});          }
     } elsif ($key =~ /^(map_(?:start|finish|pc)_)(.*)/) {   flock(LOCKFILE,LOCK_UN); # RF: this is what I don't get unless there are other
  my ($type, $url) = ($1,$2);                           # unlocked places the remainder happens..seems like if we
  my $value = $hash{$key};                                   # just kept the lock here the rest of the code would have
  $hash{$type.&putinversion($url)}=$value;                                   # been much easier? 
     }      }
  }      unless ($lock && $tiedhash && $tiedparmhash) { 
 # ---------------------------------------------------------------- Encrypt URLs  
  foreach my $id (keys(%encurl)) {  
 #    $hash{'src_'.$id}=&Apache::lonenc::encrypted($hash{'src_'.$id});  
     $hash{'encrypted_'.$id}=1;  
  }  
 # ----------------------------------------------- Close hashes to finally store  
 # --------------------------------- Routine must pass this point, no early outs  
  $hash{'first_rid'}=$retfrid;  
  my ($mapid,$resid)=split(/\./,$retfrid);  
  $hash{'first_mapurl'}=$hash{'map_id_'.$mapid};  
  my $symb=&Apache::lonnet::encode_symb($hash{'map_id_'.$mapid},$resid,$hash{'src_'.$retfrid});  
  $retfurl=&add_get_param($hash{'src_'.$retfrid},{ 'symb' => $symb });  
  if ($hash{'encrypted_'.$retfrid}) {  
     $retfurl=&Apache::lonenc::encrypted($retfurl,(&Apache::lonnet::allowed('adv') ne 'F'));  
  }  
  $hash{'first_url'}=$retfurl;  
  unless ((untie(%hash)) && (untie(%parmhash))) {  
     &Apache::lonnet::logthis("<font color=blue>WARNING: ".  
      "Could not untie coursemap $fn for $uri.</font>");   
  }  
 # ---------------------------------------------------- Store away initial state  
  {  
     my $cfh;  
     if (open($cfh,">$fn.state")) {  
  print $cfh join("\n",@cond);  
     } else {  
  &Apache::lonnet::logthis("<font color=blue>WARNING: ".  
  "Could not write statemap $fn for $uri.</font>");   
     }  
  }  
  flock(LOCKFILE,LOCK_UN);  
  close(LOCKFILE);  
     } else {  
  # if we are here it is likely because we are already trying to    # if we are here it is likely because we are already trying to 
  # initialize the course in another child, busy wait trying to    # initialize the course in another child, busy wait trying to 
  # tie the hashes for the next 90 seconds, if we succeed forward    # tie the hashes for the next 90 seconds, if we succeed forward 
  # them on to navmaps, if we fail, throw up the Could not init    # them on to navmaps, if we fail, throw up the Could not init 
  # course screen   # course screen
    #
    # RF: I'm not seeing the case where the ties/unties can fail in a way
    #     that can be remedied by this.  Since we owned the lock seems
    #     Tie/untie failures are a result of something like a permissions problem instead?
    #
   
    #  In any vent, undo what we did manage to do above first:
  if ($lock) {   if ($lock) {
     # Got the lock but not the DB files      # Got the lock but not the DB files
     flock(LOCKFILE,LOCK_UN);      flock(LOCKFILE,LOCK_UN);
               $lock = 0;
  }   }
  untie(%hash);          if ($tiedhash) {
  untie(%parmhash);              unless($untiedhash) {
  &Apache::lonnet::logthis("<font color=blue>WARNING: ".          untie(%hash);
  "Could not tie coursemap $fn for $uri.</font>");               }
           }
           if ($tiedparmhash) {
               unless($untiedparmhash) {
                   untie(%parmhash);
               }
           }
    # Log our failure:
   
    &Apache::lonnet::logthis('<font color="blue">WARNING: '.
    "Could not tie coursemap $fn for $uri.</font>");
           $tiedhash = '';
           $tiedparmhash = '';
  my $i=0;   my $i=0;
   
    # Keep on retrying the lock for 90 sec until we succeed.
   
  while($i<90) {   while($i<90) {
     $i++;      $i++;
     sleep(1);      sleep(1);
     if (flock(LOCKFILE,LOCK_EX|LOCK_NB) &&      if (flock(LOCKFILE,LOCK_EX|LOCK_NB)) {
  (tie(%hash,'GDBM_File',"$fn.db",&GDBM_READER(),0640))) {  
  if (tie(%parmhash,'GDBM_File',$fn.'_parms.db',&GDBM_READER(),0640)) {   # Got the lock, tie the hashes...the assumption in this code is
     $retfurl='/adm/navmaps';   # that some other worker thread has created the db files quite recently
     &Apache::lonnet::appenv({"request.course.id"  => $short,   # so no load is needed:
      "request.course.fn"  => $fn,  
      "request.course.uri" => $uri});                  $lock = 1;
     untie(%hash);   if (tie(%hash,'GDBM_File',"$fn.db",&GDBM_READER(),0640)) {
     untie(%parmhash);                      $tiedhash = 1;
     last;      if (tie(%parmhash,'GDBM_File',$fn.'_parms.db',&GDBM_READER(),0640)) {
  }                          $tiedparmhash = 1;
     }                          if (-e "$fn.state") {
     untie(%hash);              $retfurl='/adm/navmaps';
     untie(%parmhash);  
       # BUG BUG: Side effect!
       # Should conditionalize on something so that we can use this
       # to load maps for courses that are not current?
       #
               &Apache::lonnet::appenv({"request.course.id"  => $short,
                  "request.course.fn"  => $fn,
                "request.course.uri" => $uri});
               $untiedhash = untie(%hash);
               $untiedparmhash = untie(%parmhash);
                               $gotstate = 1;
               last;
           }
                           $untiedparmhash = untie(%parmhash);
               }
               $untiedhash = untie(%hash);
                   }
               }
  }   }
  flock(LOCKFILE,LOCK_UN);          if ($lock) {
  close(LOCKFILE);              flock(LOCKFILE,LOCK_UN);
               $lock = 0;
               if ($tiedparmhash) {
                   unless ($untiedparmhash) {
                       &Apache::lonnet::logthis('<font color="blue">WARNING: '.
                           'Could not untie coursemap parmhash '.$fn.' for '.$uri.'.</font>');
                   }
               }
               if ($tiedparmhash) {
                   unless ($untiedhash) {
                       &Apache::lonnet::logthis('<font color="blue">WARNING: '.
                           'Could not untie coursemap hash '.$fn.' for '.$uri.'.</font>');
                   }
               }
           }
       }
       # I think this branch of code is all about what happens if we just did the stuff above, 
       # but found that the  state file did not exist...again if we'd just held the lock
       # would that have made this logic simpler..as generating all the files would be
       # an atomic operation with respect to the lock.
       #
       unless ($gotstate) {
           $lock = 0;
           &Apache::lonnet::logthis('<font color="blue">WARNING: '.
                        'Could not read statemap '.$fn.' for '.$uri.'.</font>');
           &unlink_tmpfiles($fn);
           if (flock(LOCKFILE,LOCK_EX|LOCK_NB)) {
               $lock=1;
           }
           undef %randompick;
           undef %hiddenurl;
           undef %encurl;
           $retfrid='';
    #
    # Once more through the routine of tying and loading and so on.
    #
           if ($lock) {
               if (tie(%hash,'GDBM_File',"$fn.db",&GDBM_WRCREAT(),0640)) {
                   if (tie(%parmhash,'GDBM_File',$fn.'_parms.db',&GDBM_WRCREAT(),0640)) {
                       $gotstate = &build_tmp_hashes($uri,$fn,$short,\%cenv); # TODO: User dependent?
                       unless ($gotstate) {
                           &Apache::lonnet::logthis('<font color="blue">WARNING: '.
                               'Failed to write statemap at second attempt '.$fn.' for '.$uri.'.</font>');
                       }
                       unless (untie(%parmhash)) {
                           &Apache::lonnet::logthis('<font color="blue">WARNING: '.
                               'Could not untie coursemap parmhash '.$fn.'.db for '.$uri.'.</font>');
                       }
                   } else {
                       &Apache::lonnet::logthis('<font color="blue">WARNING: '.
                           'Could not tie coursemap '.$fn.'__parms.db for '.$uri.'.</font>');
                   }
                   unless (untie(%hash)) {
                       &Apache::lonnet::logthis('<font color="blue">WARNING: '.
                           'Could not untie coursemap hash '.$fn.'.db for '.$uri.'.</font>');
                   }
               } else {
                  &Apache::lonnet::logthis('<font color="blue">WARNING: '.
                      'Could not tie coursemap '.$fn.'.db for '.$uri.'.</font>');
               }
               flock(LOCKFILE,LOCK_UN);
               $lock = 0;
           } else {
       # Failed to get the immediate lock.
   
               &Apache::lonnet::logthis('<font color="blue">WARNING: '.
               'Could not obtain lock to tie coursemap hash '.$fn.'.db for '.$uri.'.</font>');
           }
       }
       close(LOCKFILE);
       unless (($errtext eq '') || ($env{'request.course.uri'} =~ m{^/uploaded/})) {
           &Apache::lonmsg::author_res_msg($env{'request.course.uri'},
                                           $errtext); # TODO: User dependent?
     }      }
     &Apache::lonmsg::author_res_msg($env{'request.course.uri'},$errtext);  
 # ------------------------------------------------- Check for critical messages  # ------------------------------------------------- Check for critical messages
   
   #  Depends on user must parameterize this as well..or separate as this is:
   #  more part of determining what someone sees on entering a course?
   
     my @what=&Apache::lonnet::dump('critical',$env{'user.domain'},      my @what=&Apache::lonnet::dump('critical',$env{'user.domain'},
    $env{'user.name'});     $env{'user.name'});
     if ($what[0]) {      if ($what[0]) {
Line 778  sub readmap { Line 1023  sub readmap {
     return ($retfurl,$errtext);      return ($retfurl,$errtext);
 }  }
   
   #
   #  This sub is called when the course hash and the param hash have been tied and
   #  their lock file is held.
   #  Parameters:
   #     $uri      -  URI that identifies the course.
   #     $fn       -  The base path/filename of the files that make up the context
   #                  being built.
   #     $short    -  Short course name.
   #     $cenvref  -  Reference to the course environment hash returned by 
   #                  Apache::lonnet::coursedescription
   #
   #  Assumptions:
   #    The globals
   #    %hash, %paramhash are tied to their gdbm files and we hold the lock on them.
   #
   sub build_tmp_hashes {
       my ($uri,$fn,$short,$cenvref) = @_;
       
       unless(ref($cenvref) eq 'HASH') {
           return;
       }
       my %cenv = %{$cenvref};
       my $gotstate = 0;
       %hash=(); # empty the global course and  parameter hashes.
       %parmhash=();
       $errtext=''; # No error messages yet.
       $pc=0;
       &clear_mapalias_count();
       &processversionfile(%cenv);
       my $furi=&Apache::lonnet::clutter($uri);
       #
       #  the map staring points.
       #
       $hash{'src_0.0'}=&versiontrack($furi);
       $hash{'title_0.0'}=&Apache::lonnet::metadata($uri,'title');
       $hash{'ids_'.$furi}='0.0';
       $hash{'is_map_0.0'}=1;
       &loadmap($uri,'0.0');
       if (defined($hash{'map_start_'.$uri})) {
           &Apache::lonnet::appenv({"request.course.id"  => $short,
                                    "request.course.fn"  => $fn,
                                    "request.course.uri" => $uri});
           $env{'request.course.id'}=$short;
           &traceroute('0',$hash{'map_start_'.$uri},'&');
           &accinit($uri,$short,$fn);
           &hiddenurls();
       }
       $errtext .= &get_mapalias_errors();
   # ------------------------------------------------------- Put versions into src
       foreach my $key (keys(%hash)) {
           if ($key=~/^src_/) {
               $hash{$key}=&putinversion($hash{$key});
           } elsif ($key =~ /^(map_(?:start|finish|pc)_)(.*)/) {
               my ($type, $url) = ($1,$2);
               my $value = $hash{$key};
               $hash{$type.&putinversion($url)}=$value;
           }
       }
   # ---------------------------------------------------------------- Encrypt URLs
       foreach my $id (keys(%encurl)) {
   #           $hash{'src_'.$id}=&Apache::lonenc::encrypted($hash{'src_'.$id});
           $hash{'encrypted_'.$id}=1;
       }
   # ----------------------------------------------- Close hashes to finally store
   # --------------------------------- Routine must pass this point, no early outs
       $hash{'first_rid'}=$retfrid;
       my ($mapid,$resid)=split(/\./,$retfrid);
       $hash{'first_mapurl'}=$hash{'map_id_'.$mapid};
       my $symb=&Apache::lonnet::encode_symb($hash{'map_id_'.$mapid},$resid,$hash{'src_'.$retfrid});
       $retfurl=&add_get_param($hash{'src_'.$retfrid},{ 'symb' => $symb });
       if ($hash{'encrypted_'.$retfrid}) {
           $retfurl=&Apache::lonenc::encrypted($retfurl,(&Apache::lonnet::allowed('adv') ne 'F'));
       }
       $hash{'first_url'}=$retfurl;
   # ---------------------------------------------------- Store away initial state
       {
           my $cfh;
           if (open($cfh,">$fn.state")) {
               print $cfh join("\n",@cond);
               $gotstate = 1;
           } else {
               &Apache::lonnet::logthis("<font color=blue>WARNING: ".
                                        "Could not write statemap $fn for $uri.</font>");
           }
       }
       return $gotstate;
   }
   
   sub unlink_tmpfiles {
       my ($fn) = @_;
       my $file_dir = dirname($fn);
   
       if ($fn eq LONCAPA::tempdir()) {
           my @files = qw (.db _symb.db .state _parms.db);
           foreach my $file (@files) {
               if (-e $fn.$file) {
                   unless (unlink($fn.$file)) {
                       &Apache::lonnet::logthis("<font color=blue>WARNING: ".
                                    "Could not unlink ".$fn.$file."</font>");
                   }
               }
           }
       }
       return;
   }
   
 # ------------------------------------------------------- Evaluate state string  # ------------------------------------------------------- Evaluate state string
   
 sub evalstate {  sub evalstate {
Line 818  sub evalstate { Line 1169  sub evalstate {
     return $state;      return $state;
 }  }
   
   #  This block seems to have code to manage/detect doubly defined
   #  aliases in maps.
   
 {  {
     my %mapalias_cache;      my %mapalias_cache;
     sub count_mapalias {      sub count_mapalias {
Line 877  of course for user. Line 1231  of course for user.
 This is part of the LearningOnline Network with CAPA project  This is part of the LearningOnline Network with CAPA project
 described at http://www.lon-capa.org.  described at http://www.lon-capa.org.
   
 =head1 HANDLER SUBROUTINE  =head1 SUBROUTINES
   
 There is no handler subroutine.  
   
 =head1 OTHER SUBROUTINES  
   
 =over 4  =over
   
 =item *  =item loadmap()
   
 loadmap() : Loads map from disk  Loads map from disk
   
 =item *  =item simplify()
   
 simplify() : Simplify expression  Simplify expression
   
 =item *  =item traceroute()
   
 traceroute() : Build condition hash  Build condition hash
   
 =item *  =item accinit()
   
 accinit() : Cascading conditions, quick access, parameters  Cascading conditions, quick access, parameters
   
 =item *  =item readmap()
   
 readmap() : Read map and all submaps  Read map and all submaps
   
 =item *  =item evalstate()
   
 evalstate() : Evaluate state string  Evaluate state string
   
 =back  =back
   

Removed from v.1.128.2.1  
changed lines
  Added in v.1.138


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