--- loncom/interface/lonhelper.pm 2007/10/02 00:33:02 1.161.2.1 +++ loncom/interface/lonhelper.pm 2010/01/12 10:40:01 1.177 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # .helper XML handler to implement the LON-CAPA helper # -# $Id: lonhelper.pm,v 1.161.2.1 2007/10/02 00:33:02 albertel Exp $ +# $Id: lonhelper.pm,v 1.177 2010/01/12 10:40:01 foxr Exp $ # # Copyright Michigan State University Board of Trustees # @@ -188,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 @@ -529,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(); } @@ -587,78 +589,75 @@ sub display { &Apache::loncommon::browser_and_searcher_javascript(). "\n".''; + # 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); - - my $previous = HTML::Entities::encode(&mt("<- Previous"), '<>&"'); - my $next = HTML::Entities::encode(&mt("Next ->"), '<>&"'); + $browser_searcher_js, + {'bread_crumbs' => $brcrum,}); + + my $previous = HTML::Entities::encode(&mt("Back"), '<>&"'); + my $next = HTML::Entities::encode(&mt("Next"), '<>&"'); # FIXME: This should be parameterized, not concatenated - Jeremy - if (!$state->overrideForm()) { $result.="
"; } + if (!$state->overrideForm()) { $result.=''; } if ($stateHelp) { - $stateHelp = &Apache::loncommon::help_open_topic($stateHelp); - } - $result .= < -

$stateTitle$stateHelp

-HEADER - - $result .= "
"; - - if (!$state->overrideForm()) { - $result .= $self->_saveVars(); + $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 @@ -1008,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 ''; } @@ -1023,7 +1025,15 @@ sub end_message { sub render { my $self = shift; - + + 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 @@ -1497,12 +1507,15 @@ sub postprocess { my $self = shift; my $chosenValue = $env{'form.' . $self->{'variable'} . '_forminput'}; + if (!defined($chosenValue) && !$self->{'allowempty'}) { $self->{ERROR_MSG} = &mt("You must choose one or more choices to continue."); return 0; } + + if (ref($chosenValue)) { $helper->{VARS}->{$self->{'variable'}} = join('|||', @$chosenValue); } @@ -1714,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', @@ -1764,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 ' . @@ -1779,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); } @@ -1806,12 +1818,12 @@ sub render { my $i; $result .= "\n"; @@ -1830,7 +1842,7 @@ sub render { # Year $result .= "\n"; } + $result .= ' '.$date->time_zone_short_name().' '; if ($self->{'anytime'}) { $result.=(< @@ -1912,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; @@ -1922,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; } @@ -1963,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; @@ -1989,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 @@ -2062,6 +2106,7 @@ sub start_resource { $helper->declareVar($paramHash->{'variable'}.'_part'); } $paramHash->{'closeallpages'} = $token->[2]{'closeallpages'}; + $paramHash->{'include_top_level_map'} = $token->[2]{'includecourse'}; return ''; } @@ -2150,20 +2195,42 @@ sub start_option { if (!defined($paramHash->{OPTION_TEXTS})) { $paramHash->{OPTION_TEXTS} = [ ]; $paramHash->{OPTION_VARS} = [ ]; + $paramHash->{OPTION_TYPES} = [ ]; } + # We can have an attribute: type which can have the + # values: "checkbox" or "text" which defaults to + # checkbox allowing us to change the type of input + # for the option: + # + my $input_widget_type = 'checkbox'; + if(defined($token->[2]{'type'})) { + my $widget_type = $token->[2]{'type'}; + if ($widget_type eq 'text') { # only accept legal alternatives + $input_widget_type = $widget_type; # Illegals are checks. + } elsif ($widget_type eq 'hidden') { + $input_widget_type = $widget_type; + } + } + # OPTION_TEXTS is a list of the text attribute # values used to create column headings. # OPTION_VARS is a list of the variable names, used to create the checkbox # inputs. + # OPTION_TYPES is a list of the option types: + # # We're ok with empty elements. as place holders # Although the 'variable' element should really exist. # + my $option_texts = $paramHash->{OPTION_TEXTS}; my $option_vars = $paramHash->{OPTION_VARS}; + my $option_types = $paramHash->{OPTION_TYPES}; push(@$option_texts, $token->[2]{'text'}); push(@$option_vars, $token->[2]{'variable'}); + push(@$option_types, $input_widget_type); + # Need to create and declare the option variables as well to make them # persistent. @@ -2239,6 +2306,7 @@ BUTTONS my $multichoice = $self->{'multichoice'}; my $option_vars = $self->{OPTION_VARS}; my $option_texts = $self->{OPTION_TEXTS}; + my $option_types = $self->{OPTION_TYPES}; my $addparts = $self->{'addparts'}; my $headings_done = 0; @@ -2297,17 +2365,50 @@ BUTTONS my $resource_name = HTML::Entities::encode($raw_name,"<>&\"'"); if($option_vars) { + my $option_num = 0; foreach my $option_var (@$option_vars) { + my $option_type = $option_types->[$option_num]; + $option_num++; my $var_value = "\|\|\|" . $helper->{VARS}->{$option_var} . "\|\|\|"; my $checked =""; if($var_value =~ /\Q|||$raw_name|||\E/) { $checked = "checked='checked'"; } - $col .= - " "; + if ($option_type eq 'text') { + # + # For text's the variable value is a ||| separated set of + # resource_name=value + # + my @values = split(/\|\|\|/, $helper->{VARS}->{$option_var}); + + # Normal practice would be to toss this in a hash but + # the only thing that saves is the compare in the loop + # below and for all but one case we'll break out of the loop + # before it completes. + + my $text_value = ''; # In case there's no match. + foreach my $value (@values) { + my ($res, $skip) = split(/=/, $value); + if($res eq $resource_name) { + $text_value = $skip; + last; + } + } + + $col .= + " "; + } elsif ($option_type eq 'hidden') { + $col .= " "; + } else { + $col .= + " "; + } } } @@ -2387,6 +2488,7 @@ RADIO 'resource_no_folder_link' => 1, 'closeAllPages' => $self->{'closeallpages'}, 'suppressEmptySequences' => $self->{'suppressEmptySequences'}, + 'include_top_level_map' => $self->{'include_top_level_map'}, 'iterator_map' => $mapUrl } ); @@ -2402,6 +2504,21 @@ sub postprocess { $self->{ERROR_MSG} = 'You must choose at least one resource to continue.'; return 0; } + # For each of the attached options. If it's env var is undefined, set it to + # an empty string instead.. an undef'd env var means no choices selected. + # + + my $option_vars = $self->{OPTION_VARS}; + if ($option_vars) { + foreach my $var (@$option_vars) { + my $env_name = "form.".$var."_forminput"; + if (!defined($env{$env_name})) { + $env{$env_name} = ''; + $helper->{VARS}->{$var} = ''; + } + } + } + if (defined($self->{NEXTSTATE})) { $helper->changeState($self->{NEXTSTATE}); @@ -2818,17 +2935,17 @@ BUTTONS &Apache::loncacc::constructaccess($subdir, $Apache::lonnet::perlvar{'lonDefDomain'}); $metadir='/res/'.$domain.'/'.$user.'/'.$2; - @fileList = &Apache::lonnet::dirlist($subdir, $domain, $user, ''); + @fileList = &Apache::lonnet::dirlist($subdir,$domain,$user,undef,undef,'/'); } elsif ($subdir =~ m|^~([^/]+)/(.*)$|) { $subdir='/home/'.$1.'/public_html/'.$2; my ($user,$domain)= &Apache::loncacc::constructaccess($subdir, $Apache::lonnet::perlvar{'lonDefDomain'}); $metadir='/res/'.$domain.'/'.$user.'/'.$2; - @fileList = &Apache::lonnet::dirlist($subdir, $domain, $user, ''); + @fileList = &Apache::lonnet::dirlist($subdir,$domain,$user,undef,undef,'/'); } else { # local library server resource space - @fileList = &Apache::lonnet::dirlist($subdir, $env{'user.domain'}, $env{'user.name'}, ''); + @fileList = &Apache::lonnet::dirlist($subdir,$env{'user.domain'},$env{'user.name'},undef,undef,'/'); } # Sort the fileList into order @@ -3377,7 +3494,7 @@ snippets and collecting the results. Fin helper, going to a provided page. If the parameter "restartCourse" is true, this will override the buttons and -will make a "Finish Helper" button that will re-initialize the course for them, +will make a Save button (Finish Helper) that will re-initialize the course for them, which is useful for the Course Initialization helper so the users never see the old values taking effect. @@ -3484,7 +3601,7 @@ sub render { my $actionURL = $self->{EXIT_PAGE}; my $targetURL = ''; - my $finish=&mt('Finish'); + my $finish=&mt('Save'); if ($self->{'restartCourse'}) { $actionURL = '/adm/roles'; $targetURL = '/adm/menu'; @@ -3496,21 +3613,19 @@ sub render { if ($env{'course.'.$env{'request.course.id'}.'.clonedfrom'}) { $targetURL = '/adm/parmset?overview=1'; } - my $finish=&mt('Finish Course Initialization'); } - my $previous = HTML::Entities::encode(&mt("<- Previous"), '<>&"'); - my $next = HTML::Entities::encode(&mt("Next ->"), '<>&"'); + my $previous = HTML::Entities::encode(&mt("Back"), '<>&"'); + my $next = HTML::Entities::encode(&mt("Next"), '<>&"'); my $target = " target='loncapaclient'"; - if (($env{'browser.interface'} eq 'textual') || - ($env{'environment.remote'} eq 'off')) { $target=''; } - $result .= "
\n" . + if ($env{'environment.remote'} eq 'off') { $target=''; } + $result .= "

\n" . "

\n" . "" . "" . "\n" . "\n\n" . - "
"; + "

\n"; return $result; } @@ -3589,7 +3704,7 @@ sub render { # Print the granularity, depending on the action if ($vars->{GRANULARITY} eq 'whole_course') { - $resourceString .= '
  • '.&mt('for all resources in the course').'
  • '; + $resourceString .= '
  • '.&mt('for [_1]all resources in the course[_2]','','').'
  • '; if ($vars->{TARGETS} eq 'course') { $level = 14; # general course, see lonparmset.pm perldoc } elsif ($vars->{TARGETS} eq 'section') { @@ -3604,10 +3719,16 @@ sub render { $paramlevel = 'general'; } elsif ($vars->{GRANULARITY} eq 'map') { my $navmap = Apache::lonnavmaps::navmap->new(); - my $res = $navmap->getByMapPc($vars->{RESOURCE_ID}); - my $title = $res->compTitle(); - $symb = $res->symb(); - $resourceString .= '
  • '.&mt('for the map named [_1]',"$title").'
  • '; + if (defined($navmap)) { + my $res = $navmap->getByMapPc($vars->{RESOURCE_ID}); + my $title = $res->compTitle(); + $symb = $res->symb(); + $resourceString .= '
  • '.&mt('for the map named [_1]',"$title").'
  • '; + } else { + $resourceString .= '
  • '.&mt('for the map ID [_1] (name unavailable)',''.$vars->{RESOURCE_ID}.'').'
  • '; + &Apache::lonnet::logthis('Retrieval of map title failed in lonhelper.pm - could not create navmap object for course.'); + + } if ($vars->{TARGETS} eq 'course') { $level = 13; # general course, see lonparmset.pm perldoc } elsif ($vars->{TARGETS} eq 'section') { @@ -3620,13 +3741,18 @@ sub render { $affectedResourceId = $vars->{RESOURCE_ID}; $paramlevel = 'map'; } else { - my $navmap = Apache::lonnavmaps::navmap->new(); - my $res = $navmap->getById($vars->{RESOURCE_ID}); my $part = $vars->{RESOURCE_ID_part}; if ($part ne 'All Parts' && $part) { $parm_name=~s/^0/$part/; } else { $part=&mt('All Parts'); } - $symb = $res->symb(); - my $title = $res->compTitle(); - $resourceString .= '
  • '.&mt('for the resource named [_1] part [_2]',"$title","$part").'
  • '; + my $navmap = Apache::lonnavmaps::navmap->new(); + if (defined($navmap)) { + my $res = $navmap->getById($vars->{RESOURCE_ID}); + $symb = $res->symb(); + my $title = $res->compTitle(); + $resourceString .= '
  • '.&mt('for the resource named [_1], part [_2]',"$title","$part").'
  • '; + } else { + $resourceString .= '
  • '.&mt('for the resource ID [_1] (name unavailable), part [_2]',''.$vars->{RESOURCE_ID}.'',"$part").'
  • '; + &Apache::lonnet::logthis('Retrieval of resource title failed in lonhelper.pm - could not create navmap object for course.'); + } if ($vars->{TARGETS} eq 'course') { $level = 10; # general course, see lonparmset.pm perldoc } elsif ($vars->{TARGETS} eq 'section') { @@ -3640,7 +3766,7 @@ sub render { $paramlevel = 'full'; } - my $result = "
    \n"; + my $result = "\n"; $result .= "\n"; $result .= "\n"; $result .= "&\"") . "' />\n"; - $result .= '

    '.&mt('Confirm that this information is correct, then click "Finish Helper" to complete setting the parameter.').'

      '; + $result .= '

      ' + .&mt('Confirm that this information is correct, then click "Save" to complete setting the parameter.') + .'

      ' + .'
        '; # Print the type of manipulation: my $extra; @@ -3705,7 +3834,7 @@ sub render { # Print targets if ($vars->{TARGETS} eq 'course') { - $result .= '
      • '.&mt('for all students in course').'
      • '; + $result .= '
      • '.&mt('for [_1]all students in course[_2]','','').'
      • '; } elsif ($vars->{TARGETS} eq 'section') { my $section = $vars->{SECTION_NAME}; $result .= '
      • '.&mt('for section [_1]',"$section").'
      • '; @@ -3730,9 +3859,21 @@ sub render { # Print value if ($vars->{ACTION_TYPE} ne 'tries' && $vars->{ACTION_TYPE} ne 'weight') { - $result .= '
      • '.&mt('to [_1] ([_2])',"".ctime($vars->{PARM_DATE})."",Apache::lonnavmaps::timeToHumanString($vars->{PARM_DATE}))."
      • \n"; + my $showdate = &Apache::lonlocal::locallocaltime($vars->{PARM_DATE}); + $result .= '
      • '.&mt('to [_1] ([_2])',"".$showdate."",Apache::lonnavmaps::timeToHumanString($vars->{PARM_DATE}))."
      • \n"; } + + $result .= '
      '; +# FIXME: Make previous button working +# Found to be dysfunctional when used to change the selected student +# my $previous = HTML::Entities::encode(&mt("Back"), '<>&"'); + my $buttons .= '

      ' +# .'' + .' ' # Finish Helper + .'

      '."\n"; + # print pres_marker $result .= "\n\n"; @@ -3742,7 +3883,7 @@ sub render { $result .= "\n"; $result .= "\n"; - $result .= "

      \n"; + $result .= $buttons; return $result; }