Diff for /loncom/xml/lonxml.pm between versions 1.87 and 1.477

version 1.87, 2001/06/08 17:52:03 version 1.477, 2008/05/02 22:00:12
Line 1 Line 1
 # The LearningOnline Network with CAPA  # The LearningOnline Network with CAPA
 # XML Parser Module   # XML Parser Module 
 #  #
 # last modified 06/26/00 by Alexander Sakharuk  # $Id$
 # 11/6 Gerd Kortemeyer  #
 # 6/1/1 Gerd Kortemeyer  # Copyright Michigan State University Board of Trustees
 # 2/21,3/13 Guy  #
 # 3/29,5/4 Gerd Kortemeyer  # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
 # 5/10 Scott Harrison  #
 # 5/26 Gerd Kortemeyer  # LON-CAPA is free software; you can redistribute it and/or modify
 # 5/27 H. K. Ng  # it under the terms of the GNU General Public License as published by
 # 6/2,6/3,6/8 Gerd Kortemeyer  # the Free Software Foundation; either version 2 of the License, or
   # (at your option) any later version.
   #
   # LON-CAPA is distributed in the hope that it will be useful,
   # but WITHOUT ANY WARRANTY; without even the implied warranty of
   # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   # GNU General Public License for more details.
   #
   # You should have received a copy of the GNU General Public License
   # along with LON-CAPA; if not, write to the Free Software
   # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   #
   # /home/httpd/html/adm/gpl.txt
   #
   # http://www.lon-capa.org/
   #
   # Copyright for TtHfunc and TtMfunc by Ian Hutchinson. 
   # TtHfunc and TtMfunc (the "Code") may be compiled and linked into 
   # binary executable programs or libraries distributed by the 
   # Michigan State University (the "Licensee"), but any binaries so 
   # distributed are hereby licensed only for use in the context
   # of a program or computational system for which the Licensee is the 
   # primary author or distributor, and which performs substantial 
   # additional tasks beyond the translation of (La)TeX into HTML.
   # The C source of the Code may not be distributed by the Licensee
   # to any other parties under any circumstances.
   #
   
   
 package Apache::lonxml;   package Apache::lonxml; 
 use vars   use vars 
 qw(@pwd @outputstack $redirection $import @extlinks $metamode $evaluate %insertlist @namespace);  qw(@pwd @outputstack $redirection $import @extlinks $metamode $evaluate %insertlist @namespace $errorcount $warningcount);
 use strict;  use strict;
 use HTML::TokeParser;  use LONCAPA;
 use Safe;  use HTML::LCParser();
 use Safe::Hole;  use HTML::TreeBuilder();
 use Math::Cephes qw(:trigs :hypers :bessels erf erfc);  use HTML::Entities();
 use Opcode;  use Safe();
   use Safe::Hole();
   use Math::Cephes();
   use Math::Random();
   use Opcode();
   use POSIX qw(strftime);
   use Time::HiRes qw( gettimeofday tv_interval );
   use Symbol();
   
 sub register {  sub register {
   my $space;    my ($space,@taglist) = @_;
   my @taglist;    foreach my $temptag (@taglist) {
   my $temptag;      push(@{ $Apache::lonxml::alltags{$temptag} },$space);
   ($space,@taglist) = @_;    }
   foreach $temptag (@taglist) {  }
     $Apache::lonxml::alltags{$temptag}=$space;  
   sub deregister {
     my ($space,@taglist) = @_;
     foreach my $temptag (@taglist) {
       my $tempspace = $Apache::lonxml::alltags{$temptag}[-1];
       if ($tempspace eq $space) {
         pop(@{ $Apache::lonxml::alltags{$temptag} });
       }
   }    }
     #&printalltags();
 }  }
   
 use Apache::Constants qw(:common);  use Apache::Constants qw(:common);
 use Apache::lontexconvert;  use Apache::lontexconvert();
 use Apache::style;  use Apache::style();
 use Apache::run;  use Apache::run();
 use Apache::londefdef;  use Apache::londefdef();
 use Apache::scripttag;  use Apache::scripttag();
 use Apache::edit;  use Apache::languagetags();
   use Apache::edit();
   use Apache::inputtags();
   use Apache::outputtags();
 use Apache::lonnet;  use Apache::lonnet;
 use Apache::File;  use Apache::File();
   use Apache::loncommon();
   use Apache::lonfeedback();
   use Apache::lonmsg();
   use Apache::loncacc();
   use Apache::lonmaxima();
   use Apache::lonlocal;
   
   #====================================   Main subroutine: xmlparse  
   
 #==================================================   Main subroutine: xmlparse    
 #debugging control, to turn on debugging modify the correct handler  #debugging control, to turn on debugging modify the correct handler
   
 $Apache::lonxml::debug=0;  $Apache::lonxml::debug=0;
   
   # keeps count of the number of warnings and errors generated in a parse
   $warningcount=0;
   $errorcount=0;
   
 #path to the directory containing the file currently being processed  #path to the directory containing the file currently being processed
 @pwd=();  @pwd=();
   
Line 67  $evaluate = 1; Line 124  $evaluate = 1;
 # data structure for eidt mode, determines what tags can go into what other tags  # data structure for eidt mode, determines what tags can go into what other tags
 %insertlist=();  %insertlist=();
   
 #stores the list of active tag namespaces  # stores the list of active tag namespaces
 @namespace=();  @namespace=();
   
 sub xmlbegin {  # stores all Scrit Vars displays for later showing
   my $output='';  my @script_var_displays=();
   if ($ENV{'browser.mathml'}) {  
       $output='<?xml version="1.0"?>'  
             .'<?xml-stylesheet type="text/css" href="/adm/MathML/mathml.css"?>'  
             .'<!DOCTYPE html SYSTEM "/adm/MathML/mathml.dtd" '  
             .'[<!ENTITY mathns "http://www.w3.org/1998/Math/MathML">]>'  
             .'<html xmlns:math="http://www.w3.org/1998/Math/MathML" '   
  .'xmlns="http://www.w3.org/TR/REC-html40">';  
   } else {  
       $output='<html>';  
   }  
   return $output;  
 }  
   
 sub xmlend {  # a pointer the the Apache request object
     return '</html>';  $Apache::lonxml::request='';
 }  
   
 sub fontsettings() {  # a problem number counter, and check on ether it is used
     my $headerstring='';  $Apache::lonxml::counter=1;
     if (($ENV{'browser.os'} eq 'mac') && (!$ENV{'browser.mathml'})) {   $Apache::lonxml::counter_changed=0;
          $headerstring.=  
              '<meta Content-Type="text/html; charset=x-mac-roman">';  
     }  
     return $headerstring;  
 }  
   
 sub registerurl {  
     if ($ENV{'REQUEST_URI'}!~/^\/(res\/)*adm\//) {  
         my $hwkadd='';  
         if ($ENV{'REQUEST_URI'}=~/\.(problem|exam|quiz|assess|survey|form)$/) {  
     if (&Apache::lonnet::allowed('vgr',$ENV{'request.course.id'})) {  
  $hwkadd.=(<<ENDSUBM);  
                      menu.switchbutton  
            (7,1,'subm.gif','view sub','missions',  
                 'gopost("/adm/grades","submission")');  
 ENDSUBM  
             }  
     if (&Apache::lonnet::allowed('mgr',$ENV{'request.course.id'})) {  
  $hwkadd.=(<<ENDGRDS);  
                      menu.switchbutton  
            (7,2,'pgrd.gif','problem','grades',  
                 'gopost("/adm/grades","")');  
 ENDGRDS  
             }  
     if (&Apache::lonnet::allowed('opa',$ENV{'request.course.id'})) {  
  $hwkadd.=(<<ENDPARM);  
                      menu.switchbutton  
            (7,3,'pparm.gif','problem','parms',  
                 'gopost("/adm/parmset","set")');  
 ENDPARM  
             }  
  }  
  return (<<ENDREGTHIS);  
        
 <script language="JavaScript">  
 // BEGIN LON-CAPA Internal  
   
     function LONCAPAreg() {  
   menu=window.open("","LONCAPAmenu");  
           menu.clearTimeout(menu.menucltim);  
   menu.currentURL=window.location.pathname;  
           menu.currentStale=0;  
           menu.clearbut(3,1);  
           menu.switchbutton  
        (8,1,'eval.gif','evaluate','this','gopost("/adm/evaluate",currentURL)');  
           menu.switchbutton  
     (8,2,'fdbk.gif','feedback','on this','gopost("/adm/feedback",currentURL)');  
           menu.switchbutton  
      (8,3,'prt.gif','prepare','printout','gopost("/adm/printout",currentURL)');  
           menu.switchbutton  
        (2,1,'back.gif','backward','','gopost("/adm/flip","back:"+currentURL)');  
           menu.switchbutton  
      (2,3,'forw.gif','forward','','gopost("/adm/flip","forward:"+currentURL)');  
           $hwkadd  
     }  
   
     function LONCAPAstale() {  
   menu=window.open("","LONCAPAmenu");  
           menu.currentStale=1;  
           menu.switchbutton  
             (3,1,'reload.gif','return','location','go(currentURL)');  
           menu.menucltim=menu.setTimeout(  
  'clearbut(2,1);clearbut(2,3);clearbut(8,1);clearbut(8,2);clearbut(8,3);',  
   2000);  
   
       }  # Part counter hash.   In analysis mode, the
   # problems can use this to record which parts increment the counter
   # by how much.  The counter subs will maintain this hash via
   # their optional part parameters.  Note that the assumption is that
   # analysis is done in one request and therefore it is not necessary to
   # save this information request-to-request.
   
 // END LON-CAPA Internal  
 </script>  
 ENDREGTHIS  
   
     } else {  %Apache::lonxml::counters_per_part = ();
         return (<<ENDDONOTREGTHIS);  
   
 <script language="JavaScript">  #internal check on whether to look at style defs
 // BEGIN LON-CAPA Internal  $Apache::lonxml::usestyle=1;
   
     function LONCAPAreg() {  #locations used to store the parameter string for style substitutions
   menu=window.open("","LONCAPAmenu");  $Apache::lonxml::style_values='';
           menu.currentStale=1;  $Apache::lonxml::style_end_values='';
           menu.clearbut(2,1);  
           menu.clearbut(2,3);  #array of ssi calls that need to occur after we are done parsing
           menu.clearbut(8,1);  @Apache::lonxml::ssi_info=();
           menu.clearbut(8,2);  
           menu.clearbut(8,3);  #should we do the postag variable interpolation
           if (menu.currentURL) {  $Apache::lonxml::post_evaluate=1;
              menu.switchbutton  
               (3,1,'reload.gif','return','location','go(currentURL)');  #a header message to emit in the case of any generated warning or errors
    } else {  $Apache::lonxml::warnings_error_header='';
       menu.clearbut(3,1);  
           }  
     }  
   
     function LONCAPAstale() {  #  Control whether or not LaTeX symbols should be substituted for their
   #  \ style equivalents...this may be turned off e.g. in an verbatim
   #  environment.
   
   $Apache::lonxml::substitute_LaTeX_symbols = 1; # Starts out on.
   
   sub enable_LaTeX_substitutions {
       $Apache::lonxml::substitute_LaTeX_symbols = 1;
   }
   sub disable_LaTeX_substitutions {
       $Apache::lonxml::substitute_LaTeX_symbols = 0;
   }
   
   sub xmlend {
       my ($target,$parser)=@_;
       my $mode='xml';
       my $status='OPEN';
       if ($Apache::lonhomework::parsing_a_problem ||
    $Apache::lonhomework::parsing_a_task ) {
    $mode='problem';
    $status=$Apache::inputtags::status[-1]; 
     }      }
       my $discussion;
       &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},
      ['LONCAPA_INTERNAL_no_discussion']);
       if (! exists($env{'form.LONCAPA_INTERNAL_no_discussion'}) ||
           $env{'form.LONCAPA_INTERNAL_no_discussion'} ne 'true') {
           $discussion=&Apache::lonfeedback::list_discussion($mode,$status);
       }
       if ($target eq 'tex') {
    $discussion.='<tex>\keephidden{ENDOFPROBLEM}\vskip 0.5mm\noindent\makebox[\textwidth/$number_of_columns][b]{\hrulefill}\end{document}</tex>';
    &Apache::lonxml::newparser($parser,\$discussion,'');
    return '';
       }
   
       return $discussion;
   }
   
 // END LON-CAPA Internal  sub tokeninputfield {
       my $defhost=$Apache::lonnet::perlvar{'lonHostID'};
       $defhost=~tr/a-z/A-Z/;
       return (<<ENDINPUTFIELD)
   <script type="text/javascript">
       function updatetoken() {
    var comp=new Array;
           var barcode=unescape(document.tokeninput.barcode.value);
           comp=barcode.split('*');
           if (typeof(comp[0])!="undefined") {
       document.tokeninput.codeone.value=comp[0];
    }
           if (typeof(comp[1])!="undefined") {
       document.tokeninput.codetwo.value=comp[1];
    }
           if (typeof(comp[2])!="undefined") {
               comp[2]=comp[2].toUpperCase();
       document.tokeninput.codethree.value=comp[2];
    }
           document.tokeninput.barcode.value='';
       }  
 </script>  </script>
 ENDDONOTREGTHIS  <form method="post" name="tokeninput">
   <table border="2" bgcolor="#FFFFBB">
   <tr><th>DocID Checkin</th></tr>
   <tr><td>
   <table>
   <tr>
   <td>Scan in Barcode</td>
   <td><input type="text" size="22" name="barcode" 
   onChange="updatetoken()"/></td>
   </tr>
   <tr><td><i>or</i> Type in DocID</td>
   <td>
   <input type="text" size="5" name="codeone" />
   <b><font size="+2">*</font></b>
   <input type="text" size="5" name="codetwo" />
   <b><font size="+2">*</font></b>
   <input type="text" size="10" name="codethree" value="$defhost" 
   onChange="this.value=this.value.toUpperCase()" />
   </td></tr>
   </table>
   </td></tr>
   <tr><td><input type="submit" value="Check in DocID" /></td></tr>
   </table>
   </form>
   ENDINPUTFIELD
   }
   
   sub maketoken {
       my ($symb,$tuname,$tudom,$tcrsid)=@_;
       unless ($symb) {
    $symb=&Apache::lonnet::symbread();
       }
       unless ($tuname) {
    $tuname=$env{'user.name'};
           $tudom=$env{'user.domain'};
           $tcrsid=$env{'request.course.id'};
     }      }
 }  
   
 sub loadevents() {      return &Apache::lonnet::checkout($symb,$tuname,$tudom,$tcrsid);
     return 'LONCAPAreg();';  
 }  }
   
 sub unloadevents() {  sub printtokenheader {
     return 'LONCAPAstale();';      my ($target,$token,$tsymb,$tcrsid,$tudom,$tuname)=@_;
       unless ($token) { return ''; }
   
       my ($symb,$courseid,$domain,$name) = &Apache::lonnet::whichuser();
       unless ($tsymb) {
    $tsymb=$symb;
       }
       unless ($tuname) {
    $tuname=$name;
           $tudom=$domain;
           $tcrsid=$courseid;
       }
   
       my $plainname=&Apache::loncommon::plainname($tuname,$tudom);
   
       if ($target eq 'web') {
           my %idhash=&Apache::lonnet::idrget($tudom,($tuname));
    return 
    '<img align="right" src="/cgi-bin/barcode.png?encode='.$token.'" />'.
                  &mt('Checked out for').' '.$plainname.
                  '<br />'.&mt('User').': '.$tuname.' at '.$tudom.
          '<br />'.&mt('ID').': '.$idhash{$tuname}.
          '<br />'.&mt('CourseID').': '.$tcrsid.
          '<br />'.&mt('Course').': '.$env{'course.'.$tcrsid.'.description'}.
                  '<br />'.&mt('DocID').': '.$token.
                  '<br />'.&mt('Time').': '.&Apache::lonlocal::locallocaltime().'<hr />';
       } else {
           return $token;
       }
 }  }
   
 sub printalltags {  sub printalltags {
   my $temp;    my $temp;
   foreach $temp (sort keys %Apache::lonxml::alltags) {    foreach $temp (sort keys %Apache::lonxml::alltags) {
     &Apache::lonxml::debug("$temp -- $Apache::lonxml::alltags{$temp}");      &Apache::lonxml::debug("$temp -- ".
     join(',',@{ $Apache::lonxml::alltags{$temp} }));
   }    }
 }  }
   
 sub xmlparse {  sub xmlparse {
    my ($request,$target,$content_file_string,$safeinit,%style_for_target) = @_;
   
    &setup_globals($request,$target);
    &Apache::inputtags::initialize_inputtags();
    &Apache::bridgetask::initialize_bridgetask();
    &Apache::outputtags::initialize_outputtags();
    &Apache::edit::initialize_edit();
    &Apache::londefdef::initialize_londefdef();
   
   #
   # do we have a course style file?
   #
   
  my ($target,$content_file_string,$safeinit,%style_for_target) = @_;   if ($env{'request.course.id'} && $env{'request.state'} ne 'construct') {
  if ($target eq 'meta') {       my $bodytext=
    $Apache::lonxml::redirection = 0;   $env{'course.'.$env{'request.course.id'}.'.default_xml_style'};
    $Apache::lonxml::metamode = 1;       if ($bodytext) {
    $Apache::lonxml::evaluate = 1;   foreach my $file (split(',',$bodytext)) {
    $Apache::lonxml::import = 0;       my $location=&Apache::lonnet::filelocation('',$file);
  } elsif ($target eq 'grade') {       my $styletext=&Apache::lonnet::getfile($location);
    &startredirection;       if ($styletext ne '-1') {
    $Apache::lonxml::metamode = 0;   %style_for_target = (%style_for_target,
    $Apache::lonxml::evaluate = 1;        &Apache::style::styleparser($target,$styletext));
    $Apache::lonxml::import = 1;       }
  } elsif ($target eq 'modified') {   }
    $Apache::lonxml::redirection = 0;       }
    $Apache::lonxml::metamode = 0;   } elsif ($env{'construct.style'}
    $Apache::lonxml::evaluate = 0;    && ($env{'request.state'} eq 'construct')) {
    $Apache::lonxml::import = 0;       my $location=&Apache::lonnet::filelocation('',$env{'construct.style'});
  } else {       my $styletext=&Apache::lonnet::getfile($location);
    $Apache::lonxml::redirection = 0;       if ($styletext ne '-1') {
    $Apache::lonxml::metamode = 0;   %style_for_target = (%style_for_target,
    $Apache::lonxml::evaluate = 1;        &Apache::style::styleparser($target,$styletext));
    $Apache::lonxml::import = 1;       }
  }   }
  #&printalltags();  #&printalltags();
  my @pars = ();   my @pars = ();
  @Apache::lonxml::pwd=();   my $pwd=$env{'request.filename'};
  my $pwd=$ENV{'request.filename'};  
  $pwd =~ s:/[^/]*$::;   $pwd =~ s:/[^/]*$::;
  &newparser(\@pars,\$content_file_string,$pwd);   &newparser(\@pars,\$content_file_string,$pwd);
  my $currentstring = '';  
  my $finaloutput = '';   
  my $newarg = '';  
  my $result;  
   
  my $safeeval = new Safe;   my $safeeval = new Safe;
  my $safehole = new Safe::Hole;   my $safehole = new Safe::Hole;
Line 252  sub xmlparse { Line 353  sub xmlparse {
   
  ($target, my @tenta) = split('&&',$target);   ($target, my @tenta) = split('&&',$target);
   
  my @stack = ();    my @stack = ();
  my @parstack = ();   my @parstack = ();
  &initdepth;   &initdepth();
  my $token;   &init_alarm();
  while ( $#pars > -1 ) {   my $finaloutput = &inner_xmlparse($target,\@stack,\@parstack,\@pars,
    while ($token = $pars[$#pars]->get_token) {     $safeeval,\%style_for_target,1);
      if (($token->[0] eq 'T') || ($token->[0] eq 'C') || ($token->[0] eq 'D') ) {  
        if ($metamode<1) { $result=$token->[1]; }   if (@stack) {
      } elsif ($token->[0] eq 'PI') {       &warning("At end of file some tags were still left unclosed, ".
        if ($metamode<1) { $result=$token->[2]; }        '<tt>&lt;'.join('&gt;</tt>, <tt>&lt;',reverse(@stack)).
      } elsif ($token->[0] eq 'S') {        '&gt;</tt>');
        # add tag to stack      
        push (@stack,$token->[1]);  
        # add parameters list to another stack  
        push (@parstack,&parstring($token));  
        &increasedepth($token);         
        if (exists $style_for_target{$token->[1]}) {  
  if ($Apache::lonxml::redirection) {  
    $Apache::lonxml::outputstack['-1'] .=    
      &recurse($style_for_target{$token->[1]},$target,$safeeval,  
       \%style_for_target,@parstack);  
  } else {  
    $finaloutput .= &recurse($style_for_target{$token->[1]},$target,  
     $safeeval,\%style_for_target,@parstack);  
  }  
        } else {  
  $result = &callsub("start_$token->[1]", $target, $token, \@stack,  
     \@parstack, \@pars, $safeeval, \%style_for_target);  
        }                
      } elsif ($token->[0] eq 'E')  {  
        #clear out any tags that didn't end  
        while ($token->[1] ne $stack[$#stack] && ($#stack > -1)) {  
  &Apache::lonxml::warning("Unbalanced tags in resource $stack['-1']");  
  pop @stack;pop @parstack;&decreasedepth($token);  
        }  
          
        if (exists $style_for_target{'/'."$token->[1]"}) {  
  if ($Apache::lonxml::redirection) {  
    $Apache::lonxml::outputstack['-1'] .=    
      &recurse($style_for_target{'/'."$token->[1]"},  
       $target,$safeeval,\%style_for_target,@parstack);  
  } else {  
    $finaloutput .= &recurse($style_for_target{'/'."$token->[1]"},  
     $target,$safeeval,\%style_for_target,  
     @parstack);  
  }  
   
        } else {  
  $result = &callsub("end_$token->[1]", $target, $token, \@stack,   
     \@parstack, \@pars,$safeeval, \%style_for_target);  
        }  
      } else {  
        &Apache::lonxml::error("Unknown token event :$token->[0]:$token->[1]:");  
      }  
      #evaluate variable refs in result  
      if ($result ne "") {  
        if ( $#parstack > -1 ) {  
  if ($Apache::lonxml::redirection) {  
    $Apache::lonxml::outputstack['-1'] .=   
      &Apache::run::evaluate($result,$safeeval,$parstack[$#parstack]);  
  } else {  
    $finaloutput .= &Apache::run::evaluate($result,$safeeval,  
   $parstack[$#parstack]);  
  }  
        } else {  
  $finaloutput .= &Apache::run::evaluate($result,$safeeval,'');  
        }  
        $result = '';  
      }   
      if ($token->[0] eq 'E') {   
        pop @stack;pop @parstack;&decreasedepth($token);  
      }  
    }  
    pop @pars;  
    pop @Apache::lonxml::pwd;  
  }   }
    if ($env{'request.uri'}) {
       &writeallows($env{'request.uri'});
    }
    &do_registered_ssi();
    if ($Apache::lonxml::counter_changed) { &store_counter() }
   
 # if ($target eq 'meta') {   &clean_safespace($safeeval);
 #   $finaloutput.=&endredirection;  
 # }  
   
   if (($ENV{'QUERY_STRING'}) && ($target eq 'web')) {  
       $finaloutput=&afterburn($finaloutput);  
   }  
   
    if (@script_var_displays) {
        $finaloutput .= join('',@script_var_displays);
        undef(@script_var_displays);
    }
    &init_state();
    if ($env{'form.return_only_error_and_warning_counts'}) {
        if ($env{'request.filename'}=~/\.(html|htm|xml)$/i) { 
           my $error=&verify_html($content_file_string);
           if ($error) { $errorcount++; }
        }
        return "$errorcount:$warningcount";
    }
  return $finaloutput;   return $finaloutput;
 }  }
   
   sub latex_special_symbols {
       my ($string,$where)=@_;
       #
       #  If e.g. in verbatim mode, then don't substitute.
       #  but return original string.
       #
       if (!($Apache::lonxml::substitute_LaTeX_symbols)) {
    return $string;
       }
       if ($where eq 'header') {
    $string =~ s/\\/\$\\backslash\$/g; # \  -> $\backslash$ per LaTex line by line pg  10.
    $string =~ s/(\$|%|\{|\})/\\$1/g;
    $string=&Apache::lonprintout::character_chart($string);
    # any & or # leftover should be safe to just escape
           $string=~s/([^\\])\&/$1\\\&/g;
           $string=~s/([^\\])\#/$1\\\#/g;
    $string =~ s/_/\\_/g;              # _ -> \_
    $string =~ s/\^/\\\^{}/g;          # ^ -> \^{} 
       } else {
    $string=~s/\\/\\ensuremath{\\backslash}/g;
    $string=~s/\\\%|\%/\\\%/g;
    $string=~s/\\{|{/\\{/g;
    $string=~s/\\}|}/\\}/g;
    $string=~s/\\ensuremath\\{\\backslash\\}/\\ensuremath{\\backslash}/g;
    $string=~s/\\\$|\$/\\\$/g;
    $string=~s/\\\_|\_/\\\_/g;
           $string=~s/([^\\]|^)(\~|\^)/$1\\$2\\strut /g;
    $string=~s/(>|<)/\\ensuremath\{$1\}/g; #more or less
    $string=&Apache::lonprintout::character_chart($string);
    # any & or # leftover should be safe to just escape
    $string=~s/\\\&|\&/\\\&/g;
    $string=~s/\\\#|\#/\\\#/g;
           $string=~s/\|/\$\\mid\$/g;
   #single { or } How to escape?
       }
       return $string;
   }
   
 sub recurse {  sub inner_xmlparse {
       my ($target,$stack,$parstack,$pars,$safeeval,$style_for_target,$start)=@_;
   my @innerstack = ();     my $finaloutput = '';
   my @innerparstack = ();    my $result;
   my ($newarg,$target,$safeeval,$style_for_target,@parstack) = @_;    my $token;
   my @pat = ();    my $dontpop=0;
   &newparser(\@pat,\$newarg);    my $startredirection = $Apache::lonxml::redirection;
   my $tokenpat;    while ( $#$pars > -1 ) {
   my $partstring = '';      while ($token = $$pars['-1']->get_token) {
   my $output='';        if (($token->[0] eq 'T') || ($token->[0] eq 'C') ) {
   my $decls='';   if ($metamode<1) {
   while ( $#pat > -1 ) {      my $text=$token->[1];
     while  ($tokenpat = $pat[$#pat]->get_token) {      if ($token->[0] eq 'C' && $target eq 'tex') {
       if (($tokenpat->[0] eq 'T') || ($tokenpat->[0] eq 'C') || ($tokenpat->[0] eq 'D') ) {   $text = '';
  if ($metamode<1) { $partstring=$tokenpat->[1]; }  # $text = '%'.$text."\n";
       } elsif ($tokenpat->[0] eq 'PI') {      }
  if ($metamode<1) { $partstring=$tokenpat->[2]; }      $result.=$text;
       } elsif ($tokenpat->[0] eq 'S') {   }
  push (@innerstack,$tokenpat->[1]);        } elsif (($token->[0] eq 'D')) {
  push (@innerparstack,&parstring($tokenpat));   if ($metamode<1 && $target eq 'web') {
  &increasedepth($tokenpat);      my $text=$token->[1];
  $partstring = &callsub("start_$tokenpat->[1]", $target, $tokenpat,      $result.=$text;
        \@innerstack, \@innerparstack, \@pat,   }
        $safeeval, $style_for_target);        } elsif ($token->[0] eq 'PI') {
       } elsif ($tokenpat->[0] eq 'E') {   if ($metamode<1 && $target eq 'web') {
  #clear out any tags that didn't end    $result=$token->[2];
  while ($tokenpat->[1] ne $innerstack[$#innerstack]    }
        && ($#innerstack > -1)) {        } elsif ($token->[0] eq 'S') {
   &Apache::lonxml::warning("Unbalanced tags in resource $innerstack['-1']");   # add tag to stack
   pop @innerstack;pop @innerparstack;&decreasedepth($tokenpat);   push (@$stack,$token->[1]);
  }   # add parameters list to another stack
  $partstring = &callsub("end_$tokenpat->[1]", $target, $tokenpat,   push (@$parstack,&parstring($token));
        \@innerstack, \@innerparstack, \@pat,   &increasedepth($token);
        $safeeval, $style_for_target);   if ($Apache::lonxml::usestyle &&
       exists($$style_for_target{$token->[1]})) {
       $Apache::lonxml::usestyle=0;
       my $string=$$style_for_target{$token->[1]}.
         '<LONCAPA_INTERNAL_TURN_STYLE_ON />';
       &Apache::lonxml::newparser($pars,\$string);
       $Apache::lonxml::style_values=$$parstack[-1];
       $Apache::lonxml::style_end_values=$$parstack[-1];
    } else {
     $result = &callsub("start_$token->[1]", $target, $token, $stack,
        $parstack, $pars, $safeeval, $style_for_target);
    }
         } elsif ($token->[0] eq 'E') {
    if ($Apache::lonxml::usestyle &&
       exists($$style_for_target{'/'."$token->[1]"})) {
       $Apache::lonxml::usestyle=0;
       my $string=$$style_for_target{'/'.$token->[1]}.
         '<LONCAPA_INTERNAL_TURN_STYLE_ON end="'.$token->[1].'" />';
       &Apache::lonxml::newparser($pars,\$string);
       $Apache::lonxml::style_values=$Apache::lonxml::style_end_values;
       $Apache::lonxml::style_end_values='';
       $dontpop=1;
    } else {
       #clear out any tags that didn't end
       while ($token->[1] ne $$stack['-1'] && ($#$stack > -1)) {
    my $lasttag=$$stack[-1];
    if ($token->[1] =~ /^\Q$lasttag\E$/i) {
       &Apache::lonxml::warning('Using tag &lt;/'.$token->[1].'&gt; on line '.$token->[3].' as end tag to &lt;'.$$stack[-1].'&gt;');
       last;
    } else {
       &Apache::lonxml::warning('Found tag &lt;/'.$token->[1].'&gt; on line '.$token->[3].' when looking for &lt;/'.$$stack[-1].'&gt; in file');
       &end_tag($stack,$parstack,$token);
    }
       }
       $result = &callsub("end_$token->[1]", $target, $token, $stack,
          $parstack, $pars,$safeeval, $style_for_target);
    }
       } else {        } else {
  &Apache::lonxml::error("Unknown token event :$tokenpat->[0]:$tokenpat->[1]:");   &Apache::lonxml::error("Unknown token event :$token->[0]:$token->[1]:");
       }        }
       #pass both the variable to the style tag, and the tag we         #evaluate variable refs in result
       #are processing inside the <definedtag>        if ($Apache::lonxml::post_evaluate &&$result ne "") {
       if ( $partstring ne "" ) {    my $extras;
  if ( $#parstack > -1 ) {     if (!$Apache::lonxml::usestyle) {
   if ( $#innerparstack > -1 ) {         $extras=$Apache::lonxml::style_values;
     $decls= $parstack[$#parstack].$innerparstack[$#innerparstack];  
   } else {  
     $decls= $parstack[$#parstack];  
   }    }
    if ( $#$parstack > -1 ) {
     $result=&Apache::run::evaluate($result,$safeeval,$extras.$$parstack[-1]);
  } else {   } else {
   if ( $#innerparstack > -1 ) {     $result= &Apache::run::evaluate($result,$safeeval,$extras);
     $decls=$innerparstack[$#innerparstack];  
   } else {  
     $decls='';  
   }  
  }   }
  $output .= &Apache::run::evaluate($partstring,$safeeval,$decls);  
  $partstring = '';  
       }        }
       if ($tokenpat->[0] eq 'E') { pop @innerstack;pop @innerparstack;        $Apache::lonxml::post_evaluate=1;
  &decreasedepth($tokenpat);}  
         if (($token->[0] eq 'T') || ($token->[0] eq 'C') || ($token->[0] eq 'D') ) {
     #Style file definitions should be correct
     if ($target eq 'tex' && ($Apache::lonxml::usestyle)) {
         $result=&latex_special_symbols($result);
     }
         }
   
         if ($Apache::lonxml::redirection) {
    $Apache::lonxml::outputstack['-1'] .= $result;
         } else {
    $finaloutput.=$result;
         }
         $result = '';
   
         if ($token->[0] eq 'E' && !$dontpop) {
    &end_tag($stack,$parstack,$token);
         }
         $dontpop=0;
       }
       if ($#$pars > -1) {
    pop @$pars;
    pop @Apache::lonxml::pwd;
     }      }
     pop @pat;  
     pop @Apache::lonxml::pwd;  
   }    }
   return $output;  
     # if ($target eq 'meta') {
     #   $finaloutput.=&endredirection;
     # }
   
     if ( $start && $target eq 'grade') { &endredirection(); }
     if ( $Apache::lonxml::redirection > $startredirection) {
         while ($Apache::lonxml::redirection > $startredirection) {
     $finaloutput .= &endredirection();
         }
     }
     if (($ENV{'QUERY_STRING'}) && ($target eq 'web')) {
       $finaloutput=&afterburn($finaloutput);
     }    
     return $finaloutput;
 }  }
   
   ## 
   ## Looks to see if there is a subroutine defined for this tag.  If so, call it,
   ## otherwise do not call it as we do not know what it is.
   ##
 sub callsub {  sub callsub {
   my ($sub,$target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;    my ($sub,$target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
   my $currentstring='';    my $currentstring='';
Line 413  sub callsub { Line 565  sub callsub {
   {    {
     my $sub1;      my $sub1;
     no strict 'refs';      no strict 'refs';
     if ($target eq 'edit' && $token->[0] eq 'S') {  
       $currentstring = &Apache::edit::tag_start($target,$token,$tagstack,  
  $parstack,$parser,  
  $safeeval,$style);  
     }  
     my $tag=$token->[1];      my $tag=$token->[1];
     my $space=$Apache::lonxml::alltags{$tag};  # get utterly rid of extended html tags
       if ($tag=~/^x\-/i) { return ''; }
       my $space=$Apache::lonxml::alltags{$tag}[-1];
     if (!$space) {      if (!$space) {
  $tag=~tr/A-Z/a-z/;        $tag=~tr/A-Z/a-z/;
  $sub=~tr/A-Z/a-z/;   $sub=~tr/A-Z/a-z/;
  $space=$Apache::lonxml::alltags{$tag}   $space=$Apache::lonxml::alltags{$tag}[-1]
     }      }
     if ($space) {  
       #&Apache::lonxml::debug("Calling sub $sub in $space $metamode<br />\n");      my $deleted=0;
       $sub1="$space\:\:$sub";      if (($token->[0] eq 'S') && ($target eq 'modified')) {
       $Apache::lonxml::curdepth=join('_',@Apache::lonxml::depthcounter);        $deleted=&Apache::edit::handle_delete($space,$target,$token,$tagstack,
       ($currentstring,$nodefault) = &$sub1($target,$token,$tagstack,       $parstack,$parser,$safeeval,
    $parstack,$parser,$safeeval,       $style);
    $style);  
     } else {  
       #&Apache::lonxml::debug("NOT Calling sub $sub in $space $metamode<br />\n");  
       if ($metamode <1) {  
  if (defined($token->[4]) && ($metamode < 1)) {  
   $currentstring = $token->[4];  
  } else {  
   $currentstring = $token->[2];  
  }  
       }  
     }      }
 #    &Apache::lonxml::debug("nodefalt:$nodefault:");      if (!$deleted) {
     if ($currentstring eq '' && $nodefault eq '') {        if ($space) {
       if ($target eq 'edit') {   #&Apache::lonxml::debug("Calling sub $sub in $space $metamode");
  &Apache::lonxml::debug("doing default edit for $token->[1]");   $sub1="$space\:\:$sub";
  if ($token->[0] eq 'S') {   ($currentstring,$nodefault) = &$sub1($target,$token,$tagstack,
   $currentstring = &Apache::edit::tag_start($target,$token);       $parstack,$parser,$safeeval,
  } elsif ($token->[0] eq 'E') {       $style);
   $currentstring = &Apache::edit::tag_end($target,$token);        } else {
             if ($target eq 'tex') {
                 # throw away tag name
                 return '';
             }
    #&Apache::lonxml::debug("NOT Calling sub $sub in $space $metamode");
    if ($metamode <1) {
     if (defined($token->[4]) && ($metamode < 1)) {
       $currentstring = $token->[4];
     } else {
       $currentstring = $token->[2];
     }
  }   }
       } elsif ($target eq 'modified') {        }
  if ($token->[0] eq 'S') {        #    &Apache::lonxml::debug("nodefalt:$nodefault:");
   $currentstring = $token->[4];        if ($currentstring eq '' && $nodefault eq '') {
   $currentstring.=&Apache::edit::handle_insert();   if ($target eq 'edit') {
  } else {    #&Apache::lonxml::debug("doing default edit for $token->[1]");
   $currentstring = $token->[2];    if ($token->[0] eq 'S') {
       $currentstring = &Apache::edit::tag_start($target,$token);
     } elsif ($token->[0] eq 'E') {
       $currentstring = &Apache::edit::tag_end($target,$token);
     }
  }   }
       }        }
         if ($target eq 'modified' && $nodefault eq '') {
     if ($currentstring eq '') {
         if ($token->[0] eq 'S') {
     $currentstring = $token->[4];
         } elsif ($token->[0] eq 'E') {
     $currentstring = $token->[2];
         } else {
     $currentstring = $token->[2];
         }
     }
     if ($token->[0] eq 'S') {
         $currentstring.=&Apache::edit::handle_insert();
     } elsif ($token->[0] eq 'E') {
         $currentstring.=&Apache::edit::handle_insertafter($token->[1]);
     }
         }
     }      }
     use strict 'refs';      use strict 'refs';
   }    }
   return $currentstring;    return $currentstring;
 }  }
   
   {
       my %state;
   
       sub init_state {
    undef(%state);
       }
       
       sub set_state {
    my ($key,$value) = @_;
    $state{$key} = $value;
    return $value;
       }
       sub get_state {
    my ($key) = @_;
    return $state{$key};
       }
   }
   
   sub setup_globals {
     my ($request,$target)=@_;
     $Apache::lonxml::request=$request;
     $errorcount=0;
     $warningcount=0;
     $Apache::lonxml::default_homework_loaded=0;
     $Apache::lonxml::usestyle=1;
     &init_counter();
     &clear_bubble_lines_for_part();
     &init_state();
     &set_state('target',$target);
     @Apache::lonxml::pwd=();
     @Apache::lonxml::extlinks=();
     @script_var_displays=();
     @Apache::lonxml::ssi_info=();
     $Apache::lonxml::post_evaluate=1;
     $Apache::lonxml::warnings_error_header='';
     $Apache::lonxml::substitute_LaTeX_symbols = 1;
     if ($target eq 'meta') {
       $Apache::lonxml::redirection = 0;
       $Apache::lonxml::metamode = 1;
       $Apache::lonxml::evaluate = 1;
       $Apache::lonxml::import = 0;
     } elsif ($target eq 'answer') {
       $Apache::lonxml::redirection = 0;
       $Apache::lonxml::metamode = 1;
       $Apache::lonxml::evaluate = 1;
       $Apache::lonxml::import = 1;
     } elsif ($target eq 'grade') {
       &startredirection(); #ended in inner_xmlparse on exit
       $Apache::lonxml::metamode = 0;
       $Apache::lonxml::evaluate = 1;
       $Apache::lonxml::import = 1;
     } elsif ($target eq 'modified') {
       $Apache::lonxml::redirection = 0;
       $Apache::lonxml::metamode = 0;
       $Apache::lonxml::evaluate = 0;
       $Apache::lonxml::import = 0;
     } elsif ($target eq 'edit') {
       $Apache::lonxml::redirection = 0;
       $Apache::lonxml::metamode = 0;
       $Apache::lonxml::evaluate = 0;
       $Apache::lonxml::import = 0;
     } elsif ($target eq 'analyze') {
       $Apache::lonxml::redirection = 0;
       $Apache::lonxml::metamode = 0;
       $Apache::lonxml::evaluate = 1;
       $Apache::lonxml::import = 1;
     } else {
       $Apache::lonxml::redirection = 0;
       $Apache::lonxml::metamode = 0;
       $Apache::lonxml::evaluate = 1;
       $Apache::lonxml::import = 1;
     }
   }
   
 sub init_safespace {  sub init_safespace {
   my ($target,$safeeval,$safehole,$safeinit) = @_;    my ($target,$safeeval,$safehole,$safeinit) = @_;
     $safeeval->deny_only(':dangerous');
     $safeeval->reval('use Math::Complex;');
     $safeeval->permit_only(":default");
   $safeeval->permit("entereval");    $safeeval->permit("entereval");
   $safeeval->permit(":base_math");    $safeeval->permit(":base_math");
   $safeeval->permit("sort");    $safeeval->permit("sort");
     $safeeval->permit("time");
     $safeeval->deny("rand");
     $safeeval->deny("srand");
   $safeeval->deny(":base_io");    $safeeval->deny(":base_io");
     $safehole->wrap(\&Apache::scripttag::xmlparse,$safeeval,'&xmlparse');
     $safehole->wrap(\&Apache::outputtags::multipart,$safeeval,'&multipart');
   $safehole->wrap(\&Apache::lonnet::EXT,$safeeval,'&EXT');    $safehole->wrap(\&Apache::lonnet::EXT,$safeeval,'&EXT');
       $safehole->wrap(\&Apache::chemresponse::chem_standard_order,$safeeval,
     '&chem_standard_order');
     $safehole->wrap(\&Apache::response::check_status,$safeeval,'&check_status');
     $safehole->wrap(\&Apache::response::implicit_multiplication,$safeeval,'&implicit_multiplication');
   
     $safehole->wrap(\&Apache::lonmaxima::maxima_eval,$safeeval,'&maxima_eval');
     $safehole->wrap(\&Apache::lonmaxima::maxima_check,$safeeval,'&maxima_check');
     $safehole->wrap(\&Apache::lonmaxima::maxima_cas_formula_fix,$safeeval,
     '&maxima_cas_formula_fix');
   
     $safehole->wrap(\&Apache::caparesponse::capa_formula_fix,$safeeval,
     '&capa_formula_fix');
   
   $safehole->wrap(\&Math::Cephes::asin,$safeeval,'&asin');    $safehole->wrap(\&Math::Cephes::asin,$safeeval,'&asin');
   $safehole->wrap(\&Math::Cephes::acos,$safeeval,'&acos');    $safehole->wrap(\&Math::Cephes::acos,$safeeval,'&acos');
   $safehole->wrap(\&Math::Cephes::atan,$safeeval,'&atan');    $safehole->wrap(\&Math::Cephes::atan,$safeeval,'&atan');
Line 493  sub init_safespace { Line 757  sub init_safespace {
   $safehole->wrap(\&Math::Cephes::yn,$safeeval,'&yn');    $safehole->wrap(\&Math::Cephes::yn,$safeeval,'&yn');
   $safehole->wrap(\&Math::Cephes::yv,$safeeval,'&yv');    $safehole->wrap(\&Math::Cephes::yv,$safeeval,'&yv');
       
     $safehole->wrap(\&Math::Cephes::bdtr  ,$safeeval,'&bdtr'  );
     $safehole->wrap(\&Math::Cephes::bdtrc ,$safeeval,'&bdtrc' );
     $safehole->wrap(\&Math::Cephes::bdtri ,$safeeval,'&bdtri' );
     $safehole->wrap(\&Math::Cephes::btdtr ,$safeeval,'&btdtr' );
     $safehole->wrap(\&Math::Cephes::chdtr ,$safeeval,'&chdtr' );
     $safehole->wrap(\&Math::Cephes::chdtrc,$safeeval,'&chdtrc');
     $safehole->wrap(\&Math::Cephes::chdtri,$safeeval,'&chdtri');
     $safehole->wrap(\&Math::Cephes::fdtr  ,$safeeval,'&fdtr'  );
     $safehole->wrap(\&Math::Cephes::fdtrc ,$safeeval,'&fdtrc' );
     $safehole->wrap(\&Math::Cephes::fdtri ,$safeeval,'&fdtri' );
     $safehole->wrap(\&Math::Cephes::gdtr  ,$safeeval,'&gdtr'  );
     $safehole->wrap(\&Math::Cephes::gdtrc ,$safeeval,'&gdtrc' );
     $safehole->wrap(\&Math::Cephes::nbdtr ,$safeeval,'&nbdtr' );
     $safehole->wrap(\&Math::Cephes::nbdtrc,$safeeval,'&nbdtrc');
     $safehole->wrap(\&Math::Cephes::nbdtri,$safeeval,'&nbdtri');
     $safehole->wrap(\&Math::Cephes::ndtr  ,$safeeval,'&ndtr'  );
     $safehole->wrap(\&Math::Cephes::ndtri ,$safeeval,'&ndtri' );
     $safehole->wrap(\&Math::Cephes::pdtr  ,$safeeval,'&pdtr'  );
     $safehole->wrap(\&Math::Cephes::pdtrc ,$safeeval,'&pdtrc' );
     $safehole->wrap(\&Math::Cephes::pdtri ,$safeeval,'&pdtri' );
     $safehole->wrap(\&Math::Cephes::stdtr ,$safeeval,'&stdtr' );
     $safehole->wrap(\&Math::Cephes::stdtri,$safeeval,'&stdtri');
   
     $safehole->wrap(\&Math::Cephes::Matrix::mat,$safeeval,'&mat');
     $safehole->wrap(\&Math::Cephes::Matrix::new,$safeeval,
     '&Math::Cephes::Matrix::new');
     $safehole->wrap(\&Math::Cephes::Matrix::coef,$safeeval,
     '&Math::Cephes::Matrix::coef');
     $safehole->wrap(\&Math::Cephes::Matrix::clr,$safeeval,
     '&Math::Cephes::Matrix::clr');
     $safehole->wrap(\&Math::Cephes::Matrix::add,$safeeval,
     '&Math::Cephes::Matrix::add');
     $safehole->wrap(\&Math::Cephes::Matrix::sub,$safeeval,
     '&Math::Cephes::Matrix::sub');
     $safehole->wrap(\&Math::Cephes::Matrix::mul,$safeeval,
     '&Math::Cephes::Matrix::mul');
     $safehole->wrap(\&Math::Cephes::Matrix::div,$safeeval,
     '&Math::Cephes::Matrix::div');
     $safehole->wrap(\&Math::Cephes::Matrix::inv,$safeeval,
     '&Math::Cephes::Matrix::inv');
     $safehole->wrap(\&Math::Cephes::Matrix::transp,$safeeval,
     '&Math::Cephes::Matrix::transp');
     $safehole->wrap(\&Math::Cephes::Matrix::simq,$safeeval,
     '&Math::Cephes::Matrix::simq');
     $safehole->wrap(\&Math::Cephes::Matrix::mat_to_vec,$safeeval,
     '&Math::Cephes::Matrix::mat_to_vec');
     $safehole->wrap(\&Math::Cephes::Matrix::vec_to_mat,$safeeval,
     '&Math::Cephes::Matrix::vec_to_mat');
     $safehole->wrap(\&Math::Cephes::Matrix::check,$safeeval,
     '&Math::Cephes::Matrix::check');
     $safehole->wrap(\&Math::Cephes::Matrix::check,$safeeval,
     '&Math::Cephes::Matrix::check');
   
   #  $safehole->wrap(\&Math::Cephes::new_fract,$safeeval,'&new_fract');
   #  $safehole->wrap(\&Math::Cephes::radd,$safeeval,'&radd');
   #  $safehole->wrap(\&Math::Cephes::rsub,$safeeval,'&rsub');
   #  $safehole->wrap(\&Math::Cephes::rmul,$safeeval,'&rmul');
   #  $safehole->wrap(\&Math::Cephes::rdiv,$safeeval,'&rdiv');
   #  $safehole->wrap(\&Math::Cephes::euclid,$safeeval,'&euclid');
   
     $safehole->wrap(\&Math::Random::random_beta,$safeeval,'&math_random_beta');
     $safehole->wrap(\&Math::Random::random_chi_square,$safeeval,'&math_random_chi_square');
     $safehole->wrap(\&Math::Random::random_exponential,$safeeval,'&math_random_exponential');
     $safehole->wrap(\&Math::Random::random_f,$safeeval,'&math_random_f');
     $safehole->wrap(\&Math::Random::random_gamma,$safeeval,'&math_random_gamma');
     $safehole->wrap(\&Math::Random::random_multivariate_normal,$safeeval,'&math_random_multivariate_normal');
     $safehole->wrap(\&Math::Random::random_multinomial,$safeeval,'&math_random_multinomial');
     $safehole->wrap(\&Math::Random::random_noncentral_chi_square,$safeeval,'&math_random_noncentral_chi_square');
     $safehole->wrap(\&Math::Random::random_noncentral_f,$safeeval,'&math_random_noncentral_f');
     $safehole->wrap(\&Math::Random::random_normal,$safeeval,'&math_random_normal');
     $safehole->wrap(\&Math::Random::random_permutation,$safeeval,'&math_random_permutation');
     $safehole->wrap(\&Math::Random::random_permuted_index,$safeeval,'&math_random_permuted_index');
     $safehole->wrap(\&Math::Random::random_uniform,$safeeval,'&math_random_uniform');
     $safehole->wrap(\&Math::Random::random_poisson,$safeeval,'&math_random_poisson');
     $safehole->wrap(\&Math::Random::random_uniform_integer,$safeeval,'&math_random_uniform_integer');
     $safehole->wrap(\&Math::Random::random_negative_binomial,$safeeval,'&math_random_negative_binomial');
     $safehole->wrap(\&Math::Random::random_binomial,$safeeval,'&math_random_binomial');
     $safehole->wrap(\&Math::Random::random_seed_from_phrase,$safeeval,'&random_seed_from_phrase');
     $safehole->wrap(\&Math::Random::random_set_seed_from_phrase,$safeeval,'&random_set_seed_from_phrase');
     $safehole->wrap(\&Math::Random::random_get_seed,$safeeval,'&random_get_seed');
     $safehole->wrap(\&Math::Random::random_set_seed,$safeeval,'&random_set_seed');
     $safehole->wrap(\&Apache::loncommon::languages,$safeeval,'&languages');
     $safehole->wrap(\&Apache::lonxml::error,$safeeval,'&LONCAPA_INTERNAL_ERROR');
     $safehole->wrap(\&Apache::lonxml::debug,$safeeval,'&LONCAPA_INTERNAL_DEBUG');
     $safehole->wrap(\&Apache::lonnet::logthis,$safeeval,'&LONCAPA_INTERNAL_LOGTHIS');
     $safehole->wrap(\&Apache::inputtags::finalizeawards,$safeeval,'&LONCAPA_INTERNAL_FINALIZEAWARDS');
     $safehole->wrap(\&Apache::caparesponse::get_sigrange,$safeeval,'&LONCAPA_INTERNAL_get_sigrange');
   #  use Data::Dumper;
   #  $safehole->wrap(\&Data::Dumper::Dumper,$safeeval,'&LONCAPA_INTERNAL_Dumper');
 #need to inspect this class of ops  #need to inspect this class of ops
 # $safeeval->deny(":base_orig");  # $safeeval->deny(":base_orig");
   $safeinit .= ';$external::target='.$target.';';    $safeeval->permit("require");
   $safeinit .= ';$external::randomseed='.&Apache::lonnet::rndseed().';';    $safeinit .= ';$external::target="'.$target.'";';
   &Apache::run::run($safeinit,$safeeval);    &Apache::run::run($safeinit,$safeeval);
     &initialize_rndseed($safeeval);
   }
   
   sub clean_safespace {
       my ($safeeval) = @_;
       delete_package_recurse($safeeval->{Root});
   }
   
   sub delete_package_recurse {
        my ($package) = @_;
        my @subp;
        {
    no strict 'refs';
    while (my ($key,$val) = each(%{*{"$package\::"}})) {
        if (!defined($val)) { next; }
        local (*ENTRY) = $val;
        if (defined *ENTRY{HASH} && $key =~ /::$/ &&
    $key ne "main::" && $key ne "<none>::")
        {
    my ($p) = $package ne "main" ? "$package\::" : "";
    ($p .= $key) =~ s/::$//;
    push(@subp,$p);
        }
    }
        }
        foreach my $p (@subp) {
    delete_package_recurse($p);
        }
        Symbol::delete_package($package);
   }
   
   sub initialize_rndseed {
       my ($safeeval)=@_;
       my $rndseed;
       my ($symb,$courseid,$domain,$name) = &Apache::lonnet::whichuser();
       $rndseed=&Apache::lonnet::rndseed($symb,$courseid,$domain,$name);
       my $safeinit = '$external::randomseed="'.$rndseed.'";';
       &Apache::lonxml::debug("Setting rndseed to $rndseed");
       &Apache::run::run($safeinit,$safeeval);
   }
   
   sub default_homework_load {
       my ($safeeval)=@_;
       &Apache::lonxml::debug('Loading default_homework');
       my $default=&Apache::lonnet::getfile('/home/httpd/html/res/adm/includes/default_homework.lcpm');
       if ($default eq -1) {
    &Apache::lonxml::error("<b>Unable to find <i>default_homework.lcpm</i></b>");
       } else {
    &Apache::run::run($default,$safeeval);
    $Apache::lonxml::default_homework_loaded=1;
       }
 }  }
   
   {
       my $alarm_depth;
       sub init_alarm {
    alarm(0);
    $alarm_depth=0;
       }
   
       sub start_alarm {
    if ($alarm_depth<1) {
       my $old=alarm($Apache::lonnet::perlvar{'lonScriptTimeout'});
       if ($old) {
    &Apache::lonxml::error("Cancelled an alarm of $old, this shouldn't occur.");
       }
    }
    $alarm_depth++;
       }
   
       sub end_alarm {
    $alarm_depth--;
    if ($alarm_depth<1) { alarm(0); }
       }
   }
   my $metamode_was;
 sub startredirection {  sub startredirection {
   $Apache::lonxml::redirection++;      if (!$Apache::lonxml::redirection) {
   push (@Apache::lonxml::outputstack, '');   $metamode_was=$Apache::lonxml::metamode;
       }
       $Apache::lonxml::metamode=0;
       $Apache::lonxml::redirection++;
       push (@Apache::lonxml::outputstack, '');
 }  }
   
 sub endredirection {  sub endredirection {
   if (!$Apache::lonxml::redirection) {      if (!$Apache::lonxml::redirection) {
     &Apache::lonxml::error("Endredirection was called, before a startredirection, perhaps you have unbalanced tags. Some debuging information:".join ":",caller);   &Apache::lonxml::error("Endredirection was called before a startredirection, perhaps you have unbalanced tags. Some debugging information:".join ":",caller);
     return '';   return '';
   }      }
   $Apache::lonxml::redirection--;      $Apache::lonxml::redirection--;
   pop @Apache::lonxml::outputstack;      if (!$Apache::lonxml::redirection) {
    $Apache::lonxml::metamode=$metamode_was;
       }
       pop @Apache::lonxml::outputstack;
   }
   sub in_redirection {
       return ($Apache::lonxml::redirection > 0)
   }
   
   sub end_tag {
     my ($tagstack,$parstack,$token)=@_;
     pop(@$tagstack);
     pop(@$parstack);
     &decreasedepth($token);
 }  }
   
 sub initdepth {  sub initdepth {
   @Apache::lonxml::depthcounter=();    @Apache::lonxml::depthcounter=();
   $Apache::lonxml::depth=-1;    undef($Apache::lonxml::last_depth_count);
   $Apache::lonxml::olddepth=-1;  
 }  }
   
   
   my @timers;
   my $lasttime;
   # @Apache::lonxml::depthcounter -> count of tags that exist so
   #                                  far at each level
   # $Apache::lonxml::last_depth_count -> when ascending, need to
   # remember the count for the level below the current level (for
   # example going from 1_2 -> 1 -> 1_3 need to remember the 2 )
   
 sub increasedepth {  sub increasedepth {
   my ($token) = @_;    my ($token) = @_;
   $Apache::lonxml::depth++;    push(@Apache::lonxml::depthcounter,$Apache::lonxml::last_depth_count+1);
   $Apache::lonxml::depthcounter[$Apache::lonxml::depth]++;    undef($Apache::lonxml::last_depth_count);
   if ($Apache::lonxml::depthcounter[$Apache::lonxml::depth]==1) {    my $time;
     $Apache::lonxml::olddepth=$Apache::lonxml::depth;    if ($Apache::lonxml::debug eq "1") {
         push(@timers,[&gettimeofday()]);
         $time=&tv_interval($lasttime);
         $lasttime=[&gettimeofday()];
   }    }
   my $curdepth=join('_',@Apache::lonxml::depthcounter);    my $spacing='  'x($#Apache::lonxml::depthcounter);
   &Apache::lonxml::debug("s $Apache::lonxml::depth : $Apache::lonxml::olddepth : $curdepth : $token->[1]\n");    $Apache::lonxml::curdepth=join('_',@Apache::lonxml::depthcounter);
   #  &Apache::lonxml::debug("s$spacing$Apache::lonxml::depth : $Apache::lonxml::olddepth : $Apache::lonxml::curdepth : $token->[1] : $time");
 #print "<br />s $Apache::lonxml::depth : $Apache::lonxml::olddepth : $curdepth : $token->[1]\n";  #print "<br />s $Apache::lonxml::depth : $Apache::lonxml::olddepth : $curdepth : $token->[1]\n";
 }  }
   
 sub decreasedepth {  sub decreasedepth {
   my ($token) = @_;    my ($token) = @_;
   $Apache::lonxml::depth--;    if (  $#Apache::lonxml::depthcounter == -1) {
   if ($Apache::lonxml::depth<$Apache::lonxml::olddepth-1) {        &Apache::lonxml::warning(&mt("Missing tags, unable to properly run file."));
     $#Apache::lonxml::depthcounter--;  
     $Apache::lonxml::olddepth=$Apache::lonxml::depth+1;  
   }  
   if (  $Apache::lonxml::depth < -1) {  
     &Apache::lonxml::warning("Unbalanced tags in resource");     
     $Apache::lonxml::depth='-1';  
   }    }
   my $curdepth=join('_',@Apache::lonxml::depthcounter);    $Apache::lonxml::last_depth_count = pop(@Apache::lonxml::depthcounter);
   &Apache::lonxml::debug("e $Apache::lonxml::depth : $Apache::lonxml::olddepth : $token->[1] : $curdepth\n");  
     my ($timer,$time);
     if ($Apache::lonxml::debug eq "1") {
         $timer=pop(@timers);
         $time=&tv_interval($lasttime);
         $lasttime=[&gettimeofday()];
     }
     my $spacing='  'x($#Apache::lonxml::depthcounter);
     $Apache::lonxml::curdepth = join('_',@Apache::lonxml::depthcounter);
   #  &Apache::lonxml::debug("e$spacing$Apache::lonxml::depth : $Apache::lonxml::olddepth : $Apache::lonxml::curdepth : $token->[1] : $time : ".&tv_interval($timer));
 #print "<br />e $Apache::lonxml::depth : $Apache::lonxml::olddepth : $token->[1] : $curdepth\n";  #print "<br />e $Apache::lonxml::depth : $Apache::lonxml::olddepth : $token->[1] : $curdepth\n";
 }  }
   
 sub get_all_text {  sub get_id {
       my ($parstack,$safeeval)=@_;
       my $id= &Apache::lonxml::get_param('id',$parstack,$safeeval);
       if ($env{'request.state'} eq 'construct' && $id =~ /([._]|[^\w\d\s[:punct:]])/) {
    &error(&mt("ID &quot;[_1]&quot; contains invalid characters, IDs are only allowed to contain letters, numbers, spaces and -",'<tt>'.$id.'</tt>'));
       }
       if ($id =~ /^\s*$/) { $id = $Apache::lonxml::curdepth; }
       return $id;
   }
   
  my($tag,$pars)= @_;  sub get_all_text_unbalanced {
  my $depth=0;  #there is a copy of this in lonpublisher.pm
  my $token;      my($tag,$pars)= @_;
  my $result='';      my $token;
  if ( $tag =~ m:^/: ) {       my $result='';
    my $tag=substr($tag,1);       $tag='<'.$tag.'>';
 #   &Apache::lonxml::debug("have:$tag:");      while ($token = $$pars[-1]->get_token) {
    while (($depth >=0) && ($token = $pars->get_token)) {   if (($token->[0] eq 'T')||($token->[0] eq 'C')||($token->[0] eq 'D')) {
 #     &Apache::lonxml::debug("e token:$token->[0]:$depth:$token->[1]");      if ($token->[0] eq 'T' && $token->[2]) {
      if (($token->[0] eq 'T')||($token->[0] eq 'C')||($token->[0] eq 'D')) {   $result.='<![CDATA['.$token->[1].']]>';
        $result.=$token->[1];      } else {
      } elsif ($token->[0] eq 'PI') {   $result.=$token->[1];
        $result.=$token->[2];      }
      } elsif ($token->[0] eq 'S') {   } elsif ($token->[0] eq 'PI') {
        if ($token->[1] eq $tag) { $depth++; }      $result.=$token->[2];
        $result.=$token->[4];   } elsif ($token->[0] eq 'S') {
      } elsif ($token->[0] eq 'E')  {      $result.=$token->[4];
        if ( $token->[1] eq $tag) { $depth--; }   } elsif ($token->[0] eq 'E')  {
        #skip sending back the last end tag      $result.=$token->[2];
        if ($depth > -1) { $result.=$token->[2]; } else {   }
  $pars->unget_token($token);   if ($result =~ /\Q$tag\E/is) {
        }      ($result,my $redo)=$result =~ /(.*)\Q$tag\E(.*)/is;
      }      #&Apache::lonxml::debug('Got a winner with leftovers ::'.$2);
    }      #&Apache::lonxml::debug('Result is :'.$1);
  } else {      $redo=$tag.$redo;
    while ($token = $pars->get_token) {      &Apache::lonxml::newparser($pars,\$redo);
 #     &Apache::lonxml::debug("s token:$token->[0]:$depth:$token->[1]");      last;
      if (($token->[0] eq 'T')||($token->[0] eq 'C')||($token->[0] eq 'D')) {   }
        $result.=$token->[1];      }
      } elsif ($token->[0] eq 'PI') {      return $result
        $result.=$token->[2];  
      } elsif ($token->[0] eq 'S') {  }
        if ( $token->[1] eq $tag) {   
  $pars->unget_token($token); last;  #########################################################################
        } else {  #                                                                       #
  $result.=$token->[4];  #           bubble line counter management                              #
        }  #                                                                       #
      } elsif ($token->[0] eq 'E')  {  #########################################################################
        $result.=$token->[2];  
      }  =pod
    }  
  }  For bubble grading mode and exam bubble printing mode, the tracking of
 # &Apache::lonxml::debug("Exit:$result:");  the current 'bubble line number' is stored in the %env element
  return $result  'form.counter', and is modifed and handled by the following routines.
   
   The value of it is stored in $Apache:lonxml::counter when live and
   stored back to env after done.
   
   =item &increment_counter($increment);
   
   Increments the internal counter environment variable a specified amount
   
   Optional Arguments:
     $increment - amount to increment by (defaults to 1)
                  Also 1 if the value is negative or zero.
     $part_response - A concatenation of the part and response id
                      identifying exactly what is being 'answered'.
   
   
   =cut
   
   sub increment_counter {
       my ($increment, $part_response) = @_;
       if ($env{'form.grade_target'} eq 'analyze') { return; }
       if (!defined($increment) || $increment le 0) {
    $increment = 1;
       }
       $Apache::lonxml::counter += $increment;
   
       # If the caller supplied the response_id parameter, 
       # Maintain its counter.. creating if necessary.
   
       if (defined($part_response)) {
    if (!defined($Apache::lonxml::counters_per_part{$part_response})) {
       $Apache::lonxml::counters_per_part{$part_response} = 0;
    }
    $Apache::lonxml::counters_per_part{$part_response} += $increment;
    my $new_value = $Apache::lonxml::counters_per_part{$part_response};
       }
   
       $Apache::lonxml::counter_changed=1;
   }
   
   =pod
   
   =item &init_counter($increment);
   
   Initialize the internal counter environment variable
   
   =cut
   
   sub init_counter {
       if ($env{'request.state'} eq 'construct') {
    $Apache::lonxml::counter=1;
    $Apache::lonxml::counter_changed=1;
       } elsif (defined($env{'form.counter'})) {
    $Apache::lonxml::counter=$env{'form.counter'};
    $Apache::lonxml::counter_changed=0;
       } else {
    $Apache::lonxml::counter=1;
    $Apache::lonxml::counter_changed=1;
       }
   }
   
   sub store_counter {
       &Apache::lonnet::appenv({'form.counter' => $Apache::lonxml::counter});
       $Apache::lonxml::counter_changed=0;
       return '';
   }
   
   {
       my $state;
       sub clear_problem_counter {
    undef($state);
    &Apache::lonnet::delenv('form.counter');
    &Apache::lonxml::init_counter();
    &Apache::lonxml::store_counter();
       }
   
       sub remember_problem_counter {
    &Apache::lonnet::transfer_profile_to_env(undef,undef,1);
    $state = $env{'form.counter'};
       }
   
       sub restore_problem_counter {
    if (defined($state)) {
       &Apache::lonnet::appenv({'form.counter' => $state});
    }
       }
       sub get_problem_counter {
    if ($Apache::lonxml::counter_changed) { &store_counter() }
    &Apache::lonnet::transfer_profile_to_env(undef,undef,1);
    return $env{'form.counter'};
       }
   }
   
   =pod
   
   =item  bubble_lines_for_part(part_response)
   
   Returns the number of lines required to get a response for
   $part_response (this is just $Apache::lonxml::counters_per_part{$part_response}
   
   =cut
   
   sub bubble_lines_for_part {
       my ($part_response) = @_;
   
       if (!defined($Apache::lonxml::counters_per_part{$part_response})) {
    return 0;
       } else {
    return $Apache::lonxml::counters_per_part{$part_response};
       }
   }
   
   =pod
   
   =item clear_bubble_lines_for_part
   
   Clears the hash of bubble lines per part.  If a caller
   needs to analyze several resources this should be called between
   resources to reset the hash for each problem being analyzed.
   
   =cut
   
   sub clear_bubble_lines_for_part {
       undef(%Apache::lonxml::counters_per_part);
   }
   
   =pod
   
   =item set_bubble_lines(part_response, value)
   
   If there is a problem part, that for whatever reason
   requires bubble lines that are not
   the same as the counter increment, it can call this sub during
   analysis to set its hash value explicitly.
   
   =cut
   
   sub set_bubble_lines {
       my ($part_response, $value) = @_;
   
       $Apache::lonxml::counters_per_part{$part_response} = $value;
   }
   
   =pod
   
   =item get_bubble_line_hash
   
   Returns the current bubble line hash.  This is assumed to 
   be small so we return a copy
   
   
   =cut
   
   sub get_bubble_line_hash {
       return %Apache::lonxml::counters_per_part;
   }
   
   
   #--------------------------------------------------
   
   sub get_all_text {
       my($tag,$pars,$style)= @_;
       my $gotfullstack=1;
       if (ref($pars) ne 'ARRAY') {
    $gotfullstack=0;
    $pars=[$pars];
       }
       if (ref($style) ne 'HASH') {
    $style={};
       }
       my $depth=0;
       my $token;
       my $result='';
       if ( $tag =~ m:^/: ) { 
    my $tag=substr($tag,1); 
    #&Apache::lonxml::debug("have:$tag:");
    my $top_empty=0;
    while (($depth >=0) && ($#$pars > -1) && (!$top_empty)) {
       while (($depth >=0) && ($token = $$pars[-1]->get_token)) {
    #&Apache::lonxml::debug("e token:$token->[0]:$depth:$token->[1]:".$#$pars.":".$#Apache::lonxml::pwd);
    if (($token->[0] eq 'T')||($token->[0] eq 'C')||($token->[0] eq 'D')) {
       if ($token->[2]) {
    $result.='<![CDATA['.$token->[1].']]>';
       } else {
    $result.=$token->[1];
       }
    } elsif ($token->[0] eq 'PI') {
       $result.=$token->[2];
    } elsif ($token->[0] eq 'S') {
       if ($token->[1] =~ /^\Q$tag\E$/i) { $depth++; }
       if ($token->[1] =~ /^LONCAPA_INTERNAL_TURN_STYLE_ON$/) { $Apache::lonxml::usestyle=1; }
       if ($token->[1] =~ /^LONCAPA_INTERNAL_TURN_STYLE_OFF$/) { $Apache::lonxml::usestyle=0; }
       $result.=$token->[4];
    } elsif ($token->[0] eq 'E')  {
       if ( $token->[1] =~ /^\Q$tag\E$/i) { $depth--; }
       #skip sending back the last end tag
       if ($depth == 0 && exists($$style{'/'.$token->[1]}) && $Apache::lonxml::usestyle) {
    my $string=
       '<LONCAPA_INTERNAL_TURN_STYLE_OFF end="yes" />'.
    $$style{'/'.$token->[1]}.
       $token->[2].
    '<LONCAPA_INTERNAL_TURN_STYLE_ON />';
    &Apache::lonxml::newparser($pars,\$string);
    #&Apache::lonxml::debug("reParsing $string");
    next;
       }
       if ($depth > -1) {
    $result.=$token->[2];
       } else {
    $$pars[-1]->unget_token($token);
       }
    }
       }
       if (($depth >=0) && ($#$pars == 0) ) { $top_empty=1; }
       if (($depth >=0) && ($#$pars > 0) ) {
    pop(@$pars);
    pop(@Apache::lonxml::pwd);
       }
    }
    if ($top_empty && $depth >= 0) {
       #never found the end tag ran out of text, throw error send back blank
       &error('Never found end tag for &lt;'.$tag.
      '&gt; current string <pre>'.
      &HTML::Entities::encode($result,'<>&"').
      '</pre>');
       if ($gotfullstack) {
    my $newstring='</'.$tag.'>'.$result;
    &Apache::lonxml::newparser($pars,\$newstring);
       }
       $result='';
    }
       } else {
    while ($#$pars > -1) {
       while ($token = $$pars[-1]->get_token) {
    #&Apache::lonxml::debug("s token:$token->[0]:$depth:$token->[1]");
    if (($token->[0] eq 'T')||($token->[0] eq 'C')||
       ($token->[0] eq 'D')) {
       if ($token->[2]) {
    $result.='<![CDATA['.$token->[1].']]>';
       } else {
    $result.=$token->[1];
       }
    } elsif ($token->[0] eq 'PI') {
       $result.=$token->[2];
    } elsif ($token->[0] eq 'S') {
       if ( $token->[1] =~ /^\Q$tag\E$/i) {
    $$pars[-1]->unget_token($token); last;
       } else {
    $result.=$token->[4];
       }
       if ($token->[1] =~ /^LONCAPA_INTERNAL_TURN_STYLE_ON$/) { $Apache::lonxml::usestyle=1; }
       if ($token->[1] =~ /^LONCAPA_INTERNAL_TURN_STYLE_OFF$/) { $Apache::lonxml::usestyle=0; }
    } elsif ($token->[0] eq 'E')  {
       $result.=$token->[2];
    }
       }
       if (($#$pars > 0) ) {
    pop(@$pars);
    pop(@Apache::lonxml::pwd);
       } else { last; }
    }
       }
       #&Apache::lonxml::debug("Exit:$result:");
       return $result
 }  }
   
 sub newparser {  sub newparser {
   my ($parser,$contentref,$dir) = @_;    my ($parser,$contentref,$dir) = @_;
   push (@$parser,HTML::TokeParser->new($contentref));    push (@$parser,HTML::LCParser->new($contentref));
   $$parser['-1']->xml_mode('1');    $$parser[-1]->xml_mode(1);
     $$parser[-1]->marked_sections(1);
   if ( $dir eq '' ) {    if ( $dir eq '' ) {
     push (@Apache::lonxml::pwd, $Apache::lonxml::pwd[$#Apache::lonxml::pwd]);      push (@Apache::lonxml::pwd, $Apache::lonxml::pwd[$#Apache::lonxml::pwd]);
   } else {    } else {
     push (@Apache::lonxml::pwd, $dir);      push (@Apache::lonxml::pwd, $dir);
   }     } 
 #  &Apache::lonxml::debug("pwd:$#Apache::lonxml::pwd");  
 #  &Apache::lonxml::debug("pwd:$Apache::lonxml::pwd[$#Apache::lonxml::pwd]");  
 }  }
   
 sub parstring {  sub parstring {
   my ($token) = @_;      my ($token) = @_;
   my $temp='';      my (@vars,@values);
   map {      foreach my $attr (@{$token->[3]}) {
     unless ($_=~/\W/) {   if ($attr!~/\W/) {
       my $val=$token->[2]->{$_};      my $val=$token->[2]->{$attr};
       $val =~ s/([\%\@\\])/\\$1/g;      $val =~ s/([\%\@\\\"\'])/\\$1/g;
       #if ($val =~ m/^[\%\@]/) { $val="\\".$val; }      $val =~ s/(\$[^\{a-zA-Z_])/\\$1/g;
       $temp .= "my \$$_=\"$val\";"      $val =~ s/(\$)$/\\$1/;
       #if ($val =~ m/^[\%\@]/) { $val="\\".$val; }
       push(@vars,"\$$attr");
       push(@values,"\"$val\"");
    }
     }      }
   } @{$token->[3]};      my $var_init = 
   return $temp;   (@vars) ? 'my ('.join(',',@vars).') = ('.join(',',@values).');'
           : '';
       return $var_init;
   }
   
   sub extlink {
       my ($res,$exact)=@_;
       if (!$exact) {
    $res=&Apache::lonnet::hreflocation($Apache::lonxml::pwd[-1],$res);
       }
       push(@Apache::lonxml::extlinks,$res)  
 }  }
   
 sub writeallows {  sub writeallows {
     my $thisurl='/res/'.&Apache::lonnet::declutter(shift);      unless ($#extlinks>=0) { return; }
       my $thisurl = &Apache::lonnet::clutter(shift);
       if ($env{'httpref.'.$thisurl}) {
    $thisurl=$env{'httpref.'.$thisurl};
       }
     my $thisdir=$thisurl;      my $thisdir=$thisurl;
     $thisdir=~s/\/[^\/]+$//;      $thisdir=~s/\/[^\/]+$//;
     my %httpref=();      my %httpref=();
     map {      foreach (@extlinks) {
        $httpref{'httpref.'.         $httpref{'httpref.'.
          &Apache::lonnet::hreflocation($thisdir,$_)}=$thisurl;              } @extlinks;           &Apache::lonnet::hreflocation($thisdir,&unescape($_))}=$thisurl;
     &Apache::lonnet::appenv(%httpref);      }
       @extlinks=();
       &Apache::lonnet::appenv(\%httpref);
   }
   
   sub register_ssi {
       my ($url,%form)=@_;
       push (@Apache::lonxml::ssi_info,{'url'=>$url,'form'=>\%form});
       return '';
   }
   
   sub do_registered_ssi {
       foreach my $info (@Apache::lonxml::ssi_info) {
    my %form=%{ $info->{'form'}};
    my $url=$info->{'url'};
    &Apache::lonnet::ssi($url,%form);
       }
   }
   
   sub add_script_result {
       my ($display) = @_;
       push(@script_var_displays, $display);
 }  }
   
 #  #
Line 639  sub writeallows { Line 1407  sub writeallows {
 #  #
 sub afterburn {  sub afterburn {
     my $result=shift;      my $result=shift;
     map {      &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},
        my ($name, $value) = split(/=/,$_);      ['highlight','anchor','link']);
        $value =~ tr/+/ /;      if ($env{'form.highlight'}) {
        $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;         foreach (split(/\,/,$env{'form.highlight'})) {
        if (($name eq 'highlight')||($name eq 'anchor')||($name eq 'link')) {  
            unless ($ENV{'form.'.$name}) {  
               $ENV{'form.'.$name}=$value;  
    }  
        }  
     } (split(/&/,$ENV{'QUERY_STRING'}));  
     if ($ENV{'form.highlight'}) {  
         map {  
            my $anchorname=$_;             my $anchorname=$_;
    my $matchthis=$anchorname;     my $matchthis=$anchorname;
            $matchthis=~s/\_+/\\s\+/g;             $matchthis=~s/\_+/\\s\+/g;
            $result=~s/($matchthis)/\<font color=\"red\"\>$1\<\/font\>/gs;             $result=~s/(\Q$matchthis\E)/\<font color=\"red\"\>$1\<\/font\>/gs;
        } split(/\,/,$ENV{'form.highlight'});         }
     }      }
     if ($ENV{'form.link'}) {      if ($env{'form.link'}) {
         map {         foreach (split(/\,/,$env{'form.link'})) {
            my ($anchorname,$linkurl)=split(/\>/,$_);             my ($anchorname,$linkurl)=split(/\>/,$_);
    my $matchthis=$anchorname;     my $matchthis=$anchorname;
            $matchthis=~s/\_+/\\s\+/g;             $matchthis=~s/\_+/\\s\+/g;
            $result=~s/($matchthis)/\<a href=\"$linkurl\"\>$1\<\/a\>/gs;             $result=~s/(\Q$matchthis\E)/\<a href=\"$linkurl\"\>$1\<\/a\>/gs;
        } split(/\,/,$ENV{'form.link'});         }
     }      }
     if ($ENV{'form.anchor'}) {      if ($env{'form.anchor'}) {
         my $anchorname=$ENV{'form.anchor'};          my $anchorname=$env{'form.anchor'};
  my $matchthis=$anchorname;   my $matchthis=$anchorname;
         $matchthis=~s/\_+/\\s\+/g;          $matchthis=~s/\_+/\\s\+/g;
         $result=~s/($matchthis)/\<a name=\"$anchorname\"\>$1\<\/a\>/s;          $result=~s/(\Q$matchthis\E)/\<a name=\"$anchorname\"\>$1\<\/a\>/s;
         $result.=(<<"ENDSCRIPT");          $result.=(<<"ENDSCRIPT");
 <script>  <script type="text/javascript">
     document.location.hash='$anchorname';      document.location.hash='$anchorname';
 </script>  </script>
 ENDSCRIPT  ENDSCRIPT
Line 681  ENDSCRIPT Line 1441  ENDSCRIPT
   
 sub storefile {  sub storefile {
     my ($file,$contents)=@_;      my ($file,$contents)=@_;
       &Apache::lonnet::correct_line_ends(\$contents);
     if (my $fh=Apache::File->new('>'.$file)) {      if (my $fh=Apache::File->new('>'.$file)) {
  print $fh $contents;   print $fh $contents;
         $fh->close();          $fh->close();
           return 1;
       } else {
    &warning("Unable to save file $file");
    return 0;
     }      }
 }  }
   
 sub inserteditinfo {  sub createnewhtml {
       my ($result,$filecontents)=@_;      my $title=&mt('Title of document goes here');
       unless ($filecontents) {      my $body=&mt('Body of document goes here');
   $filecontents=(<<SIMPLECONTENT);      my $filecontents=(<<SIMPLECONTENT);
 <html>  <html>
 <head>  <head>
 <title>  <title>$title</title>
                            Title of Document Goes Here  
 </title>  
 </head>  </head>
 <body bgcolor="#FFFFFF">  <body bgcolor="#FFFFFF">
   $body
                            Body of Document Goes Here  
   
 </body>  </body>
 </html>  </html>
 SIMPLECONTENT  SIMPLECONTENT
       return $filecontents;
   }
   
   sub createnewsty {
     my $filecontents=(<<SIMPLECONTENT);
   <definetag name="">
       <render>
          <web></web>
          <tex></tex>
       </render>
   </definetag>
   SIMPLECONTENT
     return $filecontents;
   }
   
   sub verify_html {
       my ($filecontents)=@_;
       if ($filecontents!~/(?:\<|\&lt\;)(?:html|xml)[^\<]*(?:\>|\&gt\;)/is) {
          return &mt('File does not have [_1] or [_2] starting tag','&lt;html&gt;','&lt;xml&gt;');
       }
       if ($filecontents!~/(?:\<|\&lt\;)\/(?:html|xml)(?:\>|\&gt\;)/is) {
          return &mt('File does not have [_1] or [_2] ending tag','&lt;html&gt;','&lt;xml&gt;');
       }
       if ($filecontents!~/(?:\<|\&lt\;)(?:body|frameset)[^\<]*(?:\>|\&gt\;)/is) {
          return &mt('File does not have [_1] or [_2] starting tag','&lt;body&gt;','&lt;frameset&gt;');
       }
       if ($filecontents!~/(?:\<|\&lt\;)\/(?:body|frameset)[^\<]*(?:\>|\&gt\;)/is) {
          return &mt('File does not have [_1] or [_2] ending tag','&lt;body&gt;','&lt;frameset&gt;');
       }
       return '';
   }
   
   sub inserteditinfo {
         my ($filecontents, $filetype, $filename)=@_;
         $filecontents = &HTML::Entities::encode($filecontents,'<>&"');
         my $xml_help = '';
         my $initialize='';
         my $textarea_id = 'filecont';
         my ($add_to_onload, $add_to_onresize);
         $initialize=&Apache::lonhtmlcommon::spellheader();
         if ($filetype eq 'html' 
     && (!&Apache::lonhtmlcommon::htmlareablocked() &&
         &Apache::lonhtmlcommon::htmlareabrowser())) {
     $textarea_id .= '___Frame';
     my $lang = &Apache::lonhtmlcommon::htmlarea_lang();
     $initialize.=(<<FULLPAGE);
   <script type="text/javascript">
   lonca
       function initDocument() {
           var oFCKeditor = new FCKeditor('filecont');
    oFCKeditor.Config['CustomConfigurationsPath'] = '/fckeditor/loncapaconfig.js'  ;
    oFCKeditor.Config['FullPage'] = true
    oFCKeditor.Config['AutoDetectLanguage'] = false;
           oFCKeditor.Config['DefaultLanguage'] = "$lang";
    oFCKeditor.ReplaceTextarea();
       }
       function check_if_dirty(editor) {
    if (editor.IsDirty()) {
       unClean();
    }
       }
       function FCKeditor_OnComplete(editor) {
    editor.Events.AttachEvent("OnSelectionChange",check_if_dirty);
    resize_textarea('$textarea_id','LC_aftertextarea');
       }
   </script>
   FULLPAGE
         } else {
     $initialize.=(<<FULLPAGE);
   <script type="text/javascript">
       function initDocument() {
    resize_textarea('$textarea_id','LC_aftertextarea');
       }
   </script>
   FULLPAGE
         }
   
         $add_to_onload = 'initDocument();';
         $add_to_onresize = "resize_textarea('$textarea_id','LC_aftertextarea');";
   
         if ($filetype eq 'html') {
     $xml_help=&Apache::loncommon::helpLatexCheatsheet();
         }
   
         my $titledisplay=&display_title();
         my %lt=&Apache::lonlocal::texthash('st' => 'Save and Edit',
    'vi' => 'Save and View',
    'dv' => 'Discard Edits and View',
    'un' => 'undo',
    'ed' => 'Edit');
         my $spelllink .=&Apache::lonhtmlcommon::spelllink('xmledit','filecont');
         my $textarea_events = &Apache::edit::element_change_detection();
         my $form_events     = &Apache::edit::form_change_detection();
         my $htmlerror=&verify_html($filecontents);
         if ($htmlerror) {
            $htmlerror='<span class="LC_error">'.$htmlerror.'</span>';
       }        }
       my $editheader='<a href="#editsection">Edit below</a><hr />';  
       my $editfooter=(<<ENDFOOTER);        my $editfooter=(<<ENDFOOTER);
 <hr />  $initialize
 <a name="editsection" />  <a name="editsection" />
 <form method="post">  <form $form_events method="post" name="xmledit">
 <textarea cols="80" rows="40" name="filecont">$filecontents</textarea>    <div class="LC_edit_problem_editxml_header">
 <br />      <table class="LC_edit_problem_header_title"><tr><td>
 <input type="submit" name="savethisfile" value="Save this file" />          $filename
         </td><td align="right">
           $xml_help
         </td></tr>
       </table>
       <div class="LC_edit_problem_discards">
         <input type="submit" name="discardview" accesskey="d" value="$lt{'dv'}" />
         <input type="submit" name="Undo" accesskey="u" value="$lt{'un'}" />
         $spelllink $htmlerror
       </div>
       <div class="LC_edit_problem_saves">
         <input type="submit" name="savethisfile" accesskey="s" value="$lt{'st'}" />
         <input type="submit" name="viewmode" accesskey="v" value="$lt{'vi'}" />
       </div>
     </div>
     <textarea $textarea_events style="width:100%" cols="80" rows="44" name="filecont" id="filecont">$filecontents</textarea>
     <div id="LC_aftertextarea">
       <br />
       $titledisplay
     </div>
 </form>  </form>
   </body>
 ENDFOOTER  ENDFOOTER
       $result=~s/(\<body[^\>]*\>)/$1$editheader/is;        return ($editfooter,$add_to_onload,$add_to_onresize);;
       $result=~s/(\<\/body\>)/$editfooter/is;  
       return $result;  
 }  }
   
 sub handler {  sub get_target {
   my $request=shift;    my $viewgrades=&Apache::lonnet::allowed('vgr',$env{'request.course.id'});
     if ( $env{'request.state'} eq 'published') {
   my $target='web';      if ( defined($env{'form.grade_target'})
    && ($viewgrades == 'F' )) {
   $Apache::lonxml::debug=0;        return ($env{'form.grade_target'});
       } elsif (defined($env{'form.grade_target'})) {
   if ($ENV{'browser.mathml'}) {        if (($env{'form.grade_target'} eq 'web') ||
     $request->content_type('text/xml');    ($env{'form.grade_target'} eq 'tex') ) {
    return $env{'form.grade_target'}
         } else {
    return 'web';
         }
       } else {
         return 'web';
       }
     } elsif ($env{'request.state'} eq 'construct') {
       if ( defined($env{'form.grade_target'})) {
         return ($env{'form.grade_target'});
       } else {
         return 'web';
       }
   } else {    } else {
     $request->content_type('text/html');      return 'web';
   }    }
     }
   $request->send_http_header;  
     sub handler {
   return OK if $request->header_only;      my $request=shift;
       
       my $target=&get_target();
       
       $Apache::lonxml::debug=$env{'user.debug'};
       
       &Apache::loncommon::content_type($request,'text/html');
       &Apache::loncommon::no_cache($request);
       if ($env{'request.state'} eq 'published') {
    $request->set_last_modified(&Apache::lonnet::metadata($request->uri,
         'lastrevisiondate'));
       }
       $request->send_http_header;
       
       return OK if $request->header_only;
   
   
   my $file=&Apache::lonnet::filelocation("",$request->uri);      my $file=&Apache::lonnet::filelocation("",$request->uri);
       my $filetype;
       if ($file =~ /\.sty$/) {
    $filetype='sty';
       } else {
    $filetype='html';
       }
 #  #
 # Edit action? Save file.  # Edit action? Save file.
 #  #
   unless ($ENV{'request.state'} eq 'published') {      if (!($env{'request.state'} eq 'published')) {
       if ($ENV{'form.savethisfile'}) {   if ($env{'form.savethisfile'} || $env{'form.viewmode'} || $env{'form.Undo'}) {
   &storefile($file,$ENV{'form.filecont'});      my $html_file=&Apache::lonnet::getfile($file);
       }      my $error = &Apache::lonhomework::handle_save_or_undo($request, \$html_file, \$env{'form.filecont'});
   }              if ($env{'form.savethisfile'}) {
   my %mystyle;                  $env{'form.editmode'}='Edit'; #force edit mode
   my $result = '';               }
   my $filecontents=&Apache::lonnet::getfile($file);   }
   if ($filecontents == -1) {      }
     $result=(<<ENDNOTFOUND);      my %mystyle;
 <html>      my $result = '';
 <head>      my $filecontents=&Apache::lonnet::getfile($file);
 <title>File not found</title>      if ($filecontents eq -1) {
 </head>   my $start_page=&Apache::loncommon::start_page('File Error');
 <body bgcolor="#FFFFFF">   my $end_page=&Apache::loncommon::end_page();
 <b>File not found: $file</b>   my $fnf=&mt('File not found');
 </body>   $result=(<<ENDNOTFOUND);
 </html>  $start_page
   <b>$fnf: $file</b>
   $end_page
 ENDNOTFOUND  ENDNOTFOUND
     $filecontents='';          $filecontents='';
   } else {   if ($env{'request.state'} ne 'published') {
     $result = &Apache::lonxml::xmlparse($target,$filecontents,'',%mystyle);      if ($filetype eq 'sty') {
   }   $filecontents=&createnewsty();
       } else {
    $filecontents=&createnewhtml();
       }
       $env{'form.editmode'}='Edit'; #force edit mode
    }
       } else {
    unless ($env{'request.state'} eq 'published') {
       if ($filecontents=~/BEGIN LON-CAPA Internal/) {
    &Apache::lonxml::error(&mt('This file appears to be a rendering of a LON-CAPA resource. If this is correct, this resource will act very oddly and incorrectly.'));
       }
   #
   # we are in construction space, see if edit mode forced
               &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},
       ['editmode']);
    }
    if (!$env{'form.editmode'} || $env{'form.viewmode'} || $env{'form.discardview'}) {
       &Apache::structuretags::reset_problem_globals();
       $result = &Apache::lonxml::xmlparse($request,$target,$filecontents,
    '',%mystyle);
       # .html files may contain <problem> or <Task> need to clean
       # up if it did
       &Apache::structuretags::reset_problem_globals();
       &Apache::lonhomework::finished_parsing();
       &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},
       ['rawmode']);
       if ($env{'form.rawmode'}) { $result = $filecontents; }
       if ($filetype eq 'sty') {
    my $controls =
       ($env{'request.state'} eq 'construct') ? &Apache::londefdef::edit_controls()
                                              : '';
    my %options = ('bgcolor' => '#FFFFFF');
    $result = 
       &Apache::loncommon::start_page(undef,undef,\%options).
       $controls.
       $result.
       &Apache::loncommon::end_page();
       }
    }
       }
   
 #  #
 # Edit action? Insert editing commands  # Edit action? Insert editing commands
 #  #
   unless ($ENV{'request.state'} eq 'published') {      unless ($env{'request.state'} eq 'published') {
       $result=&inserteditinfo($result,$filecontents);   if ($env{'form.editmode'} && (!($env{'form.viewmode'})) && (!($env{'form.discardview'})))
   }   {
       my $displayfile=$request->uri;
   $request->print($result);      $displayfile=~s/^\/[^\/]*//;
   
       my ($edit_info, $add_to_onload, $add_to_onresize)=
    &inserteditinfo($filecontents,$filetype,$displayfile);
   
       my %options = 
    ('add_entries' =>
                      {'onresize' => $add_to_onresize,
       'onload'   => $add_to_onload,   });
   
       if ($env{'environment.remote'} ne 'off') {
    $options{'bgcolor'}   = '#FFFFFF';
    $options{'only_body'} = 1;
       }
       my $js =
    &Apache::edit::js_change_detection().
    &Apache::loncommon::resize_textarea_js();
       my $start_page = &Apache::loncommon::start_page(undef,$js,
       \%options);
       $result=$start_page.
    &Apache::lonxml::message_location().
    $edit_info.
    &Apache::loncommon::end_page();
    }
       }
       if ($filetype eq 'html') { &writeallows($request->uri); }
       
       &Apache::lonxml::add_messages(\$result);
       $request->print($result);
       
       return OK;
   }
   
   writeallows($request->uri);  sub display_title {
   return OK;      my $result;
       if ($env{'request.state'} eq 'construct') {
    my $title=&Apache::lonnet::gettitle();
    if (!defined($title) || $title eq '') {
       $title = $env{'request.filename'};
       $title = substr($title, rindex($title, '/') + 1);
    }
           $result = "<script type='text/javascript'>top.document.title = '$title - LON-CAPA "
                     .&mt('Construction Space')."';</script>";
       }
       return $result;
 }  }
    
 sub debug {  sub debug {
   if ($Apache::lonxml::debug eq 1) {      if ($Apache::lonxml::debug eq "1") {
     print "DEBUG:".$_[0]."<br />\n";   $|=1;
   }   my $request=$Apache::lonxml::request;
    if (!$request) {
       eval { $request=Apache->request; };
    }
    if (!$request) {
       eval { $request=Apache2::RequestUtil->request; };
    }
    $request->print('<font size="-2"><pre>DEBUG:'.&HTML::Entities::encode($_[0],'<>&"')."</pre></font>\n");
    #&Apache::lonnet::logthis($_[0]);
       }
   }
   
   sub show_error_warn_msg {
       if ($env{'request.filename'} eq '/home/httpd/html/res/lib/templates/simpleproblem.problem' &&
    &Apache::lonnet::allowed('mdc',$env{'request.course.id'})) {
    return 1;
       }
       return (($Apache::lonxml::debug eq 1) ||
       ($env{'request.state'} eq 'construct') ||
       ($Apache::lonhomework::browse eq 'F'
        &&
        $env{'form.show_errors'} eq 'on'));
 }  }
   
 sub error {  sub error {
   if (($Apache::lonxml::debug eq 1) || ($ENV{'request.state'} eq 'construct') ) {      my @errors = @_;
     print "<b>ERROR:</b>".$_[0]."<br />\n";  
   } else {      $errorcount++;
     print "<b>An Error occured while processing this resource. The instructor has been notified.</b> <br />";  
     #notify author      if (defined($Apache::inputtags::part)) {
     &Apache::lonmsg::author_res_msg($ENV{'request.filename'},$_[0]);   if ( @Apache::inputtags::response ) {
     #notify course      push(@errors,
     if ( $ENV{'request.course.id'} ) {   &mt("This error occurred while processing response [_1] in part [_2]",
       my $users=$ENV{'course.'.$ENV{'request.course.id'}.'.comment.email'};       $Apache::inputtags::response[-1],
       foreach my $user (split /\,/, $users) {       $Apache::inputtags::part));
  ($user,my $domain) = split /:/, $user;   } else {
  &Apache::lonmsg::user_normal_msg($user,$domain,"Error in $ENV{'request.filename'}",$_[0]);      push(@errors,
       }   &mt("This error occurred while processing part [_1]",
        $Apache::inputtags::part));
    }
     }      }
   
     #FIXME probably shouldn't have me get everything forever.      if ( &show_error_warn_msg() ) {
     &Apache::lonmsg::user_normal_msg('albertel','msu',"Error in $ENV{'request.filename'}",$_[0]);   # If printing in construction space, put the error inside <pre></pre>
     #&Apache::lonmsg::user_normal_msg('albertel','103',"Error in $ENV{'request.filename'}",$_[0]);   push(@Apache::lonxml::error_messages,
   }       $Apache::lonxml::warnings_error_header.
                "<b>".&mt('ERROR:')."</b>".join("<br />\n",@errors)."<br />\n");
    $Apache::lonxml::warnings_error_header='';
       } else {
    my $errormsg;
    my ($symb)=&Apache::lonnet::symbread();
    if ( !$symb ) {
       #public or browsers
       $errormsg=&mt("An error occured while processing this resource. The author has been notified.");
    }
    my $host=$Apache::lonnet::perlvar{'lonHostID'};
    push(@errors,
           &mt("The error occurred on host [_1]",
                "<tt>$host</tt>"));
   
    my $msg = join('<br />', @errors);
   
    #notify author
    &Apache::lonmsg::author_res_msg($env{'request.filename'},$msg);
    #notify course
    if ( $symb && $env{'request.course.id'} ) {
       my $cnum=$env{'course.'.$env{'request.course.id'}.'.num'};
       my $cdom=$env{'course.'.$env{'request.course.id'}.'.domain'};
       my (undef,%users)=&Apache::lonmsg::decide_receiver(undef,0,1,1,1);
       my $declutter=&Apache::lonnet::declutter($env{'request.filename'});
               my $baseurl = &Apache::lonnet::clutter($declutter);
       my @userlist;
       foreach (keys %users) {
    my ($user,$domain) = split(/:/, $_);
    push(@userlist,"$user\@$domain");
    my $key=$declutter.'_'.$user.'_'.$domain;
    my %lastnotified=&Apache::lonnet::get('nohist_xmlerrornotifications',
         [$key],
         $cdom,$cnum);
    my $now=time;
    if ($now-$lastnotified{$key}>86400) {
                       my $title = &Apache::lonnet::gettitle($symb);
                       my $sentmessage;
       &Apache::lonmsg::user_normal_msg($user,$domain,
           "Error [$title]",$msg,'',$baseurl,'','',
                           \$sentmessage,$symb,$title,1);
       &Apache::lonnet::put('nohist_xmlerrornotifications',
    {$key => $now},
    $cdom,$cnum);
    }
       }
       if ($env{'request.role.adv'}) {
    $errormsg=&mt("An error occured while processing this resource. The course personnel ([_1]) and the author have been notified.",join(', ',@userlist));
       } else {
    $errormsg=&mt("An error occured while processing this resource. The instructor has been notified.");
       }
    }
    push(@Apache::lonxml::error_messages,"<b>$errormsg</b> <br />");
       }
 }  }
   
 sub warning {  sub warning {
   if ($ENV{'request.state'} eq 'construct') {      $warningcount++;
     print "<b>W</b>ARNING<b>:</b>".$_[0]."<br />\n";    
   }      if ($env{'form.grade_target'} ne 'tex') {
    if ( &show_error_warn_msg() ) {
       push(@Apache::lonxml::warning_messages,
    $Apache::lonxml::warnings_error_header.
    "<b>W</b>ARNING<b>:</b>".join('<br />',@_)."<br />\n");
       $Apache::lonxml::warnings_error_header='';
    }
       }
   }
   
   sub info {
       if ($env{'form.grade_target'} ne 'tex' 
    && $env{'request.state'} eq 'construct') {
    push(@Apache::lonxml::info_messages,join('<br />',@_)."<br />\n");
       }
   }
   
   sub message_location {
       return '__LONCAPA_INTERNAL_MESSAGE_LOCATION__';
   }
   
   sub add_messages {
       my ($msg)=@_;
       my $result=join(' ',
       @Apache::lonxml::info_messages,
       @Apache::lonxml::error_messages,
       @Apache::lonxml::warning_messages);
       undef(@Apache::lonxml::info_messages);
       undef(@Apache::lonxml::error_messages);
       undef(@Apache::lonxml::warning_messages);
       $$msg=~s/__LONCAPA_INTERNAL_MESSAGE_LOCATION__/$result/;
       $$msg=~s/__LONCAPA_INTERNAL_MESSAGE_LOCATION__//g;
 }  }
   
 sub get_param {  sub get_param {
   my ($param,$parstack,$safeeval,$context) = @_;      my ($param,$parstack,$safeeval,$context,$case_insensitive) = @_;
       if ( ! $context ) { $context = -1; }
       my $args ='';
       if ( $#$parstack > (-2-$context) ) { $args=$$parstack[$context]; }
       if ( ! $Apache::lonxml::usestyle ) {
    $args=$Apache::lonxml::style_values.$args;
       }
       if ( ! $args ) { return undef; }
       if ( $case_insensitive ) {
    if ($args =~ s/(my (?:.*))(\$\Q$param\E[,\)])/$1.lc($2)/ei) {
       return &Apache::run::run("{$args;".'return $'.$param.'}',
                                        $safeeval); #'
    } else {
       return undef;
    }
       } else {
    if ( $args =~ /my .*\$\Q$param\E[,\)]/ ) {
       return &Apache::run::run("{$args;".'return $'.$param.'}',
                                        $safeeval); #'
    } else {
       return undef;
    }
       }
   }
   
   sub get_param_var {
     my ($param,$parstack,$safeeval,$context,$case_insensitive) = @_;
   if ( ! $context ) { $context = -1; }    if ( ! $context ) { $context = -1; }
   my $args ='';    my $args ='';
   if ( $#$parstack > (-2-$context) ) { $args=$$parstack[$context]; }    if ( $#$parstack > (-2-$context) ) { $args=$$parstack[$context]; }
   return &Apache::run::run("{$args;".'return $'.$param.'}',$safeeval); #'    if ( ! $Apache::lonxml::usestyle ) {
         $args=$Apache::lonxml::style_values.$args;
     }
     &Apache::lonxml::debug("Args are $args param is $param");
     if ($case_insensitive) {
         if (! ($args=~s/(my (?:.*))(\$\Q$param\E[,\)])/$1.lc($2)/ei)) {
     return undef;
         }
     } elsif ( $args !~ /my .*\$\Q$param\E[,\)]/ ) { return undef; }
     my $value=&Apache::run::run("{$args;".'return $'.$param.'}',$safeeval); #'
     &Apache::lonxml::debug("first run is $value");
     if ($value =~ /^[\$\@\%][a-zA-Z_]\w*$/) {
         &Apache::lonxml::debug("doing second");
         my @result=&Apache::run::run("return $value",$safeeval,1);
         if (!defined($result[0])) {
     return $value
         } else {
     if (wantarray) { return @result; } else { return $result[0]; }
         }
     } else {
       return $value;
     }
   }
   
   sub register_insert_xml {
       my $parser = HTML::LCParser->new($Apache::lonnet::perlvar{'lonTabDir'}
        .'/insertlist.xml');
       my ($tagnum,$in_help)=(0,0);
       my @alltags;
       my $tag;
       while (my $token = $parser->get_token()) {
    if ($token->[0] eq 'S') {
       my $key;
       if      ($token->[1] eq 'tag') {
    $tag = $token->[2]{'name'};
    $insertlist{"$tagnum.tag"} = $tag;
    $insertlist{"$tag.num"}   = $tagnum;
    push(@alltags,$tag);
       } elsif ($in_help && $token->[1] eq 'file') {
    $key = $tag.'.helpfile';
       } elsif ($in_help && $token->[1] eq 'description') {
    $key = $tag.'.helpdesc';
       } elsif ($token->[1] eq 'description' ||
        $token->[1] eq 'color'       ||
        $token->[1] eq 'show'          ) {
    $key = $tag.'.'.$token->[1];
       } elsif ($token->[1] eq 'insert_sub') {
    $key = $tag.'.function';
       } elsif ($token->[1] eq 'help') {
    $in_help=1;
       } elsif ($token->[1] eq 'allow') {
    $key = $tag.'.allow';
       }
       if (defined($key)) {
    $insertlist{$key} = $parser->get_text();
    $insertlist{$key} =~ s/(^\s*|\s*$ )//gx;
       }
    } elsif ($token->[0] eq 'E') {
       if      ($token->[1] eq 'tag') {
    undef($tag);
    $tagnum++;
       } elsif ($token->[1] eq 'help') {
    undef($in_help);
       }
    }
       }
       
       # parse the allows and ignore tags set to <show>no</show>
       foreach my $tag (@alltags) {
           next if (!exists($insertlist{"$tag.allow"}));
    my $allow =  $insertlist{"$tag.allow"};
           foreach my $element (split(',',$allow)) {
       $element =~ s/(^\s*|\s*$ )//gx;
       if (!exists($insertlist{"$element.show"})
                   || $insertlist{"$element.show"} ne 'no') {
    push(@{ $insertlist{$tag.'.which'} },$element);
       }
    }
       }
 }  }
   
 sub register_insert {  sub register_insert {
   my @data = split /\n/, &Apache::lonnet::getfile('/home/httpd/lonTabs/insertlist.tab');      return &register_insert_xml(@_);
   my $i;  #    &dump_insertlist('2');
   my $tagnum=0;  }
   my @order;  
   for ($i=0;$i < $#data; $i++) {  sub dump_insertlist {
     my $line = $data[$i];      my ($ext) = @_;
     if ( $line =~ /^\#/ || $line =~ /^\s*\n/) { next; }      open(XML,">/tmp/insertlist.xml.$ext");
     if ( $line =~ /TABLE/ ) { last; }      print XML ("<insertlist>");
     my ($tag,$descrip,$function,$show) = split(/,/, $line);      my $i=0;
     $insertlist{"$tagnum.tag"} = $tag;  
     $insertlist{"$tagnum.description"} = $descrip;      while (exists($insertlist{"$i.tag"})) {
     $insertlist{"$tagnum.function"} = $function;   my $tag = $insertlist{"$i.tag"};
     $insertlist{"$tagnum.show"}= $show;   print XML ("
     $tagnum++;  \t<tag name=\"$tag\">");
   }   if (defined($insertlist{"$tag.description"})) {
   $i++; #skipping TABLE line      print XML ("
   $tagnum = 0;  \t\t<description>".$insertlist{"$tag.description"}."</description>");
   for (;$i < $#data;$i++) {  
     my $line = $data[$i];  
     my ($mnemonic,@which) = split(/ +/,$line);  
     my $tag = $insertlist{"$tagnum.tag"};  
     for (my $j=0;$j <$#which;$j++) {  
       if ( $which[$j] eq 'Y' ) {  
  if ($insertlist{"$j.show"} ne 'no') {  
   push(@{ $insertlist{"$tag.which"} },$j);  
  }   }
       }   if (defined($insertlist{"$tag.color"})) {
       print XML ("
   \t\t<color>".$insertlist{"$tag.color"}."</color>");
    }
    if (defined($insertlist{"$tag.function"})) {
       print XML ("
   \t\t<insert_sub>".$insertlist{"$tag.function"}."</insert_sub>");
    }
    if (defined($insertlist{"$tag.show"})
       && $insertlist{"$tag.show"} ne 'yes') {
       print XML ("
   \t\t<show>".$insertlist{"$tag.show"}."</show>");
    }
    if (defined($insertlist{"$tag.helpfile"})) {
       print XML ("
   \t\t<help>
   \t\t\t<file>".$insertlist{"$tag.helpfile"}."</file>");
       if ($insertlist{"$tag.helpdesc"} ne '') {
    print XML ("
   \t\t\t<description>".$insertlist{"$tag.helpdesc"}."</description>");
       }
       print XML ("
   \t\t</help>");
    }
    if (defined($insertlist{"$tag.which"})) {
       print XML ("
   \t\t<allow>".join(',',sort(@{ $insertlist{"$tag.which"} }))."</allow>");
    }
    print XML ("
   \t</tag>");
    $i++;
     }      }
     $tagnum++;      print XML ("\n</insertlist>\n");
   }      close(XML);
   }
   
   sub description {
       my ($token)=@_;
       my $tag = &get_tag($token);
       return $insertlist{$tag.'.description'};
   }
   
   # Returns a list containing the help file, and the description
   sub helpinfo {
       my ($token)=@_;
       my $tag = &get_tag($token);
       return ($insertlist{$tag.'.helpfile'}, $insertlist{$tag.'.helpdesc'});
 }  }
   
   sub get_tag {
       my ($token)=@_;
       my $tagnum;
       my $tag=$token->[1];
       foreach my $namespace (reverse(@Apache::lonxml::namespace)) {
    my $testtag = $namespace.'::'.$tag;
    $tagnum = $insertlist{"$testtag.num"};
    last if (defined($tagnum));
       }
       if (!defined($tagnum)) {
    $tagnum = $Apache::lonxml::insertlist{"$tag.num"};
       }
       return $insertlist{"$tagnum.tag"};
   }
   
 1;  1;
 __END__  __END__
   

Removed from v.1.87  
changed lines
  Added in v.1.477


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