--- loncom/interface/spreadsheet/Spreadsheet.pm 2005/05/13 15:59:07 1.45 +++ loncom/interface/spreadsheet/Spreadsheet.pm 2006/03/05 21:35:10 1.64 @@ -1,5 +1,5 @@ # -# $Id: Spreadsheet.pm,v 1.45 2005/05/13 15:59:07 albertel Exp $ +# $Id: Spreadsheet.pm,v 1.64 2006/03/05 21:35:10 bowersj2 Exp $ # # Copyright Michigan State University Board of Trustees # @@ -106,13 +106,13 @@ sub new { 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'}, # # 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 +126,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; } @@ -200,6 +191,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 +213,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'}; } @@ -330,6 +315,10 @@ sub initialize_safe_space { $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 # @@ -339,12 +328,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 @@ -365,12 +351,10 @@ $filename = ''; # # user data $name = ''; -$uhome = ''; $domain = ''; # # course data $csec = ''; -$chome= ''; $cnum = ''; $cdom = ''; $cid = ''; @@ -394,10 +378,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; } @@ -713,7 +699,7 @@ sub get_values { } return \@values; } else { - $num = '(\d+)'; + $num = '([1-9]\d*)'; } if (($la eq '*') || ($ua eq '*')) { $alpha='[A-z]'; @@ -729,7 +715,6 @@ sub get_values { } sub calc { - %sheet_values = %t; my $notfinished = 1; my $lastcalc = ''; my $depth = 0; @@ -737,7 +722,7 @@ sub calc { $notfinished=0; while (my ($cell,$value) = each(%t)) { my $old=$sheet_values{$cell}; - $sheet_values{$cell}=eval $value; + #$sheet_values{$cell}=eval $value; # $errorlog .= $cell.' = '.$old.'->'.$sheet_values{$cell}."\n"; if ($@) { undef %sheet_values; @@ -770,7 +755,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";}; @@ -823,7 +808,7 @@ sub expandnamed { $result.=$thissum.'+'; } $result=~s/\+$//; - return $result; + return '('.$result.')'; } else { return 0; } @@ -869,6 +854,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()) { @@ -901,8 +887,8 @@ sub sett { } elsif ( $col =~ /^[A-Z]$/ ) { if ($formula !~ /^\!/ && exists($self->{'constants'}->{$cell}) && $self->{'constants'}->{$cell} ne '') { - my $data = $self->{'constants'}->{$cell}; - $t{$cell} = $data; + $Apache::Spreadsheet::sheet_values{$cell}= + eval($self->{'constants'}->{$cell}); } } else { # $row > 1 and $col =~ /[a-z] $t{$cell}=$formula; @@ -921,11 +907,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'}}; } ## @@ -978,15 +964,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; } @@ -996,7 +999,7 @@ sub set_formula { ## sub formulas_keys { my $self = shift; - my @keys = keys(%{$self->{'formulas'}}); + $self->check_formulas_loaded(); return keys(%{$self->{'formulas'}}); } @@ -1007,6 +1010,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}; } @@ -1085,6 +1089,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+)/); @@ -1190,12 +1195,19 @@ sub display { 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); @@ -1235,10 +1247,12 @@ sub html_export_row { 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; @@ -1258,14 +1272,15 @@ sub html_template_row { &html_uneditable_cell($cell,'#FFDDDD',$allowed).''; } else { $row_html .= ''. - &html_editable_cell($cell,'#EOFFDD',$allowed).''; + &html_editable_cell($cell,'#EOFFDD',$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)) { @@ -1275,7 +1290,15 @@ 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'; @@ -1328,7 +1351,8 @@ sub html_row { $row_html .= &html_uneditable_cell($cell,'#FFDDDD'); } else { $row_html .= ''; - $row_html .= &html_editable_cell($cell,'#E0FFDD',$allowed); + $row_html .= &html_editable_cell($cell,'#E0FFDD',$allowed, + $self->{outputmode} eq 'source'); } $row_html .= ''; } @@ -1352,6 +1376,8 @@ sub output_options { description => 'HTML'}, {value => 'excel', description => 'Excel'}, + {value => 'source', + description => 'Show Source'}, # {value => 'xml', # description => 'XML'}, {value => 'csv', @@ -1525,6 +1551,7 @@ sub outsheet_xml { ## Will be rendered for the user ## But not on this day my $Str = ''."\n"; + $self->check_formulas_loaded(); while (my ($cell,$formula) = each(%{$self->{'formulas'}})) { if ($cell =~ /^template_(\w+)/) { my $col = $1; @@ -1607,7 +1634,6 @@ sub load { my $stype = $self->{'type'}; my $cnum = $self->{'cnum'}; my $cdom = $self->{'cdom'}; - my $chome = $self->{'chome'}; # my $filename = $self->filename(); my $cachekey = join('_',($cnum,$cdom,$stype,$filename)); @@ -1616,6 +1642,10 @@ sub load { my ($formulas); if (exists($spreadsheets{$cachekey})) { $formulas = $spreadsheets{$cachekey}->{'formulas'}; + $self->formulas($formulas); + $self->{'row_source'}=$spreadsheets{$cachekey}->{'row_source'}; + $self->{'row_numbers'}=$spreadsheets{$cachekey}->{'row_numbers'}; + $self->{'maxrow'}=$spreadsheets{$cachekey}->{'maxrow'}; } else { # Not cached, need to read if (! defined($filename)) { @@ -1649,17 +1679,38 @@ sub load { $formulas = $self->load_system_default_sheet(); } } - $filename=$self->filename(); # filename may have changed - $cachekey = join('_',($cnum,$cdom,$stype,$filename)); - %{$spreadsheets{$cachekey}->{'formulas'}} = %{$formulas}; - } - $self->formulas($formulas); - $self->set_row_sources(); - $self->set_row_numbers(); + $self->formulas($formulas); + $self->set_row_sources(); + $self->set_row_numbers(); + $self->cache_sheet($formulas); + } +} + +sub cache_sheet { + my $self = shift; + my ($formulas) = @_; + my $stype = $self->{'type'}; + my $cnum = $self->{'cnum'}; + my $cdom = $self->{'cdom'}; + # + my $filename = $self->filename(); + my $cachekey = join('_',($cnum,$cdom,$stype,$filename)); + + if (ref($formulas) eq 'HASH') { + %{$spreadsheets{$cachekey}->{'formulas'}} = %{$formulas}; + } + if (ref($self->{'row_source'})) { + %{$spreadsheets{$cachekey}->{'row_source'}} =%{$self->{'row_source'}}; + } + if (ref($self->{'row_numbers'})) { + %{$spreadsheets{$cachekey}->{'row_numbers'}}=%{$self->{'row_numbers'}}; + } + $spreadsheets{$cachekey}->{'maxrow'} = $self->{'maxrow'}; } sub set_row_sources { my $self = shift; + $self->check_formulas_loaded(); while (my ($cell,$value) = each(%{$self->{'formulas'}})) { next if ($cell !~ /^A(\d+)/ || $1 < 1); my $row = $1; @@ -1670,6 +1721,7 @@ sub set_row_sources { sub set_row_numbers { my $self = shift; + $self->check_formulas_loaded(); while (my ($cell,$value) = each(%{$self->{'formulas'}})) { next if ($cell !~ /^A(\d+)$/); next if (! defined($value)); @@ -1704,11 +1756,9 @@ sub save { my $stype = $self->{'type'}; my $cnum = $self->{'cnum'}; my $cdom = $self->{'cdom'}; - my $chome = $self->{'chome'}; my $filename = $self->{'filename'}; - my $cachekey = join('_',($cnum,$cdom,$stype,$filename)); # Cache new sheet - %{$spreadsheets{$cachekey}->{'formulas'}}=%f; + $self->cache_sheet(\%f); # Write sheet foreach (keys(%f)) { delete($f{$_}) if ($f{$_} eq 'import'); @@ -1724,6 +1774,8 @@ sub save { {'spreadsheet_default_'.$stype => $filename }, $cdom,$cnum); return $reply if ($reply ne 'ok'); + &Apache::lonnet::appenv('course.'.$self->{'cid'}.'.spreadsheet_default_'. + $self->{'type'} => $self->filename()); } if ($self->is_default()) { if ($self->{'type'} eq 'studentcalc') {