--- loncom/interface/lonhelper.pm 2003/10/09 15:20:15 1.55 +++ loncom/interface/lonhelper.pm 2005/07/07 03:56:52 1.104 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # .helper XML handler to implement the LON-CAPA helper # -# $Id: lonhelper.pm,v 1.55 2003/10/09 15:20:15 albertel Exp $ +# $Id: lonhelper.pm,v 1.104 2005/07/07 03:56:52 albertel Exp $ # # Copyright Michigan State University Board of Trustees # @@ -25,10 +25,6 @@ # # http://www.lon-capa.org/ # -# (Page Handler -# -# (.helper handler -# =pod @@ -186,6 +182,7 @@ use Apache::Constants qw(:common); use Apache::File; use Apache::lonxml; use Apache::lonlocal; +use Apache::lonnet; # Register all the tags with the helper, so the helper can # push and pop them @@ -230,7 +227,7 @@ my $paramHash; # In the debugger, this means that breakpoints are ignored until you step into # a function and get out of what must be a "faked up scope" in the Apache-> # mod_perl connection. In this code, it was manifesting itself in the existence -# of two seperate file-scoped $helper variables, one set to the value of the +# of two separate file-scoped $helper variables, one set to the value of the # helper in the helper constructor, and one referenced by the handler on the # "$helper->process()" line. Using the debugger, one could actually # see the two different $helper variables, as hashes at completely @@ -255,7 +252,7 @@ sub real_handler { my $r = shift; my $uri = shift; if (!defined($uri)) { $uri = $r->uri(); } - $ENV{'request.uri'} = $uri; + $env{'request.uri'} = $uri; my $filename = '/home/httpd/html' . $uri; my $fh = Apache::File->new($filename); my $file; @@ -263,21 +260,13 @@ sub real_handler { # Send header, don't cache this page - if ($r->header_only) { - if ($ENV{'browser.mathml'}) { - $r->content_type('text/xml; charset=UTF-8'); - } else { - $r->content_type('text/html; charset=UTF-8'); - } - $r->send_http_header; - return OK; - } - if ($ENV{'browser.mathml'}) { - $r->content_type('text/xml'); + if ($env{'browser.mathml'}) { + &Apache::loncommon::content_type($r,'text/xml'); } else { - $r->content_type('text/html'); + &Apache::loncommon::content_type($r,'text/html'); } $r->send_http_header; + return OK if $r->header_only; $r->rflush(); # Discard result, we just want the objects that get created by the @@ -286,7 +275,7 @@ sub real_handler { my $allowed = $helper->allowedCheck(); if (!$allowed) { - $ENV{'user.error.msg'} = $ENV{'request.uri'}.':'.$helper->{REQUIRED_PRIV}. + $env{'user.error.msg'} = $env{'request.uri'}.':'.$helper->{REQUIRED_PRIV}. ":0:0:Permission denied to access this helper."; return HTTP_NOT_ACCEPTABLE; } @@ -371,6 +360,7 @@ use HTML::Entities(); use Apache::loncommon; use Apache::File; use Apache::lonlocal; +use Apache::lonnet; sub new { my $proto = shift; @@ -382,16 +372,16 @@ sub new { # If there is a state from the previous form, use that. If there is no # state, use the start state parameter. - if (defined $ENV{"form.CURRENT_STATE"}) + if (defined $env{"form.CURRENT_STATE"}) { - $self->{STATE} = $ENV{"form.CURRENT_STATE"}; + $self->{STATE} = $env{"form.CURRENT_STATE"}; } else { $self->{STATE} = "START"; } - $self->{TOKEN} = $ENV{'form.TOKEN'}; + $self->{TOKEN} = $env{'form.TOKEN'}; # If a token was passed, we load that in. Otherwise, we need to create a # new storage file # Tried to use standard Tie'd hashes, but you can't seem to take a @@ -424,16 +414,16 @@ sub new { return undef; } # Must create the storage - $self->{TOKEN} = md5_hex($ENV{'user.name'} . $ENV{'user.domain'} . + $self->{TOKEN} = md5_hex($env{'user.name'} . $env{'user.domain'} . time() . rand()); $self->{FILENAME} = $Apache::lonnet::tmpdir . md5_hex($self->{TOKEN}); } # OK, we now have our persistent storage. - if (defined $ENV{"form.RETURN_PAGE"}) + if (defined $env{"form.RETURN_PAGE"}) { - $self->{RETURN_PAGE} = $ENV{"form.RETURN_PAGE"}; + $self->{RETURN_PAGE} = $env{"form.RETURN_PAGE"}; } else { @@ -462,11 +452,11 @@ sub _saveVars { my $self = shift; my $result = ""; $result .= '\n"; + HTML::Entities::encode($self->{STATE},'<>&"') . "\" />\n"; $result .= '\n"; $result .= '\n"; + HTML::Entities::encode($self->{RETURN_PAGE},'<>&"') . "\" />\n"; return $result; } @@ -494,11 +484,11 @@ sub declareVar { } my $envname = 'form.' . $var . '.forminput'; - if (defined($ENV{$envname})) { - if (ref($ENV{$envname})) { - $self->{VARS}->{$var} = join('|||', @{$ENV{$envname}}); + if (defined($env{$envname})) { + if (ref($env{$envname})) { + $self->{VARS}->{$var} = join('|||', @{$env{$envname}}); } else { - $self->{VARS}->{$var} = $ENV{$envname}; + $self->{VARS}->{$var} = $env{$envname}; } } } @@ -510,7 +500,7 @@ sub allowedCheck { return 1; } - return Apache::lonnet::allowed($self->{REQUIRED_PRIV}, $ENV{'request.course.id'}); + return Apache::lonnet::allowed($self->{REQUIRED_PRIV}, $env{'request.course.id'}); } sub changeState { @@ -532,7 +522,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(); } @@ -583,7 +573,8 @@ sub display { } # Phase 4: Display. - my $stateTitle = &mt($state->title()); + my $html=&Apache::lonxml::xmlbegin(); + my $stateTitle=&mt($state->title()); my $helperTitle = &mt($self->{TITLE}); my $bodytag = &Apache::loncommon::bodytag($helperTitle,'',''); my $previous = HTML::Entities::encode(&mt("<- Previous"), '<>&"'); @@ -592,9 +583,8 @@ sub display { my $loncapaHelper = &mt("LON-CAPA Helper:"); $result .= < +$html - $loncapaHelper: $helperTitle $bodytag @@ -998,8 +988,8 @@ sub start_message { return ''; } - $paramHash->{MESSAGE_TEXT} = &Apache::lonxml::get_all_text('/message', - $parser); + $paramHash->{MESSAGE_TEXT} = &mtn(&Apache::lonxml::get_all_text('/message', + $parser)); if (defined($token->[2]{'nextstate'})) { $paramHash->{NEXTSTATE} = $token->[2]{'nextstate'}; @@ -1123,6 +1113,7 @@ no strict; @ISA = ("Apache::lonhelper::element"); use strict; use Apache::lonlocal; +use Apache::lonnet; BEGIN { &Apache::lonhelper::register('Apache::lonhelper::choices', @@ -1169,11 +1160,11 @@ sub start_choice { } my $computer = $token->[2]{'computer'}; - my $human = &Apache::lonxml::get_all_text('/choice', - $parser); + my $human = &mt(&Apache::lonxml::get_all_text('/choice', + $parser)); my $nextstate = $token->[2]{'nextstate'}; my $evalFlag = $token->[2]{'eval'}; - push @{$paramHash->{CHOICES}}, [$human, $computer, $nextstate, + push @{$paramHash->{CHOICES}}, [&mtn($human), $computer, $nextstate, $evalFlag]; return ''; } @@ -1182,6 +1173,13 @@ sub end_choice { return ''; } +{ + # used to generate unique id attributes for tags. + # internal use only. + my $id = 0; + sub new_id { return $id++; } +} + sub render { my $self = shift; my $var = $self->{'variable'}; @@ -1206,10 +1204,13 @@ SCRIPT # Only print "select all" and "unselect all" if there are five or # more choices; fewer then that and it looks silly. if ($self->{'multichoice'} && scalar(@{$self->{CHOICES}}) > 4) { + my %lt=&Apache::lonlocal::texthash( + 'sa' => "Select All", + 'ua' => "Unselect All"); $buttons = < - - + +
  BUTTONS } @@ -1261,14 +1262,16 @@ BUTTONS my $type = "radio"; if ($self->{'multichoice'}) { $type = 'checkbox'; } foreach my $choice (@{$self->{CHOICES}}) { + my $id = &new_id(); $result .= "\n \n"; $result .= "[1]}) { $result .= " checked "; } + $result .= qq{id="$id"}; my $choiceLabel = $choice->[0]; if ($choice->[4]) { # if we need to evaluate this choice $choiceLabel = "sub { my $helper = shift; my $state = shift;" . @@ -1276,7 +1279,8 @@ BUTTONS $choiceLabel = eval($choiceLabel); $choiceLabel = &$choiceLabel($helper, $self); } - $result .= "/> " . &mtn($choiceLabel) . "\n"; + $result .= "/> ".qq{\n"; } $result .= "\n\n\n"; $result .= $buttons; @@ -1288,11 +1292,11 @@ BUTTONS # given, switch to it sub postprocess { my $self = shift; - my $chosenValue = $ENV{'form.' . $self->{'variable'} . '.forminput'}; + my $chosenValue = $env{'form.' . $self->{'variable'} . '.forminput'}; if (!defined($chosenValue) && !$self->{'allowempty'}) { - $self->{ERROR_MSG} = "You must choose one or more choices to" . - " continue."; + $self->{ERROR_MSG} = + &mt("You must choose one or more choices to continue."); return 0; } @@ -1342,6 +1346,7 @@ no strict; @ISA = ("Apache::lonhelper::element"); use strict; use Apache::lonlocal; +use Apache::lonnet; BEGIN { &Apache::lonhelper::register('Apache::lonhelper::dropdown', @@ -1419,7 +1424,7 @@ sub render { $result .= "\n"; $result .= "\n"; + " value='0'>" . &mt('midnight') . "\n"; for ($i = 1; $i < 12; $i++) { if ($date->hour == $i) { - $result .= "\n"; + $result .= "\n"; } else { - $result .= "\n"; + $result .= "\n"; } } $result .= "\n"; + " value='12'>" . &mt('noon') . "\n"; for ($i = 13; $i < 24; $i++) { my $printedHour = $i - 12; if ($date->hour == $i) { - $result .= "\n"; + $result .= "\n"; } else { - $result .= "\n"; + $result .= "\n"; } } @@ -1642,14 +1650,14 @@ sub render { sub postprocess { my $self = shift; my $var = $self->{'variable'}; - my $month = $ENV{'form.' . $var . 'month'}; - my $day = $ENV{'form.' . $var . 'day'}; - my $year = $ENV{'form.' . $var . 'year'}; + my $month = $env{'form.' . $var . 'month'}; + my $day = $env{'form.' . $var . 'day'}; + my $year = $env{'form.' . $var . 'year'}; my $min = 0; my $hour = 0; if ($self->{'hoursminutes'}) { - $min = $ENV{'form.' . $var . 'minute'}; - $hour = $ENV{'form.' . $var . 'hour'}; + $min = $env{'form.' . $var . 'minute'}; + $hour = $env{'form.' . $var . 'hour'}; } my $chosenDate; @@ -1707,7 +1715,9 @@ to false. The "suppressEmptySequences" a suppressEmptySequences argument to the render routine, which will cause folders that have all of their contained resources filtered out to also be filtered out. The 'addstatus' attribute, if true, will add the icon -and long status display columns to the display. +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. =head3 SUB-TAGS @@ -1747,12 +1757,13 @@ and long status display columns to the d no strict; @ISA = ("Apache::lonhelper::element"); use strict; +use Apache::lonnet; BEGIN { &Apache::lonhelper::register('Apache::lonhelper::resource', ('resource', 'filterfunc', 'choicefunc', 'valuefunc', - 'mapurl')); + 'mapurl','option')); } sub new { @@ -1774,6 +1785,11 @@ sub start_resource { $paramHash->{'suppressEmptySequences'} = $token->[2]{'suppressEmptySequences'}; $paramHash->{'toponly'} = $token->[2]{'toponly'}; $paramHash->{'addstatus'} = $token->[2]{'addstatus'}; + $paramHash->{'addparts'} = $token->[2]{'addparts'}; + if ($paramHash->{'addparts'}) { + $helper->declareVar($paramHash->{'variable'}.'_part'); + } + $paramHash->{'closeallpages'} = $token->[2]{'closeallpages'}; return ''; } @@ -1856,6 +1872,42 @@ sub start_mapurl { sub end_mapurl { return ''; } + +sub start_option { + my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; + if (!defined($paramHash->{OPTION_TEXTS})) { + $paramHash->{OPTION_TEXTS} = [ ]; + $paramHash->{OPTION_VARS} = [ ]; + + } + # 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. + # 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}; + push(@$option_texts, $token->[2]{'text'}); + push(@$option_vars, $token->[2]{'variable'}); + + # Need to create and declare the option variables as well to make them + # persistent. + # + my $varname = $token->[2]{'variable'}; + $helper->declareVar($varname); + + + return ''; +} + +sub end_option { + my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; + return ''; +} + # A note, in case I don't get to this before I leave. # If someone complains about the "Back" button returning them # to the previous folder state, instead of returning them to @@ -1889,10 +1941,14 @@ sub render { } SCRIPT + my %lt=&Apache::lonlocal::texthash( + 'sar' => "Select All Resources", + 'uar' => "Unselect All Resources"); + $buttons = <   - - + +
  BUTTONS } @@ -1903,10 +1959,14 @@ BUTTONS $result .= $buttons; - my $filterFunc = $self->{FILTER_FUNC}; - my $choiceFunc = $self->{CHOICE_FUNC}; - my $valueFunc = $self->{VALUE_FUNC}; - my $multichoice = $self->{'multichoice'}; + my $filterFunc = $self->{FILTER_FUNC}; + my $choiceFunc = $self->{CHOICE_FUNC}; + my $valueFunc = $self->{VALUE_FUNC}; + my $multichoice = $self->{'multichoice'}; + my $option_vars = $self->{OPTION_VARS}; + my $option_texts = $self->{OPTION_TEXTS}; + my $addparts = $self->{'addparts'}; + my $headings_done = 0; # Evaluate the map url as needed my $mapUrl; @@ -1918,21 +1978,54 @@ BUTTONS $mapUrl = $self->{MAP_URL}; } + # Create the composite function that renders the column on the nav map # have to admit any language that lets me do this can't be all bad # - Jeremy (Pythonista) ;-) my $checked = 0; my $renderColFunc = sub { my ($resource, $part, $params) = @_; + my $result = ""; + + if(!$headings_done) { + if ($option_texts) { + foreach my $text (@$option_texts) { + $result .= "$text"; + } + } + $result .= "Select"; + $result .= ""; # Close off the extra row and start a new one. + $headings_done = 1; + } my $inputType; if ($multichoice) { $inputType = 'checkbox'; } else {$inputType = 'radio'; } if (!&$choiceFunc($resource)) { - return ' '; + $result .= ' '; + return $result; } else { - my $col = "&\"'"); + if($option_vars) { + foreach my $option_var (@$option_vars) { + my $var_value = "\|\|\|" . $helper->{VARS}->{$option_var} . + "\|\|\|"; + my $checked =""; + if($var_value =~ /\Q|||$raw_name|||\E/) { + $checked = "checked"; + } + $col .= + " "; + } + } + + $col .= ""; - return $col; + $col .= "value='" . $resource_name . "' />"; + + return $result.$col; } }; + my $renderPartsFunc = sub { + my ($resource, $part, $params) = @_; + my $col= ""; + my $id=$resource->{ID}; + my $resource_name = + &HTML::Entities::encode(&$valueFunc($resource),"<>&\"'"); + if ($addparts && (scalar(@{$resource->parts}) > 1)) { + $col .= ""; + } + $col .= ""; + }; + $result.=(< + function updateRadio(form,name,value) { + var radiobutton=form[name]; + for (var i=0; i