--- loncom/interface/slotrequest.pm 2009/08/05 16:12:53 1.101 +++ loncom/interface/slotrequest.pm 2016/10/24 01:13:54 1.125.2.3 @@ -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.101 2009/08/05 16:12:53 bisitz Exp $ +# $Id: slotrequest.pm,v 1.125.2.3 2016/10/24 01:13:54 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,12 +56,22 @@ sub fail { } sub start_page { - my ($r,$title,$brcrum)=@_; + my ($r,$title,$brcrum,$js)=@_; my $args; if (ref($brcrum) eq 'ARRAY') { $args = {bread_crumbs => $brcrum}; } - $r->print(&Apache::loncommon::start_page($title,undef,$args)); + 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 }; + } + } + $r->print(&Apache::loncommon::start_page($title,$js,$args)); } sub end_page { @@ -69,6 +79,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"; +<script type="text/javascript"> +// <![CDATA[ +function updateSlotDisplay(form,num,slotpickradio) { + var slotstart = new Array($i); + var slotend = new Array($i); + var slotname = new Array($i); +$js + + if (slotpickradio == 'all') { + for (var i=0; i<$i; i++) { + if (document.getElementById('LC_slotrow_'+num+'_'+slotname[i])) { + document.getElementById('LC_slotrow_'+num+'_'+slotname[i]).style.display = ''; + } + if (document.getElementById('LC_slotsearch_'+num)) { + document.getElementById('LC_slotsearch_'+num).style.display = 'block'; + } + } + } else { + if (slotpickradio == 'show') { + for (var i=0; i<$i; i++) { + if (document.getElementById('LC_slotrow_'+num+'_'+slotname[i])) { + document.getElementById('LC_slotrow_'+num+'_'+slotname[i]).style.display = 'none'; + } + } + for (var j=0; j<$j; j++) { + if (document.getElementById('LC_slotrow_'+num+'_'+currslot[j])) { + document.getElementById('LC_slotrow_'+num+'_'+currslot[j]).style.display = ''; + } + } + if (document.getElementById('LC_slotsearch_'+num)) { + document.getElementById('LC_slotsearch_'+num).style.display = 'block'; + } + } else { + var numberRegExp = /^[0-9]+\$/; + var startm = form.start_month.options[form.start_month.selectedIndex].value; + var startd = form.start_day.value; + startd=startd.trim(); + var starty = form.start_year.value; + starty=starty.trim(); + var endm = form.end_month.options[form.end_month.selectedIndex].value; + var endd = form.end_day.value; + endd=endd.trim(); + var endy = form.end_year.value; + endy=endy.trim(); + if (numberRegExp.test(endd) && numberRegExp.test(endy) && numberRegExp.test(startd) && numberRegExp.test(starty)) { + var startdate = startm+"/"+startd+"/"+starty; + var starttime = new Date(startdate).getTime(); + starttime = starttime/1000; + var starth = form.start_hour.options[form.start_hour.selectedIndex].value; + if (numberRegExp.test(starth)) { + starth = parseInt(starth); + if (starth > 0 && starth <= 23) { + starttime += 3600 * starth; + } + } + var enddate = endm+"/"+endd+"/"+endy; + var endtime = new Date(enddate).getTime(); + endtime = endtime/1000; + var endh = form.end_hour.options[form.end_hour.selectedIndex].value; + if (numberRegExp.test(endh)) { + endh = parseInt(endh); + if (endh > 0 && endh <= 23) { + endtime += 3600 * endh; + } + } + + var shown = 0; + for (var i=0; i<$i; i++) { + if ((slotstart[i] >= starttime) && (slotend[i] <= endtime)) { + if (document.getElementById('LC_slotrow_'+num+'_'+slotname[i])) { + document.getElementById('LC_slotrow_'+num+'_'+slotname[i]).style.display = ''; + shown ++; + } + } else { + if (document.getElementById('LC_slotrow_'+num+'_'+slotname[i])) { + document.getElementById('LC_slotrow_'+num+'_'+slotname[i]).style.display = 'none'; + } + } + } + if (document.getElementById('LC_slotsearch_'+num)) { + if (shown) { + document.getElementById('LC_slotsearch_'+num).style.display = 'block'; + } else { + document.getElementById('LC_slotsearch_'+num).style.display = 'none'; + } + } + if (shown == 0) { + alert('$alerts{"none"}'); + } + } else { + alert('$alerts{"invalid"}'); + } + } + } + return; +} + +function toggleSlotDisplay(form,num) { + if (form.slotpick.length) { + for (var i=0; i<form.slotpick.length; i++) { + if (form.slotpick[i].checked) { + var val = form.slotpick[i].value; + if (document.getElementById('LC_slotfilter_'+num)) { + document.getElementById('LC_slotsearch_'+num).style.display = 'none'; + if (val == 'filter') { + document.getElementById('LC_slotfilter_'+num).style.display = 'block'; + } else { + document.getElementById('LC_slotfilter_'+num).style.display = 'none'; + if (val == 'all') { + updateSlotDisplay(form,num,val); + } else { + updateSlotDisplay(form,num,val); + } + } + } + break; + } + } + } + return false; +} + +if (!document.getElementsByClassName) { + function getElementsByClassName(node, classname) { + var a = []; + var re = new RegExp('(^| )'+classname+'( |$)'); + var els = node.getElementsByTagName("*"); + for(var i=0,j=els.length; i<j; i++) + if(re.test(els[i].className))a.push(els[i]); + return a; + } +} + +function uncheckSlotRadio() { + var slotpicks; + if (document.getElementsByClassName) { + slotpicks = document.getElementsByClassName('LC_slotpick_radio'); + } else { + slotpicks = getElementsByClassName(document.body,'LC_slotpick_radio'); + } + if (slotpicks.length) { + for (var i=0; i<slotpicks.length; i++) { + slotpicks[i].checked = false; + } + } +} +// ]]> +</script> +ENDSCRIPT + +} + + =pod slot_reservations db @@ -121,7 +319,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)); @@ -137,16 +334,20 @@ sub check_for_reservation { return 'error: Unable to determine current status'; } my @got; - my @sorted_slots = &Apache::loncommon::sorted_slots(\@slots,\%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 { @@ -214,16 +415,19 @@ sub check_for_conflict { if (!defined($new_slot->{'uniqueperiod'})) { return undef; } if (!ref($consumed_uniqueperiods)) { - $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'; - } + if ($consumed_uniqueperiods =~ /^error: /) { + return $consumed_uniqueperiods; } else { - return 'error: Unable to determine current status'; + $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}}; @@ -335,12 +539,12 @@ sub store_slot_parm { context => $env{'form.context'}, ); - &Apache::lonnet::instructor_log('slotreservationslog',\%storehash, - '',$env{'user.name'},$env{'user.domain'}, - $cnum,$cdom); - &Apache::lonnet::instructor_log($cdom.'_'.$cnum.'_slotlog',\%storehash, - 1,$env{'user.name'},$env{'user.domain'}, - $env{'user.name'},$env{'user.domain'}); + &Apache::lonnet::write_log('course','slotreservationslog',\%storehash, + '',$env{'user.name'},$env{'user.domain'}, + $cnum,$cdom); + &Apache::lonnet::write_log('course',$cdom.'_'.$cnum.'_slotlog',\%storehash, + 1,$env{'user.name'},$env{'user.domain'}, + $env{'user.name'},$env{'user.domain'}); return; } @@ -500,14 +704,20 @@ sub release_reservation { return (0,'error: Unable to determine current status'); } my $passed_resource = $navmap->getBySymb($symb); - if ($passed_resource->is_map()) { - my ($a_resource) = - $navmap->retrieveResources($passed_resource, - sub {$_[0]->is_problem()},0,1); - $symb = $a_resource->symb(); + if (ref($passed_resource)) { + if ($passed_resource->is_map()) { + my ($a_resource) = + $navmap->retrieveResources($passed_resource, + sub {$_[0]->is_problem()},0,1); + $symb = $a_resource->symb(); + } + } else { + unless ($mgr eq 'F') { + return (0,'error: Unable to determine current status'); + } } - # get parameter string, check for existance, rebuild string with the slot + # get parameter string, check for existence, rebuild string with the slot my $student = &Apache::lonnet::EXT("resource.0.availablestudent", $symb,$udom,$uname); my @slots = split(/:/,$student); @@ -534,10 +744,10 @@ sub release_reservation { action => 'release', context => $env{'form.context'}, ); - &Apache::lonnet::instructor_log('slotreservationslog',\%storehash, - 1,$uname,$udom,$cnum,$cdom); - &Apache::lonnet::instructor_log($cdom.'_'.$cnum.'_slotlog',\%storehash, - 1,$uname,$udom,$uname,$udom); + &Apache::lonnet::write_log('slotreservationslog',\%storehash, + 1,$uname,$udom,$cnum,$cdom); + &Apache::lonnet::write_log($cdom.'_'.$cnum.'_slotlog',\%storehash, + 1,$uname,$udom,$uname,$udom); } } @@ -562,7 +772,11 @@ sub release_reservation { if ($mgr eq 'F') { $msg = &mt('Released Reservation for user: [_1]',"$uname:$udom"); } else { - $msg = &mt('Released Reservation: [_1]',$description); + $msg = '<span style="font-weight: bold;">'.&mt('Released reservation: [_1]',$description).'</span><br /><br />'; + 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); } @@ -629,7 +843,6 @@ sub get_slot { my $description1=&get_description($slot_name,\%slot); %slot=&Apache::lonnet::get_slot($env{'form.slotname'}); my $description2=&get_description($env{'form.slotname'},\%slot); - $r->print('<p>'.&mt('Already have a reservation: [_1].',$description1).'</p>'); if ($slot_name ne $env{'form.slotname'}) { $r->print(<<STUFF); <form method="post" action="/adm/slotrequest"> @@ -638,19 +851,24 @@ sub get_slot { <input type="hidden" name="releaseslot" value="$slot_name" /> <input type="hidden" name="command" value="change" /> STUFF - $r->print('<p>' - .&mt('You can either [_1]Change[_2] your reservation from [_3] to [_4] or' - ,'<input type="submit" name="change" value="' - ,'" />' - ,'<b>'.$description1.'</b>' - ,'<b>'.$description2.'</b>') - .'<br /></p>' - ); - &return_link($r); + $r->print('<p class="LC_error">'.&mt('Reservation currently unchanged').'</p>'); + if ($slot_name ne '') { + $r->print('<p>'.&mt('To complete the transaction you [_1]must confirm[_2] you want to [_3]process the change[_4] to [_5].' + ,'<b>','</b>','<i>','</i>','<b>'.$description2.'</b>') + .'<br />' + .&mt('Or you can choose to [_1]make no change[_2] and continue[_2] with the reservation you already had: [_3].' + ,'<i>','</i>','<b>'.$description1.'</b>') + .'</p><p><span class="LC_nobreak">' + .'<input type="submit" name="change" value="'.&mt('Process the change').'" />' + .(' 'x3) + .'<input type="submit" name="nochange" value="'.&mt('Make no change').'" />' + .'</span></p>'); + } $r->print(<<STUFF); </form> STUFF } else { + $r->print('<p>'.&mt('Already have a reservation: [_1].',$description1).'</p>'); &return_link($r); } return 0; @@ -667,8 +885,15 @@ STUFF .&mt('An error occurred while attempting to make a reservation. ([_1])',$1) .'</span></p>'); } elsif ($reserved > -1) { - $r->print('<p>'.&mt('Success: [_1]',$description).'</p>'); + $r->print('<p style="font-weight: bold;">'.&mt('Successfully signed up: [_1]',$description).'</p>'); $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('<p>'.&mt('Already reserved: [_1]',$description).'</p>'); } @@ -677,7 +902,7 @@ STUFF } my %lt = &Apache::lonlocal::texthash( - 'request' => 'Availibility list', + 'request' => 'Availability list', 'try' => 'Try again?', 'or' => 'or', ); @@ -716,7 +941,7 @@ STUFF } 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) { @@ -741,6 +966,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; @@ -787,7 +1017,9 @@ 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, @@ -815,45 +1047,14 @@ sub get_description { } sub show_choices { - my ($r,$symb,$formname)=@_; - - my ($cnum,$cdom)=&get_course(); - my %slots=&Apache::lonnet::dump('slots',$cdom,$cnum); - my $consumed_uniqueperiods = &get_consumed_uniqueperiods(\%slots); - if (ref($consumed_uniqueperiods) eq 'HASH') { - if (&Apache::lonnet::error(%$consumed_uniqueperiods)) { - $r->print('<span class="LC_error">'. - &mt('An error occurred determining slot availability'). - '</span>'); - return; - } - } elsif ($consumed_uniqueperiods =~ /^error: /) { - $r->print('<span class="LC_error">'. - &mt('An error occurred determining slot availability'). - '</span>'); - return; - } - my (@available,$output); + 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'); - if ($got_slots[0] =~ /^error: /) { - $r->print('<span class="LC_error">'. - &mt('An error occurred determining slot availability'). - '</span>'); + if (!ref($available) eq 'ARRAY') { return; } - 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)); - - push(@available,$slot); - } - if (!@available) { - $output = &mt('No available times.'); + if (!@{$available}) { + $output = '<span class="LC_info">'.&mt('No available times.').'</span>'; if ($env{'form.command'} ne 'manageresv') { $output .= ' <a href="/adm/flip?postdata=return:">'. &mt('Return to last resource').'</a>'; @@ -861,30 +1062,101 @@ sub show_choices { $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 .= '<fieldset><legend>'.&mt('Actions').'</legend>'."\n". + '<form method="post" name="reservationdisplay_'.$num. + '" action="" onsubmit="toggleSlotDisplay(this.form,'."'$num'".');">'; + 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 .= '<span class="LC_nobreak"><label>'. + '<input type="radio" class="LC_slotpick_radio" name="slotpick" value="'. + $option.'" onclick="'.$onclick.'" />'. + $resmenu{$option}. + '</label></span>'.(' ' x3)."\n"; + } + $output .= '</form>'; + my $chooserform = 'reservationchooser_'.$num; + my $starttime = $slots->{$available->[0]}->{'starttime'}; + my $endtime = $slots->{$available->[-1]}->{'starttime'}; + if ($env{'form.command'} eq 'manageresv') { + $output .= <<"ENDSCRIPT"; + +<script type="text/javascript"> +// <![CDATA[ +function currSlotDisplay$num() { + var currslot = new Array($numreserved); +$js + for (var j=0; j<$numreserved; j++) { + if (document.getElementById('LC_slotrow_$num\_'+currslot[j])) { + document.getElementById('LC_slotrow_$num\_'+currslot[j]).style.display = ''; + } + } +} +// ]]> +</script> + +ENDSCRIPT + } + $output .= + '<div id="LC_slotfilter_'.$num.'" style="display:'.$showfilter.'">'. + '<form method="post" name="'.$chooserform.'" action="">'. + '<table><tr><td>'.&mt('Open after').'</td><td>'. + &Apache::lonhtmlcommon::date_setter($chooserform,'start',$starttime,'','','','','','','',1,1). + '</td></tr><tr><td>'.&mt('Closed before').'</td><td>'. + &Apache::lonhtmlcommon::date_setter($chooserform,'end',$endtime,'','','','','','','',1,1). + '</td></tr></table><br />'. + '<input type="button" name="slotfilter" value="Search for reservable slots" onclick="updateSlotDisplay(this.form,'."'$num'".');" />'. + '</form></div><div id="LC_slotsearch_'.$num.'" style="display:none"><hr />'; + } if ($env{'form.command'} eq 'manageresv') { - $output = '<table border="0">'; + $output .= '<table border="0">'; } else { - $output = &Apache::loncommon::start_data_table(); + $output .= &Apache::loncommon::start_data_table(); } - foreach my $slot (@available) { - my $description=&get_description($slot,$slots{$slot}); + foreach my $slot (@{$available}) { + my $description=&get_description($slot,$slots->{$slot}); my $form; - if ((grep(/^\Q$slot\E$/,@got_slots)) || - &space_available($slot,$slots{$slot},$symb)) { + if ((grep(/^\Q$slot\E$/,@{$got_slots})) || + &space_available($slot,$slots->{$slot},$symb)) { my $text=&mt('Select'); my $command='get'; - if (grep(/^\Q$slot\E$/,@got_slots)) { + if (grep(/^\Q$slot\E$/,@{$got_slots})) { $text=&mt('Drop Reservation'); $command='release'; } else { - my $conflict = &check_for_conflict($symb,$slot,$slots{$slot}, - \%slots, - $consumed_uniqueperiods); + my $conflict = &check_for_conflict($symb,$slot,$slots->{$slot}, + $slots,$consumed_uniqueperiods); if ($conflict) { if ($conflict =~ /^error: /) { $form = '<span class="LC_error">'. - &mt('Slot: [_1] has unknown status.',$description). - '</span>'; + &mt('Slot: [_1] has unknown status.',$description). + '</span>'; } else { $text=&mt('Change Reservation'); $command='get'; @@ -893,15 +1165,16 @@ sub show_choices { } my $escsymb=&escape($symb); if (!$form) { + my $name; if ($formname) { - $formname = 'name="'.$formname.'" '; + $name = 'name="'.$formname.'"'; } my $context = 'user'; if ($env{'form.command'} eq 'manageresv') { $context = 'usermanage'; } $form=<<STUFF; - <form method="post" action="/adm/slotrequest" $formname> + <form method="post" action="/adm/slotrequest" $name> <input type="submit" name="Select" value="$text" /> <input type="hidden" name="symb" value="$escsymb" /> <input type="hidden" name="slotname" value="$slot" /> @@ -914,9 +1187,9 @@ STUFF $form = &mt('Unavailable'); } if ($env{'form.command'} eq 'manageresv') { - $output .= '<tr>'; + $output .= '<tr id="LC_slotrow_'.$num.'_'.$slot.'" >'; } else { - $output .= &Apache::loncommon::start_data_table_row(); + $output .= &Apache::loncommon::start_data_table_row('','LC_slotrow_'.$num.'_'.$slot); } $output .= " <td>$form</td> @@ -930,9 +1203,13 @@ STUFF if ($env{'form.command'} eq 'manageresv') { $output .= '</table>'; } else { - $output .= &Apache::loncommon::end_data_table(); + $output .= &Apache::loncommon::end_data_table(); + } + if (@{$available} > 1) { + $output .= '</div></fieldset>'; } $r->print($output); + return; } sub to_show { @@ -1026,6 +1303,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); @@ -1033,7 +1311,8 @@ sub show_table { my $available; if ($mgr eq 'F') { # FIXME: This line should be deleted once Slots uses breadcrumbs - $r->print(&Apache::loncommon::help_open_topic('Slot About', 'Help on slots')); + $r->print('<br />'.&Apache::loncommon::help_open_topic( + 'Slot About', &mt('Help on slots'))); $r->print('<div>'); $r->print('<form method="post" action="/adm/slotrequest"> @@ -1049,7 +1328,11 @@ sub show_table { } if (!keys(%slots)) { - $r->print('<div>'.&mt('No slots have been created in this course.').'</div>'); + $r->print( + '<p class="LC_info">' + .&mt('No slots have been created in this '.lc($crstype).'.') + .'</p>' + ); return; } @@ -1076,6 +1359,8 @@ 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', 'space' => '# of students/max', 'ip' => 'IP or DNS restrictions', @@ -1085,9 +1370,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','space','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') @@ -1160,11 +1450,11 @@ sub show_table { 6,\%stu_display_fields, \@stu_display_order).' </td> - <td valign="top">'.&Apache::loncommon::select_form($when,'when',%when_fields). + <td valign="top">'.&Apache::loncommon::select_form($when,'when',\%when_fields). '</td> <td valign="top">'.&Apache::loncommon::select_form($name_filter_type, 'name_filter_type', - %name_filter_type_fields). + \%name_filter_type_fields). '<br />'. &Apache::lonhtmlcommon::textbox('name_filter_value', $env{'form.name_filter_value'}, @@ -1173,11 +1463,11 @@ sub show_table { <td valign="top"> <table> <tr> - <td rowspan="2">Deleted slots:</td> - <td><label>'.$show_radio.'Show</label></td> + <td rowspan="2">'.&mt('Deleted slots:').'</td> + <td><label>'.$show_radio.&mt('Show').'</label></td> </tr> <tr> - <td><label>'.$hide_radio.'Hide</label></td> + <td><label>'.$hide_radio.&mt('Hide').'</label></td> </tr> </table> </td> @@ -1186,19 +1476,20 @@ sub show_table { $r->print('</div>'); $r->print('<p><input type="submit" name="start" value="'.&mt('Update Display').'" /></p>'); my $linkstart='<a href="/adm/slotrequest?command=showslots&order='; - $r->print(&Apache::loncommon::start_data_table(). - &Apache::loncommon::start_data_table_header_row().' - <th></th>'); + my $tableheader = &Apache::loncommon::start_data_table(). + &Apache::loncommon::start_data_table_header_row().' + <th></th>'; foreach my $which (@show_order) { if ($which ne 'proctor' && exists($show{$which})) { - $r->print('<th>'.$linkstart.$which.'">'.$show_fields{$which}.'</a></th>'); + $tableheader .= '<th>'.$linkstart.$which.'">'.$show_fields{$which}.'</a></th>'; } } - $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|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'}}) @@ -1235,12 +1526,15 @@ sub show_table { 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 ($id_count,$ids); @@ -1277,6 +1571,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'})) { @@ -1338,30 +1634,39 @@ sub show_table { delete => 'Delete', slotlog => 'History', ); - my $edit=(<<"EDITLINK"); + my ($edit,$delete,$showlog,$remove_all); + if ($mgr) { + $edit=(<<"EDITLINK"); <a href="/adm/helper/newslot.helper?name=$slot">$lt{'edit'}</a> EDITLINK - my $delete=(<<"DELETELINK"); + $delete=(<<"DELETELINK"); <a href="/adm/slotrequest?command=delete&slotname=$slot">$lt{'delete'}</a> DELETELINK - my $showlog=(<<"LOGLINK"); + $remove_all=&remove_link($slot,'remove all').'<br />'; + + if ($ids eq '') { + undef($remove_all); + } else { + undef($delete); + } + } + + $showlog=(<<"LOGLINK"); <a href="/adm/slotrequest?command=slotlog&slotname=$slot">$lt{'slotlog'}</a> LOGLINK - my $remove_all=&remove_link($slot,'remove all').'<br />'; - - if ($ids eq '') { - undef($remove_all); - } else { - undef($delete); - } 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. @@ -1384,6 +1689,12 @@ LOGLINK if (exists($show{'startreserve'})) { $colspan++;$r->print("<td>$start_reserve</td>\n"); } + if (exists($show{'endreserve'})) { + $colspan++;$r->print("<td>$end_reserve</td>\n"); + } + if (exists($show{'reservationmsg'})) { + $colspan++;$r->print("<td>$reservemsg</td>\n"); + } if (exists($show{'secret'})) { $colspan++;$r->print("<td>$slots{$slot}{'secret'}</td>\n"); } @@ -1424,26 +1735,46 @@ $row_end STUFF } } - $r->print(&Apache::loncommon::end_data_table().'</form>'); + if ($shownheader) { + $r->print(&Apache::loncommon::end_data_table()); + } else { + $r->print('<p>'.&mt('No slots meet the criteria for display').'</p>'); + } + $r->print('</form>'); return; } sub manage_reservations { - my ($r,$type) = @_; + my ($r,$crstype,$slots,$consumed_uniqueperiods,$allavailable) = @_; my $navmap = Apache::lonnavmaps::navmap->new(); $r->print('<p>' .&mt('Instructors may use a reservation system to place restrictions on when and where assignments can be worked on.') .'<br />' .&mt('One example is for management of laboratory space, which is only available at certain times, and has a limited number of seats.') - .'</p><p>' - .&mt('Your reservation status for any such assignments is listed below:') .'</p>' ); if (!defined($navmap)) { - $r->print('<div class="LC_error">'. - &mt('Unable to retrieve information about course contents'). - '</div>'); - &Apache::lonnet::logthis('Manage Reservations - could not create navmap object in '.lc($type).':'.$env{'request.course.id'}); + $r->print('<div class="LC_error">'); + 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('</div>'); + &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('<span class="LC_error">'. + &mt('An error occurred determining slot availability.'). + '</span>'); + return; + } + } elsif ($consumed_uniqueperiods =~ /^error: /) { + $r->print('<span class="LC_error">'. + &mt('An error occurred determining slot availability.'). + '</span>'); return; } my (%parent,%shownparent,%container,%container_title,%contents); @@ -1451,7 +1782,11 @@ sub manage_reservations { my @backgrounds = ("LC_odd_row","LC_even_row"); my $numcolors = scalar(@backgrounds); my $location=&Apache::loncommon::lonhttpdurl("/adm/lonIcons/whitespace_21.gif"); - $r->print('<table class="LC_data_table LC_tableOfContent">'."\n"); + my $slotheader = '<p>'. + &mt('Your reservation status for any such assignments is listed below:'). + '</p>'. + '<table class="LC_data_table LC_tableOfContent">'."\n"; + my $shownheader = 0; my $it=$navmap->getIterator(undef,undef,undef,1,undef,undef); while (my $resource = $it->next()) { if ($resource == $it->BEGIN_MAP()) { @@ -1513,6 +1848,8 @@ sub manage_reservations { } 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) { @@ -1546,11 +1883,19 @@ sub manage_reservations { foreach my $item (@maprows) { $rownum ++; my $bgcolor = $backgrounds[$rownum % $numcolors]; + if (!$shownheader) { + $r->print($slotheader); + $shownheader = 1; + } $r->print('<tr class="'.$bgcolor.'">'.$item.'</tr>'."\n"); } } $rownum ++; my $bgcolor = $backgrounds[$rownum % $numcolors]; + if (!$shownheader) { + $r->print($slotheader); + $shownheader = 1; + } $r->print('<tr class="'.$bgcolor.'"><td>'."\n"); for (my $i=0; $i<$depth; $i++) { $r->print('<img src="'.$location.'" alt="" />'); @@ -1570,26 +1915,57 @@ sub manage_reservations { } } if ($hasaction) { - $result .= '<td valgn="middle">'.$msg.'</td>'. - '<td valign="middle">'.(' ' x6); + $result .= '<td valign="top">'.$msg.'</td>'. + '<td valign="top">'; } else { $result .= '<td colspan="2" valign="middle">'.$msg.'</td>'; } $r->print($result); if ($hasaction) { - my $formname = 'manageres_'.$reservable; - &show_choices($r,$symb,$formname); + my @got_slots=&check_for_reservation($symb,'allslots'); + if ($got_slots[0] =~ /^error: /) { + $r->print('<span class="LC_error">'. + &mt('An error occurred determining slot availability.'). + '</span>'); + } 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('</td>'); } $r->print('</tr>'); } } } + if ($shownheader) { + $r->print('</table>'); + } if (!$reservable) { - $r->print('<span class="LC_info">'.&mt('No course items currently require a reservation to gain access.').'</span>'); + $r->print('<span class="LC_info">'); + 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('</span>'); } - $r->print('</table>'. - '<p><a href="/adm/slotrequest?command=showresv">'. + $r->print('<p><a href="/adm/slotrequest?command=showresv">'. &mt('Reservation History').'</a></p>'); } @@ -1620,12 +1996,21 @@ sub show_reservations { 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('<div class="LC_fontsize_large">'. - &mt('History of student-reservable slots for: [_1]', - &Apache::loncommon::plainname($env{'form.uname'},$env{'form.udom'}, - 'firstname')).'</div>'); + $r->print('<div class="LC_fontsize_large">'); + 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('</div>'); } $r->print('<form action="/adm/slotrequest" method="post" name="'.$formname.'">'); # set defaults @@ -1674,7 +2059,7 @@ sub show_reservations { } } my (%titles,%maptitles); - my %lt = &reservationlog_contexts(); + 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'})); @@ -1711,16 +2096,19 @@ sub show_reservations { if ($showntablehdr) { $r->print(&Apache::loncommon::end_data_table().'<br />'); if (($curr{'page'} > 1) || ($more_records)) { - $r->print('<table><tr>'); + $r->print('<p>'); if ($curr{'page'} > 1) { - $r->print('<td><a href="javascript:chgPage('."'previous'".');">'.&mt('Previous [_1] changes',$curr{'show'}).'</a></td>'); + $r->print('<input type="button" onclick="javascript:chgPage('."'previous'".');" value="'. + &mt('Previous [_1] changes',$curr{'show'}).'" />'); } if ($more_records) { - $r->print('<td><a href="javascript:chgPage('."'next'".');">'.&mt('Next [_1] changes',$curr{'show'}).'</a></td>'); + $r->print('<input type="button" onclick="javascript:chgPage('."'next'".');" value="'. + &mt('Next [_1] changes',$curr{'show'}).'" />'); } - $r->print('</tr></table>'); + $r->print('</p>'); $r->print(<<"ENDSCRIPT"); <script type="text/javascript"> +// <![CDATA[ function chgPage(caller) { if (caller == 'previous') { document.$formname.page.value --; @@ -1731,6 +2119,7 @@ function chgPage(caller) { document.$formname.submit(); return; } +// ]]> </script> ENDSCRIPT } @@ -1754,6 +2143,7 @@ ENDSCRIPT sub show_reservations_log { my ($r) = @_; my $badslot; + my $crstype = &Apache::loncommon::course_type(); if ($env{'form.slotname'} eq '') { $r->print('<div class="LC_warning">'.&mt('No slot name provided').'</div>'); $badslot = 1; @@ -1764,7 +2154,13 @@ sub show_reservations_log { $badslot = 1; } elsif ($slot{type} ne 'schedulable_student') { my $description = &get_description($env{'form.slotname'},\%slot); - $r->print('<div class="LC_warning">'.&mt('Reservation history unavailable for non-student-reservable slot: [_1].',$description).'</div>'); + $r->print('<div class="LC_warning">'); + 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('</div>'); $badslot = 1; } } @@ -1835,9 +2231,13 @@ sub show_reservations_log { my %slot=&Apache::lonnet::get_slot($env{'form.slotname'}); my $description = $slot{'description'}; - $r->print('<span class="LC_fontsize_large">'. - &mt('Reservation changes for student-reservable slot: [_1]',$description).'</span><br />'); - + $r->print('<span class="LC_fontsize_large">'); + 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('</span><br />'); $r->print(&display_filter($formname,$cdom,$cnum,\%curr,$version,\@allsymbs)); my $showntablehdr = 0; my $tablehdr = &Apache::loncommon::start_data_table(). @@ -1855,7 +2255,7 @@ sub show_reservations_log { $minshown = 1 + ($curr{'page'} - 1) * $curr{'show'}; } } - my %lt = &reservationlog_contexts(); + 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'}) || @@ -1909,14 +2309,16 @@ sub show_reservations_log { if ($showntablehdr) { $r->print(&Apache::loncommon::end_data_table().'<br />'); if (($curr{'page'} > 1) || ($more_records)) { - $r->print('<table><tr>'); + $r->print('<p>'); if ($curr{'page'} > 1) { - $r->print('<td><a href="javascript:chgPage('."'previous'".');">'.&mt('Previous [_1] changes',$curr{'show'}).'</a></td>'); + $r->print('<input type="button" onclick="javascript:chgPage('."'previous'".');" value="'. + &mt('Previous [_1] changes',$curr{'show'}).'" />'); } if ($more_records) { - $r->print('<td><a href="javascript:chgPage('."'next'".');">'.&mt('Next [_1] changes',$curr{'show'}).'</a></td>'); + $r->print('<input type="button" onclick="javascript:chgPage('."'next'".');" value="'. + &mt('Next [_1] changes',$curr{'show'}).'" />'); } - $r->print('</tr></table>'); + $r->print('</p>'); $r->print(<<"ENDSCRIPT"); <script type="text/javascript"> function chgPage(caller) { @@ -1957,7 +2359,7 @@ sub get_resource_title { $maptitle = $maptitles->{$mapurl}; } else { if ($mapurl eq $env{'course.'.$env{'request.course.id'}.'.url'}) { - $maptitle=&mt('Main Course Documents'); + $maptitle=&mt('Main Content'); } else { $maptitle=&Apache::lonnet::gettitle($mapurl); } @@ -1975,6 +2377,7 @@ sub get_resource_title { } sub reservationlog_contexts { + my ($crstype) = @_; my %lt = &Apache::lonlocal::texthash ( any => 'Any', user => 'By student', @@ -1984,6 +2387,10 @@ sub reservationlog_contexts { release => 'Dropped reservation', usermanage => 'By student', ); + if ($crstype eq 'Community') { + $lt{'user'} = &mt('By member'); + $lt{'usermanage'} = $lt{'user'}; + } return %lt; } @@ -2004,7 +2411,8 @@ sub display_filter { &Apache::lonhtmlcommon::date_setter($formname,'log_end_date', $curr->{'log_end_date'},undef, undef,undef,undef,undef,undef,undef,$nolink); - my %lt = &reservationlog_contexts(); + my $crstype = &Apache::loncommon::course_type(); + my %lt = &reservationlog_contexts($crstype); $output .= '<td valign="top"><b>'.&mt('Window during which changes occurred:'). '</b><br /><table><tr><td>'.&mt('After:'). '</td><td>'.$startform.'</td></tr><tr><td>'.&mt('Before:').'</td><td>'. @@ -2050,11 +2458,12 @@ sub display_filter { } $output .= '</select></td>'; } - $output .= '<td> </td><td valign="middle"><input type="submit" value="'. - &mt('Update Display').'" /></tr></table>'. + $output .= '<td> </td></tr></table>'. + '<p><input type="submit" value="'. + &mt('Update Display').'" /></p>'. '<p class="LC_info">'. &mt('Only changes made from servers running LON-CAPA [_1] or later are displayed.' - ,'2.6.99.0'); + ,'2.9.0'); if ($version) { $output .= ' '.&mt('This LON-CAPA server is version [_1]',$version); } @@ -2062,11 +2471,47 @@ sub display_filter { 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 = '<li>'.&mt('Sent to you: [_1]', + $message_status).' </li>'; + } + if ($setting eq 'student_and_user_notes_screen') { + $comment_status = + &Apache::lonmsg::store_instructor_comment($subject.'<br />'. + $msg,$user,$domain); + $comment_status = '<li>'.&mt('Entry added to course record (viewable by instructor): [_1]', + $comment_status).'</li>'; + } + 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 '<span class="LC_info">'.$msgtitle.'</span>' + .'<ul>' + .$message_status + .$comment_status + .'</ul><hr />'; + } +} + sub upload_start { my ($r)=@_; $r->print( &Apache::grades::checkforfile_js() - .'<h3>'.&mt('Specify a file containing the slot definitions.').'</h3>' + .'<h2>'.&mt('Upload a file containing the slot definitions').'</h2>' .'<form method="post" enctype="multipart/form-data"' .' action="/adm/slotrequest" name="slotupload">' .'<input type="hidden" name="command" value="csvuploadmap" />' @@ -2100,16 +2545,40 @@ sub csvuploadmap_header { my $checked=(($env{'form.noFirstLine'})?' checked="checked"':''); my $ignore=&mt('Ignore First Line'); - my $help_field = &Apache::loncommon::help_open_topic('Slot SelectingField'); + my $buttontext = &mt('Reverse Association'); + + $r->print( + '<form method="post" enctype="multipart/form-data" action="/adm/slotrequest" name="slotupload">' + .'<h2>'.&mt('Identify fields in uploaded list').'</h2>' + .'<div class="LC_columnSection">' + .&Apache::loncommon::help_open_topic( + 'Slot About',&mt('Help on slots')) + .' '.&Apache::loncommon::help_open_topic( + 'Slot SelectingField',&mt('Help on selecting Fields')) + ."</div>\n" + .'<p class="LC_info">' + .&mt('Total number of records found in file: [_1]','<b>'.$distotal.'</b>') + ."</p>\n" + ); + if ($distotal == 0) { + $r->print('<p class="LC_warning">'.&mt('None found').'</p>'); + } + $r->print( + '<p>' + .&mt('Enter as many fields as you can.').'<br />' + .&mt('The system will inform you and bring you back to this page,[_1]if the data selected is insufficient to create the slots.','<br />') + .'</p>' + ); + $r->print( + '<div class="LC_left_float">' + .'<fieldset><legend>'.&mt('Functions').'</legend>' + .'<label><input type="checkbox" name="noFirstLine"'.$checked.' />'.$ignore.'</label>' + .' <input type="button" value="'.$buttontext + .'" onclick="javascript:this.form.associate.value=\'Reverse Association\';submit(this.form);" />' + .'</fieldset></div><br clear="all" />' + ); $r->print(<<ENDPICK); -<form method="post" enctype="multipart/form-data" action="/adm/slotrequest" name="slotupload"> -<h3>Identify fields $help_field</h3> -Total number of records found in file: $distotal <hr /> -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.<hr /> -<input type="button" value="Reverse Association" onclick="javascript:this.form.associate.value='Reverse Association';submit(this.form);" /> -<label><input type="checkbox" name="noFirstLine"$checked />$ignore</label> <input type="hidden" name="associate" value="" /> <input type="hidden" name="datatoken" value="$datatoken" /> <input type="hidden" name="fileupload" value="$env{'form.fileupload'}" /> @@ -2117,9 +2586,10 @@ to this page if the data selected is ins <input type="hidden" name="upfile_associate" value="$env{'form.upfile_associate'}" /> <input type="hidden" name="command" value="csvuploadassign" /> -<hr /> <script type="text/javascript" language="Javascript"> +// <![CDATA[ $javascript +// ]]> </script> ENDPICK return ''; @@ -2130,7 +2600,6 @@ sub csvuploadmap_footer { my ($request,$i,$keyfields) =@_; my $buttontext = &mt('Create Slots'); $request->print(<<ENDPICK); -</table> <input type="hidden" name="nfields" value="$i" /> <input type="hidden" name="keyfields" value="$keyfields" /> <input type="button" onclick="javascript:verify(this.form)" value="$buttontext" /><br /> @@ -2139,7 +2608,7 @@ 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(<<ENDPICK); function verify(vf) { var foundstart=0; @@ -2165,7 +2634,7 @@ ENDPICK } sub csvupload_javascript_forward_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(<<ENDPICK); function verify(vf) { var foundstart=0; @@ -2232,6 +2701,8 @@ sub csvupload_fields { ['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'], @@ -2304,8 +2775,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'}) { @@ -2313,20 +2788,80 @@ 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 ($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; @@ -2337,6 +2872,9 @@ sub csv_upload_assign { $r->rflush(); $countdone++; } + if ($countdone) { + &Apache::lonnet::devalidate_slots_cache($cname,$cdom); + } $r->print('<p>'.&mt('Created [quant,_1,slot]',$countdone)."\n".'</p>'); foreach my $error (@errors) { $r->print('<p><span class="LC_warning">'.$error.'</span></p>'."\n"); @@ -2362,6 +2900,15 @@ sub slot_command_titles { 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; @@ -2379,6 +2926,7 @@ sub handler { 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'; @@ -2399,9 +2947,14 @@ sub handler { if (ref($brcrum) eq 'ARRAY') { push(@{$brcrum},{href=>"/adm/slotrequest?command=showresv",text=>$title}); } - } elsif ($env{'form.command'} eq 'manageresv') { - $title = 'Manage Reservations'; - $brcrum =[{href=>"/adm/slotrequest?command=manageresv",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", @@ -2421,14 +2974,27 @@ sub handler { 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'); } - &start_page($r,$title,$brcrum); + if (($env{'form.requestattempt'}) || ($env{'form.command'} eq 'manageresv')) { + $js = &reservation_js(\%slots,$consumed_uniqueperiods,$available,$got_slots,$symb); + } + &start_page($r,$title,$brcrum,$js); if ($env{'form.command'} eq 'manageresv') { + $allavailable = $available; + undef($available); + undef($got_slots); my $crstype = &Apache::loncommon::course_type(); - &manage_reservations($r,$crstype); + &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') { @@ -2458,7 +3024,7 @@ sub handler { } &csv_upload_map($r); } - } elsif ($env{'form.command'} eq 'slotlog' && $mgr eq 'F') { + } elsif (($env{'form.command'} eq 'slotlog') && ($vgr eq 'F')) { &show_reservations_log($r); } else { my $symb=&unescape($env{'form.symb'}); @@ -2485,13 +3051,34 @@ sub handler { return OK; } if ($env{'form.requestattempt'}) { - &show_choices($r,$symb); + $r->print('<div class="LC_left_float">'); + &show_choices($r,$symb,undef,undef,\%slots,$consumed_uniqueperiods,$available,$got_slots); + $r->print('</div><div style="padding:0;clear:both;margin:0;border:0"></div>'); } 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 (&get_slot($r,$symb,$env{'form.releaseslot'},1)) { + 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 = '<span style="font-weight: bold;">'. + &mt('Unchanged reservation: [_1]',$description).'</span><br /><br />'; + 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 = '<span class="LC_warning">'.&mt('Reservation no longer reported as available.').'</span>'; + } + $r->print($msg); + &return_link($r); + } elsif (&get_slot($r,$symb,$env{'form.releaseslot'},1)) { &release_slot($r,$symb,$env{'form.releaseslot'}); } } else {