--- loncom/interface/lonhelper.pm 2003/05/27 19:59:38 1.34 +++ loncom/interface/lonhelper.pm 2003/08/13 14:49:58 1.42 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # .helper XML handler to implement the LON-CAPA helper # -# $Id: lonhelper.pm,v 1.34 2003/05/27 19:59:38 bowersj2 Exp $ +# $Id: lonhelper.pm,v 1.42 2003/08/13 14:49:58 bowersj2 Exp $ # # Copyright Michigan State University Board of Trustees # @@ -779,6 +779,18 @@ some setting accidentally. Again, see the course initialization helper for examples. +B + +Some elements that accepts user input can contain a "validator" tag that, +when surrounded by "sub { my $helper = shift; my $state = shift; my $element = shift; my $val = shift " +and "}", where "$val" is the value the user entered, will form a subroutine +that when called will verify whether the given input is valid or not. If it +is valid, the routine will return a false value. If invalid, the routine +will return an error message to be displayed for the user. + +Consult the documentation for each element to see whether it supports this +tag. + B If the element stores the name of the variable in a 'variable' member, which @@ -790,7 +802,7 @@ this method. BEGIN { &Apache::lonhelper::register('Apache::lonhelper::element', ('nextstate', 'finalcode', - 'defaultvalue')); + 'defaultvalue', 'validator')); } # Because we use the param hash, this is often a sufficent @@ -855,6 +867,22 @@ sub start_defaultvalue { sub end_defaultvalue { return ''; } +sub start_validator { + my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; + + if ($target ne 'helper') { + return ''; + } + + $paramHash->{VALIDATOR} = &Apache::lonxml::get_all_text('/validator', + $parser); + $paramHash->{VALIDATOR} = 'sub { my $helper = shift; my $state = shift; my $element = shift; my $val = shift;' . + $paramHash->{VALIDATOR} . '}'; + return ''; +} + +sub end_validator { return ''; } + sub preprocess { return 1; } @@ -1114,7 +1142,6 @@ sub end_choice { } sub render { - # START HERE: Replace this with correct choices code. my $self = shift; my $var = $self->{'variable'}; my $buttons = ''; @@ -1247,6 +1274,153 @@ sub postprocess { } 1; +package Apache::lonhelper::dropdown; + +=pod + +=head2 Element: dropdown + +A drop-down provides a drop-down box instead of a radio button +box. Because most people do not know how to use a multi-select +drop-down box, that option is not allowed. Otherwise, the arguments +are the same as "choices", except "allowempty" is also meaningless. + + takes an attribute "variable" to control which helper variable +the result is stored in. + +B + +, which acts just as it does in the "choices" element. + +=back + +=cut + +no strict; +@ISA = ("Apache::lonhelper::element"); +use strict; + +BEGIN { + &Apache::lonhelper::register('Apache::lonhelper::dropdown', + ('dropdown')); +} + +sub new { + my $ref = Apache::lonhelper::element->new(); + bless($ref); +} + +# CONSTRUCTION: Construct the message element from the XML +sub start_dropdown { + my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; + + if ($target ne 'helper') { + return ''; + } + + # Need to initialize the choices list, so everything can assume it exists + $paramHash->{'variable'} = $token->[2]{'variable'} if (!defined($paramHash->{'variable'})); + $helper->declareVar($paramHash->{'variable'}); + $paramHash->{CHOICES} = []; + return ''; +} + +sub end_dropdown { + my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; + + if ($target ne 'helper') { + return ''; + } + Apache::lonhelper::dropdown->new(); + return ''; +} + +sub render { + my $self = shift; + my $var = $self->{'variable'}; + my $result = ''; + + if (defined $self->{ERROR_MSG}) { + $result .= '
' . $self->{ERROR_MSG} . '
'; + } + + my %checkedChoices; + my $checkedChoicesFunc; + + if (defined($self->{DEFAULT_VALUE})) { + $checkedChoicesFunc = eval ($self->{DEFAULT_VALUE}); + die 'Error in default value code for variable ' . + $self->{'variable'} . ', Perl said: ' . $@ if $@; + } else { + $checkedChoicesFunc = sub { return ''; }; + } + + # single choice + my $selectedChoice = &$checkedChoicesFunc($helper, $self); + + my $foundChoice = 0; + + # check that the choice is in the list of choices. + for my $choice (@{$self->{CHOICES}}) { + if ($choice->[1] eq $selectedChoice) { + $checkedChoices{$choice->[1]} = 1; + $foundChoice = 1; + } + } + + # If we couldn't find the choice, pick the first one + if (!$foundChoice) { + $checkedChoices{$self->{CHOICES}->[0]->[1]} = 1; + } + + $result .= "{$_}->[$section]) + " value='" . HTML::Entities::encode($choice->[0] . ':' . $choice->[2]) . "' />" - . HTML::Entities::encode($choices->{$_}->[$fullname]) + . HTML::Entities::encode($choice->[1]) . "" - . HTML::Entities::encode($choices->{$_}->[$section]) - . "\n"; + . HTML::Entities::encode($choice->[2]) + . "\n" + . HTML::Entities::encode($choice->[3]) . "\n"; } $result .= "\n\n"; @@ -2262,6 +2473,8 @@ also pass through 'maxlength' and 'size' string honors the defaultvalue tag, if given. +string honors the validation function, if given. + =cut no strict; @@ -2307,7 +2520,13 @@ sub end_string { sub render { my $self = shift; - my $result = '{ERROR_MSG}) { + $result .= '
' . $self->{ERROR_MSG} . '

'; + } + + $result .= '{'size'})) { $result .= ' size="' . $self->{'size'} . '"'; @@ -2331,8 +2550,19 @@ sub render { # If a NEXTSTATE was given, switch to it sub postprocess { my $self = shift; - if (defined($self->{NEXTSTATE})) { - $helper->changeState($self->{NEXTSTATE}); + + if (defined($self->{VALIDATOR})) { + my $validator = eval($self->{VALIDATOR}); + die 'Died during evaluation of evaulation code; Perl said: ' . $@ if $@; + my $invalid = &$validator($helper, $state, $self, $self->getValue()); + if ($invalid) { + $self->{ERROR_MSG} = $invalid; + return 0; + } + } + + if (defined($self->{'nextstate'})) { + $helper->changeState($self->{'nextstate'}); } return 1; @@ -2570,31 +2800,33 @@ sub render { } } - if (scalar(@results) == 0) { - return ''; - } - - my $result = "
    \n"; - for my $re (@results) { - $result .= '
  • ' . $re . "
  • \n"; - } + my $result; - if (!@results) { - $result .= '
  • No changes were made to current settings.
  • '; + if (scalar(@results) != 0) { + $result .= "
      \n"; + for my $re (@results) { + $result .= '
    • ' . $re . "
    • \n"; + } + + if (!@results) { + $result .= '
    • No changes were made to current settings.
    • '; + } + + $result .= '
    '; } if ($self->{'restartCourse'}) { $result .= "
    \n" . "
    \n" . "" . - "" . + "" . "\n" . "\n\n" . "
    "; } - return $result . '
'; + return $result; } sub overrideForm { @@ -2646,10 +2878,13 @@ sub render { # FIXME: Unify my designators with the standard ones my %dateTypeHash = ('open_date' => "Opening Date", 'due_date' => "Due Date", - 'answer_date' => "Answer Date"); + 'answer_date' => "Answer Date", + 'tries' => 'Number of Tries' + ); my %parmTypeHash = ('open_date' => "0_opendate", 'due_date' => "0_duedate", - 'answer_date' => "0_answerdate"); + 'answer_date' => "0_answerdate", + 'tries' => '0_maxtries' ); my $affectedResourceId = ""; my $parm_name = $parmTypeHash{$vars->{ACTION_TYPE}}; @@ -2666,10 +2901,8 @@ sub render { $symb = 'a'; $paramlevel = 'general'; } elsif ($vars->{GRANULARITY} eq 'map') { - my $navmap = Apache::lonnavmaps::navmap->new( - $ENV{"request.course.fn"}.".db", - $ENV{"request.course.fn"}."_parms.db", 0, 0); - my $res = $navmap->getById($vars->{RESOURCE_ID}); + my $navmap = Apache::lonnavmaps::navmap->new(); + my $res = $navmap->getByMapPc($vars->{RESOURCE_ID}); my $title = $res->compTitle(); $symb = $res->symb(); $navmap->untieHashes(); @@ -2678,9 +2911,7 @@ sub render { $affectedResourceId = $vars->{RESOURCE_ID}; $paramlevel = 'map'; } else { - my $navmap = Apache::lonnavmaps::navmap->new( - $ENV{"request.course.fn"}.".db", - $ENV{"request.course.fn"}."_parms.db", 0, 0); + my $navmap = Apache::lonnavmaps::navmap->new(); my $res = $navmap->getById($vars->{RESOURCE_ID}); $symb = $res->symb(); my $title = $res->compTitle(); @@ -2695,8 +2926,11 @@ sub render { $result .= '

Confirm that this information is correct, then click "Finish Wizard" to complete setting the parameter.

    '; # Print the type of manipulation: - $result .= '
  • Setting the ' . $dateTypeHash{$vars->{ACTION_TYPE}} - . "
  • \n"; + $result .= '
  • Setting the ' . $dateTypeHash{$vars->{ACTION_TYPE}} . ''; + if ($vars->{ACTION_TYPE} eq 'tries') { + $result .= ' to ' . $vars->{TRIES} . ''; + } + $result .= "
  • \n"; if ($vars->{ACTION_TYPE} eq 'due_date' || $vars->{ACTION_TYPE} eq 'answer_date') { # for due dates, we default to "date end" type entries @@ -2713,7 +2947,10 @@ sub render { "value='" . $vars->{PARM_DATE} . "' />\n"; $result .= "\n"; - } + } elsif ($vars->{ACTION_TYPE} eq 'tries') { + $result .= "\n"; + } $result .= $resourceString; @@ -2743,10 +2980,12 @@ sub render { } # Print value - $result .= "
  • to " . ctime($vars->{PARM_DATE}) . " (" . - Apache::lonnavmaps::timeToHumanString($vars->{PARM_DATE}) - . ")
  • \n"; - + if ($vars->{ACTION_TYPE} ne 'tries') { + $result .= "
  • to " . ctime($vars->{PARM_DATE}) . " (" . + Apache::lonnavmaps::timeToHumanString($vars->{PARM_DATE}) + . ")
  • \n"; + } + # print pres_marker $result .= "\n\n";