--- loncom/interface/lonhelper.pm 2010/10/31 15:54:25 1.182 +++ loncom/interface/lonhelper.pm 2023/02/03 14:15:09 1.197.2.2.2.2 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # .helper XML handler to implement the LON-CAPA helper # -# $Id: lonhelper.pm,v 1.182 2010/10/31 15:54:25 raeburn Exp $ +# $Id: lonhelper.pm,v 1.197.2.2.2.2 2023/02/03 14:15:09 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 @@ -261,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; @@ -283,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; } @@ -508,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 { @@ -651,7 +659,7 @@ sub display { $result .= $buttons; - #foreach my $key (keys %{$self->{VARS}}) { + #foreach my $key (keys(%{$self->{VARS}})) { # $result .= "|$key| -> " . $self->{VARS}->{$key} . "
"; #} @@ -1792,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 { @@ -1802,7 +1810,7 @@ sub render { if (!defined($date)) { $date = &get_date_object(time); - $date->min(0); + $date->set_minute(0); } if ($anytime) { @@ -1913,7 +1921,13 @@ CHECK if ($anytime) { $result.=' checked="checked" ' } - $result.="name='${var}anytime'/>".&mt('Any time').'' + 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.'' } return $result; @@ -2033,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 @@ -2145,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'); } @@ -2351,6 +2370,7 @@ BUTTONS 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 @@ -2390,7 +2410,7 @@ BUTTONS $result .= "$text"; } } - $result .= "Select"; + $result .= ''.&Apache::lonlocal::mt('Select').''; $result .= ""; # Close off the extra row and start a new one. $headings_done = 1; } @@ -2450,7 +2470,7 @@ BUTTONS $resource_name . "'/> "; } else { $col .= - " "; } @@ -2487,9 +2507,9 @@ BUTTONS &HTML::Entities::encode(&$valueFunc($resource),"<>&\"'"); if ($addparts && (scalar(@{$resource->parts}) > 1)) { $col .= ""; } @@ -2526,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, @@ -2534,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; @@ -2606,6 +2635,12 @@ selection. Defaults to false. If true, only active students and course personnel will be shown. Defaults to false. +=item * B: + +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: If true, the selection of no users is allowed. Defaults to false. @@ -2641,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'}; @@ -2682,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 @@ -2697,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 .= '

'.&mt('Select Currently Enrolled Students and Active Course Personnel').'

'; + $result .= '

'.$titles{'active'}.'

'; $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 .= '

'.&mt('Select Future Enrolled Students and Future Course Personnel').'

'; + $result .= '

'.$titles{'future'}.'

'; $result .= &Apache::lonselstudent::render_student_list( $future_members, "helpform", @@ -2729,7 +2786,7 @@ sub render { if (!$self->{'activeonly'} && ((scalar(@$expired_members)) > 0)) { # Past - $result .= '

'.&mt('Select Previously Enrolled Students and Inactive Course Personnel').'

'; + $result .= '

'.$titles{'expired'}.'

'; $result .= &Apache::lonselstudent::render_student_list($expired_members, "helpform", "past", @@ -2971,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; @@ -3089,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; @@ -3139,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 @@ -3177,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]; @@ -3228,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; @@ -3257,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'}; } @@ -3264,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]); } @@ -3626,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}}) {