--- loncom/interface/lonprintout.pm 2010/05/10 10:21:24 1.575.2.1 +++ loncom/interface/lonprintout.pm 2012/06/11 11:07:33 1.619 @@ -1,8 +1,7 @@ -# # The LearningOnline Network # Printout # -# $Id: lonprintout.pm,v 1.575.2.1 2010/05/10 10:21:24 foxr Exp $ +# $Id: lonprintout.pm,v 1.619 2012/06/11 11:07:33 foxr Exp $ # # Copyright Michigan State University Board of Trustees # @@ -28,6 +27,7 @@ # package Apache::lonprintout; use strict; +use POSIX; use Apache::Constants qw(:common :http); use Apache::lonxml; use Apache::lonnet; @@ -41,17 +41,18 @@ use Apache::admannotations; use Apache::lonenc; use Apache::entities; use Apache::londefdef; +# use Apache::structurelags; # for language management. use File::Basename; use HTTP::Response; - use LONCAPA::map(); -use POSIX qw(strftime); +use POSIX qw(ctime); use Apache::lonlocal; use Carp; use LONCAPA; + my %perm; my %parmhash; my $resources_printed; @@ -59,24 +60,214 @@ my $resources_printed; # Global variables that describe errors in ssi calls detected by ssi_with_retries. # -my $ssi_error; # True if there was an ssi error. -my $ssi_last_error_resource; # The resource URI that could not be fetched. -my $ssi_last_error - ; # The error text from the server. (e.g. 500 Server timed out). +my $ssi_error; # True if there was an ssi error. +my $ssi_last_error_resource; # The resource URI that could not be fetched. +my $ssi_last_error; # The error text from the server. (e.g. 500 Server timed out). # # Our ssi max retry count. # -my $ssi_retry_count = 5; # Some arbitrary value. +my $ssi_retry_count = 5; # Some arbitrary value. + # Font size: -my $font_size = 'normalsize'; # Default is normalsize... +my $font_size = 'normalsize'; # Default is normalsize... #---------------------------- Helper helpers. ------------------------- -# Returns the text needd for a student chooser. +## +# 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. +# 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. @@ -84,8 +275,12 @@ my $font_size = 'normalsize'; # Defau # variable - Name of variable to hold students. # next_state - State after chooser. + sub generate_student_chooser { - my ( $this_state, $sort_choice, $variable, $next_state ) = @_; + my ($this_state, + $sort_choice, + $variable, + $next_state) = @_; my $result = < Select sorting order of printout @@ -104,7 +299,7 @@ sub generate_student_chooser { CHOOSE_STUDENTS - return $result; + return $result; } # Generate the text needed for a resource chooser given the top level of @@ -125,17 +320,21 @@ CHOOSE_STUDENTS # filter - How to filter the resources. # value_func - function. # choice_func - If not empty generates a with this function. -# start_new_option +# start_new_option # - Fragment appended after valuefunc. # # sub generate_resource_chooser { - my ( - $this_state, $prompt_text, $resource_options, - $variable, $next_state, $top_url, - $filter, $choice_func, $value_func, - $start_new_option - ) = @_; + my ($this_state, + $prompt_text, + $resource_options, + $variable, + $next_state, + $top_url, + $filter, + $choice_func, + $value_func, + $start_new_option) = @_; my $result = < @@ -144,11 +343,11 @@ sub generate_resource_chooser { $next_state return $filter; CHOOSE_RESOURCES - if ( $choice_func ne '' ) { - $result .= "return $choice_func;"; + if ($choice_func ne '') { + $result .= "return $choice_func;"; } - if ( $top_url ne '' ) { - $result .= "$top_url"; + if ($top_url ne '') { + $result .= "$top_url"; } $result .= <return $value_func; @@ -156,10 +355,8 @@ CHOOSE_RESOURCES CHOOSE_RESOURCES - return $result; } - # # Generate the helper XML for a code choice helper dialog: # @@ -175,9 +372,12 @@ CHOOSE_RESOURCES # The Xml of the code chooser. # sub generate_code_selector { - my ( $helper, $state, $next_state, $bubble_types, $code_selections, - $saved_codes ) - = @_; # Unpack the parameters. + my ($helper, + $state, + $next_state, + $bubble_types, + $code_selections, + $saved_codes) = @_; # Unpack the parameters. my $result = < @@ -186,14 +386,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; @@ -202,7 +407,7 @@ sub generate_code_selector {
- Bubble sheet type: + Bubblesheet type: $bubble_types @@ -218,6 +423,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. } @@ -236,11 +443,331 @@ sub generate_code_selector { CHOOSE_ANON1 - return $result; + return $result; +} + +# Returns the XML for choosing how assignments are to be formatted +# that text must still be parsed by the helper xml parser. +# Parameters: 3 (required) + +# helper - The helper; $helper->{'VARS'}->{'PRINT_TYPE'} used +# to check if splitting PDFs by section can be offered. +# title - Title for the current state. +# this_state - State name of the chooser. + +sub generate_format_selector { + my ($helper,$title,$this_state) = @_; + my $secpdfoption; + 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 < +
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 } #----------------------------------------------------------------------- +# Computes an open and close date from a list of open/close dates for a resource's +# parts. +# +# @param \@opens - reference to an array of open dates. +# @param \@closes - reference to an array of close dates. +# +# @return ($open, $close) +# +# @note If open/close dates are not defined they will be retunred as undef +# @note It is possible for there to be no overlap in which case -1,-1 +# will be returned. +# @note The algorithm used is to take the latest open date and the earliest end date. +# +sub compute_open_window { + my ($opensref, $closesref) = @_; + + my @opens = @$opensref; + my @closes = @$closesref; + + # latest open date: + my $latest_open; + + foreach my $open (@opens) { + if (!defined($latest_open) || ($open > $latest_open)) { + $latest_open = $open; + } + } + # Earliest close: + + my $earliest_close; + foreach my $close (@closes) { + if (!defined($earliest_close) || ($close < $earliest_close)) { + $earliest_close = $close; + } + } + + # If no overlap...both are -1 as promised. + + if (defined($earliest_close) && defined($latest_open) + && ($earliest_close < $latest_open)) { + $latest_open = -1; + $earliest_close = -1; + } + + return ($latest_open, $earliest_close); + +} + +## +# Determines if 'now' is within the set of printable dates. +# +# @param $open_date - Starting date/timestamp. +# @param $close_date - Ending date/timestamp. +# +# @return 0 - Not open. +# @return 1 - open. +# +sub printable { + my ($open_date, $close_date) = @_; + + + my $now = time(); + + # Have to do a bit of fancy footwork around undefined open/close dates: + + if ($open_date && ($open_date > $now)) { + return 0; + } + + if ($close_date && ($close_date < $now)) { + return 0; + } + + return 1; + +} + +## +# Returns the innermost print start/print end dates for a resource. +# This is done by looking at the start/end dates for its parts and choosing +# 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 retunred as undef +# @note It is possible for there to be no overlap in which case -1,-1 +# will be returned. +# @note The algorithm used is to take the latest open date and the earliest end date. +# + +sub get_print_dates { + my $res = shift; + my $partsref = $res->parts(); + my @parts = @$partsref; + my $open_date; + my $close_date; + my @open_dates; + my @close_dates; + + + if (defined(@parts) && (scalar(@parts) > 0)) { + foreach my $part (@parts) { + my $partopen = $res->parmval('printstartdate', $part); + my $partclose = $res->parmval('printenddate', $part); + + push(@open_dates, $partopen); + push(@close_dates, $partclose); + } + } + + ($open_date, $close_date) = &compute_open_window(\@open_dates, \@close_dates); + + if ($open_date) { + $open_date = POSIX::strftime('%D', localtime($open_date)); + } + if ($close_date) { + $close_date = POSIX::strftime('%D', localtime($close_date)); + } + + return ($open_date, $close_date); +} + +## +# Get the dates for which a course says a resource can be printed. This is like +# get_print_dates but namvaps::course_print_dates are gotten...and not converted +# to times either. +# +# @param $res - Reference to a resource has from lonnvampas::resource. +# +# @return (opendate, closedate) +# +sub course_print_dates { + my $res = shift; + my $partsref = $res->parts(); + my @parts = @$partsref; + my $open_date; + my $close_date; + my @open_dates; + my @close_dates; + my $navmap = $res->{NAV_MAP}; # Slightly OO dirty. + + # Don't bother looping over undefined or empty parts arraY; + + if (defined(@parts) && (scalar(@parts) > 0)) { + foreach my $part (@parts) { + my ($partopen, $partclose) = $navmap->course_printdates($res, $part); + push(@open_dates, $partopen); + push(@close_dates, $partclose); + } + ($open_date, $close_date) = &compute_open_window(\@open_dates, \@close_dates); + } + return ($open_date, $close_date); +} +## +# Same as above but for the enclosing map: +# +sub map_print_dates { + my $res = shift; + my $partsref = $res->parts(); + my @parts = @$partsref; + my $open_date; + my $close_date; + my @open_dates; + my @close_dates; + my $navmap = $res->{NAV_MAP}; # slightly OO dirty. + + + # Don't bother looping over undefined or empty parts arraY; + + if (defined(@parts) && (scalar(@parts) > 0)) { + foreach my $part (@parts) { + my ($partopen, $partclose) = $navmap->map_printdates($res, $part); + push(@open_dates, $partopen); + push(@close_dates, $partclose); + } + ($open_date, $close_date) = &compute_open_window(\@open_dates, \@close_dates); + } + return ($open_date, $close_date); +} + +# 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 (array of symb's). +# $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 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); + } + } + } + + + return \@output_seq; # for now. + +} + + # Fetch the contents of a resource, uninterpreted. # This is used here to fetch a latex file to be included # verbatim into the printout< @@ -252,35 +779,34 @@ CHOOSE_ANON1 sub fetch_raw_resource { my ($url) = @_; - my $filename = &Apache::lonnet::filelocation( "", $url ); - my $contents = &Apache::lonnet::getfile($filename); + my $filename = &Apache::lonnet::filelocation("", $url); + my $contents = &Apache::lonnet::getfile($filename); - if ( $contents == -1 ) { - return "File open failed for $filename"; # This will bomb the print. + if ($contents == -1) { + return "File open failed for $filename"; # This will bomb the print. } return $contents; + } -# Fetch the annotations associated with a URL and +# Fetch the annotations associated with a URL and # put a centered 'annotations:' title. # This is all suppressed if the annotations are empty. # sub annotate { my ($symb) = @_; - my $annotation_text = &Apache::loncommon::get_annotation( $symb, 1 ); + my $annotation_text = &Apache::loncommon::get_annotation($symb, 1); + my $result = ""; - if ( length($annotation_text) > 0 ) { - $result .= - '\\hspace*{\\fill} \\\\[\\baselineskip] \textbf{Annotations:} \\\\ '; - $result .= "\n"; - $result .= - &Apache::lonxml::latex_special_symbols( $annotation_text, "" ) - ; # Escape latex. - $result .= "\n\n"; + if (length($annotation_text) > 0) { + $result .= '\\hspace*{\\fill} \\\\[\\baselineskip] \textbf{Annotations:} \\\\ '; + $result .= "\n"; + $result .= &Apache::lonxml::latex_special_symbols($annotation_text,""); # Escape latex. + $result .= "\n\n"; } return $result; } @@ -300,16 +826,17 @@ sub set_font_size { # case the first substituion would insert a spurious \ oh happy day. # as this has been the cause of much mystery and hair pulling _sigh_ - if ( $font_size ne '' ) { + 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 +# include_pdf - PDF files are included into the # output as follows: # - The PDF, if necessary, is replicated. # - The PDF is added to the list of files to convert to postscript (along with the images). @@ -318,7 +845,7 @@ sub set_font_size { # # Parameters: # pdf_uri - URI of the PDF file to include. -# +# # Returns: # The LaTeX to include. # @@ -332,41 +859,65 @@ sub include_pdf { # Where is the file? If not local we'll need to repcopy it:' - my $file = &Apache::lonnet::filelocation( '', $pdf_uri ); - if ( !-e $file ) { - &Apache::lonnet::repcopy($file); - $file = &Apache::lonnet::filelocation( '', $pdf_uri ); + my $file = &Apache::lonnet::filelocation('', $pdf_uri); + if (! -e $file) { + &Apache::lonnet::repcopy($file); + $file = &Apache::lonnet::filelocation('',$pdf_uri); } - # The file isn ow replicated locally.. or it did not exist in the first place - # (unlikely). If it did exist, add the pdf to the set of files/images that - # need tob e converted for this print job: + # The file isn ow replicated locally.. or it did not exist in the first place + # (unlikely). If it did exist, add the pdf to the set of files/images that + # need tob e converted for this print job: - $file =~ s|(.*)/res/|/home/httpd/html/res/|; + my $londocroot = $Apache::lonnet::perlvar{'lonDocRoot'}; + $file =~ s{(.*)/res/}{$londocroot/res/}; - open( FILE, -">>/home/httpd/prtspool/$env{'user.name'}_$env{'user.domain'}_printout.dat" - ); + open(FILE,">>$Apache::lonnet::perlvar{'lonPrtDir'}/$env{'user.name'}_$env{'user.domain'}_printout.dat"); print FILE ("$file\n"); - close(FILE); + close (FILE); # Construct the special to put out. To do this we need to get the # resulting filename after conversion. The file will have the same name # but will be in the user's spool directory with converted images. my $dirname = "/home/httpd/prtspool/$env{'user.name'}/"; - my ( $base, $path, $ext ) = &fileparse( $file, '.pdf' ); - + my ( $base, $path, $ext) = &fileparse($file, '.pdf'); # my $destname = $dirname.'/'.$base.'.eps'; # Not really an eps but easier in printout.pl $base =~ s/ /\_/g; + my $output = &print_latex_header(); - $output .= - '\special{ps: _begin_job_ (' . $base . '.pdf.eps' . ')run _end_job_}'; + $output .= '\special{ps: _begin_job_ (' + .$base.'.pdf.eps'. + ')run _end_job_}'; return $output; + +} +## +# Collect the various \select_language{language_name} +# latex tags to build a \usepackage[lang-list]{babel} which will +# appear just prior to the \begin{document} at the front of the concatenated +# set of resources: +# @param doc - The string of latex to search/replace. +# @return string +# @retval - the modified document stringt. +# +sub collect_languages { + my $doc = shift; + my %languages; + 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}/; + } + return $doc; } +#------------------------------------------------------------------- # # ssi_with_retries- Does the server side include of a resource. @@ -374,7 +925,7 @@ sub include_pdf { # the number of times requested by the caller. # If we still have a proble, no text is appended to the # output and we set some global variables. -# to indicate to the caller an SSI error occurred. +# to indicate to the caller an SSI error occurred. # All of this is supposed to deal with the issues described # in LonCAPA BZ 5631 see: # http://bugs.lon-capa.org/show_bug.cgi?id=5631 @@ -384,7 +935,7 @@ sub include_pdf { # resource - The resource to include. This is passed directly, without # interpretation to lonnet::ssi. # form - The form hash parameters that guide the interpretation of the resource -# +# # retries - Number of retries allowed before giving up completely. # Returns: # On success, returns the rendered resource identified by the resource parameter. @@ -400,20 +951,19 @@ sub include_pdf { # in the event of an error. # sub ssi_with_retries { - my ( $resource, $retries, %form ) = @_; + my ($resource, $retries, %form) = @_; my $target = $form{'grade_target'}; my $aom = $form{'answer_output_mode'}; - my ( $content, $response ) = - &Apache::loncommon::ssi_with_retries( $resource, $retries, %form ); - if ( !$response->is_success ) { - $ssi_error = 1; - $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"); + + + my ($content, $response) = &Apache::loncommon::ssi_with_retries($resource, $retries, %form); + if (!$response->is_success) { + $ssi_error = 1; + $ssi_last_error_resource = $resource; + $ssi_last_error = $response->code . " " . $response->message; + $content='\section*{!!! An error occurred !!!}'; } return $content; @@ -421,25 +971,14 @@ sub ssi_with_retries { } sub get_student_view_with_retries { - my ( - $curresline, $retries, $username, $userdomain, - $courseid, $target, $moreenv - ) = @_; - - my ( $content, $response ) = - &Apache::loncommon::get_student_view_with_retries( - $curresline, $retries, $username, $userdomain, - $courseid, $target, $moreenv - ); - if ( !$response->is_success ) { - $ssi_error = 1; - $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" - ); + my ($curresline,$retries,$username,$userdomain,$courseid,$target,$moreenv)=@_; + + my ($content, $response) = &Apache::loncommon::get_student_view_with_retries($curresline,$retries,$username,$userdomain,$courseid,$target,$moreenv); + if (!$response->is_success) { + $ssi_error = 1; + $ssi_last_error_resource = $curresline.' for user '.$username.':'.$userdomain; + $ssi_last_error = $response->code . " " . $response->message; + $content='\section*{!!! An error occurred !!!}'; } return $content; @@ -447,41 +986,41 @@ sub get_student_view_with_retries { # # printf_style_subst item format_string repl -# +# # Does printf style substitution for a format string that # can have %[n]item in it.. wherever, %[n]item occurs, # rep is substituted in format_string. Note that # [n] is an optional integer length. If provided, -# repl is truncated to at most [n] characters prior to +# repl is truncated to at most [n] characters prior to # substitution. # sub printf_style_subst { - my ( $item, $format_string, $repl ) = @_; + my ($item, $format_string, $repl) = @_; my $result = ""; - while ( $format_string =~ /(%)(\d*)\Q$item\E/g ) { - my $fmt = $1; - my $size = $2; - my $subst = $repl; - if ( $size ne "" ) { - $subst = substr( $subst, 0, $size ); - - # Here's a nice edge case.. supose the end of the - # substring is a \. In that case may have just - # chopped off a TeX escape... in that case, we append - # " " for the trailing character, and let the field - # spill over a bit (sigh). - # We don't just chop off the last character in order to deal - # with one last pathology, and that would be if substr had - # trimmed us to e.g. \\\ - - if ( $subst =~ /\\$/ ) { - $subst .= " "; - } - } - my $item_pos = pos($format_string); - $result .= - substr( $format_string, 0, $item_pos - length($size) - 2 ) . $subst; - $format_string = substr( $format_string, pos($format_string) ); + while ($format_string =~ /(%)(\d*)\Q$item\E/g ) { + my $fmt = $1; + my $size = $2; + my $subst = $repl; + if ($size ne "") { + $subst = substr($subst, 0, $size); + + # Here's a nice edge case.. supose the end of the + # substring is a \. In that case may have just + # chopped off a TeX escape... in that case, we append + # " " for the trailing character, and let the field + # spill over a bit (sigh). + # We don't just chop off the last character in order to deal + # with one last pathology, and that would be if substr had + # trimmed us to e.g. \\\ + + + if ($subst =~ /\\$/) { + $subst .= " "; + } + } + my $item_pos = pos($format_string); + $result .= substr($format_string, 0, $item_pos - length($size) -2) . $subst; + $format_string = substr($format_string, pos($format_string)); } # Put the residual format string into the result: @@ -491,8 +1030,9 @@ sub printf_style_subst { return $result; } -# Format a header according to a format. -# + +# Format a header according to a format. +# # Substitutions: # %a - Assignment name. @@ -501,62 +1041,63 @@ sub printf_style_subst { # %s - The section if it is supplied. # sub format_page_header { - my ( $width, $format, $assignment, $course, $student, $section ) = @_; + my ($width, $format, $assignment, $course, $student, $section) = @_; - $width = &recalcto_mm($width); # Get width in mm. - my $chars_per_line = int( $width / 1.6 ); # Character/textline. - # Default format? - if ( $format eq '' ) { + $width = &recalcto_mm($width); # Get width in mm. + my $chars_per_line = int($width/1.6); # Character/textline. - # For the default format, we may need to truncate - # elements.. To do this we need to get the page width. - # we assume that each character is about 2mm in width. - # (correct for the header text size??). We ignore - # any formatting (e.g. boldfacing in this). - # - # - Allow the student/course to be one line. - # but only truncate the course. - # - Allow the assignment to be 2 lines (wrapped). - # + # Default format? - my $name_length = int( $chars_per_line * 3 / 4 ); - my $sec_length = int( $chars_per_line / 5 ); + if ($format eq '') { + # For the default format, we may need to truncate + # elements.. To do this we need to get the page width. + # we assume that each character is about 2mm in width. + # (correct for the header text size??). We ignore + # any formatting (e.g. boldfacing in this). + # + # - Allow the student/course to be one line. + # but only truncate the course. + # - Allow the assignment to be 2 lines (wrapped). + # + + - $format = "%$name_length" . 'n'; + my $name_length = int($chars_per_line *3 /4); + my $sec_length = int($chars_per_line / 5); - if ($section) { - $format .= ' - Sec: ' . "%$sec_length" . 's'; - } + $format = "%$name_length".'n'; - $format .= '\\\\%c \\\\ %a'; + if ($section) { + $format .= ' - Sec: '."%$sec_length".'s'; + } - } + $format .= '\\\\%c \\\\ %a'; + + } # 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 ); - + $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/ ) { - + if($format =~ /\\\\\s\\\\\s/) { #TODO find sensible caption for page header - my $testPrintout = - '\\\\' . &mt('Construction Space') . ' \\\\' . &mt('Test-Printout '); + 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 @@ -565,13 +1106,14 @@ sub format_page_header { # 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 ); + my $total_length = 3*$chars_per_line; + if (length($format) > $total_length) { + $format = substr($format, 0, $total_length); } - return $format; + return $format; + } # @@ -579,31 +1121,29 @@ sub format_page_header { # sub num_to_letters { my ($num) = @_; - my @nums = split( '', $num ); - my @num_to_let = ( 'A' .. 'Z' ); + my @nums= split('',$num); + my @num_to_let=('A'..'Z'); my $word; - foreach my $digit (@nums) { $word .= $num_to_let[$digit]; } + foreach my $digit (@nums) { $word.=$num_to_let[$digit]; } return $word; } - # Convert a letter code to numeric. # sub letters_to_num { my ($letters) = @_; - my @letters = split( '', uc($letters) ); - my %substitution; + my @letters = split('', uc($letters)); + my %substitution; my $digit = 0; - foreach my $letter ( 'A' .. 'J' ) { - $substitution{$letter} = $digit; - $digit++; + foreach my $letter ('A'..'J') { + $substitution{$letter} = $digit; + $digit++; } - # The substitution is done as below to preserve leading # zeroes which are needed to keep the code size exact # - my $result = ""; + my $result =""; foreach my $letter (@letters) { - $result .= $substitution{$letter}; + $result.=$substitution{$letter}; } return $result; } @@ -617,24 +1157,20 @@ sub letters_to_num { # num_digits - Number of digits required. # sub is_valid_numeric_code { - my ( $value, $num_digits ) = @_; - + my ($value, $num_digits) = @_; # Remove leading/trailing whitespace; $value =~ s/^\s*//g; $value =~ s/\s*$//g; - + # All digits? - if ( $value !~ /^[0-9]+$/ ) { - return - "Numeric code $value has invalid characters - must only be digits"; - } - if ( length($value) != $num_digits ) { - return -"Numeric code $value incorrect number of digits (correct = $num_digits)"; + if ($value !~ /^[0-9]+$/) { + return "Numeric code $value has invalid characters - must only be digits"; + } + if (length($value) != $num_digits) { + return "Numeric code $value incorrect number of digits (correct = $num_digits)"; } return undef; } - # Determines if a code is a valid alhpa code. Alpha codes # are ciphers that map [A-J,a-j] -> 0..9 0..9. # They also have a correct digit count. @@ -645,27 +1181,26 @@ sub is_valid_numeric_code { # leading and trailing whitespace are ignored. # sub is_valid_alpha_code { - my ( $value, $num_letters ) = @_; - - # strip leading and trailing spaces. + my ($value, $num_letters) = @_; + + # strip leading and trailing spaces. $value =~ s/^\s*//g; $value =~ s/\s*$//g; # All alphas in the right range? - if ( $value !~ /^[A-J,a-j]+$/ ) { - return "Invalid letter code $value must only contain A-J"; + if ($value !~ /^[A-J,a-j]+$/) { + return "Invalid letter code $value must only contain A-J"; } - if ( length($value) != $num_letters ) { - return -"Letter code $value has incorrect number of letters (correct = $num_letters)"; + if (length($value) != $num_letters) { + return "Letter code $value has incorrect number of letters (correct = $num_letters)"; } return undef; } # Determine if a code entered by the user in a helper is valid. # valid depends on the code type and the type of code selected. -# The type of code selected can either be numeric or +# The type of code selected can either be numeric or # Alphabetic. If alphabetic, the code, in fact is a simple # substitution cipher for the actual numeric code: 0->A, 1->B ... # We'll be nice and be case insensitive for alpha codes. @@ -678,27 +1213,42 @@ sub is_valid_alpha_code { # other - An error message indicating what's wrong. # sub is_code_valid { - my ( $code_value, $code_option ) = @_; - my ( $code_type, $code_length ) = ( 'letter', 6 ); # defaults. + my ($code_value, $code_option) = @_; + my ($code_type, $code_length) = ('letter', 6); # defaults. my @lines = &Apache::grades::get_scantronformat_file(); foreach my $line (@lines) { - my ( $name, $type, $length ) = ( split( /:/, $line ) )[ 0, 2, 4 ]; - if ( $name eq $code_option ) { - $code_length = $length; - if ( $type eq 'number' ) { - $code_type = 'number'; - } - } + my ($name, $type, $length) = (split(/:/, $line))[0,2,4]; + if($name eq $code_option) { + $code_length = $length; + if($type eq 'number') { + $code_type = 'number'; + } + } } my $valid; - if ( $code_type eq 'number' ) { - return &is_valid_numeric_code( $code_value, $code_length ); - } - else { - return &is_valid_alpha_code( $code_value, $code_length ); + if ($code_type eq 'number') { + return &is_valid_numeric_code($code_value, $code_length); + } else { + return &is_valid_alpha_code($code_value, $code_length); } } +# +# 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: @@ -716,44 +1266,43 @@ sub is_code_valid { # # sub compare_names { - # First split the names up into the primary fields. - my ( $u1, $d1, $s1, $n1, $stat1 ) = split( /:/, $a ); - my ( $u2, $d2, $s2, $n2, $stat2 ) = split( /:/, $b ); + my ($u1, $d1, $s1, $n1, $stat1) = split(/:/, $a); + my ($u2, $d2, $s2, $n2, $stat2) = split(/:/, $b); # Now split the last name and first name of each n: # - my ( $l1, $f1 ) = split( /,/, $n1 ); - my ( $l2, $f2 ) = split( /,/, $n2 ); + my ($l1,$f1) = split(/,/, $n1); + my ($l2,$f2) = split(/,/, $n2); # We don't bother to remove the leading/trailing whitespace from the # firstname, unless the last names compare identical. - if ( $l1 lt $l2 ) { - return -1; + if($l1 lt $l2) { + return -1; } - if ( $l1 gt $l2 ) { - return 1; + if($l1 gt $l2) { + return 1; } # Break the tie on the first name, but there are leading (possibly trailing - # whitespaces to get rid of first + # whitespaces to get rid of first # - $f1 =~ s/^\s+//; # Remove leading... - $f1 =~ s/\s+$//; # Trailing spaces from first 1... - + $f1 =~ s/^\s+//; # Remove leading... + $f1 =~ s/\s+$//; # Trailing spaces from first 1... + $f2 =~ s/^\s+//; - $f2 =~ s/\s+$//; # And the same for first 2... + $f2 =~ s/\s+$//; # And the same for first 2... - if ( $f1 lt $f2 ) { - return -1; + if($f1 lt $f2) { + return -1; } - if ( $f1 gt $f2 ) { - return 1; + if($f1 gt $f2) { + return 1; } - + # Must be the same name. return 0; @@ -765,20 +1314,18 @@ sub latex_header_footer_remove { $text =~ s/\\documentclass([^&]*)\\begin{document}//; return $text; } - # -# If necessary, encapsulate text inside +# If necessary, encapsulate text inside # a minipage env. # necessity is determined by the problem_split param. # sub encapsulate_minipage { my ($text) = @_; - if ( !( $env{'form.problem.split'} =~ /yes/i ) ) { - $text = '\begin{minipage}{\textwidth}' . $text . '\end{minipage}'; + if (!($env{'form.problem.split'} =~ /yes/i)) { + $text = '\begin{minipage}{\textwidth}'.$text.'\end{minipage}'; } return $text; } - # # The NUMBER_TO_PRINT and SPLIT_PDFS # variables interact, this sub looks at these two parameters @@ -792,37 +1339,31 @@ sub adjust_number_to_print { my $helper = shift; my $split_pdf = $helper->{'VARS'}->{'SPLIT_PDFS'}; - - if ( $split_pdf eq 'all' ) { - $helper->{'VARS'}->{'NUMBER_TO_PRINT'} = 'all'; - } - elsif ( $split_pdf eq 'oneper' ) { - $helper->{'VARS'}->{'NUMBER_TO_PRINT'} = 1; - } - elsif ( $split_pdf eq 'sections' ) { - $helper->{'VARS'}->{'NUMBER_TO_PRINT'} = 'section'; - } - elsif ( $split_pdf eq 'usenumber' ) { - - # Unmodified. - } - else { - - # Error!!!! - - croak - "bad SPLIT_PDFS: $split_pdf in lonprintout::adjust_number_to_print"; + + if ($split_pdf eq 'all') { + $helper->{'VARS'}->{'NUMBER_TO_PRINT'} = 'all'; + } elsif ($split_pdf eq 'oneper') { + $helper->{'VARS'}->{'NUMBER_TO_PRINT'} = 1; + } elsif ($split_pdf eq 'sections') { + $helper->{'VARS'}->{'NUMBER_TO_PRINT'} = 'section'; + } elsif ($split_pdf eq 'usenumber') { + # Unmodified. + } else { + # Error!!!! + + croak "bad SPLIT_PDFS: $split_pdf in lonprintout::adjust_number_to_print"; } } + sub character_chart { my $result = shift; - return &Apache::entities::replace_entities($result); + return &Apache::entities::replace_entities($result); } sub old_character_chart { - my $result = shift; + my $result = shift; $result =~ s/&\#0?0?(7|9);//g; $result =~ s/&\#0?(10|13);//g; $result =~ s/&\#0?32;/ /g; @@ -830,8 +1371,8 @@ sub old_character_chart { $result =~ s/&(\#0?34|quot);/\"/g; $result =~ s/&\#0?35;/\\\#/g; $result =~ s/&\#0?36;/\\\$/g; - $result =~ s/&\#0?37;/\\%/g; - $result =~ s/&(\#0?38|amp);/\\&/g; + $result =~ s/&\#0?37;/\\%/g; + $result =~ s/&(\#0?38|amp);/\\&/g; $result =~ s/&\#(0?39|146);/\'/g; $result =~ s/&\#0?40;/(/g; $result =~ s/&\#0?41;/)/g; @@ -938,7 +1479,7 @@ sub old_character_chart { $result =~ s/&(\#160|nbsp);/~/g; $result =~ s/&(\#161|iexcl);/!\`/g; $result =~ s/&(\#162|cent);/\\textcent /g; - $result =~ s/&(\#163|pound);/\\pounds /g; + $result =~ s/&(\#163|pound);/\\pounds /g; $result =~ s/&(\#164|curren);/\\textcurrency /g; $result =~ s/&(\#165|yen);/\\textyen /g; $result =~ s/&(\#166|brvbar);/\\textbrokenbar /g; @@ -964,32 +1505,32 @@ sub old_character_chart { $result =~ s/&(\#188|frac14);/\\textonequarter /g; $result =~ s/&(\#189|frac12);/\\textonehalf /g; $result =~ s/&(\#190|frac34);/\\textthreequarters /g; - $result =~ s/&(\#191|iquest);/?\`/g; - $result =~ s/&(\#192|Agrave);/\\\`{A}/g; - $result =~ s/&(\#193|Aacute);/\\\'{A}/g; + $result =~ s/&(\#191|iquest);/?\`/g; + $result =~ s/&(\#192|Agrave);/\\\`{A}/g; + $result =~ s/&(\#193|Aacute);/\\\'{A}/g; $result =~ s/&(\#194|Acirc);/\\^{A}/g; $result =~ s/&(\#195|Atilde);/\\~{A}/g; - $result =~ s/&(\#196|Auml);/\\\"{A}/g; + $result =~ s/&(\#196|Auml);/\\\"{A}/g; $result =~ s/&(\#197|Aring);/{\\AA}/g; $result =~ s/&(\#198|AElig);/{\\AE}/g; $result =~ s/&(\#199|Ccedil);/\\c{c}/g; - $result =~ s/&(\#200|Egrave);/\\\`{E}/g; - $result =~ s/&(\#201|Eacute);/\\\'{E}/g; + $result =~ s/&(\#200|Egrave);/\\\`{E}/g; + $result =~ s/&(\#201|Eacute);/\\\'{E}/g; $result =~ s/&(\#202|Ecirc);/\\^{E}/g; $result =~ s/&(\#203|Euml);/\\\"{E}/g; $result =~ s/&(\#204|Igrave);/\\\`{I}/g; - $result =~ s/&(\#205|Iacute);/\\\'{I}/g; + $result =~ s/&(\#205|Iacute);/\\\'{I}/g; $result =~ s/&(\#206|Icirc);/\\^{I}/g; - $result =~ s/&(\#207|Iuml);/\\\"{I}/g; + $result =~ s/&(\#207|Iuml);/\\\"{I}/g; $result =~ s/&(\#209|Ntilde);/\\~{N}/g; $result =~ s/&(\#210|Ograve);/\\\`{O}/g; $result =~ s/&(\#211|Oacute);/\\\'{O}/g; $result =~ s/&(\#212|Ocirc);/\\^{O}/g; $result =~ s/&(\#213|Otilde);/\\~{O}/g; - $result =~ s/&(\#214|Ouml);/\\\"{O}/g; + $result =~ s/&(\#214|Ouml);/\\\"{O}/g; $result =~ s/&(\#215|times);/\\ensuremath\{\\times\}/g; $result =~ s/&(\#216|Oslash);/{\\O}/g; - $result =~ s/&(\#217|Ugrave);/\\\`{U}/g; + $result =~ s/&(\#217|Ugrave);/\\\`{U}/g; $result =~ s/&(\#218|Uacute);/\\\'{U}/g; $result =~ s/&(\#219|Ucirc);/\\^{U}/g; $result =~ s/&(\#220|Uuml);/\\\"{U}/g; @@ -1020,7 +1561,7 @@ sub old_character_chart { $result =~ s/&(\#246|ouml);/\\\"{o}/g; $result =~ s/&(\#247|divide);/\\ensuremath\{\\div\}/g; $result =~ s/&(\#248|oslash);/{\\o}/g; - $result =~ s/&(\#249|ugrave);/\\\`{u}/g; + $result =~ s/&(\#249|ugrave);/\\\`{u}/g; $result =~ s/&(\#250|uacute);/\\\'{u}/g; $result =~ s/&(\#251|ucirc);/\\^{u}/g; $result =~ s/&(\#252|uuml);/\\\"{u}/g; @@ -1028,8 +1569,7 @@ sub old_character_chart { $result =~ s/&(\#255|yuml);/\\\"{y}/g; $result =~ s/&\#295;/\\ensuremath\{\\hbar\}/g; $result =~ s/&\#952;/\\ensuremath\{\\theta\}/g; - - #Greek Alphabet +#Greek Alphabet $result =~ s/&(alpha|\#945);/\\ensuremath\{\\alpha\}/g; $result =~ s/&(beta|\#946);/\\ensuremath\{\\beta\}/g; $result =~ s/&(gamma|\#947);/\\ensuremath\{\\gamma\}/g; @@ -1080,8 +1620,7 @@ sub old_character_chart { $result =~ s/&(Chi|\#935);/X/g; $result =~ s/&(Psi|\#936);/\\ensuremath\{\\Psi\}/g; $result =~ s/&(Omega|\#937);/\\ensuremath\{\\Omega\}/g; - - #Arrows (extended HTML 4.01) +#Arrows (extended HTML 4.01) $result =~ s/&(larr|\#8592);/\\ensuremath\{\\leftarrow\}/g; $result =~ s/&(uarr|\#8593);/\\ensuremath\{\\uparrow\}/g; $result =~ s/&(rarr|\#8594);/\\ensuremath\{\\rightarrow\}/g; @@ -1092,8 +1631,7 @@ sub old_character_chart { $result =~ s/&(rArr|\#8658);/\\ensuremath\{\\Rightarrow\}/g; $result =~ s/&(dArr|\#8659);/\\ensuremath\{\\Downarrow\}/g; $result =~ s/&(hArr|\#8660);/\\ensuremath\{\\Leftrightarrow\}/g; - - #Mathematical Operators (extended HTML 4.01) +#Mathematical Operators (extended HTML 4.01) $result =~ s/&(forall|\#8704);/\\ensuremath\{\\forall\}/g; $result =~ s/&(part|\#8706);/\\ensuremath\{\\partial\}/g; $result =~ s/&(exist|\#8707);/\\ensuremath\{\\exists\}/g; @@ -1132,17 +1670,14 @@ sub old_character_chart { $result =~ s/&(otimes|\#8855);/\\ensuremath\{\\otimes\}/g; $result =~ s/&(perp|\#8869);/\\ensuremath\{\\perp\}/g; $result =~ s/&(sdot|\#8901);/\\ensuremath\{\\cdot\}/g; - - #Geometric Shapes (extended HTML 4.01) +#Geometric Shapes (extended HTML 4.01) $result =~ s/&(loz|\#9674);/\\ensuremath\{\\Diamond\}/g; - - #Miscellaneous Symbols (extended HTML 4.01) +#Miscellaneous Symbols (extended HTML 4.01) $result =~ s/&(spades|\#9824);/\\ensuremath\{\\spadesuit\}/g; $result =~ s/&(clubs|\#9827);/\\ensuremath\{\\clubsuit\}/g; $result =~ s/&(hearts|\#9829);/\\ensuremath\{\\heartsuit\}/g; $result =~ s/&(diams|\#9830);/\\ensuremath\{\\diamondsuit\}/g; - - # Chemically useful 'things' contributed by Hon Kie (bug 4652). +# Chemically useful 'things' contributed by Hon Kie (bug 4652). $result =~ s/&\#8636;/\\ensuremath\{\\leftharpoonup\}/g; $result =~ s/&\#8637;/\\ensuremath\{\\leftharpoondown\}/g; @@ -1163,411 +1698,372 @@ sub old_character_chart { $result =~ s/&(ldquo|#8220);/\`\`/g; $result =~ s/&(rdquo|#8221);/\'\'/g; + + return $result; } -#width, height, oddsidemargin, evensidemargin, topmargin -my %page_formats = ( - 'letter' => { - 'book' => { - '1' => [ '7.1 in', '9.8 in', '-0.57 in', '-0.57 in', '0.275 in' ], - '2' => [ '3.66 in', '9.8 in', '-0.57 in', '-0.57 in', '0.275 in' ] - }, - 'album' => { - '1' => [ '8.8 in', '6.8 in', '-0.55 in', '-0.55 in', '0.394 in' ], - '2' => [ '4.8 in', '6.8 in', '-0.5 in', '-1.0 in', '3.5 in' ] - }, - }, - 'legal' => { - 'book' => { - '1' => [ '7.1 in', '13 in',, '-0.57 in', '-0.57 in', '-0.5 in' ], - '2' => [ '3.66 in', '13 in', '-0.57 in', '-0.57 in', '-0.5 in' ] - }, - 'album' => { - '1' => [ '12 in', '7.1 in',, '-0.57 in', '-0.57 in', '-0.5 in' ], - '2' => [ '6.0 in', '7.1 in', '-1 in', '-1 in', '5 in' ] - }, - }, - 'tabloid' => { - 'book' => { - '1' => [ '9.8 in', '16 in', '-0.57 in', '-0.57 in', '-0.5 in' ], - '2' => [ '4.9 in', '16 in', '-0.57 in', '-0.57 in', '-0.5 in' ] - }, - 'album' => { - '1' => [ '16 in', '9.8 in', '-0.57 in', '-0.57 in', '-0.5 in' ], - '2' => [ '16 in', '4.9 in', '-0.57 in', '-0.57 in', '-0.5 in' ] - }, - }, - 'executive' => { - 'book' => { - '1' => [ '6.8 in', '9 in', '-0.57 in', '-0.57 in', '1.2 in' ], - '2' => [ '3.1 in', '9 in', '-0.57 in', '-0.57 in', '1.2 in' ] - }, - 'album' => { - '1' => [], - '2' => [] - }, - }, - 'a2' => { - 'book' => { - '1' => [], - '2' => [] - }, - 'album' => { - '1' => [], - '2' => [] - }, - }, - 'a3' => { - 'book' => { - '1' => [], - '2' => [] - }, - 'album' => { - '1' => [], - '2' => [] - }, - }, - 'a4' => { - 'book' => { - '1' => - [ '17.6 cm', '27.2 cm', '-1.397 cm', '-2.11 cm', '-1.27 cm' ], - '2' => [ '9.1 cm', '27.2 cm', '-1.397 cm', '-2.11 cm', '-1.27 cm' ] - }, - 'album' => { - '1' => [ '21.59 cm', '19.558 cm', '-1.397cm', '-2.11 cm', '0 cm' ], - '2' => [ '9.91 cm', '19.558 cm', '-1.397 cm', '-2.11 cm', '0 cm' ] - }, - }, - 'a5' => { - 'book' => { - '1' => [], - '2' => [] - }, - 'album' => { - '1' => [], - '2' => [] - }, - }, - 'a6' => { - 'book' => { - '1' => [], - '2' => [] - }, - 'album' => { - '1' => [], - '2' => [] - }, - }, -); -sub page_format { + #width, height, oddsidemargin, evensidemargin, topmargin +my %page_formats= + ('letter' => { + 'book' => { + '1' => [ '7.1 in','9.8 in', '-0.57 in','-0.57 in','0.275 in'], + '2' => ['3.66 in','9.8 in', '-0.57 in','-0.57 in','0.275 in'] + }, + 'album' => { + '1' => [ '8.8 in', '6.8 in','-0.55 in', '-0.55 in','0.394 in'], + '2' => [ '4.8 in', '6.8 in','-0.5 in', '-1.0 in','3.5 in'] + }, + }, + 'legal' => { + 'book' => { + '1' => ['7.1 in','13 in',,'-0.57 in','-0.57 in','-0.5 in'], + '2' => ['3.66 in','13 in','-0.57 in','-0.57 in','-0.5 in'] + }, + 'album' => { + '1' => ['12 in','7.1 in',,'-0.57 in','-0.57 in','-0.5 in'], + '2' => ['6.0 in','7.1 in','-1 in','-1 in','5 in'] + }, + }, + 'tabloid' => { + 'book' => { + '1' => ['9.8 in','16 in','-0.57 in','-0.57 in','-0.5 in'], + '2' => ['4.9 in','16 in','-0.57 in','-0.57 in','-0.5 in'] + }, + 'album' => { + '1' => ['16 in','9.8 in','-0.57 in','-0.57 in','-0.5 in'], + '2' => ['16 in','4.9 in','-0.57 in','-0.57 in','-0.5 in'] + }, + }, + 'executive' => { + 'book' => { + '1' => ['6.8 in','9 in','-0.57 in','-0.57 in','1.2 in'], + '2' => ['3.1 in','9 in','-0.57 in','-0.57 in','1.2 in'] + }, + 'album' => { + '1' => [], + '2' => [] + }, + }, + 'a2' => { + 'book' => { + '1' => [], + '2' => [] + }, + 'album' => { + '1' => [], + '2' => [] + }, + }, + 'a3' => { + 'book' => { + '1' => [], + '2' => [] + }, + 'album' => { + '1' => [], + '2' => [] + }, + }, + 'a4' => { + 'book' => { + '1' => ['17.6 cm','27.2 cm','-1.397 cm','-2.11 cm','-1.27 cm'], + '2' => [ '9.1 cm','27.2 cm','-1.397 cm','-2.11 cm','-1.27 cm'] + }, + 'album' => { + '1' => ['21.59 cm','19.558 cm','-1.397cm','-2.11 cm','0 cm'], + '2' => ['9.91 cm','19.558 cm','-1.397 cm','-2.11 cm','0 cm'] + }, + }, + 'a5' => { + 'book' => { + '1' => [], + '2' => [] + }, + 'album' => { + '1' => [], + '2' => [] + }, + }, + 'a6' => { + 'book' => { + '1' => [], + '2' => [] + }, + 'album' => { + '1' => [], + '2' => [] + }, + }, + ); +sub page_format { # #Supported paper format: "Letter [8 1/2x11 in]", "Legal [8 1/2x14 in]", # "Ledger/Tabloid [11x17 in]", "Executive [7 1/2x10 in]", # "A2 [420x594 mm]", "A3 [297x420 mm]", # "A4 [210x297 mm]", "A5 [148x210 mm]", # "A6 [105x148 mm]" -# - my ( $papersize, $layout, $numberofcolumns ) = @_; - return @{ $page_formats{$papersize}->{$layout}->{$numberofcolumns} }; +# + my ($papersize,$layout,$numberofcolumns) = @_; + return @{$page_formats{$papersize}->{$layout}->{$numberofcolumns}}; } + sub get_name { - my ( $uname, $udom ) = @_; - if ( !defined($uname) ) { $uname = $env{'user.name'}; } - if ( !defined($udom) ) { $udom = $env{'user.domain'}; } - my $plainname = &Apache::loncommon::plainname( $uname, $udom ); - if ( $plainname =~ /^\s*$/ ) { $plainname = $uname . '@' . $udom; } - $plainname = &Apache::lonxml::latex_special_symbols( $plainname, 'header' ); + my ($uname,$udom)=@_; + if (!defined($uname)) { $uname=$env{'user.name'}; } + if (!defined($udom)) { $udom=$env{'user.domain'}; } + my $plainname=&Apache::loncommon::plainname($uname,$udom); + if ($plainname=~/^\s*$/) { $plainname=$uname.'@'.$udom; } + $plainname=&Apache::lonxml::latex_special_symbols($plainname,'header'); return $plainname; } 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'}; - + 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; } sub page_format_transformation { - my ( - $papersize, $layout, $numberofcolumns, - $choice, $text, $assignment, - $tableofcontents, $indexlist, $selectionmade - ) = @_; - my ( $textwidth, $textheight, $oddoffset, $evenoffset, $topmargin ); - - if ( $selectionmade eq '4' ) { - if ( $choice eq 'all_problems' ) { - $assignment = &mt('Problems from the Whole Course'); - } - else { - $assignment = &mt('Resources from the Whole Course'); - } - } - else { - $assignment = - &Apache::lonxml::latex_special_symbols( $assignment, 'header' ); + my ($papersize,$layout,$numberofcolumns,$choice,$text,$assignment,$tableofcontents,$indexlist,$selectionmade) = @_; + my ($textwidth,$textheight,$oddoffset,$evenoffset,$topmargin); + + if ($selectionmade eq '4') { + if ($choice eq 'all_problems') { + $assignment=&mt('Problems from the Whole Course'); + } else { + $assignment=&mt('Resources from the Whole Course'); + } + } else { + $assignment=&Apache::lonxml::latex_special_symbols($assignment,'header'); } - ( $textwidth, $textheight, $oddoffset, $evenoffset, $topmargin ) = - &page_format( $papersize, $layout, $numberofcolumns, $topmargin ); + ($textwidth,$textheight,$oddoffset,$evenoffset,$topmargin) = &page_format($papersize,$layout,$numberofcolumns,$topmargin); + - my $name = &get_name(); + my $name = &get_name(); my $courseidinfo = &get_course(); my $header_text = $parmhash{'print_header_format'}; - $header_text = - &format_page_header( $textwidth, $header_text, $assignment, $courseidinfo, - $name ); + $header_text = &format_page_header($textwidth, $header_text, $assignment, + $courseidinfo, $name); my $topmargintoinsert = ''; - if ( $topmargin ne '0' ) { - $topmargintoinsert = '\setlength{\topmargin}{' . $topmargin . '}'; - } - my $fancypagestatement = ''; - if ( $numberofcolumns eq '2' ) { - $fancypagestatement = "\\fancyhead{}\\fancyhead[LO]{$header_text}"; - } - else { - $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 /; - } - 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/; - } - 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 /; - } - 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/; - } - } - if ( $tableofcontents eq 'yes' ) { - $text =~ s/(\\setcounter\{page\}\{1\})/$1 \\tableofcontents\\newpage /; + if ($topmargin ne '0') {$topmargintoinsert='\setlength{\topmargin}{'.$topmargin.'}';} + my $fancypagestatement=''; + if ($numberofcolumns eq '2') { + $fancypagestatement="\\fancyhead{}\\fancyhead[LO]{$header_text}"; + } else { + $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 /; + } 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/; + } 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 /; + } + 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/; + } } - if ( $indexlist eq 'yes' ) { - $text =~ s/(\\begin{document})/\\makeindex $1/; - $text =~ s/(\\end{document})/\\strut\\\\\\strut\\printindex $1/; + 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/; } return $text; } -sub page_cleanup { - my $result = shift; +sub page_cleanup { + my $result = shift; + $result =~ m/\\end{document}(\d*)$/; my $number_of_columns = $1; - my $insert = '{'; - for ( my $id = 1 ; $id <= $number_of_columns ; $id++ ) { $insert .= 'l'; } + 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; + return $result,$number_of_columns; } + sub details_for_menu { - my ($helper) = @_; - my $postdata = $env{'form.postdata'}; - if ( !$postdata ) { $postdata = $helper->{VARS}{'postdata'}; } + my ($helper)=@_; + my $postdata=$env{'form.postdata'}; + if (!$postdata) { $postdata=$helper->{VARS}{'postdata'}; } my $name_of_resource = &Apache::lonnet::gettitle($postdata); - my $symbolic = &Apache::lonnet::symbread($postdata); - return if ( $symbolic eq '' ); + my $symbolic = &Apache::lonnet::symbread($postdata); + return if ( $symbolic eq ''); - my ( $map, $id, $resource ) = &Apache::lonnet::decode_symb($symbolic); - $map = &Apache::lonnet::clutter($map); + my ($map,$id,$resource)=&Apache::lonnet::decode_symb($symbolic); + $map=&Apache::lonnet::clutter($map); my $name_of_sequence = &Apache::lonnet::gettitle($map); - if ( $name_of_sequence =~ /^\s*$/ ) { - $map =~ m|([^/]+)$|; - $name_of_sequence = $1; - } - my $name_of_map = &Apache::lonnet::gettitle( $env{'request.course.uri'} ); - if ( $name_of_map =~ /^\s*$/ ) { - $env{'request.course.uri'} =~ m|([^/]+)$|; - $name_of_map = $1; + if ($name_of_sequence =~ /^\s*$/) { + $map =~ m|([^/]+)$|; + $name_of_sequence = $1; + } + my $name_of_map = &Apache::lonnet::gettitle($env{'request.course.uri'}); + if ($name_of_map =~ /^\s*$/) { + $env{'request.course.uri'} =~ m|([^/]+)$|; + $name_of_map = $1; } - return ( $name_of_resource, $name_of_sequence, $name_of_map ); + return ($name_of_resource,$name_of_sequence,$name_of_map); } sub copyright_line { - return -'\noindent\makebox[\textwidth/$number_of_columns][b]{\hrulefill}\vspace*{-2 mm}\newline\noindent{\tiny Printed from LON-CAPA\copyright MSU{\hfill} Licensed under GNU General Public License } '; + return '\noindent\makebox[\textwidth/$number_of_columns][b]{\hrulefill}\vspace*{-2 mm}\newline\noindent{\tiny Printed from LON-CAPA\copyright MSU{\hfill} Licensed under GNU General Public License } '; } -my $end_of_student = "\n" . '\special{ps:ENDOFSTUDENTSTAMP}' . "\n"; +my $end_of_student = "\n".'\special{ps:ENDOFSTUDENTSTAMP}'."\n"; sub latex_corrections { - my ( $number_of_columns, $result, $selectionmade, $answer_mode ) = @_; - + my ($number_of_columns,$result,$selectionmade,$answer_mode) = @_; # $result =~ s/\\includegraphics{/\\includegraphics\[width=\\minipagewidth\]{/g; my $copyright = ©right_line(); - if ( $selectionmade eq '1' || $answer_mode eq 'only' ) { - $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/; + if ($selectionmade eq '1' || $answer_mode eq 'only') { + $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/\$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\\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, '', $first_comment ); - substr( $result, $first_comment, $end_comment - $first_comment + 3 ) = - ''; - $first_comment = index( $result, '',$first_comment); + substr($result,$first_comment,$end_comment-$first_comment+3) = ''; + $first_comment = index($result,'