--- loncom/interface/spreadsheet/Spreadsheet.pm	2005/05/17 20:17:03	1.51
+++ loncom/interface/spreadsheet/Spreadsheet.pm	2006/05/30 12:46:26	1.69
@@ -1,5 +1,5 @@
 #
-# $Id: Spreadsheet.pm,v 1.51 2005/05/17 20:17:03 albertel Exp $
+# $Id: Spreadsheet.pm,v 1.69 2006/05/30 12:46:26 www 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,7 +88,7 @@ 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;
     }
@@ -95,10 +98,22 @@ sub new {
     if (! defined($domain) || $domain eq '') {
         $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 => '',
@@ -191,6 +206,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);    
@@ -211,16 +228,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'};
     }
@@ -384,10 +393,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;
 }
 
@@ -703,7 +714,7 @@ sub get_values {
 	}
 	return \@values;
     } else {
-	$num = '(\d+)';
+	$num = '([1-9]\d*)';
     }
     if (($la eq '*') || ($ua eq '*')) {
         $alpha='[A-z]';
@@ -812,7 +823,7 @@ sub expandnamed {
                 $result.=$thissum.'+';
             } 
             $result=~s/\+$//;
-            return $result;
+            return '('.$result.')';
         } else {
 	    return 0;
         }
@@ -968,25 +979,32 @@ sub formulas {
         $self->{'formulas'} = $formulas;
         $self->{'rows'} = [];
         $self->{'template_cells'} = [];
+	$self->{'loaded'} = 1;
         return;
     } else {
-	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();
-	    }
-	}
+	$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 +1014,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 +1025,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 +1104,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 +1210,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 +1262,12 @@ sub html_export_row {
     foreach my $cell (@rowdata) {
         if ($cell->{'name'} =~ /^[A-Z]/) {
 	    $row_html .= '<td bgcolor="'.$color.'">'.
-                &html_editable_cell($cell,$color,$allowed).'</td>';
+                &html_editable_cell($cell,$color,$allowed,
+                                    $self->{outputmode} eq 'source').'</td>';
         } else {
 	    $row_html .= '<td bgcolor="#DDCCFF">'.
-                &html_editable_cell($cell,'#DDCCFF',$allowed).'</td>';
+                &html_editable_cell($cell,'#DDCCFF',$allowed,
+                                    $self->{outputmode} eq 'source').'</td>';
         }
     }
     return $row_html;
@@ -1258,14 +1287,15 @@ sub html_template_row {
                 &html_uneditable_cell($cell,'#FFDDDD',$allowed).'</td>';
         } else {
 	    $row_html .= '<td bgcolor="#EOFFDD">'.
-                &html_editable_cell($cell,'#EOFFDD',$allowed).'</td>';
+                &html_editable_cell($cell,'#EOFFDD',$allowed,
+                                    $self->{outputmode} eq 'source').'</td>';
         }
     }
     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 +1305,13 @@ sub html_editable_cell {
     }
     $name    = '' if (! defined($name));
     $formula = '' if (! defined($formula));
-    if (! defined($value)) {
+    if ($showsource) {
+        if (!defined($formula) || $formula =~ /^\s*$/) {
+            $value = '<font color="'.$bgcolor.'">#</font>';
+        } else {
+            $value = &HTML::Entities::encode($formula, '<>&"');
+        }
+    } elsif (! defined($value)) {
         $value = '<font color="'.$bgcolor.'">#</font>';
         if ($formula ne '') {
             $value = '<i>undefined value</i>';
@@ -1328,7 +1364,8 @@ sub html_row {
 	    $row_html .= &html_uneditable_cell($cell,'#FFDDDD');
 	} else {
 	    $row_html .= '<td bgcolor="#EOFFDD">';
-	    $row_html .= &html_editable_cell($cell,'#E0FFDD',$allowed);
+	    $row_html .= &html_editable_cell($cell,'#E0FFDD',$allowed,
+                                             $self->{outputmode} eq 'source');
 	}
 	$row_html .= '</td>';
     }
@@ -1352,6 +1389,8 @@ sub output_options {
               description => 'HTML'},
              {value       => 'excel',
               description => 'Excel'},
+             {value       => 'source',
+              description => 'Show Source'},
 #             {value       => 'xml',
 #              description => 'XML'},
              {value       => 'csv',
@@ -1525,6 +1564,7 @@ sub outsheet_xml   {
     ## Will be rendered for the user
     ## But not on this day
     my $Str = '<spreadsheet type="'.$self->{'type'}.'">'."\n";
+    $self->check_formulas_loaded();
     while (my ($cell,$formula) = each(%{$self->{'formulas'}})) {
         if ($cell =~ /^template_(\w+)/) {
             my $col = $1;
@@ -1619,7 +1659,7 @@ sub load {
         $self->{'row_source'}=$spreadsheets{$cachekey}->{'row_source'};
         $self->{'row_numbers'}=$spreadsheets{$cachekey}->{'row_numbers'};
         $self->{'maxrow'}=$spreadsheets{$cachekey}->{'maxrow'};
-   } else {
+    } else {
         # Not cached, need to read
         if (! defined($filename)) {
             $formulas = $self->load_system_default_sheet();
@@ -1652,20 +1692,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();
-	%{$spreadsheets{$cachekey}->{'row_source'}} = %{$self->{'row_source'}};
-        %{$spreadsheets{$cachekey}->{'row_numbers'}} = %{$self->{'row_numbers'}};
-        $spreadsheets{$cachekey}->{'maxrow'} = $self->{'maxrow'};
+	$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;
@@ -1676,6 +1734,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));
@@ -1711,9 +1770,8 @@ sub save {
         my $cnum  = $self->{'cnum'};
         my $cdom  = $self->{'cdom'};
         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');
@@ -1729,6 +1787,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') {
@@ -1758,8 +1818,8 @@ sub save_tmp {
         my %f = $self->formulas();
         while( my ($cell,$formula) = each(%f)) {
             next if ($formula eq 'import');
-            print $fh &Apache::lonnet::escape($cell)."=".
-                &Apache::lonnet::escape($formula)."\n";
+            print $fh &escape($cell)."=".
+                &escape($formula)."\n";
         }
         $fh->close();
     }
@@ -1777,8 +1837,8 @@ sub load_tmp {
         while (<$spreadsheet_file>) {
 	    chomp;
             my ($cell,$formula) = split(/=/);
-            $cell    = &Apache::lonnet::unescape($cell);
-            $formula = &Apache::lonnet::unescape($formula);
+            $cell    = &unescape($cell);
+            $formula = &unescape($formula);
             $formulas{$cell} = $formula;
         }
         $spreadsheet_file->close();