Diff for /loncom/interface/spreadsheet/Spreadsheet.pm between versions 1.11 and 1.31

version 1.11, 2003/05/29 13:39:38 version 1.31, 2003/11/21 21:51:28
Line 48  Spreadsheet Line 48  Spreadsheet
 package Apache::Spreadsheet;  package Apache::Spreadsheet;
   
 use strict;  use strict;
   #use warnings FATAL=>'all';
   #no warnings 'uninitialized';
 use Apache::Constants qw(:common :http);  use Apache::Constants qw(:common :http);
 use Apache::lonnet;  use Apache::lonnet;
 use Safe;  use Safe;
Line 57  use HTML::Entities(); Line 59  use HTML::Entities();
 use HTML::TokeParser;  use HTML::TokeParser;
 use Spreadsheet::WriteExcel;  use Spreadsheet::WriteExcel;
 use Time::HiRes;  use Time::HiRes;
   use Apache::lonlocal;
   
 ##  ##
 ## Package Variables  ## Package Variables
Line 90  sub new { Line 93  sub new {
         type     => $stype,          type     => $stype,
         symb     => $usymb,          symb     => $usymb,
         errorlog => '',          errorlog => '',
         maxrow   => '',          maxrow   => 0,
         cid      => $ENV{'request.course.id'},          cid      => $ENV{'request.course.id'},
         cnum     => $ENV{'course.'.$ENV{'request.course.id'}.'.num'},          cnum     => $ENV{'course.'.$ENV{'request.course.id'}.'.num'},
         cdom     => $ENV{'course.'.$ENV{'request.course.id'}.'.domain'},          cdom     => $ENV{'course.'.$ENV{'request.course.id'}.'.domain'},
         chome    => $ENV{'course.'.$ENV{'request.course.id'}.'.home'},          chome    => $ENV{'course.'.$ENV{'request.course.id'}.'.home'},
         coursedesc => $ENV{'course.'.$ENV{'request.course.id'}.'.description'},          coursedesc => $ENV{'course.'.$ENV{'request.course.id'}.'.description'},
         coursefilename => $ENV{'request.course.fn'},          coursefilename => $ENV{'request.course.fn'},
         temporary => '',          #
           # Flags
           temporary => 0,  # true if this sheet has been modified but not saved
           new_rows  => 0, # true if this sheet has new rows
         #          #
         # blackout is used to determine if any data needs to be hidden from the          # blackout is used to determine if any data needs to be hidden from the
         # student.          # student.
Line 142  sub filename { Line 148  sub filename {
     if (@_) {      if (@_) {
         my ($newfilename) = @_;          my ($newfilename) = @_;
         if (! defined($newfilename) || $newfilename eq 'Default' ||          if (! defined($newfilename) || $newfilename eq 'Default' ||
             $newfilename !~ /\w/) {              $newfilename !~ /\w/ || $newfilename eq '') {
             my %tmphash = &Apache::lonnet::get('environment',              my $key = 'course.'.$self->{'cid'}.'.spreadsheet_default_'.
                                                ['spreadsheet_default_'.                  $self->{'type'};
                                                 $self->{'type'}],              if (exists($ENV{$key}) && $ENV{$key} ne '') {
                                                $self->{'cdom'},                  $newfilename = $ENV{$key};
                                                $self->{'cnum'});              } else {
             my ($tmp) = keys(%tmphash);                  $newfilename = 'default_'.$self->{'type'};
             if ($tmp !~ /^(con_lost|error|no_such_host)/i) {  
                 $newfilename=$tmphash{'spreadsheet_default_'.$self->{'type'}};  
             }  
             if (! defined($newfilename) ||   
                 $newfilename !~ /\w/   ||   
                 $newfilename =~ /^\W*$/) {  
                 $newfilename = 'default.'.$self->{'type'};  
             }              }
         } elsif ($newfilename !~ /^\/res\/.*\.spreadsheet/ &&          }
                  $newfilename !~ /^default\.$self->{'type'}$/ ) {          if ($newfilename !~ /\w/ || $newfilename =~ /^\W*$/) {
             my $regexp = '_'.$self->{'type'}.'$';              $newfilename = 'default_'.$self->{'type'};
             if ($newfilename !~ /$regexp/) {          }
           if ($newfilename !~ /^default\.$self->{'type'}$/ ) {
               if ($newfilename !~ /_$self->{'type'}$/) {
                   $newfilename =~ s/[\s_]*$//;
                 $newfilename .= '_'.$self->{'type'};                  $newfilename .= '_'.$self->{'type'};
             }              }
         }          }
Line 185  default spreadsheets.......! Line 187  default spreadsheets.......!
 sub make_default {  sub make_default {
     my $self = shift();      my $self = shift();
     my $result = &Apache::lonnet::put('environment',      my $result = &Apache::lonnet::put('environment',
          {'spreadsheet_default_'.$self->{'type'} => $self->filename()},              {'spreadsheet_default_'.$self->{'type'} => $self->filename()},
                                      $self->{'cdom'},$self->{'cnum'});                                       $self->{'cdom'},$self->{'cnum'});
     return $result if ($result ne 'ok');      return $result if ($result ne 'ok');
     my $symb = $self->{'symb'};      my $symb = $self->{'symb'};
Line 218  sub is_default { Line 220  sub is_default {
     if ($tmp !~ /^(con_lost|error|no_such_host)/i) {      if ($tmp !~ /^(con_lost|error|no_such_host)/i) {
         $default_filename = $tmphash{'spreadsheet_default_'.$self->{'type'}};          $default_filename = $tmphash{'spreadsheet_default_'.$self->{'type'}};
     }      }
       if ($default_filename =~ /^\s*$/) {
           $default_filename = 'default_'.$self->{'type'};
       }
     return 1 if ($self->filename() eq $default_filename);      return 1 if ($self->filename() eq $default_filename);
     return 0;      return 0;
 }  }
Line 227  sub initialize { Line 232  sub initialize {
     # the descendents of the spreadsheet class.      # the descendents of the spreadsheet class.
 }  }
   
   sub clear_package {
       # This method is here to remind you that it will be overridden by
       # the descendents of the spreadsheet class.
   }
   
   sub cleanup {
       my $self = shift();
       $self->clear_package();
   }
   
 sub initialize_spreadsheet_package {  sub initialize_spreadsheet_package {
     &load_spreadsheet_expirationdates();      &load_spreadsheet_expirationdates();
     &clear_spreadsheet_definition_cache();      &clear_spreadsheet_definition_cache();
Line 246  sub load_spreadsheet_expirationdates { Line 261  sub load_spreadsheet_expirationdates {
 sub check_expiration_time {  sub check_expiration_time {
     my $self = shift;      my $self = shift;
     my ($time)=@_;      my ($time)=@_;
     my ($key1,$key2,$key3,$key4);      return 0 if (! defined($time));
       my ($key1,$key2,$key3,$key4,$key5);
       # Description of keys
       #
       # key1: all sheets of this type have expired
       # key2: all sheets of this type for this student
       # key3: all sheets of this type in this map for this student
       # key4: this assessment sheet for this student
       # key5: this assessment sheet for all students
     $key1 = '::'.$self->{'type'}.':';      $key1 = '::'.$self->{'type'}.':';
     $key2 = $self->{'name'}.':'.$self->{'domain'}.':'.$self->{'type'}.':';      $key2 = $self->{'name'}.':'.$self->{'domain'}.':'.$self->{'type'}.':';
     $key3 = $key2.$self->{'container'} if (defined($self->{'container'}));      $key3 = $key2.$self->{'container'} if (defined($self->{'container'}));
     $key4 = $key2.$self->{'usymb'} if (defined($self->{'usymb'}));      $key4 = $key2.$self->{'symb'} if (defined($self->{'symb'}));
     foreach my $key ($key1,$key2,$key3,$key4) {      $key5 = $key1.$self->{'symb'} if (defined($self->{'symb'}));
       my $returnvalue = 1; # default to okay
       foreach my $key ($key1,$key2,$key3,$key4,$key5) {
         next if (! defined($key));          next if (! defined($key));
         if (exists($expiredates{$key}) &&$expiredates{$key} > $time) {          if (exists($expiredates{$key}) && $expiredates{$key} > $time) {
             return 0;              $returnvalue = 0; # need to recompute
         }          }
     }      }
     return 1;      return $returnvalue;
 }  }
   
 ######################################################  ######################################################
Line 275  Returns the safe space required by a Spr Line 300  Returns the safe space required by a Spr
 =cut  =cut
   
 ######################################################  ######################################################
   { 
   
       my $safeeval;
   
 sub initialize_safe_space {  sub initialize_safe_space {
     my $self = shift;    my $self = shift;
     my $safeeval = new Safe(shift);    if (! defined($safeeval)) {
     my $safehole = new Safe::Hole;        $safeeval = new Safe(shift);
     $safeeval->permit("entereval");        my $safehole = new Safe::Hole;
     $safeeval->permit(":base_math");        $safeeval->permit("entereval");
     $safeeval->permit("sort");        $safeeval->permit(":base_math");
     $safeeval->deny(":base_io");        $safeeval->permit("sort");
     $safehole->wrap(\&Apache::lonnet::EXT,$safeeval,'&EXT');        $safeeval->deny(":base_io");
     $safehole->wrap(\&mask,$safeeval,'&mask');        $safehole->wrap(\&Apache::lonnet::EXT,$safeeval,'&EXT');
     $safeeval->share('$@');        $safehole->wrap(\&mask,$safeeval,'&mask');
     my $code=<<'ENDDEFS';        $safeeval->share('$@');
         my $code=<<'ENDDEFS';
 # ---------------------------------------------------- Inside of the safe space  # ---------------------------------------------------- Inside of the safe space
 #  #
 # f: formulas  # f: formulas
Line 351  returns the number of items in the range Line 381  returns the number of items in the range
 #-------------------------------------------------------  #-------------------------------------------------------
 sub NUM {  sub NUM {
     my $mask=&mask(@_);      my $mask=&mask(@_);
     my $num= $#{@{grep(/$mask/,keys(%sheet_values))}}+1;      my $num= $#{@{grep(eval("/$mask/"),keys(%sheet_values))}}+1;
     return $num;         return $num;   
 }  }
   
Line 368  sub BIN { Line 398  sub BIN {
     my ($low,$high,$lower,$upper)=@_;      my ($low,$high,$lower,$upper)=@_;
     my $mask=&mask($lower,$upper);      my $mask=&mask($lower,$upper);
     my $num=0;      my $num=0;
     foreach (grep /$mask/,keys(%sheet_values)) {      foreach (grep eval("/$mask/"),keys(%sheet_values)) {
         if (($sheet_values{$_}>=$low) && ($sheet_values{$_}<=$high)) {          if (($sheet_values{$_}>=$low) && ($sheet_values{$_}<=$high)) {
             $num++;              $num++;
         }          }
Line 390  returns the sum of items in the range. Line 420  returns the sum of items in the range.
 sub SUM {  sub SUM {
     my $mask=&mask(@_);      my $mask=&mask(@_);
     my $sum=0;      my $sum=0;
     foreach (grep /$mask/,keys(%sheet_values)) {      foreach (grep eval("/$mask/"),keys(%sheet_values)) {
         $sum+=$sheet_values{$_};          $sum+=$sheet_values{$_};
     }      }
     return $sum;         return $sum;   
Line 411  sub MEAN { Line 441  sub MEAN {
     my $mask=&mask(@_);      my $mask=&mask(@_);
     my $sum=0;       my $sum=0; 
     my $num=0;      my $num=0;
     foreach (grep /$mask/,keys(%sheet_values)) {      foreach (grep eval("/$mask/"),keys(%sheet_values)) {
         $sum+=$sheet_values{$_};          $sum+=$sheet_values{$_};
         $num++;          $num++;
     }      }
Line 436  compute the standard deviation of the it Line 466  compute the standard deviation of the it
 sub STDDEV {  sub STDDEV {
     my $mask=&mask(@_);      my $mask=&mask(@_);
     my $sum=0; my $num=0;      my $sum=0; my $num=0;
     foreach (grep /$mask/,keys(%sheet_values)) {      foreach (grep eval("/$mask/"),keys(%sheet_values)) {
         $sum+=$sheet_values{$_};          $sum+=$sheet_values{$_};
         $num++;          $num++;
     }      }
     unless ($num>1) { return undef; }      unless ($num>1) { return undef; }
     my $mean=$sum/$num;      my $mean=$sum/$num;
     $sum=0;      $sum=0;
     foreach (grep /$mask/,keys(%sheet_values)) {      foreach (grep eval("/$mask/"),keys(%sheet_values)) {
         $sum+=($sheet_values{$_}-$mean)**2;          $sum+=($sheet_values{$_}-$mean)**2;
     }      }
     return sqrt($sum/($num-1));          return sqrt($sum/($num-1));    
Line 463  compute the product of the items in the Line 493  compute the product of the items in the
 sub PROD {  sub PROD {
     my $mask=&mask(@_);      my $mask=&mask(@_);
     my $prod=1;      my $prod=1;
     foreach (grep /$mask/,keys(%sheet_values)) {      foreach (grep eval("/$mask/"),keys(%sheet_values)) {
         $prod*=$sheet_values{$_};          $prod*=$sheet_values{$_};
     }      }
     return $prod;         return $prod;   
Line 483  compute the maximum of the items in the Line 513  compute the maximum of the items in the
 sub MAX {  sub MAX {
     my $mask=&mask(@_);      my $mask=&mask(@_);
     my $max='-';      my $max='-';
     foreach (grep /$mask/,keys(%sheet_values)) {      foreach (grep eval("/$mask/"),keys(%sheet_values)) {
         unless ($max) { $max=$sheet_values{$_}; }          unless ($max) { $max=$sheet_values{$_}; }
         if (($sheet_values{$_}>$max) || ($max eq '-')) {           if (($sheet_values{$_}>$max) || ($max eq '-')) { 
             $max=$sheet_values{$_};               $max=$sheet_values{$_}; 
Line 506  compute the minimum of the items in the Line 536  compute the minimum of the items in the
 sub MIN {  sub MIN {
     my $mask=&mask(@_);      my $mask=&mask(@_);
     my $min='-';      my $min='-';
     foreach (grep /$mask/,keys(%sheet_values)) {      foreach (grep eval("/$mask/"),keys(%sheet_values)) {
         unless ($max) { $max=$sheet_values{$_}; }          unless ($max) { $max=$sheet_values{$_}; }
         if (($sheet_values{$_}<$min) || ($min eq '-')) {           if (($sheet_values{$_}<$min) || ($min eq '-')) { 
             $min=$sheet_values{$_};               $min=$sheet_values{$_}; 
Line 531  sub SUMMAX { Line 561  sub SUMMAX {
     my ($num,$lower,$upper)=@_;      my ($num,$lower,$upper)=@_;
     my $mask=&mask($lower,$upper);      my $mask=&mask($lower,$upper);
     my @inside=();      my @inside=();
     foreach (grep /$mask/,keys(%sheet_values)) {      foreach (grep eval("/$mask/"),keys(%sheet_values)) {
  push (@inside,$sheet_values{$_});   push (@inside,$sheet_values{$_});
     }      }
     @inside=sort(@inside);      @inside=sort(@inside);
Line 558  sub SUMMIN { Line 588  sub SUMMIN {
     my ($num,$lower,$upper)=@_;      my ($num,$lower,$upper)=@_;
     my $mask=&mask($lower,$upper);      my $mask=&mask($lower,$upper);
     my @inside=();      my @inside=();
     foreach (grep /$mask/,keys(%sheet_values)) {      foreach (grep eval("/$mask/"),keys(%sheet_values)) {
  $inside[$#inside+1]=$sheet_values{$_};   $inside[$#inside+1]=$sheet_values{$_};
     }      }
     @inside=sort(@inside);      @inside=sort(@inside);
Line 648  sub calc { Line 678  sub calc {
             return $lastcalc.': Maximum calculation depth exceeded';              return $lastcalc.': Maximum calculation depth exceeded';
         }          }
     }      }
     return '';      return 'okay';
 }  }
   
 # ------------------------------------------- End of "Inside of the safe space"  # ------------------------------------------- End of "Inside of the safe space"
 ENDDEFS  ENDDEFS
     $safeeval->reval($code);          $safeeval->reval($code);
       }
     $self->{'safe'} = $safeeval;      $self->{'safe'} = $safeeval;
     $self->{'root'} = $self->{'safe'}->root();      $self->{'root'} = $self->{'safe'}->root();
     #      #
Line 667  ENDDEFS Line 698  ENDDEFS
     $self->{'safe'}->reval($initstring);      $self->{'safe'}->reval($initstring);
     return $self;      return $self;
 }  }
   
   }
   
 ######################################################  ######################################################
   
 =pod  =pod
Line 680  ENDDEFS Line 714  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
   
 ######################################################  ######################################################
 {  {
Line 694  sub mask { Line 739  sub mask {
     }      }
     $upper = $lower if (! defined($upper));      $upper = $lower if (! defined($upper));
     #      #
     my ($la,$ld) = ($lower=~/([A-Za-z]|\*)(\d+|\*)/);      my ($la,$ld) = ($lower=~/([A-z]|\*)(\d+|\*)/);
     my ($ua,$ud) = ($upper=~/([A-Za-z]|\*)(\d+|\*)/);      my ($ua,$ud) = ($upper=~/([A-z]|\*)(\d+|\*)/);
     #      #
     my $alpha='';      my $alpha='';
     my $num='';      my $num='';
     #      #
       # Do not put parenthases around $alpha.
       # $num depends on the value in $1.
     if (($la eq '*') || ($ua eq '*')) {      if (($la eq '*') || ($ua eq '*')) {
         $alpha='[A-Za-z]';          $alpha='[A-z]';
     } else {      } else {
        if (($la=~/[A-Z]/) && ($ua=~/[A-Z]/) ||          if ($la gt $ua) {
            ($la=~/[a-z]/) && ($ua=~/[a-z]/)) {              my $tmp = $ua;
           $alpha='['.$la.'-'.$ua.']';              $ua = $la;
        } else {              $la = $ua;
           $alpha='['.$la.'-Za-'.$ua.']';          }
        }          $alpha=qq/[$la-$ua]/;
     }         }
     if (($ld eq '*') || ($ud eq '*')) {      if ($ld ne '*' && $ud ne '*') {
  $num='\d+';          # 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 {      } else {
         if (length($ld)!=length($ud)) {          $num = '(\d+)';
            $num.='(';  
    foreach ($ld=~m/\d/g) {  
               $num.='['.$_.'-9]';  
    }  
            if (length($ud)-length($ld)>1) {  
               $num.='|\d{'.(length($ld)+1).','.(length($ud)-1).'}';  
    }  
            $num.='|';  
            foreach ($ud=~m/\d/g) {  
                $num.='[0-'.$_.']';  
            }  
            $num.=')';  
        } else {  
            my @lda=($ld=~m/\d/g);  
            my @uda=($ud=~m/\d/g);  
            my $i;   
            my $j=0;   
            my $notdone=1;  
            for ($i=0;($i<=$#lda)&&($notdone);$i++) {  
                if ($lda[$i]==$uda[$i]) {  
    $num.=$lda[$i];  
                    $j=$i;  
                } else {  
                    $notdone=0;  
                }  
            }  
            if ($j<$#lda-1) {  
        $num.='('.$lda[$j+1];  
                for ($i=$j+2;$i<=$#lda;$i++) {  
                    $num.='['.$lda[$i].'-9]';  
                }  
                if ($uda[$j+1]-$lda[$j+1]>1) {  
    $num.='|['.($lda[$j+1]+1).'-'.($uda[$j+1]-1).']\d{'.  
                    ($#lda-$j-1).'}';  
                }  
        $num.='|'.$uda[$j+1];  
                for ($i=$j+2;$i<=$#uda;$i++) {  
                    $num.='[0-'.$uda[$i].']';  
                }  
                $num.=')';  
            } else {  
                if ($lda[-1]!=$uda[-1]) {  
                   $num.='['.$lda[-1].'-'.$uda[-1].']';  
        }  
            }  
        }  
     }      }
     my $expression ='^'.$alpha.$num."\$";      my $expression = '^'.$alpha.$num.'$';
     $memoizer{$key} = $expression;      $memoizer{$key} = $expression;
     return $expression;      return $expression;
 }  }
   
   #
   # Debugging routine
   sub dump_memoized_values {
       while (my ($key,$value) = each(%memoizer)) {
           &Apache::lonnet::logthis('memoizer: '.$key.' = '.$value);
       }
       return;
   }
   
 }  }
   
 ##  ##
Line 783  sub expandnamed { Line 812  sub expandnamed {
  my @vars=split(/\W+/,$formula);   my @vars=split(/\W+/,$formula);
         my %values=();          my %values=();
  foreach my $varname ( @vars ) {   foreach my $varname ( @vars ) {
             if ($varname=~/\D/) {              if ($varname=~/^(parameter|stores|timestamp)/) {
                $formula=~s/$varname/'$c{\''.$varname.'\'}'/ge;                  $formula=~s/$varname/'$c{\''.$varname.'\'}'/ge;
                $varname=~s/$var/\([\\w:\\- ]\+\)/g;                 $varname=~s/$var/\([\\w:\\- ]\+\)/g;
        foreach (keys(%{$self->{'constants'}})) {         foreach (keys(%{$self->{'constants'}})) {
   if ($_=~/$varname/) {    if ($_=~/$varname/) {
Line 814  sub expandnamed { Line 843  sub expandnamed {
         #    4. If there is a collision, return 'bad parameter name error'          #    4. If there is a collision, return 'bad parameter name error'
         my $returnvalue = '';          my $returnvalue = '';
         my @matches = ();          my @matches = ();
           my @values = ();
         $#matches = -1;          $#matches = -1;
         study $expression;          study $expression;
         my $parameter;          while (my($parameter,$value) = each(%{$self->{'constants'}})) {
         foreach $parameter (keys(%{$self->{'constants'}})) {              next if ($parameter !~ /$expression/);
             push @matches,$parameter if ($parameter =~ /$expression/);              push(@matches,$parameter);
               push(@values,$value);
         }          }
         if (scalar(@matches) == 0) {          if (scalar(@matches) == 0) {
             $returnvalue = '""';#'"unmatched parameter: '.$parameter.'"';              $returnvalue = '""';#'"unmatched parameter: '.$parameter.'"';
         } elsif (scalar(@matches) == 1) {          } elsif (scalar(@matches) == 1) {
             # why do we not do this lookup here, instead of delaying it?              # why do we not do this lookup here, instead of delaying it?
             $returnvalue = '$c{\''.$matches[0].'\'}';              $returnvalue = $values[0];
         } elsif (scalar(@matches) > 0) {          } elsif (scalar(@matches) > 0) {
             # more than one match.  Look for a concise one              # more than one match.  Look for a concise one
             $returnvalue =  "'non-unique parameter name : $expression'";              $returnvalue =  "'non-unique parameter name : $expression'";
             foreach (@matches) {              for (my $i=0; $i<=$#matches;$i++) {
                 if (/^$expression$/) {                  if ($matches[$i] =~ /^$expression$/) {
                     # why do we not do this lookup here?                      # why do we not do this lookup here?
                     $returnvalue = '$c{\''.$_.'\'}';                      $returnvalue = $values[$i];
                 }                  }
             }              }
         } else {          } else {
             # There was a negative number of matches, which indicates               # There was a negative number of matches, which indicates 
             # something is wrong with reality.  Better warn the user.              # something is wrong with reality.  Better warn the user.
             $returnvalue = '"bizzare parameter: '.$parameter.'"';              $returnvalue = '"bizzare parameter: '.$expression.'"';
         }          }
         return $returnvalue;          return $returnvalue;
     }      }
Line 1108  sub calcsheet { Line 1139  sub calcsheet {
 #    $self->logthis($self->get_errorlog());  #    $self->logthis($self->get_errorlog());
     %{$self->{'values'}} = %{$self->{'safe'}->varglob('sheet_values')};      %{$self->{'values'}} = %{$self->{'safe'}->varglob('sheet_values')};
 #    $self->logthis($self->get_errorlog());  #    $self->logthis($self->get_errorlog());
       if ($result ne 'okay') {
           $self->set_calcerror($result);
       }
     return $result;      return $result;
 }  }
   
   sub set_badcalc {
       my $self = shift();
       $self->{'badcalc'} =1;
       return;
   }
   
   sub badcalc {
       my $self = shift;
       if (exists($self->{'badcalc'}) && $self->{'badcalc'}) {
           return 1;
       } else {
           return 0;
       }
   }
   
   sub set_calcerror {
       my $self = shift;
       if (@_) {
           $self->set_badcalc();
           if (exists($self->{'calcerror'})) {
               $self->{'calcerror'}.="\n".$_[0];
           } else {
               $self->{'calcerror'}.=$_[0];
           }
       }
   }
   
   sub calcerror {
       my $self = shift;
       if ($self->badcalc()) {
           if (exists($self->{'calcerror'})) {
               return $self->{'calcerror'};
           }
       }
       return;
   }
   
 ###########################################################  ###########################################################
 ##  ##
 ## Output Helpers  ## Output Helpers
Line 1119  sub calcsheet { Line 1190  sub calcsheet {
 sub display {  sub display {
     my $self = shift;      my $self = shift;
     my ($r) = @_;      my ($r) = @_;
     $self->compute($r);  
     my $outputmode = 'html';      my $outputmode = 'html';
     if ($ENV{'form.output_format'} =~ /^(html|excel|csv)$/) {      foreach ($self->output_options()) {
         $outputmode = $ENV{'form.output_format'};          if ($ENV{'form.output_format'} eq $_->{'value'}) {
               $outputmode = $_->{'value'};
               last;
           }
     }      }
     if ($outputmode eq 'html') {      if ($outputmode eq 'html') {
           $self->compute($r);
         $self->outsheet_html($r);          $self->outsheet_html($r);
       } elsif ($outputmode eq 'htmlclasslist') {
           # No computation neccessary...  This is kludgy
           $self->outsheet_htmlclasslist($r);
     } elsif ($outputmode eq 'excel') {      } elsif ($outputmode eq 'excel') {
           $self->compute($r);
         $self->outsheet_excel($r);          $self->outsheet_excel($r);
     } elsif ($outputmode eq 'csv') {      } elsif ($outputmode eq 'csv') {
           $self->compute($r);
         $self->outsheet_csv($r);          $self->outsheet_csv($r);
     }      }
       $self->cleanup();
     return;      return;
 }  }
   
 ############################################  ############################################
 ##         HTML output routines           ##  ##         HTML output routines           ##
 ############################################  ############################################
   sub html_report_error {
       my $self = shift();
       my $Str = '';
       if ($self->badcalc()) {
           $Str = '<h3 style="color:red">'.
               &mt('An error occurred while calculating this spreadsheet').
               "</h3>\n".
               '<pre>'.$self->calcerror()."</pre>\n";
       }
       return $Str;
   }
   
 sub html_export_row {  sub html_export_row {
     my $self = shift();      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 $row_html;
     my @rowdata = $self->get_row(0);      my @rowdata = $self->get_row(0);
     foreach my $cell (@rowdata) {      foreach my $cell (@rowdata) {
         if ($cell->{'name'} =~ /^[A-Z]/) {          if ($cell->{'name'} =~ /^[A-Z]/) {
     $row_html .= '<td bgcolor="#CCCCFF">'.      $row_html .= '<td bgcolor="'.$color.'">'.
                 &html_editable_cell($cell,'#CCCCFF',$allowed).'</td>';                  &html_editable_cell($cell,$color,$allowed).'</td>';
         } else {          } else {
     $row_html .= '<td bgcolor="#DDCCFF">'.      $row_html .= '<td bgcolor="#DDCCFF">'.
                 &html_editable_cell($cell,'#DDCCFF',$allowed).'</td>';                  &html_editable_cell($cell,'#DDCCFF',$allowed).'</td>';
Line 1157  sub html_export_row { Line 1251  sub html_export_row {
 sub html_template_row {  sub html_template_row {
     my $self = shift();      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) = @_;      my ($num_uneditable,$importcolor) = @_;
     my $row_html;      my $row_html;
     my @rowdata = $self->get_template_row();      my @rowdata = $self->get_template_row();
     my $count = 0;      my $count = 0;
     for (my $i = 0; $i<=$#rowdata; $i++) {      for (my $i = 0; $i<=$#rowdata; $i++) {
         my $cell = $rowdata[$i];          my $cell = $rowdata[$i];
         if ($i < $num_uneditable) {          if ($i < $num_uneditable) {
     $row_html .= '<td bgcolor="#FFDDDD">'.      $row_html .= '<td bgcolor="'.$importcolor.'">'.
                 &html_uneditable_cell($cell,'#FFDDDD',$allowed).'</td>';                  &html_uneditable_cell($cell,'#FFDDDD',$allowed).'</td>';
         } else {          } else {
     $row_html .= '<td bgcolor="#EOFFDD">'.      $row_html .= '<td bgcolor="#EOFFDD">'.
Line 1196  sub html_editable_cell { Line 1290  sub html_editable_cell {
         $value = &HTML::Entities::encode($value) if ($value !~/&nbsp;/);          $value = &HTML::Entities::encode($value) if ($value !~/&nbsp;/);
     }      }
     return $value if (! $allowed);      return $value if (! $allowed);
     # Make the formula safe for outputting      #
     $formula =~ s/\'/\"/g;  
     # The formula will be parsed by the browser twice before being       # The formula will be parsed by the browser twice before being 
     # displayed to the user for editing.      # displayed to the user for editing. 
     $formula = &HTML::Entities::encode(&HTML::Entities::encode($formula));      #
     # Escape newlines so they make it into the edit window      # The encoding string "^A-blah" is placed in []'s inside a regexp, so 
     $formula =~ s/\n/\\n/gs;      # we specify the characters we want left alone by putting a '^' in front.
       $formula = &HTML::Entities::encode($formula,'^A-z0-9 !#$%-;=?~');
       # HTML::Entities::encode does not catch everything - we need '\' encoded
       $formula =~ s/\\/&\#092/g;
       # Escape it again - this time the only encodable character is '&'
       $formula =~ s/\&/\&amp;/g;
     # Glue everything together      # Glue everything together
     $result .= "<a href=\"javascript:celledit(\'".      $result .= "<a href=\"javascript:celledit(\'".
         $name."','".$formula."');\">".$value."</a>";          $name."','".$formula."');\">".$value."</a>";
Line 1218  sub html_uneditable_cell { Line 1316  sub html_uneditable_cell {
   
 sub html_row {  sub html_row {
     my $self = shift();      my $self = shift();
     my ($num_uneditable,$row) = @_;      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 @rowdata = $self->get_row($row);
     my $num_cols_output = 0;      my $num_cols_output = 0;
     my $row_html;      my $row_html;
       my $color = $importcolor;
       if ($row == 0) {
           $color = $exportcolor;
       }
       $color = '#FFDDDD' if (! defined($color));
     foreach my $cell (@rowdata) {      foreach my $cell (@rowdata) {
  if ($num_cols_output++ < $num_uneditable) {   if ($num_cols_output++ < $num_uneditable) {
     $row_html .= '<td bgcolor="#FFDDDD">';      $row_html .= '<td bgcolor="'.$color.'">';
     $row_html .= &html_uneditable_cell($cell,'#FFDDDD');      $row_html .= &html_uneditable_cell($cell,'#FFDDDD');
  } else {   } else {
     $row_html .= '<td bgcolor="#EOFFDD">';      $row_html .= '<td bgcolor="#EOFFDD">';
Line 1240  sub html_header { Line 1343  sub html_header {
     my $self = shift;      my $self = shift;
     return '' if (! $ENV{'request.role.adv'});      return '' if (! $ENV{'request.role.adv'});
     return "<table>\n".      return "<table>\n".
         '<tr><th align="center">Output Format</th><tr>'."\n".          '<tr><th align="center">'.&mt('Output Format').'</th></tr>'."\n".
         '<tr><td>'.&output_selector()."</td></tr>\n".          '<tr><td>'.$self->output_selector()."</td></tr>\n".
         "</table>\n";          "</table>\n";
 }  }
   
   ##
   ## Default output types are HTML, Excel, and CSV
   sub output_options {
       my $self = shift();
       return  ({value       => 'html',
                 description => 'HTML'},
                {value       => 'excel',
                 description => 'Excel'},
                {value       => 'csv',
                 description => 'Comma Separated Values'},);
   }
   
 sub output_selector {  sub output_selector {
       my $self = shift();
     my $output_selector = '<select name="output_format" size="3">'."\n";      my $output_selector = '<select name="output_format" size="3">'."\n";
     my $default = 'html';      my $default = 'html';
     if (exists($ENV{'form.output_format'})) {      if (exists($ENV{'form.output_format'})) {
Line 1253  sub output_selector { Line 1369  sub output_selector {
     } else {      } else {
         $ENV{'form.output_format'} = $default;          $ENV{'form.output_format'} = $default;
     }      }
     foreach (['html','HTML'],      foreach  ($self->output_options()) {
              ['excel','Excel'],          $output_selector.='<option value="'.$_->{'value'}.'"';
              ['csv','Comma Seperated Values']) {          if ($_->{'value'} eq $default) {
         my ($name,$description) = @{$_};  
         $output_selector.=qq{<option value="$name"};  
         if ($name eq $default) {  
             $output_selector .= ' selected';              $output_selector .= ' selected';
         }          }
         $output_selector .= ">$description</option>\n";          $output_selector .= ">".&mt($_->{'description'})."</option>\n";
     }      }
     $output_selector .= "</select>\n";      $output_selector .= "</select>\n";
     return $output_selector;      return $output_selector;
Line 1294  sub create_excel_spreadsheet { Line 1407  sub create_excel_spreadsheet {
     my $workbook  = Spreadsheet::WriteExcel->new('/home/httpd'.$filename);      my $workbook  = Spreadsheet::WriteExcel->new('/home/httpd'.$filename);
     if (! defined($workbook)) {      if (! defined($workbook)) {
         $r->log_error("Error creating excel spreadsheet $filename: $!");          $r->log_error("Error creating excel spreadsheet $filename: $!");
         $r->print("Problems creating new Excel file.  ".          $r->print(&mt("Problems creating new Excel file.  ".
                   "This error has been logged.  ".                    "This error has been logged.  ".
                   "Please alert your LON-CAPA administrator");                    "Please alert your LON-CAPA administrator"));
         return undef;          return undef;
     }      }
     #      #
Line 1310  sub create_excel_spreadsheet { Line 1423  sub create_excel_spreadsheet {
     return ($workbook,$filename);      return ($workbook,$filename);
 }  }
   
   #
   # This routine is just a stub 
   sub outsheet_htmlclasslist {
       my $self = shift;
       my ($r) = @_;
       $r->print('<h2>'.&mt("This output is not supported").'</h2>');
       $r->rflush();
       return;
   }
   
 sub outsheet_excel {  sub outsheet_excel {
     my $self = shift;      my $self = shift;
     my ($r) = @_;      my ($r) = @_;
     $r->print("<h2>Preparing Excel Spreadsheet</h2>");      my $connection = $r->connection();
       $r->print("<h2>".&mt('Preparing Excel Spreadsheet')."</h2>");
     #      #
     # Create excel worksheet      # Create excel worksheet
     my ($workbook,$filename) = $self->create_excel_spreadsheet($r);      my ($workbook,$filename) = $self->create_excel_spreadsheet($r);
Line 1336  sub outsheet_excel { Line 1460  sub outsheet_excel {
     $self->excel_output_row($worksheet,0,$rows_output++,'Summary');      $self->excel_output_row($worksheet,0,$rows_output++,'Summary');
     $rows_output++;    # skip a line      $rows_output++;    # skip a line
     #      #
     $self->excel_rows($worksheet,$cols_output,$rows_output);      $self->excel_rows($connection,$worksheet,$cols_output,$rows_output);
     #      #
     #      #
     # Close the excel file      # Close the excel file
Line 1354  sub outsheet_excel { Line 1478  sub outsheet_excel {
 sub outsheet_csv   {  sub outsheet_csv   {
     my $self = shift;      my $self = shift;
     my ($r) = @_;      my ($r) = @_;
       my $connection = $r->connection();
     my $csvdata = '';      my $csvdata = '';
     my @Values;      my @Values;
     #      #
Line 1364  sub outsheet_csv   { Line 1489  sub outsheet_csv   {
     my $file;      my $file;
     unless ($file = Apache::File->new('>'.'/home/httpd'.$filename)) {      unless ($file = Apache::File->new('>'.'/home/httpd'.$filename)) {
         $r->log_error("Couldn't open $filename for output $!");          $r->log_error("Couldn't open $filename for output $!");
         $r->print("Problems occured in writing the csv file.  ".          $r->print(&mt("Problems occured in writing the csv file.  ".
                   "This error has been logged.  ".                    "This error has been logged.  ".
                   "Please alert your LON-CAPA administrator.");                    "Please alert your LON-CAPA administrator."));
         $r->print("<pre>\n".$csvdata."</pre>\n");          $r->print("<pre>\n".$csvdata."</pre>\n");
         return 0;          return 0;
     }      }
Line 1377  sub outsheet_csv   { Line 1502  sub outsheet_csv   {
     }      }
     #      #
     # Output the body of the spreadsheet      # Output the body of the spreadsheet
     $self->csv_rows($file);      $self->csv_rows($connection,$file);
     #      #
     # Close the csv file      # Close the csv file
     close($file);      close($file);
     $r->print('<br /><br />'.      $r->print('<br /><br />'.
               '<a href="'.$filename.'">Your CSV spreadsheet.</a>'."\n");                '<a href="'.$filename.'">'.&mt('Your CSV spreadsheet.').'</a>'."\n");
     #      #
     return 1;      return 1;
 }  }
Line 1473  sub clear_spreadsheet_definition_cache { Line 1598  sub clear_spreadsheet_definition_cache {
     undef(%spreadsheets);      undef(%spreadsheets);
 }  }
   
 sub load {  sub load_system_default_sheet {
     my $self = shift;      my $self = shift;
     my $includedir = $Apache::lonnet::perlvar{'lonIncludes'};      my $includedir = $Apache::lonnet::perlvar{'lonIncludes'};
       # load in the default defined spreadsheet
       my $sheetxml='';
       my $fh;
       if ($fh=Apache::File->new($includedir.'/default_'.$self->{'type'})) {
           $sheetxml=join('',<$fh>);
           $fh->close();
       } else {
           # $sheetxml='<field row="0" col="A">"Error"</field>';
           $sheetxml='<field row="0" col="A"></field>';
       }
       $self->filename('default_');
       my ($formulas,undef) = &parse_sheet(\$sheetxml);
       return $formulas;
   }
   
   sub load {
       my $self = shift;
     #      #
     my $stype = $self->{'type'};      my $stype = $self->{'type'};
     my $cnum  = $self->{'cnum'};      my $cnum  = $self->{'cnum'};
     my $cdom  = $self->{'cdom'};      my $cdom  = $self->{'cdom'};
     my $chome = $self->{'chome'};      my $chome = $self->{'chome'};
     my $filename = $self->{'filename'};  
     #      #
       my $filename = $self->filename();
     my $cachekey = join('_',($cnum,$cdom,$stype,$filename));      my $cachekey = join('_',($cnum,$cdom,$stype,$filename));
     #      #
     # see if sheet is cached      # see if sheet is cached
Line 1491  sub load { Line 1633  sub load {
         $formulas = $spreadsheets{$cachekey}->{'formulas'};          $formulas = $spreadsheets{$cachekey}->{'formulas'};
     } else {      } else {
         # Not cached, need to read          # Not cached, need to read
         if (! defined($self->filename())) {          if (! defined($filename)) {
             # load in the default defined spreadsheet              $formulas = $self->load_system_default_sheet();
             my $sheetxml='';  
             my $fh;  
             if ($fh=Apache::File->new($includedir.'/default.'.$filename)) {  
                 $sheetxml=join('',<$fh>);  
                 $fh->close();  
             } else {  
                 # $sheetxml='<field row="0" col="A">"Error"</field>';  
                 $sheetxml='<field row="0" col="A"></field>';  
             }  
             ($formulas,undef) = &parse_sheet(\$sheetxml);  
         } elsif($self->filename() =~ /^\/res\/.*\.spreadsheet$/) {          } elsif($self->filename() =~ /^\/res\/.*\.spreadsheet$/) {
             # Load a spreadsheet definition file              # Load a spreadsheet definition file
             my $sheetxml=&Apache::lonnet::getfile              my $sheetxml=&Apache::lonnet::getfile
Line 1512  sub load { Line 1644  sub load {
                     .$self->filename().'"</field>';                      .$self->filename().'"</field>';
             }              }
             ($formulas,undef) = &parse_sheet(\$sheetxml);              ($formulas,undef) = &parse_sheet(\$sheetxml);
               # Get just the filename and set the sheets filename
               my ($newfilename) = ($filename =~ /\/([^\/]*)\.spreadsheet$/);
               if ($self->is_default()) {
                   $self->filename($newfilename);
                   $self->make_default();
               } else {
                   $self->filename($newfilename);
               }
         } else {          } else {
             # Load the spreadsheet definition file from the save file              # Load the spreadsheet definition file from the save file
             my %tmphash = &Apache::lonnet::dump($self->filename(),$cdom,$cnum);              my %tmphash = &Apache::lonnet::dump($filename,$cdom,$cnum);
             my ($tmp) = keys(%tmphash);              my ($tmp) = keys(%tmphash);
             if ($tmp !~ /^(con_lost|error|no_such_host)/i) {              if ($tmp !~ /^(con_lost|error|no_such_host)/i) {
                 while (my ($cell,$formula) = each(%tmphash)) {                  while (my ($cell,$formula) = each(%tmphash)) {
                     $formulas->{$cell}=$formula;                      $formulas->{$cell}=$formula;
                 }                  }
             } else {              } else {
                 # Unable to grab the specified spreadsheet,                  $formulas = $self->load_system_default_sheet();
                 # so we get the default ones instead.  
                 $filename = 'default.'.$stype;  
                 $self->filename($filename);  
                 my $sheetxml;  
                 if (my $fh=Apache::File->new($includedir.'/'.$filename)) {  
                     $sheetxml = join('',<$fh>);  
                     $fh->close();  
                 } else {  
                     $sheetxml='<field row="0" col="A">'.  
                         '"Unable to load spreadsheet"</field>';  
                 }  
                 ($formulas,undef) = &parse_sheet(\$sheetxml);  
                 $self->formulas($formulas);  
             }              }
         }          }
           $filename=$self->filename(); # filename may have changed
         $cachekey = join('_',($cnum,$cdom,$stype,$filename));          $cachekey = join('_',($cnum,$cdom,$stype,$filename));
         %{$spreadsheets{$cachekey}->{'formulas'}} = %{$formulas};          %{$spreadsheets{$cachekey}->{'formulas'}} = %{$formulas};
     }      }
Line 1548  sub load { Line 1676  sub load {
 sub set_row_sources {  sub set_row_sources {
     my $self = shift;      my $self = shift;
     while (my ($cell,$value) = each(%{$self->{'formulas'}})) {      while (my ($cell,$value) = each(%{$self->{'formulas'}})) {
         next if ($cell !~ /^A(\d+)/ && $1 > 0);          next if ($cell !~ /^A(\d+)/ || $1 < 1);
         my $row = $1;          my $row = $1;
         $self->{'row_source'}->{$row} = $value;          $self->{'row_source'}->{$row} = $value;
     }      }
     return;      return;
 }  }
   
   sub set_row_numbers {
       my $self = shift;
       while (my ($cell,$value) = each(%{$self->{'formulas'}})) {
    next if ($cell !~ /^A(\d+)$/);
           next if (! defined($value));
    $self->{'row_numbers'}->{$value} = $1;
           $self->{'maxrow'} = $1 if ($1 > $self->{'maxrow'});
       }
   }
   
 ##  ##
 ## exportrow is *not* used to get the export row from a computed sub-sheet.  ## exportrow is *not* used to get the export row from a computed sub-sheet.
 ##  ##
 sub exportrow {  sub exportrow {
     my $self = shift;      my $self = shift;
       if (exists($self->{'badcalc'}) && $self->{'badcalc'}) {
           return ();
       }
     my @exportarray;      my @exportarray;
     foreach my $column (@UC_Columns) {      foreach my $column (@UC_Columns) {
         push(@exportarray,$self->value($column.'0'));          push(@exportarray,$self->value($column.'0'));
Line 1579  sub save { Line 1720  sub save {
         my $cnum  = $self->{'cnum'};          my $cnum  = $self->{'cnum'};
         my $cdom  = $self->{'cdom'};          my $cdom  = $self->{'cdom'};
         my $chome = $self->{'chome'};          my $chome = $self->{'chome'};
         my $fn    = $self->{'filename'};          my $filename    = $self->{'filename'};
           my $cachekey = join('_',($cnum,$cdom,$stype,$filename));
         # Cache new sheet          # Cache new sheet
         $spreadsheets{$cnum.'_'.$cdom.'_'.$stype.'_'.$fn}=join('___;___',%f);          %{$spreadsheets{$cachekey}->{'formulas'}}=%f;
         # Write sheet          # Write sheet
         foreach (keys(%f)) {          foreach (keys(%f)) {
             delete($f{$_}) if ($f{$_} eq 'import');              delete($f{$_}) if ($f{$_} eq 'import');
         }          }
         my $reply = &Apache::lonnet::put($fn,\%f,$cdom,$cnum);          my $reply = &Apache::lonnet::put($filename,\%f,$cdom,$cnum);
         return $reply if ($reply ne 'ok');          return $reply if ($reply ne 'ok');
         $reply = &Apache::lonnet::put($stype.'_spreadsheets',          $reply = &Apache::lonnet::put($stype.'_spreadsheets',
                        {$fn => $ENV{'user.name'}.'@'.$ENV{'user.domain'}},                       {$filename => $ENV{'user.name'}.'@'.$ENV{'user.domain'}},
                                       $cdom,$cnum);                                        $cdom,$cnum);
         return $reply if ($reply ne 'ok');          return $reply if ($reply ne 'ok');
         if ($makedef) {           if ($makedef) { 
             $reply = &Apache::lonnet::put('environment',              $reply = &Apache::lonnet::put('environment',
                                 {'spreadsheet_default_'.$stype => $fn },                                  {'spreadsheet_default_'.$stype => $filename },
                                           $cdom,$cnum);                                            $cdom,$cnum);
             return $reply if ($reply ne 'ok');              return $reply if ($reply ne 'ok');
         }           } 
         if ($self->is_default()) {          if ($self->is_default()) {
             &Apache::lonnet::expirespread('','',$self->{'type'},'');              if ($self->{'type'} eq 'studentcalc') {
                   &Apache::lonnet::expirespread('','','studentcalc','');
               } elsif ($self->{'type'} eq 'assesscalc') {
                   &Apache::lonnet::expirespread('','','assesscalc','');
                   &Apache::lonnet::expirespread('','','studentcalc','');
               }
         }          }
         return $reply;          return $reply;
     }      }
Line 1611  sub save { Line 1758  sub save {
 sub save_tmp {  sub save_tmp {
     my $self = shift;      my $self = shift;
     my $filename=$ENV{'user.name'}.'_'.      my $filename=$ENV{'user.name'}.'_'.
         $ENV{'user.domain'}.'_spreadsheet_'.$self->{'usymb'}.'_'.          $ENV{'user.domain'}.'_spreadsheet_'.$self->{'symb'}.'_'.
            $self->{'filename'};             $self->{'filename'};
     $filename=~s/\W/\_/g;      $filename=~s/\W/\_/g;
     $filename=$Apache::lonnet::tmpdir.$filename.'.tmp';      $filename=$Apache::lonnet::tmpdir.$filename.'.tmp';
Line 1631  sub save_tmp { Line 1778  sub save_tmp {
 sub load_tmp {  sub load_tmp {
     my $self = shift;      my $self = shift;
     my $filename=$ENV{'user.name'}.'_'.      my $filename=$ENV{'user.name'}.'_'.
         $ENV{'user.domain'}.'_spreadsheet_'.$self->{'usymb'}.'_'.          $ENV{'user.domain'}.'_spreadsheet_'.$self->{'symb'}.'_'.
             $self->{'filename'};              $self->{'filename'};
     $filename=~s/\W/\_/g;      $filename=~s/\W/\_/g;
     $filename=$Apache::lonnet::tmpdir.$filename.'.tmp';      $filename=$Apache::lonnet::tmpdir.$filename.'.tmp';
Line 1689  sub othersheets { Line 1836  sub othersheets {
                                       $self->{'cdom'}, $self->{'cnum'});                                        $self->{'cdom'}, $self->{'cnum'});
     my ($tmp) = keys(%results);      my ($tmp) = keys(%results);
     if ($tmp =~ /^(con_lost|error|no_such_host)/i ) {      if ($tmp =~ /^(con_lost|error|no_such_host)/i ) {
         @alternatives = ('Default');          @alternatives = (&mt('Default'));
     } else {      } else {
         @alternatives = sort (keys(%results));          @alternatives = (&mt('Default'), sort (keys(%results)));
     }      }
     return @alternatives;       return @alternatives; 
 }  }
Line 1727  sub get_template_row { Line 1874  sub get_template_row {
     return @cols;      return @cols;
 }  }
   
 sub set_row_numbers {  sub need_to_save {
     my $self = shift;      my $self = shift;
     my %f=$self->formulas();      if ($self->{'new_rows'} && ! $self->temporary()) {
     while (my ($cell,$value) = each(%{$self->{'formulas'}})) {          return 1;
  next if ($cell !~ /^A(\d+)$/);  
         next if (! defined($value));  
  $self->{'row_numbers'}->{$value} = $1;  
     }      }
       return 0;
 }  }
   
 sub get_row_number_from_key {  sub get_row_number_from_key {
Line 1746  sub get_row_number_from_key { Line 1891  sub get_row_number_from_key {
         # may not be the key we need to save          # may not be the key we need to save
  $self->{'maxrow'}++;   $self->{'maxrow'}++;
  $self->{'row_numbers'}->{$key} = $self->{'maxrow'};   $self->{'row_numbers'}->{$key} = $self->{'maxrow'};
   #        $self->logthis('added row '.$self->{'row_numbers'}->{$key}.
   #                       ' for '.$key);
           $self->{'new_rows'} = 1;
     }      }
     return $self->{'row_numbers'}->{$key};      return $self->{'row_numbers'}->{$key};
 }  }

Removed from v.1.11  
changed lines
  Added in v.1.31


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>