--- loncom/interface/slotrequest.pm 2019/06/22 19:18:28 1.125.2.7 +++ loncom/interface/slotrequest.pm 2023/07/12 15:48:23 1.147 @@ -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.125.2.7 2019/06/22 19:18:28 raeburn Exp $ +# $Id: slotrequest.pm,v 1.147 2023/07/12 15:48:23 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -50,16 +50,19 @@ sub fail { } else { $r->print('
'.&mt('Failed.').'
'); } - + &return_link($r); &end_page($r); } sub start_page { - my ($r,$title,$brcrum,$js)=@_; + 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 = ( @@ -71,6 +74,31 @@ sub start_page { $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)); } @@ -256,7 +284,104 @@ function uncheckSlotRadio() { } if (slotpicks.length) { for (var i=0; i'.&mt('Releasing reservations').'
'); foreach my $entry (sort { $consumed{$a}{'name'} cmp @@ -642,7 +767,7 @@ sub release_all_slot { &release_reservation($slot_name,$uname,$udom, $consumed{$entry}{'symb'},$mgr); if (!$result) { - $r->print(''.&mt($msg).'
'); + $r->print(''.&mt($msg).'
'); } else { $r->print("$msg
"); } @@ -672,11 +797,11 @@ sub release_slot { my ($result,$msg) = &release_reservation($slot_name,$uname,$udom,$symb,$mgr); if (!$result) { - $r->print(''.&mt($msg).'
'); + $r->print(''.&mt($msg).'
'); } else { $r->print("$msg
"); } - + if ($mgr eq 'F') { $r->print(''. &mt('Return to slot list').'
'); @@ -690,26 +815,184 @@ 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)); } } + my $context = $env{'form.context'}; - # if the reservation symb is for a map get a resource in that map - # to check slot parameters on + # get navmap object my $navmap=Apache::lonnavmaps::navmap->new; if (!defined($navmap)) { return (0,'error: Unable to determine current status'); } + + my ($cnum,$cdom)=&get_course(); + + # get slot reservations, check if user has reservation + my %consumed=&Apache::lonnet::dump('slot_reservations',$cdom,$cnum, + "^$slot_name\0"); + + # + # If release is because of a reservation *change*, symb(s) associated with reservation + # being dropped may differ from the current symb. + # + # We need to get symb(s) from slot_reservations.db, and for each symb, update + # the value of the availablestudent parameter, at the appropriate level + # (as dictated by the value of the useslots parameter for the symb and user). + # + # We also delete all entries for the slot being released, for the specific user. + # + + my $conflict; + + if (($env{'form.command'} eq 'change') && ($slot_name eq $env{'form.releaseslot'}) && + ($env{'form.slotname'} ne $slot_name)) { + my %changedto = &Apache::lonnet::get_slot($env{'form.slotname'}); + + # check for conflicts + my ($to_uniq_start,$to_uniq_end,$from_uniq_start,$from_uniq_end); + if (ref($changedto{'uniqueperiod'}) eq 'ARRAY') { + ($to_uniq_start,$to_uniq_end) = @{$changedto{'uniqueperiod'}}; + } + if (ref($slot{'uniqueperiod'}) eq 'ARRAY') { + ($from_uniq_start,$from_uniq_end) = @{$slot{'uniqueperiod'}}; + } + my $to_start = $changedto{'starttime'}; + my $to_end = $changedto{'endtime'}; + my $from_start = $slot{'starttime'}; + my $from_end = $slot{'endtime'}; + + if (! + ($from_start < $to_uniq_start && $from_end < $to_uniq_start) || + ($from_start > $to_uniq_end && $from_end > $to_uniq_end )) { + $conflict = 1; + } + if (! + ($to_start < $from_uniq_start && $to_end < $from_uniq_start) || + ($to_start > $from_uniq_end && $to_end > $from_uniq_end )) { + $conflict = 1; + } + + if ($conflict) { + my %symbs_for_slot; + my (%to_delete,%failed,%released); + foreach my $entry (keys(%consumed)) { + if ( $consumed{$entry}->{'name'} eq ($uname.':'.$udom) ) { + $symbs_for_slot{$consumed{$entry}->{'symb'}} = 1; + $to_delete{$entry} = 1; + } + } + if (keys(%to_delete)) { + my @removals = keys(%to_delete); + if (&Apache::lonnet::del('slot_reservations',\@removals, + $cdom,$cnum) eq 'ok') { + foreach my $item (keys(%symbs_for_slot)) { + my $result = &update_selectable($navmap,$slot_name,$item,$cdom, + $cnum,$udom,$uname,$context); + if ($result =~ /^error/) { + $failed{$item} = 1; + } else { + $released{$item} = 1; + } + } + } + } + if (keys(%released)) { + $msg = ''. + &mt('Released Reservation: [_1]',$description).' '. + &mt('The following items had their reservation status change').':'; + my (%folders,%pages,%container,%titles); + foreach my $item (keys(%released)) { + my $res = $navmap->getBySymb($item); + if (ref($res)) { + $titles{$item} = $res->title(); + if ($res->is_map()) { + $folders{$item}{'title'} = $titles{$item}; + if ($res->is_page()) { + $pages{$item}{'title'} = $titles{$item}; + } else { + $folders{$item}{'title'} = $titles{$item}; + } + } else { + my $mapsrc = $res->enclosing_map_src(); + my $map = $navmap->getResourceByUrl($mapsrc); + if (ref($map)) { + if ($map->id() eq '0.0') { + $container{$mapsrc}{'title'} &mt('Top level of course'); + } else { + $container{$mapsrc}{'title'} = $map->title(); + if ($map->is_page()) { + $container{$mapsrc}{'page'} = 1; + } + } + } + $container{$mapsrc}{'resources'}{$item} = 1; + } + } + } + $msg .= ''.&mt('Slot [_1] marked as deleted.',''.$slot_name.'').'
'); } else { - $r->print(''.&mt('An error occurred when attempting to delete slot: [_1]',''.$slot_name.'')." ($ret)
"); + $r->print(''.&mt('An error occurred when attempting to delete slot: [_1]',''.$slot_name.'')." ($ret)
"); } } else { if (%consumed) { @@ -830,15 +1163,32 @@ sub delete_slot { sub return_link { my ($r) = @_; + my $target = &return_target(); if (($env{'form.command'} eq 'manageresv') || ($env{'form.context'} eq 'usermanage')) { - $r->print(''.
- &mt('Return to reservations'));
+ $r->print(' '.
+ &mt('Return to reservations').' '.
+ $r->print(' '.
&mt('Return to last resource').' '
+ $r->print(' '
.&mt('An error occurred while attempting to make a reservation. ([_1])',$1)
- .' '
+ $r->print(' '
.&mt('An error occurred while attempting to make a reservation. ([_1])',$1)
- .'
'.&mt('Successfully signed up: [_1]',$description).'
'); $retvalue = 1; @@ -990,7 +1340,7 @@ sub allowed_slot { if (($slot->{'endreserve'}) && ($slot->{'endreserve'} < time)) { return 0; - } + } &Apache::lonxml::debug("$slot_name reserve good"); my $userallowed=0; @@ -1035,10 +1385,32 @@ sub allowed_slot { return 0 if (!$userallowed); # not allowed for this resource - if (defined($slot->{'symb'}) - && $slot->{'symb'} ne $symb) { - unless ((ref($toskip) eq 'HASH') && ($toskip->{'symb'})) { - return 0; + if (defined($slot->{'symb'})) { + my $exclude = 1; + my @symbs; + if ($slot->{'symb'} =~ /,/) { + @symbs = split(/\s*,\s*/,$slot->{'symb'}); + } else { + @symbs = ($slot->{'symb'}); + } + my ($map,$id,$url) = &Apache::lonnet::decode_symb($symb); + foreach my $reqsymb (@symbs) { + next if ($reqsymb eq ''); + my ($slotmap,$slotid,$sloturl) = &Apache::lonnet::decode_symb($reqsymb); + if ($sloturl=~/\.(page|sequence)$/) { + if (($map ne '') && ($map eq $sloturl)) { + $exclude = 0; + last; + } + } elsif ($reqsymb eq $symb) { + $exclude = 0; + last; + } + } + if ($exclude) { + unless ((ref($toskip) eq 'HASH') && ($toskip->{'symb'})) { + return 0; + } } } @@ -1067,7 +1439,7 @@ sub get_description { } sub show_choices { - my ($r,$symb,$formname,$num,$slots,$consumed_uniqueperiods,$available,$got_slots)=@_; + my ($symb,$formname,$num,$class,$slots,$consumed_uniqueperiods,$available,$got_slots)=@_; my $output; &Apache::lonxml::debug("Checking Slots"); if (!ref($available) eq 'ARRAY') { @@ -1076,11 +1448,15 @@ sub show_choices { if (!@{$available}) { $output = ''.&mt('No available times.').''; if ($env{'form.command'} ne 'manageresv') { - $output .= ' '. + my $target = &return_target(); + $output .= ' '. &mt('Return to last resource').''; } - $r->print($output); - return; + if ($class) { + return ''. &mt('Your reservation status for any such assignments is listed below:'). '
'. - ''.$spacers.''. + $icon.(' ' x6).' | '."\n"; + if (ref($output{$currmap}) eq 'HASH') { + my $formnum = $mapnum.'_'.$reservable+1; + my $class = 'LC_slotmaptext_'.$mapnum; + if ($output{$currmap}{'hasaction'}) { + $row .= ''. + $output{$currmap}{'msg'}. + ' | '. + &slot_chooser($repsymbs{$currmap},$class,$formnum, + $allavailable,$slots,$consumed_uniqueperiods). + ' | '; + } else { + $row .= ''. + $output{$currmap}{'msg'}. + ' | '; + } + $row .= ''."\n"; } - $r->print(' | ||||
'.$spacers.$icon.(' ' x6).' | '."\n"; } + $r->print($row); } + } + } elsif ($resource == $it->END_MAP()) { + $depth--; + $currcontainer = $parent{$depth}; + } elsif (ref($resource)) { + my $symb = $resource->symb(); + next if (!$resource->is_problem() && !$resource->is_tool() && + !$resource->is_sequence() && !$resource->is_page()); + $count ++; + if (($resource->is_sequence()) || ($resource->is_page())) { + $currcontainer = $count; + $container{$currcontainer} = $resource; + $container_title{$currcontainer} = $resource->compTitle(); + } + if ($resource->is_problem() || $resource->is_tool()) { + next unless (exists($output{$symb})); + $reservable ++; $rownum ++; - my $bgcolor = $backgrounds[$rownum % $numcolors]; if (!$shownheader) { $r->print($slotheader); $shownheader = 1; } - $r->print(' | |||||||
'."\n"); + my $style; + if (exists($output{$currmap})) { + $style = 'none'; + } else { + $style = 'table-row'; + $shown ++; + } + my $title = $resource->compTitle(); + my $bgcolor = $backgrounds[$shown % $numcolors]; + $r->print(' | ||||||||
'); for (my $i=0; $i<$depth; $i++) { $r->print(''); } - my $result = ''. - 'src().'?symb='.$symb.'">'. + ''.$title.''.(' ' x6).' | '; - my $hasaction; - if ($status == $resource->OPEN) { - if ($get_choices) { - $hasaction = 1; - } - } - if ($hasaction) { - $result .= ''.$msg.' | '. - ''; + $r->print('" />'.$title.''.(' ' x6).' | '); + my $class = 'LC_slottext_'.$mapnum; + if ($output{$symb}{'hasaction'}) { + $r->print(''.$output{$symb}{'msg'}.' | '. + ''. + &slot_chooser($symb,$class,$reservable,$allavailable,$slots, + $consumed_uniqueperiods).' | '); } else { - $result .= ''.$msg.' | '; - } - $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(''. + ''.$output{$symb}{'msg'}.''. + ' | '); } - $r->print(''; + my ($depth,$location,$type,$title) = @_; + my $spacers; for (my $i=0; $i<$depth-1; $i++) { - $output .= ''; + $spacers .= ''; } + my $icon; if ($type eq 'page') { - $output .= ' '."\n"; + $icon = ' '."\n"; } else { - $output .= ' '."\n"; + $icon = ' '."\n"; } - $output .= $title.' | '."\n"; - unshift (@{$maprows},$output); - return; + $icon .= $title; + return ($spacers,$icon); +} + +sub slot_chooser { + my ($symb,$class,$formnum,$allavailable,$slots,$consumed_uniqueperiods) = @_; + my $output; + my @got_slots=&check_for_reservation($symb,'allslots'); + if ($got_slots[0] =~ /^error: /) { + $output = ''. + &mt('An error occurred determining slot availability.'). + ''; + } else { + my $formname = 'manageres_'.$formnum; + 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 ($slots->{$slot}->{'symb'} ne '') { + my ($map,$id,$url) = &Apache::lonnet::decode_symb($symb); + my $exclude = 1; + my @reqsymbs = split(/\s*,\s*/,$slots->{$slot}->{'symb'}); + if (@reqsymbs) { + if (grep(/^\Q$symb\E$/,@reqsymbs)) { + $exclude = 0; + } else { + foreach my $reqsymb (@reqsymbs) { + my (undef,undef,$sloturl) = &Apache::lonnet::decode_symb($reqsymb); + if ($sloturl=~/\.(page|sequence)$/) { + if (($map ne '') && ($map eq $sloturl)) { + $exclude = 0; + last; + } + } + } + } + next if ($exclude); + } + } + } + push(@available,$slot); + } + } + $output .= &show_choices($symb,$formname,$formnum,$class, + $slots,$consumed_uniqueperiods, + \@available,\@got_slots); + } + } + return $output; } sub show_reservations { @@ -2014,8 +2512,7 @@ sub show_reservations { $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 ($cnum,$cdom)=&get_course(); my $crstype = &Apache::loncommon::course_type(); my %log=&Apache::lonnet::dump('nohist_'.$cdom.'_'.$cnum.'_slotlog',$udom,$uname); if ($env{'form.origin'} eq 'aboutme') { @@ -2190,8 +2687,7 @@ sub show_reservations_log { return; } my $formname = 'reservationslog'; - my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; - my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; + my ($cnum,$cdom)=&get_course(); my %slotlog=&Apache::lonnet::dump('nohist_slotreservationslog',$cdom,$cnum); if ((keys(%slotlog))[0]=~/^error\:/) { undef(%slotlog); } @@ -2420,7 +2916,7 @@ sub display_filter { my (%titles,%maptitles); my $output = ' |
'.
''.&mt('Changes/page:').' '. - &Apache::lonmeta::selectbox('show',$curr->{'show'},undef, + &Apache::lonmeta::selectbox('show',$curr->{'show'},'',undef, (&mt('all'),5,10,20,50,100,1000,10000)). ' | '; my $startform = @@ -2528,7 +3024,7 @@ sub slot_change_messaging { } sub upload_start { - my ($r)=@_; + my ($r)=@_; $r->print( &Apache::grades::checkforfile_js() .' |