--- loncom/xml/lonxml.pm 2005/10/18 21:49:37 1.385 +++ loncom/xml/lonxml.pm 2006/11/10 19:41:34 1.425 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # XML Parser Module # -# $Id: lonxml.pm,v 1.385 2005/10/18 21:49:37 albertel Exp $ +# $Id: lonxml.pm,v 1.425 2006/11/10 19:41:34 albertel Exp $ # # Copyright Michigan State University Board of Trustees # @@ -40,7 +40,7 @@ package Apache::lonxml; use vars -qw(@pwd @outputstack $redirection $import @extlinks $metamode $evaluate %insertlist @namespace $errorcount $warningcount @htmlareafields); +qw(@pwd @outputstack $redirection $import @extlinks $metamode $evaluate %insertlist @namespace $errorcount $warningcount); use strict; use HTML::LCParser(); use HTML::TreeBuilder(); @@ -52,6 +52,7 @@ use Math::Random(); use Opcode(); use POSIX qw(strftime); use Time::HiRes qw( gettimeofday tv_interval ); +use Symbol(); sub register { my ($space,@taglist) = @_; @@ -122,9 +123,6 @@ $evaluate = 1; # stores the list of active tag namespaces @namespace=(); -# has the dynamic menu been updated to know about this resource -$Apache::lonxml::registered=0; - # a pointer the the Apache request object $Apache::lonxml::request=''; @@ -148,26 +146,17 @@ $Apache::lonxml::post_evaluate=1; #a header message to emit in the case of any generated warning or errors $Apache::lonxml::warnings_error_header=''; -sub xmlbegin { - my ($style)=@_; - my $output=''; - @htmlareafields=(); - if ($env{'browser.mathml'}) { - $output='' - #.''."\n" -# .'] >' - .'' - .''; - } else { - $output=''; - } - if ($style eq 'encode') { - $output=&HTML::Entities::encode($output,'<>&"'); - } - return $output; +# 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 { @@ -190,9 +179,9 @@ sub xmlend { $discussion.='\keephidden{ENDOFPROBLEM}\vskip 0.5mm\noindent\makebox[\textwidth/$number_of_columns][b]{\hrulefill}\end{document}'; &Apache::lonxml::newparser($parser,\$discussion,''); return ''; - } else { - return $discussion.&Apache::loncommon::endbodytag(); } + + return $discussion; } sub tokeninputfield { @@ -262,7 +251,7 @@ sub printtokenheader { my ($target,$token,$tsymb,$tcrsid,$tudom,$tuname)=@_; unless ($token) { return ''; } - my ($symb,$courseid,$domain,$name) = &Apache::lonxml::whichuser(); + my ($symb,$courseid,$domain,$name) = &Apache::lonnet::whichuser(); unless ($tsymb) { $tsymb=$symb; } @@ -272,13 +261,7 @@ sub printtokenheader { $tcrsid=$courseid; } - my %reply=&Apache::lonnet::get('environment', - ['firstname','middlename','lastname','generation'], - $tudom,$tuname); - my $plainname=$reply{'firstname'}.' '. - $reply{'middlename'}.' '. - $reply{'lastname'}.' '. - $reply{'generation'}; + my $plainname=&Apache::loncommon::plainname($tuname,$tudom); if ($target eq 'web') { my %idhash=&Apache::lonnet::idrget($tudom,($tuname)); @@ -296,18 +279,6 @@ sub printtokenheader { } } -sub fontsettings { - my $headerstring=''; - if (($env{'browser.os'} eq 'mac') && (!$env{'browser.mathml'})) { - $headerstring.= - ''; - } elsif (!$env{'browser.mathml'} && $env{'browser.unicode'}) { - $headerstring.= - ''; - } - return $headerstring; -} - sub printalltags { my $temp; foreach $temp (sort keys %Apache::lonxml::alltags) { @@ -369,13 +340,21 @@ sub xmlparse { &initdepth(); &init_alarm(); my $finaloutput = &inner_xmlparse($target,\@stack,\@parstack,\@pars, - $safeeval,\%style_for_target); + $safeeval,\%style_for_target,1); + if (@stack) { + &warning("At end of file some tags were still left unclosed, ". + '<'.join('>, <',reverse(@stack)). + '>'); + } if ($env{'request.uri'}) { &writeallows($env{'request.uri'}); } &do_registered_ssi(); if ($Apache::lonxml::counter_changed) { &store_counter() } + + &clean_safespace($safeeval); + if ($env{'form.return_only_error_and_warning_counts'}) { return "$errorcount:$warningcount"; } @@ -384,14 +363,22 @@ sub xmlparse { 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/(\\|_|\^)/ /g; + $string =~ s/\\/\$\\backslash\$/g; # \ -> $\backslash$ per LaTex line by line pg 10. $string =~ s/(\$|%|\{|\})/\\$1/g; - $string =~ s/_/ /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; @@ -413,11 +400,12 @@ sub latex_special_symbols { } sub inner_xmlparse { - my ($target,$stack,$parstack,$pars,$safeeval,$style_for_target)=@_; + my ($target,$stack,$parstack,$pars,$safeeval,$style_for_target,$start)=@_; my $finaloutput = ''; my $result; my $token; my $dontpop=0; + my $startredirection = $Apache::lonxml::redirection; while ( $#$pars > -1 ) { while ($token = $$pars['-1']->get_token) { if (($token->[0] eq 'T') || ($token->[0] eq 'C') ) { @@ -527,7 +515,12 @@ sub inner_xmlparse { # $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); } @@ -613,8 +606,6 @@ sub callsub { sub setup_globals { my ($request,$target)=@_; $Apache::lonxml::request=$request; - $Apache::lonxml::registered = 0; - @Apache::lonxml::htmlareafields=(); $errorcount=0; $warningcount=0; $Apache::lonxml::default_homework_loaded=0; @@ -625,6 +616,7 @@ sub setup_globals { @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; @@ -636,7 +628,7 @@ sub setup_globals { $Apache::lonxml::evaluate = 1; $Apache::lonxml::import = 1; } elsif ($target eq 'grade') { - &startredirection; + &startredirection(); #ended in inner_xmlparse on exit $Apache::lonxml::metamode = 0; $Apache::lonxml::evaluate = 1; $Apache::lonxml::import = 1; @@ -785,8 +777,11 @@ sub init_safespace { $safehole->wrap(\&Math::Random::random_set_seed,$safeeval,'&random_set_seed'); $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 # $safeeval->deny(":base_orig"); $safeeval->permit("require"); @@ -795,10 +790,38 @@ sub init_safespace { &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 "::") + { + 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::lonxml::whichuser(); + 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"); @@ -918,6 +941,16 @@ sub decreasedepth { #print "
e $Apache::lonxml::depth : $Apache::lonxml::olddepth : $token->[1] : $curdepth\n"; } +sub get_id { + my ($parstack,$safeeval)=@_; + my $id= &Apache::lonxml::get_param('id',$parstack,$safeeval); + if ($env{'request.state'} eq 'construct' && $id =~ /(\.|_)/) { + &error(&mt("IDs are not allowed to contain "_" or "."")); + } + if ($id =~ /^\s*$/) { $id = $Apache::lonxml::curdepth; } + return $id; +} + sub get_all_text_unbalanced { #there is a copy of this in lonpublisher.pm my($tag,$pars)= @_; @@ -926,7 +959,7 @@ sub get_all_text_unbalanced { $tag='<'.$tag.'>'; while ($token = $$pars[-1]->get_token) { if (($token->[0] eq 'T')||($token->[0] eq 'C')||($token->[0] eq 'D')) { - if ($token->[2]) { + if ($token->[0] eq 'T' && $token->[2]) { $result.='[1].']]>'; } else { $result.=$token->[1]; @@ -961,7 +994,10 @@ sub increment_counter { } sub init_counter { - if (defined($env{'form.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 { @@ -972,9 +1008,36 @@ sub init_counter { 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'}; + } +} + sub get_all_text { my($tag,$pars,$style)= @_; my $gotfullstack=1; @@ -1094,19 +1157,23 @@ sub newparser { } sub parstring { - my ($token) = @_; - my $temp=''; - foreach (@{$token->[3]}) { - unless ($_=~/\W/) { - my $val=$token->[2]->{$_}; - $val =~ s/([\%\@\\\"\'])/\\$1/g; - $val =~ s/(\$[^{a-zA-Z_])/\\$1/g; - $val =~ s/(\$)$/\\$1/; - #if ($val =~ m/^[\%\@]/) { $val="\\".$val; } - $temp .= "my \$$_=\"$val\";"; - } - } - return $temp; + my ($token) = @_; + my (@vars,@values); + foreach my $attr (@{$token->[3]}) { + if ($attr!~/\W/) { + my $val=$token->[2]->{$attr}; + $val =~ s/([\%\@\\\"\'])/\\$1/g; + $val =~ s/(\$[^\{a-zA-Z_])/\\$1/g; + $val =~ s/(\$)$/\\$1/; + #if ($val =~ m/^[\%\@]/) { $val="\\".$val; } + push(@vars,"\$$attr"); + push(@values,"\"$val\""); + } + } + my $var_init = + (@vars) ? 'my ('.join(',',@vars).') = ('.join(',',@values).');' + : ''; + return $var_init; } sub extlink { @@ -1234,8 +1301,7 @@ sub inserteditinfo { my $initialize=''; if ($filetype eq 'html') { my $addbuttons=&Apache::lonhtmlcommon::htmlareaaddbuttons(); - $initialize=&Apache::lonhtmlcommon::htmlareaheaders(). - &Apache::lonhtmlcommon::spellheader(); + $initialize=&Apache::lonhtmlcommon::spellheader(); if (!&Apache::lonhtmlcommon::htmlareablocked() && &Apache::lonhtmlcommon::htmlareabrowser()) { $initialize.=(< BUTTONS $buttons.=&Apache::lonhtmlcommon::spelllink('xmledit','filecont'); - $buttons.=&Apache::lonhtmlcommon::htmlareaselectactive('filecont'); my $editfooter=(< @@ -1365,17 +1430,13 @@ sub handler { my $result = ''; my $filecontents=&Apache::lonnet::getfile($file); if ($filecontents eq -1) { - my $bodytag=&Apache::loncommon::bodytag('File Error'); + my $start_page=&Apache::loncommon::start_page('File Error'); + my $end_page=&Apache::loncommon::end_page(); my $fnf=&mt('File not found'); $result=(< - -$fnf - -$bodytag +$start_page $fnf: $file - - +$end_page ENDNOTFOUND $filecontents=''; if ($env{'request.state'} ne 'published') { @@ -1402,7 +1463,7 @@ ENDNOTFOUND undef($Apache::lonhomework::parsing_a_task); &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'}, ['rawmode']); - if ($env{'rawmode'}) { $result = $filecontents; } + if ($env{'form.rawmode'}) { $result = $filecontents; } } } @@ -1413,18 +1474,20 @@ ENDNOTFOUND if ($env{'form.editmode'} && (!($env{'form.viewmode'}))) { my $displayfile=$request->uri; $displayfile=~s/^\/[^\/]*//; - my $bodytag=''; - if ($env{'environment.remote'} eq 'off') { - $bodytag=&Apache::loncommon::bodytag(); + my %options = (); + if ($env{'environment.remote'} ne 'off') { + $options{'bgcolor'} = '#FFFFFF'; } - $result=''.$bodytag. + my $start_page = &Apache::loncommon::start_page(undef,undef, + \%options); + $result=$start_page. &Apache::lonxml::message_location().'

'. $displayfile. - '

'; + ''.&Apache::loncommon::end_page(); $result=&inserteditinfo($result,$filecontents,$filetype); } } - if ($filetype eq 'html') { writeallows($request->uri); } + if ($filetype eq 'html') { &writeallows($request->uri); } &Apache::lonxml::add_messages(\$result); @@ -1450,7 +1513,12 @@ sub debug { if ($Apache::lonxml::debug eq "1") { $|=1; my $request=$Apache::lonxml::request; - if (!$request) { $request=Apache->request; } + if (!$request) { + eval { $request=Apache->request; }; + } + if (!$request) { + eval { $request=Apache2::RequestUtil->request; }; + } $request->print('
DEBUG:'.&HTML::Entities::encode($_[0],'<>&"')."
\n"); #&Apache::lonnet::logthis($_[0]); } @@ -1482,9 +1550,11 @@ sub error { 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'}; + my $msg = join('
',(@_,"The error occurred on host $host")); #notify author - &Apache::lonmsg::author_res_msg($env{'request.filename'},join('
',@_)); + &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'}; @@ -1502,7 +1572,7 @@ sub error { my $now=time; if ($now-$lastnotified{$key}>86400) { &Apache::lonmsg::user_normal_msg($user,$domain, - "Error [$declutter]",join('
',@_)); + "Error [$declutter]",$msg); &Apache::lonnet::put('nohist_xmlerrornotifications', {$key => $now}, $cdom,$cnum); @@ -1565,14 +1635,14 @@ sub get_param { } if ( ! $args ) { return undef; } if ( $case_insensitive ) { - if ($args =~ s/(my \$)(\Q$param\E)(=\")/$1.lc($2).$3/ei) { + 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=\"/ ) { + if ( $args =~ /my .*\$\Q$param\E[,\)]/ ) { return &Apache::run::run("{$args;".'return $'.$param.'}', $safeeval); #' } else { @@ -1591,10 +1661,10 @@ sub get_param_var { } &Apache::lonxml::debug("Args are $args param is $param"); if ($case_insensitive) { - if (! ($args=~s/(my \$)(\Q$param\E)(=\")/$1.lc($2).$3/ei)) { + if (! ($args=~s/(my (?:.*))(\$\Q$param\E[,\)])/$1.lc($2)/ei)) { return undef; } - } elsif ( $args !~ /my \$\Q$param\E=\"/ ) { 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*$/) { @@ -1677,48 +1747,6 @@ sub helpinfo { return ($insertlist{$tagnum.'.helpfile'}, $insertlist{$tagnum.'.helpdesc'}); } -# ----------------------------------------------------------------- whichuser -# returns a list of $symb, $courseid, $domain, $name that is correct for -# calls to lonnet functions for this setup. -# - looks for form.grade_ parameters -sub whichuser { - my ($passedsymb)=@_; - my ($symb,$courseid,$domain,$name,$publicuser); - if (defined($env{'form.grade_symb'})) { - my ($tmp_courseid)= - &Apache::loncommon::get_env_multiple('form.grade_courseid'); - my $allowed=&Apache::lonnet::allowed('vgr',$tmp_courseid); - if (!$allowed && - exists($env{'request.course.sec'}) && - $env{'request.course.sec'} !~ /^\s*$/) { - $allowed=&Apache::lonnet::allowed('vgr',$tmp_courseid. - '/'.$env{'request.course.sec'}); - } - if ($allowed) { - ($symb)=&Apache::loncommon::get_env_multiple('form.grade_symb'); - $courseid=$tmp_courseid; - ($domain)=&Apache::loncommon::get_env_multiple('form.grade_domain'); - ($name)=&Apache::loncommon::get_env_multiple('form.grade_username'); - return ($symb,$courseid,$domain,$name,$publicuser); - } - } - if (!$passedsymb) { - $symb=&Apache::lonnet::symbread(); - } else { - $symb=$passedsymb; - } - $courseid=$env{'request.course.id'}; - $domain=$env{'user.domain'}; - $name=$env{'user.name'}; - if ($name eq 'public' && $domain eq 'public') { - if (!defined($env{'form.username'})) { - $env{'form.username'}.=time.rand(10000000); - } - $name.=$env{'form.username'}; - } - return ($symb,$courseid,$domain,$name,$publicuser); -} - 1; __END__