Annotation of loncom/interface/lonhelper.pm, revision 1.130

1.1       bowersj2    1: # The LearningOnline Network with CAPA
                      2: # .helper XML handler to implement the LON-CAPA helper
                      3: #
1.130   ! albertel    4: # $Id: lonhelper.pm,v 1.129 2006/01/17 18:39:50 albertel Exp $
1.1       bowersj2    5: #
                      6: # Copyright Michigan State University Board of Trustees
                      7: #
                      8: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
                      9: #
                     10: # LON-CAPA is free software; you can redistribute it and/or modify
                     11: # it under the terms of the GNU General Public License as published by
                     12: # the Free Software Foundation; either version 2 of the License, or
                     13: # (at your option) any later version.
                     14: #
                     15: # LON-CAPA is distributed in the hope that it will be useful,
                     16: # but WITHOUT ANY WARRANTY; without even the implied warranty of
                     17: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                     18: # GNU General Public License for more details.
                     19: #
                     20: # You should have received a copy of the GNU General Public License
                     21: # along with LON-CAPA; if not, write to the Free Software
                     22: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
                     23: #
                     24: # /home/httpd/html/adm/gpl.txt
                     25: #
                     26: # http://www.lon-capa.org/
                     27: #
                     28: 
1.3       bowersj2   29: =pod
                     30: 
1.44      bowersj2   31: =head1 NAME
1.3       bowersj2   32: 
1.44      bowersj2   33: lonhelper - implements helper framework
                     34: 
                     35: =head1 SYNOPSIS
                     36: 
                     37: lonhelper implements the helper framework for LON-CAPA, and provides
                     38:     many generally useful components for that framework.
                     39: 
                     40: Helpers are little programs which present the user with a sequence of
                     41:     simple choices, instead of one monolithic multi-dimensional
                     42:     choice. They are also referred to as "wizards", "druids", and
                     43:     other potentially trademarked or semantically-loaded words.
                     44: 
                     45: =head1 OVERVIEWX<lonhelper>
                     46: 
                     47: Helpers are well-established UI widgets that users
1.3       bowersj2   48: feel comfortable with. It can take a complicated multidimensional problem the
                     49: user has and turn it into a series of bite-sized one-dimensional questions.
                     50: 
                     51: For developers, helpers provide an easy way to bundle little bits of functionality
                     52: for the user, without having to write the tedious state-maintenence code.
                     53: 
                     54: Helpers are defined as XML documents, placed in the /home/httpd/html/adm/helpers 
                     55: directory and having the .helper file extension. For examples, see that directory.
                     56: 
                     57: All classes are in the Apache::lonhelper namespace.
                     58: 
1.44      bowersj2   59: =head1 lonhelper XML file formatX<lonhelper, XML file format>
1.3       bowersj2   60: 
                     61: A helper consists of a top-level <helper> tag which contains a series of states.
                     62: Each state contains one or more state elements, which are what the user sees, like
                     63: messages, resource selections, or date queries.
                     64: 
                     65: The helper tag is required to have one attribute, "title", which is the name
1.31      bowersj2   66: of the helper itself, such as "Parameter helper". The helper tag may optionally
                     67: have a "requiredpriv" attribute, specifying the priviledge a user must have
                     68: to use the helper, or get denied access. See loncom/auth/rolesplain.tab for
                     69: useful privs. Default is full access, which is often wrong!
1.3       bowersj2   70: 
                     71: =head2 State tags
                     72: 
                     73: State tags are required to have an attribute "name", which is the symbolic
1.7       bowersj2   74: name of the state and will not be directly seen by the user. The helper is
                     75: required to have one state named "START", which is the state the helper
1.5       bowersj2   76: will start with. By convention, this state should clearly describe what
1.3       bowersj2   77: the helper will do for the user, and may also include the first information
                     78: entry the user needs to do for the helper.
                     79: 
                     80: State tags are also required to have an attribute "title", which is the
                     81: human name of the state, and will be displayed as the header on top of 
                     82: the screen for the user.
                     83: 
                     84: =head2 Example Helper Skeleton
                     85: 
                     86: An example of the tags so far:
                     87: 
                     88:  <helper title="Example Helper">
                     89:    <state name="START" title="Demonstrating the Example Helper">
                     90:      <!-- notice this is the START state the wizard requires -->
                     91:      </state>
                     92:    <state name="GET_NAME" title="Enter Student Name">
                     93:      </state>
                     94:    </helper>
                     95: 
                     96: Of course this does nothing. In order for the wizard to do something, it is
                     97: necessary to put actual elements into the wizard. Documentation for each
                     98: of these elements follows.
                     99: 
1.44      bowersj2  100: =head1 Creating a Helper With Code, Not XML
1.13      bowersj2  101: 
                    102: In some situations, such as the printing wizard (see lonprintout.pm), 
                    103: writing the helper in XML would be too complicated, because of scope 
                    104: issues or the fact that the code actually outweighs the XML. It is
                    105: possible to create a helper via code, though it is a little odd.
                    106: 
                    107: Creating a helper via code is more like issuing commands to create
                    108: a helper then normal code writing. For instance, elements will automatically
                    109: be added to the last state created, so it's important to create the 
                    110: states in the correct order.
                    111: 
                    112: First, create a new helper:
                    113: 
                    114:  use Apache::lonhelper;
                    115: 
                    116:  my $helper = Apache::lonhelper::new->("Helper Title");
                    117: 
                    118: Next you'll need to manually add states to the helper:
                    119: 
                    120:  Apache::lonhelper::state->new("STATE_NAME", "State's Human Title");
                    121: 
                    122: You don't need to save a reference to it because all elements up until
                    123: the next state creation will automatically be added to this state.
                    124: 
                    125: Elements are created by populating the $paramHash in 
                    126: Apache::lonhelper::paramhash. To prevent namespace issues, retrieve 
                    127: a reference to that has with getParamHash:
                    128: 
                    129:  my $paramHash = Apache::lonhelper::getParamHash();
                    130: 
                    131: You will need to do this for each state you create.
                    132: 
                    133: Populate the $paramHash with the parameters for the element you wish
                    134: to add next; the easiest way to find out what those entries are is
                    135: to read the code. Some common ones are 'variable' to record the variable
                    136: to store the results in, and NEXTSTATE to record a next state transition.
                    137: 
                    138: Then create your element:
                    139: 
                    140:  $paramHash->{MESSAGETEXT} = "This is a message.";
                    141:  Apache::lonhelper::message->new();
                    142: 
                    143: The creation will take the $paramHash and bless it into a
                    144: Apache::lonhelper::message object. To create the next element, you need
                    145: to get a reference to the new, empty $paramHash:
                    146: 
                    147:  $paramHash = Apache::lonhelper::getParamHash();
                    148: 
                    149: and you can repeat creating elements that way. You can add states
                    150: and elements as needed.
                    151: 
                    152: See lonprintout.pm, subroutine printHelper for an example of this, where
                    153: we dynamically add some states to prevent security problems, for instance.
                    154: 
                    155: Normally the machinery in the XML format is sufficient; dynamically 
                    156: adding states can easily be done by wrapping the state in a <condition>
                    157: tag. This should only be used when the code dominates the XML content,
                    158: the code is so complicated that it is difficult to get access to
1.44      bowersj2  159: all of the information you need because of scoping issues, or would-be <exec> or 
                    160: <eval> blocks using the {DATA} mechanism results in hard-to-read
                    161: and -maintain code. (See course.initialization.helper for a borderline
                    162: case.)
1.13      bowersj2  163: 
                    164: It is possible to do some of the work with an XML fragment parsed by
1.17      bowersj2  165: lonxml; again, see lonprintout.pm for an example. In that case it is 
                    166: imperative that you call B<Apache::lonhelper::registerHelperTags()>
                    167: before parsing XML fragments and B<Apache::lonhelper::unregisterHelperTags()>
                    168: when you are done. See lonprintout.pm for examples of this usage in the
                    169: printHelper subroutine.
1.13      bowersj2  170: 
1.57      albertel  171: =head2 Localization
                    172: 
                    173: The helper framework tries to handle as much localization as
                    174: possible. The text is always run through
                    175: Apache::lonlocal::normalize_string, so be sure to run the keys through
                    176: that function for maximum usefulness and robustness.
                    177: 
1.3       bowersj2  178: =cut
                    179: 
1.1       bowersj2  180: package Apache::lonhelper;
1.2       bowersj2  181: use Apache::Constants qw(:common);
                    182: use Apache::File;
1.3       bowersj2  183: use Apache::lonxml;
1.57      albertel  184: use Apache::lonlocal;
1.100     albertel  185: use Apache::lonnet;
1.2       bowersj2  186: 
1.7       bowersj2  187: # Register all the tags with the helper, so the helper can 
                    188: # push and pop them
                    189: 
                    190: my @helperTags;
                    191: 
                    192: sub register {
                    193:     my ($namespace, @tags) = @_;
                    194: 
                    195:     for my $tag (@tags) {
                    196:         push @helperTags, [$namespace, $tag];
                    197:     }
                    198: }
                    199: 
1.2       bowersj2  200: BEGIN {
1.7       bowersj2  201:     Apache::lonxml::register('Apache::lonhelper', 
                    202:                              ('helper'));
                    203:       register('Apache::lonhelper', ('state'));
1.2       bowersj2  204: }
                    205: 
1.7       bowersj2  206: # Since all helpers are only three levels deep (helper tag, state tag, 
1.3       bowersj2  207: # substate type), it's easier and more readble to explicitly track 
                    208: # those three things directly, rather then futz with the tag stack 
                    209: # every time.
                    210: my $helper;
                    211: my $state;
                    212: my $substate;
1.4       bowersj2  213: # To collect parameters, the contents of the subtags are collected
                    214: # into this paramHash, then passed to the element object when the 
                    215: # end of the element tag is located.
                    216: my $paramHash; 
1.2       bowersj2  217: 
1.25      bowersj2  218: # Note from Jeremy 5-8-2003: It is *vital* that the real handler be called
                    219: # as a subroutine from the handler, or very mysterious things might happen.
                    220: # I don't know exactly why, but it seems that the scope where the Apache
                    221: # server enters the perl handler is treated differently from the rest of
                    222: # the handler. This also seems to manifest itself in the debugger as entering
                    223: # the perl handler in seemingly random places (sometimes it starts in the
                    224: # compiling phase, sometimes in the handler execution phase where it runs
                    225: # the code and stepping into the "1;" the module ends with goes into the handler,
                    226: # sometimes starting directly with the handler); I think the cause is related.
                    227: # In the debugger, this means that breakpoints are ignored until you step into
                    228: # a function and get out of what must be a "faked up scope" in the Apache->
                    229: # mod_perl connection. In this code, it was manifesting itself in the existence
1.65      www       230: # of two separate file-scoped $helper variables, one set to the value of the
1.25      bowersj2  231: # helper in the helper constructor, and one referenced by the handler on the
1.44      bowersj2  232: # "$helper->process()" line. Using the debugger, one could actually
                    233: # see the two different $helper variables, as hashes at completely
                    234: # different addresses. The second was therefore never set, and was still
1.25      bowersj2  235: # undefined when I tried to call process on it.
                    236: # By pushing the "real handler" down into the "real scope", everybody except the 
                    237: # actual handler function directly below this comment gets the same $helper and
                    238: # everybody is happy.
                    239: # The upshot of all of this is that for safety when a handler is  using 
                    240: # file-scoped variables in LON-CAPA, the handler should be pushed down one 
                    241: # call level, as I do here, to ensure that the top-level handler function does
                    242: # not get a different file scope from the rest of the code.
                    243: sub handler {
                    244:     my $r = shift;
                    245:     return real_handler($r);
                    246: }
                    247: 
1.13      bowersj2  248: # For debugging purposes, one can send a second parameter into this
                    249: # function, the 'uri' of the helper you wish to have rendered, and
                    250: # call this from other handlers.
1.25      bowersj2  251: sub real_handler {
1.3       bowersj2  252:     my $r = shift;
1.13      bowersj2  253:     my $uri = shift;
                    254:     if (!defined($uri)) { $uri = $r->uri(); }
1.100     albertel  255:     $env{'request.uri'} = $uri;
1.13      bowersj2  256:     my $filename = '/home/httpd/html' . $uri;
1.2       bowersj2  257:     my $fh = Apache::File->new($filename);
                    258:     my $file;
1.3       bowersj2  259:     read $fh, $file, 100000000;
                    260: 
1.27      bowersj2  261: 
1.3       bowersj2  262:     # Send header, don't cache this page
1.100     albertel  263:     if ($env{'browser.mathml'}) {
1.70      sakharuk  264: 	&Apache::loncommon::content_type($r,'text/xml');
                    265:     } else {
                    266: 	&Apache::loncommon::content_type($r,'text/html');
                    267:     }
                    268:     $r->send_http_header;
                    269:     return OK if $r->header_only;
1.3       bowersj2  270:     $r->rflush();
1.2       bowersj2  271: 
1.3       bowersj2  272:     # Discard result, we just want the objects that get created by the
                    273:     # xml parsing
                    274:     &Apache::lonxml::xmlparse($r, 'helper', $file);
1.2       bowersj2  275: 
1.31      bowersj2  276:     my $allowed = $helper->allowedCheck();
                    277:     if (!$allowed) {
1.100     albertel  278:         $env{'user.error.msg'} = $env{'request.uri'}.':'.$helper->{REQUIRED_PRIV}.
1.31      bowersj2  279:             ":0:0:Permission denied to access this helper.";
                    280:         return HTTP_NOT_ACCEPTABLE;
                    281:     }
                    282: 
1.13      bowersj2  283:     $helper->process();
                    284: 
1.3       bowersj2  285:     $r->print($helper->display());
1.31      bowersj2  286:     return OK;
1.2       bowersj2  287: }
                    288: 
1.13      bowersj2  289: sub registerHelperTags {
                    290:     for my $tagList (@helperTags) {
                    291:         Apache::lonxml::register($tagList->[0], $tagList->[1]);
                    292:     }
                    293: }
                    294: 
                    295: sub unregisterHelperTags {
                    296:     for my $tagList (@helperTags) {
                    297:         Apache::lonxml::deregister($tagList->[0], $tagList->[1]);
                    298:     }
                    299: }
                    300: 
1.2       bowersj2  301: sub start_helper {
                    302:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
                    303: 
                    304:     if ($target ne 'helper') {
                    305:         return '';
                    306:     }
1.7       bowersj2  307: 
1.13      bowersj2  308:     registerHelperTags();
                    309: 
1.31      bowersj2  310:     Apache::lonhelper::helper->new($token->[2]{'title'}, $token->[2]{'requiredpriv'});
1.4       bowersj2  311:     return '';
1.2       bowersj2  312: }
                    313: 
                    314: sub end_helper {
                    315:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
                    316:     
1.3       bowersj2  317:     if ($target ne 'helper') {
                    318:         return '';
                    319:     }
1.7       bowersj2  320: 
1.13      bowersj2  321:     unregisterHelperTags();
1.7       bowersj2  322: 
1.4       bowersj2  323:     return '';
1.2       bowersj2  324: }
1.1       bowersj2  325: 
1.3       bowersj2  326: sub start_state {
                    327:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
                    328: 
                    329:     if ($target ne 'helper') {
                    330:         return '';
                    331:     }
                    332: 
1.13      bowersj2  333:     Apache::lonhelper::state->new($token->[2]{'name'},
                    334:                                   $token->[2]{'title'});
1.3       bowersj2  335:     return '';
                    336: }
                    337: 
1.13      bowersj2  338: # Use this to get the param hash from other files.
                    339: sub getParamHash {
                    340:     return $paramHash;
                    341: }
                    342: 
                    343: # Use this to get the helper, if implementing elements in other files
                    344: # (like lonprintout.pm)
                    345: sub getHelper {
                    346:     return $helper;
                    347: }
                    348: 
1.3       bowersj2  349: # don't need this, so ignore it
                    350: sub end_state {
                    351:     return '';
                    352: }
                    353: 
1.1       bowersj2  354: 1;
                    355: 
1.3       bowersj2  356: package Apache::lonhelper::helper;
                    357: 
                    358: use Digest::MD5 qw(md5_hex);
1.57      albertel  359: use HTML::Entities();
1.3       bowersj2  360: use Apache::loncommon;
                    361: use Apache::File;
1.57      albertel  362: use Apache::lonlocal;
1.100     albertel  363: use Apache::lonnet;
1.3       bowersj2  364: 
                    365: sub new {
                    366:     my $proto = shift;
                    367:     my $class = ref($proto) || $proto;
                    368:     my $self = {};
                    369: 
                    370:     $self->{TITLE} = shift;
1.31      bowersj2  371:     $self->{REQUIRED_PRIV} = shift;
1.3       bowersj2  372:     
                    373:     # If there is a state from the previous form, use that. If there is no
                    374:     # state, use the start state parameter.
1.100     albertel  375:     if (defined $env{"form.CURRENT_STATE"})
1.3       bowersj2  376:     {
1.100     albertel  377: 	$self->{STATE} = $env{"form.CURRENT_STATE"};
1.3       bowersj2  378:     }
                    379:     else
                    380:     {
                    381: 	$self->{STATE} = "START";
                    382:     }
                    383: 
1.100     albertel  384:     $self->{TOKEN} = $env{'form.TOKEN'};
1.3       bowersj2  385:     # If a token was passed, we load that in. Otherwise, we need to create a 
                    386:     # new storage file
                    387:     # Tried to use standard Tie'd hashes, but you can't seem to take a 
                    388:     # reference to a tied hash and write to it. I'd call that a wart.
                    389:     if ($self->{TOKEN}) {
                    390:         # Validate the token before trusting it
                    391:         if ($self->{TOKEN} !~ /^[a-f0-9]{32}$/) {
                    392:             # Not legit. Return nothing and let all hell break loose.
                    393:             # User shouldn't be doing that!
                    394:             return undef;
                    395:         }
                    396: 
                    397:         # Get the hash.
                    398:         $self->{FILENAME} = $Apache::lonnet::tmpdir . md5_hex($self->{TOKEN}); # Note the token is not the literal file
                    399:         
                    400:         my $file = Apache::File->new($self->{FILENAME});
                    401:         my $contents = <$file>;
1.5       bowersj2  402: 
                    403:         # Now load in the contents
                    404:         for my $value (split (/&/, $contents)) {
                    405:             my ($name, $value) = split(/=/, $value);
                    406:             $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
                    407:             $self->{VARS}->{$name} = $value;
                    408:         }
                    409: 
1.3       bowersj2  410:         $file->close();
                    411:     } else {
                    412:         # Only valid if we're just starting.
                    413:         if ($self->{STATE} ne 'START') {
                    414:             return undef;
                    415:         }
                    416:         # Must create the storage
1.100     albertel  417:         $self->{TOKEN} = md5_hex($env{'user.name'} . $env{'user.domain'} .
1.3       bowersj2  418:                                  time() . rand());
                    419:         $self->{FILENAME} = $Apache::lonnet::tmpdir . md5_hex($self->{TOKEN});
                    420:     }
                    421: 
                    422:     # OK, we now have our persistent storage.
                    423: 
1.100     albertel  424:     if (defined $env{"form.RETURN_PAGE"})
1.3       bowersj2  425:     {
1.100     albertel  426: 	$self->{RETURN_PAGE} = $env{"form.RETURN_PAGE"};
1.3       bowersj2  427:     }
                    428:     else
                    429:     {
                    430: 	$self->{RETURN_PAGE} = $ENV{REFERER};
                    431:     }
                    432: 
                    433:     $self->{STATES} = {};
                    434:     $self->{DONE} = 0;
                    435: 
1.9       bowersj2  436:     # Used by various helpers for various things; see lonparm.helper
                    437:     # for an example.
                    438:     $self->{DATA} = {};
                    439: 
1.13      bowersj2  440:     $helper = $self;
                    441: 
                    442:     # Establish the $paramHash
                    443:     $paramHash = {};
                    444: 
1.3       bowersj2  445:     bless($self, $class);
                    446:     return $self;
                    447: }
                    448: 
                    449: # Private function; returns a string to construct the hidden fields
                    450: # necessary to have the helper track state.
                    451: sub _saveVars {
                    452:     my $self = shift;
                    453:     my $result = "";
                    454:     $result .= '<input type="hidden" name="CURRENT_STATE" value="' .
1.67      albertel  455:         HTML::Entities::encode($self->{STATE},'<>&"') . "\" />\n";
1.3       bowersj2  456:     $result .= '<input type="hidden" name="TOKEN" value="' .
                    457:         $self->{TOKEN} . "\" />\n";
                    458:     $result .= '<input type="hidden" name="RETURN_PAGE" value="' .
1.67      albertel  459:         HTML::Entities::encode($self->{RETURN_PAGE},'<>&"') . "\" />\n";
1.3       bowersj2  460: 
                    461:     return $result;
                    462: }
                    463: 
                    464: # Private function: Create the querystring-like representation of the stored
                    465: # data to write to disk.
                    466: sub _varsInFile {
                    467:     my $self = shift;
                    468:     my @vars = ();
                    469:     for my $key (keys %{$self->{VARS}}) {
                    470:         push @vars, &Apache::lonnet::escape($key) . '=' .
                    471:             &Apache::lonnet::escape($self->{VARS}->{$key});
                    472:     }
                    473:     return join ('&', @vars);
                    474: }
                    475: 
1.5       bowersj2  476: # Use this to declare variables.
                    477: # FIXME: Document this
                    478: sub declareVar {
                    479:     my $self = shift;
                    480:     my $var = shift;
                    481: 
                    482:     if (!defined($self->{VARS}->{$var})) {
                    483:         $self->{VARS}->{$var} = '';
                    484:     }
                    485: 
                    486:     my $envname = 'form.' . $var . '.forminput';
1.100     albertel  487:     if (defined($env{$envname})) {
                    488:         if (ref($env{$envname})) {
                    489:             $self->{VARS}->{$var} = join('|||', @{$env{$envname}});
1.28      bowersj2  490:         } else {
1.100     albertel  491:             $self->{VARS}->{$var} = $env{$envname};
1.28      bowersj2  492:         }
1.5       bowersj2  493:     }
                    494: }
                    495: 
1.31      bowersj2  496: sub allowedCheck {
                    497:     my $self = shift;
                    498: 
                    499:     if (!defined($self->{REQUIRED_PRIV})) { 
                    500:         return 1;
                    501:     }
                    502: 
1.100     albertel  503:     return Apache::lonnet::allowed($self->{REQUIRED_PRIV}, $env{'request.course.id'});
1.31      bowersj2  504: }
                    505: 
1.3       bowersj2  506: sub changeState {
                    507:     my $self = shift;
                    508:     $self->{STATE} = shift;
                    509: }
                    510: 
                    511: sub registerState {
                    512:     my $self = shift;
                    513:     my $state = shift;
                    514: 
                    515:     my $stateName = $state->name();
                    516:     $self->{STATES}{$stateName} = $state;
                    517: }
                    518: 
1.13      bowersj2  519: sub process {
1.3       bowersj2  520:     my $self = shift;
                    521: 
                    522:     # Phase 1: Post processing for state of previous screen (which is actually
                    523:     # the "current state" in terms of the helper variables), if it wasn't the 
                    524:     # beginning state.
1.100     albertel  525:     if ($self->{STATE} ne "START" || $env{"form.SUBMIT"} eq &mt("Next ->")) {
1.3       bowersj2  526: 	my $prevState = $self->{STATES}{$self->{STATE}};
1.13      bowersj2  527:         $prevState->postprocess();
1.3       bowersj2  528:     }
                    529:     
                    530:     # Note, to handle errors in a state's input that a user must correct,
                    531:     # do not transition in the postprocess, and force the user to correct
                    532:     # the error.
                    533: 
                    534:     # Phase 2: Preprocess current state
                    535:     my $startState = $self->{STATE};
1.17      bowersj2  536:     my $state = $self->{STATES}->{$startState};
1.3       bowersj2  537:     
1.13      bowersj2  538:     # For debugging, print something here to determine if you're going
                    539:     # to an undefined state.
1.3       bowersj2  540:     if (!defined($state)) {
1.13      bowersj2  541:         return;
1.3       bowersj2  542:     }
                    543:     $state->preprocess();
                    544: 
                    545:     # Phase 3: While the current state is different from the previous state,
                    546:     # keep processing.
1.17      bowersj2  547:     while ( $startState ne $self->{STATE} && 
                    548:             defined($self->{STATES}->{$self->{STATE}}) )
1.3       bowersj2  549:     {
                    550: 	$startState = $self->{STATE};
1.17      bowersj2  551: 	$state = $self->{STATES}->{$startState};
1.3       bowersj2  552: 	$state->preprocess();
                    553:     }
                    554: 
1.13      bowersj2  555:     return;
                    556: } 
                    557: 
                    558: # 1: Do the post processing for the previous state.
                    559: # 2: Do the preprocessing for the current state.
                    560: # 3: Check to see if state changed, if so, postprocess current and move to next.
                    561: #    Repeat until state stays stable.
                    562: # 4: Render the current state to the screen as an HTML page.
                    563: sub display {
                    564:     my $self = shift;
                    565: 
                    566:     my $state = $self->{STATES}{$self->{STATE}};
                    567: 
                    568:     my $result = "";
                    569: 
1.17      bowersj2  570:     if (!defined($state)) {
                    571:         $result = "<font color='#ff0000'>Error: state '$state' not defined!</font>";
                    572:         return $result;
                    573:     }
                    574: 
1.3       bowersj2  575:     # Phase 4: Display.
1.96      albertel  576:     my $html=&Apache::lonxml::xmlbegin();
1.68      sakharuk  577:     my $stateTitle=&mt($state->title());
1.57      albertel  578:     my $helperTitle = &mt($self->{TITLE});
1.129     albertel  579:     my $browser_searcher_js = &Apache::loncommon::browser_and_searcher_javascript();
1.57      albertel  580:     my $bodytag = &Apache::loncommon::bodytag($helperTitle,'','');
                    581:     my $previous = HTML::Entities::encode(&mt("<- Previous"), '<>&"');
                    582:     my $next = HTML::Entities::encode(&mt("Next ->"), '<>&"');
                    583:     # FIXME: This should be parameterized, not concatenated - Jeremy
                    584:     my $loncapaHelper = &mt("LON-CAPA Helper:");
1.3       bowersj2  585: 
                    586:     $result .= <<HEADER;
1.96      albertel  587: $html
1.3       bowersj2  588:     <head>
1.57      albertel  589:         <title>$loncapaHelper: $helperTitle</title>
1.129     albertel  590:         <script type="text/javascript">
                    591: $browser_searcher_js
                    592:         </script>
1.3       bowersj2  593:     </head>
                    594:     $bodytag
                    595: HEADER
1.28      bowersj2  596:     if (!$state->overrideForm()) { $result.="<form name='helpform' method='POST'>"; }
1.3       bowersj2  597:     $result .= <<HEADER;
1.31      bowersj2  598:         <table border="0" width='100%'><tr><td>
1.3       bowersj2  599:         <h2><i>$stateTitle</i></h2>
                    600: HEADER
                    601: 
1.31      bowersj2  602:     $result .= "<table cellpadding='10' width='100%'><tr><td rowspan='2' valign='top'>";
1.30      bowersj2  603: 
1.3       bowersj2  604:     if (!$state->overrideForm()) {
                    605:         $result .= $self->_saveVars();
                    606:     }
1.30      bowersj2  607:     $result .= $state->render();
                    608: 
1.31      bowersj2  609:     $result .= "</td><td valign='top' align='right'>";
1.3       bowersj2  610: 
1.30      bowersj2  611:     # Warning: Copy and pasted from below, because it's too much trouble to 
                    612:     # turn this into a subroutine
1.3       bowersj2  613:     if (!$state->overrideForm()) {
                    614:         if ($self->{STATE} ne $self->{START_STATE}) {
                    615:             #$result .= '<input name="SUBMIT" type="submit" value="&lt;- Previous" />&nbsp;&nbsp;';
                    616:         }
                    617:         if ($self->{DONE}) {
                    618:             my $returnPage = $self->{RETURN_PAGE};
1.57      albertel  619:             $result .= "<a href=\"$returnPage\">" . &mt("End Helper") . "</a>";
1.3       bowersj2  620:         }
                    621:         else {
1.30      bowersj2  622:             $result .= '<nobr><input name="back" type="button" ';
1.57      albertel  623:             $result .= 'value="' . $previous . '" onclick="history.go(-1)" /> ';
                    624:             $result .= '<input name="SUBMIT" type="submit" value="' . $next . '" /></nobr>';
1.30      bowersj2  625:         }
                    626:     }
                    627: 
1.31      bowersj2  628:     $result .= "</td></tr><tr><td valign='bottom' align='right'>";
1.30      bowersj2  629: 
                    630:     # Warning: Copy and pasted from above, because it's too much trouble to 
                    631:     # turn this into a subroutine
                    632:     if (!$state->overrideForm()) {
                    633:         if ($self->{STATE} ne $self->{START_STATE}) {
                    634:             #$result .= '<input name="SUBMIT" type="submit" value="&lt;- Previous" />&nbsp;&nbsp;';
                    635:         }
                    636:         if ($self->{DONE}) {
                    637:             my $returnPage = $self->{RETURN_PAGE};
1.57      albertel  638:             $result .= "<a href=\"$returnPage\">" . &mt('End Helper') . "</a>";
1.30      bowersj2  639:         }
                    640:         else {
                    641:             $result .= '<nobr><input name="back" type="button" ';
1.57      albertel  642:             $result .= 'value="' . $previous . '" onclick="history.go(-1)" /> ';
                    643:             $result .= '<input name="SUBMIT" type="submit" value="' . $next . '" /></nobr>';
1.3       bowersj2  644:         }
                    645:     }
                    646: 
1.13      bowersj2  647:     #foreach my $key (keys %{$self->{VARS}}) {
                    648:     #    $result .= "|$key| -> " . $self->{VARS}->{$key} . "<br />";
                    649:     #}
1.5       bowersj2  650: 
1.30      bowersj2  651:     $result .= "</td></tr></table>";
                    652: 
1.3       bowersj2  653:     $result .= <<FOOTER;
                    654:               </td>
                    655:             </tr>
                    656:           </table>
                    657:         </form>
                    658:     </body>
                    659: </html>
                    660: FOOTER
                    661: 
                    662:     # Handle writing out the vars to the file
                    663:     my $file = Apache::File->new('>'.$self->{FILENAME});
1.33      bowersj2  664:     print $file $self->_varsInFile();
1.3       bowersj2  665: 
                    666:     return $result;
                    667: }
                    668: 
                    669: 1;
                    670: 
                    671: package Apache::lonhelper::state;
                    672: 
                    673: # States bundle things together and are responsible for compositing the
1.4       bowersj2  674: # various elements together. It is not generally necessary for users to
                    675: # use the state object directly, so it is not perldoc'ed.
                    676: 
                    677: # Basically, all the states do is pass calls to the elements and aggregate
                    678: # the results.
1.3       bowersj2  679: 
                    680: sub new {
                    681:     my $proto = shift;
                    682:     my $class = ref($proto) || $proto;
                    683:     my $self = {};
                    684: 
                    685:     $self->{NAME} = shift;
                    686:     $self->{TITLE} = shift;
                    687:     $self->{ELEMENTS} = [];
                    688: 
                    689:     bless($self, $class);
                    690: 
                    691:     $helper->registerState($self);
                    692: 
1.13      bowersj2  693:     $state = $self;
                    694: 
1.3       bowersj2  695:     return $self;
                    696: }
                    697: 
                    698: sub name {
                    699:     my $self = shift;
                    700:     return $self->{NAME};
                    701: }
                    702: 
                    703: sub title {
                    704:     my $self = shift;
                    705:     return $self->{TITLE};
                    706: }
                    707: 
1.4       bowersj2  708: sub preprocess {
                    709:     my $self = shift;
                    710:     for my $element (@{$self->{ELEMENTS}}) {
                    711:         $element->preprocess();
                    712:     }
                    713: }
                    714: 
1.6       bowersj2  715: # FIXME: Document that all postprocesses must return a true value or
                    716: # the state transition will be overridden
1.4       bowersj2  717: sub postprocess {
                    718:     my $self = shift;
1.6       bowersj2  719: 
                    720:     # Save the state so we can roll it back if we need to.
                    721:     my $originalState = $helper->{STATE};
                    722:     my $everythingSuccessful = 1;
                    723: 
1.4       bowersj2  724:     for my $element (@{$self->{ELEMENTS}}) {
1.6       bowersj2  725:         my $result = $element->postprocess();
                    726:         if (!$result) { $everythingSuccessful = 0; }
                    727:     }
                    728: 
                    729:     # If not all the postprocesses were successful, override
                    730:     # any state transitions that may have occurred. It is the
                    731:     # responsibility of the states to make sure they have 
                    732:     # error handling in that case.
                    733:     if (!$everythingSuccessful) {
                    734:         $helper->{STATE} = $originalState;
1.4       bowersj2  735:     }
                    736: }
                    737: 
1.13      bowersj2  738: # Override the form if any element wants to.
                    739: # two elements overriding the form will make a mess, but that should
                    740: # be considered helper author error ;-)
1.4       bowersj2  741: sub overrideForm {
1.13      bowersj2  742:     my $self = shift;
                    743:     for my $element (@{$self->{ELEMENTS}}) {
                    744:         if ($element->overrideForm()) {
                    745:             return 1;
                    746:         }
                    747:     }
1.4       bowersj2  748:     return 0;
                    749: }
                    750: 
                    751: sub addElement {
                    752:     my $self = shift;
                    753:     my $element = shift;
                    754:     
                    755:     push @{$self->{ELEMENTS}}, $element;
                    756: }
                    757: 
                    758: sub render {
                    759:     my $self = shift;
                    760:     my @results = ();
                    761: 
                    762:     for my $element (@{$self->{ELEMENTS}}) {
                    763:         push @results, $element->render();
                    764:     }
1.28      bowersj2  765: 
1.4       bowersj2  766:     return join("\n", @results);
                    767: }
                    768: 
                    769: 1;
                    770: 
                    771: package Apache::lonhelper::element;
                    772: # Support code for elements
                    773: 
                    774: =pod
                    775: 
1.44      bowersj2  776: =head1 Element Base Class
1.4       bowersj2  777: 
1.28      bowersj2  778: The Apache::lonhelper::element base class provides support for elements
                    779: and defines some generally useful tags for use in elements.
1.4       bowersj2  780: 
1.44      bowersj2  781: =head2 finalcode tagX<finalcode>
1.25      bowersj2  782: 
                    783: Each element can contain a "finalcode" tag that, when the special FINAL
                    784: helper state is used, will be executed, surrounded by "sub { my $helper = shift;"
                    785: and "}". It is expected to return a string describing what it did, which 
                    786: may be an empty string. See course initialization helper for an example. This is
                    787: generally intended for helpers like the course initialization helper, which consist
                    788: of several panels, each of which is performing some sort of bite-sized functionality.
                    789: 
1.44      bowersj2  790: =head2 defaultvalue tagX<defaultvalue>
1.25      bowersj2  791: 
                    792: Each element that accepts user input can contain a "defaultvalue" tag that,
                    793: when surrounded by "sub { my $helper = shift; my $state = shift; " and "}",
                    794: will form a subroutine that when called will provide a default value for
                    795: the element. How this value is interpreted by the element is specific to
                    796: the element itself, and possibly the settings the element has (such as 
                    797: multichoice vs. single choice for <choices> tags). 
                    798: 
                    799: This is also intended for things like the course initialization wizard, where the
                    800: user is setting various parameters. By correctly grabbing current settings 
                    801: and including them into the helper, it allows the user to come back to the
                    802: helper later and re-execute it, without needing to worry about overwriting
                    803: some setting accidentally.
                    804: 
                    805: Again, see the course initialization helper for examples.
                    806: 
1.44      bowersj2  807: =head2 validator tagX<validator>
1.38      bowersj2  808: 
                    809: Some elements that accepts user input can contain a "validator" tag that,
                    810: when surrounded by "sub { my $helper = shift; my $state = shift; my $element = shift; my $val = shift " 
                    811: and "}", where "$val" is the value the user entered, will form a subroutine 
                    812: that when called will verify whether the given input is valid or not. If it 
                    813: is valid, the routine will return a false value. If invalid, the routine 
                    814: will return an error message to be displayed for the user.
                    815: 
                    816: Consult the documentation for each element to see whether it supports this 
                    817: tag.
                    818: 
1.44      bowersj2  819: =head2 getValue methodX<getValue (helper elements)>
1.31      bowersj2  820: 
                    821: If the element stores the name of the variable in a 'variable' member, which
                    822: the provided ones all do, you can retreive the value of the variable by calling
                    823: this method.
                    824: 
1.4       bowersj2  825: =cut
                    826: 
1.5       bowersj2  827: BEGIN {
1.7       bowersj2  828:     &Apache::lonhelper::register('Apache::lonhelper::element',
1.25      bowersj2  829:                                  ('nextstate', 'finalcode',
1.38      bowersj2  830:                                   'defaultvalue', 'validator'));
1.5       bowersj2  831: }
                    832: 
1.4       bowersj2  833: # Because we use the param hash, this is often a sufficent
                    834: # constructor
                    835: sub new {
                    836:     my $proto = shift;
                    837:     my $class = ref($proto) || $proto;
                    838:     my $self = $paramHash;
                    839:     bless($self, $class);
                    840: 
                    841:     $self->{PARAMS} = $paramHash;
                    842:     $self->{STATE} = $state;
                    843:     $state->addElement($self);
                    844:     
                    845:     # Ensure param hash is not reused
                    846:     $paramHash = {};
                    847: 
                    848:     return $self;
                    849: }   
                    850: 
1.5       bowersj2  851: sub start_nextstate {
                    852:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
                    853: 
                    854:     if ($target ne 'helper') {
                    855:         return '';
                    856:     }
                    857:     
                    858:     $paramHash->{NEXTSTATE} = &Apache::lonxml::get_all_text('/nextstate',
                    859:                                                              $parser);
                    860:     return '';
                    861: }
                    862: 
                    863: sub end_nextstate { return ''; }
                    864: 
1.25      bowersj2  865: sub start_finalcode {
                    866:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
                    867: 
                    868:     if ($target ne 'helper') {
                    869:         return '';
                    870:     }
                    871:     
                    872:     $paramHash->{FINAL_CODE} = &Apache::lonxml::get_all_text('/finalcode',
                    873:                                                              $parser);
                    874:     return '';
                    875: }
                    876: 
                    877: sub end_finalcode { return ''; }
                    878: 
                    879: sub start_defaultvalue {
                    880:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
                    881: 
                    882:     if ($target ne 'helper') {
                    883:         return '';
                    884:     }
                    885:     
                    886:     $paramHash->{DEFAULT_VALUE} = &Apache::lonxml::get_all_text('/defaultvalue',
                    887:                                                              $parser);
                    888:     $paramHash->{DEFAULT_VALUE} = 'sub { my $helper = shift; my $state = shift;' .
                    889:         $paramHash->{DEFAULT_VALUE} . '}';
                    890:     return '';
                    891: }
                    892: 
                    893: sub end_defaultvalue { return ''; }
                    894: 
1.57      albertel  895: # Validators may need to take language specifications
1.38      bowersj2  896: sub start_validator {
                    897:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
                    898: 
                    899:     if ($target ne 'helper') {
                    900:         return '';
                    901:     }
                    902:     
                    903:     $paramHash->{VALIDATOR} = &Apache::lonxml::get_all_text('/validator',
                    904:                                                              $parser);
                    905:     $paramHash->{VALIDATOR} = 'sub { my $helper = shift; my $state = shift; my $element = shift; my $val = shift;' .
                    906:         $paramHash->{VALIDATOR} . '}';
                    907:     return '';
                    908: }
                    909: 
                    910: sub end_validator { return ''; }
                    911: 
1.4       bowersj2  912: sub preprocess {
                    913:     return 1;
                    914: }
                    915: 
                    916: sub postprocess {
                    917:     return 1;
                    918: }
                    919: 
                    920: sub render {
                    921:     return '';
                    922: }
                    923: 
1.13      bowersj2  924: sub overrideForm {
                    925:     return 0;
                    926: }
                    927: 
1.31      bowersj2  928: sub getValue {
                    929:     my $self = shift;
                    930:     return $helper->{VARS}->{$self->{'variable'}};
                    931: }
                    932: 
1.4       bowersj2  933: 1;
                    934: 
                    935: package Apache::lonhelper::message;
                    936: 
                    937: =pod
                    938: 
1.48      bowersj2  939: =head1 Elements
                    940: 
                    941: =head2 Element: messageX<message, helper element>
1.4       bowersj2  942: 
1.44      bowersj2  943: Message elements display their contents, and
                    944: transition directly to the state in the <nextstate> attribute. Example:
1.4       bowersj2  945: 
1.44      bowersj2  946:  <message nextstate='GET_NAME'>
                    947:    This is the <b>message</b> the user will see, 
                    948:                  <i>HTML allowed</i>.
1.4       bowersj2  949:    </message>
                    950: 
1.44      bowersj2  951: This will display the HTML message and transition to the 'nextstate' if
1.7       bowersj2  952: given. The HTML will be directly inserted into the helper, so if you don't
1.44      bowersj2  953: want text to run together, you'll need to manually wrap the message text
1.4       bowersj2  954: in <p> tags, or whatever is appropriate for your HTML.
                    955: 
1.5       bowersj2  956: Message tags do not add in whitespace, so if you want it, you'll need to add
                    957: it into states. This is done so you can inline some elements, such as 
                    958: the <date> element, right between two messages, giving the appearence that 
                    959: the <date> element appears inline. (Note the elements can not be embedded
                    960: within each other.)
                    961: 
1.4       bowersj2  962: This is also a good template for creating your own new states, as it has
                    963: very little code beyond the state template.
                    964: 
1.57      albertel  965: =head3 Localization
                    966: 
                    967: The contents of the message tag will be run through the
                    968: normalize_string function and that will be used as a call to &mt.
                    969: 
1.4       bowersj2  970: =cut
                    971: 
                    972: no strict;
                    973: @ISA = ("Apache::lonhelper::element");
                    974: use strict;
1.57      albertel  975: use Apache::lonlocal;
1.4       bowersj2  976: 
                    977: BEGIN {
1.7       bowersj2  978:     &Apache::lonhelper::register('Apache::lonhelper::message',
1.10      bowersj2  979:                               ('message'));
1.3       bowersj2  980: }
                    981: 
1.5       bowersj2  982: sub new {
                    983:     my $ref = Apache::lonhelper::element->new();
                    984:     bless($ref);
                    985: }
1.4       bowersj2  986: 
                    987: # CONSTRUCTION: Construct the message element from the XML
                    988: sub start_message {
                    989:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
                    990: 
                    991:     if ($target ne 'helper') {
                    992:         return '';
                    993:     }
1.10      bowersj2  994: 
1.69      sakharuk  995:     $paramHash->{MESSAGE_TEXT} = &mtn(&Apache::lonxml::get_all_text('/message',
                    996:                                                                $parser));
1.10      bowersj2  997: 
                    998:     if (defined($token->[2]{'nextstate'})) {
                    999:         $paramHash->{NEXTSTATE} = $token->[2]{'nextstate'};
                   1000:     }
1.4       bowersj2 1001:     return '';
1.3       bowersj2 1002: }
                   1003: 
1.10      bowersj2 1004: sub end_message {
1.4       bowersj2 1005:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
                   1006: 
                   1007:     if ($target ne 'helper') {
                   1008:         return '';
                   1009:     }
1.10      bowersj2 1010:     Apache::lonhelper::message->new();
                   1011:     return '';
1.5       bowersj2 1012: }
                   1013: 
                   1014: sub render {
                   1015:     my $self = shift;
                   1016: 
1.57      albertel 1017:     return &mtn($self->{MESSAGE_TEXT});
1.5       bowersj2 1018: }
                   1019: # If a NEXTSTATE was given, switch to it
                   1020: sub postprocess {
                   1021:     my $self = shift;
                   1022:     if (defined($self->{NEXTSTATE})) {
                   1023:         $helper->changeState($self->{NEXTSTATE});
                   1024:     }
1.6       bowersj2 1025: 
                   1026:     return 1;
1.5       bowersj2 1027: }
                   1028: 1;
                   1029: 
                   1030: package Apache::lonhelper::choices;
                   1031: 
                   1032: =pod
                   1033: 
1.44      bowersj2 1034: =head2 Element: choicesX<choices, helper element>
1.5       bowersj2 1035: 
                   1036: Choice states provide a single choice to the user as a text selection box.
                   1037: A "choice" is two pieces of text, one which will be displayed to the user
                   1038: (the "human" value), and one which will be passed back to the program
                   1039: (the "computer" value). For instance, a human may choose from a list of
                   1040: resources on disk by title, while your program wants the file name.
                   1041: 
                   1042: <choices> takes an attribute "variable" to control which helper variable
                   1043: the result is stored in.
                   1044: 
                   1045: <choices> takes an attribute "multichoice" which, if set to a true
                   1046: value, will allow the user to select multiple choices.
                   1047: 
1.26      bowersj2 1048: <choices> takes an attribute "allowempty" which, if set to a true 
                   1049: value, will allow the user to select none of the choices without raising
                   1050: an error message.
                   1051: 
1.44      bowersj2 1052: =head3 SUB-TAGS
1.5       bowersj2 1053: 
1.44      bowersj2 1054: <choices> can have the following subtags:X<choice, helper tag>
1.5       bowersj2 1055: 
                   1056: =over 4
                   1057: 
                   1058: =item * <nextstate>state_name</nextstate>: If given, this will cause the
1.44      bowersj2 1059:       choice element to transition to the given state after executing.
                   1060:       This will override the <nextstate> passed to <choices> (if any).
1.5       bowersj2 1061: 
                   1062: =item * <choice />: If the choices are static,
                   1063:       this element will allow you to specify them. Each choice
                   1064:       contains  attribute, "computer", as described above. The
                   1065:       content of the tag will be used as the human label.
                   1066:       For example,  
                   1067:       <choice computer='234-12-7312'>Bobby McDormik</choice>.
                   1068: 
1.44      bowersj2 1069: <choice> can take a parameter "eval", which if set to
                   1070: a true value, will cause the contents of the tag to be
                   1071: evaluated as it would be in an <eval> tag; see <eval> tag
                   1072: below.
1.13      bowersj2 1073: 
1.5       bowersj2 1074: <choice> may optionally contain a 'nextstate' attribute, which
1.44      bowersj2 1075: will be the state transistioned to if the choice is made, if
                   1076: the choice is not multichoice. This will override the nextstate
                   1077: passed to the parent C<choices> tag.
1.5       bowersj2 1078: 
                   1079: =back
                   1080: 
                   1081: To create the choices programmatically, either wrap the choices in 
                   1082: <condition> tags (prefered), or use an <exec> block inside the <choice>
                   1083: tag. Store the choices in $state->{CHOICES}, which is a list of list
                   1084: references, where each list has three strings. The first is the human
                   1085: name, the second is the computer name. and the third is the option
                   1086: next state. For example:
                   1087: 
                   1088:  <exec>
                   1089:     for (my $i = 65; $i < 65 + 26; $i++) {
                   1090:         push @{$state->{CHOICES}}, [chr($i), $i, 'next'];
                   1091:     }
                   1092:  </exec>
                   1093: 
                   1094: This will allow the user to select from the letters A-Z (in ASCII), while
                   1095: passing the ASCII value back into the helper variables, and the state
                   1096: will in all cases transition to 'next'.
                   1097: 
                   1098: You can mix and match methods of creating choices, as long as you always 
                   1099: "push" onto the choice list, rather then wiping it out. (You can even 
                   1100: remove choices programmatically, but that would probably be bad form.)
                   1101: 
1.44      bowersj2 1102: =head3 defaultvalue support
1.25      bowersj2 1103: 
                   1104: Choices supports default values both in multichoice and single choice mode.
                   1105: In single choice mode, have the defaultvalue tag's function return the 
                   1106: computer value of the box you want checked. If the function returns a value
                   1107: that does not correspond to any of the choices, the default behavior of selecting
                   1108: the first choice will be preserved.
                   1109: 
                   1110: For multichoice, return a string with the computer values you want checked,
                   1111: delimited by triple pipes. Note this matches how the result of the <choices>
                   1112: tag is stored in the {VARS} hash.
                   1113: 
1.5       bowersj2 1114: =cut
                   1115: 
                   1116: no strict;
                   1117: @ISA = ("Apache::lonhelper::element");
                   1118: use strict;
1.57      albertel 1119: use Apache::lonlocal;
1.100     albertel 1120: use Apache::lonnet;
1.5       bowersj2 1121: 
                   1122: BEGIN {
1.7       bowersj2 1123:     &Apache::lonhelper::register('Apache::lonhelper::choices',
1.5       bowersj2 1124:                               ('choice', 'choices'));
                   1125: }
                   1126: 
                   1127: sub new {
                   1128:     my $ref = Apache::lonhelper::element->new();
                   1129:     bless($ref);
                   1130: }
                   1131: 
                   1132: # CONSTRUCTION: Construct the message element from the XML
                   1133: sub start_choices {
                   1134:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
                   1135: 
                   1136:     if ($target ne 'helper') {
                   1137:         return '';
                   1138:     }
                   1139: 
                   1140:     # Need to initialize the choices list, so everything can assume it exists
1.24      sakharuk 1141:     $paramHash->{'variable'} = $token->[2]{'variable'} if (!defined($paramHash->{'variable'}));
1.5       bowersj2 1142:     $helper->declareVar($paramHash->{'variable'});
                   1143:     $paramHash->{'multichoice'} = $token->[2]{'multichoice'};
1.26      bowersj2 1144:     $paramHash->{'allowempty'} = $token->[2]{'allowempty'};
1.5       bowersj2 1145:     $paramHash->{CHOICES} = [];
                   1146:     return '';
                   1147: }
                   1148: 
                   1149: sub end_choices {
                   1150:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
                   1151: 
                   1152:     if ($target ne 'helper') {
                   1153:         return '';
                   1154:     }
                   1155:     Apache::lonhelper::choices->new();
                   1156:     return '';
                   1157: }
                   1158: 
                   1159: sub start_choice {
                   1160:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
                   1161: 
                   1162:     if ($target ne 'helper') {
                   1163:         return '';
                   1164:     }
                   1165: 
                   1166:     my $computer = $token->[2]{'computer'};
1.69      sakharuk 1167:     my $human = &mt(&Apache::lonxml::get_all_text('/choice',
                   1168:                                               $parser));
1.5       bowersj2 1169:     my $nextstate = $token->[2]{'nextstate'};
1.13      bowersj2 1170:     my $evalFlag = $token->[2]{'eval'};
1.94      albertel 1171:     push @{$paramHash->{CHOICES}}, [&mtn($human), $computer, $nextstate, 
1.13      bowersj2 1172:                                     $evalFlag];
1.5       bowersj2 1173:     return '';
                   1174: }
                   1175: 
                   1176: sub end_choice {
                   1177:     return '';
                   1178: }
                   1179: 
1.87      matthew  1180: {
                   1181:     # used to generate unique id attributes for <input> tags. 
                   1182:     # internal use only.
                   1183:     my $id = 0;
                   1184:     sub new_id { return $id++; }
                   1185: }
                   1186: 
1.5       bowersj2 1187: sub render {
                   1188:     my $self = shift;
                   1189:     my $var = $self->{'variable'};
                   1190:     my $buttons = '';
                   1191:     my $result = '';
                   1192: 
                   1193:     if ($self->{'multichoice'}) {
1.6       bowersj2 1194:         $result .= <<SCRIPT;
1.112     albertel 1195: <script type="text/javascript">
                   1196: // <!--
1.18      bowersj2 1197:     function checkall(value, checkName) {
1.15      bowersj2 1198: 	for (i=0; i<document.forms.helpform.elements.length; i++) {
1.18      bowersj2 1199:             ele = document.forms.helpform.elements[i];
                   1200:             if (ele.name == checkName + '.forminput') {
                   1201:                 document.forms.helpform.elements[i].checked=value;
                   1202:             }
1.5       bowersj2 1203:         }
                   1204:     }
1.112     albertel 1205: // -->
1.5       bowersj2 1206: </script>
                   1207: SCRIPT
1.25      bowersj2 1208:     }
                   1209: 
                   1210:     # Only print "select all" and "unselect all" if there are five or
                   1211:     # more choices; fewer then that and it looks silly.
                   1212:     if ($self->{'multichoice'} && scalar(@{$self->{CHOICES}}) > 4) {
1.68      sakharuk 1213:         my %lt=&Apache::lonlocal::texthash(
                   1214: 			'sa'  => "Select All",
                   1215: 		        'ua'  => "Unselect All");
1.5       bowersj2 1216:         $buttons = <<BUTTONS;
                   1217: <br />
1.68      sakharuk 1218: <input type="button" onclick="checkall(true, '$var')" value="$lt{'sa'}" />
                   1219: <input type="button" onclick="checkall(false, '$var')" value="$lt{'ua'}" />
1.6       bowersj2 1220: <br />&nbsp;
1.5       bowersj2 1221: BUTTONS
                   1222:     }
                   1223: 
1.16      bowersj2 1224:     if (defined $self->{ERROR_MSG}) {
1.6       bowersj2 1225:         $result .= '<br /><font color="#FF0000">' . $self->{ERROR_MSG} . '</font><br />';
1.5       bowersj2 1226:     }
                   1227: 
                   1228:     $result .= $buttons;
1.6       bowersj2 1229:     
1.5       bowersj2 1230:     $result .= "<table>\n\n";
                   1231: 
1.25      bowersj2 1232:     my %checkedChoices;
                   1233:     my $checkedChoicesFunc;
                   1234: 
                   1235:     if (defined($self->{DEFAULT_VALUE})) {
                   1236:         $checkedChoicesFunc = eval ($self->{DEFAULT_VALUE});
                   1237:         die 'Error in default value code for variable ' . 
1.34      bowersj2 1238:             $self->{'variable'} . ', Perl said: ' . $@ if $@;
1.25      bowersj2 1239:     } else {
                   1240:         $checkedChoicesFunc = sub { return ''; };
                   1241:     }
                   1242: 
                   1243:     # Process which choices should be checked.
                   1244:     if ($self->{'multichoice'}) {
                   1245:         for my $selectedChoice (split(/\|\|\|/, (&$checkedChoicesFunc($helper, $self)))) {
                   1246:             $checkedChoices{$selectedChoice} = 1;
                   1247:         }
                   1248:     } else {
                   1249:         # single choice
                   1250:         my $selectedChoice = &$checkedChoicesFunc($helper, $self);
                   1251:         
                   1252:         my $foundChoice = 0;
                   1253:         
                   1254:         # check that the choice is in the list of choices.
                   1255:         for my $choice (@{$self->{CHOICES}}) {
                   1256:             if ($choice->[1] eq $selectedChoice) {
                   1257:                 $checkedChoices{$choice->[1]} = 1;
                   1258:                 $foundChoice = 1;
                   1259:             }
                   1260:         }
                   1261:         
                   1262:         # If we couldn't find the choice, pick the first one 
                   1263:         if (!$foundChoice) {
                   1264:             $checkedChoices{$self->{CHOICES}->[0]->[1]} = 1;
                   1265:         }
                   1266:     }
                   1267: 
1.5       bowersj2 1268:     my $type = "radio";
                   1269:     if ($self->{'multichoice'}) { $type = 'checkbox'; }
                   1270:     foreach my $choice (@{$self->{CHOICES}}) {
1.87      matthew  1271:         my $id = &new_id();
1.5       bowersj2 1272:         $result .= "<tr>\n<td width='20'>&nbsp;</td>\n";
                   1273:         $result .= "<td valign='top'><input type='$type' name='$var.forminput'"
1.111     albertel 1274:             . " value='" . 
1.89      foxr     1275:             HTML::Entities::encode($choice->[1],"<>&\"'") 
1.5       bowersj2 1276:             . "'";
1.25      bowersj2 1277:         if ($checkedChoices{$choice->[1]}) {
1.111     albertel 1278:             $result .= " checked='checked' ";
1.5       bowersj2 1279:         }
1.111     albertel 1280:         $result .= qq{id="id$id"};
1.13      bowersj2 1281:         my $choiceLabel = $choice->[0];
                   1282:         if ($choice->[4]) {  # if we need to evaluate this choice
                   1283:             $choiceLabel = "sub { my $helper = shift; my $state = shift;" .
                   1284:                 $choiceLabel . "}";
                   1285:             $choiceLabel = eval($choiceLabel);
                   1286:             $choiceLabel = &$choiceLabel($helper, $self);
                   1287:         }
1.111     albertel 1288:         $result .= "/></td><td> ".qq{<label for="id$id">}.
1.94      albertel 1289:             $choiceLabel. "</label></td></tr>\n";
1.5       bowersj2 1290:     }
                   1291:     $result .= "</table>\n\n\n";
                   1292:     $result .= $buttons;
                   1293: 
                   1294:     return $result;
                   1295: }
                   1296: 
                   1297: # If a NEXTSTATE was given or a nextstate for this choice was
                   1298: # given, switch to it
                   1299: sub postprocess {
                   1300:     my $self = shift;
1.100     albertel 1301:     my $chosenValue = $env{'form.' . $self->{'variable'} . '.forminput'};
1.5       bowersj2 1302: 
1.26      bowersj2 1303:     if (!defined($chosenValue) && !$self->{'allowempty'}) {
1.59      bowersj2 1304:         $self->{ERROR_MSG} = 
                   1305: 	    &mt("You must choose one or more choices to continue.");
1.6       bowersj2 1306:         return 0;
                   1307:     }
                   1308: 
1.28      bowersj2 1309:     if (ref($chosenValue)) {
                   1310:         $helper->{VARS}->{$self->{'variable'}} = join('|||', @$chosenValue);
1.42      bowersj2 1311:     }
                   1312: 
                   1313:     if (defined($self->{NEXTSTATE})) {
                   1314:         $helper->changeState($self->{NEXTSTATE});
                   1315:     }
                   1316:     
                   1317:     foreach my $choice (@{$self->{CHOICES}}) {
                   1318:         if ($choice->[1] eq $chosenValue) {
                   1319:             if (defined($choice->[2])) {
                   1320:                 $helper->changeState($choice->[2]);
                   1321:             }
                   1322:         }
                   1323:     }
                   1324:     return 1;
                   1325: }
                   1326: 1;
                   1327: 
                   1328: package Apache::lonhelper::dropdown;
                   1329: 
                   1330: =pod
                   1331: 
1.44      bowersj2 1332: =head2 Element: dropdownX<dropdown, helper tag>
1.42      bowersj2 1333: 
                   1334: A drop-down provides a drop-down box instead of a radio button
                   1335: box. Because most people do not know how to use a multi-select
                   1336: drop-down box, that option is not allowed. Otherwise, the arguments
                   1337: are the same as "choices", except "allowempty" is also meaningless.
                   1338: 
                   1339: <dropdown> takes an attribute "variable" to control which helper variable
                   1340: the result is stored in.
                   1341: 
1.44      bowersj2 1342: =head3 SUB-TAGS
1.42      bowersj2 1343: 
                   1344: <choice>, which acts just as it does in the "choices" element.
                   1345: 
                   1346: =cut
                   1347: 
1.57      albertel 1348: # This really ought to be a sibling class to "choice" which is itself
                   1349: # a child of some abstract class.... *shrug*
                   1350: 
1.42      bowersj2 1351: no strict;
                   1352: @ISA = ("Apache::lonhelper::element");
                   1353: use strict;
1.57      albertel 1354: use Apache::lonlocal;
1.100     albertel 1355: use Apache::lonnet;
1.42      bowersj2 1356: 
                   1357: BEGIN {
                   1358:     &Apache::lonhelper::register('Apache::lonhelper::dropdown',
                   1359:                               ('dropdown'));
                   1360: }
                   1361: 
                   1362: sub new {
                   1363:     my $ref = Apache::lonhelper::element->new();
                   1364:     bless($ref);
                   1365: }
                   1366: 
                   1367: # CONSTRUCTION: Construct the message element from the XML
                   1368: sub start_dropdown {
                   1369:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
                   1370: 
                   1371:     if ($target ne 'helper') {
                   1372:         return '';
                   1373:     }
                   1374: 
                   1375:     # Need to initialize the choices list, so everything can assume it exists
                   1376:     $paramHash->{'variable'} = $token->[2]{'variable'} if (!defined($paramHash->{'variable'}));
                   1377:     $helper->declareVar($paramHash->{'variable'});
                   1378:     $paramHash->{CHOICES} = [];
                   1379:     return '';
                   1380: }
                   1381: 
                   1382: sub end_dropdown {
                   1383:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
                   1384: 
                   1385:     if ($target ne 'helper') {
                   1386:         return '';
                   1387:     }
                   1388:     Apache::lonhelper::dropdown->new();
                   1389:     return '';
                   1390: }
                   1391: 
                   1392: sub render {
                   1393:     my $self = shift;
                   1394:     my $var = $self->{'variable'};
                   1395:     my $result = '';
                   1396: 
                   1397:     if (defined $self->{ERROR_MSG}) {
                   1398:         $result .= '<br /><font color="#FF0000">' . $self->{ERROR_MSG} . '</font><br />';
                   1399:     }
                   1400: 
                   1401:     my %checkedChoices;
                   1402:     my $checkedChoicesFunc;
                   1403: 
                   1404:     if (defined($self->{DEFAULT_VALUE})) {
                   1405:         $checkedChoicesFunc = eval ($self->{DEFAULT_VALUE});
                   1406:         die 'Error in default value code for variable ' . 
                   1407:             $self->{'variable'} . ', Perl said: ' . $@ if $@;
                   1408:     } else {
                   1409:         $checkedChoicesFunc = sub { return ''; };
                   1410:     }
                   1411: 
                   1412:     # single choice
                   1413:     my $selectedChoice = &$checkedChoicesFunc($helper, $self);
                   1414:     
                   1415:     my $foundChoice = 0;
                   1416:     
                   1417:     # check that the choice is in the list of choices.
                   1418:     for my $choice (@{$self->{CHOICES}}) {
                   1419: 	if ($choice->[1] eq $selectedChoice) {
                   1420: 	    $checkedChoices{$choice->[1]} = 1;
                   1421: 	    $foundChoice = 1;
                   1422: 	}
                   1423:     }
                   1424:     
                   1425:     # If we couldn't find the choice, pick the first one 
                   1426:     if (!$foundChoice) {
                   1427: 	$checkedChoices{$self->{CHOICES}->[0]->[1]} = 1;
                   1428:     }
                   1429: 
                   1430:     $result .= "<select name='${var}.forminput'>\n";
                   1431:     foreach my $choice (@{$self->{CHOICES}}) {
                   1432:         $result .= "<option value='" . 
1.89      foxr     1433:             HTML::Entities::encode($choice->[1],"<>&\"'") 
1.42      bowersj2 1434:             . "'";
                   1435:         if ($checkedChoices{$choice->[1]}) {
1.111     albertel 1436:             $result .= " selected='selected' ";
1.42      bowersj2 1437:         }
                   1438:         my $choiceLabel = $choice->[0];
                   1439:         if ($choice->[4]) {  # if we need to evaluate this choice
                   1440:             $choiceLabel = "sub { my $helper = shift; my $state = shift;" .
                   1441:                 $choiceLabel . "}";
                   1442:             $choiceLabel = eval($choiceLabel);
                   1443:             $choiceLabel = &$choiceLabel($helper, $self);
                   1444:         }
1.112     albertel 1445:         $result .= ">" . &mtn($choiceLabel) . "</option>\n";
1.42      bowersj2 1446:     }
1.43      bowersj2 1447:     $result .= "</select>\n";
1.42      bowersj2 1448: 
                   1449:     return $result;
                   1450: }
                   1451: 
                   1452: # If a NEXTSTATE was given or a nextstate for this choice was
                   1453: # given, switch to it
                   1454: sub postprocess {
                   1455:     my $self = shift;
1.100     albertel 1456:     my $chosenValue = $env{'form.' . $self->{'variable'} . '.forminput'};
1.42      bowersj2 1457: 
                   1458:     if (!defined($chosenValue) && !$self->{'allowempty'}) {
                   1459:         $self->{ERROR_MSG} = "You must choose one or more choices to" .
                   1460:             " continue.";
                   1461:         return 0;
1.6       bowersj2 1462:     }
                   1463: 
1.5       bowersj2 1464:     if (defined($self->{NEXTSTATE})) {
                   1465:         $helper->changeState($self->{NEXTSTATE});
                   1466:     }
                   1467:     
                   1468:     foreach my $choice (@{$self->{CHOICES}}) {
                   1469:         if ($choice->[1] eq $chosenValue) {
                   1470:             if (defined($choice->[2])) {
                   1471:                 $helper->changeState($choice->[2]);
                   1472:             }
                   1473:         }
                   1474:     }
1.6       bowersj2 1475:     return 1;
1.5       bowersj2 1476: }
                   1477: 1;
                   1478: 
                   1479: package Apache::lonhelper::date;
                   1480: 
                   1481: =pod
                   1482: 
1.44      bowersj2 1483: =head2 Element: dateX<date, helper element>
1.5       bowersj2 1484: 
                   1485: Date elements allow the selection of a date with a drop down list.
                   1486: 
                   1487: Date elements can take two attributes:
                   1488: 
                   1489: =over 4
                   1490: 
                   1491: =item * B<variable>: The name of the variable to store the chosen
                   1492:         date in. Required.
                   1493: 
                   1494: =item * B<hoursminutes>: If a true value, the date will show hours
                   1495:         and minutes, as well as month/day/year. If false or missing,
                   1496:         the date will only show the month, day, and year.
                   1497: 
                   1498: =back
                   1499: 
                   1500: Date elements contain only an option <nextstate> tag to determine
                   1501: the next state.
                   1502: 
                   1503: Example:
                   1504: 
                   1505:  <date variable="DUE_DATE" hoursminutes="1">
                   1506:    <nextstate>choose_why</nextstate>
                   1507:    </date>
                   1508: 
                   1509: =cut
                   1510: 
                   1511: no strict;
                   1512: @ISA = ("Apache::lonhelper::element");
                   1513: use strict;
1.57      albertel 1514: use Apache::lonlocal; # A localization nightmare
1.100     albertel 1515: use Apache::lonnet;
1.5       bowersj2 1516: use Time::localtime;
                   1517: 
                   1518: BEGIN {
1.7       bowersj2 1519:     &Apache::lonhelper::register('Apache::lonhelper::date',
1.5       bowersj2 1520:                               ('date'));
                   1521: }
                   1522: 
                   1523: # Don't need to override the "new" from element
                   1524: sub new {
                   1525:     my $ref = Apache::lonhelper::element->new();
                   1526:     bless($ref);
                   1527: }
                   1528: 
                   1529: my @months = ("January", "February", "March", "April", "May", "June", "July",
                   1530: 	      "August", "September", "October", "November", "December");
                   1531: 
                   1532: # CONSTRUCTION: Construct the message element from the XML
                   1533: sub start_date {
                   1534:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
                   1535: 
                   1536:     if ($target ne 'helper') {
                   1537:         return '';
                   1538:     }
                   1539: 
                   1540:     $paramHash->{'variable'} = $token->[2]{'variable'};
                   1541:     $helper->declareVar($paramHash->{'variable'});
                   1542:     $paramHash->{'hoursminutes'} = $token->[2]{'hoursminutes'};
1.118     albertel 1543:     $paramHash->{'anytime'} = $token->[2]{'anytime'};
1.5       bowersj2 1544: }
                   1545: 
                   1546: sub end_date {
                   1547:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
                   1548: 
                   1549:     if ($target ne 'helper') {
                   1550:         return '';
                   1551:     }
                   1552:     Apache::lonhelper::date->new();
                   1553:     return '';
                   1554: }
                   1555: 
                   1556: sub render {
                   1557:     my $self = shift;
                   1558:     my $result = "";
                   1559:     my $var = $self->{'variable'};
                   1560: 
                   1561:     my $date;
1.118     albertel 1562: 
                   1563:     my $time=time;
1.130   ! albertel 1564:     $date = localtime($time);
        !          1565:     $date->min(0);
1.119     albertel 1566:     my ($anytime,$onclick);
1.118     albertel 1567: 
                   1568:     if (defined($self->{DEFAULT_VALUE})) {
                   1569:         my $valueFunc = eval($self->{DEFAULT_VALUE});
                   1570:         die('Error in default value code for variable ' . 
                   1571:             $self->{'variable'} . ', Perl said: ' . $@) if $@;
                   1572:         $time = &$valueFunc($helper, $self);
1.130   ! albertel 1573: 	if (lc($time) eq 'anytime') {
        !          1574: 	    $anytime=1;
        !          1575: 	} else {
        !          1576: 	    $date = localtime($time);
        !          1577: 	}
        !          1578:     } else {
        !          1579: 
1.118     albertel 1580:     }
1.119     albertel 1581:     if ($anytime) {
                   1582: 	$onclick = "onclick=\"javascript:updateCheck(this.form,'${var}anytime',false)\"";
                   1583:     }
1.5       bowersj2 1584:     # Default date: The current hour.
                   1585: 
                   1586:     if (defined $self->{ERROR_MSG}) {
                   1587:         $result .= '<font color="#FF0000">' . $self->{ERROR_MSG} . '</font><br /><br />';
                   1588:     }
                   1589: 
                   1590:     # Month
                   1591:     my $i;
1.119     albertel 1592:     $result .= "<select $onclick name='${var}month'>\n";
1.5       bowersj2 1593:     for ($i = 0; $i < 12; $i++) {
                   1594:         if ($i == $date->mon) {
1.111     albertel 1595:             $result .= "<option value='$i' selected='selected'>";
1.5       bowersj2 1596:         } else {
                   1597:             $result .= "<option value='$i'>";
                   1598:         }
1.57      albertel 1599:         $result .= &mt($months[$i]) . "</option>\n";
1.5       bowersj2 1600:     }
                   1601:     $result .= "</select>\n";
                   1602: 
                   1603:     # Day
1.119     albertel 1604:     $result .= "<select $onclick name='${var}day'>\n";
1.5       bowersj2 1605:     for ($i = 1; $i < 32; $i++) {
                   1606:         if ($i == $date->mday) {
1.111     albertel 1607:             $result .= '<option selected="selected">';
1.5       bowersj2 1608:         } else {
                   1609:             $result .= '<option>';
                   1610:         }
                   1611:         $result .= "$i</option>\n";
                   1612:     }
                   1613:     $result .= "</select>,\n";
                   1614: 
                   1615:     # Year
1.119     albertel 1616:     $result .= "<select $onclick name='${var}year'>\n";
1.5       bowersj2 1617:     for ($i = 2000; $i < 2030; $i++) { # update this after 64-bit dates
                   1618:         if ($date->year + 1900 == $i) {
1.111     albertel 1619:             $result .= "<option selected='selected'>";
1.5       bowersj2 1620:         } else {
                   1621:             $result .= "<option>";
                   1622:         }
                   1623:         $result .= "$i</option>\n";
                   1624:     }
                   1625:     $result .= "</select>,\n";
                   1626: 
                   1627:     # Display Hours and Minutes if they are called for
                   1628:     if ($self->{'hoursminutes'}) {
1.59      bowersj2 1629: 	# This needs parameterization for times.
                   1630: 	my $am = &mt('a.m.');
                   1631: 	my $pm = &mt('p.m.');
1.5       bowersj2 1632:         # Build hour
1.119     albertel 1633:         $result .= "<select $onclick name='${var}hour'>\n";
1.111     albertel 1634:         $result .= "<option " . ($date->hour == 0 ? 'selected="selected" ':'') .
1.59      bowersj2 1635:             " value='0'>" . &mt('midnight') . "</option>\n";
1.5       bowersj2 1636:         for ($i = 1; $i < 12; $i++) {
                   1637:             if ($date->hour == $i) {
1.111     albertel 1638:                 $result .= "<option selected='selected' value='$i'>$i $am</option>\n";
1.5       bowersj2 1639:             } else {
1.59      bowersj2 1640:                 $result .= "<option value='$i'>$i $am</option>\n";
1.5       bowersj2 1641:             }
                   1642:         }
1.111     albertel 1643:         $result .= "<option " . ($date->hour == 12 ? 'selected="selected" ':'') .
1.59      bowersj2 1644:             " value='12'>" . &mt('noon') . "</option>\n";
1.5       bowersj2 1645:         for ($i = 13; $i < 24; $i++) {
                   1646:             my $printedHour = $i - 12;
                   1647:             if ($date->hour == $i) {
1.111     albertel 1648:                 $result .= "<option selected='selected' value='$i'>$printedHour $pm</option>\n";
1.5       bowersj2 1649:             } else {
1.59      bowersj2 1650:                 $result .= "<option value='$i'>$printedHour $pm</option>\n";
1.5       bowersj2 1651:             }
                   1652:         }
                   1653: 
                   1654:         $result .= "</select> :\n";
                   1655: 
1.119     albertel 1656:         $result .= "<select $onclick name='${var}minute'>\n";
1.120     albertel 1657: 	my $selected=0;
                   1658:         for my $i ((0,15,30,45,59,undef,0..59)) {
1.5       bowersj2 1659:             my $printedMinute = $i;
1.117     albertel 1660:             if (defined($i) && $i < 10) {
1.5       bowersj2 1661:                 $printedMinute = "0" . $printedMinute;
                   1662:             }
1.120     albertel 1663:             if (!$selected && $date->min == $i) {
1.111     albertel 1664:                 $result .= "<option selected='selected'>";
1.120     albertel 1665: 		$selected=1;
1.5       bowersj2 1666:             } else {
                   1667:                 $result .= "<option>";
                   1668:             }
                   1669:             $result .= "$printedMinute</option>\n";
                   1670:         }
                   1671:         $result .= "</select>\n";
                   1672:     }
1.118     albertel 1673:     if ($self->{'anytime'}) {
1.121     albertel 1674: 	$result.=(<<CHECK);
1.119     albertel 1675: <script type="text/javascript">
                   1676: // <!--
                   1677:     function updateCheck(form,name,value) {
                   1678: 	var checkbox=form[name];
                   1679: 	checkbox.checked = value;
                   1680:     }
                   1681: // -->
                   1682: </script>
                   1683: CHECK
1.118     albertel 1684: 	$result.="&nbsp;or&nbsp;<label><input type='checkbox' ";
                   1685: 	if ($anytime) {
                   1686: 	    $result.=' checked="checked" '
                   1687: 	}
                   1688: 	$result.="name='${var}anytime'/>".&mt('Anytime').'</label>'
                   1689:     }
1.5       bowersj2 1690:     return $result;
                   1691: 
                   1692: }
                   1693: # If a NEXTSTATE was given, switch to it
                   1694: sub postprocess {
                   1695:     my $self = shift;
                   1696:     my $var = $self->{'variable'};
1.118     albertel 1697:     if ($env{'form.' . $var . 'anytime'}) {
                   1698: 	$helper->{VARS}->{$var} = undef;
                   1699:     } else {
                   1700: 	my $month = $env{'form.' . $var . 'month'}; 
                   1701: 	my $day = $env{'form.' . $var . 'day'}; 
                   1702: 	my $year = $env{'form.' . $var . 'year'}; 
                   1703: 	my $min = 0; 
                   1704: 	my $hour = 0;
                   1705: 	if ($self->{'hoursminutes'}) {
                   1706: 	    $min = $env{'form.' . $var . 'minute'};
                   1707: 	    $hour = $env{'form.' . $var . 'hour'};
                   1708: 	}
                   1709: 
                   1710: 	my $chosenDate;
                   1711: 	eval {$chosenDate = Time::Local::timelocal(0, $min, $hour, $day, $month, $year);};
                   1712: 	my $error = $@;
                   1713: 
                   1714: 	# Check to make sure that the date was not automatically co-erced into a 
                   1715: 	# valid date, as we want to flag that as an error
                   1716: 	# This happens for "Feb. 31", for instance, which is coerced to March 2 or
                   1717: 	# 3, depending on if it's a leap year
                   1718: 	my $checkDate = localtime($chosenDate);
                   1719: 	
                   1720: 	if ($error || $checkDate->mon != $month || $checkDate->mday != $day ||
                   1721: 	    $checkDate->year + 1900 != $year) {
                   1722: 	    unless (Apache::lonlocal::current_language()== ~/^en/) {
                   1723: 		$self->{ERROR_MSG} = &mt("Invalid date entry");
                   1724: 		return 0;
                   1725: 	    }
                   1726: 	    # LOCALIZATION FIXME: Needs to be parameterized
                   1727: 	    $self->{ERROR_MSG} = "Can't use " . $months[$month] . " $day, $year as a "
                   1728: 		. "date because it doesn't exist. Please enter a valid date.";
1.5       bowersj2 1729: 
1.57      albertel 1730: 	    return 0;
                   1731: 	}
1.118     albertel 1732: 	$helper->{VARS}->{$var} = $chosenDate;
1.5       bowersj2 1733:     }
                   1734: 
                   1735:     if (defined($self->{NEXTSTATE})) {
                   1736:         $helper->changeState($self->{NEXTSTATE});
                   1737:     }
1.6       bowersj2 1738: 
                   1739:     return 1;
1.5       bowersj2 1740: }
                   1741: 1;
                   1742: 
                   1743: package Apache::lonhelper::resource;
                   1744: 
                   1745: =pod
                   1746: 
1.44      bowersj2 1747: =head2 Element: resourceX<resource, helper element>
1.5       bowersj2 1748: 
                   1749: <resource> elements allow the user to select one or multiple resources
                   1750: from the current course. You can filter out which resources they can view,
                   1751: and filter out which resources they can select. The course will always
                   1752: be displayed fully expanded, because of the difficulty of maintaining
                   1753: selections across folder openings and closings. If this is fixed, then
                   1754: the user can manipulate the folders.
                   1755: 
                   1756: <resource> takes the standard variable attribute to control what helper
1.44      bowersj2 1757: variable stores the results. It also takes a "multichoice"X<multichoice> attribute,
1.17      bowersj2 1758: which controls whether the user can select more then one resource. The 
                   1759: "toponly" attribute controls whether the resource display shows just the
                   1760: resources in that sequence, or recurses into all sub-sequences, defaulting
1.29      bowersj2 1761: to false. The "suppressEmptySequences" attribute reflects the 
                   1762: suppressEmptySequences argument to the render routine, which will cause
                   1763: folders that have all of their contained resources filtered out to also
1.46      bowersj2 1764: be filtered out. The 'addstatus' attribute, if true, will add the icon
1.95      albertel 1765: and long status display columns to the display. The 'addparts'
                   1766: attribute will add in a part selector beside problems that have more
                   1767: than 1 part.
1.5       bowersj2 1768: 
1.44      bowersj2 1769: =head3 SUB-TAGS
1.5       bowersj2 1770: 
                   1771: =over 4
                   1772: 
1.44      bowersj2 1773: =item * <filterfunc>X<filterfunc>: If you want to filter what resources are displayed
1.5       bowersj2 1774:   to the user, use a filter func. The <filterfunc> tag should contain
                   1775:   Perl code that when wrapped with "sub { my $res = shift; " and "}" is 
                   1776:   a function that returns true if the resource should be displayed, 
                   1777:   and false if it should be skipped. $res is a resource object. 
                   1778:   (See Apache::lonnavmaps documentation for information about the 
                   1779:   resource object.)
                   1780: 
1.44      bowersj2 1781: =item * <choicefunc>X<choicefunc>: Same as <filterfunc>, except that controls whether
1.5       bowersj2 1782:   the given resource can be chosen. (It is almost always a good idea to
                   1783:   show the user the folders, for instance, but you do not always want to 
                   1784:   let the user select them.)
                   1785: 
                   1786: =item * <nextstate>: Standard nextstate behavior.
                   1787: 
1.44      bowersj2 1788: =item * <valuefunc>X<valuefunc>: This function controls what is returned by the resource
1.5       bowersj2 1789:   when the user selects it. Like filterfunc and choicefunc, it should be
                   1790:   a function fragment that when wrapped by "sub { my $res = shift; " and
                   1791:   "}" returns a string representing what you want to have as the value. By
                   1792:   default, the value will be the resource ID of the object ($res->{ID}).
                   1793: 
1.44      bowersj2 1794: =item * <mapurl>X<mapurl>: If the URL of a map is given here, only that map
1.48      bowersj2 1795:   will be displayed, instead of the whole course. If the attribute
                   1796:   "evaluate" is given and is true, the contents of the mapurl will be
                   1797:   evaluated with "sub { my $helper = shift; my $state = shift;" and
                   1798:   "}", with the return value used as the mapurl.
1.13      bowersj2 1799: 
1.5       bowersj2 1800: =back
                   1801: 
                   1802: =cut
                   1803: 
                   1804: no strict;
                   1805: @ISA = ("Apache::lonhelper::element");
                   1806: use strict;
1.100     albertel 1807: use Apache::lonnet;
1.5       bowersj2 1808: 
                   1809: BEGIN {
1.7       bowersj2 1810:     &Apache::lonhelper::register('Apache::lonhelper::resource',
1.5       bowersj2 1811:                               ('resource', 'filterfunc', 
1.13      bowersj2 1812:                                'choicefunc', 'valuefunc',
1.90      foxr     1813:                                'mapurl','option'));
1.5       bowersj2 1814: }
                   1815: 
                   1816: sub new {
                   1817:     my $ref = Apache::lonhelper::element->new();
                   1818:     bless($ref);
                   1819: }
                   1820: 
                   1821: # CONSTRUCTION: Construct the message element from the XML
                   1822: sub start_resource {
                   1823:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
                   1824: 
                   1825:     if ($target ne 'helper') {
                   1826:         return '';
                   1827:     }
                   1828: 
                   1829:     $paramHash->{'variable'} = $token->[2]{'variable'};
                   1830:     $helper->declareVar($paramHash->{'variable'});
1.14      bowersj2 1831:     $paramHash->{'multichoice'} = $token->[2]{'multichoice'};
1.29      bowersj2 1832:     $paramHash->{'suppressEmptySequences'} = $token->[2]{'suppressEmptySequences'};
1.17      bowersj2 1833:     $paramHash->{'toponly'} = $token->[2]{'toponly'};
1.46      bowersj2 1834:     $paramHash->{'addstatus'} = $token->[2]{'addstatus'};
1.95      albertel 1835:     $paramHash->{'addparts'} = $token->[2]{'addparts'};
                   1836:     if ($paramHash->{'addparts'}) {
                   1837: 	$helper->declareVar($paramHash->{'variable'}.'_part');
                   1838:     }
1.66      albertel 1839:     $paramHash->{'closeallpages'} = $token->[2]{'closeallpages'};
1.5       bowersj2 1840:     return '';
                   1841: }
                   1842: 
                   1843: sub end_resource {
                   1844:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
                   1845: 
                   1846:     if ($target ne 'helper') {
                   1847:         return '';
                   1848:     }
                   1849:     if (!defined($paramHash->{FILTER_FUNC})) {
                   1850:         $paramHash->{FILTER_FUNC} = sub {return 1;};
                   1851:     }
                   1852:     if (!defined($paramHash->{CHOICE_FUNC})) {
                   1853:         $paramHash->{CHOICE_FUNC} = sub {return 1;};
                   1854:     }
                   1855:     if (!defined($paramHash->{VALUE_FUNC})) {
                   1856:         $paramHash->{VALUE_FUNC} = sub {my $res = shift; return $res->{ID}; };
                   1857:     }
                   1858:     Apache::lonhelper::resource->new();
1.4       bowersj2 1859:     return '';
                   1860: }
                   1861: 
1.5       bowersj2 1862: sub start_filterfunc {
                   1863:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
                   1864: 
                   1865:     if ($target ne 'helper') {
                   1866:         return '';
                   1867:     }
                   1868: 
                   1869:     my $contents = Apache::lonxml::get_all_text('/filterfunc',
                   1870:                                                 $parser);
                   1871:     $contents = 'sub { my $res = shift; ' . $contents . '}';
                   1872:     $paramHash->{FILTER_FUNC} = eval $contents;
                   1873: }
                   1874: 
                   1875: sub end_filterfunc { return ''; }
                   1876: 
                   1877: sub start_choicefunc {
                   1878:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
                   1879: 
                   1880:     if ($target ne 'helper') {
                   1881:         return '';
                   1882:     }
                   1883: 
                   1884:     my $contents = Apache::lonxml::get_all_text('/choicefunc',
                   1885:                                                 $parser);
                   1886:     $contents = 'sub { my $res = shift; ' . $contents . '}';
                   1887:     $paramHash->{CHOICE_FUNC} = eval $contents;
                   1888: }
                   1889: 
                   1890: sub end_choicefunc { return ''; }
                   1891: 
                   1892: sub start_valuefunc {
                   1893:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
                   1894: 
                   1895:     if ($target ne 'helper') {
                   1896:         return '';
                   1897:     }
                   1898: 
                   1899:     my $contents = Apache::lonxml::get_all_text('/valuefunc',
                   1900:                                                 $parser);
                   1901:     $contents = 'sub { my $res = shift; ' . $contents . '}';
                   1902:     $paramHash->{VALUE_FUNC} = eval $contents;
                   1903: }
                   1904: 
                   1905: sub end_valuefunc { return ''; }
                   1906: 
1.13      bowersj2 1907: sub start_mapurl {
                   1908:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
                   1909: 
                   1910:     if ($target ne 'helper') {
                   1911:         return '';
                   1912:     }
                   1913: 
                   1914:     my $contents = Apache::lonxml::get_all_text('/mapurl',
                   1915:                                                 $parser);
1.48      bowersj2 1916:     $paramHash->{EVAL_MAP_URL} = $token->[2]{'evaluate'};
1.14      bowersj2 1917:     $paramHash->{MAP_URL} = $contents;
1.13      bowersj2 1918: }
                   1919: 
                   1920: sub end_mapurl { return ''; }
                   1921: 
1.90      foxr     1922: 
                   1923: sub start_option {
                   1924:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
                   1925:     if (!defined($paramHash->{OPTION_TEXTS})) {
                   1926: 	$paramHash->{OPTION_TEXTS} = [ ];
                   1927: 	$paramHash->{OPTION_VARS}  = [ ];
1.91      foxr     1928: 
1.90      foxr     1929:     }
1.91      foxr     1930:     # OPTION_TEXTS is a list of the text attribute
                   1931:     #               values used to create column headings.
                   1932:     # OPTION_VARS is a list of the variable names, used to create the checkbox
                   1933:     #             inputs.
1.90      foxr     1934:     #  We're ok with empty elements. as place holders
                   1935:     # Although the 'variable' element should really exist.
1.91      foxr     1936:     #
                   1937: 
1.90      foxr     1938:     my $option_texts  = $paramHash->{OPTION_TEXTS};
                   1939:     my $option_vars   = $paramHash->{OPTION_VARS};
                   1940:     push(@$option_texts,  $token->[2]{'text'});
                   1941:     push(@$option_vars,   $token->[2]{'variable'});
                   1942: 
1.91      foxr     1943:     #  Need to create and declare the option variables as well to make them
                   1944:     # persistent.
                   1945:     #
                   1946:     my $varname = $token->[2]{'variable'};
                   1947:     $helper->declareVar($varname);
                   1948: 
                   1949: 
1.90      foxr     1950:     return '';
                   1951: }
                   1952: 
                   1953: sub end_option {
                   1954:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
                   1955:     return '';
                   1956: }
                   1957: 
1.5       bowersj2 1958: # A note, in case I don't get to this before I leave.
                   1959: # If someone complains about the "Back" button returning them
                   1960: # to the previous folder state, instead of returning them to
                   1961: # the previous helper state, the *correct* answer is for the helper
                   1962: # to keep track of how many times the user has manipulated the folders,
                   1963: # and feed that to the history.go() call in the helper rendering routines.
                   1964: # If done correctly, the helper itself can keep track of how many times
                   1965: # it renders the same states, so it doesn't go in just this state, and
                   1966: # you can lean on the browser back button to make sure it all chains
                   1967: # correctly.
                   1968: # Right now, though, I'm just forcing all folders open.
                   1969: 
                   1970: sub render {
                   1971:     my $self = shift;
                   1972:     my $result = "";
                   1973:     my $var = $self->{'variable'};
                   1974:     my $curVal = $helper->{VARS}->{$var};
                   1975: 
1.15      bowersj2 1976:     my $buttons = '';
                   1977: 
                   1978:     if ($self->{'multichoice'}) {
                   1979:         $result = <<SCRIPT;
1.112     albertel 1980: <script type="text/javascript">
                   1981: // <!--
1.18      bowersj2 1982:     function checkall(value, checkName) {
1.15      bowersj2 1983: 	for (i=0; i<document.forms.helpform.elements.length; i++) {
                   1984:             ele = document.forms.helpform.elements[i];
1.18      bowersj2 1985:             if (ele.name == checkName + '.forminput') {
1.15      bowersj2 1986:                 document.forms.helpform.elements[i].checked=value;
                   1987:             }
                   1988:         }
                   1989:     }
1.112     albertel 1990: // -->
1.15      bowersj2 1991: </script>
                   1992: SCRIPT
1.68      sakharuk 1993:         my %lt=&Apache::lonlocal::texthash(
                   1994: 			'sar'  => "Select All Resources",
                   1995: 		        'uar'  => "Unselect All Resources");
                   1996: 
1.15      bowersj2 1997:         $buttons = <<BUTTONS;
                   1998: <br /> &nbsp;
1.68      sakharuk 1999: <input type="button" onclick="checkall(true, '$var')" value="$lt{'sar'}" />
                   2000: <input type="button" onclick="checkall(false, '$var')" value="$lt{'uar'}" />
1.15      bowersj2 2001: <br /> &nbsp;
                   2002: BUTTONS
                   2003:     }
                   2004: 
1.5       bowersj2 2005:     if (defined $self->{ERROR_MSG}) {
1.14      bowersj2 2006:         $result .= '<br /><font color="#FF0000">' . $self->{ERROR_MSG} . '</font><br /><br />';
1.5       bowersj2 2007:     }
                   2008: 
1.15      bowersj2 2009:     $result .= $buttons;
                   2010: 
1.90      foxr     2011:     my $filterFunc     = $self->{FILTER_FUNC};
                   2012:     my $choiceFunc     = $self->{CHOICE_FUNC};
                   2013:     my $valueFunc      = $self->{VALUE_FUNC};
1.95      albertel 2014:     my $multichoice    = $self->{'multichoice'};
1.90      foxr     2015:     my $option_vars    = $self->{OPTION_VARS};
                   2016:     my $option_texts   = $self->{OPTION_TEXTS};
1.95      albertel 2017:     my $addparts       = $self->{'addparts'};
1.90      foxr     2018:     my $headings_done  = 0;
1.5       bowersj2 2019: 
1.48      bowersj2 2020:     # Evaluate the map url as needed
                   2021:     my $mapUrl;
1.49      bowersj2 2022:     if ($self->{EVAL_MAP_URL}) {
1.48      bowersj2 2023: 	my $mapUrlFunc = eval('sub { my $helper = shift; my $state = shift; ' . 
                   2024: 	    $self->{MAP_URL} . '}');
                   2025: 	$mapUrl = &$mapUrlFunc($helper, $self);
                   2026:     } else {
                   2027: 	$mapUrl = $self->{MAP_URL};
                   2028:     }
                   2029: 
1.125     albertel 2030:     my %defaultSymbs;
1.124     albertel 2031:     if (defined($self->{DEFAULT_VALUE})) {
                   2032:         my $valueFunc = eval($self->{DEFAULT_VALUE});
                   2033:         die 'Error in default value code for variable ' . 
                   2034:             $self->{'variable'} . ', Perl said: ' . $@ if $@;
1.125     albertel 2035:         my @defaultSymbs = &$valueFunc($helper, $self);
                   2036: 	if (!$multichoice && @defaultSymbs) { # only allowed 1
1.124     albertel 2037: 	    @defaultSymbs = ($defaultSymbs[0]);
                   2038: 	}
1.125     albertel 2039: 	%defaultSymbs = map { if ($_) {($_,1) } } @defaultSymbs;
                   2040: 	delete($defaultSymbs{''});
1.124     albertel 2041:     }
                   2042: 
1.5       bowersj2 2043:     # Create the composite function that renders the column on the nav map
                   2044:     # have to admit any language that lets me do this can't be all bad
                   2045:     #  - Jeremy (Pythonista) ;-)
                   2046:     my $checked = 0;
                   2047:     my $renderColFunc = sub {
                   2048:         my ($resource, $part, $params) = @_;
1.90      foxr     2049: 	my $result = "";
                   2050: 
                   2051: 	if(!$headings_done) {
                   2052: 	    if ($option_texts) {
                   2053: 		foreach my $text (@$option_texts) {
                   2054: 		    $result .= "<th>$text</th>";
                   2055: 		}
                   2056: 	    }
                   2057: 	    $result .= "<th>Select</th>";
                   2058: 	    $result .= "</tr><tr>"; # Close off the extra row and start a new one.
                   2059: 	    $headings_done = 1;
                   2060: 	}
1.14      bowersj2 2061: 
                   2062:         my $inputType;
                   2063:         if ($multichoice) { $inputType = 'checkbox'; }
                   2064:         else {$inputType = 'radio'; }
                   2065: 
1.5       bowersj2 2066:         if (!&$choiceFunc($resource)) {
1.90      foxr     2067: 	    $result .= '<td>&nbsp;</td>';
                   2068:             return $result;
1.5       bowersj2 2069:         } else {
1.90      foxr     2070: 	    my $col = "";
1.98      foxr     2071: 	    my $raw_name = &$valueFunc($resource);
1.90      foxr     2072: 	    my $resource_name =   
1.98      foxr     2073:                    HTML::Entities::encode($raw_name,"<>&\"'");
1.90      foxr     2074: 	    if($option_vars) {
1.91      foxr     2075: 		foreach my $option_var (@$option_vars) {
1.99      foxr     2076: 		    my $var_value = "\|\|\|" . $helper->{VARS}->{$option_var} . 
                   2077: 			"\|\|\|";
1.98      foxr     2078: 		    my $checked ="";
1.99      foxr     2079: 		    if($var_value =~ /\Q|||$raw_name|||\E/) {
1.111     albertel 2080: 			$checked = "checked='checked'";
1.98      foxr     2081: 		    }
1.90      foxr     2082: 		    $col .= 
1.91      foxr     2083:                         "<td align='center'><input type='checkbox' name ='$option_var".
                   2084: 			".forminput' value='".
1.98      foxr     2085: 			$resource_name . "' $checked /> </td>";
1.90      foxr     2086: 		}
                   2087: 	    }
                   2088: 
                   2089:             $col .= "<td align='center'><input type='$inputType' name='${var}.forminput' ";
1.125     albertel 2090: 	    if (%defaultSymbs) {
1.124     albertel 2091: 		my $symb=$resource->symb();
1.125     albertel 2092: 		if (exists($defaultSymbs{$symb})) {
1.124     albertel 2093: 		    $col .= "checked='checked' ";
                   2094: 		    $checked = 1;
                   2095: 		}
                   2096: 	    } else {
                   2097: 		if (!$checked && !$multichoice) {
                   2098: 		    $col .= "checked='checked' ";
                   2099: 		    $checked = 1;
                   2100: 		}
                   2101: 		if ($multichoice) { # all resources start checked; see bug 1174
                   2102: 		    $col .= "checked='checked' ";
                   2103: 		    $checked = 1;
                   2104: 		}
1.37      bowersj2 2105: 	    }
1.90      foxr     2106:             $col .= "value='" . $resource_name  . "' /></td>";
1.95      albertel 2107: 
1.90      foxr     2108:             return $result.$col;
1.5       bowersj2 2109:         }
                   2110:     };
1.95      albertel 2111:     my $renderPartsFunc = sub {
                   2112:         my ($resource, $part, $params) = @_;
                   2113: 	my $col= "<td>";
                   2114: 	my $id=$resource->{ID};
                   2115: 	my $resource_name =   
                   2116: 	    &HTML::Entities::encode(&$valueFunc($resource),"<>&\"'");
                   2117: 	if ($addparts && (scalar(@{$resource->parts}) > 1)) {
                   2118: 	    $col .= "<select onclick=\"javascript:updateRadio(this.form,'${var}.forminput','$resource_name');updateHidden(this.form,'$id','${var}');\" name='part_$id.forminput'>\n";
                   2119: 	    $col .= "<option value=\"$part\">All Parts</option>\n";
                   2120: 	    foreach my $part (@{$resource->parts}) {
                   2121: 		$col .= "<option value=\"$part\">Part: $part</option>\n";
                   2122: 	    }
                   2123: 	    $col .= "</select>";
                   2124: 	}
                   2125: 	$col .= "</td>";
                   2126:     };
                   2127:     $result.=(<<RADIO);
                   2128: <script type="text/javascript">
1.112     albertel 2129: // <!--
1.95      albertel 2130:     function updateRadio(form,name,value) {
                   2131: 	var radiobutton=form[name];
                   2132: 	for (var i=0; i<radiobutton.length; i++) {
                   2133: 	    if (radiobutton[i].value == value) {
                   2134: 		radiobutton[i].checked = true;
                   2135: 		break;
                   2136: 	    }
                   2137: 	}
                   2138:     }
                   2139:     function updateHidden(form,id,name) {
                   2140: 	var select=form['part_'+id+'.forminput'];
                   2141: 	var hidden=form[name+'_part.forminput'];
                   2142: 	var which=select.selectedIndex;
                   2143: 	hidden.value=select.options[which].value;
                   2144:     }
1.112     albertel 2145: // -->
1.95      albertel 2146: </script>
1.101     albertel 2147: <input type="hidden" name="${var}_part.forminput" />
1.5       bowersj2 2148: 
1.95      albertel 2149: RADIO
1.100     albertel 2150:     $env{'form.condition'} = !$self->{'toponly'};
1.95      albertel 2151:     my $cols = [$renderColFunc];
                   2152:     if ($self->{'addparts'}) { push(@$cols, $renderPartsFunc); }
                   2153:     push(@$cols, Apache::lonnavmaps::resource());
1.46      bowersj2 2154:     if ($self->{'addstatus'}) {
                   2155: 	push @$cols, (Apache::lonnavmaps::part_status_summary());
                   2156: 	
                   2157:     }
1.5       bowersj2 2158:     $result .= 
1.46      bowersj2 2159:         &Apache::lonnavmaps::render( { 'cols' => $cols,
1.5       bowersj2 2160:                                        'showParts' => 0,
                   2161:                                        'filterFunc' => $filterFunc,
1.13      bowersj2 2162:                                        'resource_no_folder_link' => 1,
1.66      albertel 2163: 				       'closeAllPages' => $self->{'closeallpages'},
1.29      bowersj2 2164:                                        'suppressEmptySequences' => $self->{'suppressEmptySequences'},
1.13      bowersj2 2165:                                        'iterator_map' => $mapUrl }
1.5       bowersj2 2166:                                        );
1.15      bowersj2 2167: 
                   2168:     $result .= $buttons;
1.5       bowersj2 2169:                                                 
                   2170:     return $result;
                   2171: }
                   2172:     
                   2173: sub postprocess {
                   2174:     my $self = shift;
1.14      bowersj2 2175: 
                   2176:     if ($self->{'multichoice'} && !$helper->{VARS}->{$self->{'variable'}}) {
                   2177:         $self->{ERROR_MSG} = 'You must choose at least one resource to continue.';
                   2178:         return 0;
                   2179:     }
                   2180: 
1.5       bowersj2 2181:     if (defined($self->{NEXTSTATE})) {
                   2182:         $helper->changeState($self->{NEXTSTATE});
                   2183:     }
1.6       bowersj2 2184: 
                   2185:     return 1;
1.5       bowersj2 2186: }
                   2187: 
                   2188: 1;
                   2189: 
                   2190: package Apache::lonhelper::student;
                   2191: 
                   2192: =pod
                   2193: 
1.44      bowersj2 2194: =head2 Element: studentX<student, helper element>
1.5       bowersj2 2195: 
                   2196: Student elements display a choice of students enrolled in the current
                   2197: course. Currently it is primitive; this is expected to evolve later.
                   2198: 
1.48      bowersj2 2199: Student elements take the following attributes: 
                   2200: 
                   2201: =over 4
                   2202: 
                   2203: =item * B<variable>: 
                   2204: 
                   2205: Does what it usually does: declare which helper variable to put the
                   2206: result in.
                   2207: 
                   2208: =item * B<multichoice>: 
                   2209: 
                   2210: If true allows the user to select multiple students. Defaults to false.
                   2211: 
                   2212: =item * B<coursepersonnel>: 
                   2213: 
                   2214: If true adds the course personnel to the top of the student
                   2215: selection. Defaults to false.
                   2216: 
                   2217: =item * B<activeonly>:
                   2218: 
                   2219: If true, only active students and course personnel will be
                   2220: shown. Defaults to false.
                   2221: 
1.123     albertel 2222: =item * B<emptyallowed>:
                   2223: 
                   2224: If true, the selection of no users is allowed. Defaults to false.
                   2225: 
1.48      bowersj2 2226: =back
1.5       bowersj2 2227: 
                   2228: =cut
                   2229: 
                   2230: no strict;
                   2231: @ISA = ("Apache::lonhelper::element");
                   2232: use strict;
1.59      bowersj2 2233: use Apache::lonlocal;
1.100     albertel 2234: use Apache::lonnet;
1.5       bowersj2 2235: 
                   2236: BEGIN {
1.7       bowersj2 2237:     &Apache::lonhelper::register('Apache::lonhelper::student',
1.5       bowersj2 2238:                               ('student'));
                   2239: }
                   2240: 
                   2241: sub new {
                   2242:     my $ref = Apache::lonhelper::element->new();
                   2243:     bless($ref);
                   2244: }
1.4       bowersj2 2245: 
1.5       bowersj2 2246: sub start_student {
1.4       bowersj2 2247:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
                   2248: 
                   2249:     if ($target ne 'helper') {
                   2250:         return '';
                   2251:     }
                   2252: 
1.5       bowersj2 2253:     $paramHash->{'variable'} = $token->[2]{'variable'};
                   2254:     $helper->declareVar($paramHash->{'variable'});
                   2255:     $paramHash->{'multichoice'} = $token->[2]{'multichoice'};
1.39      bowersj2 2256:     $paramHash->{'coursepersonnel'} = $token->[2]{'coursepersonnel'};
1.93      albertel 2257:     $paramHash->{'activeonly'} = $token->[2]{'activeonly'};
1.12      bowersj2 2258:     if (defined($token->[2]{'nextstate'})) {
                   2259:         $paramHash->{NEXTSTATE} = $token->[2]{'nextstate'};
                   2260:     }
1.123     albertel 2261:     $paramHash->{'emptyallowed'} = $token->[2]{'emptyallowed'};
1.12      bowersj2 2262:     
1.5       bowersj2 2263: }    
                   2264: 
                   2265: sub end_student {
                   2266:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
                   2267: 
                   2268:     if ($target ne 'helper') {
                   2269:         return '';
                   2270:     }
                   2271:     Apache::lonhelper::student->new();
1.3       bowersj2 2272: }
1.5       bowersj2 2273: 
                   2274: sub render {
                   2275:     my $self = shift;
                   2276:     my $result = '';
                   2277:     my $buttons = '';
1.18      bowersj2 2278:     my $var = $self->{'variable'};
1.5       bowersj2 2279: 
                   2280:     if ($self->{'multichoice'}) {
                   2281:         $result = <<SCRIPT;
1.112     albertel 2282: <script type="text/javascript">
                   2283: // <!--
1.18      bowersj2 2284:     function checkall(value, checkName) {
1.15      bowersj2 2285: 	for (i=0; i<document.forms.helpform.elements.length; i++) {
1.18      bowersj2 2286:             ele = document.forms.helpform.elements[i];
                   2287:             if (ele.name == checkName + '.forminput') {
                   2288:                 document.forms.helpform.elements[i].checked=value;
                   2289:             }
1.5       bowersj2 2290:         }
                   2291:     }
1.58      sakharuk 2292:     function checksec(value) {
1.53      sakharuk 2293: 	for (i=0; i<document.forms.helpform.elements.length; i++) {
                   2294: 	    comp = document.forms.helpform.elements.chksec.value;
                   2295:             if (document.forms.helpform.elements[i].value.indexOf(':'+comp+':') != -1) {
1.82      sakharuk 2296: 		if (document.forms.helpform.elements[i].value.indexOf(':Active') != -1) {
                   2297: 		    document.forms.helpform.elements[i].checked=value;
                   2298: 		}
1.53      sakharuk 2299:             }
                   2300:         }
                   2301:     }
                   2302:     function checkactive() {
                   2303: 	for (i=0; i<document.forms.helpform.elements.length; i++) {
                   2304:             if (document.forms.helpform.elements[i].value.indexOf(':Active') != -1) {
                   2305:                 document.forms.helpform.elements[i].checked=true;
1.82      sakharuk 2306:             } 
                   2307:         }
                   2308:     }
                   2309:     function uncheckexpired() {
                   2310: 	for (i=0; i<document.forms.helpform.elements.length; i++) {
                   2311:             if (document.forms.helpform.elements[i].value.indexOf(':Expired') != -1) {
                   2312:                 document.forms.helpform.elements[i].checked=false;
                   2313:             } 
1.53      sakharuk 2314:         }
                   2315:     }
1.113     foxr     2316:     function getDesiredState() {     // Return desired person state radio value.
                   2317:         numRadio = document.forms.helpform.personstate.length;
                   2318:         for (i =0; i < numRadio; i++) {
                   2319: 	    if (document.forms.helpform.personstate[i].checked) {
                   2320:                 return document.forms.helpform.personstate[i].value;
                   2321:             }
                   2322:         }
                   2323:         return "";
                   2324:     }
                   2325: 
                   2326:     function checksections(value) {    // Check selected sections.
                   2327:         numSections  = document.forms.helpform.chosensections.length;
                   2328: 	desiredState = getDesiredState();
                   2329: 
1.116     albertel 2330: 	for (var option = 0; option < numSections; option++) {
1.113     foxr     2331: 	    if(document.forms.helpform.chosensections.options[option].selected) {
                   2332: 		section = document.forms.helpform.chosensections.options[option].text;
1.116     albertel 2333: 		if (section == "none") {
1.113     foxr     2334: 		    section ="";
                   2335: 		}
                   2336: 		for (i = 0; i < document.forms.helpform.elements.length; i++ ) {
                   2337: 		    if (document.forms.helpform.elements[i].value.indexOf(':') != -1) {
                   2338: 			info = document.forms.helpform.elements[i].value.split(':');
                   2339: 			hisSection = info[2];
                   2340: 			hisState   = info[4];
1.116     albertel 2341: 			if (desiredState == hisState ||
                   2342: 			    desiredState == "All") {
                   2343: 			    if(hisSection == section ||
                   2344: 			       section =="" ) {
                   2345: 				document.forms.helpform.elements[i].checked = value;
                   2346: 			    }
1.113     foxr     2347: 			}
                   2348: 		    }
                   2349: 		}
                   2350:             }
                   2351: 	}
                   2352: 				   }
1.112     albertel 2353: // -->
1.5       bowersj2 2354: </script>
                   2355: SCRIPT
1.59      bowersj2 2356: 
1.68      sakharuk 2357:         my %lt=&Apache::lonlocal::texthash(
                   2358:                     'ocs'  => "Select Only Current Students",
1.82      sakharuk 2359:                     'ues'  => "Unselect Expired Students",
1.68      sakharuk 2360:                     'sas'  => "Select All Students",
                   2361:                     'uas'  => "Unselect All Students",
1.82      sakharuk 2362:                     'sfsg' => "Select Current Students for Section/Group",
1.68      sakharuk 2363: 		    'ufsg' => "Unselect for Section/Group");
                   2364:  
1.5       bowersj2 2365:         $buttons = <<BUTTONS;
                   2366: <br />
1.92      foxr     2367: <table>
                   2368:   
                   2369:   <tr>
                   2370:      <td><input type="button" onclick="checkall(true, '$var')" value="$lt{'sas'}" /></td>
                   2371:      <td> <input type="button" onclick="checkall(false, '$var')" value="$lt{'uas'}" /><br /></td>
                   2372:   </tr>
1.113     foxr     2373:   
1.92      foxr     2374: </table>
1.5       bowersj2 2375: <br />
                   2376: BUTTONS
                   2377:     }
                   2378: 
                   2379:     if (defined $self->{ERROR_MSG}) {
                   2380:         $result .= '<font color="#FF0000">' . $self->{ERROR_MSG} . '</font><br /><br />';
                   2381:     }
                   2382: 
1.126     albertel 2383:     my %defaultUsers;
                   2384:     if (defined($self->{DEFAULT_VALUE})) {
                   2385:         my $valueFunc = eval($self->{DEFAULT_VALUE});
                   2386:         die 'Error in default value code for variable ' . 
                   2387:             $self->{'variable'} . ', Perl said: ' . $@ if $@;
                   2388:         my @defaultUsers = &$valueFunc($helper, $self);
                   2389: 	if (!$self->{'multichoice'} && @defaultUsers) { # only allowed 1
                   2390: 	    @defaultUsers = ($defaultUsers[0]);
                   2391: 	}
                   2392: 	%defaultUsers = map { if ($_) {($_,1) } } @defaultUsers;
                   2393: 	delete($defaultUsers{''});
                   2394:     }
1.39      bowersj2 2395:     my $choices = [];
                   2396: 
                   2397:     # Load up the non-students, if necessary
                   2398:     if ($self->{'coursepersonnel'}) {
                   2399: 	my %coursepersonnel = Apache::lonnet::get_course_adv_roles();
                   2400: 	for (sort keys %coursepersonnel) {
                   2401: 	    for my $role (split /,/, $coursepersonnel{$_}) {
                   2402: 		# extract the names so we can sort them
                   2403: 		my @people;
                   2404: 		
                   2405: 		for (split /,/, $role) {
                   2406: 		    push @people, [split /:/, $role];
                   2407: 		}
                   2408: 		
                   2409: 		@people = sort { $a->[0] cmp $b->[0] } @people;
                   2410: 		
                   2411: 		for my $person (@people) {
                   2412: 		    push @$choices, [join(':', @$person), $person->[0], '', $_];
                   2413: 		}
                   2414: 	    }
                   2415: 	}
                   2416:     }
1.5       bowersj2 2417: 
                   2418:     # Constants
                   2419:     my $section = Apache::loncoursedata::CL_SECTION();
                   2420:     my $fullname = Apache::loncoursedata::CL_FULLNAME();
1.48      bowersj2 2421:     my $status = Apache::loncoursedata::CL_STATUS();
1.5       bowersj2 2422: 
1.39      bowersj2 2423:     # Load up the students
                   2424:     my $classlist = &Apache::loncoursedata::get_classlist();
                   2425:     my @keys = keys %{$classlist};
1.5       bowersj2 2426:     # Sort by: Section, name
                   2427:     @keys = sort {
1.39      bowersj2 2428:         if ($classlist->{$a}->[$section] ne $classlist->{$b}->[$section]) {
                   2429:             return $classlist->{$a}->[$section] cmp $classlist->{$b}->[$section];
1.5       bowersj2 2430:         }
1.39      bowersj2 2431:         return $classlist->{$a}->[$fullname] cmp $classlist->{$b}->[$fullname];
1.5       bowersj2 2432:     } @keys;
                   2433: 
1.39      bowersj2 2434:     # username, fullname, section, type
                   2435:     for (@keys) {
1.48      bowersj2 2436: 	# Filter out inactive students if we've set "activeonly"
                   2437: 	if (!$self->{'activeonly'} || $classlist->{$_}->[$status] eq
                   2438: 	    'Active') {
                   2439: 	    push @$choices, [$_, $classlist->{$_}->[$fullname], 
1.53      sakharuk 2440: 			     $classlist->{$_}->[$section],
                   2441: 			     $classlist->{$_}->[$status], 'Student'];
1.48      bowersj2 2442: 	}
1.39      bowersj2 2443:     }
                   2444: 
1.68      sakharuk 2445:     my $name = $self->{'coursepersonnel'} ? &mt('Name') : &mt('Student Name');
1.5       bowersj2 2446:     my $type = 'radio';
                   2447:     if ($self->{'multichoice'}) { $type = 'checkbox'; }
                   2448:     $result .= "<table cellspacing='2' cellpadding='2' border='0'>\n";
1.39      bowersj2 2449:     $result .= "<tr><td></td><td align='center'><b>$name</b></td>".
1.59      bowersj2 2450:         "<td align='center'><b>" . &mt('Section') . "</b></td>" . 
1.68      sakharuk 2451: 	"<td align='center'><b>".&mt('Status')."</b></td>" . 
                   2452: 	"<td align='center'><b>" . &mt("Role") . "</b></td>" .
                   2453: 	"<td align='center'><b>".&mt('Username').":".&mt('Domain')."</b></td></tr>";
1.5       bowersj2 2454: 
                   2455:     my $checked = 0;
1.39      bowersj2 2456:     for my $choice (@$choices) {
1.5       bowersj2 2457:         $result .= "<tr><td><input type='$type' name='" .
                   2458:             $self->{'variable'} . '.forminput' . "'";
                   2459:             
1.126     albertel 2460: 	if (%defaultUsers) {
                   2461: 	    my $user=$choice->[0];
                   2462: 	    if (exists($defaultUsers{$user})) {
                   2463: 		$result .= " checked='checked' ";
                   2464: 		$checked = 1;
                   2465: 	    }
                   2466: 	} elsif (!$self->{'multichoice'} && !$checked) {
1.111     albertel 2467:             $result .= " checked='checked' ";
1.5       bowersj2 2468:             $checked = 1;
                   2469:         }
                   2470:         $result .=
1.89      foxr     2471:             " value='" . HTML::Entities::encode($choice->[0] . ':' 
                   2472: 						.$choice->[2] . ':' 
                   2473: 						.$choice->[1] . ':' 
                   2474: 						.$choice->[3], "<>&\"'")
1.5       bowersj2 2475:             . "' /></td><td>"
1.67      albertel 2476:             . HTML::Entities::encode($choice->[1],'<>&"')
1.5       bowersj2 2477:             . "</td><td align='center'>" 
1.67      albertel 2478:             . HTML::Entities::encode($choice->[2],'<>&"')
1.39      bowersj2 2479:             . "</td>\n<td>" 
1.67      albertel 2480: 	    . HTML::Entities::encode($choice->[3],'<>&"')
1.53      sakharuk 2481:             . "</td>\n<td>" 
1.67      albertel 2482: 	    . HTML::Entities::encode($choice->[4],'<>&"')
1.53      sakharuk 2483:             . "</td>\n<td>" 
1.67      albertel 2484: 	    . HTML::Entities::encode($choice->[0],'<>&"')
1.53      sakharuk 2485: 	    . "</td></tr>\n";
1.5       bowersj2 2486:     }
                   2487: 
                   2488:     $result .= "</table>\n\n";
1.113     foxr     2489:     $result .= $buttons;   
                   2490:     #
                   2491:     #  now add the fancy section choice... first enumerate the sections:
1.116     albertel 2492:     if ($self->{'multichoice'}) {
                   2493: 	my %sections;
                   2494: 	for my $key (@keys) {
                   2495: 	    my $section_name = $classlist->{$key}->[$section];
                   2496: 	    if ($section_name ne "") {
                   2497: 		$sections{$section_name} = 1;
                   2498: 	    }
                   2499: 	}
                   2500: 	#  The variable $choice_widget will have the html to make the choice 
                   2501: 	#  selector.
                   2502: 	my $size=5;
                   2503: 	if (scalar(keys(%sections)) < 5) {
                   2504: 	    $size=scalar(keys(%sections));
                   2505: 	}
                   2506: 	my $choice_widget = '<select multiple name="chosensections" size="'.$size.'">'."\n";
                   2507: 	foreach my $sec (sort {lc($a) cmp lc($b)} (keys(%sections))) {
                   2508: 	    $choice_widget .= "<option name=\"$sec\">$sec</option>\n";
1.113     foxr     2509: 	}
1.116     albertel 2510: 	$choice_widget .= "<option>none</option></select>\n";
1.113     foxr     2511: 
1.116     albertel 2512: 	# Build a table without any borders to contain the section based
                   2513: 	# selection:
1.113     foxr     2514: 
1.116     albertel 2515: 	my $section_selectors =<<SECTIONSELECT;
                   2516: <table border="0">
                   2517:   <tr valign="top">
                   2518:    <td>For Sections:</td><td>$choice_widget</td>
                   2519:    <td><label><input type="radio" name="personstate" value="Active" checked />
                   2520:                Current Students</label></td>
                   2521:    <td><label><input type="radio" name="personstate" value="All" />
                   2522:                All students</label></td>
                   2523:    <td><label><input type="radio" name="personstate" value="Expired" />
                   2524:                Expired Students</label></td>
                   2525:   </tr>
                   2526:   <tr>
                   2527:    <td><input type="button" value="Select" onclick="checksections(true);" /></td>
                   2528:    <td><input type="button" value="Unselect" onclick="checksections(false);" /></td></tr>
                   2529: </table>
                   2530: <br />
                   2531: SECTIONSELECT
                   2532:          $result .= $section_selectors;
                   2533:     }
1.5       bowersj2 2534:     return $result;
                   2535: }
                   2536: 
1.6       bowersj2 2537: sub postprocess {
                   2538:     my $self = shift;
                   2539: 
1.100     albertel 2540:     my $result = $env{'form.' . $self->{'variable'} . '.forminput'};
1.123     albertel 2541:     if (!$result && !$self->{'emptyallowed'}) {
                   2542: 	if ($self->{'coursepersonnel'}) {
                   2543: 	    $self->{ERROR_MSG} = 
                   2544: 		&mt('You must choose at least one user to continue.');
                   2545: 	} else {
                   2546: 	    $self->{ERROR_MSG} = 
                   2547: 		&mt('You must choose at least one student to continue.');
                   2548: 	}
1.6       bowersj2 2549:         return 0;
                   2550:     }
                   2551: 
                   2552:     if (defined($self->{NEXTSTATE})) {
                   2553:         $helper->changeState($self->{NEXTSTATE});
                   2554:     }
                   2555: 
                   2556:     return 1;
                   2557: }
                   2558: 
1.5       bowersj2 2559: 1;
                   2560: 
                   2561: package Apache::lonhelper::files;
                   2562: 
                   2563: =pod
                   2564: 
1.44      bowersj2 2565: =head2 Element: filesX<files, helper element>
1.5       bowersj2 2566: 
                   2567: files allows the users to choose files from a given directory on the
                   2568: server. It is always multichoice and stores the result as a triple-pipe
                   2569: delimited entry in the helper variables. 
                   2570: 
                   2571: Since it is extremely unlikely that you can actually code a constant
                   2572: representing the directory you wish to allow the user to search, <files>
                   2573: takes a subroutine that returns the name of the directory you wish to
                   2574: have the user browse.
                   2575: 
                   2576: files accepts the attribute "variable" to control where the files chosen
                   2577: are put. It accepts the attribute "multichoice" as the other attribute,
                   2578: defaulting to false, which if true will allow the user to select more
                   2579: then one choice. 
                   2580: 
1.44      bowersj2 2581: <files> accepts three subtags: 
                   2582: 
                   2583: =over 4
                   2584: 
                   2585: =item * B<nextstate>: works as it does with the other tags. 
                   2586: 
                   2587: =item * B<filechoice>: When the contents of this tag are surrounded by
                   2588:     "sub {" and "}", will return a string representing what directory
                   2589:     on the server to allow the user to choose files from. 
                   2590: 
                   2591: =item * B<filefilter>: Should contain Perl code that when surrounded
                   2592:     by "sub { my $filename = shift; " and "}", returns a true value if
                   2593:     the user can pick that file, or false otherwise. The filename
                   2594:     passed to the function will be just the name of the file, with no
                   2595:     path info. By default, a filter function will be used that will
                   2596:     mask out old versions of files. This function is available as
                   2597:     Apache::lonhelper::files::not_old_version if you want to use it to
                   2598:     composite your own filters.
                   2599: 
                   2600: =back
                   2601: 
                   2602: B<General security note>: You should ensure the user can not somehow 
                   2603: pass something into your code that would allow them to look places 
                   2604: they should not be able to see, like the C</etc/> directory. However,
                   2605: the security impact would be minimal, since it would only expose
                   2606: the existence of files, there should be no way to parlay that into
                   2607: viewing the files. 
1.5       bowersj2 2608: 
                   2609: =cut
                   2610: 
                   2611: no strict;
                   2612: @ISA = ("Apache::lonhelper::element");
                   2613: use strict;
1.59      bowersj2 2614: use Apache::lonlocal;
1.100     albertel 2615: use Apache::lonnet;
1.32      bowersj2 2616: use Apache::lonpubdir; # for getTitleString
                   2617: 
1.5       bowersj2 2618: BEGIN {
1.7       bowersj2 2619:     &Apache::lonhelper::register('Apache::lonhelper::files',
                   2620:                                  ('files', 'filechoice', 'filefilter'));
1.5       bowersj2 2621: }
                   2622: 
1.44      bowersj2 2623: sub not_old_version {
                   2624:     my $file = shift;
                   2625:     
                   2626:     # Given a file name, return false if it is an "old version" of a
                   2627:     # file, or true if it is not.
                   2628: 
                   2629:     if ($file =~ /^.*\.[0-9]+\.[A-Za-z]+(\.meta)?$/) {
                   2630: 	return 0;
                   2631:     }
                   2632:     return 1;
                   2633: }
                   2634: 
1.5       bowersj2 2635: sub new {
                   2636:     my $ref = Apache::lonhelper::element->new();
                   2637:     bless($ref);
                   2638: }
                   2639: 
                   2640: sub start_files {
                   2641:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
                   2642: 
                   2643:     if ($target ne 'helper') {
                   2644:         return '';
                   2645:     }
                   2646:     $paramHash->{'variable'} = $token->[2]{'variable'};
                   2647:     $helper->declareVar($paramHash->{'variable'});
                   2648:     $paramHash->{'multichoice'} = $token->[2]{'multichoice'};
                   2649: }    
                   2650: 
                   2651: sub end_files {
                   2652:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
                   2653: 
                   2654:     if ($target ne 'helper') {
                   2655:         return '';
                   2656:     }
                   2657:     if (!defined($paramHash->{FILTER_FUNC})) {
                   2658:         $paramHash->{FILTER_FUNC} = sub { return 1; };
                   2659:     }
                   2660:     Apache::lonhelper::files->new();
                   2661: }    
                   2662: 
                   2663: sub start_filechoice {
                   2664:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
                   2665: 
                   2666:     if ($target ne 'helper') {
                   2667:         return '';
                   2668:     }
                   2669:     $paramHash->{'filechoice'} = Apache::lonxml::get_all_text('/filechoice',
                   2670:                                                               $parser);
                   2671: }
                   2672: 
                   2673: sub end_filechoice { return ''; }
                   2674: 
                   2675: sub start_filefilter {
                   2676:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
                   2677: 
                   2678:     if ($target ne 'helper') {
                   2679:         return '';
                   2680:     }
                   2681: 
                   2682:     my $contents = Apache::lonxml::get_all_text('/filefilter',
                   2683:                                                 $parser);
                   2684:     $contents = 'sub { my $filename = shift; ' . $contents . '}';
                   2685:     $paramHash->{FILTER_FUNC} = eval $contents;
                   2686: }
                   2687: 
                   2688: sub end_filefilter { return ''; }
1.3       bowersj2 2689: 
1.87      matthew  2690: { 
                   2691:     # used to generate unique id attributes for <input> tags. 
                   2692:     # internal use only.
                   2693:     my $id=0;
                   2694:     sub new_id { return $id++;}
                   2695: }
                   2696: 
1.3       bowersj2 2697: sub render {
                   2698:     my $self = shift;
1.5       bowersj2 2699:     my $result = '';
                   2700:     my $var = $self->{'variable'};
                   2701:     
                   2702:     my $subdirFunc = eval('sub {' . $self->{'filechoice'} . '}');
1.11      bowersj2 2703:     die 'Error in resource filter code for variable ' . 
                   2704:         {'variable'} . ', Perl said:' . $@ if $@;
                   2705: 
1.5       bowersj2 2706:     my $subdir = &$subdirFunc();
                   2707: 
                   2708:     my $filterFunc = $self->{FILTER_FUNC};
1.44      bowersj2 2709:     if (!defined($filterFunc)) {
                   2710: 	$filterFunc = &not_old_version;
                   2711:     }
1.5       bowersj2 2712:     my $buttons = '';
1.22      bowersj2 2713:     my $type = 'radio';
                   2714:     if ($self->{'multichoice'}) {
                   2715:         $type = 'checkbox';
                   2716:     }
1.5       bowersj2 2717: 
                   2718:     if ($self->{'multichoice'}) {
                   2719:         $result = <<SCRIPT;
1.112     albertel 2720: <script type="text/javascript">
                   2721: // <!--
1.18      bowersj2 2722:     function checkall(value, checkName) {
1.15      bowersj2 2723: 	for (i=0; i<document.forms.helpform.elements.length; i++) {
                   2724:             ele = document.forms.helpform.elements[i];
1.18      bowersj2 2725:             if (ele.name == checkName + '.forminput') {
1.15      bowersj2 2726:                 document.forms.helpform.elements[i].checked=value;
1.5       bowersj2 2727:             }
                   2728:         }
                   2729:     }
1.21      bowersj2 2730: 
1.22      bowersj2 2731:     function checkallclass(value, className) {
1.21      bowersj2 2732:         for (i=0; i<document.forms.helpform.elements.length; i++) {
                   2733:             ele = document.forms.helpform.elements[i];
1.22      bowersj2 2734:             if (ele.type == "$type" && ele.onclick) {
1.21      bowersj2 2735:                 document.forms.helpform.elements[i].checked=value;
                   2736:             }
                   2737:         }
                   2738:     }
1.112     albertel 2739: // -->
1.5       bowersj2 2740: </script>
                   2741: SCRIPT
1.68      sakharuk 2742:        my %lt=&Apache::lonlocal::texthash(
                   2743: 			'saf'  => "Select All Files",
                   2744: 		        'uaf'  => "Unselect All Files");
                   2745:        $buttons = <<BUTTONS;
1.5       bowersj2 2746: <br /> &nbsp;
1.68      sakharuk 2747: <input type="button" onclick="checkall(true, '$var')" value="$lt{'saf'}" />
                   2748: <input type="button" onclick="checkall(false, '$var')" value="$lt{'uaf'}" />
1.23      bowersj2 2749: BUTTONS
                   2750: 
1.69      sakharuk 2751:        %lt=&Apache::lonlocal::texthash(
1.68      sakharuk 2752: 			'sap'  => "Select All Published",
                   2753: 		        'uap'  => "Unselect All Published");
1.23      bowersj2 2754:         if ($helper->{VARS}->{'construction'}) {
1.68      sakharuk 2755:        $buttons .= <<BUTTONS;
                   2756: <input type="button" onclick="checkallclass(true, 'Published')" value="$lt{'sap'}" />
                   2757: <input type="button" onclick="checkallclass(false, 'Published')" value="$lt{'uap'}" />
1.5       bowersj2 2758: <br /> &nbsp;
                   2759: BUTTONS
1.23      bowersj2 2760:        }
1.5       bowersj2 2761:     }
                   2762: 
                   2763:     # Get the list of files in this directory.
                   2764:     my @fileList;
                   2765: 
                   2766:     # If the subdirectory is in local CSTR space
1.47      albertel 2767:     my $metadir;
                   2768:     if ($subdir =~ m|/home/([^/]+)/public_html/(.*)|) {
1.110     albertel 2769: 	my ($user,$domain)= 
                   2770: 	    &Apache::loncacc::constructaccess($subdir,
                   2771: 				     $Apache::lonnet::perlvar{'lonDefDomain'});
1.47      albertel 2772: 	$metadir='/res/'.$domain.'/'.$user.'/'.$2;
                   2773:         @fileList = &Apache::lonnet::dirlist($subdir, $domain, $user, '');
                   2774:     } elsif ($subdir =~ m|^~([^/]+)/(.*)$|) {
                   2775: 	$subdir='/home/'.$1.'/public_html/'.$2;
1.110     albertel 2776: 	my ($user,$domain)= 
                   2777: 	    &Apache::loncacc::constructaccess($subdir,
                   2778: 				     $Apache::lonnet::perlvar{'lonDefDomain'});
1.47      albertel 2779: 	$metadir='/res/'.$domain.'/'.$user.'/'.$2;
1.5       bowersj2 2780:         @fileList = &Apache::lonnet::dirlist($subdir, $domain, $user, '');
                   2781:     } else {
                   2782:         # local library server resource space
1.100     albertel 2783:         @fileList = &Apache::lonnet::dirlist($subdir, $env{'user.domain'}, $env{'user.name'}, '');
1.5       bowersj2 2784:     }
1.3       bowersj2 2785: 
1.44      bowersj2 2786:     # Sort the fileList into order
1.85      albertel 2787:     @fileList = sort {lc($a) cmp lc($b)} @fileList;
1.44      bowersj2 2788: 
1.5       bowersj2 2789:     $result .= $buttons;
                   2790: 
1.6       bowersj2 2791:     if (defined $self->{ERROR_MSG}) {
                   2792:         $result .= '<br /><font color="#FF0000">' . $self->{ERROR_MSG} . '</font><br /><br />';
                   2793:     }
                   2794: 
1.20      bowersj2 2795:     $result .= '<table border="0" cellpadding="2" cellspacing="0">';
1.5       bowersj2 2796: 
                   2797:     # Keeps track if there are no choices, prints appropriate error
                   2798:     # if there are none. 
                   2799:     my $choices = 0;
                   2800:     # Print each legitimate file choice.
                   2801:     for my $file (@fileList) {
                   2802:         $file = (split(/&/, $file))[0];
                   2803:         if ($file eq '.' || $file eq '..') {
                   2804:             next;
                   2805:         }
                   2806:         my $fileName = $subdir .'/'. $file;
                   2807:         if (&$filterFunc($file)) {
1.24      sakharuk 2808: 	    my $status;
                   2809: 	    my $color;
                   2810: 	    if ($helper->{VARS}->{'construction'}) {
                   2811: 		($status, $color) = @{fileState($subdir, $file)};
                   2812: 	    } else {
                   2813: 		$status = '';
                   2814: 		$color = '';
                   2815: 	    }
1.22      bowersj2 2816: 
1.32      bowersj2 2817:             # Get the title
1.47      albertel 2818:             my $title = Apache::lonpubdir::getTitleString(($metadir?$metadir:$subdir) .'/'. $file);
1.32      bowersj2 2819: 
1.22      bowersj2 2820:             # Netscape 4 is stupid and there's nowhere to put the
                   2821:             # information on the input tag that the file is Published,
                   2822:             # Unpublished, etc. In *real* browsers we can just say
                   2823:             # "class='Published'" and check the className attribute of
                   2824:             # the input tag, but Netscape 4 is too stupid to understand
                   2825:             # that attribute, and un-comprehended attributes are not
                   2826:             # reflected into the object model. So instead, what I do 
                   2827:             # is either have or don't have an "onclick" handler that 
                   2828:             # does nothing, give Published files the onclick handler, and
                   2829:             # have the checker scripts check for that. Stupid and clumsy,
                   2830:             # and only gives us binary "yes/no" information (at least I
                   2831:             # couldn't figure out how to reach into the event handler's
                   2832:             # actual code to retreive a value), but it works well enough
                   2833:             # here.
1.23      bowersj2 2834:         
1.22      bowersj2 2835:             my $onclick = '';
1.23      bowersj2 2836:             if ($status eq 'Published' && $helper->{VARS}->{'construction'}) {
1.22      bowersj2 2837:                 $onclick = 'onclick="a=1" ';
                   2838:             }
1.87      matthew  2839:             my $id = &new_id();
1.20      bowersj2 2840:             $result .= '<tr><td align="right"' . " bgcolor='$color'>" .
1.22      bowersj2 2841:                 "<input $onclick type='$type' name='" . $var
1.89      foxr     2842:             . ".forminput' ".qq{id="$id"}." value='" . HTML::Entities::encode($fileName,"<>&\"'").
1.5       bowersj2 2843:                 "'";
                   2844:             if (!$self->{'multichoice'} && $choices == 0) {
1.111     albertel 2845:                 $result .= ' checked="checked"';
1.5       bowersj2 2846:             }
1.87      matthew  2847:             $result .= "/></td><td bgcolor='$color'>".
                   2848:                 qq{<label for="$id">}. $file . "</label></td>" .
1.32      bowersj2 2849:                 "<td bgcolor='$color'>$title</td>" .
                   2850:                 "<td bgcolor='$color'>$status</td>" . "</tr>\n";
1.5       bowersj2 2851:             $choices++;
                   2852:         }
                   2853:     }
                   2854: 
                   2855:     $result .= "</table>\n";
                   2856: 
                   2857:     if (!$choices) {
1.47      albertel 2858:         $result .= '<font color="#FF0000">There are no files available to select in this directory ('.$subdir.'). Please go back and select another option.</font><br /><br />';
1.5       bowersj2 2859:     }
                   2860: 
                   2861:     $result .= $buttons;
                   2862: 
                   2863:     return $result;
1.20      bowersj2 2864: }
                   2865: 
                   2866: # Determine the state of the file: Published, unpublished, modified.
                   2867: # Return the color it should be in and a label as a two-element array
                   2868: # reference.
                   2869: # Logic lifted from lonpubdir.pm, even though I don't know that it's still
                   2870: # the most right thing to do.
                   2871: 
                   2872: sub fileState {
                   2873:     my $constructionSpaceDir = shift;
                   2874:     my $file = shift;
                   2875:     
1.100     albertel 2876:     my ($uname,$udom)=($env{'user.name'},$env{'user.domain'});
                   2877:     if ($env{'request.role'}=~/^ca\./) {
                   2878: 	(undef,$udom,$uname)=split(/\//,$env{'request.role'});
1.86      albertel 2879:     }
1.20      bowersj2 2880:     my $docroot = $Apache::lonnet::perlvar{'lonDocRoot'};
                   2881:     my $subdirpart = $constructionSpaceDir;
1.86      albertel 2882:     $subdirpart =~ s/^\/home\/$uname\/public_html//;
                   2883:     my $resdir = $docroot . '/res/' . $udom . '/' . $uname .
1.20      bowersj2 2884:         $subdirpart;
                   2885: 
                   2886:     my @constructionSpaceFileStat = stat($constructionSpaceDir . '/' . $file);
                   2887:     my @resourceSpaceFileStat = stat($resdir . '/' . $file);
                   2888:     if (!@resourceSpaceFileStat) {
                   2889:         return ['Unpublished', '#FFCCCC'];
                   2890:     }
                   2891: 
                   2892:     my $constructionSpaceFileModified = $constructionSpaceFileStat[9];
                   2893:     my $resourceSpaceFileModified = $resourceSpaceFileStat[9];
                   2894:     
                   2895:     if ($constructionSpaceFileModified > $resourceSpaceFileModified) {
                   2896:         return ['Modified', '#FFFFCC'];
                   2897:     }
                   2898:     return ['Published', '#CCFFCC'];
1.4       bowersj2 2899: }
1.5       bowersj2 2900: 
1.4       bowersj2 2901: sub postprocess {
                   2902:     my $self = shift;
1.100     albertel 2903:     my $result = $env{'form.' . $self->{'variable'} . '.forminput'};
1.6       bowersj2 2904:     if (!$result) {
                   2905:         $self->{ERROR_MSG} = 'You must choose at least one file '.
                   2906:             'to continue.';
                   2907:         return 0;
                   2908:     }
                   2909: 
1.5       bowersj2 2910:     if (defined($self->{NEXTSTATE})) {
                   2911:         $helper->changeState($self->{NEXTSTATE});
1.3       bowersj2 2912:     }
1.6       bowersj2 2913: 
                   2914:     return 1;
1.3       bowersj2 2915: }
1.8       bowersj2 2916: 
                   2917: 1;
                   2918: 
1.11      bowersj2 2919: package Apache::lonhelper::section;
                   2920: 
                   2921: =pod
                   2922: 
1.44      bowersj2 2923: =head2 Element: sectionX<section, helper element>
1.11      bowersj2 2924: 
                   2925: <section> allows the user to choose one or more sections from the current
                   2926: course.
                   2927: 
                   2928: It takes the standard attributes "variable", "multichoice", and
                   2929: "nextstate", meaning what they do for most other elements.
                   2930: 
                   2931: =cut
                   2932: 
                   2933: no strict;
                   2934: @ISA = ("Apache::lonhelper::choices");
                   2935: use strict;
                   2936: 
                   2937: BEGIN {
                   2938:     &Apache::lonhelper::register('Apache::lonhelper::section',
                   2939:                                  ('section'));
                   2940: }
                   2941: 
                   2942: sub new {
                   2943:     my $ref = Apache::lonhelper::choices->new();
                   2944:     bless($ref);
                   2945: }
                   2946: 
                   2947: sub start_section {
                   2948:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
                   2949: 
                   2950:     if ($target ne 'helper') {
                   2951:         return '';
                   2952:     }
1.12      bowersj2 2953: 
                   2954:     $paramHash->{CHOICES} = [];
                   2955: 
1.11      bowersj2 2956:     $paramHash->{'variable'} = $token->[2]{'variable'};
                   2957:     $helper->declareVar($paramHash->{'variable'});
                   2958:     $paramHash->{'multichoice'} = $token->[2]{'multichoice'};
                   2959:     if (defined($token->[2]{'nextstate'})) {
1.12      bowersj2 2960:         $paramHash->{NEXTSTATE} = $token->[2]{'nextstate'};
1.11      bowersj2 2961:     }
                   2962: 
                   2963:     # Populate the CHOICES element
                   2964:     my %choices;
                   2965: 
                   2966:     my $section = Apache::loncoursedata::CL_SECTION();
                   2967:     my $classlist = Apache::loncoursedata::get_classlist();
                   2968:     foreach (keys %$classlist) {
                   2969:         my $sectionName = $classlist->{$_}->[$section];
                   2970:         if (!$sectionName) {
                   2971:             $choices{"No section assigned"} = "";
                   2972:         } else {
                   2973:             $choices{$sectionName} = $sectionName;
                   2974:         }
1.12      bowersj2 2975:     } 
                   2976:    
1.11      bowersj2 2977:     for my $sectionName (sort(keys(%choices))) {
1.12      bowersj2 2978:         
1.11      bowersj2 2979:         push @{$paramHash->{CHOICES}}, [$sectionName, $sectionName];
                   2980:     }
                   2981: }    
                   2982: 
1.12      bowersj2 2983: sub end_section {
                   2984:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
1.11      bowersj2 2985: 
1.12      bowersj2 2986:     if ($target ne 'helper') {
                   2987:         return '';
                   2988:     }
                   2989:     Apache::lonhelper::section->new();
                   2990: }    
1.11      bowersj2 2991: 1;
                   2992: 
1.128     raeburn  2993: package Apache::lonhelper::group;
                   2994: 
                   2995: =pod
                   2996:  
                   2997: =head2 Element: groupX<group, helper element>
                   2998:  
                   2999: <section> allows the user to choose one or more groups from the current course.
                   3000: 
                   3001: It takes the standard attributes "variable", "multichoice", and "nextstate", meaning what they do for most other elements.
                   3002:  
                   3003: =cut
                   3004: 
                   3005: no strict;
                   3006: @ISA = ("Apache::lonhelper::choices");
                   3007: use strict;
                   3008: 
                   3009: BEGIN {
                   3010:     &Apache::lonhelper::register('Apache::lonhelper::group',
                   3011:                                  ('group'));
                   3012: }
                   3013: 
                   3014: sub new {
                   3015:     my $ref = Apache::lonhelper::choices->new();
                   3016:     bless($ref);
                   3017: }
                   3018:  
                   3019: sub start_group {
                   3020:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
                   3021:  
                   3022:     if ($target ne 'helper') {
                   3023:         return '';
                   3024:     }
                   3025: 
                   3026:     $paramHash->{CHOICES} = [];
                   3027: 
                   3028:     $paramHash->{'variable'} = $token->[2]{'variable'};
                   3029:     $helper->declareVar($paramHash->{'variable'});
                   3030:     $paramHash->{'multichoice'} = $token->[2]{'multichoice'};
                   3031:     if (defined($token->[2]{'nextstate'})) {
                   3032:         $paramHash->{NEXTSTATE} = $token->[2]{'nextstate'};
                   3033:     }
                   3034: 
                   3035:     # Populate the CHOICES element
                   3036:     my %choices;
                   3037: 
                   3038:     my $numgroups;
                   3039:     my %curr_groups;
                   3040:     if (&Apache::loncommon::coursegroups(\%curr_groups)) {
                   3041:         foreach my $group_name (keys %curr_groups) {
                   3042:             $choices{$group_name} = $group_name;
                   3043:         }
                   3044:     }
                   3045:     foreach my $group_name (sort(keys(%choices))) {
                   3046:         push @{$paramHash->{CHOICES}}, [$group_name, $group_name];
                   3047:     }
                   3048: }
                   3049:                                                                                     
                   3050: sub end_group {
                   3051:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
                   3052: 
                   3053:     if ($target ne 'helper') {
                   3054:         return '';
                   3055:     }
                   3056:     Apache::lonhelper::group->new();
                   3057: }
                   3058: 1;
                   3059: 
1.34      bowersj2 3060: package Apache::lonhelper::string;
                   3061: 
                   3062: =pod
                   3063: 
1.44      bowersj2 3064: =head2 Element: stringX<string, helper element>
1.34      bowersj2 3065: 
                   3066: string elements provide a string entry field for the user. string elements
                   3067: take the usual 'variable' and 'nextstate' parameters. string elements
                   3068: also pass through 'maxlength' and 'size' attributes to the input tag.
                   3069: 
                   3070: string honors the defaultvalue tag, if given.
                   3071: 
1.38      bowersj2 3072: string honors the validation function, if given.
                   3073: 
1.34      bowersj2 3074: =cut
                   3075: 
                   3076: no strict;
                   3077: @ISA = ("Apache::lonhelper::element");
                   3078: use strict;
1.76      sakharuk 3079: use Apache::lonlocal;
1.34      bowersj2 3080: 
                   3081: BEGIN {
                   3082:     &Apache::lonhelper::register('Apache::lonhelper::string',
                   3083:                               ('string'));
                   3084: }
                   3085: 
                   3086: sub new {
                   3087:     my $ref = Apache::lonhelper::element->new();
                   3088:     bless($ref);
                   3089: }
                   3090: 
                   3091: # CONSTRUCTION: Construct the message element from the XML
                   3092: sub start_string {
                   3093:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
                   3094: 
                   3095:     if ($target ne 'helper') {
                   3096:         return '';
                   3097:     }
                   3098: 
                   3099:     $paramHash->{'variable'} = $token->[2]{'variable'};
                   3100:     $helper->declareVar($paramHash->{'variable'});
                   3101:     $paramHash->{'nextstate'} = $token->[2]{'nextstate'};
                   3102:     $paramHash->{'maxlength'} = $token->[2]{'maxlength'};
                   3103:     $paramHash->{'size'} = $token->[2]{'size'};
                   3104: 
                   3105:     return '';
                   3106: }
                   3107: 
                   3108: sub end_string {
                   3109:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
                   3110: 
                   3111:     if ($target ne 'helper') {
                   3112:         return '';
                   3113:     }
                   3114:     Apache::lonhelper::string->new();
                   3115:     return '';
                   3116: }
                   3117: 
                   3118: sub render {
                   3119:     my $self = shift;
1.38      bowersj2 3120:     my $result = '';
                   3121: 
                   3122:     if (defined $self->{ERROR_MSG}) {
1.97      albertel 3123:         $result .= '<p><font color="#FF0000">' . $self->{ERROR_MSG} . '</font></p>';
1.38      bowersj2 3124:     }
                   3125: 
                   3126:     $result .= '<input type="string" name="' . $self->{'variable'} . '.forminput"';
1.34      bowersj2 3127: 
                   3128:     if (defined($self->{'size'})) {
                   3129:         $result .= ' size="' . $self->{'size'} . '"';
                   3130:     }
                   3131:     if (defined($self->{'maxlength'})) {
                   3132:         $result .= ' maxlength="' . $self->{'maxlength'} . '"';
                   3133:     }
                   3134: 
                   3135:     if (defined($self->{DEFAULT_VALUE})) {
                   3136:         my $valueFunc = eval($self->{DEFAULT_VALUE});
                   3137:         die 'Error in default value code for variable ' . 
                   3138:             $self->{'variable'} . ', Perl said: ' . $@ if $@;
                   3139:         $result .= ' value="' . &$valueFunc($helper, $self) . '"';
                   3140:     }
                   3141: 
                   3142:     $result .= ' />';
                   3143: 
                   3144:     return $result;
                   3145: }
                   3146: 
                   3147: # If a NEXTSTATE was given, switch to it
                   3148: sub postprocess {
                   3149:     my $self = shift;
1.38      bowersj2 3150: 
                   3151:     if (defined($self->{VALIDATOR})) {
                   3152: 	my $validator = eval($self->{VALIDATOR});
                   3153: 	die 'Died during evaluation of evaulation code; Perl said: ' . $@ if $@;
                   3154: 	my $invalid = &$validator($helper, $state, $self, $self->getValue());
                   3155: 	if ($invalid) {
                   3156: 	    $self->{ERROR_MSG} = $invalid;
                   3157: 	    return 0;
                   3158: 	}
                   3159:     }
                   3160: 
                   3161:     if (defined($self->{'nextstate'})) {
                   3162:         $helper->changeState($self->{'nextstate'});
1.34      bowersj2 3163:     }
                   3164: 
                   3165:     return 1;
                   3166: }
                   3167: 
                   3168: 1;
                   3169: 
1.8       bowersj2 3170: package Apache::lonhelper::general;
                   3171: 
                   3172: =pod
                   3173: 
1.44      bowersj2 3174: =head2 General-purpose tag: <exec>X<exec, helper tag>
1.8       bowersj2 3175: 
1.44      bowersj2 3176: The contents of the exec tag are executed as Perl code, B<not> inside a 
1.100     albertel 3177: safe space, so the full range of $env and such is available. The code
1.8       bowersj2 3178: will be executed as a subroutine wrapped with the following code:
                   3179: 
                   3180: "sub { my $helper = shift; my $state = shift;" and
                   3181: 
                   3182: "}"
                   3183: 
                   3184: The return value is ignored.
                   3185: 
                   3186: $helper is the helper object. Feel free to add methods to the helper
                   3187: object to support whatever manipulation you may need to do (for instance,
                   3188: overriding the form location if the state is the final state; see 
1.44      bowersj2 3189: parameter.helper for an example).
1.8       bowersj2 3190: 
                   3191: $state is the $paramHash that has currently been generated and may
                   3192: be manipulated by the code in exec. Note that the $state is not yet
                   3193: an actual state B<object>, it is just a hash, so do not expect to
                   3194: be able to call methods on it.
                   3195: 
                   3196: =cut
                   3197: 
1.83      sakharuk 3198: use Apache::lonlocal;
1.102     albertel 3199: use Apache::lonnet;
1.83      sakharuk 3200: 
1.8       bowersj2 3201: BEGIN {
                   3202:     &Apache::lonhelper::register('Apache::lonhelper::general',
1.11      bowersj2 3203:                                  'exec', 'condition', 'clause',
                   3204:                                  'eval');
1.8       bowersj2 3205: }
                   3206: 
                   3207: sub start_exec {
                   3208:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
                   3209: 
                   3210:     if ($target ne 'helper') {
                   3211:         return '';
                   3212:     }
                   3213:     
                   3214:     my $code = &Apache::lonxml::get_all_text('/exec', $parser);
                   3215:     
                   3216:     $code = eval ('sub { my $helper = shift; my $state = shift; ' .
                   3217:         $code . "}");
1.11      bowersj2 3218:     die 'Error in <exec>, Perl said: '. $@ if $@;
1.8       bowersj2 3219:     &$code($helper, $paramHash);
                   3220: }
                   3221: 
                   3222: sub end_exec { return ''; }
                   3223: 
                   3224: =pod
                   3225: 
                   3226: =head2 General-purpose tag: <condition>
                   3227: 
                   3228: The <condition> tag allows you to mask out parts of the helper code
                   3229: depending on some programatically determined condition. The condition
                   3230: tag contains a tag <clause> which contains perl code that when wrapped
                   3231: with "sub { my $helper = shift; my $state = shift; " and "}", returns
                   3232: a true value if the XML in the condition should be evaluated as a normal
                   3233: part of the helper, or false if it should be completely discarded.
                   3234: 
                   3235: The <clause> tag must be the first sub-tag of the <condition> tag or
                   3236: it will not work as expected.
                   3237: 
                   3238: =cut
                   3239: 
                   3240: # The condition tag just functions as a marker, it doesn't have
                   3241: # to "do" anything. Technically it doesn't even have to be registered
                   3242: # with the lonxml code, but I leave this here to be explicit about it.
                   3243: sub start_condition { return ''; }
                   3244: sub end_condition { return ''; }
                   3245: 
                   3246: sub start_clause {
                   3247:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
                   3248: 
                   3249:     if ($target ne 'helper') {
                   3250:         return '';
                   3251:     }
                   3252:     
                   3253:     my $clause = Apache::lonxml::get_all_text('/clause', $parser);
                   3254:     $clause = eval('sub { my $helper = shift; my $state = shift; '
                   3255:         . $clause . '}');
1.11      bowersj2 3256:     die 'Error in clause of condition, Perl said: ' . $@ if $@;
1.8       bowersj2 3257:     if (!&$clause($helper, $paramHash)) {
                   3258:         # Discard all text until the /condition.
                   3259:         &Apache::lonxml::get_all_text('/condition', $parser);
                   3260:     }
                   3261: }
                   3262: 
                   3263: sub end_clause { return ''; }
1.11      bowersj2 3264: 
                   3265: =pod
                   3266: 
1.44      bowersj2 3267: =head2 General-purpose tag: <eval>X<eval, helper tag>
1.11      bowersj2 3268: 
                   3269: The <eval> tag will be evaluated as a subroutine call passed in the
                   3270: current helper object and state hash as described in <condition> above,
                   3271: but is expected to return a string to be printed directly to the
                   3272: screen. This is useful for dynamically generating messages. 
                   3273: 
                   3274: =cut
                   3275: 
                   3276: # This is basically a type of message.
                   3277: # Programmatically setting $paramHash->{NEXTSTATE} would work, though
                   3278: # it's probably bad form.
                   3279: 
                   3280: sub start_eval {
                   3281:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
                   3282: 
                   3283:     if ($target ne 'helper') {
                   3284:         return '';
                   3285:     }
                   3286:     
                   3287:     my $program = Apache::lonxml::get_all_text('/eval', $parser);
                   3288:     $program = eval('sub { my $helper = shift; my $state = shift; '
                   3289:         . $program . '}');
                   3290:     die 'Error in eval code, Perl said: ' . $@ if $@;
                   3291:     $paramHash->{MESSAGE_TEXT} = &$program($helper, $paramHash);
                   3292: }
                   3293: 
                   3294: sub end_eval { 
                   3295:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
                   3296: 
                   3297:     if ($target ne 'helper') {
                   3298:         return '';
                   3299:     }
                   3300: 
                   3301:     Apache::lonhelper::message->new();
                   3302: }
                   3303: 
1.13      bowersj2 3304: 1;
                   3305: 
1.27      bowersj2 3306: package Apache::lonhelper::final;
                   3307: 
                   3308: =pod
                   3309: 
1.44      bowersj2 3310: =head2 Element: finalX<final, helper tag>
1.27      bowersj2 3311: 
                   3312: <final> is a special element that works with helpers that use the <finalcode>
1.44      bowersj2 3313: tagX<finalcode, helper tag>. It goes through all the states and elements, executing the <finalcode>
1.27      bowersj2 3314: snippets and collecting the results. Finally, it takes the user out of the
                   3315: helper, going to a provided page.
                   3316: 
1.34      bowersj2 3317: If the parameter "restartCourse" is true, this will override the buttons and
                   3318: will make a "Finish Helper" button that will re-initialize the course for them,
                   3319: which is useful for the Course Initialization helper so the users never see
                   3320: the old values taking effect.
                   3321: 
1.93      albertel 3322: If the parameter "restartCourse" is not true a 'Finish' Button will be
                   3323: presented that takes the user back to whatever was defined as <exitpage>
                   3324: 
1.27      bowersj2 3325: =cut
                   3326: 
                   3327: no strict;
                   3328: @ISA = ("Apache::lonhelper::element");
                   3329: use strict;
1.62      matthew  3330: use Apache::lonlocal;
1.100     albertel 3331: use Apache::lonnet;
1.27      bowersj2 3332: BEGIN {
                   3333:     &Apache::lonhelper::register('Apache::lonhelper::final',
                   3334:                                  ('final', 'exitpage'));
                   3335: }
                   3336: 
                   3337: sub new {
                   3338:     my $ref = Apache::lonhelper::element->new();
                   3339:     bless($ref);
                   3340: }
                   3341: 
1.34      bowersj2 3342: sub start_final { 
                   3343:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
                   3344: 
                   3345:     if ($target ne 'helper') {
                   3346:         return '';
                   3347:     }
                   3348: 
                   3349:     $paramHash->{'restartCourse'} = $token->[2]{'restartCourse'};
                   3350: 
                   3351:     return ''; 
                   3352: }
1.27      bowersj2 3353: 
                   3354: sub end_final {
                   3355:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
                   3356: 
                   3357:     if ($target ne 'helper') {
                   3358:         return '';
                   3359:     }
                   3360: 
                   3361:     Apache::lonhelper::final->new();
                   3362:    
                   3363:     return '';
                   3364: }
                   3365: 
                   3366: sub start_exitpage {
                   3367:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
                   3368: 
                   3369:     if ($target ne 'helper') {
                   3370:         return '';
                   3371:     }
                   3372: 
                   3373:     $paramHash->{EXIT_PAGE} = &Apache::lonxml::get_all_text('/exitpage',
                   3374:                                                             $parser);
                   3375: 
                   3376:     return '';
                   3377: }
                   3378: 
                   3379: sub end_exitpage { return ''; }
                   3380: 
                   3381: sub render {
                   3382:     my $self = shift;
                   3383: 
                   3384:     my @results;
                   3385: 
                   3386:     # Collect all the results
                   3387:     for my $stateName (keys %{$helper->{STATES}}) {
                   3388:         my $state = $helper->{STATES}->{$stateName};
                   3389:         
                   3390:         for my $element (@{$state->{ELEMENTS}}) {
                   3391:             if (defined($element->{FINAL_CODE})) {
                   3392:                 # Compile the code.
1.31      bowersj2 3393:                 my $code = 'sub { my $helper = shift; my $element = shift; ' 
                   3394:                     . $element->{FINAL_CODE} . '}';
1.27      bowersj2 3395:                 $code = eval($code);
                   3396:                 die 'Error while executing final code for element with var ' .
                   3397:                     $element->{'variable'} . ', Perl said: ' . $@ if $@;
                   3398: 
1.31      bowersj2 3399:                 my $result = &$code($helper, $element);
1.27      bowersj2 3400:                 if ($result) {
                   3401:                     push @results, $result;
                   3402:                 }
                   3403:             }
                   3404:         }
                   3405:     }
                   3406: 
1.40      bowersj2 3407:     my $result;
1.27      bowersj2 3408: 
1.40      bowersj2 3409:     if (scalar(@results) != 0) {
                   3410: 	$result .= "<ul>\n";
                   3411: 	for my $re (@results) {
                   3412: 	    $result .= '    <li>' . $re . "</li>\n";
                   3413: 	}
                   3414: 	
                   3415: 	if (!@results) {
1.59      bowersj2 3416: 	    $result .= '    <li>' . 
                   3417: 		&mt('No changes were made to current settings.') . '</li>';
1.40      bowersj2 3418: 	}
                   3419: 	
                   3420: 	$result .= '</ul>';
1.34      bowersj2 3421:     }
                   3422: 
1.93      albertel 3423:     my $actionURL = $self->{EXIT_PAGE};
                   3424:     my $targetURL = '';
                   3425:     my $finish=&mt('Finish');
1.34      bowersj2 3426:     if ($self->{'restartCourse'}) {
1.103     albertel 3427: 	$actionURL = '/adm/roles';
1.93      albertel 3428: 	$targetURL = '/adm/menu';
1.100     albertel 3429: 	if ($env{'course.'.$env{'request.course.id'}.'.url'}=~/^uploaded/) {
1.64      albertel 3430: 	    $targetURL = '/adm/coursedocs';
                   3431: 	} else {
                   3432: 	    $targetURL = '/adm/navmaps';
                   3433: 	}
1.100     albertel 3434: 	if ($env{'course.'.$env{'request.course.id'}.'.clonedfrom'}) {
1.45      bowersj2 3435: 	    $targetURL = '/adm/parmset?overview=1';
                   3436: 	}
1.93      albertel 3437: 	my $finish=&mt('Finish Course Initialization');
1.34      bowersj2 3438:     }
1.93      albertel 3439:     my $previous = HTML::Entities::encode(&mt("<- Previous"), '<>&"');
                   3440:     my $next = HTML::Entities::encode(&mt("Next ->"), '<>&"');
1.127     albertel 3441:     my $target = " target='loncapaclient'";
                   3442:     if (($env{'browser.interface'} eq 'textual') ||
                   3443:         ($env{'environment.remote'} eq 'off')) {  $target='';  }
1.93      albertel 3444:     $result .= "<center>\n" .
1.127     albertel 3445: 	"<form action='".$actionURL."' method='post' $target>\n" .
1.93      albertel 3446: 	"<input type='button' onclick='history.go(-1)' value='$previous' />" .
                   3447: 	"<input type='hidden' name='orgurl' value='$targetURL' />" .
                   3448: 	"<input type='hidden' name='selectrole' value='1' />\n" .
1.100     albertel 3449: 	"<input type='hidden' name='" . $env{'request.role'} . 
1.93      albertel 3450: 	"' value='1' />\n<input type='submit' value='" . $finish . "' />\n" .
                   3451: 	"</form></center>";
1.34      bowersj2 3452: 
1.40      bowersj2 3453:     return $result;
1.34      bowersj2 3454: }
                   3455: 
                   3456: sub overrideForm {
1.93      albertel 3457:     return 1;
1.27      bowersj2 3458: }
                   3459: 
                   3460: 1;
                   3461: 
1.13      bowersj2 3462: package Apache::lonhelper::parmwizfinal;
                   3463: 
                   3464: # This is the final state for the parmwizard. It is not generally useful,
                   3465: # so it is not perldoc'ed. It does its own processing.
                   3466: # It is represented with <parmwizfinal />, and
                   3467: # should later be moved to lonparmset.pm .
                   3468: 
                   3469: no strict;
                   3470: @ISA = ('Apache::lonhelper::element');
                   3471: use strict;
1.69      sakharuk 3472: use Apache::lonlocal;
1.102     albertel 3473: use Apache::lonnet;
1.11      bowersj2 3474: 
1.13      bowersj2 3475: BEGIN {
                   3476:     &Apache::lonhelper::register('Apache::lonhelper::parmwizfinal',
                   3477:                                  ('parmwizfinal'));
                   3478: }
                   3479: 
                   3480: use Time::localtime;
                   3481: 
                   3482: sub new {
                   3483:     my $ref = Apache::lonhelper::choices->new();
                   3484:     bless ($ref);
                   3485: }
                   3486: 
                   3487: sub start_parmwizfinal { return ''; }
                   3488: 
                   3489: sub end_parmwizfinal {
                   3490:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
                   3491: 
                   3492:     if ($target ne 'helper') {
                   3493:         return '';
                   3494:     }
                   3495:     Apache::lonhelper::parmwizfinal->new();
                   3496: }
                   3497: 
                   3498: # Renders a form that, when submitted, will form the input to lonparmset.pm
                   3499: sub render {
                   3500:     my $self = shift;
                   3501:     my $vars = $helper->{VARS};
                   3502: 
                   3503:     # FIXME: Unify my designators with the standard ones
1.48      bowersj2 3504:     my %dateTypeHash = ('open_date' => "opening date",
                   3505:                         'due_date' => "due date",
                   3506:                         'answer_date' => "answer date",
                   3507: 			'tries' => 'number of tries',
                   3508: 			'weight' => 'problem weight'
1.38      bowersj2 3509: 			);
1.13      bowersj2 3510:     my %parmTypeHash = ('open_date' => "0_opendate",
                   3511:                         'due_date' => "0_duedate",
1.38      bowersj2 3512:                         'answer_date' => "0_answerdate",
1.48      bowersj2 3513: 			'tries' => '0_maxtries',
                   3514: 			'weight' => '0_weight' );
1.107     albertel 3515:     my %realParmName = ('open_date' => "opendate",
                   3516:                         'due_date' => "duedate",
                   3517:                         'answer_date' => "answerdate",
                   3518: 			'tries' => 'maxtries',
                   3519: 			'weight' => 'weight' );
1.13      bowersj2 3520:     
                   3521:     my $affectedResourceId = "";
                   3522:     my $parm_name = $parmTypeHash{$vars->{ACTION_TYPE}};
                   3523:     my $level = "";
1.27      bowersj2 3524:     my $resourceString;
                   3525:     my $symb;
                   3526:     my $paramlevel;
1.95      albertel 3527:     
1.13      bowersj2 3528:     # Print the granularity, depending on the action
                   3529:     if ($vars->{GRANULARITY} eq 'whole_course') {
1.76      sakharuk 3530:         $resourceString .= '<li>'.&mt('for <b>all resources in the course</b>').'</li>';
1.104     albertel 3531: 	if ($vars->{TARGETS} eq 'course') {
                   3532: 	    $level = 11; # general course, see lonparmset.pm perldoc
                   3533: 	} elsif ($vars->{TARGETS} eq 'section') {
                   3534: 	    $level = 6;
                   3535: 	} else {
                   3536: 	    $level = 3;
                   3537: 	}
1.13      bowersj2 3538:         $affectedResourceId = "0.0";
1.27      bowersj2 3539:         $symb = 'a';
                   3540:         $paramlevel = 'general';
1.13      bowersj2 3541:     } elsif ($vars->{GRANULARITY} eq 'map') {
1.41      bowersj2 3542:         my $navmap = Apache::lonnavmaps::navmap->new();
1.35      bowersj2 3543:         my $res = $navmap->getByMapPc($vars->{RESOURCE_ID});
1.13      bowersj2 3544:         my $title = $res->compTitle();
1.27      bowersj2 3545:         $symb = $res->symb();
1.78      sakharuk 3546:         $resourceString .= '<li>'.&mt('for the map named [_1]',"<b>$title</b>").'</li>';
1.104     albertel 3547: 	if ($vars->{TARGETS} eq 'course') {
                   3548: 	    $level = 10; # general course, see lonparmset.pm perldoc
                   3549: 	} elsif ($vars->{TARGETS} eq 'section') {
                   3550: 	    $level = 5;
                   3551: 	} else {
                   3552: 	    $level = 2;
                   3553: 	}
1.13      bowersj2 3554:         $affectedResourceId = $vars->{RESOURCE_ID};
1.27      bowersj2 3555:         $paramlevel = 'map';
1.13      bowersj2 3556:     } else {
1.41      bowersj2 3557:         my $navmap = Apache::lonnavmaps::navmap->new();
1.13      bowersj2 3558:         my $res = $navmap->getById($vars->{RESOURCE_ID});
1.95      albertel 3559:         my $part = $vars->{RESOURCE_ID_part};
                   3560: 	if ($part ne 'All Parts' && $part) { $parm_name=~s/^0/$part/; } else { $part=&mt('All Parts'); }
1.27      bowersj2 3561:         $symb = $res->symb();
1.13      bowersj2 3562:         my $title = $res->compTitle();
1.95      albertel 3563:         $resourceString .= '<li>'.&mt('for the resource named [_1] part [_2]',"<b>$title</b>","<b>$part</b>").'</li>';
1.104     albertel 3564: 	if ($vars->{TARGETS} eq 'course') {
                   3565: 	    $level = 7; # general course, see lonparmset.pm perldoc
                   3566: 	} elsif ($vars->{TARGETS} eq 'section') {
                   3567: 	    $level = 4;
                   3568: 	} else {
                   3569: 	    $level = 1;
                   3570: 	}
1.13      bowersj2 3571:         $affectedResourceId = $vars->{RESOURCE_ID};
1.27      bowersj2 3572:         $paramlevel = 'full';
1.13      bowersj2 3573:     }
                   3574: 
1.105     albertel 3575:     my $result = "<form name='helpform' method='POST' action='/adm/parmset#$affectedResourceId&$parm_name&$level'>\n";
1.104     albertel 3576:     $result .= "<input type='hidden' name='action' value='settable' />\n";
                   3577:     $result .= "<input type='hidden' name='dis' value='helper' />\n";
1.107     albertel 3578:     $result .= "<input type='hidden' name='pscat' value='".
                   3579: 	$realParmName{$vars->{ACTION_TYPE}}."' />\n";
1.95      albertel 3580:     if ($vars->{GRANULARITY} eq 'resource') {
                   3581: 	$result .= "<input type='hidden' name='symb' value='".
                   3582: 	    HTML::Entities::encode($symb,"'<>&\"") . "' />\n";
1.108     albertel 3583:     } elsif ($vars->{GRANULARITY} eq 'map') {
                   3584: 	$result .= "<input type='hidden' name='pschp' value='".
                   3585: 	    $affectedResourceId."' />\n";
1.95      albertel 3586:     }
1.104     albertel 3587:     my $part = $vars->{RESOURCE_ID_part};
                   3588:     if ($part eq 'All Parts' || !$part) { $part=0; }
                   3589:     $result .= "<input type='hidden' name='psprt' value='".
                   3590: 	HTML::Entities::encode($part,"'<>&\"") . "' />\n";
                   3591: 
1.69      sakharuk 3592:     $result .= '<p>'.&mt('Confirm that this information is correct, then click &quot;Finish Helper&quot; to complete setting the parameter.').'<ul>';
1.27      bowersj2 3593:     
                   3594:     # Print the type of manipulation:
1.73      albertel 3595:     my $extra;
1.38      bowersj2 3596:     if ($vars->{ACTION_TYPE} eq 'tries') {
1.73      albertel 3597: 	$extra =  $vars->{TRIES};
1.38      bowersj2 3598:     }
1.48      bowersj2 3599:     if ($vars->{ACTION_TYPE} eq 'weight') {
1.73      albertel 3600: 	$extra =  $vars->{WEIGHT};
                   3601:     }
                   3602:     $result .= "<li>";
1.74      matthew  3603:     my $what = &mt($dateTypeHash{$vars->{ACTION_TYPE}});
1.73      albertel 3604:     if ($extra) {
                   3605: 	$result .= &mt('Setting the [_1] to [_2]',"<b>$what</b>",$extra);
                   3606:     } else {
                   3607: 	$result .= &mt('Setting the [_1]',"<b>$what</b>");
1.48      bowersj2 3608:     }
1.38      bowersj2 3609:     $result .= "</li>\n";
1.27      bowersj2 3610:     if ($vars->{ACTION_TYPE} eq 'due_date' || 
                   3611:         $vars->{ACTION_TYPE} eq 'answer_date') {
                   3612:         # for due dates, we default to "date end" type entries
                   3613:         $result .= "<input type='hidden' name='recent_date_end' " .
                   3614:             "value='" . $vars->{PARM_DATE} . "' />\n";
                   3615:         $result .= "<input type='hidden' name='pres_value' " . 
                   3616:             "value='" . $vars->{PARM_DATE} . "' />\n";
                   3617:         $result .= "<input type='hidden' name='pres_type' " .
                   3618:             "value='date_end' />\n";
                   3619:     } elsif ($vars->{ACTION_TYPE} eq 'open_date') {
                   3620:         $result .= "<input type='hidden' name='recent_date_start' ".
                   3621:             "value='" . $vars->{PARM_DATE} . "' />\n";
                   3622:         $result .= "<input type='hidden' name='pres_value' " .
                   3623:             "value='" . $vars->{PARM_DATE} . "' />\n";
                   3624:         $result .= "<input type='hidden' name='pres_type' " .
                   3625:             "value='date_start' />\n";
1.38      bowersj2 3626:     } elsif ($vars->{ACTION_TYPE} eq 'tries') {
                   3627: 	$result .= "<input type='hidden' name='pres_value' " .
                   3628: 	    "value='" . $vars->{TRIES} . "' />\n";
1.104     albertel 3629:         $result .= "<input type='hidden' name='pres_type' " .
                   3630:             "value='int_pos' />\n";
1.48      bowersj2 3631:     } elsif ($vars->{ACTION_TYPE} eq 'weight') {
                   3632: 	$result .= "<input type='hidden' name='pres_value' " .
                   3633: 	    "value='" . $vars->{WEIGHT} . "' />\n";
1.38      bowersj2 3634:     }
1.27      bowersj2 3635: 
                   3636:     $result .= $resourceString;
                   3637:     
1.13      bowersj2 3638:     # Print targets
                   3639:     if ($vars->{TARGETS} eq 'course') {
1.76      sakharuk 3640:         $result .= '<li>'.&mt('for <b>all students in course</b>').'</li>';
1.13      bowersj2 3641:     } elsif ($vars->{TARGETS} eq 'section') {
                   3642:         my $section = $vars->{SECTION_NAME};
1.79      sakharuk 3643:         $result .= '<li>'.&mt('for section [_1]',"<b>$section</b>").'</li>';
1.104     albertel 3644: 	$result .= "<input type='hidden' name='csec' value='" .
1.89      foxr     3645:             HTML::Entities::encode($section,"'<>&\"") . "' />\n";
1.128     raeburn  3646:     } elsif ($vars->{TARGETS} eq 'group') {
                   3647:         my $group = $vars->{GROUP_NAME};
                   3648:         $result .= '<li>'.&mt('for group [_1]',"<b>$group</b>").'</li>';
                   3649:         $result .= "<input type='hidden' name='cgroup' value='" .
                   3650:             HTML::Entities::encode($group,"'<>&\"") . "' />\n";
1.13      bowersj2 3651:     } else {
                   3652:         # FIXME: This is probably wasteful! Store the name!
                   3653:         my $classlist = Apache::loncoursedata::get_classlist();
1.109     albertel 3654: 	my ($uname,$udom)=split(':',$vars->{USER_NAME});
1.106     albertel 3655:         my $name = $classlist->{$uname.':'.$udom}->[6];
1.79      sakharuk 3656:         $result .= '<li>'.&mt('for [_1]',"<b>$name</b>").'</li>';
1.13      bowersj2 3657:         $result .= "<input type='hidden' name='uname' value='".
1.89      foxr     3658:             HTML::Entities::encode($uname,"'<>&\"") . "' />\n";
1.13      bowersj2 3659:         $result .= "<input type='hidden' name='udom' value='".
1.89      foxr     3660:             HTML::Entities::encode($udom,"'<>&\"") . "' />\n";
1.13      bowersj2 3661:     }
                   3662: 
                   3663:     # Print value
1.48      bowersj2 3664:     if ($vars->{ACTION_TYPE} ne 'tries' && $vars->{ACTION_TYPE} ne 'weight') {
1.81      sakharuk 3665: 	$result .= '<li>'.&mt('to [_1] ([_2])',"<b>".ctime($vars->{PARM_DATE})."</b>",Apache::lonnavmaps::timeToHumanString($vars->{PARM_DATE}))."</li>\n";
1.38      bowersj2 3666:     }
                   3667:  
1.13      bowersj2 3668:     # print pres_marker
                   3669:     $result .= "\n<input type='hidden' name='pres_marker'" .
                   3670:         " value='$affectedResourceId&$parm_name&$level' />\n";
1.27      bowersj2 3671:     
                   3672:     # Make the table appear
                   3673:     $result .= "\n<input type='hidden' value='true' name='prevvisit' />";
                   3674:     $result .= "\n<input type='hidden' value='$symb' name='pssymb' />";
                   3675:     $result .= "\n<input type='hidden' value='$paramlevel' name='parmlev' />";
1.13      bowersj2 3676: 
1.71      sakharuk 3677:     $result .= "<br /><br /><center><input type='submit' value='".&mt('Finish Helper')."' /></center></form>\n";
1.13      bowersj2 3678: 
                   3679:     return $result;
                   3680: }
                   3681:     
                   3682: sub overrideForm {
                   3683:     return 1;
                   3684: }
1.5       bowersj2 3685: 
1.4       bowersj2 3686: 1;
1.3       bowersj2 3687: 
1.1       bowersj2 3688: __END__
1.3       bowersj2 3689: 

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>
500 Internal Server Error

Internal Server Error

The server encountered an internal error or misconfiguration and was unable to complete your request.

Please contact the server administrator at root@localhost to inform them of the time this error occurred, and the actions you performed just before this error.

More information about this error may be available in the server error log.