--- loncom/interface/lonprintout.pm 2010/11/13 01:57:14 1.568.2.10 +++ loncom/interface/lonprintout.pm 2011/09/15 12:09:44 1.597 @@ -1,8 +1,8 @@ -# + # The LearningOnline Network # Printout # -# $Id: lonprintout.pm,v 1.568.2.10 2010/11/13 01:57:14 raeburn Exp $ +# $Id: lonprintout.pm,v 1.597 2011/09/15 12:09:44 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -45,13 +45,13 @@ use Apache::londefdef; use File::Basename; use HTTP::Response; - use LONCAPA::map(); use POSIX qw(strftime); use Apache::lonlocal; use Carp; use LONCAPA; + my %perm; my %parmhash; my $resources_printed; @@ -76,7 +76,146 @@ my $font_size = 'normalsize'; # Default #---------------------------- Helper helpers. ------------------------- -# Returns the text needd for a student chooser. +# BZ5209: +# Create the states needed to run the helper for incomplete problems from +# the current folder for selected students. +# This includes: +# - A resource selector limited to problems (incompleteness must be +# calculated on a student per student basis. +# - A student selector. +# - Tie in to the FORMAT of the print job. +# +# States: +# CHOOSE_INCOMPLETE_PEOPLE_SEQ - Resource selection. +# CHOOSE_STUDENTS_INCOMPLETE - Student selection. +# CHOOSE_STUDENTS_INCOMPLETE_FORMAT - Format selection +# Parameters: +# helper - the helper which already contains info about the current folder we can +# purloin. +# url - Top url of the sequence +# Return: +# XML that can be parsed by the helper to drive the state machine. +# +sub create_incomplete_folder_selstud_helper($helper) +{ + my ($helper, $map) = @_; + + + my $symbFilter = '$res->shown_symb()'; + my $selFilter = '$res->is_problem()'; + + + my $resource_chooser = &generate_resource_chooser('CHOOSE_INCOMPLETE_PEOPLE_SEQ', + 'Select problem(s) to print', + 'multichoice="1" toponly="1" addstatus="1" closeallpages="1"', + 'RESOURCES', + 'CHOOSE_STUDENTS_INCOMPLETE', + $map, + $selFilter, + '', + $symbFilter, + ''); + + my $student_chooser = &generate_student_chooser('CHOOSE_STUDENTS_INCOMPLETE', + 'student_sort', + 'STUDENTS', + 'CHOOSE_STUDENTS_INCOMPLETE_FORMAT'); + + my $format_chooser = &generate_format_selector($helper, + 'Format of the print job', + '','CHOOSE_STUDENTS_INCOMPLETE_FORMAT'); # end state. + + return $resource_chooser . $student_chooser . $format_chooser; +} + + +# BZ 5209 +# Create the states needed to run the helper for incomplete problems from +# the current folder for selected students. +# This includes: +# - A resource selector limited to problems. (incompleteness must be calculated +# on a student per student basis. +# - A student selector. +# - Tie in to format for the print job. +# States: +# INCOMPLETE_PROBLEMS_COURSE_RESOURCES - Resource selector. +# INCOMPLETE_PROBLEMS_COURSE_STUDENTS - Student selector. +# INCOMPLETE_PROBLEMS_COURSE_FORMAT - Format selection. +# +# Parameters: +# helper - Helper we are creating states for. +# Returns: +# Text that can be parsed by the helper. +# + +sub create_incomplete_course_helper { + my $helper = shift; + + my $filter = '$res->is_problem() || $res->contains_problem() || $res->is_sequence() || $res->is_practice())'; + my $symbfilter = '$res->shown_symb()'; + + my $resource_chooser = &generate_resource_chooser('INCOMPLETE_PROBLEMS_COURSE_RESOURCES', + 'Select problem(s) to print', + 'multichoice = "1" suppressEmptySequences="0" addstatus="1" closeallpagtes="1"', + 'RESOURCES', + 'INCOMPLETE_PROBLEMS_COURSE_STUDENTS', + '', + $filter, + '', + $symbfilter, + ''); + + my $people_chooser = &generate_student_chooser('INCOMPLETE_PROBLEMS_COURSE_STUDENTS', + 'student_sort', + 'STUDENTS', + 'INCOMPLETE_PROBLEMS_COURSE_FORMAT'); + + my $format = &generate_format_selector($helper, + 'Format of the print job', + '', + 'INCOMPLETE_PROBLEMS_COURSE_FORMAT'); # end state. + + return $resource_chooser . $people_chooser . $format; + + +} + +# BZ5209 +# Creates the states needed to run the print helper for a student +# that wants to print his incomplete problems from the current folder. +# Parameters: +# $helper - helper we are generating states for. +# $map - The map for which the student wants incomplete problems. +# Returns: +# XML that defines the helper states being created. +# +# States: +# CHOOSE_INCOMPLETE_SEQ - Resource selector. +# +sub create_incomplete_folder_helper { + my ($helper, $map) = @_; + + my $filter = '$res->is_problem()'; + $filter .= ' && $res->resprintable() '; + $filter .= ' && $res->is_incomplete() '; + + my $symfilter = '$res->shown_symb()'; + + my $resource_chooser = &generate_resource_chooser('CHOOSE_INCOMPLETE_SEQ', + 'Select problem(s) to print', + 'multichoice="1", toponly ="1", addstatus="1", closeallpages="1"', + 'RESOURCES', + 'PAGESIZE', + $map, + $filter, '', + $symfilter, + ''); + + return $resource_chooser; +} + + +# Returns the text neded for a student chooser. # that text must still be parsed by the helper xml parser. # Parameters: # this_state - State name of the chooser. @@ -196,14 +335,19 @@ sub generate_code_selector {


Generate new CODEd Assignments

Number of CODEd assignments to print: - + if (((\$helper->{'VARS'}{'NUMBER_TO_PRINT_TOTAL'}+0) < 1) && !\$helper->{'VARS'}{'REUSE_OLD_CODES'} && !\$helper->{'VARS'}{'SINGLE_CODE'} && - !\$helper->{'VARS'}{'CODE_SELECTED_FROM_LIST'}) { + !\$helper->{'VARS'}{'CODE_SELECTED_FROM_LIST'} ) { + return "You need to specify the number of assignments to print"; } + if (((\$helper->{'VARS'}{'NUMBER_TO_PRINT_TOTAL'}+0) >= 1) && + (\$helper->{'VARS'}{'SINGLE_CODE'} ne '') ) { + return 'Specifying number of codes to print and a specific code is not compatible'; + } return undef; @@ -228,6 +372,8 @@ sub generate_code_selector { !\$helper->{'VARS'}{'CODE_SELECTED_FROM_LIST'}) { return &Apache::lonprintout::is_code_valid(\$helper->{'VARS'}{'SINGLE_CODE'}, \$helper->{'VARS'}{'CODE_OPTION'}); + } elsif (\$helper->{'VARS'}{'SINGLE_CODE'} ne ''){ + return 'Specifying a code name is incompatible with specifying number of codes.'; } else { return undef; # Other forces control us. } @@ -249,8 +395,138 @@ CHOOSE_ANON1 return $result; } +sub generate_format_selector { + my ($helper,$title,$nextstate, $thisstate) = @_; + my $secpdfoption; + my $state = 'PRINT_FORMATTING'; + if ($thisstate) { + $state = $thisstate; + } + unless (($helper->{'VARS'}->{'PRINT_TYPE'} eq 'problems_for_anon') || + ($helper->{'VARS'}->{'PRINT_TYPE'} eq 'problems_for_anon_page') || + ($helper->{'VARS'}->{'PRINT_TYPE'} eq 'resources_for_anon') ) { + $secpdfoption = 'Each PDF contains exactly one section'; + } + return < + $nextstate +
How should the results be printed?
+ + Start each student\'s assignment on a new page/column (add a pagefeed after each assignment) + Add one empty page/column after each student\'s assignment + Add two empty pages/column after each student\'s assignment + Add three empty pages/column after each student\'s assignment + + PAGESIZE +
How do you want assignments split into PDF files?
+ + All assignments in a single PDF file + $secpdfoption + Each PDF contains exactly one assignment + + Specify the number of assignments per PDF: + + +RESOURCE_SELECTOR +} + #----------------------------------------------------------------------- +# Determine if a resource is incomplete given the map: +# Parameters: +# $username - Name of user for whom we are checking. +# $domain - Domain of user we are checking. +# $map - map name. +# Returns: +# 0 - map is not incomplete. +# 1 - map is incomplete. +# +sub incomplete { + my ($username, $domain, $map) = @_; + + + my $navmap = Apache::lonnavmaps::navmap->new($username, $domain); + + + if (defined($navmap)) { + my $res = $navmap->getResourceByUrl($map); + my $result = $res->is_incomplete(); + return $result; + } else { + return 1; + } +} +# +# When printing for students, the resoures 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. +# +# Parameters: +# $helper - The helper we need $helper->{'VARS'}->{'symb'} +# to construct the navmap and the iteration. +# $seq - The original set of resources to print +# (really an array of resource names +# $who - Student/domain for whome the sequence will be generated. +# +# Implicit inputs: +# $ +# Returns: +# reference to an array of resources that can be passed to +# print_resources. +# +sub master_seq_to_person_seq { + my ($helper, $seq, $who) = @_; + + + 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 ($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 + + ); # These items are not resources but appear in the midst of iteration. + + # Iterate on the resource..select the items that are randomly selected + # and that are in the seq_has. Presumably the iterator will take care + # of the random ordering part of the deal. + # + my $curres; + while ($curres = $iterator->next()) { + # + # Only process resources..that re not removed by randomout... + # + if (! exists $nonResourceItems{$curres} && ! $curres->randomout()) { + my $symb = $curres->symb(); + if (exists $seq_hash{$symb}) { + push(@output_seq, $symb); + } + } + } + + + return \@output_seq; # for now. + +} + # Fetch the contents of a resource, uninterpreted. # This is used here to fetch a latex file to be included @@ -312,10 +588,12 @@ 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}/; return $text; + + } # include_pdf - PDF files are included into the @@ -423,7 +701,6 @@ sub ssi_with_retries { $ssi_last_error_resource = $resource; $ssi_last_error = $response->code . " " . $response->message; $content='\section*{!!! An error occurred !!!}'; - &Apache::lonnet::logthis("Error in SSI resource: $resource Error: $ssi_last_error"); } return $content; @@ -439,7 +716,6 @@ sub get_student_view_with_retries { $ssi_last_error_resource = $curresline.' for user '.$username.':'.$userdomain; $ssi_last_error = $response->code . " " . $response->message; $content='\section*{!!! An error occurred !!!}'; - &Apache::lonnet::logthis("Error in SSI (student view) resource: $curresline Error: $ssi_last_error User: $username:$userdomain"); } return $content; @@ -499,12 +775,15 @@ sub printf_style_subst { # %a - Assignment name. # %c - Course name. # %n - Student name. +# %s - The section if it is supplied. # sub format_page_header { - my ($width, $format, $assignment, $course, $student) = @_; + my ($width, $format, $assignment, $course, $student, $section) = @_; + + $width = &recalcto_mm($width); # Get width in mm. - my $chars_per_line = int($width/2); # Character/textline. + my $chars_per_line = int($width/1.6); # Character/textline. # Default format? @@ -520,36 +799,58 @@ sub format_page_header { # - Allow the assignment to be 2 lines (wrapped). # - my $firstline = "$student $course"; - if (length($firstline) > $chars_per_line) { - my $lastchar = $chars_per_line - length($student) - 1; - if ($lastchar > 0) { - $course = substr($course, 0, $lastchar); - } else { # Nothing left of course: - $course = ''; - } - } - if (length($assignment) > $chars_per_line) { - $assignment = substr($assignment, 0, $chars_per_line); - } + - $format = "\\textbf{$student} $course \\hfill \\thepage \\\\ \\textit{$assignment}"; + my $name_length = int($chars_per_line *3 /4); + my $sec_length = int($chars_per_line / 5); + + $format = "%$name_length".'n'; + + if ($section) { + $format .= ' - Sec: '."%$sec_length".'s'; + } + + $format .= '\\\\%c \\\\ %a'; + - } else { - # An open question is how to handle long user formatted page headers... - # A possible future is to support e.g. %na so that the user can control - # the truncation of the elements that can appear in the header. - # - $format = &printf_style_subst("a", $format, $assignment); - $format = &printf_style_subst("c", $format, $course); - $format = &printf_style_subst("n", $format, $student); - - # If the user put %'s in the format string, they must be escaped - # to \% else LaTeX will think they are comments and terminate - # the line.. which is bad!!! } + # An open question is how to handle long user formatted page headers... + # A possible future is to support e.g. %na so that the user can control + # the truncation of the elements that can appear in the header. + # + $format = &printf_style_subst("a", $format, $assignment); + $format = &printf_style_subst("c", $format, $course); + $format = &printf_style_subst("n", $format, $student); + $format = &printf_style_subst("s", $format, $section); + + # If the user put %'s in the format string, they must be escaped + # to \% else LaTeX will think they are comments and terminate + # the line.. which is bad!!! + + # If the user has role author, $course and $assignment are empty so + # 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 '); + $format =~ s/\\\\\s\\\\\s/$testPrintout/; + } + # + # We're going to trust LaTeX to break lines appropriately, but + # we'll truncate anything that's more than 3 lines worth of + # text. This is also assuming (which will probably end badly) + # nobody's going to embed LaTeX control sequences in the title + # header or rather that those control sequences won't get broken + # by the stuff below. + # + my $total_length = 3*$chars_per_line; + if (length($format) > $total_length) { + $format = substr($format, 0, $total_length); + } + + return $format; + } # @@ -1245,6 +1546,8 @@ sub get_course { my $courseidinfo; if (defined($env{'request.course.id'})) { $courseidinfo = &Apache::lonxml::latex_special_symbols(&unescape($env{'course.'.$env{'request.course.id'}.'.description'}),'header'); + my $sec = $env{'request.course.sec'}; + } return $courseidinfo; } @@ -1267,7 +1570,6 @@ sub page_format_transformation { my $name = &get_name(); my $courseidinfo = &get_course(); - if (defined($courseidinfo)) { $courseidinfo=' - '.$courseidinfo } my $header_text = $parmhash{'print_header_format'}; $header_text = &format_page_header($textwidth, $header_text, $assignment, $courseidinfo, $name); @@ -1288,13 +1590,13 @@ sub page_format_transformation { $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; - if ($perm{'pav'}) { - $papersize_text = '\\special{papersize=210mm,297mm}'; - } else { - $papersize_text = '\special{papersize=210mm,297mm}'; - } - $text =~ s/(\\begin{document})/$1$papersize_text/; + my $papersize_text; + if ($perm{'pav'}) { + $papersize_text = '\\special{papersize=210mm,297mm}'; + } else { + $papersize_text = '\special{papersize=210mm,297mm}'; + } + $text =~ s/(\\begin{document})/$1$papersize_text/; } } if ($tableofcontents eq 'yes') {$text=~s/(\\setcounter\{page\}\{1\})/$1 \\tableofcontents\\newpage /;} @@ -1527,7 +1829,7 @@ sub print_page_in_course { } else { my $esc_currentURL= $currentURL; $esc_currentURL =~ s/_/\\_/g; - $result.=$esc_currentURL; + $result.=$esc_currentURL; } $result .= '\\\\'; @@ -1568,7 +1870,7 @@ sub print_page_in_course { $form{'grade_target'} = 'tex'; $form{'textwidth'} = &get_textwidth($helper, $LaTeXwidth); - $form{'pdfFormFields'} = 'no'; + $form{'pdfFormFields'} = $pdfFormFields; # $form{'showallfoils'} = $helper->{'VARS'}->{'showallfoils'}; $form{'problem_split'}=$parmhash{'problem_stream_switch'}; @@ -1674,7 +1976,6 @@ sub print_page_in_course { sub recently_generated { my ($prtspool) = @_; my $output; - my $zip_result; my $pdf_result; opendir(DIR,$prtspool); @@ -1707,7 +2008,7 @@ sub recently_generated { if ($ext eq 'zip') { $zip_result .= $result; } } if ($zip_result || $pdf_result) { - $output = '
'; + $output ='
'; } if ($zip_result) { $output .='

'.&mt('Recently generated printout zip files')."

\n" @@ -1744,7 +2045,7 @@ sub recently_generated { # # # use Data::Dumper; -#sub dump_helper_vars { +# sub dump_helper_vars { # my ($helper) = @_; # my $helpervars = Dumper($helper->{'VARS'}); # &Apache::lonnet::logthis("Dump of helper vars:\n $helpervars"); @@ -1759,6 +2060,122 @@ sub get_page_breaks { } return %page_breaks; } +# +# Returns text to insert for any extra vskip prior to the resource. +# Parameters: +# helper - Reference to the helper object driving the printout. +# resource - Identifies the resource about to be printed. +# +# This is done as follows: +# POSSIBLE_RESOURCES has the list of possible resources. +# EXTRASPACE has the list of extra space values. +# EXTRASPACE_UNITS is the set of resources for which the units are +# mm. All others are 'in'. +# +# The resource is found in the POSSIBLE_RESOURCES to get the index +# of the EXTRASPACE value. +# +# In order to speed this up for lengthy printouts, the first time, +# POSSIBLE_RESOURCES is turned into a look up hash and +# EXTRASPACE is turned into an array. +# + + +my %possible_resources; +my %extraspace_mm; +my @extraspace; +my $skips_loaded = 0; + +# Function to load the skips hash and array + +sub load_skips { + + my ($helper) = @_; + + # If this is the first time, unrap the resources and extra spaces: + + if (!$skips_loaded) { + @extraspace = (split(/\|\|\|/, $helper->{'VARS'}->{'EXTRASPACE'})); + my @resource_list = (split(/\|\|\|/, $helper->{'VARS'}->{'POSSIBLE_RESOURCES'})); + my $i = 0; + foreach my $resource (@resource_list) { + $possible_resources{$resource} = $i; + $i++; + } + foreach my $mm_resource (split(/\|\|\|/, $helper->{'VARS'}->{'EXTRASPACE_UNITS'})) { + $extraspace_mm{$mm_resource} = 1; + } + $skips_loaded = 1; + } +} + +sub get_extra_vspaces { + my ($helper, $resource) = @_; + + &load_skips($helper); + + # Lookup the resource in the possible resources hash.. that is the index + # into the extraspace array that gives us either an empty string or + # the number of mm to skip: + + my $index = $possible_resources{$resource}; + my $skip = $extraspace[$index]; + + my $result = ''; + if ($skip ne '') { + my $units = 'in'; + if (defined($extraspace_mm{$resource})) { + $units = 'mm'; + } + $result = '\vskip '.$skip.' '.$units; + } + + + return $result; + + +} + +# +# The resource chooser part of the helper needs more than just +# the value of the extraspaces var to recover the value into a text +# field option. This sub produces the required format for the saved var: +# specifically +# ||| separated fields of the form resourcename=value +# +# Parameters: +# $helper - Refers to the helper we are configuring +# Implicit input: +# $helper->{'VARS'}->{'EXTRASPACE'} - the spaces helper var has the text field +# value. +# $helper->{'VARS'}->{'EXTRASPACE_UNITS'} - units for the skips (checkboxes). +# $helper->{'VARS'}->{'POSSIBLE_RESOURCES'} - has the list of resources. ||| +# separated of course. +# Implicit outputs: +# $env{'form.extraspace'} +# $env{'form.extraspace_units'} +# +sub set_form_extraspace { + my ($helper) = @_; + + # the most convenient way to do this is to drive from the skips arrays/hash. + # may not be the fastest, but this is once per print request so it's not so + # speed critical: + + &load_skips($helper); + + my $result = ''; + + foreach my $resource (keys(%possible_resources)) { + my $vskip = $extraspace[$possible_resources{$resource}]; + $result .= $resource .'=' . $vskip . '|||'; + } + + $env{'form.extraspace'} = $result; + $env{'form.extraspace_units'} = $helper->{'VARS'}->{'EXTRASPACE_UNITS'}; + return $result; + +} # Output a sequence (recursively if neeed) # from construction space. @@ -1775,6 +2192,8 @@ sub get_page_breaks { # sub print_construction_sequence { my ($currentURL, $helper, %form, $LaTeXwidth) = @_; + + my $result; my $rndseed=time; if ($helper->{'VARS'}->{'curseed'}) { @@ -1876,12 +2295,44 @@ sub print_construction_sequence { return $result; } +# +# Top level for generating print output. +# +# May call print_resources if multiple resources will be printed. +# +# The main driver is $selectionmade which reflects the type of print out +# requested: +# Value Print type: +# 1 Print resource that's being looked at. +# 2 Print problems in a map or in a page. +# 3 Print pages in a map or resources in a page. +# 4 Print all problems or all resources. +# 5 Print problems for seleted students. +# 6 Print selected problems from a folder. +# 7 Print print selected resources from some scope. +# 8 Print resources for selected students. +# +#BZ 5209 +# 2 map_incomplete_problems_seq Print incomplete problems from the current +# folder in student context. +# 5 map_incomplete_problems_people_seq Print incomplete problems from the +# current folder in privileged context. +# 5 incomplete_problems_selpeople_course Print incomplete problems for +# selected people from the entire course. +# +# Item 101 has much the same processing as 8, +# +# Differences: Item 101, 102 require per-student filtering of the resource +# set so that only the incomplete resources are printed. +# 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. $resources_printed = ''; $font_size = $helper->{'VARS'}->{'fontsize'}; + my $print_type = $helper->{'VARS'}->{'PRINT_TYPE'}; # Allows textual simplification. my $do_postprocessing = 1; my $js = < @@ -1930,11 +2381,13 @@ ENDPART # indexed by symb and contains 1's for each break. $env{'form.pagebreaks'} = $helper->{'VARS'}->{'FINISHPAGE'}; - $env{'form.lastprinttype'} = $helper->{'VARS'}->{'PRINT_TYPE'}; + &set_form_extraspace($helper); + $env{'form.lastprinttype'} = $print_type; &Apache::loncommon::store_course_settings('print', {'pagebreaks' => 'scalar', + 'extraspace' => 'scalar', + 'extraspace_units' => 'scalar', 'lastprinttype' => 'scalar'}); - my %page_breaks = &get_page_breaks($helper); my $format_from_helper = $helper->{'VARS'}->{'FORMAT'}; @@ -1959,7 +2412,7 @@ ENDPART my %form; $form{'grade_target'} = 'tex'; $form{'textwidth'} = &get_textwidth($helper, $LaTeXwidth); - $form{'pdfFormFields'} = 'no'; + $form{'pdfFormFields'} = $pdfFormFields; # If form.showallfoils is set, then request all foils be shown: # privilege will be enforced both by not allowing the @@ -1979,8 +2432,7 @@ ENDPART &Apache::lonnet::delenv('construct.style'); } - - if ($helper->{'VARS'}->{'PRINT_TYPE'} eq 'current_document') { + if ($print_type eq 'current_document') { #-- single document - problem, page, html, xml, ... my ($currentURL,$cleanURL); @@ -2142,32 +2594,35 @@ ENDPART $result.=&unsupported($currentURL,$helper->{'VARS'}->{'LATEX_TYPE'}, $helper->{'VARS'}->{'symb'}); } - } elsif (($helper->{'VARS'}->{'PRINT_TYPE'} eq 'map_problems') or - ($helper->{'VARS'}->{'PRINT_TYPE'} eq 'map_problems_in_page') or - ($helper->{'VARS'}->{'PRINT_TYPE'} eq 'map_resources_in_page') or - ($helper->{'VARS'}->{'PRINT_TYPE'} eq 'map_problems_pages') or - ($helper->{'VARS'}->{'PRINT_TYPE'} eq 'all_problems') or - ($helper->{'VARS'}->{'PRINT_TYPE'} eq 'all_resources') or # BUGBUG - ($helper->{'VARS'}->{'PRINT_TYPE'} eq 'select_sequences') - ) { - + } elsif (($print_type eq 'map_problems') or + ($print_type eq 'map_problems_in_page') or + ($print_type eq 'map_resources_in_page') or + ($print_type eq 'map_problems_pages') or + ($print_type eq 'all_problems') or + ($print_type eq 'all_resources') or # BUGBUG + ($print_type eq 'select_sequences') or + ($print_type eq 'map_incomplete_problems_seq') + ) { + #-- produce an output string - if (($helper->{'VARS'}->{'PRINT_TYPE'} eq 'map_problems') or - ($helper->{'VARS'}->{'PRINT_TYPE'} eq 'map_problems_in_page') ) { + if (($print_type eq 'map_problems') or + ($print_type eq 'map_incomplete_problems_seq') or + ($print_type eq 'map_problems_in_page') ) { $selectionmade = 2; - } elsif (($helper->{'VARS'}->{'PRINT_TYPE'} eq 'map_problems_pages') or - ($helper->{'VARS'}->{'PRINT_TYPE'} eq 'map_resources_in_page')) + } elsif (($print_type eq 'map_problems_pages') or + ($print_type eq 'map_resources_in_page')) { $selectionmade = 3; - } elsif (($helper->{'VARS'}->{'PRINT_TYPE'} eq 'all_problems') + } elsif (($print_type eq 'all_problems') ) { $selectionmade = 4; - } elsif ($helper->{'VARS'}->{'PRINT_TYPE'} eq 'all_resources') { #BUGBUG + } elsif ($print_type eq 'all_resources') { #BUGBUG $selectionmade = 4; - } elsif ($helper->{'VARS'}->{'PRINT_TYPE'} eq 'select_sequences') { + } elsif ($print_type eq 'select_sequences') { $selectionmade = 7; } + $form{'problem_split'}=$parmhash{'problem_stream_switch'}; $form{'suppress_tries'}=$parmhash{'suppress_tries'}; $form{'latex_type'}=$helper->{'VARS'}->{'LATEX_TYPE'}; @@ -2198,6 +2653,7 @@ ENDPART $result.="\\newpage\n"; } } + $result .= &get_extra_vspaces($helper, $master_seq[$i]); my ($sequence,$middle_thingy,$urlp)=&Apache::lonnet::decode_symb($master_seq[$i]); $urlp=&Apache::lonnet::clutter($urlp); $form{'symb'}=$master_seq[$i]; @@ -2216,10 +2672,9 @@ ENDPART unless (($helper->{'VARS'}->{'ANSWER_TYPE'} eq 'only') || (($i==0) && (($urlp=~/\.page$/) || - ($helper->{'VARS'}->{'PRINT_TYPE'} eq 'map_problems_in_page') || - ($helper->{'VARS'}->{'PRINT_TYPE'} eq 'map_resources_in_page')))) { - - $flag_latex_header_remove = 'YES'; + ($print_type eq 'map_problems_in_page') || + ($print_type eq 'map_resources_in_page')))) { + $flag_latex_header_remove = 'YES'; } } $texversion.=&ssi_with_retries($urlp, $ssi_retry_count, %form); @@ -2228,7 +2683,7 @@ ENDPART if ($number_of_columns_page > $number_of_columns) {$number_of_columns=$number_of_columns_page;} $texversion =~ s/\\end{document}\d*/\\end{document}/; $flag_page_in_sequence = 'YES'; - } + } if(($helper->{'VARS'}->{'ANSWER_TYPE'} eq 'no') || ($helper->{'VARS'}->{'ANSWER_TYPE'} eq 'only')) { @@ -2240,7 +2695,6 @@ ENDPART &Apache::lonxml::restore_problem_counter(); my $answer=&ssi_with_retries($urlp, $ssi_retry_count, %answerform); - if ($urlp =~ /\.page$/) { $answer =~ s/\\end{document}(\d*)$//; } @@ -2256,7 +2710,7 @@ ENDPART } else { if ($urlp=~/\.(problem|exam|quiz|assess|survey|form|library|page)$/) { $texversion=&print_latex_header($helper->{'VARS'}->{'LATEX_TYPE'}); -# $texversion =~ s/\\begin{document}//; +# $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 '; @@ -2286,7 +2740,6 @@ ENDPART if (($selectionmade == 4) and ($assignment ne $prevassignment)) { my $name = &get_name(); my $courseidinfo = &get_course(); - if (defined($courseidinfo)) { $courseidinfo=' - '.$courseidinfo } $prevassignment=$assignment; my $header_text = $parmhash{'print_header_format'}; $header_text = &format_page_header($textwidth, $header_text, @@ -2350,7 +2803,7 @@ ENDPART } $result .= $texversion; $flag_latex_header_remove = 'YES'; - } + } if (&Apache::loncommon::connection_aborted($r)) { last; } @@ -2360,20 +2813,24 @@ ENDPART $result =~ s/\\usepackage{calc}/\\usepackage{calc}\\usepackage{longtable}/; } $result .= '\end{document}'; - } elsif (($helper->{'VARS'}->{'PRINT_TYPE'} eq 'problems_for_students') || - ($helper->{'VARS'}->{'PRINT_TYPE'} eq 'problems_for_students_from_page') || - ($helper->{'VARS'}->{'PRINT_TYPE'} eq 'all_problems_students') || - ($helper->{'VARS'}->{'PRINT_TYPE'} eq 'resources_for_students')){ + } elsif (($print_type eq 'problems_for_students') || + ($print_type eq 'problems_for_students_from_page') || + ($print_type eq 'all_problems_students') || + ($print_type eq 'resources_for_students') || + ($print_type eq 'incomplete_problems_selpeople_course') || + ($print_type eq 'map_incomplete_problems_people_seq')){ #-- prints assignments for whole class or for selected students my $type; - if (($helper->{'VARS'}->{'PRINT_TYPE'} eq 'problems_for_students') || - ($helper->{'VARS'}->{'PRINT_TYPE'} eq 'problems_for_students_from_page') || - ($helper->{'VARS'}->{'PRINT_TYPE'} eq 'all_problems_students') ) { + if (($print_type eq 'problems_for_students') || + ($print_type eq 'problems_for_students_from_page') || + ($print_type eq 'all_problems_students') || + ($print_type eq 'incomplete_problems_selpeople_course') || + ($print_type eq 'map_incomplete_problems_people_seq')) { $selectionmade=5; $type='problems'; - } elsif ($helper->{'VARS'}->{'PRINT_TYPE'} eq 'resources_for_students') { + } elsif ($print_type eq 'resources_for_students') { $selectionmade=8; $type='resources'; } @@ -2427,7 +2884,6 @@ ENDPART my $i = 0; my $last_section = (split(/:/,$students[0]))[2]; foreach my $person (@students) { - my $duefile="/home/httpd/prtspool/$env{'user.name'}_$env{'user.domain'}_printout.due"; if (-e $duefile) { my $temp_file = Apache::File->new('>>'.$duefile); @@ -2443,23 +2899,25 @@ ENDPART } else { $i=int($student_counter/$helper->{'VARS'}{'NUMBER_TO_PRINT'}); } + my $actual_seq = master_seq_to_person_seq($helper, \@master_seq, $person); my ($output,$fullname, $printed)=&print_resources($r,$helper, $person,$type, - \%moreenv,\@master_seq, + \%moreenv, $actual_seq, $flag_latex_header_remove, $LaTeXwidth); $resources_printed .= ":"; $print_array[$i].=$output; $student_names[$i].=$person.':'.$fullname.'_END_'; - &Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state,&mt('last student').' '.$fullname); +# &Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state,&mt('last student').' '.$fullname); + &Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state,'last student'); $flag_latex_header_remove = 'YES'; if (&Apache::loncommon::connection_aborted($r)) { last; } } &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state); $result .= $print_array[0].' \end{document}'; - } elsif (($helper->{'VARS'}->{'PRINT_TYPE'} eq 'problems_for_anon') || - ($helper->{'VARS'}->{'PRINT_TYPE'} eq 'problems_for_anon_page') || - ($helper->{'VARS'}->{'PRINT_TYPE'} eq 'resources_for_anon') ) { + } elsif (($print_type eq 'problems_for_anon') || + ($print_type eq 'problems_for_anon_page') || + ($print_type eq 'resources_for_anon') ) { my $cdom =$env{'course.'.$env{'request.course.id'}.'.domain'}; my $cnum =$env{'course.'.$env{'request.course.id'}.'.num'}; my $num_todo=$helper->{'VARS'}->{'NUMBER_TO_PRINT_TOTAL'}; @@ -2470,17 +2928,24 @@ ENDPART my $code_option=$helper->{'VARS'}->{'CODE_OPTION'}; my @lines = &Apache::grades::get_scantronformat_file(); - my ($code_type,$code_length)=('letter',6); + my ($code_type,$code_length,$bubbles_per_row)=('letter',6,10); foreach my $line (@lines) { - my ($name,$type,$length) = (split(/:/,$line))[0,2,4]; + chomp($line); + my ($name,$type,$length,$bubbles_per_item) = + (split(/:/,$line))[0,2,4,17]; if ($name eq $code_option) { $code_length=$length; if ($type eq 'number') { $code_type = 'number'; } + chomp($bubbles_per_item); + if (($bubbles_per_item ne '') && ($bubbles_per_item > 0)) { + $bubbles_per_row = $bubbles_per_item; + } } } my %moreenv = ('textwidth' => &get_textwidth($helper,$LaTeXwidth)); $moreenv{'problem_split'} = $parmhash{'problem_stream_switch'}; $moreenv{'instructor_comments'}='hide'; + $moreenv{'bubbles_per_row'} = $bubbles_per_row; my $seed=time+($$<<16)+($$); my @allcodes; if ($old_name) { @@ -2521,11 +2986,12 @@ ENDPART @allcodes=keys(%allcodes); } my @master_seq=split /\|\|\|/, $helper->{'VARS'}->{'RESOURCES'}; - my ($type) = split(/_/,$helper->{'VARS'}->{'PRINT_TYPE'}); + my ($type) = split(/_/,$print_type); &adjust_number_to_print($helper); my $number_per_page=$helper->{'VARS'}->{'NUMBER_TO_PRINT'}; - if ($number_per_page eq '0' || $number_per_page eq 'all') { - $number_per_page=$num_todo; + if ($number_per_page eq '0' || $number_per_page eq 'all' + || $number_per_page eq 'section') { + $number_per_page=$num_todo > 0 ? $num_todo : 1; } my $flag_latex_header_remove = 'NO'; my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin($r,'Print Status','Class Print Status',$num_todo,'inline','75'); @@ -2551,7 +3017,7 @@ ENDPART } &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state); $result .= $print_array[0].' \end{document}'; - } elsif ($helper->{'VARS'}->{'PRINT_TYPE'} eq 'problems_from_directory') { + } elsif ($print_type eq 'problems_from_directory') { #prints selected problems from the subdirectory $selectionmade = 6; my @list_of_files=split /\|\|\|/, $helper->{'VARS'}->{'FILES'}; @@ -2633,7 +3099,13 @@ ENDPART # Only post process if that has not been turned off e.g. by a raw latex resource. if ($do_postprocessing) { - $result = &page_format_transformation($papersize,$laystyle,$numberofcolumns,$helper->{'VARS'}->{'PRINT_TYPE'},$result,$helper->{VARS}->{'assignment'},$helper->{'VARS'}->{'TABLE_CONTENTS'},$helper->{'VARS'}->{'TABLE_INDEX'},$selectionmade); + $result = &page_format_transformation($papersize, + $laystyle,$numberofcolumns, + $print_type,$result, + $helper->{VARS}->{'assignment'}, + $helper->{'VARS'}->{'TABLE_CONTENTS'}, + $helper->{'VARS'}->{'TABLE_INDEX'}, + $selectionmade); $result = &latex_corrections($number_of_columns,$result,$selectionmade, $helper->{'VARS'}->{'ANSWER_TYPE'}); #if ($numberofcolumns == 1) { @@ -2790,14 +3262,26 @@ sub print_resources { my ($username,$userdomain,$usersection) = split /:/,$person; my $fullname = &get_name($username,$userdomain); my $namepostfix = "\\\\"; # Both anon and not anon should get the same vspace. - if ($person =~ 'anon') { + # + # Figure out if we need to filter the output by + # the incomplete problems for that person + # + my $print_type = $helper->{'VARS'}->{'PRINT_TYPE'}; + my $print_incomplete = 0; + if (($print_type eq 'map_incomplete_problems_people_seq') || + ($print_type eq 'incomplete_problems_selpeople_course')) { + $print_incomplete = 1; + } + if ($person eq 'anonymous') { $namepostfix .="Name: "; $fullname = "CODE - ".$moreenv->{'CODE'}; } + # Fullname may have special latex characters that need \ prefixing: # my $i = 0; + my $actually_printed = 0; # Count of resources printed. #goes through all resources, checks if they are available for #current student, and produces output @@ -2814,6 +3298,7 @@ sub print_resources { # so we will just rely on prntout.pl to strip ENDOFSTUDENTSTAMP from the # postscript. Each ENDOFSTUDENTSTAMP will go on a line by itself. # + my $syllabus_first = 0; foreach my $curresline (@{$master_seq}) { if (defined $page_breaks{$curresline}) { @@ -2821,11 +3306,15 @@ sub print_resources { $current_output.= "\\newpage\n"; } } + $current_output .= &get_extra_vspaces($helper, $curresline); $i++; - if ( !($type eq 'problems' && ($curresline!~ m/\.(problem|exam|quiz|assess|survey|form|library|page)$/)) ) { 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)$/) { @@ -2839,6 +3328,7 @@ sub print_resources { $rendered = &print_latex_header().$rendered; } } +; if ($remove_latex_header eq 'YES') { $rendered = &latex_header_footer_remove($rendered); } else { @@ -2850,6 +3340,8 @@ sub print_resources { # Use a copy of the hash so we don't pervert it on future loop passes. my %answerenv = %{$moreenv}; $answerenv{'answer_output_mode'}='tex'; + + $answerenv{'latex_type'}=$helper->{'VARS'}->{'LATEX_TYPE'}; &Apache::lonxml::restore_problem_counter(); @@ -2863,7 +3355,7 @@ sub print_resources { 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); @@ -2921,20 +3413,32 @@ sub print_resources { } } $remove_latex_header = 'YES'; - } + } if (&Apache::loncommon::connection_aborted($r)) { last; } } + # If we are printing incomplete it's possible we don't have + # anything to print. The print subsystem is not so good at handling + # that so we're going to generate a stub that says there are no + # incomplete resources for the person. + # + + if ($actually_printed == 0) { + $current_output = &encapsulate_minipage("\\vskip -10mm \nNo incomplete resources\n \\vskip 100 mm { }\n"); + if ($remove_latex_header eq "NO") { + $current_output = &print_latex_header() . $current_output; + } else { + $current_output = &latex_header_footer_remove($current_output); + } + } if ($syllabus_first) { $current_output =~ s/\\\\ Last updated:/Last updated:/ } my $courseidinfo = &get_course(); - if (defined($courseidinfo)) { $courseidinfo=' - '.$courseidinfo } - if ($usersection ne '') {$courseidinfo.=' - Sec. '.$usersection} 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); + $currentassignment, $courseidinfo, $fullname, $usersection); my $header_start = ($columns_in_format == 1) ? '\lhead' : '\fancyhead[LO]'; $header_line = $header_start.'{'.$header_line.'}'; @@ -3012,6 +3516,11 @@ sub init_perm { $perm{'pfo'}=&Apache::lonnet::allowed('pfo', $env{'request.course.id'}.'/'.$env{'request.course.sec'}); } + $perm{'vgr'}=&Apache::lonnet::allowed('vgr',$env{'request.course.id'}); + if (!$perm{'vgr'}) { + $perm{'vgr'}=&Apache::lonnet::allowed('vgr', + $env{'request.course.id'}.'/'.$env{'request.course.sec'}); + } } sub get_randomly_ordered_warning { @@ -3083,19 +3592,21 @@ sub printHelper { $helper->declareVar('PRINT_TYPE'); $helper->declareVar("showallfoils"); $helper->declareVar("STUDENTS"); - + $helper->declareVar("EXTRASPACE"); - # The page breaks can get loaded initially from the course environment: + # The page breaks and extra spaces + # can get loaded initially from the course environment: # But we only do this in the initial state so that they are allowed to change. # - # $helper->{VARS}->{FINISHPAGE} = ''; &Apache::loncommon::restore_course_settings('print', {'pagebreaks' => 'scalar', + 'extraspace' => 'scalar', + 'extraspace_units' => 'scalar', 'lastprinttype' => 'scalar'}); # This will persistently load in the data we want from the @@ -3105,10 +3616,15 @@ sub printHelper { if (!defined ($env{"form.CURRENT_STATE"})) { $helper->{VARS}->{FINISHPAGE} = $env{'form.pagebreaks'}; + $helper->{VARS}->{EXTRASPACE} = $env{'form.extraspace'}; + $helper->{VARS}->{EXTRASPACE_UNITS} = $env{'form.extraspace_units'}; } else { my $state = $env{"form.CURRENT_STATE"}; if ($state eq "START") { $helper->{VARS}->{FINISHPAGE} = $env{'form.pagebreaks'}; + $helper->{VARS}->{EXTRASPACE} = $env{'form.extraspace'}; + $helper->{VARS}->{EXTRASPACE_UNITS} = $env{'form.extraspace_units'}; + } } @@ -3152,6 +3668,8 @@ sub printHelper { my ($map, $id, $url); my $subdir; 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'}); # Get the resource name from construction space if ($helper->{VARS}->{'construction'}) { @@ -3166,7 +3684,12 @@ sub printHelper { ($map, $id, $url) = &Apache::lonnet::decode_symb($symb); $helper->{VARS}->{'postdata'} = &Apache::lonenc::check_encrypt(&Apache::lonnet::clutter($url)); + my $navmap = Apache::lonnavmaps::navmap->new(); + my $res = $navmap->getBySymb($symb); + $res_printable = $res->resprintable() || $userCanPrint; #printability in course context } else { + # Resource space. + $url = $helper->{VARS}->{'postdata'}; $is_published=1; # From resource space. } @@ -3176,6 +3699,8 @@ sub printHelper { $resourceTitle = substr($postdata, rindex($postdata, '/') + 1); } $subdir = &Apache::lonnet::filelocation("", $url); + + } if (!$helper->{VARS}->{'curseed'} && $env{'form.curseed'}) { $helper->{VARS}->{'curseed'}=$env{'form.curseed'}; @@ -3205,18 +3730,29 @@ sub printHelper { my $printChoices = []; my $paramHash; - if ($resourceTitle) { + # If there is a current resource and it is printable + # Give that as a choice. + + if ($resourceTitle && $res_printable) { push @{$printChoices}, ["$resourceTitle (".&mt('the resource you just saw on the screen').")", 'current_document', 'PAGESIZE']; } + # Useful filter strings - my $isProblem = '($res->is_problem()||$res->contains_problem||$res->is_practice()) '; + + my $isPrintable = ' && $res->resprintable()'; + + my $isProblem = '(($res->is_problem()||$res->contains_problem() ||$res->is_practice()))'; + $isProblem .= $isPrintable unless $userCanPrint; $isProblem .= ' && !$res->randomout()' if !$userCanSeeHidden; - my $isProblemOrMap = '$res->is_problem() || $res->contains_problem() || $res->is_sequence() || $res->is_practice()'; - my $isNotMap = '!$res->is_sequence()'; + my $isProblemOrMap = '($res->is_problem() || $res->contains_problem() || $res->is_sequence() || $res->is_practice())'; + $isProblemOrMap .= $isPrintable unless $userCanPrint; + my $isNotMap = '(!$res->is_sequence())'; + $isNotMap .= $isPrintable unless $userCanPrint; $isNotMap .= ' && !$res->randomout()' if !$userCanSeeHidden; my $isMap = '$res->is_map()'; - my $symbFilter = '$res->shown_symb()'; + $isMap .= $isPrintable unless $userCanPrint; + my $symbFilter = '$res->shown_symb() '; my $urlValue = '$res->link()'; $helper->declareVar('SEQUENCE'); @@ -3227,7 +3763,16 @@ sub printHelper { if ($perm{'pav'}) { $start_new_option = "