--- loncom/interface/lonprintout.pm 2012/04/10 09:49:36 1.615 +++ loncom/interface/lonprintout.pm 2012/05/28 10:31:17 1.616 @@ -1,7 +1,7 @@ # The LearningOnline Network # Printout # -# $Id: lonprintout.pm,v 1.615 2012/04/10 09:49:36 foxr Exp $ +# $Id: lonprintout.pm,v 1.616 2012/05/28 10:31:17 foxr Exp $ # # Copyright Michigan State University Board of Trustees # @@ -47,7 +47,7 @@ use File::Basename; use HTTP::Response; use LONCAPA::map(); -use POSIX qw(strftime); +use POSIX qw(ctime); use Apache::lonlocal; use Carp; use LONCAPA; @@ -77,6 +77,71 @@ my $font_size = 'normalsize'; # Default #---------------------------- Helper helpers. ------------------------- +## +# Filter function to determine if a resource is a printable sequence. +# +# @param $res -Resource to check. +# +# @return 1 - printable and a resource +# 0 - either notm a sequence or not printable. +# +sub printable_sequence { + my $res = shift; + + # Non-sequences are not listed: + + if (!$res->is_sequence()) { + return 0; + } + + # Person with pav or pfo can always print: + + if ($perm{'pav'} || $perm{'pfo'}) { + return 1; + } + + if ($res->is_sequence()) { + my $symb = $res->symb(); + &Apache::lonnet::logthis("Symb: $symb"); + my $navmap = $res->{NAV_MAP}; + + # Find the first resource in the map: + + my $iterator = $navmap->getIterator($res, undef, undef, 1, 1); + my $first = $iterator->next(); + + while (1) { + if ($first == $iterator->END_ITERATOR) { + &Apache::lonnet::logthis("End of iterator"); + last; } + if (ref($first)) { + &Apache::lonnet::logthis("Looking at: " . $first->symb()); + } else { + &Apache::lonnet::logthis("Got: $first"); + } + if (ref($first) && ! $first->is_sequence()) {last; } + $first = $iterator->next(); + } + + + # Might be an empty map: + + if (!ref($first)) { + &Apache::lonnet::logthis("printable_sequence: empty"); + return 0; + } + my $partsref = $first->parts(); + my @parts = @$partsref; + &Apache::lonnet::logthis("Dates for " . $first->symb()); + my ($open, $close) = $navmap->map_printdates($first, $parts[0]); + &Apache::lonnet::logthis("Opens $open, closes $close"); + &Apache::lonnet::logthis(ctime($open)); + &Apache::lonnet::logthis(ctime($close)); + return &printable($open, $close); + } + return 0; +} + # BZ5209: # Create the states needed to run the helper for incomplete problems from # the current folder for selected students. @@ -435,6 +500,83 @@ RESOURCE_SELECTOR #----------------------------------------------------------------------- +# Computes an open and close date from a list of open/close dates for a resource's +# parts. +# +# @param \@opens - reference to an array of open dates. +# @param \@closes - reference to an array of close dates. +# +# @return ($open, $close) +# +# @note If open/close dates are not defined they will be retunred as undef +# @note It is possible for there to be no overlap in which case -1,-1 +# will be returned. +# @note The algorithm used is to take the latest open date and the earliest end date. +# +sub compute_open_window { + my ($opensref, $closesref) = @_; + + my @opens = @$opensref; + my @closes = @$closesref; + + # latest open date: + my $latest_open; + + foreach my $open (@opens) { + if (!defined($latest_open) || ($open > $latest_open)) { + $latest_open = $open; + } + } + # Earliest close: + + my $earliest_close; + foreach my $close (@closes) { + if (!defined($earliest_close) || ($close < $earliest_close)) { + $earliest_close = $close; + } + } + + # If no overlap...both are -1 as promised. + + if (defined($earliest_close) && defined($latest_open) + && ($earliest_close < $latest_open)) { + $latest_open = -1; + $earliest_close = -1; + } + + return ($latest_open, $earliest_close); + +} + +## +# Determines if 'now' is within the set of printable dates. +# +# @param $open_date - Starting date/timestamp. +# @param $close_date - Ending date/timestamp. +# +# @return 0 - Not open. +# @return 1 - open. +# +sub printable { + my ($open_date, $close_date) = @_; + + + my $now = time(); + + # Have to do a bit of fancy footwork around undefined open/close dates: + + if ($open_date && ($open_date > $now)) { + return 0; + } + + if ($close_date && ($close_date < $now)) { + return 0; + } + + return 1; + +} + ## # Returns the innermost print start/print end dates for a resource. # This is done by looking at the start/end dates for its parts and choosing @@ -456,22 +598,87 @@ sub get_print_dates { my @parts = @$partsref; my $open_date; my $close_date; + my @open_dates; + my @close_dates; + if (defined(@parts) && (scalar(@parts) > 0)) { foreach my $part (@parts) { my $partopen = $res->parmval('printstartdate', $part); my $partclose = $res->parmval('printenddate', $part); - - $open_date = POSIX::strftime('%D', localtime($partopen)); - $close_date = POSIX::strftime('%D', localtime($partclose)); - - # TODO: Complete this function and use it to tailor the - # can't print current resource message. - # - + + push(@open_dates, $partopen); + push(@close_dates, $partclose); } } + ($open_date, $close_date) = &compute_open_window(\@open_dates, \@close_dates); + + if ($open_date) { + $open_date = POSIX::strftime('%D', localtime($open_date)); + } + if ($close_date) { + $close_date = POSIX::strftime('%D', localtime($close_date)); + } + + return ($open_date, $close_date); +} + +## +# Get the dates for which a course says a resource can be printed. This is like +# get_print_dates but namvaps::course_print_dates are gotten...and not converted +# to times either. +# +# @param $res - Reference to a resource has from lonnvampas::resource. +# +# @return (opendate, closedate) +# +sub course_print_dates { + my $res = shift; + my $partsref = $res->parts(); + my @parts = @$partsref; + my $open_date; + my $close_date; + my @open_dates; + my @close_dates; + my $navmap = $res->{NAV_MAP}; # Slightly OO dirty. + + # Don't bother looping over undefined or empty parts arraY; + + if (defined(@parts) && (scalar(@parts) > 0)) { + foreach my $part (@parts) { + my ($partopen, $partclose) = $navmap->course_printdates($res, $part); + push(@open_dates, $partopen); + push(@close_dates, $partclose); + } + ($open_date, $close_date) = &compute_open_window(\@open_dates, \@close_dates); + } + return ($open_date, $close_date); +} +## +# Same as above but for the enclosing map: +# +sub map_print_dates { + my $res = shift; + my $partsref = $res->parts(); + my @parts = @$partsref; + my $open_date; + my $close_date; + my @open_dates; + my @close_dates; + my $navmap = $res->{NAV_MAP}; # slightly OO dirty. + + + # Don't bother looping over undefined or empty parts arraY; + + if (defined(@parts) && (scalar(@parts) > 0)) { + foreach my $part (@parts) { + my ($partopen, $partclose) = $navmap->map_printdates($res, $part); + push(@open_dates, $partopen); + push(@close_dates, $partclose); + } + ($open_date, $close_date) = &compute_open_window(\@open_dates, \@close_dates); + } return ($open_date, $close_date); } @@ -3756,6 +3963,10 @@ sub printHelper { my $userCanPrint = ($perm{'pav'} || $perm{'pfo'}); my $res_printstartdate; my $res_printenddate; + my $map_open = 0; + my $map_close = 0xffffffff; + my $course_open = 0; + my $course_close = 0xffffffff; # Get the resource name from construction space if ($helper->{VARS}->{'construction'}) { @@ -3774,6 +3985,9 @@ sub printHelper { my $res = $navmap->getBySymb($symb); $res_printable = $res->resprintable() | $userCanPrint; #printability in course context ($res_printstartdate, $res_printenddate) = &get_print_dates($res); + ($course_open, $course_close) = &course_print_dates($res); + ($map_open, $map_close) = &map_print_dates($res); + } else { # Resource space. @@ -3792,6 +4006,7 @@ sub printHelper { if (!$helper->{VARS}->{'curseed'} && $env{'form.curseed'}) { $helper->{VARS}->{'curseed'}=$env{'form.curseed'}; } + if (!$helper->{VARS}->{'probstatus'} && $env{'form.problemtype'}) { $helper->{VARS}->{'probstatus'}=$env{'form.problemstatus'}; } @@ -3927,7 +4142,7 @@ sub printHelper { my $nextState = 'CHOOSE_INCOMPLETE_SEQ'; my $textSuffix = ''; - if ($userCanPrint) { + if ($userCanPrint) { $printSelector = 'map_incomplete_problems_people_seq'; $nextState = 'CHOOSE_INCOMPLETE_PEOPLE_SEQ'; $textSuffix = ' for selected students'; @@ -3935,51 +4150,59 @@ sub printHelper { &create_incomplete_folder_selstud_helper($helper, $map); &Apache::lonxml::xmlparse($r, 'helper', $helperStates); } else { - my $helperStates = &create_incomplete_folder_helper($helper, $map); # Create needed states for student. - &Apache::lonxml::xmlparse($r, 'helper', $helperStates); + if (&printable($map_open, $map_close)) { + my $helperStates = &create_incomplete_folder_helper($helper, $map); # Create needed states for student. + &Apache::lonxml::xmlparse($r, 'helper', $helperStates); + } else { + # TODO: Figure out how to break the news...this folder is not printable. + } } - push(@{$printChoices}, - [&mt('Selected [_1]Incomplete Problems[_2] from folder [_3]' . $textSuffix, - '', '', - ''. $sequenceTitle . ''), - $printSelector, - $nextState]); - + if ($userCanPrint || &printable($map_open, $map_close)) { + push(@{$printChoices}, + [&mt('Selected [_1]Incomplete Problems[_2] from folder [_3]' . $textSuffix, + '', '', + ''. $sequenceTitle . ''), + $printSelector, + $nextState]); + } # Allow problems from sequence - push @{$printChoices}, + if ($userCanPrint || &printable($map_open, $map_close)) { + push @{$printChoices}, [&mt('Selected [_1]Problems[_2] from folder [_3]','','',''.$sequenceTitle.''), 'map_problems', 'CHOOSE_PROBLEMS']; - # Allow all resources from sequence - push @{$printChoices}, [&mt('Selected [_1]Resources[_2] from folder [_3]','','',''.$sequenceTitle.''), - 'map_problems_pages', - 'CHOOSE_PROBLEMS_HTML']; - my $helperFragment = &generate_resource_chooser('CHOOSE_PROBLEMS', - 'Select Problem(s) to print', - 'multichoice="1" toponly="1" addstatus="1" closeallpages="1"', - 'RESOURCES', - 'PAGESIZE', - $map, - $isProblem, '', - $symbFilter, - $start_new_option); - $helperFragment .= &generate_resource_chooser('CHOOSE_PROBLEMS_HTML', - 'Select Resource(s) to print', - 'multichoice="1" toponly="1" addstatus="1" closeallpages="1"', - 'RESOURCES', - 'PAGESIZE', - $map, - $isNotMap, '', - $symbFilter, - $start_new_option); - - &Apache::lonxml::xmlparse($r, 'helper', $helperFragment); + # Allow all resources from sequence + push @{$printChoices}, [&mt('Selected [_1]Resources[_2] from folder [_3]','','',''.$sequenceTitle.''), + 'map_problems_pages', + 'CHOOSE_PROBLEMS_HTML']; + my $helperFragment = &generate_resource_chooser('CHOOSE_PROBLEMS', + 'Select Problem(s) to print', + 'multichoice="1" toponly="1" addstatus="1" closeallpages="1"', + 'RESOURCES', + 'PAGESIZE', + $map, + ! $isProblem, '', + $symbFilter, + $start_new_option); + $helperFragment .= &generate_resource_chooser('CHOOSE_PROBLEMS_HTML', + 'Select Resource(s) to print', + 'multichoice="1" toponly="1" addstatus="1" closeallpages="1"', + 'RESOURCES', + 'PAGESIZE', + $map, + $isNotMap, '', + $symbFilter, + $start_new_option); + + &Apache::lonxml::xmlparse($r, 'helper', $helperFragment); + } else { + # TODO: Figure out how to tell them the folder is not printable. + } } - - # If the user has pfo (print for others) allow them to print all - # problems and resources in the entire course, optionally for selected students - my $post_data = $helper->{VARS}->{'postdata'}; + # If the user has pfo (print for others) allow them to print all + # problems and resources in the entire course, optionally for selected students + my $post_data = $helper->{VARS}->{'postdata'}; if ($perm{'pfo'} && !$is_published && ($post_data=~/\/res\// || $post_data =~/\/(syllabus|smppg|aboutme|bulletinboard)$/)) { @@ -4342,7 +4565,7 @@ CHOOSE_FROM_SUBDIR Select the sequence to print resources from: CHOOSE_FROM_ANY_SEQUENCE - return \$res->is_sequence; + return &Apache::lonprintout::printable_sequence(\$res); return $urlValue; return \$res->hasResource(\$res,sub { return !\$_[0]->is_sequence() },0,0);