--- loncom/interface/lonnavmaps.pm 2011/09/22 12:48:40 1.464 +++ loncom/interface/lonnavmaps.pm 2014/11/18 03:33:53 1.503 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Navigate Maps Handler # -# $Id: lonnavmaps.pm,v 1.464 2011/09/22 12:48:40 raeburn Exp $ +# $Id: lonnavmaps.pm,v 1.503 2014/11/18 03:33:53 raeburn Exp $ # # Copyright Michigan State University Board of Trustees @@ -164,6 +164,12 @@ If true, the resource's folder will not it. Default is false. True implies printCloseAll is false, since you can't close or open folders when this is on anyhow. +=item * B: + +If true, the title of the folder or page will not be followed by an +icon/link to direct editing of a folder or composite page, originally +added via the Course Editor. + =back =item * B: @@ -464,7 +470,7 @@ returns 4 =item add_linkitem() -=item show_linkitems() +=item show_linkitems_toolbar() =back @@ -478,12 +484,19 @@ use Apache::loncommon(); use Apache::lonenc(); use Apache::lonlocal; use Apache::lonnet; +use Apache::lonmap; use POSIX qw (floor strftime); use Time::HiRes qw( gettimeofday tv_interval ); use LONCAPA; use DateTime(); -use Data::Dumper; +use HTML::Entities; + +# For debugging + +#use Data::Dumper; + + # symbolic constants sub SYMB { return 1; } sub URL { return 2; } @@ -536,9 +549,6 @@ my %colormap = # is not yet done and due in less than 24 hours my $hurryUpColor = "#FF0000"; -my $future_slots_checked = 0; -my $future_slots = 0; - sub addToFilter { my $hashIn = shift; my $addition = shift; @@ -609,10 +619,10 @@ sub getDescription { return &mt("Having technical difficulties; please check status later"); } if ($status == $res->NOTHING_SET) { - return &Apache::lonhtmlcommon::direct_parm_link(&mt("Not currently assigned.",$res->symb(),'opendate'),$part); + return &Apache::lonhtmlcommon::direct_parm_link(&mt('Not currently assigned'),$res->symb(),'opendate',$part); } if ($status == $res->OPEN_LATER) { - return &mt("Open [_1]",&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($open,'start')),$res->symb(),'opendate',$part); + return &mt("Open [_1]",&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($open,'start'),$res->symb(),'opendate',$part)); } if ($res->simpleStatus($part) == $res->OPEN) { unless (&Apache::lonnet::allowed('mgr',$env{'request.course.id'})) { @@ -629,10 +639,10 @@ sub getDescription { return &mt('Reserved - next open [_1]', timeToHumanString($slot_time,'start')); } elsif ($slot_status == $res->RESERVABLE) { - return &mt('Reservable ending [_1]', + return &mt('Reservable, reservations close [_1]', timeToHumanString($slot_time,'end')); } elsif ($slot_status == $res->RESERVABLE_LATER) { - return &mt('Reservable starting [_1]', + return &mt('Reservable, reservations open [_1]', timeToHumanString($slot_time,'start')); } elsif ($slot_status == $res->NOT_IN_A_SLOT) { return &mt('Reserve a time/place to work'); @@ -646,22 +656,22 @@ sub getDescription { if ($status == $res->OPEN) { if ($due) { if ($res->is_practice()) { - return &mt("Closes [_1]",&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($due,'start')),$res->symb(),'duedate',$part); + return &mt("Closes [_1]",&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($due,'start'),$res->symb(),'duedate',$part)); } else { - return &mt("Due [_1]",&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($due,'end')),$res->symb(),'duedate',$part); + return &mt("Due [_1]",&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($due,'end'),$res->symb(),'duedate',$part)); } } else { return &Apache::lonhtmlcommon::direct_parm_link(&mt("Open, no due date"),$res->symb(),'duedate',$part); } } if ($status == $res->PAST_DUE_ANSWER_LATER) { - return &mt("Answer open [_1]",&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($answer,'start')),$res->symb(),'answerdate',$part); + return &mt("Answer open [_1]",&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($answer,'start'),$res->symb(),'answerdate',$part)); } if ($status == $res->PAST_DUE_NO_ANSWER) { if ($res->is_practice()) { - return &mt("Closed [_1]",&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($due,'start')),$res->symb(),'answerdate,duedate',$part); + return &mt("Closed [_1]",&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($due,'start'),$res->symb(),'answerdate,duedate',$part)); } else { - return &mt("Was due [_1]",&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($due,'end')),$res->symb(),'answerdate,duedate',$part); + return &mt("Was due [_1]",&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($due,'end'),$res->symb(),'answerdate,duedate',$part)); } } if (($status == $res->ANSWER_OPEN || $status == $res->PARTIALLY_CORRECT) @@ -688,13 +698,13 @@ sub getDescription { my $maxtries = $res->maxtries($part); my $triesString = ""; if ($tries && $maxtries) { - $triesString = '('.&mt('[_1] of [quant,_2,try,tries] used',$tries,$maxtries).')'; + $triesString = '('.&mt('[_1] of [quant,_2,try,tries] used',$tries,$maxtries).')'; if ($maxtries > 1 && $maxtries - $tries == 1) { $triesString = "$triesString"; } } if ($due) { - return &mt("Due [_1]",&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($due,'end')),$res->symb(),'duedate',$part) . + return &mt("Due [_1]",&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($due,'end'),$res->symb(),'duedate',$part)) . " $triesString"; } else { return &Apache::lonhtmlcommon::direct_parm_link(&mt("No due date"),$res->symb(),'duedate',$part)." $triesString"; @@ -893,6 +903,7 @@ sub part_status_summary { return 4; } sub render_resource { my ($resource, $part, $params) = @_; + my $editmapLink; my $nonLinkedText = ''; # stuff after resource title not in link my $link = $params->{"resourceLink"}; @@ -974,6 +985,16 @@ sub render_resource { $linkopen = ""; $linkclose = ""; } + if ((&Apache::lonnet::allowed('mdc',$env{'request.course.id'})) && + ($resource->symb=~/\_\_\_[^\_]+\_\_\_uploaded/)) { + if (!$params->{'map_no_edit_link'}) { + my $icon = &Apache::loncommon::lonhttpdurl('/res/adm/pages').'/editmap.png'; + $editmapLink=' '. + ''. + ''.&mt('Edit Content').''. + ''; + } + } } if ($resource->randomout()) { @@ -983,7 +1004,7 @@ sub render_resource { $nonLinkedText .= ' ('.&mt('conditionally hidden').') '; } if (($resource->is_practice()) && ($resource->is_raw_problem())) { - $nonLinkedText .=' '.&mt('not graded').''; + $nonLinkedText .=' '.&mt('not graded').''; } # We're done preparing and finally ready to start the rendering @@ -1007,8 +1028,10 @@ sub render_resource { # Is this the current resource? if (!$params->{'displayedHereMarker'} && $resource->symb() eq $params->{'here'} ) { - $curMarkerBegin = ''; - $curMarkerEnd = ''; + unless ($resource->is_map()) { + $curMarkerBegin = ''; + $curMarkerEnd = ''; + } $params->{'displayedHereMarker'} = 1; } @@ -1025,9 +1048,9 @@ sub render_resource { } if (!$params->{'resource_nolink'} && !$resource->is_sequence() && !$resource->is_empty_sequence) { - $result .= "$curMarkerBegin$title$partLabel$curMarkerEnd$nonLinkedText"; + $result .= "$curMarkerBegin$title$partLabel$curMarkerEnd$editmapLink$nonLinkedText"; } else { - $result .= "$curMarkerBegin$linkopen$title$partLabel$curMarkerEnd$nonLinkedText"; + $result .= "$curMarkerBegin$linkopen$title$partLabel$curMarkerEnd$editmapLink$nonLinkedText"; } return $result; @@ -1041,6 +1064,7 @@ sub render_communication_status { my $linkopen = ""; my $linkclose = ""; my $location=&Apache::loncommon::lonhttpdurl("/adm/lonMisc"); + if ($resource->hasDiscussion()) { $discussionHTML = $linkopen . ''.&mt('New Discussion').'' . @@ -1113,21 +1137,28 @@ sub render_long_status { $params->{'multipart'} && $part eq "0"; my $color; + my $info = ''; if ($resource->is_problem() || $resource->is_practice()) { $color = $colormap{$resource->status}; - - if (dueInLessThan24Hours($resource, $part) || - lastTry($resource, $part)) { + + if (dueInLessThan24Hours($resource, $part)) { $color = $hurryUpColor; - } + $info = ' title="'.&mt('Due in less than 24 hours!').'"'; + } elsif (lastTry($resource, $part)) { + unless (($resource->problemstatus($part) eq 'no') || + ($resource->problemstatus($part) eq 'no_feedback_ever')) { + $color = $hurryUpColor; + $info = ' title="'.&mt('One try remaining!').'"'; + } + } } if ($resource->kind() eq "res" && $resource->is_raw_problem() && !$firstDisplayed) { - if ($color) {$result .= ""; } + if ($color) {$result .= ''; } $result .= getDescription($resource, $part); - if ($color) {$result .= ""; } + if ($color) {$result .= ""; } } if ($resource->is_map() && &advancedUser() && $resource->randompick()) { $result .= &mt('(randomly select [_1])', $resource->randompick()); @@ -1209,11 +1240,11 @@ sub render_parts_summary_status { } $return.= $td . $totalParts . ' parts: '; foreach my $status (@statuses) { - if ($overallstatus{$status}) { - $return.="" . $overallstatus{$status} . ' ' - . $statusStrings{$status} . ""; - } + if ($overallstatus{$status}) { + $return.='' . $overallstatus{$status} . ' ' + . $statusStrings{$status} . ''; + } } $return.= $endtd; return $return; @@ -1450,8 +1481,10 @@ sub render { $link .= '&register='.$env{'form.register'}; } if ($args->{'caller'} eq 'navmapsdisplay') { - &add_linkitem($args->{'linkitems'},'changefolder', - "location.href='$link'",$text); + unless ($args->{'notools'}) { + &add_linkitem($args->{'linkitems'},'changefolder', + "location.href='$link'",$text); + } } else { $result.= ''.&mt($text).''; } @@ -1459,14 +1492,15 @@ sub render { } # Check for any unread discussions in all resources. - if ($args->{'caller'} eq 'navmapsdisplay') { + if (($args->{'caller'} eq 'navmapsdisplay') && (!$args->{'notools'})) { &add_linkitem($args->{'linkitems'},'clearbubbles', 'document.clearbubbles.submit()', 'Mark all posts read'); my $time=time; + my $querystr = &HTML::Entities::encode($ENV{'QUERY_STRING'},'<>&"'); $result .= (< - + END if ($env{'form.register'}) { @@ -1492,18 +1526,20 @@ END } $result.=''; } + if (($args->{'caller'} eq 'navmapsdisplay') && + (&Apache::lonnet::allowed('mdc',$env{'request.course.id'}))) { + my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; + my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; + if ($env{'course.'.$env{'request.course.id'}.'.url'} eq + "uploaded/$cdom/$cnum/default.sequence") { + &add_linkitem($args->{'linkitems'},'edittoplevel', + "javascript:gocmd('/adm/coursedocs','editdocs');", + 'Content Editor'); + } + } if ($args->{'caller'} eq 'navmapsdisplay') { - $result .= ''; - $result .= ''; - $result.=''; - $result.=&show_linkitems_toolbar($args->{'linkitems'}); - if ($args->{'sort_html'}) { - $result.=''. - ''; - } - $result .= '
'. - &Apache::loncommon::help_open_menu('Navigation Screen','Navigation_Screen',undef,'RAT').' '.&mt('Tools:').'   '.$args->{'sort_html'}.'
'; + $result .= &show_linkitems_toolbar($args,$condition); } elsif ($args->{'sort_html'}) { $result.=$args->{'sort_html'}; } @@ -1806,6 +1842,8 @@ END } } } + + $result.=&Apache::loncommon::end_data_table(); # Print out the part that jumps to #curloc if it exists # delay needed because the browser is processing the jump before @@ -1822,8 +1860,6 @@ if (location.href.indexOf('#curloc')==-1 "); } - $result.=&Apache::loncommon::end_data_table(); - if ($r) { $r->print($result); $result = ""; @@ -1840,41 +1876,64 @@ sub add_linkitem { } sub show_linkitems_toolbar { - my ($linkitems,$condition)=@_; - my @linkorder = ('firsthomework','everything','uncompleted', - 'changefolder','clearbubbles'); - my $result .=''."\n". - ''."\n". - '
    '; - foreach my $link (@linkorder) { - my $link_id = 'LC_content_toolbar_'.$link; - if (defined($linkitems->{$link})) { - if ($linkitems->{$link}{'text'} ne '') { - $linkitems->{$link}{'cmd'}=~s/"/'/g; - if ($linkitems->{$link}{'cmd'}) { - if ($link eq 'changefolder') { - if ($condition) { - $link_id='LC_content_toolbar_changefolder_toggled'; - } else { - $link_id='LC_content_toolbar_changefolder'; + my ($args,$condition) = @_; + my $result; + if (ref($args) eq 'HASH') { + if (ref($args->{'linkitems'}) eq 'HASH') { + my $numlinks = scalar(keys(%{$args->{'linkitems'}})); + if ($numlinks > 1) { + $result = ''. + &Apache::loncommon::help_open_menu('Navigation Screen','Navigation_Screen', + undef,'RAT'). + ''. + ' '. + ''.&mt('Tools:').''; + } + $result .= ''."\n". + '
      '; + my @linkorder = ('firsthomework','everything','uncompleted', + 'changefolder','clearbubbles','edittoplevel'); + foreach my $link (@linkorder) { + if (ref($args->{'linkitems'}{$link}) eq 'HASH') { + if ($args->{'linkitems'}{$link}{'text'} ne '') { + $args->{'linkitems'}{$link}{'cmd'}=~s/"/'/g; + if ($args->{'linkitems'}{$link}{'cmd'}) { + my $link_id = 'LC_content_toolbar_'.$link; + if ($link eq 'changefolder') { + if ($condition) { + $link_id='LC_content_toolbar_changefolder_toggled'; + } else { + $link_id='LC_content_toolbar_changefolder'; + } + } + $result .= '
    • '. + '
    • '."\n"; } } - $result .= '
    • '. - '
    • '."\n"; } } + $result .= '
    '. + ''; + if (($numlinks==1) && (exists($args->{'linkitems'}{'edittoplevel'}))) { + $result .= ''. + &mt('Content Editor').''; + } + } + if ($args->{'sort_html'}) { + $result .= '   '. + ''.$args->{'sort_html'}.''; } } - $result .= '
'. - '
'."\n"; + if ($result) { + $result = "$result
"; + } return $result; } - 1; @@ -1961,9 +2020,14 @@ sub new { my $proto = shift; my $class = ref($proto) || $proto; my $self = {}; + bless($self); # So we can call change_user if necessary $self->{USERNAME} = shift || $env{'user.name'}; $self->{DOMAIN} = shift || $env{'user.domain'}; + $self->{CODE} = shift; + $self->{NOHIDE} = shift; + + # Resource cache stores navmap resources as we reference them. We generate # them on-demand so we don't pay for creating resources unless we use them. @@ -1973,30 +2037,96 @@ sub new { # failed $self->{NETWORK_FAILURE} = 0; - # tie the nav hash + # We can only tie the nav hash as done below if the username/domain + # match the env one. Otherwise change_user does everything we need...since we can't + # assume there are course hashes for the specific requested user:domain + # Note: change_user is also called if we need the nav hash when printing CODEd + # assignments or printing an exam, in which the enclosing folder for the items in + # the exam has hidden set. + # - my %navmaphash; - my %parmhash; - my $courseFn = $env{"request.course.fn"}; - if (!(tie(%navmaphash, 'GDBM_File', "${courseFn}.db", - &GDBM_READER(), 0640))) { - return undef; + if (($self->{USERNAME} eq $env{'user.name'}) && ($self->{DOMAIN} eq $env{'user.domain'}) && + !$self->{CODE} && !$self->{NOHIDE}) { + + # tie the nav hash + + my %navmaphash; + my %parmhash; + my $courseFn = $env{"request.course.fn"}; + if (!(tie(%navmaphash, 'GDBM_File', "${courseFn}.db", + &GDBM_READER(), 0640))) { + return undef; + } + + if (!(tie(%parmhash, 'GDBM_File', "${courseFn}_parms.db", + &GDBM_READER(), 0640))) + { + untie %{$self->{PARM_HASH}}; + return undef; + } + + $self->{NAV_HASH} = \%navmaphash; + $self->{PARM_HASH} = \%parmhash; + $self->{PARM_CACHE} = {}; + } else { + $self->change_user($self->{USERNAME}, $self->{DOMAIN}, $self->{CODE}, $self->{NOHIDE}); } + + return $self; +} + +# +# In some instances it is useful to be able to dynamically change the +# username/domain associated with a navmap (e.g. to navigate for someone +# else besides the current user...if sufficiently privileged. +# Parameters: +# user - New user. +# domain- Domain the user belongs to. +# code - Anonymous CODE in use. +# Implicit inputs: +# +sub change_user { + my $self = shift; + $self->{USERNAME} = shift; + $self->{DOMAIN} = shift; + $self->{CODE} = shift; + $self->{NOHIDE} = shift; + + # If the hashes are already tied make sure to break that bond: + + untie %{$self->{NAV_HASH}}; + untie %{$self->{PARM_HASH}}; + + # The assumption is that we have to + # use lonmap here to re-read the hash and from it reconstruct + # new big and parameter hashes. An implicit assumption at this time + # is that the course file is probably not created locally yet + # an that we will therefore just read without tying. + + my ($cdom, $cnum) = split(/\_/, $env{'request.course.id'}); + + my %big_hash; + &Apache::lonmap::loadmap($cnum, $cdom, $self->{USERNAME}, $self->{DOMAIN}, $self->{CODE}, $self->{NOHIDE}, \%big_hash); + $self->{NAV_HASH} = \%big_hash; + + + + # Now clear the parm cache and reconstruct the parm hash fromt he big_hash + # param.xxxx keys. + + $self->{PARM_CACHE} = {}; - if (!(tie(%parmhash, 'GDBM_File', "${courseFn}_parms.db", - &GDBM_READER(), 0640))) - { - untie %{$self->{PARM_HASH}}; - return undef; + my %parm_hash = {}; + foreach my $key (keys %big_hash) { + if ($key =~ /^param\./) { + my $param_key = $key; + $param_key =~ s/^param\.//; + $parm_hash{$param_key} = $big_hash{$key}; + } } - $self->{NAV_HASH} = \%navmaphash; - $self->{PARM_HASH} = \%parmhash; - $self->{PARM_CACHE} = {}; + $self->{PARM_HASH} = \%parm_hash; - bless($self); - - return $self; } sub generate_course_user_opt { @@ -2039,6 +2169,8 @@ sub generate_course_user_opt { return; } + + sub generate_email_discuss_status { my $self = shift; my $symb = shift; @@ -2452,10 +2584,12 @@ sub parmval { return $result->[0]; } + sub parmval_real { my $self = shift; my ($what,$symb,$recurse) = @_; + # Make sure the {USER_OPT} and {COURSE_OPT} hashes are populated $self->generate_course_user_opt(); @@ -2484,18 +2618,23 @@ sub parmval_real { my $mapparm=$mapname.'___(all).'.$what; my $usercourseprefix=$cid; + + my $grplevel=$usercourseprefix.'.['.$cgroup.'].'.$what; my $grplevelr=$usercourseprefix.'.['.$cgroup.'].'.$symbparm; my $grplevelm=$usercourseprefix.'.['.$cgroup.'].'.$mapparm; + my $seclevel= $usercourseprefix.'.['.$csec.'].'.$what; my $seclevelr=$usercourseprefix.'.['.$csec.'].'.$symbparm; my $seclevelm=$usercourseprefix.'.['.$csec.'].'.$mapparm; + my $courselevel= $usercourseprefix.'.'.$what; my $courselevelr=$usercourseprefix.'.'.$symbparm; my $courselevelm=$usercourseprefix.'.'.$mapparm; + my $useropt = $self->{USER_OPT}; my $courseopt = $self->{COURSE_OPT}; my $parmhash = $self->{PARM_HASH}; @@ -2562,6 +2701,217 @@ sub parmval_real { if (defined($pack_def)) { return [$pack_def,'resource']; } return ['']; } +# +# Determines the open/close dates for printing a map that +# encloses a resource. +# +sub map_printdates { + my ($self, $res, $part) = @_; + + + + + + my $opendate = $self->get_mapparam($res->symb(), "$part.printstartdate"); + my $closedate= $self->get_mapparam($res->symb(), "$part.printenddate"); + + + return ($opendate, $closedate); +} + +sub get_mapparam { + my ($self, $symb, $what) = @_; + + # Ensure the course option hash is populated: + + $self->generate_course_user_opt(); + + # Get the course id and section if there is one. + + my $cid=$env{'request.course.id'}; + my $csec=$env{'request.course.sec'}; + my $cgroup=''; + my @cgrps=split(/:/,$env{'request.course.groups'}); + if (@cgrps > 0) { + @cgrps = sort(@cgrps); + $cgroup = $cgrps[0]; + } + my $uname=$self->{USERNAME}; + my $udom=$self->{DOMAIN}; + + unless ($symb) { return ['']; } + my $result=''; + + + # Figure out which map we are in. + + my ($mapname,$id,$fn)=&Apache::lonnet::decode_symb($symb); + $mapname = &Apache::lonnet::deversion($mapname); + + + my $rwhat=$what; + $what=~s/^parameter\_//; + $what=~s/\_/\./; + + # Build the hash keys for the lookup: + + my $symbparm=$symb.'.'.$what; + my $mapparm=$mapname.'___(all).'.$what; + my $usercourseprefix=$cid; + + + my $grplevel = "$usercourseprefix.[$cgroup].$mapparm"; + my $seclevel = "$usercourseprefix.[$csec].$mapparm"; + my $courselevel = "$usercourseprefix.$mapparm"; + + + # Get handy references to the hashes we need in $self: + + my $useropt = $self->{USER_OPT}; + my $courseopt = $self->{COURSE_OPT}; + my $parmhash = $self->{PARM_HASH}; + + # Check per user + + + + if ($uname and defined($useropt)) { + if (defined($$useropt{$courselevel})) { + return $$useropt{$courselevel}; + } + } + + # Check course -- group + + + + if ($cgroup ne '' and defined ($courseopt)) { + if (defined($$courseopt{$grplevel})) { + return $$courseopt{$grplevel}; + } + } + + # Check course -- section + + + + + + if ($csec and defined($courseopt)) { + if (defined($$courseopt{$seclevel})) { + return $$courseopt{$seclevel}; + } + } + # Check the map parameters themselves: + + my $thisparm = $$parmhash{$symbparm}; + if (defined($thisparm)) { + return $thisparm; + } + + + # Additional course parameters: + + if (defined($courseopt)) { + if (defined($$courseopt{$courselevel})) { + return $$courseopt{$courselevel}; + } + } + return undef; # Unefined if we got here. +} + +sub course_printdates { + my ($self, $symb, $part) = @_; + + + my $opendate = $self->getcourseparam($symb, $part . '.printstartdate'); + my $closedate = $self->getcourseparam($symb, $part . '.printenddate'); + return ($opendate, $closedate); + +} + +sub getcourseparam { + my ($self, $symb, $what) = @_; + + $self->generate_course_user_opt(); # If necessary populate the hashes. + + my $uname = $self->{USERNAME}; + my $udom = $self->{DOMAIN}; + + # Course, section, group ids come from the env: + + my $cid = $env{'request.course.id'}; + my $csec = $env{'request.course.sec'}; + my $cgroup = ''; # Assume no group + + my @cgroups = split(/:/, $env{'request.course.groups'}); + if(@cgroups > 0) { + @cgroups = sort(@cgroups); + $cgroup = $cgroups[0]; # There is a course group. + } + my ($mapname,$id,$fn)=&Apache::lonnet::decode_symb($symb); + $mapname = &Apache::lonnet::deversion($mapname); + + # + # Make the various lookup keys: + # + + $what=~s/^parameter\_//; + $what=~s/\_/\./; + + + my $symbparm = $symb . '.' . $what; + my $mapparm=$mapname.'___(all).'.$what; + + # Local refs to the hashes we're going to look at: + + my $useropt = $self->{USER_OPT}; + my $courseopt = $self->{COURSE_OPT}; + + # + # We want the course level stuff from the way + # parmval_real operates + # TODO: Fator some of this stuff out of + # both parmval_real and here + # + my $courselevel = $cid . '.' . $what; + my $grplevel = $cid . '.[' . $cgroup . ']' . $what; + my $seclevel = $cid . '.[' . $csec . ']' . $what; + + + # Try for the user's course level option: + + if ($uname and defined($useropt)) { + if (defined($$useropt{$courselevel})) { + return $$useropt{$courselevel}; + } + } + # Try for the group's course level option: + + if ($uname ne '' and defined($courseopt)) { + if (defined($$courseopt{$grplevel})) { + return $$courseopt{$grplevel}; + } + } + + # Try for section level parameters: + + if ($csec and defined($courseopt)) { + if (defined($$courseopt{$seclevel})) { + return $$courseopt{$seclevel}; + } + } + # Try for 'additional' course parameterse: + + if (defined($courseopt)) { + if (defined($$courseopt{$courselevel})) { + return $$courseopt{$courselevel}; + } + } + return undef; + +} + =pod @@ -2986,13 +3336,11 @@ sub new { if ($resourceCount == 1 && $resource->is_sequence() && !$self->{FORCE_TOP}) { my $firstResource = $resource->map_start(); my $finishResource = $resource->map_finish(); - return - Apache::lonnavmaps::iterator->new($self->{NAV_MAP}, $firstResource, - $finishResource, $self->{FILTER}, - $self->{ALREADY_SEEN}, - $self->{CONDITION}, - $self->{FORCE_TOP}); - + return Apache::lonnavmaps::iterator->new($self->{NAV_MAP}, $firstResource, + $finishResource, $self->{FILTER}, + $self->{ALREADY_SEEN}, + $self->{CONDITION}, + $self->{FORCE_TOP}); } # Set up some bookkeeping information. @@ -3011,7 +3359,6 @@ sub new { $self->{ALREADY_SEEN}->{$self->{FIRST_RESOURCE}->{ID}} = 1; bless ($self); - return $self; } @@ -3026,6 +3373,7 @@ sub next { # do so. if ($self->{RETURN_0} && !$self->{HAVE_RETURNED_0}) { $self->{HAVE_RETURNED_0} = 1; + my $nextTopLevel = $self->{NAV_MAP}->getById('0.0'); return $self->{NAV_MAP}->getById('0.0'); } if ($self->{RETURN_0} && !$self->{HAVE_RETURNED_0_BEGIN_MAP}) { @@ -3045,7 +3393,6 @@ sub next { if ($self->{RECURSIVE_DEPTH} == 0) { $self->{RECURSIVE_ITERATOR_FLAG} = 0; } - return $next; } @@ -3121,6 +3468,8 @@ sub next { # So we need to look at all the resources we can get to from here, # categorize them if we haven't seen them, remember if we have a new my $nextUnfiltered = $here->getNext(); + + my $maxDepthAdded = -1; for (@$nextUnfiltered) { @@ -3154,7 +3503,6 @@ sub next { $self->{RECURSIVE_ITERATOR_FLAG} = 1; my $firstResource = $self->{HERE}->map_start(); my $finishResource = $self->{HERE}->map_finish(); - $self->{RECURSIVE_ITERATOR} = Apache::lonnavmaps::iterator->new($self->{NAV_MAP}, $firstResource, $finishResource, $self->{FILTER}, @@ -3492,6 +3840,8 @@ sub new { $self->{NAV_MAP}->{RESOURCE_CACHE}->{$self->{ID}} = $self; $self->{RESOURCE_ERROR} = 0; + $self->{DUEDATE_CACHE} = undef; + # A hash that can be used by two-pass algorithms to store data # about this resource in. Not used by the resource object # directly. @@ -3751,7 +4101,7 @@ sub is_practice { sub is_problem { my $self=shift; my $src = $self->src(); - if ($src =~ /\.(problem|exam|quiz|assess|survey|form|library|task)$/) { + if ($src =~ /$LONCAPA::assess_re/) { return !($self->is_practice()); } return 0; @@ -3786,7 +4136,7 @@ sub is_incomplete { sub is_raw_problem { my $self=shift; my $src = $self->src(); - if ($src =~ /\.(problem|exam|quiz|assess|survey|form|library|task)$/) { + if ($src =~ /$LONCAPA::assess_re/) { return 1; } return 0; @@ -3888,7 +4238,7 @@ resource of the map. Returns a string with the type of the map in it. -=item *B: +=item * B: Returns a string with a comma-separated ordered list of map_pc IDs for the hierarchy of maps containing a map, with the top level @@ -3959,11 +4309,13 @@ their code.) =over 4 + =item * B returns true if the current date is such that the specified resource part is printable. + =item * B Returns true if all parts in the resource are printable making the @@ -4022,23 +4374,38 @@ Get the weight for the problem. =cut -sub printable { - my ($self, $part) = @_; - # Get the print open/close dates for the resource. - my $start = $self->parmval("prinstartdate", $part); - my $end = $self->parmval("printenddate", $part); +sub printable { + + my ($self, $part) = @_; # The following cases apply: - # - No dates set: Printable. + # - If a start date is not set, it is replaced by the open date. + # - Ditto for start/open replaced by content open. + # - If neither start nor printdates are set the part is printable. # - Start date set but no end date: Printable if now >= start date. # - End date set but no start date: Printable if now <= end date. # - both defined: printable if start <= now <= end # + + # Get the print open/close dates for the resource. + + my $start = $self->parmval("printstartdate", $part); + my $end = $self->parmval("printenddate", $part); + + if (!$start) { + $start = $self->parmval("opendate", $part); + } + if (!$start) { + $start = $self->parmval("contentopen", $part); + } + + my $now = time(); + my $startok = 1; my $endok = 1; @@ -4059,7 +4426,7 @@ sub resprintable { my $partsref = $self->parts(); my @parts = @$partsref; - if ((!defined(@parts)) || (scalar(@parts) == 0)) { + if (!@parts) { return $self->printable(0); } else { foreach my $part (@parts) { @@ -4130,6 +4497,9 @@ sub checkedin { sub duedate { (my $self, my $part) = @_; + if (defined ($self->{DUEDATE_CACHE}->{$part})) { + return $self->{DUEDATE_CACHE}->{$part}; + } my $date; my @interval=$self->parmval("interval", $part); my $due_date=$self->parmval("duedate", $part); @@ -4146,6 +4516,7 @@ sub duedate { } else { $date = $due_date; } + $self->{DUEDATE_CACHE}->{$part} = $date; return $date; } sub handgrade { @@ -4997,12 +5368,13 @@ sub status { } # Otherwise, it's untried and open - return OPEN; + return OPEN; } sub check_for_slot { my $self = shift; my $part = shift; + my $symb = $self->symb(); my ($use_slots,$available,$availablestudent) = $self->slot_control($part); if (($use_slots ne '') && ($use_slots !~ /^\s*no\s*$/i)) { my @slots = (split(/:/,$availablestudent),split(/:/,$available)); @@ -5010,50 +5382,45 @@ sub check_for_slot { my $cdom=$env{'course.'.$cid.'.domain'}; my $cnum=$env{'course.'.$cid.'.num'}; my $now = time; + my $num_usable_slots = 0; if (@slots > 0) { my %slots=&Apache::lonnet::get('slots',[@slots],$cdom,$cnum); if (&Apache::lonnet::error(%slots)) { return (UNKNOWN); } - my @sorted_slots = &Apache::loncommon::sorted_slots(\@slots,\%slots); + my @sorted_slots = &Apache::loncommon::sorted_slots(\@slots,\%slots,'starttime'); my ($checkedin,$checkedinslot); foreach my $slot_name (@sorted_slots) { - next if (!defined($slots{$slot_name}) || - !ref($slots{$slot_name})); + next if (!defined($slots{$slot_name}) || !ref($slots{$slot_name})); my $end = $slots{$slot_name}->{'endtime'}; my $start = $slots{$slot_name}->{'starttime'}; my $ip = $slots{$slot_name}->{'ip'}; if ($self->simpleStatus() == OPEN) { - my $startreserve = $slots{$slot_name}->{'startreserve'}; - my @proctors; - if ($slots{$slot_name}->{'proctor'} ne '') { - @proctors = split(',',$slots{$slot_name}->{'proctor'}); - } if ($end > $now) { - ($checkedin,$checkedinslot) = $self->checkedin(); - if ($startreserve < $now) { - if ($start > $now) { - return (RESERVED_LATER,$start,$slot_name); - } else { - if ($ip ne '') { - if (!&Apache::loncommon::check_ip_acc($ip)) { - return (RESERVED_LOCATION,$ip,$slot_name); - } - } - if (@proctors > 0) { - unless ((grep(/^\Q$checkedin\E/,@proctors)) && - ($checkedinslot eq $slot_name)) { - return (NEEDS_CHECKIN,undef,$slot_name); - } + if ($start > $now) { + return (RESERVED_LATER,$start,$slot_name); + } else { + if ($ip ne '') { + if (!&Apache::loncommon::check_ip_acc($ip)) { + return (RESERVED_LOCATION,$end,$slot_name); } - return (RESERVED,$end,$slot_name); } - } else { - if ($start > $now) { - return (RESERVABLE,$startreserve,$slot_name); + my @proctors; + if ($slots{$slot_name}->{'proctor'} ne '') { + @proctors = split(',',$slots{$slot_name}->{'proctor'}); } + if (@proctors > 0) { + ($checkedin,$checkedinslot) = $self->checkedin(); + unless ((grep(/^\Q$checkedin\E/,@proctors)) && + ($checkedinslot eq $slot_name)) { + return (NEEDS_CHECKIN,undef,$slot_name); + } + } + return (RESERVED,$end,$slot_name); } } + } elsif ($end > $now) { + $num_usable_slots ++; } } my ($is_correct,$got_grade); @@ -5074,32 +5441,33 @@ sub check_for_slot { return (CORRECT); } } - return(NOT_IN_A_SLOT); - } else { - if (!$future_slots_checked) { - $future_slots = &get_future_slots($cdom,$cnum,$now); - $future_slots_checked = 1; - } - if ($future_slots) { + if ($num_usable_slots) { return(NOT_IN_A_SLOT); } - return(NOTRESERVABLE); } - } - return; -} - -sub get_future_slots { - my ($cdom,$cnum,$now) = @_; - my %slots=&Apache::lonnet::dump('slots',$cdom,$cnum); - my $future_slots = 0; - foreach my $slot (keys(%slots)) { - if (($slots{$slot}->{'starttime'} > $now) && - ($slots{$slot}->{'endtime'} > $now)) { - $future_slots ++; + my $reservable = &Apache::lonnet::get_reservable_slots($cnum,$cdom,$env{'user.name'}, + $env{'user.domain'}); + if (ref($reservable) eq 'HASH') { + if ((ref($reservable->{'now_order'}) eq 'ARRAY') && (ref($reservable->{'now'}) eq 'HASH')) { + foreach my $slot (reverse (@{$reservable->{'now_order'}})) { + if (($reservable->{'now'}{$slot}{'symb'} eq '') || + ($reservable->{'now'}{$slot}{'symb'} eq $symb)) { + return(RESERVABLE,$reservable->{'now'}{$slot}{'endreserve'}); + } + } + } + if ((ref($reservable->{'future_order'}) eq 'ARRAY') && (ref($reservable->{'future'}) eq 'HASH')) { + foreach my $slot (@{$reservable->{'future_order'}}) { + if (($reservable->{'future'}{$slot}{'symb'} eq '') || + ($reservable->{'future'}{$slot}{'symb'} eq $symb)) { + return(RESERVABLE_LATER,$reservable->{'future'}{$slot}{'startreserve'}); + } + } + } } + return(NOTRESERVABLE); } - return $future_slots; + return; } sub CLOSED { return 23; } 500 Internal Server Error

Internal Server Error

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

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

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