--- loncom/interface/lonhelper.pm 2005/11/22 12:49:50 1.128 +++ loncom/interface/lonhelper.pm 2008/09/06 00:47:16 1.165 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # .helper XML handler to implement the LON-CAPA helper # -# $Id: lonhelper.pm,v 1.128 2005/11/22 12:49:50 raeburn Exp $ +# $Id: lonhelper.pm,v 1.165 2008/09/06 00:47:16 raeburn 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: <helper title="Example Helper"> <state name="START" title="Demonstrating the Example Helper"> - <!-- notice this is the START state the wizard requires --> + <!-- notice this is the START state the helper requires --> </state> <state name="GET_NAME" title="Enter Student Name"> </state> </helper> -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. @@ -183,6 +186,9 @@ use Apache::File; use Apache::lonxml; 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 # push and pop them @@ -331,7 +337,8 @@ sub start_state { } Apache::lonhelper::state->new($token->[2]{'name'}, - $token->[2]{'title'}); + $token->[2]{'title'}, + $token->[2]{'help'}); return ''; } @@ -361,6 +368,7 @@ use Apache::loncommon; use Apache::File; use Apache::lonlocal; use Apache::lonnet; +use LONCAPA; sub new { my $proto = shift; @@ -466,9 +474,8 @@ sub _saveVars { sub _varsInFile { my $self = shift; my @vars = (); - for my $key (keys %{$self->{VARS}}) { - push @vars, &Apache::lonnet::escape($key) . '=' . - &Apache::lonnet::escape($self->{VARS}->{$key}); + for my $key (keys(%{$self->{VARS}})) { + push(@vars, &escape($key) . '=' . &escape($self->{VARS}->{$key})); } return join ('&', @vars); } @@ -483,7 +490,7 @@ sub declareVar { $self->{VARS}->{$var} = ''; } - my $envname = 'form.' . $var . '.forminput'; + my $envname = 'form.' . $var . '_forminput'; if (defined($env{$envname})) { if (ref($env{$envname})) { $self->{VARS}->{$var} = join('|||', @{$env{$envname}}); @@ -573,26 +580,28 @@ 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 $stateHelp= $state->help(); + 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'>"; } + if ($stateHelp) { + $stateHelp = &Apache::loncommon::help_open_topic($stateHelp); + } $result .= <<HEADER; <table border="0" width='100%'><tr><td> - <h2><i>$stateTitle</i></h2> + <h2><i>$stateTitle</i>$stateHelp</h2> HEADER $result .= "<table cellpadding='10' width='100%'><tr><td rowspan='2' valign='top'>"; @@ -651,10 +660,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(); @@ -680,6 +688,7 @@ sub new { $self->{NAME} = shift; $self->{TITLE} = shift; + $self->{HELP} = shift; $self->{ELEMENTS} = []; bless($self, $class); @@ -701,6 +710,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 +806,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 <choices> 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 +1008,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 +1026,16 @@ sub end_message { sub render { my $self = shift; - - return &mtn($self->{MESSAGE_TEXT}); + + if ($self->{TYPE} =~ /^\s*warning\s*$/i) { + $self->{MESSAGE_TEXT} = + '<span class="LC_warning">'. $self->{MESSAGE_TEXT}.'</span>'; + } + if ($self->{TYPE} =~ /^\s*error\s*$/i) { + $self->{MESSAGE_TEXT} = + '<span class="LC_error">'. $self->{MESSAGE_TEXT}.'</span>'; + } + return $self->{MESSAGE_TEXT}; } # If a NEXTSTATE was given, switch to it sub postprocess { @@ -1023,6 +1048,175 @@ sub postprocess { } 1; +package Apache::lonhelper::helpicon; + +=pod + +=head1 Elements + +=head2 Element: helpiconX<helpicon, helper element> + +Helpicon elements add a help icon at the current location. +Example: + + <helpicon file="Help"> + General Help + </helpicon> + +In this example will generate a help icon to the Help.hlp url with a +description of 'General Help'. The description is not required and if +left out (Example: <helpicon file="Help" /> only the icon will be +added.) + +=head3 Localization + +The description text will be run through the normalize_string function +and that will be used as a call to &mt. + +=cut + +no strict; +@ISA = ("Apache::lonhelper::element"); +use strict; +use Apache::lonlocal; + +BEGIN { + &Apache::lonhelper::register('Apache::lonhelper::helpicon', + ('helpicon')); +} + +sub new { + my $ref = Apache::lonhelper::element->new(); + bless($ref); +} + +# CONSTRUCTION: Construct the message element from the XML +sub start_helpicon { + my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; + + if ($target ne 'helper') { + return ''; + } + + $paramHash->{HELP_TEXT} = &mtn(&Apache::lonxml::get_all_text('/helpicon', + $parser)); + + $paramHash->{HELP_TEXT} =~s/^\s+//; + $paramHash->{HELP_TEXT} =~s/\s+$//; + + if (defined($token->[2]{'file'})) { + $paramHash->{HELP_FILE} = $token->[2]{'file'}; + } + return ''; +} + +sub end_helpicon { + my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; + + if ($target ne 'helper') { + return ''; + } + Apache::lonhelper::helpicon->new(); + return ''; +} + +sub render { + my $self = shift; + + my $text; + if ( $self->{HELP_TEXT} ne '') { + $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; + +=pod + +=head1 Elements + +=head2 Element: skipX<skip> + +The <skip> tag allows you define conditions under which the current state +should be skipped over and define what state to skip to. + + <state name="SKIP"> + <skip> + <clause> + #some code that decides whether to skip the state or not + </clause> + <nextstate>FINISH</nextstate> + </skip> + <message nextstate="FINISH">A possibly skipped state</message> + </state> + +=cut + +no strict; +@ISA = ("Apache::lonhelper::element"); +use strict; + +BEGIN { + &Apache::lonhelper::register('Apache::lonhelper::skip', + ('skip')); +} + +sub new { + my $ref = Apache::lonhelper::element->new(); + bless($ref); +} + +sub start_skip { + my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; + + if ($target ne 'helper') { + return ''; + } + # let <cluase> know what text to skip to + $paramHash->{SKIPTAG}='/skip'; + return ''; +} + +sub end_skip { + my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; + + if ($target ne 'helper') { + return ''; + } + Apache::lonhelper::skip->new(); + return ''; +} + +sub render { + my $self = shift; + return ''; +} +# If a NEXTSTATE is set, switch to it +sub preprocess { + my ($self) = @_; + + if (defined($self->{NEXTSTATE})) { + $helper->changeState($self->{NEXTSTATE}); + } + + return 1; +} + +1; + package Apache::lonhelper::choices; =pod @@ -1072,6 +1266,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 +1366,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 ''; } @@ -1193,7 +1399,7 @@ sub render { function checkall(value, checkName) { for (i=0; i<document.forms.helpform.elements.length; i++) { ele = document.forms.helpform.elements[i]; - if (ele.name == checkName + '.forminput') { + if (ele.name == checkName + '_forminput') { document.forms.helpform.elements[i].checked=value; } } @@ -1266,7 +1472,7 @@ BUTTONS foreach my $choice (@{$self->{CHOICES}}) { my $id = &new_id(); $result .= "<tr>\n<td width='20'> </td>\n"; - $result .= "<td valign='top'><input type='$type' name='$var.forminput'" + $result .= "<td valign='top'><input type='$type' name='${var}_forminput'" . " value='" . HTML::Entities::encode($choice->[1],"<>&\"'") . "'"; @@ -1275,14 +1481,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; @@ -1294,7 +1506,7 @@ 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} = @@ -1316,6 +1528,10 @@ sub postprocess { $helper->changeState($choice->[2]); } } + if ($choice->[4]) { + my $varname = $choice->[4]; + $helper->{'VARS'}->{$varname} = $env{'form.'."${varname}_forminput"}; + } } return 1; } @@ -1423,7 +1639,7 @@ sub render { $checkedChoices{$self->{CHOICES}->[0]->[1]} = 1; } - $result .= "<select name='${var}.forminput'>\n"; + $result .= "<select name='${var}_forminput'>\n"; foreach my $choice (@{$self->{CHOICES}}) { $result .= "<option value='" . HTML::Entities::encode($choice->[1],"<>&\"'") @@ -1449,7 +1665,7 @@ sub render { # 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" . @@ -1559,19 +1775,39 @@ sub render { my $time=time; my ($anytime,$onclick); - if (defined($self->{DEFAULT_VALUE})) { + + # 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}); + } elsif (defined($self->{DEFAULT_VALUE})) { my $valueFunc = eval($self->{DEFAULT_VALUE}); 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; + $date = localtime(time); + $date->min(0); + } elsif (defined($time) && $time ne 0) { + $date = localtime($time); + } else { + # leave date undefined so it'll default to now + } + } + + if (!defined($date)) { + $date = localtime(time); + $date->min(0); } + 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 />'; @@ -1675,7 +1911,7 @@ CHECK if ($anytime) { $result.=' checked="checked" ' } - $result.="name='${var}anytime'/>".&mt('Anytime').'</label>' + $result.="name='${var}anytime'/>".&mt('Any time').'</label>' } return $result; @@ -1722,6 +1958,16 @@ sub postprocess { $helper->{VARS}->{$var} = $chosenDate; } + if (defined($self->{VALIDATOR})) { + my $validator = eval($self->{VALIDATOR}); + die 'Died during evaluation of validator 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}); } @@ -1754,7 +2000,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 @@ -1827,6 +2074,7 @@ sub start_resource { $helper->declareVar($paramHash->{'variable'}.'_part'); } $paramHash->{'closeallpages'} = $token->[2]{'closeallpages'}; + $paramHash->{'include_top_level_map'} = $token->[2]{'includecourse'}; return ''; } @@ -1972,7 +2220,7 @@ sub render { function checkall(value, checkName) { for (i=0; i<document.forms.helpform.elements.length; i++) { ele = document.forms.helpform.elements[i]; - if (ele.name == checkName + '.forminput') { + if (ele.name == checkName + '_forminput') { document.forms.helpform.elements[i].checked=value; } } @@ -2071,12 +2319,12 @@ BUTTONS } $col .= "<td align='center'><input type='checkbox' name ='$option_var". - ".forminput' value='". + "_forminput' value='". $resource_name . "' $checked /> </td>"; } } - $col .= "<td align='center'><input type='$inputType' name='${var}.forminput' "; + $col .= "<td align='center'><input type='$inputType' name='${var}_forminput' "; if (%defaultSymbs) { my $symb=$resource->symb(); if (exists($defaultSymbs{$symb})) { @@ -2105,7 +2353,7 @@ BUTTONS my $resource_name = &HTML::Entities::encode(&$valueFunc($resource),"<>&\"'"); if ($addparts && (scalar(@{$resource->parts}) > 1)) { - $col .= "<select onclick=\"javascript:updateRadio(this.form,'${var}.forminput','$resource_name');updateHidden(this.form,'$id','${var}');\" name='part_$id.forminput'>\n"; + $col .= "<select onclick=\"javascript:updateRadio(this.form,'${var}_forminput','$resource_name');updateHidden(this.form,'$id','${var}');\" name='part_${id}_forminput'>\n"; $col .= "<option value=\"$part\">All Parts</option>\n"; foreach my $part (@{$resource->parts}) { $col .= "<option value=\"$part\">Part: $part</option>\n"; @@ -2127,14 +2375,14 @@ BUTTONS } } function updateHidden(form,id,name) { - var select=form['part_'+id+'.forminput']; - var hidden=form[name+'_part.forminput']; + var select=form['part_'+id+'_forminput']; + var hidden=form[name+'_part_forminput']; var which=select.selectedIndex; hidden.value=select.options[which].value; } // --> </script> -<input type="hidden" name="${var}_part.forminput" /> +<input type="hidden" name="${var}_part_forminput" /> RADIO $env{'form.condition'} = !$self->{'toponly'}; @@ -2152,6 +2400,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 } ); @@ -2267,104 +2516,6 @@ sub render { my $buttons = ''; my $var = $self->{'variable'}; - if ($self->{'multichoice'}) { - $result = <<SCRIPT; -<script type="text/javascript"> -// <!-- - function checkall(value, checkName) { - for (i=0; i<document.forms.helpform.elements.length; i++) { - ele = document.forms.helpform.elements[i]; - if (ele.name == checkName + '.forminput') { - document.forms.helpform.elements[i].checked=value; - } - } - } - function checksec(value) { - for (i=0; i<document.forms.helpform.elements.length; i++) { - comp = document.forms.helpform.elements.chksec.value; - if (document.forms.helpform.elements[i].value.indexOf(':'+comp+':') != -1) { - if (document.forms.helpform.elements[i].value.indexOf(':Active') != -1) { - document.forms.helpform.elements[i].checked=value; - } - } - } - } - function checkactive() { - for (i=0; i<document.forms.helpform.elements.length; i++) { - if (document.forms.helpform.elements[i].value.indexOf(':Active') != -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) { - document.forms.helpform.elements[i].checked=false; - } - } - } - function getDesiredState() { // Return desired person state radio value. - numRadio = document.forms.helpform.personstate.length; - for (i =0; i < numRadio; i++) { - if (document.forms.helpform.personstate[i].checked) { - return document.forms.helpform.personstate[i].value; - } - } - return ""; - } - - function checksections(value) { // Check selected sections. - numSections = document.forms.helpform.chosensections.length; - desiredState = getDesiredState(); - - for (var option = 0; option < numSections; option++) { - if(document.forms.helpform.chosensections.options[option].selected) { - section = document.forms.helpform.chosensections.options[option].text; - if (section == "none") { - section =""; - } - for (i = 0; i < document.forms.helpform.elements.length; i++ ) { - if (document.forms.helpform.elements[i].value.indexOf(':') != -1) { - info = document.forms.helpform.elements[i].value.split(':'); - hisSection = info[2]; - hisState = info[4]; - if (desiredState == hisState || - desiredState == "All") { - if(hisSection == section || - section =="" ) { - document.forms.helpform.elements[i].checked = value; - } - } - } - } - } - } - } -// --> -</script> -SCRIPT - - my %lt=&Apache::lonlocal::texthash( - 'ocs' => "Select Only Current Students", - 'ues' => "Unselect Expired Students", - 'sas' => "Select All Students", - 'uas' => "Unselect All Students", - 'sfsg' => "Select Current Students for Section/Group", - 'ufsg' => "Unselect for Section/Group"); - - $buttons = <<BUTTONS; -<br /> -<table> - - <tr> - <td><input type="button" onclick="checkall(true, '$var')" value="$lt{'sas'}" /></td> - <td> <input type="button" onclick="checkall(false, '$var')" value="$lt{'uas'}" /><br /></td> - </tr> - -</table> -<br /> -BUTTONS - } if (defined $self->{ERROR_MSG}) { $result .= '<font color="#FF0000">' . $self->{ERROR_MSG} . '</font><br /><br />'; @@ -2382,152 +2533,73 @@ BUTTONS %defaultUsers = map { if ($_) {($_,1) } } @defaultUsers; delete($defaultUsers{''}); } - my $choices = []; + + + my ($course_personnel, + $current_members, + $expired_members, + $future_members) = + &Apache::lonselstudent::get_people_in_class($env{'request.course.sec'}); + + # Load up the non-students, if necessary + if ($self->{'coursepersonnel'}) { - my %coursepersonnel = Apache::lonnet::get_course_adv_roles(); - for (sort keys %coursepersonnel) { - for my $role (split /,/, $coursepersonnel{$_}) { - # extract the names so we can sort them - my @people; - - for (split /,/, $role) { - push @people, [split /:/, $role]; - } - - @people = sort { $a->[0] cmp $b->[0] } @people; - - for my $person (@people) { - push @$choices, [join(':', @$person), $person->[0], '', $_]; - } - } - } + unshift @$current_members, (@$course_personnel); } - # Constants - my $section = Apache::loncoursedata::CL_SECTION(); - my $fullname = Apache::loncoursedata::CL_FULLNAME(); - my $status = Apache::loncoursedata::CL_STATUS(); - # Load up the students - my $classlist = &Apache::loncoursedata::get_classlist(); - my @keys = keys %{$classlist}; - # Sort by: Section, name - @keys = sort { - if ($classlist->{$a}->[$section] ne $classlist->{$b}->[$section]) { - return $classlist->{$a}->[$section] cmp $classlist->{$b}->[$section]; - } - return $classlist->{$a}->[$fullname] cmp $classlist->{$b}->[$fullname]; - } @keys; - - # username, fullname, section, type - for (@keys) { - # Filter out inactive students if we've set "activeonly" - if (!$self->{'activeonly'} || $classlist->{$_}->[$status] eq - 'Active') { - push @$choices, [$_, $classlist->{$_}->[$fullname], - $classlist->{$_}->[$section], - $classlist->{$_}->[$status], 'Student']; - } - } + # Current personel - my $name = $self->{'coursepersonnel'} ? &mt('Name') : &mt('Student Name'); - my $type = 'radio'; - if ($self->{'multichoice'}) { $type = 'checkbox'; } - $result .= "<table cellspacing='2' cellpadding='2' border='0'>\n"; - $result .= "<tr><td></td><td align='center'><b>$name</b></td>". - "<td align='center'><b>" . &mt('Section') . "</b></td>" . - "<td align='center'><b>".&mt('Status')."</b></td>" . - "<td align='center'><b>" . &mt("Role") . "</b></td>" . - "<td align='center'><b>".&mt('Username').":".&mt('Domain')."</b></td></tr>"; + $result .= '<h4>'.&mt('Select Currently Enrolled Students and Active Course Personnel').'</h4>'; + $result .= &Apache::lonselstudent::render_student_list( $current_members, + "helpform", + "current", + \%defaultUsers, + $self->{'multichoice'}, + $self->{'variable'}, + 1); - my $checked = 0; - for my $choice (@$choices) { - $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"; - } - $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; - } - } - # 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; + if (!$self->{'activeonly'} && ((scalar(@$future_members)) > 0)) { + + # And future. + + $result .= '<h4>'.&mt('Select Future Enrolled Students and Future Course Personnel').'</h4>'; + + $result .= &Apache::lonselstudent::render_student_list( $future_members, + "helpform", + "future", + \%defaultUsers, + $self->{'multichoice'}, + $self->{'variable'}, + 0); + } + if (!$self->{'activeonly'} && ((scalar(@$expired_members)) > 0)) { + # Past + + $result .= '<h4>'.&mt('Select Previously Enrolled Students and Inactive Course Personnel').'</h4>'; + $result .= &Apache::lonselstudent::render_student_list($expired_members, + "helpform", + "past", + \%defaultUsers, + $self->{'multichoice'}, + $self->{'variable'}, + 0); } + + + return $result; } sub postprocess { my $self = shift; - my $result = $env{'form.' . $self->{'variable'} . '.forminput'}; + my $result = $env{'form.' . $self->{'variable'} . '_forminput'}; if (!$result && !$self->{'emptyallowed'}) { if ($self->{'coursepersonnel'}) { $self->{ERROR_MSG} = @@ -2712,7 +2784,7 @@ sub render { function checkall(value, checkName) { for (i=0; i<document.forms.helpform.elements.length; i++) { ele = document.forms.helpform.elements[i]; - if (ele.name == checkName + '.forminput') { + if (ele.name == checkName + '_forminput') { document.forms.helpform.elements[i].checked=value; } } @@ -2760,17 +2832,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 @@ -2829,7 +2901,7 @@ BUTTONS my $id = &new_id(); $result .= '<tr><td align="right"' . " bgcolor='$color'>" . "<input $onclick type='$type' name='" . $var - . ".forminput' ".qq{id="$id"}." value='" . HTML::Entities::encode($fileName,"<>&\"'"). + . "_forminput' ".qq{id="$id"}." value='" . HTML::Entities::encode($fileName,"<>&\"'"). "'"; if (!$self->{'multichoice'} && $choices == 0) { $result .= ' checked="checked"'; @@ -2890,7 +2962,7 @@ sub fileState { sub postprocess { my $self = shift; - my $result = $env{'form.' . $self->{'variable'} . '.forminput'}; + my $result = $env{'form.' . $self->{'variable'} . '_forminput'}; if (!$result) { $self->{ERROR_MSG} = 'You must choose at least one file '. 'to continue.'; @@ -2915,8 +2987,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 @@ -2946,6 +3022,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'}; } @@ -2955,18 +3032,29 @@ sub start_section { my $section = Apache::loncoursedata::CL_SECTION(); my $classlist = Apache::loncoursedata::get_classlist(); - foreach (keys %$classlist) { - my $sectionName = $classlist->{$_}->[$section]; - if (!$sectionName) { + foreach my $user (keys(%$classlist)) { + my $section_name = $classlist->{$user}[$section]; + if (!$section_name) { $choices{"No section assigned"} = ""; } else { - $choices{$sectionName} = $sectionName; + $choices{$section_name} = $section_name; } } - for my $sectionName (sort(keys(%choices))) { - - push @{$paramHash->{CHOICES}}, [$sectionName, $sectionName]; + if (exists($choices{"No section assigned"})) { + push(@{$paramHash->{CHOICES}}, + ['No section assigned','No section assigned']); + delete($choices{"No section assigned"}); + } + for my $section_name (sort {lc($a) cmp lc($b) } (keys(%choices))) { + push @{$paramHash->{CHOICES}}, [$section_name, $section_name]; + } + return if ($token->[2]{'onlysections'}); + + # add in groups to the end of the list + my %curr_groups = &Apache::longroup::coursegroups(); + foreach my $group_name (sort(keys(%curr_groups))) { + push(@{$paramHash->{CHOICES}}, [$group_name, $group_name]); } } @@ -2986,10 +3074,12 @@ package Apache::lonhelper::group; =head2 Element: groupX<group, helper element> -<section> allows the user to choose one or more groups from the current course. +<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. -It takes the standard attributes "variable", "multichoice", and "nextstate", meaning what they do for most other elements. - =cut no strict; @@ -3018,6 +3108,7 @@ sub start_group { $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'}; } @@ -3025,18 +3116,12 @@ sub start_group { # Populate the CHOICES element my %choices; - my $numgroups; - my %curr_groups; - if (&Apache::loncommon::coursegroups(\%curr_groups)) { - foreach my $group_name (keys %curr_groups) { - $choices{$group_name} = $group_name; - } - } - foreach my $group_name (sort(keys(%choices))) { - push @{$paramHash->{CHOICES}}, [$group_name, $group_name]; + my %curr_groups = &Apache::longroup::coursegroups(); + foreach my $group_name (sort {lc($a) cmp lc($b)} (keys(%curr_groups))) { + push(@{$paramHash->{CHOICES}}, [$group_name, $group_name]); } } - + sub end_group { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; @@ -3113,7 +3198,7 @@ sub render { $result .= '<p><font color="#FF0000">' . $self->{ERROR_MSG} . '</font></p>'; } - $result .= '<input type="string" name="' . $self->{'variable'} . '.forminput"'; + $result .= '<input type="string" name="' . $self->{'variable'} . '_forminput"'; if (defined($self->{'size'})) { $result .= ' size="' . $self->{'size'} . '"'; @@ -3140,7 +3225,7 @@ sub postprocess { if (defined($self->{VALIDATOR})) { my $validator = eval($self->{VALIDATOR}); - die 'Died during evaluation of evaulation code; Perl said: ' . $@ if $@; + die 'Died during evaluation of validator code; Perl said: ' . $@ if $@; my $invalid = &$validator($helper, $state, $self, $self->getValue()); if ($invalid) { $self->{ERROR_MSG} = $invalid; @@ -3246,7 +3331,8 @@ sub start_clause { die 'Error in clause of condition, Perl said: ' . $@ if $@; if (!&$clause($helper, $paramHash)) { # Discard all text until the /condition. - &Apache::lonxml::get_all_text('/condition', $parser); + my $end_tag = $paramHash->{SKIPTAG} || '/condition'; + &Apache::lonxml::get_all_text($end_tag, $parser); } } @@ -3451,7 +3537,7 @@ sub overrideForm { package Apache::lonhelper::parmwizfinal; -# This is the final state for the parmwizard. It is not generally useful, +# This is the final state for the parm helper. It is not generally useful, # so it is not perldoc'ed. It does its own processing. # It is represented with <parmwizfinal />, and # should later be moved to lonparmset.pm . @@ -3519,8 +3605,10 @@ sub render { if ($vars->{GRANULARITY} eq 'whole_course') { $resourceString .= '<li>'.&mt('for <b>all resources in the course</b>').'</li>'; if ($vars->{TARGETS} eq 'course') { - $level = 11; # general course, see lonparmset.pm perldoc + $level = 14; # general course, see lonparmset.pm perldoc } elsif ($vars->{TARGETS} eq 'section') { + $level = 9; + } elsif ($vars->{TARGETS} eq 'group') { $level = 6; } else { $level = 3; @@ -3535,8 +3623,10 @@ sub render { $symb = $res->symb(); $resourceString .= '<li>'.&mt('for the map named [_1]',"<b>$title</b>").'</li>'; if ($vars->{TARGETS} eq 'course') { - $level = 10; # general course, see lonparmset.pm perldoc + $level = 13; # general course, see lonparmset.pm perldoc } elsif ($vars->{TARGETS} eq 'section') { + $level = 8; + } elsif ($vars->{TARGETS} eq 'group') { $level = 5; } else { $level = 2; @@ -3552,8 +3642,10 @@ sub render { my $title = $res->compTitle(); $resourceString .= '<li>'.&mt('for the resource named [_1] part [_2]',"<b>$title</b>","<b>$part</b>").'</li>'; if ($vars->{TARGETS} eq 'course') { - $level = 7; # general course, see lonparmset.pm perldoc + $level = 10; # general course, see lonparmset.pm perldoc } elsif ($vars->{TARGETS} eq 'section') { + $level = 7; + } elsif ($vars->{TARGETS} eq 'group') { $level = 4; } else { $level = 1;