--- loncom/interface/lonhelper.pm 2005/10/17 20:21:31 1.125 +++ loncom/interface/lonhelper.pm 2006/04/06 22:30:52 1.136 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # .helper XML handler to implement the LON-CAPA helper # -# $Id: lonhelper.pm,v 1.125 2005/10/17 20:21:31 albertel Exp $ +# $Id: lonhelper.pm,v 1.136 2006/04/06 22:30:52 foxr Exp $ # # Copyright Michigan State University Board of Trustees # @@ -573,22 +573,20 @@ sub display { } # Phase 4: Display. - my $html=&Apache::lonxml::xmlbegin(); my $stateTitle=&mt($state->title()); - my $helperTitle = &mt($self->{TITLE}); - my $bodytag = &Apache::loncommon::bodytag($helperTitle,'',''); + my $browser_searcher_js = + '<script type="text/javascript">'."\n". + &Apache::loncommon::browser_and_searcher_javascript(). + "\n".'</script>'; + + $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 - my $loncapaHelper = &mt("LON-CAPA Helper:"); - $result .= <<HEADER; -$html - <head> - <title>$loncapaHelper: $helperTitle</title> - </head> - $bodytag -HEADER + if (!$state->overrideForm()) { $result.="<form name='helpform' method='POST'>"; } $result .= <<HEADER; <table border="0" width='100%'><tr><td> @@ -651,10 +649,9 @@ HEADER </tr> </table> </form> - </body> -</html> FOOTER + $result .= &Apache::loncommon::end_page(); # Handle writing out the vars to the file my $file = Apache::File->new('>'.$self->{FILENAME}); print $file $self->_varsInFile(); @@ -1072,6 +1069,16 @@ will be the state transistioned to if th the choice is not multichoice. This will override the nextstate passed to the parent C<choices> tag. +<choice> may optionally contain a 'relatedvalue' attribute, which +if present will cause a text entry to appear to the right of the +selection. The value of the relatedvalue attribute is a variable +into which the text entry will be stored e.g.: +<choice computer='numberprovided" relatedvalue="num">Type the number in:</choice> + +<choice> may contain a relatededefault atribute which, if the +relatedvalue attribute is present will be the initial value of the input +box. + =back To create the choices programmatically, either wrap the choices in @@ -1162,10 +1169,12 @@ sub start_choice { my $computer = $token->[2]{'computer'}; my $human = &mt(&Apache::lonxml::get_all_text('/choice', $parser)); - my $nextstate = $token->[2]{'nextstate'}; - my $evalFlag = $token->[2]{'eval'}; + my $nextstate = $token->[2]{'nextstate'}; + my $evalFlag = $token->[2]{'eval'}; + my $relatedVar = $token->[2]{'relatedvalue'}; + my $relatedDefault = $token->[2]{'relateddefault'}; push @{$paramHash->{CHOICES}}, [&mtn($human), $computer, $nextstate, - $evalFlag]; + $evalFlag, $relatedVar, $relatedDefault]; return ''; } @@ -1275,14 +1284,20 @@ BUTTONS } $result .= qq{id="id$id"}; my $choiceLabel = $choice->[0]; - if ($choice->[4]) { # if we need to evaluate this choice + if ($choice->[3]) { # if we need to evaluate this choice $choiceLabel = "sub { my $helper = shift; my $state = shift;" . $choiceLabel . "}"; $choiceLabel = eval($choiceLabel); $choiceLabel = &$choiceLabel($helper, $self); } $result .= "/></td><td> ".qq{<label for="id$id">}. - $choiceLabel. "</label></td></tr>\n"; + $choiceLabel. "</label></td>"; + if ($choice->[4]) { + $result .='<td><input type="text" size="5" name="' + .$choice->[4].'.forminput" value="' + .$choice->[5].'" /></td>'; + } + $result .= "</tr>\n"; } $result .= "</table>\n\n\n"; $result .= $buttons; @@ -1316,6 +1331,10 @@ sub postprocess { $helper->changeState($choice->[2]); } } + if ($choice->[4]) { + my $varname = $choice->[4]; + $helper->{'VARS'}->{$varname} = $env{'form.'."$varname.forminput"}; + } } return 1; } @@ -1557,6 +1576,8 @@ sub render { my $date; my $time=time; + $date = localtime($time); + $date->min(0); my ($anytime,$onclick); if (defined($self->{DEFAULT_VALUE})) { @@ -1564,14 +1585,18 @@ sub render { die('Error in default value code for variable ' . $self->{'variable'} . ', Perl said: ' . $@) if $@; $time = &$valueFunc($helper, $self); - if (lc($time) eq 'anytime') { $time=time; $anytime=1; } + if (lc($time) eq 'anytime') { + $anytime=1; + } else { + $date = localtime($time); + } + } else { + } if ($anytime) { $onclick = "onclick=\"javascript:updateCheck(this.form,'${var}anytime',false)\""; } # Default date: The current hour. - $date = localtime($time); - $date->min(0); if (defined $self->{ERROR_MSG}) { $result .= '<font color="#FF0000">' . $self->{ERROR_MSG} . '</font><br /><br />'; @@ -2296,6 +2321,13 @@ sub render { } } } + function checkexpired() { + for (i=0; i<document.forms.helpform.elements.length; i++) { + if (document.forms.helpform.elements[i].value.indexOf(':Expired') != -1) { + document.forms.helpform.elements[i].checked=true; + } + } + } function uncheckexpired() { for (i=0; i<document.forms.helpform.elements.length; i++) { if (document.forms.helpform.elements[i].value.indexOf(':Expired') != -1) { @@ -2364,13 +2396,28 @@ SCRIPT </table> <br /> BUTTONS + $result .= $buttons; + } if (defined $self->{ERROR_MSG}) { $result .= '<font color="#FF0000">' . $self->{ERROR_MSG} . '</font><br /><br />'; } + my %defaultUsers; + if (defined($self->{DEFAULT_VALUE})) { + my $valueFunc = eval($self->{DEFAULT_VALUE}); + die 'Error in default value code for variable ' . + $self->{'variable'} . ', Perl said: ' . $@ if $@; + my @defaultUsers = &$valueFunc($helper, $self); + if (!$self->{'multichoice'} && @defaultUsers) { # only allowed 1 + @defaultUsers = ($defaultUsers[0]); + } + %defaultUsers = map { if ($_) {($_,1) } } @defaultUsers; + delete($defaultUsers{''}); + } my $choices = []; + my $expired_students = []; # Will hold expired students. # Load up the non-students, if necessary if ($self->{'coursepersonnel'}) { @@ -2408,15 +2455,67 @@ BUTTONS } return $classlist->{$a}->[$fullname] cmp $classlist->{$b}->[$fullname]; } @keys; + # + # now add the fancy section choice... first enumerate the sections: + if ($self->{'multichoice'}) { + my %sections; + for my $key (@keys) { + my $section_name = $classlist->{$key}->[$section]; + if ($section_name ne "") { + $sections{$section_name} = 1; + } + } + # The variable $choice_widget will have the html to make the choice + # selector. + my $size=5; + if (scalar(keys(%sections)) < 5) { + $size=scalar(keys(%sections)); + } + my $choice_widget = '<select multiple name="chosensections" size="'.$size.'">'."\n"; + foreach my $sec (sort {lc($a) cmp lc($b)} (keys(%sections))) { + $choice_widget .= "<option name=\"$sec\">$sec</option>\n"; + } + $choice_widget .= "<option>none</option></select>\n"; + + # Build a table without any borders to contain the section based + # selection: + + my $section_selectors =<<SECTIONSELECT; +<table border="0"> + <tr valign="top"> + <td>For Sections:</td><td>$choice_widget</td> + <td><label><input type="radio" name="personstate" value="Active" checked /> + Current Students</label></td> + <td><label><input type="radio" name="personstate" value="All" /> + All students</label></td> + <td><label><input type="radio" name="personstate" value="Expired" /> + Expired Students</label></td> + </tr> + <tr> + <td><input type="button" value="Select" onclick="checksections(true);" /></td> + <td><input type="button" value="Unselect" onclick="checksections(false);" /></td></tr> +</table> +<br /> +SECTIONSELECT + $result .= $section_selectors; + } # username, fullname, section, type for (@keys) { - # Filter out inactive students if we've set "activeonly" - if (!$self->{'activeonly'} || $classlist->{$_}->[$status] eq + + # We split the active students into the choices array and + # inactive ones into expired_students so that we can put them in 2 separate + # tables. + + if ( $classlist->{$_}->[$status] eq 'Active') { push @$choices, [$_, $classlist->{$_}->[$fullname], $classlist->{$_}->[$section], $classlist->{$_}->[$status], 'Student']; + } else { + push @$expired_students, [$_, $classlist->{$_}->[$fullname], + $classlist->{$_}->[$section], + $classlist->{$_}->[$status], 'Student']; } } @@ -2431,11 +2530,20 @@ BUTTONS "<td align='center'><b>".&mt('Username').":".&mt('Domain')."</b></td></tr>"; my $checked = 0; + # + # Give the active students and staff: + # for my $choice (@$choices) { $result .= "<tr><td><input type='$type' name='" . $self->{'variable'} . '.forminput' . "'"; - if (!$self->{'multichoice'} && !$checked) { + if (%defaultUsers) { + my $user=$choice->[0]; + if (exists($defaultUsers{$user})) { + $result .= " checked='checked' "; + $checked = 1; + } + } elsif (!$self->{'multichoice'} && !$checked) { $result .= " checked='checked' "; $checked = 1; } @@ -2456,53 +2564,59 @@ BUTTONS . HTML::Entities::encode($choice->[0],'<>&"') . "</td></tr>\n"; } - $result .= "</table>\n\n"; - $result .= $buttons; + + # If activeonly is not set then we can also give the expired students: # - # now add the fancy section choice... first enumerate the sections: - if ($self->{'multichoice'}) { - my %sections; - for my $key (@keys) { - my $section_name = $classlist->{$key}->[$section]; - if ($section_name ne "") { - $sections{$section_name} = 1; + if (!$self->{'activeonly'} && ((scalar @$expired_students) > 0)) { + $result .= "<p>Inactive students: </p>\n"; + $result .= <<INACTIVEBUTTONS; + <table> + <tr> + <td><input type="button" value="Select expired" onclick="checkexpired();" /> </td> + <td><input type="button" value="Unselect expired" onclick="uncheckexpired();" /></td> + </tr> + </table> +INACTIVEBUTTONS + $result .= "<table>\n"; + + for my $choice (@$expired_students) { + $result .= "<tr><td><input type='$type' name='" . + $self->{'variable'} . '.forminput' . "'"; + + if (%defaultUsers) { + my $user=$choice->[0]; + if (exists($defaultUsers{$user})) { + $result .= " checked='checked' "; + $checked = 1; } + } elsif (!$self->{'multichoice'} && !$checked) { + $result .= " checked='checked' "; + $checked = 1; + } + $result .= + " value='" . HTML::Entities::encode($choice->[0] . ':' + .$choice->[2] . ':' + .$choice->[1] . ':' + .$choice->[3], "<>&\"'") + . "' /></td><td>" + . HTML::Entities::encode($choice->[1],'<>&"') + . "</td><td align='center'>" + . HTML::Entities::encode($choice->[2],'<>&"') + . "</td>\n<td>" + . HTML::Entities::encode($choice->[3],'<>&"') + . "</td>\n<td>" + . HTML::Entities::encode($choice->[4],'<>&"') + . "</td>\n<td>" + . HTML::Entities::encode($choice->[0],'<>&"') + . "</td></tr>\n"; } - # The variable $choice_widget will have the html to make the choice - # selector. - my $size=5; - if (scalar(keys(%sections)) < 5) { - $size=scalar(keys(%sections)); - } - my $choice_widget = '<select multiple name="chosensections" size="'.$size.'">'."\n"; - foreach my $sec (sort {lc($a) cmp lc($b)} (keys(%sections))) { - $choice_widget .= "<option name=\"$sec\">$sec</option>\n"; - } - $choice_widget .= "<option>none</option></select>\n"; + $result .= "</table>\n"; + + } + - # Build a table without any borders to contain the section based - # selection: - my $section_selectors =<<SECTIONSELECT; -<table border="0"> - <tr valign="top"> - <td>For Sections:</td><td>$choice_widget</td> - <td><label><input type="radio" name="personstate" value="Active" checked /> - Current Students</label></td> - <td><label><input type="radio" name="personstate" value="All" /> - All students</label></td> - <td><label><input type="radio" name="personstate" value="Expired" /> - Expired Students</label></td> - </tr> - <tr> - <td><input type="button" value="Select" onclick="checksections(true);" /></td> - <td><input type="button" value="Unselect" onclick="checksections(false);" /></td></tr> -</table> -<br /> -SECTIONSELECT - $result .= $section_selectors; - } return $result; } @@ -2897,8 +3011,12 @@ package Apache::lonhelper::section; <section> allows the user to choose one or more sections from the current course. -It takes the standard attributes "variable", "multichoice", and -"nextstate", meaning what they do for most other elements. +It takes the standard attributes "variable", "multichoice", +"allowempty" and "nextstate", meaning what they do for most other +elements. + +also takes a boolean 'onlysections' whcih will restrict this to only +have sections and not include groups =cut @@ -2928,6 +3046,7 @@ sub start_section { $paramHash->{'variable'} = $token->[2]{'variable'}; $helper->declareVar($paramHash->{'variable'}); $paramHash->{'multichoice'} = $token->[2]{'multichoice'}; + $paramHash->{'allowempty'} = $token->[2]{'allowempty'}; if (defined($token->[2]{'nextstate'})) { $paramHash->{NEXTSTATE} = $token->[2]{'nextstate'}; } @@ -2947,8 +3066,16 @@ sub start_section { } for my $sectionName (sort(keys(%choices))) { - - push @{$paramHash->{CHOICES}}, [$sectionName, $sectionName]; + push @{$paramHash->{CHOICES}}, [$sectionName, $sectionName]; + } + return if ($token->[2]{'onlysections'}); + + # add in groups to the end of the list + my %curr_groups; + if (&Apache::loncommon::coursegroups(\%curr_groups)) { + foreach my $group_name (sort(keys(%curr_groups))) { + push(@{$paramHash->{CHOICES}}, [$group_name, $group_name]); + } } } @@ -2962,6 +3089,72 @@ sub end_section { } 1; +package Apache::lonhelper::group; + +=pod + +=head2 Element: groupX<group, helper element> + +<group> allows the user to choose one or more groups from the current course. + +It takes the standard attributes "variable", "multichoice", + "allowempty" and "nextstate", meaning what they do for most other + elements. + +=cut + +no strict; +@ISA = ("Apache::lonhelper::choices"); +use strict; + +BEGIN { + &Apache::lonhelper::register('Apache::lonhelper::group', + ('group')); +} + +sub new { + my $ref = Apache::lonhelper::choices->new(); + bless($ref); +} + +sub start_group { + my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; + + if ($target ne 'helper') { + return ''; + } + + $paramHash->{CHOICES} = []; + + $paramHash->{'variable'} = $token->[2]{'variable'}; + $helper->declareVar($paramHash->{'variable'}); + $paramHash->{'multichoice'} = $token->[2]{'multichoice'}; + $paramHash->{'allowempty'} = $token->[2]{'allowempty'}; + if (defined($token->[2]{'nextstate'})) { + $paramHash->{NEXTSTATE} = $token->[2]{'nextstate'}; + } + + # Populate the CHOICES element + my %choices; + + my %curr_groups; + if (&Apache::loncommon::coursegroups(\%curr_groups)) { + foreach my $group_name (sort(keys(%curr_groups))) { + push(@{$paramHash->{CHOICES}}, [$group_name, $group_name]); + } + } +} + +sub end_group { + my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; + + if ($target ne 'helper') { + return ''; + } + Apache::lonhelper::group->new(); +} +1; + package Apache::lonhelper::string; =pod @@ -3343,8 +3536,11 @@ sub render { } my $previous = HTML::Entities::encode(&mt("<- Previous"), '<>&"'); my $next = HTML::Entities::encode(&mt("Next ->"), '<>&"'); + my $target = " target='loncapaclient'"; + if (($env{'browser.interface'} eq 'textual') || + ($env{'environment.remote'} eq 'off')) { $target=''; } $result .= "<center>\n" . - "<form action='".$actionURL."' method='post' target='loncapaclient'>\n" . + "<form action='".$actionURL."' method='post' $target>\n" . "<input type='button' onclick='history.go(-1)' value='$previous' />" . "<input type='hidden' name='orgurl' value='$targetURL' />" . "<input type='hidden' name='selectrole' value='1' />\n" . @@ -3545,6 +3741,11 @@ sub render { $result .= '<li>'.&mt('for section [_1]',"<b>$section</b>").'</li>'; $result .= "<input type='hidden' name='csec' value='" . HTML::Entities::encode($section,"'<>&\"") . "' />\n"; + } elsif ($vars->{TARGETS} eq 'group') { + my $group = $vars->{GROUP_NAME}; + $result .= '<li>'.&mt('for group [_1]',"<b>$group</b>").'</li>'; + $result .= "<input type='hidden' name='cgroup' value='" . + HTML::Entities::encode($group,"'<>&\"") . "' />\n"; } else { # FIXME: This is probably wasteful! Store the name! my $classlist = Apache::loncoursedata::get_classlist();