![]() ![]() | ![]() |
- Remove unnecessary call to verify_html().
1: # The LearningOnline Network with CAPA 2: # XML Parser Module 3: # 4: # $Id: lonxml.pm,v 1.517 2010/09/29 15:56:03 raeburn Exp $ 5: # 6: # Copyright Michigan State University Board of Trustees 7: # 8: # This file is part of the LearningOnline Network with CAPA (LON-CAPA). 9: # 10: # LON-CAPA is free software; you can redistribute it and/or modify 11: # it under the terms of the GNU General Public License as published by 12: # the Free Software Foundation; either version 2 of the License, or 13: # (at your option) any later version. 14: # 15: # LON-CAPA is distributed in the hope that it will be useful, 16: # but WITHOUT ANY WARRANTY; without even the implied warranty of 17: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18: # GNU General Public License for more details. 19: # 20: # You should have received a copy of the GNU General Public License 21: # along with LON-CAPA; if not, write to the Free Software 22: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 23: # 24: # /home/httpd/html/adm/gpl.txt 25: # 26: # http://www.lon-capa.org/ 27: # 28: # Copyright for TtHfunc and TtMfunc by Ian Hutchinson. 29: # TtHfunc and TtMfunc (the "Code") may be compiled and linked into 30: # binary executable programs or libraries distributed by the 31: # Michigan State University (the "Licensee"), but any binaries so 32: # distributed are hereby licensed only for use in the context 33: # of a program or computational system for which the Licensee is the 34: # primary author or distributor, and which performs substantial 35: # additional tasks beyond the translation of (La)TeX into HTML. 36: # The C source of the Code may not be distributed by the Licensee 37: # to any other parties under any circumstances. 38: # 39: 40: =pod 41: 42: =head1 NAME 43: 44: Apache::lonxml 45: 46: =head1 SYNOPSIS 47: 48: XML Parsing Module 49: 50: This is part of the LearningOnline Network with CAPA project 51: described at http://www.lon-capa.org. 52: 53: 54: =head1 SUBROUTINES 55: 56: =cut 57: 58: 59: 60: package Apache::lonxml; 61: use vars 62: qw(@pwd @outputstack $redirection $import @extlinks $metamode $evaluate %insertlist @namespace $errorcount $warningcount); 63: use strict; 64: use LONCAPA; 65: use HTML::LCParser(); 66: use HTML::TreeBuilder(); 67: use HTML::Entities(); 68: use Safe(); 69: use Safe::Hole(); 70: use Math::Cephes(); 71: use Math::Random(); 72: use Opcode(); 73: use POSIX qw(strftime); 74: use Time::HiRes qw( gettimeofday tv_interval ); 75: use Symbol(); 76: 77: sub register { 78: my ($space,@taglist) = @_; 79: foreach my $temptag (@taglist) { 80: push(@{ $Apache::lonxml::alltags{$temptag} },$space); 81: } 82: } 83: 84: sub deregister { 85: my ($space,@taglist) = @_; 86: foreach my $temptag (@taglist) { 87: my $tempspace = $Apache::lonxml::alltags{$temptag}[-1]; 88: if ($tempspace eq $space) { 89: pop(@{ $Apache::lonxml::alltags{$temptag} }); 90: } 91: } 92: #&printalltags(); 93: } 94: 95: use Apache::Constants qw(:common); 96: use Apache::lontexconvert(); 97: use Apache::style(); 98: use Apache::run(); 99: use Apache::londefdef(); 100: use Apache::scripttag(); 101: use Apache::languagetags(); 102: use Apache::edit(); 103: use Apache::inputtags(); 104: use Apache::outputtags(); 105: use Apache::lonnet; 106: use Apache::File(); 107: use Apache::loncommon(); 108: use Apache::lonfeedback(); 109: use Apache::lonmsg(); 110: use Apache::loncacc(); 111: use Apache::lonmaxima(); 112: use Apache::lonr(); 113: use Apache::lonlocal; 114: use Apache::lonhtmlcommon(); 115: 116: #==================================== Main subroutine: xmlparse 117: 118: #debugging control, to turn on debugging modify the correct handler 119: 120: $Apache::lonxml::debug=0; 121: 122: # keeps count of the number of warnings and errors generated in a parse 123: $warningcount=0; 124: $errorcount=0; 125: 126: #path to the directory containing the file currently being processed 127: @pwd=(); 128: 129: #these two are used for capturing a subset of the output for later processing, 130: #don't touch them directly use &startredirection and &endredirection 131: @outputstack = (); 132: $redirection = 0; 133: 134: #controls wheter the <import> tag actually does 135: $import = 1; 136: @extlinks=(); 137: 138: # meta mode is a bit weird only some output is to be turned off 139: #<output> tag turns metamode off (defined in londefdef.pm) 140: $metamode = 0; 141: 142: # turns on and of run::evaluate actually derefencing var refs 143: $evaluate = 1; 144: 145: # data structure for eidt mode, determines what tags can go into what other tags 146: %insertlist=(); 147: 148: # stores the list of active tag namespaces 149: @namespace=(); 150: 151: # stores all Scrit Vars displays for later showing 152: my @script_var_displays=(); 153: 154: # a pointer the the Apache request object 155: $Apache::lonxml::request=''; 156: 157: # a problem number counter, and check on ether it is used 158: $Apache::lonxml::counter=1; 159: $Apache::lonxml::counter_changed=0; 160: 161: # Part counter hash. In analysis mode, the 162: # problems can use this to record which parts increment the counter 163: # by how much. The counter subs will maintain this hash via 164: # their optional part parameters. Note that the assumption is that 165: # analysis is done in one request and therefore it is not necessary to 166: # save this information request-to-request. 167: 168: 169: %Apache::lonxml::counters_per_part = (); 170: 171: #internal check on whether to look at style defs 172: $Apache::lonxml::usestyle=1; 173: 174: #locations used to store the parameter string for style substitutions 175: $Apache::lonxml::style_values=''; 176: $Apache::lonxml::style_end_values=''; 177: 178: #array of ssi calls that need to occur after we are done parsing 179: @Apache::lonxml::ssi_info=(); 180: 181: #should we do the postag variable interpolation 182: $Apache::lonxml::post_evaluate=1; 183: 184: #a header message to emit in the case of any generated warning or errors 185: $Apache::lonxml::warnings_error_header=''; 186: 187: # Control whether or not LaTeX symbols should be substituted for their 188: # \ style equivalents...this may be turned off e.g. in an verbatim 189: # environment. 190: 191: $Apache::lonxml::substitute_LaTeX_symbols = 1; # Starts out on. 192: 193: sub enable_LaTeX_substitutions { 194: $Apache::lonxml::substitute_LaTeX_symbols = 1; 195: } 196: sub disable_LaTeX_substitutions { 197: $Apache::lonxml::substitute_LaTeX_symbols = 0; 198: } 199: 200: sub xmlend { 201: my ($target,$parser)=@_; 202: my $mode='xml'; 203: my $status='OPEN'; 204: if ($Apache::lonhomework::parsing_a_problem || 205: $Apache::lonhomework::parsing_a_task ) { 206: $mode='problem'; 207: $status=$Apache::inputtags::status[-1]; 208: } 209: my $discussion; 210: &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'}, 211: ['LONCAPA_INTERNAL_no_discussion']); 212: if (! exists($env{'form.LONCAPA_INTERNAL_no_discussion'}) || 213: $env{'form.LONCAPA_INTERNAL_no_discussion'} ne 'true') { 214: $discussion=&Apache::lonfeedback::list_discussion($mode,$status); 215: } 216: if ($target eq 'tex') { 217: $discussion.='<tex>\keephidden{ENDOFPROBLEM}\vskip 0.5mm\noindent\makebox[\textwidth/$number_of_columns][b]{\hrulefill}\end{document}</tex>'; 218: &Apache::lonxml::newparser($parser,\$discussion,''); 219: return ''; 220: } 221: 222: return $discussion; 223: } 224: 225: sub printalltags { 226: my $temp; 227: foreach $temp (sort keys %Apache::lonxml::alltags) { 228: &Apache::lonxml::debug("$temp -- ". 229: join(',',@{ $Apache::lonxml::alltags{$temp} })); 230: } 231: } 232: 233: sub xmlparse { 234: my ($request,$target,$content_file_string,$safeinit,%style_for_target) = @_; 235: 236: &setup_globals($request,$target); 237: &Apache::inputtags::initialize_inputtags(); 238: &Apache::bridgetask::initialize_bridgetask(); 239: &Apache::outputtags::initialize_outputtags(); 240: &Apache::edit::initialize_edit(); 241: &Apache::londefdef::initialize_londefdef(); 242: 243: # 244: # do we have a course style file? 245: # 246: 247: if ($env{'request.course.id'} && $env{'request.state'} ne 'construct') { 248: my $bodytext= 249: $env{'course.'.$env{'request.course.id'}.'.default_xml_style'}; 250: if ($bodytext) { 251: foreach my $file (split(',',$bodytext)) { 252: my $location=&Apache::lonnet::filelocation('',$file); 253: my $styletext=&Apache::lonnet::getfile($location); 254: if ($styletext ne '-1') { 255: %style_for_target = (%style_for_target, 256: &Apache::style::styleparser($target,$styletext)); 257: } 258: } 259: } 260: } elsif ($env{'construct.style'} 261: && ($env{'request.state'} eq 'construct')) { 262: my $location=&Apache::lonnet::filelocation('',$env{'construct.style'}); 263: my $styletext=&Apache::lonnet::getfile($location); 264: if ($styletext ne '-1') { 265: %style_for_target = (%style_for_target, 266: &Apache::style::styleparser($target,$styletext)); 267: } 268: } 269: #&printalltags(); 270: my @pars = (); 271: my $pwd=$env{'request.filename'}; 272: $pwd =~ s:/[^/]*$::; 273: &newparser(\@pars,\$content_file_string,$pwd); 274: 275: my $safeeval = new Safe; 276: my $safehole = new Safe::Hole; 277: &init_safespace($target,$safeeval,$safehole,$safeinit); 278: #-------------------- Redefinition of the target in the case of compound target 279: 280: ($target, my @tenta) = split('&&',$target); 281: 282: my @stack = (); 283: my @parstack = (); 284: &initdepth(); 285: &init_alarm(); 286: my $finaloutput = &inner_xmlparse($target,\@stack,\@parstack,\@pars, 287: $safeeval,\%style_for_target,1); 288: 289: if (@stack) { 290: &warning(&mt('At end of file some tags were still left unclosed:'). 291: ' <tt><'.join('></tt>, <tt><',reverse(@stack)). 292: '></tt>'); 293: } 294: if ($env{'request.uri'}) { 295: &writeallows($env{'request.uri'}); 296: } 297: &do_registered_ssi(); 298: if ($Apache::lonxml::counter_changed) { &store_counter() } 299: 300: &clean_safespace($safeeval); 301: 302: if (@script_var_displays) { 303: $finaloutput .= join('',@script_var_displays); 304: undef(@script_var_displays); 305: } 306: &init_state(); 307: if ($env{'form.return_only_error_and_warning_counts'}) { 308: if ($env{'request.filename'}=~/\.(html|htm|xml)$/i) { 309: my $error=&verify_html($content_file_string); 310: if ($error) { $errorcount++; } 311: } 312: return "$errorcount:$warningcount"; 313: } 314: return $finaloutput; 315: } 316: 317: sub latex_special_symbols { 318: my ($string,$where)=@_; 319: # 320: # If e.g. in verbatim mode, then don't substitute. 321: # but return original string. 322: # 323: if (!($Apache::lonxml::substitute_LaTeX_symbols)) { 324: return $string; 325: } 326: if ($where eq 'header') { 327: $string =~ s/\\/\$\\backslash\$/g; # \ -> $\backslash$ per LaTex line by line pg 10. 328: $string =~ s/(\$|%|\{|\})/\\$1/g; 329: $string=&Apache::lonprintout::character_chart($string); 330: # any & or # leftover should be safe to just escape 331: $string=~s/([^\\])\&/$1\\\&/g; 332: $string=~s/([^\\])\#/$1\\\#/g; 333: $string =~ s/_/\\_/g; # _ -> \_ 334: $string =~ s/\^/\\\^{}/g; # ^ -> \^{} 335: } else { 336: $string=~s/\\/\\ensuremath{\\backslash}/g; 337: $string=~s/\\\%|\%/\\\%/g; 338: $string=~s/\\{|{/\\{/g; 339: $string=~s/\\}|}/\\}/g; 340: $string=~s/\\ensuremath\\{\\backslash\\}/\\ensuremath{\\backslash}/g; 341: $string=~s/\\\$|\$/\\\$/g; 342: $string=~s/\\\_|\_/\\\_/g; 343: $string=~s/([^\\]|^)(\~|\^)/$1\\$2\\strut /g; 344: $string=~s/(>|<)/\\ensuremath\{$1\}/g; #more or less 345: $string=&Apache::lonprintout::character_chart($string); 346: # any & or # leftover should be safe to just escape 347: $string=~s/\\\&|\&/\\\&/g; 348: $string=~s/\\\#|\#/\\\#/g; 349: $string=~s/\|/\$\\mid\$/g; 350: #single { or } How to escape? 351: } 352: return $string; 353: } 354: 355: sub inner_xmlparse { 356: my ($target,$stack,$parstack,$pars,$safeeval,$style_for_target,$start)=@_; 357: my $finaloutput = ''; 358: my $result; 359: my $token; 360: my $dontpop=0; 361: my $startredirection = $Apache::lonxml::redirection; 362: while ( $#$pars > -1 ) { 363: while ($token = $$pars['-1']->get_token) { 364: if (($token->[0] eq 'T') || ($token->[0] eq 'C') ) { 365: if ($metamode<1) { 366: my $text=$token->[1]; 367: if ($token->[0] eq 'C' && $target eq 'tex') { 368: $text = ''; 369: # $text = '%'.$text."\n"; 370: } 371: $result.=$text; 372: } 373: } elsif (($token->[0] eq 'D')) { 374: if ($metamode<1 && $target eq 'web') { 375: my $text=$token->[1]; 376: $result.=$text; 377: } 378: } elsif ($token->[0] eq 'PI') { 379: if ($metamode<1 && $target eq 'web') { 380: $result=$token->[2]; 381: } 382: } elsif ($token->[0] eq 'S') { 383: # add tag to stack 384: push (@$stack,$token->[1]); 385: # add parameters list to another stack 386: push (@$parstack,&parstring($token)); 387: &increasedepth($token); 388: if ($Apache::lonxml::usestyle && 389: exists($$style_for_target{$token->[1]})) { 390: $Apache::lonxml::usestyle=0; 391: my $string=$$style_for_target{$token->[1]}. 392: '<LONCAPA_INTERNAL_TURN_STYLE_ON />'; 393: &Apache::lonxml::newparser($pars,\$string); 394: $Apache::lonxml::style_values=$$parstack[-1]; 395: $Apache::lonxml::style_end_values=$$parstack[-1]; 396: } else { 397: $result = &callsub("start_$token->[1]", $target, $token, $stack, 398: $parstack, $pars, $safeeval, $style_for_target); 399: } 400: } elsif ($token->[0] eq 'E') { 401: if ($Apache::lonxml::usestyle && 402: exists($$style_for_target{'/'."$token->[1]"})) { 403: $Apache::lonxml::usestyle=0; 404: my $string=$$style_for_target{'/'.$token->[1]}. 405: '<LONCAPA_INTERNAL_TURN_STYLE_ON end="'.$token->[1].'" />'; 406: &Apache::lonxml::newparser($pars,\$string); 407: $Apache::lonxml::style_values=$Apache::lonxml::style_end_values; 408: $Apache::lonxml::style_end_values=''; 409: $dontpop=1; 410: } else { 411: #clear out any tags that didn't end 412: while ($token->[1] ne $$stack['-1'] && ($#$stack > -1)) { 413: my $lasttag=$$stack[-1]; 414: if ($token->[1] =~ /^\Q$lasttag\E$/i) { 415: &Apache::lonxml::warning(&mt('Using tag [_1] on line [_2] as end tag to [_3]','</'.$token->[1].'>','.$token->[3].','<'.$$stack[-1].'>')); 416: last; 417: } else { 418: &Apache::lonxml::warning(&mt('Found tag [_1] on line [_2] when looking for [_3] in file.','</'.$token->[1].'>',$token->[3],'</'.$$stack[-1].'>')); 419: &end_tag($stack,$parstack,$token); 420: } 421: } 422: $result = &callsub("end_$token->[1]", $target, $token, $stack, 423: $parstack, $pars,$safeeval, $style_for_target); 424: } 425: } else { 426: &Apache::lonxml::error("Unknown token event :$token->[0]:$token->[1]:"); 427: } 428: #evaluate variable refs in result 429: if ($Apache::lonxml::post_evaluate &&$result ne "") { 430: my $extras; 431: if (!$Apache::lonxml::usestyle) { 432: $extras=$Apache::lonxml::style_values; 433: } 434: if ( $#$parstack > -1 ) { 435: $result=&Apache::run::evaluate($result,$safeeval,$extras.$$parstack[-1]); 436: } else { 437: $result= &Apache::run::evaluate($result,$safeeval,$extras); 438: } 439: } 440: $Apache::lonxml::post_evaluate=1; 441: 442: if (($token->[0] eq 'T') || ($token->[0] eq 'C') || ($token->[0] eq 'D') ) { 443: #Style file definitions should be correct 444: if ($target eq 'tex' && ($Apache::lonxml::usestyle)) { 445: $result=&latex_special_symbols($result); 446: } 447: } 448: 449: if ($Apache::lonxml::redirection) { 450: $Apache::lonxml::outputstack['-1'] .= $result; 451: } else { 452: $finaloutput.=$result; 453: } 454: $result = ''; 455: 456: if ($token->[0] eq 'E' && !$dontpop) { 457: &end_tag($stack,$parstack,$token); 458: } 459: $dontpop=0; 460: } 461: if ($#$pars > -1) { 462: pop @$pars; 463: pop @Apache::lonxml::pwd; 464: } 465: } 466: 467: # if ($target eq 'meta') { 468: # $finaloutput.=&endredirection; 469: # } 470: 471: if ( $start && $target eq 'grade') { &endredirection(); } 472: if ( $Apache::lonxml::redirection > $startredirection) { 473: while ($Apache::lonxml::redirection > $startredirection) { 474: $finaloutput .= &endredirection(); 475: } 476: } 477: if (($ENV{'QUERY_STRING'}) && ($target eq 'web')) { 478: $finaloutput=&afterburn($finaloutput); 479: } 480: if ($target eq 'modified') { 481: # if modfied, handle startpart and endpart 482: $finaloutput=~s/\<startpartmarker[^\>]*\>(.*)\<endpartmarker[^\>]*\>/<part>$1<\/part>/gs; 483: } 484: return $finaloutput; 485: } 486: 487: ## 488: ## Looks to see if there is a subroutine defined for this tag. If so, call it, 489: ## otherwise do not call it as we do not know what it is. 490: ## 491: sub callsub { 492: my ($sub,$target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; 493: my $currentstring=''; 494: my $nodefault; 495: { 496: my $sub1; 497: no strict 'refs'; 498: my $tag=$token->[1]; 499: # get utterly rid of extended html tags 500: if ($tag=~/^x\-/i) { return ''; } 501: my $space=$Apache::lonxml::alltags{$tag}[-1]; 502: if (!$space) { 503: $tag=~tr/A-Z/a-z/; 504: $sub=~tr/A-Z/a-z/; 505: $space=$Apache::lonxml::alltags{$tag}[-1] 506: } 507: 508: my $deleted=0; 509: if (($token->[0] eq 'S') && ($target eq 'modified')) { 510: $deleted=&Apache::edit::handle_delete($space,$target,$token,$tagstack, 511: $parstack,$parser,$safeeval, 512: $style); 513: } 514: if (!$deleted) { 515: if ($space) { 516: #&Apache::lonxml::debug("Calling sub $sub in $space $metamode"); 517: $sub1="$space\:\:$sub"; 518: ($currentstring,$nodefault) = &$sub1($target,$token,$tagstack, 519: $parstack,$parser,$safeeval, 520: $style); 521: } else { 522: if ($target eq 'tex') { 523: # throw away tag name 524: return ''; 525: } 526: #&Apache::lonxml::debug("NOT Calling sub $sub in $space $metamode"); 527: if ($metamode <1) { 528: if (defined($token->[4]) && ($metamode < 1)) { 529: $currentstring = $token->[4]; 530: } else { 531: $currentstring = $token->[2]; 532: } 533: } 534: } 535: # &Apache::lonxml::debug("nodefalt:$nodefault:"); 536: if ($currentstring eq '' && $nodefault eq '') { 537: if ($target eq 'edit') { 538: #&Apache::lonxml::debug("doing default edit for $token->[1]"); 539: if ($token->[0] eq 'S') { 540: $currentstring = &Apache::edit::tag_start($target,$token); 541: } elsif ($token->[0] eq 'E') { 542: $currentstring = &Apache::edit::tag_end($target,$token); 543: } 544: } 545: } 546: if ($target eq 'modified' && $nodefault eq '') { 547: if ($currentstring eq '') { 548: if ($token->[0] eq 'S') { 549: $currentstring = $token->[4]; 550: } elsif ($token->[0] eq 'E') { 551: $currentstring = $token->[2]; 552: } else { 553: $currentstring = $token->[2]; 554: } 555: } 556: if ($token->[0] eq 'S') { 557: $currentstring.=&Apache::edit::handle_insert(); 558: } elsif ($token->[0] eq 'E') { 559: $currentstring.=&Apache::edit::handle_insertafter($token->[1]); 560: } 561: } 562: } 563: use strict 'refs'; 564: } 565: return $currentstring; 566: } 567: 568: { 569: my %state; 570: 571: sub init_state { 572: undef(%state); 573: } 574: 575: sub set_state { 576: my ($key,$value) = @_; 577: $state{$key} = $value; 578: return $value; 579: } 580: sub get_state { 581: my ($key) = @_; 582: return $state{$key}; 583: } 584: } 585: 586: sub setup_globals { 587: my ($request,$target)=@_; 588: $Apache::lonxml::request=$request; 589: $errorcount=0; 590: $warningcount=0; 591: $Apache::lonxml::internal_error=0; 592: $Apache::lonxml::default_homework_loaded=0; 593: $Apache::lonxml::usestyle=1; 594: &init_counter(); 595: &clear_bubble_lines_for_part(); 596: &init_state(); 597: &set_state('target',$target); 598: @Apache::lonxml::pwd=(); 599: @Apache::lonxml::extlinks=(); 600: @script_var_displays=(); 601: @Apache::lonxml::ssi_info=(); 602: $Apache::lonxml::post_evaluate=1; 603: $Apache::lonxml::warnings_error_header=''; 604: $Apache::lonxml::substitute_LaTeX_symbols = 1; 605: if ($target eq 'meta') { 606: $Apache::lonxml::redirection = 0; 607: $Apache::lonxml::metamode = 1; 608: $Apache::lonxml::evaluate = 1; 609: $Apache::lonxml::import = 0; 610: } elsif ($target eq 'answer') { 611: $Apache::lonxml::redirection = 0; 612: $Apache::lonxml::metamode = 1; 613: $Apache::lonxml::evaluate = 1; 614: $Apache::lonxml::import = 1; 615: } elsif ($target eq 'grade') { 616: &startredirection(); #ended in inner_xmlparse on exit 617: $Apache::lonxml::metamode = 0; 618: $Apache::lonxml::evaluate = 1; 619: $Apache::lonxml::import = 1; 620: } elsif ($target eq 'modified') { 621: $Apache::lonxml::redirection = 0; 622: $Apache::lonxml::metamode = 0; 623: $Apache::lonxml::evaluate = 0; 624: $Apache::lonxml::import = 0; 625: } elsif ($target eq 'edit') { 626: $Apache::lonxml::redirection = 0; 627: $Apache::lonxml::metamode = 0; 628: $Apache::lonxml::evaluate = 0; 629: $Apache::lonxml::import = 0; 630: } elsif ($target eq 'analyze') { 631: $Apache::lonxml::redirection = 0; 632: $Apache::lonxml::metamode = 0; 633: $Apache::lonxml::evaluate = 1; 634: $Apache::lonxml::import = 1; 635: } else { 636: $Apache::lonxml::redirection = 0; 637: $Apache::lonxml::metamode = 0; 638: $Apache::lonxml::evaluate = 1; 639: $Apache::lonxml::import = 1; 640: } 641: } 642: 643: sub init_safespace { 644: my ($target,$safeeval,$safehole,$safeinit) = @_; 645: $safeeval->reval('use Math::Complex;'); 646: $safeeval->reval('use LaTeX::Table;'); 647: $safeeval->deny_only(':dangerous'); 648: $safeeval->permit_only(":default"); 649: $safeeval->permit("entereval"); 650: $safeeval->permit(":base_math"); 651: $safeeval->permit("sort"); 652: $safeeval->permit("time"); 653: $safeeval->permit("caller"); 654: $safeeval->deny("rand"); 655: $safeeval->deny("srand"); 656: $safeeval->deny(":base_io"); 657: $safehole->wrap(\&Apache::scripttag::xmlparse,$safeeval,'&xmlparse'); 658: $safehole->wrap(\&Apache::outputtags::multipart,$safeeval,'&multipart'); 659: $safehole->wrap(\&Apache::lonnet::EXT,$safeeval,'&EXT'); 660: $safehole->wrap(\&Apache::chemresponse::chem_standard_order,$safeeval, 661: '&chem_standard_order'); 662: $safehole->wrap(\&Apache::response::check_status,$safeeval,'&check_status'); 663: $safehole->wrap(\&Apache::response::implicit_multiplication,$safeeval,'&implicit_multiplication'); 664: 665: $safehole->wrap(\&Apache::lonmaxima::maxima_eval,$safeeval,'&maxima_eval'); 666: $safehole->wrap(\&Apache::lonmaxima::maxima_check,$safeeval,'&maxima_check'); 667: $safehole->wrap(\&Apache::lonmaxima::maxima_cas_formula_fix,$safeeval, 668: '&maxima_cas_formula_fix'); 669: 670: $safehole->wrap(\&Apache::lonr::r_eval,$safeeval,'&r_eval'); 671: $safehole->wrap(\&Apache::lonr::Rentry,$safeeval,'&Rentry'); 672: $safehole->wrap(\&Apache::lonr::Rarray,$safeeval,'&Rarray'); 673: $safehole->wrap(\&Apache::lonr::r_check,$safeeval,'&r_check'); 674: $safehole->wrap(\&Apache::lonr::r_cas_formula_fix,$safeeval, 675: '&r_cas_formula_fix'); 676: 677: $safehole->wrap(\&Apache::caparesponse::capa_formula_fix,$safeeval, 678: '&capa_formula_fix'); 679: 680: $safehole->wrap(\&Apache::lonlocal::locallocaltime,$safeeval, 681: '&locallocaltime'); 682: 683: $safehole->wrap(\&Math::Cephes::asin,$safeeval,'&asin'); 684: $safehole->wrap(\&Math::Cephes::acos,$safeeval,'&acos'); 685: $safehole->wrap(\&Math::Cephes::atan,$safeeval,'&atan'); 686: $safehole->wrap(\&Math::Cephes::sinh,$safeeval,'&sinh'); 687: $safehole->wrap(\&Math::Cephes::cosh,$safeeval,'&cosh'); 688: $safehole->wrap(\&Math::Cephes::tanh,$safeeval,'&tanh'); 689: $safehole->wrap(\&Math::Cephes::asinh,$safeeval,'&asinh'); 690: $safehole->wrap(\&Math::Cephes::acosh,$safeeval,'&acosh'); 691: $safehole->wrap(\&Math::Cephes::atanh,$safeeval,'&atanh'); 692: $safehole->wrap(\&Math::Cephes::erf,$safeeval,'&erf'); 693: $safehole->wrap(\&Math::Cephes::erfc,$safeeval,'&erfc'); 694: $safehole->wrap(\&Math::Cephes::j0,$safeeval,'&j0'); 695: $safehole->wrap(\&Math::Cephes::j1,$safeeval,'&j1'); 696: $safehole->wrap(\&Math::Cephes::jn,$safeeval,'&jn'); 697: $safehole->wrap(\&Math::Cephes::jv,$safeeval,'&jv'); 698: $safehole->wrap(\&Math::Cephes::y0,$safeeval,'&y0'); 699: $safehole->wrap(\&Math::Cephes::y1,$safeeval,'&y1'); 700: $safehole->wrap(\&Math::Cephes::yn,$safeeval,'&yn'); 701: $safehole->wrap(\&Math::Cephes::yv,$safeeval,'&yv'); 702: 703: $safehole->wrap(\&Math::Cephes::bdtr ,$safeeval,'&bdtr' ); 704: $safehole->wrap(\&Math::Cephes::bdtrc ,$safeeval,'&bdtrc' ); 705: $safehole->wrap(\&Math::Cephes::bdtri ,$safeeval,'&bdtri' ); 706: $safehole->wrap(\&Math::Cephes::btdtr ,$safeeval,'&btdtr' ); 707: $safehole->wrap(\&Math::Cephes::chdtr ,$safeeval,'&chdtr' ); 708: $safehole->wrap(\&Math::Cephes::chdtrc,$safeeval,'&chdtrc'); 709: $safehole->wrap(\&Math::Cephes::chdtri,$safeeval,'&chdtri'); 710: $safehole->wrap(\&Math::Cephes::fdtr ,$safeeval,'&fdtr' ); 711: $safehole->wrap(\&Math::Cephes::fdtrc ,$safeeval,'&fdtrc' ); 712: $safehole->wrap(\&Math::Cephes::fdtri ,$safeeval,'&fdtri' ); 713: $safehole->wrap(\&Math::Cephes::gdtr ,$safeeval,'&gdtr' ); 714: $safehole->wrap(\&Math::Cephes::gdtrc ,$safeeval,'&gdtrc' ); 715: $safehole->wrap(\&Math::Cephes::nbdtr ,$safeeval,'&nbdtr' ); 716: $safehole->wrap(\&Math::Cephes::nbdtrc,$safeeval,'&nbdtrc'); 717: $safehole->wrap(\&Math::Cephes::nbdtri,$safeeval,'&nbdtri'); 718: $safehole->wrap(\&Math::Cephes::ndtr ,$safeeval,'&ndtr' ); 719: $safehole->wrap(\&Math::Cephes::ndtri ,$safeeval,'&ndtri' ); 720: $safehole->wrap(\&Math::Cephes::pdtr ,$safeeval,'&pdtr' ); 721: $safehole->wrap(\&Math::Cephes::pdtrc ,$safeeval,'&pdtrc' ); 722: $safehole->wrap(\&Math::Cephes::pdtri ,$safeeval,'&pdtri' ); 723: $safehole->wrap(\&Math::Cephes::stdtr ,$safeeval,'&stdtr' ); 724: $safehole->wrap(\&Math::Cephes::stdtri,$safeeval,'&stdtri'); 725: 726: $safehole->wrap(\&Math::Cephes::Matrix::mat,$safeeval,'&mat'); 727: $safehole->wrap(\&Math::Cephes::Matrix::new,$safeeval, 728: '&Math::Cephes::Matrix::new'); 729: $safehole->wrap(\&Math::Cephes::Matrix::coef,$safeeval, 730: '&Math::Cephes::Matrix::coef'); 731: $safehole->wrap(\&Math::Cephes::Matrix::clr,$safeeval, 732: '&Math::Cephes::Matrix::clr'); 733: $safehole->wrap(\&Math::Cephes::Matrix::add,$safeeval, 734: '&Math::Cephes::Matrix::add'); 735: $safehole->wrap(\&Math::Cephes::Matrix::sub,$safeeval, 736: '&Math::Cephes::Matrix::sub'); 737: $safehole->wrap(\&Math::Cephes::Matrix::mul,$safeeval, 738: '&Math::Cephes::Matrix::mul'); 739: $safehole->wrap(\&Math::Cephes::Matrix::div,$safeeval, 740: '&Math::Cephes::Matrix::div'); 741: $safehole->wrap(\&Math::Cephes::Matrix::inv,$safeeval, 742: '&Math::Cephes::Matrix::inv'); 743: $safehole->wrap(\&Math::Cephes::Matrix::transp,$safeeval, 744: '&Math::Cephes::Matrix::transp'); 745: $safehole->wrap(\&Math::Cephes::Matrix::simq,$safeeval, 746: '&Math::Cephes::Matrix::simq'); 747: $safehole->wrap(\&Math::Cephes::Matrix::mat_to_vec,$safeeval, 748: '&Math::Cephes::Matrix::mat_to_vec'); 749: $safehole->wrap(\&Math::Cephes::Matrix::vec_to_mat,$safeeval, 750: '&Math::Cephes::Matrix::vec_to_mat'); 751: $safehole->wrap(\&Math::Cephes::Matrix::check,$safeeval, 752: '&Math::Cephes::Matrix::check'); 753: $safehole->wrap(\&Math::Cephes::Matrix::check,$safeeval, 754: '&Math::Cephes::Matrix::check'); 755: 756: # $safehole->wrap(\&Math::Cephes::new_fract,$safeeval,'&new_fract'); 757: # $safehole->wrap(\&Math::Cephes::radd,$safeeval,'&radd'); 758: # $safehole->wrap(\&Math::Cephes::rsub,$safeeval,'&rsub'); 759: # $safehole->wrap(\&Math::Cephes::rmul,$safeeval,'&rmul'); 760: # $safehole->wrap(\&Math::Cephes::rdiv,$safeeval,'&rdiv'); 761: # $safehole->wrap(\&Math::Cephes::euclid,$safeeval,'&euclid'); 762: 763: $safehole->wrap(\&Math::Random::random_beta,$safeeval,'&math_random_beta'); 764: $safehole->wrap(\&Math::Random::random_chi_square,$safeeval,'&math_random_chi_square'); 765: $safehole->wrap(\&Math::Random::random_exponential,$safeeval,'&math_random_exponential'); 766: $safehole->wrap(\&Math::Random::random_f,$safeeval,'&math_random_f'); 767: $safehole->wrap(\&Math::Random::random_gamma,$safeeval,'&math_random_gamma'); 768: $safehole->wrap(\&Math::Random::random_multivariate_normal,$safeeval,'&math_random_multivariate_normal'); 769: $safehole->wrap(\&Math::Random::random_multinomial,$safeeval,'&math_random_multinomial'); 770: $safehole->wrap(\&Math::Random::random_noncentral_chi_square,$safeeval,'&math_random_noncentral_chi_square'); 771: $safehole->wrap(\&Math::Random::random_noncentral_f,$safeeval,'&math_random_noncentral_f'); 772: $safehole->wrap(\&Math::Random::random_normal,$safeeval,'&math_random_normal'); 773: $safehole->wrap(\&Math::Random::random_permutation,$safeeval,'&math_random_permutation'); 774: $safehole->wrap(\&Math::Random::random_permuted_index,$safeeval,'&math_random_permuted_index'); 775: $safehole->wrap(\&Math::Random::random_uniform,$safeeval,'&math_random_uniform'); 776: $safehole->wrap(\&Math::Random::random_poisson,$safeeval,'&math_random_poisson'); 777: $safehole->wrap(\&Math::Random::random_uniform_integer,$safeeval,'&math_random_uniform_integer'); 778: $safehole->wrap(\&Math::Random::random_negative_binomial,$safeeval,'&math_random_negative_binomial'); 779: $safehole->wrap(\&Math::Random::random_binomial,$safeeval,'&math_random_binomial'); 780: $safehole->wrap(\&Math::Random::random_seed_from_phrase,$safeeval,'&random_seed_from_phrase'); 781: $safehole->wrap(\&Math::Random::random_set_seed_from_phrase,$safeeval,'&random_set_seed_from_phrase'); 782: $safehole->wrap(\&Math::Random::random_get_seed,$safeeval,'&random_get_seed'); 783: $safehole->wrap(\&Math::Random::random_set_seed,$safeeval,'&random_set_seed'); 784: $safehole->wrap(\&Apache::loncommon::languages,$safeeval,'&languages'); 785: $safehole->wrap(\&Apache::lonxml::error,$safeeval,'&LONCAPA_INTERNAL_ERROR'); 786: $safehole->wrap(\&Apache::lonxml::debug,$safeeval,'&LONCAPA_INTERNAL_DEBUG'); 787: $safehole->wrap(\&Apache::lonnet::logthis,$safeeval,'&LONCAPA_INTERNAL_LOGTHIS'); 788: $safehole->wrap(\&Apache::inputtags::finalizeawards,$safeeval,'&LONCAPA_INTERNAL_FINALIZEAWARDS'); 789: $safehole->wrap(\&Apache::caparesponse::get_sigrange,$safeeval,'&LONCAPA_INTERNAL_get_sigrange'); 790: # use Data::Dumper; 791: # $safehole->wrap(\&Data::Dumper::Dumper,$safeeval,'&LONCAPA_INTERNAL_Dumper'); 792: #need to inspect this class of ops 793: # $safeeval->deny(":base_orig"); 794: $safeeval->permit("require"); 795: $safeinit .= ';$external::target="'.$target.'";'; 796: &Apache::run::run($safeinit,$safeeval); 797: &initialize_rndseed($safeeval); 798: } 799: 800: sub clean_safespace { 801: my ($safeeval) = @_; 802: delete_package_recurse($safeeval->{Root}); 803: } 804: 805: sub delete_package_recurse { 806: my ($package) = @_; 807: my @subp; 808: { 809: no strict 'refs'; 810: while (my ($key,$val) = each(%{*{"$package\::"}})) { 811: if (!defined($val)) { next; } 812: local (*ENTRY) = $val; 813: if (defined *ENTRY{HASH} && $key =~ /::$/ && 814: $key ne "main::" && $key ne "<none>::") 815: { 816: my ($p) = $package ne "main" ? "$package\::" : ""; 817: ($p .= $key) =~ s/::$//; 818: push(@subp,$p); 819: } 820: } 821: } 822: foreach my $p (@subp) { 823: delete_package_recurse($p); 824: } 825: Symbol::delete_package($package); 826: } 827: 828: sub initialize_rndseed { 829: my ($safeeval)=@_; 830: my $rndseed; 831: my ($symb,$courseid,$domain,$name) = &Apache::lonnet::whichuser(); 832: $rndseed=&Apache::lonnet::rndseed($symb,$courseid,$domain,$name); 833: my $safeinit = '$external::randomseed="'.$rndseed.'";'; 834: &Apache::lonxml::debug("Setting rndseed to $rndseed"); 835: &Apache::run::run($safeinit,$safeeval); 836: } 837: 838: sub default_homework_load { 839: my ($safeeval)=@_; 840: &Apache::lonxml::debug('Loading default_homework'); 841: my $default=&Apache::lonnet::getfile('/home/httpd/html/res/adm/includes/default_homework.lcpm'); 842: if ($default eq -1) { 843: &Apache::lonxml::error("<b>Unable to find <i>default_homework.lcpm</i></b>"); 844: } else { 845: &Apache::run::run($default,$safeeval); 846: $Apache::lonxml::default_homework_loaded=1; 847: } 848: } 849: 850: { 851: my $alarm_depth; 852: sub init_alarm { 853: alarm(0); 854: $alarm_depth=0; 855: } 856: 857: sub start_alarm { 858: if ($alarm_depth<1) { 859: my $old=alarm($Apache::lonnet::perlvar{'lonScriptTimeout'}); 860: if ($old) { 861: &Apache::lonxml::error("Cancelled an alarm of $old, this shouldn't occur."); 862: } 863: } 864: $alarm_depth++; 865: } 866: 867: sub end_alarm { 868: $alarm_depth--; 869: if ($alarm_depth<1) { alarm(0); } 870: } 871: } 872: my $metamode_was; 873: sub startredirection { 874: if (!$Apache::lonxml::redirection) { 875: $metamode_was=$Apache::lonxml::metamode; 876: } 877: $Apache::lonxml::metamode=0; 878: $Apache::lonxml::redirection++; 879: push (@Apache::lonxml::outputstack, ''); 880: } 881: 882: sub endredirection { 883: if (!$Apache::lonxml::redirection) { 884: &Apache::lonxml::error("Endredirection was called before a startredirection, perhaps you have unbalanced tags. Some debugging information:".join ":",caller); 885: return ''; 886: } 887: $Apache::lonxml::redirection--; 888: if (!$Apache::lonxml::redirection) { 889: $Apache::lonxml::metamode=$metamode_was; 890: } 891: pop @Apache::lonxml::outputstack; 892: } 893: sub in_redirection { 894: return ($Apache::lonxml::redirection > 0) 895: } 896: 897: sub end_tag { 898: my ($tagstack,$parstack,$token)=@_; 899: pop(@$tagstack); 900: pop(@$parstack); 901: &decreasedepth($token); 902: } 903: 904: sub initdepth { 905: @Apache::lonxml::depthcounter=(); 906: undef($Apache::lonxml::last_depth_count); 907: } 908: 909: 910: my @timers; 911: my $lasttime; 912: # @Apache::lonxml::depthcounter -> count of tags that exist so 913: # far at each level 914: # $Apache::lonxml::last_depth_count -> when ascending, need to 915: # remember the count for the level below the current level (for 916: # example going from 1_2 -> 1 -> 1_3 need to remember the 2 ) 917: 918: sub increasedepth { 919: my ($token) = @_; 920: push(@Apache::lonxml::depthcounter,$Apache::lonxml::last_depth_count+1); 921: undef($Apache::lonxml::last_depth_count); 922: my $time; 923: if ($Apache::lonxml::debug eq "1") { 924: push(@timers,[&gettimeofday()]); 925: $time=&tv_interval($lasttime); 926: $lasttime=[&gettimeofday()]; 927: } 928: my $spacing=' 'x($#Apache::lonxml::depthcounter); 929: $Apache::lonxml::curdepth=join('_',@Apache::lonxml::depthcounter); 930: # &Apache::lonxml::debug("s$spacing$Apache::lonxml::depth : $Apache::lonxml::olddepth : $Apache::lonxml::curdepth : $token->[1] : $time"); 931: #print "<br />s $Apache::lonxml::depth : $Apache::lonxml::olddepth : $curdepth : $token->[1]\n"; 932: } 933: 934: sub decreasedepth { 935: my ($token) = @_; 936: if ( $#Apache::lonxml::depthcounter == -1) { 937: &Apache::lonxml::warning(&mt("Missing tags, unable to properly run file.")); 938: } 939: $Apache::lonxml::last_depth_count = pop(@Apache::lonxml::depthcounter); 940: 941: my ($timer,$time); 942: if ($Apache::lonxml::debug eq "1") { 943: $timer=pop(@timers); 944: $time=&tv_interval($lasttime); 945: $lasttime=[&gettimeofday()]; 946: } 947: my $spacing=' 'x($#Apache::lonxml::depthcounter); 948: $Apache::lonxml::curdepth = join('_',@Apache::lonxml::depthcounter); 949: # &Apache::lonxml::debug("e$spacing$Apache::lonxml::depth : $Apache::lonxml::olddepth : $Apache::lonxml::curdepth : $token->[1] : $time : ".&tv_interval($timer)); 950: #print "<br />e $Apache::lonxml::depth : $Apache::lonxml::olddepth : $token->[1] : $curdepth\n"; 951: } 952: 953: sub get_id { 954: my ($parstack,$safeeval)=@_; 955: my $id= &Apache::lonxml::get_param('id',$parstack,$safeeval); 956: if ($env{'request.state'} eq 'construct' && $id =~ /([._]|[^\w\d\s[:punct:]])/) { 957: &error(&mt('ID [_1] contains invalid characters. IDs are only allowed to contain letters, numbers, spaces and -','"<tt>'.$id.'</tt>"')); 958: } 959: if ($id =~ /^\s*$/) { $id = $Apache::lonxml::curdepth; } 960: return $id; 961: } 962: 963: sub get_all_text_unbalanced { 964: #there is a copy of this in lonpublisher.pm 965: my($tag,$pars)= @_; 966: my $token; 967: my $result=''; 968: $tag='<'.$tag.'>'; 969: while ($token = $$pars[-1]->get_token) { 970: if (($token->[0] eq 'T')||($token->[0] eq 'C')||($token->[0] eq 'D')) { 971: if ($token->[0] eq 'T' && $token->[2]) { 972: $result.='<![CDATA['.$token->[1].']]>'; 973: } else { 974: $result.=$token->[1]; 975: } 976: } elsif ($token->[0] eq 'PI') { 977: $result.=$token->[2]; 978: } elsif ($token->[0] eq 'S') { 979: $result.=$token->[4]; 980: } elsif ($token->[0] eq 'E') { 981: $result.=$token->[2]; 982: } 983: if ($result =~ /\Q$tag\E/is) { 984: ($result,my $redo)=$result =~ /(.*)\Q$tag\E(.*)/is; 985: #&Apache::lonxml::debug('Got a winner with leftovers ::'.$2); 986: #&Apache::lonxml::debug('Result is :'.$1); 987: $redo=$tag.$redo; 988: &Apache::lonxml::newparser($pars,\$redo); 989: last; 990: } 991: } 992: return $result 993: 994: } 995: 996: ######################################################################### 997: # # 998: # bubble line counter management # 999: # # 1000: ######################################################################### 1001: 1002: =pod 1003: 1004: For bubble grading mode and exam bubble printing mode, the tracking of 1005: the current 'bubble line number' is stored in the %env element 1006: 'form.counter', and is modifed and handled by the following routines. 1007: 1008: The value of it is stored in $Apache:lonxml::counter when live and 1009: stored back to env after done. 1010: 1011: =item &increment_counter($increment); 1012: 1013: Increments the internal counter environment variable a specified amount 1014: 1015: Optional Arguments: 1016: $increment - amount to increment by (defaults to 1) 1017: Also 1 if the value is negative or zero. 1018: $part_response - A concatenation of the part and response id 1019: identifying exactly what is being 'answered'. 1020: 1021: 1022: =cut 1023: 1024: sub increment_counter { 1025: my ($increment, $part_response) = @_; 1026: if ($env{'form.grade_noincrement'}) { return; } 1027: if (!defined($increment) || $increment le 0) { 1028: $increment = 1; 1029: } 1030: $Apache::lonxml::counter += $increment; 1031: 1032: # If the caller supplied the response_id parameter, 1033: # Maintain its counter.. creating if necessary. 1034: 1035: if (defined($part_response)) { 1036: if (!defined($Apache::lonxml::counters_per_part{$part_response})) { 1037: $Apache::lonxml::counters_per_part{$part_response} = 0; 1038: } 1039: $Apache::lonxml::counters_per_part{$part_response} += $increment; 1040: my $new_value = $Apache::lonxml::counters_per_part{$part_response}; 1041: } 1042: 1043: $Apache::lonxml::counter_changed=1; 1044: } 1045: 1046: =pod 1047: 1048: =item &init_counter($increment); 1049: 1050: Initialize the internal counter environment variable 1051: 1052: =cut 1053: 1054: sub init_counter { 1055: if ($env{'request.state'} eq 'construct') { 1056: $Apache::lonxml::counter=1; 1057: $Apache::lonxml::counter_changed=1; 1058: } elsif (defined($env{'form.counter'})) { 1059: $Apache::lonxml::counter=$env{'form.counter'}; 1060: $Apache::lonxml::counter_changed=0; 1061: } else { 1062: $Apache::lonxml::counter=1; 1063: $Apache::lonxml::counter_changed=1; 1064: } 1065: } 1066: 1067: sub store_counter { 1068: &Apache::lonnet::appenv({'form.counter' => $Apache::lonxml::counter}); 1069: $Apache::lonxml::counter_changed=0; 1070: return ''; 1071: } 1072: 1073: { 1074: my $state; 1075: sub clear_problem_counter { 1076: undef($state); 1077: &Apache::lonnet::delenv('form.counter'); 1078: &Apache::lonxml::init_counter(); 1079: &Apache::lonxml::store_counter(); 1080: } 1081: 1082: sub remember_problem_counter { 1083: &Apache::lonnet::transfer_profile_to_env(undef,undef,1); 1084: $state = $env{'form.counter'}; 1085: } 1086: 1087: sub restore_problem_counter { 1088: if (defined($state)) { 1089: &Apache::lonnet::appenv({'form.counter' => $state}); 1090: } 1091: } 1092: sub get_problem_counter { 1093: if ($Apache::lonxml::counter_changed) { &store_counter() } 1094: &Apache::lonnet::transfer_profile_to_env(undef,undef,1); 1095: return $env{'form.counter'}; 1096: } 1097: } 1098: 1099: =pod 1100: 1101: =item bubble_lines_for_part(part_response) 1102: 1103: Returns the number of lines required to get a response for 1104: $part_response (this is just $Apache::lonxml::counters_per_part{$part_response} 1105: 1106: =cut 1107: 1108: sub bubble_lines_for_part { 1109: my ($part_response) = @_; 1110: 1111: if (!defined($Apache::lonxml::counters_per_part{$part_response})) { 1112: return 0; 1113: } else { 1114: return $Apache::lonxml::counters_per_part{$part_response}; 1115: } 1116: } 1117: 1118: =pod 1119: 1120: =item clear_bubble_lines_for_part 1121: 1122: Clears the hash of bubble lines per part. If a caller 1123: needs to analyze several resources this should be called between 1124: resources to reset the hash for each problem being analyzed. 1125: 1126: =cut 1127: 1128: sub clear_bubble_lines_for_part { 1129: undef(%Apache::lonxml::counters_per_part); 1130: } 1131: 1132: =pod 1133: 1134: =item set_bubble_lines(part_response, value) 1135: 1136: If there is a problem part, that for whatever reason 1137: requires bubble lines that are not 1138: the same as the counter increment, it can call this sub during 1139: analysis to set its hash value explicitly. 1140: 1141: =cut 1142: 1143: sub set_bubble_lines { 1144: my ($part_response, $value) = @_; 1145: 1146: $Apache::lonxml::counters_per_part{$part_response} = $value; 1147: } 1148: 1149: =pod 1150: 1151: =item get_bubble_line_hash 1152: 1153: Returns the current bubble line hash. This is assumed to 1154: be small so we return a copy 1155: 1156: 1157: =cut 1158: 1159: sub get_bubble_line_hash { 1160: return %Apache::lonxml::counters_per_part; 1161: } 1162: 1163: 1164: #-------------------------------------------------- 1165: 1166: sub get_all_text { 1167: my($tag,$pars,$style)= @_; 1168: my $gotfullstack=1; 1169: if (ref($pars) ne 'ARRAY') { 1170: $gotfullstack=0; 1171: $pars=[$pars]; 1172: } 1173: if (ref($style) ne 'HASH') { 1174: $style={}; 1175: } 1176: my $depth=0; 1177: my $token; 1178: my $result=''; 1179: if ( $tag =~ m:^/: ) { 1180: my $tag=substr($tag,1); 1181: #&Apache::lonxml::debug("have:$tag:"); 1182: my $top_empty=0; 1183: while (($depth >=0) && ($#$pars > -1) && (!$top_empty)) { 1184: while (($depth >=0) && ($token = $$pars[-1]->get_token)) { 1185: #&Apache::lonxml::debug("e token:$token->[0]:$depth:$token->[1]:".$#$pars.":".$#Apache::lonxml::pwd); 1186: if (($token->[0] eq 'T')||($token->[0] eq 'C')||($token->[0] eq 'D')) { 1187: if ($token->[2]) { 1188: $result.='<![CDATA['.$token->[1].']]>'; 1189: } else { 1190: $result.=$token->[1]; 1191: } 1192: } elsif ($token->[0] eq 'PI') { 1193: $result.=$token->[2]; 1194: } elsif ($token->[0] eq 'S') { 1195: if ($token->[1] =~ /^\Q$tag\E$/i) { $depth++; } 1196: if ($token->[1] =~ /^LONCAPA_INTERNAL_TURN_STYLE_ON$/) { $Apache::lonxml::usestyle=1; } 1197: if ($token->[1] =~ /^LONCAPA_INTERNAL_TURN_STYLE_OFF$/) { $Apache::lonxml::usestyle=0; } 1198: $result.=$token->[4]; 1199: } elsif ($token->[0] eq 'E') { 1200: if ( $token->[1] =~ /^\Q$tag\E$/i) { $depth--; } 1201: #skip sending back the last end tag 1202: if ($depth == 0 && exists($$style{'/'.$token->[1]}) && $Apache::lonxml::usestyle) { 1203: my $string= 1204: '<LONCAPA_INTERNAL_TURN_STYLE_OFF end="yes" />'. 1205: $$style{'/'.$token->[1]}. 1206: $token->[2]. 1207: '<LONCAPA_INTERNAL_TURN_STYLE_ON />'; 1208: &Apache::lonxml::newparser($pars,\$string); 1209: #&Apache::lonxml::debug("reParsing $string"); 1210: next; 1211: } 1212: if ($depth > -1) { 1213: $result.=$token->[2]; 1214: } else { 1215: $$pars[-1]->unget_token($token); 1216: } 1217: } 1218: } 1219: if (($depth >=0) && ($#$pars == 0) ) { $top_empty=1; } 1220: if (($depth >=0) && ($#$pars > 0) ) { 1221: pop(@$pars); 1222: pop(@Apache::lonxml::pwd); 1223: } 1224: } 1225: if ($top_empty && $depth >= 0) { 1226: #never found the end tag ran out of text, throw error send back blank 1227: &error('Never found end tag for <'.$tag. 1228: '> current string <pre>'. 1229: &HTML::Entities::encode($result,'<>&"'). 1230: '</pre>'); 1231: if ($gotfullstack) { 1232: my $newstring='</'.$tag.'>'.$result; 1233: &Apache::lonxml::newparser($pars,\$newstring); 1234: } 1235: $result=''; 1236: } 1237: } else { 1238: while ($#$pars > -1) { 1239: while ($token = $$pars[-1]->get_token) { 1240: #&Apache::lonxml::debug("s token:$token->[0]:$depth:$token->[1]"); 1241: if (($token->[0] eq 'T')||($token->[0] eq 'C')|| 1242: ($token->[0] eq 'D')) { 1243: if ($token->[2]) { 1244: $result.='<![CDATA['.$token->[1].']]>'; 1245: } else { 1246: $result.=$token->[1]; 1247: } 1248: } elsif ($token->[0] eq 'PI') { 1249: $result.=$token->[2]; 1250: } elsif ($token->[0] eq 'S') { 1251: if ( $token->[1] =~ /^\Q$tag\E$/i) { 1252: $$pars[-1]->unget_token($token); last; 1253: } else { 1254: $result.=$token->[4]; 1255: } 1256: if ($token->[1] =~ /^LONCAPA_INTERNAL_TURN_STYLE_ON$/) { $Apache::lonxml::usestyle=1; } 1257: if ($token->[1] =~ /^LONCAPA_INTERNAL_TURN_STYLE_OFF$/) { $Apache::lonxml::usestyle=0; } 1258: } elsif ($token->[0] eq 'E') { 1259: $result.=$token->[2]; 1260: } 1261: } 1262: if (($#$pars > 0) ) { 1263: pop(@$pars); 1264: pop(@Apache::lonxml::pwd); 1265: } else { last; } 1266: } 1267: } 1268: #&Apache::lonxml::debug("Exit:$result:"); 1269: return $result 1270: } 1271: 1272: sub newparser { 1273: my ($parser,$contentref,$dir) = @_; 1274: push (@$parser,HTML::LCParser->new($contentref)); 1275: $$parser[-1]->xml_mode(1); 1276: $$parser[-1]->marked_sections(1); 1277: if ( $dir eq '' ) { 1278: push (@Apache::lonxml::pwd, $Apache::lonxml::pwd[$#Apache::lonxml::pwd]); 1279: } else { 1280: push (@Apache::lonxml::pwd, $dir); 1281: } 1282: } 1283: 1284: sub parstring { 1285: my ($token) = @_; 1286: my (@vars,@values); 1287: foreach my $attr (@{$token->[3]}) { 1288: if ($attr!~/\W/) { 1289: my $val=$token->[2]->{$attr}; 1290: $val =~ s/([\%\@\\\"\'])/\\$1/g; 1291: $val =~ s/(\$[^\{a-zA-Z_])/\\$1/g; 1292: $val =~ s/(\$)$/\\$1/; 1293: #if ($val =~ m/^[\%\@]/) { $val="\\".$val; } 1294: push(@vars,"\$$attr"); 1295: push(@values,"\"$val\""); 1296: } 1297: } 1298: my $var_init = 1299: (@vars) ? 'my ('.join(',',@vars).') = ('.join(',',@values).');' 1300: : ''; 1301: return $var_init; 1302: } 1303: 1304: sub extlink { 1305: my ($res,$exact)=@_; 1306: if (!$exact) { 1307: $res=&Apache::lonnet::hreflocation($Apache::lonxml::pwd[-1],$res); 1308: } 1309: push(@Apache::lonxml::extlinks,$res) 1310: } 1311: 1312: sub writeallows { 1313: unless ($#extlinks>=0) { return; } 1314: my $thisurl = &Apache::lonnet::clutter(shift); 1315: if ($env{'httpref.'.$thisurl}) { 1316: $thisurl=$env{'httpref.'.$thisurl}; 1317: } 1318: my $thisdir=$thisurl; 1319: $thisdir=~s/\/[^\/]+$//; 1320: my %httpref=(); 1321: foreach (@extlinks) { 1322: $httpref{'httpref.'. 1323: &Apache::lonnet::hreflocation($thisdir,&unescape($_))}=$thisurl; 1324: } 1325: @extlinks=(); 1326: &Apache::lonnet::appenv(\%httpref); 1327: } 1328: 1329: sub register_ssi { 1330: my ($url,%form)=@_; 1331: push (@Apache::lonxml::ssi_info,{'url'=>$url,'form'=>\%form}); 1332: return ''; 1333: } 1334: 1335: sub do_registered_ssi { 1336: foreach my $info (@Apache::lonxml::ssi_info) { 1337: my %form=%{ $info->{'form'}}; 1338: my $url=$info->{'url'}; 1339: &Apache::lonnet::ssi($url,%form); 1340: } 1341: } 1342: 1343: sub add_script_result { 1344: my ($display) = @_; 1345: push(@script_var_displays, $display); 1346: } 1347: 1348: # 1349: # Afterburner handles anchors, highlights and links 1350: # 1351: sub afterburn { 1352: my $result=shift; 1353: &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'}, 1354: ['highlight','anchor','link']); 1355: if ($env{'form.highlight'}) { 1356: foreach (split(/\,/,$env{'form.highlight'})) { 1357: my $anchorname=$_; 1358: my $matchthis=$anchorname; 1359: $matchthis=~s/\_+/\\s\+/g; 1360: $result=~s/(\Q$matchthis\E)/\<font color=\"red\"\>$1\<\/font\>/gs; 1361: } 1362: } 1363: if ($env{'form.link'}) { 1364: foreach (split(/\,/,$env{'form.link'})) { 1365: my ($anchorname,$linkurl)=split(/\>/,$_); 1366: my $matchthis=$anchorname; 1367: $matchthis=~s/\_+/\\s\+/g; 1368: $result=~s/(\Q$matchthis\E)/\<a href=\"$linkurl\"\>$1\<\/a\>/gs; 1369: } 1370: } 1371: if ($env{'form.anchor'}) { 1372: my $anchorname=$env{'form.anchor'}; 1373: my $matchthis=$anchorname; 1374: $matchthis=~s/\_+/\\s\+/g; 1375: $result=~s/(\Q$matchthis\E)/\<a name=\"$anchorname\"\>$1\<\/a\>/s; 1376: $result.=(<<"ENDSCRIPT"); 1377: <script type="text/javascript"> 1378: document.location.hash='$anchorname'; 1379: </script> 1380: ENDSCRIPT 1381: } 1382: return $result; 1383: } 1384: 1385: sub storefile { 1386: my ($file,$contents)=@_; 1387: &Apache::lonnet::correct_line_ends(\$contents); 1388: if (my $fh=Apache::File->new('>'.$file)) { 1389: print $fh $contents; 1390: $fh->close(); 1391: return 1; 1392: } else { 1393: &warning(&mt('Unable to save file [_1]','<tt>'.$file.'</tt>')); 1394: return 0; 1395: } 1396: } 1397: 1398: sub createnewhtml { 1399: my $title=&mt('Title of document goes here'); 1400: my $body=&mt('Body of document goes here'); 1401: my $filecontents=(<<SIMPLECONTENT); 1402: <html> 1403: <head> 1404: <title>$title</title> 1405: </head> 1406: <body bgcolor="#FFFFFF"> 1407: $body 1408: </body> 1409: </html> 1410: SIMPLECONTENT 1411: return $filecontents; 1412: } 1413: 1414: sub createnewsty { 1415: my $filecontents=(<<SIMPLECONTENT); 1416: <definetag name=""> 1417: <render> 1418: <web></web> 1419: <tex></tex> 1420: </render> 1421: </definetag> 1422: SIMPLECONTENT 1423: return $filecontents; 1424: } 1425: 1426: sub createnewjs { 1427: my $filecontents=(<<SIMPLECONTENT); 1428: <script type="text/javascript" language="Javascript"> 1429: 1430: </script> 1431: SIMPLECONTENT 1432: return $filecontents; 1433: } 1434: 1435: sub verify_html { 1436: my ($filecontents)=@_; 1437: my ($is_html,$is_xml); 1438: if ($filecontents =~/(?:\<|\<\;)\?xml[^\<]*\?(?:\>|\>\;)/is) { 1439: $is_xml = 1; 1440: } elsif ($filecontents =~/(?:\<|\<\;)html(?:\s+[^\<]+|\s*)[^\<]*(?:\>|\>\;)/is) { 1441: $is_html = 1; 1442: } 1443: unless ($is_xml || $is_html) { 1444: return &mt('File does not have [_1] or [_2] starting tag','<html>','<?xml ?>'); 1445: } 1446: if ($is_html) { 1447: if ($filecontents!~/(?:\<|\<\;)\/html(?:\>|\>\;)/is) { 1448: return &mt('File does not have [_1] ending tag','<html>'); 1449: } 1450: if ($filecontents!~/(?:\<|\<\;)(?:body|frameset)[^\<]*(?:\>|\>\;)/is) { 1451: return &mt('File does not have [_1] or [_2] starting tag','<body>','<frameset>'); 1452: } 1453: if ($filecontents!~/(?:\<|\<\;)\/(?:body|frameset)[^\<]*(?:\>|\>\;)/is) { 1454: return &mt('File does not have [_1] or [_2] ending tag','<body>','<frameset>'); 1455: } 1456: } 1457: return ''; 1458: } 1459: 1460: sub renderingoptions { 1461: my %langchoices=('' => ''); 1462: foreach (&Apache::loncommon::languageids()) { 1463: if (&Apache::loncommon::supportedlanguagecode($_)) { 1464: $langchoices{&Apache::loncommon::supportedlanguagecode($_)} 1465: = &Apache::loncommon::plainlanguagedescription($_); 1466: } 1467: } 1468: my $output; 1469: unless ($env{'form.forceedit'}) { 1470: $output .= 1471: '<span class="LC_nobreak">'. 1472: &mt('Language:').' '. 1473: &Apache::loncommon::select_form( 1474: $env{'form.languages'}, 1475: 'languages', 1476: {&Apache::lonlocal::texthash(%langchoices)}). 1477: '</span>'; 1478: } 1479: $output .= 1480: ' <span class="LC_nobreak">'. 1481: &mt('Math Rendering:').' '. 1482: &Apache::loncommon::select_form( 1483: $env{'form.texengine'}, 1484: 'texengine', 1485: {&Apache::lonlocal::texthash 1486: ('' => '', 1487: 'tth' => 'tth (TeX to HTML)', 1488: 'jsMath' => 'jsMath', 1489: 'mimetex' => 'mimetex (Convert to Images)')}). 1490: '</span>'; 1491: return $output; 1492: } 1493: 1494: sub inserteditinfo { 1495: my ($filecontents, $filetype, $filename)=@_; 1496: $filecontents = &HTML::Entities::encode($filecontents,'<>&"'); 1497: my $xml_help = ''; 1498: my $initialize=''; 1499: my $textarea_id = 'filecont'; 1500: my $dragmath_button; 1501: my ($add_to_onload, $add_to_onresize); 1502: $initialize=&Apache::lonhtmlcommon::spellheader(); 1503: if (($filetype eq 'html') && (&Apache::lonhtmlcommon::htmlareabrowser())) { 1504: my $lang = &Apache::lonhtmlcommon::htmlarea_lang(); 1505: my %textarea_args = ( 1506: fullpage => 'true', 1507: dragmath => 'math', 1508: ); 1509: $initialize .= &Apache::lonhtmlcommon::htmlareaselectactive(\%textarea_args); 1510: } 1511: $initialize .= (<<FULLPAGE); 1512: <script type="text/javascript"> 1513: // <![CDATA[ 1514: function initDocument() { 1515: resize_textarea('$textarea_id','LC_aftertextarea'); 1516: } 1517: // ]]> 1518: </script> 1519: FULLPAGE 1520: if ($filetype eq 'html') { 1521: $dragmath_button = '<span id="math_filecont">'.&Apache::lonhtmlcommon::dragmath_button('filecont',1).'</span>'; 1522: $initialize .= "\n".&Apache::lonhtmlcommon::dragmath_js('EditMathPopup'); 1523: } 1524: $add_to_onload = 'initDocument();'; 1525: $add_to_onresize = "resize_textarea('$textarea_id','LC_aftertextarea');"; 1526: 1527: if ($filetype eq 'html') { 1528: $xml_help=&Apache::loncommon::helpLatexCheatsheet(); 1529: } 1530: 1531: my $titledisplay=&display_title(); 1532: my $textareaclass; 1533: my %lt=&Apache::lonlocal::texthash('st' => 'Save and Edit', 1534: 'vi' => 'Save and View', 1535: 'dv' => 'Discard Edits and View', 1536: 'un' => 'undo', 1537: 'ed' => 'Edit'); 1538: my $spelllink = &Apache::lonhtmlcommon::spelllink('xmledit','filecont'); 1539: my $textarea_events = &Apache::edit::element_change_detection(); 1540: my $form_events = &Apache::edit::form_change_detection(); 1541: my $htmlerror; 1542: if ($filetype eq 'html') { 1543: $htmlerror=&verify_html($filecontents); 1544: if ($htmlerror) { 1545: $htmlerror='<span class="LC_error">'.$htmlerror.'</span>'; 1546: } 1547: if (&Apache::lonhtmlcommon::htmlareabrowser()) { 1548: $textareaclass = 'class="LC_richDefaultOff"'; 1549: } 1550: } 1551: my $editfooter=(<<ENDFOOTER); 1552: $initialize 1553: <a name="editsection" /> 1554: <form $form_events method="post" name="xmledit"> 1555: <div class="LC_edit_problem_editxml_header"> 1556: <table class="LC_edit_problem_header_title"><tr><td> 1557: $filename 1558: </td><td align="right"> 1559: $xml_help 1560: </td></tr> 1561: </table> 1562: <div class="LC_edit_problem_discards"> 1563: <input type="submit" name="discardview" accesskey="d" value="$lt{'dv'}" /> 1564: <input type="submit" name="Undo" accesskey="u" value="$lt{'un'}" /> 1565: $htmlerror $dragmath_button 1566: </div> 1567: <div class="LC_edit_problem_saves"> 1568: <input type="submit" name="savethisfile" accesskey="s" value="$lt{'st'}" /> 1569: <input type="submit" name="viewmode" accesskey="v" value="$lt{'vi'}" /> 1570: </div> 1571: </div> 1572: <textarea $textarea_events style="width:100%" cols="80" rows="44" name="filecont" id="filecont" $textareaclass>$filecontents</textarea><br />$spelllink 1573: <div id="LC_aftertextarea"> 1574: <br /> 1575: $titledisplay 1576: </div> 1577: </form> 1578: </body> 1579: ENDFOOTER 1580: return ($editfooter,$add_to_onload,$add_to_onresize);; 1581: } 1582: 1583: sub get_target { 1584: my $viewgrades=&Apache::lonnet::allowed('vgr',$env{'request.course.id'}); 1585: if ( $env{'request.state'} eq 'published') { 1586: if ( defined($env{'form.grade_target'}) 1587: && ($viewgrades == 'F' )) { 1588: return ($env{'form.grade_target'}); 1589: } elsif (defined($env{'form.grade_target'})) { 1590: if (($env{'form.grade_target'} eq 'web') || 1591: ($env{'form.grade_target'} eq 'tex') ) { 1592: return $env{'form.grade_target'} 1593: } else { 1594: return 'web'; 1595: } 1596: } else { 1597: return 'web'; 1598: } 1599: } elsif ($env{'request.state'} eq 'construct') { 1600: if ( defined($env{'form.grade_target'})) { 1601: return ($env{'form.grade_target'}); 1602: } else { 1603: return 'web'; 1604: } 1605: } else { 1606: return 'web'; 1607: } 1608: } 1609: 1610: sub handler { 1611: my $request=shift; 1612: 1613: my $target=&get_target(); 1614: $Apache::lonxml::debug=$env{'user.debug'}; 1615: 1616: &Apache::loncommon::content_type($request,'text/html'); 1617: &Apache::loncommon::no_cache($request); 1618: if ($env{'request.state'} eq 'published') { 1619: $request->set_last_modified(&Apache::lonnet::metadata($request->uri, 1620: 'lastrevisiondate')); 1621: } 1622: # Embedded Flash movies from Camtasia served from https will not display in IE 1623: # if XML config file has expired from cache. 1624: if ($ENV{'SERVER_PORT'} == 443) { 1625: if ($request->uri =~ /\.xml$/) { 1626: my ($httpbrowser,$clientbrowser) = 1627: &Apache::loncommon::decode_user_agent($request); 1628: if ($clientbrowser =~ /^explorer$/i) { 1629: delete $request->headers_out->{'Cache-control'}; 1630: delete $request->headers_out->{'Pragma'}; 1631: my $expiration = time + 60; 1632: my $date=strftime("%a, %d %b %Y %H:%M:%S GMT",gmtime($expiration)); 1633: $request->headers_out->set("Expires" => $date); 1634: } 1635: } 1636: } 1637: $request->send_http_header; 1638: 1639: return OK if $request->header_only; 1640: 1641: 1642: my $file=&Apache::lonnet::filelocation("",$request->uri); 1643: my ($filetype,$breadcrumbtext); 1644: if ($file =~ /\.(sty|css|js|txt|tex)$/) { 1645: $filetype=$1; 1646: } else { 1647: $filetype='html'; 1648: } 1649: if ($filetype eq 'sty') { 1650: $breadcrumbtext = 'Style File Editor'; 1651: } elsif ($filetype eq 'js') { 1652: $breadcrumbtext = 'Javascript Editor'; 1653: } elsif ($filetype eq 'css') { 1654: $breadcrumbtext = 'CSS Editor'; 1655: } elsif ($filetype eq 'txt') { 1656: $breadcrumbtext = 'Text Editor'; 1657: } elsif ($filetype eq 'tex') { 1658: $breadcrumbtext = 'TeX Editor'; 1659: } else { 1660: $breadcrumbtext = 'HTML Editor'; 1661: } 1662: 1663: # 1664: # Edit action? Save file. 1665: # 1666: if (!($env{'request.state'} eq 'published')) { 1667: if ($env{'form.savethisfile'} || $env{'form.viewmode'} || $env{'form.Undo'}) { 1668: my $html_file=&Apache::lonnet::getfile($file); 1669: my $error = &Apache::lonhomework::handle_save_or_undo($request, \$html_file, \$env{'form.filecont'}); 1670: if ($env{'form.savethisfile'}) { 1671: $env{'form.editmode'}='Edit'; #force edit mode 1672: } 1673: } 1674: } 1675: my %mystyle; 1676: my $result = ''; 1677: my $filecontents=&Apache::lonnet::getfile($file); 1678: if ($filecontents eq -1) { 1679: my $start_page=&Apache::loncommon::start_page('File Error'); 1680: my $end_page=&Apache::loncommon::end_page(); 1681: my $errormsg='<p class="LC_error">' 1682: .&mt('File not found: [_1]' 1683: ,'<span class="LC_filename">'.$file.'</span>') 1684: .'</p>'; 1685: $result=(<<ENDNOTFOUND); 1686: $start_page 1687: $errormsg 1688: $end_page 1689: ENDNOTFOUND 1690: $filecontents=''; 1691: if ($env{'request.state'} ne 'published') { 1692: if ($filetype eq 'sty') { 1693: $filecontents=&createnewsty(); 1694: } elsif ($filetype eq 'js') { 1695: $filecontents=&createnewjs(); 1696: } elsif ($filetype ne 'css' && $filetype ne 'txt' && $filetype ne 'tex') { 1697: $filecontents=&createnewhtml(); 1698: } 1699: $env{'form.editmode'}='Edit'; #force edit mode 1700: } 1701: } else { 1702: unless ($env{'request.state'} eq 'published') { 1703: if ($filecontents=~/BEGIN LON-CAPA Internal/) { 1704: &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.')); 1705: } 1706: # 1707: # we are in construction space, see if edit mode forced 1708: &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'}, 1709: ['editmode']); 1710: } 1711: if (!$env{'form.editmode'} || $env{'form.viewmode'} || $env{'form.discardview'}) { 1712: if ($filetype eq 'html' || $filetype eq 'sty') { 1713: &Apache::structuretags::reset_problem_globals(); 1714: $result = &Apache::lonxml::xmlparse($request,$target, 1715: $filecontents,'',%mystyle); 1716: # .html files may contain <problem> or <Task> need to clean 1717: # up if it did 1718: &Apache::structuretags::reset_problem_globals(); 1719: &Apache::lonhomework::finished_parsing(); 1720: } elsif ($filetype eq 'tex') { 1721: $result = &Apache::lontexconvert::converted(\$filecontents, 1722: $env{'form.texengine'}); 1723: if ($env{'form.return_only_error_and_warning_counts'}) { 1724: $result = "$errorcount:$warningcount"; 1725: } 1726: } else { 1727: $result = $filecontents; 1728: } 1729: &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'}, 1730: ['rawmode']); 1731: if ($env{'form.rawmode'}) { $result = $filecontents; } 1732: if (($filetype ne 'html') && 1733: (!$env{'form.return_only_error_and_warning_counts'})) { 1734: my $nochgview = 1; 1735: my $controls = ''; 1736: if ($env{'request.state'} eq 'construct') { 1737: $controls = &Apache::loncommon::head_subbox( 1738: &Apache::loncommon::CSTR_pageheader() 1739: .&Apache::londefdef::edit_controls($nochgview)); 1740: } 1741: if ($filetype ne 'sty' && $filetype ne 'tex') { 1742: $result =~ s/</</g; 1743: $result =~ s/>/>/g; 1744: $result = '<table class="LC_sty_begin">'. 1745: '<tr><td><b><pre>'.$result. 1746: '</pre></b></td></tr></table>'; 1747: } 1748: my $brcrum; 1749: if ($env{'request.state'} eq 'construct') { 1750: $brcrum = [{'href' => &Apache::loncommon::authorspace(), 1751: 'text' => 'Construction Space'}, 1752: {'href' => '', 1753: 'text' => $breadcrumbtext}]; 1754: } else { 1755: $brcrum = ''; # FIXME: Where are we? 1756: } 1757: my %options = ('bread_crumbs' => $brcrum, 1758: 'bgcolor' => '#FFFFFF'); 1759: $result = 1760: &Apache::loncommon::start_page(undef,undef,\%options) 1761: .$controls 1762: .$result 1763: .&Apache::loncommon::end_page(); 1764: } 1765: } 1766: } 1767: 1768: # 1769: # Edit action? Insert editing commands 1770: # 1771: unless ($env{'request.state'} eq 'published') { 1772: if ($env{'form.editmode'} && (!($env{'form.viewmode'})) && (!($env{'form.discardview'}))) 1773: { 1774: my $displayfile=$request->uri; 1775: $displayfile=~s/^\/[^\/]*//; 1776: 1777: my ($edit_info, $add_to_onload, $add_to_onresize)= 1778: &inserteditinfo($filecontents,$filetype,$displayfile); 1779: 1780: my %options = 1781: ('add_entries' => 1782: {'onresize' => $add_to_onresize, 1783: 'onload' => $add_to_onload, }); 1784: my $header; 1785: if ($env{'request.state'} eq 'construct') { 1786: $options{'bread_crumbs'} = [{ 1787: 'href' => &Apache::loncommon::authorspace(), 1788: 'text' => 'Construction Space'}, 1789: {'href' => '', 1790: 'text' => $breadcrumbtext}]; 1791: $header = &Apache::loncommon::head_subbox( 1792: &Apache::loncommon::CSTR_pageheader()); 1793: } 1794: my $js = 1795: &Apache::edit::js_change_detection(). 1796: &Apache::loncommon::resize_textarea_js(); 1797: my $start_page = &Apache::loncommon::start_page(undef,$js, 1798: \%options); 1799: $result = $start_page 1800: .$header 1801: .&Apache::lonxml::message_location() 1802: .$edit_info 1803: .&Apache::loncommon::end_page(); 1804: } 1805: } 1806: if ($filetype eq 'html') { &writeallows($request->uri); } 1807: 1808: &Apache::lonxml::add_messages(\$result); 1809: $request->print($result); 1810: 1811: return OK; 1812: } 1813: 1814: sub display_title { 1815: my $result; 1816: if ($env{'request.state'} eq 'construct') { 1817: my $title=&Apache::lonnet::gettitle(); 1818: if (!defined($title) || $title eq '') { 1819: $title = $env{'request.filename'}; 1820: $title = substr($title, rindex($title, '/') + 1); 1821: } 1822: $result = "<script type='text/javascript'>top.document.title = '$title - LON-CAPA " 1823: .&mt('Construction Space')."';</script>"; 1824: } 1825: return $result; 1826: } 1827: 1828: sub debug { 1829: if ($Apache::lonxml::debug eq "1") { 1830: $|=1; 1831: my $request=$Apache::lonxml::request; 1832: if (!$request) { 1833: eval { $request=Apache->request; }; 1834: } 1835: if (!$request) { 1836: eval { $request=Apache2::RequestUtil->request; }; 1837: } 1838: $request->print('<font size="-2"><pre>DEBUG:'.&HTML::Entities::encode($_[0],'<>&"')."</pre></font>\n"); 1839: #&Apache::lonnet::logthis($_[0]); 1840: } 1841: } 1842: 1843: sub show_error_warn_msg { 1844: if ($env{'request.filename'} eq '/home/httpd/html/res/lib/templates/simpleproblem.problem' && 1845: &Apache::lonnet::allowed('mdc',$env{'request.course.id'})) { 1846: return 1; 1847: } 1848: return (($Apache::lonxml::debug eq 1) || 1849: ($env{'request.state'} eq 'construct') || 1850: ($Apache::lonhomework::browse eq 'F' 1851: && 1852: $env{'form.show_errors'} eq 'on')); 1853: } 1854: 1855: sub error { 1856: my @errors = @_; 1857: 1858: $errorcount++; 1859: 1860: $Apache::lonxml::internal_error=1; 1861: 1862: if (defined($Apache::inputtags::part)) { 1863: if ( @Apache::inputtags::response ) { 1864: push(@errors, 1865: &mt("This error occurred while processing response [_1] in part [_2]", 1866: $Apache::inputtags::response[-1], 1867: $Apache::inputtags::part)); 1868: } else { 1869: push(@errors, 1870: &mt("This error occurred while processing part [_1]", 1871: $Apache::inputtags::part)); 1872: } 1873: } 1874: 1875: if ( &show_error_warn_msg() ) { 1876: # If printing in construction space, put the error inside <pre></pre> 1877: push(@Apache::lonxml::error_messages, 1878: $Apache::lonxml::warnings_error_header 1879: .'<div class="LC_error">' 1880: .'<b>'.&mt('ERROR:').' </b>'.join("<br />\n",@errors) 1881: ."</div>\n"); 1882: $Apache::lonxml::warnings_error_header=''; 1883: } else { 1884: my $errormsg; 1885: my ($symb)=&Apache::lonnet::symbread(); 1886: if ( !$symb ) { 1887: #public or browsers 1888: $errormsg=&mt("An error occurred while processing this resource. The author has been notified."); 1889: } 1890: my $host=$Apache::lonnet::perlvar{'lonHostID'}; 1891: push(@errors, 1892: &mt("The error occurred on host [_1]", 1893: "<tt>$host</tt>")); 1894: 1895: my $msg = join('<br />', @errors); 1896: 1897: #notify author 1898: &Apache::lonmsg::author_res_msg($env{'request.filename'},$msg); 1899: #notify course 1900: if ( $symb && $env{'request.course.id'} ) { 1901: my $cnum=$env{'course.'.$env{'request.course.id'}.'.num'}; 1902: my $cdom=$env{'course.'.$env{'request.course.id'}.'.domain'}; 1903: my (undef,%users)=&Apache::lonmsg::decide_receiver(undef,0,1,1,1); 1904: my $declutter=&Apache::lonnet::declutter($env{'request.filename'}); 1905: my $baseurl = &Apache::lonnet::clutter($declutter); 1906: my @userlist; 1907: foreach (keys %users) { 1908: my ($user,$domain) = split(/:/, $_); 1909: push(@userlist,"$user\@$domain"); 1910: my $key=$declutter.'_'.$user.'_'.$domain; 1911: my %lastnotified=&Apache::lonnet::get('nohist_xmlerrornotifications', 1912: [$key], 1913: $cdom,$cnum); 1914: my $now=time; 1915: if ($now-$lastnotified{$key}>86400) { 1916: my $title = &Apache::lonnet::gettitle($symb); 1917: my $sentmessage; 1918: &Apache::lonmsg::user_normal_msg($user,$domain, 1919: "Error [$title]",$msg,'',$baseurl,'','', 1920: \$sentmessage,$symb,$title,1); 1921: &Apache::lonnet::put('nohist_xmlerrornotifications', 1922: {$key => $now}, 1923: $cdom,$cnum); 1924: } 1925: } 1926: if ($env{'request.role.adv'}) { 1927: $errormsg=&mt("An error occurred while processing this resource. The course personnel ([_1]) and the author have been notified.",join(', ',@userlist)); 1928: } else { 1929: $errormsg=&mt("An error occurred while processing this resource. The instructor has been notified."); 1930: } 1931: } 1932: push(@Apache::lonxml::error_messages,"<b>$errormsg</b> <br />"); 1933: } 1934: } 1935: 1936: sub warning { 1937: $warningcount++; 1938: 1939: if ($env{'form.grade_target'} ne 'tex') { 1940: if ( &show_error_warn_msg() ) { 1941: push(@Apache::lonxml::warning_messages, 1942: $Apache::lonxml::warnings_error_header 1943: .'<div class="LC_warning">' 1944: .&mt('[_1]W[_2]ARNING','<b>','</b>')."<b>:</b> ".join('<br />',@_) 1945: ."</div>\n" 1946: ); 1947: $Apache::lonxml::warnings_error_header=''; 1948: } 1949: } 1950: } 1951: 1952: sub info { 1953: if ($env{'form.grade_target'} ne 'tex' 1954: && $env{'request.state'} eq 'construct') { 1955: push(@Apache::lonxml::info_messages,join('<br />',@_)."<br />\n"); 1956: } 1957: } 1958: 1959: sub message_location { 1960: return '__LONCAPA_INTERNAL_MESSAGE_LOCATION__'; 1961: } 1962: 1963: sub add_messages { 1964: my ($msg)=@_; 1965: my $result=join(' ', 1966: @Apache::lonxml::info_messages, 1967: @Apache::lonxml::error_messages, 1968: @Apache::lonxml::warning_messages); 1969: undef(@Apache::lonxml::info_messages); 1970: undef(@Apache::lonxml::error_messages); 1971: undef(@Apache::lonxml::warning_messages); 1972: $$msg=~s/__LONCAPA_INTERNAL_MESSAGE_LOCATION__/$result/; 1973: $$msg=~s/__LONCAPA_INTERNAL_MESSAGE_LOCATION__//g; 1974: } 1975: 1976: sub get_param { 1977: my ($param,$parstack,$safeeval,$context,$case_insensitive) = @_; 1978: if ( ! $context ) { $context = -1; } 1979: my $args =''; 1980: if ( $#$parstack > (-2-$context) ) { $args=$$parstack[$context]; } 1981: if ( ! $Apache::lonxml::usestyle ) { 1982: $args=$Apache::lonxml::style_values.$args; 1983: } 1984: if ( ! $args ) { return undef; } 1985: if ( $case_insensitive ) { 1986: if ($args =~ s/(my (?:.*))(\$\Q$param\E[,\)])/$1.lc($2)/ei) { 1987: return &Apache::run::run("{$args;".'return $'.$param.'}', 1988: $safeeval); #' 1989: } else { 1990: return undef; 1991: } 1992: } else { 1993: if ( $args =~ /my .*\$\Q$param\E[,\)]/ ) { 1994: return &Apache::run::run("{$args;".'return $'.$param.'}', 1995: $safeeval); #' 1996: } else { 1997: return undef; 1998: } 1999: } 2000: } 2001: 2002: sub get_param_var { 2003: my ($param,$parstack,$safeeval,$context,$case_insensitive) = @_; 2004: if ( ! $context ) { $context = -1; } 2005: my $args =''; 2006: if ( $#$parstack > (-2-$context) ) { $args=$$parstack[$context]; } 2007: if ( ! $Apache::lonxml::usestyle ) { 2008: $args=$Apache::lonxml::style_values.$args; 2009: } 2010: &Apache::lonxml::debug("Args are $args param is $param"); 2011: if ($case_insensitive) { 2012: if (! ($args=~s/(my (?:.*))(\$\Q$param\E[,\)])/$1.lc($2)/ei)) { 2013: return undef; 2014: } 2015: } elsif ( $args !~ /my .*\$\Q$param\E[,\)]/ ) { return undef; } 2016: my $value=&Apache::run::run("{$args;".'return $'.$param.'}',$safeeval); #' 2017: &Apache::lonxml::debug("first run is $value"); 2018: if ($value =~ /^[\$\@\%][a-zA-Z_]\w*$/) { 2019: &Apache::lonxml::debug("doing second"); 2020: my @result=&Apache::run::run("return $value",$safeeval,1); 2021: if (!defined($result[0])) { 2022: return $value 2023: } else { 2024: if (wantarray) { return @result; } else { return $result[0]; } 2025: } 2026: } else { 2027: return $value; 2028: } 2029: } 2030: 2031: sub register_insert_xml { 2032: my $parser = HTML::LCParser->new($Apache::lonnet::perlvar{'lonTabDir'} 2033: .'/insertlist.xml'); 2034: my ($tagnum,$in_help)=(0,0); 2035: my @alltags; 2036: my $tag; 2037: while (my $token = $parser->get_token()) { 2038: if ($token->[0] eq 'S') { 2039: my $key; 2040: if ($token->[1] eq 'tag') { 2041: $tag = $token->[2]{'name'}; 2042: $insertlist{"$tagnum.tag"} = $tag; 2043: $insertlist{"$tag.num"} = $tagnum; 2044: push(@alltags,$tag); 2045: } elsif ($in_help && $token->[1] eq 'file') { 2046: $key = $tag.'.helpfile'; 2047: } elsif ($in_help && $token->[1] eq 'description') { 2048: $key = $tag.'.helpdesc'; 2049: } elsif ($token->[1] eq 'description' || 2050: $token->[1] eq 'color' || 2051: $token->[1] eq 'show' ) { 2052: $key = $tag.'.'.$token->[1]; 2053: } elsif ($token->[1] eq 'insert_sub') { 2054: $key = $tag.'.function'; 2055: } elsif ($token->[1] eq 'help') { 2056: $in_help=1; 2057: } elsif ($token->[1] eq 'allow') { 2058: $key = $tag.'.allow'; 2059: } 2060: if (defined($key)) { 2061: $insertlist{$key} = $parser->get_text(); 2062: $insertlist{$key} =~ s/(^\s*|\s*$ )//gx; 2063: } 2064: } elsif ($token->[0] eq 'E') { 2065: if ($token->[1] eq 'tag') { 2066: undef($tag); 2067: $tagnum++; 2068: } elsif ($token->[1] eq 'help') { 2069: undef($in_help); 2070: } 2071: } 2072: } 2073: 2074: # parse the allows and ignore tags set to <show>no</show> 2075: foreach my $tag (@alltags) { 2076: next if (!exists($insertlist{"$tag.allow"})); 2077: my $allow = $insertlist{"$tag.allow"}; 2078: foreach my $element (split(',',$allow)) { 2079: $element =~ s/(^\s*|\s*$ )//gx; 2080: if (!exists($insertlist{"$element.show"}) 2081: || $insertlist{"$element.show"} ne 'no') { 2082: push(@{ $insertlist{$tag.'.which'} },$element); 2083: } 2084: } 2085: } 2086: } 2087: 2088: sub register_insert { 2089: return ®ister_insert_xml(@_); 2090: # &dump_insertlist('2'); 2091: } 2092: 2093: sub dump_insertlist { 2094: my ($ext) = @_; 2095: open(XML,">/tmp/insertlist.xml.$ext"); 2096: print XML ("<insertlist>"); 2097: my $i=0; 2098: 2099: while (exists($insertlist{"$i.tag"})) { 2100: my $tag = $insertlist{"$i.tag"}; 2101: print XML (" 2102: \t<tag name=\"$tag\">"); 2103: if (defined($insertlist{"$tag.description"})) { 2104: print XML (" 2105: \t\t<description>".$insertlist{"$tag.description"}."</description>"); 2106: } 2107: if (defined($insertlist{"$tag.color"})) { 2108: print XML (" 2109: \t\t<color>".$insertlist{"$tag.color"}."</color>"); 2110: } 2111: if (defined($insertlist{"$tag.function"})) { 2112: print XML (" 2113: \t\t<insert_sub>".$insertlist{"$tag.function"}."</insert_sub>"); 2114: } 2115: if (defined($insertlist{"$tag.show"}) 2116: && $insertlist{"$tag.show"} ne 'yes') { 2117: print XML (" 2118: \t\t<show>".$insertlist{"$tag.show"}."</show>"); 2119: } 2120: if (defined($insertlist{"$tag.helpfile"})) { 2121: print XML (" 2122: \t\t<help> 2123: \t\t\t<file>".$insertlist{"$tag.helpfile"}."</file>"); 2124: if ($insertlist{"$tag.helpdesc"} ne '') { 2125: print XML (" 2126: \t\t\t<description>".$insertlist{"$tag.helpdesc"}."</description>"); 2127: } 2128: print XML (" 2129: \t\t</help>"); 2130: } 2131: if (defined($insertlist{"$tag.which"})) { 2132: print XML (" 2133: \t\t<allow>".join(',',sort(@{ $insertlist{"$tag.which"} }))."</allow>"); 2134: } 2135: print XML (" 2136: \t</tag>"); 2137: $i++; 2138: } 2139: print XML ("\n</insertlist>\n"); 2140: close(XML); 2141: } 2142: 2143: sub description { 2144: my ($token)=@_; 2145: my $tag = &get_tag($token); 2146: return $insertlist{$tag.'.description'}; 2147: } 2148: 2149: # Returns a list containing the help file, and the description 2150: sub helpinfo { 2151: my ($token)=@_; 2152: my $tag = &get_tag($token); 2153: return ($insertlist{$tag.'.helpfile'}, $insertlist{$tag.'.helpdesc'}); 2154: } 2155: 2156: sub get_tag { 2157: my ($token)=@_; 2158: my $tagnum; 2159: my $tag=$token->[1]; 2160: foreach my $namespace (reverse(@Apache::lonxml::namespace)) { 2161: my $testtag = $namespace.'::'.$tag; 2162: $tagnum = $insertlist{"$testtag.num"}; 2163: last if (defined($tagnum)); 2164: } 2165: if (!defined($tagnum)) { 2166: $tagnum = $Apache::lonxml::insertlist{"$tag.num"}; 2167: } 2168: return $insertlist{"$tagnum.tag"}; 2169: } 2170: 2171: ############################################################ 2172: # PDF-FORM-METHODS 2173: 2174: =pod 2175: 2176: =item &print_pdf_radiobutton(fieldname, value) 2177: 2178: Returns a latexline to generate a PDF-Form-Radiobutton. 2179: Note: Radiobuttons with equal names are automaticly grouped 2180: in a selection-group. 2181: 2182: $fieldname: PDF internalname of the radiobutton(group) 2183: $value: Value of radiobutton 2184: 2185: =cut 2186: sub print_pdf_radiobutton { 2187: my ($fieldname, $value) = @_; 2188: return '\radioButton[\symbolchoice{circle}]{' 2189: .$fieldname.'}{10bp}{10bp}{'.$value.'}'; 2190: } 2191: 2192: 2193: =pod 2194: 2195: =item &print_pdf_start_combobox(fieldname) 2196: 2197: Starts a latexline to generate a PDF-Form-Combobox with text. 2198: 2199: $fieldname: PDF internal name of the Combobox 2200: 2201: =cut 2202: sub print_pdf_start_combobox { 2203: my $result; 2204: my ($fieldName) = @_; 2205: $result .= '\begin{tabularx}{\textwidth}{p{2.5cm}X}'."\n"; 2206: $result .= '\comboBox[]{'.$fieldName.'}{2.3cm}{14bp}{'; # 2207: 2208: return $result; 2209: } 2210: 2211: 2212: =pod 2213: 2214: =item &print_pdf_add_combobox_option(options) 2215: 2216: Generates a latexline to add Options to a PDF-Form-ComboBox. 2217: 2218: $option: PDF internal name of the Combobox-Option 2219: 2220: =cut 2221: sub print_pdf_add_combobox_option { 2222: 2223: my $result; 2224: my ($option) = @_; 2225: 2226: $result .= '('.$option.')'; 2227: 2228: return $result; 2229: } 2230: 2231: 2232: =pod 2233: 2234: =item &print_pdf_end_combobox(text) { 2235: 2236: Returns latexcode to end a PDF-Form-Combobox with text. 2237: 2238: =cut 2239: sub print_pdf_end_combobox { 2240: my $result; 2241: my ($text) = @_; 2242: 2243: $result .= '}&'.$text."\\\\\n"; 2244: $result .= '\end{tabularx}' . "\n"; 2245: $result .= '\hspace{2mm}' . "\n"; 2246: return $result; 2247: } 2248: 2249: 2250: =pod 2251: 2252: =item &print_pdf_hiddenField(fieldname, user, domain) 2253: 2254: Returns a latexline to generate a PDF-Form-hiddenField with userdata. 2255: 2256: $fieldname label for hiddentextfield 2257: $user: name of user 2258: $domain: domain of user 2259: 2260: =cut 2261: sub print_pdf_hiddenfield { 2262: my $result; 2263: my ($fieldname, $user, $domain) = @_; 2264: 2265: $result .= '\textField [\F{\FHidden}\F{-\FPrint}\V{'.$domain.'&'.$user.'}]{'.$fieldname.'}{0in}{0in}'."\n"; 2266: 2267: return $result; 2268: } 2269: 2270: 1; 2271: __END__ 2272: