# The LearningOnline Network with CAPA # Page Handler # # $Id: lonpage.pm,v 1.121 2017/02/20 18:29:33 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # # This file is part of the LearningOnline Network with CAPA (LON-CAPA). # # LON-CAPA is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # LON-CAPA is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with LON-CAPA; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # /home/httpd/html/adm/gpl.txt # # http://www.lon-capa.org/ # ### package Apache::lonpage; use strict; use Apache::Constants qw(:common :http); use Apache::lonnet; use Apache::loncommon(); use Apache::lonhtmlcommon; use Apache::lonxml(); use Apache::lonlocal; use Apache::lonmenu; use Apache::lonhomework; use Apache::lonparmset; use HTML::TokeParser; use GDBM_File; use Apache::lonsequence; use lib '/home/httpd/lib/perl/'; use LONCAPA; # -------------------------------------------------------------- Module Globals my %hash; my @rows; # ------------------------------------------------------------------ Euclid gcd sub euclid { my ($e,$f)=@_; my $a; my $b; my $r; if ($e>$f) { $b=$e; $r=$f; } else { $r=$e; $b=$f; } while ($r!=0) { $a=$b; $b=$r; $r=$a%$b; } return $b; } # ------------------------------------------------------------ Build page table sub tracetable { my ($sofar,$rid,$beenhere)=@_; my $further=$sofar; my $randomout=0; unless ($env{'request.role.adv'}) { $randomout = $hash{'randomout_'.$rid}; } unless ($beenhere=~/\&$rid\&/) { $beenhere.=$rid.'&'; unless ($randomout) { if (defined($hash{'is_map_'.$rid})) { if ((defined($hash{'map_start_'.$hash{'src_'.$rid}})) && (defined($hash{'map_finish_'.$hash{'src_'.$rid}}))) { my $frid=$hash{'map_finish_'.$hash{'src_'.$rid}}; $sofar= &tracetable($sofar,$hash{'map_start_'.$hash{'src_'.$rid}}, '&'.$frid.$beenhere); $sofar++; if ($hash{'src_'.$frid}) { my $brepriv=&Apache::lonnet::allowed('bre',$hash{'src_'.$frid}); if (($brepriv eq '2') || ($brepriv eq 'F')) { if (defined($rows[$sofar])) { $rows[$sofar].='&'.$frid; } else { $rows[$sofar]=$frid; } } } } } else { $sofar++; if ($hash{'src_'.$rid}) { my $brepriv=&Apache::lonnet::allowed('bre',$hash{'src_'.$rid}); if (($brepriv eq '2') || ($brepriv eq 'F')) { if (defined($rows[$sofar])) { $rows[$sofar].='&'.$rid; } else { $rows[$sofar]=$rid; } } } } } if (defined($hash{'to_'.$rid})) { my $mincond=1; my $next=''; foreach (split(/\,/,$hash{'to_'.$rid})) { my $thiscond= &Apache::lonnet::directcondval($hash{'condid_'.$hash{'undercond_'.$_}}); if ($thiscond>=$mincond) { if ($next) { $next.=','.$_.':'.$thiscond; } else { $next=$_.':'.$thiscond; } if ($thiscond>$mincond) { $mincond=$thiscond; } } } foreach (split(/\,/,$next)) { my ($linkid,$condval)=split(/\:/,$_); if ($condval>=$mincond) { my $now=&tracetable($sofar,$hash{'goesto_'.$linkid},$beenhere); if ($now>$further) { $further=$now; } } } } } return $further; } # ================================================================ Main Handler sub handler { my $r=shift; # ------------------------------------------- Set document type for header only if ($r->header_only) { if ($env{'browser.mathml'}) { &Apache::loncommon::content_type($r,'text/xml'); } else { &Apache::loncommon::content_type($r,'text/html'); } $r->send_http_header; return OK; } &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'}, ['forceselect','launch']); my $number_of_columns = 1; my $requrl=$r->uri; my $target = $env{'form.grade_target'}; # Short term solution: define target as 'tex_answer' when retrieving answers # for resources in a .page when generating printouts. # A better long-term fix would be to modify the way problem rendering, and # answer rendering are retrieved for individual resources when printing a .page, # so rendered problem and answer are sequential for individual resources in # the .page # if ($target eq 'answer') { if ($env{'form.answer_output_mode'} eq 'tex') { $target = 'tex_answer'; } } # &Apache::lonnet::logthis("Got a target of $target"); if ($target eq 'meta') { &Apache::loncommon::content_type($r,'text/html'); $r->send_http_header; return OK; } # ----------------------------------------------------------------- Tie db file if (($env{'request.course.fn'}) && (!$env{'form.forceselect'})) { my $fn=$env{'request.course.fn'}; if (-e "$fn.db") { my %buttonshide; if (tie(%hash,'GDBM_File',"$fn.db",&GDBM_READER(),0640)) { # ------------------------------------------------------------------- Hash tied my $firstres=$hash{'map_start_'.$requrl}; my $lastres=$hash{'map_finish_'.$requrl}; if (($firstres) && ($lastres)) { # ------------------------------------------------------------- Countdown Timer my $now = time; my ($pagefirstaccess,%hastimeleft,%countdowndisp,%donebutton, %donebtnextra,%buttonbytime,$donetime,$symbtosetdone); my ($pagesymb,$courseid,$domain,$name)=&Apache::lonnet::whichuser(); unless ($pagesymb) { $pagesymb=&Apache::lonnet::symbread($requrl); } if ($pagesymb && ($courseid ne '') && ($domain ne '') && ($name ne '')) { my %times=&Apache::lonnet::get('firstaccesstimes', [$courseid."\0".$pagesymb], $domain,$name); if ($times{$courseid."\0".$pagesymb} =~ /^\d+$/) { $pagefirstaccess = $times{$courseid."\0".$pagesymb}; if ($pagefirstaccess && $env{'form.LC_interval_done'} eq 'true') { $donetime = $now - $pagefirstaccess; } } } # ----------------------------------------------------------------- Render page @rows=(); &tracetable(0,$firstres,'&'); # ------------------------------------------------------------ Add to symb list my $i; my %symbhash=(); for ($i=0;$i<=$#rows;$i++) { if ($rows[$i]) { my @colcont=split(/\&/,$rows[$i]); foreach my $rid (@colcont) { my ($mapid,$resid)=split(/\./,$rid); $symbhash{$hash{'src_'.$rid}}= [$hash{'src_'.$rid},$resid]; if (($donetime) && ($symbtosetdone eq '')) { my $src = $hash{'src_'.$rid}; if ($hash{'encrypted_'.$rid}) { $src=&Apache::lonenc::encrypted($src); } my ($mapid,$resid)=split(/\./,$rid); my $symb=&Apache::lonnet::encode_symb($hash{'map_id_'.$mapid},$resid,$src); if ($src =~ /$LONCAPA::assess_re/) { my @interval=&Apache::lonnet::EXT("resource.0.interval",$symb); if (@interval > 1) { if (($interval[1] eq 'map') && ($pagefirstaccess)) { my ($timelimit) = ($interval[0] =~ /^(\d+)/); if ($timelimit) { if ($pagefirstaccess + $timelimit > $now) { $symbtosetdone = $symb; } } } } } } } } } &Apache::lonnet::symblist($requrl,%symbhash); # ------------------------------------------------------------------ Page parms my $j; my $lcm=1; my $contents=0; my $nforms=0; my $nuploads=0; my $ntimers=0; my %turninpaths; my %multiresps; my $turninparent; my %ssibody=(); my %ssibgcolor=(); my %ssitext=(); my %ssilink=(); my %ssivlink=(); my %ssialink=(); my %cellemb=(); my %cellexternal=(); my $allscript=''; my $allmeta=''; my $isxml=0; my $xmlheader=''; my $xmlbody=''; # ---------------------------------------------------------- Handle Done button # Set the event timer to zero if the "done button" was clicked. if ($donetime && $symbtosetdone) { &Apache::lonparmset::storeparm_by_symb_inner($symbtosetdone,'0_interval', 2,$donetime,'date_interval', $name,$domain); undef($env{'form.LC_interval_done'}); } # --------------------------------------------- Get SSI output, post parameters for ($i=0;$i<=$#rows;$i++) { if ($rows[$i]) { $contents++; my @colcont=split(/\&/,$rows[$i]); $lcm*=($#colcont+1)/euclid($lcm,($#colcont+1)); foreach (@colcont) { my $src=$hash{'src_'.$_}; my $plainsrc = $src; my ($extension)=($src=~/\.(\w+)$/); $cellexternal{$_}=($hash{'ext_'.$_} eq 'true:'); if ($hash{'encrypted_'.$_}) { $src=&Apache::lonenc::encrypted($src); } my ($mapid,$resid)=split(/\./,$_); my $symb=&Apache::lonnet::encode_symb($hash{'map_id_'.$mapid},$resid,$src); unless ($env{'request.role.adv'}) { $buttonshide{$symb} = &Apache::lonnet::EXT("resource.0.buttonshide",$symb); } $cellemb{$_}= &Apache::loncommon::fileembstyle($extension); if ($cellexternal{$_}) { unless (($target eq 'tex') || ($target eq 'tex_answer')) { $ssibody{$_} = <No iframe support! ENDEXT } } elsif ($cellemb{$_} eq 'ssi') { # --------------------------------------------------------- This is an SSI cell my $prefix=$_.'_'; my $idprefix= join('_',($mapid,$resid,'')); my %posthash=('request.prefix' => $prefix, 'LONCAPA_INTERNAL_no_discussion' => 'true', 'symb' => $symb); if (($env{'form.grade_target'} eq 'tex') || ($env{'form.answer_output_mode'} eq 'tex')) { $posthash{'grade_target'}=$env{'form.grade_target'}; $posthash{'textwidth'}=$env{'form.textwidth'}; $posthash{'problem_split'}=$env{'form.problem_split'}; $posthash{'latex_type'}=$env{'form.latex_type'}; $posthash{'rndseed'}=$env{'form.rndseed'}; $posthash{'answer_output_mode'} = $env{'form.answer_output_mode'}; } my $submitted=exists($env{'form.all_submit'}); if (!$submitted) { foreach my $key (keys(%env)) { if ($key=~/^form.\Q$prefix\Esubmit_/) { $submitted=1;last; } } } if ($submitted) { foreach my $key (keys(%env)) { if ($key=~/^form.\Q$prefix\E/) { my $name=$key; $name=~s/^form.\Q$prefix\E//; $posthash{$name}=$env{$key}; } } if (exists($env{'form.all_submit'})) { $posthash{'all_submit'}='yes'; } } my $output=Apache::lonnet::ssi($src,%posthash); $output=~s|//(\s*)?\s||gs; if (($target eq 'tex') || ($target eq 'tex_answer')) { $output =~ s/^([^&]+)\\begin\{document}//; $output =~ s/\\end\{document}//; # $output = '\parbox{\minipagewidth}{ '.$output.' }'; #some additional cleanup necessary for LateX (due to limitations of table environment $output =~ s/(\\vskip\s*\d+mm)\s*(\\\\)+/$1/g; } my $matheditor; if ($output =~ /\Qjavascript:LC_mathedit_HWVAL_\E/) { $matheditor = 'dragmath'; } elsif ($output =~ /LCmathField/) { $matheditor = 'lcmath'; } my $parser=HTML::TokeParser->new(\$output); my $token; my $thisdir=$src; my $bodydef=0; my $thisxml=0; my @rlinks=(); if ($output=~/\?xml/) { $isxml=1; $thisxml=1; $output=~ /((?:\<(?:\?xml|\!DOC|html)[^\>]*(?:\>|\>\]\>)\s*)+)\]*\>/si; $xmlheader=$1; } while ($token=$parser->get_token) { if ($token->[0] eq 'S') { if ($token->[1] eq 'a') { if ($token->[2]->{'href'}) { $rlinks[$#rlinks+1]= $token->[2]->{'href'}; } } elsif ($token->[1] eq 'img') { $rlinks[$#rlinks+1]= $token->[2]->{'src'}; } elsif ($token->[1] eq 'embed') { $rlinks[$#rlinks+1]= $token->[2]->{'src'}; } elsif ($token->[1] eq 'base') { $thisdir=$token->[2]->{'href'}; } elsif ($token->[1] eq 'body') { $bodydef=1; $ssibgcolor{$_}=$token->[2]->{'bgcolor'}; $ssitext{$_}=$token->[2]->{'text'}; $ssilink{$_}=$token->[2]->{'link'}; $ssivlink{$_}=$token->[2]->{'vlink'}; $ssialink{$_}=$token->[2]->{'alink'}; if ($thisxml) { $xmlbody=$token->[4]; } } elsif ($token->[1] eq 'meta') { if ($token->[4] !~ m:/>$:) { $allmeta.="\n".$token->[4].''; } else { $allmeta.="\n".$token->[4]; } } elsif (($token->[1] eq 'script') && ($bodydef==0)) { $allscript.="\n\n" .$parser->get_text('/script'); } } } if ($output=~/\]*\>(.*)/si) { $output=$1; } $output=~s/\<\/body\>.*//si; if ($output=~/\
]*\>//gsi; $output=~s/\<\/form[^\>]*\>//gsi; if ($output=~/\]+name\s*=\s*[\'\"]*HWFILE/) { $nuploads++; } if ($output=~/\]+name\s*=\s*[\'\"]*accessbutton/) { $ntimers++; $hastimer = 1; } $output=~ s/\<((?:input|select|button|textarea)[^\>]+)name\s*\=\s*[\'\"]*([^\'\"]+)[\'\"]*([^\>]*)\>/\<$1 name="$prefix$2" $3\>/gsi; $output=~ s/\<((?:input|select|button|textarea)[^\>]+)id\s*\=\s*[\'\"]*([^\'\"]+)[\'\"]*([^\>]*)\>/\<$1 id="$idprefix$2" $3\>/gsi; if ($hastimer) { $output=~ s/\<(input[^\>]+name=\Q"$prefix\Eaccessbutton"[^\>]+)(?:\Qdocument.markaccess.submit();\E)([^\>]*)\>/\<$1pageTimer(this.form,'$prefix')$2\>/gsi; $output=~ s/\<(input[^\>]+name=\Q"$prefix\Emarkaccess"[^\>]+value=["'])(?:yes)(['"][^\>]*)\>/\<$1$2\>/gsi; } if ($matheditor eq 'dragmath') { $output=~ s/(\Qjavascript:LC_mathedit_\E)(HWVAL_)([^'"]+?)(\(['"]*)(\QHWVAL_\E\3['"]\)\;void\(0\)\;)/$1$idprefix$2$3$4$idprefix$5/g; $output=~ s/(function\s+LC_mathedit_)(HWVAL_)([^'"]+?)(\s+\(LCtextline\))/$1$idprefix$2$3$4/g; } elsif ($matheditor eq 'lcmath') { $output=~ s/(var\s+LCmathField\s+=\s+document\.getElementById\(['"])([^'"]+?)(['"]\)\;)/$1$idprefix$2$3/g; } $output=~ s/(\Q