--- loncom/interface/slotrequest.pm 2006/10/17 15:11:26 1.69 +++ loncom/interface/slotrequest.pm 2023/07/08 17:21:33 1.125.2.10.2.1 @@ -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.69 2006/10/17 15:11:26 albertel Exp $ +# $Id: slotrequest.pm,v 1.125.2.10.2.1 2023/07/08 17:21:33 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -37,7 +37,7 @@ use Apache::lonnet; use Apache::lonnavmaps(); use Date::Manip; use lib '/home/httpd/lib/perl/'; -use LONCAPA; +use LONCAPA qw(:DEFAULT :match); sub fail { my ($r,$code)=@_; @@ -56,8 +56,50 @@ sub fail { } sub start_page { - my ($r,$title)=@_; - $r->print(&Apache::loncommon::start_page($title)); + 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 { @@ -65,6 +107,194 @@ sub end_page { $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 @@ -117,7 +347,6 @@ 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)); @@ -133,22 +362,20 @@ sub check_for_reservation { return 'error: Unable to determine current status'; } my @got; - foreach my $slot_name (sort { - if (ref($slots{$a}) && ref($slots{$b})) { - return $slots{$a}{'starttime'} <=> $slots{$b}{'starttime'} - } - if (ref($slots{$a})) { return -1;} - if (ref($slots{$b})) { return 1;} - return 0; - } @slots) { + 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'}); - if ($slots{$slot_name}->{'endtime'} > time && - $slots{$slot_name}->{'startreserve'} < time) { - # between start of reservation times and end of slot + $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 { @@ -165,8 +392,11 @@ sub check_for_reservation { 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() },1,0); + sub { $_[0]->is_problem() || $_[0]->is_tool() },1,0); my %used_slots; foreach my $problem (@problems) { my $symb = $problem->symb(); @@ -213,12 +443,19 @@ sub check_for_conflict { if (!defined($new_slot->{'uniqueperiod'})) { return undef; } if (!ref($consumed_uniqueperiods)) { - $consumed_uniqueperiods = &get_consumed_uniqueperiods($slots); - if (&Apache::lonnet::error(%$consumed_uniqueperiods)) { - return 'error: Unable to determine current status'; - } - } - + 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}}; @@ -229,19 +466,17 @@ sub check_for_conflict { } } return undef; - } sub make_reservation { - my ($slot_name,$slot,$symb)=@_; - - my ($cnum,$cdom)=&get_course(); + 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"); + 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) @@ -249,11 +484,11 @@ sub make_reservation { return 'error: Unable to determine current status'; } - my $parm_symb = $symb; + 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); - $parm_symb = &Apache::lonnet::symbread($map); + $symb_for_db = &Apache::lonnet::symbread($map); $parm_level = 2; } @@ -296,7 +531,7 @@ sub make_reservation { my %reservation=('name' => $env{'user.name'}.':'.$env{'user.domain'}, 'timestamp' => time, - 'symb' => $parm_symb); + 'symb' => $symb_for_db); my $success=&Apache::lonnet::newput('slot_reservations', {"$slot_name\0$wanted" => @@ -308,13 +543,9 @@ sub make_reservation { if ($value) { $new_value=$value.':'.$new_value; } - my $result=&Apache::lonparmset::storeparm_by_symb($symb, - '0_availablestudent', - $parm_level, $new_value, - 'string', - $env{'user.name'}, - $env{'user.domain'}); - &Apache::lonxml::debug("hrrm $result"); + 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; } @@ -322,6 +553,30 @@ sub make_reservation { 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') { @@ -334,12 +589,12 @@ sub remove_registration { my %consumed=&Apache::lonnet::dump('slot_reservations',$cdom,$cnum, "^$slot_name\0"); if (&Apache::lonnet::error(%consumed)) { - $r->print("

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

'); + $r->print("

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

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

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

"); + $r->print('

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

'); return; } @@ -347,7 +602,7 @@ sub remove_registration { my $names = join(' ',@names); my $msg = &mt('Remove all of [_1] from slot [_2]?',$names,$slot_name); - &remove_registration_confirmation($r,$msg,['entry','slotname']); + &remove_registration_confirmation($r,$msg,['entry','slotname','context']); } sub remove_registration_user { @@ -364,7 +619,7 @@ sub remove_registration_user { $name,$slot_name,$title); &remove_registration_confirmation($r,$msg,['uname','udom','slotname', - 'entry','symb']); + 'entry','symb','context']); } sub remove_registration_confirmation { @@ -376,8 +631,10 @@ sub remove_registration_confirmation { '&\'').'" />'."\n"; } - my %lt = &Apache::lonlocal::texthash('yes' => 'Yes', - 'no' => 'No',); + my %lt = &Apache::lonlocal::texthash( + 'yes' => 'Yes', + 'no' => 'No', + ); $r->print(<<"END_CONFIRM");

$msg

@@ -412,7 +669,11 @@ sub release_all_slot { my ($result,$msg) = &release_reservation($slot_name,$uname,$udom, $consumed{$entry}{'symb'},$mgr); - $r->print("

$msg

"); + if (!$result) { + $r->print('

'.&mt($msg).'

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

$msg

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

'. @@ -433,12 +694,16 @@ sub release_slot { if ($mgr eq 'F' && defined($env{'form.symb'})) { - $symb = $env{'form.symb'}; + $symb = &unescape($env{'form.symb'}); } my ($result,$msg) = &release_reservation($slot_name,$uname,$udom,$symb,$mgr); - $r->print("

$msg

"); + if (!$result) { + $r->print('

'.&mt($msg).'

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

$msg

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

'. @@ -453,59 +718,317 @@ 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"); + "^$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) ) { - &Apache::lonnet::del('slot_reservations',[$entry], - $cdom,$cnum); - } + 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"); + 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 (0,'error: Unable to determine current status'); + 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; + $parm_level = 2; } - # store new parameter string - my $result=&Apache::lonparmset::storeparm_by_symb($symb, - '0_availablestudent', - $parm_level, $new_param, - 'string', $uname, $udom); - my $msg; - if ($mgr eq 'F') { - $msg = &mt('Released Reservation for user: [_1]',"$uname:$udom"); - } else { - $msg = &mt('Released Reservation: [_1]',$description); + my @slots = split(/:/,$student); + + my @new_slots; + foreach my $exist_slot (@slots) { + next if ($exist_slot eq $slot_name); + push(@new_slots,$exist_slot); } - return (1,$msg); + 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 { @@ -525,15 +1048,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.'').'

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

'. @@ -543,28 +1066,57 @@ sub delete_slot { sub return_link { my ($r) = @_; - $r->print('

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

'); + 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)=@_; + 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(< @@ -572,74 +1124,101 @@ sub get_slot { STUFF - $r->print("

You can either "); - $r->print(< -STUFF - $r->print(' your reservation from '.$description1.' to '. - $description2. - '
or

'); - &return_link($r); + $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; + return 0; } + my ($cnum,$cdom)=&get_course(); my $reserved=&make_reservation($env{'form.slotname'}, - \%slot,$symb); + \%slot,$symb,$cnum,$cdom); my $description=&get_description($env{'form.slotname'},\%slot); if (defined($reserved)) { + my $retvalue = 0; 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) + .'

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

Success: $description

"); + $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("

Already reserved: $description

"); + $r->print('

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

'); } - &return_link($r); - return; + if (!$inhibit_return_link) { &return_link($r); } + return 1; } - my %lt=('request'=>"Availibility list", - 'try' =>'Try again'); - %lt=&Apache::lonlocal::texthash(%lt); + 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(< Failed to reserve a spot for $description.

- + + $extra_input

-?

-or +$lt{'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"); @@ -660,6 +1239,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; @@ -706,12 +1290,16 @@ sub allowed_slot { # not allowed for this resource if (defined($slot->{'symb'}) && $slot->{'symb'} ne $symb) { - return 0; + 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; } @@ -732,67 +1320,170 @@ 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 ($r,$symb,$formname,$num,$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').''; + } + $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"; + + - my $form=&mt('Unavailable'); - if ((grep(/^\Q$slot\E$/,@got_slots)) || - &space_available($slot,$slots{$slot},$symb)) { +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(''); + $r->print($output); + return; } sub to_show { @@ -876,7 +1567,7 @@ sub remove_link { $symb = &escape($symb); return <<"END_LINK"; - ($remove) END_LINK @@ -886,6 +1577,7 @@ 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); @@ -897,11 +1589,22 @@ sub show_table { '); + $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', @@ -926,8 +1629,10 @@ 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.', 'allowedsections' => 'Sections slot is restricted to.', @@ -935,9 +1640,14 @@ sub show_table { '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','secret','maxspace','ip','symb', - 'allowedsections','allowedusers','uniqueperiod', + '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') @@ -1003,31 +1713,31 @@ sub show_table { '.&mt('Slot Name Filter').' '.&mt('Options').' - '.&Apache::loncommon::multiple_select_form('show',\@show,6,\%show_fields,\@show_order). + '.&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($when,'when',\%when_fields). ' - '.&Apache::loncommon::select_form($name_filter_type, + '.&Apache::loncommon::select_form($name_filter_type, 'name_filter_type', - %name_filter_type_fields). + \%name_filter_type_fields). '
'. &Apache::lonhtmlcommon::textbox('name_filter_value', $env{'form.name_filter_value'}, 15). ' - + - - + + - +
Deleted slots:'.&mt('Deleted slots:').'
@@ -1036,24 +1746,29 @@ sub show_table { $r->print(''); $r->print('

'); my $linkstart=''.$show_fields{$which}.''); + $tableheader .= ''.$linkstart.$which.'">'.$show_fields{$which}.''; } } - $r->print(&Apache::loncommon::end_data_table_header_row()); + $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|maxspace|ip|symb|allowedsections|allowedusers)$/) { + 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); @@ -1073,26 +1788,38 @@ sub show_table { } 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'} ne 'schedulable_student') { - #next; + && $slots{$slot}->{'type'} eq 'schedulable_student') { + $reservemsg = $msgops{$slots{$slot}->{'reservationmsg'}}; } my $description=&get_description($slot,$slots{$slot}); - my $ids; - if (exists($show{'scheduled'})) { - my %consumed=&Apache::lonnet::dump('slot_reservations',$cdom,$cnum, - "^$slot\0"); - my ($tmp)=%consumed; - if ($tmp !~ /^error: /) { + 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} } - (keys(%consumed))) { + (@this_slot)) { my (undef,$id)=split("\0",$entry); my ($uname,$udom) = split(':',$consumed{$entry}{'name'}); - $ids.= ''; + $ids.= ''; foreach my $item (@stu_display_order) { if ($stu_display{$item}) { if ($item eq 'fullname') { @@ -1103,7 +1830,7 @@ sub show_table { } } $ids.=&remove_link($slot,$entry,$uname,$udom, - $consumed{$entry}{'symb'}).'
'; + $consumed{$entry}{'symb'}).'
'; } } } @@ -1114,6 +1841,8 @@ sub show_table { &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'})) { @@ -1170,26 +1899,48 @@ sub show_table { } my $proctors=join(', ',@proctors); - my $edit=(<<"EDITLINK"); -Edit + my %lt = &Apache::lonlocal::texthash ( + edit => 'Edit', + delete => 'Delete', + slotlog => 'History', + ); + my ($edit,$delete,$showlog,$remove_all); + if ($mgr) { + $edit=(<<"EDITLINK"); +$lt{'edit'} EDITLINK - my $delete=(<<"DELETELINK"); -Delete + $delete=(<<"DELETELINK"); +$lt{'delete'} DELETELINK - my $remove_all=&remove_link($slot,'remove all').'
'; + $remove_all=&remove_link($slot,'remove all').'
'; + + if ($ids eq '') { + undef($remove_all); + } else { + undef($delete); + } + } - if ($ids ne '') { undef($delete); } - if ($slots{$slot}{'type'} ne 'schedulable_student' - || $ids eq '') { + $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\n"); + "\n$edit $delete $showlog\n"); if (exists($show{'name'})) { $colspan++;$r->print("$slot"); } @@ -1208,11 +1959,24 @@ DELETELINK 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{'maxspace'})) { - $colspan++;$r->print("$slots{$slot}{'maxspace'}\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"); @@ -1241,30 +2005,803 @@ $row_end STUFF } } - $r->print(''); + 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()); - my $result.=''."\n"; - $result.='
    '."\n"; - $result.=' '. - &mt('Specify a file containing the slot definitions.'). - '
    '."\n"; - my $upfile_select=&Apache::loncommon::upfile_select_html(); - my $ignore=&mt('Ignore First Line'); - $result.=< - -$upfile_select -
    - - -ENDUPFORM - $result.='
    '."\n"; - $result.=''."\n"; - $r->print($result); + $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 { @@ -1278,14 +2815,40 @@ sub csvuploadmap_header { 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(< -

    Identify fields

    -Total number of records found in file: $distotal
    -Enter as many fields as you can. The system will inform you and bring you back -to this page if the data selected is insufficient to create the slots.
    - - @@ -1293,9 +2856,10 @@ to this page if the data selected is ins -
    ENDPICK return ''; @@ -1304,17 +2868,17 @@ ENDPICK 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, 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; @@ -1479,8 +3050,12 @@ sub csv_upload_assign { } 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'}) { @@ -1488,20 +3063,83 @@ sub csv_upload_assign { 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)=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 (defined($slot{'uniqueperiod'}) + 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; @@ -1512,14 +3150,43 @@ sub csv_upload_assign { $r->rflush(); $countdone++; } - $r->print("

    Created $countdone slots\n

    "); + 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

    "); + $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; @@ -1531,16 +3198,86 @@ sub handler { } &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'} =~ /^(showslots|uploadstart|csvuploadmap|csvuploadassign)$/ && $vgr eq 'F') { - $title = 'Managing Slots'; + 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 =[]; } - &start_page($r,$title); - - if ($env{'form.command'} eq 'showslots' && $vgr eq 'F') { + 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); @@ -1567,6 +3304,8 @@ sub handler { } &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)) { @@ -1592,17 +3331,38 @@ sub handler { return OK; } if ($env{'form.requestattempt'}) { - &show_choices($r,$symb); + $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 (&release_slot($r,$symb,$env{'form.releaseslot'},1)) { - &get_slot($r,$symb); + 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("

    Unknown command: ".$env{'form.command'}."

    "); + $r->print('

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

    '); } } &end_page($r); 500 Internal Server Error

    Internal Server Error

    The server encountered an internal error or misconfiguration and was unable to complete your request.

    Please contact the server administrator at root@localhost to inform them of the time this error occurred, and the actions you performed just before this error.

    More information about this error may be available in the server error log.