--- loncom/interface/spreadsheet/Spreadsheet.pm 2005/03/10 17:33:57 1.40 +++ loncom/interface/spreadsheet/Spreadsheet.pm 2014/02/12 16:54:21 1.83 @@ -1,5 +1,5 @@ # -# $Id: Spreadsheet.pm,v 1.40 2005/03/10 17:33:57 matthew Exp $ +# $Id: Spreadsheet.pm,v 1.83 2014/02/12 16:54:21 bisitz Exp $ # # Copyright Michigan State University Board of Trustees # @@ -60,6 +60,9 @@ use HTML::TokeParser; use Spreadsheet::WriteExcel; use Time::HiRes; use Apache::lonlocal; +use lib '/home/httpd/lib/perl/'; +use LONCAPA; + ## ## Package Variables @@ -85,34 +88,46 @@ sub new { my $class = ref($this) || $this; my ($stype) = ($class =~ /Apache::(.*)$/); # - my ($name,$domain,$filename,$usymb)=@_; + my ($name,$domain,$filename,$usymb,$section,$groups)=@_; if (defined($usymb) && ref($usymb)) { $usymb = $usymb->symb; } if (! defined($name) || $name eq '') { - $name = $ENV{'user.name'}; + $name = $env{'user.name'}; } if (! defined($domain) || $domain eq '') { - $domain = $ENV{'user.domain'}; + $domain = $env{'user.domain'}; + } + if (! defined($section) || $section eq '') { + $section = &Apache::lonnet::getsection($domain,$name, + $env{'request.course.id'}); + } + if (! defined($groups)) { + + my @usersgroups = &Apache::lonnet::get_users_groups($domain,$name, + $env{'request.course.id'}); + $groups = \@usersgroups; } # my $self = { name => $name, domain => $domain, + section => $section, + groups => $groups, type => $stype, symb => $usymb, errorlog => '', maxrow => 0, - cid => $ENV{'request.course.id'}, - cnum => $ENV{'course.'.$ENV{'request.course.id'}.'.num'}, - cdom => $ENV{'course.'.$ENV{'request.course.id'}.'.domain'}, - chome => $ENV{'course.'.$ENV{'request.course.id'}.'.home'}, - coursedesc => $ENV{'course.'.$ENV{'request.course.id'}.'.description'}, - coursefilename => $ENV{'request.course.fn'}, + cid => $env{'request.course.id'}, + cnum => $env{'course.'.$env{'request.course.id'}.'.num'}, + cdom => $env{'course.'.$env{'request.course.id'}.'.domain'}, + coursedesc => $env{'course.'.$env{'request.course.id'}.'.description'}, + coursefilename => $env{'request.course.fn'}, # # Flags temporary => 0, # true if this sheet has been modified but not saved - new_rows => 0, # true if this sheet has new rows + new_rows => 0, # true if this sheet has new rows + loaded => 0, # true if the formulas have been loaded # # blackout is used to determine if any data needs to be hidden from the # student. @@ -126,18 +141,9 @@ sub new { othersheets => [], }; # - $self->{'uhome'} = &Apache::lonnet::homeserver($name,$domain); - # bless($self,$class); - # - # Load in the spreadsheet definition $self->filename($filename); - if (exists($ENV{'form.workcopy'}) && - $self->{'type'} eq $ENV{'form.workcopy'}) { - $self->load_tmp(); - } else { - $self->load(); - } + # return $self; } @@ -160,22 +166,26 @@ sub filename { $newfilename !~ /\w/ || $newfilename eq '') { my $key = 'course.'.$self->{'cid'}.'.spreadsheet_default_'. $self->{'type'}; - if (exists($ENV{$key}) && $ENV{$key} ne '') { - $newfilename = $ENV{$key}; + if (exists($env{$key}) && $env{$key} ne '') { + $newfilename = $env{$key}; } else { $newfilename = 'default_'.$self->{'type'}; } } - if ($newfilename !~ /\w/ || $newfilename =~ /^\W*$/) { - $newfilename = 'default_'.$self->{'type'}; - } - if ($newfilename !~ /^default\.$self->{'type'}$/ && - $newfilename !~ /^\/res\/(.*)spreadsheet$/) { - if ($newfilename !~ /_$self->{'type'}$/) { - $newfilename =~ s/[\s_]*$//; - $newfilename .= '_'.$self->{'type'}; - } - } + if ($newfilename eq &mt('LON-CAPA Standard')) { + undef($newfilename); + } else { + if ($newfilename !~ /\w/ || $newfilename =~ /^\W*$/) { + $newfilename = 'default_'.$self->{'type'}; + } + if ($newfilename !~ /^default\.$self->{'type'}$/ && + $newfilename !~ /^\/res\/(.*)spreadsheet$/) { + if ($newfilename !~ /_$self->{'type'}$/) { + $newfilename =~ s/[\s_]*$//; + $newfilename .= '_'.$self->{'type'}; + } + } + } $self->{'filename'} = $newfilename; return; } @@ -200,6 +210,8 @@ sub make_default { {'spreadsheet_default_'.$self->{'type'} => $self->filename()}, $self->{'cdom'},$self->{'cnum'}); return $result if ($result ne 'ok'); + &Apache::lonnet::appenv({'course.'.$self->{'cid'}.'.spreadsheet_default_'. + $self->{'type'} => $self->filename()}); my $symb = $self->{'symb'}; $symb = '' if (! defined($symb)); &Apache::lonnet::expirespread('','',$self->{'type'},$symb); @@ -220,16 +232,8 @@ course environment. Returns 0 otherwise sub is_default { my $self = shift; # Check to find out if we are the default spreadsheet (filenames match) - my $default_filename = ''; - my %tmphash = &Apache::lonnet::get('environment', - ['spreadsheet_default_'. - $self->{'type'}], - $self->{'cdom'}, - $self->{'cnum'}); - my ($tmp) = keys(%tmphash); - if ($tmp !~ /^(con_lost|error|no_such_host)/i) { - $default_filename = $tmphash{'spreadsheet_default_'.$self->{'type'}}; - } + my $default_filename = $env{'course.'.$self->{'cid'}. + '.spreadsheet_default_'.$self->{'type'}}; if ($default_filename =~ /^\s*$/) { $default_filename = 'default_'.$self->{'type'}; } @@ -259,10 +263,10 @@ sub initialize_spreadsheet_package { sub load_spreadsheet_expirationdates { undef %expiredates; - my $cid=$ENV{'request.course.id'}; + my $cid=$env{'request.course.id'}; my @tmp = &Apache::lonnet::dump('nohist_expirationdates', - $ENV{'course.'.$cid.'.domain'}, - $ENV{'course.'.$cid.'.num'}); + $env{'course.'.$cid.'.domain'}, + $env{'course.'.$cid.'.num'}); if (lc($tmp[0]) !~ /^error/){ %expiredates = @tmp; } @@ -318,7 +322,7 @@ sub initialize_safe_space { my $self = shift; my $usection = &Apache::lonnet::getsection($self->{'domain'}, $self->{'name'}, - $ENV{'request.course.id'}); + $env{'request.course.id'}); if (! defined($safeeval)) { $safeeval = new Safe(shift); my $safehole = new Safe::Hole; @@ -328,7 +332,12 @@ sub initialize_safe_space { $safeeval->deny(":base_io"); $safehole->wrap(\&Apache::lonnet::EXT,$safeeval,'&Apache::lonnet::EXT'); $safehole->wrap(\&mask,$safeeval,'&mask'); + $safehole->wrap(\&Apache::lonnet::logthis,$safeeval,'&logthis'); $safeeval->share('$@'); + # Holds the (computed, final) values for the sheet + # This is only written to by &calc, the spreadsheet computation routine. + # It is read by many functions + $safeeval->share('%sheet_values'); my $code=<<'ENDDEFS'; # ---------------------------------------------------- Inside of the safe space # @@ -338,12 +347,9 @@ sub initialize_safe_space { # c: preloaded constants (A-column) # rl: row label # os: other spreadsheets (for student spreadsheet only) -undef %sheet_values; # Holds the (computed, final) values for the sheet - # This is only written to by &calc, the spreadsheet computation routine. - # It is read by many functions -undef %t; # Holds the values of the spreadsheet temporarily. Set in &sett, - # which does the translation of strings like C5 into the value in C5. - # Used in &calc - %t holds the values that are actually eval'd. +undef %t; # Holds the forumlas of the spreadsheet to be computed. Set in + # &sett, which does the translation of strings like C5 into the value + # in C5. Used in &calc - %t holds the values that are actually eval'd. undef %f; # Holds the formulas for each cell. This is the users # (spreadsheet authors) data for each cell. undef %c; # Holds the constants for a sheet. In the assessment @@ -364,12 +370,10 @@ $filename = ''; # # user data $name = ''; -$uhome = ''; $domain = ''; # # course data $csec = ''; -$chome= ''; $cnum = ''; $cdom = ''; $cid = ''; @@ -393,10 +397,12 @@ Calls the system EXT function to determi #------------------------------------------------------- sub EXT { - my ($parameter) = @_; + my ($parameter,$specific_symb) = @_; return '' if (! defined($parameter) || $parameter eq ''); $parameter =~ s/^parameter\./resource\./; - my $value = &Apache::lonnet::EXT($parameter,$symb,$domain,$name,$usection); + if ($specific_symb eq '') { $specific_symb = $symb; } + my $value = &Apache::lonnet::EXT($parameter,$specific_symb,$domain,$name, + $usection); return $value; } @@ -412,8 +418,8 @@ returns the number of items in the range #------------------------------------------------------- sub NUM { - my $mask=&mask(@_); - my $num= $#{@{grep(eval("/$mask/"),keys(%sheet_values))}}+1; + my $values=&get_values(@_); + my $num= scalar(@$values); return $num; } @@ -428,10 +434,10 @@ sub NUM { #------------------------------------------------------- sub BIN { my ($low,$high,$lower,$upper)=@_; - my $mask=&mask($lower,$upper); + my $values=&get_values($lower,$upper); my $num=0; - foreach (grep eval("/$mask/"),keys(%sheet_values)) { - if (($sheet_values{$_}>=$low) && ($sheet_values{$_}<=$high)) { + foreach (@$values) { + if (($_>=$low) && ($_<=$high)) { $num++; } } @@ -450,10 +456,10 @@ returns the sum of items in the range. #------------------------------------------------------- sub SUM { - my $mask=&mask(@_); + my $values=&get_values(@_); my $sum=0; - foreach (grep eval("/$mask/"),keys(%sheet_values)) { - $sum+=$sheet_values{$_}; + foreach (@$values) { + $sum+=$_; } return $sum; } @@ -470,11 +476,11 @@ compute the average of the items in the #------------------------------------------------------- sub MEAN { - my $mask=&mask(@_); + my $values=&get_values(@_); my $sum=0; my $num=0; - foreach (grep eval("/$mask/"),keys(%sheet_values)) { - $sum+=$sheet_values{$_}; + foreach (@$values) { + $sum+=$_; $num++; } if ($num) { @@ -496,17 +502,17 @@ compute the standard deviation of the it #------------------------------------------------------- sub STDDEV { - my $mask=&mask(@_); + my $values=&get_values(@_); my $sum=0; my $num=0; - foreach (grep eval("/$mask/"),keys(%sheet_values)) { - $sum+=$sheet_values{$_}; + foreach (@$values) { + $sum+=$_; $num++; } unless ($num>1) { return undef; } my $mean=$sum/$num; $sum=0; - foreach (grep eval("/$mask/"),keys(%sheet_values)) { - $sum+=($sheet_values{$_}-$mean)**2; + foreach (@$values) { + $sum+=($_-$mean)**2; } return sqrt($sum/($num-1)); } @@ -523,10 +529,10 @@ compute the product of the items in the #------------------------------------------------------- sub PROD { - my $mask=&mask(@_); + my $values=&get_values(@_); my $prod=1; - foreach (grep eval("/$mask/"),keys(%sheet_values)) { - $prod*=$sheet_values{$_}; + foreach (@$values) { + $prod*=$_; } return $prod; } @@ -543,12 +549,11 @@ compute the maximum of the items in the #------------------------------------------------------- sub MAX { - my $mask=&mask(@_); + my $values=&get_values(@_); my $max='-'; - foreach (grep eval("/$mask/"),keys(%sheet_values)) { - unless ($max) { $max=$sheet_values{$_}; } - if (($sheet_values{$_}>$max) || ($max eq '-')) { - $max=$sheet_values{$_}; + foreach (@$values) { + if (($_>$max) || ($max eq '-')) { + $max=$_; } } return $max; @@ -566,12 +571,11 @@ compute the minimum of the items in the #------------------------------------------------------- sub MIN { - my $mask=&mask(@_); + my $values=&get_values(@_); my $min='-'; - foreach (grep eval("/$mask/"),keys(%sheet_values)) { - unless ($max) { $max=$sheet_values{$_}; } - if (($sheet_values{$_}<$min) || ($min eq '-')) { - $min=$sheet_values{$_}; + foreach (@$values) { + if (($_<$min) || ($min eq '-')) { + $min=$_; } } return $min; @@ -591,12 +595,8 @@ compute the sum of the largest 'num' ite #------------------------------------------------------- sub SUMMAX { my ($num,$lower,$upper)=@_; - my $mask=&mask($lower,$upper); - my @inside=(); - foreach (grep eval("/$mask/"),keys(%sheet_values)) { - push (@inside,$sheet_values{$_}); - } - @inside=sort(@inside); + my $values=&get_values($lower,$upper); + my @inside=sort {$a <=> $b} (@$values); my $sum=0; my $i; for ($i=$#inside;(($i>$#inside-$num) && ($i>=0));$i--) { $sum+=$inside[$i]; @@ -618,12 +618,8 @@ compute the sum of the smallest 'num' it #------------------------------------------------------- sub SUMMIN { my ($num,$lower,$upper)=@_; - my $mask=&mask($lower,$upper); - my @inside=(); - foreach (grep eval("/$mask/"),keys(%sheet_values)) { - $inside[$#inside+1]=$sheet_values{$_}; - } - @inside=sort(@inside); + my $values=&get_values($lower,$upper); + my @inside=sort {$a <=> $b} (@$values); my $sum=0; my $i; for ($i=0;(($i<$num) && ($i<=$#inside));$i++) { $sum+=$inside[$i]; @@ -680,8 +676,81 @@ sub MAXPARM { } +=pod + +=item PARM(parametername) + +Returns the value of the parameter matching the input parameter name. +parametername should be a string such as 'parameter_1_opendate'. + +=cut + +#------------------------------------------------------- +sub PARM { + return $c{$_[0]}; +} + +#------------------------------------------------------- + +=pod + +=item &get_values($lower,$upper) + +Inputs: $lower and $upper, cell names ("X12" or "a150") or globs ("X*"). + +Returns: an array ref of the values of the cells that exist in the + speced range + +=cut + +#------------------------------------------------------- +sub get_values { + my ($lower,$upper)=@_; + $upper = $lower if (! defined($upper)); + my @values; + my ($la,$ld) = ($lower=~/([A-z]|\*)(\d+|\*)/); + my ($ua,$ud) = ($upper=~/([A-z]|\*)(\d+|\*)/); + my ($alpha,$num); + if ($ld ne '*' && $ud ne '*') { + my @alpha; + if (($la eq '*') || ($ua eq '*')) { + @alpha=('A'..'z'); + } else { + if ($la gt $ua) { ($la,$ua)=($ua,$la); } + if ((lc($la) ne $la) && (lc($ua) eq $ua)) { + @alpha=($la..'Z','a'..$ua); + } else { + @alpha=($la..$ua); + } + } + my @num=($ld..$ud); + foreach my $a (@alpha) { + foreach my $n (@num) { + if ((exists($sheet_values{$a.$n})) && ($sheet_values{$a.$n} ne '')) { + push(@values,$sheet_values{$a.$n}); + } + } + } + return \@values; + } else { + $num = '([1-9]\d*)'; + } + if (($la eq '*') || ($ua eq '*')) { + $alpha='[A-z]'; + } else { + if ($la gt $ua) { ($la,$ua)=($ua,$la); } + $alpha=qq/[$la-$ua]/; + } + my $expression = '^'.$alpha.$num.'$'; + foreach my $item (grep(/$expression/,keys(%sheet_values))) { + unless ($sheet_values{$item} eq '') { + push(@values,$sheet_values{$item}); + } + } + return \@values; +} + sub calc { - %sheet_values = %t; my $notfinished = 1; my $lastcalc = ''; my $depth = 0; @@ -705,7 +774,7 @@ sub calc { $depth++; if ($depth>100) { undef %sheet_values; - return $lastcalc.': Maximum calculation depth exceeded'; + return $lastcalc.': '.&mt('Maximum calculation depth exceeded'); } } return 'okay'; @@ -722,7 +791,7 @@ ENDDEFS # itself my $initstring = ''; foreach (qw/name domain type symb cid csec coursefilename - cnum cdom chome uhome/) { + cnum cdom/) { $initstring.= qq{\$$_="$self->{$_}";}; } $initstring.=qq{\$usection="$usection";}; @@ -742,92 +811,6 @@ ENDDEFS ###################################################### - -###################################################### - -=pod - -=item &mask($lower,$upper) - -Inputs: $lower and $upper, cell names ("X12" or "a150") or globs ("X*"). - -Returns: Regular expression matching spreadsheet cells that are within -the rectangle defined by $lower and $upper. Due to the nature of the -regular expression this result must be used inside an eval(). - -=cut - -###################################################### -{ - -my %memoizer; - -sub mask { - my ($lower,$upper)=@_; - my $key = $lower.'_'.$upper; - if (exists($memoizer{$key})) { - return $memoizer{$key}; - } - $upper = $lower if (! defined($upper)); - # - my ($la,$ld) = ($lower=~/([A-z]|\*)(\d+|\*)/); - my ($ua,$ud) = ($upper=~/([A-z]|\*)(\d+|\*)/); - # - my $alpha=''; - my $num=''; - # - # Do not put parenthases around $alpha. - # $num depends on the value in $1. - if (($la eq '*') || ($ua eq '*')) { - $alpha='[A-z]'; - } else { - if ($la gt $ua) { - my $tmp = $ua; - $ua = $la; - $la = $ua; - } - $alpha=qq/[$la-$ua]/; - } - if ($ld ne '*' && $ud ne '*') { - # Make sure $ld <= $ud - if ($ld > $ud) { - my $tmp = $ud; - $ud = $ld; - $ld = $tmp; - } - # Here we make a regular expression using some advanced regexp - # abilities. - # (\d+) will match the digits of the cell name and dump them in - # to $1 - # (?(?{ ... code ...} pattern_if_true | pattern_if_false)) will - # choose pattern_if_true if { ... code ... } is true and - # pattern_if_false if { ... code ... } is false. - # In this case, pattern_if_true is empty. pattern_if_false is - # 'donotmatch' and will not match our cells because none of - # them end with donotmatch. - # Unfortunately, the use of this type of regular expression - # requires that each match be wrapped in an eval(). Search for - # $mask in this module for examples - $num = '(\d+)(?(?{$1>= '.$ld.' && $1<='.$ud.'})|donotmatch)'; - } else { - $num = '(\d+)'; - } - my $expression = '^'.$alpha.$num.'$'; - $memoizer{$key} = $expression; - return $expression; -} - -# -# Debugging routine -sub dump_memoized_values { - while (my ($key,$value) = each(%memoizer)) { - &Apache::lonnet::logthis('memoizer: '.$key.' = '.$value); - } - return; -} - -} - ## ## sub add_hash_to_safe {} # spreadsheet, would like to destroy ## @@ -839,18 +822,20 @@ sub expandnamed { my $self = shift; my $expression=shift; if ($expression=~/^\&/) { - my ($func,$var,$formula)=($expression=~/^\&(\w+)\(([^\;]+)\;(.*)\)/); + my ($func,$var,$formula)=($expression=~/^\&(\w+)\(([^\;]+)\;(.*)\)/s); my @vars=split(/\W+/,$formula); + # make the list uniq + @vars = keys(%{{ map { $_ => 1 } @vars }}); my %values=(); foreach my $varname ( @vars ) { if ($varname=~/^(parameter|stores|timestamp)/) { $formula=~s/$varname/'$c{\''.$varname.'\'}'/ge; - $varname=~s/$var/\([\\w:\\- ]\+\)/g; - foreach (keys(%{$self->{'constants'}})) { - if ($_=~/$varname/) { - $values{$1}=1; - } - } + $varname=~s/$var/\([\\w:\\- ]\+\)/g; + foreach (keys(%{$self->{'constants'}})) { + if ($_=~/$varname/) { + $values{$1}=1; + } + } } } if ($func eq 'EXPANDSUM') { @@ -861,7 +846,7 @@ sub expandnamed { $result.=$thissum.'+'; } $result=~s/\+$//; - return $result; + return '('.$result.')'; } else { return 0; } @@ -888,7 +873,7 @@ sub expandnamed { $returnvalue = $values[0]; } elsif (scalar(@matches) > 0) { # more than one match. Look for a concise one - $returnvalue = "'non-unique parameter name : $expression'"; + $returnvalue = "'".&mt('non-unique parameter name: [_1]',$expression).'"'; for (my $i=0; $i<=$#matches;$i++) { if ($matches[$i] =~ /^$expression$/) { # why do we not do this lookup here? @@ -898,7 +883,7 @@ sub expandnamed { } else { # There was a negative number of matches, which indicates # something is wrong with reality. Better warn the user. - $returnvalue = '"bizzare parameter: '.$expression.'"'; + $returnvalue = "'".&mt('bizzare parameter: [_1]',$expression)."'"; } return $returnvalue; } @@ -907,6 +892,7 @@ sub expandnamed { sub sett { my $self = shift; my %t=(); + undef(%Apache::Spreadsheet::sheet_values); # # Deal with the template row foreach my $col ($self->template_cells()) { @@ -923,7 +909,7 @@ sub sett { # Replace 'A0' with the value from 'A0' $t{$cell}=~s/(^|[^\"\'])([A-Za-z]\d+)/$1\$sheet_values\{\'$2\'\}/g; # Replace parameters - $t{$cell}=~s/(^|[^\"\'])\[([^\]]+)\]/$1.$self->expandnamed($2)/ge; + $t{$cell}=~s/(^|[^\"\'])\[([^\]]+)\]/$1.$self->expandnamed($2)/sge; } } # @@ -935,17 +921,18 @@ sub sett { $t{$cell}=$formula; $t{$cell}=~s/\.\.+/\,/g; $t{$cell}=~s/(^|[^\"\'])([A-Za-z]\d+)/$1\$sheet_values\{\'$2\'\}/g; - $t{$cell}=~s/(^|[^\"\'])\[([^\]]+)\]/$1.$self->expandnamed($2)/ge; + $t{$cell}=~s/(^|[^\"\'])\[([^\]]+)\]/$1.$self->expandnamed($2)/sge; } elsif ( $col =~ /^[A-Z]$/ ) { - if ($formula !~ /^\!/ && exists($self->{'constants'}->{$cell})) { - my $data = $self->{'constants'}->{$cell}; - $t{$cell} = $data; + if ($formula !~ /^\!/ && exists($self->{'constants'}->{$cell}) + && $self->{'constants'}->{$cell} ne '') { + $Apache::Spreadsheet::sheet_values{$cell}= + eval($self->{'constants'}->{$cell}); } } else { # $row > 1 and $col =~ /[a-z] $t{$cell}=$formula; $t{$cell}=~s/\.\.+/\,/g; $t{$cell}=~s/(^|[^\"\'])([A-Za-z]\d+)/$1\$sheet_values\{\'$2\'\}/g; - $t{$cell}=~s/(^|[^\"\'])\[([^\]]+)\]/$1.$self->expandnamed($2)/ge; + $t{$cell}=~s/(^|[^\"\'])\[([^\]]+)\]/$1.$self->expandnamed($2)/sge; } } %{$self->{'safe'}->varglob('t')}=%t; @@ -958,11 +945,11 @@ sub sett { sub sync_safe_space { my $self = shift; # Inside the safe space 'formulas' has a diabolical alter-ego named 'f'. - %{$self->{'safe'}->varglob('f')}=%{$self->{'formulas'}}; + #%{$self->{'safe'}->varglob('f')}=%{$self->{'formulas'}}; # 'constants' leads a peaceful hidden life of 'c'. %{$self->{'safe'}->varglob('c')}=%{$self->{'constants'}}; # 'othersheets' hides as 'os', a disguise few can penetrate. - @{$self->{'safe'}->varglob('os')}=@{$self->{'othersheets'}}; + #@{$self->{'safe'}->varglob('os')}=@{$self->{'othersheets'}}; } ## @@ -1015,15 +1002,32 @@ sub formulas { $self->{'formulas'} = $formulas; $self->{'rows'} = []; $self->{'template_cells'} = []; + $self->{'loaded'} = 1; return; } else { + $self->check_formulas_loaded(); return %{$self->{'formulas'}}; } } +sub check_formulas_loaded { + my $self=shift; + if (!$self->{'loaded'}) { + $self->{'loaded'}=1; + # Load in the spreadsheet definition + if (exists($env{'form.workcopy'}) && + $self->{'type'} eq $env{'form.workcopy'}) { + $self->load_tmp(); + } else { + $self->load(); + } + } +} + sub set_formula { my $self = shift; my ($cell,$formula) = @_; + $self->check_formulas_loaded(); $self->{'formulas'}->{$cell}=$formula; return; } @@ -1033,7 +1037,7 @@ sub set_formula { ## sub formulas_keys { my $self = shift; - my @keys = keys(%{$self->{'formulas'}}); + $self->check_formulas_loaded(); return keys(%{$self->{'formulas'}}); } @@ -1044,6 +1048,7 @@ sub formulas_keys { sub formula { my $self = shift; my $cell = shift; + $self->check_formulas_loaded(); if (defined($cell) && exists($self->{'formulas'}->{$cell})) { return $self->{'formulas'}->{$cell}; } @@ -1122,6 +1127,7 @@ sub rebuild_stats { my $self = shift; $self->{'rows'}=[]; $self->{'template_cells'}=[]; + $self->check_formulas_loaded(); while (my ($cell,$formula) = each(%{$self->{'formulas'}})) { push(@{$self->{'rows'}},$1) if ($cell =~ /^A(\d+)/ && $1 != 0); push(@{$self->{'template_cells'}},$1) if ($cell =~ /^template_(\w+)/); @@ -1222,17 +1228,24 @@ sub display { my ($r) = @_; my $outputmode = 'html'; foreach ($self->output_options()) { - if ($ENV{'form.output_format'} eq $_->{'value'}) { + if ($env{'form.output_format'} eq $_->{'value'}) { $outputmode = $_->{'value'}; last; } } + $self->{outputmode} = $outputmode; if ($outputmode eq 'html') { $self->compute($r); $self->outsheet_html($r); } elsif ($outputmode eq 'htmlclasslist') { # No computation neccessary... This is kludgy $self->outsheet_htmlclasslist($r); + } elsif ($outputmode eq 'source') { + # No computation necessary. Rumor has it that this is some + # sort of kludge w.r.t. not "computing". It's also + # a bit of of a kludge that we call "outsheet_html" and + # let the 'outputmode' cause the outputting of source. + $self->outsheet_html($r); } elsif ($outputmode eq 'excel') { $self->compute($r); $self->outsheet_excel($r); @@ -1254,9 +1267,9 @@ sub html_report_error { my $self = shift(); my $Str = ''; if ($self->badcalc()) { - $Str = '

'. + $Str = '

'. &mt('An error occurred while calculating this spreadsheet'). - "

\n". + "

\n". '
'.$self->calcerror()."
\n"; } return $Str; @@ -1266,16 +1279,18 @@ sub html_export_row { my $self = shift(); my ($color) = @_; $color = '#CCCCFF' if (! defined($color)); - my $allowed = &Apache::lonnet::allowed('mgr',$ENV{'request.course.id'}); + my $allowed = &Apache::lonnet::allowed('mgr',$env{'request.course.id'}); my $row_html; my @rowdata = $self->get_row(0); foreach my $cell (@rowdata) { if ($cell->{'name'} =~ /^[A-Z]/) { $row_html .= ''. - &html_editable_cell($cell,$color,$allowed).''; + &html_editable_cell($cell,$color,$allowed, + $self->{outputmode} eq 'source').''; } else { $row_html .= ''. - &html_editable_cell($cell,'#DDCCFF',$allowed).''; + &html_editable_cell($cell,'#DDCCFF',$allowed, + $self->{outputmode} eq 'source').''; } } return $row_html; @@ -1283,7 +1298,7 @@ sub html_export_row { sub html_template_row { my $self = shift(); - my $allowed = &Apache::lonnet::allowed('mgr',$ENV{'request.course.id'}); + my $allowed = &Apache::lonnet::allowed('mgr',$env{'request.course.id'}); my ($num_uneditable,$importcolor) = @_; my $row_html; my @rowdata = $self->get_template_row(); @@ -1294,15 +1309,16 @@ sub html_template_row { $row_html .= ''. &html_uneditable_cell($cell,'#FFDDDD',$allowed).''; } else { - $row_html .= ''. - &html_editable_cell($cell,'#EOFFDD',$allowed).''; + $row_html .= ''. + &html_editable_cell($cell,'#E0FFDD',$allowed, + $self->{outputmode} eq 'source').''; } } return $row_html; } sub html_editable_cell { - my ($cell,$bgcolor,$allowed) = @_; + my ($cell,$bgcolor,$allowed,$showsource) = @_; my $result; my ($name,$formula,$value); if (defined($cell)) { @@ -1312,7 +1328,13 @@ sub html_editable_cell { } $name = '' if (! defined($name)); $formula = '' if (! defined($formula)); - if (! defined($value)) { + if ($showsource) { + if (!defined($formula) || $formula =~ /^\s*$/) { + $value = '#'; + } else { + $value = &HTML::Entities::encode($formula, '<>&"'); + } + } elsif (! defined($value)) { $value = '#'; if ($formula ne '') { $value = 'undefined value'; @@ -1350,7 +1372,7 @@ sub html_uneditable_cell { sub html_row { my $self = shift(); my ($num_uneditable,$row,$exportcolor,$importcolor) = @_; - my $allowed = &Apache::lonnet::allowed('mgr',$ENV{'request.course.id'}); + my $allowed = &Apache::lonnet::allowed('mgr',$env{'request.course.id'}); my @rowdata = $self->get_row($row); my $num_cols_output = 0; my $row_html; @@ -1364,8 +1386,9 @@ sub html_row { $row_html .= ''; $row_html .= &html_uneditable_cell($cell,'#FFDDDD'); } else { - $row_html .= ''; - $row_html .= &html_editable_cell($cell,'#E0FFDD',$allowed); + $row_html .= ''; + $row_html .= &html_editable_cell($cell,'#E0FFDD',$allowed, + $self->{outputmode} eq 'source'); } $row_html .= ''; } @@ -1374,7 +1397,7 @@ sub html_row { sub html_header { my $self = shift; - return '' if (! $ENV{'request.role.adv'}); + return '' if (! $env{'request.role.adv'}); return "\n". ''."\n". '\n". @@ -1389,6 +1412,8 @@ sub output_options { description => 'HTML'}, {value => 'excel', description => 'Excel'}, + {value => 'source', + description => 'Show Source'}, # {value => 'xml', # description => 'XML'}, {value => 'csv', @@ -1399,15 +1424,15 @@ sub output_selector { my $self = shift(); my $output_selector = '
'.&mt('Output Format').'
'.$self->output_selector()."