--- loncom/xml/lonxml.pm 2001/11/29 21:38:17 1.140
+++ loncom/xml/lonxml.pm 2023/11/28 02:39:01 1.566
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# XML Parser Module
#
-# $Id: lonxml.pm,v 1.140 2001/11/29 21:38:17 albertel Exp $
+# $Id: lonxml.pm,v 1.566 2023/11/28 02:39:01 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -36,60 +36,97 @@
# The C source of the Code may not be distributed by the Licensee
# to any other parties under any circumstances.
#
-# last modified 06/26/00 by Alexander Sakharuk
-# 11/6 Gerd Kortemeyer
-# 6/1/1 Gerd Kortemeyer
-# 2/21,3/13 Guy
-# 3/29,5/4 Gerd Kortemeyer
-# 5/10 Scott Harrison
-# 5/26 Gerd Kortemeyer
-# 5/27 H. K. Ng
-# 6/2,6/3,6/8,6/9 Gerd Kortemeyer
-# 6/12,6/13 H. K. Ng
-# 6/16 Gerd Kortemeyer
-# 7/27 H. K. Ng
-# 8/7,8/9,8/10,8/11,8/15,8/16,8/17,8/18,8/20,8/23,8/24 Gerd Kortemeyer
-# Guy Albertelli
-# 9/26 Gerd Kortemeyer
+
+=pod
+
+=head1 NAME
+
+Apache::lonxml
+
+=head1 SYNOPSIS
+
+XML Parsing Module
+
+This is part of the LearningOnline Network with CAPA project
+described at http://www.lon-capa.org.
+
+
+=head1 SUBROUTINES
+
+=cut
+
package Apache::lonxml;
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 HTML::TokeParser;
-use HTML::TreeBuilder;
-use Safe;
-use Safe::Hole;
-use Math::Cephes qw(:trigs :hypers :bessels erf erfc);
-use Math::Random qw(:all);
-use Opcode;
+use LONCAPA;
+use HTML::LCParser();
+use HTML::TreeBuilder();
+use HTML::Entities();
+use Safe();
+use Safe::Hole();
+use Math::Cephes();
+use Math::Random();
+use Math::Calculus::Expression();
+use Number::FormatEng();
+use Opcode();
+use POSIX qw(strftime);
+use Time::HiRes qw( gettimeofday tv_interval );
+use Symbol();
sub register {
- my $space;
- my @taglist;
- my $temptag;
- ($space,@taglist) = @_;
- foreach $temptag (@taglist) {
- $Apache::lonxml::alltags{$temptag}=$space;
+ my ($space,@taglist) = @_;
+ foreach my $temptag (@taglist) {
+ push(@{ $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::lontexconvert;
-use Apache::style;
-use Apache::run;
-use Apache::londefdef;
-use Apache::scripttag;
-use Apache::edit;
+use Apache::lontexconvert();
+use Apache::style();
+use Apache::run();
+use Apache::londefdef();
+use Apache::scripttag();
+use Apache::languagetags();
+use Apache::edit();
+use Apache::inputtags();
+use Apache::outputtags();
use Apache::lonnet;
-use Apache::File;
-use Apache::loncommon;
+use Apache::File();
+use Apache::loncommon();
+use Apache::lonfeedback();
+use Apache::lonmsg();
+use Apache::loncacc();
+use Apache::lonmaxima();
+use Apache::lonr();
+use Apache::lonlocal;
+use Apache::lonhtmlcommon();
+use Apache::functionplotresponse();
+use Apache::lonnavmaps();
+
+#==================================== Main subroutine: xmlparse
-#================================================== Main subroutine: xmlparse
#debugging control, to turn on debugging modify the correct handler
+
$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
@pwd=();
@@ -109,333 +146,136 @@ $metamode = 0;
# turns on and of run::evaluate actually derefencing var refs
$evaluate = 1;
-# data structure for eidt mode, determines what tags can go into what other tags
+# data structure for edit mode, determines what tags can go into what other tags
%insertlist=();
# stores the list of active tag namespaces
@namespace=();
-# has the dynamic menu been updated to know about this resource
-$Apache::lonxml::registered=0;
-
-sub xmlbegin {
- my $output='';
- if ($ENV{'browser.mathml'}) {
- $output=''
- .''
- .']>'
- .'';
- } else {
- $output='';
- }
- return $output;
-}
-
-sub xmlend {
- my $discussion='';
- if ($ENV{'request.course.id'}) {
- my $crs='/'.$ENV{'request.course.id'};
- if ($ENV{'request.course.sec'}) {
- $crs.='_'.$ENV{'request.course.sec'};
- }
- $crs=~s/\_/\//g;
- my $seeid=&Apache::lonnet::allowed('rin',$crs);
- my $symb=&Apache::lonnet::symbread();
- if ($symb) {
- my %contrib=&Apache::lonnet::restore($symb,$ENV{'request.course.id'},
- $ENV{'course.'.$ENV{'request.course.id'}.'.domain'},
- $ENV{'course.'.$ENV{'request.course.id'}.'.num'});
- if ($contrib{'version'}) {
- $discussion.=
- '
Course Discussion of Resource
';
- my $idx;
- for ($idx=1;$idx<=$contrib{'version'};$idx++) {
- my $hidden=($contrib{'hidden'}=~/\.$idx\./);
- unless (($hidden) && (!$seeid)) {
- my $message=$contrib{$idx.':message'};
- $message=~s/\n/\
/g;
- if ($message) {
- if ($hidden) {
- $message=''.$message.'';
- }
- my $sender='Anonymous';
- if ((!$contrib{$idx.':anonymous'}) || ($seeid)) {
- $sender=$contrib{$idx.':sendername'}.' at '.
- $contrib{$idx.':senderdomain'};
- if ($contrib{$idx.':anonymous'}) {
- $sender.=' (anonymous)';
- }
- if ($seeid) {
- if ($hidden) {
- $sender.=' Make Visible';
- } else {
- $sender.=' Hide';
- }
- }
- }
- $discussion.=''.$sender.' ('.
- localtime($contrib{$idx.':timestamp'}).
- '):
'.$message.
- '
';
- }
- }
- }
- $discussion.='';
- }
- }
- }
- return $discussion.'';
-}
-
-sub tokeninputfield {
- my $defhost=$Apache::lonnet::perlvar{'lonHostID'};
- $defhost=~tr/a-z/A-Z/;
- return (<
- 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='';
- }
-
-
-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'};
- }
-
- return &Apache::lonnet::checkout($symb,$tuname,$tudom,$tcrsid);
-}
+# stores all Scrit Vars displays for later showing
+my @script_var_displays=();
-sub printtokenheader {
- my ($target,$token,$tsymb,$tcrsid,$tudom,$tuname)=@_;
- unless ($token) { return ''; }
+# a pointer the the Apache request object
+$Apache::lonxml::request='';
- my ($symb,$courseid,$domain,$name) = &Apache::lonxml::whichuser();
- unless ($tsymb) {
- $tsymb=$symb;
- }
- unless ($tuname) {
- $tuname=$name;
- $tudom=$domain;
- $tcrsid=$courseid;
- }
+# a problem number counter, and check on ether it is used
+$Apache::lonxml::counter=1;
+$Apache::lonxml::counter_changed=0;
- my %reply=&Apache::lonnet::get('environment',
- ['firstname','middlename','lastname','generation'],
- $tudom,$tuname);
- my $plainname=$reply{'firstname'}.' '.
- $reply{'middlename'}.' '.
- $reply{'lastname'}.' '.
- $reply{'generation'};
+# 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.
- if ($target eq 'web') {
- return
- ''.
- 'Checked out for '.$plainname.
- '
User: '.$tuname.' at '.$tudom.
- '
CourseID: '.$tcrsid.
- '
DocID: '.$token.
- '
Time: '.localtime().'
';
- } else {
- return $token;
- }
-}
-sub fontsettings() {
- my $headerstring='';
- if (($ENV{'browser.os'} eq 'mac') && (!$ENV{'browser.mathml'})) {
- $headerstring.=
- '';
- }
- return $headerstring;
-}
-
-sub registerurl {
- my $forcereg=shift;
- if ($ENV{'request.publicaccess'}) {
- return
- '';
- }
- if ($Apache::lonxml::registered && !$forcereg) { return ''; }
- $Apache::lonxml::registered=1;
- if (($ENV{'REQUEST_URI'}!~/^\/(res\/)*adm\//) || ($forcereg)) {
- my $hwkadd='';
- if ($ENV{'REQUEST_URI'}=~/\.(problem|exam|quiz|assess|survey|form)$/) {
- if (&Apache::lonnet::allowed('vgr',$ENV{'request.course.id'})) {
- $hwkadd.=(<
-// 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
- (6,3,'catalog.gif','catalog','info','catalog_info()');
- 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)');
- menu.switchbutton
- (9,1,'sbkm.gif','set','bookmark','set_bookmark()');
- menu.switchbutton
- (9,2,'vbkm.gif','view','bookmark','edit_bookmarks()');
- menu.switchbutton
- (9,3,'anot.gif','anno-','tations','annotate()');
- $hwkadd
- }
-
- function LONCAPAstale() {
- menu=window.open("","LONCAPAmenu");
- menu.currentStale=1;
- menu.switchbutton
- (3,1,'reload.gif','return','location','go(currentURL)');
- menu.clearbut(7,1);
- menu.clearbut(7,2);
- menu.clearbut(7,3);
- menu.menucltim=menu.setTimeout(
- 'clearbut(2,1);clearbut(2,3);clearbut(8,1);clearbut(8,2);clearbut(8,3);'+
- 'clearbut(9,1);clearbut(9,2);clearbut(9,3);clearbut(6,3)',
- 2000);
+%Apache::lonxml::counters_per_part = ();
- }
+#internal check on whether to look at style defs
+$Apache::lonxml::usestyle=1;
-// END LON-CAPA Internal
-
-ENDREGTHIS
+#locations used to store the parameter string for style substitutions
+$Apache::lonxml::style_values='';
+$Apache::lonxml::style_end_values='';
- } else {
- return (<
-// BEGIN LON-CAPA Internal
+#should we do the postag variable interpolation
+$Apache::lonxml::post_evaluate=1;
- function LONCAPAreg() {
- menu=window.open("","LONCAPAmenu");
- menu.currentStale=1;
- menu.clearbut(2,1);
- menu.clearbut(2,3);
- menu.clearbut(8,1);
- menu.clearbut(8,2);
- menu.clearbut(8,3);
- if (menu.currentURL) {
- menu.switchbutton
- (3,1,'reload.gif','return','location','go(currentURL)');
- } else {
- menu.clearbut(3,1);
- }
- }
+#a header message to emit in the case of any generated warning or errors
+$Apache::lonxml::warnings_error_header='';
- 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.
-// END LON-CAPA Internal
-
-ENDDONOTREGTHIS
+$Apache::lonxml::substitute_LaTeX_symbols = 1; # Starts out on.
- }
+sub enable_LaTeX_substitutions {
+ $Apache::lonxml::substitute_LaTeX_symbols = 1;
}
-
-sub loadevents() {
- return 'LONCAPAreg();';
+sub disable_LaTeX_substitutions {
+ $Apache::lonxml::substitute_LaTeX_symbols = 0;
}
-sub unloadevents() {
- return 'LONCAPAstale();';
+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')
+ )
+ && ($env{'form.inhibitmenu'} ne 'yes')
+ ) {
+ $discussion=&Apache::lonfeedback::list_discussion($mode,$status);
+ }
+ if ($target eq 'tex') {
+ $discussion.='\keephidden{ENDOFPROBLEM}\vskip 0.5mm\noindent\makebox[\textwidth/$number_of_columns][b]{\hrulefill}\end{document}';
+ &Apache::lonxml::newparser($parser,\$discussion,'');
+ return '';
+ }
+
+ return $discussion;
}
sub printalltags {
- my $temp;
- foreach $temp (sort keys %Apache::lonxml::alltags) {
- &Apache::lonxml::debug("$temp -- $Apache::lonxml::alltags{$temp}");
- }
+ foreach my $temp (sort(keys(%Apache::lonxml::alltags))) {
+ &Apache::lonxml::debug("$temp -- ".
+ join(',',@{ $Apache::lonxml::alltags{$temp} }));
+ }
}
sub xmlparse {
- my ($target,$content_file_string,$safeinit,%style_for_target) = @_;
+ 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();
- &setup_globals($target);
- #&printalltags();
+#
+# do we have a course style file?
+#
+
+ if ($env{'request.course.id'} && $env{'request.state'} ne 'construct') {
+ my $bodytext=
+ $env{'course.'.$env{'request.course.id'}.'.default_xml_style'};
+ if ($bodytext) {
+ foreach my $file (split(',',$bodytext)) {
+ my $location=&Apache::lonnet::filelocation('',$file);
+ my $styletext=&Apache::lonnet::getfile($location);
+ if ($styletext ne '-1') {
+ %style_for_target = (%style_for_target,
+ &Apache::style::styleparser($target,$styletext));
+ }
+ }
+ }
+ } 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));
+ }
+ }
+#&printalltags();
my @pars = ();
- my $pwd=$ENV{'request.filename'};
+ my $pwd=$env{'request.filename'};
$pwd =~ s:/[^/]*$::;
&newparser(\@pars,\$content_file_string,$pwd);
@@ -446,52 +286,111 @@ sub xmlparse {
($target, my @tenta) = split('&&',$target);
- my @stack = ();
+ my @stack = ();
my @parstack = ();
- &initdepth;
-
+ &initdepth();
+ &init_alarm();
my $finaloutput = &inner_xmlparse($target,\@stack,\@parstack,\@pars,
- $safeeval,\%style_for_target);
- if ($ENV{'request.uri'}) {
- &writeallows($ENV{'request.uri'});
- }
- return $finaloutput;
-}
+ $safeeval,\%style_for_target,1);
-sub htmlclean {
- my ($raw,$full)=@_;
-
- my $tree = HTML::TreeBuilder->new;
- $tree->ignore_unknown(0);
+ if (@stack) {
+ &warning(&mt('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() }
- $tree->parse($raw);
+ &clean_safespace($safeeval);
- my $output= $tree->as_HTML(undef,' ');
+ if (@script_var_displays) {
+ if ($finaloutput =~ m{\s*