--- loncom/interface/lonhelper.pm	2005/10/17 20:21:31	1.125
+++ loncom/interface/lonhelper.pm	2006/04/06 22:30:52	1.136
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # .helper XML handler to implement the LON-CAPA helper
 #
-# $Id: lonhelper.pm,v 1.125 2005/10/17 20:21:31 albertel Exp $
+# $Id: lonhelper.pm,v 1.136 2006/04/06 22:30:52 foxr Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -573,22 +573,20 @@ sub display {
     }
 
     # Phase 4: Display.
-    my $html=&Apache::lonxml::xmlbegin();
     my $stateTitle=&mt($state->title());
-    my $helperTitle = &mt($self->{TITLE});
-    my $bodytag = &Apache::loncommon::bodytag($helperTitle,'','');
+    my $browser_searcher_js = 
+	'<script type="text/javascript">'."\n".
+	&Apache::loncommon::browser_and_searcher_javascript().
+	"\n".'</script>';
+
+    $result .= &Apache::loncommon::start_page($self->{TITLE},
+					      $browser_searcher_js);
+    
     my $previous = HTML::Entities::encode(&mt("<- Previous"), '<>&"');
     my $next = HTML::Entities::encode(&mt("Next ->"), '<>&"');
     # FIXME: This should be parameterized, not concatenated - Jeremy
-    my $loncapaHelper = &mt("LON-CAPA Helper:");
 
-    $result .= <<HEADER;
-$html
-    <head>
-        <title>$loncapaHelper: $helperTitle</title>
-    </head>
-    $bodytag
-HEADER
+
     if (!$state->overrideForm()) { $result.="<form name='helpform' method='POST'>"; }
     $result .= <<HEADER;
         <table border="0" width='100%'><tr><td>
@@ -651,10 +649,9 @@ HEADER
             </tr>
           </table>
         </form>
-    </body>
-</html>
 FOOTER
 
+    $result .= &Apache::loncommon::end_page();
     # Handle writing out the vars to the file
     my $file = Apache::File->new('>'.$self->{FILENAME});
     print $file $self->_varsInFile();
@@ -1072,6 +1069,16 @@ will be the state transistioned to if th
 the choice is not multichoice. This will override the nextstate
 passed to the parent C<choices> tag.
 
+<choice> may optionally contain a 'relatedvalue' attribute, which
+if present will cause a text entry to appear to the right of the
+selection.  The value of the relatedvalue attribute is a variable
+into which the text entry will be stored e.g.:
+<choice computer='numberprovided" relatedvalue="num">Type the number in:</choice>
+
+<choice> may contain a relatededefault atribute which, if the
+relatedvalue attribute is present will be the initial value of the input
+box.
+
 =back
 
 To create the choices programmatically, either wrap the choices in 
@@ -1162,10 +1169,12 @@ sub start_choice {
     my $computer = $token->[2]{'computer'};
     my $human = &mt(&Apache::lonxml::get_all_text('/choice',
                                               $parser));
-    my $nextstate = $token->[2]{'nextstate'};
-    my $evalFlag = $token->[2]{'eval'};
+    my $nextstate  = $token->[2]{'nextstate'};
+    my $evalFlag   = $token->[2]{'eval'};
+    my $relatedVar = $token->[2]{'relatedvalue'}; 
+    my $relatedDefault = $token->[2]{'relateddefault'};
     push @{$paramHash->{CHOICES}}, [&mtn($human), $computer, $nextstate, 
-                                    $evalFlag];
+                                    $evalFlag, $relatedVar, $relatedDefault];
     return '';
 }
 
@@ -1275,14 +1284,20 @@ BUTTONS
         }
         $result .= qq{id="id$id"};
         my $choiceLabel = $choice->[0];
-        if ($choice->[4]) {  # if we need to evaluate this choice
+        if ($choice->[3]) {  # if we need to evaluate this choice
             $choiceLabel = "sub { my $helper = shift; my $state = shift;" .
                 $choiceLabel . "}";
             $choiceLabel = eval($choiceLabel);
             $choiceLabel = &$choiceLabel($helper, $self);
         }
         $result .= "/></td><td> ".qq{<label for="id$id">}.
-            $choiceLabel. "</label></td></tr>\n";
+            $choiceLabel. "</label></td>";
+	if ($choice->[4]) {
+	    $result .='<td><input type="text" size="5" name="'
+		.$choice->[4].'.forminput" value="'
+                .$choice->[5].'" /></td>';
+	}
+	$result .= "</tr>\n";
     }
     $result .= "</table>\n\n\n";
     $result .= $buttons;
@@ -1316,6 +1331,10 @@ sub postprocess {
                 $helper->changeState($choice->[2]);
             }
         }
+	if ($choice->[4]) {
+	    my $varname = $choice->[4];
+	    $helper->{'VARS'}->{$varname} = $env{'form.'."$varname.forminput"};
+	}
     }
     return 1;
 }
@@ -1557,6 +1576,8 @@ sub render {
     my $date;
 
     my $time=time;
+    $date = localtime($time);
+    $date->min(0);
     my ($anytime,$onclick);
 
     if (defined($self->{DEFAULT_VALUE})) {
@@ -1564,14 +1585,18 @@ sub render {
         die('Error in default value code for variable ' . 
             $self->{'variable'} . ', Perl said: ' . $@) if $@;
         $time = &$valueFunc($helper, $self);
-	if (lc($time) eq 'anytime') { $time=time; $anytime=1; }
+	if (lc($time) eq 'anytime') {
+	    $anytime=1;
+	} else {
+	    $date = localtime($time);
+	}
+    } else {
+
     }
     if ($anytime) {
 	$onclick = "onclick=\"javascript:updateCheck(this.form,'${var}anytime',false)\"";
     }
     # Default date: The current hour.
-    $date = localtime($time);
-    $date->min(0);
 
     if (defined $self->{ERROR_MSG}) {
         $result .= '<font color="#FF0000">' . $self->{ERROR_MSG} . '</font><br /><br />';
@@ -2296,6 +2321,13 @@ sub render {
             } 
         }
     }
+    function checkexpired()  {
+	for (i=0; i<document.forms.helpform.elements.length; i++) {
+            if (document.forms.helpform.elements[i].value.indexOf(':Expired') != -1) {
+                document.forms.helpform.elements[i].checked=true;
+            } 
+        }
+    }
     function uncheckexpired() {
 	for (i=0; i<document.forms.helpform.elements.length; i++) {
             if (document.forms.helpform.elements[i].value.indexOf(':Expired') != -1) {
@@ -2364,13 +2396,28 @@ SCRIPT
 </table>
 <br />
 BUTTONS
+    $result .= $buttons;   
+
     }
 
     if (defined $self->{ERROR_MSG}) {
         $result .= '<font color="#FF0000">' . $self->{ERROR_MSG} . '</font><br /><br />';
     }
 
+    my %defaultUsers;
+    if (defined($self->{DEFAULT_VALUE})) {
+        my $valueFunc = eval($self->{DEFAULT_VALUE});
+        die 'Error in default value code for variable ' . 
+            $self->{'variable'} . ', Perl said: ' . $@ if $@;
+        my @defaultUsers = &$valueFunc($helper, $self);
+	if (!$self->{'multichoice'} && @defaultUsers) { # only allowed 1
+	    @defaultUsers = ($defaultUsers[0]);
+	}
+	%defaultUsers = map { if ($_) {($_,1) } } @defaultUsers;
+	delete($defaultUsers{''});
+    }
     my $choices = [];
+    my $expired_students = [];	# Will hold expired students.
 
     # Load up the non-students, if necessary
     if ($self->{'coursepersonnel'}) {
@@ -2408,15 +2455,67 @@ BUTTONS
         }
         return $classlist->{$a}->[$fullname] cmp $classlist->{$b}->[$fullname];
     } @keys;
+    #
+    #  now add the fancy section choice... first enumerate the sections:
+    if ($self->{'multichoice'}) {
+	my %sections;
+	for my $key (@keys) {
+	    my $section_name = $classlist->{$key}->[$section];
+	    if ($section_name ne "") {
+		$sections{$section_name} = 1;
+	    }
+	}
+	#  The variable $choice_widget will have the html to make the choice 
+	#  selector.
+	my $size=5;
+	if (scalar(keys(%sections)) < 5) {
+	    $size=scalar(keys(%sections));
+	}
+	my $choice_widget = '<select multiple name="chosensections" size="'.$size.'">'."\n";
+	foreach my $sec (sort {lc($a) cmp lc($b)} (keys(%sections))) {
+	    $choice_widget .= "<option name=\"$sec\">$sec</option>\n";
+	}
+	$choice_widget .= "<option>none</option></select>\n";
+
+	# Build a table without any borders to contain the section based
+	# selection:
+
+	my $section_selectors =<<SECTIONSELECT;
+<table border="0">
+  <tr valign="top">
+   <td>For Sections:</td><td>$choice_widget</td>
+   <td><label><input type="radio" name="personstate" value="Active" checked />
+               Current Students</label></td>
+   <td><label><input type="radio" name="personstate" value="All" />
+               All students</label></td>
+   <td><label><input type="radio" name="personstate" value="Expired" />
+               Expired Students</label></td>
+  </tr>
+  <tr>
+   <td><input type="button" value="Select" onclick="checksections(true);" /></td>
+   <td><input type="button" value="Unselect" onclick="checksections(false);" /></td></tr>
+</table>
+<br />
+SECTIONSELECT
+         $result .= $section_selectors;
+    }
 
     # username, fullname, section, type
     for (@keys) {
-	# Filter out inactive students if we've set "activeonly"
-	if (!$self->{'activeonly'} || $classlist->{$_}->[$status] eq
+
+	# We split the active students into the choices array and
+        # inactive ones into expired_students so that we can put them in 2 separate
+	# tables.
+
+	if ( $classlist->{$_}->[$status] eq
 	    'Active') {
 	    push @$choices, [$_, $classlist->{$_}->[$fullname], 
 			     $classlist->{$_}->[$section],
 			     $classlist->{$_}->[$status], 'Student'];
+	} else {
+	    push @$expired_students, [$_, $classlist->{$_}->[$fullname], 
+				      $classlist->{$_}->[$section],
+				      $classlist->{$_}->[$status], 'Student'];
 	}
     }
 
@@ -2431,11 +2530,20 @@ BUTTONS
 	"<td align='center'><b>".&mt('Username').":".&mt('Domain')."</b></td></tr>";
 
     my $checked = 0;
+    #
+    # Give the active students and staff:
+    #
     for my $choice (@$choices) {
         $result .= "<tr><td><input type='$type' name='" .
             $self->{'variable'} . '.forminput' . "'";
             
-        if (!$self->{'multichoice'} && !$checked) {
+	if (%defaultUsers) {
+	    my $user=$choice->[0];
+	    if (exists($defaultUsers{$user})) {
+		$result .= " checked='checked' ";
+		$checked = 1;
+	    }
+	} elsif (!$self->{'multichoice'} && !$checked) {
             $result .= " checked='checked' ";
             $checked = 1;
         }
@@ -2456,53 +2564,59 @@ BUTTONS
 	    . HTML::Entities::encode($choice->[0],'<>&"')
 	    . "</td></tr>\n";
     }
-
     $result .= "</table>\n\n";
-    $result .= $buttons;   
+
+    # If activeonly is not set then we can also give the expired students:
     #
-    #  now add the fancy section choice... first enumerate the sections:
-    if ($self->{'multichoice'}) {
-	my %sections;
-	for my $key (@keys) {
-	    my $section_name = $classlist->{$key}->[$section];
-	    if ($section_name ne "") {
-		$sections{$section_name} = 1;
+    if (!$self->{'activeonly'} && ((scalar @$expired_students) > 0)) {
+	$result .= "<p>Inactive students: </p>\n";
+	$result .= <<INACTIVEBUTTONS;
+	   <table>
+              <tr>
+                 <td><input type="button" value="Select expired" onclick="checkexpired();" /> </td>
+		 <td><input type="button" value="Unselect expired" onclick="uncheckexpired();" /></td>
+              </tr>
+           </table>
+INACTIVEBUTTONS
+	$result .= "<table>\n";
+
+	for my $choice (@$expired_students) {
+        $result .= "<tr><td><input type='$type' name='" .
+            $self->{'variable'} . '.forminput' . "'";
+            
+	if (%defaultUsers) {
+	    my $user=$choice->[0];
+	    if (exists($defaultUsers{$user})) {
+		$result .= " checked='checked' ";
+		$checked = 1;
 	    }
+	} elsif (!$self->{'multichoice'} && !$checked) {
+            $result .= " checked='checked' ";
+            $checked = 1;
+        }
+        $result .=
+            " value='" . HTML::Entities::encode($choice->[0] . ':' 
+						.$choice->[2] . ':' 
+						.$choice->[1] . ':' 
+						.$choice->[3], "<>&\"'")
+            . "' /></td><td>"
+            . HTML::Entities::encode($choice->[1],'<>&"')
+            . "</td><td align='center'>" 
+            . HTML::Entities::encode($choice->[2],'<>&"')
+            . "</td>\n<td>" 
+	    . HTML::Entities::encode($choice->[3],'<>&"')
+            . "</td>\n<td>" 
+	    . HTML::Entities::encode($choice->[4],'<>&"')
+            . "</td>\n<td>" 
+	    . HTML::Entities::encode($choice->[0],'<>&"')
+	    . "</td></tr>\n";	    
 	}
-	#  The variable $choice_widget will have the html to make the choice 
-	#  selector.
-	my $size=5;
-	if (scalar(keys(%sections)) < 5) {
-	    $size=scalar(keys(%sections));
-	}
-	my $choice_widget = '<select multiple name="chosensections" size="'.$size.'">'."\n";
-	foreach my $sec (sort {lc($a) cmp lc($b)} (keys(%sections))) {
-	    $choice_widget .= "<option name=\"$sec\">$sec</option>\n";
-	}
-	$choice_widget .= "<option>none</option></select>\n";
+	$result .= "</table>\n";
+	
+    }
+
 
-	# Build a table without any borders to contain the section based
-	# selection:
 
-	my $section_selectors =<<SECTIONSELECT;
-<table border="0">
-  <tr valign="top">
-   <td>For Sections:</td><td>$choice_widget</td>
-   <td><label><input type="radio" name="personstate" value="Active" checked />
-               Current Students</label></td>
-   <td><label><input type="radio" name="personstate" value="All" />
-               All students</label></td>
-   <td><label><input type="radio" name="personstate" value="Expired" />
-               Expired Students</label></td>
-  </tr>
-  <tr>
-   <td><input type="button" value="Select" onclick="checksections(true);" /></td>
-   <td><input type="button" value="Unselect" onclick="checksections(false);" /></td></tr>
-</table>
-<br />
-SECTIONSELECT
-         $result .= $section_selectors;
-    }
     return $result;
 }
 
@@ -2897,8 +3011,12 @@ package Apache::lonhelper::section;
 <section> allows the user to choose one or more sections from the current
 course.
 
-It takes the standard attributes "variable", "multichoice", and
-"nextstate", meaning what they do for most other elements.
+It takes the standard attributes "variable", "multichoice",
+"allowempty" and "nextstate", meaning what they do for most other
+elements.
+
+also takes a boolean 'onlysections' whcih will restrict this to only
+have sections and not include groups
 
 =cut
 
@@ -2928,6 +3046,7 @@ sub start_section {
     $paramHash->{'variable'} = $token->[2]{'variable'};
     $helper->declareVar($paramHash->{'variable'});
     $paramHash->{'multichoice'} = $token->[2]{'multichoice'};
+    $paramHash->{'allowempty'} = $token->[2]{'allowempty'};
     if (defined($token->[2]{'nextstate'})) {
         $paramHash->{NEXTSTATE} = $token->[2]{'nextstate'};
     }
@@ -2947,8 +3066,16 @@ sub start_section {
     } 
    
     for my $sectionName (sort(keys(%choices))) {
-        
-        push @{$paramHash->{CHOICES}}, [$sectionName, $sectionName];
+	push @{$paramHash->{CHOICES}}, [$sectionName, $sectionName];
+    }
+    return if ($token->[2]{'onlysections'});
+
+    # add in groups to the end of the list
+    my %curr_groups;
+    if (&Apache::loncommon::coursegroups(\%curr_groups)) {
+	foreach my $group_name (sort(keys(%curr_groups))) {
+	    push(@{$paramHash->{CHOICES}}, [$group_name, $group_name]);
+	}
     }
 }    
 
@@ -2962,6 +3089,72 @@ sub end_section {
 }    
 1;
 
+package Apache::lonhelper::group;
+
+=pod
+ 
+=head2 Element: groupX<group, helper element>
+ 
+<group> allows the user to choose one or more groups from the current course.
+
+It takes the standard attributes "variable", "multichoice",
+ "allowempty" and "nextstate", meaning what they do for most other
+ elements.
+
+=cut
+
+no strict;
+@ISA = ("Apache::lonhelper::choices");
+use strict;
+
+BEGIN {
+    &Apache::lonhelper::register('Apache::lonhelper::group',
+                                 ('group'));
+}
+
+sub new {
+    my $ref = Apache::lonhelper::choices->new();
+    bless($ref);
+}
+ 
+sub start_group {
+    my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
+ 
+    if ($target ne 'helper') {
+        return '';
+    }
+
+    $paramHash->{CHOICES} = [];
+
+    $paramHash->{'variable'} = $token->[2]{'variable'};
+    $helper->declareVar($paramHash->{'variable'});
+    $paramHash->{'multichoice'} = $token->[2]{'multichoice'};
+    $paramHash->{'allowempty'} = $token->[2]{'allowempty'};
+    if (defined($token->[2]{'nextstate'})) {
+        $paramHash->{NEXTSTATE} = $token->[2]{'nextstate'};
+    }
+
+    # Populate the CHOICES element
+    my %choices;
+
+    my %curr_groups;
+    if (&Apache::loncommon::coursegroups(\%curr_groups)) {
+	foreach my $group_name (sort(keys(%curr_groups))) {
+	    push(@{$paramHash->{CHOICES}}, [$group_name, $group_name]);
+	}
+    }
+}
+
+sub end_group {
+    my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
+
+    if ($target ne 'helper') {
+        return '';
+    }
+    Apache::lonhelper::group->new();
+}
+1;
+
 package Apache::lonhelper::string;
 
 =pod
@@ -3343,8 +3536,11 @@ sub render {
     }
     my $previous = HTML::Entities::encode(&mt("<- Previous"), '<>&"');
     my $next = HTML::Entities::encode(&mt("Next ->"), '<>&"');
+    my $target = " target='loncapaclient'";
+    if (($env{'browser.interface'} eq 'textual') ||
+        ($env{'environment.remote'} eq 'off')) {  $target='';  }
     $result .= "<center>\n" .
-	"<form action='".$actionURL."' method='post' target='loncapaclient'>\n" .
+	"<form action='".$actionURL."' method='post' $target>\n" .
 	"<input type='button' onclick='history.go(-1)' value='$previous' />" .
 	"<input type='hidden' name='orgurl' value='$targetURL' />" .
 	"<input type='hidden' name='selectrole' value='1' />\n" .
@@ -3545,6 +3741,11 @@ sub render {
         $result .= '<li>'.&mt('for section [_1]',"<b>$section</b>").'</li>';
 	$result .= "<input type='hidden' name='csec' value='" .
             HTML::Entities::encode($section,"'<>&\"") . "' />\n";
+    } elsif ($vars->{TARGETS} eq 'group') {
+        my $group = $vars->{GROUP_NAME};
+        $result .= '<li>'.&mt('for group [_1]',"<b>$group</b>").'</li>';
+        $result .= "<input type='hidden' name='cgroup' value='" .
+            HTML::Entities::encode($group,"'<>&\"") . "' />\n";
     } else {
         # FIXME: This is probably wasteful! Store the name!
         my $classlist = Apache::loncoursedata::get_classlist();