# The LearningOnline Network with CAPA # Handler for requesting to have slots added to a students record # # $Id: slotrequest.pm,v 1.125.2.10.2.2 2023/07/10 01:49:09 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # # This file is part of the LearningOnline Network with CAPA (LON-CAPA). # # LON-CAPA is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # LON-CAPA is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with LON-CAPA; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # /home/httpd/html/adm/gpl.txt # # http://www.lon-capa.org/ # ### package Apache::slotrequest; use strict; use Apache::Constants qw(:common :http :methods); use Apache::loncommon(); 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.').'

'); } elsif ($code eq 'not_available') { $r->print('

'.&mt('No slots are available.').'

'); } elsif ($code eq 'not_allowed') { $r->print('

'.&mt('Not allowed to sign up or change reservations at this time.').'

'); } else { $r->print('

'.&mt('Failed.').'

'); } &return_link($r); &end_page($r); } sub start_page { my ($r,$title,$brcrum,$bread_crumbs_component,$js,$mgr)=@_; my $args; if (ref($brcrum) eq 'ARRAY') { $args = {bread_crumbs => $brcrum}; if ($bread_crumbs_component) { $args->{bread_crumbs_component} = $bread_crumbs_component; } } if (($env{'form.requestattempt'}) || ($env{'form.command'} eq 'manageresv')) { my %loaditems = ( onload => 'javascript:uncheckSlotRadio();', ); if (ref($args) eq 'HASH') { $args->{'add_entries'} = \%loaditems; } else { $args = { 'add_entries' => \%loaditems }; } } unless (($env{'form.context'} eq 'usermanage') || (($mgr eq 'F') && (($env{'form.command'} eq 'release') || ($env{'form.command'} eq 'remove_registration')))) { if ($env{'form.symb'}) { my $symb=&unescape($env{'form.symb'}); my ($mapurl,$id,$resurl) = &Apache::lonnet::decode_symb($symb); if ($resurl =~ /ext\.tool$/) { my $target; my ($marker,$exttool) = (split(m{/},$resurl))[3,4]; $marker=~s/\D//g; if (($marker) && ($exttool) && ($env{'request.course.id'})) { my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; my ($idx,$crstool,$is_tool,%toolhash,%toolsettings); if ($resurl eq "adm/$cdom/$cnum/$marker/$exttool") { my %toolsettings=&Apache::lonnet::dump('exttool_'.$marker,$cdom,$cnum); $target = $toolsettings{'target'}; } } if ($target eq 'iframe') { $args->{'only_body'} = 1; } } } } $r->print(&Apache::loncommon::start_page($title,$js,$args)); } sub end_page { my ($r)=@_; $r->print(&Apache::loncommon::end_page()); } sub reservation_js { my ($slots,$consumed_uniqueperiods,$available,$got_slots,$symb) = @_; return unless ((ref($slots) eq 'HASH') && (ref($available) eq 'ARRAY')); my $toskip; if ($symb eq '') { $toskip = { symb => 1, }; } my ($i,$j) = (0,0); my $js; foreach my $slot (sort { return $slots->{$a}->{'starttime'} <=> $slots->{$b}->{'starttime'} } (keys(%{$slots}))) { next if (!&allowed_slot($slot,$slots->{$slot},$symb,$slots, $consumed_uniqueperiods,$toskip)); $js .= " slotstart[$i]='$slots->{$slot}->{'starttime'}';\n". " slotend[$i]='$slots->{$slot}->{'endtime'}';\n". " slotname[$i]='$slot';\n"; if (($symb) && (ref($got_slots) eq 'ARRAY')) { if (grep(/^\Q$slot\E$/,@{$got_slots})) { $js .= " currslot[$j]='$slot';\n"; $j++; } } $i++; push(@{$available},$slot); } if ($j) { $js = " var currslot = new Array($j);\n\n$js"; } my %alerts = &Apache::lonlocal::texthash ( none => 'No reservable time slots found', invalid => 'Invalid date format', ); return <<"ENDSCRIPT"; ENDSCRIPT } =pod slot_reservations db - keys are - slotname\0id -> value is an hashref of name -> user@domain of holder timestamp -> timestamp of reservation symb -> symb of resource that it is reserved for =cut sub get_course { (undef,my $courseid)=&Apache::lonnet::whichuser(); my $cdom=$env{'course.'.$courseid.'.domain'}; my $cnum=$env{'course.'.$courseid.'.num'}; return ($cnum,$cdom); } sub get_reservation_ids { my ($slot_name)=@_; my ($cnum,$cdom)=&get_course(); my %consumed=&Apache::lonnet::dump('slot_reservations',$cdom,$cnum, "^$slot_name\0"); if (&Apache::lonnet::error(%consumed)) { return 'error: Unable to determine current status'; } my ($tmp)=%consumed; if ($tmp=~/^error: 2 / ) { return 0; } return keys(%consumed); } sub space_available { my ($slot_name,$slot)=@_; my $max=$slot->{'maxspace'}; if (!defined($max)) { return 1; } my $consumed=scalar(&get_reservation_ids($slot_name)); if ($consumed < $max) { return 1 } return 0; } sub check_for_reservation { my ($symb,$mode)=@_; my $student = &Apache::lonnet::EXT("resource.0.availablestudent", $symb, $env{'user.domain'}, $env{'user.name'}); my $course = &Apache::lonnet::EXT("resource.0.available", $symb, $env{'user.domain'}, $env{'user.name'}); my @slots = (split(/:/,$student), split(/:/, $course)); &Apache::lonxml::debug(" slot list is ".join(':',@slots)); my ($cnum,$cdom)=&get_course(); my %slots=&Apache::lonnet::get('slots', [@slots], $cdom, $cnum); if (&Apache::lonnet::error($student) || &Apache::lonnet::error($course) || &Apache::lonnet::error(%slots)) { return 'error: Unable to determine current status'; } my @got; my @sorted_slots = &Apache::loncommon::sorted_slots(\@slots,\%slots,'starttime'); foreach my $slot_name (@sorted_slots) { next if (!defined($slots{$slot_name}) || !ref($slots{$slot_name})); &Apache::lonxml::debug(time." $slot_name ". $slots{$slot_name}->{'starttime'}." -- ". $slots{$slot_name}->{'startreserve'}." -- ". $slots{$slot_name}->{'endreserve'}); if (($slots{$slot_name}->{'endtime'} > time) && ($slots{$slot_name}->{'startreserve'} < time) && ((!$slots{$slot_name}->{'endreserve'}) || ($slots{$slot_name}->{'endreserve'} > time))) { # between start of reservation time and end of reservation time # and before end of slot if ($mode eq 'allslots') { push(@got,$slot_name); } else { return($slot_name, $slots{$slot_name}); } } } if ($mode eq 'allslots' && @got) { return @got; } return (undef,undef); } sub get_consumed_uniqueperiods { my ($slots) = @_; my $navmap=Apache::lonnavmaps::navmap->new; if (!defined($navmap)) { return 'error: Unable to determine current status'; } my @problems = $navmap->retrieveResources(undef, sub { $_[0]->is_problem() || $_[0]->is_tool() },1,0); my %used_slots; foreach my $problem (@problems) { my $symb = $problem->symb(); my $student = &Apache::lonnet::EXT("resource.0.availablestudent", $symb, $env{'user.domain'}, $env{'user.name'}); my $course = &Apache::lonnet::EXT("resource.0.available", $symb, $env{'user.domain'}, $env{'user.name'}); if (&Apache::lonnet::error($student) || &Apache::lonnet::error($course)) { return 'error: Unable to determine current status'; } foreach my $slot (split(/:/,$student), split(/:/, $course)) { $used_slots{$slot}=1; } } if (!ref($slots)) { my ($cnum,$cdom)=&get_course(); my %slots=&Apache::lonnet::get('slots', [keys(%used_slots)], $cdom, $cnum); if (&Apache::lonnet::error(%slots)) { return 'error: Unable to determine current status'; } $slots = \%slots; } my %consumed_uniqueperiods; foreach my $slot_name (keys(%used_slots)) { next if (!defined($slots->{$slot_name}) || !ref($slots->{$slot_name})); next if (!defined($slots->{$slot_name}{'uniqueperiod'}) || !ref($slots->{$slot_name}{'uniqueperiod'})); $consumed_uniqueperiods{$slot_name} = $slots->{$slot_name}{'uniqueperiod'}; } return \%consumed_uniqueperiods; } sub check_for_conflict { my ($symb,$new_slot_name,$new_slot,$slots,$consumed_uniqueperiods)=@_; if (!defined($new_slot->{'uniqueperiod'})) { return undef; } if (!ref($consumed_uniqueperiods)) { if ($consumed_uniqueperiods =~ /^error: /) { return $consumed_uniqueperiods; } else { $consumed_uniqueperiods = &get_consumed_uniqueperiods($slots); if (ref($consumed_uniqueperiods) eq 'HASH') { if (&Apache::lonnet::error(%$consumed_uniqueperiods)) { return 'error: Unable to determine current status'; } } else { return 'error: Unable to determine current status'; } } } my ($new_uniq_start,$new_uniq_end) = @{$new_slot->{'uniqueperiod'}}; foreach my $slot_name (keys(%$consumed_uniqueperiods)) { my ($start,$end)=@{$consumed_uniqueperiods->{$slot_name}}; if (! ($start < $new_uniq_start && $end < $new_uniq_start) || ($start > $new_uniq_end && $end > $new_uniq_end )) { return $slot_name; } } return undef; } sub make_reservation { my ($slot_name,$slot,$symb,$cnum,$cdom)=@_; my $value=&Apache::lonnet::EXT("resource.0.availablestudent",$symb, $env{'user.domain'},$env{'user.name'}); &Apache::lonxml::debug("value is $value
"); my $use_slots = &Apache::lonnet::EXT("resource.0.useslots",$symb, $env{'user.domain'},$env{'user.name'}); &Apache::lonxml::debug("use_slots is $use_slots
"); if (&Apache::lonnet::error($value) || &Apache::lonnet::error($use_slots)) { return 'error: Unable to determine current status'; } my $symb_for_db = $symb; my $parm_level = 1; if ($use_slots eq 'map' || $use_slots eq 'map_map') { my ($map) = &Apache::lonnet::decode_symb($symb); $symb_for_db = &Apache::lonnet::symbread($map); $parm_level = 2; } foreach my $other_slot (split(/:/, $value)) { if ($other_slot eq $slot_name) { my %consumed=&Apache::lonnet::dump('slot_reservations', $cdom, $cnum, "^$slot_name\0"); if (&Apache::lonnet::error($value)) { return 'error: Unable to determine current status'; } my $me=$env{'user.name'}.':'.$env{'user.domain'}; foreach my $key (keys(%consumed)) { if ($consumed{$key}->{'name'} eq $me) { my $num=(split('\0',$key))[1]; return -$num; } } } } my $max=$slot->{'maxspace'}; if (!defined($max)) { $max=99999; } my (@ids)=&get_reservation_ids($slot_name); if (&Apache::lonnet::error(@ids)) { return 'error: Unable to determine current status'; } my $last=0; foreach my $id (@ids) { my $num=(split('\0',$id))[1]; if ($num > $last) { $last=$num; } } my $wanted=$last+1; &Apache::lonxml::debug("wanted $wanted
"); if (scalar(@ids) >= $max) { # full up return undef; } my %reservation=('name' => $env{'user.name'}.':'.$env{'user.domain'}, 'timestamp' => time, 'symb' => $symb_for_db); my $success=&Apache::lonnet::newput('slot_reservations', {"$slot_name\0$wanted" => \%reservation}, $cdom, $cnum); if ($success eq 'ok') { my $new_value=$slot_name; if ($value) { $new_value=$value.':'.$new_value; } my $result = &store_slot_parm($symb,$symb_for_db,$slot_name,$parm_level, $new_value,$cnum,$cdom,$env{'user.name'}, $env{'user.domain'},'reserve',$env{'form.context'}); return $wanted; } # someone else got it return undef; } sub store_slot_parm { my ($symb_for_parm,$symb_for_db,$slot_name,$parm_level,$new_value, $cnum,$cdom,$uname,$udom,$action,$context,$delflag) = @_; # store new parameter string my $result=&Apache::lonparmset::storeparm_by_symb($symb_for_parm, '0_availablestudent', $parm_level,$new_value, 'string',$uname,$udom); &Apache::lonxml::debug("hrrm $result"); my %storehash = ( symb => $symb_for_db, slot => $slot_name, action => $action, context => $context, ); &Apache::lonnet::write_log('course','slotreservationslog',\%storehash, $delflag,$uname,$udom,$cnum,$cdom); &Apache::lonnet::write_log('course',$cdom.'_'.$cnum.'_slotlog',\%storehash, $delflag,$uname,$udom,$uname,$udom); return $result; } sub remove_registration { my ($r) = @_; if ($env{'form.entry'} ne 'remove all') { return &remove_registration_user($r); } my $slot_name = $env{'form.slotname'}; my %slot=&Apache::lonnet::get_slot($slot_name); my ($cnum,$cdom)=&get_course(); my %consumed=&Apache::lonnet::dump('slot_reservations',$cdom,$cnum, "^$slot_name\0"); if (&Apache::lonnet::error(%consumed)) { $r->print("

".&mt('A network error has occurred.').'

'); return; } if (!%consumed) { $r->print('

'.&mt('Slot [_1] has no reservations.', ''.$slot_name.'').'

'); return; } my @names = map { $consumed{$_}{'name'} } (sort(keys(%consumed))); my $names = join(' ',@names); my $msg = &mt('Remove all of [_1] from slot [_2]?',$names,$slot_name); &remove_registration_confirmation($r,$msg,['entry','slotname','context']); } sub remove_registration_user { my ($r) = @_; my $slot_name = $env{'form.slotname'}; my $name = &Apache::loncommon::plainname($env{'form.uname'}, $env{'form.udom'}); my $title = &Apache::lonnet::gettitle($env{'form.symb'}); my $msg = &mt('Remove [_1] from slot [_2] for [_3]', $name,$slot_name,$title); &remove_registration_confirmation($r,$msg,['uname','udom','slotname', 'entry','symb','context']); } sub remove_registration_confirmation { my ($r,$msg,$inputs) =@_; my $hidden_input; foreach my $parm (@{$inputs}) { $hidden_input .= '&\'').'" />'."\n"; } my %lt = &Apache::lonlocal::texthash( 'yes' => 'Yes', 'no' => 'No', ); $r->print(<<"END_CONFIRM");

$msg

$hidden_input
END_CONFIRM } sub release_all_slot { my ($r,$mgr)=@_; my $slot_name = $env{'form.slotname'}; my ($cnum,$cdom)=&get_course(); my %consumed=&Apache::lonnet::dump('slot_reservations',$cdom,$cnum, "^$slot_name\0"); $r->print('

'.&mt('Releasing reservations').'

'); foreach my $entry (sort { $consumed{$a}{'name'} cmp $consumed{$b}{'name'} } (keys(%consumed))) { my ($uname,$udom) = split(':',$consumed{$entry}{'name'}); my ($result,$msg) = &release_reservation($slot_name,$uname,$udom, $consumed{$entry}{'symb'},$mgr); if (!$result) { $r->print('

'.&mt($msg).'

'); } else { $r->print("

$msg

"); } $r->rflush(); } $r->print('

'. &mt('Return to slot list').'

'); &return_link($r); } sub release_slot { my ($r,$symb,$slot_name,$inhibit_return_link,$mgr)=@_; if ($slot_name eq '') { $slot_name=$env{'form.slotname'}; } my ($uname,$udom) = ($env{'user.name'}, $env{'user.domain'}); if ($mgr eq 'F' && defined($env{'form.uname'}) && defined($env{'form.udom'})) { ($uname,$udom) = ($env{'form.uname'}, $env{'form.udom'}); } if ($mgr eq 'F' && defined($env{'form.symb'})) { $symb = &unescape($env{'form.symb'}); } my ($result,$msg) = &release_reservation($slot_name,$uname,$udom,$symb,$mgr); if (!$result) { $r->print('

'.&mt($msg).'

'); } else { $r->print("

$msg

"); } if ($mgr eq 'F') { $r->print('

'. &mt('Return to slot list').'

'); } if (!$inhibit_return_link) { &return_link($r); } return $result; } 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 started.',$description)); } } my $context = $env{'form.context'}; # get navmap object my $navmap=Apache::lonnavmaps::navmap->new; if (!defined($navmap)) { return (0,'error: Unable to determine current status'); } my ($cnum,$cdom)=&get_course(); # get slot reservations, check if user has reservation my %consumed=&Apache::lonnet::dump('slot_reservations',$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 .= ''; 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); } } 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'); } } # 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 { $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 = ''.&mt('Released reservation: [_1]',$description).'

'; 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); } } 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 { my ($r)=@_; my $slot_name = $env{'form.slotname'}; my %slot=&Apache::lonnet::get_slot($slot_name); my ($cnum,$cdom)=&get_course(); my %consumed=&Apache::lonnet::dump('slot_reservations',$cdom,$cnum, "^$slot_name\0"); my ($tmp) = %consumed; if ($tmp =~ /error: 2/) { undef(%consumed); } if (%slot && !%consumed) { $slot{'type'} = 'deleted'; my $ret = &Apache::lonnet::cput('slots', {$slot_name => \%slot}, $cdom, $cnum); if ($ret eq 'ok') { $r->print('

'.&mt('Slot [_1] marked as deleted.',''.$slot_name.'').'

'); } else { $r->print('

'.&mt('An error occurred when attempting to delete slot: [_1]',''.$slot_name.'')." ($ret)

"); } } else { if (%consumed) { $r->print('

'.&mt('Slot [_1] has active reservations.',''.$slot_name.'').'

'); } else { $r->print('

'.&mt('Slot [_1] does not exist.',''.$slot_name.'').'

'); } } $r->print('

'. &mt('Return to slot list').'

'); &return_link($r); } sub return_link { my ($r) = @_; my $target = &return_target(); if (($env{'form.command'} eq 'manageresv') || ($env{'form.context'} eq 'usermanage')) { $r->print('

'. &mt('Return to reservations')); } else { $r->print('

'. &mt('Return to last resource').'

'); } } 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,$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('

' .&mt('An error occurred while attempting to make a reservation. ([_1])',$1) .'

'); &return_link($r); return 0; } 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); 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(< STUFF $r->print('

'.&mt('Reservation currently unchanged').'

'); if ($slot_name ne '') { $r->print('

'.&mt('To complete the transaction you [_1]must confirm[_2] you want to [_3]process the change[_4] to [_5].' ,'','','','',''.$description2.'') .'
' .&mt('Or you can choose to [_1]make no change[_2] and continue[_2] with the reservation you already had: [_3].' ,'','',''.$description1.'') .'

' .'' .(' 'x3) .'' .'

'); } $r->print(< STUFF } else { $r->print('

'.&mt('Already have a reservation: [_1].',$description1).'

'); &return_link($r); } return 0; } my ($cnum,$cdom)=&get_course(); my $reserved=&make_reservation($env{'form.slotname'}, \%slot,$symb,$cnum,$cdom); my $description=&get_description($env{'form.slotname'},\%slot); if (defined($reserved)) { my $retvalue = 0; if ($slot_name =~ /^error: (.*)/) { $r->print('

' .&mt('An error occurred while attempting to make a reservation. ([_1])',$1) .'

'); } elsif ($reserved > -1) { $r->print('

'.&mt('Successfully signed up: [_1]',$description).'

'); $retvalue = 1; my $person = &Apache::loncommon::plainname($env{'user.name'},$env{'user.domain'}); my $subject = &mt('Reservation change: [_1]',$description); my $msgbody = &mt('Successful reservation by [_1] for [_2].',$person,$description); my $msg = &slot_change_messaging($slot{'reservationmsg'},$subject,$msgbody,'reserve'); if ($msg) { $r->print($msg); } } elsif ($reserved < 0) { $r->print('

'.&mt('Already reserved: [_1]',$description).'

'); } if (!$inhibit_return_link) { &return_link($r); } return 1; } my %lt = &Apache::lonlocal::texthash( 'request' => 'Availability list', 'try' => 'Try again?', 'or' => 'or', ); my $extra_input; if ($conflictable_slot) { $extra_input=''; } $r->print('

'.&mt('[_1]Failed[_2] to reserve a slot for [_3].','','',$description).'

'); $r->print(<
$extra_input

$lt{'or'}

STUFF 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,$toskip)=@_; #already started if ($slot->{'starttime'} < time) { return 0; } &Apache::lonxml::debug("$slot_name starttime good"); #already ended if ($slot->{'endtime'} < time) { return 0; } &Apache::lonxml::debug("$slot_name endtime good"); # not allowed to pick this one if (defined($slot->{'type'}) && $slot->{'type'} ne 'schedulable_student') { return 0; } &Apache::lonxml::debug("$slot_name type good"); # reserve time not yet started 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; # its for a different set of users if (defined($slot->{'allowedsections'})) { if (!defined($env{'request.role.sec'}) && grep(/^No section assigned$/, split(',',$slot->{'allowedsections'}))) { $userallowed=1; } if (defined($env{'request.role.sec'}) && grep(/^\Q$env{'request.role.sec'}\E$/, 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"); # its for a different set of users if (defined($slot->{'allowedusers'}) && grep(/^\Q$env{'user.name'}:$env{'user.domain'}\E$/, split(',',$slot->{'allowedusers'}))) { $userallowed=1; } if (!defined($slot->{'allowedusers'}) && !defined($slot->{'allowedsections'})) { $userallowed=1; } &Apache::lonxml::debug("$slot_name user is $userallowed"); return 0 if (!$userallowed); # not allowed for this resource if (defined($slot->{'symb'}) && $slot->{'symb'} ne $symb) { unless ((ref($toskip) eq 'HASH') && ($toskip->{'symb'})) { return 0; } } my $conflict = &check_for_conflict($symb,$slot_name,$slot,$slots, $consumed_uniqueperiods); if ($conflict =~ /^error: /) { return 0; } elsif ($conflict ne '') { if ($slots->{$conflict}{'starttime'} < time) { return 0; } } &Apache::lonxml::debug("$slot_name symb good"); return 1; } sub get_description { my ($slot_name,$slot)=@_; my $description=$slot->{'description'}; if (!defined($description)) { $description=&mt('[_1] From [_2] to [_3]',$slot_name, &Apache::lonlocal::locallocaltime($slot->{'starttime'}), &Apache::lonlocal::locallocaltime($slot->{'endtime'})); } return $description; } sub show_choices { my ($r,$symb,$formname,$num,$slots,$consumed_uniqueperiods,$available,$got_slots)=@_; my $output; &Apache::lonxml::debug("Checking Slots"); 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').''; } $r->print($output); return; } 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 .= '
'.&mt('Actions').''."\n". '
'; my @options = ('all','filter'); if ($numreserved) { unshift(@options,'show'); } my %resmenu = &Apache::lonlocal::texthash ( show => 'Show current reservation', all => 'Show all', filter => 'Search by date', ); foreach my $option (@options) { my $onclick = "toggleSlotDisplay(this.form,'$num');"; if (($option eq 'show') && ($env{'form.command'} eq 'manageresv')) { $onclick .= "currSlotDisplay$num(this.form,'$num');"; } $output .= ''.(' ' x3)."\n"; } $output .= '
'; my $chooserform = 'reservationchooser_'.$num; my $starttime = $slots->{$available->[0]}->{'starttime'}; my $endtime = $slots->{$available->[-1]}->{'starttime'}; if ($env{'form.command'} eq 'manageresv') { $output .= <<"ENDSCRIPT"; ENDSCRIPT } $output .= '
'. '
'. '
'.&mt('Open after').''. &Apache::lonhtmlcommon::date_setter($chooserform,'start',$starttime,'','','','','','','',1,1). '
'.&mt('Closed before').''. &Apache::lonhtmlcommon::date_setter($chooserform,'end',$endtime,'','','','','','','',1,1). '

'. ''. '
'; } $r->print($output); return; } sub to_show { my ($slotname,$slot,$when,$deleted,$name) = @_; my $time=time; my $week=60*60*24*7; if ($deleted eq 'hide' && $slot->{'type'} eq 'deleted') { return 0; } if ($name && $name->{'value'} =~ /\w/) { if ($name->{'type'} eq 'substring') { if ($slotname !~ /\Q$name->{'value'}\E/) { return 0; } } if ($name->{'type'} eq 'exact') { if ($slotname eq $name->{'value'}) { return 0; } } } if ($when eq 'any') { return 1; } elsif ($when eq 'now') { if ($time > $slot->{'starttime'} && $time < $slot->{'endtime'}) { return 1; } return 0; } elsif ($when eq 'nextweek') { if ( ($time < $slot->{'starttime'} && ($time+$week) > $slot->{'starttime'}) || ($time < $slot->{'endtime'} && ($time+$week) > $slot->{'endtime'}) ) { return 1; } return 0; } elsif ($when eq 'lastweek') { if ( ($time > $slot->{'starttime'} && ($time-$week) < $slot->{'starttime'}) || ($time > $slot->{'endtime'} && ($time-$week) < $slot->{'endtime'}) ) { return 1; } return 0; } elsif ($when eq 'willopen') { if ($time < $slot->{'starttime'}) { return 1; } return 0; } elsif ($when eq 'wereopen') { if ($time > $slot->{'endtime'}) { return 1; } return 0; } return 1; } sub remove_link { my ($slotname,$entry,$uname,$udom,$symb) = @_; my $remove = &mt('Remove'); if ($entry eq 'remove all') { $remove = &mt('Remove All'); undef($uname); undef($udom); } $slotname = &escape($slotname); $entry = &escape($entry); $uname = &escape($uname); $udom = &escape($udom); $symb = &escape($symb); return <<"END_LINK"; ($remove) END_LINK } sub show_table { my ($r,$mgr)=@_; my ($cnum,$cdom)=&get_course(); my $crstype=&Apache::loncommon::course_type($cdom.'_'.$cnum); my %slots=&Apache::lonnet::dump('slots',$cdom,$cnum); if ( (keys(%slots))[0] =~ /^error: 2 /) { undef(%slots); } my $available; if ($mgr eq 'F') { $r->print('
'); $r->print('
'); $r->print(&Apache::loncommon::help_open_topic('Slot CommaDelimited')); $r->print('
'); $r->print(&Apache::loncommon::help_open_topic('Slot About')); $r->print('
'); } if (!keys(%slots)) { $r->print( '

' .&mt('No slots have been created in this '.lc($crstype).'.') .'

' ); return; } my %Saveable_Parameters = ('show' => 'array', 'when' => 'scalar', 'order' => 'scalar', 'deleted' => 'scalar', 'name_filter_type' => 'scalar', 'name_filter_value' => 'scalar', ); &Apache::loncommon::store_course_settings('slotrequest', \%Saveable_Parameters); &Apache::loncommon::restore_course_settings('slotrequest', \%Saveable_Parameters); &Apache::grades::init_perm(); my ($classlist,$section,$fullname)=&Apache::grades::getclasslist('all'); &Apache::grades::reset_perm(); # what to display filtering my %show_fields=&Apache::lonlocal::texthash( 'name' => 'Slot Name', 'description' => 'Description', 'type' => 'Type', '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', 'space' => '# of students/max', 'ip' => 'IP or DNS restrictions', 'symb' => 'Resource 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'); 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','endreserve','reservationmsg','secret','space', 'ip','symb','allowedsections','allowedusers','uniqueperiod', 'scheduled','proctor'); my @show = (exists($env{'form.show'})) ? &Apache::loncommon::get_env_multiple('form.show') : keys(%show_fields); my %show = map { $_ => 1 } (@show); #when filtering setup my %when_fields=&Apache::lonlocal::texthash( 'now' => 'Open now', 'nextweek' => 'Open within the next week', 'lastweek' => 'Were open last week', 'willopen' => 'Will open later', 'wereopen' => 'Were open', 'any' => 'Anytime', ); my @when_order=('any','now','nextweek','lastweek','willopen','wereopen'); $when_fields{'select_form_order'} = \@when_order; my $when = (exists($env{'form.when'})) ? $env{'form.when'} : 'now'; #display of students setup my %stu_display_fields= &Apache::lonlocal::texthash('username' => 'User name', 'fullname' => 'Full name', ); my @stu_display_order=('fullname','username'); my @stu_display = (exists($env{'form.studisplay'})) ? &Apache::loncommon::get_env_multiple('form.studisplay') : keys(%stu_display_fields); my %stu_display = map { $_ => 1 } (@stu_display); #name filtering setup my %name_filter_type_fields= &Apache::lonlocal::texthash('substring' => 'Substring', 'exact' => 'Exact', #'reg' => 'Regular Expression', ); my @name_filter_type_order=('substring','exact'); $name_filter_type_fields{'select_form_order'} = \@name_filter_type_order; my $name_filter_type = (exists($env{'form.name_filter_type'})) ? $env{'form.name_filter_type'} : 'substring'; 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('
'); $r->print('
'); $r->print('
'.&mt('Show').' '.&mt('Student Display').' '.&mt('Open').' '.&mt('Slot Name Filter').' '.&mt('Options').'
'.&Apache::loncommon::multiple_select_form('show',\@show,6,\%show_fields,\@show_order). ' '.&Apache::loncommon::multiple_select_form('studisplay',\@stu_display, 6,\%stu_display_fields, \@stu_display_order).' '.&Apache::loncommon::select_form($when,'when',\%when_fields). ' '.&Apache::loncommon::select_form($name_filter_type, 'name_filter_type', \%name_filter_type_fields). '
'. &Apache::lonhtmlcommon::textbox('name_filter_value', $env{'form.name_filter_value'}, 15). '
'.&mt('Deleted slots:').'
'); $r->print('
'); $r->print('

'); my $linkstart=''.$show_fields{$which}.''; } } $tableheader .= &Apache::loncommon::end_data_table_header_row(); my $shownheader = 0; my %name_cache; my $slotsort = sub { if ($env{'form.order'}=~/^(type|description|endtime|startreserve|endreserve|ip|symb|allowedsections|allowedusers|reservationmsg)$/) { if (lc($slots{$a}->{$env{'form.order'}}) ne lc($slots{$b}->{$env{'form.order'}})) { return (lc($slots{$a}->{$env{'form.order'}}) cmp lc($slots{$b}->{$env{'form.order'}})); } } elsif ($env{'form.order'} eq 'space') { if ($slots{$a}{'maxspace'} ne $slots{$b}{'maxspace'}) { return ($slots{$a}{'maxspace'} cmp $slots{$b}{'maxspace'}); } } elsif ($env{'form.order'} eq 'name') { if (lc($a) cmp lc($b)) { return lc($a) cmp lc($b); } } elsif ($env{'form.order'} eq 'uniqueperiod') { if ($slots{$a}->{'uniqueperiod'}[0] ne $slots{$b}->{'uniqueperiod'}[0]) { return ($slots{$a}->{'uniqueperiod'}[0] cmp $slots{$b}->{'uniqueperiod'}[0]); } if ($slots{$a}->{'uniqueperiod'}[1] ne $slots{$b}->{'uniqueperiod'}[1]) { return ($slots{$a}->{'uniqueperiod'}[1] cmp $slots{$b}->{'uniqueperiod'}[1]); } } return $slots{$a}->{'starttime'} <=> $slots{$b}->{'starttime'}; }; my %consumed; if (exists($show{'scheduled'}) || exists($show{'space'}) ) { %consumed=&Apache::lonnet::dump('slot_reservations',$cdom,$cnum); my ($tmp)=%consumed; if ($tmp =~ /^error: /) { undef(%consumed); } } my %msgops = &slot_reservationmsg_options(); foreach my $slot (sort $slotsort (keys(%slots))) { if (!&to_show($slot,$slots{$slot},$when, $env{'form.deleted'},$name_filter)) { next; } my $reservemsg; if (defined($slots{$slot}->{'type'}) && $slots{$slot}->{'type'} eq 'schedulable_student') { $reservemsg = $msgops{$slots{$slot}->{'reservationmsg'}}; } my $description=&get_description($slot,$slots{$slot}); my ($id_count,$ids); if (exists($show{'scheduled'}) || exists($show{'space'}) ) { my $re_str = "$slot\0"; my @this_slot = grep(/^\Q$re_str\E/,keys(%consumed)); $id_count = scalar(@this_slot); if (exists($show{'scheduled'})) { foreach my $entry (sort { $consumed{$a}{name} cmp $consumed{$b}{name} } (@this_slot)) { my (undef,$id)=split("\0",$entry); my ($uname,$udom) = split(':',$consumed{$entry}{'name'}); $ids.= ''; foreach my $item (@stu_display_order) { if ($stu_display{$item}) { if ($item eq 'fullname') { $ids.=$fullname->{"$uname:$udom"}.' '; } elsif ($item eq 'username') { $ids.="$uname:$udom "; } } } $ids.=&remove_link($slot,$entry,$uname,$udom, $consumed{$entry}{'symb'}).'
'; } } } my $start=($slots{$slot}->{'starttime'}? &Apache::lonlocal::locallocaltime($slots{$slot}->{'starttime'}):''); my $end=($slots{$slot}->{'endtime'}? &Apache::lonlocal::locallocaltime($slots{$slot}->{'endtime'}):''); my $start_reserve=($slots{$slot}->{'startreserve'}? &Apache::lonlocal::locallocaltime($slots{$slot}->{'startreserve'}):''); my $end_reserve=($slots{$slot}->{'endreserve'}? &Apache::lonlocal::locallocaltime($slots{$slot}->{'endreserve'}):''); my $unique; if (ref($slots{$slot}{'uniqueperiod'})) { $unique=localtime($slots{$slot}{'uniqueperiod'}[0]).', '. localtime($slots{$slot}{'uniqueperiod'}[1]); } my $title; if (exists($slots{$slot}{'symb'})) { my (undef,undef,$res)= &Apache::lonnet::decode_symb($slots{$slot}{'symb'}); $res = &Apache::lonnet::clutter($res); $title = &Apache::lonnet::gettitle($slots{$slot}{'symb'}); $title=''.$title.''; } my $allowedsections; if (exists($show{'allowedsections'})) { $allowedsections = join(', ',sort(split(/\s*,\s*/, $slots{$slot}->{'allowedsections'}))); } my @allowedusers; if (exists($show{'allowedusers'})) { @allowedusers= map { my ($uname,$udom)=split(/:/,$_); my $fullname=$name_cache{$_}; if (!defined($fullname)) { $fullname = &Apache::loncommon::plainname($uname,$udom); $fullname =~s/\s/ /g; $name_cache{$_} = $fullname; } &Apache::loncommon::aboutmewrapper($fullname,$uname,$udom); } (sort(split(/\s*,\s*/,$slots{$slot}->{'allowedusers'}))); } my $allowedusers=join(', ',@allowedusers); my @proctors; my $rowspan=1; my $colspan=1; if (exists($show{'proctor'})) { $rowspan=2; @proctors= map { my ($uname,$udom)=split(/:/,$_); my $fullname=$name_cache{$_}; if (!defined($fullname)) { $fullname = &Apache::loncommon::plainname($uname,$udom); $fullname =~s/\s/ /g; $name_cache{$_} = $fullname; } &Apache::loncommon::aboutmewrapper($fullname,$uname,$udom); } (sort(split(/\s*,\s*/,$slots{$slot}->{'proctor'}))); } my $proctors=join(', ',@proctors); my %lt = &Apache::lonlocal::texthash ( edit => 'Edit', delete => 'Delete', slotlog => 'History', ); my ($edit,$delete,$showlog,$remove_all); if ($mgr) { $edit=(<<"EDITLINK"); $lt{'edit'} EDITLINK $delete=(<<"DELETELINK"); $lt{'delete'} DELETELINK $remove_all=&remove_link($slot,'remove all').'
'; if ($ids eq '') { undef($remove_all); } else { undef($delete); } } $showlog=(<<"LOGLINK"); $lt{'slotlog'} LOGLINK if ($slots{$slot}{'type'} ne 'schedulable_student') { undef($showlog); undef($remove_all); } unless ($shownheader) { $r->print($tableheader); $shownheader = 1; } my $row_start=&Apache::loncommon::start_data_table_row(); my $row_end=&Apache::loncommon::end_data_table_row(); $r->print($row_start. "\n$edit $delete $showlog\n"); if (exists($show{'name'})) { $colspan++;$r->print("$slot"); } if (exists($show{'description'})) { $colspan++;$r->print("$description\n"); } if (exists($show{'type'})) { $colspan++;$r->print("$slots{$slot}->{'type'}\n"); } if (exists($show{'starttime'})) { $colspan++;$r->print("$start\n"); } if (exists($show{'endtime'})) { $colspan++;$r->print("$end\n"); } if (exists($show{'startreserve'})) { $colspan++;$r->print("$start_reserve\n"); } if (exists($show{'endreserve'})) { $colspan++;$r->print("$end_reserve\n"); } if (exists($show{'reservationmsg'})) { $colspan++;$r->print("$reservemsg\n"); } if (exists($show{'secret'})) { $colspan++;$r->print("$slots{$slot}{'secret'}\n"); } if (exists($show{'space'})) { my $display = $id_count; if ($slots{$slot}{'maxspace'}>0) { $display.='/'.$slots{$slot}{'maxspace'}; if ($slots{$slot}{'maxspace'} <= $id_count) { $display = ''.$display.' (full) '; } } $colspan++;$r->print("$display\n"); } if (exists($show{'ip'})) { $colspan++;$r->print("$slots{$slot}{'ip'}\n"); } if (exists($show{'symb'})) { $colspan++;$r->print("$title\n"); } if (exists($show{'allowedsections'})) { $colspan++;$r->print("$allowedsections\n"); } if (exists($show{'allowedusers'})) { $colspan++;$r->print("$allowedusers\n"); } if (exists($show{'uniqueperiod'})) { $colspan++;$r->print("$unique\n"); } if (exists($show{'scheduled'})) { $colspan++;$r->print("$remove_all $ids\n"); } $r->print("$row_end\n"); if (exists($show{'proctor'})) { $r->print(<$proctors $row_end STUFF } } if ($shownheader) { $r->print(&Apache::loncommon::end_data_table()); } else { $r->print('

'.&mt('No slots meet the criteria for display').'

'); } $r->print(''); return; } sub manage_reservations { my ($r,$crstype,$slots,$consumed_uniqueperiods,$allavailable) = @_; 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 (%parent,%shownparent,%container,%container_title,%contents); my ($depth,$count,$reservable,$lastcontainer,$rownum) = (0,0,0,0,0); my @backgrounds = ("LC_odd_row","LC_even_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"; my $shownheader = 0; my $it=$navmap->getIterator(undef,undef,undef,1,undef,undef); while (my $resource = $it->next()) { if ($resource == $it->BEGIN_MAP()) { $depth++; $parent{$depth} = $lastcontainer; } if ($resource == $it->END_MAP()) { $depth--; $lastcontainer = $parent{$depth}; } if (ref($resource)) { my $symb = $resource->symb(); my $ressymb = $symb; $contents{$lastcontainer} ++; next if (!$resource->is_problem() && !$resource->is_tool() && !$resource->is_sequence() && !$resource->is_page()); $count ++; if (($resource->is_sequence()) || ($resource->is_page())) { $lastcontainer = $count; $container{$lastcontainer} = $resource; $container_title{$lastcontainer} = $resource->compTitle(); } if ($resource->is_problem() || $resource->is_tool()) { my ($useslots) = $resource->slot_control(); next if (($useslots eq '') || ($useslots =~ /^\s*no\s*$/i)); my ($msg,$get_choices,$slotdescription); my $title = $resource->compTitle(); my $status = $resource->simpleStatus('0'); my ($slot_status,$date,$slot_name) = $resource->check_for_slot('0'); 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'); } } $reservable ++; my $treelevel = $depth; my $higherup = $lastcontainer; if ($depth > 1) { my @maprows; while ($treelevel > 1) { if (ref($container{$higherup})) { my $res = $container{$higherup}; last if (defined($shownparent{$higherup})); my $maptitle = $res->compTitle(); my $type = 'sequence'; if ($res->is_page()) { $type = 'page'; } &show_map_row($treelevel,$location,$type,$maptitle, \@maprows); $shownparent{$higherup} = 1; } $treelevel --; $higherup = $parent{$treelevel}; } foreach my $item (@maprows) { $rownum ++; my $bgcolor = $backgrounds[$rownum % $numcolors]; if (!$shownheader) { $r->print($slotheader); $shownheader = 1; } $r->print(''.$item.''."\n"); } } $rownum ++; my $bgcolor = $backgrounds[$rownum % $numcolors]; if (!$shownheader) { $r->print($slotheader); $shownheader = 1; } $r->print(''; my $hasaction; if ($status == $resource->OPEN) { if ($get_choices) { $hasaction = 1; } } if ($hasaction) { $result .= ''. ''; } $r->print($result); if ($hasaction) { my @got_slots=&check_for_reservation($symb,'allslots'); if ($got_slots[0] =~ /^error: /) { $r->print(''. &mt('An error occurred determining slot availability.'). ''); } else { my $formname = 'manageres_'.$reservable; if (ref($allavailable) eq 'ARRAY') { my @available; if (ref($slots) eq 'HASH') { foreach my $slot (@{$allavailable}) { # not allowed for this resource if (ref($slots->{$slot}) eq 'HASH') { if ((defined($slots->{$slot}->{'symb'})) && ($slots->{$slot}->{'symb'} ne $symb)) { next; } } push(@available,$slot); } } &show_choices($r,$symb,$formname,$reservable,$slots,$consumed_uniqueperiods, \@available,\@got_slots); } } $r->print(''); } $r->print(''); } } } if ($shownheader) { $r->print('
'."\n"); for (my $i=0; $i<$depth; $i++) { $r->print(''); } my $result = ''. ''.&mt('Task');
                } else {
                    $result .= 'problem.gif'.$title.''.(' ' x6).''.$msg.''; } else { $result .= ''.$msg.'
'); } 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('Reservation History').'

'); } sub show_map_row { my ($depth,$location,$type,$title,$maprows) = @_; my $output = ''; for (my $i=0; $i<$depth-1; $i++) { $output .= ''; } if ($type eq 'page') { $output .= ' '."\n"; } else { $output .= ' '."\n"; } $output .= $title.' '."\n"; unshift (@{$maprows},$output); return; } sub show_reservations { my ($r,$uname,$udom) = @_; if (!defined($uname)) { $uname = $env{'user.name'}; } if (!defined($udom)) { $udom = $env{'user.domain'}; } my $formname = 'slotlog'; my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; my $crstype = &Apache::loncommon::course_type(); my %log=&Apache::lonnet::dump('nohist_'.$cdom.'_'.$cnum.'_slotlog',$udom,$uname); if ($env{'form.origin'} eq 'aboutme') { $r->print('
'); my $name = &Apache::loncommon::plainname($env{'form.uname'},$env{'form.udom'}, 'firstname'); if ($crstype eq 'Community') { $r->print(&mt('History of member-reservable slots for: [_1]', $name)); } else { $r->print(&mt('History of student-reservable slots for: [_1]', $name)); } $r->print('
'); } $r->print('
'); # set defaults my $now = time(); my $defstart = $now - (7*24*3600); #7 days ago my %defaults = ( page => '1', show => '10', action => 'any', log_start_date => $defstart, log_end_date => $now, ); my $more_records = 0; # set current my %curr; foreach my $item ('show','page','action') { $curr{$item} = $env{'form.'.$item}; } my ($startdate,$enddate) = &Apache::lonuserutils::get_dates_from_form('log_start_date', 'log_end_date'); $curr{'log_start_date'} = $startdate; $curr{'log_end_date'} = $enddate; foreach my $key (keys(%defaults)) { if ($curr{$key} eq '') { $curr{$key} = $defaults{$key}; } } my ($version) = ($r->dir_config('lonVersion') =~ /^([\d\.]+)\-/); $r->print(&display_filter($formname,$cdom,$cnum,\%curr,$version)); my $showntablehdr = 0; my $tablehdr = &Apache::loncommon::start_data_table(). &Apache::loncommon::start_data_table_header_row(). ' '.&mt('When').''.&mt('Action').''. ''.&mt('Description').''.&mt('Start time').''. ''.&mt('End time').''.&mt('Resource').''. &Apache::loncommon::end_data_table_header_row(); my ($minshown,$maxshown); $minshown = 1; my $count = 0; if ($curr{'show'} ne &mt('all')) { $maxshown = $curr{'page'} * $curr{'show'}; if ($curr{'page'} > 1) { $minshown = 1 + ($curr{'page'} - 1) * $curr{'show'}; } } my (%titles,%maptitles); my %lt = &reservationlog_contexts($crstype); foreach my $id (sort { $log{$b}{'exe_time'}<=>$log{$a}{'exe_time'} } (keys(%log))) { next if (($log{$id}{'exe_time'} < $curr{'log_start_date'}) || ($log{$id}{'exe_time'} > $curr{'log_end_date'})); if ($curr{'show'} ne &mt('all')) { if ($count >= $curr{'page'} * $curr{'show'}) { $more_records = 1; last; } } if ($curr{'action'} ne 'any') { next if ($log{$id}{'logentry'}{'action'} ne $curr{'action'}); } $count ++; next if ($count < $minshown); if (!$showntablehdr) { $r->print($tablehdr); $showntablehdr = 1; } my $symb = $log{$id}{'logentry'}{'symb'}; my $slot_name = $log{$id}{'logentry'}{'slot'}; my %slot=&Apache::lonnet::get_slot($slot_name); my $description = $slot{'description'}; my $start = ($slot{'starttime'}? &Apache::lonlocal::locallocaltime($slot{'starttime'}):''); my $end = ($slot{'endtime'}? &Apache::lonlocal::locallocaltime($slot{'endtime'}):''); my $title = &get_resource_title($symb,\%titles,\%maptitles); my $chgaction = $log{$id}{'logentry'}{'action'}; if ($chgaction ne '' && $lt{$chgaction} ne '') { $chgaction = $lt{$chgaction}; } $r->print(&Apache::loncommon::start_data_table_row().''.$count.''.&Apache::lonlocal::locallocaltime($log{$id}{'exe_time'}).''.$chgaction.''.$description.''.$start.''.$end.''.$title.''.&Apache::loncommon::end_data_table_row()."\n"); } if ($showntablehdr) { $r->print(&Apache::loncommon::end_data_table().'
'); if (($curr{'page'} > 1) || ($more_records)) { $r->print('

'); if ($curr{'page'} > 1) { $r->print(''); } if ($more_records) { $r->print(''); } $r->print('

'); $r->print(<<"ENDSCRIPT"); ENDSCRIPT } } else { $r->print('' .&mt('There are no transactions to display.') .'' ); } $r->print(''."\n". ''."\n"); if ($env{'form.origin'} eq 'aboutme') { $r->print(''."\n". ''."\n". ''."\n"); } $r->print('
'); return; } sub show_reservations_log { my ($r) = @_; my $badslot; my $crstype = &Apache::loncommon::course_type(); if ($env{'form.slotname'} eq '') { $r->print('
'.&mt('No slot name provided').'
'); $badslot = 1; } else { my %slot=&Apache::lonnet::get_slot($env{'form.slotname'}); if (keys(%slot) == 0) { $r->print('
'.&mt('Invalid slot name: [_1]',$env{'form.slotname'}).'
'); $badslot = 1; } elsif ($slot{type} ne 'schedulable_student') { my $description = &get_description($env{'form.slotname'},\%slot); $r->print('
'); if ($crstype eq 'Community') { $r->print(&mt('Reservation history unavailable for non-member-reservable slot: [_1].',$description)); } else { $r->print(&mt('Reservation history unavailable for non-student-reservable slot: [_1].',$description)); } $r->print('
'); $badslot = 1; } } if ($badslot) { $r->print('

'. &mt('Return to slot list').'

'); return; } my $formname = 'reservationslog'; my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; my %slotlog=&Apache::lonnet::dump('nohist_slotreservationslog',$cdom,$cnum); if ((keys(%slotlog))[0]=~/^error\:/) { undef(%slotlog); } my (%log,@allsymbs); if (keys(%slotlog)) { foreach my $key (keys(%slotlog)) { if (ref($slotlog{$key}) eq 'HASH') { if (ref($slotlog{$key}{'logentry'}) eq 'HASH') { if ($slotlog{$key}{'logentry'}{'slot'} eq $env{'form.slotname'}) { $log{$key} = $slotlog{$key}; if ($slotlog{$key}{'logentry'}{'symb'} ne '') { push(@allsymbs,$slotlog{$key}{'logentry'}{'symb'}); } } } } } } $r->print('
'); my %saveable_parameters = ('show' => 'scalar',); &Apache::loncommon::store_course_settings('reservationslog', \%saveable_parameters); &Apache::loncommon::restore_course_settings('reservationslog', \%saveable_parameters); # set defaults my $now = time(); my $defstart = $now - (7*24*3600); #7 days ago my %defaults = ( page => '1', show => '10', chgcontext => 'any', action => 'any', symb => 'any', log_start_date => $defstart, log_end_date => $now, ); my $more_records = 0; # set current my %curr; foreach my $item ('show','page','chgcontext','action','symb') { $curr{$item} = $env{'form.'.$item}; } my ($startdate,$enddate) = &Apache::lonuserutils::get_dates_from_form('log_start_date', 'log_end_date'); $curr{'log_start_date'} = $startdate; $curr{'log_end_date'} = $enddate; foreach my $key (keys(%defaults)) { if ($curr{$key} eq '') { $curr{$key} = $defaults{$key}; } } my (%whodunit,%changed,$version); ($version) = ($r->dir_config('lonVersion') =~ /^([\d\.]+)\-/); my %slot=&Apache::lonnet::get_slot($env{'form.slotname'}); my $description = $slot{'description'}; $r->print(''); if ($crstype eq 'Community') { $r->print(&mt('Reservation changes for member-reservable slot: [_1]',$description)); } else { $r->print(&mt('Reservation changes for student-reservable slot: [_1]',$description)); } $r->print('
'); $r->print(&display_filter($formname,$cdom,$cnum,\%curr,$version,\@allsymbs)); my $showntablehdr = 0; my $tablehdr = &Apache::loncommon::start_data_table(). &Apache::loncommon::start_data_table_header_row(). ' '.&mt('When').''.&mt('Who made the change'). ''.&mt('Affected User').''.&mt('Action').''. ''.&mt('Resource').''.&mt('Context').''. &Apache::loncommon::end_data_table_header_row(); my ($minshown,$maxshown); $minshown = 1; my $count = 0; if ($curr{'show'} ne &mt('all')) { $maxshown = $curr{'page'} * $curr{'show'}; if ($curr{'page'} > 1) { $minshown = 1 + ($curr{'page'} - 1) * $curr{'show'}; } } my %lt = &reservationlog_contexts($crstype); my (%titles,%maptitles); foreach my $id (sort { $log{$b}{'exe_time'}<=>$log{$a}{'exe_time'} } (keys(%log))) { next if (($log{$id}{'exe_time'} < $curr{'log_start_date'}) || ($log{$id}{'exe_time'} > $curr{'log_end_date'})); if ($curr{'show'} ne &mt('all')) { if ($count >= $curr{'page'} * $curr{'show'}) { $more_records = 1; last; } } if ($curr{'chgcontext'} ne 'any') { if ($curr{'chgcontext'} eq 'user') { next if (($log{$id}{'logentry'}{'context'} ne 'user') && ($log{$id}{'logentry'}{'context'} ne 'usermanage')); } else { next if ($log{$id}{'logentry'}{'context'} ne $curr{'chgcontext'}); } } if ($curr{'action'} ne 'any') { next if ($log{$id}{'logentry'}{'action'} ne $curr{'action'}); } if ($curr{'symb'} ne 'any') { next if ($log{$id}{'logentry'}{'symb'} ne $curr{'symb'}); } $count ++; next if ($count < $minshown); if (!$showntablehdr) { $r->print($tablehdr); $showntablehdr = 1; } if ($whodunit{$log{$id}{'exe_uname'}.':'.$log{$id}{'exe_udom'}} eq '') { $whodunit{$log{$id}{'exe_uname'}.':'.$log{$id}{'exe_udom'}} = &Apache::loncommon::plainname($log{$id}{'exe_uname'},$log{$id}{'exe_udom'}); } if ($changed{$log{$id}{'uname'}.':'.$log{$id}{'udom'}} eq '') { $changed{$log{$id}{'uname'}.':'.$log{$id}{'udom'}} = &Apache::loncommon::plainname($log{$id}{'uname'},$log{$id}{'udom'}); } my $symb = $log{$id}{'logentry'}{'symb'}; my $title = &get_resource_title($symb,\%titles,\%maptitles); my $chgcontext = $log{$id}{'logentry'}{'context'}; if ($chgcontext ne '' && $lt{$chgcontext} ne '') { $chgcontext = $lt{$chgcontext}; } my $chgaction = $log{$id}{'logentry'}{'action'}; if ($chgaction ne '' && $lt{$chgaction} ne '') { $chgaction = $lt{$chgaction}; } $r->print(&Apache::loncommon::start_data_table_row().''.$count.''.&Apache::lonlocal::locallocaltime($log{$id}{'exe_time'}).''.$whodunit{$log{$id}{'exe_uname'}.':'.$log{$id}{'exe_udom'}}.''.$changed{$log{$id}{'uname'}.':'.$log{$id}{'udom'}}.''.$chgaction.''.$title.''.$chgcontext.''.&Apache::loncommon::end_data_table_row()."\n"); } if ($showntablehdr) { $r->print(&Apache::loncommon::end_data_table().'
'); if (($curr{'page'} > 1) || ($more_records)) { $r->print('

'); if ($curr{'page'} > 1) { $r->print(''); } if ($more_records) { $r->print(''); } $r->print('

'); $r->print(<<"ENDSCRIPT"); ENDSCRIPT } } else { $r->print(&mt('There are no records to display.')); } $r->print(''. ''. '
'. '

'. &mt('Return to slot list').'

'); return; } sub get_resource_title { my ($symb,$titles,$maptitles) = @_; my $title; if ((ref($titles) eq 'HASH') && (ref($maptitles) eq 'HASH')) { if (defined($titles->{$symb})) { $title = $titles->{$symb}; } else { $title = &Apache::lonnet::gettitle($symb); my $maptitle; my ($mapurl) = &Apache::lonnet::decode_symb($symb); if (defined($maptitles->{$mapurl})) { $maptitle = $maptitles->{$mapurl}; } else { if ($mapurl eq $env{'course.'.$env{'request.course.id'}.'.url'}) { $maptitle=&mt('Main Content'); } else { $maptitle=&Apache::lonnet::gettitle($mapurl); } $maptitles->{$mapurl} = $maptitle; } if ($maptitle ne '') { $title .= ' '.&mt('(in [_1])',$maptitle); } $titles->{$symb} = $title; } } else { $title = $symb; } return $title; } sub reservationlog_contexts { my ($crstype) = @_; my %lt = &Apache::lonlocal::texthash ( any => 'Any', user => 'By student', manage => 'Via Slot Manager', parameter => 'Via Parameter Manager', reserve => 'Made reservation', release => 'Dropped reservation', usermanage => 'By student', ); if ($crstype eq 'Community') { $lt{'user'} = &mt('By member'); $lt{'usermanage'} = $lt{'user'}; } return %lt; } sub display_filter { my ($formname,$cdom,$cnum,$curr,$version,$allsymbs) = @_; my $nolink = 1; my (%titles,%maptitles); my $output = '
'; my $startform = &Apache::lonhtmlcommon::date_setter($formname,'log_start_date', $curr->{'log_start_date'},undef, undef,undef,undef,undef,undef,undef,$nolink); my $endform = &Apache::lonhtmlcommon::date_setter($formname,'log_end_date', $curr->{'log_end_date'},undef, undef,undef,undef,undef,undef,undef,$nolink); my $crstype = &Apache::loncommon::course_type(); my %lt = &reservationlog_contexts($crstype); $output .= ''; if (ref($allsymbs) eq 'ARRAY') { $output .= ''; } else { $output .= ''; } $output .= '
'. ''.&mt('Changes/page:').'
'. &Apache::lonmeta::selectbox('show',$curr->{'show'},undef, (&mt('all'),5,10,20,50,100,1000,10000)). '
  '.&mt('Window during which changes occurred:'). '
'.&mt('After:'). ''.$startform.'
'.&mt('Before:').''. $endform.'
  '.&mt('Resource').'
'. '
  '. &mt('Context:').'
'.&mt('Action').'
'. '
  
'. '

'. '

'. &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('Sent to you: [_1]', $message_status).'
  • '; } if ($setting eq 'student_and_user_notes_screen') { $comment_status = &Apache::lonmsg::store_instructor_comment($subject.'
    '. $msg,$user,$domain); $comment_status = '
  • '.&mt('Entry added to course record (viewable by instructor): [_1]', $comment_status).'
  • '; } if ($message_status || $comment_status) { my $msgtitle; if ($action eq 'reserve') { $msgtitle = &mt('Status of messages about saved reservation'); } elsif ($action eq 'release') { $msgtitle = &mt('Status of messages about dropped reservation'); } elsif ($action eq 'nochange') { $msgtitle = &mt('Status of messages about unchanged existing reservation'); } return ''.$msgtitle.'' .'
      ' .$message_status .$comment_status .'

    '; } } sub upload_start { my ($r)=@_; $r->print( &Apache::grades::checkforfile_js() .'

    '.&mt('Upload a file containing the slot definitions').'

    ' .'
    ' .'' .&Apache::lonhtmlcommon::start_pick_box() .&Apache::lonhtmlcommon::row_title(&mt('File')) .&Apache::loncommon::upfile_select_html() .&Apache::lonhtmlcommon::row_closure() .&Apache::lonhtmlcommon::row_title( '') .'' .&Apache::lonhtmlcommon::row_closure(1) .&Apache::lonhtmlcommon::end_pick_box() .'

    ' .'' .'

    ' .'
    ' ); } sub csvuploadmap_header { my ($r,$datatoken,$distotal)= @_; my $javascript; if ($env{'form.upfile_associate'} eq 'reverse') { $javascript=&csvupload_javascript_reverse_associate(); } else { $javascript=&csvupload_javascript_forward_associate(); } my $checked=(($env{'form.noFirstLine'})?' checked="checked"':''); my $ignore=&mt('Ignore First Line'); my $buttontext = &mt('Reverse Association'); $r->print( '
    ' .'

    '.&mt('Identify fields in uploaded list').'

    ' .'
    ' .&Apache::loncommon::help_open_topic( 'Slot About',&mt('Help on slots')) .' '.&Apache::loncommon::help_open_topic( 'Slot SelectingField',&mt('Help on selecting Fields')) ."
    \n" .'

    ' .&mt('Total number of records found in file: [_1]',''.$distotal.'') ."

    \n" ); if ($distotal == 0) { $r->print('

    '.&mt('None found').'

    '); } $r->print( '

    ' .&mt('Enter as many fields as you can.').'
    ' .&mt('The system will inform you and bring you back to this page,[_1]if the data selected is insufficient to create the slots.','
    ') .'

    ' ); $r->print( '
    ' .'
    '.&mt('Functions').'' .'' .' ' .'

    ' ); $r->print(< ENDPICK return ''; } sub csvuploadmap_footer { my ($request,$i,$keyfields) =@_; my $buttontext = &mt('Create Slots'); $request->print(<
    ENDPICK } sub csvupload_javascript_reverse_associate { my $error1=&mt('You need to specify the name, start time, end time and a type.'); return(<[0].','; } chop($keyfields); } else { unshift(@fields,['none','']); $i=&Apache::loncommon::csv_samples_select_table($r,\@records, \@fields); my %sone=&Apache::loncommon::record_sep($records[0]); $keyfields=join(',',sort(keys(%sone))); } } &csvuploadmap_footer($r,$i,$keyfields); return ''; } sub csvupload_fields { return (['name','Slot name'], ['type','Type of slot'], ['starttime','Start Time of slot'], ['endtime','End Time of slot'], ['startreserve','Reservation Start Time'], ['endreserve','Reservation End Time'], ['reservationmsg','Message when reservation changed'], ['ip','IP or DNS restriction'], ['proctor','List of proctor ids'], ['description','Slot Description'], ['maxspace','Maximum number of reservations'], ['symb','Resource Restriction'], ['uniqueperiod','Date range of slot exclusion'], ['secret','Secret word proctor uses to validate'], ['allowedsections','Sections slot is restricted to'], ['allowedusers','Users slot is restricted to'], ); } sub csv_upload_assign { my ($r,$mgr)= @_; my $datatoken = &Apache::loncommon::valid_datatoken($env{'form.datatoken'}); if ($datatoken ne '') { &Apache::loncommon::load_tmp_file($r,$datatoken); } my @slotdata = &Apache::loncommon::upfile_record_sep(); if ($env{'form.noFirstLine'}) { shift(@slotdata); } my %fields=&Apache::grades::get_fields(); $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; my @errors; foreach my $slot (@slotdata) { my %slot; my %entries=&Apache::loncommon::record_sep($slot); my $domain; my $name=$entries{$fields{'name'}}; if ($name=~/^\s*$/) { push(@errors,"Did not create slot with no name"); next; } if ($name=~/\s/) { push(@errors,"$name not created -- Name must not contain spaces"); next; } if ($name=~/\W/) { push(@errors,"$name not created -- Name must contain only letters, numbers and _"); next; } if ($entries{$fields{'type'}}) { $slot{'type'}=$entries{$fields{'type'}}; } else { $slot{'type'}='preassigned'; } if ($slot{'type'} ne 'preassigned' && $slot{'type'} ne 'schedulable_student') { push(@errors,"$name not created -- invalid type ($slot{'type'}) must be either preassigned or schedulable_student"); next; } if ($entries{$fields{'starttime'}}) { $slot{'starttime'}=&UnixDate($entries{$fields{'starttime'}},"%s"); } 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'}}) { 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{'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)= 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); $r->print('.'); $r->rflush(); $countdone++; } if ($countdone) { &Apache::lonnet::devalidate_slots_cache($cname,$cdom); } $r->print('

    '.&mt('Created [quant,_1,slot]',$countdone)."\n".'

    '); foreach my $error (@errors) { $r->print('

    '.$error.'

    '."\n"); } &show_table($r,$mgr); return ''; } sub slot_command_titles { my %titles = ( slotlog => 'Reservation Logs', showslots => 'Manage Slots', showresv => 'Reservation History', manageresv => 'Manage Reservations', uploadstart => 'Upload Slots File', csvuploadmap => 'Upload Slots File', csvuploadassign => 'Upload Slots File', delete => 'Slot Deletion', release => 'Reservation Result', remove_reservation => 'Remove Registration', get_reservation => 'Request Reservation', ); return %titles; } sub slot_reservationmsg_options { my %options = &Apache::lonlocal::texthash ( only_student => 'Sent to student', student_and_user_notes_screen => 'Sent to student and added to user notes', none => 'None sent and no record in user notes', ); return %options; } sub handler { my $r=shift; &Apache::loncommon::content_type($r,'text/html'); &Apache::loncommon::no_cache($r); if ($r->header_only()) { $r->send_http_header(); return OK; } &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'}); my %crumb_titles = &slot_command_titles(); my ($brcrum,$bread_crumbs_component); my $vgr=&Apache::lonnet::allowed('vgr',$env{'request.course.id'}); my $mgr=&Apache::lonnet::allowed('mgr',$env{'request.course.id'}); my (%slots,$consumed_uniqueperiods); if ($env{'form.command'} eq 'showslots') { if (($vgr ne 'F') && ($mgr ne 'F')) { $env{'form.command'} = 'manageresv'; } } elsif ($env{'form.command'} eq 'manageresv') { if (($vgr eq 'F') || ($mgr eq 'F')) { $env{'form.command'} = 'showslots'; } } my $title='Requesting Another Worktime'; if ($env{'form.command'} eq 'showresv') { $title = 'Reservation History'; if ($env{'form.origin'} eq 'aboutme') { $brcrum =[{href=>"/adm/$env{'form.udom'}/$env{'form.uname'}/aboutme",text=>'Personal Information Page'}]; } else { $brcrum =[{href=>"/adm/slotrequest?command=manageresv",text=>'Manage Reservations'}]; } if (ref($brcrum) eq 'ARRAY') { push(@{$brcrum},{href=>"/adm/slotrequest?command=showresv",text=>$title}); } } elsif (($env{'form.requestattempt'}) || ($env{'form.command'} eq 'manageresv')) { if ($env{'form.command'} eq 'manageresv') { $title = 'Manage Reservations'; $brcrum =[{href=>"/adm/slotrequest?command=manageresv",text=>$title}]; } my ($cnum,$cdom)=&get_course(); %slots = &Apache::lonnet::get_course_slots($cnum,$cdom); $consumed_uniqueperiods = &get_consumed_uniqueperiods(\%slots); } elsif ($vgr eq 'F') { if ($env{'form.command'} =~ /^(slotlog|showslots|uploadstart|csvuploadmap|csvuploadassign|delete|release|remove_registration)$/) { $brcrum =[{href=>"/adm/slotrequest?command=showslots", text=>$crumb_titles{'showslots'}, help=>'Slot_Use'}]; $title = 'Managing Slots'; $bread_crumbs_component = 'Slots'; unless ($env{'form.command'} eq 'showslots') { if (ref($brcrum) eq 'ARRAY') { push(@{$brcrum},{href=>"/adm/slotrequest?command=$env{'form.command'}",text=>$crumb_titles{$env{'form.command'}}}); } } } } elsif ($env{'form.command'} eq 'release') { if ($env{'form.context'} eq 'usermanage') { $brcrum =[{href=>"/adm/slotrequest?command=manageresv", text=>$crumb_titles{'showslots'}}]; $title = 'Manage Reservations'; if (ref($brcrum) eq 'ARRAY') { push(@{$brcrum},{href=>"/adm/slotrequest?command=$env{'form.command'}",text=>$crumb_titles{$env{'form.command'}}}); } } } else { $brcrum =[]; } my ($symb,$js,$available,$allavailable,$got_slots); $available = []; if ($env{'form.requestattempt'}) { $symb=&unescape($env{'form.symb'}); @{$got_slots}=&check_for_reservation($symb,'allslots'); } if (($env{'form.requestattempt'}) || ($env{'form.command'} eq 'manageresv')) { $js = &reservation_js(\%slots,$consumed_uniqueperiods,$available,$got_slots,$symb); } &start_page($r,$title,$brcrum,$bread_crumbs_component,$js,$mgr); if ($env{'form.command'} eq 'manageresv') { $allavailable = $available; undef($available); undef($got_slots); my $crstype = &Apache::loncommon::course_type(); &manage_reservations($r,$crstype,\%slots,$consumed_uniqueperiods,$allavailable); } elsif ($env{'form.command'} eq 'showresv') { &show_reservations($r,$env{'form.uname'},$env{'form.udom'}); } elsif ($env{'form.command'} eq 'showslots' && $vgr eq 'F') { &show_table($r,$mgr); } elsif ($env{'form.command'} eq 'remove_registration' && $mgr eq 'F') { &remove_registration($r); } elsif ($env{'form.command'} eq 'release' && $mgr eq 'F') { if ($env{'form.entry'} eq 'remove all') { &release_all_slot($r,$mgr); } else { &release_slot($r,undef,undef,undef,$mgr); } } elsif ($env{'form.command'} eq 'delete' && $mgr eq 'F') { &delete_slot($r); } elsif ($env{'form.command'} eq 'uploadstart' && $mgr eq 'F') { &upload_start($r); } elsif ($env{'form.command'} eq 'csvuploadmap' && $mgr eq 'F') { &csv_upload_map($r); } elsif ($env{'form.command'} eq 'csvuploadassign' && $mgr eq 'F') { if ($env{'form.associate'} ne 'Reverse Association') { &csv_upload_assign($r,$mgr); } else { if ( $env{'form.upfile_associate'} ne 'reverse' ) { $env{'form.upfile_associate'} = 'reverse'; } else { $env{'form.upfile_associate'} = 'forward'; } &csv_upload_map($r); } } elsif (($env{'form.command'} eq 'slotlog') && ($vgr eq 'F')) { &show_reservations_log($r); } else { my $symb=&unescape($env{'form.symb'}); if (!defined($symb)) { &fail($r,'not_valid'); return OK; } my (undef,undef,$res)=&Apache::lonnet::decode_symb($symb); my $useslots = &Apache::lonnet::EXT("resource.0.useslots",$symb); if ($useslots ne 'resource' && $useslots ne 'map' && $useslots ne 'map_map') { &fail($r,'not_available'); return OK; } $env{'request.symb'}=$symb; my $type = ($res =~ /\.task$/) ? 'Task' : 'problem'; my ($status) = &Apache::lonhomework::check_slot_access('0',$type); if ($status eq 'CAN_ANSWER' || $status eq 'NEEDS_CHECKIN' || $status eq 'WAITING_FOR_GRADE') { &fail($r,'not_allowed'); return OK; } if ($env{'form.requestattempt'}) { $r->print('
    '); &show_choices($r,$symb,undef,undef,\%slots,$consumed_uniqueperiods,$available,$got_slots); $r->print('
    '); } elsif ($env{'form.command'} eq 'release') { &release_slot($r,$symb); } elsif ($env{'form.command'} eq 'get') { &get_slot($r,$symb); } elsif ($env{'form.command'} eq 'change') { if ($env{'form.nochange'}) { my $slot_name = $env{'form.releaseslot'}; my @slots = &check_for_reservation($symb,'allslots'); my $msg; if (($slot_name ne '') && (grep(/^\Q$slot_name\E/,@slots))) { my %slot=&Apache::lonnet::get_slot($env{'form.releaseslot'}); my $description=&get_description($slot_name,\%slot); $msg = ''. &mt('Unchanged reservation: [_1]',$description).'

    '; my $person = &Apache::loncommon::plainname($env{'user.name'},$env{'user.domain'}); my $subject = &mt('Reservation unchanged: [_1]',$description); my $msgbody = &mt('No change to existing registration by [_1] for [_2].',$person,$description); $msg .= &slot_change_messaging($slot{'reservationmsg'},$subject,$msgbody,'nochange'); } else { $msg = ''.&mt('Reservation no longer reported as available.').''; } $r->print($msg); &return_link($r); } elsif (&get_slot($r,$symb,$env{'form.releaseslot'},1)) { &release_slot($r,$symb,$env{'form.releaseslot'}); } } else { $r->print('

    '.&mt('Unknown command: [_1]',$env{'form.command'}).'

    '); } } &end_page($r); return OK; } 1; __END__