--- loncom/interface/lonhelper.pm 2007/08/29 00:46:39 1.162
+++ 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.162 2007/08/29 00:46:39 albertel 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".'';
+ # 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.="
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,8 +1494,8 @@ BUTTONS
$choiceLabel = eval($choiceLabel);
$choiceLabel = &$choiceLabel($helper, $self);
}
- $result .= "/>
".qq{
";
+ $result .= " />
".qq{
";
if ($choice->[4]) {
$result .='
{VARS}->{$self->{'variable'}} = join('|||', @$chosenValue);
}
@@ -1725,7 +1735,7 @@ no strict;
use strict;
use Apache::lonlocal; # A localization nightmare
use Apache::lonnet;
-use Time::localtime;
+use DateTime;
BEGIN {
&Apache::lonhelper::register('Apache::lonhelper::date',
@@ -1775,14 +1785,13 @@ sub render {
my $time=time;
my ($anytime,$onclick);
-
# 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});
+ $date = &get_date_object($helper->{VARS}{$var});
} elsif (defined($self->{DEFAULT_VALUE})) {
my $valueFunc = eval($self->{DEFAULT_VALUE});
die('Error in default value code for variable ' .
@@ -1790,18 +1799,18 @@ sub render {
$time = &$valueFunc($helper, $self);
if (lc($time) eq 'anytime') {
$anytime=1;
- $date = localtime(time);
- $date->min(0);
+ $date = &get_date_object(time);
+ $date->set_minute(0);
} elsif (defined($time) && $time ne 0) {
- $date = localtime($time);
+ $date = &get_date_object($time);
} else {
# leave date undefined so it'll default to now
}
}
if (!defined($date)) {
- $date = localtime(time);
- $date->min(0);
+ $date = &get_date_object(time);
+ $date->set_minute(0);
}
if ($anytime) {
@@ -1817,12 +1826,12 @@ sub render {
my $i;
$result .= "\n";
@@ -1841,7 +1850,7 @@ sub render {
# Year
$result .= "\n";
}
+ $result .= ' '.$date->time_zone_short_name().' ';
if ($self->{'anytime'}) {
$result.=(<
@@ -1911,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;
@@ -1923,7 +1939,8 @@ sub postprocess {
if ($env{'form.' . $var . 'anytime'}) {
$helper->{VARS}->{$var} = undef;
} else {
- my $month = $env{'form.' . $var . 'month'};
+ my $month = $env{'form.' . $var . 'month'};
+ $month ++;
my $day = $env{'form.' . $var . 'day'};
my $year = $env{'form.' . $var . 'year'};
my $min = 0;
@@ -1933,25 +1950,40 @@ sub postprocess {
$hour = $env{'form.' . $var . 'hour'};
}
- my $chosenDate;
- eval {$chosenDate = Time::Local::timelocal(0, $min, $hour, $day, $month, $year);};
+ my ($chosenDate,$checkDate);
+ my $timezone = &Apache::lonlocal::gettimezone();
+ my $dt;
+ eval {
+ $dt = DateTime->new( year => $year,
+ month => $month,
+ day => $day,
+ hour => $hour,
+ minute => $min,
+ second => 0,
+ time_zone => $timezone,
+ );
+ };
+
my $error = $@;
+ if (!$error) {
+ $chosenDate = $dt->epoch;
+ $checkDate = &get_date_object($chosenDate);
+ }
# 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 leap year
- my $checkDate = localtime($chosenDate);
if ($error || $checkDate->mon != $month || $checkDate->mday != $day ||
- $checkDate->year + 1900 != $year) {
+ $checkDate->year != $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.";
+ $self->{ERROR_MSG} = "Can't use ".$months[$env{'form.'.$var.'month'}]. " $day, $year as a ".
+ "date because it doesn't exist. Please enter a valid date.";
return 0;
}
@@ -1974,6 +2006,20 @@ sub postprocess {
return 1;
}
+
+sub get_date_object {
+ my ($epoch) = @_;
+ my $dt = DateTime->from_epoch(epoch => $epoch)
+ ->set_time_zone(&Apache::lonlocal::gettimezone());
+ my $lang = Apache::lonlocal::current_language();
+ if ($lang ne '') {
+ eval {
+ $dt->set_locale($lang);
+ };
+ }
+ return $dt;
+}
+
1;
package Apache::lonhelper::resource;
@@ -2000,7 +2046,10 @@ 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. 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
@@ -2033,6 +2082,49 @@ than 1 part.
evaluated with "sub { my $helper = shift; my $state = shift;" and
"}", with the return value used as the mapurl.
+=item * : 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
@@ -2069,10 +2161,14 @@ 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');
}
$paramHash->{'closeallpages'} = $token->[2]{'closeallpages'};
+ $paramHash->{'include_top_level_map'} = $token->[2]{'includecourse'};
return '';
}
@@ -2161,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.
@@ -2250,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
@@ -2290,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;
}
@@ -2308,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 .=
- "
";
+ 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