--- loncom/interface/lonnavmaps.pm 2012/03/05 11:15:37 1.481 +++ loncom/interface/lonnavmaps.pm 2014/05/30 13:48:39 1.501 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Navigate Maps Handler # -# $Id: lonnavmaps.pm,v 1.481 2012/03/05 11:15:37 foxr Exp $ +# $Id: lonnavmaps.pm,v 1.501 2014/05/30 13:48:39 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: @@ -225,7 +231,7 @@ automatically. =over 4 -=item * B +=item * B: default: constructs one from %env A reference to a fresh ::iterator to use from the navmaps. The rendering will reflect the options passed to the iterator, so you can @@ -484,10 +490,11 @@ use POSIX qw (floor strftime); use Time::HiRes qw( gettimeofday tv_interval ); use LONCAPA; use DateTime(); +use HTML::Entities; # For debugging -# use Data::Dumper; +#use Data::Dumper; # symbolic constants @@ -612,7 +619,7 @@ 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)); @@ -691,7 +698,7 @@ 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"; } @@ -980,11 +987,13 @@ sub render_resource { } if ((&Apache::lonnet::allowed('mdc',$env{'request.course.id'})) && ($resource->symb=~/\_\_\_[^\_]+\_\_\_uploaded/)) { - my $icon = &Apache::loncommon::lonhttpdurl('/res/adm/pages').'/editmap.png'; - $editmapLink=' '. - ''. + if (!$params->{'map_no_edit_link'}) { + my $icon = &Apache::loncommon::lonhttpdurl('/res/adm/pages').'/editmap.png'; + $editmapLink=' '. + ''. ''.&mt('Edit Content').''. ''; + } } } @@ -995,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 @@ -1039,7 +1048,7 @@ 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$editmapLink$nonLinkedText"; } @@ -1128,21 +1137,23 @@ 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)) { $color = $hurryUpColor; + $info = ' title="'.&mt('Due in less than 24 hours!').'"'; } } 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()); @@ -1224,11 +1235,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; @@ -1398,7 +1409,6 @@ sub render { } } - # (re-)Locate the jump point, if any # Note this does not take filtering or hidden into account... need # to be fixed? @@ -1482,9 +1492,10 @@ sub render { 'document.clearbubbles.submit()', 'Mark all posts read'); my $time=time; + my $querystr = &HTML::Entities::encode($ENV{'QUERY_STRING'},'<>&"'); $result .= (< - + END if ($env{'form.register'}) { @@ -1655,7 +1666,6 @@ END $curRes = shift(@resources); } else { $curRes = $it->next($closeAllPages); - } if (!$curRes) { last; } @@ -1827,6 +1837,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 @@ -1843,8 +1855,6 @@ if (location.href.indexOf('#curloc')==-1 "); } - $result.=&Apache::loncommon::end_data_table(); - if ($r) { $r->print($result); $result = ""; @@ -1875,7 +1885,6 @@ sub show_linkitems_toolbar { ''.&mt('Tools:').''; } $result .= ''."\n". - ''."\n". '
    '; my @linkorder = ('firsthomework','everything','uncompleted', 'changefolder','clearbubbles','edittoplevel'); @@ -1903,7 +1912,7 @@ sub show_linkitems_toolbar { } } $result .= '
'. - '
'; + ''; if (($numlinks==1) && (exists($args->{'linkitems'}{'edittoplevel'}))) { $result .= ''. &mt('Content Editor').''; @@ -2006,10 +2015,12 @@ sub new { my $proto = shift; my $class = ref($proto) || $proto; my $self = {}; - bless($self); # So we can call change_user if neceesary + 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; @@ -2023,10 +2034,14 @@ sub new { # 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@domamin: + # 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. # - if ( ($self->{USERNAME} eq $env{'user.name'}) && ($self->{DOMAIN} eq $env{'user.domain'})) { + if (($self->{USERNAME} eq $env{'user.name'}) && ($self->{DOMAIN} eq $env{'user.domain'}) && + !$self->{CODE} && !$self->{NOHIDE}) { # tie the nav hash @@ -2049,11 +2064,9 @@ sub new { $self->{PARM_HASH} = \%parmhash; $self->{PARM_CACHE} = {}; } else { - $self->change_user($self->{USERNAME}, $self->{DOMAIN}); + $self->change_user($self->{USERNAME}, $self->{DOMAIN}, $self->{CODE}, $self->{NOHIDE}); } - my $d = Data::Dumper->new([$self]); - return $self; } @@ -2064,12 +2077,15 @@ sub new { # 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: @@ -2085,7 +2101,7 @@ sub change_user { my ($cdom, $cnum) = split(/\_/, $env{'request.course.id'}); my %big_hash; - &Apache::lonmap::loadmap($cnum, $cdom, $self->{USERNAME}, $self->{DOMAIN}, \%big_hash); + &Apache::lonmap::loadmap($cnum, $cdom, $self->{USERNAME}, $self->{DOMAIN}, $self->{CODE}, $self->{NOHIDE}, \%big_hash); $self->{NAV_HASH} = \%big_hash; @@ -2105,11 +2121,8 @@ sub change_user { } $self->{PARM_HASH} = \%parm_hash; - - - -} +} sub generate_course_user_opt { my $self = shift; @@ -2151,6 +2164,8 @@ sub generate_course_user_opt { return; } + + sub generate_email_discuss_status { my $self = shift; my $symb = shift; @@ -2564,10 +2579,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(); @@ -2596,18 +2613,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}; @@ -2674,6 +2696,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 @@ -3098,16 +3331,11 @@ sub new { if ($resourceCount == 1 && $resource->is_sequence() && !$self->{FORCE_TOP}) { my $firstResource = $resource->map_start(); my $finishResource = $resource->map_finish(); - my $result; - my $rdump = Data::Dumper->new([$result]); - $result = Apache::lonnavmaps::iterator->new($self->{NAV_MAP}, $firstResource, - $finishResource, $self->{FILTER}, - $self->{ALREADY_SEEN}, - $self->{CONDITION}, - $self->{FORCE_TOP}); - return $result; - - + 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. @@ -3126,8 +3354,6 @@ sub new { $self->{ALREADY_SEEN}->{$self->{FIRST_RESOURCE}->{ID}} = 1; bless ($self); - my $selfDump = Data::Dumper->new([$self]); - return $self; } @@ -3143,7 +3369,6 @@ sub next { 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}) { @@ -3169,7 +3394,6 @@ sub next { if (defined($self->{FORCE_NEXT})) { my $tmp = $self->{FORCE_NEXT}; $self->{FORCE_NEXT} = undef; - return $tmp; } @@ -3291,8 +3515,6 @@ sub next { return $self->next($closeAllPages); } - my $hereResource = $self->{HERE}; - return $self->{HERE}; } @@ -3459,7 +3681,6 @@ sub next { my $nextUnfiltered; if ($self->{DIRECTION} == FORWARD()) { $nextUnfiltered = $self->{HERE}->getNext(); - } else { $nextUnfiltered = $self->{HERE}->getPrevious(); } @@ -4012,7 +4233,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 @@ -4083,11 +4304,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 @@ -4146,23 +4369,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; @@ -4183,7 +4421,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) {