--- loncom/interface/lonhelper.pm	2006/03/07 16:09:59	1.133
+++ loncom/interface/lonhelper.pm	2022/06/27 20:35:51	1.204
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # .helper XML handler to implement the LON-CAPA helper
 #
-# $Id: lonhelper.pm,v 1.133 2006/03/07 16:09:59 albertel Exp $
+# $Id: lonhelper.pm,v 1.204 2022/06/27 20:35:51 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -64,9 +64,11 @@ messages, resource selections, or date q
 
 The helper tag is required to have one attribute, "title", which is the name
 of the helper itself, such as "Parameter helper". The helper tag may optionally
-have a "requiredpriv" attribute, specifying the priviledge a user must have
+have a "requiredpriv" attribute, specifying the privilege a user must have
 to use the helper, or get denied access. See loncom/auth/rolesplain.tab for
-useful privs. Default is full access, which is often wrong!
+useful privs. You may add the modifier &S at the end of the three letter priv
+if you want to grant access to users for whom the corresponding privilege is 
+section-specific. The default is full access, which is often wrong!
 
 =head2 State tags
 
@@ -81,25 +83,28 @@ State tags are also required to have an
 human name of the state, and will be displayed as the header on top of 
 the screen for the user.
 
+State tags may also optionally have an attribute "help" which should be
+the filename of a help file, this will add a blue ? to the title.
+
 =head2 Example Helper Skeleton
 
 An example of the tags so far:
 
  <helper title="Example Helper">
    <state name="START" title="Demonstrating the Example Helper">
-     <!-- notice this is the START state the wizard requires -->
+     <!-- notice this is the START state the helper requires -->
      </state>
    <state name="GET_NAME" title="Enter Student Name">
      </state>
    </helper>
 
-Of course this does nothing. In order for the wizard to do something, it is
-necessary to put actual elements into the wizard. Documentation for each
+Of course this does nothing. In order for the helper to do something, it is
+necessary to put actual elements into the helper. Documentation for each
 of these elements follows.
 
 =head1 Creating a Helper With Code, Not XML
 
-In some situations, such as the printing wizard (see lonprintout.pm), 
+In some situations, such as the printing helper (see lonprintout.pm), 
 writing the helper in XML would be too complicated, because of scope 
 issues or the fact that the code actually outweighs the XML. It is
 possible to create a helper via code, though it is a little odd.
@@ -183,6 +188,11 @@ use Apache::File;
 use Apache::lonxml;
 use Apache::lonlocal;
 use Apache::lonnet;
+use Apache::longroup;
+use Apache::lonselstudent;
+
+
+use LONCAPA;
 
 # Register all the tags with the helper, so the helper can 
 # push and pop them
@@ -253,7 +263,7 @@ sub real_handler {
     my $uri = shift;
     if (!defined($uri)) { $uri = $r->uri(); }
     $env{'request.uri'} = $uri;
-    my $filename = '/home/httpd/html' . $uri;
+    my $filename = $r->dir_config('lonDocRoot').$uri;
     my $fh = Apache::File->new($filename);
     my $file;
     read $fh, $file, 100000000;
@@ -275,7 +285,8 @@ sub real_handler {
 
     my $allowed = $helper->allowedCheck();
     if (!$allowed) {
-        $env{'user.error.msg'} = $env{'request.uri'}.':'.$helper->{REQUIRED_PRIV}.
+        my ($priv,$modifier) = split(/\&/,$helper->{REQUIRED_PRIV});
+        $env{'user.error.msg'} = $env{'request.uri'}.':'.$priv.
             ":0:0:Permission denied to access this helper.";
         return HTTP_NOT_ACCEPTABLE;
     }
@@ -331,7 +342,8 @@ sub start_state {
     }
 
     Apache::lonhelper::state->new($token->[2]{'name'},
-                                  $token->[2]{'title'});
+                                  $token->[2]{'title'},
+				  $token->[2]{'help'});
     return '';
 }
 
@@ -361,6 +373,7 @@ use Apache::loncommon;
 use Apache::File;
 use Apache::lonlocal;
 use Apache::lonnet;
+use LONCAPA;
 
 sub new {
     my $proto = shift;
@@ -466,9 +479,8 @@ sub _saveVars {
 sub _varsInFile {
     my $self = shift;
     my @vars = ();
-    for my $key (keys %{$self->{VARS}}) {
-        push @vars, &Apache::lonnet::escape($key) . '=' .
-            &Apache::lonnet::escape($self->{VARS}->{$key});
+    for my $key (keys(%{$self->{VARS}})) {
+        push(@vars, &escape($key) . '=' . &escape($self->{VARS}->{$key}));
     }
     return join ('&', @vars);
 }
@@ -483,7 +495,7 @@ sub declareVar {
         $self->{VARS}->{$var} = '';
     }
 
-    my $envname = 'form.' . $var . '.forminput';
+    my $envname = 'form.' . $var . '_forminput';
     if (defined($env{$envname})) {
         if (ref($env{$envname})) {
             $self->{VARS}->{$var} = join('|||', @{$env{$envname}});
@@ -499,8 +511,13 @@ sub allowedCheck {
     if (!defined($self->{REQUIRED_PRIV})) { 
         return 1;
     }
-
-    return Apache::lonnet::allowed($self->{REQUIRED_PRIV}, $env{'request.course.id'});
+    my ($priv,$modifier) = split(/\&/,$self->{REQUIRED_PRIV});
+    my $allowed = &Apache::lonnet::allowed($priv,$env{'request.course.id'});
+    if ((!$allowed) && ($modifier eq 'S') && ($env{'request.course.sec'} ne '')) {
+        $allowed = &Apache::lonnet::allowed($priv,$env{'request.course.id'}.'/'.
+                                                  $env{'request.course.sec'});
+    }
+    return $allowed;
 }
 
 sub changeState {
@@ -522,7 +539,7 @@ sub process {
     # Phase 1: Post processing for state of previous screen (which is actually
     # the "current state" in terms of the helper variables), if it wasn't the 
     # beginning state.
-    if ($self->{STATE} ne "START" || $env{"form.SUBMIT"} eq &mt("Next ->")) {
+    if ($self->{STATE} ne "START" || $env{"form.SUBMIT"} eq &mt("Next")) {
 	my $prevState = $self->{STATES}{$self->{STATE}};
         $prevState->postprocess();
     }
@@ -562,7 +579,7 @@ sub process {
 # 4: Render the current state to the screen as an HTML page.
 sub display {
     my $self = shift;
-
+    my $footer = shift;
     my $state = $self->{STATES}{$self->{STATE}};
 
     my $result = "";
@@ -573,92 +590,86 @@ sub display {
     }
 
     # Phase 4: Display.
-    my $html=&Apache::lonxml::xmlbegin();
     my $stateTitle=&mt($state->title());
-    my $helperTitle = &mt($self->{TITLE});
-    my $browser_searcher_js = &Apache::loncommon::browser_and_searcher_javascript();
-    my $bodytag = &Apache::loncommon::bodytag($helperTitle,'','');
-    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:");
+    my $stateHelp=     $state->help();
+    my $browser_searcher_js = 
+	'<script type="text/javascript">'."\n".
+	&Apache::loncommon::browser_and_searcher_javascript().
+	"\n".'</script>';
+
+    # Breadcrumbs
+    my $brcrum = [{'href' => '',
+                   'text' => 'Helper'}];
+    # FIXME: Dynamically add context sensitive breadcrumbs
+    #        depending on the caller,
+    #        e.g. printing, parametrization, etc.
+    # FIXME: Add breadcrumbs to reflect current helper state
+
+    $result .= &Apache::loncommon::start_page($self->{TITLE},
+                                              $browser_searcher_js,
+                                              {'bread_crumbs' => $brcrum,});
 
-    $result .= <<HEADER;
-$html
-    <head>
-        <title>$loncapaHelper: $helperTitle</title>
-        <script type="text/javascript">
-$browser_searcher_js
-        </script>
-    </head>
-    $bodytag
-HEADER
-    if (!$state->overrideForm()) { $result.="<form name='helpform' method='POST'>"; }
-    $result .= <<HEADER;
-        <table border="0" width='100%'><tr><td>
-        <h2><i>$stateTitle</i></h2>
-HEADER
+    my $previous = HTML::Entities::encode(&mt("Back"), '<>&"');
+    my $next = HTML::Entities::encode(&mt("Next"), '<>&"');
+    # FIXME: This should be parameterized, not concatenated - Jeremy
 
-    $result .= "<table cellpadding='10' width='100%'><tr><td rowspan='2' valign='top'>";
 
-    if (!$state->overrideForm()) {
-        $result .= $self->_saveVars();
+    if (!$state->overrideForm()) { $result.='<form name="helpform" method="post" action="">'; }
+    if ($stateHelp) {
+        $stateHelp = &Apache::loncommon::help_open_topic($stateHelp);
     }
-    $result .= $state->render();
-
-    $result .= "</td><td valign='top' align='right'>";
 
-    # Warning: Copy and pasted from below, because it's too much trouble to 
-    # turn this into a subroutine
+    # Prepare buttons
+    my $buttons;
     if (!$state->overrideForm()) {
         if ($self->{STATE} ne $self->{START_STATE}) {
             #$result .= '<input name="SUBMIT" type="submit" value="&lt;- Previous" />&nbsp;&nbsp;';
         }
+        $buttons = '<p>'; # '<fieldset>';
         if ($self->{DONE}) {
             my $returnPage = $self->{RETURN_PAGE};
-            $result .= "<a href=\"$returnPage\">" . &mt("End Helper") . "</a>";
+            $buttons .= '<a href="'.$returnPage.'">'.&mt('End Helper').'</a>';
         }
         else {
-            $result .= '<nobr><input name="back" type="button" ';
-            $result .= 'value="' . $previous . '" onclick="history.go(-1)" /> ';
-            $result .= '<input name="SUBMIT" type="submit" value="' . $next . '" /></nobr>';
+            $buttons .= '<span class="LC_nobreak">'
+                       .'<input name="back" type="button" '
+                       .'value="'.$previous.'" onclick="history.go(-1)" /> '
+                       .'<input name="SUBMIT" type="submit" value="'.$next.'" />'
+                       .'</span>';
         }
+    $buttons .= '</p>'; # '</fieldset>';
     }
 
-    $result .= "</td></tr><tr><td valign='bottom' align='right'>";
 
-    # Warning: Copy and pasted from above, because it's too much trouble to 
-    # turn this into a subroutine
+
+    $result .= '<h2>'.$stateTitle.$stateHelp.'</h2>';
+
+#   $result .= '<div>';
+
+    # Top buttons
+    $result .= $buttons;
+
+    # Main content of current helper screen
     if (!$state->overrideForm()) {
-        if ($self->{STATE} ne $self->{START_STATE}) {
-            #$result .= '<input name="SUBMIT" type="submit" value="&lt;- Previous" />&nbsp;&nbsp;';
-        }
-        if ($self->{DONE}) {
-            my $returnPage = $self->{RETURN_PAGE};
-            $result .= "<a href=\"$returnPage\">" . &mt('End Helper') . "</a>";
-        }
-        else {
-            $result .= '<nobr><input name="back" type="button" ';
-            $result .= 'value="' . $previous . '" onclick="history.go(-1)" /> ';
-            $result .= '<input name="SUBMIT" type="submit" value="' . $next . '" /></nobr>';
-        }
+        $result .= $self->_saveVars();
     }
+    $result .= $state->render();
+
+    # Bottom buttons
+    $result .= $buttons;
 
-    #foreach my $key (keys %{$self->{VARS}}) {
+
+    #foreach my $key (keys(%{$self->{VARS}})) {
     #    $result .= "|$key| -> " . $self->{VARS}->{$key} . "<br />";
     #}
 
-    $result .= "</td></tr></table>";
+#   $result .= '</div>';
 
     $result .= <<FOOTER;
-              </td>
-            </tr>
-          </table>
         </form>
-    </body>
-</html>
 FOOTER
 
+    $result .= $footer.&Apache::loncommon::end_page();
     # Handle writing out the vars to the file
     my $file = Apache::File->new('>'.$self->{FILENAME});
     print $file $self->_varsInFile();
@@ -684,6 +695,7 @@ sub new {
 
     $self->{NAME} = shift;
     $self->{TITLE} = shift;
+    $self->{HELP} = shift;
     $self->{ELEMENTS} = [];
 
     bless($self, $class);
@@ -705,6 +717,11 @@ sub title {
     return $self->{TITLE};
 }
 
+sub help {
+    my $self = shift;
+    return $self->{HELP};
+}
+
 sub preprocess {
     my $self = shift;
     for my $element (@{$self->{ELEMENTS}}) {
@@ -796,7 +813,7 @@ the element. How this value is interpret
 the element itself, and possibly the settings the element has (such as 
 multichoice vs. single choice for <choices> tags). 
 
-This is also intended for things like the course initialization wizard, where the
+This is also intended for things like the course initialization helper, where the
 user is setting various parameters. By correctly grabbing current settings 
 and including them into the helper, it allows the user to come back to the
 helper later and re-execute it, without needing to worry about overwriting
@@ -998,6 +1015,9 @@ sub start_message {
     if (defined($token->[2]{'nextstate'})) {
         $paramHash->{NEXTSTATE} = $token->[2]{'nextstate'};
     }
+    if (defined($token->[2]{'type'})) {
+        $paramHash->{TYPE} = $token->[2]{'type'};
+    }
     return '';
 }
 
@@ -1013,8 +1033,16 @@ sub end_message {
 
 sub render {
     my $self = shift;
-
-    return &mtn($self->{MESSAGE_TEXT});
+    
+    if ($self->{TYPE} =~ /^\s*warning\s*$/i) {
+	 $self->{MESSAGE_TEXT} = 
+	     '<span class="LC_warning">'. $self->{MESSAGE_TEXT}.'</span>';
+    }
+    if ($self->{TYPE} =~ /^\s*error\s*$/i) {
+	 $self->{MESSAGE_TEXT} = 
+	     '<span class="LC_error">'. $self->{MESSAGE_TEXT}.'</span>';
+    }
+    return $self->{MESSAGE_TEXT};
 }
 # If a NEXTSTATE was given, switch to it
 sub postprocess {
@@ -1027,6 +1055,175 @@ sub postprocess {
 }
 1;
 
+package Apache::lonhelper::helpicon;
+
+=pod
+
+=head1 Elements
+
+=head2 Element: helpiconX<helpicon, helper element>
+
+Helpicon elements add a help icon at the current location.
+Example:
+
+   <helpicon file="Help">
+     General Help
+   </helpicon>
+
+In this example will generate a help icon to the Help.hlp url with a
+description of 'General Help'. The description is not required and if
+left out (Example: <helpicon file="Help" /> only the icon will be
+added.)
+
+=head3 Localization
+
+The description text will be run through the normalize_string function
+and that will be used as a call to &mt.
+
+=cut
+
+no strict;
+@ISA = ("Apache::lonhelper::element");
+use strict;
+use Apache::lonlocal;
+
+BEGIN {
+    &Apache::lonhelper::register('Apache::lonhelper::helpicon',
+				 ('helpicon'));
+}
+
+sub new {
+    my $ref = Apache::lonhelper::element->new();
+    bless($ref);
+}
+
+# CONSTRUCTION: Construct the message element from the XML
+sub start_helpicon {
+    my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
+
+    if ($target ne 'helper') {
+        return '';
+    }
+
+    $paramHash->{HELP_TEXT} = &mtn(&Apache::lonxml::get_all_text('/helpicon',
+								 $parser));
+
+    $paramHash->{HELP_TEXT} =~s/^\s+//;
+    $paramHash->{HELP_TEXT} =~s/\s+$//;
+
+    if (defined($token->[2]{'file'})) {
+        $paramHash->{HELP_FILE} = $token->[2]{'file'};
+    }
+    return '';
+}
+
+sub end_helpicon {
+    my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
+
+    if ($target ne 'helper') {
+        return '';
+    }
+    Apache::lonhelper::helpicon->new();
+    return '';
+}
+
+sub render {
+    my $self = shift;
+
+    my $text;
+    if ( $self->{HELP_TEXT} ne '') {
+	$text=&mtn($self->{HELP_TEXT});
+    }
+
+    return &Apache::loncommon::help_open_topic($self->{HELP_FILE},
+					       $text);
+}
+sub postprocess {
+    my $self = shift;
+    if (defined($self->{NEXTSTATE})) {
+        $helper->changeState($self->{NEXTSTATE});
+    }
+
+    return 1;
+}
+
+1;
+
+package Apache::lonhelper::skip;
+
+=pod
+
+=head1 Elements
+
+=head2 Element: skipX<skip>
+
+The <skip> tag allows you define conditions under which the current state 
+should be skipped over and define what state to skip to.
+
+  <state name="SKIP">
+    <skip>
+       <clause>
+         #some code that decides whether to skip the state or not
+       </clause>
+       <nextstate>FINISH</nextstate>
+    </skip>
+    <message nextstate="FINISH">A possibly skipped state</message>
+  </state>
+
+=cut
+
+no strict;
+@ISA = ("Apache::lonhelper::element");
+use strict;
+
+BEGIN {
+    &Apache::lonhelper::register('Apache::lonhelper::skip',
+				 ('skip'));
+}
+
+sub new {
+    my $ref = Apache::lonhelper::element->new();
+    bless($ref);
+}
+
+sub start_skip {
+    my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
+
+    if ($target ne 'helper') {
+        return '';
+    }
+    # let <cluase> know what text to skip to
+    $paramHash->{SKIPTAG}='/skip';
+    return '';
+}
+
+sub end_skip {
+    my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
+
+    if ($target ne 'helper') {
+        return '';
+    }
+    Apache::lonhelper::skip->new();
+    return '';
+}
+
+sub render {
+    my $self = shift;
+    return '';
+}
+# If a NEXTSTATE is set, switch to it
+sub preprocess {
+    my ($self) = @_;
+
+    if (defined($self->{NEXTSTATE})) {
+        $helper->changeState($self->{NEXTSTATE});
+    }
+
+    return 1;
+}
+
+1;
+
 package Apache::lonhelper::choices;
 
 =pod
@@ -1076,6 +1273,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 
@@ -1166,10 +1373,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 '';
 }
 
@@ -1197,7 +1406,7 @@ sub render {
     function checkall(value, checkName) {
 	for (i=0; i<document.forms.helpform.elements.length; i++) {
             ele = document.forms.helpform.elements[i];
-            if (ele.name == checkName + '.forminput') {
+            if (ele.name == checkName + '_forminput') {
                 document.forms.helpform.elements[i].checked=value;
             }
         }
@@ -1270,23 +1479,29 @@ BUTTONS
     foreach my $choice (@{$self->{CHOICES}}) {
         my $id = &new_id();
         $result .= "<tr>\n<td width='20'>&nbsp;</td>\n";
-        $result .= "<td valign='top'><input type='$type' name='$var.forminput'"
+        $result .= "<td valign='top'><input type='$type' name='${var}_forminput'"
             . " value='" . 
             HTML::Entities::encode($choice->[1],"<>&\"'") 
             . "'";
         if ($checkedChoices{$choice->[1]}) {
-            $result .= " checked='checked' ";
+            $result .= " checked='checked'";
         }
-        $result .= qq{id="id$id"};
+        $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";
+        $result .= " /></td><td> ".qq{<label for="id$id">}.
+	    $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;
@@ -1298,7 +1513,8 @@ BUTTONS
 # given, switch to it
 sub postprocess {
     my $self = shift;
-    my $chosenValue = $env{'form.' . $self->{'variable'} . '.forminput'};
+    my $chosenValue = $env{'form.' . $self->{'variable'} . '_forminput'};
+
 
     if (!defined($chosenValue) && !$self->{'allowempty'}) {
         $self->{ERROR_MSG} = 
@@ -1306,6 +1522,8 @@ sub postprocess {
         return 0;
     }
 
+
+
     if (ref($chosenValue)) {
         $helper->{VARS}->{$self->{'variable'}} = join('|||', @$chosenValue);
     }
@@ -1320,6 +1538,10 @@ sub postprocess {
                 $helper->changeState($choice->[2]);
             }
         }
+	if ($choice->[4]) {
+	    my $varname = $choice->[4];
+	    $helper->{'VARS'}->{$varname} = $env{'form.'."${varname}_forminput"};
+	}
     }
     return 1;
 }
@@ -1427,7 +1649,7 @@ sub render {
 	$checkedChoices{$self->{CHOICES}->[0]->[1]} = 1;
     }
 
-    $result .= "<select name='${var}.forminput'>\n";
+    $result .= "<select name='${var}_forminput'>\n";
     foreach my $choice (@{$self->{CHOICES}}) {
         $result .= "<option value='" . 
             HTML::Entities::encode($choice->[1],"<>&\"'") 
@@ -1453,7 +1675,7 @@ sub render {
 # given, switch to it
 sub postprocess {
     my $self = shift;
-    my $chosenValue = $env{'form.' . $self->{'variable'} . '.forminput'};
+    my $chosenValue = $env{'form.' . $self->{'variable'} . '_forminput'};
 
     if (!defined($chosenValue) && !$self->{'allowempty'}) {
         $self->{ERROR_MSG} = "You must choose one or more choices to" .
@@ -1513,7 +1735,7 @@ no strict;
 use strict;
 use Apache::lonlocal; # A localization nightmare
 use Apache::lonnet;
-use Time::localtime;
+use DateTime;
 
 BEGIN {
     &Apache::lonhelper::register('Apache::lonhelper::date',
@@ -1561,23 +1783,36 @@ sub render {
     my $date;
 
     my $time=time;
-    $date = localtime($time);
-    $date->min(0);
     my ($anytime,$onclick);
 
-    if (defined($self->{DEFAULT_VALUE})) {
+    # first check VARS for a valid new value from the user
+    # then check DEFAULT_VALUE for a valid default time value
+    # otherwise pick now as reasonably good time
+
+    if (defined($helper->{VARS}{$var})
+	&&  $helper->{VARS}{$var} > 0) {
+        $date = &get_date_object($helper->{VARS}{$var}); 
+    } elsif (defined($self->{DEFAULT_VALUE})) {
         my $valueFunc = eval($self->{DEFAULT_VALUE});
         die('Error in default value code for variable ' . 
             $self->{'variable'} . ', Perl said: ' . $@) if $@;
         $time = &$valueFunc($helper, $self);
 	if (lc($time) eq 'anytime') {
 	    $anytime=1;
+	    $date = &get_date_object(time);
+            $date->set_minute(0);
+	} elsif (defined($time) && $time ne 0) {
+	    $date = &get_date_object($time);
 	} else {
-	    $date = localtime($time);
+	    # leave date undefined so it'll default to now
 	}
-    } else {
+    }
 
+    if (!defined($date)) {
+	$date = &get_date_object(time);
+        $date->set_minute(0);
     }
+
     if ($anytime) {
 	$onclick = "onclick=\"javascript:updateCheck(this.form,'${var}anytime',false)\"";
     }
@@ -1591,12 +1826,12 @@ sub render {
     my $i;
     $result .= "<select $onclick name='${var}month'>\n";
     for ($i = 0; $i < 12; $i++) {
-        if ($i == $date->mon) {
+        if (($i + 1) == $date->mon) {
             $result .= "<option value='$i' selected='selected'>";
         } else {
             $result .= "<option value='$i'>";
         }
-        $result .= &mt($months[$i]) . "</option>\n";
+        $result .= &mt($months[$i])."</option>\n";
     }
     $result .= "</select>\n";
 
@@ -1615,7 +1850,7 @@ sub render {
     # Year
     $result .= "<select $onclick name='${var}year'>\n";
     for ($i = 2000; $i < 2030; $i++) { # update this after 64-bit dates
-        if ($date->year + 1900 == $i) {
+        if ($date->year == $i) {
             $result .= "<option selected='selected'>";
         } else {
             $result .= "<option>";
@@ -1670,6 +1905,7 @@ sub render {
         }
         $result .= "</select>\n";
     }
+    $result  .= ' '.$date->time_zone_short_name().' ';
     if ($self->{'anytime'}) {
 	$result.=(<<CHECK);
 <script type="text/javascript">
@@ -1685,7 +1921,13 @@ CHECK
 	if ($anytime) {
 	    $result.=' checked="checked" '
 	}
-	$result.="name='${var}anytime'/>".&mt('Anytime').'</label>'
+        my $anytimetext = &mt('Any time');
+        if (($var eq 'startreserve') || ($var eq 'endreserve')) {
+            $anytimetext = &mt('Any time before slot starts');
+        } elsif (($var eq 'startunique') || ($var eq 'endunique')) {
+            $anytimetext = &mt('No restriction on uniqueness');     
+        }
+	$result.="name='${var}anytime'/>".$anytimetext.'</label>'
     }
     return $result;
 
@@ -1697,7 +1939,8 @@ sub postprocess {
     if ($env{'form.' . $var . 'anytime'}) {
 	$helper->{VARS}->{$var} = undef;
     } else {
-	my $month = $env{'form.' . $var . 'month'}; 
+	my $month = $env{'form.' . $var . 'month'};
+        $month ++;
 	my $day = $env{'form.' . $var . 'day'}; 
 	my $year = $env{'form.' . $var . 'year'}; 
 	my $min = 0; 
@@ -1707,37 +1950,76 @@ sub postprocess {
 	    $hour = $env{'form.' . $var . 'hour'};
 	}
 
-	my $chosenDate;
-	eval {$chosenDate = Time::Local::timelocal(0, $min, $hour, $day, $month, $year);};
+	my ($chosenDate,$checkDate);
+        my $timezone = &Apache::lonlocal::gettimezone();
+        my $dt;
+	eval {
+               $dt = DateTime->new( year   => $year,
+                                    month  => $month,
+                                    day    => $day,
+                                    hour   => $hour,
+                                    minute => $min,
+                                    second => 0,
+                                    time_zone => $timezone,
+                             );
+        };
+
 	my $error = $@;
+        if (!$error) {
+            $chosenDate  = $dt->epoch;
+            $checkDate = &get_date_object($chosenDate);
+        }
 
 	# Check to make sure that the date was not automatically co-erced into a 
 	# valid date, as we want to flag that as an error
 	# This happens for "Feb. 31", for instance, which is coerced to March 2 or
 	# 3, depending on if it's a leap year
-	my $checkDate = localtime($chosenDate);
 	
 	if ($error || $checkDate->mon != $month || $checkDate->mday != $day ||
-	    $checkDate->year + 1900 != $year) {
+	    $checkDate->year != $year) {
 	    unless (Apache::lonlocal::current_language()== ~/^en/) {
 		$self->{ERROR_MSG} = &mt("Invalid date entry");
 		return 0;
 	    }
 	    # LOCALIZATION FIXME: Needs to be parameterized
-	    $self->{ERROR_MSG} = "Can't use " . $months[$month] . " $day, $year as a "
-		. "date because it doesn't exist. Please enter a valid date.";
+	    $self->{ERROR_MSG} = "Can't use ".$months[$env{'form.'.$var.'month'}].                                 " $day, $year as a ".
+		                 "date because it doesn't exist. Please enter a valid date.";
 
 	    return 0;
 	}
 	$helper->{VARS}->{$var} = $chosenDate;
     }
 
+    if (defined($self->{VALIDATOR})) {
+	my $validator = eval($self->{VALIDATOR});
+	die 'Died during evaluation of validator code; Perl said: ' . $@ if $@;
+	my $invalid = &$validator($helper, $state, $self, $self->getValue());
+	if ($invalid) {
+	    $self->{ERROR_MSG} = $invalid;
+	    return 0;
+	}
+    }
+
     if (defined($self->{NEXTSTATE})) {
         $helper->changeState($self->{NEXTSTATE});
     }
 
     return 1;
 }
+
+sub get_date_object {
+    my ($epoch) = @_;
+    my $dt = DateTime->from_epoch(epoch => $epoch)
+                     ->set_time_zone(&Apache::lonlocal::gettimezone());
+    my $lang = Apache::lonlocal::current_language();
+    if ($lang ne '') {
+        eval {
+            $dt->set_locale($lang);
+        };
+    }
+    return $dt;
+}
+
 1;
 
 package Apache::lonhelper::resource;
@@ -1764,7 +2046,10 @@ folders that have all of their contained
 be filtered out. The 'addstatus' attribute, if true, will add the icon
 and long status display columns to the display. The 'addparts'
 attribute will add in a part selector beside problems that have more
-than 1 part.
+than 1 part. The 'includecourse' attribute if true, will include
+the toplevel default.sequence in the results. The 'modalLink' attribute,
+if true, will cause links to be launched as modal pop-ups, instead of 
+replacing the resource selection listing, currently being displayed. 
 
 =head3 SUB-TAGS
 
@@ -1797,6 +2082,49 @@ than 1 part.
   evaluated with "sub { my $helper = shift; my $state = shift;" and
   "}", with the return value used as the mapurl.
 
+=item * <option />: Allows you to add optional elements to the
+  resource chooser currently these can be a checkbox, or a text entry
+  or hidden (see the 'type' attribute below).
+  the following attributes are supported by this tag:
+
+=over 4
+
+=item * type=control-type : determines the type of control displayed.
+  This can be one of the following types: 'checkbox' provides a true/false
+  checkbox.  'text' provides a text entry control. 'hidden' provides a
+  hidden form element that returns the name of the resource for each
+  element of the text box.
+
+=item * text=header-text : provides column header text for the option.
+  
+=item * variable=helpervar : provides a helper variable to contain the
+  value of the input control for each resource.  In general, the result
+  will be a set of values separated by |||  for the checkbox the value between
+  the |||'s will either be empty, if the box is not checked, or the resource
+  name if checked.  For the text entry, the values will be the text in the
+  text box.  This could be empty.  Hidden elements unconditionally provide
+  the resource name for each row of the chooser and allow you to therefore
+  correlate text entries to their resources.
+  The helper variable can be initialized by the user code to pre-load values
+  into the controls:
+
+=over 4
+
+  
+=item * Preloading checkboxes : Set the helper variable to the value you
+   would have gotten from the control if it had been manually set as desired.
+
+=item * Preloading text entries : Set the helper variable to triple pipe
+   separated values where each value is of the form resource-name=value
+
+=item * Preloading hidden fields : These cannot be pre-loaded and will always
+  be pipe separated resource names.
+
+=back
+
+
+=back
+
 =back
 
 =cut
@@ -1833,10 +2161,14 @@ sub start_resource {
     $paramHash->{'toponly'} = $token->[2]{'toponly'};
     $paramHash->{'addstatus'} = $token->[2]{'addstatus'};
     $paramHash->{'addparts'} = $token->[2]{'addparts'};
+    $paramHash->{'modalLink'} = $token->[2]{'modallink'};
+    $paramHash->{'nocurrloc'} = $token->[2]{'nocurrloc'};
+    $paramHash->{'suppressNavmap'} = $token->[2]{'suppressNavmap'};
     if ($paramHash->{'addparts'}) {
 	$helper->declareVar($paramHash->{'variable'}.'_part');
     }
     $paramHash->{'closeallpages'} = $token->[2]{'closeallpages'};
+    $paramHash->{'include_top_level_map'} = $token->[2]{'includecourse'};
     return '';
 }
 
@@ -1925,20 +2257,42 @@ sub start_option {
     if (!defined($paramHash->{OPTION_TEXTS})) {
 	$paramHash->{OPTION_TEXTS} = [ ];
 	$paramHash->{OPTION_VARS}  = [ ];
+	$paramHash->{OPTION_TYPES} = [ ];
 
     }
+    #  We can have an attribute: type which can have the
+    #  values: "checkbox" or "text" which defaults to 
+    #           checkbox allowing us to change the type of input
+    #           for the option:
+    #
+    my $input_widget_type = 'checkbox';
+    if(defined($token->[2]{'type'})) {
+	my $widget_type  = $token->[2]{'type'};
+	if ($widget_type eq 'text') {          # only accept legal alternatives
+	    $input_widget_type = $widget_type; # Illegals are checks.
+	} elsif ($widget_type eq 'hidden') {
+	    $input_widget_type = $widget_type;
+	}
+    }
+
     # OPTION_TEXTS is a list of the text attribute
     #               values used to create column headings.
     # OPTION_VARS is a list of the variable names, used to create the checkbox
     #             inputs.
+    # OPTION_TYPES is a list of the option types:
+    #
     #  We're ok with empty elements. as place holders
     # Although the 'variable' element should really exist.
     #
 
+
     my $option_texts  = $paramHash->{OPTION_TEXTS};
     my $option_vars   = $paramHash->{OPTION_VARS};
+    my $option_types   = $paramHash->{OPTION_TYPES};
     push(@$option_texts,  $token->[2]{'text'});
     push(@$option_vars,   $token->[2]{'variable'});
+    push(@$option_types,   $input_widget_type);
+
 
     #  Need to create and declare the option variables as well to make them
     # persistent.
@@ -1982,7 +2336,7 @@ sub render {
     function checkall(value, checkName) {
 	for (i=0; i<document.forms.helpform.elements.length; i++) {
             ele = document.forms.helpform.elements[i];
-            if (ele.name == checkName + '.forminput') {
+            if (ele.name == checkName + '_forminput') {
                 document.forms.helpform.elements[i].checked=value;
             }
         }
@@ -2014,7 +2368,9 @@ BUTTONS
     my $multichoice    = $self->{'multichoice'};
     my $option_vars    = $self->{OPTION_VARS};
     my $option_texts   = $self->{OPTION_TEXTS};
+    my $option_types   = $self->{OPTION_TYPES};
     my $addparts       = $self->{'addparts'};
+    my $modalLink      = $self->{'modalLink'};
     my $headings_done  = 0;
 
     # Evaluate the map url as needed
@@ -2054,7 +2410,7 @@ BUTTONS
 		    $result .= "<th>$text</th>";
 		}
 	    }
-	    $result .= "<th>Select</th>";
+	    $result .= '<th>'.&Apache::lonlocal::mt('Select').'</th>';
 	    $result .= "</tr><tr>"; # Close off the extra row and start a new one.
 	    $headings_done = 1;
 	}
@@ -2072,21 +2428,56 @@ BUTTONS
 	    my $resource_name =   
                    HTML::Entities::encode($raw_name,"<>&\"'");
 	    if($option_vars) {
+		my $option_num = 0;
 		foreach my $option_var (@$option_vars) {
+		    my $option_type = $option_types->[$option_num];
+		    $option_num++;
 		    my $var_value = "\|\|\|" . $helper->{VARS}->{$option_var} . 
 			"\|\|\|";
 		    my $checked ="";
 		    if($var_value =~ /\Q|||$raw_name|||\E/) {
 			$checked = "checked='checked'";
 		    }
-		    $col .= 
-                        "<td align='center'><input type='checkbox' name ='$option_var".
-			".forminput' value='".
-			$resource_name . "' $checked /> </td>";
+		    if ($option_type eq 'text') {
+			#
+			# For text's the variable value is a ||| separated set of
+			# resource_name=value 
+			#
+			my @values = split(/\|\|\|/, $helper->{VARS}->{$option_var});
+
+			# Normal practice would be to toss this in a hash but 
+			# the only thing that saves is the compare in the loop
+			# below and for all but one case we'll break out of the loop
+			# before it completes.
+
+			my $text_value = '';    # In case there's no match.
+			foreach my $value (@values) {
+			    my ($res, $skip) = split(/=/, $value);
+			    if($res eq $resource_name) {
+				$text_value = $skip;
+				last;
+			    }
+			}
+			# TODO: add an attribute to <option> that allows the
+			#       programmer to set the width of the tex entry box.
+
+			$col .=
+			    "<td align='center'><input type='text' name ='$option_var".
+			    "_forminput' value='".$text_value."' size='5' /> </td>";
+		    } elsif ($option_type eq 'hidden') {
+ 			$col .= "<td align='center'><input type='hidden' name ='$option_var".
+			    "_forminput' value='".
+			    $resource_name . "'/> </td>";
+		    } else {
+			$col .= 
+			    "<td align='center'><input type='$option_type' name ='$option_var".
+			    "_forminput' value='".
+			    $resource_name . "' $checked /> </td>";
+		    }
 		}
 	    }
 
-            $col .= "<td align='center'><input type='$inputType' name='${var}.forminput' ";
+            $col .= "<td align='center'><input type='$inputType' name='${var}_forminput' ";
 	    if (%defaultSymbs) {
 		my $symb=$resource->symb();
 		if (exists($defaultSymbs{$symb})) {
@@ -2115,10 +2506,10 @@ BUTTONS
 	my $resource_name =   
 	    &HTML::Entities::encode(&$valueFunc($resource),"<>&\"'");
 	if ($addparts && (scalar(@{$resource->parts}) > 1)) {
-	    $col .= "<select onclick=\"javascript:updateRadio(this.form,'${var}.forminput','$resource_name');updateHidden(this.form,'$id','${var}');\" name='part_$id.forminput'>\n";
-	    $col .= "<option value=\"$part\">All Parts</option>\n";
+	    $col .= "<select onclick=\"javascript:updateRadio(this.form,'${var}_forminput','$resource_name');updateHidden(this.form,'$id','${var}');\" name='part_${id}_forminput'>\n";
+	    $col .= "<option value=\"$part\">".&Apache::lonlocal::mt('All Parts')."</option>\n";
 	    foreach my $part (@{$resource->parts}) {
-		$col .= "<option value=\"$part\">Part: $part</option>\n";
+		$col .= "<option value=\"$part\">".&Apache::lonlocal::mt('Part: [_1]',$part)."</option>\n";
 	    }
 	    $col .= "</select>";
 	}
@@ -2137,14 +2528,14 @@ BUTTONS
 	}
     }
     function updateHidden(form,id,name) {
-	var select=form['part_'+id+'.forminput'];
-	var hidden=form[name+'_part.forminput'];
+	var select=form['part_'+id+'_forminput'];
+	var hidden=form[name+'_part_forminput'];
 	var which=select.selectedIndex;
 	hidden.value=select.options[which].value;
     }
 // -->
 </script>
-<input type="hidden" name="${var}_part.forminput" />
+<input type="hidden" name="${var}_part_forminput" />
 
 RADIO
     $env{'form.condition'} = !$self->{'toponly'};
@@ -2155,6 +2546,10 @@ RADIO
 	push @$cols, (Apache::lonnavmaps::part_status_summary());
 	
     }
+    my $caller;
+    if ($helper->{TITLE} eq 'Printing Helper') {
+        $caller = 'printout';
+    }
     $result .= 
         &Apache::lonnavmaps::render( { 'cols' => $cols,
                                        'showParts' => 0,
@@ -2162,7 +2557,13 @@ RADIO
                                        'resource_no_folder_link' => 1,
 				       'closeAllPages' => $self->{'closeallpages'},
                                        'suppressEmptySequences' => $self->{'suppressEmptySequences'},
-                                       'iterator_map' => $mapUrl }
+				       'include_top_level_map' => $self->{'include_top_level_map'},
+                                       'iterator_map' => $mapUrl,
+                                       'map_no_edit_link' => 1,
+                                       'modalLink' => $modalLink,
+                                       'nocurrloc' => $self->{'nocurrloc'},
+                                       'suppressNavmap' => $self->{'suppressNavmap'},
+                                       'caller' => $caller, }
                                        );
 
     $result .= $buttons;
@@ -2177,6 +2578,21 @@ sub postprocess {
         $self->{ERROR_MSG} = 'You must choose at least one resource to continue.';
         return 0;
     }
+    # For each of the attached options.  If it's env var is undefined, set it to
+    # an empty string instead.. an undef'd env var means no choices selected.
+    #
+
+    my $option_vars = $self->{OPTION_VARS};
+    if ($option_vars) {
+	foreach my $var (@$option_vars) {
+	    my $env_name = "form.".$var."_forminput";
+	    if (!defined($env{$env_name})) {
+		$env{$env_name} = '';
+		$helper->{VARS}->{$var} = '';
+	    }
+	}
+    }
+
 
     if (defined($self->{NEXTSTATE})) {
         $helper->changeState($self->{NEXTSTATE});
@@ -2219,6 +2635,12 @@ selection. Defaults to false.
 If true, only active students and course personnel will be
 shown. Defaults to false.
 
+=item * B<sectiononly>:
+
+If true, and user's role is in a specific section, only course personnel 
+will be shown if they also have a section-specific role in the same section.
+Defaults to false.
+
 =item * B<emptyallowed>:
 
 If true, the selection of no users is allowed. Defaults to false.
@@ -2254,6 +2676,7 @@ sub start_student {
     $helper->declareVar($paramHash->{'variable'});
     $paramHash->{'multichoice'} = $token->[2]{'multichoice'};
     $paramHash->{'coursepersonnel'} = $token->[2]{'coursepersonnel'};
+    $paramHash->{'sectiononly'} = $token->[2]{'sectiononly'};
     $paramHash->{'activeonly'} = $token->[2]{'activeonly'};
     if (defined($token->[2]{'nextstate'})) {
         $paramHash->{NEXTSTATE} = $token->[2]{'nextstate'};
@@ -2277,113 +2700,6 @@ sub render {
     my $buttons = '';
     my $var = $self->{'variable'};
 
-    if ($self->{'multichoice'}) {
-        $result = <<SCRIPT;
-<script type="text/javascript">
-// <!--
-    function checkall(value, checkName) {
-	for (i=0; i<document.forms.helpform.elements.length; i++) {
-            ele = document.forms.helpform.elements[i];
-            if (ele.name == checkName + '.forminput') {
-                document.forms.helpform.elements[i].checked=value;
-            }
-        }
-    }
-    function checksec(value) {
-	for (i=0; i<document.forms.helpform.elements.length; i++) {
-	    comp = document.forms.helpform.elements.chksec.value;
-            if (document.forms.helpform.elements[i].value.indexOf(':'+comp+':') != -1) {
-		if (document.forms.helpform.elements[i].value.indexOf(':Active') != -1) {
-		    document.forms.helpform.elements[i].checked=value;
-		}
-            }
-        }
-    }
-    function checkactive() {
-	for (i=0; i<document.forms.helpform.elements.length; i++) {
-            if (document.forms.helpform.elements[i].value.indexOf(':Active') != -1) {
-                document.forms.helpform.elements[i].checked=true;
-            } 
-        }
-    }
-    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) {
-                document.forms.helpform.elements[i].checked=false;
-            } 
-        }
-    }
-    function getDesiredState() {     // Return desired person state radio value.
-        numRadio = document.forms.helpform.personstate.length;
-        for (i =0; i < numRadio; i++) {
-	    if (document.forms.helpform.personstate[i].checked) {
-                return document.forms.helpform.personstate[i].value;
-            }
-        }
-        return "";
-    }
-
-    function checksections(value) {    // Check selected sections.
-        numSections  = document.forms.helpform.chosensections.length;
-	desiredState = getDesiredState();
-
-	for (var option = 0; option < numSections; option++) {
-	    if(document.forms.helpform.chosensections.options[option].selected) {
-		section = document.forms.helpform.chosensections.options[option].text;
-		if (section == "none") {
-		    section ="";
-		}
-		for (i = 0; i < document.forms.helpform.elements.length; i++ ) {
-		    if (document.forms.helpform.elements[i].value.indexOf(':') != -1) {
-			info = document.forms.helpform.elements[i].value.split(':');
-			hisSection = info[2];
-			hisState   = info[4];
-			if (desiredState == hisState ||
-			    desiredState == "All") {
-			    if(hisSection == section ||
-			       section =="" ) {
-				document.forms.helpform.elements[i].checked = value;
-			    }
-			}
-		    }
-		}
-            }
-	}
-				   }
-// -->
-</script>
-SCRIPT
-
-        my %lt=&Apache::lonlocal::texthash(
-                    'ocs'  => "Select Only Current Students",
-                    'ues'  => "Unselect Expired Students",
-                    'sas'  => "Select All Students",
-                    'uas'  => "Unselect All Students",
-                    'sfsg' => "Select Current Students for Section/Group",
-		    'ufsg' => "Unselect for Section/Group");
- 
-        $buttons = <<BUTTONS;
-<br />
-<table>
-  
-  <tr>
-     <td><input type="button" onclick="checkall(true, '$var')" value="$lt{'sas'}" /></td>
-     <td> <input type="button" onclick="checkall(false, '$var')" value="$lt{'uas'}" /><br /></td>
-  </tr>
-  
-</table>
-<br />
-BUTTONS
-    $result .= $buttons;   
-
-    }
 
     if (defined $self->{ERROR_MSG}) {
         $result .= '<font color="#FF0000">' . $self->{ERROR_MSG} . '</font><br /><br />';
@@ -2401,203 +2717,83 @@ BUTTONS
 	%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'}) {
-	my %coursepersonnel = Apache::lonnet::get_course_adv_roles();
-	for (sort keys %coursepersonnel) {
-	    for my $role (split /,/, $coursepersonnel{$_}) {
-		# extract the names so we can sort them
-		my @people;
-		
-		for (split /,/, $role) {
-		    push @people, [split /:/, $role];
-		}
-		
-		@people = sort { $a->[0] cmp $b->[0] } @people;
-		
-		for my $person (@people) {
-		    push @$choices, [join(':', @$person), $person->[0], '', $_];
-		}
-	    }
-	}
+    my $personnel_section;
+    if ($self->{'sectiononly'}) {
+        $personnel_section = $env{'request.course.sec'};
     }
 
-    # Constants
-    my $section = Apache::loncoursedata::CL_SECTION();
-    my $fullname = Apache::loncoursedata::CL_FULLNAME();
-    my $status = Apache::loncoursedata::CL_STATUS();
-
-    # Load up the students
-    my $classlist = &Apache::loncoursedata::get_classlist();
-    my @keys = keys %{$classlist};
-    # Sort by: Section, name
-    @keys = sort {
-        if ($classlist->{$a}->[$section] ne $classlist->{$b}->[$section]) {
-            return $classlist->{$a}->[$section] cmp $classlist->{$b}->[$section];
-        }
-        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;
-    }
+    my ($course_personnel, 
+	$current_members, 
+	$expired_members, 
+	$future_members) = 
+	    &Apache::lonselstudent::get_people_in_class($env{'request.course.sec'},
+	                                                $personnel_section);
 
-    # username, fullname, section, type
-    for (@keys) {
+    # Load up the non-students, if necessary
 
-	# 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'];
-	}
+    if ($self->{'coursepersonnel'}) {
+	unshift @$current_members, (@$course_personnel);
     }
 
-    my $name = $self->{'coursepersonnel'} ? &mt('Name') : &mt('Student Name');
-    my $type = 'radio';
-    if ($self->{'multichoice'}) { $type = 'checkbox'; }
-    $result .= "<table cellspacing='2' cellpadding='2' border='0'>\n";
-    $result .= "<tr><td></td><td align='center'><b>$name</b></td>".
-        "<td align='center'><b>" . &mt('Section') . "</b></td>" . 
-	"<td align='center'><b>".&mt('Status')."</b></td>" . 
-	"<td align='center'><b>" . &mt("Role") . "</b></td>" .
-	"<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 (%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";
-    }
-    $result .= "</table>\n\n";
+    my %titles = &Apache::lonlocal::texthash(
+                   'active'  => 'Select Currently Enrolled Students and Active Course Personnel',
+                   'future'  => 'Select Future Enrolled Students',
+                   'expired' => 'Select Previously Enrolled Students',
+                 );
+
+    if ($env{'request.course.sec'}) {
+        if ($self->{'sectiononly'}) {
+            $titles{'active'} = &mt('Select Currently Enrolled Students and Active Course Personnel in Section: [_1]',
+                                $env{'request.course.sec'});
+        } else {
+            $titles{'active'} = &mt('Select Currently Enrolled Students in Section: [_1], and Active Course Personnel',
+                                    $env{'request.course.sec'});
+        }
+        $titles{'future'} = &mt('Select Future Enrolled Students in Section: [_1]',
+                                $env{'request.course.sec'});
+        $titles{'expired'} = &mt('Select Previously Enrolled Students in Section: [_1]',
+                                 $env{'request.course.sec'});
+    }
+
+    #   Current personnel
+
+    $result .= '<h4>'.$titles{'active'}.'</h4>';
+    $result .= &Apache::lonselstudent::render_student_list( $current_members,
+							    "helpform",
+							    "current",
+							    \%defaultUsers,
+							    $self->{'multichoice'},
+							    $self->{'variable'},
+							    1);
 
     # If activeonly is not set then we can also give the expired students:
     #
-    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";	    
-	}
-	$result .= "</table>\n";
-	
+    if (!$self->{'activeonly'} && ((scalar(@$future_members)) > 0)) {
+
+	# And future.
+
+	$result .= '<h4>'.$titles{'future'}.'</h4>';
+       
+	$result .= &Apache::lonselstudent::render_student_list( $future_members,
+								"helpform",
+								"future",
+								\%defaultUsers,
+								$self->{'multichoice'},
+								$self->{'variable'},
+								0);
+    }
+    if (!$self->{'activeonly'} && ((scalar(@$expired_members)) > 0)) {
+	# Past 
+
+	$result .= '<h4>'.$titles{'expired'}.'</h4>';
+	$result .= &Apache::lonselstudent::render_student_list($expired_members,
+							       "helpform",
+							       "past",
+							       \%defaultUsers,
+							       $self->{'multichoice'},
+							       $self->{'variable'},
+							       0);
     }
 
 
@@ -2608,7 +2804,7 @@ INACTIVEBUTTONS
 sub postprocess {
     my $self = shift;
 
-    my $result = $env{'form.' . $self->{'variable'} . '.forminput'};
+    my $result = $env{'form.' . $self->{'variable'} . '_forminput'};
     if (!$result && !$self->{'emptyallowed'}) {
 	if ($self->{'coursepersonnel'}) {
 	    $self->{ERROR_MSG} = 
@@ -2793,7 +2989,7 @@ sub render {
     function checkall(value, checkName) {
 	for (i=0; i<document.forms.helpform.elements.length; i++) {
             ele = document.forms.helpform.elements[i];
-            if (ele.name == checkName + '.forminput') {
+            if (ele.name == checkName + '_forminput') {
                 document.forms.helpform.elements[i].checked=value;
             }
         }
@@ -2832,30 +3028,31 @@ BUTTONS
     }
 
     # Get the list of files in this directory.
-    my @fileList;
+    my (@fileList,$listref,$listerror);
 
     # If the subdirectory is in local CSTR space
     my $metadir;
-    if ($subdir =~ m|/home/([^/]+)/public_html/(.*)|) {
-	my ($user,$domain)= 
-	    &Apache::loncacc::constructaccess($subdir,
-				     $Apache::lonnet::perlvar{'lonDefDomain'});
-	$metadir='/res/'.$domain.'/'.$user.'/'.$2;
-        @fileList = &Apache::lonnet::dirlist($subdir, $domain, $user, '');
-    } elsif ($subdir =~ m|^~([^/]+)/(.*)$|) {
-	$subdir='/home/'.$1.'/public_html/'.$2;
+    my $londocroot = $Apache::lonnet::perlvar{'lonDocRoot'};
+    if ($subdir =~ m{^(?:\Q$londocroot\E)*/priv/[^/]+/[^/]+/(.*)$}) {
+        my $innerpath=$1;
+        unless ($subdir=~m{^\Q$londocroot\E}) {
+           $subdir=$londocroot.$subdir;
+        }
 	my ($user,$domain)= 
-	    &Apache::loncacc::constructaccess($subdir,
-				     $Apache::lonnet::perlvar{'lonDefDomain'});
-	$metadir='/res/'.$domain.'/'.$user.'/'.$2;
-        @fileList = &Apache::lonnet::dirlist($subdir, $domain, $user, '');
+	    &Apache::lonnet::constructaccess($subdir);
+	$metadir='/res/'.$domain.'/'.$user.'/'.$innerpath;
+        ($listref,$listerror) =
+            &Apache::lonnet::dirlist($subdir,$domain,$user,undef,undef,'/');
     } else {
         # local library server resource space
-        @fileList = &Apache::lonnet::dirlist($subdir, $env{'user.domain'}, $env{'user.name'}, '');
+        ($listref,$listerror) = 
+            &Apache::lonnet::dirlist($subdir,$env{'user.domain'},$env{'user.name'},undef,undef,'/');
     }
 
     # Sort the fileList into order
-    @fileList = sort {lc($a) cmp lc($b)} @fileList;
+    if (ref($listref) eq 'ARRAY') {
+        @fileList = sort {lc($a) cmp lc($b)} @{$listref};
+    }
 
     $result .= $buttons;
 
@@ -2910,7 +3107,7 @@ BUTTONS
             my $id = &new_id();
             $result .= '<tr><td align="right"' . " bgcolor='$color'>" .
                 "<input $onclick type='$type' name='" . $var
-            . ".forminput' ".qq{id="$id"}." value='" . HTML::Entities::encode($fileName,"<>&\"'").
+            . "_forminput' ".qq{id="$id"}." value='" . HTML::Entities::encode($fileName,"<>&\"'").
                 "'";
             if (!$self->{'multichoice'} && $choices == 0) {
                 $result .= ' checked="checked"';
@@ -2950,7 +3147,7 @@ sub fileState {
     }
     my $docroot = $Apache::lonnet::perlvar{'lonDocRoot'};
     my $subdirpart = $constructionSpaceDir;
-    $subdirpart =~ s/^\/home\/$uname\/public_html//;
+    $subdirpart =~ s{^\Q$docroot/priv/$udom/$uname\E}{};
     my $resdir = $docroot . '/res/' . $udom . '/' . $uname .
         $subdirpart;
 
@@ -2971,7 +3168,7 @@ sub fileState {
 
 sub postprocess {
     my $self = shift;
-    my $result = $env{'form.' . $self->{'variable'} . '.forminput'};
+    my $result = $env{'form.' . $self->{'variable'} . '_forminput'};
     if (!$result) {
         $self->{ERROR_MSG} = 'You must choose at least one file '.
             'to continue.';
@@ -2996,8 +3193,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' which will restrict this to only
+have sections and not include groups
 
 =cut
 
@@ -3034,21 +3235,37 @@ sub start_section {
 
     # Populate the CHOICES element
     my %choices;
+    my $usersec = $Apache::lonnet::env{'request.course.sec'};
 
-    my $section = Apache::loncoursedata::CL_SECTION();
-    my $classlist = Apache::loncoursedata::get_classlist();
-    foreach (keys %$classlist) {
-        my $sectionName = $classlist->{$_}->[$section];
-        if (!$sectionName) {
-            $choices{"No section assigned"} = "";
-        } else {
-            $choices{$sectionName} = $sectionName;
+    if ($usersec ne '') {
+        $choices{$usersec} = $usersec;
+    } else {
+        my $section = Apache::loncoursedata::CL_SECTION();
+        my $classlist = Apache::loncoursedata::get_classlist();
+        foreach my $user (keys(%$classlist)) {
+            my $section_name = $classlist->{$user}[$section];
+            if (!$section_name) {
+                $choices{"No section assigned"} = "";
+            } else {
+                $choices{$section_name} = $section_name;
+            }
         }
-    } 
-   
-    for my $sectionName (sort(keys(%choices))) {
-        
-        push @{$paramHash->{CHOICES}}, [$sectionName, $sectionName];
+ 
+        if (exists($choices{"No section assigned"})) {
+	    push(@{$paramHash->{CHOICES}}, 
+	         ['No section assigned','No section assigned']);
+	    delete($choices{"No section assigned"});
+        }
+    }
+    for my $section_name (sort {lc($a) cmp lc($b) } (keys(%choices))) {
+	push @{$paramHash->{CHOICES}}, [$section_name, $section_name];
+    }
+    return if ($token->[2]{'onlysections'});
+
+    # add in groups to the end of the list
+    my %curr_groups = &Apache::longroup::coursegroups();
+    foreach my $group_name (sort(keys(%curr_groups))) {
+	push(@{$paramHash->{CHOICES}}, [$group_name, $group_name]);
     }
 }    
 
@@ -3068,10 +3285,16 @@ package Apache::lonhelper::group;
  
 =head2 Element: groupX<group, helper element>
  
-<section> allows the user to choose one or more groups from the current course.
+<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.
+
+also takes a boolean grouponly, which if true, will restrict choice to
+groups in which user is a member, unless user has the mdg priv in the course,
+in which case all groups will be possible choices. Defaults to false.
 
-It takes the standard attributes "variable", "multichoice", and "nextstate", meaning what they do for most other elements.
- 
 =cut
 
 no strict;
@@ -3101,6 +3324,7 @@ sub start_group {
     $helper->declareVar($paramHash->{'variable'});
     $paramHash->{'multichoice'} = $token->[2]{'multichoice'};
     $paramHash->{'allowempty'} = $token->[2]{'allowempty'};
+    $paramHash->{'grouponly'} = $token->[2]{'grouponly'};
     if (defined($token->[2]{'nextstate'})) {
         $paramHash->{NEXTSTATE} = $token->[2]{'nextstate'};
     }
@@ -3108,18 +3332,17 @@ sub start_group {
     # Populate the CHOICES element
     my %choices;
 
-    my $numgroups;
     my %curr_groups;
-    if (&Apache::loncommon::coursegroups(\%curr_groups)) {
-        foreach my $group_name (keys %curr_groups) {
-            $choices{$group_name} = $group_name;
-        }
+    if ((!$paramHash->{'grouponly'}) || (&Apache::lonnet::allowed('mdg',$Apache::lonnet::env{'request.course.id'}))) {
+        %curr_groups = &Apache::longroup::coursegroups();
+    } elsif ($Apache::lonnet::env{'request.course.groups'} ne '') {
+        map { $curr_groups{$_} = 1; } split(/:/,$Apache::lonnet::env{'request.course.groups'});
     }
-    foreach my $group_name (sort(keys(%choices))) {
-        push @{$paramHash->{CHOICES}}, [$group_name, $group_name];
+    foreach my $group_name (sort {lc($a) cmp lc($b)} (keys(%curr_groups))) {
+	push(@{$paramHash->{CHOICES}}, [$group_name, $group_name]);
     }
 }
-                                                                                    
+
 sub end_group {
     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
 
@@ -3139,6 +3362,10 @@ package Apache::lonhelper::string;
 string elements provide a string entry field for the user. string elements
 take the usual 'variable' and 'nextstate' parameters. string elements
 also pass through 'maxlength' and 'size' attributes to the input tag.
+Since you could have multiple strings in a helper state, each with its own
+validator, all but the last string should have
+noproceed='1' so that _all_ validators are evaluated before the next
+state can be reached.
 
 string honors the defaultvalue tag, if given.
 
@@ -3158,6 +3385,7 @@ BEGIN {
 
 sub new {
     my $ref = Apache::lonhelper::element->new();
+    $ref->{'PROCEED'} = 1;      # By default postprocess goes to next state.
     bless($ref);
 }
 
@@ -3174,20 +3402,33 @@ sub start_string {
     $paramHash->{'nextstate'} = $token->[2]{'nextstate'};
     $paramHash->{'maxlength'} = $token->[2]{'maxlength'};
     $paramHash->{'size'} = $token->[2]{'size'};
-
     return '';
 }
 
 sub end_string {
     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
 
+
     if ($target ne 'helper') {
         return '';
     }
-    Apache::lonhelper::string->new();
+    my $state = Apache::lonhelper::string->new();
+
+
+    if(&Apache::lonxml::get_param('noproceed', $parstack, $safeeval, undef, 1)) {
+	$state->noproceed();
+    }
+
+   
+
     return '';
 }
 
+sub noproceed() {
+    my $self = shift;
+    $self->{PROCEED}  = 0;
+}
+
 sub render {
     my $self = shift;
     my $result = '';
@@ -3196,7 +3437,7 @@ sub render {
         $result .= '<p><font color="#FF0000">' . $self->{ERROR_MSG} . '</font></p>';
     }
 
-    $result .= '<input type="string" name="' . $self->{'variable'} . '.forminput"';
+    $result .= '<input type="text" name="' . $self->{'variable'} . '_forminput"';
 
     if (defined($self->{'size'})) {
         $result .= ' size="' . $self->{'size'} . '"';
@@ -3223,7 +3464,7 @@ sub postprocess {
 
     if (defined($self->{VALIDATOR})) {
 	my $validator = eval($self->{VALIDATOR});
-	die 'Died during evaluation of evaulation code; Perl said: ' . $@ if $@;
+	die 'Died during evaluation of validator code; Perl said: ' . $@ if $@;
 	my $invalid = &$validator($helper, $state, $self, $self->getValue());
 	if ($invalid) {
 	    $self->{ERROR_MSG} = $invalid;
@@ -3231,7 +3472,7 @@ sub postprocess {
 	}
     }
 
-    if (defined($self->{'nextstate'})) {
+    if (defined($self->{'nextstate'}) && $self->{PROCEED}) {
         $helper->changeState($self->{'nextstate'});
     }
 
@@ -3329,7 +3570,8 @@ sub start_clause {
     die 'Error in clause of condition, Perl said: ' . $@ if $@;
     if (!&$clause($helper, $paramHash)) {
         # Discard all text until the /condition.
-        &Apache::lonxml::get_all_text('/condition', $parser);
+	my $end_tag = $paramHash->{SKIPTAG} || '/condition';
+        &Apache::lonxml::get_all_text($end_tag, $parser);
     }
 }
 
@@ -3388,7 +3630,7 @@ snippets and collecting the results. Fin
 helper, going to a provided page.
 
 If the parameter "restartCourse" is true, this will override the buttons and
-will make a "Finish Helper" button that will re-initialize the course for them,
+will make a Save button (Finish Helper) that will re-initialize the course for them,
 which is useful for the Course Initialization helper so the users never see
 the old values taking effect.
 
@@ -3457,7 +3699,7 @@ sub render {
     my @results;
 
     # Collect all the results
-    for my $stateName (keys %{$helper->{STATES}}) {
+    for my $stateName (keys(%{$helper->{STATES}})) {
         my $state = $helper->{STATES}->{$stateName};
         
         for my $element (@{$state->{ELEMENTS}}) {
@@ -3495,7 +3737,7 @@ sub render {
 
     my $actionURL = $self->{EXIT_PAGE};
     my $targetURL = '';
-    my $finish=&mt('Finish');
+	my $finish=&mt('Save');
     if ($self->{'restartCourse'}) {
 	$actionURL = '/adm/roles';
 	$targetURL = '/adm/menu';
@@ -3507,21 +3749,17 @@ sub render {
 	if ($env{'course.'.$env{'request.course.id'}.'.clonedfrom'}) {
 	    $targetURL = '/adm/parmset?overview=1';
 	}
-	my $finish=&mt('Finish Course Initialization');
     }
-    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>\n" .
+    my $previous = HTML::Entities::encode(&mt("Back"), '<>&"');
+    my $next = HTML::Entities::encode(&mt("Next"), '<>&"');
+    $result .= "<p>\n" .
+	"<form action='".$actionURL."' method='post' >\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" .
 	"<input type='hidden' name='" . $env{'request.role'} . 
 	"' value='1' />\n<input type='submit' value='" . $finish . "' />\n" .
-	"</form></center>";
+	"</form></p>\n";
 
     return $result;
 }
@@ -3534,7 +3772,7 @@ sub overrideForm {
 
 package Apache::lonhelper::parmwizfinal;
 
-# This is the final state for the parmwizard. It is not generally useful,
+# This is the final state for the parm helper. It is not generally useful,
 # so it is not perldoc'ed. It does its own processing.
 # It is represented with <parmwizfinal />, and
 # should later be moved to lonparmset.pm .
@@ -3600,44 +3838,77 @@ sub render {
     
     # Print the granularity, depending on the action
     if ($vars->{GRANULARITY} eq 'whole_course') {
-        $resourceString .= '<li>'.&mt('for <b>all resources in the course</b>').'</li>';
+        $resourceString .= '<li>'.&mt('for [_1]all resources in the course[_2]','<b>','</b>').'</li>';
 	if ($vars->{TARGETS} eq 'course') {
-	    $level = 11; # general course, see lonparmset.pm perldoc
+	    $level = 18; # general course, see lonparmset.pm perldoc
 	} elsif ($vars->{TARGETS} eq 'section') {
-	    $level = 6;
+	    $level = 12;
+	} elsif ($vars->{TARGETS} eq 'group') {
+	    $level = 8;
 	} else {
-	    $level = 3;
+	    $level = 4;
 	}
         $affectedResourceId = "0.0";
         $symb = 'a';
         $paramlevel = 'general';
-    } elsif ($vars->{GRANULARITY} eq 'map') {
+    } elsif (($vars->{GRANULARITY} eq 'map') || ($vars->{GRANULARITY} eq 'maprecurse')) {
         my $navmap = Apache::lonnavmaps::navmap->new();
-        my $res = $navmap->getByMapPc($vars->{RESOURCE_ID});
-        my $title = $res->compTitle();
-        $symb = $res->symb();
-        $resourceString .= '<li>'.&mt('for the map named [_1]',"<b>$title</b>").'</li>';
-	if ($vars->{TARGETS} eq 'course') {
-	    $level = 10; # general course, see lonparmset.pm perldoc
-	} elsif ($vars->{TARGETS} eq 'section') {
-	    $level = 5;
-	} else {
-	    $level = 2;
-	}
+        if (defined($navmap)) {
+             my $res = $navmap->getByMapPc($vars->{RESOURCE_ID});
+             my $title = $res->compTitle();
+             $symb = $res->symb();
+             if ($vars->{GRANULARITY} eq 'map') {
+                 $resourceString .= '<li>'.&mt('for the map named [_1]',"<b>$title</b>").'</li>';
+             } else {
+                 $resourceString .= '<li>'.&mt('for the map named [_1] (applies recursively to sub-folders)',"<b>$title</b>").'</li>';
+             }
+        } else {
+            $resourceString .= '<li>'.&mt('for the map ID [_1] (name unavailable)','<b>'.$vars->{RESOURCE_ID}.'</b>').'</li>';
+            &Apache::lonnet::logthis('Retrieval of map title failed in lonhelper.pm - could not create navmap object for course.');
+
+        }
+        if ($vars->{GRANULARITY} eq 'maprecurse') {
+            if ($vars->{TARGETS} eq 'course') {
+                $level = 17; # general course, see lonparmset.pm perldoc
+            } elsif ($vars->{TARGETS} eq 'section') {
+                $level = 11;
+            } elsif ($vars->{TARGETS} eq 'group') {
+                $level = 7;
+            } else {
+                $level = 3;
+            }
+        } else {
+	    if ($vars->{TARGETS} eq 'course') {
+	        $level = 16; # general course, see lonparmset.pm perldoc
+	    } elsif ($vars->{TARGETS} eq 'section') {
+	        $level = 10;
+	    } elsif ($vars->{TARGETS} eq 'group') {
+	        $level = 6;
+	    } else {
+	        $level = 2;
+	    }
+        }
         $affectedResourceId = $vars->{RESOURCE_ID};
         $paramlevel = 'map';
     } else {
-        my $navmap = Apache::lonnavmaps::navmap->new();
-        my $res = $navmap->getById($vars->{RESOURCE_ID});
         my $part = $vars->{RESOURCE_ID_part};
 	if ($part ne 'All Parts' && $part) { $parm_name=~s/^0/$part/; } else { $part=&mt('All Parts'); }
-        $symb = $res->symb();
-        my $title = $res->compTitle();
-        $resourceString .= '<li>'.&mt('for the resource named [_1] part [_2]',"<b>$title</b>","<b>$part</b>").'</li>';
+        my $navmap = Apache::lonnavmaps::navmap->new();
+        if (defined($navmap)) {
+            my $res = $navmap->getById($vars->{RESOURCE_ID});
+            $symb = $res->symb();
+            my $title = $res->compTitle();
+            $resourceString .= '<li>'.&mt('for the resource named [_1], part [_2]',"<b>$title</b>","<b>$part</b>").'</li>';
+        } else {
+            $resourceString .= '<li>'.&mt('for the resource ID [_1] (name unavailable), part [_2]','<b>'.$vars->{RESOURCE_ID}.'</b>',"<b>$part</b>").'</li>';
+            &Apache::lonnet::logthis('Retrieval of resource title failed in lonhelper.pm - could not create navmap object for course.');
+        }
 	if ($vars->{TARGETS} eq 'course') {
-	    $level = 7; # general course, see lonparmset.pm perldoc
+	    $level = 13; # general course, see lonparmset.pm perldoc
 	} elsif ($vars->{TARGETS} eq 'section') {
-	    $level = 4;
+	    $level = 9;
+	} elsif ($vars->{TARGETS} eq 'group') {
+	    $level = 5;
 	} else {
 	    $level = 1;
 	}
@@ -3645,7 +3916,7 @@ sub render {
         $paramlevel = 'full';
     }
 
-    my $result = "<form name='helpform' method='POST' action='/adm/parmset#$affectedResourceId&$parm_name&$level'>\n";
+    my $result = "<form name='helpform' method='post' action='/adm/parmset#$affectedResourceId&$parm_name&$level'>\n";
     $result .= "<input type='hidden' name='action' value='settable' />\n";
     $result .= "<input type='hidden' name='dis' value='helper' />\n";
     $result .= "<input type='hidden' name='pscat' value='".
@@ -3653,7 +3924,7 @@ sub render {
     if ($vars->{GRANULARITY} eq 'resource') {
 	$result .= "<input type='hidden' name='symb' value='".
 	    HTML::Entities::encode($symb,"'<>&\"") . "' />\n";
-    } elsif ($vars->{GRANULARITY} eq 'map') {
+    } elsif (($vars->{GRANULARITY} eq 'map') || ($vars->{GRANULARITY} eq 'maprecurse')) {
 	$result .= "<input type='hidden' name='pschp' value='".
 	    $affectedResourceId."' />\n";
     }
@@ -3662,7 +3933,10 @@ sub render {
     $result .= "<input type='hidden' name='psprt' value='".
 	HTML::Entities::encode($part,"'<>&\"") . "' />\n";
 
-    $result .= '<p>'.&mt('Confirm that this information is correct, then click &quot;Finish Helper&quot; to complete setting the parameter.').'<ul>';
+    $result .= '<p class="LC_info">'
+              .&mt('Confirm that this information is correct, then click &quot;Save&quot; to complete setting the parameter.')
+              .'</p>'
+              .'<ul>';
     
     # Print the type of manipulation:
     my $extra;
@@ -3710,7 +3984,7 @@ sub render {
     
     # Print targets
     if ($vars->{TARGETS} eq 'course') {
-        $result .= '<li>'.&mt('for <b>all students in course</b>').'</li>';
+        $result .= '<li>'.&mt('for [_1]all students in course[_2]','<b>','</b>').'</li>';
     } elsif ($vars->{TARGETS} eq 'section') {
         my $section = $vars->{SECTION_NAME};
         $result .= '<li>'.&mt('for section [_1]',"<b>$section</b>").'</li>';
@@ -3735,9 +4009,21 @@ sub render {
 
     # Print value
     if ($vars->{ACTION_TYPE} ne 'tries' && $vars->{ACTION_TYPE} ne 'weight') {
-	$result .= '<li>'.&mt('to [_1] ([_2])',"<b>".ctime($vars->{PARM_DATE})."</b>",Apache::lonnavmaps::timeToHumanString($vars->{PARM_DATE}))."</li>\n";
+        my $showdate = &Apache::lonlocal::locallocaltime($vars->{PARM_DATE});
+	$result .= '<li>'.&mt('to [_1] ([_2])',"<b>".$showdate."</b>",Apache::lonnavmaps::timeToHumanString($vars->{PARM_DATE}))."</li>\n";
     }
+
+	$result .= '</ul>';
  
+# FIXME: Make previous button working
+#        Found to be dysfunctional when used to change the selected student
+#   my $previous = HTML::Entities::encode(&mt("Back"), '<>&"');
+    my $buttons .= '<p><span class="LC_nobreak">'
+#                 .'<input name="back" type="button"'
+#                 .' value="'.$previous.'" onclick="history.go(-1)" />'
+                  .' <input type="submit" value="'.&mt('Save').'" />' # Finish Helper
+                  .'</span></p>'."\n";
+
     # print pres_marker
     $result .= "\n<input type='hidden' name='pres_marker'" .
         " value='$affectedResourceId&$parm_name&$level' />\n";
@@ -3747,7 +4033,7 @@ sub render {
     $result .= "\n<input type='hidden' value='$symb' name='pssymb' />";
     $result .= "\n<input type='hidden' value='$paramlevel' name='parmlev' />";
 
-    $result .= "<br /><br /><center><input type='submit' value='".&mt('Finish Helper')."' /></center></form>\n";
+    $result .= $buttons;
 
     return $result;
 }