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