--- loncom/interface/lonhelper.pm	2009/06/15 11:18:11	1.176
+++ loncom/interface/lonhelper.pm	2012/12/30 16:05:16	1.190
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # .helper XML handler to implement the LON-CAPA helper
 #
-# $Id: lonhelper.pm,v 1.176 2009/06/15 11:18:11 bisitz Exp $
+# $Id: lonhelper.pm,v 1.190 2012/12/30 16:05:16 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -261,7 +261,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;
@@ -571,7 +571,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 = "";
@@ -606,7 +606,7 @@ sub display {
     # FIXME: This should be parameterized, not concatenated - Jeremy
 
 
-    if (!$state->overrideForm()) { $result.='<form name="helpform" method="post">'; }
+    if (!$state->overrideForm()) { $result.='<form name="helpform" method="post" action="">'; }
     if ($stateHelp) {
         $stateHelp = &Apache::loncommon::help_open_topic($stateHelp);
     }
@@ -661,7 +661,7 @@ sub display {
         </form>
 FOOTER
 
-    $result .= &Apache::loncommon::end_page();
+    $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();
@@ -1476,9 +1476,9 @@ BUTTONS
             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->[3]) {  # if we need to evaluate this choice
             $choiceLabel = "sub { my $helper = shift; my $state = shift;" .
@@ -1486,7 +1486,7 @@ BUTTONS
             $choiceLabel = eval($choiceLabel);
             $choiceLabel = &$choiceLabel($helper, $self);
         }
-        $result .= "/></td><td> ".qq{<label for="id$id">}.
+        $result .= " /></td><td> ".qq{<label for="id$id">}.
 	    $choiceLabel. "</label></td>";
 	if ($choice->[4]) {
 	    $result .='<td><input type="text" size="5" name="'
@@ -1913,7 +1913,13 @@ CHECK
 	if ($anytime) {
 	    $result.=' checked="checked" '
 	}
-	$result.="name='${var}anytime'/>".&mt('Any time').'</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;
 
@@ -2066,6 +2072,49 @@ the toplevel default.sequence in the res
   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
@@ -2195,20 +2244,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.
@@ -2284,6 +2355,7 @@ 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 $headings_done  = 0;
 
@@ -2342,17 +2414,52 @@ 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>";
+		    }
 		}
 	    }
 
@@ -2433,7 +2540,8 @@ RADIO
 				       'closeAllPages' => $self->{'closeallpages'},
                                        'suppressEmptySequences' => $self->{'suppressEmptySequences'},
 				       'include_top_level_map' => $self->{'include_top_level_map'},
-                                       'iterator_map' => $mapUrl }
+                                       'iterator_map' => $mapUrl,
+                                       'map_no_edit_link' => 1, } 
                                        );
 
     $result .= $buttons;
@@ -2870,30 +2978,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,undef,undef,'/');
-    } 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,undef,undef,'/');
+	    &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'},undef,undef,'/');
+        ($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;
 
@@ -2988,7 +3097,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;
 
@@ -3188,6 +3297,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.
 
@@ -3207,6 +3320,7 @@ BEGIN {
 
 sub new {
     my $ref = Apache::lonhelper::element->new();
+    $ref->{'PROCEED'} = 1;      # By default postprocess goes to next state.
     bless($ref);
 }
 
@@ -3223,20 +3337,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 = '';
@@ -3245,7 +3372,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'} . '"';
@@ -3280,7 +3407,7 @@ sub postprocess {
 	}
     }
 
-    if (defined($self->{'nextstate'})) {
+    if (defined($self->{'nextstate'}) && $self->{PROCEED}) {
         $helper->changeState($self->{'nextstate'});
     }
 
@@ -3560,10 +3687,8 @@ sub render {
     }
     my $previous = HTML::Entities::encode(&mt("Back"), '<>&"');
     my $next = HTML::Entities::encode(&mt("Next"), '<>&"');
-    my $target = " target='loncapaclient'";
-    if ($env{'environment.remote'} eq 'off') {  $target='';  }
     $result .= "<p>\n" .
-	"<form action='".$actionURL."' method='post' $target>\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" .