Diff for /loncom/interface/lonhelper.pm between versions 1.24 and 1.25

version 1.24, 2003/05/08 19:17:31 version 1.25, 2003/05/08 19:52:43
Line 193  my $substate; Line 193  my $substate;
 # end of the element tag is located.  # end of the element tag is located.
 my $paramHash;   my $paramHash; 
   
   # Note from Jeremy 5-8-2003: It is *vital* that the real handler be called
   # as a subroutine from the handler, or very mysterious things might happen.
   # I don't know exactly why, but it seems that the scope where the Apache
   # server enters the perl handler is treated differently from the rest of
   # the handler. This also seems to manifest itself in the debugger as entering
   # the perl handler in seemingly random places (sometimes it starts in the
   # compiling phase, sometimes in the handler execution phase where it runs
   # the code and stepping into the "1;" the module ends with goes into the handler,
   # sometimes starting directly with the handler); I think the cause is related.
   # In the debugger, this means that breakpoints are ignored until you step into
   # a function and get out of what must be a "faked up scope" in the Apache->
   # mod_perl connection. In this code, it was manifesting itself in the existence
   # of two seperate file-scoped $helper variables, one set to the value of the
   # helper in the helper constructor, and one referenced by the handler on the
   # "$helper->process()" line. The second was therefore never set, and was still
   # undefined when I tried to call process on it.
   # By pushing the "real handler" down into the "real scope", everybody except the 
   # actual handler function directly below this comment gets the same $helper and
   # everybody is happy.
   # The upshot of all of this is that for safety when a handler is  using 
   # file-scoped variables in LON-CAPA, the handler should be pushed down one 
   # call level, as I do here, to ensure that the top-level handler function does
   # not get a different file scope from the rest of the code.
   sub handler {
       my $r = shift;
       return real_handler($r);
   }
   
 # For debugging purposes, one can send a second parameter into this  # For debugging purposes, one can send a second parameter into this
 # function, the 'uri' of the helper you wish to have rendered, and  # function, the 'uri' of the helper you wish to have rendered, and
 # call this from other handlers.  # call this from other handlers.
 sub handler {  sub real_handler {
     my $r = shift;      my $r = shift;
     my $uri = shift;      my $uri = shift;
     if (!defined($uri)) { $uri = $r->uri(); }      if (!defined($uri)) { $uri = $r->uri(); }
Line 315  sub new { Line 343  sub new {
   
     $self->{TITLE} = shift;      $self->{TITLE} = shift;
           
       Apache::loncommon::get_unprocessed_cgi($ENV{QUERY_STRING});
   
     # If there is a state from the previous form, use that. If there is no      # If there is a state from the previous form, use that. If there is no
     # state, use the start state parameter.      # state, use the start state parameter.
     if (defined $ENV{"form.CURRENT_STATE"})      if (defined $ENV{"form.CURRENT_STATE"})
Line 326  sub new { Line 356  sub new {
  $self->{STATE} = "START";   $self->{STATE} = "START";
     }      }
   
     Apache::loncommon::get_unprocessed_cgi($ENV{QUERY_STRING});  
   
     $self->{TOKEN} = $ENV{'form.TOKEN'};      $self->{TOKEN} = $ENV{'form.TOKEN'};
     # If a token was passed, we load that in. Otherwise, we need to create a       # If a token was passed, we load that in. Otherwise, we need to create a 
     # new storage file      # new storage file
Line 690  the helper variables, like this: Line 718  the helper variables, like this:
   
 =back  =back
   
   B<finalcode tag>
   
   Each element can contain a "finalcode" tag that, when the special FINAL
   helper state is used, will be executed, surrounded by "sub { my $helper = shift;"
   and "}". It is expected to return a string describing what it did, which 
   may be an empty string. See course initialization helper for an example. This is
   generally intended for helpers like the course initialization helper, which consist
   of several panels, each of which is performing some sort of bite-sized functionality.
   
   B<defaultvalue tag>
   
   Each element that accepts user input can contain a "defaultvalue" tag that,
   when surrounded by "sub { my $helper = shift; my $state = shift; " and "}",
   will form a subroutine that when called will provide a default value for
   the element. How this value is interpreted by the element is specific to
   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
   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
   some setting accidentally.
   
   Again, see the course initialization helper for examples.
   
 =cut  =cut
   
 BEGIN {  BEGIN {
     &Apache::lonhelper::register('Apache::lonhelper::element',      &Apache::lonhelper::register('Apache::lonhelper::element',
                                  ('nextstate'));                                   ('nextstate', 'finalcode',
                                     'defaultvalue'));
 }  }
   
 # Because we use the param hash, this is often a sufficent  # Because we use the param hash, this is often a sufficent
Line 729  sub start_nextstate { Line 784  sub start_nextstate {
   
 sub end_nextstate { return ''; }  sub end_nextstate { return ''; }
   
   sub start_finalcode {
       my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
   
       if ($target ne 'helper') {
           return '';
       }
       
       $paramHash->{FINAL_CODE} = &Apache::lonxml::get_all_text('/finalcode',
                                                                $parser);
       return '';
   }
   
   sub end_finalcode { return ''; }
   
   sub start_defaultvalue {
       my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
   
       if ($target ne 'helper') {
           return '';
       }
       
       $paramHash->{DEFAULT_VALUE} = &Apache::lonxml::get_all_text('/defaultvalue',
                                                                $parser);
       $paramHash->{DEFAULT_VALUE} = 'sub { my $helper = shift; my $state = shift;' .
           $paramHash->{DEFAULT_VALUE} . '}';
       return '';
   }
   
   sub end_defaultvalue { return ''; }
   
 sub preprocess {  sub preprocess {
     return 1;      return 1;
 }  }
Line 924  You can mix and match methods of creatin Line 1009  You can mix and match methods of creatin
 "push" onto the choice list, rather then wiping it out. (You can even   "push" onto the choice list, rather then wiping it out. (You can even 
 remove choices programmatically, but that would probably be bad form.)  remove choices programmatically, but that would probably be bad form.)
   
   B<defaultvalue support>
   
   Choices supports default values both in multichoice and single choice mode.
   In single choice mode, have the defaultvalue tag's function return the 
   computer value of the box you want checked. If the function returns a value
   that does not correspond to any of the choices, the default behavior of selecting
   the first choice will be preserved.
   
   For multichoice, return a string with the computer values you want checked,
   delimited by triple pipes. Note this matches how the result of the <choices>
   tag is stored in the {VARS} hash.
   
 =cut  =cut
   
 no strict;  no strict;
Line 1007  sub render { Line 1104  sub render {
     }      }
 </script>  </script>
 SCRIPT  SCRIPT
       }
   
       # Only print "select all" and "unselect all" if there are five or
       # more choices; fewer then that and it looks silly.
       if ($self->{'multichoice'} && scalar(@{$self->{CHOICES}}) > 4) {
         $buttons = <<BUTTONS;          $buttons = <<BUTTONS;
 <br />  <br />
 <input type="button" onclick="checkall(true, '$var')" value="Select All" />  <input type="button" onclick="checkall(true, '$var')" value="Select All" />
Line 1023  BUTTONS Line 1125  BUTTONS
           
     $result .= "<table>\n\n";      $result .= "<table>\n\n";
   
       my %checkedChoices;
       my $checkedChoicesFunc;
   
       if (defined($self->{DEFAULT_VALUE})) {
           $checkedChoicesFunc = eval ($self->{DEFAULT_VALUE});
           die 'Error in default value code for variable ' . 
               {'variable'} . ', Perl said:' . $@ if $@;
       } else {
           $checkedChoicesFunc = sub { return ''; };
       }
   
       # Process which choices should be checked.
       if ($self->{'multichoice'}) {
           for my $selectedChoice (split(/\|\|\|/, (&$checkedChoicesFunc($helper, $self)))) {
               $checkedChoices{$selectedChoice} = 1;
           }
       } else {
           # single choice
           my $selectedChoice = &$checkedChoicesFunc($helper, $self);
           
           my $foundChoice = 0;
           
           # check that the choice is in the list of choices.
           for my $choice (@{$self->{CHOICES}}) {
               if ($choice->[1] eq $selectedChoice) {
                   $checkedChoices{$choice->[1]} = 1;
                   $foundChoice = 1;
               }
           }
           
           # If we couldn't find the choice, pick the first one 
           if (!$foundChoice) {
               $checkedChoices{$self->{CHOICES}->[0]->[1]} = 1;
           }
       }
   
     my $type = "radio";      my $type = "radio";
     if ($self->{'multichoice'}) { $type = 'checkbox'; }      if ($self->{'multichoice'}) { $type = 'checkbox'; }
     my $checked = 0;  
     foreach my $choice (@{$self->{CHOICES}}) {      foreach my $choice (@{$self->{CHOICES}}) {
         $result .= "<tr>\n<td width='20'>&nbsp;</td>\n";          $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='" .               . "' value='" . 
             HTML::Entities::encode($choice->[1])               HTML::Entities::encode($choice->[1]) 
             . "'";              . "'";
         if (!$self->{'multichoice'} && !$checked) {          if ($checkedChoices{$choice->[1]}) {
             $result .= " checked ";              $result .= " checked ";
             $checked = 1;  
         }          }
         my $choiceLabel = $choice->[0];          my $choiceLabel = $choice->[0];
         if ($choice->[4]) {  # if we need to evaluate this choice          if ($choice->[4]) {  # if we need to evaluate this choice
Line 1057  sub postprocess { Line 1193  sub postprocess {
     my $self = shift;      my $self = shift;
     my $chosenValue = $ENV{'form.' . $self->{'variable'} . '.forminput'};      my $chosenValue = $ENV{'form.' . $self->{'variable'} . '.forminput'};
   
     if (!$chosenValue) {      if (!defined($chosenValue)) {
         $self->{ERROR_MSG} = "You must choose one or more choices to" .          $self->{ERROR_MSG} = "You must choose one or more choices to" .
             " continue.";              " continue.";
         return 0;          return 0;

Removed from v.1.24  
changed lines
  Added in v.1.25


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