File:  [LON-CAPA] / loncom / interface / slotrequest.pm
Revision 1.131: download - view: text, annotated - select for diffs
Mon Oct 5 02:35:45 2015 UTC (8 years, 7 months ago) by raeburn
Branches: MAIN
CVS tags: HEAD
- Slots can now require each student to check-in from a unique IP address
  when using a particular slot for a specific resource. The IP address of
  the student's computer is then tied to that student's access to the resource
  thereafter, until the end date of the slot.
  (Intended use case is high stakes online testing where students are using
  computers with statically assigned IP addresses).

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

package Apache::slotrequest;

use strict;
use Apache::Constants qw(:common :http :methods);
use Apache::loncommon();
use Apache::lonlocal;
use Apache::lonnet;
use Apache::lonnavmaps();
use Date::Manip;
use lib '/home/httpd/lib/perl/';
use LONCAPA qw(:DEFAULT :match);

sub fail {
    my ($r,$code)=@_;
    if ($code eq 'not_valid') {
	$r->print('<p>'.&mt('Unable to understand what resource you wanted to sign up for.').'</p>');
    } elsif ($code eq 'not_available') {
	$r->print('<p>'.&mt('No slots are available.').'</p>');
    } elsif ($code eq 'not_allowed') {
	$r->print('<p>'.&mt('Not allowed to sign up or change reservations at this time.').'</p>');
    } else {
	$r->print('<p>'.&mt('Failed.').'</p>');
    }
    
    &return_link($r);
    &end_page($r);
}

sub start_page {
    my ($r,$title,$brcrum,$js)=@_;
    my $args;
    if (ref($brcrum) eq 'ARRAY') {
        $args = {bread_crumbs => $brcrum};
    }
    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 {
    my ($r)=@_;
    $r->print(&Apache::loncommon::end_page());
}

sub reservation_js {
    my ($slots,$consumed_uniqueperiods,$available,$got_slots,$symb) = @_;
    return unless ((ref($slots) eq 'HASH') && (ref($available) eq 'ARRAY'));
    my $toskip;
    if ($symb eq '') {
        $toskip = { symb => 1, };
    }
    my ($i,$j) = (0,0);
    my $js;
    foreach my $slot (sort
        { return $slots->{$a}->{'starttime'} <=> $slots->{$b}->{'starttime'} }
                    (keys(%{$slots})))  {

        next if (!&allowed_slot($slot,$slots->{$slot},$symb,$slots,
                                $consumed_uniqueperiods,$toskip));
        $js .= "    slotstart[$i]='$slots->{$slot}->{'starttime'}';\n".
               "    slotend[$i]='$slots->{$slot}->{'endtime'}';\n".
               "    slotname[$i]='$slot';\n";
        if (($symb) && (ref($got_slots) eq 'ARRAY')) {
            if (grep(/^\Q$slot\E$/,@{$got_slots})) {
                $js .= "    currslot[$j]='$slot';\n";
                $j++;
            }
        }
        $i++;
        push(@{$available},$slot);
    }
    if ($j) {
        $js = "    var currslot = new Array($j);\n\n$js";
    }
    my %alerts = &Apache::lonlocal::texthash (
                                                none    => 'No reservable time slots found',
                                                invalid => 'Invalid date format',
                                             );
    return <<"ENDSCRIPT";
<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;  
        }
    }
}

function toggleSlotMap(maprownum,rownum) {
    if (document.getElementById('arrow'+maprownum)) {
        var img = document.getElementById('arrow'+maprownum);
        var rowdisplay;
        var celldisplay = '';
        if (img.src.indexOf('arrow.open.gif')!=-1) {
            img.src = '/adm/lonIcons/arrow.closed.gif';
            rowdisplay = 'none';
        }
        else {
            img.src = '/adm/lonIcons/arrow.open.gif';
            rowdisplay = 'table-row';
            celldisplay = 'none';
        }
        var resrows;
        var maptext;
        if (document.getElementsByClassName) {
            resrows = document.getElementsByClassName('LC_slotresrow_'+maprownum);
            maptext = document.getElementsByClassName('LC_slotmaptext_'+maprownum);
        } else {
            resrows = getElementsByClassName(document.body,'LC_slotresrow_'+maprownum);
            maptext = getElementsByClassName(document.body,'LC_slotmaptext_'+maprownum);
        }
        if (maptext.length) {
            for (var i=0; i<maptext.length; i++) {
                maptext[i].style.display = celldisplay;
            }
        }
        if (resrows.length) {
           var mapbgidx = 0;
           var rowclasses = ['LC_even_row','LC_odd_row'];
           var mapbgClass = 'LC_even_row';
           var regExpBg = /LC_odd_row/i;
           if (rowdisplay == 'table-row') {
                if (document.getElementById('LC_slotmaprow_'+rownum)) {
                    mapbgClass = document.getElementById('LC_slotmaprow_'+rownum).className;
                    if (regExpBg.test(mapbgClass)) {
                        mapbgidx = 1;
                    }
                }
            }
            for (var i=0; i<resrows.length; i++) {
                resrows[i].style.display = rowdisplay;
                if (rowdisplay == 'table-row') {
                    mapbgidx ++;        
                    var bgcolnew = mapbgidx % 2;
                    var bgcolold = (mapbgidx+1) % 2;
                    var k = i+parseInt(rownum)+1; 
                    if (document.getElementById('LC_slotresrow_'+k)) {
                        document.getElementById('LC_slotresrow_'+k).className = document.getElementById('LC_slotresrow_'+k).className.replace(rowclasses[bgcolold],rowclasses[bgcolnew]);
                    }
                }
            }
            if (document.getElementById('LC_slot_reservations')) {
                var numrowsOdd = resrows.length % 2;
                if (numrowsOdd) {
                    var lastbgClass = 'LC_even_row';
                    var idx = 0;
                    var lastresnum = parseInt(rownum) + resrows.length; 
                    if (rowdisplay == 'none') {
                        lastresnum = rownum;
                        if (document.getElementById('LC_slotmaprow_'+rownum)) {
                            lastbgClass = document.getElementById('LC_slotmaprow_'+rownum).className;
                        }
                    } else {
                        lastresnum = parseInt(rownum) + resrows.length;
                        if (document.getElementById('LC_slotresrow_'+lastresnum)) {
                            lastbgClass = document.getElementById('LC_slotresrow_'+lastresnum).className;
                        }
                    }
                    if (regExpBg.test(lastbgClass)) {
                        idx = 1;
                    }
                    var table = document.getElementById('LC_slot_reservations');
                    if ((table.rows.length) && (table.rows.length >= lastresnum)) {
                        for (var i=lastresnum; i<table.rows.length; i++) {
                            if (table.rows[i].style.display != 'none') {
                                idx ++;
                                var bgcolnew = idx % 2;
                                var bgcolold = (idx+1) % 2;  
                                j = i+1;
                                if (document.getElementById('LC_slotmaprow_'+j)) {
                                    document.getElementById('LC_slotmaprow_'+j).className = rowclasses[bgcolnew];
                                } else {
                                    if (document.getElementById('LC_slotresrow_'+j)) {
                                        document.getElementById('LC_slotresrow_'+j).className = document.getElementById('LC_slotresrow_'+j).className.replace(rowclasses[bgcolold],rowclasses[bgcolnew]);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
// ]]>
</script>
ENDSCRIPT

}


=pod

 slot_reservations db
   - keys are 
    - slotname\0id -> value is an hashref of
                         name -> user@domain of holder
                         timestamp -> timestamp of reservation
                         symb -> symb of resource that it is reserved for

=cut

sub get_course {
    (undef,my $courseid)=&Apache::lonnet::whichuser();
    my $cdom=$env{'course.'.$courseid.'.domain'};
    my $cnum=$env{'course.'.$courseid.'.num'};
    return ($cnum,$cdom);
}

sub get_reservation_ids {
    my ($slot_name)=@_;
    
    my ($cnum,$cdom)=&get_course();

    my %consumed=&Apache::lonnet::dump('slot_reservations',$cdom,$cnum,
				       "^$slot_name\0");
    if (&Apache::lonnet::error(%consumed)) { 
	return 'error: Unable to determine current status';
    }
    my ($tmp)=%consumed;
    if ($tmp=~/^error: 2 / ) {
	return 0;
    }
    return keys(%consumed);
}

sub space_available {
    my ($slot_name,$slot)=@_;
    my $max=$slot->{'maxspace'};

    if (!defined($max)) { return 1; }

    my $consumed=scalar(&get_reservation_ids($slot_name));
    if ($consumed < $max) {
	return 1
    }
    return 0;
}

sub check_for_reservation {
    my ($symb,$mode)=@_;
    my $student = &Apache::lonnet::EXT("resource.0.availablestudent", $symb,
				       $env{'user.domain'}, $env{'user.name'});
    my $course = &Apache::lonnet::EXT("resource.0.available", $symb,
				    $env{'user.domain'}, $env{'user.name'});
    my @slots = (split(/:/,$student), split(/:/, $course));

    &Apache::lonxml::debug(" slot list is ".join(':',@slots));

    my ($cnum,$cdom)=&get_course();
    my %slots=&Apache::lonnet::get('slots', [@slots], $cdom, $cnum);

    if (&Apache::lonnet::error($student) 
	|| &Apache::lonnet::error($course)
	|| &Apache::lonnet::error(%slots)) {
	return 'error: Unable to determine current status';
    }    
    my @got;
    my @sorted_slots = &Apache::loncommon::sorted_slots(\@slots,\%slots,'starttime');
    foreach my $slot_name (@sorted_slots) {
	next if (!defined($slots{$slot_name}) ||
		 !ref($slots{$slot_name}));
	&Apache::lonxml::debug(time." $slot_name ".
			       $slots{$slot_name}->{'starttime'}." -- ".
			       $slots{$slot_name}->{'startreserve'}." -- ".
                               $slots{$slot_name}->{'endreserve'});
	if (($slots{$slot_name}->{'endtime'} > time) &&
	    ($slots{$slot_name}->{'startreserve'} < time) &&
            ((!$slots{$slot_name}->{'endreserve'}) || 
             ($slots{$slot_name}->{'endreserve'} > time))) {
	    # between start of reservation time and end of reservation time
            # and before end of slot
	    if ($mode eq 'allslots') {
		push(@got,$slot_name);
	    } else {
		return($slot_name, $slots{$slot_name});
	    }
	}
    }
    if ($mode eq 'allslots' && @got) {
	return @got;
    }
    return (undef,undef);
}

sub get_consumed_uniqueperiods {
    my ($slots) = @_;
    my $navmap=Apache::lonnavmaps::navmap->new;
    if (!defined($navmap)) {
        return 'error: Unable to determine current status';
    }
    my @problems = $navmap->retrieveResources(undef,
					      sub { $_[0]->is_problem() },1,0);
    my %used_slots;
    foreach my $problem (@problems) {
	my $symb = $problem->symb();
	my $student = &Apache::lonnet::EXT("resource.0.availablestudent",
					   $symb, $env{'user.domain'},
					   $env{'user.name'});
	my $course =  &Apache::lonnet::EXT("resource.0.available",
					   $symb, $env{'user.domain'},
					   $env{'user.name'});
	if (&Apache::lonnet::error($student) 
	    || &Apache::lonnet::error($course)) {
	    return 'error: Unable to determine current status';
	}
	foreach my $slot (split(/:/,$student), split(/:/, $course)) {
	    $used_slots{$slot}=1;
	}
    }

    if (!ref($slots)) {
	my ($cnum,$cdom)=&get_course();
	my %slots=&Apache::lonnet::get('slots', [keys(%used_slots)], $cdom, $cnum);
	if (&Apache::lonnet::error(%slots)) {
	    return 'error: Unable to determine current status';
	}
	$slots = \%slots;
    }

    my %consumed_uniqueperiods;
    foreach my $slot_name (keys(%used_slots)) {
	next if (!defined($slots->{$slot_name}) ||
		 !ref($slots->{$slot_name}));
	
        next if (!defined($slots->{$slot_name}{'uniqueperiod'}) ||
		 !ref($slots->{$slot_name}{'uniqueperiod'}));
	$consumed_uniqueperiods{$slot_name} = 
	    $slots->{$slot_name}{'uniqueperiod'};
    }
    return \%consumed_uniqueperiods;
}

sub check_for_conflict {
    my ($symb,$new_slot_name,$new_slot,$slots,$consumed_uniqueperiods)=@_;

    if (!defined($new_slot->{'uniqueperiod'})) { return undef; }

    if (!ref($consumed_uniqueperiods)) {
        if ($consumed_uniqueperiods =~ /^error: /) {
            return $consumed_uniqueperiods;
        } else {
	    $consumed_uniqueperiods = &get_consumed_uniqueperiods($slots);
            if (ref($consumed_uniqueperiods) eq 'HASH') {
	        if (&Apache::lonnet::error(%$consumed_uniqueperiods)) {
	            return 'error: Unable to determine current status';
	        }
            } else {
                return 'error: Unable to determine current status';
            }
        }
    } 
    my ($new_uniq_start,$new_uniq_end) = @{$new_slot->{'uniqueperiod'}};
    foreach my $slot_name (keys(%$consumed_uniqueperiods)) {
	my ($start,$end)=@{$consumed_uniqueperiods->{$slot_name}};
	if (!
	    ($start < $new_uniq_start &&  $end < $new_uniq_start) ||
	    ($start > $new_uniq_end   &&  $end > $new_uniq_end  )) {
	    return $slot_name;
	}
    }
    return undef;
}

sub make_reservation {
    my ($slot_name,$slot,$symb,$cnum,$cdom)=@_;

    my $value=&Apache::lonnet::EXT("resource.0.availablestudent",$symb,
				   $env{'user.domain'},$env{'user.name'});
    &Apache::lonxml::debug("value is  $value<br />");

    my $use_slots = &Apache::lonnet::EXT("resource.0.useslots",$symb,
					 $env{'user.domain'},$env{'user.name'});
    &Apache::lonxml::debug("use_slots is  $use_slots<br />");

    if (&Apache::lonnet::error($value) 
	|| &Apache::lonnet::error($use_slots)) { 
	return 'error: Unable to determine current status';
    }

    my $parm_symb  = $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);
	$parm_level = 2;
    }

    foreach my $other_slot (split(/:/, $value)) {
	if ($other_slot eq $slot_name) {
	    my %consumed=&Apache::lonnet::dump('slot_reservations', $cdom,
					       $cnum, "^$slot_name\0");   
	    if (&Apache::lonnet::error($value)) { 
		return 'error: Unable to determine current status';
	    }
	    my $me=$env{'user.name'}.':'.$env{'user.domain'};
	    foreach my $key (keys(%consumed)) {
		if ($consumed{$key}->{'name'} eq $me) {
		    my $num=(split('\0',$key))[1];
		    return -$num;
		}
	    }
	}
    }

    my $max=$slot->{'maxspace'};
    if (!defined($max)) { $max=99999; }

    my (@ids)=&get_reservation_ids($slot_name);
    if (&Apache::lonnet::error(@ids)) { 
	return 'error: Unable to determine current status';
    }
    my $last=0;
    foreach my $id (@ids) {
	my $num=(split('\0',$id))[1];
	if ($num > $last) { $last=$num; }
    }
    
    my $wanted=$last+1;
    &Apache::lonxml::debug("wanted $wanted<br />");
    if (scalar(@ids) >= $max) {
	# full up
	return undef;
    }
    
    my %reservation=('name'      => $env{'user.name'}.':'.$env{'user.domain'},
		     'timestamp' => time,
		     'symb'      => $parm_symb);

    my $success=&Apache::lonnet::newput('slot_reservations',
					{"$slot_name\0$wanted" =>
					     \%reservation},
					$cdom, $cnum);

    if ($success eq 'ok') {
	my $new_value=$slot_name;
	if ($value) {
	    $new_value=$value.':'.$new_value;
	}
        &store_slot_parm($symb,$slot_name,$parm_level,$new_value,$cnum,$cdom);
	return $wanted;
    }

    # someone else got it
    return undef;
}

sub store_slot_parm {
    my ($symb,$slot_name,$parm_level,$new_value,$cnum,$cdom) = @_;
    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 %storehash = (
                       symb    => $symb,
                       slot    => $slot_name,
                       action  => 'reserve',
                       context => $env{'form.context'},
                    );

    &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;
}

sub remove_registration {
    my ($r) = @_;
    if ($env{'form.entry'} ne 'remove all') {
	return &remove_registration_user($r);
    }
    my $slot_name = $env{'form.slotname'};
    my %slot=&Apache::lonnet::get_slot($slot_name);

    my ($cnum,$cdom)=&get_course();
    my %consumed=&Apache::lonnet::dump('slot_reservations',$cdom,$cnum,
				       "^$slot_name\0");
    if (&Apache::lonnet::error(%consumed)) {
	$r->print("<p><span class=\"LC_error\">".&mt('A network error has occurred.').'</span></p>');
	return;
    }
    if (!%consumed) {
	$r->print('<p>'.&mt('Slot [_1] has no reservations.',
			    '<tt>'.$slot_name.'</tt>').'</p>');
	return;
    }

    my @names = map { $consumed{$_}{'name'} } (sort(keys(%consumed)));
    my $names = join(' ',@names);

    my $msg = &mt('Remove all of [_1] from slot [_2]?',$names,$slot_name);
    &remove_registration_confirmation($r,$msg,['entry','slotname','context']);
}

sub remove_registration_user {
    my ($r) = @_;
    
    my $slot_name = $env{'form.slotname'};

    my $name = &Apache::loncommon::plainname($env{'form.uname'},
					     $env{'form.udom'});

    my $title = &Apache::lonnet::gettitle($env{'form.symb'});

    my $msg = &mt('Remove [_1] from slot [_2] for [_3]',
		  $name,$slot_name,$title);
    
    &remove_registration_confirmation($r,$msg,['uname','udom','slotname',
					       'entry','symb','context']);
}

sub remove_registration_confirmation {
    my ($r,$msg,$inputs) =@_;

    my $hidden_input;
    foreach my $parm (@{$inputs}) {
	$hidden_input .=
	    '<input type="hidden" name="'.$parm.'" value="'
	    .&HTML::Entities::encode($env{'form.'.$parm},'"<>&\'').'" />'."\n";
    }
    my %lt = &Apache::lonlocal::texthash(
        'yes' => 'Yes',
        'no'  => 'No',
    );
    $r->print(<<"END_CONFIRM");
<p> $msg </p>
<form action="/adm/slotrequest" method="post">
    <input type="hidden" name="command" value="release" />
    <input type="hidden" name="button" value="yes" />
    $hidden_input
    <input type="submit" value="$lt{'yes'}" />
</form>
<form action="/adm/slotrequest" method="post">
    <input type="hidden" name="command" value="showslots" />
    <input type="submit" value="$lt{'no'}" />
</form>
END_CONFIRM

}

sub release_all_slot {
    my ($r,$mgr)=@_;
    
    my $slot_name = $env{'form.slotname'};

    my ($cnum,$cdom)=&get_course();

    my %consumed=&Apache::lonnet::dump('slot_reservations',$cdom,$cnum,
				       "^$slot_name\0");
    
    $r->print('<p>'.&mt('Releasing reservations').'</p>');

    foreach my $entry (sort { $consumed{$a}{'name'} cmp 
				  $consumed{$b}{'name'} } (keys(%consumed))) {
	my ($uname,$udom) = split(':',$consumed{$entry}{'name'});
	my ($result,$msg) =
	    &release_reservation($slot_name,$uname,$udom,
				 $consumed{$entry}{'symb'},$mgr);
        if (!$result) {
            $r->print('<p><span class="LC_error">'.&mt($msg).'</span></p>');
        } else {
	    $r->print("<p>$msg</p>");
        }
	$r->rflush();
    }
    $r->print('<p><a href="/adm/slotrequest?command=showslots">'.
	      &mt('Return to slot list').'</a></p>');
    &return_link($r);
}

sub release_slot {
    my ($r,$symb,$slot_name,$inhibit_return_link,$mgr)=@_;

    if ($slot_name eq '') { $slot_name=$env{'form.slotname'}; }

    my ($uname,$udom) = ($env{'user.name'}, $env{'user.domain'});
    if ($mgr eq 'F' 
	&& defined($env{'form.uname'}) && defined($env{'form.udom'})) {
	($uname,$udom) = ($env{'form.uname'}, $env{'form.udom'});
    }

    if ($mgr eq 'F' 
	&& defined($env{'form.symb'})) {
	$symb = &unescape($env{'form.symb'});
    }

    my ($result,$msg) =
	&release_reservation($slot_name,$uname,$udom,$symb,$mgr);
    if (!$result) {
        $r->print('<p><span class="LC_error">'.&mt($msg).'</span></p>');
    } else {
        $r->print("<p>$msg</p>");
    }
    
    if ($mgr eq 'F') {
	$r->print('<p><a href="/adm/slotrequest?command=showslots">'.
		  &mt('Return to slot list').'</a></p>');
    }

    if (!$inhibit_return_link) { &return_link($r);  }
    return $result;
}

sub release_reservation {
    my ($slot_name,$uname,$udom,$symb,$mgr) = @_;
    my %slot=&Apache::lonnet::get_slot($slot_name);
    my $description=&get_description($slot_name,\%slot);

    if ($mgr ne 'F') {
	if ($slot{'starttime'} < time) {
	    return (0,&mt('Not allowed to release Reservation: [_1], as it has already ended.',$description));
	}
    }

    # if the reservation symb is for a map get a resource in that map
    # to check slot parameters on
    my $navmap=Apache::lonnavmaps::navmap->new;
    if (!defined($navmap)) {
        return (0,'error: Unable to determine current status');
    }
    my $passed_resource = $navmap->getBySymb($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 existence, rebuild string with the slot
    my $student = &Apache::lonnet::EXT("resource.0.availablestudent",
                                       $symb,$udom,$uname);
    my @slots = split(/:/,$student);

    my @new_slots;
    foreach my $exist_slot (@slots) {
	if ($exist_slot eq $slot_name) { next; }
	push(@new_slots,$exist_slot);
    }
    my $new_param = join(':',@new_slots);

    my ($cnum,$cdom)=&get_course();

    # get slot reservations, check if user has one, if so remove reservation
    my %consumed=&Apache::lonnet::dump('slot_reservations',$cdom,$cnum,
				       "^$slot_name\0");
    foreach my $entry (keys(%consumed)) {
	if ( $consumed{$entry}->{'name'} eq ($uname.':'.$udom) ) {
	    &Apache::lonnet::del('slot_reservations',[$entry],
				 $cdom,$cnum);
            my %storehash = (
                               symb    => $symb,
                               slot    => $slot_name,
                               action  => 'release',
                               context => $env{'form.context'},
                        );
            &Apache::lonnet::write_log('slotreservationslog',\%storehash,
                                       1,$uname,$udom,$cnum,$cdom);
            &Apache::lonnet::write_log($cdom.'_'.$cnum.'_slotlog',\%storehash,
                                       1,$uname,$udom,$uname,$udom);
	}
    }

    my $use_slots = &Apache::lonnet::EXT("resource.0.useslots",
					 $symb,$udom,$uname);
    &Apache::lonxml::debug("use_slots is  $use_slots<br />");

    if (&Apache::lonnet::error($use_slots)) { 
	return (0,'error: Unable to determine current status');
    }

    my $parm_level = 1;
    if ($use_slots eq 'map' || $use_slots eq 'map_map') {
	$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 = '<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);
}

sub delete_slot {
    my ($r)=@_;

    my $slot_name = $env{'form.slotname'};
    my %slot=&Apache::lonnet::get_slot($slot_name);

    my ($cnum,$cdom)=&get_course();
    my %consumed=&Apache::lonnet::dump('slot_reservations',$cdom,$cnum,
				       "^$slot_name\0");
    my ($tmp) = %consumed;
    if ($tmp =~ /error: 2/) { undef(%consumed); }

    if (%slot && !%consumed) {
	$slot{'type'} = 'deleted';
	my $ret = &Apache::lonnet::cput('slots', {$slot_name => \%slot},
					$cdom, $cnum);
	if ($ret eq 'ok') {
	    $r->print('<p>'.&mt('Slot [_1] marked as deleted.','<tt>'.$slot_name.'</tt>').'</p>');
	} else {
	    $r->print('<p><span class="LC_error">'.&mt('An error occurred when attempting to delete slot: [_1]','<tt>'.$slot_name.'</tt>')." ($ret)</span></p>");
	}
    } else {
	if (%consumed) {
	    $r->print('<p>'.&mt('Slot [_1] has active reservations.','<tt>'.$slot_name.'</tt>').'</p>');
	} else {
	    $r->print('<p>'.&mt('Slot [_1] does not exist.','<tt>'.$slot_name.'</tt>').'</p>');
	}
    }
    $r->print('<p><a href="/adm/slotrequest?command=showslots">'.
	      &mt('Return to slot list').'</a></p>');
    &return_link($r);
}

sub return_link {
    my ($r) = @_;
    if (($env{'form.command'} eq 'manageresv') || ($env{'form.context'} eq 'usermanage')) {
	$r->print('<p><a href="/adm/slotrequest?command=manageresv">'.
                  &mt('Return to reservations'));  
    } else {
        $r->print('<p><a href="/adm/flip?postdata=return:">'.
	          &mt('Return to last resource').'</a></p>');
    }
}

sub get_slot {
    my ($r,$symb,$conflictable_slot,$inhibit_return_link)=@_;

    my %slot=&Apache::lonnet::get_slot($env{'form.slotname'});
    my $slot_name=&check_for_conflict($symb,$env{'form.slotname'},\%slot);

    if ($slot_name =~ /^error: (.*)/) {
	$r->print('<p><span class="LC_error">'
                 .&mt('An error occurred while attempting to make a reservation. ([_1])',$1)
                 .'</span></p>');
	&return_link($r);
	return 0;
    }
    if ($slot_name && $slot_name ne $conflictable_slot) {
	my %slot=&Apache::lonnet::get_slot($slot_name);
	my $description1=&get_description($slot_name,\%slot);
	%slot=&Apache::lonnet::get_slot($env{'form.slotname'});
	my $description2=&get_description($env{'form.slotname'},\%slot);
	if ($slot_name ne $env{'form.slotname'}) {
	    $r->print(<<STUFF);
<form method="post" action="/adm/slotrequest">
   <input type="hidden" name="symb" value="$env{'form.symb'}" />
   <input type="hidden" name="slotname" value="$env{'form.slotname'}" />
   <input type="hidden" name="releaseslot" value="$slot_name" />
   <input type="hidden" name="command" value="change" />
STUFF
            $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').'" />' 
                         .('&nbsp;'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;
    }

    my ($cnum,$cdom)=&get_course();
    my $reserved=&make_reservation($env{'form.slotname'},
				   \%slot,$symb,$cnum,$cdom);
    my $description=&get_description($env{'form.slotname'},\%slot);
    if (defined($reserved)) {
	my $retvalue = 0;
	if ($slot_name =~ /^error: (.*)/) {
	    $r->print('<p><span class="LC_error">'
                     .&mt('An error occurred while attempting to make a reservation. ([_1])',$1)
                     .'</span></p>');
	} elsif ($reserved > -1) {
	    $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>');
	}
	if (!$inhibit_return_link) { &return_link($r); }
	return 1;
    }

    my %lt = &Apache::lonlocal::texthash(
        'request' => 'Availability list',
        'try'     => 'Try again?',
        'or'      => 'or',
    );

    my $extra_input;
    if ($conflictable_slot) {
	$extra_input='<input type="hidden" name="releaseslot" value="'.$env{'form.slotname'}.'" />';
    }

    $r->print('<p>'.&mt('[_1]Failed[_2] to reserve a slot for [_3].','<span class="LC_warning">','</span>',$description).'</p>');
    $r->print(<<STUFF);
<p>
<form method="post" action="/adm/slotrequest">
   <input type="submit" name="Try Again" value="$lt{'try'}" />
   <input type="hidden" name="symb" value="$env{'form.symb'}" />
   <input type="hidden" name="slotname" value="$env{'form.slotname'}" />
   <input type="hidden" name="command" value="$env{'form.command'}" />
   $extra_input
</form>
</p>
<p>
$lt{'or'}
<form method="post" action="/adm/slotrequest">
    <input type="hidden" name="symb" value="$env{'form.symb'}" />
    <input type="submit" name="requestattempt" value="$lt{'request'}" />
</form>
STUFF

    if (!$inhibit_return_link) { 
        $r->print(&mt('or').'</p>');
        &return_link($r);
    } else {
        $r->print('</p>');
    }
    return 0;
}

sub allowed_slot {
    my ($slot_name,$slot,$symb,$slots,$consumed_uniqueperiods,$toskip)=@_;

    #already started
    if ($slot->{'starttime'} < time) {
	return 0;
    }
    &Apache::lonxml::debug("$slot_name starttime good");

    #already ended
    if ($slot->{'endtime'} < time) {
	return 0;
    }
    &Apache::lonxml::debug("$slot_name endtime good");

    # not allowed to pick this one
    if (defined($slot->{'type'})
	&& $slot->{'type'} ne 'schedulable_student') {
	return 0;
    }
    &Apache::lonxml::debug("$slot_name type good");

    # reserve time not yet started
    if ($slot->{'startreserve'} > time) {
	return 0;
    }
    # reserve time ended
    if (($slot->{'endreserve'}) &&
        ($slot->{'endreserve'} < time)) {
        return 0;
    }    
    &Apache::lonxml::debug("$slot_name reserve good");

    my $userallowed=0;
    # its for a different set of users
    if (defined($slot->{'allowedsections'})) {
	if (!defined($env{'request.role.sec'})
	    && grep(/^No section assigned$/,
		    split(',',$slot->{'allowedsections'}))) {
	    $userallowed=1;
	}
	if (defined($env{'request.role.sec'})
	    && grep(/^\Q$env{'request.role.sec'}\E$/,
		    split(',',$slot->{'allowedsections'}))) {
	    $userallowed=1;
	}
	if (defined($env{'request.course.groups'})) {
	    my @groups = split(/:/,$env{'request.course.groups'});
	    my @allowed_sec = split(',',$slot->{'allowedsections'});
	    foreach my $group (@groups) {
		if (grep {$_ eq $group} (@allowed_sec)) {
		    $userallowed=1;
		    last;
		}
	    }
	}
    }
    &Apache::lonxml::debug("$slot_name sections is $userallowed");

    # its for a different set of users
    if (defined($slot->{'allowedusers'})
	&& grep(/^\Q$env{'user.name'}:$env{'user.domain'}\E$/,
		split(',',$slot->{'allowedusers'}))) {
	$userallowed=1;
    }

    if (!defined($slot->{'allowedusers'})
	&& !defined($slot->{'allowedsections'})) {
	$userallowed=1;
    }

    &Apache::lonxml::debug("$slot_name user is $userallowed");
    return 0 if (!$userallowed);

    # not allowed for this resource
    if (defined($slot->{'symb'})) {
        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;
            }
        }
    }

    my $conflict = &check_for_conflict($symb,$slot_name,$slot,$slots,
				       $consumed_uniqueperiods);
    if ($conflict =~ /^error: /) {
        return 0;
    } elsif ($conflict ne '') {
	if ($slots->{$conflict}{'starttime'} < time) {
	    return 0;
	}
    }
    &Apache::lonxml::debug("$slot_name symb good");
    return 1;
}

sub get_description {
    my ($slot_name,$slot)=@_;
    my $description=$slot->{'description'};
    if (!defined($description)) {
	$description=&mt('[_1] From [_2] to [_3]',$slot_name,
			 &Apache::lonlocal::locallocaltime($slot->{'starttime'}),
			 &Apache::lonlocal::locallocaltime($slot->{'endtime'}));
    }
    return $description;
}

sub show_choices {
    my ($symb,$formname,$num,$class,$slots,$consumed_uniqueperiods,$available,$got_slots)=@_;
    my $output;
    &Apache::lonxml::debug("Checking Slots");
    if (!ref($available) eq 'ARRAY') {
        return;
    }
    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>';
        }
        if ($class) {
            return '<div class="'.$class.'">'.$output.'</div>';
        } else {
            return $output;
        }
    }
    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>'.('&nbsp;' 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">';
    } else {
        $output .= &Apache::loncommon::start_data_table();
    }
    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)) {
	    my $text=&mt('Select');
	    my $command='get';
	    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);
                if ($conflict) {
                    if ($conflict =~ /^error: /) {
                        $form = '<span class="LC_error">'.
                                 &mt('Slot: [_1] has unknown status.',$description).
                                 '</span>';
                    } else {
		        $text=&mt('Change Reservation');
		        $command='get';
		    }
                }
	    }
	    my $escsymb=&escape($symb);
            if (!$form) {
                my $name;
                if ($formname) {
                     $name = 'name="'.$formname.'"';
                }
                my $context = 'user';
                if ($env{'form.command'} eq 'manageresv') {
                    $context = 'usermanage';
                }
	        $form=<<STUFF;
   <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" />
     <input type="hidden" name="command" value="$command" />
     <input type="hidden" name="context" value="$context" />
   </form>
STUFF
	    }
        } else {
            $form = &mt('Unavailable');
        }
        if ($env{'form.command'} eq 'manageresv') {
            $output .= '<tr id="LC_slotrow_'.$num.'_'.$slot.'" >';
        } else {
	    $output .= &Apache::loncommon::start_data_table_row('','LC_slotrow_'.$num.'_'.$slot);
        }
        $output .= " 
 <td>$form</td>
 <td>$description</td>\n";
        if ($env{'form.command'} eq 'manageresv') {
            $output .= '</tr>';
        } else {
            $output .= &Apache::loncommon::end_data_table_row();
        }
    }
    if ($env{'form.command'} eq 'manageresv') {
        $output .= '</table>';
    } else {
        $output .= &Apache::loncommon::end_data_table();
    }
    if (@{$available} > 1) {
        $output .= '</div></fieldset>';
    }
    if ($class) {
        return '<div class="'.$class.'">'.$output.'</div>';
    } else {
        return $output;
    }
}

sub to_show {
    my ($slotname,$slot,$when,$deleted,$name) = @_;
    my $time=time;
    my $week=60*60*24*7;

    if ($deleted eq 'hide' && $slot->{'type'} eq 'deleted') {
	return 0;
    }

    if ($name && $name->{'value'} =~ /\w/) {
	if ($name->{'type'} eq 'substring') {
	    if ($slotname !~ /\Q$name->{'value'}\E/) {
		return 0;
	    }
	}
	if ($name->{'type'} eq 'exact') {
	    if ($slotname eq $name->{'value'}) {
		return 0;
	    }
	}
    }

    if ($when eq 'any') {
	return 1;
    } elsif ($when eq 'now') {
	if ($time > $slot->{'starttime'} &&
	    $time < $slot->{'endtime'}) {
	    return 1;
	}
	return 0;
    } elsif ($when eq 'nextweek') {
	if ( ($time        < $slot->{'starttime'} &&
	      ($time+$week) > $slot->{'starttime'})
	     ||
	     ($time        < $slot->{'endtime'} &&
	      ($time+$week) > $slot->{'endtime'}) ) {
	    return 1;
	}
	return 0;
    } elsif ($when eq 'lastweek') {
	if ( ($time        > $slot->{'starttime'} &&
	      ($time-$week) < $slot->{'starttime'})
	     ||
	     ($time        > $slot->{'endtime'} &&
	      ($time-$week) < $slot->{'endtime'}) ) {
	    return 1;
	}
	return 0;
    } elsif ($when eq 'willopen') {
	if ($time < $slot->{'starttime'}) {
	    return 1;
	}
	return 0;
    } elsif ($when eq 'wereopen') {
	if ($time > $slot->{'endtime'}) {
	    return 1;
	}
	return 0;
    }
    
    return 1;
}

sub remove_link {
    my ($slotname,$entry,$uname,$udom,$symb) = @_;

    my $remove = &mt('Remove');

    if ($entry eq 'remove all') {
	$remove = &mt('Remove All');
	undef($uname);
	undef($udom);
    }

    $slotname  = &escape($slotname);
    $entry     = &escape($entry);
    $uname     = &escape($uname);
    $udom      = &escape($udom);
    $symb      = &escape($symb);

    return <<"END_LINK";
 <a href="/adm/slotrequest?command=remove_registration&amp;slotname=$slotname&amp;entry=$entry&amp;uname=$uname&amp;udom=$udom&amp;symb=$symb&amp;context=manage"
   >($remove)</a>
END_LINK

}

sub show_table {
    my ($r,$mgr)=@_;

    my ($cnum,$cdom)=&get_course();
    my $crstype=&Apache::loncommon::course_type($cdom.'_'.$cnum);
    my %slots=&Apache::lonnet::dump('slots',$cdom,$cnum);
    if ( (keys(%slots))[0] =~ /^error: 2 /) {
	undef(%slots);
    } 
    my $available;
    if ($mgr eq 'F') {
    # FIXME: This line should be deleted once Slots uses breadcrumbs
    $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">
<input type="hidden" name="command" value="uploadstart" />
<input type="submit" name="start" value="'.&mt('Upload Slot List').'" />
</form>');
	$r->print(&Apache::loncommon::help_open_topic('Slot CommaDelimited'));
	$r->print('<form method="post" action="/adm/helper/newslot.helper">
<input type="submit" name="newslot" value="'.&mt('Create a New Slot').'" />
</form>');
	$r->print(&Apache::loncommon::help_open_topic('Slot AddInterface'));
	$r->print('</div>');
    }

    if (!keys(%slots)) {
        $r->print(
            '<p class="LC_info">'
           .&mt('No slots have been created in this '.lc($crstype).'.')
           .'</p>'
        );
        return;
    }
    
    my %Saveable_Parameters = ('show'              => 'array',
			       'when'              => 'scalar',
			       'order'             => 'scalar',
			       'deleted'           => 'scalar',
			       'name_filter_type'  => 'scalar',
			       'name_filter_value' => 'scalar',
			       );
    &Apache::loncommon::store_course_settings('slotrequest',
					      \%Saveable_Parameters);
    &Apache::loncommon::restore_course_settings('slotrequest',
						\%Saveable_Parameters);
    &Apache::grades::init_perm();
    my ($classlist,$section,$fullname)=&Apache::grades::getclasslist('all');
    &Apache::grades::reset_perm();

    # what to display filtering
    my %show_fields=&Apache::lonlocal::texthash(
	     'name'            => 'Slot Name',
	     'description'     => 'Description',
	     'type'            => 'Type',
	     'starttime'       => 'Start time',
	     'endtime'         => 'End Time',
             'startreserve'    => 'Time students can start reserving',
             'endreserve'      => 'Time students can no longer reserve',
             'reservationmsg'  => 'Message triggered by reservation',
	     'secret'          => 'Secret Word',
	     'space'           => '# of students/max',
	     'ip'              => 'IP or DNS restrictions',
	     'symb'            => 'Resource(s)/Map(s) slot is restricted to.',
	     'allowedsections' => 'Sections slot is restricted to.',
	     'allowedusers'    => 'Users slot is restricted to.',
	     'uniqueperiod'    => 'Period of time slot is unique',
	     'scheduled'       => 'Scheduled Students',
	     'proctor'         => 'List of proctors',
	     'iptied'          => 'Unique IP each student',);
    if ($crstype eq 'Community') {
        $show_fields{'startreserve'} = &mt('Time members can start reserving');
        $show_fields{'endreserve'} = &mt('Time members can no longer reserve');
        $show_fields{'scheduled'} = &mt('Scheduled Members');
    }
    my @show_order=('name','description','type','starttime','endtime',
		    'startreserve','endreserve','reservationmsg','secret','space',
		    'ip','iptied','symb','allowedsections','allowedusers','uniqueperiod',
		    'scheduled','proctor');
    my @show = 
	(exists($env{'form.show'})) ? &Apache::loncommon::get_env_multiple('form.show')
	                            : keys(%show_fields);
    my %show =  map { $_ => 1 } (@show);

    #when filtering setup
    my %when_fields=&Apache::lonlocal::texthash(
	     'now'      => 'Open now',
	     'nextweek' => 'Open within the next week',
	     'lastweek' => 'Were open last week',
	     'willopen' => 'Will open later',
	     'wereopen' => 'Were open',
	     'any'      => 'Anytime',
						);
    my @when_order=('any','now','nextweek','lastweek','willopen','wereopen');
    $when_fields{'select_form_order'} = \@when_order;
    my $when = 	(exists($env{'form.when'})) ? $env{'form.when'}
                                            : 'now';

    #display of students setup
    my %stu_display_fields=
	&Apache::lonlocal::texthash('username' => 'User name',
				    'fullname' => 'Full name',
				    );
    my @stu_display_order=('fullname','username');
    my @stu_display = 
	(exists($env{'form.studisplay'})) ? &Apache::loncommon::get_env_multiple('form.studisplay')
	                                  : keys(%stu_display_fields);
    my %stu_display =  map { $_ => 1 } (@stu_display);

    #name filtering setup
    my %name_filter_type_fields=
	&Apache::lonlocal::texthash('substring' => 'Substring',
				    'exact'     => 'Exact',
				    #'reg'       => 'Regular Expression',
				    );
    my @name_filter_type_order=('substring','exact');

    $name_filter_type_fields{'select_form_order'} = \@name_filter_type_order;
    my $name_filter_type = 
	(exists($env{'form.name_filter_type'})) ? $env{'form.name_filter_type'}
                                                : 'substring';
    my $name_filter = {'type'  => $name_filter_type,
		       'value' => $env{'form.name_filter_value'},};

    
    #deleted slot filtering
    #default to hide if no value
    $env{'form.deleted'} ||= 'hide';
    my $hide_radio = 
	&Apache::lonhtmlcommon::radio('deleted',$env{'form.deleted'},'hide');
    my $show_radio = 
	&Apache::lonhtmlcommon::radio('deleted',$env{'form.deleted'},'show');
	
    $r->print('<form method="post" action="/adm/slotrequest">
<input type="hidden" name="command" value="showslots" />');
    $r->print('<div>');
    $r->print('<table class="inline">
      <tr><th>'.&mt('Show').'</th>
          <th>'.&mt('Student Display').'</th>
          <th>'.&mt('Open').'</th>
          <th>'.&mt('Slot Name Filter').'</th>
          <th>'.&mt('Options').'</th>
      </tr>
      <tr><td valign="top">'.&Apache::loncommon::multiple_select_form('show',\@show,6,\%show_fields,\@show_order).
	      '</td>
           <td valign="top">
         '.&Apache::loncommon::multiple_select_form('studisplay',\@stu_display,
						    6,\%stu_display_fields,
						    \@stu_display_order).'
           </td>
           <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).
	      '<br />'.
	      &Apache::lonhtmlcommon::textbox('name_filter_value',
					      $env{'form.name_filter_value'},
					      15).
          '</td>
           <td valign="top">
            <table>
              <tr>
                <td rowspan="2">'.&mt('Deleted slots:').'</td>
                <td><label>'.$show_radio.&mt('Show').'</label></td>
              </tr>
              <tr>
                <td><label>'.$hide_radio.&mt('Hide').'</label></td>
              </tr>
            </table>
	  </td>
       </tr>
    </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&amp;order=';
    $r->print(&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>');
	}
    }
    $r->print(&Apache::loncommon::end_data_table_header_row());

    my %name_cache;
    my $slotsort = sub {
	if ($env{'form.order'}=~/^(type|description|endtime|startreserve|endreserve|ip|symb|allowedsections|allowedusers|reservationmsg)$/) {
	    if (lc($slots{$a}->{$env{'form.order'}})
		ne lc($slots{$b}->{$env{'form.order'}})) {
		return (lc($slots{$a}->{$env{'form.order'}}) 
			cmp lc($slots{$b}->{$env{'form.order'}}));
	    }
	} elsif ($env{'form.order'} eq 'space') {
	    if ($slots{$a}{'maxspace'} ne $slots{$b}{'maxspace'}) {
		return ($slots{$a}{'maxspace'} cmp $slots{$b}{'maxspace'});
	    }
	} elsif ($env{'form.order'} eq 'name') {
	    if (lc($a) cmp lc($b)) {
		return lc($a) cmp lc($b);
	    }
	} elsif ($env{'form.order'} eq 'uniqueperiod') {
	    
	    if ($slots{$a}->{'uniqueperiod'}[0] 
		ne $slots{$b}->{'uniqueperiod'}[0]) {
		return ($slots{$a}->{'uniqueperiod'}[0]
			cmp $slots{$b}->{'uniqueperiod'}[0]);
	    }
	    if ($slots{$a}->{'uniqueperiod'}[1] 
		ne $slots{$b}->{'uniqueperiod'}[1]) {
		return ($slots{$a}->{'uniqueperiod'}[1]
			cmp $slots{$b}->{'uniqueperiod'}[1]);
	    }
	}
	return $slots{$a}->{'starttime'} <=> $slots{$b}->{'starttime'};
    };

    my %consumed;
    if (exists($show{'scheduled'}) || exists($show{'space'}) ) {
	%consumed=&Apache::lonnet::dump('slot_reservations',$cdom,$cnum);
	my ($tmp)=%consumed;
	if ($tmp =~ /^error: /) { undef(%consumed); }
    }

    my %msgops = &slot_reservationmsg_options();

    foreach my $slot (sort $slotsort (keys(%slots)))  {
	if (!&to_show($slot,$slots{$slot},$when,
		      $env{'form.deleted'},$name_filter)) { next; }
        my $reservemsg;
	if (defined($slots{$slot}->{'type'})
	    && $slots{$slot}->{'type'} eq 'schedulable_student') {
	    $reservemsg = $msgops{$slots{$slot}->{'reservationmsg'}};
	}
	my $description=&get_description($slot,$slots{$slot});
	my ($id_count,$ids);
	    
	if (exists($show{'scheduled'}) || exists($show{'space'}) ) {
	    my $re_str = "$slot\0";
	    my @this_slot = grep(/^\Q$re_str\E/,keys(%consumed));
	    $id_count = scalar(@this_slot);
	    if (exists($show{'scheduled'})) {
		foreach my $entry (sort { $consumed{$a}{name} cmp 
					      $consumed{$b}{name} }
				   (@this_slot)) {
		    my (undef,$id)=split("\0",$entry);
		    my ($uname,$udom) = split(':',$consumed{$entry}{'name'});
		    $ids.= '<span class="LC_nobreak">';
		    foreach my $item (@stu_display_order) {
			if ($stu_display{$item}) {
			    if ($item eq 'fullname') {
				$ids.=$fullname->{"$uname:$udom"}.' ';
			    } elsif ($item eq 'username') {
				$ids.="<tt>$uname:$udom</tt> ";
			    }
			}
		    }
		    $ids.=&remove_link($slot,$entry,$uname,$udom,
				       $consumed{$entry}{'symb'}).'</span><br />';
		}
	    }
	}

	my $start=($slots{$slot}->{'starttime'}?
		   &Apache::lonlocal::locallocaltime($slots{$slot}->{'starttime'}):'');
	my $end=($slots{$slot}->{'endtime'}?
		 &Apache::lonlocal::locallocaltime($slots{$slot}->{'endtime'}):'');
	my $start_reserve=($slots{$slot}->{'startreserve'}?
			   &Apache::lonlocal::locallocaltime($slots{$slot}->{'startreserve'}):'');
        my $end_reserve=($slots{$slot}->{'endreserve'}?
                         &Apache::lonlocal::locallocaltime($slots{$slot}->{'endreserve'}):'');
	
	my $unique;
	if (ref($slots{$slot}{'uniqueperiod'})) {
	    $unique=localtime($slots{$slot}{'uniqueperiod'}[0]).', '.
		localtime($slots{$slot}{'uniqueperiod'}[1]);
	}

	my @titles;
	if (exists($slots{$slot}{'symb'})) {
            my @symbs;
            if ($slots{$slot}{'symb'} =~ /,/) {
                @symbs = split(/\s*,\s*/,$slots{$slot}{'symb'});
            } else {
                @symbs = ($slots{$slot}{'symb'});
            }
            foreach my $reqsymb (@symbs) {
	        my (undef,undef,$res) =
		    &Apache::lonnet::decode_symb($reqsymb);
	        $res = &Apache::lonnet::clutter($res);
	        my $title = &Apache::lonnet::gettitle($reqsymb);
	        push(@titles,'<a href="'.$res.'?symb='.$reqsymb.'">'.$title.'</a>');
            }
	}

	my $allowedsections;
	if (exists($show{'allowedsections'})) {
	    $allowedsections = 
		join(', ',sort(split(/\s*,\s*/,
				     $slots{$slot}->{'allowedsections'})));
	}

	my @allowedusers;
	if (exists($show{'allowedusers'})) {
	    @allowedusers= map {
		my ($uname,$udom)=split(/:/,$_);
		my $fullname=$name_cache{$_};
		if (!defined($fullname)) {
		    $fullname = &Apache::loncommon::plainname($uname,$udom);
		    $fullname =~s/\s/&nbsp;/g;
		    $name_cache{$_} = $fullname;
		}
		&Apache::loncommon::aboutmewrapper($fullname,$uname,$udom);
	    } (sort(split(/\s*,\s*/,$slots{$slot}->{'allowedusers'})));
	}
	my $allowedusers=join(', ',@allowedusers);
	
	my @proctors;
	my $rowspan=1;
	my $colspan=1;
	if (exists($show{'proctor'})) {
	    $rowspan=2;
	    @proctors= map {
		my ($uname,$udom)=split(/:/,$_);
		my $fullname=$name_cache{$_};
		if (!defined($fullname)) {
		    $fullname = &Apache::loncommon::plainname($uname,$udom);
		    $fullname =~s/\s/&nbsp;/g;
		    $name_cache{$_} = $fullname;
		}
		&Apache::loncommon::aboutmewrapper($fullname,$uname,$udom);
	    } (sort(split(/\s*,\s*/,$slots{$slot}->{'proctor'})));
	}
	my $proctors=join(', ',@proctors);

        my %lt = &Apache::lonlocal::texthash (
                                               edit   => 'Edit',
                                               delete => 'Delete',
                                               slotlog => 'History',
        );
	my $edit=(<<"EDITLINK");
<a href="/adm/helper/newslot.helper?name=$slot">$lt{'edit'}</a>
EDITLINK

	my $delete=(<<"DELETELINK");
<a href="/adm/slotrequest?command=delete&amp;slotname=$slot">$lt{'delete'}</a>
DELETELINK

        my $showlog=(<<"LOGLINK");
<a href="/adm/slotrequest?command=slotlog&amp;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);
	}

	my $row_start=&Apache::loncommon::start_data_table_row();
	my $row_end=&Apache::loncommon::end_data_table_row();
        $r->print($row_start.
		  "\n<td rowspan=\"$rowspan\">$edit $delete $showlog</td>\n");
	if (exists($show{'name'})) {
	    $colspan++;$r->print("<td>$slot</td>");
	}
	if (exists($show{'description'})) {
	    $colspan++;$r->print("<td>$description</td>\n");
	}
	if (exists($show{'type'})) {
	    $colspan++;$r->print("<td>$slots{$slot}->{'type'}</td>\n");
	}
	if (exists($show{'starttime'})) {
	    $colspan++;$r->print("<td>$start</td>\n");
	}
	if (exists($show{'endtime'})) {
	    $colspan++;$r->print("<td>$end</td>\n");
	}
	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");
	}
	if (exists($show{'space'})) {
	    my $display = $id_count;
	    if ($slots{$slot}{'maxspace'}>0) {
		$display.='/'.$slots{$slot}{'maxspace'};
		if ($slots{$slot}{'maxspace'} <= $id_count) {
		    $display = '<strong>'.$display.' (full) </strong>';
		}
	    }
	    $colspan++;$r->print("<td>$display</td>\n");
	}
	if (exists($show{'ip'})) {
	    $colspan++;$r->print("<td>$slots{$slot}{'ip'}</td>\n");
	}
        if (exists($show{'iptied'})) {
            $colspan++;
            if ($slots{$slot}{'iptied'} eq 'yes') {
                $r->print('<td>'.&mt('Yes')."</td>\n");
            } elsif ($slots{$slot}{'iptied'} eq 'answer') {
                $r->print('<td>'.&mt('Yes, including post-answer date')."</td>\n");
            } else {
                $r->print('<td>'.&mt('No')."</td>\n");
            }
        }
	if (exists($show{'symb'})) {
	    $colspan++;$r->print("<td>".join('<br />',@titles)."</td>\n");
	}
	if (exists($show{'allowedsections'})) {
	    $colspan++;$r->print("<td>$allowedsections</td>\n");
	}
	if (exists($show{'allowedusers'})) {
	    $colspan++;$r->print("<td>$allowedusers</td>\n");
	}
	if (exists($show{'uniqueperiod'})) {
	    $colspan++;$r->print("<td>$unique</td>\n");
	}
	if (exists($show{'scheduled'})) {
	    $colspan++;$r->print("<td>$remove_all $ids</td>\n");
	}
	$r->print("$row_end\n");
	if (exists($show{'proctor'})) {
	    $r->print(<<STUFF);
$row_start
 <td colspan="$colspan">$proctors</td>
$row_end
STUFF
        }
    }
    $r->print(&Apache::loncommon::end_data_table().'</form>');
    return;
}

sub manage_reservations {
    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>'
    );
    if (!defined($navmap)) {
        $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);
    my ($depth,$count,$reservable,$lastcontainer,$rownum,$shown) = (0,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 = '<p>'.
                 &mt('Your reservation status for any such assignments is listed below:').
                 '</p>'.
                 '<table class="LC_data_table LC_tableOfContent" id="LC_slot_reservations">'."\n";
    my $shownheader = 0;
    my $it=$navmap->getIterator(undef,undef,undef,1,undef,undef);
    my (@ordered,%output,$mapitem,$got_map_slot,$currmapoutput,$mapnum);
    $mapnum = 0;
    $shown = 0;
    while (my $resource = $it->next()) {
        if ($resource == $it->BEGIN_MAP()) {
            $depth++;
            $parent{$depth} = $lastcontainer;
        }
        if ($resource == $it->END_MAP()) {
            $depth--;
            $lastcontainer = $parent{$depth};
            my %allstatuses;
            foreach my $symb (@ordered) {
                if (ref($output{$symb}) eq 'HASH') {
                    if (($output{$symb}{'type'} eq 'map_map') || ($output{$symb}{'type'} eq 'map')) {
                        if ($output{$symb}{'slotstatus'} ne '') {
                            if (ref($allstatuses{$output{$symb}{'slotstatus'}}) eq 'ARRAY') {
                                push(@{$allstatuses{$output{$symb}{'slotstatus'}}},$symb);
                            } else {
                                $allstatuses{$output{$symb}{'slotstatus'}} = [$symb];
                            }
                        }
                    }
                }
            }
            if (keys(%allstatuses) == 1) {
                $got_map_slot = 1;
                my $repsymb;
                my @values = values(%allstatuses);
                if (ref($values[0]) eq 'ARRAY') {
                    if (ref($output{$values[0][0]}) eq 'HASH') {
                        $repsymb = $values[0][0];
                    }
                }
                if (($mapitem) && ($repsymb)) {
                    my $formnum = $mapnum.'_'.$output{$repsymb}{'reservable'};
                    my $class = 'LC_slotmaptext_'.$mapnum;
                    if ($output{$repsymb}{'hasaction'}) {
                        $mapitem .= '<td valign="top"><span class="'.$class.'">'.
                                    $output{$repsymb}{'msg'}.
                                    '</span></td><td valign="top">'.
                                    &slot_chooser($repsymb,$class,$formnum,$allavailable,$slots,
                                                  $consumed_uniqueperiods).
                                   '</td>';

                    } else {
                        $mapitem .= '<td colspan="2" valign="middle"><span class="'.$class.'">'.
                                    $output{$repsymb}{'msg'}.
                                    '</span></td>';
                    }
                }
                my $counter = 0;
                foreach my $symb (@ordered) {
                    if (ref($output{$symb}) eq 'HASH') {
                        $counter ++;
                        my $bgcolor = $backgrounds[($output{$symb}{'shown'} + $counter) % $numcolors];
                        $currmapoutput .= $output{$symb}{'header'}.
                                          '<tr class="'.$bgcolor.' LC_slotresrow_'.$output{$symb}{'mapnum'}.'"'.
                                          ' style="display:none" id="LC_slotresrow_'.$output{$symb}{'rownum'}.'">'.
                                          $output{$symb}{'info'}.
                                          $output{$symb}{'data'}.'</tr>'."\n";
                    }
                }
            } else {
                my $counter = 0;
                foreach my $symb (@ordered) {
                    if (ref($output{$symb}) eq 'HASH') {
                        $counter ++;
                        my $bgcolor = $backgrounds[($output{$symb}{'shown'} + $counter) % $numcolors];
                        $currmapoutput .= $output{$symb}{'header'}.
                                          '<tr class="'.$bgcolor.' LC_slotresrow_'.$output{$symb}{'mapnum'}.'"'.
                                          ' style="display:table-row" id="LC_slotresrow_'.$output{$symb}{'rownum'}.'">'.
                                          $output{$symb}{'info'}.
                                          $output{$symb}{'data'}.'</tr>'."\n";
                        $shown ++;
                    }
                }
            }
            if ($mapitem) {
                if ($got_map_slot) {
                    $mapitem =~ s{(<img src=\"/adm/lonIcons/arrow\.)open(\.gif\")}{$1closed$2};
                    $mapitem .= '</tr>'."\n";
                } else {
                    $mapitem .= '<td colspan="2">&nbsp;</td></tr>'."\n";
                }
            }
            $r->print($mapitem.$currmapoutput);
            @ordered=();
            undef(%output);
            $currmapoutput = '';
            $got_map_slot = '';
            $mapitem = '';
        }
        if (ref($resource)) {
            my $symb = $resource->symb();
            $contents{$lastcontainer} ++;
            next if (!$resource->is_problem() && !$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()) {
                my ($useslots) = $resource->slot_control();
                next if (($useslots eq '') || ($useslots =~ /^\s*no\s*$/i));
                push(@ordered,$symb); 
                $output{$symb}{type} = $useslots;  
                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');

                $output{$symb}{'slotstatus'} = $slot_status;
                $output{$symb}{'slotname'} = $slot_name; 
                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='<span class="LC_nobreak">'.&mt('Reserved:').
                         '&nbsp;'.$slotdescription.'</span><br />'.
                         &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='<span class="LC_nobreak">'.&mt('Reserved:').
                         '&nbsp;'.$slotdescription.'</span><br />'.
                         &mt('Problem is currently available.');
                } elsif ($slot_status == $resource->RESERVED_LOCATION) {
                    $msg='<span class="LC_nobreak">'.&mt('Reserved:').
                         '&nbsp;'.$slotdescription.'</span><br />'.
                         &mt('Problem is available at a different location.');
                    $get_choices = 1;
                } elsif ($slot_status == $resource->RESERVED_LATER) {
                    $msg='<span class="LC_nobreak">'.&mt('Reserved:').
                         '&nbsp;'.$slotdescription.'</span><br />'.
                         &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'); 
                    }
                }
                $output{$symb}{'msg'} = $msg;
                $reservable ++;
                $output{$symb}{'reservable'} = $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};
                    }
                    for (my $i=0; $i<@maprows; $i++) {
                        $mapnum ++;
                        $rownum ++;
                        $shown ++;
                        my $bgcolor = $backgrounds[$shown % $numcolors];
                        if (!$shownheader) {
                            $mapitem .= $slotheader;
                            $shownheader = 1;
                        }
                        if (ref($maprows[$i]) eq 'ARRAY') {
                            if ($i < scalar(@maprows)-1) {
                                $mapitem .= '<tr class="'.$bgcolor.'" id="LC_slotmaprow_'.$rownum.'">'.
                                            '<td>'.join('',@{$maprows[$i]}).'</td>'.
                                            '<td colspan="2">&nbsp;</td></tr>'."\n";
                            } else {
                                $mapitem .= 
                                   '<tr class="'.$bgcolor.'" id="LC_slotmaprow_'.$rownum.'">'.
                                   '<td>'.$maprows[$i][0].
                                   '<img src="/adm/lonIcons/arrow.open.gif" id="arrow'.$mapnum.'" '.
                                   'alt="arrow" onmouseover="this.style.cursor=\'pointer\'" '.
                                   'onclick="'."toggleSlotMap('$mapnum','$rownum');".'" />'.
                                   $maprows[$i][1].('&nbsp;' x6).'</td>'."\n";
                            }
                        }
                    }
                    $output{$symb}{'mapnum'} = $mapnum;
                }
                $rownum ++;
                $output{$symb}{'rownum'} = $rownum;
                $output{$symb}{'shown'} = $shown;
                if (!$shownheader) {
                    $output{$symb}{'header'} = $slotheader;
                    $shownheader = 1;
                }
                $output{$symb}{'info'} = '<td>';
                for (my $i=0; $i<$depth; $i++) {
                    $output{$symb}{'info'} .= '<img src="'.$location.'" alt="" />';
                }
                $output{$symb}{'info'} .= '<a href="'.$resource->src().'?symb='.$symb.'">'.
                                         '<img class="LC_contentImage" src="/adm/lonIcons/';
                if ($resource->is_task()) {
                    $output{$symb}{'info'} .= 'task.gif" alt="'.&mt('Task');
                } else {
                    $output{$symb}{'info'} .= 'problem.gif" alt="'.&mt('Problem');
                }
                $output{$symb}{'info'} .= '" /><b>'.$title.'</b></a>'.('&nbsp;' x6).'</td>';

                my $hasaction;
                if ($status == $resource->OPEN) {
                    if ($get_choices) {
                        $hasaction = 1;
                        $output{$symb}{'hasaction'} = $hasaction;
                    }
                }
                my $class = 'LC_slottext_'.$mapnum;
                if ($hasaction) {
                    $output{$symb}{'data'} = '<td valign="top"><span class="'.$class.'">'.$msg.'</span></td>'.
                                             '<td valign="top">'.
                                             &slot_chooser($symb,$class,$reservable,$allavailable,$slots,
                                                           $consumed_uniqueperiods).'</td>';
                } else {
                    $output{$symb}{'data'} = '<td colspan="2" valign="middle">'.
                                             '<span class="'.$class.'">'.$msg.'</span>'.
                                             '</td>';
                }
            }
        }
    }
    if ($shownheader) {
        $r->print('</table>');
    }
    if (!$reservable) {
        $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('<p><a href="/adm/slotrequest?command=showresv">'.
              &mt('Reservation History').'</a></p>');
}

sub show_map_row {
    my ($depth,$location,$type,$title,$maprows) = @_;
    my $spacers;
    for (my $i=0; $i<$depth-2; $i++) {
        $spacers .= '<img src="'.$location.'" alt="" />';
    }
    my $icon;
    if ($type eq 'page') {
        $icon = '<img src="/adm/lonIcons/navmap.page.open.gif" alt="" />&nbsp;'."\n";
    } else {
        $icon = '<img src="/adm/lonIcons/navmap.folder.open.gif" alt="" />&nbsp;'."\n";
    }
    $icon .= $title;
    unshift (@{$maprows},[$spacers,$icon]);
    return;
}

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 = '<span class="'.$class.'"><span class="LC_error">'.
                  &mt('An error occurred determining slot availability.').
                  '</span></span>';
    } 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 {
    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('<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
    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().
                   '<th>&nbsp;</th><th>'.&mt('When').'</th><th>'.&mt('Action').'</th>'.
                   '<th>'.&mt('Description').'</th><th>'.&mt('Start time').'</th>'.
                   '<th>'.&mt('End time').'</th><th>'.&mt('Resource').'</th>'.
                   &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().'<td>'.$count.'</td><td>'.&Apache::lonlocal::locallocaltime($log{$id}{'exe_time'}).'</td><td>'.$chgaction.'</td><td>'.$description.'</td><td>'.$start.'</td><td>'.$end.'</td><td>'.$title.'</td>'.&Apache::loncommon::end_data_table_row()."\n");
    }
    if ($showntablehdr) {
        $r->print(&Apache::loncommon::end_data_table().'<br />');
        if (($curr{'page'} > 1) || ($more_records)) {
            $r->print('<table><tr>');
            if ($curr{'page'} > 1) {
                $r->print('<td><a href="javascript:chgPage('."'previous'".');">'.&mt('Previous [_1] changes',$curr{'show'}).'</a></td>');
            }
            if ($more_records) {
                $r->print('<td><a href="javascript:chgPage('."'next'".');">'.&mt('Next [_1] changes',$curr{'show'}).'</a></td>');
            }
            $r->print('</tr></table>');
            $r->print(<<"ENDSCRIPT");
<script type="text/javascript">
// <![CDATA[
function chgPage(caller) {
    if (caller == 'previous') {
        document.$formname.page.value --;
    }
    if (caller == 'next') {
        document.$formname.page.value ++;
    }
    document.$formname.submit();
    return;
}
// ]]>
</script>
ENDSCRIPT
        }
    } else {
        $r->print('<span class="LC_info">'
                 .&mt('There are no transactions to display.')
                 .'</span>'
        );
    }
    $r->print('<input type="hidden" name="page" value="'.$curr{'page'}.'" />'."\n".
              '<input type="hidden" name="command" value="showresv" />'."\n");
    if ($env{'form.origin'} eq 'aboutme') {
        $r->print('<input type="hidden" name="origin" value="'.$env{'form.origin'}.'" />'."\n".
                  '<input type="hidden" name="uname" value="'.$env{'form.uname'}.'" />'."\n".
                  '<input type="hidden" name="udom" value="'.$env{'form.udom'}.'" />'."\n");
    }
    $r->print('</form>');
    return;
}

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;
    } else {
        my %slot=&Apache::lonnet::get_slot($env{'form.slotname'});
        if (keys(%slot) == 0) {
            $r->print('<div class="LC_warning">'.&mt('Invalid slot name: [_1]',$env{'form.slotname'}).'</div>');
            $badslot = 1;
        } elsif ($slot{type} ne 'schedulable_student') {
            my $description = &get_description($env{'form.slotname'},\%slot);
            $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;
        }
    }
    if ($badslot) {
        $r->print('<p><a href="/adm/slotrequest?command=showslots">'.
                  &mt('Return to slot list').'</a></p>');
        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('<form action="/adm/slotrequest" method="post" name="'.$formname.'">');
    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('<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().
                   &Apache::loncommon::start_data_table_header_row().
                   '<th>&nbsp;</th><th>'.&mt('When').'</th><th>'.&mt('Who made the change').
                   '</th><th>'.&mt('Affected User').'</th><th>'.&mt('Action').'</th>'.
                   '<th>'.&mt('Resource').'</th><th>'.&mt('Context').'</th>'.
                   &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().'<td>'.$count.'</td><td>'.&Apache::lonlocal::locallocaltime($log{$id}{'exe_time'}).'</td><td>'.$whodunit{$log{$id}{'exe_uname'}.':'.$log{$id}{'exe_udom'}}.'</td><td>'.$changed{$log{$id}{'uname'}.':'.$log{$id}{'udom'}}.'</td><td>'.$chgaction.'</td><td>'.$title.'</td><td>'.$chgcontext.'</td>'.&Apache::loncommon::end_data_table_row()."\n");
    }
    if ($showntablehdr) {
        $r->print(&Apache::loncommon::end_data_table().'<br />');
        if (($curr{'page'} > 1) || ($more_records)) {
            $r->print('<table><tr>');
            if ($curr{'page'} > 1) {
                $r->print('<td><a href="javascript:chgPage('."'previous'".');">'.&mt('Previous [_1] changes',$curr{'show'}).'</a></td>');
            }
            if ($more_records) {
                $r->print('<td><a href="javascript:chgPage('."'next'".');">'.&mt('Next [_1] changes',$curr{'show'}).'</a></td>');
            }
            $r->print('</tr></table>');
            $r->print(<<"ENDSCRIPT");
<script type="text/javascript">
function chgPage(caller) {
    if (caller == 'previous') {
        document.$formname.page.value --;
    }
    if (caller == 'next') {
        document.$formname.page.value ++;
    }
    document.$formname.submit();
    return;
}
</script>
ENDSCRIPT
        }
    } else {
        $r->print(&mt('There are no records to display.'));
    }
    $r->print('<input type="hidden" name="page" value="'.$curr{'page'}.'" />'.
              '<input type="hidden" name="slotname" value="'.$env{'form.slotname'}.'" />'.
              '<input type="hidden" name="command" value="slotlog" /></form>'.
              '<p><a href="/adm/slotrequest?command=showslots">'.
              &mt('Return to slot list').'</a></p>');
    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 = '<br /><table><tr><td valign="top">'.
                 '<span class="LC_nobreak"><b>'.&mt('Changes/page:').'</b><br />'.
                 &Apache::lonmeta::selectbox('show',$curr->{'show'},undef,
                                              (&mt('all'),5,10,20,50,100,1000,10000)).
                 '</td><td>&nbsp;&nbsp;</td>';
    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 .= '<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>'.
               $endform.'</td></tr></table></td><td>&nbsp;&nbsp;</td>';
    if (ref($allsymbs) eq 'ARRAY') {
        $output .= '<td valign="top"><b>'.&mt('Resource').'</b><br />'.
                   '<select name="resource"><option value="any"';
        if ($curr->{'resource'} eq 'any') {
            $output .= ' selected="selected"';
        }
        $output .=  '>'.&mt('Any').'</option>'."\n";
        foreach my $symb (@{$allsymbs}) {
            my $title = &get_resource_title($symb,\%titles,\%maptitles);
            my $selstr = '';
            if ($curr->{'resource'} eq $symb) {
                $selstr = ' selected="selected"';
            }
            $output .= '  <option value="'.$symb.'"'.$selstr.'>'.$title.'</option>';
        }
        $output .= '</select></td><td>&nbsp;&nbsp;</td><td valign="top"><b>'.
                   &mt('Context:').'</b><br /><select name="chgcontext">';
        foreach my $chgtype ('any','user','manage','parameter') {
            my $selstr = '';
            if ($curr->{'chgcontext'} eq $chgtype) {
                $output .= $selstr = ' selected="selected"';
            }
            $output .= '<option value="'.$chgtype.'"'.$selstr.'>'.$lt{$chgtype}.'</option>'."\n";
        }
        $output .= '</select></td>';
    } else {
        $output .= '<td valign="top"><b>'.&mt('Action').'</b><br />'.
                   '<select name="action"><option value="any"';
        if ($curr->{'action'} eq 'any') {
            $output .= ' selected="selected"';
        }
        $output .=  '>'.&mt('Any').'</option>'."\n";
        foreach my $actiontype ('reserve','release') {
            my $selstr = '';
            if ($curr->{'action'} eq $actiontype) {
                $output .= $selstr = ' selected="selected"';
            }
            $output .= '<option value="'.$actiontype.'"'.$selstr.'>'.$lt{$actiontype}.'</option>'."\n";
        }
        $output .= '</select></td>';
    }
    $output .= '<td>&nbsp;&nbsp;</td><td valign="middle"><input type="submit" value="'.
               &mt('Update Display').'" /></tr></table>'.
               '<p class="LC_info">'.
               &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 .= '</p><hr /><br />';
    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()
       .'<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" />'
       .&Apache::lonhtmlcommon::start_pick_box()
       .&Apache::lonhtmlcommon::row_title(&mt('File'))
       .&Apache::loncommon::upfile_select_html()
       .&Apache::lonhtmlcommon::row_closure()
       .&Apache::lonhtmlcommon::row_title(
            '<label for="noFirstLine">'
           .&mt('Ignore First Line')
           .'</label>')
       .'<input type="checkbox" name="noFirstLine" id="noFirstLine" />'
       .&Apache::lonhtmlcommon::row_closure(1)
       .&Apache::lonhtmlcommon::end_pick_box()
       .'<p>'
       .'<input type="button" onclick="javascript:checkUpload(this.form);"'
       .' value="'.&mt('Next').'" />'
       .'</p>'
      .'</form>'
    );
}

sub csvuploadmap_header {
    my ($r,$datatoken,$distotal)= @_;
    my $javascript;
    if ($env{'form.upfile_associate'} eq 'reverse') {
	$javascript=&csvupload_javascript_reverse_associate();
    } else {
	$javascript=&csvupload_javascript_forward_associate();
    }

    my $checked=(($env{'form.noFirstLine'})?' checked="checked"':'');
    my $ignore=&mt('Ignore First Line');
    my $buttontext = &mt('Reverse Association');

    $r->print(
        '<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);
<input type="hidden" name="associate"  value="" />
<input type="hidden" name="datatoken"  value="$datatoken" />
<input type="hidden" name="fileupload" value="$env{'form.fileupload'}" />
<input type="hidden" name="upfiletype" value="$env{'form.upfiletype'}" />
<input type="hidden" name="upfile_associate" 
                                       value="$env{'form.upfile_associate'}" />
<input type="hidden" name="command"    value="csvuploadassign" />
<script type="text/javascript" language="Javascript">
// <![CDATA[
$javascript
// ]]>
</script>
ENDPICK
    return '';

}

sub csvuploadmap_footer {
    my ($request,$i,$keyfields) =@_;
    my $buttontext = &mt('Create Slots');
    $request->print(<<ENDPICK);
<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 />
</form>
ENDPICK
}

sub csvupload_javascript_reverse_associate {
    my $error1=&mt('You need to specify the name, start time, end time and a type.');
    return(<<ENDPICK);
  function verify(vf) {
    var foundstart=0;
    var foundend=0;
    var foundname=0;
    var foundtype=0;
    for (i=0;i<=vf.nfields.value;i++) {
      tw=eval('vf.f'+i+'.selectedIndex');
      if (i==0 && tw!=0) { foundname=1; }
      if (i==1 && tw!=0) { foundtype=1; }
      if (i==2 && tw!=0) { foundstat=1; }
      if (i==3 && tw!=0) { foundend=1; }
    }
    if (foundstart==0 && foundend==0 && foundtype==0 && foundname==0) {
	alert('$error1');
	return;
    }
    vf.submit();
  }
  function flip(vf,tf) {
  }
ENDPICK
}

sub csvupload_javascript_forward_associate {
    my $error1=&mt('You need to specify the name, start time, end time and a type.');
  return(<<ENDPICK);
  function verify(vf) {
    var foundstart=0;
    var foundend=0;
    var foundname=0;
    var foundtype=0;
    for (i=0;i<=vf.nfields.value;i++) {
      tw=eval('vf.f'+i+'.selectedIndex');
      if (tw==1) { foundname=1; }
      if (tw==2) { foundtype=1; }
      if (tw==3) { foundstat=1; }
      if (tw==4) { foundend=1; }
    }
    if (foundstart==0 && foundend==0 && foundtype==0 && foundname==0) {
	alert('$error1');
	return;
    }
    vf.submit();
  }
  function flip(vf,tf) {
  }
ENDPICK
}

sub csv_upload_map {
    my ($r)= @_;

    my $datatoken;
    if (!$env{'form.datatoken'}) {
	$datatoken=&Apache::loncommon::upfile_store($r);
    } else {
	$datatoken=$env{'form.datatoken'};
	&Apache::loncommon::load_tmp_file($r);
    }
    my @records=&Apache::loncommon::upfile_record_sep();
    if ($env{'form.noFirstLine'}) { shift(@records); }
    &csvuploadmap_header($r,$datatoken,$#records+1);
    my ($i,$keyfields);
    if (@records) {
	my @fields=&csvupload_fields();

	if ($env{'form.upfile_associate'} eq 'reverse') {	
	    &Apache::loncommon::csv_print_samples($r,\@records);
	    $i=&Apache::loncommon::csv_print_select_table($r,\@records,
							  \@fields);
	    foreach (@fields) { $keyfields.=$_->[0].','; }
	    chop($keyfields);
	} else {
	    unshift(@fields,['none','']);
	    $i=&Apache::loncommon::csv_samples_select_table($r,\@records,
							    \@fields);
	    my %sone=&Apache::loncommon::record_sep($records[0]);
	    $keyfields=join(',',sort(keys(%sone)));
	}
    }
    &csvuploadmap_footer($r,$i,$keyfields);

    return '';
}

sub csvupload_fields {
    return (['name','Slot name'],
	    ['type','Type of slot'],
	    ['starttime','Start Time of slot'],
	    ['endtime','End Time of slot'],
	    ['startreserve','Reservation Start Time'],
            ['endreserve','Reservation End Time'],
            ['reservationmsg','Message when reservation changed'],
	    ['ip','IP or DNS restriction'],
            ['iptied','Unique IP each student'],
	    ['proctor','List of proctor ids'],
	    ['description','Slot Description'],
	    ['maxspace','Maximum number of reservations'],
	    ['symb','Resource(s)/Map(s) Restriction'],
	    ['uniqueperiod','Date range of slot exclusion'],
	    ['secret','Secret word proctor uses to validate'],
	    ['allowedsections','Sections slot is restricted to'],
	    ['allowedusers','Users slot is restricted to'],
	    );
}

sub csv_upload_assign {
    my ($r,$mgr)= @_;
    &Apache::loncommon::load_tmp_file($r);
    my @slotdata = &Apache::loncommon::upfile_record_sep();
    if ($env{'form.noFirstLine'}) { shift(@slotdata); }
    my %fields=&Apache::grades::get_fields();
    $r->print('<h3>'.&mt('Creating Slots').'</h3>');
    my $cname=$env{'course.'.$env{'request.course.id'}.'.num'};
    my $cdom=$env{'course.'.$env{'request.course.id'}.'.domain'};
    my $countdone=0;
    my @errors;
    foreach my $slot (@slotdata) {
	my %slot;
	my %entries=&Apache::loncommon::record_sep($slot);
	my $domain;
	my $name=$entries{$fields{'name'}};
	if ($name=~/^\s*$/) {
	    push(@errors,"Did not create slot with no name");
	    next;
	}
	if ($name=~/\s/) { 
	    push(@errors,"$name not created -- Name must not contain spaces");
	    next;
	}
	if ($name=~/\W/) { 
	    push(@errors,"$name not created -- Name must contain only letters, numbers and _");
	    next;
	}
	if ($entries{$fields{'type'}}) {
	    $slot{'type'}=$entries{$fields{'type'}};
	} else {
	    $slot{'type'}='preassigned';
	}
	if ($slot{'type'} ne 'preassigned' &&
	    $slot{'type'} ne 'schedulable_student') {
	    push(@errors,"$name not created -- invalid type ($slot{'type'}) must be either preassigned or schedulable_student");
	    next;
	}
	if ($entries{$fields{'starttime'}}) {
	    $slot{'starttime'}=&UnixDate($entries{$fields{'starttime'}},"%s");
	}
	if ($entries{$fields{'endtime'}}) {
	    $slot{'endtime'}=&UnixDate($entries{$fields{'endtime'}},"%s");
	}

	# start/endtime must be defined and greater than zero
	if (!$slot{'starttime'}) {
	    push(@errors,"$name not created -- Invalid start time");
	    next;
	}
	if (!$slot{'endtime'}) {
	    push(@errors,"$name not created -- Invalid end time");
	    next;
	}
	if ($slot{'starttime'} > $slot{'endtime'}) {
	    push(@errors,"$name not created -- Slot starts after it ends");
	    next;
	}

	if ($entries{$fields{'startreserve'}}) {
	    $slot{'startreserve'}=
		&UnixDate($entries{$fields{'startreserve'}},"%s");
	}
	if (defined($slot{'startreserve'})
	    && $slot{'startreserve'} > $slot{'starttime'}) {
	    push(@errors,"$name not created -- Slot's reservation start time is after the slot's start time.");
	    next;
	}

        if ($entries{$fields{'endreserve'}}) {
            $slot{'endreserve'}=
                &UnixDate($entries{$fields{'endreserve'}},"%s");
        }
        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{'iptied'}} =~ /^\s*(yes|1)\s*$/i) {
            $slot{'iptied'}='yes'; 
        } elsif ($entries{$fields{'iptied'}} =~ /^\s*answer\s*$/i) {
            $slot{'iptied'}='answer';
        }
        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;
	}
	if (defined($slot{'uniqueperiod'})
	    && $slot{'uniqueperiod'}[0] > $slot{'uniqueperiod'}[1]) {
	    push(@errors,"$name not created -- Slot's unique period start time is later than the unique period's end time.");
	    next;
	}

	&Apache::lonnet::cput('slots',{$name=>\%slot},$cdom,$cname);
	$r->print('.');
	$r->rflush();
	$countdone++;
    }
    if ($countdone) {
        &Apache::lonnet::devalidate_slots_cache($cname,$cdom); 
    }
    $r->print('<p>'.&mt('Created [quant,_1,slot]',$countdone)."\n".'</p>');
    foreach my $error (@errors) {
	$r->print('<p><span class="LC_warning">'.$error.'</span></p>'."\n");
    }
    &show_table($r,$mgr);
    return '';
}

sub slot_command_titles {
    my %titles = (
                 slotlog            => 'Reservation Logs',
                 showslots          => 'Manage Slots',
                 showresv           => 'Reservation History',
                 manageresv         => 'Manage Reservations',
                 uploadstart        => 'Upload Slots File',
                 csvuploadmap       => 'Upload Slots File',
                 csvuploadassign    => 'Upload Slots File',
                 delete             => 'Slot Deletion',
                 release            => 'Reservation Result',
                 remove_reservation => 'Remove Registration',
                 get_reservation    => 'Request Reservation',
              );
    return %titles;
}

sub slot_reservationmsg_options {
    my %options = &Apache::lonlocal::texthash (
                        only_student  => 'Sent to student',
        student_and_user_notes_screen => 'Sent to student and added to user notes',
                                 none => 'None sent and no record in user notes',
    );
    return %options;
}

sub handler {
    my $r=shift;

    &Apache::loncommon::content_type($r,'text/html');
    &Apache::loncommon::no_cache($r);
    if ($r->header_only()) {
	$r->send_http_header();
	return OK;
    }

    &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'});

    my %crumb_titles = &slot_command_titles();
    my $brcrum;

    my $vgr=&Apache::lonnet::allowed('vgr',$env{'request.course.id'});
    my $mgr=&Apache::lonnet::allowed('mgr',$env{'request.course.id'});
    my (%slots,$consumed_uniqueperiods);
    if ($env{'form.command'} eq 'showslots') {
        if (($vgr ne 'F') && ($mgr ne 'F')) {
            $env{'form.command'} = 'manageresv'; 
        }
    } elsif ($env{'form.command'} eq 'manageresv') {
        if (($vgr eq 'F') || ($mgr eq 'F')) {
            $env{'form.command'} = 'showslots';
        }
    }
    my $title='Requesting Another Worktime';
    if ($env{'form.command'} eq 'showresv') {
        $title = 'Reservation History';
        if ($env{'form.origin'} eq 'aboutme') {
            $brcrum =[{href=>"/adm/$env{'form.udom'}/$env{'form.uname'}/aboutme",text=>'Personal Information Page'}];
        } else {
            $brcrum =[{href=>"/adm/slotrequest?command=manageresv",text=>'Manage Reservations'}];
        }
        if (ref($brcrum) eq 'ARRAY') {
            push(@{$brcrum},{href=>"/adm/slotrequest?command=showresv",text=>$title});
        }
    } elsif (($env{'form.requestattempt'}) || ($env{'form.command'} eq 'manageresv')) {  
        if ($env{'form.command'} eq 'manageresv') {
            $title = 'Manage Reservations';
            $brcrum =[{href=>"/adm/slotrequest?command=manageresv",text=>$title}];
        }
        my ($cnum,$cdom)=&get_course();
        %slots = &Apache::lonnet::get_course_slots($cnum,$cdom);
        $consumed_uniqueperiods = &get_consumed_uniqueperiods(\%slots);
    } elsif ($vgr eq 'F') {
        if ($env{'form.command'} =~ /^(slotlog|showslots|uploadstart|csvuploadmap|csvuploadassign|delete|release|remove_registration)$/) {
            $brcrum =[{href=>"/adm/slotrequest?command=showslots",
                       text=>$crumb_titles{'showslots'}}];
	    $title = 'Managing Slots';
            unless ($env{'form.command'} eq 'showslots') {
                if (ref($brcrum) eq 'ARRAY') {
                    push(@{$brcrum},{href=>"/adm/slotrequest?command=$env{'form.command'}",text=>$crumb_titles{$env{'form.command'}}});
                }
            }
        }
    } elsif ($env{'form.command'} eq 'release') {
        if ($env{'form.context'} eq 'usermanage') {
            $brcrum =[{href=>"/adm/slotrequest?command=manageresv",
                       text=>$crumb_titles{'showslots'}}];
            $title = 'Manage Reservations';
            if (ref($brcrum) eq 'ARRAY') {
                push(@{$brcrum},{href=>"/adm/slotrequest?command=$env{'form.command'}",text=>$crumb_titles{$env{'form.command'}}});
            }
        }
    } else {
        $brcrum =[];
    }
    my ($symb,$js,$available,$allavailable,$got_slots);
    $available = [];
    if ($env{'form.requestattempt'}) {
        $symb=&unescape($env{'form.symb'});
        @{$got_slots}=&check_for_reservation($symb,'allslots');
    }
    if (($env{'form.requestattempt'}) || ($env{'form.command'} eq 'manageresv')) {
        $js = &reservation_js(\%slots,$consumed_uniqueperiods,$available,$got_slots,$symb);
    }
    &start_page($r,$title,$brcrum,$js);

    if ($env{'form.command'} eq 'manageresv') {
        $allavailable = $available;
        undef($available);
        undef($got_slots);
        my $crstype = &Apache::loncommon::course_type();
        &manage_reservations($r,$crstype,\%slots,$consumed_uniqueperiods,$allavailable);
    } elsif ($env{'form.command'} eq 'showresv') {
        &show_reservations($r,$env{'form.uname'},$env{'form.udom'});
    } elsif ($env{'form.command'} eq 'showslots' && $vgr eq 'F') {
	&show_table($r,$mgr);
    } elsif ($env{'form.command'} eq 'remove_registration' && $mgr eq 'F') {
	&remove_registration($r);
    } elsif ($env{'form.command'} eq 'release' && $mgr eq 'F') {
	if ($env{'form.entry'} eq 'remove all') {
	    &release_all_slot($r,$mgr);
	} else {
	    &release_slot($r,undef,undef,undef,$mgr);
	}
    } elsif ($env{'form.command'} eq 'delete' && $mgr eq 'F') {
	&delete_slot($r);
    } elsif ($env{'form.command'} eq 'uploadstart' && $mgr eq 'F') {
	&upload_start($r);
    } elsif ($env{'form.command'} eq 'csvuploadmap' && $mgr eq 'F') {
	&csv_upload_map($r);
    } elsif ($env{'form.command'} eq 'csvuploadassign' && $mgr eq 'F') {
	if ($env{'form.associate'} ne 'Reverse Association') {
	    &csv_upload_assign($r,$mgr);
	} else {
	    if ( $env{'form.upfile_associate'} ne 'reverse' ) {
		$env{'form.upfile_associate'} = 'reverse';
	    } else {
		$env{'form.upfile_associate'} = 'forward';
	    }
	    &csv_upload_map($r);
	}
    } elsif ($env{'form.command'} eq 'slotlog' && $mgr eq 'F') {
        &show_reservations_log($r);
    } else {
	my $symb=&unescape($env{'form.symb'});
	if (!defined($symb)) {
	    &fail($r,'not_valid');
	    return OK;
	}
	my (undef,undef,$res)=&Apache::lonnet::decode_symb($symb);
	my $useslots = &Apache::lonnet::EXT("resource.0.useslots",$symb);
	if ($useslots ne 'resource' 
	    && $useslots ne 'map' 
	    && $useslots ne 'map_map') {
	    &fail($r,'not_available');
	    return OK;
	}
	$env{'request.symb'}=$symb;
	my $type = ($res =~ /\.task$/) ? 'Task'
	                               : 'problem';
	my ($status) = &Apache::lonhomework::check_slot_access('0',$type);
	if ($status eq 'CAN_ANSWER' ||
	    $status eq 'NEEDS_CHECKIN' ||
	    $status eq 'WAITING_FOR_GRADE' ||
            $status eq 'NEED_DIFFERENT_IP') {
	    &fail($r,'not_allowed');
	    return OK;
	}
	if ($env{'form.requestattempt'}) {
            $r->print('<div class="LC_left_float">'); 
	    $r->print(&show_choices($symb,undef,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 ($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 {
	    $r->print('<p>'.&mt('Unknown command: [_1]',$env{'form.command'}).'</p>');
	}
    }
    &end_page($r);
    return OK;
}

1;
__END__

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>