--- loncom/interface/lonprintout.pm	2012/04/04 21:04:56	1.614
+++ loncom/interface/lonprintout.pm	2016/08/10 16:06:03	1.650
@@ -1,7 +1,7 @@
 # The LearningOnline Network
 # Printout
 #
-# $Id: lonprintout.pm,v 1.614 2012/04/04 21:04:56 raeburn Exp $
+# $Id: lonprintout.pm,v 1.650 2016/08/10 16:06:03 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -46,7 +46,6 @@ use File::Basename;
 
 use HTTP::Response;
 use LONCAPA::map();
-use POSIX qw(strftime);
 use Apache::lonlocal;
 use Carp;
 use LONCAPA;
@@ -76,6 +75,58 @@ 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();
+	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) { last; }
+	    if (ref($first) && ! $first->is_sequence()) {last; }
+	    $first = $iterator->next();
+	}
+
+
+	# Might be an empty map:
+
+	if (!ref($first)) {
+	    return 0;
+	}
+	my $partsref = $first->parts();
+	my @parts    = @$partsref;
+	my ($open, $close) = $navmap->map_printdates($first, $parts[0]);
+	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.
@@ -96,8 +147,7 @@ my $font_size = 'normalsize';	# Default
 # Return:
 #     XML that can be parsed by the helper to drive the state machine.
 #
-sub create_incomplete_folder_selstud_helper($helper)
-{
+sub create_incomplete_folder_selstud_helper {
     my ($helper, $map)  = @_;
 
 
@@ -434,6 +484,197 @@ 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 returned 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 (($earliest_close ne '') && ($latest_open ne '')
+	 && ($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
+# the intersection of those dates.
+# 
+# @param res - lonnvamaps::resource object that represents the resource.
+#
+# @return (opendate, closedate)
+#
+# @note If open/close dates are not defined they will be returned 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.
+#       For consistency with &printable() in lonnavmaps.pm determination of start
+#       date for printing checks printstartdate param first, then, if not set,
+#       opendate param, then, if not set, contentopen param.
+
+sub get_print_dates {
+    my $res = shift;
+    my $partsref = $res->parts();
+    my @parts;
+    if (ref($partsref) eq 'ARRAY') {
+        @parts   = @{$partsref};
+    }
+    my $open_date;
+    my $close_date;
+    my @open_dates;
+    my @close_dates;
+
+
+    if (@parts) {
+	foreach my $part (@parts) {
+	    my $partopen  = $res->parmval('printstartdate', $part);
+	    my $partclose = $res->parmval('printenddate',  $part);
+            if (!$partopen) {
+                $partopen = $res->parmval('opendate',$part);
+            }
+            if (!$partopen) {
+                $partopen = $res->parmval('contentopen',$part);
+            }
+            if ($partopen) {
+                push(@open_dates, $partopen);
+            }
+            if ($partclose) {
+                push(@close_dates, $partclose);
+            }
+	    push(@open_dates, $partopen);
+	    push(@close_dates, $partclose);
+	}
+    }
+
+    ($open_date, $close_date)  = &compute_open_window(\@open_dates, \@close_dates);
+
+    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 (@parts) {
+	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 (@parts) {
+	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);
+}
+
 # Determine if a resource is incomplete given the map:
 # Parameters:
 #   $username - Name of user for whom we are checking.
@@ -459,19 +700,25 @@ sub incomplete {
     }
 }
 #
-#  When printing for students, the resoures and order of the
+#  When printing for students, the resources and order of the
 #  resources may need to be altered if there are folders with
 #  random selectiopn or random ordering (or both) enabled.
 #  This sub computes the set of resources to print for a student
 #  modified both by random ordering and selection and filtered
-#  to only those that are in the original set selcted to be printed.
+#  to only those that are in the original set selected to be printed.
 #
 # Parameters:
-#   $helper - The helper we need $helper->{'VARS'}->{'symb'}
-#            to construct the navmap and the iteration.
-#   $seq   - The original set of resources to print 
+#   $map - The URL of the folder being printed.
+#          Used to determine which startResource and finishResource
+#          to use when using the navmap's getIterator method.
+#   $seq   - The original set of resources to print.
 #            (really an array of resource names (array of symb's).
 #   $who   - Student/domain for whome the sequence will be generated.
+#   $code  - CODE being printed when printing Problems/Resources
+#            from folder for CODEd assignments
+#   $nohidemap - If true, parameter in map for hiddenresource will be
+#                ignored.  The user calling the routine should have
+#                both the pav and vgr privileges if this is set to true).
 #
 # Implicit inputs:
 #   $
@@ -480,33 +727,40 @@ sub incomplete {
 #   print_resources.
 # 
 sub master_seq_to_person_seq {
-    my ($helper, $seq, $who) = @_;
+    my ($map, $seq, $who, $code, $nohidemap) = @_;
 
 
     my ($username, $userdomain, $usersection) = split(/:/, $who);
 
-
     # Toss the sequence up into a hash so that we have O(1) lookup time.
     # on the items that come out of the user's list of resources.
     #
-    
+
     my %seq_hash = map {$_  => 1} @$seq;
     my @output_seq;
+
+    my $unhidden;
+    if ($nohidemap) {
+        $unhidden = &Apache::lonnet::clutter($map);
+    }
     
-    my ($map, $id, $url) = &Apache::lonnet::decode_symb($helper->{VARS}->{'symb'});
-    my $navmap           = Apache::lonnavmaps::navmap->new($username, $userdomain);
-    my $iterator         = $navmap->getIterator($navmap->firstResource(),
-						$navmap->finishResource(),
-						{}, 1);
-    my %nonResourceItems = (
-	$iterator->BEGIN_MAP    => 1,
-	$iterator->BEGIN_BRANCH => 1,
-	$iterator->END_BRANCH   => 1,
-	$iterator->END_MAP      => 1,
-	$iterator->FORWARD      => 1,
-	$iterator->BACKWARD     => 1
+    my $navmap           = Apache::lonnavmaps::navmap->new($username, $userdomain,
+                                                           $code, $unhidden);
+    my ($start,$finish);
+
+    if ($map) {
+        my $mapres = $navmap->getResourceByUrl($map);
+        if ($mapres->is_map()) {
+            $start = $mapres->map_start();
+            $finish = $mapres->map_finish();
+        }
+    }
+    unless ($start && $finish) {
+        $start = $navmap->firstResource();
+        $finish = $navmap->finishResource();
+    }
 
-    );		# These items are not resources but appear in the midst of iteration.
+    my $iterator         = $navmap->getIterator($start,$finish,{},1);
 
     #  Iterate on the resource..select the items that are randomly selected
     #  and that are in the seq_has.  Presumably the iterator will take care
@@ -518,15 +772,13 @@ sub master_seq_to_person_seq {
 	#  Only process resources..that are not removed by randomout...
 	#  and are selected for printint as well.
 	#
-     
-	if (! exists $nonResourceItems{$curres} && ! $curres->randomout()) {
-	    my $symb = $curres->symb();
-	    if (exists $seq_hash{$symb}) {
-		push(@output_seq, $symb);
+        if (ref($curres) && ! $curres->randomout()) {
+            my $currsymb = $curres->symb();
+            if (exists($seq_hash{$currsymb})) {
+                push(@output_seq, $currsymb);
 	    }
 	}
     }
-   				
 
     return \@output_seq;		# for now.
     
@@ -593,9 +845,9 @@ sub set_font_size {
 
     if ($font_size ne '') {
 
-	$text =~ s/\\begin{document}/\\begin{document}{\\$font_size/;
+	$text =~ s/\\begin\{document}/\\begin{document}{\\$font_size/;
     }
-    $text =~ s/\\end{document}/}\\end{document}/;
+    $text =~ s/\\end\{document}/}\\end{document}/;
     return $text;
 
 
@@ -672,13 +924,13 @@ sub include_pdf {
 sub collect_languages {
     my $doc = shift;
     my %languages;
-    while ($doc =~ /\\selectlanguage{(\w+)}/mg) {
+    while ($doc =~ /\\selectlanguage\{(\w+)}/mg) {
 	$languages{$1} = 1;	# allows us to request each language exactly once.
     }
     my @lang_list = (keys(%languages)); # List of unique languages
     if (scalar @lang_list) {
 	my $babel_header = '\usepackage[' . join(',', @lang_list) .']{babel}'. "\n";
-	$doc =~ s/\\begin{document}/$babel_header\\begin{document}/;
+	$doc =~ s/\\begin\{document}/$babel_header\\begin{document}/;
     }
     return $doc;
 }
@@ -860,7 +1112,7 @@ sub format_page_header {
     # there is '\\ \\ ' in the page header. That's cause a error in LaTeX
     if($format =~ /\\\\\s\\\\\s/) {
         #TODO find sensible caption for page header
-        my $testPrintout = '\\\\'.&mt('Construction Space').' \\\\'.&mt('Test-Printout ');
+        my $testPrintout = '\\\\'.&mt('Authoring Space').' \\\\'.&mt('Test-Printout ');
         $format =~ s/\\\\\s\\\\\s/$testPrintout/;
     }
     #
@@ -998,6 +1250,22 @@ sub is_code_valid {
     }
 
 }
+#
+# Compare two students by section (Used to sort by section).
+#
+#  Implicit inputs, 
+#    $a - The first one
+#    $b - The second one.
+#
+#  Returns:
+#     a-section cmp b-section
+#
+sub compare_sections {
+    my ($u1, $d1, $s1, $n1, $stat1) = split(/:/, $a);
+    my ($u2, $d2, $s2, $n2, $stat2) = split(/:/, $b);
+
+    return $s1 cmp $s2;
+}
 
 #   Compare two students by name.  The students are in the form
 #   returned by the helper:
@@ -1059,8 +1327,8 @@ sub compare_names {
 
 sub latex_header_footer_remove {
     my $text = shift;
-    $text =~ s/\\end{document}//;
-    $text =~ s/\\documentclass([^&]*)\\begin{document}//;
+    $text =~ s/\\end\{document}//;
+    $text =~ s/\\documentclass([^&]*)\\begin\{document}//;
     return $text;
 }
 #
@@ -1610,12 +1878,12 @@ sub page_format_transformation {
 	$fancypagestatement="\\rhead{}\\chead{}\\lhead{$header_text}";
     }
     if ($layout eq 'album') {
-	    $text =~ s/\\begin{document}/\\setlength{\\oddsidemargin}{$oddoffset}\\setlength{\\evensidemargin}{$evenoffset}$topmargintoinsert\n\\setlength{\\textwidth}{$textwidth}\\setlength{\\textheight}{$textheight}\\setlength{\\textfloatsep}{8pt plus 2\.0pt minus 4\.0pt}\n\\newlength{\\minipagewidth}\\setlength{\\minipagewidth}{\\textwidth\/\$number_of_columns-0\.2cm}\\usepackage{fancyhdr}\\addtolength{\\headheight}{\\baselineskip}\n\\pagestyle{fancy}$fancypagestatement\\usepackage{booktabs}\\begin{document}\\voffset=-0\.8 cm\\setcounter{page}{1}\n /;
+	    $text =~ s/\\begin\{document}/\\setlength{\\oddsidemargin}{$oddoffset}\\setlength{\\evensidemargin}{$evenoffset}$topmargintoinsert\n\\setlength{\\textwidth}{$textwidth}\\setlength{\\textheight}{$textheight}\\setlength{\\textfloatsep}{8pt plus 2\.0pt minus 4\.0pt}\n\\newlength{\\minipagewidth}\\setlength{\\minipagewidth}{\\textwidth\/\$number_of_columns-0\.2cm}\\usepackage{fancyhdr}\\addtolength{\\headheight}{\\baselineskip}\n\\pagestyle{fancy}$fancypagestatement\\usepackage{booktabs}\\begin{document}\\voffset=-0\.8 cm\\setcounter{page}{1}\n /;
     } elsif ($layout eq 'book') {
 	if ($choice ne 'All class print') { 
-	    $text =~ s/\\begin{document}/\\textheight $textheight\\oddsidemargin = $evenoffset\\evensidemargin = $evenoffset $topmargintoinsert\n\\textwidth= $textwidth\\newlength{\\minipagewidth}\\setlength{\\minipagewidth}{\\textwidth\/\$number_of_columns-0\.2cm}\n\\renewcommand{\\ref}{\\keephidden\}\\usepackage{fancyhdr}\\addtolength{\\headheight}{\\baselineskip}\\pagestyle{fancy}$fancypagestatement\\usepackage{booktabs}\\begin{document}\n\\voffset=-0\.8 cm\\setcounter{page}{1}\n/;
+	    $text =~ s/\\begin\{document}/\\textheight $textheight\\oddsidemargin = $evenoffset\\evensidemargin = $evenoffset $topmargintoinsert\n\\textwidth= $textwidth\\newlength{\\minipagewidth}\\setlength{\\minipagewidth}{\\textwidth\/\$number_of_columns-0\.2cm}\n\\renewcommand{\\ref}{\\keephidden\}\\usepackage{fancyhdr}\\addtolength{\\headheight}{\\baselineskip}\\pagestyle{fancy}$fancypagestatement\\usepackage{booktabs}\\begin{document}\n\\voffset=-0\.8 cm\\setcounter{page}{1}\n/;
 	} else {
-	    $text =~ s/\\pagestyle{fancy}\\rhead{}\\chead{}\s*\\begin{document}/\\textheight = $textheight\\oddsidemargin = $evenoffset\n\\evensidemargin = $evenoffset $topmargintoinsert\\textwidth= $textwidth\\newlength{\\minipagewidth}\n\\setlength{\\minipagewidth}{\\textwidth\/\$number_of_columns-0\.2cm}\\renewcommand{\\ref}{\\keephidden\}\\pagestyle{fancy}\\rhead{}\\chead{}\\usepackage{booktabs}\\begin{document}\\voffset=-0\.8cm\n\\setcounter{page}{1}  \\vskip 5 mm\n /;
+	    $text =~ s/\\pagestyle\{fancy}\\rhead\{}\\chead\{}\s*\\begin\{document}/\\textheight = $textheight\\oddsidemargin = $evenoffset\n\\evensidemargin = $evenoffset $topmargintoinsert\\textwidth= $textwidth\\newlength{\\minipagewidth}\n\\setlength{\\minipagewidth}{\\textwidth\/\$number_of_columns-0\.2cm}\\renewcommand{\\ref}{\\keephidden\}\\pagestyle{fancy}\\rhead{}\\chead{}\\usepackage{booktabs}\\begin{document}\\voffset=-0\.8cm\n\\setcounter{page}{1}  \\vskip 5 mm\n /;
 	}
 	if ($papersize eq 'a4') {
 	    my $papersize_text;
@@ -1624,13 +1892,13 @@ sub page_format_transformation {
 	    } else {
 		$papersize_text = '\special{papersize=210mm,297mm}';
 	    }
-	    $text =~ s/(\\begin{document})/$1$papersize_text/;
+	    $text =~ s/(\\begin\{document})/$1$papersize_text/;
 	}
     }
     if ($tableofcontents eq 'yes') {$text=~s/(\\setcounter\{page\}\{1\})/$1 \\tableofcontents\\newpage /;}
     if ($indexlist eq 'yes') {
-	$text=~s/(\\begin{document})/\\makeindex $1/;
-	$text=~s/(\\end{document})/\\strut\\\\\\strut\\printindex $1/;
+	$text=~s/(\\begin\{document})/\\makeindex $1/;
+	$text=~s/(\\end\{document})/\\strut\\\\\\strut\\printindex $1/;
     }
     return $text;
 }
@@ -1639,12 +1907,12 @@ sub page_format_transformation {
 sub page_cleanup {
     my $result = shift;	
  
-    $result =~ m/\\end{document}(\d*)$/;
+    $result =~ m/\\end\{document}(\d*)$/;
     my $number_of_columns = $1;
     my $insert = '{';
     for (my $id=1;$id<=$number_of_columns;$id++) { $insert .='l'; }
     $insert .= '}';
-    $result =~ s/(\\begin{longtable})INSERTTHEHEADOFLONGTABLE\\endfirsthead\\endhead/$1$insert/g;
+    $result =~ s/(\\begin\{longtable})INSERTTHEHEADOFLONGTABLE\\endfirsthead\\endhead/$1$insert/g;
     $result =~ s/&\s*REMOVETHEHEADOFLONGTABLE\\\\/\\\\/g;
     return $result,$number_of_columns;
 }
@@ -1680,16 +1948,16 @@ my $end_of_student = "\n".'\special{ps:E
 
 sub latex_corrections {
     my ($number_of_columns,$result,$selectionmade,$answer_mode) = @_;
-#    $result =~ s/\\includegraphics{/\\includegraphics\[width=\\minipagewidth\]{/g;
+#    $result =~ s/\\includegraphics\{/\\includegraphics\[width=\\minipagewidth\]{/g;
     my $copyright = &copyright_line();
     if ($selectionmade eq '1' || $answer_mode eq 'only') {
-	$result =~ s/(\\end{document})/\\strut\\vskip 0 mm $copyright $end_of_student $1/;
+	$result =~ s/(\\end\{document})/\\strut\\vskip 0 mm $copyright $end_of_student $1/;
     } else {
-	$result =~ s/(\\end{document})/\\strut\\vspace\*{-4 mm}\\newline $copyright $end_of_student $1/;
+	$result =~ s/(\\end\{document})/\\strut\\vspace\*{-4 mm}\\newline $copyright $end_of_student $1/;
     }
     $result =~ s/\$number_of_columns/$number_of_columns/g;
-    $result =~ s/(\\end{longtable}\s*)(\\strut\\newline\\noindent\\makebox\[\\textwidth\/$number_of_columns\]\[b\]{\\hrulefill})/$2$1/g;
-    $result =~ s/(\\end{longtable}\s*)\\strut\\newline/$1/g;
+    $result =~ s/(\\end\{longtable}\s*)(\\strut\\newline\\noindent\\makebox\[\\textwidth\/$number_of_columns\]\[b\]{\\hrulefill})/$2$1/g;
+    $result =~ s/(\\end\{longtable}\s*)\\strut\\newline/$1/g;
 #-- LaTeX corrections     
     my $first_comment = index($result,'<!--',0);
     while ($first_comment != -1) {
@@ -1708,8 +1976,8 @@ sub latex_corrections {
     if ($result =~ m/&(\w+|#\d+);/) {
 	$result = &character_chart($result);
     }
-    $result =~ s/(\\end{tabular})\s*\\vskip 0 mm/$1/g;
-    $result =~ s/(\\begin{enumerate})\s*\\noindent/$1/g;
+    $result =~ s/(\\end\{tabular})\s*\\vskip 0 mm/$1/g;
+    $result =~ s/(\\begin\{enumerate})\s*\\noindent/$1/g;
     return $result;
 }
 
@@ -1806,6 +2074,7 @@ sub unsupported {
     my $result.= &print_latex_header($mode);
     if ($currentURL=~m|^(/adm/wrapper/)?ext/|) {
 	$currentURL=~s|^(/adm/wrapper/)?ext/|http://|;
+        $currentURL=~s|^http://https://|https://|;
 	my $title=&Apache::lonnet::gettitle($symb);
 	$title = &Apache::lonxml::latex_special_symbols($title);
 	$result.=' \strut \\\\ '.$title.' \strut \\\\ '.$currentURL.' ';
@@ -1885,6 +2154,8 @@ sub print_page_in_course {
 	    my @page_resources = $navmap->retrieveResources($resource_src);
 	    $result           .= &print_page_in_course($helper, $rparmhash, 
 						       $resource_src, \@page_resources);
+        } elsif ($resource->ext()) {
+            $result .= &unsupported($currentURL,$mode,$symb);
 	}
 	# these resources go through the XML transformer:
 
@@ -1944,7 +2215,7 @@ sub print_page_in_course {
 		my $answer=&ssi_with_retries($urlp,$ssi_retry_count, %answerform);
 
 		if ($helper->{'VARS'}->{'ANSWER_TYPE'} eq 'no') {
-		    $texversion=~s/(\\keephidden{ENDOFPROBLEM})/$answer$1/;
+		    $texversion=~s/(\\keephidden\{ENDOFPROBLEM})/$answer$1/;
 		} else {
 		    $texversion= &print_latex_header($helper->{'VARS'}->{'LATEX_TYPE'});
 		    if ($helper->{'VARS'}->{'construction'} ne '1') {
@@ -1954,7 +2225,7 @@ sub print_page_in_course {
 			$texversion.=&path_to_problem($urlp,$LaTeXwidth);
 		    } else {
 			$texversion.='\vskip 0 mm \noindent\textbf{'.
-                        &mt("Printing from Construction Space: No Title").'}\vskip 0 mm ';
+                        &mt("Printing from Authoring Space: No Title").'}\vskip 0 mm ';
 			$texversion.=&path_to_problem($urlp,$LaTeXwidth);
 		    }
 		    $texversion.='\vskip 1 mm '.$answer.'\end{document}';
@@ -1970,7 +2241,7 @@ sub print_page_in_course {
 
 	    if ($helper->{'VARS'}->{'PRINT_ANNOTATIONS'} eq 'yes') {
 		my $annotation .= &annotate($currentURL);
-		$texversion =~ s/(\\keephidden{ENDOFPROBLEM})/$annotation$1/;
+		$texversion =~ s/(\\keephidden\{ENDOFPROBLEM})/$annotation$1/;
 	    }
 	    
 	    if ($helper->{'VARS'}->{'TABLE_INDEX'} eq 'yes') {
@@ -2262,7 +2533,7 @@ sub print_construction_sequence {
 		$resources_printed .= $urlp.':';
 		my $answer=&ssi_with_retries($urlp, $ssi_retry_count, %answerform);
 		if ($helper->{'VARS'}->{'ANSWER_TYPE'} eq 'no') {
-		    $texversion=~s/(\\keephidden{ENDOFPROBLEM})/$answer$1/;
+		    $texversion=~s/(\\keephidden\{ENDOFPROBLEM})/$answer$1/;
 		} else {
 		    # If necessary, encapsulate answer in minipage:
 		    
@@ -2315,7 +2586,7 @@ sub print_construction_sequence {
 	    $result .= $texversion;
 	}
     }
-    if ($helper->{VARS}->{'construction'} eq '1') {$result=~s/(\\begin{document})/$1 \\fbox\{RANDOM SEED IS $rndseed\} /;}
+    if ($helper->{VARS}->{'construction'} eq '1') {$result=~s/(\\begin\{document})/$1 \\fbox\{RANDOM SEED IS $rndseed\} /;}
     return $result;
 }
 
@@ -2351,6 +2622,7 @@ sub print_construction_sequence {
 #  For item 100, filtering was done at the helper level.
 
 sub output_data {
+
     my ($r,$helper,$rparmhash) = @_;
     my %parmhash = %$rparmhash;
     $ssi_error = 0;		# This will be set nonzero by failing ssi's.
@@ -2524,7 +2796,7 @@ ENDPART
 		
 
 		if ($helper->{'VARS'}->{'ANSWER_TYPE'} eq 'no') {
-		    $texversion=~s/(\\keephidden{ENDOFPROBLEM})/$answer$1/;
+		    $texversion=~s/(\\keephidden\{ENDOFPROBLEM})/$answer$1/;
 		} else {
 		    $texversion=&print_latex_header($helper->{'VARS'}->{'LATEX_TYPE'});
 		    if ($helper->{'VARS'}->{'construction'} ne '1') {
@@ -2534,7 +2806,7 @@ ENDPART
 			$texversion.=&path_to_problem($cleanURL,$LaTeXwidth);
 		    } else {
 			$texversion.='\vskip 0 mm \noindent\textbf{'.
-                        &mt("Printing from Construction Space: No Title").'}\vskip 0 mm ';
+                        &mt("Printing from Authoring Space: No Title").'}\vskip 0 mm ';
 
 			$texversion.=&path_to_problem($cleanURL,$LaTeXwidth);
 		    }
@@ -2551,7 +2823,7 @@ ENDPART
 
 	    if ($helper->{'VARS'}->{'PRINT_ANNOTATIONS'} eq 'yes') {
 		my $annotation .= &annotate($currentURL);
-		$texversion =~ s/(\\keephidden{ENDOFPROBLEM})/$annotation$1/;
+		$texversion =~ s/(\\keephidden\{ENDOFPROBLEM})/$annotation$1/;
 	    }
 
 
@@ -2582,7 +2854,7 @@ ENDPART
 		my $texversion = &ssi_with_retries($currentURL, $ssi_retry_count, %form);
 		if ($helper->{'VARS'}->{'PRINT_ANNOTATIONS'} eq 'yes') {
 		    my $annotation = &annotate($currentURL);
-		    $texversion    =~ s/(\\end{document})/$annotation$1/;
+		    $texversion    =~ s/(\\end\{document})/$annotation$1/;
 		}
 		$result .= $texversion;
 	} elsif ($cleanURL =~/\.tex$/) {
@@ -2593,7 +2865,7 @@ ENDPART
 	    $result = &fetch_raw_resource($currentURL);
 	    if ($helper->{'VARS'}->{'PRINT_ANNOTATIONS'} eq 'yes') {
 		my $annotation = &annotate($currentURL);
-		$result =~ s/(\\end{document})/$annotation$1/;
+		$result =~ s/(\\end\{document})/$annotation$1/;
 	    }
 
 	    $do_postprocessing = 0; # Don't massage the result.
@@ -2624,7 +2896,6 @@ ENDPART
 	     ($print_type eq 'select_sequences')      or
 	     ($print_type eq 'map_incomplete_problems_seq')
 	     ) {
-
  
         #-- produce an output string
 	if (($print_type eq 'map_problems')                or
@@ -2660,7 +2931,6 @@ ENDPART
 
 	&Apache::lonxml::clear_problem_counter();
 
-	my $pbreakresources = keys %page_breaks;
 	for (my $i=0;$i<=$#master_seq;$i++) {
 
 	    &Apache::lonenc::reset_enc();
@@ -2702,7 +2972,7 @@ ENDPART
 		if ($urlp=~/\.page$/) {
 		    ($texversion,my $number_of_columns_page) = &page_cleanup($texversion);
 		    if ($number_of_columns_page > $number_of_columns) {$number_of_columns=$number_of_columns_page;} 
-		    $texversion =~ s/\\end{document}\d*/\\end{document}/;
+		    $texversion =~ s/\\end\{document}\d*/\\end{document}/;
 		    $flag_page_in_sequence = 'YES';
 		}
 
@@ -2717,21 +2987,21 @@ ENDPART
 		    &Apache::lonxml::restore_problem_counter();
 		    my $answer=&ssi_with_retries($urlp, $ssi_retry_count, %answerform);
                     if ($urlp =~ /\.page$/) {
-                        $answer =~ s/\\end{document}(\d*)$//;
+                        $answer =~ s/\\end\{document}(\d*)$//;
                     }
 		    if ($helper->{'VARS'}->{'ANSWER_TYPE'} eq 'no') {
                         if ($urlp =~ /\.page$/) {
-                            my @probs = split(/\\keephidden{ENDOFPROBLEM}/,$texversion);
+                            my @probs = split(/\\keephidden\{ENDOFPROBLEM}/,$texversion);
                             my $lastprob = pop(@probs);
                             $texversion = join('\keephidden{ENDOFPROBLEM}',@probs).
                             $answer.'\keephidden{ENDOFPROBLEM}'.$lastprob;
                         } else {
-                            $texversion=~s/(\\keephidden{ENDOFPROBLEM})/$answer$1/;
+                            $texversion=~s/(\\keephidden\{ENDOFPROBLEM})/$answer$1/;
                         }
 		    } else {
 			if ($urlp=~/$LONCAPA::assess_page_re/) {
 			    $texversion=&print_latex_header($helper->{'VARS'}->{'LATEX_TYPE'});
-#			    $texversion =~ s/\\begin{document}//; # FIXME
+#			    $texversion =~ s/\\begin\{document}//; # FIXME
 			    my $title = &Apache::lonnet::gettitle($master_seq[$i]);
 			    $title = &Apache::lonxml::latex_special_symbols($title);
 			    my $body ='\vskip 0 mm \noindent\textbf{'.$title.'}\vskip 0 mm ';
@@ -2747,13 +3017,13 @@ ENDPART
 		}
 		if ($helper->{'VARS'}->{'PRINT_ANNOTATIONS'} eq 'yes') {
 		    my $annotation .= &annotate($urlp);
-		    $texversion =~ s/(\\keephidden{ENDOFPROBLEM})/$annotation$1/;
+		    $texversion =~ s/(\\keephidden\{ENDOFPROBLEM})/$annotation$1/;
 		}
 
 		if ($flag_latex_header_remove ne 'NO') {
 		    $texversion = &latex_header_footer_remove($texversion);
 		} else {
-		    $texversion =~ s/\\end{document}//;
+		    $texversion =~ s/\\end\{document}//;
 		}
 		if ($helper->{'VARS'}->{'TABLE_INDEX'} eq 'yes') {
 		    $texversion=&IndexCreation($texversion,$urlp);
@@ -2767,7 +3037,6 @@ ENDPART
 							  $assignment, 
 							  $courseidinfo, 
 							  $name);
-
 		    if ($numberofcolumns eq '1') {
 			$result .='\newpage \noindent\parbox{\minipagewidth}{\noindent\\lhead{'.$header_text.'}} \vskip 5 mm ';
 		    } else {
@@ -2783,13 +3052,13 @@ ENDPART
 		my $texversion = &ssi_with_retries($urlp, $ssi_retry_count, %form);
 		if ($helper->{'VARS'}->{'PRINT_ANNOTATIONS'} eq 'yes') {
 		    my $annotation = &annotate($urlp);
-		    $texversion =~ s/(\\end{document)/$annotation$1/;
+		    $texversion =~ s/(\\end\{document)/$annotation$1/;
 		}
 
 		if ($flag_latex_header_remove ne 'NO') {
 		    $texversion = &latex_header_footer_remove($texversion);
 		} else {	
-		    $texversion =~ s/\\end{document}/\\vskip 0\.5mm\\noindent\\makebox\[\\textwidth\/\$number_of_columns\]\[b\]\{\\hrulefill\}/;
+		    $texversion =~ s/\\end\{document}/\\vskip 0\.5mm\\noindent\\makebox\[\\textwidth\/\$number_of_columns\]\[b\]\{\\hrulefill\}/;
 		}
 		$result .= $texversion;
 		$flag_latex_header_remove = 'YES'; 
@@ -2820,7 +3089,7 @@ ENDPART
 		if ($flag_latex_header_remove ne 'NO') {
 		    $texversion = &latex_header_footer_remove($texversion);
 		} else {
-		    $texversion =~ s/\\end{document}//;
+		    $texversion =~ s/\\end\{document}//;
 		}
 		$result .= $texversion;
 		$flag_latex_header_remove = 'YES';   
@@ -2831,7 +3100,7 @@ ENDPART
 	}
 	&Apache::lonxml::clear_problem_counter();
 	if ($flag_page_in_sequence eq 'YES') {
-	    $result =~ s/\\usepackage{calc}/\\usepackage{calc}\\usepackage{longtable}/;
+	    $result =~ s/\\usepackage\{calc}/\\usepackage{calc}\\usepackage{longtable}/;
 	}	
 	$result .= '\end{document}';
      } elsif (($print_type eq 'problems_for_students')           ||
@@ -2868,6 +3137,8 @@ ENDPART
 	 if (($helper->{'VARS'}->{'student_sort'}    eq 1)  && 
 	     ($helper->{'VARS'}->{'SPLIT_PDFS'} ne "sections")) {
 	     @students = sort compare_names  @students;
+	 } else {
+	     @students = sort compare_sections @students; 
 	 }
 	 &adjust_number_to_print($helper);
 
@@ -2886,6 +3157,12 @@ ENDPART
 	 }
 	 my @master_seq=split /\|\|\|/, $helper->{'VARS'}->{'RESOURCES'};
 
+         my $map;
+         if ($helper->{VARS}->{'symb'}) {
+             ($map, my $id, my $resource) =
+                 &Apache::lonnet::decode_symb($helper->{VARS}->{'symb'});
+         }
+
 	 #loop over students
 
  	 my $flag_latex_header_remove = 'NO';
@@ -2904,6 +3181,10 @@ ENDPART
 	 my $student_counter=-1;
 	 my $i = 0;
 	 my $last_section = (split(/:/,$students[0]))[2];
+         my $nohidemap;
+         if ($perm{'pav'} && $perm{'vgr'}) {
+             $nohidemap = 1;
+         }
 	 foreach my $person (@students) {
              my $duefile="/home/httpd/prtspool/$env{'user.name'}_$env{'user.domain'}_printout.due";
 	     if (-e $duefile) {
@@ -2920,7 +3201,8 @@ ENDPART
 	     } else {
 		 $i=int($student_counter/$helper->{'VARS'}{'NUMBER_TO_PRINT'});
 	     }
-	     my $actual_seq = master_seq_to_person_seq($helper, \@master_seq, $person);
+	     my $actual_seq = master_seq_to_person_seq($map, \@master_seq,
+                                                       $person, undef, $nohidemap);
 	     my ($output,$fullname, $printed)=&print_resources($r,$helper,
 						     $person,$type,
 						     \%moreenv,  $actual_seq,
@@ -2946,7 +3228,6 @@ ENDPART
 	 my $old_name=$helper->{'VARS'}->{'REUSE_OLD_CODES'};
 	 my $single_code = $helper->{'VARS'}->{'SINGLE_CODE'};
 	 my $selected_code = $helper->{'VARS'}->{'CODE_SELECTED_FROM_LIST'};
-
 	 my $code_option=$helper->{'VARS'}->{'CODE_OPTION'};
          my @lines = &Apache::grades::get_scantronformat_file();
 	 my ($code_type,$code_length,$bubbles_per_row)=('letter',6,10);
@@ -2963,6 +3244,19 @@ ENDPART
                  }
 	     }
 	 }
+         my ($randomorder,$randompick,$map);
+         if ($helper->{VARS}{'symb'}) {
+             ($map, my $id, my $resource) =
+                 &Apache::lonnet::decode_symb($helper->{VARS}{'symb'});
+             my $navmap = Apache::lonnavmaps::navmap->new();
+             if (defined($navmap)) {
+                 if ($map) {
+                     my $mapres = $navmap->getResourceByUrl($map);
+                     $randomorder = $mapres->randomorder();
+                     $randompick = $mapres->randompick();
+                 }
+             }
+         }
 	 my %moreenv = ('textwidth' => &get_textwidth($helper,$LaTeXwidth));
 	 $moreenv{'problem_split'}    = $parmhash{'problem_stream_switch'};
          $moreenv{'instructor_comments'}='hide';
@@ -2996,6 +3290,8 @@ ENDPART
 		 $moreenv{'CODE'}=&get_CODE(\%allcodes,$i,$seed,$code_length,
 					    $code_type);
 	     }
+             $code_name =~ s/^\s+//;
+             $code_name =~ s/\s+$//;
 	     if ($code_name) {
 		 &Apache::lonnet::put('CODEs',
 				      {
@@ -3017,6 +3313,10 @@ ENDPART
 	 my $flag_latex_header_remove = 'NO'; 
 	 my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin($r,$num_todo);
 	 my $count=0;
+         my $nohidemap;
+         if ($perm{'pav'} && $perm{'vgr'}) {
+             $nohidemap = 1;
+         }
 	 foreach my $code (sort(@allcodes)) {
 	     my $file_num=int($count/$number_per_page);
 	     if ($code_type eq 'number') { 
@@ -3024,9 +3324,17 @@ ENDPART
 	     } else {
 		 $moreenv{'CODE'}=&num_to_letters($code);
 	     }
+             my $actual_seq = \@master_seq;
+             if ($randomorder || $randompick) {
+                 $env{'form.CODE'} = $moreenv{'CODE'};
+                 $actual_seq = master_seq_to_person_seq($map, \@master_seq,
+                                                        undef,
+                                                        $moreenv{'CODE'}, $nohidemap);
+                 delete($env{'form.CODE'});
+             }
 	     my ($output,$fullname, $printed)=
 		 &print_resources($r,$helper,'anonymous',$type,\%moreenv,
-				  \@master_seq,$flag_latex_header_remove,
+				  $actual_seq,$flag_latex_header_remove,
 				  $LaTeXwidth);
 	     $resources_printed .= ":";
 	     $print_array[$file_num].=$output;
@@ -3071,7 +3379,7 @@ ENDPART
 		    $resources_printed .= $urlp.':';
 		    my $answer=&ssi_with_retries($urlp, $ssi_retry_count, %answerform);
 		    if ($helper->{'VARS'}->{'ANSWER_TYPE'} eq 'no') {
-			$texversion=~s/(\\keephidden{ENDOFPROBLEM})/$answer$1/;
+			$texversion=~s/(\\keephidden\{ENDOFPROBLEM})/$answer$1/;
 		    } else {
 			$texversion=&print_latex_header($helper->{'VARS'}->{'LATEX_TYPE'});
 			if ($helper->{'VARS'}->{'construction'} ne '1') {
@@ -3079,7 +3387,7 @@ ENDPART
 			    $texversion.=&path_to_problem ($urlp,$LaTeXwidth);
 			} else {
 			    $texversion.='\vskip 0 mm \noindent\textbf{'.
-                                         &mt("Printing from Construction Space: No Title").'}\vskip 0 mm ';
+                                         &mt("Printing from Authoring Space: No Title").'}\vskip 0 mm ';
 			    $texversion.=&path_to_problem ($urlp,$LaTeXwidth);
 			}
 			$texversion.='\vskip 1 mm '.$answer.'\end{document}';
@@ -3088,11 +3396,11 @@ ENDPART
                 #this chunk is responsible for printing the path to problem
 
 		my $newurlp=&path_to_problem($urlp,$LaTeXwidth);
-		$texversion =~ s/(\\begin{minipage}{\\textwidth})/$1 $newurlp/;
+		$texversion =~ s/(\\begin\{minipage}\{\\textwidth})/$1 $newurlp/;
 		if ($flag_latex_header_remove ne 'NO') {
 		    $texversion = &latex_header_footer_remove($texversion);
 		} else {
-		    $texversion =~ s/\\end{document}//;
+		    $texversion =~ s/\\end\{document}//;
 		}
 		if ($helper->{'VARS'}->{'TABLE_INDEX'} eq 'yes') {
 		    $texversion=&IndexCreation($texversion,$urlp);
@@ -3130,13 +3438,16 @@ ENDPART
 	#}
     }
 
-    # Set URLback if this is a construction space print so we can provide
-    # a link to the resource being edited.
-    #
+    # Set URLback so we can provide a link back to the resource and to change options.
+    # (Since the browser back button does not currently work with https,
+    # the back link is useful even when there is an easy-to-miss LON-CAPA back button.)
 
     my $URLback=''; #link to original document
     if ($helper->{'VARS'}->{'construction'} eq '1') {
 	$URLback=$helper->{'VARS'}->{'filename'};
+    } elsif ($helper->{VARS}{'symb'}) {
+        my ($map, $id, $url) = &Apache::lonnet::decode_symb($helper->{VARS}{'symb'});
+        $URLback = &Apache::lonnet::clutter($url);
     }
     #
     # Final adjustment of the font size:
@@ -3277,6 +3588,7 @@ sub print_resources {
     my $fullname = &get_name($username,$userdomain);
     my $namepostfix = "\\\\";	# Both anon and not anon should get the same vspace.
 
+
     #
     # Figure out if we need to filter the output by
     # the incomplete problems for that person
@@ -3315,6 +3627,11 @@ sub print_resources {
     #
 
     my $syllabus_first = 0;
+    my $current_assignment = "";
+    my $assignment;
+    my $courseidinfo = &get_course();
+    my $possprint = scalar(@{$master_seq});
+
     foreach my $curresline (@{$master_seq})  {
 	if (defined $page_breaks{$curresline}) {
 	    if($i != 0) {
@@ -3323,13 +3640,17 @@ sub print_resources {
 	}
 	$current_output .= &get_extra_vspaces($helper, $curresline);
 	$i++;
+	my ($map,$id,$res_url) = &Apache::lonnet::decode_symb($curresline);
+
+	# See if we need to emit a new header:
+
 	if ( !($type eq 'problems' && 
 	       ($curresline!~ m/$LONCAPA::assess_page_re/)) ) {
-	    my ($map,$id,$res_url) = &Apache::lonnet::decode_symb($curresline);
 	    if ($print_incomplete && !&incomplete($username, $userdomain, $res_url)) {
 		next;
 	    }
 	    $actually_printed++; # we're going to print one.
+
 	    if (&Apache::lonnet::allowed('bre',$res_url)) {
 		if ($res_url!~m|^ext/|
 		    && $res_url=~/\.(problem|exam|quiz|assess|survey|form|library|page|xml|html|htm|xhtml|xhtm)$/) {
@@ -3347,7 +3668,7 @@ sub print_resources {
                         if ($remove_latex_header eq 'YES') {
                             $rendered = &latex_header_footer_remove($rendered);
                         } else {
-                            $rendered =~ s/\\end{document}\d*//;
+                            $rendered =~ s/\\end\{document}\d*//;
                         }
                     }
 		    if(($helper->{'VARS'}->{'ANSWER_TYPE'} eq 'no') ||
@@ -3364,13 +3685,13 @@ sub print_resources {
 			my $ansrendered = &Apache::loncommon::get_student_answers($curresline,$username,$userdomain,$env{'request.course.id'},%answerenv);
 
 			if ($helper->{'VARS'}->{'ANSWER_TYPE'} eq 'no') {
-			    $rendered=~s/(\\keephidden{ENDOFPROBLEM})/$ansrendered$1/;
+			    $rendered=~s/(\\keephidden\{ENDOFPROBLEM})/$ansrendered$1/;
 			} else {
 
 			    
 			    my $header =&print_latex_header($helper->{'VARS'}->{'LATEX_TYPE'});
                             unless ($helper->{'VARS'}->{'ANSWER_TYPE'} eq 'only') {
-                                $header =~ s/\\begin{document}//;     #<<<<<
+                                $header =~ s/\\begin\{document}//;     #<<<<<
                             }
 			    my $title = &Apache::lonnet::gettitle($curresline);
 			    $title = &Apache::lonxml::latex_special_symbols($title);
@@ -3384,13 +3705,13 @@ sub print_resources {
 		    if ($helper->{'VARS'}->{'PRINT_ANNOTATIONS'} eq 'yes') {
 			my $url = &Apache::lonnet::clutter($res_url);
 			my $annotation = &annotate($url);
-			$rendered =~  s/(\\keephidden{ENDOFPROBLEM})/$annotation$1/;
+			$rendered =~  s/(\\keephidden\{ENDOFPROBLEM})/$annotation$1/;
 		    }
 		    my $junk;
 		    if ($remove_latex_header eq 'YES') {
 			$rendered = &latex_header_footer_remove($rendered);
 		    } else {
-			$rendered =~ s/\\end{document}//;
+			$rendered =~ s/\\end\{document}//;
 		    }
 		    $current_output .= $rendered;		    
 		} elsif ($res_url=~/\/(smppg|syllabus|aboutme|bulletinboard)$/) {
@@ -3402,12 +3723,12 @@ sub print_resources {
 		    if ($helper->{'VARS'}->{'PRINT_ANNOTATIONS'} eq 'yes') {
 			my $url = &Apache::lonnet::clutter($res_url);
 			my $annotation = &annotate($url);
-			$annotation    =~ s/(\\end{document})/$annotation$1/;
+			$annotation    =~ s/(\\end\{document})/$annotation$1/;
 		    }
 		    if ($remove_latex_header eq 'YES') {
 			$rendered = &latex_header_footer_remove($rendered);
 		    } else {
-			$rendered =~ s/\\end{document}//;
+			$rendered =~ s/\\end\{document}//;
 		    }
 		    $current_output .= $rendered.'\vskip 0.5mm\noindent\makebox[\textwidth/$number_of_columns][b]{\hrulefill}\strut \vskip 0 mm \strut ';
 		} elsif($res_url = ~/\.pdf$/) {
@@ -3422,13 +3743,26 @@ sub print_resources {
 		    if ($remove_latex_header ne 'NO') {
 			$rendered = &latex_header_footer_remove($rendered);
 		    } else {
-			$rendered =~ s/\\end{document}//;
+			$rendered =~ s/\\end\{document}//;
 		    }
 		    $current_output .= $rendered;
 		}
 	    }
 	    $remove_latex_header = 'YES';
 	}
+	$assignment = &Apache::lonxml::latex_special_symbols(
+	    &Apache::lonnet::gettitle($map), 'header');
+	if (($assignment ne $current_assignment) && ($assignment ne "")) {
+	    my $header_line = &format_page_header($LaTeXwidth, $parmhash{'print_header_format'},
+						  $assignment, $courseidinfo, 
+						  $fullname, $usersection);
+	    my $header_start = ($columns_in_format == 1) ? '\lhead'
+		: '\fancyhead[LO]';
+	    $header_line = $header_start.'{'.$header_line.'}';
+	    $current_output = $current_output . $header_line;
+	    $current_assignment = $assignment;
+	}
+
 	if (&Apache::loncommon::connection_aborted($r)) { last; }
     }
     # If we are printing incomplete it's possible we don't have
@@ -3438,7 +3772,24 @@ sub print_resources {
     #
 
     if ($actually_printed == 0) {
-	$current_output  = &encapsulate_minipage("\\vskip -10mm \nNo incomplete resources\n \\vskip 100 mm { }\n");
+        my $message = &mt('No resources to print');
+        if (!$possprint) {
+            if ($perm{'pav'} || $perm{'pfo'}) {
+                $message = &mt('There are no unhidden resources to print.')."\n\n".
+                           &mt('The most likely reason is one of the following: ')."\n".
+                           '\begin{itemize}'."\n".
+                           '\item '.&mt("The 'Resource hidden from students' parameter is set for the folder being printed.")."\n".
+                           '\item '.&mt("'Hidden' is checked in the Course Editor individually for each resource in the folder being printed.")."\n".
+                           '\end{itemize}'."\n\n".
+                           &mt("Note: to print a bubblesheet exam which you want to hide from students, ".
+                               "use the Course Editor to check the 'Hidden' checkbox for the exam folder itself.")."\n";
+            }
+        } elsif ($print_incomplete) {
+            $message = &mt('No incomplete resources');
+        }
+        if ($message) { 
+	    $current_output  = &encapsulate_minipage("\\vskip -10mm \n$message\n \\vskip 100 mm { }\n");
+        }
 	if ($remove_latex_header eq "NO") {
 	    $current_output = &print_latex_header() . $current_output;
 	} else {
@@ -3449,24 +3800,24 @@ sub print_resources {
     if ($syllabus_first) {
         $current_output =~ s/\\\\ Last updated:/Last updated:/
     }
-    my $courseidinfo = &get_course();
     my $currentassignment=&Apache::lonxml::latex_special_symbols($helper->{VARS}->{'assignment'},'header');
     my $header_line =
-	&format_page_header($LaTeXwidth, $parmhash{'print_header_format'},
-			    $currentassignment, $courseidinfo, $fullname, $usersection);
-    my $header_start = ($columns_in_format == 1) ? '\lhead'
-	                                         : '\fancyhead[LO]';
-    $header_line = $header_start.'{'.$header_line.'}';
+    &format_page_header($LaTeXwidth, $parmhash{'print_header_format'},
+                        $currentassignment, $courseidinfo, $fullname, $usersection);
+    my $header_start = ($columns_in_format == 1) ? '\lhead' : '\fancyhead[LO]';
+    my $newheader = $header_start.'{'.$header_line.'}';
     if ($current_output=~/\\documentclass/) {
-	$current_output =~ s/\\begin{document}/\\setlength{\\topmargin}{1cm} \\begin{document}\\noindent\\parbox{\\minipagewidth}{\\noindent$header_line$namepostfix}\\vskip 5 mm /;
+	$current_output =~ s/\\begin\{document}/\\setlength{\\topmargin}{1cm} \\begin{document}\\noindent\\parbox{\\minipagewidth}{\\noindent$newheader$namepostfix}\\vskip 5 mm /;
+
     } else {
 	my $blankpages = 
 	    '\clearpage\strut\clearpage'x$helper->{'VARS'}->{'EMPTY_PAGES'};
-	    
+	
 	$current_output = '\strut\vspace*{-6 mm}\\newline'.
 	    &copyright_line().' \newpage '.$blankpages.$end_of_student.
 	    '\setcounter{page}{1}\noindent\parbox{\minipagewidth}{\noindent'.
-	    $header_line.$namepostfix.'} \vskip 5 mm '.$current_output;
+	    $newheader.$namepostfix. '} \vskip 5 mm '.$current_output;
+
     }
     #
     #  Close the student bracketing.
@@ -3515,7 +3866,6 @@ sub handler {
     }
     
     &init_perm();
-
     my $helper = printHelper($r);
     if (!ref($helper)) {
 	return $helper;
@@ -3713,6 +4063,12 @@ sub printHelper {
     my $is_published=0;		# True when printing from resource space.
     my $res_printable = 1;	# By default the current resource is printable.    
     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'}) {
@@ -3730,6 +4086,10 @@ sub printHelper {
 	    my $navmap = Apache::lonnavmaps::navmap->new();
 	    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.
 
@@ -3748,6 +4108,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'};
     }
@@ -3778,8 +4139,7 @@ sub printHelper {
 
     if ($resourceTitle && $res_printable) {
         push @{$printChoices}, ["<b><i>$resourceTitle</i></b> (".&mt('the resource you just saw on the screen').")", 'current_document', 'PAGESIZE'];
-    }
-    
+    } 
 
     # Useful filter strings
 
@@ -3805,13 +4165,13 @@ sub printHelper {
     my $start_new_option;
     if ($perm{'pav'}) {
 	$start_new_option = 
-	    "<option text='".&mt('Start new page<br />before selected').
+	    "<option text='".&mt('Start new page[_1]before selected','<br />').
 	    "' variable='FINISHPAGE' />".
-	    "<option text='".&mt('Extra space<br />before selected').
+	    "<option text='".&mt('Extra space[_1]before selected','<br />').
 	    "' variable='EXTRASPACE' type='text' />" .
 	    "<option " .
 	    "' variable='POSSIBLE_RESOURCES' type='hidden' />".
-	    "<option text='".&mt('Space units<br />check for mm').
+	    "<option text='".&mt('Space units[_1]check for mm','<br />').
 	    "' variable='EXTRASPACE_UNITS' type='checkbox' />"
 	    ;
 	    
@@ -3884,7 +4244,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';
@@ -3892,51 +4252,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,
-		  '<b>', '</b>',
-		  '<b><i>'. $sequenceTitle . '</b></i>'),
-	      $printSelector,
-	      $nextState]);
-
+	if ($userCanPrint || &printable($map_open, $map_close)) {
+	    push(@{$printChoices},
+		 [&mt('Selected  [_1]Incomplete Problems[_2] from folder [_3]' . $textSuffix,
+		      '<b>', '</b>',
+		      '<b><i>'. $sequenceTitle . '</b></i>'),
+		  $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]','<b>','</b>','<b><i>'.$sequenceTitle.'</i></b>'), 
 	     'map_problems', 
 	     'CHOOSE_PROBLEMS'];
-        # Allow all resources from sequence
-        push @{$printChoices}, [&mt('Selected [_1]Resources[_2] from folder [_3]','<b>','</b>','<b><i>'.$sequenceTitle.'</i></b>'), 
-				'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]','<b>','</b>','<b><i>'.$sequenceTitle.'</i></b>'), 
+				    '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)$/)) { 
@@ -3968,7 +4336,7 @@ my $suffixXml = <<ALL_PROBLEMS;
 ALL_PROBLEMS
          &Apache::lonxml::xmlparse($r, 'helper', 
 				   &generate_resource_chooser('ALL_PROBLEMS',
-							      'SelectProblem(s) to print',
+							      'Select Problem(s) to print',
 							      'multichoice="1" suppressEmptySequences="0" addstatus="1" closeallpages="1"',
 							      'RESOURCES',
 							      'PAGESIZE',
@@ -4111,6 +4479,7 @@ ALL_PROBLEMS
         my @lines = &Apache::grades::get_scantronformat_file();
 	my $codechoice='';
 	foreach my $line (@lines) {
+            next if (($line =~ /^\#/) || ($line eq ''));
 	    my ($name,$description,$code_type,$code_length)=
 		(split(/:/,$line))[0,1,2,4];
 	    if ($code_length > 0 && 
@@ -4299,7 +4668,7 @@ CHOOSE_FROM_SUBDIR
     <message>Select the sequence to print resources from:</message>
     <resource variable="SEQUENCE">
       <nextstate>CHOOSE_FROM_ANY_SEQUENCE</nextstate>
-      <filterfunc>return \$res->is_sequence;</filterfunc>
+      <filterfunc>return &Apache::lonprintout::printable_sequence(\$res);</filterfunc>
       <valuefunc>return $urlValue;</valuefunc>
       <choicefunc>return \$res->hasResource(\$res,sub { return !\$_[0]->is_sequence() },0,0);
 	</choicefunc>
@@ -4321,6 +4690,36 @@ CHOOSE_FROM_ANY_SEQUENCE
 
     # Generate the first state, to select which resources get printed.
     Apache::lonhelper::state->new("START", "Select Printing Options:");
+    if (!$res_printable) {
+        my $now = time;
+        my $shownprintstart = &Apache::lonlocal::locallocaltime($res_printstartdate);
+        my $shownprintend = &Apache::lonlocal::locallocaltime($res_printenddate);
+        my $noprintmsg;
+        if (($res_printenddate) && ($res_printenddate < $now)) {
+                $noprintmsg = &mt('Printing for current resource no longer available (ended: [_1])',
+                                  $shownprintend);
+        } else {
+            if (($res_printstartdate) && ($res_printstartdate > $now)) {
+                if (($res_printenddate) && ($res_printenddate > $now) && ($res_printenddate > $res_printstartdate)) {
+                    $noprintmsg = &mt('Printing for current resource is only possible between [_1] and [_2]',
+                                  $shownprintstart,$shownprintend);
+                } elsif (!$res_printenddate) {
+                    $noprintmsg = &mt('Printing for current resource will only be possible starting [_1]',
+                                      $shownprintstart);
+                } else {
+                    $noprintmsg = &mt('Printing for current resource is unavailable');
+                }
+            }
+        }
+
+        if ($noprintmsg) {
+            $paramHash = Apache::lonhelper::getParamHash();
+	    $paramHash->{MESSAGE_TEXT} = 
+                '<p class="LC_info">'.$noprintmsg.'</p>';
+	    Apache::lonhelper::message->new();
+        }
+    }
+    $paramHash = Apache::lonhelper::getParamHash();
     $paramHash = Apache::lonhelper::getParamHash();
     $paramHash->{MESSAGE_TEXT} = "";
     Apache::lonhelper::message->new();
@@ -4542,7 +4941,7 @@ FONT_SELECTION
 		      return "$helper->{VARS}->{'probstatus'}";
                    </defaultvalue>
 		   <choice computer="problem">Homework Problem</choice>
-		   <choice computer="exam">Exam Problem</choice>
+		   <choice computer="exam">Bubblesheet Exam Problem</choice>
 		   <choice computer="survey">Survey question</choice>
                    ,choice computer="anonsurvey"Anonymous survey question</choice>
 		</dropdown>
@@ -4660,9 +5059,9 @@ sub render {
     my $PaperType=&mt('Paper type');
     my $landscape=&mt('Landscape');
     my $portrait=&mt('Portrait');
-    my $pdfFormLabel=&mt('PDF-Formfields');
-    my $with=&mt('with Formfields');
-    my $without=&mt('without Formfields');
+    my $pdfFormLabel=&mt('PDF Form Fields');
+    my $with=&mt('with Form Fields');
+    my $without=&mt('without Form Fields');
     
 
     $result.='<h3>'.&mt('Layout Options').'</h3>'