--- loncom/interface/lonhelper.pm 2008/12/13 04:36:28 1.166.2.1 +++ loncom/interface/lonhelper.pm 2022/06/27 20:35:51 1.204 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # .helper XML handler to implement the LON-CAPA helper # -# $Id: lonhelper.pm,v 1.166.2.1 2008/12/13 04:36:28 raeburn Exp $ +# $Id: lonhelper.pm,v 1.204 2022/06/27 20:35:51 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -64,9 +64,11 @@ messages, resource selections, or date q The helper tag is required to have one attribute, "title", which is the name of the helper itself, such as "Parameter helper". The helper tag may optionally -have a "requiredpriv" attribute, specifying the priviledge a user must have +have a "requiredpriv" attribute, specifying the privilege a user must have to use the helper, or get denied access. See loncom/auth/rolesplain.tab for -useful privs. Default is full access, which is often wrong! +useful privs. You may add the modifier &S at the end of the three letter priv +if you want to grant access to users for whom the corresponding privilege is +section-specific. The default is full access, which is often wrong! =head2 State tags @@ -188,6 +190,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 @@ -259,7 +263,7 @@ sub real_handler { my $uri = shift; if (!defined($uri)) { $uri = $r->uri(); } $env{'request.uri'} = $uri; - my $filename = '/home/httpd/html' . $uri; + my $filename = $r->dir_config('lonDocRoot').$uri; my $fh = Apache::File->new($filename); my $file; read $fh, $file, 100000000; @@ -281,7 +285,8 @@ sub real_handler { my $allowed = $helper->allowedCheck(); if (!$allowed) { - $env{'user.error.msg'} = $env{'request.uri'}.':'.$helper->{REQUIRED_PRIV}. + my ($priv,$modifier) = split(/\&/,$helper->{REQUIRED_PRIV}); + $env{'user.error.msg'} = $env{'request.uri'}.':'.$priv. ":0:0:Permission denied to access this helper."; return HTTP_NOT_ACCEPTABLE; } @@ -506,8 +511,13 @@ sub allowedCheck { if (!defined($self->{REQUIRED_PRIV})) { return 1; } - - return Apache::lonnet::allowed($self->{REQUIRED_PRIV}, $env{'request.course.id'}); + my ($priv,$modifier) = split(/\&/,$self->{REQUIRED_PRIV}); + my $allowed = &Apache::lonnet::allowed($priv,$env{'request.course.id'}); + if ((!$allowed) && ($modifier eq 'S') && ($env{'request.course.sec'} ne '')) { + $allowed = &Apache::lonnet::allowed($priv,$env{'request.course.id'}.'/'. + $env{'request.course.sec'}); + } + return $allowed; } sub changeState { @@ -529,7 +539,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(); } @@ -569,7 +579,7 @@ sub process { # 4: Render the current state to the screen as an HTML page. sub display { my $self = shift; - + my $footer = shift; my $state = $self->{STATES}{$self->{STATE}}; my $result = ""; @@ -587,82 +597,79 @@ sub display { &Apache::loncommon::browser_and_searcher_javascript(). "\n".'</script>'; + # 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.="<form name='helpform' method='POST'>"; } + if (!$state->overrideForm()) { $result.='<form name="helpform" method="post" action="">'; } if ($stateHelp) { - $stateHelp = &Apache::loncommon::help_open_topic($stateHelp); + $stateHelp = &Apache::loncommon::help_open_topic($stateHelp); } - $result .= <<HEADER; - <table border="0" width='100%'><tr><td> - <h2><i>$stateTitle</i>$stateHelp</h2> -HEADER - - $result .= "<table cellpadding='10' width='100%'><tr><td rowspan='2' valign='top'>"; - if (!$state->overrideForm()) { - $result .= $self->_saveVars(); - } - $result .= $state->render(); - - $result .= "</td><td valign='top' align='right'>"; - - # 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 .= '<input name="SUBMIT" type="submit" value="<- Previous" /> '; } + $buttons = '<p>'; # '<fieldset>'; if ($self->{DONE}) { my $returnPage = $self->{RETURN_PAGE}; - $result .= "<a href=\"$returnPage\">" . &mt("End Helper") . "</a>"; + $buttons .= '<a href="'.$returnPage.'">'.&mt('End Helper').'</a>'; } else { - $result .= '<span class="LC_nobreak"><input name="back" type="button" '; - $result .= 'value="' . $previous . '" onclick="history.go(-1)" /> '; - $result .= '<input name="SUBMIT" type="submit" value="' . $next . '" /></span>'; + $buttons .= '<span class="LC_nobreak">' + .'<input name="back" type="button" ' + .'value="'.$previous.'" onclick="history.go(-1)" /> ' + .'<input name="SUBMIT" type="submit" value="'.$next.'" />' + .'</span>'; } + $buttons .= '</p>'; # '</fieldset>'; } - $result .= "</td></tr><tr><td valign='bottom' align='right'>"; - # Warning: Copy and pasted from above, because it's too much trouble to - # turn this into a subroutine + + $result .= '<h2>'.$stateTitle.$stateHelp.'</h2>'; + +# $result .= '<div>'; + + # Top buttons + $result .= $buttons; + + # Main content of current helper screen if (!$state->overrideForm()) { - if ($self->{STATE} ne $self->{START_STATE}) { - #$result .= '<input name="SUBMIT" type="submit" value="<- Previous" /> '; - } - if ($self->{DONE}) { - my $returnPage = $self->{RETURN_PAGE}; - $result .= "<a href=\"$returnPage\">" . &mt('End Helper') . "</a>"; - } - else { - $result .= '<span class="LC_nobreak"><input name="back" type="button" '; - $result .= 'value="' . $previous . '" onclick="history.go(-1)" /> '; - $result .= '<input name="SUBMIT" type="submit" value="' . $next . '" /></span>'; - } + $result .= $self->_saveVars(); } + $result .= $state->render(); + + # Bottom buttons + $result .= $buttons; + - #foreach my $key (keys %{$self->{VARS}}) { + #foreach my $key (keys(%{$self->{VARS}})) { # $result .= "|$key| -> " . $self->{VARS}->{$key} . "<br />"; #} - $result .= "</td></tr></table>"; +# $result .= '</div>'; $result .= <<FOOTER; - </td> - </tr> - </table> </form> FOOTER - $result .= &Apache::loncommon::end_page(); + $result .= $footer.&Apache::loncommon::end_page(); # Handle writing out the vars to the file my $file = Apache::File->new('>'.$self->{FILENAME}); print $file $self->_varsInFile(); @@ -1477,9 +1484,9 @@ BUTTONS HTML::Entities::encode($choice->[1],"<>&\"'") . "'"; if ($checkedChoices{$choice->[1]}) { - $result .= " checked='checked' "; + $result .= " checked='checked'"; } - $result .= qq{id="id$id"}; + $result .= qq{ id="id$id"}; my $choiceLabel = $choice->[0]; if ($choice->[3]) { # if we need to evaluate this choice $choiceLabel = "sub { my $helper = shift; my $state = shift;" . @@ -1487,7 +1494,7 @@ BUTTONS $choiceLabel = eval($choiceLabel); $choiceLabel = &$choiceLabel($helper, $self); } - $result .= "/></td><td> ".qq{<label for="id$id">}. + $result .= " /></td><td> ".qq{<label for="id$id">}. $choiceLabel. "</label></td>"; if ($choice->[4]) { $result .='<td><input type="text" size="5" name="' @@ -1508,12 +1515,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); } @@ -1790,7 +1800,7 @@ sub render { if (lc($time) eq 'anytime') { $anytime=1; $date = &get_date_object(time); - $date->min(0); + $date->set_minute(0); } elsif (defined($time) && $time ne 0) { $date = &get_date_object($time); } else { @@ -1800,7 +1810,7 @@ sub render { if (!defined($date)) { $date = &get_date_object(time); - $date->min(0); + $date->set_minute(0); } if ($anytime) { @@ -1911,7 +1921,13 @@ CHECK if ($anytime) { $result.=' checked="checked" ' } - $result.="name='${var}anytime'/>".&mt('Any time').'</label>' + my $anytimetext = &mt('Any time'); + if (($var eq 'startreserve') || ($var eq 'endreserve')) { + $anytimetext = &mt('Any time before slot starts'); + } elsif (($var eq 'startunique') || ($var eq 'endunique')) { + $anytimetext = &mt('No restriction on uniqueness'); + } + $result.="name='${var}anytime'/>".$anytimetext.'</label>' } return $result; @@ -2031,7 +2047,9 @@ be filtered out. The 'addstatus' attribu 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. The 'includecourse' attribute if true, will include -the toplevel default.sequence in the results. +the toplevel default.sequence in the results. The 'modalLink' attribute, +if true, will cause links to be launched as modal pop-ups, instead of +replacing the resource selection listing, currently being displayed. =head3 SUB-TAGS @@ -2064,6 +2082,49 @@ the toplevel default.sequence in the res evaluated with "sub { my $helper = shift; my $state = shift;" and "}", with the return value used as the mapurl. +=item * <option />: Allows you to add optional elements to the + resource chooser currently these can be a checkbox, or a text entry + or hidden (see the 'type' attribute below). + the following attributes are supported by this tag: + +=over 4 + +=item * type=control-type : determines the type of control displayed. + This can be one of the following types: 'checkbox' provides a true/false + checkbox. 'text' provides a text entry control. 'hidden' provides a + hidden form element that returns the name of the resource for each + element of the text box. + +=item * text=header-text : provides column header text for the option. + +=item * variable=helpervar : provides a helper variable to contain the + value of the input control for each resource. In general, the result + will be a set of values separated by ||| for the checkbox the value between + the |||'s will either be empty, if the box is not checked, or the resource + name if checked. For the text entry, the values will be the text in the + text box. This could be empty. Hidden elements unconditionally provide + the resource name for each row of the chooser and allow you to therefore + correlate text entries to their resources. + The helper variable can be initialized by the user code to pre-load values + into the controls: + +=over 4 + + +=item * Preloading checkboxes : Set the helper variable to the value you + would have gotten from the control if it had been manually set as desired. + +=item * Preloading text entries : Set the helper variable to triple pipe + separated values where each value is of the form resource-name=value + +=item * Preloading hidden fields : These cannot be pre-loaded and will always + be pipe separated resource names. + +=back + + +=back + =back =cut @@ -2100,6 +2161,9 @@ sub start_resource { $paramHash->{'toponly'} = $token->[2]{'toponly'}; $paramHash->{'addstatus'} = $token->[2]{'addstatus'}; $paramHash->{'addparts'} = $token->[2]{'addparts'}; + $paramHash->{'modalLink'} = $token->[2]{'modallink'}; + $paramHash->{'nocurrloc'} = $token->[2]{'nocurrloc'}; + $paramHash->{'suppressNavmap'} = $token->[2]{'suppressNavmap'}; if ($paramHash->{'addparts'}) { $helper->declareVar($paramHash->{'variable'}.'_part'); } @@ -2193,20 +2257,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. @@ -2282,7 +2368,9 @@ 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 $modalLink = $self->{'modalLink'}; my $headings_done = 0; # Evaluate the map url as needed @@ -2322,7 +2410,7 @@ BUTTONS $result .= "<th>$text</th>"; } } - $result .= "<th>Select</th>"; + $result .= '<th>'.&Apache::lonlocal::mt('Select').'</th>'; $result .= "</tr><tr>"; # Close off the extra row and start a new one. $headings_done = 1; } @@ -2340,17 +2428,52 @@ 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 .= - "<td align='center'><input type='checkbox' name ='$option_var". - "_forminput' value='". - $resource_name . "' $checked /> </td>"; + 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; + } + } + # TODO: add an attribute to <option> that allows the + # programmer to set the width of the tex entry box. + + $col .= + "<td align='center'><input type='text' name ='$option_var". + "_forminput' value='".$text_value."' size='5' /> </td>"; + } elsif ($option_type eq 'hidden') { + $col .= "<td align='center'><input type='hidden' name ='$option_var". + "_forminput' value='". + $resource_name . "'/> </td>"; + } else { + $col .= + "<td align='center'><input type='$option_type' name ='$option_var". + "_forminput' value='". + $resource_name . "' $checked /> </td>"; + } } } @@ -2384,9 +2507,9 @@ BUTTONS &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 .= "<option value=\"$part\">All Parts</option>\n"; + $col .= "<option value=\"$part\">".&Apache::lonlocal::mt('All Parts')."</option>\n"; foreach my $part (@{$resource->parts}) { - $col .= "<option value=\"$part\">Part: $part</option>\n"; + $col .= "<option value=\"$part\">".&Apache::lonlocal::mt('Part: [_1]',$part)."</option>\n"; } $col .= "</select>"; } @@ -2423,6 +2546,10 @@ RADIO push @$cols, (Apache::lonnavmaps::part_status_summary()); } + my $caller; + if ($helper->{TITLE} eq 'Printing Helper') { + $caller = 'printout'; + } $result .= &Apache::lonnavmaps::render( { 'cols' => $cols, 'showParts' => 0, @@ -2431,7 +2558,12 @@ RADIO 'closeAllPages' => $self->{'closeallpages'}, 'suppressEmptySequences' => $self->{'suppressEmptySequences'}, 'include_top_level_map' => $self->{'include_top_level_map'}, - 'iterator_map' => $mapUrl } + 'iterator_map' => $mapUrl, + 'map_no_edit_link' => 1, + 'modalLink' => $modalLink, + 'nocurrloc' => $self->{'nocurrloc'}, + 'suppressNavmap' => $self->{'suppressNavmap'}, + 'caller' => $caller, } ); $result .= $buttons; @@ -2446,6 +2578,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}); @@ -2488,6 +2635,12 @@ selection. Defaults to false. If true, only active students and course personnel will be shown. Defaults to false. +=item * B<sectiononly>: + +If true, and user's role is in a specific section, only course personnel +will be shown if they also have a section-specific role in the same section. +Defaults to false. + =item * B<emptyallowed>: If true, the selection of no users is allowed. Defaults to false. @@ -2523,6 +2676,7 @@ sub start_student { $helper->declareVar($paramHash->{'variable'}); $paramHash->{'multichoice'} = $token->[2]{'multichoice'}; $paramHash->{'coursepersonnel'} = $token->[2]{'coursepersonnel'}; + $paramHash->{'sectiononly'} = $token->[2]{'sectiononly'}; $paramHash->{'activeonly'} = $token->[2]{'activeonly'}; if (defined($token->[2]{'nextstate'})) { $paramHash->{NEXTSTATE} = $token->[2]{'nextstate'}; @@ -2564,14 +2718,17 @@ sub render { delete($defaultUsers{''}); } + my $personnel_section; + if ($self->{'sectiononly'}) { + $personnel_section = $env{'request.course.sec'}; + } my ($course_personnel, $current_members, $expired_members, $future_members) = - &Apache::lonselstudent::get_people_in_class($env{'request.course.sec'}); - - + &Apache::lonselstudent::get_people_in_class($env{'request.course.sec'}, + $personnel_section); # Load up the non-students, if necessary @@ -2579,26 +2736,44 @@ sub render { unshift @$current_members, (@$course_personnel); } + my %titles = &Apache::lonlocal::texthash( + 'active' => 'Select Currently Enrolled Students and Active Course Personnel', + 'future' => 'Select Future Enrolled Students', + 'expired' => 'Select Previously Enrolled Students', + ); + + if ($env{'request.course.sec'}) { + if ($self->{'sectiononly'}) { + $titles{'active'} = &mt('Select Currently Enrolled Students and Active Course Personnel in Section: [_1]', + $env{'request.course.sec'}); + } else { + $titles{'active'} = &mt('Select Currently Enrolled Students in Section: [_1], and Active Course Personnel', + $env{'request.course.sec'}); + } + $titles{'future'} = &mt('Select Future Enrolled Students in Section: [_1]', + $env{'request.course.sec'}); + $titles{'expired'} = &mt('Select Previously Enrolled Students in Section: [_1]', + $env{'request.course.sec'}); + } - # Current personel + # Current personnel - $result .= '<h4>'.&mt('Select Currently Enrolled Students and Active Course Personnel').'</h4>'; + $result .= '<h4>'.$titles{'active'}.'</h4>'; $result .= &Apache::lonselstudent::render_student_list( $current_members, "helpform", - "current", + "current", \%defaultUsers, $self->{'multichoice'}, $self->{'variable'}, 1); - # If activeonly is not set then we can also give the expired students: # if (!$self->{'activeonly'} && ((scalar(@$future_members)) > 0)) { # And future. - $result .= '<h4>'.&mt('Select Future Enrolled Students and Future Course Personnel').'</h4>'; + $result .= '<h4>'.$titles{'future'}.'</h4>'; $result .= &Apache::lonselstudent::render_student_list( $future_members, "helpform", @@ -2611,7 +2786,7 @@ sub render { if (!$self->{'activeonly'} && ((scalar(@$expired_members)) > 0)) { # Past - $result .= '<h4>'.&mt('Select Previously Enrolled Students and Inactive Course Personnel').'</h4>'; + $result .= '<h4>'.$titles{'expired'}.'</h4>'; $result .= &Apache::lonselstudent::render_student_list($expired_members, "helpform", "past", @@ -2853,30 +3028,31 @@ BUTTONS } # Get the list of files in this directory. - my @fileList; + my (@fileList,$listref,$listerror); # If the subdirectory is in local CSTR space my $metadir; - if ($subdir =~ m|/home/([^/]+)/public_html/(.*)|) { - my ($user,$domain)= - &Apache::loncacc::constructaccess($subdir, - $Apache::lonnet::perlvar{'lonDefDomain'}); - $metadir='/res/'.$domain.'/'.$user.'/'.$2; - @fileList = &Apache::lonnet::dirlist($subdir,$domain,$user,undef,undef,'/'); - } elsif ($subdir =~ m|^~([^/]+)/(.*)$|) { - $subdir='/home/'.$1.'/public_html/'.$2; + my $londocroot = $Apache::lonnet::perlvar{'lonDocRoot'}; + if ($subdir =~ m{^(?:\Q$londocroot\E)*/priv/[^/]+/[^/]+/(.*)$}) { + my $innerpath=$1; + unless ($subdir=~m{^\Q$londocroot\E}) { + $subdir=$londocroot.$subdir; + } my ($user,$domain)= - &Apache::loncacc::constructaccess($subdir, - $Apache::lonnet::perlvar{'lonDefDomain'}); - $metadir='/res/'.$domain.'/'.$user.'/'.$2; - @fileList = &Apache::lonnet::dirlist($subdir,$domain,$user,undef,undef,'/'); + &Apache::lonnet::constructaccess($subdir); + $metadir='/res/'.$domain.'/'.$user.'/'.$innerpath; + ($listref,$listerror) = + &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'},undef,undef,'/'); + ($listref,$listerror) = + &Apache::lonnet::dirlist($subdir,$env{'user.domain'},$env{'user.name'},undef,undef,'/'); } # Sort the fileList into order - @fileList = sort {lc($a) cmp lc($b)} @fileList; + if (ref($listref) eq 'ARRAY') { + @fileList = sort {lc($a) cmp lc($b)} @{$listref}; + } $result .= $buttons; @@ -2971,7 +3147,7 @@ sub fileState { } my $docroot = $Apache::lonnet::perlvar{'lonDocRoot'}; my $subdirpart = $constructionSpaceDir; - $subdirpart =~ s/^\/home\/$uname\/public_html//; + $subdirpart =~ s{^\Q$docroot/priv/$udom/$uname\E}{}; my $resdir = $docroot . '/res/' . $udom . '/' . $uname . $subdirpart; @@ -3021,7 +3197,7 @@ It takes the standard attributes "variab "allowempty" and "nextstate", meaning what they do for most other elements. -also takes a boolean 'onlysections' whcih will restrict this to only +also takes a boolean 'onlysections' which will restrict this to only have sections and not include groups =cut @@ -3059,22 +3235,27 @@ sub start_section { # Populate the CHOICES element my %choices; + my $usersec = $Apache::lonnet::env{'request.course.sec'}; - my $section = Apache::loncoursedata::CL_SECTION(); - my $classlist = Apache::loncoursedata::get_classlist(); - foreach my $user (keys(%$classlist)) { - my $section_name = $classlist->{$user}[$section]; - if (!$section_name) { - $choices{"No section assigned"} = ""; - } else { - $choices{$section_name} = $section_name; + if ($usersec ne '') { + $choices{$usersec} = $usersec; + } else { + my $section = Apache::loncoursedata::CL_SECTION(); + my $classlist = Apache::loncoursedata::get_classlist(); + foreach my $user (keys(%$classlist)) { + my $section_name = $classlist->{$user}[$section]; + if (!$section_name) { + $choices{"No section assigned"} = ""; + } else { + $choices{$section_name} = $section_name; + } + } + + if (exists($choices{"No section assigned"})) { + push(@{$paramHash->{CHOICES}}, + ['No section assigned','No section assigned']); + delete($choices{"No section assigned"}); } - } - - 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]; @@ -3110,6 +3291,10 @@ It takes the standard attributes "variab "allowempty" and "nextstate", meaning what they do for most other elements. +also takes a boolean grouponly, which if true, will restrict choice to +groups in which user is a member, unless user has the mdg priv in the course, +in which case all groups will be possible choices. Defaults to false. + =cut no strict; @@ -3139,6 +3324,7 @@ sub start_group { $helper->declareVar($paramHash->{'variable'}); $paramHash->{'multichoice'} = $token->[2]{'multichoice'}; $paramHash->{'allowempty'} = $token->[2]{'allowempty'}; + $paramHash->{'grouponly'} = $token->[2]{'grouponly'}; if (defined($token->[2]{'nextstate'})) { $paramHash->{NEXTSTATE} = $token->[2]{'nextstate'}; } @@ -3146,7 +3332,12 @@ sub start_group { # Populate the CHOICES element my %choices; - my %curr_groups = &Apache::longroup::coursegroups(); + my %curr_groups; + if ((!$paramHash->{'grouponly'}) || (&Apache::lonnet::allowed('mdg',$Apache::lonnet::env{'request.course.id'}))) { + %curr_groups = &Apache::longroup::coursegroups(); + } elsif ($Apache::lonnet::env{'request.course.groups'} ne '') { + map { $curr_groups{$_} = 1; } split(/:/,$Apache::lonnet::env{'request.course.groups'}); + } foreach my $group_name (sort {lc($a) cmp lc($b)} (keys(%curr_groups))) { push(@{$paramHash->{CHOICES}}, [$group_name, $group_name]); } @@ -3171,6 +3362,10 @@ package Apache::lonhelper::string; string elements provide a string entry field for the user. string elements take the usual 'variable' and 'nextstate' parameters. string elements also pass through 'maxlength' and 'size' attributes to the input tag. +Since you could have multiple strings in a helper state, each with its own +validator, all but the last string should have +noproceed='1' so that _all_ validators are evaluated before the next +state can be reached. string honors the defaultvalue tag, if given. @@ -3190,6 +3385,7 @@ BEGIN { sub new { my $ref = Apache::lonhelper::element->new(); + $ref->{'PROCEED'} = 1; # By default postprocess goes to next state. bless($ref); } @@ -3206,20 +3402,33 @@ sub start_string { $paramHash->{'nextstate'} = $token->[2]{'nextstate'}; $paramHash->{'maxlength'} = $token->[2]{'maxlength'}; $paramHash->{'size'} = $token->[2]{'size'}; - return ''; } sub end_string { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; + if ($target ne 'helper') { return ''; } - Apache::lonhelper::string->new(); + my $state = Apache::lonhelper::string->new(); + + + if(&Apache::lonxml::get_param('noproceed', $parstack, $safeeval, undef, 1)) { + $state->noproceed(); + } + + + return ''; } +sub noproceed() { + my $self = shift; + $self->{PROCEED} = 0; +} + sub render { my $self = shift; my $result = ''; @@ -3228,7 +3437,7 @@ sub render { $result .= '<p><font color="#FF0000">' . $self->{ERROR_MSG} . '</font></p>'; } - $result .= '<input type="string" name="' . $self->{'variable'} . '_forminput"'; + $result .= '<input type="text" name="' . $self->{'variable'} . '_forminput"'; if (defined($self->{'size'})) { $result .= ' size="' . $self->{'size'} . '"'; @@ -3263,7 +3472,7 @@ sub postprocess { } } - if (defined($self->{'nextstate'})) { + if (defined($self->{'nextstate'}) && $self->{PROCEED}) { $helper->changeState($self->{'nextstate'}); } @@ -3421,7 +3630,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. @@ -3490,7 +3699,7 @@ sub render { my @results; # Collect all the results - for my $stateName (keys %{$helper->{STATES}}) { + for my $stateName (keys(%{$helper->{STATES}})) { my $state = $helper->{STATES}->{$stateName}; for my $element (@{$state->{ELEMENTS}}) { @@ -3528,7 +3737,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'; @@ -3540,21 +3749,17 @@ 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 $target = " target='loncapaclient'"; - if (($env{'browser.interface'} eq 'textual') || - ($env{'environment.remote'} eq 'off')) { $target=''; } - $result .= "<center>\n" . - "<form action='".$actionURL."' method='post' $target>\n" . + my $previous = HTML::Entities::encode(&mt("Back"), '<>&"'); + my $next = HTML::Entities::encode(&mt("Next"), '<>&"'); + $result .= "<p>\n" . + "<form action='".$actionURL."' method='post' >\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" . "<input type='hidden' name='" . $env{'request.role'} . "' value='1' />\n<input type='submit' value='" . $finish . "' />\n" . - "</form></center>"; + "</form></p>\n"; return $result; } @@ -3633,50 +3838,77 @@ sub render { # Print the granularity, depending on the action if ($vars->{GRANULARITY} eq 'whole_course') { - $resourceString .= '<li>'.&mt('for <b>all resources in the course</b>').'</li>'; + $resourceString .= '<li>'.&mt('for [_1]all resources in the course[_2]','<b>','</b>').'</li>'; if ($vars->{TARGETS} eq 'course') { - $level = 14; # general course, see lonparmset.pm perldoc + $level = 18; # general course, see lonparmset.pm perldoc } elsif ($vars->{TARGETS} eq 'section') { - $level = 9; + $level = 12; } elsif ($vars->{TARGETS} eq 'group') { - $level = 6; + $level = 8; } else { - $level = 3; + $level = 4; } $affectedResourceId = "0.0"; $symb = 'a'; $paramlevel = 'general'; - } elsif ($vars->{GRANULARITY} eq 'map') { + } elsif (($vars->{GRANULARITY} eq 'map') || ($vars->{GRANULARITY} eq 'maprecurse')) { my $navmap = Apache::lonnavmaps::navmap->new(); - my $res = $navmap->getByMapPc($vars->{RESOURCE_ID}); - my $title = $res->compTitle(); - $symb = $res->symb(); - $resourceString .= '<li>'.&mt('for the map named [_1]',"<b>$title</b>").'</li>'; - if ($vars->{TARGETS} eq 'course') { - $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; - } + if (defined($navmap)) { + my $res = $navmap->getByMapPc($vars->{RESOURCE_ID}); + my $title = $res->compTitle(); + $symb = $res->symb(); + if ($vars->{GRANULARITY} eq 'map') { + $resourceString .= '<li>'.&mt('for the map named [_1]',"<b>$title</b>").'</li>'; + } else { + $resourceString .= '<li>'.&mt('for the map named [_1] (applies recursively to sub-folders)',"<b>$title</b>").'</li>'; + } + } else { + $resourceString .= '<li>'.&mt('for the map ID [_1] (name unavailable)','<b>'.$vars->{RESOURCE_ID}.'</b>').'</li>'; + &Apache::lonnet::logthis('Retrieval of map title failed in lonhelper.pm - could not create navmap object for course.'); + + } + if ($vars->{GRANULARITY} eq 'maprecurse') { + if ($vars->{TARGETS} eq 'course') { + $level = 17; # general course, see lonparmset.pm perldoc + } elsif ($vars->{TARGETS} eq 'section') { + $level = 11; + } elsif ($vars->{TARGETS} eq 'group') { + $level = 7; + } else { + $level = 3; + } + } else { + if ($vars->{TARGETS} eq 'course') { + $level = 16; # general course, see lonparmset.pm perldoc + } elsif ($vars->{TARGETS} eq 'section') { + $level = 10; + } elsif ($vars->{TARGETS} eq 'group') { + $level = 6; + } else { + $level = 2; + } + } $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 .= '<li>'.&mt('for the resource named [_1] part [_2]',"<b>$title</b>","<b>$part</b>").'</li>'; + my $navmap = Apache::lonnavmaps::navmap->new(); + if (defined($navmap)) { + my $res = $navmap->getById($vars->{RESOURCE_ID}); + $symb = $res->symb(); + my $title = $res->compTitle(); + $resourceString .= '<li>'.&mt('for the resource named [_1], part [_2]',"<b>$title</b>","<b>$part</b>").'</li>'; + } else { + $resourceString .= '<li>'.&mt('for the resource ID [_1] (name unavailable), part [_2]','<b>'.$vars->{RESOURCE_ID}.'</b>',"<b>$part</b>").'</li>'; + &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 + $level = 13; # general course, see lonparmset.pm perldoc } elsif ($vars->{TARGETS} eq 'section') { - $level = 7; + $level = 9; } elsif ($vars->{TARGETS} eq 'group') { - $level = 4; + $level = 5; } else { $level = 1; } @@ -3684,7 +3916,7 @@ sub render { $paramlevel = 'full'; } - my $result = "<form name='helpform' method='POST' action='/adm/parmset#$affectedResourceId&$parm_name&$level'>\n"; + my $result = "<form name='helpform' method='post' action='/adm/parmset#$affectedResourceId&$parm_name&$level'>\n"; $result .= "<input type='hidden' name='action' value='settable' />\n"; $result .= "<input type='hidden' name='dis' value='helper' />\n"; $result .= "<input type='hidden' name='pscat' value='". @@ -3692,7 +3924,7 @@ sub render { if ($vars->{GRANULARITY} eq 'resource') { $result .= "<input type='hidden' name='symb' value='". HTML::Entities::encode($symb,"'<>&\"") . "' />\n"; - } elsif ($vars->{GRANULARITY} eq 'map') { + } elsif (($vars->{GRANULARITY} eq 'map') || ($vars->{GRANULARITY} eq 'maprecurse')) { $result .= "<input type='hidden' name='pschp' value='". $affectedResourceId."' />\n"; } @@ -3701,7 +3933,10 @@ sub render { $result .= "<input type='hidden' name='psprt' value='". HTML::Entities::encode($part,"'<>&\"") . "' />\n"; - $result .= '<p>'.&mt('Confirm that this information is correct, then click "Finish Helper" to complete setting the parameter.').'<ul>'; + $result .= '<p class="LC_info">' + .&mt('Confirm that this information is correct, then click "Save" to complete setting the parameter.') + .'</p>' + .'<ul>'; # Print the type of manipulation: my $extra; @@ -3749,7 +3984,7 @@ sub render { # Print targets if ($vars->{TARGETS} eq 'course') { - $result .= '<li>'.&mt('for <b>all students in course</b>').'</li>'; + $result .= '<li>'.&mt('for [_1]all students in course[_2]','<b>','</b>').'</li>'; } elsif ($vars->{TARGETS} eq 'section') { my $section = $vars->{SECTION_NAME}; $result .= '<li>'.&mt('for section [_1]',"<b>$section</b>").'</li>'; @@ -3777,7 +4012,18 @@ sub render { my $showdate = &Apache::lonlocal::locallocaltime($vars->{PARM_DATE}); $result .= '<li>'.&mt('to [_1] ([_2])',"<b>".$showdate."</b>",Apache::lonnavmaps::timeToHumanString($vars->{PARM_DATE}))."</li>\n"; } + + $result .= '</ul>'; +# 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 .= '<p><span class="LC_nobreak">' +# .'<input name="back" type="button"' +# .' value="'.$previous.'" onclick="history.go(-1)" />' + .' <input type="submit" value="'.&mt('Save').'" />' # Finish Helper + .'</span></p>'."\n"; + # print pres_marker $result .= "\n<input type='hidden' name='pres_marker'" . " value='$affectedResourceId&$parm_name&$level' />\n"; @@ -3787,7 +4033,7 @@ sub render { $result .= "\n<input type='hidden' value='$symb' name='pssymb' />"; $result .= "\n<input type='hidden' value='$paramlevel' name='parmlev' />"; - $result .= "<br /><br /><center><input type='submit' value='".&mt('Finish Helper')."' /></center></form>\n"; + $result .= $buttons; return $result; }