--- loncom/interface/slotrequest.pm 2012/08/01 20:26:43 1.114 +++ loncom/interface/slotrequest.pm 2015/09/07 17:44:11 1.124 @@ -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.114 2012/08/01 20:26:43 raeburn Exp $ +# $Id: slotrequest.pm,v 1.124 2015/09/07 17:44:11 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,179 @@ 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 @@ -121,7 +304,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)); @@ -218,16 +400,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}}; @@ -339,12 +524,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; } @@ -504,14 +689,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); @@ -538,10 +729,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); } } @@ -696,7 +887,7 @@ STUFF } my %lt = &Apache::lonlocal::texthash( - 'request' => 'Availibility list', + 'request' => 'Availability list', 'try' => 'Try again?', 'or' => 'or', ); @@ -735,7 +926,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) { @@ -811,7 +1002,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, @@ -839,77 +1032,116 @@ sub get_description { } sub show_choices { - my ($r,$symb,$formname)=@_; - - my ($cnum,$cdom)=&get_course(); - my %slots = &Apache::lonnet::get_course_slots($cnum,$cdom); - my $consumed_uniqueperiods = &get_consumed_uniqueperiods(\%slots); - 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 (@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(''. - &mt('An error occurred determining slot availability'). - ''); + 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},$symb,\%slots, - $consumed_uniqueperiods)); - - push(@available,$slot); - } - if (!@available) { - $output = '
'.&mt('No available times.'); + if (!@{$available}) { + $output = ''.&mt('No available times.').''; if ($env{'form.command'} ne 'manageresv') { $output .= ' '. &mt('Return to last resource').''; } - $output .= '
'; $r->print($output); return; } + if (@{$available} > 1) { + my $numavailable = scalar(@{$available}); + my $numreserved = 0; + my $js; + my $j = 0; + foreach my $got (@{$got_slots}) { + unless (($got eq '') || (!defined($got))) { + $numreserved ++; + if ($env{'form.command'} eq 'manageresv') { + $js .= " currslot[$j]='$got';\n"; + $j++; + } + } + } + my $showfilter = 'none'; + $output .= '
'.&mt('Actions').''."\n". + '
'; + my @options = ('all','filter'); + if ($numreserved) { + unshift(@options,'show'); + } + my %resmenu = &Apache::lonlocal::texthash ( + show => 'Show current reservation', + all => 'Show all', + filter => 'Search by date', + ); + foreach my $option (@options) { + my $onclick = "toggleSlotDisplay(this.form,'$num');"; + if (($option eq 'show') && ($env{'form.command'} eq 'manageresv')) { + $onclick .= "currSlotDisplay$num(this.form,'$num');"; + } + $output .= ''.(' ' x3)."\n"; + } + $output .= '
'; + my $chooserform = 'reservationchooser_'.$num; + my $starttime = $slots->{$available->[0]}->{'starttime'}; + my $endtime = $slots->{$available->[-1]}->{'starttime'}; + if ($env{'form.command'} eq 'manageresv') { + $output .= <<"ENDSCRIPT"; + + + +ENDSCRIPT + } + $output .= + '
'. + '
'. + '
'.&mt('Open after').''. + &Apache::lonhtmlcommon::date_setter($chooserform,'start',$starttime,'','','','','','','',1,1). + '
'.&mt('Closed before').''. + &Apache::lonhtmlcommon::date_setter($chooserform,'end',$endtime,'','','','','','','',1,1). + '

'. + ''. + '
'; } $r->print($output); + return; } sub to_show { @@ -1059,7 +1296,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('
'.&Apache::loncommon::help_open_topic( + 'Slot About', &mt('Help on slots'))); $r->print('
'); $r->print(' @@ -1075,11 +1313,11 @@ sub show_table { } if (!keys(%slots)) { - if ($crstype eq 'Community') { - $r->print('
'.&mt('No slots have been created in this community.').'
'); - } else { - $r->print('
'.&mt('No slots have been created in this course.').'
'); - } + $r->print( + '

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

' + ); return; } @@ -1210,11 +1448,11 @@ sub show_table { - - + + - +
Deleted slots:'.&mt('Deleted slots:').'
@@ -1477,7 +1715,7 @@ STUFF } sub manage_reservations { - my ($r,$crstype) = @_; + 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.') @@ -1496,6 +1734,19 @@ sub manage_reservations { &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"); @@ -1634,15 +1885,38 @@ sub manage_reservations { } } if ($hasaction) { - $result .= ''.$msg.''. - ''.(' ' x6); + $result .= ''.$msg.''. + ''; } else { $result .= ''.$msg.''; } $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(''. + &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(''); @@ -1802,6 +2076,7 @@ sub show_reservations { $r->print(''); $r->print(<<"ENDSCRIPT"); ENDSCRIPT } @@ -2049,7 +2325,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); } @@ -2200,7 +2476,7 @@ sub upload_start { my ($r)=@_; $r->print( &Apache::grades::checkforfile_js() - .'

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

' + .'

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

' .'' .'' @@ -2234,16 +2510,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( + '' + .'

'.&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 $help_field

-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.
- - @@ -2251,9 +2551,10 @@ to this page if the data selected is ins -
ENDPICK return ''; @@ -2264,7 +2565,6 @@ sub csvuploadmap_footer { my ($request,$i,$keyfields) =@_; my $buttontext = &mt('Create Slots'); $request->print(<
@@ -2273,7 +2573,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(< 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"), @@ -2551,6 +2881,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'; @@ -2571,9 +2902,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", @@ -2597,11 +2933,23 @@ sub handler { } else { $brcrum =[]; } - &start_page($r,$title,$brcrum); + my ($symb,$js,$available,$allavailable,$got_slots); + $available = []; + if ($env{'form.requestattempt'}) { + $symb=&unescape($env{'form.symb'}); + @{$got_slots}=&check_for_reservation($symb,'allslots'); + } + if (($env{'form.requestattempt'}) || ($env{'form.command'} eq 'manageresv')) { + $js = &reservation_js(\%slots,$consumed_uniqueperiods,$available,$got_slots,$symb); + } + &start_page($r,$title,$brcrum,$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') { @@ -2658,7 +3006,9 @@ 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') {