Diff for /loncom/interface/lonhelper.pm between versions 1.6 and 1.15

version 1.6, 2003/04/11 17:21:18 version 1.15, 2003/04/30 19:08:15
Line 30 Line 30
 # (.helper handler  # (.helper handler
 #  #
   
 # FIXME: Change register calls to register with the helper.  
 # Then have the helper reg and unreg the tags.  
 # This removes my concerns about breaking other code.  
   
 =pod  =pod
   
 =head1 lonhelper - HTML Helper framework for LON-CAPA  =head1 lonhelper - HTML Helper framework for LON-CAPA
Line 62  of the helper itself, such as "Parameter Line 58  of the helper itself, such as "Parameter
 =head2 State tags  =head2 State tags
   
 State tags are required to have an attribute "name", which is the symbolic  State tags are required to have an attribute "name", which is the symbolic
 name of the state and will not be directly seen by the user. The wizard is  name of the state and will not be directly seen by the user. The helper is
 required to have one state named "START", which is the state the wizard  required to have one state named "START", which is the state the helper
 will start with. By convention, this state should clearly describe what  will start with. By convention, this state should clearly describe what
 the helper will do for the user, and may also include the first information  the helper will do for the user, and may also include the first information
 entry the user needs to do for the helper.  entry the user needs to do for the helper.
Line 88  Of course this does nothing. In order fo Line 84  Of course this does nothing. In order fo
 necessary to put actual elements into the wizard. Documentation for each  necessary to put actual elements into the wizard. Documentation for each
 of these elements follows.  of these elements follows.
   
   =head2 Creating a Helper With Code, Not XML
   
   In some situations, such as the printing wizard (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.
   
   Creating a helper via code is more like issuing commands to create
   a helper then normal code writing. For instance, elements will automatically
   be added to the last state created, so it's important to create the 
   states in the correct order.
   
   First, create a new helper:
   
    use Apache::lonhelper;
   
    my $helper = Apache::lonhelper::new->("Helper Title");
   
   Next you'll need to manually add states to the helper:
   
    Apache::lonhelper::state->new("STATE_NAME", "State's Human Title");
   
   You don't need to save a reference to it because all elements up until
   the next state creation will automatically be added to this state.
   
   Elements are created by populating the $paramHash in 
   Apache::lonhelper::paramhash. To prevent namespace issues, retrieve 
   a reference to that has with getParamHash:
   
    my $paramHash = Apache::lonhelper::getParamHash();
   
   You will need to do this for each state you create.
   
   Populate the $paramHash with the parameters for the element you wish
   to add next; the easiest way to find out what those entries are is
   to read the code. Some common ones are 'variable' to record the variable
   to store the results in, and NEXTSTATE to record a next state transition.
   
   Then create your element:
   
    $paramHash->{MESSAGETEXT} = "This is a message.";
    Apache::lonhelper::message->new();
   
   The creation will take the $paramHash and bless it into a
   Apache::lonhelper::message object. To create the next element, you need
   to get a reference to the new, empty $paramHash:
   
    $paramHash = Apache::lonhelper::getParamHash();
   
   and you can repeat creating elements that way. You can add states
   and elements as needed.
   
   See lonprintout.pm, subroutine printHelper for an example of this, where
   we dynamically add some states to prevent security problems, for instance.
   
   Normally the machinery in the XML format is sufficient; dynamically 
   adding states can easily be done by wrapping the state in a <condition>
   tag. This should only be used when the code dominates the XML content,
   the code is so complicated that it is difficult to get access to
   all of the information you need because of scoping issues, or so much
   of the information used is persistent because would-be <exec> or 
   <eval> blocks that using the {DATA} mechanism results in hard-to-read
   and -maintain code.
   
   It is possible to do some of the work with an XML fragment parsed by
   lonxml; again, see lonprintout.pm for an example.
   
 =cut  =cut
   
 package Apache::lonhelper;  package Apache::lonhelper;
Line 95  use Apache::Constants qw(:common); Line 158  use Apache::Constants qw(:common);
 use Apache::File;  use Apache::File;
 use Apache::lonxml;  use Apache::lonxml;
   
   # Register all the tags with the helper, so the helper can 
   # push and pop them
   
   my @helperTags;
   
   sub register {
       my ($namespace, @tags) = @_;
   
       for my $tag (@tags) {
           push @helperTags, [$namespace, $tag];
       }
   }
   
 BEGIN {  BEGIN {
     &Apache::lonxml::register('Apache::lonhelper',       Apache::lonxml::register('Apache::lonhelper', 
                               ('helper', 'state'));                               ('helper'));
         register('Apache::lonhelper', ('state'));
 }  }
   
 # Since all wizards are only three levels deep (wizard tag, state tag,   # Since all helpers are only three levels deep (helper tag, state tag, 
 # substate type), it's easier and more readble to explicitly track   # substate type), it's easier and more readble to explicitly track 
 # those three things directly, rather then futz with the tag stack   # those three things directly, rather then futz with the tag stack 
 # every time.  # every time.
Line 112  my $substate; Line 189  my $substate;
 # end of the element tag is located.  # end of the element tag is located.
 my $paramHash;   my $paramHash; 
   
   # For debugging purposes, one can send a second parameter into this
   # function, the 'uri' of the helper you wish to have rendered, and
   # call this from other handlers.
 sub handler {  sub handler {
     my $r = shift;      my $r = shift;
     $ENV{'request.uri'} = $r->uri();      my $uri = shift;
     my $filename = '/home/httpd/html' . $r->uri();      if (!defined($uri)) { $uri = $r->uri(); }
       $ENV{'request.uri'} = $uri;
       my $filename = '/home/httpd/html' . $uri;
     my $fh = Apache::File->new($filename);      my $fh = Apache::File->new($filename);
     my $file;      my $file;
     read $fh, $file, 100000000;      read $fh, $file, 100000000;
   
     Apache::loncommon::get_unprocessed_cgi($ENV{QUERY_STRING});  
   
     # Send header, don't cache this page      # Send header, don't cache this page
     if ($r->header_only) {      if ($r->header_only) {
         if ($ENV{'browser.mathml'}) {          if ($ENV{'browser.mathml'}) {
Line 144  sub handler { Line 224  sub handler {
     # xml parsing      # xml parsing
     &Apache::lonxml::xmlparse($r, 'helper', $file);      &Apache::lonxml::xmlparse($r, 'helper', $file);
   
       $helper->process();
   
     $r->print($helper->display());      $r->print($helper->display());
     return OK;     return OK;
   }
   
   sub registerHelperTags {
       for my $tagList (@helperTags) {
           Apache::lonxml::register($tagList->[0], $tagList->[1]);
       }
   }
   
   sub unregisterHelperTags {
       for my $tagList (@helperTags) {
           Apache::lonxml::deregister($tagList->[0], $tagList->[1]);
       }
 }  }
   
 sub start_helper {  sub start_helper {
Line 154  sub start_helper { Line 248  sub start_helper {
     if ($target ne 'helper') {      if ($target ne 'helper') {
         return '';          return '';
     }      }
       
     $helper = Apache::lonhelper::helper->new($token->[2]{'title'});      registerHelperTags();
   
       Apache::lonhelper::helper->new($token->[2]{'title'});
     return '';      return '';
 }  }
   
Line 165  sub end_helper { Line 261  sub end_helper {
     if ($target ne 'helper') {      if ($target ne 'helper') {
         return '';          return '';
     }      }
       
       unregisterHelperTags();
   
     return '';      return '';
 }  }
   
Line 176  sub start_state { Line 274  sub start_state {
         return '';          return '';
     }      }
   
     $state = Apache::lonhelper::state->new($token->[2]{'name'},      Apache::lonhelper::state->new($token->[2]{'name'},
                                            $token->[2]{'title'});                                    $token->[2]{'title'});
     return '';      return '';
 }  }
   
   # Use this to get the param hash from other files.
   sub getParamHash {
       return $paramHash;
   }
   
   # Use this to get the helper, if implementing elements in other files
   # (like lonprintout.pm)
   sub getHelper {
       return $helper;
   }
   
 # don't need this, so ignore it  # don't need this, so ignore it
 sub end_state {  sub end_state {
     return '';      return '';
Line 213  sub new { Line 322  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 265  sub new { Line 376  sub new {
     $self->{STATES} = {};      $self->{STATES} = {};
     $self->{DONE} = 0;      $self->{DONE} = 0;
   
       # Used by various helpers for various things; see lonparm.helper
       # for an example.
       $self->{DATA} = {};
   
       $helper = $self;
   
       # Establish the $paramHash
       $paramHash = {};
   
     bless($self, $class);      bless($self, $class);
     return $self;      return $self;
 }  }
Line 325  sub registerState { Line 445  sub registerState {
     $self->{STATES}{$stateName} = $state;      $self->{STATES}{$stateName} = $state;
 }  }
   
 # Done in four phases  sub process {
 # 1: Do the post processing for the previous state.  
 # 2: Do the preprocessing for the current state.  
 # 3: Check to see if state changed, if so, postprocess current and move to next.  
 #    Repeat until state stays stable.  
 # 4: Render the current state to the screen as an HTML page.  
 sub display {  
     my $self = shift;      my $self = shift;
   
     my $result = "";  
   
     # Phase 1: Post processing for state of previous screen (which is actually      # 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       # the "current state" in terms of the helper variables), if it wasn't the 
     # beginning state.      # beginning state.
     if ($self->{STATE} ne "START" || $ENV{"form.SUBMIT"} eq "Next ->") {      if ($self->{STATE} ne "START" || $ENV{"form.SUBMIT"} eq "Next ->") {
  my $prevState = $self->{STATES}{$self->{STATE}};   my $prevState = $self->{STATES}{$self->{STATE}};
             $prevState->postprocess();          $prevState->postprocess();
     }      }
           
     # Note, to handle errors in a state's input that a user must correct,      # Note, to handle errors in a state's input that a user must correct,
Line 352  sub display { Line 464  sub display {
     my $startState = $self->{STATE};      my $startState = $self->{STATE};
     my $state = $self->{STATES}{$startState};      my $state = $self->{STATES}{$startState};
           
     # Error checking; it is intended that the developer will have      # For debugging, print something here to determine if you're going
     # checked all paths and the user can't see this!      # to an undefined state.
     if (!defined($state)) {      if (!defined($state)) {
         $result .="Error! The state ". $startState ." is not defined.";          return;
         return $result;  
     }      }
     $state->preprocess();      $state->preprocess();
   
Line 369  sub display { Line 480  sub display {
  $state->preprocess();   $state->preprocess();
     }      }
   
       return;
   } 
   
   # 1: Do the post processing for the previous state.
   # 2: Do the preprocessing for the current state.
   # 3: Check to see if state changed, if so, postprocess current and move to next.
   #    Repeat until state stays stable.
   # 4: Render the current state to the screen as an HTML page.
   sub display {
       my $self = shift;
   
       my $state = $self->{STATES}{$self->{STATE}};
   
       my $result = "";
   
     # Phase 4: Display.      # Phase 4: Display.
     my $stateTitle = $state->title();      my $stateTitle = $state->title();
     my $bodytag = &Apache::loncommon::bodytag("$self->{TITLE}",'','');      my $bodytag = &Apache::loncommon::bodytag("$self->{TITLE}",'','');
Line 380  sub display { Line 506  sub display {
     </head>      </head>
     $bodytag      $bodytag
 HEADER  HEADER
     if (!$state->overrideForm()) { $result.="<form name='wizform' method='GET'>"; }      if (!$state->overrideForm()) { $result.="<form name='helpform' method='GET'>"; }
     $result .= <<HEADER;      $result .= <<HEADER;
         <table border="0"><tr><td>          <table border="0"><tr><td>
         <h2><i>$stateTitle</i></h2>          <h2><i>$stateTitle</i></h2>
Line 408  HEADER Line 534  HEADER
         $result .= "</center>\n";          $result .= "</center>\n";
     }      }
   
     foreach my $key (keys %{$self->{VARS}}) {      #foreach my $key (keys %{$self->{VARS}}) {
         $result .= "|$key| -> " . $self->{VARS}->{$key} . "<br />";      #    $result .= "|$key| -> " . $self->{VARS}->{$key} . "<br />";
     }      #}
   
     $result .= <<FOOTER;      $result .= <<FOOTER;
               </td>                </td>
Line 452  sub new { Line 578  sub new {
   
     $helper->registerState($self);      $helper->registerState($self);
   
       $state = $self;
   
     return $self;      return $self;
 }  }
   
Line 495  sub postprocess { Line 623  sub postprocess {
     }      }
 }  }
   
   # Override the form if any element wants to.
   # two elements overriding the form will make a mess, but that should
   # be considered helper author error ;-)
 sub overrideForm {  sub overrideForm {
       my $self = shift;
       for my $element (@{$self->{ELEMENTS}}) {
           if ($element->overrideForm()) {
               return 1;
           }
       }
     return 0;      return 0;
 }  }
   
Line 546  the helper variables, like this: Line 683  the helper variables, like this:
 =cut  =cut
   
 BEGIN {  BEGIN {
     &Apache::lonxml::register('Apache::lonhelper::element',      &Apache::lonhelper::register('Apache::lonhelper::element',
                               ('nextstate'));                                   ('nextstate'));
 }  }
   
 # Because we use the param hash, this is often a sufficent  # Because we use the param hash, this is often a sufficent
Line 594  sub render { Line 731  sub render {
     return '';      return '';
 }  }
   
   sub overrideForm {
       return 0;
   }
   
 sub process_multiple_choices {  sub process_multiple_choices {
     my $self = shift;      my $self = shift;
     my $formname = shift;      my $formname = shift;
     my $var = shift;      my $var = shift;
   
     my $formvalue = $ENV{'form.' . $formname};      # Must extract values from data directly, as there
     if ($formvalue) {      # may be more then one.
         # Must extract values from querystring directly, as there      my @values;
         # may be more then one.      for my $formparam (split (/&/, $ENV{QUERY_STRING})) {
         my @values;          my ($name, $value) = split(/=/, $formparam);
         for my $formparam (split (/&/, $ENV{QUERY_STRING})) {          if ($name ne $formname) {
             my ($name, $value) = split(/=/, $formparam);              next;
             if ($name ne $formname) {  
                 next;  
             }  
             $value =~ tr/+/ /;  
             $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;  
             push @values, $value;  
         }          }
         $helper->{VARS}->{$var} = join('|||', @values);          $value =~ tr/+/ /;
           $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
           push @values, $value;
     }      }
       $helper->{VARS}->{$var} = join('|||', @values);
           
     return;      return;
 }  }
Line 637  transition directly to the state in the Line 775  transition directly to the state in the
    </message>     </message>
   
 This will display the HTML message and transition to the <nextstate> if  This will display the HTML message and transition to the <nextstate> if
 given. The HTML will be directly inserted into the wizard, so if you don't  given. The HTML will be directly inserted into the helper, so if you don't
 want text to run together, you'll need to manually wrap the <message_text>  want text to run together, you'll need to manually wrap the <message_text>
 in <p> tags, or whatever is appropriate for your HTML.  in <p> tags, or whatever is appropriate for your HTML.
   
Line 657  no strict; Line 795  no strict;
 use strict;  use strict;
   
 BEGIN {  BEGIN {
     &Apache::lonxml::register('Apache::lonhelper::message',      &Apache::lonhelper::register('Apache::lonhelper::message',
                               ('message', 'message_text'));                                ('message'));
 }  }
   
 sub new {  sub new {
Line 668  sub new { Line 806  sub new {
   
 # CONSTRUCTION: Construct the message element from the XML  # CONSTRUCTION: Construct the message element from the XML
 sub start_message {  sub start_message {
     return '';  
 }  
   
 sub end_message {  
     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;      my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
   
     if ($target ne 'helper') {      if ($target ne 'helper') {
         return '';          return '';
     }      }
     Apache::lonhelper::message->new();  
       $paramHash->{MESSAGE_TEXT} = &Apache::lonxml::get_all_text('/message',
                                                                  $parser);
   
       if (defined($token->[2]{'nextstate'})) {
           $paramHash->{NEXTSTATE} = $token->[2]{'nextstate'};
       }
     return '';      return '';
 }  }
   
 sub start_message_text {  sub end_message {
     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;      my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
   
     if ($target ne 'helper') {      if ($target ne 'helper') {
         return '';          return '';
     }      }
       Apache::lonhelper::message->new();
     $paramHash->{MESSAGE_TEXT} = &Apache::lonxml::get_all_text('/message_text',      return '';
                                                                $parser);  
 }  }
       
 sub end_message_text { return 1; }  
   
 sub render {  sub render {
     my $self = shift;      my $self = shift;
Line 745  B<SUB-TAGS> Line 882  B<SUB-TAGS>
       For example,          For example,  
       <choice computer='234-12-7312'>Bobby McDormik</choice>.        <choice computer='234-12-7312'>Bobby McDormik</choice>.
   
         <choice> can take a parameter "eval", which if set to
         a true value, will cause the contents of the tag to be
         evaluated as it would be in an <eval> tag; see <eval> tag
         below.
   
 <choice> may optionally contain a 'nextstate' attribute, which  <choice> may optionally contain a 'nextstate' attribute, which
 will be the state transisitoned to if the choice is made, if  will be the state transisitoned to if the choice is made, if
 the choice is not multichoice.  the choice is not multichoice.
Line 772  You can mix and match methods of creatin Line 914  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.)
   
 FIXME: Document and implement <exec> and <condition> in the element package.  
   
 =cut  =cut
   
 no strict;  no strict;
Line 781  no strict; Line 921  no strict;
 use strict;  use strict;
   
 BEGIN {  BEGIN {
     &Apache::lonxml::register('Apache::lonhelper::choices',      &Apache::lonhelper::register('Apache::lonhelper::choices',
                               ('choice', 'choices'));                                ('choice', 'choices'));
 }  }
   
Line 827  sub start_choice { Line 967  sub start_choice {
     my $human = &Apache::lonxml::get_all_text('/choice',      my $human = &Apache::lonxml::get_all_text('/choice',
                                               $parser);                                                $parser);
     my $nextstate = $token->[2]{'nextstate'};      my $nextstate = $token->[2]{'nextstate'};
     push @{$paramHash->{CHOICES}}, [$human, $computer, $nextstate];      my $evalFlag = $token->[2]{'eval'};
       push @{$paramHash->{CHOICES}}, [$human, $computer, $nextstate, 
                                       $evalFlag];
     return '';      return '';
 }  }
   
Line 846  sub render { Line 988  sub render {
         $result .= <<SCRIPT;          $result .= <<SCRIPT;
 <script>  <script>
     function checkall(value) {      function checkall(value) {
  for (i=0; i<document.forms.wizform.elements.length; i++) {   for (i=0; i<document.forms.helpform.elements.length; i++) {
             document.forms.wizform.elements[i].checked=value;              document.forms.helpform.elements[i].checked=value;
         }          }
     }      }
 </script>  </script>
Line 860  SCRIPT Line 1002  SCRIPT
 BUTTONS  BUTTONS
     }      }
   
     if (defined $self->{ERROR_MSG}) {      if (defined $self->{ERRO_MSG}) {
         $result .= '<br /><font color="#FF0000">' . $self->{ERROR_MSG} . '</font><br />';          $result .= '<br /><font color="#FF0000">' . $self->{ERROR_MSG} . '</font><br />';
     }      }
   
Line 881  BUTTONS Line 1023  BUTTONS
             $result .= " checked ";              $result .= " checked ";
             $checked = 1;              $checked = 1;
         }          }
         $result .= "/></td><td> " . $choice->[0] . "</td></tr>\n";          my $choiceLabel = $choice->[0];
           if ($choice->[4]) {  # 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> " . $choiceLabel . "</td></tr>\n";
     }      }
     $result .= "</table>\n\n\n";      $result .= "</table>\n\n\n";
     $result .= $buttons;      $result .= $buttons;
Line 960  use strict; Line 1109  use strict;
 use Time::localtime;  use Time::localtime;
   
 BEGIN {  BEGIN {
     &Apache::lonxml::register('Apache::lonhelper::date',      &Apache::lonhelper::register('Apache::lonhelper::date',
                               ('date'));                                ('date'));
 }  }
   
Line 1173  B<SUB-TAGS> Line 1322  B<SUB-TAGS>
   "}" returns a string representing what you want to have as the value. By    "}" returns a string representing what you want to have as the value. By
   default, the value will be the resource ID of the object ($res->{ID}).    default, the value will be the resource ID of the object ($res->{ID}).
   
   =item * <mapurl>: If the URL of a map is given here, only that map
     will be displayed, instead of the whole course.
   
 =back  =back
   
 =cut  =cut
Line 1182  no strict; Line 1334  no strict;
 use strict;  use strict;
   
 BEGIN {  BEGIN {
     &Apache::lonxml::register('Apache::lonhelper::resource',      &Apache::lonhelper::register('Apache::lonhelper::resource',
                               ('resource', 'filterfunc',                                 ('resource', 'filterfunc', 
                                'choicefunc', 'valuefunc'));                                 'choicefunc', 'valuefunc',
                                  'mapurl'));
 }  }
   
 sub new {  sub new {
Line 1202  sub start_resource { Line 1355  sub start_resource {
   
     $paramHash->{'variable'} = $token->[2]{'variable'};      $paramHash->{'variable'} = $token->[2]{'variable'};
     $helper->declareVar($paramHash->{'variable'});      $helper->declareVar($paramHash->{'variable'});
       $paramHash->{'multichoice'} = $token->[2]{'multichoice'};
     return '';      return '';
 }  }
   
Line 1269  sub start_valuefunc { Line 1423  sub start_valuefunc {
   
 sub end_valuefunc { return ''; }  sub end_valuefunc { return ''; }
   
   sub start_mapurl {
       my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
   
       if ($target ne 'helper') {
           return '';
       }
   
       my $contents = Apache::lonxml::get_all_text('/mapurl',
                                                   $parser);
       $paramHash->{MAP_URL} = $contents;
   }
   
   sub end_mapurl { return ''; }
   
 # A note, in case I don't get to this before I leave.  # A note, in case I don't get to this before I leave.
 # If someone complains about the "Back" button returning them  # If someone complains about the "Back" button returning them
 # to the previous folder state, instead of returning them to  # to the previous folder state, instead of returning them to
Line 1287  sub render { Line 1455  sub render {
     my $var = $self->{'variable'};      my $var = $self->{'variable'};
     my $curVal = $helper->{VARS}->{$var};      my $curVal = $helper->{VARS}->{$var};
   
       my $buttons = '';
   
       if ($self->{'multichoice'}) {
           $result = <<SCRIPT;
   <script>
       function checkall(value) {
    for (i=0; i<document.forms.helpform.elements.length; i++) {
               ele = document.forms.helpform.elements[i];
               if (ele.type == "checkbox") {
                   document.forms.helpform.elements[i].checked=value;
               }
           }
       }
   </script>
   SCRIPT
           $buttons = <<BUTTONS;
   <br /> &nbsp;
   <input type="button" onclick="checkall(true)" value="Select All" />
   <input type="button" onclick="checkall(false)" value="Unselect All" />
   <br /> &nbsp;
   BUTTONS
       }
   
     if (defined $self->{ERROR_MSG}) {      if (defined $self->{ERROR_MSG}) {
         $result .= '<font color="#FF0000">' . $self->{ERROR_MSG} . '</font><br /><br />';          $result .= '<br /><font color="#FF0000">' . $self->{ERROR_MSG} . '</font><br /><br />';
     }      }
   
       $result .= $buttons;
   
     my $filterFunc = $self->{FILTER_FUNC};      my $filterFunc = $self->{FILTER_FUNC};
     my $choiceFunc = $self->{CHOICE_FUNC};      my $choiceFunc = $self->{CHOICE_FUNC};
     my $valueFunc = $self->{VALUE_FUNC};      my $valueFunc = $self->{VALUE_FUNC};
       my $mapUrl = $self->{MAP_URL};
       my $multichoice = $self->{'multichoice'};
   
     # Create the composite function that renders the column on the nav map      # Create the composite function that renders the column on the nav map
     # have to admit any language that lets me do this can't be all bad      # have to admit any language that lets me do this can't be all bad
Line 1301  sub render { Line 1496  sub render {
     my $checked = 0;      my $checked = 0;
     my $renderColFunc = sub {      my $renderColFunc = sub {
         my ($resource, $part, $params) = @_;          my ($resource, $part, $params) = @_;
           
           my $inputType;
           if ($multichoice) { $inputType = 'checkbox'; }
           else {$inputType = 'radio'; }
   
         if (!&$choiceFunc($resource)) {          if (!&$choiceFunc($resource)) {
             return '<td>&nbsp;</td>';              return '<td>&nbsp;</td>';
         } else {          } else {
             my $col = "<td><input type='radio' name='${var}.forminput' ";              my $col = "<td><input type='$inputType' name='${var}.forminput' ";
             if (!$checked) {              if (!$checked && !$multichoice) {
                 $col .= "checked ";                  $col .= "checked ";
                 $checked = 1;                  $checked = 1;
             }              }
Line 1322  sub render { Line 1521  sub render {
         &Apache::lonnavmaps::render( { 'cols' => [$renderColFunc,           &Apache::lonnavmaps::render( { 'cols' => [$renderColFunc, 
                                                   Apache::lonnavmaps::resource()],                                                    Apache::lonnavmaps::resource()],
                                        'showParts' => 0,                                         'showParts' => 0,
                                        'url' => $helper->{URL},  
                                        'filterFunc' => $filterFunc,                                         'filterFunc' => $filterFunc,
                                        'resource_no_folder_link' => 1 }                                         'resource_no_folder_link' => 1,
                                          'iterator_map' => $mapUrl }
                                        );                                         );
   
       $result .= $buttons;
                                                                                                   
     return $result;      return $result;
 }  }
           
 sub postprocess {  sub postprocess {
     my $self = shift;      my $self = shift;
   
       if ($self->{'multichoice'}) {
           $self->process_multiple_choices($self->{'variable'}.'.forminput',
                                           $self->{'variable'});
       }
   
       if ($self->{'multichoice'} && !$helper->{VARS}->{$self->{'variable'}}) {
           $self->{ERROR_MSG} = 'You must choose at least one resource to continue.';
           return 0;
       }
   
     if (defined($self->{NEXTSTATE})) {      if (defined($self->{NEXTSTATE})) {
         $helper->changeState($self->{NEXTSTATE});          $helper->changeState($self->{NEXTSTATE});
     }      }
Line 1363  use strict; Line 1575  use strict;
   
   
 BEGIN {  BEGIN {
     &Apache::lonxml::register('Apache::lonhelper::student',      &Apache::lonhelper::register('Apache::lonhelper::student',
                               ('student'));                                ('student'));
 }  }
   
Line 1382  sub start_student { Line 1594  sub start_student {
     $paramHash->{'variable'} = $token->[2]{'variable'};      $paramHash->{'variable'} = $token->[2]{'variable'};
     $helper->declareVar($paramHash->{'variable'});      $helper->declareVar($paramHash->{'variable'});
     $paramHash->{'multichoice'} = $token->[2]{'multichoice'};      $paramHash->{'multichoice'} = $token->[2]{'multichoice'};
       if (defined($token->[2]{'nextstate'})) {
           $paramHash->{NEXTSTATE} = $token->[2]{'nextstate'};
       }
       
 }      }    
   
 sub end_student {  sub end_student {
Line 1402  sub render { Line 1618  sub render {
         $result = <<SCRIPT;          $result = <<SCRIPT;
 <script>  <script>
     function checkall(value) {      function checkall(value) {
  for (i=0; i<document.forms.wizform.elements.length; i++) {   for (i=0; i<document.forms.helpform.elements.length; i++) {
             document.forms.wizform.elements[i].checked=value;              document.forms.helpform.elements[i].checked=value;
         }          }
     }      }
 </script>  </script>
Line 1422  BUTTONS Line 1638  BUTTONS
   
     # Load up the students      # Load up the students
     my $choices = &Apache::loncoursedata::get_classlist();      my $choices = &Apache::loncoursedata::get_classlist();
   
     my @keys = keys %{$choices};      my @keys = keys %{$choices};
   
     # Constants      # Constants
Line 1527  no strict; Line 1742  no strict;
 use strict;  use strict;
   
 BEGIN {  BEGIN {
     &Apache::lonxml::register('Apache::lonhelper::files',      &Apache::lonhelper::register('Apache::lonhelper::files',
                               ('files', 'filechoice', 'filefilter'));                                   ('files', 'filechoice', 'filefilter'));
 }  }
   
 sub new {  sub new {
Line 1592  sub render { Line 1807  sub render {
     my $var = $self->{'variable'};      my $var = $self->{'variable'};
           
     my $subdirFunc = eval('sub {' . $self->{'filechoice'} . '}');      my $subdirFunc = eval('sub {' . $self->{'filechoice'} . '}');
       die 'Error in resource filter code for variable ' . 
           {'variable'} . ', Perl said:' . $@ if $@;
   
     my $subdir = &$subdirFunc();      my $subdir = &$subdirFunc();
   
     my $filterFunc = $self->{FILTER_FUNC};      my $filterFunc = $self->{FILTER_FUNC};
Line 1601  sub render { Line 1819  sub render {
         $result = <<SCRIPT;          $result = <<SCRIPT;
 <script>  <script>
     function checkall(value) {      function checkall(value) {
  for (i=0; i<document.forms.wizform.elements.length; i++) {   for (i=0; i<document.forms.helpform.elements.length; i++) {
             ele = document.forms.wizform.elements[i];              ele = document.forms.helpform.elements[i];
             if (ele.type == "checkbox") {              if (ele.type == "checkbox") {
                 document.forms.wizform.elements[i].checked=value;                  document.forms.helpform.elements[i].checked=value;
             }              }
         }          }
     }      }
 </script>  </script>
 SCRIPT  SCRIPT
         my $buttons = <<BUTTONS;          $buttons = <<BUTTONS;
 <br /> &nbsp;  <br /> &nbsp;
 <input type="button" onclick="checkall(true)" value="Select All" />  <input type="button" onclick="checkall(true)" value="Select All" />
 <input type="button" onclick="checkall(false)" value="Unselect All" />  <input type="button" onclick="checkall(false)" value="Unselect All" />
Line 1697  sub postprocess { Line 1915  sub postprocess {
     return 1;      return 1;
 }  }
   
   1;
   
   package Apache::lonhelper::section;
   
   =pod
   
   =head2 Element: 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.
   
   =cut
   
   no strict;
   @ISA = ("Apache::lonhelper::choices");
   use strict;
   
   BEGIN {
       &Apache::lonhelper::register('Apache::lonhelper::section',
                                    ('section'));
   }
   
   sub new {
       my $ref = Apache::lonhelper::choices->new();
       bless($ref);
   }
   
   sub start_section {
       my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
   
       if ($target ne 'helper') {
           return '';
       }
   
       $paramHash->{CHOICES} = [];
   
       $paramHash->{'variable'} = $token->[2]{'variable'};
       $helper->declareVar($paramHash->{'variable'});
       $paramHash->{'multichoice'} = $token->[2]{'multichoice'};
       if (defined($token->[2]{'nextstate'})) {
           $paramHash->{NEXTSTATE} = $token->[2]{'nextstate'};
       }
   
       # Populate the CHOICES element
       my %choices;
   
       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;
           }
       } 
      
       for my $sectionName (sort(keys(%choices))) {
           
           push @{$paramHash->{CHOICES}}, [$sectionName, $sectionName];
       }
   }    
   
   sub end_section {
       my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
   
       if ($target ne 'helper') {
           return '';
       }
       Apache::lonhelper::section->new();
   }    
   1;
   
   package Apache::lonhelper::general;
   
   =pod
   
   =head2 General-purpose tag: <exec>
   
   The contents of the exec tag are executed as Perl code, not inside a 
   safe space, so the full range of $ENV and such is available. The code
   will be executed as a subroutine wrapped with the following code:
   
   "sub { my $helper = shift; my $state = shift;" and
   
   "}"
   
   The return value is ignored.
   
   $helper is the helper object. Feel free to add methods to the helper
   object to support whatever manipulation you may need to do (for instance,
   overriding the form location if the state is the final state; see 
   lonparm.helper for an example).
   
   $state is the $paramHash that has currently been generated and may
   be manipulated by the code in exec. Note that the $state is not yet
   an actual state B<object>, it is just a hash, so do not expect to
   be able to call methods on it.
   
   =cut
   
   BEGIN {
       &Apache::lonhelper::register('Apache::lonhelper::general',
                                    'exec', 'condition', 'clause',
                                    'eval');
   }
   
   sub start_exec {
       my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
   
       if ($target ne 'helper') {
           return '';
       }
       
       my $code = &Apache::lonxml::get_all_text('/exec', $parser);
       
       $code = eval ('sub { my $helper = shift; my $state = shift; ' .
           $code . "}");
       die 'Error in <exec>, Perl said: '. $@ if $@;
       &$code($helper, $paramHash);
   }
   
   sub end_exec { return ''; }
   
   =pod
   
   =head2 General-purpose tag: <condition>
   
   The <condition> tag allows you to mask out parts of the helper code
   depending on some programatically determined condition. The condition
   tag contains a tag <clause> which contains perl code that when wrapped
   with "sub { my $helper = shift; my $state = shift; " and "}", returns
   a true value if the XML in the condition should be evaluated as a normal
   part of the helper, or false if it should be completely discarded.
   
   The <clause> tag must be the first sub-tag of the <condition> tag or
   it will not work as expected.
   
   =cut
   
   # The condition tag just functions as a marker, it doesn't have
   # to "do" anything. Technically it doesn't even have to be registered
   # with the lonxml code, but I leave this here to be explicit about it.
   sub start_condition { return ''; }
   sub end_condition { return ''; }
   
   sub start_clause {
       my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
   
       if ($target ne 'helper') {
           return '';
       }
       
       my $clause = Apache::lonxml::get_all_text('/clause', $parser);
       $clause = eval('sub { my $helper = shift; my $state = shift; '
           . $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);
       }
   }
   
   sub end_clause { return ''; }
   
   =pod
   
   =head2 General-purpose tag: <eval>
   
   The <eval> tag will be evaluated as a subroutine call passed in the
   current helper object and state hash as described in <condition> above,
   but is expected to return a string to be printed directly to the
   screen. This is useful for dynamically generating messages. 
   
   =cut
   
   # This is basically a type of message.
   # Programmatically setting $paramHash->{NEXTSTATE} would work, though
   # it's probably bad form.
   
   sub start_eval {
       my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
   
       if ($target ne 'helper') {
           return '';
       }
       
       my $program = Apache::lonxml::get_all_text('/eval', $parser);
       $program = eval('sub { my $helper = shift; my $state = shift; '
           . $program . '}');
       die 'Error in eval code, Perl said: ' . $@ if $@;
       $paramHash->{MESSAGE_TEXT} = &$program($helper, $paramHash);
   }
   
   sub end_eval { 
       my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
   
       if ($target ne 'helper') {
           return '';
       }
   
       Apache::lonhelper::message->new();
   }
   
   1;
   
   package Apache::lonhelper::parmwizfinal;
   
   # This is the final state for the parmwizard. 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 .
   
   no strict;
   @ISA = ('Apache::lonhelper::element');
   use strict;
   
   BEGIN {
       &Apache::lonhelper::register('Apache::lonhelper::parmwizfinal',
                                    ('parmwizfinal'));
   }
   
   use Time::localtime;
   
   sub new {
       my $ref = Apache::lonhelper::choices->new();
       bless ($ref);
   }
   
   sub start_parmwizfinal { return ''; }
   
   sub end_parmwizfinal {
       my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
   
       if ($target ne 'helper') {
           return '';
       }
       Apache::lonhelper::parmwizfinal->new();
   }
   
   # Renders a form that, when submitted, will form the input to lonparmset.pm
   sub render {
       my $self = shift;
       my $vars = $helper->{VARS};
   
       # FIXME: Unify my designators with the standard ones
       my %dateTypeHash = ('open_date' => "Opening Date",
                           'due_date' => "Due Date",
                           'answer_date' => "Answer Date");
       my %parmTypeHash = ('open_date' => "0_opendate",
                           'due_date' => "0_duedate",
                           'answer_date' => "0_answerdate");
       
       my $result = "<form name='helpform' method='get' action='/adm/parmset'>\n";
       $result .= '<p>Confirm that this information is correct, then click &quot;Finish Wizard&quot; to complete setting the parameter.<ul>';
       my $affectedResourceId = "";
       my $parm_name = $parmTypeHash{$vars->{ACTION_TYPE}};
       my $level = "";
       
       # Print the type of manipulation:
       $result .= '<li>Setting the <b>' . $dateTypeHash{$vars->{ACTION_TYPE}}
                  . "</b></li>\n";
       if ($vars->{ACTION_TYPE} eq 'due_date' || 
           $vars->{ACTION_TYPE} eq 'answer_date') {
           # for due dates, we default to "date end" type entries
           $result .= "<input type='hidden' name='recent_date_end' " .
               "value='" . $vars->{PARM_DATE} . "' />\n";
           $result .= "<input type='hidden' name='pres_value' " . 
               "value='" . $vars->{PARM_DATE} . "' />\n";
           $result .= "<input type='hidden' name='pres_type' " .
               "value='date_end' />\n";
       } elsif ($vars->{ACTION_TYPE} eq 'open_date') {
           $result .= "<input type='hidden' name='recent_date_start' ".
               "value='" . $vars->{PARM_DATE} . "' />\n";
           $result .= "<input type='hidden' name='pres_value' " .
               "value='" . $vars->{PARM_DATE} . "' />\n";
           $result .= "<input type='hidden' name='pres_type' " .
               "value='date_start' />\n";
       } 
       
       # Print the granularity, depending on the action
       if ($vars->{GRANULARITY} eq 'whole_course') {
           $result .= '<li>for <b>all resources in the course</b></li>';
           $level = 9; # general course, see lonparmset.pm perldoc
           $affectedResourceId = "0.0";
       } elsif ($vars->{GRANULARITY} eq 'map') {
           my $navmap = Apache::lonnavmaps::navmap->new(
                              $ENV{"request.course.fn"}.".db",
                              $ENV{"request.course.fn"}."_parms.db", 0, 0);
           my $res = $navmap->getById($vars->{RESOURCE_ID});
           my $title = $res->compTitle();
           $navmap->untieHashes();
           $result .= "<li>for the map named <b>$title</b></li>";
           $level = 8;
           $affectedResourceId = $vars->{RESOURCE_ID};
       } else {
           my $navmap = Apache::lonnavmaps::navmap->new(
                              $ENV{"request.course.fn"}.".db",
                              $ENV{"request.course.fn"}."_parms.db", 0, 0);
           my $res = $navmap->getById($vars->{RESOURCE_ID});
           my $title = $res->compTitle();
           $navmap->untieHashes();
           $result .= "<li>for the resource named <b>$title</b></li>";
           $level = 7;
           $affectedResourceId = $vars->{RESOURCE_ID};
       }
   
       # Print targets
       if ($vars->{TARGETS} eq 'course') {
           $result .= '<li>for <b>all students in course</b></li>';
       } elsif ($vars->{TARGETS} eq 'section') {
           my $section = $vars->{SECTION_NAME};
           $result .= "<li>for section <b>$section</b></li>";
           $level -= 3;
           $result .= "<input type='hidden' name='csec' value='" .
               HTML::Entities::encode($section) . "' />\n";
       } else {
           # FIXME: This is probably wasteful! Store the name!
           my $classlist = Apache::loncoursedata::get_classlist();
           my $name = $classlist->{$vars->{USER_NAME}}->[6];
           $result .= "<li>for <b>$name</b></li>";
           $level -= 6;
           my ($uname, $udom) = split /:/, $vars->{USER_NAME};
           $result .= "<input type='hidden' name='uname' value='".
               HTML::Entities::encode($uname) . "' />\n";
           $result .= "<input type='hidden' name='udom' value='".
               HTML::Entities::encode($udom) . "' />\n";
       }
   
       # Print value
       $result .= "<li>to <b>" . ctime($vars->{PARM_DATE}) . "</b> (" .
           Apache::lonnavmaps::timeToHumanString($vars->{PARM_DATE}) 
           . ")</li>\n";
   
       # print pres_marker
       $result .= "\n<input type='hidden' name='pres_marker'" .
           " value='$affectedResourceId&$parm_name&$level' />\n";
   
       $result .= "<br /><br /><center><input type='submit' value='Finish Helper' /></center></form>\n";
   
       return $result;
   }
       
   sub overrideForm {
       return 1;
   }
   
 1;  1;
   
 __END__  __END__

Removed from v.1.6  
changed lines
  Added in v.1.15


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