--- loncom/interface/lonhelper.pm 2006/12/12 20:41:08 1.159 +++ loncom/interface/lonhelper.pm 2011/10/25 22:27:11 1.184 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # .helper XML handler to implement the LON-CAPA helper # -# $Id: lonhelper.pm,v 1.159 2006/12/12 20:41:08 albertel Exp $ +# $Id: lonhelper.pm,v 1.184 2011/10/25 22:27:11 www Exp $ # # Copyright Michigan State University Board of Trustees # @@ -81,25 +81,28 @@ State tags are also required to have an human name of the state, and will be displayed as the header on top of the screen for the user. +State tags may also optionally have an attribute "help" which should be +the filename of a help file, this will add a blue ? to the title. + =head2 Example Helper Skeleton An example of the tags so far: - + -Of course this does nothing. In order for the wizard to do something, it is -necessary to put actual elements into the wizard. Documentation for each +Of course this does nothing. In order for the helper to do something, it is +necessary to put actual elements into the helper. Documentation for each of these elements follows. =head1 Creating a Helper With Code, Not XML -In some situations, such as the printing wizard (see lonprintout.pm), +In some situations, such as the printing helper (see lonprintout.pm), writing the helper in XML would be too complicated, because of scope issues or the fact that the code actually outweighs the XML. It is possible to create a helper via code, though it is a little odd. @@ -185,6 +188,8 @@ use Apache::lonlocal; use Apache::lonnet; use Apache::longroup; use Apache::lonselstudent; + + use LONCAPA; # Register all the tags with the helper, so the helper can @@ -334,7 +339,8 @@ sub start_state { } Apache::lonhelper::state->new($token->[2]{'name'}, - $token->[2]{'title'}); + $token->[2]{'title'}, + $token->[2]{'help'}); return ''; } @@ -525,7 +531,7 @@ sub process { # Phase 1: Post processing for state of previous screen (which is actually # the "current state" in terms of the helper variables), if it wasn't the # beginning state. - if ($self->{STATE} ne "START" || $env{"form.SUBMIT"} eq &mt("Next ->")) { + if ($self->{STATE} ne "START" || $env{"form.SUBMIT"} eq &mt("Next")) { my $prevState = $self->{STATES}{$self->{STATE}}; $prevState->postprocess(); } @@ -565,7 +571,7 @@ sub process { # 4: Render the current state to the screen as an HTML page. sub display { my $self = shift; - + my $footer = shift; my $state = $self->{STATES}{$self->{STATE}}; my $result = ""; @@ -577,84 +583,85 @@ sub display { # Phase 4: Display. my $stateTitle=&mt($state->title()); + my $stateHelp= $state->help(); my $browser_searcher_js = ''; - $result .= &Apache::loncommon::start_page($self->{TITLE}, - $browser_searcher_js); - - my $previous = HTML::Entities::encode(&mt("<- Previous"), '<>&"'); - my $next = HTML::Entities::encode(&mt("Next ->"), '<>&"'); - # FIXME: This should be parameterized, not concatenated - Jeremy + # Breadcrumbs + my $brcrum = [{'href' => '', + 'text' => 'Helper'}]; + # FIXME: Dynamically add context sensitive breadcrumbs + # depending on the caller, + # e.g. printing, parametrization, etc. + # FIXME: Add breadcrumbs to reflect current helper state + $result .= &Apache::loncommon::start_page($self->{TITLE}, + $browser_searcher_js, + {'bread_crumbs' => $brcrum,}); - if (!$state->overrideForm()) { $result.="
"; } - $result .= < -

$stateTitle

-HEADER + my $previous = HTML::Entities::encode(&mt("Back"), '<>&"'); + my $next = HTML::Entities::encode(&mt("Next"), '<>&"'); + # FIXME: This should be parameterized, not concatenated - Jeremy - $result .= "
"; - if (!$state->overrideForm()) { - $result .= $self->_saveVars(); + if (!$state->overrideForm()) { $result.=''; } + if ($stateHelp) { + $stateHelp = &Apache::loncommon::help_open_topic($stateHelp); } - $result .= $state->render(); - - $result .= ""; - # Warning: Copy and pasted from below, because it's too much trouble to - # turn this into a subroutine + # Prepare buttons + my $buttons; if (!$state->overrideForm()) { if ($self->{STATE} ne $self->{START_STATE}) { #$result .= '  '; } + $buttons = '

'; # '

'; if ($self->{DONE}) { my $returnPage = $self->{RETURN_PAGE}; - $result .= "" . &mt("End Helper") . ""; + $buttons .= ''.&mt('End Helper').''; } else { - $result .= ' '; - $result .= ''; + $buttons .= '' + .' ' + .'' + .''; } + $buttons .= '

'; # '
'; } - $result .= "
"; - # Warning: Copy and pasted from above, because it's too much trouble to - # turn this into a subroutine + + $result .= '

'.$stateTitle.$stateHelp.'

'; + +# $result .= '
'; + + # Top buttons + $result .= $buttons; + + # Main content of current helper screen if (!$state->overrideForm()) { - if ($self->{STATE} ne $self->{START_STATE}) { - #$result .= '  '; - } - if ($self->{DONE}) { - my $returnPage = $self->{RETURN_PAGE}; - $result .= "" . &mt('End Helper') . ""; - } - else { - $result .= ' '; - $result .= ''; - } + $result .= $self->_saveVars(); } + $result .= $state->render(); + + # Bottom buttons + $result .= $buttons; + #foreach my $key (keys %{$self->{VARS}}) { # $result .= "|$key| -> " . $self->{VARS}->{$key} . "
"; #} - $result .= "
"; +# $result .= ''; $result .= < - - FOOTER - $result .= &Apache::loncommon::end_page(); + $result .= $footer.&Apache::loncommon::end_page(); # Handle writing out the vars to the file my $file = Apache::File->new('>'.$self->{FILENAME}); print $file $self->_varsInFile(); @@ -680,6 +687,7 @@ sub new { $self->{NAME} = shift; $self->{TITLE} = shift; + $self->{HELP} = shift; $self->{ELEMENTS} = []; bless($self, $class); @@ -701,6 +709,11 @@ sub title { return $self->{TITLE}; } +sub help { + my $self = shift; + return $self->{HELP}; +} + sub preprocess { my $self = shift; for my $element (@{$self->{ELEMENTS}}) { @@ -792,7 +805,7 @@ the element. How this value is interpret the element itself, and possibly the settings the element has (such as multichoice vs. single choice for tags). -This is also intended for things like the course initialization wizard, where the +This is also intended for things like the course initialization helper, where the user is setting various parameters. By correctly grabbing current settings and including them into the helper, it allows the user to come back to the helper later and re-execute it, without needing to worry about overwriting @@ -994,6 +1007,9 @@ sub start_message { if (defined($token->[2]{'nextstate'})) { $paramHash->{NEXTSTATE} = $token->[2]{'nextstate'}; } + if (defined($token->[2]{'type'})) { + $paramHash->{TYPE} = $token->[2]{'type'}; + } return ''; } @@ -1009,8 +1025,16 @@ sub end_message { sub render { my $self = shift; - - return &mtn($self->{MESSAGE_TEXT}); + + if ($self->{TYPE} =~ /^\s*warning\s*$/i) { + $self->{MESSAGE_TEXT} = + ''. $self->{MESSAGE_TEXT}.''; + } + if ($self->{TYPE} =~ /^\s*error\s*$/i) { + $self->{MESSAGE_TEXT} = + ''. $self->{MESSAGE_TEXT}.''; + } + return $self->{MESSAGE_TEXT}; } # If a NEXTSTATE was given, switch to it sub postprocess { @@ -1100,12 +1124,21 @@ sub render { my $text; if ( $self->{HELP_TEXT} ne '') { - $text=&mtn($text); + $text=&mtn($self->{HELP_TEXT}); } return &Apache::loncommon::help_open_topic($self->{HELP_FILE}, $text); } +sub postprocess { + my $self = shift; + if (defined($self->{NEXTSTATE})) { + $helper->changeState($self->{NEXTSTATE}); + } + + return 1; +} + 1; package Apache::lonhelper::skip; @@ -1443,9 +1476,9 @@ BUTTONS HTML::Entities::encode($choice->[1],"<>&\"'") . "'"; if ($checkedChoices{$choice->[1]}) { - $result .= " checked='checked' "; + $result .= " checked='checked'"; } - $result .= qq{id="id$id"}; + $result .= qq{ id="id$id"}; my $choiceLabel = $choice->[0]; if ($choice->[3]) { # if we need to evaluate this choice $choiceLabel = "sub { my $helper = shift; my $state = shift;" . @@ -1453,8 +1486,8 @@ BUTTONS $choiceLabel = eval($choiceLabel); $choiceLabel = &$choiceLabel($helper, $self); } - $result .= "/> ".qq{"; + $result .= " /> ".qq{"; if ($choice->[4]) { $result .='{VARS}->{$self->{'variable'}} = join('|||', @$chosenValue); } @@ -1691,7 +1727,7 @@ no strict; use strict; use Apache::lonlocal; # A localization nightmare use Apache::lonnet; -use Time::localtime; +use DateTime; BEGIN { &Apache::lonhelper::register('Apache::lonhelper::date', @@ -1741,14 +1777,13 @@ sub render { my $time=time; my ($anytime,$onclick); - # first check VARS for a valid new value from the user # then check DEFAULT_VALUE for a valid default time value # otherwise pick now as reasonably good time if (defined($helper->{VARS}{$var}) && $helper->{VARS}{$var} > 0) { - $date = localtime($helper->{VARS}{$var}); + $date = &get_date_object($helper->{VARS}{$var}); } elsif (defined($self->{DEFAULT_VALUE})) { my $valueFunc = eval($self->{DEFAULT_VALUE}); die('Error in default value code for variable ' . @@ -1756,17 +1791,17 @@ sub render { $time = &$valueFunc($helper, $self); if (lc($time) eq 'anytime') { $anytime=1; - $date = localtime(time); + $date = &get_date_object(time); $date->min(0); } elsif (defined($time) && $time ne 0) { - $date = localtime($time); + $date = &get_date_object($time); } else { # leave date undefined so it'll default to now } } if (!defined($date)) { - $date = localtime(time); + $date = &get_date_object(time); $date->min(0); } @@ -1783,12 +1818,12 @@ sub render { my $i; $result .= "\n"; @@ -1807,7 +1842,7 @@ sub render { # Year $result .= "\n"; } + $result .= ' '.$date->time_zone_short_name().' '; if ($self->{'anytime'}) { $result.=(< @@ -1889,7 +1925,8 @@ sub postprocess { if ($env{'form.' . $var . 'anytime'}) { $helper->{VARS}->{$var} = undef; } else { - my $month = $env{'form.' . $var . 'month'}; + my $month = $env{'form.' . $var . 'month'}; + $month ++; my $day = $env{'form.' . $var . 'day'}; my $year = $env{'form.' . $var . 'year'}; my $min = 0; @@ -1899,25 +1936,40 @@ sub postprocess { $hour = $env{'form.' . $var . 'hour'}; } - my $chosenDate; - eval {$chosenDate = Time::Local::timelocal(0, $min, $hour, $day, $month, $year);}; + my ($chosenDate,$checkDate); + my $timezone = &Apache::lonlocal::gettimezone(); + my $dt; + eval { + $dt = DateTime->new( year => $year, + month => $month, + day => $day, + hour => $hour, + minute => $min, + second => 0, + time_zone => $timezone, + ); + }; + my $error = $@; + if (!$error) { + $chosenDate = $dt->epoch; + $checkDate = &get_date_object($chosenDate); + } # Check to make sure that the date was not automatically co-erced into a # valid date, as we want to flag that as an error # This happens for "Feb. 31", for instance, which is coerced to March 2 or # 3, depending on if it's a leap year - my $checkDate = localtime($chosenDate); if ($error || $checkDate->mon != $month || $checkDate->mday != $day || - $checkDate->year + 1900 != $year) { + $checkDate->year != $year) { unless (Apache::lonlocal::current_language()== ~/^en/) { $self->{ERROR_MSG} = &mt("Invalid date entry"); return 0; } # LOCALIZATION FIXME: Needs to be parameterized - $self->{ERROR_MSG} = "Can't use " . $months[$month] . " $day, $year as a " - . "date because it doesn't exist. Please enter a valid date."; + $self->{ERROR_MSG} = "Can't use ".$months[$env{'form.'.$var.'month'}]. " $day, $year as a ". + "date because it doesn't exist. Please enter a valid date."; return 0; } @@ -1940,6 +1992,20 @@ sub postprocess { return 1; } + +sub get_date_object { + my ($epoch) = @_; + my $dt = DateTime->from_epoch(epoch => $epoch) + ->set_time_zone(&Apache::lonlocal::gettimezone()); + my $lang = Apache::lonlocal::current_language(); + if ($lang ne '') { + eval { + $dt->set_locale($lang); + }; + } + return $dt; +} + 1; package Apache::lonhelper::resource; @@ -1966,7 +2032,8 @@ folders that have all of their contained be filtered out. The 'addstatus' attribute, if true, will add the icon and long status display columns to the display. The 'addparts' attribute will add in a part selector beside problems that have more -than 1 part. +than 1 part. The 'includecourse' attribute if true, will include +the toplevel default.sequence in the results. =head3 SUB-TAGS @@ -1999,6 +2066,49 @@ than 1 part. evaluated with "sub { my $helper = shift; my $state = shift;" and "}", with the return value used as the mapurl. +=item *