--- loncom/xml/lonxml.pm 2000/06/19 15:52:29 1.1
+++ loncom/xml/lonxml.pm 2003/05/28 21:05:33 1.258
@@ -1,1305 +1,1455 @@
-package Apache::lonxml;
-
+# The LearningOnline Network with CAPA
+# XML Parser Module
+#
+# $Id: lonxml.pm,v 1.258 2003/05/28 21:05:33 albertel Exp $
+#
+# Copyright Michigan State University Board of Trustees
+#
+# This file is part of the LearningOnline Network with CAPA (LON-CAPA).
+#
+# LON-CAPA is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# 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.
+#
+# 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/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
+# Dec Guy Albertelli
+# YEAR=2002
+# 1/1 Gerd Kortemeyer
+# 1/2 Matthew Hall
+# 1/3 Gerd Kortemeyer
+#
+
+package Apache::lonxml;
+use vars
+qw(@pwd @outputstack $redirection $import @extlinks $metamode $evaluate %insertlist @namespace $prevent_entity_encode $errorcount $warningcount);
use strict;
-use HTML::TokeParser;
-use Safe;
+use HTML::LCParser();
+use HTML::TreeBuilder();
+use HTML::Entities();
+use Safe();
+use Safe::Hole();
+use Math::Cephes();
+use Math::Random();
+use Opcode();
+
+sub register {
+ 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::lontexconvert();
+use Apache::style();
+use Apache::run();
+use Apache::londefdef();
+use Apache::scripttag();
+use Apache::edit();
+use Apache::lonnet();
+use Apache::File();
+use Apache::loncommon();
+use Apache::lonfeedback();
+use Apache::lonmsg();
+use Apache::loncacc();
+
+#================================================== 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=();
+
+#these two are used for capturing a subset of the output for later processing,
+#don't touch them directly use &startredirection and &endredirection
+@outputstack = ();
+$redirection = 0;
+
+#controls wheter the tag actually does
+$import = 1;
+@extlinks=();
+
+# meta mode is a bit weird only some output is to be turned off
+#
';
+ }
+ }
+ }
+ unless ($discussiononly) {
+ $discussion.='';
+ }
+ }
+ if ($discussiononly) {
+ $discussion.=(<
+
+
+
+
+
+Note: in anonymous discussion, your name is visible only to
+course faculty
+
+
+Attachment (128 KB max size):
+
+
+ENDDISCUSS
+ $discussion.=&Apache::lonfeedback::generate_preview_button();
+ }
+ }
+ }
+ return $discussion.($discussiononly?'':'');
+}
+
+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);
+}
-#======================================================= Main subroutine: xmlparse
+sub printtokenheader {
+ my ($target,$token,$tsymb,$tcrsid,$tudom,$tuname)=@_;
+ unless ($token) { return ''; }
+
+ my ($symb,$courseid,$domain,$name) = &Apache::lonxml::whichuser();
+ unless ($tsymb) {
+ $tsymb=$symb;
+ }
+ unless ($tuname) {
+ $tuname=$name;
+ $tudom=$domain;
+ $tcrsid=$courseid;
+ }
+
+ my %reply=&Apache::lonnet::get('environment',
+ ['firstname','middlename','lastname','generation'],
+ $tudom,$tuname);
+ my $plainname=$reply{'firstname'}.' '.
+ $reply{'middlename'}.' '.
+ $reply{'lastname'}.' '.
+ $reply{'generation'};
+
+ if ($target eq 'web') {
+ my %idhash=&Apache::lonnet::idrget($tudom,($tuname));
+ return
+ ''.
+ 'Checked out for '.$plainname.
+ ' User: '.$tuname.' at '.$tudom.
+ ' ID: '.$idhash{$tuname}.
+ ' CourseID: '.$tcrsid.
+ ' Course: '.$ENV{'course.'.$tcrsid.'.description'}.
+ ' DocID: '.$token.
+ ' Time: '.localtime().'';
+ } else {
+ return $token;
+ }
+}
+
+sub fontsettings() {
+ my $headerstring='';
+ if (($ENV{'browser.os'} eq 'mac') && (!$ENV{'browser.mathml'})) {
+ $headerstring.=
+ '';
+ } elsif (!$ENV{'browser.mathml'}) {
+ $headerstring.=
+ '';
+ }
+ return $headerstring;
+}
+
+sub printalltags {
+ my $temp;
+ foreach $temp (sort keys %Apache::lonxml::alltags) {
+ &Apache::lonxml::debug("$temp -- ".
+ join(',',@{ $Apache::lonxml::alltags{$temp} }));
+ }
+}
sub xmlparse {
+ my ($request,$target,$content_file_string,$safeinit,%style_for_target) = @_;
- my ($target,$content_file_string,%style_for_target) = @_;
- my $pars = HTML::TokeParser->new(\$content_file_string);
- my $currentstring = '';
- my $finaloutput = '';
- my $newarg = '';
- my $tempostring = '';
- my $tempocont = '';
- my $safeeval = new Safe;
-
-#------------------------- Redefinition of the target in the case of compound target
- ($target, my @tenta) = split('&&',$target);
-#------------------------------ Stack definition (in stack we have all current tags)
-
- my @stack = ();
- my @parstack = ();
-
-#------------------------------------------ Parse input string (content_file_string)
-
- my $token;
-
- while ($token = $pars->get_token) {
- if ($token->[0] eq 'T') {
- $finaloutput .= $token->[1];
- $tempocont .= $token->[1];
- } elsif ($token->[0] eq 'S') {
-#------------------------------------------------------------------ add tag to stack
- push (@stack,$token->[1]);
-#---------------------------------------------- add parameters list to another stack
- map {$tempostring .= "$_=$token->[2]->{$_},"} @{$token->[3]};
- push (@parstack,$tempostring);
- $tempostring = '';
- $tempocont = '';
-
- if (exists $style_for_target{$token->[1]}) {
-
-#--------------------------------------------------------- use style file definition
-
- $newarg = $style_for_target{$token->[1]};
-
- if (index($newarg,'script') != -1 ) {
- my $pat = HTML::TokeParser->new(\$newarg);
- my $tokenpat;
- my $partstring = '';
- my $oustring = '';
- my $outputstring;
-
- while ($tokenpat = $pat->get_token) {
- if ($tokenpat->[0] eq 'T') {
- $oustring .= $tokenpat->[1];
- } elsif ($tokenpat->[0] eq 'S') {
- if ($tokenpat->[1] eq 'script') {
- while ($tokenpat = $pat->get_token and $tokenpat->[1] ne 'script') {
- if ($tokenpat->[0] eq 'S') {
- $partstring .= $tokenpat->[4];
- } elsif ($tokenpat->[0] eq 'T') {
- $partstring .= $tokenpat->[1];
- } elsif ($tokenpat->[0] eq 'E') {
- $partstring .= $tokenpat->[2];
- }
- }
-
- map {$partstring =~ s/\$$_/$token->[2]->{$_}/g; } @{$token->[3]};
-
- &run($partstring,$safeeval);
- $partstring = '';
- } elsif ($tokenpat->[1] eq 'evaluate') {
- $outputstring = &evaluate($tokenpat->[2]{expression},$safeeval);
- $oustring .= $outputstring;
- } else {
- $oustring .= $tokenpat->[4];
- }
- } elsif ($tokenpat->[0] eq 'E' and $tokenpat->[1] ne 'evaluate') {
- $oustring .= $tokenpat->[1];
- }
- }
- $newarg = $oustring;
- } else {
- map {$newarg =~ s/\$$_/$token->[2]->{$_}/g; } @{$token->[3]};
- }
- $finaloutput .= $newarg;
- } else {
-#----------------------------------------------------- use default definition of tag
- my $sub="start_$token->[1]";
-
- {
- no strict 'refs';
- if (defined (&$sub)) {
- $currentstring = &$sub($target,$token,\@parstack);
- $finaloutput .= $currentstring;
- $currentstring = '';
- } else {
- $finaloutput .= $token->[4];
- }
- use strict 'refs';
- }
- }
- } elsif ($token->[0] eq 'E') {
- pop @stack;
- unless (exists $style_for_target{$token->[1]}) {
- my $sub="end_$token->[1]";
- {
- no strict 'refs';
- if (defined (&$sub)) {
- $currentstring = &$sub($target,$token,\@parstack);
- $finaloutput .= $currentstring;
- $currentstring = '';
- } else {
- $finaloutput .= $token->[4];
- }
- use strict 'refs';
- }
- }
-#------------------------------------------------------- end tag from the style file
- if (exists $style_for_target{'/'."$token->[1]"}) {
- $newarg = $style_for_target{'/'."$token->[1]"};
- my @very_temp = split(',',@parstack[$#parstack]);
- map {my @ret= split('=',$_); $newarg =~ s/\$$ret[0]/$ret[1]/g; } @very_temp;
- $finaloutput .= $newarg;
- }
- pop @parstack;
- }
+ &setup_globals($request,$target);
+ &Apache::inputtags::initialize_inputtags();
+ &Apache::outputtags::initialize_outputtags();
+ &Apache::edit::initialize_edit();
+
+#
+# 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) {
+ my $location=&Apache::lonnet::filelocation('',$bodytext);
+ my $styletext=&Apache::lonnet::getfile($location);
+ if ($styletext ne '-1') {
+ %style_for_target = (%style_for_target,
+ &Apache::style::styleparser($target,$styletext));
+ }
}
- return $finaloutput;
+ }
+#&printalltags();
+ my @pars = ();
+ my $pwd=$ENV{'request.filename'};
+ $pwd =~ s:/[^/]*$::;
+ &newparser(\@pars,\$content_file_string,$pwd);
+
+ my $safeeval = new Safe;
+ my $safehole = new Safe::Hole;
+ &init_safespace($target,$safeeval,$safehole,$safeinit);
+#-------------------- Redefinition of the target in the case of compound target
+
+ ($target, my @tenta) = split('&&',$target);
+
+ my @stack = ();
+ my @parstack = ();
+ &initdepth;
+
+ my $finaloutput = &inner_xmlparse($target,\@stack,\@parstack,\@pars,
+ $safeeval,\%style_for_target);
+
+ if ($ENV{'request.uri'}) {
+ &writeallows($ENV{'request.uri'});
+ }
+ if ($Apache::lonxml::counter_changed) { &store_counter() }
+ return $finaloutput;
}
+sub htmlclean {
+ my ($raw,$full)=@_;
-#================================================================== style subroutine
+ my $tree = HTML::TreeBuilder->new;
+ $tree->ignore_unknown(0);
-sub styleparser {
+ $tree->parse($raw);
- my ($target,$content_style_string) = @_;
+ my $output= $tree->as_HTML(undef,' ');
-#------------------------------------------------ target redefinition (if necessary)
-
- my @target_string = '';
- my $element;
-
- ($element,@target_string) = split ('&&',$target);
-
- map {$content_style_string =~ s/\<(.*)$_\>/\<$1$element\>/g; } @target_string;
-
- $target = $element;
-
-#------------------------------------------------- create a table for defined target
-#---------------------------------------------- from the information from Style File
-
- my @value_style = ();
- my $current_key = '';
- my $current_value = '';
-
- my $pstyle = HTML::TokeParser->new(\$content_style_string);
-
- my $stoken;
-
- while ($stoken = $pstyle->get_token) {
-#---------------------------------------------------------- start for tag definition
- if ($stoken->[0] eq 'S' and $stoken->[1] eq 'definetag') {
-#------------------------------------------------------------------- new key in hash
- $current_key = $stoken->[2]{name};
- if ($target eq 'meta') {
-#-------------------------------------------------- reserved for the metadate output
-
-
- } else {
-#-------------------------------------------------------------------- outtext output
- while ($stoken = $pstyle->get_token and $stoken->[1] ne 'outtext') {
- }
- while ($stoken = $pstyle->get_token and $stoken->[0] ne 'S') {
- $current_value .= $stoken->[1];
- }
- while ($stoken->[1] ne 'definetag') {
- if ($stoken->[0] eq 'S' and $stoken->[1] eq $target) {
- while ($stoken = $pstyle->get_token) {
- if ($stoken->[1] ne $target) {
- if ($stoken->[0] eq 'S') {
- $current_value .= $stoken->[4];
- }
- if ($stoken->[0] eq 'E') {
- $current_value .= $stoken->[2];
- }
- if ($stoken->[0] eq 'T') {
- $current_value .= $stoken->[1];
- }
- } else {
- last;
- }
- }
- } elsif ($stoken->[0] eq 'S' and $stoken->[1] ne $target) {
- while ($stoken = $pstyle->get_token and $stoken->[0] ne 'E') {
- }
- }
-
- while ($stoken = $pstyle->get_token) {
- if ($stoken->[0] eq 'T') {
- $current_value .= $stoken->[1];
- }
- if ($stoken->[0] eq 'E') {
- last;
- }
- if ($stoken->[0] eq 'S') {
- last;
- }
- }
-
- }
- }
-
- }
- push (@value_style,lc $current_key,$current_value);
- $current_key = '';
- $current_value = '';
+ $output=~s/\<(br|hr|img|meta|allow)(.*?)\>/\<$1$2 \/\>/gis;
+ $output=~s/\<\/(br|hr|img|meta|allow)\>//gis;
+ unless ($full) {
+ $output=~s/\<[\/]*(body|head|html)\>//gis;
+ }
- }
-
- my %style_for_target = @value_style;
-
-#-------------------------------------------------------------------- check printing
-# while (($current_key,$current_value) = each %style_for_target) {
-# print "$current_key => $current_value\n";
-# }
+ $tree = $tree->delete;
- return %style_for_target;
-
+ return $output;
}
+sub latex_special_symbols {
+ my ($current_token,$stack,$parstack,$where)=@_;
+ if ($where eq 'header') {
+ $current_token =~ s/(\\|_|\^)/ /g;
+ $current_token =~ s/(\$|%|\#|&|\{|\})/\\$1/g;
+ } else {
+ $current_token=~s/\\ /\\char92 /g;
+ $current_token=~s/\^/\\char94 /g;
+ $current_token=~s/\~/\\char126 /g;
+ $current_token=~s/(&[^A-Za-z\#])/\\$1/g;
+ $current_token=~s/([^&])\#/$1\\#/g;
+ $current_token=~s/(\$|_|{|})/\\$1/g;
+ $current_token=~s/\\char92 /\\texttt{\\char92}/g;
+ $current_token=~s/(>|<)/\$$1\$/g; #more or less
+ if ($current_token=~m/\d%/) {$current_token =~ s/(\d)%/$1\\%/g;} #percent after digit
+ if ($current_token=~m/\s%/) {$current_token =~ s/(\s)%/$1\\%/g;} #persent after space
+ }
+ return $current_token;
+}
-
-#=============================================================== Subroutine definition
-#--------------------------------------------------------------------------------- Run
- sub evaluate {
- my ($expression,$safeeval) = @_;
- return $safeeval->reval($expression);
- }
-
- sub run {
- my ($code,$safeeval) = @_;
- $safeeval->reval($code);
- }
-
-#===================================================================== TAG SUBROUTINES
-#----------------------------------------------------------------------------- tag
- sub start_m {
- my ($target,$token) = @_;
- my $currentstring = '';
- if ($target eq 'web') {
- $currentstring = "\$out = lontexconvert::converted(\$in = '\$'.\"";
- } elsif ($target eq 'tex') {
- $currentstring = "\$";
- }
- return $currentstring;
- }
- sub end_m {
- my ($target,$token) = @_;
- my $currentstring = '';
- if ($target eq 'web') {
- $currentstring = "\".'\$') ";
- } elsif ($target eq 'tex') {
- $currentstring = "\$";
- }
- return $currentstring;
- }
-#-------------------------------------------------------------------------- tag
- sub start_html {
- my ($target,$token) = @_;
- my $currentstring = '';
- if ($target eq 'web') {
- $currentstring = $token->[4];
- }
- return $currentstring;
- }
- sub end_html {
- my ($target,$token) = @_;
- my $currentstring = '';
- if ($target eq 'web') {
- $currentstring = $token->[2];
- }
- return $currentstring;
- }
-#-------------------------------------------------------------------------- tag
- sub start_head {
- my ($target,$token) = @_;
- my $currentstring = '';
- if ($target eq 'web') {
- $currentstring = $token->[4];
- }
- return $currentstring;
- }
- sub end_head {
- my ($target,$token) = @_;
- my $currentstring = '';
- if ($target eq 'web') {
- $currentstring = $token->[2];
- }
- return $currentstring;
- }
-#---------------------------------------------------------------------------