--- loncom/xml/lonxml.pm 2006/04/18 20:43:47 1.407
+++ loncom/xml/lonxml.pm 2007/09/12 10:58:18 1.460
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# XML Parser Module
#
-# $Id: lonxml.pm,v 1.407 2006/04/18 20:43:47 albertel Exp $
+# $Id: lonxml.pm,v 1.460 2007/09/12 10:58:18 foxr Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -40,8 +40,9 @@
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 LONCAPA;
use HTML::LCParser();
use HTML::TreeBuilder();
use HTML::Entities();
@@ -88,6 +89,7 @@ use Apache::loncommon();
use Apache::lonfeedback();
use Apache::lonmsg();
use Apache::loncacc();
+use Apache::lonmaxima();
use Apache::lonlocal;
#================================================== Main subroutine: xmlparse
@@ -123,8 +125,8 @@ $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;
+# stores all Scrit Vars displays for later showing
+my @script_var_displays=();
# a pointer the the Apache request object
$Apache::lonxml::request='';
@@ -133,6 +135,13 @@ $Apache::lonxml::request='';
$Apache::lonxml::counter=1;
$Apache::lonxml::counter_changed=0;
+# A count of bubble lines needed for a set.. and a check on
+# whether or not it is ever used too:
+
+$Apache::lonxml::bubble_line_counter = 1;
+$Apache::lonxml::bubble_line_counter_changed = 0;
+
+
#internal check on whether to look at style defs
$Apache::lonxml::usestyle=1;
@@ -162,28 +171,6 @@ sub disable_LaTeX_substitutions {
$Apache::lonxml::substitute_LaTeX_symbols = 0;
}
-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;
-}
-
sub xmlend {
my ($target,$parser)=@_;
my $mode='xml';
@@ -276,7 +263,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;
}
@@ -304,18 +291,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) {
@@ -351,13 +326,14 @@ sub xmlparse {
}
}
}
- } elsif ($env{'construct.style'} && ($env{'request.state'} eq 'construct')) {
+ } elsif ($env{'construct.style'}
+ && ($env{'request.state'} eq 'construct')) {
my $location=&Apache::lonnet::filelocation('',$env{'construct.style'});
my $styletext=&Apache::lonnet::getfile($location);
- if ($styletext ne '-1') {
- %style_for_target = (%style_for_target,
- &Apache::style::styleparser($target,$styletext));
- }
+ if ($styletext ne '-1') {
+ %style_for_target = (%style_for_target,
+ &Apache::style::styleparser($target,$styletext));
+ }
}
#&printalltags();
my @pars = ();
@@ -379,14 +355,27 @@ sub xmlparse {
my $finaloutput = &inner_xmlparse($target,\@stack,\@parstack,\@pars,
$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() }
+ if ($Apache::lonxml::bubble_line_counter_changed) {
+ &store_bubble_counter();
+ }
&clean_safespace($safeeval);
+ if (@script_var_displays) {
+ $finaloutput .= join('',@script_var_displays);
+ undef(@script_var_displays);
+ }
+
if ($env{'form.return_only_error_and_warning_counts'}) {
return "$errorcount:$warningcount";
}
@@ -403,13 +392,14 @@ sub latex_special_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;
@@ -580,7 +570,6 @@ sub callsub {
}
my $deleted=0;
- $Apache::lonxml::curdepth=join('_',@Apache::lonxml::depthcounter);
if (($token->[0] eq 'S') && ($target eq 'modified')) {
$deleted=&Apache::edit::handle_delete($space,$target,$token,$tagstack,
$parstack,$parser,$safeeval,
@@ -616,17 +605,23 @@ sub callsub {
} elsif ($token->[0] eq 'E') {
$currentstring = &Apache::edit::tag_end($target,$token);
}
- } elsif ($target eq 'modified') {
+ }
+ }
+ 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 = $token->[4];
- $currentstring.=&Apache::edit::handle_insert();
+ $currentstring.=&Apache::edit::handle_insert();
} elsif ($token->[0] eq 'E') {
- $currentstring = $token->[2];
- $currentstring.=&Apache::edit::handle_insertafter($token->[1]);
- } else {
- $currentstring = $token->[2];
+ $currentstring.=&Apache::edit::handle_insertafter($token->[1]);
}
- }
}
}
use strict 'refs';
@@ -637,15 +632,15 @@ 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;
$Apache::lonxml::usestyle=1;
&init_counter();
+ &init_bubble_counter();
@Apache::lonxml::pwd=();
@Apache::lonxml::extlinks=();
+ @script_var_displays=();
@Apache::lonxml::ssi_info=();
$Apache::lonxml::post_evaluate=1;
$Apache::lonxml::warnings_error_header='';
@@ -707,6 +702,14 @@ sub init_safespace {
'&chem_standard_order');
$safehole->wrap(\&Apache::response::check_status,$safeeval,'&check_status');
+ $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::acos,$safeeval,'&acos');
$safehole->wrap(\&Math::Cephes::atan,$safeeval,'&atan');
@@ -808,10 +811,14 @@ sub init_safespace {
$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
# $safeeval->deny(":base_orig");
$safeeval->permit("require");
@@ -851,7 +858,7 @@ sub delete_package_recurse {
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");
@@ -913,6 +920,9 @@ sub endredirection {
}
pop @Apache::lonxml::outputstack;
}
+sub in_redirection {
+ return ($Apache::lonxml::redirection > 0)
+}
sub end_tag {
my ($tagstack,$parstack,$token)=@_;
@@ -923,59 +933,58 @@ sub end_tag {
sub initdepth {
@Apache::lonxml::depthcounter=();
- $Apache::lonxml::depth=-1;
- $Apache::lonxml::olddepth=-1;
+ undef($Apache::lonxml::last_depth_count);
}
+
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 {
my ($token) = @_;
- $Apache::lonxml::depth++;
- $Apache::lonxml::depthcounter[$Apache::lonxml::depth]++;
- if ($Apache::lonxml::depthcounter[$Apache::lonxml::depth]==1) {
- $Apache::lonxml::olddepth=$Apache::lonxml::depth;
- }
+ push(@Apache::lonxml::depthcounter,$Apache::lonxml::last_depth_count+1);
+ undef($Apache::lonxml::last_depth_count);
my $time;
if ($Apache::lonxml::debug eq "1") {
push(@timers,[&gettimeofday()]);
$time=&tv_interval($lasttime);
$lasttime=[&gettimeofday()];
}
- my $spacing=' 'x($Apache::lonxml::depth-1);
- my $curdepth=join('_',@Apache::lonxml::depthcounter);
- &Apache::lonxml::debug("s$spacing$Apache::lonxml::depth : $Apache::lonxml::olddepth : $curdepth : $token->[1] : $time : \n");
+ my $spacing=' 'x($#Apache::lonxml::depthcounter);
+ $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 "
s $Apache::lonxml::depth : $Apache::lonxml::olddepth : $curdepth : $token->[1]\n";
}
sub decreasedepth {
my ($token) = @_;
- $Apache::lonxml::depth--;
- if ($Apache::lonxml::depth<$Apache::lonxml::olddepth-1) {
- $#Apache::lonxml::depthcounter--;
- $Apache::lonxml::olddepth=$Apache::lonxml::depth+1;
- }
- if ( $Apache::lonxml::depth < -1) {
- &Apache::lonxml::warning(&mt("Missing tags, unable to properly run file."));
- $Apache::lonxml::depth='-1';
+ if ( $#Apache::lonxml::depthcounter == -1) {
+ &Apache::lonxml::warning(&mt("Missing tags, unable to properly run file."));
}
+ $Apache::lonxml::last_depth_count = pop(@Apache::lonxml::depthcounter);
+
my ($timer,$time);
if ($Apache::lonxml::debug eq "1") {
$timer=pop(@timers);
$time=&tv_interval($lasttime);
$lasttime=[&gettimeofday()];
}
- my $spacing=' 'x$Apache::lonxml::depth;
- my $curdepth=join('_',@Apache::lonxml::depthcounter);
- &Apache::lonxml::debug("e$spacing$Apache::lonxml::depth : $Apache::lonxml::olddepth : $curdepth : $token->[1] : $time : ".&tv_interval($timer)."\n");
+ 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 "
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 ($env{'request.state'} eq 'construct' && $id =~ /([._]|[^\w\d\s[:punct:]])/) {
+ &error(&mt("ID "[_1]" contains invalid characters, IDs are only allowed to contain letters, numbers, spaces and -",''.$id.''));
}
if ($id =~ /^\s*$/) { $id = $Apache::lonxml::curdepth; }
return $id;
@@ -1013,6 +1022,133 @@ sub get_all_text_unbalanced {
return $result
}
+
+=pod
+
+For bubble grading mode and exam bubble printing mode, the tracking of
+the current 'bubble line number' is stored in the %env element
+'form.bubble_line_counter', and is modifed and handled by
+the following routines.
+
+The value of it is stored in $Apache:lonxml::bubble_line_counter when
+live and stored back to env after done.
+
+=item &increment_bubble_counter($increment)
+
+Increments the bubble line counter by the optional value
+$increment (defaults to 1).
+
+ 'bad increments' are also treated as an increment of 1.
+('bad' means <=0).
+
+=cut
+
+sub increment_bubble_counter {
+ my ($increment) = @_;
+ if (!defined($increment) || $increment le 0) {
+ $increment = 1;
+ }
+ $Apache::lonxml::bubble_line_counter += $increment;
+ $Apache::lonxml::bubble_line_counter_changed = 1;
+}
+=pod
+
+=item &init_bubble_counter
+
+Initialize the internal counter to the env. variable
+or 1 if we are inconstruction space, or if the env var
+is not defined.
+
+=cut
+
+sub init_bubble_counter {
+ if ($env{'request.state'} eq 'construct') {
+ $Apache::lonxml::bubble_line_counter = 1;
+ $Apache::lonxml::bubble_line_counter_changed = 1;
+ } elsif (defined($env{'form.bubble_line_counter'})) {
+ $Apache::lonxml::bubble_line_counter =
+ $env{'form.bubble_line_counter'};
+ $Aapche::lonxml::bubble_line_counter_changed = 0;
+ } else {
+ $Apache::lonxml::bubble_line_counter = 1;
+ $Apache::lonxml::bubble_line_counter_changed = 1;
+ }
+
+}
+
+=pod
+
+=item store_bubble_counter;
+
+ Store the bubble line counter in its env var. The changed flag
+ is reset indicating the env is up to date with respect to the
+ local variable.
+=cut
+
+sub store_bubble_counter {
+ &Apache::lonnet::appenv(('form.bubble.counter' =>
+ $Apache::lonxml::bubble_line_counter));
+ $Apache::lonnet::bubble_line_counter_changed = 0;
+
+ return '';
+}
+
+=pod
+
+The next set of subs allow a single level of save/restore for the
+bubble_line_counter.
+
+=cut
+
+{
+ my $bubble_counter_state;
+
+ sub clear_bubble_counter {
+ undef($bubble_counter_state);
+ &Apache::lonnet::delenv('form.bubble_line_counter');
+ &Apache::lonxml::init_bubble_counter();
+ &Apache::lonxml::store_bubble_counter();
+ }
+
+ sub remember_bubble_counter {
+ &Apache::lonnet::transfer_profile_to_env(undef,undef, 1);
+ $bubble_counter_state = $env{'form.bubble_line_counter'};
+ }
+
+ sub restore_bubble_counter {
+ if (defined($bubble_counter_state)) {
+ &Apache::lonnet::appenv(('form.bubble_line_counter' =>
+ $bubble_counter_state));
+ }
+ }
+
+ sub get_bubble_counter {
+ if ($Apache::lonxml::bubble_line_counter_changed) {
+ &store_bubble_counter();
+ }
+ &Apache::lonnet::transfer_profile_to_env(undef, undef, 1);
+ return $env{'form.bubble_line_counter'};
+ }
+}
+
+=pod
+
+For bubble grading mode and exam bubble printing mode, the tracking of
+the current 'bubble line number' is stored in the %env element
+'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)
+
+=cut
+
sub increment_counter {
my ($increment) = @_;
if (defined($increment) && $increment gt 0) {
@@ -1023,6 +1159,14 @@ sub increment_counter {
$Apache::lonxml::counter_changed=1;
}
+=pod
+
+=item &init_counter;
+
+Initialize the internal counter environment variable
+
+=cut
+
sub init_counter {
if ($env{'request.state'} eq 'construct') {
$Apache::lonxml::counter=1;
@@ -1052,7 +1196,7 @@ sub store_counter {
}
sub remember_problem_counter {
- &Apache::lonnet::transfer_profile_to_env();
+ &Apache::lonnet::transfer_profile_to_env(undef,undef,1);
$state = $env{'form.counter'};
}
@@ -1061,11 +1205,13 @@ sub store_counter {
&Apache::lonnet::appenv(('form.counter' => $state));
}
}
+
sub get_problem_counter {
if ($Apache::lonxml::counter_changed) { &store_counter() }
- &Apache::lonnet::transfer_profile_to_env();
+ &Apache::lonnet::transfer_profile_to_env(undef,undef,1);
return $env{'form.counter'};
}
+
}
sub get_all_text {
@@ -1187,19 +1333,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 {
@@ -1221,7 +1371,7 @@ sub writeallows {
my %httpref=();
foreach (@extlinks) {
$httpref{'httpref.'.
- &Apache::lonnet::hreflocation($thisdir,$_)}=$thisurl;
+ &Apache::lonnet::hreflocation($thisdir,&unescape($_))}=$thisurl;
}
@extlinks=();
&Apache::lonnet::appenv(%httpref);
@@ -1240,6 +1390,12 @@ sub do_registered_ssi {
&Apache::lonnet::ssi($url,%form);
}
}
+
+sub add_script_result {
+ my ($display) = @_;
+ push(@script_var_displays, $display);
+}
+
#
# Afterburner handles anchors, highlights and links
#
@@ -1320,18 +1476,20 @@ SIMPLECONTENT
sub inserteditinfo {
- my ($result,$filecontents,$filetype)=@_;
+ my ($filecontents,$filetype)=@_;
$filecontents = &HTML::Entities::encode($filecontents,'<>&"');
# my $editheader='Edit below