![]() ![]() | ![]() |
- page needs to rupdate the last_known symb
1: # The LearningOnline Network with CAPA 2: # Page Handler 3: # 4: # $Id: lonpage.pm,v 1.69 2005/03/01 00:40:05 albertel 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: ### 29: 30: package Apache::lonpage; 31: 32: use strict; 33: use Apache::Constants qw(:common :http); 34: use Apache::lonnet(); 35: use Apache::loncommon(); 36: use Apache::lonxml(); 37: use Apache::lonlocal; 38: use Apache::lonmenu; 39: use HTML::TokeParser; 40: use GDBM_File; 41: use Apache::lonsequence; 42: 43: # -------------------------------------------------------------- Module Globals 44: my %hash; 45: my @rows; 46: 47: # ------------------------------------------------------------------ Euclid gcd 48: 49: sub euclid { 50: my ($e,$f)=@_; 51: my $a; my $b; my $r; 52: if ($e>$f) { $b=$e; $r=$f; } else { $r=$e; $b=$f; } 53: while ($r!=0) { 54: $a=$b; $b=$r; 55: $r=$a%$b; 56: } 57: return $b; 58: } 59: 60: # ------------------------------------------------------------ Build page table 61: 62: sub tracetable { 63: my ($sofar,$rid,$beenhere)=@_; 64: my $further=$sofar; 65: my $randomout=0; 66: unless ($ENV{'request.role.adv'}) { 67: $randomout = $hash{'randomout_'.$rid}; 68: } 69: unless ($beenhere=~/\&$rid\&/) { 70: $beenhere.=$rid.'&'; 71: unless ($randomout) { 72: if (defined($hash{'is_map_'.$rid})) { 73: if ((defined($hash{'map_start_'.$hash{'src_'.$rid}})) && 74: (defined($hash{'map_finish_'.$hash{'src_'.$rid}}))) { 75: my $frid=$hash{'map_finish_'.$hash{'src_'.$rid}}; 76: $sofar= 77: &tracetable($sofar,$hash{'map_start_'.$hash{'src_'.$rid}}, 78: '&'.$frid.'&'); 79: $sofar++; 80: if ($hash{'src_'.$frid}) { 81: my $brepriv=&Apache::lonnet::allowed('bre',$hash{'src_'.$frid}); 82: if (($brepriv eq '2') || ($brepriv eq 'F')) { 83: if (defined($rows[$sofar])) { 84: $rows[$sofar].='&'.$frid; 85: } else { 86: $rows[$sofar]=$frid; 87: } 88: } 89: } 90: } 91: } else { 92: $sofar++; 93: if ($hash{'src_'.$rid}) { 94: my $brepriv=&Apache::lonnet::allowed('bre',$hash{'src_'.$rid}); 95: if (($brepriv eq '2') || ($brepriv eq 'F')) { 96: if (defined($rows[$sofar])) { 97: $rows[$sofar].='&'.$rid; 98: } else { 99: $rows[$sofar]=$rid; 100: } 101: } 102: } 103: } 104: } 105: 106: if (defined($hash{'to_'.$rid})) { 107: my $mincond=1; 108: my $next=''; 109: foreach (split(/\,/,$hash{'to_'.$rid})) { 110: my $thiscond= 111: &Apache::lonnet::directcondval($hash{'condid_'.$hash{'undercond_'.$_}}); 112: if ($thiscond>=$mincond) { 113: if ($next) { 114: $next.=','.$_.':'.$thiscond; 115: } else { 116: $next=$_.':'.$thiscond; 117: } 118: if ($thiscond>$mincond) { $mincond=$thiscond; } 119: } 120: } 121: foreach (split(/\,/,$next)) { 122: my ($linkid,$condval)=split(/\:/,$_); 123: if ($condval>=$mincond) { 124: my $now=&tracetable($sofar,$hash{'goesto_'.$linkid},$beenhere); 125: if ($now>$further) { $further=$now; } 126: } 127: } 128: } 129: } 130: return $further; 131: } 132: 133: # ================================================================ Main Handler 134: 135: sub handler { 136: my $r=shift; 137: 138: # ------------------------------------------- Set document type for header only 139: 140: if ($r->header_only) { 141: if ($ENV{'browser.mathml'}) { 142: &Apache::loncommon::content_type($r,'text/xml'); 143: } else { 144: &Apache::loncommon::content_type($r,'text/html'); 145: } 146: $r->send_http_header; 147: return OK; 148: } 149: 150: &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'}, 151: ['forceselect','launch']); 152: my $number_of_columns = 1; 153: my $requrl=$r->uri; 154: my $target = $ENV{'form.grade_target'}; 155: # &Apache::lonnet::logthis("Got a target of $target"); 156: if ($target eq 'meta') { 157: &Apache::loncommon::content_type($r,'text/html'); 158: $r->send_http_header; 159: return OK; 160: } 161: # ----------------------------------------------------------------- Tie db file 162: if (($ENV{'request.course.fn'}) && (!$ENV{'form.forceselect'})) { 163: my $fn=$ENV{'request.course.fn'}; 164: if (-e "$fn.db") { 165: if (tie(%hash,'GDBM_File',"$fn.db",&GDBM_READER(),0640)) { 166: # ------------------------------------------------------------------- Hash tied 167: my $firstres=$hash{'map_start_'.$requrl}; 168: my $lastres=$hash{'map_finish_'.$requrl}; 169: if (($firstres) && ($lastres)) { 170: # ----------------------------------------------------------------- Render page 171: 172: @rows=(); 173: 174: &tracetable(0,$firstres,'&'); 175: 176: # ------------------------------------------------------------ Add to symb list 177: 178: my $i; 179: my %symbhash=(); 180: for ($i=0;$i<=$#rows;$i++) { 181: if ($rows[$i]) { 182: my @colcont=split(/\&/,$rows[$i]); 183: foreach (@colcont) { 184: $symbhash{$hash{'src_'.$_}}=['page','notasymb']; 185: } 186: } 187: } 188: &Apache::lonnet::symblist($requrl,%symbhash); 189: 190: # ------------------------------------------------------------------ Page parms 191: 192: my $j; 193: my $lcm=1; 194: my $contents=0; 195: my $nforms=0; 196: 197: my %ssibody=(); 198: my %ssibgcolor=(); 199: my %ssitext=(); 200: my %ssilink=(); 201: my %ssivlink=(); 202: my %ssialink=(); 203: 204: my %metalink=(); 205: 206: my %cellemb=(); 207: 208: my $allscript=''; 209: my $allmeta=''; 210: 211: my $isxml=0; 212: my $xmlheader=''; 213: my $xmlbody=''; 214: 215: # --------------------------------------------- Get SSI output, post parameters 216: 217: for ($i=0;$i<=$#rows;$i++) { 218: if ($rows[$i]) { 219: $contents++; 220: my @colcont=split(/\&/,$rows[$i]); 221: $lcm*=($#colcont+1)/euclid($lcm,($#colcont+1)); 222: foreach (@colcont) { 223: my $src=$hash{'src_'.$_}; 224: my ($extension)=($src=~/\.(\w+)$/); 225: if ($hash{'encrypted_'.$_}) { 226: $src=&Apache::lonenc::encrypted($src); 227: } 228: $metalink{$_}=$src.'.meta'; 229: $cellemb{$_}= 230: &Apache::loncommon::fileembstyle($extension); 231: if ($cellemb{$_} eq 'ssi') { 232: # --------------------------------------------------------- This is an SSI cell 233: my ($mapid,$resid)=split(/\./,$_); 234: my $symb=&Apache::lonnet::encode_symb($hash{'map_id_'.$mapid},$resid,$src); 235: 236: my $prefix=$_.'_'; 237: my %posthash=('request.prefix' => $prefix, 238: 'symb' => $symb); 239: if ($ENV{'form.grade_target'} eq 'tex') { 240: $posthash{'grade_target'}=$ENV{'form.grade_target'}; 241: $posthash{'textwidth'}=$ENV{'form.textwidth'}; 242: $posthash{'problem_split'}=$ENV{'form.problem_split'}; 243: $posthash{'latex_type'}=$ENV{'form.latex_type'}; 244: $posthash{'rndseed'}=$ENV{'form.rndseed'}; 245: } 246: if (($ENV{'form.'.$prefix.'submit'}) 247: || ($ENV{'form.all_submit'})) { 248: foreach (keys %ENV) { 249: if ($_=~/^form.$prefix/) { 250: my $name=$_; 251: $name=~s/^form.$prefix//; 252: $posthash{$name}=$ENV{$_}; 253: } 254: } 255: } 256: my $output=Apache::lonnet::ssi($src,%posthash); 257: $output=~s|//(\s*<!--)? BEGIN LON-CAPA Internal.+// END LON-CAPA Internal\s*(-->)?\s||gs; 258: if ($target eq 'tex') { 259: $output =~ s/^([^&]+)\\begin{document}//; 260: $output =~ s/\\end{document}//; 261: $output = '\parbox{\minipagewidth}{ '.$output.' }'; 262: #some additional cleanup necessary for LateX (due to limitations of table environment 263: $output =~ s/(\\vskip\s*\d+mm)\s*(\\\\)+/$1/g; 264: } 265: my $parser=HTML::TokeParser->new(\$output); 266: my $token; 267: my $thisdir=$src; 268: my $bodydef=0; 269: my $thisxml=0; 270: my @rlinks=(); 271: if ($output=~/\?xml/) { 272: $isxml=1; 273: $thisxml=1; 274: $output=~ 275: /((?:\<(?:\?xml|\!DOC|html)[^\>]*(?:\>|\>\]\>)\s*)+)\<body[^\>]*\>/si; 276: $xmlheader=$1; 277: } 278: while ($token=$parser->get_token) { 279: if ($token->[0] eq 'S') { 280: if ($token->[1] eq 'a') { 281: if ($token->[2]->{'href'}) { 282: $rlinks[$#rlinks+1]= 283: $token->[2]->{'href'}; 284: } 285: } elsif ($token->[1] eq 'img') { 286: $rlinks[$#rlinks+1]= 287: $token->[2]->{'src'}; 288: } elsif ($token->[1] eq 'embed') { 289: $rlinks[$#rlinks+1]= 290: $token->[2]->{'src'}; 291: } elsif ($token->[1] eq 'base') { 292: $thisdir=$token->[2]->{'href'}; 293: } elsif ($token->[1] eq 'body') { 294: $bodydef=1; 295: $ssibgcolor{$_}=$token->[2]->{'bgcolor'}; 296: $ssitext{$_}=$token->[2]->{'text'}; 297: $ssilink{$_}=$token->[2]->{'link'}; 298: $ssivlink{$_}=$token->[2]->{'vlink'}; 299: $ssialink{$_}=$token->[2]->{'alink'}; 300: if ($thisxml) { 301: $xmlbody=$token->[4]; 302: } 303: } elsif ($token->[1] eq 'meta') { 304: if ($token->[4] !~ m:/>$:) { 305: $allmeta.="\n".$token->[4].'</meta>'; 306: } else { 307: $allmeta.="\n".$token->[4]; 308: } 309: } elsif (($token->[1] eq 'script') && 310: ($bodydef==0)) { 311: $allscript.="\n\n" 312: .$parser->get_text('/script'); 313: } 314: } 315: } 316: if ($output=~/\<body[^\>]*\>(.*)/si) { 317: $output=$1; 318: } 319: $output=~s/\<\/body\>.*//si; 320: if ($output=~/\<form/si) { 321: $nforms++; 322: $output=~s/\<form[^\>]*\>//gsi; 323: $output=~s/\<\/form[^\>]*\>//gsi; 324: $output=~ 325: s/\<((?:input|select|button|textarea)[^\>]+)name\s*\=\s*[\'\"]*([\w\.\:]+)[\'\"]*([^\>]*)\>/\<$1 name="$prefix$2" $3\>/gsi; 326: } 327: $thisdir=~s/\/[^\/]*$//; 328: foreach (@rlinks) { 329: unless (($_=~/^http:\/\//i) || 330: ($_=~/^\//) || 331: ($_=~/^javascript:/i) || 332: ($_=~/^mailto:/i) || 333: ($_=~/^\#/)) { 334: my $newlocation= 335: &Apache::lonnet::hreflocation($thisdir,$_); 336: $output=~s/(\"|\'|\=\s*)$_(\"|\'|\s|\>)/$1$newlocation$2/; 337: } 338: } 339: # -------------------------------------------------- Deal with Applet codebases 340: $output=~s/(\<applet[^\>]+)(codebase\=[^\S\>]+)*([^\>]*)\>/$1.($2?$2:' codebase="'.$thisdir.'"').$3.'>'/gei; 341: $ssibody{$_}=$output; 342: # ---------------------------------------------------------------- End SSI cell 343: } 344: } 345: } 346: } 347: unless ($contents) { 348: &Apache::loncommon::content_type($r,'text/html'); 349: $r->send_http_header; 350: $r->print('<html>'."\n". 351: '<head>'."\n". 352: &Apache::lonmenu::registerurl(1,undef)."\n". 353: '</head>'."\n". 354: '<body bgcolor="#FFFFFF" onLoad="' 355: .&Apache::lonmenu::loadevents. 356: '" onUnload="'.&Apache::lonmenu::unloadevents.'">'. 357: &Apache::lonmenu::menubuttons(undef,$target,1) 358: ); 359: $r->print(&mt('This page is either empty or it only contains resources that are currently hidden').'. '); 360: $r->print('<br /><br />'.&mt('Please use the LON-CAPA navigation arrows to move to another item in the course').'.</body></html>'); 361: } else { 362: # ------------------------------------------------------------------ Build page 363: 364: # ---------------------------------------------------------------- Send headers 365: unless ($target eq 'tex') { 366: if ($isxml) { 367: &Apache::loncommon::content_type($r,'text/xml'); 368: $r->send_http_header; 369: $r->print($xmlheader); 370: } else { 371: &Apache::loncommon::content_type($r,'text/html'); 372: $r->send_http_header; 373: $r->print('<html>'); 374: } 375: # ------------------------------------------------------------------------ Head 376: $r->print("\n<head>\n".$allmeta); 377: if ($allscript) { 378: $r->print("\n<script language='JavaScript'>\n". 379: $allscript."\n</script>\n"); 380: } 381: $r->print(&Apache::lonmenu::registerurl(1,undef)); 382: $r->print("\n</head>\n"); 383: # ------------------------------------------------------------------ Start body 384: if ($isxml) { 385: $r->print($xmlbody); 386: } else { 387: $r->print( 388: '<body bgcolor="#FFFFFF" onLoad="'.&Apache::lonmenu::loadevents. 389: '" onUnload="'.&Apache::lonmenu::unloadevents.'">'. 390: &Apache::lonmenu::menubuttons(undef,$target,1) 391: ); 392: } 393: # ------------------------------------------------------------------ Start form 394: if ($nforms) { 395: $r->print('<form method="post" action="'. 396: &Apache::lonenc::check_encrypt($requrl) 397: .'">'); 398: } 399: } elsif ($target eq 'tex') { 400: $r->print('\documentclass{article} 401: \newcommand{\keephidden}[1]{} 402: \usepackage[dvips]{graphicx} 403: \usepackage{epsfig} 404: \usepackage{calc} 405: \usepackage{longtable} 406: \begin{document}'); 407: } 408: # ----------------------------------------------------------------- Start table 409: if ($target eq 'tex') { 410: $r->print('\begin{longtable}INSERTTHEHEADOFLONGTABLE\endfirsthead\endhead '); 411: if ($number_of_columns le $lcm) {$number_of_columns=$lcm;}; 412: } else { 413: $r->print('<table width="100%" cols="'.$lcm.'" border="0">'); 414: } 415: for ($i=0;$i<=$#rows;$i++) { 416: if ($rows[$i]) { 417: unless ($target eq 'tex') { 418: $r->print("\n<tr>"); 419: } 420: my @colcont=split(/\&/,$rows[$i]); 421: my $avespan=$lcm/($#colcont+1); 422: for ($j=0;$j<=$#colcont;$j++) { 423: my $rid=$colcont[$j]; 424: my $metainfo = ''; 425: my $esrc=&Apache::lonnet::declutter($hash{'src_'.$rid}); 426: my ($mapid,$resid)=split(/\./,$rid); 427: my $symb=&Apache::lonnet::encode_symb($hash{'map_id_'.$mapid},$resid,$hash{'src_'.$rid}); 428: if ($hash{'encrypted_'.$rid}) { 429: $symb=&Apache::lonenc::encrypted($symb); 430: $esrc=&Apache::lonenc::encrypted($esrc); 431: } 432: unless ($hash{'src_'.$rid} =~ m-^/uploaded/-) { 433: $metainfo ='<a name="'.&Apache::lonnet::escape($symb).'" />'. 434: '<a href="'.$metalink{$rid}.'" target="LONcatInfo">'. 435: '<img src="/adm/lonMisc/cat_button.gif" border=0>'. 436: '</img></a>'; 437: } 438: $metainfo .= '<a href="/adm/evaluate?postdata='. 439: &Apache::lonnet::escape($esrc). 440: '" target="LONcatInfo">'. 441: '<img src="/adm/lonMisc/eval_button.gif" border=0>'. 442: '</img></a>'; 443: if ( 444: ($hash{'src_'.$rid}=~/\.(problem|exam|quiz|assess|survey|form)$/) && 445: (&Apache::lonnet::allowed('mgr',$ENV{'request.course.id'})) && 446: ($hash{'src_'.$rid} !~ m-^/uploaded/-)) { 447: my ($mapid,$resid)=split(/\./,$rid); 448: my $symb= 449: &Apache::lonnet::declutter($hash{'map_id_'.$mapid}). 450: '___'.$resid.'___'. 451: &Apache::lonnet::declutter($hash{'src_'.$rid}); 452: $metainfo.= 453: '<a href="/adm/grades?symb='.&Apache::lonnet::escape($symb). 454: # '&command=submission" target="LONcatInfo">'. 455: '&command=submission">'. 456: '<img src="/adm/lonMisc/subm_button.gif" border=0>'. 457: '</img></a>'. 458: '<a href="/adm/grades?symb='.&Apache::lonnet::escape($symb). 459: # '&command=gradingmenu" target="LONcatInfo">'. 460: '&command=gradingmenu">'. 461: '<img src="/adm/lonMisc/pgrd_button.gif" border=0>'. 462: '</img></a>'. 463: '<a href="/adm/parmset?symb='.&Apache::lonnet::escape($symb). 464: # '" target="LONcatInfo">'. 465: '" >'. 466: '<img src="/adm/lonMisc/pprm_button.gif" border=0>'. 467: '</img></a>'; 468: } 469: $metainfo.='<br></br>'; 470: unless ($target eq 'tex') { 471: $r->print('<td colspan="'.$avespan.'"'); 472: } 473: if ($cellemb{$rid} eq 'ssi') { 474: unless ($target eq 'tex') { 475: if ($ssibgcolor{$rid}) { 476: $r->print(' bgcolor="'. 477: $ssibgcolor{$rid}.'"'); 478: } 479: $r->print('>'.$metainfo.'<font'); 480: 481: if ($ssitext{$rid}) { 482: $r->print(' text="'.$ssitext{$rid}.'"'); 483: } 484: if ($ssilink{$rid}) { 485: $r->print(' link="'.$ssilink{$rid}.'"'); 486: } 487: if ($ssitext{$rid}) { 488: $r->print(' vlink="'.$ssivlink{$rid}.'"'); 489: } 490: if ($ssialink{$rid}) { 491: $r->print(' alink="'.$ssialink{$rid}.'"'); 492: } 493: $r->print('>'); 494: } 495: $r->print($ssibody{$rid}); 496: unless ($target eq 'tex') { 497: $r->print('</font>'); 498: } 499: if ($ENV{'course.'. 500: $ENV{'request.course.id'}. 501: '.pageseparators'} eq 'yes') { 502: unless($target eq 'tex') { 503: $r->print('<hr />'); 504: } else { 505: $r->print('\hline'); 506: } 507: } 508: } elsif ($cellemb{$rid} eq 'img') { 509: $r->print('>'.$metainfo.'<img src="'. 510: $hash{'src_'.$rid}.'"></img>'); 511: } elsif ($cellemb{$rid} eq 'emb') { 512: $r->print('>'.$metainfo.'<embed src="'. 513: $hash{'src_'.$rid}.'"></embed>'); 514: } elsif (&Apache::lonnet::declutter($hash{'src_'.$rid}) !~/\.(sequence|page)$/) { 515: $r->print($metainfo.'<b>'.$hash{'title_'.$rid}.'</b><br />'. 516: &mt('It is recommended that you use an up-to-date virus scanner before handling this file.').'</p><p><table>'. 517: &Apache::londocs::entryline(0,&mt("Click to download or use your browser's Save Link function"),'/'.&Apache::lonnet::declutter($hash{'src_'.$rid})).'</table></p><br />'); 518: } 519: unless ($target eq 'tex') { 520: $r->print('</td>'); 521: } else { 522: for (my $incol=1;$incol<=$avespan;$incol++) { 523: $r->print(' & '); 524: } 525: } 526: } 527: unless ($target eq 'tex') { 528: $r->print('</tr>'); 529: } else { 530: $r->print('REMOVETHEHEADOFLONGTABLE\\\\'); 531: } 532: } 533: } 534: unless ($target eq 'tex') { 535: $r->print("\n</table>"); 536: } else { 537: $r->print('\end{longtable}\strut'); 538: } 539: # ---------------------------------------------------------------- Submit, etc. 540: if ($nforms) { 541: $r->print( 542: '<input name="all_submit" value="Submit All" type="'. 543: (($nforms>1)?'submit':'hidden').'"></input></form>'); 544: } 545: unless ($target eq 'tex') { 546: $r->print('</body>'.&Apache::lonxml::xmlend()); 547: } else { 548: $r->print('\end{document}'.$number_of_columns); 549: } 550: &Apache::lonnet::symblist($requrl,%symbhash); 551: my ($map,$id,$url)=&Apache::lonnet::decode_symb(&Apache::lonnet::symbread()); 552: &Apache::lonnet::symblist($map,'last_known'=>[$url,$id]); 553: # -------------------------------------------------------------------- End page 554: } 555: # ------------------------------------------------------------- End render page 556: } else { 557: &Apache::loncommon::content_type($r,'text/html'); 558: $r->send_http_header; 559: &Apache::lonsequence::viewmap($r,$requrl); 560: } 561: # ------------------------------------------------------------------ Untie hash 562: unless (untie(%hash)) { 563: &Apache::lonnet::logthis("<font color=blue>WARNING: ". 564: "Could not untie coursemap $fn (browse).</font>"); 565: } 566: # -------------------------------------------------------------------- All done 567: return OK; 568: # ----------------------------------------------- Errors, hash could no be tied 569: } 570: } 571: } 572: &Apache::loncommon::content_type($r,'text/html'); 573: $r->send_http_header; 574: &Apache::lonsequence::viewmap($r,$requrl); 575: return OK; 576: } 577: 578: 1; 579: __END__ 580: 581: =head1 NAME 582: 583: Apache::lonpage - Page Handler 584: 585: =head1 SYNOPSIS 586: 587: Invoked by /etc/httpd/conf/srm.conf: 588: 589: <LocationMatch "^/res/.*\.page$> 590: SetHandler perl-script 591: PerlHandler Apache::lonpage 592: </LocationMatch> 593: 594: =head1 INTRODUCTION 595: 596: This module renders a .page resource. 597: 598: This is part of the LearningOnline Network with CAPA project 599: described at http://www.lon-capa.org. 600: 601: =head1 HANDLER SUBROUTINE 602: 603: This routine is called by Apache and mod_perl. 604: 605: =over 4 606: 607: =item * 608: 609: set document type for header only 610: 611: =item * 612: 613: tie db file 614: 615: =item * 616: 617: render page 618: 619: =item * 620: 621: add to symb list 622: 623: =item * 624: 625: page parms 626: 627: =item * 628: 629: Get SSI output, post parameters 630: 631: =item * 632: 633: SSI cell rendering 634: 635: =item * 636: 637: Deal with Applet codebases 638: 639: =item * 640: 641: Build page 642: 643: =item * 644: 645: send headers 646: 647: =item * 648: 649: start body 650: 651: =item * 652: 653: start form 654: 655: =item * 656: 657: start table 658: 659: =item * 660: 661: submit element, etc, render page, untie hash 662: 663: =back 664: 665: =head1 OTHER SUBROUTINES 666: 667: =over 4 668: 669: =item * 670: 671: euclid() : Euclid's method for determining the greatest common denominator. 672: 673: =item * 674: 675: tracetable() : Build page table. 676: 677: =back 678: 679: =cut 680: 681: 682: 683: