--- loncom/interface/lonhelper.pm 2003/09/16 20:00:57 1.46 +++ loncom/interface/lonhelper.pm 2003/10/09 19:07:33 1.59 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # .helper XML handler to implement the LON-CAPA helper # -# $Id: lonhelper.pm,v 1.46 2003/09/16 20:00:57 bowersj2 Exp $ +# $Id: lonhelper.pm,v 1.59 2003/10/09 19:07:33 bowersj2 Exp $ # # Copyright Michigan State University Board of Trustees # @@ -172,12 +172,20 @@ before parsing XML fragments and B<Apach when you are done. See lonprintout.pm for examples of this usage in the printHelper subroutine. +=head2 Localization + +The helper framework tries to handle as much localization as +possible. The text is always run through +Apache::lonlocal::normalize_string, so be sure to run the keys through +that function for maximum usefulness and robustness. + =cut package Apache::lonhelper; use Apache::Constants qw(:common); use Apache::File; use Apache::lonxml; +use Apache::lonlocal; # Register all the tags with the helper, so the helper can # push and pop them @@ -257,9 +265,9 @@ sub real_handler { # Send header, don't cache this page if ($r->header_only) { if ($ENV{'browser.mathml'}) { - $r->content_type('text/xml'); + $r->content_type('text/xml; charset=UTF-8'); } else { - $r->content_type('text/html'); + $r->content_type('text/html; charset=UTF-8'); } $r->send_http_header; return OK; @@ -359,9 +367,10 @@ sub end_state { package Apache::lonhelper::helper; use Digest::MD5 qw(md5_hex); -use HTML::Entities; +use HTML::Entities(); use Apache::loncommon; use Apache::File; +use Apache::lonlocal; sub new { my $proto = shift; @@ -523,7 +532,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 "Next ->") { + if ($self->{STATE} ne "START" || $ENV{"form.SUBMIT"} eq &mt("Next ->")) { my $prevState = $self->{STATES}{$self->{STATE}}; $prevState->postprocess(); } @@ -574,13 +583,19 @@ sub display { } # Phase 4: Display. - my $stateTitle = $state->title(); - my $bodytag = &Apache::loncommon::bodytag("$self->{TITLE}",'',''); + my $stateTitle = &mt($state->title()); + my $helperTitle = &mt($self->{TITLE}); + my $bodytag = &Apache::loncommon::bodytag($helperTitle,'',''); + 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>LON-CAPA Helper: $self->{TITLE}</title> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <title>$loncapaHelper: $helperTitle</title> </head> $bodytag HEADER @@ -607,12 +622,12 @@ HEADER } if ($self->{DONE}) { my $returnPage = $self->{RETURN_PAGE}; - $result .= "<a href=\"$returnPage\">End Helper</a>"; + $result .= "<a href=\"$returnPage\">" . &mt("End Helper") . "</a>"; } else { $result .= '<nobr><input name="back" type="button" '; - $result .= 'value="<- Previous" onclick="history.go(-1)" /> '; - $result .= '<input name="SUBMIT" type="submit" value="Next ->" /></nobr>'; + $result .= 'value="' . $previous . '" onclick="history.go(-1)" /> '; + $result .= '<input name="SUBMIT" type="submit" value="' . $next . '" /></nobr>'; } } @@ -626,12 +641,12 @@ HEADER } if ($self->{DONE}) { my $returnPage = $self->{RETURN_PAGE}; - $result .= "<a href=\"$returnPage\">End Helper</a>"; + $result .= "<a href=\"$returnPage\">" . &mt('End Helper') . "</a>"; } else { $result .= '<nobr><input name="back" type="button" '; - $result .= 'value="<- Previous" onclick="history.go(-1)" /> '; - $result .= '<input name="SUBMIT" type="submit" value="Next ->" /></nobr>'; + $result .= 'value="' . $previous . '" onclick="history.go(-1)" /> '; + $result .= '<input name="SUBMIT" type="submit" value="' . $next . '" /></nobr>'; } } @@ -883,6 +898,7 @@ sub start_defaultvalue { sub end_defaultvalue { return ''; } +# Validators may need to take language specifications sub start_validator { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; @@ -926,7 +942,9 @@ package Apache::lonhelper::message; =pod -=head1 Element: messageX<message, helper element> +=head1 Elements + +=head2 Element: messageX<message, helper element> Message elements display their contents, and transition directly to the state in the <nextstate> attribute. Example: @@ -950,11 +968,17 @@ within each other.) This is also a good template for creating your own new states, as it has very little code beyond the state template. +=head3 Localization + +The contents of the message tag 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::message', @@ -996,7 +1020,7 @@ sub end_message { sub render { my $self = shift; - return $self->{MESSAGE_TEXT}; + return &mtn($self->{MESSAGE_TEXT}); } # If a NEXTSTATE was given, switch to it sub postprocess { @@ -1098,6 +1122,7 @@ tag is stored in the {VARS} hash. no strict; @ISA = ("Apache::lonhelper::element"); use strict; +use Apache::lonlocal; BEGIN { &Apache::lonhelper::register('Apache::lonhelper::choices', @@ -1251,7 +1276,7 @@ BUTTONS $choiceLabel = eval($choiceLabel); $choiceLabel = &$choiceLabel($helper, $self); } - $result .= "/></td><td> " . $choiceLabel . "</td></tr>\n"; + $result .= "/></td><td> " . &mtn($choiceLabel) . "</td></tr>\n"; } $result .= "</table>\n\n\n"; $result .= $buttons; @@ -1266,8 +1291,8 @@ sub postprocess { 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; } @@ -1310,9 +1335,13 @@ the result is stored in. =cut +# This really ought to be a sibling class to "choice" which is itself +# a child of some abstract class.... *shrug* + no strict; @ISA = ("Apache::lonhelper::element"); use strict; +use Apache::lonlocal; BEGIN { &Apache::lonhelper::register('Apache::lonhelper::dropdown', @@ -1402,7 +1431,7 @@ sub render { $choiceLabel = eval($choiceLabel); $choiceLabel = &$choiceLabel($helper, $self); } - $result .= ">" . $choiceLabel . "\n"; + $result .= ">" . &mtn($choiceLabel) . "\n"; } $result .= "</select>\n"; @@ -1471,6 +1500,7 @@ Example: no strict; @ISA = ("Apache::lonhelper::element"); use strict; +use Apache::lonlocal; # A localization nightmare use Time::localtime; @@ -1535,7 +1565,7 @@ sub render { } else { $result .= "<option value='$i'>"; } - $result .= $months[$i] . "</option>\n"; + $result .= &mt($months[$i]) . "</option>\n"; } $result .= "</select>\n"; @@ -1565,25 +1595,28 @@ sub render { # Display Hours and Minutes if they are called for if ($self->{'hoursminutes'}) { + # This needs parameterization for times. + my $am = &mt('a.m.'); + my $pm = &mt('p.m.'); # Build hour $result .= "<select name='${var}hour'>\n"; $result .= "<option " . ($date->hour == 0 ? 'selected ':'') . - " value='0'>midnight</option>\n"; + " value='0'>" . &mt('midnight') . "</option>\n"; for ($i = 1; $i < 12; $i++) { if ($date->hour == $i) { - $result .= "<option selected value='$i'>$i a.m.</option>\n"; + $result .= "<option selected value='$i'>$i $am</option>\n"; } else { - $result .= "<option value='$i'>$i a.m</option>\n"; + $result .= "<option value='$i'>$i $am</option>\n"; } } $result .= "<option " . ($date->hour == 12 ? 'selected ':'') . - " value='12'>noon</option>\n"; + " value='12'>" . &mt('noon') . "</option>\n"; for ($i = 13; $i < 24; $i++) { my $printedHour = $i - 12; if ($date->hour == $i) { - $result .= "<option selected value='$i'>$printedHour p.m.</option>\n"; + $result .= "<option selected value='$i'>$printedHour $pm</option>\n"; } else { - $result .= "<option value='$i'>$printedHour p.m.</option>\n"; + $result .= "<option value='$i'>$printedHour $pm</option>\n"; } } @@ -1622,17 +1655,26 @@ sub postprocess { $hour = $ENV{'form.' . $var . 'hour'}; } - my $chosenDate = Time::Local::timelocal(0, $min, $hour, $day, $month, $year); + my $chosenDate; + eval {$chosenDate = Time::Local::timelocal(0, $min, $hour, $day, $month, $year);}; + my $error = $@; + # 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 leapyear + # 3, depending on if it's a leap year my $checkDate = localtime($chosenDate); - if ($checkDate->mon != $month || $checkDate->mday != $day || + if ($error || $checkDate->mon != $month || $checkDate->mday != $day || $checkDate->year + 1900 != $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."; + return 0; } @@ -1696,7 +1738,10 @@ and long status display columns to the d default, the value will be the resource ID of the object ($res->{ID}). =item * <mapurl>X<mapurl>: If the URL of a map is given here, only that map - will be displayed, instead of the whole course. + will be displayed, instead of the whole course. If the attribute + "evaluate" is given and is true, the contents of the mapurl will be + evaluated with "sub { my $helper = shift; my $state = shift;" and + "}", with the return value used as the mapurl. =back @@ -1808,6 +1853,7 @@ sub start_mapurl { my $contents = Apache::lonxml::get_all_text('/mapurl', $parser); + $paramHash->{EVAL_MAP_URL} = $token->[2]{'evaluate'}; $paramHash->{MAP_URL} = $contents; } @@ -1863,9 +1909,18 @@ BUTTONS my $filterFunc = $self->{FILTER_FUNC}; my $choiceFunc = $self->{CHOICE_FUNC}; my $valueFunc = $self->{VALUE_FUNC}; - my $mapUrl = $self->{MAP_URL}; my $multichoice = $self->{'multichoice'}; + # Evaluate the map url as needed + my $mapUrl; + if ($self->{EVAL_MAP_URL}) { + my $mapUrlFunc = eval('sub { my $helper = shift; my $state = shift; ' . + $self->{MAP_URL} . '}'); + $mapUrl = &$mapUrlFunc($helper, $self); + } else { + $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) ;-) @@ -1942,17 +1997,37 @@ package Apache::lonhelper::student; Student elements display a choice of students enrolled in the current course. Currently it is primitive; this is expected to evolve later. -Student elements take three attributes: "variable", which means what -it usually does, "multichoice", which if true allows the user -to select multiple students, and "coursepersonnel" which if true -adds the course personnel to the top of the student selection. +Student elements take the following attributes: + +=over 4 + +=item * B<variable>: + +Does what it usually does: declare which helper variable to put the +result in. + +=item * B<multichoice>: + +If true allows the user to select multiple students. Defaults to false. + +=item * B<coursepersonnel>: + +If true adds the course personnel to the top of the student +selection. Defaults to false. + +=item * B<activeonly>: + +If true, only active students and course personnel will be +shown. Defaults to false. + +=back =cut no strict; @ISA = ("Apache::lonhelper::element"); use strict; - +use Apache::lonlocal; BEGIN { @@ -1976,6 +2051,7 @@ sub start_student { $helper->declareVar($paramHash->{'variable'}); $paramHash->{'multichoice'} = $token->[2]{'multichoice'}; $paramHash->{'coursepersonnel'} = $token->[2]{'coursepersonnel'}; + $paramHash->{'sctiveonly'} = $token->[2]{'activeonly'}; if (defined($token->[2]{'nextstate'})) { $paramHash->{NEXTSTATE} = $token->[2]{'nextstate'}; } @@ -2008,12 +2084,34 @@ sub render { } } } + 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) { + 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; + } + } + } </script> SCRIPT + + my $selectAllStudents = &mt('Select All Students'); + my $unselectAllStudents = &mt('Unselect All Students'); $buttons = <<BUTTONS; <br /> -<input type="button" onclick="checkall(true, '$var')" value="Select All Students" /> -<input type="button" onclick="checkall(false, '$var')" value="Unselect All Students" /> +<input type="button" onclick="checkactive()" value="Select Only Current Students" /> +<input type="button" onclick="checkall(true, '$var')" value="$selectAllStudents" /> +<input type="button" onclick="checkall(false, '$var')" value="$unselectAllStudents" /> +<input type="button" onclick="checksec(true)" value="Select for Section/Group"> +<input type="text" size="5" name="chksec"> +<input type="button" onclick="checksec(false)" value="Unselect for Section/Group"> <br /> BUTTONS } @@ -2048,6 +2146,7 @@ BUTTONS # 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(); @@ -2062,17 +2161,24 @@ BUTTONS # username, fullname, section, type for (@keys) { - push @$choices, [$_, $classlist->{$_}->[$fullname], - $classlist->{$_}->[$section], 'Student']; + # 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']; + } } - my $name = $self->{'coursepersonnel'} ? 'Name' : 'Student Name'; + my $name = &mt($self->{'coursepersonnel'} ? 'Name' : '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>Section</b></td>" . - "<td align='center'><b>Role</b></td></tr>"; + "<td align='center'><b>" . &mt('Section') . "</b></td>" . + "<td align='center'><b>Status</b></td>" . + "<td align='center'><b>" . &mt("Role") . "</b></td></tr>"; + "<td align='center'><b>Username:Domain</b></td></tr>"; my $checked = 0; for my $choice (@$choices) { @@ -2084,13 +2190,18 @@ BUTTONS $checked = 1; } $result .= - " value='" . HTML::Entities::encode($choice->[0] . ':' . $choice->[2]) + " 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></tr>\n"; + . 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"; @@ -2104,8 +2215,8 @@ sub postprocess { my $result = $ENV{'form.' . $self->{'variable'} . '.forminput'}; if (!$result) { - $self->{ERROR_MSG} = 'You must choose at least one student '. - 'to continue.'; + $self->{ERROR_MSG} = + &mt('You must choose at least one student to continue.'); return 0; } @@ -2171,6 +2282,7 @@ viewing the files. no strict; @ISA = ("Apache::lonhelper::element"); use strict; +use Apache::lonlocal; use Apache::lonpubdir; # for getTitleString @@ -2289,16 +2401,20 @@ sub render { } </script> SCRIPT + my $selectAllFiles = &mt("Select All Files"); + my $unselectAllFiles = &mt("Unselect All Files"); $buttons = <<BUTTONS; <br /> -<input type="button" onclick="checkall(true, '$var')" value="Select All Files" /> -<input type="button" onclick="checkall(false, '$var')" value="Unselect All Files" /> +<input type="button" onclick="checkall(true, '$var')" value="$selectAllFiles" /> +<input type="button" onclick="checkall(false, '$var')" value="$unselectAllFiles" /> BUTTONS + my $selectAllPublished = &mt("Select All Published"); + my $unselectAllPublished= &mt("UnselectAllPublished"); if ($helper->{VARS}->{'construction'}) { $buttons .= <<BUTTONS; -<input type="button" onclick="checkallclass(true, 'Published')" value="Select All Published" /> -<input type="button" onclick="checkallclass(false, 'Published')" value="Unselect All Published" /> +<input type="button" onclick="checkallclass(true, 'Published')" value="$selectAllPublished" /> +<input type="button" onclick="checkallclass(false, 'Published')" value="$unselectAllPublished" /> <br /> BUTTONS } @@ -2308,9 +2424,17 @@ BUTTONS my @fileList; # If the subdirectory is in local CSTR space - if ($subdir =~ m|/home/([^/]+)/public_html|) { + my $metadir; + if ($subdir =~ m|/home/([^/]+)/public_html/(.*)|) { my $user = $1; my $domain = $Apache::lonnet::perlvar{'lonDefDomain'}; + $metadir='/res/'.$domain.'/'.$user.'/'.$2; + @fileList = &Apache::lonnet::dirlist($subdir, $domain, $user, ''); + } elsif ($subdir =~ m|^~([^/]+)/(.*)$|) { + $subdir='/home/'.$1.'/public_html/'.$2; + my $user = $1; + my $domain = $Apache::lonnet::perlvar{'lonDefDomain'}; + $metadir='/res/'.$domain.'/'.$user.'/'.$2; @fileList = &Apache::lonnet::dirlist($subdir, $domain, $user, ''); } else { # local library server resource space @@ -2349,7 +2473,7 @@ BUTTONS } # Get the title - my $title = Apache::lonpubdir::getTitleString($fileName); + my $title = Apache::lonpubdir::getTitleString(($metadir?$metadir:$subdir) .'/'. $file); # Netscape 4 is stupid and there's nowhere to put the # information on the input tag that the file is Published, @@ -2387,7 +2511,7 @@ BUTTONS $result .= "</table>\n"; if (!$choices) { - $result .= '<font color="#FF0000">There are no files available to select in this directory. Please go back and select another option.</font><br /><br />'; + $result .= '<font color="#FF0000">There are no files available to select in this directory ('.$subdir.'). Please go back and select another option.</font><br /><br />'; } $result .= $buttons; @@ -2866,7 +2990,8 @@ sub render { } if (!@results) { - $result .= ' <li>No changes were made to current settings.</li>'; + $result .= ' <li>' . + &mt('No changes were made to current settings.') . '</li>'; } $result .= '</ul>'; @@ -2877,13 +3002,16 @@ sub render { if ($ENV{'course.'.$ENV{'request.course.id'}.'.clonedfrom'}) { $targetURL = '/adm/parmset?overview=1'; } + my $previous = HTML::Entities::encode(&mt("<- Previous"), '<>&"'); + my $next = HTML::Entities::encode(&mt("Next ->"), '<>&"'); $result .= "<center>\n" . "<form action='/adm/roles' method='post' target='loncapaclient'>\n" . - "<input type='button' onclick='history.go(-1)' value='<- Previous' />" . + "<input type='button' onclick='history.go(-1)' value='$previous' />" . "<input type='hidden' name='orgurl' value='$targetURL' />" . "<input type='hidden' name='selectrole' value='1' />\n" . "<input type='hidden' name='" . $ENV{'request.role'} . - "' value='1' />\n<input type='submit' value='Finish Course Initialization' />\n" . + "' value='1' />\n<input type='submit' value='" . + &mt('Finish Course Initialization') . " />\n" . "</form></center>"; } @@ -2937,15 +3065,17 @@ sub render { my $vars = $helper->{VARS}; # FIXME: Unify my designators with the standard ones - my %dateTypeHash = ('open_date' => "Opening Date", - 'due_date' => "Due Date", - 'answer_date' => "Answer Date", - 'tries' => 'Number of Tries' + my %dateTypeHash = ('open_date' => "opening date", + 'due_date' => "due date", + 'answer_date' => "answer date", + 'tries' => 'number of tries', + 'weight' => 'problem weight' ); my %parmTypeHash = ('open_date' => "0_opendate", 'due_date' => "0_duedate", 'answer_date' => "0_answerdate", - 'tries' => '0_maxtries' ); + 'tries' => '0_maxtries', + 'weight' => '0_weight' ); my $affectedResourceId = ""; my $parm_name = $parmTypeHash{$vars->{ACTION_TYPE}}; @@ -2991,6 +3121,9 @@ sub render { if ($vars->{ACTION_TYPE} eq 'tries') { $result .= ' to <b>' . $vars->{TRIES} . '</b>'; } + if ($vars->{ACTION_TYPE} eq 'weight') { + $result .= ' to <b>' . $vars->{WEIGHT} . '</b>'; + } $result .= "</li>\n"; if ($vars->{ACTION_TYPE} eq 'due_date' || $vars->{ACTION_TYPE} eq 'answer_date') { @@ -3011,6 +3144,9 @@ sub render { } elsif ($vars->{ACTION_TYPE} eq 'tries') { $result .= "<input type='hidden' name='pres_value' " . "value='" . $vars->{TRIES} . "' />\n"; + } elsif ($vars->{ACTION_TYPE} eq 'weight') { + $result .= "<input type='hidden' name='pres_value' " . + "value='" . $vars->{WEIGHT} . "' />\n"; } $result .= $resourceString; @@ -3041,7 +3177,7 @@ sub render { } # Print value - if ($vars->{ACTION_TYPE} ne 'tries') { + if ($vars->{ACTION_TYPE} ne 'tries' && $vars->{ACTION_TYPE} ne 'weight') { $result .= "<li>to <b>" . ctime($vars->{PARM_DATE}) . "</b> (" . Apache::lonnavmaps::timeToHumanString($vars->{PARM_DATE}) . ")</li>\n";