--- loncom/interface/slotrequest.pm 2006/04/10 07:37:16 1.57
+++ loncom/interface/slotrequest.pm 2025/04/02 23:44:01 1.152
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# Handler for requesting to have slots added to a students record
#
-# $Id: slotrequest.pm,v 1.57 2006/04/10 07:37:16 albertel Exp $
+# $Id: slotrequest.pm,v 1.152 2025/04/02 23:44:01 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -36,32 +36,369 @@ use Apache::lonlocal;
use Apache::lonnet;
use Apache::lonnavmaps();
use Date::Manip;
+use lib '/home/httpd/lib/perl/';
+use LONCAPA qw(:DEFAULT :match);
sub fail {
my ($r,$code)=@_;
if ($code eq 'not_valid') {
$r->print('
'.&mt('Unable to understand what resource you wanted to sign up for.').'
');
@@ -442,48 +829,318 @@ sub release_reservation {
my ($slot_name,$uname,$udom,$symb,$mgr) = @_;
my %slot=&Apache::lonnet::get_slot($slot_name);
my $description=&get_description($slot_name,\%slot);
+ my $msg;
if ($mgr ne 'F') {
if ($slot{'starttime'} < time) {
- return (0,&mt('Not allowed to release Reservation: [_1], as it has already ended.',$description));
+ return (0,&mt('Not allowed to release Reservation: [_1], as it has already started.',$description));
}
}
- # get parameter string, check for existance, rebuild string with the slot
- my @slots = split(/:/,&Apache::lonnet::EXT("resource.0.availablestudent",
- $symb,$udom,$uname));
+ my $context = $env{'form.context'};
- my @new_slots;
- foreach my $exist_slot (@slots) {
- if ($exist_slot eq $slot_name) { next; }
- push(@new_slots,$exist_slot);
+ # get navmap object
+ my $navmap=Apache::lonnavmaps::navmap->new;
+ if (!defined($navmap)) {
+ return (0,'error: Unable to determine current status');
}
- my $new_param = join(':',@new_slots);
my ($cnum,$cdom)=&get_course();
- # get slot reservations, check if user has one, if so remove reservation
+ # get slot reservations, check if user has reservation
my %consumed=&Apache::lonnet::dump('slot_reservations',$cdom,$cnum,
- "^$slot_name\0");
- foreach my $entry (keys(%consumed)) {
- if ( $consumed{$entry}->{'name'} eq ($uname.':'.$udom) ) {
- &Apache::lonnet::del('slot_reservations',[$entry],
- $cdom,$cnum);
- }
+ "^$slot_name\0");
+
+ #
+ # If release is because of a reservation *change*, symb(s) associated with reservation
+ # being dropped may differ from the current symb.
+ #
+ # We need to get symb(s) from slot_reservations.db, and for each symb, update
+ # the value of the availablestudent parameter, at the appropriate level
+ # (as dictated by the value of the useslots parameter for the symb and user).
+ #
+ # We also delete all entries for the slot being released, for the specific user.
+ #
+
+ my $conflict;
+
+ if (($env{'form.command'} eq 'change') && ($slot_name eq $env{'form.releaseslot'}) &&
+ ($env{'form.slotname'} ne $slot_name)) {
+ my %changedto = &Apache::lonnet::get_slot($env{'form.slotname'});
+
+ # check for conflicts
+ my ($to_uniq_start,$to_uniq_end,$from_uniq_start,$from_uniq_end);
+ if (ref($changedto{'uniqueperiod'}) eq 'ARRAY') {
+ ($to_uniq_start,$to_uniq_end) = @{$changedto{'uniqueperiod'}};
+ }
+ if (ref($slot{'uniqueperiod'}) eq 'ARRAY') {
+ ($from_uniq_start,$from_uniq_end) = @{$slot{'uniqueperiod'}};
+ }
+ my $to_start = $changedto{'starttime'};
+ my $to_end = $changedto{'endtime'};
+ my $from_start = $slot{'starttime'};
+ my $from_end = $slot{'endtime'};
+
+ if (!
+ ($from_start < $to_uniq_start && $from_end < $to_uniq_start) ||
+ ($from_start > $to_uniq_end && $from_end > $to_uniq_end )) {
+ $conflict = 1;
+ }
+ if (!
+ ($to_start < $from_uniq_start && $to_end < $from_uniq_start) ||
+ ($to_start > $from_uniq_end && $to_end > $from_uniq_end )) {
+ $conflict = 1;
+ }
+
+ if ($conflict) {
+ my %symbs_for_slot;
+ my (%to_delete,%failed,%released);
+ foreach my $entry (keys(%consumed)) {
+ if ( $consumed{$entry}->{'name'} eq ($uname.':'.$udom) ) {
+ $symbs_for_slot{$consumed{$entry}->{'symb'}} = 1;
+ $to_delete{$entry} = 1;
+ }
+ }
+ if (keys(%to_delete)) {
+ my @removals = keys(%to_delete);
+ if (&Apache::lonnet::del('slot_reservations',\@removals,
+ $cdom,$cnum) eq 'ok') {
+ foreach my $item (keys(%symbs_for_slot)) {
+ my $result = &update_selectable($navmap,$slot_name,$item,$cdom,
+ $cnum,$udom,$uname,$context);
+ if ($result =~ /^error/) {
+ $failed{$item} = 1;
+ } else {
+ $released{$item} = 1;
+ }
+ }
+ }
+ }
+ if (keys(%released)) {
+ $msg = ''.
+ &mt('Released Reservation: [_1]',$description).' '.
+ &mt('The following items had their reservation status change').':';
+ my (%folders,%pages,%container,%titles);
+ foreach my $item (keys(%released)) {
+ my $res = $navmap->getBySymb($item);
+ if (ref($res)) {
+ $titles{$item} = $res->title();
+ if ($res->is_map()) {
+ $folders{$item}{'title'} = $titles{$item};
+ if ($res->is_page()) {
+ $pages{$item}{'title'} = $titles{$item};
+ } else {
+ $folders{$item}{'title'} = $titles{$item};
+ }
+ } else {
+ my $mapsrc = $res->enclosing_map_src();
+ my $map = $navmap->getResourceByUrl($mapsrc);
+ if (ref($map)) {
+ if ($map->id() eq '0.0') {
+ $container{$mapsrc}{'title'} &mt('Top level of course');
+ } else {
+ $container{$mapsrc}{'title'} = $map->title();
+ if ($map->is_page()) {
+ $container{$mapsrc}{'page'} = 1;
+ }
+ }
+ }
+ $container{$mapsrc}{'resources'}{$item} = 1;
+ }
+ }
+ }
+ $msg .= '
';
+ }
+ $msg .= '';
+ my $person = &Apache::loncommon::plainname($env{'user.name'},$env{'user.domain'});
+ my $subject = &mt('Reservation change: [_1]',$description);
+ my $msgbody = &mt('Reservation released by [_1] for [_2].',$person,$description);
+ $msg .= &slot_change_messaging($slot{'reservationmsg'},$subject,$msgbody,'release');
+ return (1,$msg);
+ } else {
+ if (keys(%to_delete)) {
+ $msg = &mt('Reservation release partially complete for [_1]',$description);
+ } else {
+ $msg = &mt('No entries found for this user to release for [_1].',$description);
+ }
+ return (0,$msg);
+ }
+ } else {
+ $msg = &mt('No conflict found; not releasing: [_1].',$description);
+ return (0,$msg);
+ }
}
- # store new parameter string
- my $result=&Apache::lonparmset::storeparm_by_symb($symb,
- '0_availablestudent',
- 1, $new_param, 'string',
- $uname,$udom);
+ my $map_symb;
+ my $parm_symb = $symb;
+ my $passed_resource = $navmap->getBySymb($symb);
+
+ # if the reservation symb is for a map get a resource in that map
+ # to check slot parameters on
+ my $parm_level = 1;
+ if (ref($passed_resource)) {
+ if ($passed_resource->is_map()) {
+ my ($a_resource) =
+ $navmap->retrieveResources($passed_resource,
+ sub {$_[0]->is_problem() || $_[0]->is_tool() },0,1);
+ $parm_symb = $a_resource->symb();
+ }
+ } else {
+ unless ($mgr eq 'F') {
+ return (0,'error: Unable to determine current status');
+ }
+ }
- my $msg;
- if ($mgr eq 'F') {
- $msg = &mt('Released Reservation for user: [_1]',"$uname:$udom");
+ # Get value of useslots parameter in effect for this user.
+ # If value is map or map_map, then the parm level is 2 (i.e.,
+ # non-recursive enclosing map/folder level for specific user)
+ # and the symb for this reservation in slot_reservations.db
+ # will be the symb of the map itself.
+
+ my $use_slots = &Apache::lonnet::EXT('resource.0.useslots',
+ $parm_symb,$udom,$uname);
+ if (&Apache::lonnet::error($use_slots)) {
+ return (0,'error: Unable to determine current status');
+ }
+ if ($use_slots eq 'map' || $use_slots eq 'map_map') {
+ $parm_level = 2;
+ if ($passed_resource->is_map()) {
+ $map_symb = $passed_resource->symb();
+ } else {
+ my ($map) = &Apache::lonnet::decode_symb($symb);
+ $map_symb = &Apache::lonnet::symbread($map);
+ }
+ }
+
+ #
+ # If release is *not* because of a reservation change, i.e., this is a "drop"
+ # by a student, or a removal for a single student by an instructor then
+ # only remove one entry from slot_reservations.db, where both the user
+ # and the symb match the current context. If useslots was set to map or
+ # map_map, then the symb to match in slot_reservations.db is the symb of
+ # the enclosing map/folder, not the symb of the resource.
+ #
+
+ my ($match,$symb_to_check);
+ if ($parm_level == 2) {
+ $symb_to_check = $map_symb;
} else {
- $msg = &mt('Released Reservation: [_1]',$description);
+ $symb_to_check = $parm_symb;
+ }
+ foreach my $entry (keys(%consumed)) {
+ if ( $consumed{$entry}->{'name'} eq ($uname.':'.$udom) ) {
+ if ($consumed{$entry}->{'symb'} eq $symb_to_check) {
+ if (&Apache::lonnet::del('slot_reservations',[$entry],
+ $cdom,$cnum) eq 'ok') {
+ $match = $symb_to_check;
+ }
+ last;
+ }
+ }
+ }
+ if ($match) {
+ if (&update_selectable($navmap,$slot_name,$symb,$cdom,
+ $cnum,$udom,$uname,$context) =~ /^error/) {
+ if ($mgr eq 'F') {
+ $msg = &mt('Reservation release partially complete for: [_1]',"$uname:$udom").' '.
+ &mt('Update of availablestudent parameter for [_1] was not completed.',"$uname:$udom");
+ } else {
+ $msg = &mt('Release partially complete for: [_1]',$description);
+ }
+ return (0,$msg);
+ } else {
+ if ($mgr eq 'F') {
+ $msg = &mt('Released Reservation for user: [_1]',"$uname:$udom");
+ } else {
+ $msg = '
';
+ my $person = &Apache::loncommon::plainname($env{'user.name'},$env{'user.domain'});
+ my $subject = &mt('Reservation change: [_1]',$description);
+ my $msgbody = &mt('Reservation released by [_1] for [_2].',$person,$description);
+ $msg .= &slot_change_messaging($slot{'reservationmsg'},$subject,$msgbody,'release');
+ }
+ return (1,$msg);
+ }
+ } else {
+ $msg = &mt('Release failed for: [_1]',$description);
+ return (0,$msg);
}
- return (1,$msg);
+}
+
+sub update_selectable {
+ my ($navmap,$slot_name,$symb,$cdom,$cnum,$udom,$uname,$context) = @_;
+ return 'error: ' unless (ref($navmap));
+ my $symb_for_parm = $symb;
+ my $passed_resource = $navmap->getBySymb($symb);
+ return 'error: invalid symb' unless (ref($passed_resource));
+
+ # if the reservation symb is for a map get a resource in that map
+ # to check slot parameters on
+ if ($passed_resource->is_map()) {
+ my ($a_resource) =
+ $navmap->retrieveResources($passed_resource,
+ sub {$_[0]->is_problem() || $_[0]->is_tool() },0,1);
+ $symb_for_parm = $a_resource->symb();
+ }
+ # get parameter string, check for existence, rebuild string with the slot
+ my $student = &Apache::lonnet::EXT('resource.0.availablestudent',
+ $symb_for_parm,$udom,$uname);
+
+ # Get value of useslots parameter in effect for this user.
+ # If value is map or map_map, then the parm level is 2 (i.e.,
+ # non-recursive enclosing map/folder level for specific user)
+ # and the symb for this reservation in slot_reservations.db
+ # will be the symb of the map itself.
+
+ my $use_slots = &Apache::lonnet::EXT('resource.0.useslots',
+ $symb_for_parm,$udom,$uname);
+ &Apache::lonxml::debug("use_slots is $use_slots ");
+
+ if (&Apache::lonnet::error($use_slots)) {
+ return 'error: Unable to determine current status';
+ }
+
+ my $parm_level = 1;
+ if ($use_slots eq 'map' || $use_slots eq 'map_map') {
+ $parm_level = 2;
+ }
+
+ my @slots = split(/:/,$student);
+
+ my @new_slots;
+ foreach my $exist_slot (@slots) {
+ next if ($exist_slot eq $slot_name);
+ push(@new_slots,$exist_slot);
+ }
+ my $new_value = join(':',@new_slots);
+
+ my $result = &store_slot_parm($symb_for_parm,$symb,$slot_name,$parm_level,
+ $new_value,$cnum,$cdom,$uname,$udom,'release',
+ $context,1);
+ return $result;
}
sub delete_slot {
@@ -503,15 +1160,15 @@ sub delete_slot {
my $ret = &Apache::lonnet::cput('slots', {$slot_name => \%slot},
$cdom, $cnum);
if ($ret eq 'ok') {
- $r->print("
Slot $slot_name marked as deleted.
");
+ $r->print('
'.&mt('Slot [_1] marked as deleted.',''.$slot_name.'').'
');
} else {
- $r->print("
An error ($ret) occurse when attempting to delete Slot $slot_name.
");
+ $r->print('
'.&mt('An error occurred when attempting to delete slot: [_1]',''.$slot_name.'')." ($ret)
");
}
} else {
if (%consumed) {
- $r->print("
Slot $slot_name has active reservations.
");
+ $r->print('
'.&mt('Slot [_1] has active reservations.',''.$slot_name.'').'
');
} else {
- $r->print("
Slot $slot_name does not exist.
");
+ $r->print('
'.&mt('Slot [_1] does not exist.',''.$slot_name.'').'
');
+ }
+}
+
+sub return_target {
+ my ($target,$ltitarget,$deeplinktarget);
+ if ($env{'request.lti.login'}) {
+ $ltitarget = $env{'request.lti.target'};
+ }
+ if ($env{'request.deeplink.login'}) {
+ $deeplinktarget = $env{'request.deeplink.target'};
+ }
+ if (($ltitarget eq 'iframe') || ($deeplinktarget eq '_self')) {
+ $target = '_self';
+ } else {
+ $target = '_top';
+ }
+ return $target;
}
sub get_slot {
- my ($r,$symb)=@_;
+ my ($r,$symb,$conflictable_slot,$inhibit_return_link)=@_;
my %slot=&Apache::lonnet::get_slot($env{'form.slotname'});
my $slot_name=&check_for_conflict($symb,$env{'form.slotname'},\%slot);
if ($slot_name =~ /^error: (.*)/) {
- $r->print("
An error occured while attempting to make a reservation. ($1)
");
+ $r->print('
'
+ .&mt('An error occurred while attempting to make a reservation. ([_1])',$1)
+ .'
');
&return_link($r);
- return;
+ return 0;
}
- if ($slot_name) {
+ if ($slot_name && $slot_name ne $conflictable_slot) {
my %slot=&Apache::lonnet::get_slot($slot_name);
my $description1=&get_description($slot_name,\%slot);
+ my $slottype1=$slot{'type'};
%slot=&Apache::lonnet::get_slot($env{'form.slotname'});
my $description2=&get_description($env{'form.slotname'},\%slot);
- $r->print("
Already have a reservation: $description1
");
- if ($slot_name ne $env{'form.slotname'}) {
+ if ($slottype1 eq 'preassigned') {
+ $r->print('
'.&mt('You already have a reservation: "[_1]", assigned by your instructor.',
+ $description1).'
'.
+ '
'.&mt('Your instructor must unassign it before you can make a new reservation.').
+ '
');
+ } elsif ($slot_name ne $env{'form.slotname'}) {
$r->print(<
+
-
-?
-or
-
-
-or
STUFF
- &return_link($r);
- return;
+ if (!$inhibit_return_link) {
+ $r->print(&mt('or').'');
+ &return_link($r);
+ } else {
+ $r->print('');
+ }
+ return 0;
}
sub allowed_slot {
- my ($slot_name,$slot,$symb,$slots,$consumed_uniqueperiods)=@_;
+ my ($slot_name,$slot,$symb,$slots,$consumed_uniqueperiods,$toskip)=@_;
#already started
if ($slot->{'starttime'} < time) {
- # all open slot to be schedulable
- #return 0;
+ return 0;
}
&Apache::lonxml::debug("$slot_name starttime good");
@@ -638,6 +1351,11 @@ sub allowed_slot {
if ($slot->{'startreserve'} > time) {
return 0;
}
+ # reserve time ended
+ if (($slot->{'endreserve'}) &&
+ ($slot->{'endreserve'} < time)) {
+ return 0;
+ }
&Apache::lonxml::debug("$slot_name reserve good");
my $userallowed=0;
@@ -653,6 +1371,16 @@ sub allowed_slot {
split(',',$slot->{'allowedsections'}))) {
$userallowed=1;
}
+ if (defined($env{'request.course.groups'})) {
+ my @groups = split(/:/,$env{'request.course.groups'});
+ my @allowed_sec = split(',',$slot->{'allowedsections'});
+ foreach my $group (@groups) {
+ if (grep {$_ eq $group} (@allowed_sec)) {
+ $userallowed=1;
+ last;
+ }
+ }
+ }
}
&Apache::lonxml::debug("$slot_name sections is $userallowed");
@@ -672,14 +1400,40 @@ sub allowed_slot {
return 0 if (!$userallowed);
# not allowed for this resource
- if (defined($slot->{'symb'})
- && $slot->{'symb'} ne $symb) {
- return 0;
+ if (defined($slot->{'symb'})) {
+ my $exclude = 1;
+ my @symbs;
+ if ($slot->{'symb'} =~ /,/) {
+ @symbs = split(/\s*,\s*/,$slot->{'symb'});
+ } else {
+ @symbs = ($slot->{'symb'});
+ }
+ my ($map,$id,$url) = &Apache::lonnet::decode_symb($symb);
+ foreach my $reqsymb (@symbs) {
+ next if ($reqsymb eq '');
+ my ($slotmap,$slotid,$sloturl) = &Apache::lonnet::decode_symb($reqsymb);
+ if ($sloturl=~/\.(page|sequence)$/) {
+ if (($map ne '') && ($map eq $sloturl)) {
+ $exclude = 0;
+ last;
+ }
+ } elsif ($reqsymb eq $symb) {
+ $exclude = 0;
+ last;
+ }
+ }
+ if ($exclude) {
+ unless ((ref($toskip) eq 'HASH') && ($toskip->{'symb'})) {
+ return 0;
+ }
+ }
}
my $conflict = &check_for_conflict($symb,$slot_name,$slot,$slots,
$consumed_uniqueperiods);
- if ($conflict) {
+ if ($conflict =~ /^error: /) {
+ return 0;
+ } elsif ($conflict ne '') {
if ($slots->{$conflict}{'starttime'} < time) {
return 0;
}
@@ -700,67 +1454,192 @@ sub get_description {
}
sub show_choices {
- my ($r,$symb)=@_;
-
- my ($cnum,$cdom)=&get_course();
- my %slots=&Apache::lonnet::dump('slots',$cdom,$cnum);
- my $consumed_uniqueperiods = &get_consumed_uniqueperiods(\%slots);
- my $available;
- $r->print('
');
+ my ($symb,$formname,$num,$class,$slots,$consumed_uniqueperiods,$available,$got_slots)=@_;
+ my $output;
&Apache::lonxml::debug("Checking Slots");
- my @got_slots=&check_for_reservation($symb,'allslots');
- foreach my $slot (sort
- { return $slots{$a}->{'starttime'} <=> $slots{$b}->{'starttime'} }
- (keys(%slots))) {
-
- &Apache::lonxml::debug("Checking Slot $slot");
- next if (!&allowed_slot($slot,$slots{$slot},undef,\%slots,
- $consumed_uniqueperiods));
-
- $available++;
-
- my $description=&get_description($slot,$slots{$slot});
+ if (!ref($available) eq 'ARRAY') {
+ return;
+ }
+ if (!@{$available}) {
+ $output = '
'.&mt('No available times.').'
';
+ if ($env{'form.command'} ne 'manageresv') {
+ my $target = &return_target();
+ $output .= ' '.
+ &mt('Return to last resource').'';
+ }
+ if ($class) {
+ return '
'.$output.'
';
+ } else {
+ return $output;
+ }
+ } elsif ($env{'form.command'} ne 'manageresv') {
+ my $title = &Apache::lonnet::gettitle($symb);
+ my $headertext = &mt('Manage Reservation(s) for [_1]',$title);
+ $output .= '
'.$headertext.'
';
+ }
+ if (@{$available} > 1) {
+ my $numavailable = scalar(@{$available});
+ my $numreserved = 0;
+ my $js;
+ my $j = 0;
+ foreach my $got (@{$got_slots}) {
+ unless (($got eq '') || (!defined($got))) {
+ $numreserved ++;
+ if ($env{'form.command'} eq 'manageresv') {
+ $js .= " currslot[$j]='$got';\n";
+ $j++;
+ }
+ }
+ }
+ my $showfilter = 'none';
+ $output .= '';
+ }
+ if ($class) {
+ return '
'
+ .&mt('No slots have been created in this '.lc($crstype).'.')
+ .'
'
+ );
+ return;
+ }
+
my %Saveable_Parameters = ('show' => 'array',
'when' => 'scalar',
'order' => 'scalar',
@@ -894,18 +1794,26 @@ sub show_table {
'starttime' => 'Start time',
'endtime' => 'End Time',
'startreserve' => 'Time students can start reserving',
+ 'endreserve' => 'Time students can no longer reserve',
+ 'reservationmsg' => 'Message triggered by reservation',
'secret' => 'Secret Word',
- 'maxspace' => 'Maximum # of students',
+ 'space' => '# of students/max',
'ip' => 'IP or DNS restrictions',
- 'symb' => 'Resource slot is restricted to.',
+ 'symb' => 'Resource(s)/Map(s) slot is restricted to.',
'allowedsections' => 'Sections slot is restricted to.',
'allowedusers' => 'Users slot is restricted to.',
'uniqueperiod' => 'Period of time slot is unique',
'scheduled' => 'Scheduled Students',
- 'proctor' => 'List of proctors');
+ 'proctor' => 'List of proctors',
+ 'iptied' => 'Unique IP each student',);
+ if ($crstype eq 'Community') {
+ $show_fields{'startreserve'} = &mt('Time members can start reserving');
+ $show_fields{'endreserve'} = &mt('Time members can no longer reserve');
+ $show_fields{'scheduled'} = &mt('Scheduled Members');
+ }
my @show_order=('name','description','type','starttime','endtime',
- 'startreserve','secret','maxspace','ip','symb',
- 'allowedsections','allowedusers','uniqueperiod',
+ 'startreserve','endreserve','reservationmsg','secret','space',
+ 'ip','iptied','symb','allowedsections','allowedusers','uniqueperiod',
'scheduled','proctor');
my @show =
(exists($env{'form.show'})) ? &Apache::loncommon::get_env_multiple('form.show')
@@ -952,78 +1860,83 @@ sub show_table {
my $name_filter = {'type' => $name_filter_type,
'value' => $env{'form.name_filter_value'},};
+
#deleted slot filtering
+ #default to hide if no value
+ $env{'form.deleted'} ||= 'hide';
my $hide_radio =
&Apache::lonhtmlcommon::radio('deleted',$env{'form.deleted'},'hide');
my $show_radio =
&Apache::lonhtmlcommon::radio('deleted',$env{'form.deleted'},'show');
- $r->print('');
+ return;
+}
+
+sub manage_reservations {
+ my ($r,$crstype,$slots,$consumed_uniqueperiods,$allavailable) = @_;
+ my ($cnum,$cdom)=&get_course();
+ my $navmap = Apache::lonnavmaps::navmap->new();
+ $r->print('
'
+ .&mt('Instructors may use a reservation system to place restrictions on when and where assignments can be worked on.')
+ .' '
+ .&mt('One example is for management of laboratory space, which is only available at certain times, and has a limited number of seats.')
+ .'
'
+ );
+ if (!defined($navmap)) {
+ $r->print('
');
+ if ($crstype eq 'Community') {
+ $r->print(&mt('Unable to retrieve information about community contents'));
+ } else {
+ $r->print(&mt('Unable to retrieve information about course contents'));
+ }
+ $r->print('
');
+ &Apache::lonnet::logthis('Manage Reservations - could not create navmap object in '.lc($crstype).':'.$env{'request.course.id'});
+ return;
+ }
+ if (ref($consumed_uniqueperiods) eq 'HASH') {
+ if (&Apache::lonnet::error(%$consumed_uniqueperiods)) {
+ $r->print(''.
+ &mt('An error occurred determining slot availability.').
+ '');
+ return;
+ }
+ } elsif ($consumed_uniqueperiods =~ /^error: /) {
+ $r->print(''.
+ &mt('An error occurred determining slot availability.').
+ '');
+ return;
+ }
+ my (%output,%slotinfo,%statusbymap,%repsymbs,%shownmaps);
+ my @possibles = $navmap->retrieveResources(undef,
+ sub { $_[0]->is_problem() || $_[0]->is_tool() },1,0);
+
+ foreach my $resource (@possibles) {
+ my ($useslots) = $resource->slot_control();
+ next if (($useslots eq '') || ($useslots =~ /^\s*no\s*$/i));
+ my $symb = $resource->symb();
+ my ($slot_status,$date,$slot_name) = $resource->check_for_slot('0');
+ my ($msg,$get_choices,$slotdescription);
+ my $status = $resource->simpleStatus('0');
+ my ($msg,$get_choices,$slotdescription);
+ if ($slot_name ne '') {
+ my %slot=&Apache::lonnet::get_slot($slot_name);
+ $slotdescription=&get_description($slot_name,\%slot);
+ }
+ if ($slot_status == $resource->NOT_IN_A_SLOT) {
+ $msg=&mt('No current reservation.');
+ $get_choices = 1;
+ } elsif ($slot_status == $resource->NEEDS_CHECKIN) {
+ $msg=''.&mt('Reserved:').
+ ' '.$slotdescription.' '.
+ &mt('Access requires proctor validation.');
+ } elsif ($slot_status == $resource->WAITING_FOR_GRADE) {
+ $msg=&mt('Submitted and currently in grading queue.');
+ } elsif ($slot_status == $resource->CORRECT) {
+ $msg=&mt('Problem is unavailable.');
+ } elsif ($slot_status == $resource->RESERVED) {
+ $msg=''.&mt('Reserved:').
+ ' '.$slotdescription.' '.
+ &mt('Problem is currently available.');
+ } elsif ($slot_status == $resource->RESERVED_LOCATION) {
+ $msg=''.&mt('Reserved:').
+ ' '.$slotdescription.' '.
+ &mt('Problem is available at a different location.');
+ $get_choices = 1;
+ } elsif ($slot_status == $resource->RESERVED_LATER) {
+ $msg=''.&mt('Reserved:').
+ ' '.$slotdescription.' '.
+ &mt('Problem will be available later.');
+ $get_choices = 1;
+ } elsif ($slot_status == $resource->RESERVABLE) {
+ $msg=&mt('Reservation needed');
+ $get_choices = 1;
+ } elsif ($slot_status == $resource->RESERVABLE_LATER) {
+ $msg=&mt('Reservation needed: will be reservable later.');
+ } elsif ($slot_status == $resource->NOTRESERVABLE) {
+ $msg=&mt('Reservation needed: none available.');
+ } elsif ($slot_status == $resource->UNKNOWN) {
+ $msg=&mt('Unable to determine status due to network problems.');
+ } else {
+ if ($status != $resource->OPEN) {
+ $msg = &Apache::lonnavmaps::getDescription($resource,'0');
+ }
+ }
+ $output{$symb}{'msg'} = $msg;
+ if (($status == $resource->OPEN) && ($get_choices)) {
+ $output{$symb}{'hasaction'} = 1;
+ }
+ my ($mapurl,$id,$resurl) = &Apache::lonnet::decode_symb($symb);
+ $mapurl = &Apache::lonnet::clutter($mapurl);
+ unless ($mapurl =~ /default\.sequence$/) {
+ $shownmaps{$mapurl} = 1;
+ my $map = $navmap->getResourceByUrl($mapurl);
+ if (ref($map)) {
+ my @pcs = split(/,/,$map->map_hierarchy());
+ shift(@pcs);
+ shift(@pcs);
+ if (@pcs) {
+ map { $shownmaps{$navmap->getByMapPc($_)->src()} = 1; } reverse(@pcs);
+ }
+ }
+ }
+ if (($useslots eq 'map_map') || ($useslots eq 'map')) {
+ if ($slot_status ne '') {
+ if (ref($statusbymap{$mapurl}{$slot_status}) eq 'ARRAY') {
+ push(@{$statusbymap{$mapurl}{$slot_status}},$symb);
+ } else {
+ $statusbymap{$mapurl}{$slot_status} = [$symb];
+ }
+ }
+ }
+ }
+
+ foreach my $mapurl (keys(%statusbymap)) {
+ if (ref($statusbymap{$mapurl}) eq 'HASH') {
+ if (keys(%{$statusbymap{$mapurl}}) == 1) {
+ my @values = values(%{$statusbymap{$mapurl}});
+ my $repsymb = $values[0][0];
+ if (ref($output{$repsymb}) eq 'HASH') {
+ $output{$mapurl}{'msg'} = $output{$repsymb}{'msg'};
+ $output{$mapurl}{'hasaction'} = $output{$repsymb}{'hasaction'};
+ }
+ $repsymbs{$mapurl} = $repsymb;
+ }
+ }
+ }
+
+ my (%parent,%container,%container_title);
+ my ($depth,$count,$reservable,$currcontainer,$rownum,$mapnum,$shown) = (0,0,0,0,0,0,0);
+ my @backgrounds = ("LC_even_row","LC_odd_row");
+ my $numcolors = scalar(@backgrounds);
+ my $location=&Apache::loncommon::lonhttpdurl("/adm/lonIcons/whitespace_21.gif");
+ my $slotheader = '
'.
+ &mt('Your reservation status for any such assignments is listed below:').
+ '
'.
+ '
'."\n".
+ '
'.
+ '
'.&mt('Resource or Folder').'
'.
+ '
'.&mt('Reservation Status').'
'."\n";
+ my $shownheader = 0;
+ my $currmap;
+ my $it=$navmap->getIterator(undef,undef,undef,1,undef,undef);
+ while (my $resource = $it->next()) {
+ if ($resource == $it->BEGIN_MAP()) {
+ $depth++;
+ $parent{$depth} = $currcontainer;
+ if (ref($container{$currcontainer})) {
+ my $currmapres = $container{$currcontainer};
+ my $currmaptitle = $container_title{$currcontainer};
+ $currmap = $currmapres->src();
+ my $currmaptype = 'sequence';
+ if ($currmapres->is_page()) {
+ $currmaptype = 'page';
+ }
+ if ($shownmaps{$currmap}) {
+ $mapnum ++;
+ $rownum ++;
+ $shown ++;
+ if (!$shownheader) {
+ $r->print($slotheader);
+ $shownheader = 1;
+ }
+ my $bgcolor = $backgrounds[$shown % $numcolors];
+ my ($spacers,$icon);
+ my $row = '
';
+ if (ref($statusbymap{$currmap}) eq 'HASH') {
+ my ($spacers,$icon) = &show_map_row($depth-1,$location,$currmaptype,$currmaptitle);
+ my $arrowstate = 'open';
+ if (keys(%{$statusbymap{$currmap}}) == 1) {
+ $arrowstate = 'closed';
+ }
+ $row .= '
'.$spacers.''.
+ $icon.(' ' x6).'
'."\n";
+ if (ref($output{$currmap}) eq 'HASH') {
+ my $formnum = $mapnum.'_'.$reservable+1;
+ my $class = 'LC_slotmaptext_'.$mapnum;
+ if ($output{$currmap}{'hasaction'}) {
+ $row .= '
');
+ }
+ if (!$reservable) {
+ $r->print('');
+ if ($crstype eq 'Community') {
+ $r->print(&mt('No community items currently require a reservation to gain access.'));
+ } else {
+ $r->print(&mt('No course items currently require a reservation to gain access.'));
+ }
+ $r->print('');
+ }
+ $r->print('
'.
+ &mt('Only changes made from servers running LON-CAPA [_1] or later are displayed.'
+ ,'2.9.0');
+ if ($version) {
+ $output .= ' '.&mt('This LON-CAPA server is version [_1]',$version);
+ }
+ $output .= '
';
+ return $output;
+}
+
+sub slot_change_messaging {
+ my ($setting,$subject,$msg,$action) = @_;
+ my $user = $env{'user.name'};
+ my $domain = $env{'user.domain'};
+ my ($message_status,$comment_status);
+ if ($setting eq 'only_student'
+ || $setting eq 'student_and_user_notes_screen') {
+ $message_status =
+ &Apache::lonmsg::user_normal_msg($user,$domain,$subject,$msg);
+ $message_status = '
'.&mt('Upload a file containing the slot definitions').'
'
+ .''
+ );
}
sub csvuploadmap_header {
@@ -1235,14 +3115,40 @@ sub csvuploadmap_header {
my $checked=(($env{'form.noFirstLine'})?' checked="checked"':'');
my $ignore=&mt('Ignore First Line');
+ my $buttontext = &mt('Reverse Association');
+
+ $r->print(
+ '
ENDPICK
}
sub csvupload_javascript_reverse_associate {
- my $error1=&mt('You need to specify the name, starttime, endtime and a type');
+ my $error1=&mt('You need to specify the name, start time, end time and a type.');
return(<print('
Creating Slots
');
+ $r->print('
'.&mt('Creating Slots').'
');
my $cname=$env{'course.'.$env{'request.course.id'}.'.num'};
my $cdom=$env{'course.'.$env{'request.course.id'}.'.domain'};
my $countdone=0;
@@ -1420,21 +3335,120 @@ sub csv_upload_assign {
if ($entries{$fields{'endtime'}}) {
$slot{'endtime'}=&UnixDate($entries{$fields{'endtime'}},"%s");
}
+
+ # start/endtime must be defined and greater than zero
+ if (!$slot{'starttime'}) {
+ push(@errors,"$name not created -- Invalid start time");
+ next;
+ }
+ if (!$slot{'endtime'}) {
+ push(@errors,"$name not created -- Invalid end time");
+ next;
+ }
+ if ($slot{'starttime'} > $slot{'endtime'}) {
+ push(@errors,"$name not created -- Slot starts after it ends");
+ next;
+ }
+
if ($entries{$fields{'startreserve'}}) {
- $slot{'startreserve'}=
- &UnixDate($entries{$fields{'startreserve'}},"%s");
+ my $date = &UnixDate($entries{$fields{'startreserve'}},"%s");
+ if ($date eq '') {
+ push(@errors,"$name -- No reservation start time set for slot -- value provided had invalid format");
+ } else {
+ $slot{'startreserve'} = $date;
+ }
+ }
+ if (defined($slot{'startreserve'})
+ && $slot{'startreserve'} > $slot{'starttime'}) {
+ push(@errors,"$name not created -- Slot's reservation start time is after the slot's start time.");
+ next;
}
+
+ if ($entries{$fields{'endreserve'}}) {
+ my $date = &UnixDate($entries{$fields{'endreserve'}},"%s");
+ if ($date eq '') {
+ push(@errors,"$name -- No reservation end time set for slot -- value provided had invalid format");
+ } else {
+ $slot{'endreserve'} = $date;
+ }
+ }
+ if (defined($slot{'endreserve'})
+ && $slot{'endreserve'} > $slot{'starttime'}) {
+ push(@errors,"$name not created -- Slot's reservation end time is after the slot's start time.");
+ next;
+ }
+
+ if ($slot{'type'} eq 'schedulable_student') {
+ if ($entries{$fields{'reservationmsg'}}) {
+ if (($entries{$fields{'reservationmsg'}} eq 'only_student') ||
+ ($entries{$fields{'reservationmsg'}} eq 'student_and_user_notes_screen')) {
+ $slot{'reservationmsg'}=$entries{$fields{'reservationmsg'}};
+ } else {
+ unless (($entries{$fields{'reservationmsg'}} eq 'none') ||
+ ($entries{$fields{'reservationmsg'}} eq '')) {
+ push(@errors,"$name -- Slot's reservationmsg setting ignored - not one of: 'only_student', 'student_and_user_notes_screen', 'none' or ''");
+ }
+ }
+ }
+ }
+
foreach my $key ('ip','proctor','description','maxspace',
'secret','symb') {
if ($entries{$fields{$key}}) {
$slot{$key}=$entries{$fields{$key}};
+ if ($key eq 'maxspace') {
+ $slot{$key} =~ s/\D+//g;
+ }
}
}
+ if ($entries{$fields{'iptied'}} =~ /^\s*(yes|1)\s*$/i) {
+ $slot{'iptied'}='yes';
+ } elsif ($entries{$fields{'iptied'}} =~ /^\s*answer\s*$/i) {
+ $slot{'iptied'}='answer';
+ }
+ if ($entries{$fields{'allowedusers'}}) {
+ $entries{$fields{'allowedusers'}} =~ s/^\s+//;
+ $entries{$fields{'allowedusers'}} =~ s/\s+$//;
+ my @allowedusers;
+ foreach my $poss (split(/\s*,\s*/,$entries{$fields{'allowedusers'}})) {
+ my ($possuname,$possudom) = split(/:/,$poss);
+ if (($possuname =~ /^$match_username$/) && ($possudom =~ /^$match_domain$/)) {
+ unless (grep(/^\Q$poss\E$/,@allowedusers)) {
+ push(@allowedusers,$poss);
+ }
+ }
+ }
+ if (@allowedusers > 0) {
+ $slot{'allowedusers'} = join(',',@allowedusers);
+ }
+ }
+ if ($entries{$fields{'allowedsections'}}) {
+ $entries{$fields{'allowedsections'}} =~ s/^\s+//;
+ $entries{$fields{'allowedsections'}} =~ s/\s+$//;
+ my @allowedsections;
+ foreach my $poss (split(/\s*,\s*/,$entries{$fields{'allowedsections'}})) {
+ if (($poss !~ /\W/) && ($poss ne 'none')) {
+ unless (grep(/^\Q$poss\E$/,@allowedsections)) {
+ push(@allowedsections,$poss);
+ }
+ }
+ }
+ if (@allowedsections > 0) {
+ $slot{'allowedsections'} = join(',',@allowedsections);
+ }
+ }
if ($entries{$fields{'uniqueperiod'}}) {
- my ($start,$end)=split(',',$entries{$fields{'uniqueperiod'}});
- my @times=(&UnixDate($start,"%s"),
- &UnixDate($end,"%s"));
- $slot{'uniqueperiod'}=\@times;
+ my ($start,$end)= map { &UnixDate($_,"%s"); } split(',',$entries{$fields{'uniqueperiod'}});
+ if (($start ne '') && ($end ne '')) {
+ $slot{'uniqueperiod'}=[$start,$end];
+ } else {
+ push(@errors,"$name -- Slot's unique period ignored -- one or both of the comma separated values for start and end had an invalid format");
+ }
+ }
+ if (ref($slot{'uniqueperiod'}) eq 'ARRAY'
+ && $slot{'uniqueperiod'}[0] > $slot{'uniqueperiod'}[1]) {
+ push(@errors,"$name not created -- Slot's unique period start time is later than the unique period's end time.");
+ next;
}
&Apache::lonnet::cput('slots',{$name=>\%slot},$cdom,$cname);
@@ -1442,14 +3456,98 @@ sub csv_upload_assign {
$r->rflush();
$countdone++;
}
- $r->print("