You can subscribe to this list here.
| 2004 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
(58) |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2005 |
Jan
(53) |
Feb
(56) |
Mar
|
Apr
|
May
(30) |
Jun
(78) |
Jul
(121) |
Aug
(155) |
Sep
(77) |
Oct
(61) |
Nov
(45) |
Dec
(94) |
| 2006 |
Jan
(116) |
Feb
(33) |
Mar
(11) |
Apr
(23) |
May
(60) |
Jun
(89) |
Jul
(130) |
Aug
(109) |
Sep
(124) |
Oct
(63) |
Nov
(82) |
Dec
(45) |
| 2007 |
Jan
(31) |
Feb
(35) |
Mar
(123) |
Apr
(36) |
May
(18) |
Jun
(134) |
Jul
(133) |
Aug
(241) |
Sep
(126) |
Oct
(31) |
Nov
(15) |
Dec
(5) |
| 2008 |
Jan
(11) |
Feb
(6) |
Mar
(16) |
Apr
(29) |
May
(43) |
Jun
(149) |
Jul
(27) |
Aug
(29) |
Sep
(37) |
Oct
(20) |
Nov
(4) |
Dec
(6) |
| 2009 |
Jan
(34) |
Feb
(30) |
Mar
(16) |
Apr
(6) |
May
(1) |
Jun
(32) |
Jul
(22) |
Aug
(7) |
Sep
(18) |
Oct
(50) |
Nov
(22) |
Dec
(8) |
| 2010 |
Jan
(17) |
Feb
(15) |
Mar
(10) |
Apr
(9) |
May
(67) |
Jun
(30) |
Jul
|
Aug
|
Sep
(2) |
Oct
|
Nov
(1) |
Dec
|
|
From: Mike G. v. a. <we...@ma...> - 2010-05-14 12:02:42
|
Log Message:
-----------
major update which adds objective methods to the basic code of PG.
HEAD should be considered more beta than usual for a few days until minor glitches
are shaken out.
new modules needed:
PGcore PGalias PGresource PGloadfiles PGanswergroup PGresponsegroup Tie::IxHash
Modified Files:
--------------
pg/lib/WeBWorK/PG:
Translator.pm
Added Files:
-----------
pg/lib:
PGalias.pm
PGanswergroup.pm
PGcore.pm
PGloadfiles.pm
PGresponsegroup.pm
Revision Data
-------------
--- /dev/null
+++ lib/PGcore.pm
@@ -0,0 +1,714 @@
+################################################################################
+# WeBWorK Online Homework Delivery System
+# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/
+# $CVSHeader: pg/lib/PGcore.pm,v 1.1 2010/05/14 11:39:02 gage Exp $
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of either: (a) the GNU General Public License as published by the
+# Free Software Foundation; either version 2, or (at your option) any later
+# version, or (b) the "Artistic License" which comes with this package.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
+# Artistic License for more details.
+################################################################################
+package PGcore;
+
+use strict;
+BEGIN {
+ use Exporter;
+ our @EXPORT_OK = qw( not_null);
+}
+our $internal_debug_messages = [];
+
+use PGanswergroup;
+use PGresponsegroup;
+use PGrandom;
+use PGalias;
+use PGloadfiles;
+use WeBWorK::PG::IO;
+use Tie::IxHash;
+
+##################################
+# Utility macro
+##################################
+
+sub not_null { # empty arrays, empty hashes and strings containing only whitespace are all NULL
+ my $item = shift;
+ return 0 unless defined($item);
+ if (ref($item)=~/ARRAY/) {
+ return scalar(@{$item}); # return the length
+ } elsif (ref($item)=~/HASH/) {
+ return scalar( keys %{$item});
+ } else { # string case return 1 if none empty
+ return ($item =~ /\S/)? 1:0;
+ }
+}
+
+##################################
+# PGcore object
+##################################
+
+sub new {
+ my $class = shift;
+ my $envir = shift; #pointer to environment hash
+ warn "PGcore must be called with an environment" unless ref($envir) eq 'HASH';
+ #warn "creating a new PGcore object";
+ my %options = @_;
+ my $self = {
+ OUTPUT_ARRAY => [], # holds output body text
+ HEADER_ARRAY => [], # holds output for the header text
+# PG_ANSWERS => [], # holds answers with labels
+# PG_UNLABELED_ANSWERS => [], # holds
+ PG_ANSWERS_HASH => {}, # holds label=>answer pairs
+ PERSISTENCE_HASH => {}, # holds other data, besides answers, which persists during a session and beyond
+# PG_persistence_hash => {}, # stores information (other than answers) from one session to another
+ answer_eval_count => 0,
+ answer_blank_count => 0,
+ unlabeled_answer_blank_count =>0,
+ unlabeled_answer_eval_count => 0,
+ KEPT_EXTRA_ANSWERS => [],
+ ANSWER_PREFIX => 'AnSwEr',
+ ARRAY_PREFIX => 'ArRaY',
+ vec_num => 0, # for distinguishing matrices
+ QUIZ_PREFIX => '',
+ SECTION_PREFIX => '', # might be used for sequential (compound) questions?
+
+ PG_ACTIVE => 1, # turn to zero to stop processing
+ submittedAnswers => 0, # have any answers been submitted? is this the first time this session?
+ PG_session_persistence_hash =>{}, # stores data from one invoction of the session to the next.
+ PG_original_problem_seed => 0,
+ PG_random_generator => undef,
+ PG_alias => undef,
+ PG_problem_grader => undef,
+ displayMode => undef,
+ envir => $envir,
+ gifs_created => {},
+ external_refs => {}, # record of external references
+ %options, # allows overrides and initialization
+ };
+ bless $self, $class;
+ tie %{$self->{PG_ANSWERS_HASH}}, "Tie::IxHash"; # creates a Hash with order
+ $self->initialize;
+ return $self;
+}
+
+sub initialize {
+ my $self = shift;
+ warn "environment is not defined in PGcore" unless ref($self->{envir}) eq 'HASH';
+
+
+
+
+ $self->{displayMode} = $self->{envir}->{displayMode};
+ $self->{PG_original_problem_seed} = $self->{envir}->{problemSeed};
+ $self->{PG_random_generator} = new PGrandom( $self->{PG_original_problem_seed});
+
+ $self->{tempDirectory} = $self->{envir}->{tempDirectory};
+ $self->{PG_problem_grader} = $self->{envir}->{PROBLEM_GRADER_TO_USE};
+ $self->{PG_alias} = new PGalias($self->{envir});
+ $self->{PG_loadMacros} = new PGloadfiles($self->{envir});
+ $self->{PG_FLAGS} = {
+ showpartialCorrectAnswers => 1,
+ showHint => 1,
+ hintExists => 0,
+ showHintLimit => 0,
+ solutionExists => 0,
+ WARNING_messages => [],
+ DEBUG_messages => [],
+ recordSubmittedAnswers => 1,
+ refreshCAchedImages => 0,
+# ANSWER_ENTRY_ORDER => [], # may not be needed if we ue Tie:IxHash
+ comment => '', # implement as array?
+
+
+
+ };
+
+}
+
+
+####################################################################
+
+=head1 DESCRIPTION
+
+This file provides the fundamental macros that define the PG language. It
+maintains a problem's text, header text, and answers:
+
+=over
+
+=item *
+
+Problem text: The text to appear in the body of the problem. See TEXT()
+below.
+
+=item *
+
+Header text: When a problem is processed in an HTML-based display mode,
+this variable can contain text that the caller should place in the HEAD of the
+resulting HTML page. See HEADER_TEXT() below.
+
+=item *
+
+Implicitly-labeled answers: Answers that have not been explicitly
+assigned names, and are associated with their answer blanks by the order in
+which they appear in the problem. These types of answers are designated using
+the ANS() macro.
+
+=item *
+
+Explicitly-labeled answers: Answers that have been explicitly assigned
+names with the LABELED_ANS() macro, or a macro that uses it. An explicitly-
+labeled answer is associated with its answer blank by name.
+
+=item *
+
+"Extra" answers: Names of answer blanks that do not have a 1-to-1
+correspondance to an answer evaluator. For example, in matrix problems, there
+will be several input fields that correspond to the same answer evaluator.
+
+=back
+
+=head1 USAGE
+
+This file is automatically loaded into the namespace of every PG problem. The
+macros within can then be called to define the structure of the problem.
+
+DOCUMENT() should be the first executable statement in any problem. It
+initializes vriables and defines the problem environment.
+
+ENDDOCUMENT() must be the last executable statement in any problem. It packs
+up the results of problem processing for delivery back to WeBWorK.
+
+The HEADER_TEXT(), TEXT(), and ANS() macros add to the header text string,
+body text string, and answer evaluator queue, respectively.
+
+=cut
+
+
+=item HEADER_TEXT()
+
+ HEADER_TEXT("string1", "string2", "string3");
+
+HEADER_TEXT() concatenates its arguments and appends them to the stored header
+text string. It can be used more than once in a file.
+
+The macro is used for material which is destined to be placed in the HEAD of
+the page when in HTML mode, such as JavaScript code.
+
+Spaces are placed between the arguments during concatenation, but no spaces are
+introduced between the existing content of the header text string and the new
+content being appended.
+
+=cut
+
+# ^function HEADER_TEXT
+# ^uses $STRINGforHEADER_TEXT
+sub HEADER_TEXT {
+ my $self = shift;
+ push @{$self->{HEADER_ARRAY}}, map { (defined($_) )?$_:'' } @_;
+ $self->{HEADER_ARRAY} ;
+}
+
+=item TEXT()
+
+ TEXT("string1", "string2", "string3");
+
+TEXT() concatenates its arguments and appends them to the stored problem text
+string. It is used to define the text which will appear in the body of the
+problem. It can be used more than once in a file.
+
+This macro has no effect if rendering has been stopped with the STOP_RENDERING()
+macro.
+
+This macro defines text which will appear in the problem. All text must be
+passed to this macro, passed to another macro that calls this macro, or included
+in a BEGIN_TEXT/END_TEXT block, which uses this macro internally. No other
+statements in a PG file will directly appear in the output. Think of this as the
+"print" function for the PG language.
+
+Spaces are placed between the arguments during concatenation, but no spaces are
+introduced between the existing content of the header text string and the new
+content being appended.
+
+=cut
+
+# ^function TEXT
+# ^uses $PG_STOP_FLAG
+# ^uses $STRINGforOUTPUT
+
+sub TEXT {
+ my $self = shift; #FIXME filter for undefined entries replace by "";
+ push @{$self->{OUTPUT_ARRAY}}, map { (defined($_) )?$_:'' } @_ ;
+ $self->{OUTPUT_ARRAY};
+}
+
+
+=item LABELED_ANS()
+
+ TEXT(labeled_ans_rule("name1"), labeled_ans_rule("name2"));
+ LABELED_ANS(name1 => answer_evaluator1, name2 => answer_evaluator2);
+
+Adds the answer evaluators listed to the list of labeled answer evaluators.
+They will be paired with labeled answer rules (a.k.a. answer blanks) in the
+order entered. This allows pairing of answer evaluators and answer rules that
+may not have been entered in the same order.
+
+=cut
+
+# ^function NAMED_ANS
+# ^uses &LABELED_ANS
+sub NAMED_ANS {
+ &LABELED_ANS;
+}
+
+=item NAMED_ANS()
+
+Old name for LABELED_ANS(). DEPRECATED.
+
+=cut
+
+# ^function NAMED_ANS
+# ^uses $PG_STOP_FLAG
+sub LABELED_ANS{
+ my $self = shift;
+ my @in = @_;
+ while (@in ) {
+ my $label = shift @in;
+ $label = join("", $self->{QUIZ_PREFIX}, $self->{SECTION_PREFIX}, $label);
+ my $ans_eval = shift @in;
+ $self->WARN("<BR><B>Error in LABELED_ANS:|$label|</B>
+ -- inputs must be references to AnswerEvaluator objects or subroutines<BR>")
+ unless ref($ans_eval) =~ /CODE/ or ref($ans_eval) =~ /AnswerEvaluator/ ;
+ if (defined($self->{PG_ANSWERS_HASH}->{$label}) ){
+ $self->{PG_ANSWERS_HASH}->{$label}->insert(ans_label => $label, ans_eval => $ans_eval, active=>$self->{PG_ACTIVE});
+ } else {
+ $self->{PG_ANSWERS_HASH}->{$label} = PGanswergroup->new($label, ans_eval => $ans_eval, active=>$self->{PG_ACTIVE});
+ }
+ $self->{answer_eval_count}++;
+ }
+ $self->{PG_ANSWERS_HASH};
+}
+
+
+=item ANS()
+
+ TEXT(ans_rule(), ans_rule(), ans_rule());
+ ANS($answer_evaluator1, $answer_evaluator2, $answer_evaluator3);
+
+Adds the answer evaluators listed to the list of unlabeled answer evaluators.
+They will be paired with unlabeled answer rules (a.k.a. answer blanks) in the
+order entered. This is the standard method for entering answers.
+
+In the above example, answer_evaluator1 will be associated with the first
+answer rule, answer_evaluator2 with the second, and answer_evaluator3 with the
+third. In practice, the arguments to ANS() will usually be calls to an answer
+evaluator generator such as the cmp() method of MathObjects or the num_cmp()
+macro in L<PGanswermacros.pl>.
+
+=cut
+
+# ^function ANS
+# ^uses $PG_STOP_FLAG
+# ^uses @PG_ANSWERS
+
+sub ANS{
+ my $self = shift;
+ my @in = @_;
+ while (@in ) {
+ # create new label
+ $self->{unlabeled_answer_eval_count}++;
+ my $label = $self->new_label($self->{unlabeled_answer_eval_count});
+ my $evaluator = shift @in;
+ $self->LABELED_ANS($label, $evaluator);
+ }
+ $self->{PG_ANSWERS_HASH};
+}
+
+
+
+
+=item STOP_RENDERING()
+
+ STOP_RENDERING() unless all_answers_are_correct();
+
+Temporarily suspends accumulation of problem text and storing of answer blanks
+and answer evaluators until RESUME_RENDERING() is called.
+
+=cut
+
+# ^function STOP_RENDERING
+# ^uses $PG_STOP_FLAG
+sub STOP_RENDERING {
+ my $self = shift;
+ $self->{PG_ACTIVE}=0;
+ "";
+}
+
+=item RESUME_RENDERING()
+
+ RESUME_RENDERING();
+
+Resumes accumulating problem text and storing answer blanks and answer
+evaluators. Reverses the effect of STOP_RENDERING().
+
+=cut
+
+# ^function RESUME_RENDERING
+# ^uses $PG_STOP_FLAG
+sub RESUME_RENDERING {
+ my $self = shift;
+ $self->{PG_ACTIVE}=1;
+ "";
+}
+########
+# Internal methods
+#########
+sub new_label { #creates a new label for unlabeled submissions ASNWER_PREFIX.$number
+ my $self = shift;
+ my $number = shift;
+ $self->{QUIZ_PREFIX}.$self->{ANSWER_PREFIX}.sprintf("%04u", $number);
+}
+sub new_array_label { #creates a new label for unlabeled submissions ASNWER_PREFIX.$number
+ my $self = shift;
+ my $number = shift;
+ $self->{QUIZ_PREFIX}.$self->{ARRAY_PREFIX}.sprintf("%04u", $number);
+}
+sub new_array_element_label { #creates a new label for unlabeled submissions ARRAY_PREFIX.$number
+ my $self = shift;
+ my $ans_label = shift; # name of the PGanswer group holding this array
+ my $row_num = shift;
+ my $col_num = shift;
+ my %options = @_;
+ my $vec_num = (defined $options{vec_num})?$options{vec_num}: 0 ;
+ $self->{QUIZ_PREFIX}.$ans_label.'__'.$vec_num.':'.$row_num.':'.$col_num.'__';
+}
+sub new_answer_name { # bit of a legacy item
+ &new_label;
+}
+
+
+sub record_ans_name { # the labels in the PGanswer group and response group should match in this case
+ my $self = shift;
+ my $label = shift;
+ my $value = shift;
+ $self->internal_debug_message("record_ans_name $label $value");
+ my $response_group = new PGresponsegroup($label,$label,$value);
+ if (defined($self->{PG_ANSWERS_HASH}->{$label}) ) {
+ $self->{PG_ANSWERS_HASH}->{$label}->replace(ans_label => $label,
+ response => $response_group,
+ active => $self->{PG_ACTIVE});
+ } else {
+ $self->{PG_ANSWERS_HASH}->{$label} = PGanswergroup->new($label,
+ response => $response_group,
+ active => $self->{PG_ACTIVE});
+ }
+ $self->{answer_blank_count}++;
+ $label;
+}
+
+sub record_array_name { # currently the same as record ans group
+ my $self = shift;
+ my $label = shift;
+ my $value = shift;
+ my $response_group = new PGresponsegroup($label,$label,$value);
+ if (defined($self->{PG_ANSWERS_HASH}->{$label}) ) {
+ $self->{PG_ANSWERS_HASH}->{$label}->replace(ans_label => $label,
+ response => $response_group,
+ active => $self->{PG_ACTIVE});
+ } else {
+ $self->{PG_ANSWERS_HASH}->{$label} = PGanswergroup->new($label,
+ response => $response_group,
+ active => $self->{PG_ACTIVE});
+ }
+ $self->{answer_blank_count}++;
+ #$self->{PG_ANSWERS_HASH}->{$label}->{response}->clear; #why is this ?
+ $label;
+
+}
+sub extend_ans_group { # modifies the group type
+ my $self = shift;
+ my $label = shift;
+ my @response_list = @_;
+ my $answer_group = $self->{PG_ANSWERS_HASH}->{$label};
+ if (ref($answer_group) =~/PGanswergroup/) {
+ $answer_group->append_responses(@response_list);
+ } else {
+ $self->WARN("The answer |$label| has not yet been defined, you cannot extend it.",caller() );
+
+ }
+ $label;
+}
+sub record_unlabeled_ans_name {
+ my $self = shift;
+ $self->{unlabeled_answer_blank_count}++;
+ my $label = $self->new_label($self->{unlabeled_answer_blank_count});
+ $self->record_ans_name($label);
+ $label;
+}
+sub record_unlabeled_array_name {
+ my $self = shift;
+ $self->{unlabeled_answer_blank_count}++;
+ my $ans_label = $self->new_array_label($self->{unlabeled_answer_blank_count});
+ $self->record_array_name($ans_label);
+}
+sub store_persistent_data { # will store strings only (so far)
+ my $self = shift;
+ my $label = shift;
+ my @content = @_;
+ $self->internal_debug_message("storing $label in PERSISTENCE_HASH");
+ if (defined($self->{PERSISTENCE_HASH}->{$label}) ) {
+ warn "can' overwrite $label in persistent data";
+ } else {
+ $self->{PERSISTENCE_HASH}->{$label} = join("",@content); #need base64 encoding?
+ }
+ $label;
+}
+sub check_answer_hash {
+ my $self = shift;
+ foreach my $key (keys %{ $self->{PG_ANSWERS_HASH} }) {
+ my $ans_eval = $self->{PG_ANSWERS_HASH}->{$key}->{ans_eval};
+ unless (ref($ans_eval) =~ /CODE/ or ref($ans_eval) =~ /AnswerEvaluator/ ) {
+ warn "The answer group labeled $key is missing an answer evaluator";
+ }
+ unless (ref( $self->{PG_ANSWERS_HASH}->{$key}->{response} ) =~ /PGresponsegroup/ ) {
+ warn "The answer group labeled $key is missing answer blanks ";
+ }
+ }
+}
+
+sub PG_restricted_eval {
+ my $self = shift;
+ WeBWorK::PG::Translator::PG_restricted_eval(@_);
+}
+
+# sub AUTOLOAD {
+# my $self = shift;
+#
+# my $type = ref($self) or die "$self is not an object";
+#
+# # $AUTOLOAD is sent in by Perl and is the full name of the object (i.e. main::blah::blah_more)
+# my $name = $PGcore::AUTOLOAD;
+# $name =~ s/.*://; #strips fully-qualified portion
+#
+# unless ( exists $self->{'_permitted'}->{$name} ) { die "Can't find '$name' field in object of class '$type'";}
+#
+# if (@_) {
+# return $self->{$name} = shift; #set the variable to the first parameter
+# } else {
+# return $self->($name); #if no parameters just return the value
+# }
+# }
+
+sub append_debug_message {
+ my $self = shift;
+ my @str = @_;
+ push @{$self->{DEBUG_messages}}, @str;
+}
+sub get_debug_messages {
+ my $self = shift;
+ $self->{DEBUG_messages};
+}
+sub DESTROY {
+ # doing nothing about destruction, hope that isn't dangerous
+}
+
+sub WARN {
+ warn(@_);
+}
+
+
+# This creates on the fly graphs
+
+=head2 insertGraph
+
+ # returns a path to the file containing the graph image.
+ $filePath = insertGraph($graphObject);
+
+insertGraph writes a GIF or PNG image file to the gif subdirectory of the
+current course's HTML temp directory. The file name is obtained from the graph
+object. Warnings are issued if errors occur while writing to the file.
+
+Returns a string containing the full path to the temporary file containing the
+image. This is most often used in the construct
+
+ TEXT(alias(insertGraph($graph)));
+
+where alias converts the directory address to a URL when serving HTML pages and
+insures that an EPS file is generated when creating TeX code for downloading.
+
+=cut
+
+# ^function insertGraph
+# ^uses $WWPlot::use_png
+# ^uses convertPath
+# ^uses surePathToTmpFile
+# ^uses PG_restricted_eval
+# ^uses $refreshCachedImages
+# ^uses $templateDirectory
+# ^uses %envir
+sub insertGraph {
+ # Convert the image to GIF and print it on standard output
+ my $self = shift;
+ my $graph = shift;
+ my $extension = ($WWPlot::use_png) ? '.png' : '.gif';
+ my $fileName = $graph->imageName . $extension;
+ my $filePath = $self->convertPath("gif/$fileName");
+ my $templateDirectory = $self->{envir}->{templateDirectory};
+ $filePath = $self->surePathToTmpFile( $filePath );
+ my $refreshCachedImages = $self->PG_restricted_eval(q!$refreshCachedImages!);
+ # Check to see if we already have this graph, or if we have to make it
+ if( not -e $filePath # does it exist?
+ or ((stat "$templateDirectory"."$main::envir{fileName}")[9] > (stat $filePath)[9]) # source has changed
+ or $graph->imageName =~ /Undefined_Set/ # problems from SetMaker and its ilk should always be redone
+ or $refreshCachedImages
+ ) {
+ #createFile($filePath, $main::tmp_file_permission, $main::numericalGroupID);
+ local(*OUTPUT); # create local file handle so it won't overwrite other open files.
+ open(OUTPUT, ">$filePath")||warn ("$0","Can't open $filePath<BR>","");
+ chmod( 0777, $filePath);
+ print OUTPUT $graph->draw|| warn("$0","Can't print graph to $filePath<BR>","");
+ close(OUTPUT)||warn("$0","Can't close $filePath<BR>","");
+ }
+ $filePath;
+}
+
+=head1 Macros from IO.pm
+
+ includePGtext
+ read_whole_problem_file
+ read_whole_file
+ convertPath
+ getDirDelim
+ fileFromPath
+ directoryFromPath
+ createFile
+ createDirectory
+
+=cut
+
+sub includePGtext {
+ my $self = shift;
+ WeBWorK::PG::IO::includePGtext(@_);
+ };
+sub read_whole_problem_file {
+ my $self = shift;
+ WeBWorK::PG::IO::read_whole_problem_file(@_);
+ };
+sub read_whole_file {
+ my $self = shift;
+ WeBWorK::PG::IO::read_whole_file(@_);
+ };
+sub convertPath {
+ my $self = shift;
+ WeBWorK::PG::IO::convertPath(@_);
+ };
+sub getDirDelim {
+ my $self = shift;
+ WeBWorK::PG::IO::getDirDelim(@_);
+ };
+sub fileFromPath {
+ my $self = shift;
+ WeBWorK::PG::IO::fileFromPath(@_);
+ };
+sub directoryFromPath {
+ my $self = shift;
+ WeBWorK::PG::IO::directoryFromPath(@_);
+ };
+sub createFile {
+ my $self = shift;
+ WeBWorK::PG::IO::createFile(@_);
+ };
+sub createDirectory {
+ my $self = shift;
+ WeBWorK::PG::IO::createDirectory(@_);
+ };
+
+sub tempDirectory {
+ my $self = shift;
+ return $self->{tempDirectory};
+}
+
+
+=head2 surePathToTmpFile
+
+ $path = surePathToTmpFile($path);
+
+Creates all of the intermediate directories between the tempDirectory
+
+If $path begins with the tempDirectory path, then the
+path is treated as absolute. Otherwise, the path is treated as relative the the
+course temp directory.
+
+=cut
+
+# A very useful macro for making sure that all of the directories to a file have been constructed.
+
+# ^function surePathToTmpFile
+# ^uses getCourseTempDirectory
+# ^uses createDirectory
+
+
+sub surePathToTmpFile {
+ # constructs intermediate directories if needed beginning at ${Global::htmlDirectory}tmp/
+ # the input path must be either the full path, or the path relative to this tmp sub directory
+
+ my $self = shift;
+ my $path = shift;
+ my $delim = "/";
+ my $tmpDirectory = $self->tempDirectory();
+ unless ( -e $tmpDirectory) { # if by some unlucky chance the tmpDirectory hasn't been created, create it.
+ my $parentDirectory = $tmpDirectory;
+ $parentDirectory =~s|/$||; # remove a trailing /
+ $parentDirectory =~s|/\w*$||; # remove last node
+ my ($perms, $groupID) = (stat $parentDirectory)[2,5];
+ createDirectory($tmpDirectory, $perms, $groupID)
+ or warn "Failed to create directory at $path";
+
+ }
+ # use the permissions/group on the temp directory itself as a template
+ my ($perms, $groupID) = (stat $tmpDirectory)[2,5];
+ #warn "&urePathToTmpFile: perms=$perms groupID=$groupID\n";
+
+ # if the path starts with $tmpDirectory (which is permitted but optional) remove this initial segment
+ $path =~ s|^$tmpDirectory|| if $path =~ m|^$tmpDirectory|;
+ #$path = convertPath($path);
+
+ # find the nodes on the given path
+ my @nodes = split("$delim",$path);
+
+ # create new path
+ $path = $tmpDirectory; #convertPath("$tmpDirectory");
+
+ while (@nodes>1) {
+ $path = $path . shift (@nodes) . "/"; #convertPath($path . shift (@nodes) . "/");
+ unless (-e $path) {
+ #system("mkdir $path");
+ #createDirectory($path,$Global::tmp_directory_permission, $Global::numericalGroupID)
+ createDirectory($path, $perms, $groupID)
+ or warn "Failed to create directory at $path";
+ }
+
+ }
+
+ $path = $path . shift(@nodes); #convertPath($path . shift(@nodes));
+ #system(qq!echo "" > $path! );
+ return $path;
+}
+
+sub internal_debug_message {
+ my $self = shift;
+ my @str = @_;
+ push @{$internal_debug_messages}, @str;
+}
+sub get_internal_debug_messages {
+ my $self = shift;
+ $internal_debug_messages;
+}
+sub clear_internal_debug_messages {
+ my $self = shift;
+ $internal_debug_messages=[];
+}
+
+1;
\ No newline at end of file
--- /dev/null
+++ lib/PGalias.pm
@@ -0,0 +1,776 @@
+################################################################################
+# WeBWorK Online Homework Delivery System
+# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/
+# $CVSHeader: pg/lib/PGalias.pm,v 1.1 2010/05/14 11:39:02 gage Exp $
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of either: (a) the GNU General Public License as published by the
+# Free Software Foundation; either version 2, or (at your option) any later
+# version, or (b) the "Artistic License" which comes with this package.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
+# Artistic License for more details.
+################################################################################
+
+package PGresource;
+use strict;
+use Exporter;
+use PGcore;
+
+sub new {
+ my $class = shift;
+ my $aux_file_name = shift; #pointer to auxiliary fle
+ my $self = {
+ type => 'png', # gif eps pdf html pg (macro: pl) (applets: java js fla geogebra )
+ path => { content => undef,
+ is_complete=>0,
+ is_accessible => 0,
+ },
+ url => { content => undef,
+ is_complete=>0,
+ is_accessible => 0,
+ },
+ return_uri => '',
+ recorded_uri => '',
+ convert => { needed => 0,
+ from_type => undef,
+ from_path => undef,
+ to_type => undef,
+ to_path => undef,
+ },
+ copy_link => { type => undef, # copy or link or ??
+ link_to_path => undef,
+ copy_to_path => undef,
+ },
+ cache_info => {},
+ unique_id => undef,
+ };
+ bless $self, $class;
+ # $self->initialize;
+ # $self->check_parameters;
+ return $self;
+}
+
+package PGalias;
+use strict;
+use Exporter;
+use PGcore;
+use WeBWorK::PG::IO;
+
+our @ISA = ( qw ( PGcore ) ); # look up features in PGcore -- in this case we want the environment.
+
+# new
+# Create one alias object per question (and per PGcore object)
+# Check that information is intact
+# Construct unique id stubs
+# Keep list of external links
+sub new {
+ my $class = shift;
+ my $envir = shift; #pointer to environment hash
+ warn "PGlias must be called with an environment" unless ref($envir) eq 'HASH';
+ my $self = {
+ envir => $envir,
+ searchList => [{url=>'foo',dir=>'.'}], # for subclasses -> list of url/directories to search
+ resourceList => {},
+
+ };
+ bless $self, $class;
+ $self->initialize;
+ $self->check_parameters;
+ return $self;
+}
+
+# methods
+# make_alias -- outputs url and does what needs to be done
+# normalize paths (remove extra precursors to the path)
+# search directories for item
+# make_links -- in those cases where links need to be made
+# create_files -- e.g. when printing hardcopy
+# dispatcher -- decides what needs to be done based on displayMode and file type
+# alias_for_html
+# alias_for_image_in_html image includes gif, png, jpg, swf, svg, flv?? ogg??
+# alias_for_image_in_tex
+
+
+sub initialize {
+ my $self = shift;
+ my $envir = $self->{envir};
+ # warn "envir-- ", join(" ", %$envir);
+ $self->{fileName} = $envir->{probFileName};
+ $self->{htmlDirectory} = $envir->{htmlDirectory};
+ $self->{htmlURL} = $envir->{htmlURL};
+ $self->{tempDirectory} = $envir->{tempDirectory};
+ $self->{templateDirectory} = $envir->{templateDirectory};
+ $self->{tempURL} = $envir->{tempURL};
+ $self->{studentLogin} = $envir->{studentLogin};
+ $self->{psvnNumber} = $envir->{psvnNumber};
+ $self->{setNumber} = $envir->{setNumber};
+ $self->{probNum} = $envir->{probNum};
+ $self->{displayMode} = $envir->{displayMode};
+ $self->{externalGif2EpsPath} = $envir->{externalGif2EpsPath};
+ $self->{externalPng2EpsPath} = $envir->{externalPng2EpsPath};
+ #
+ # Find auxiliary files even when the main file is in tempates/tmpEdit
+ #
+ $self->{fileName} =~ s!(^|/)tmpEdit/!$1!;
+
+ $self->{ext} = "";
+
+ # create uniqeID stub "gif/uniqIDstub-filePath"
+ $self->{uniqIDstub} = join("-",
+ $self->{studentLogin},
+ $self->{psvnNumber},
+ 'set'.$self->{setNumber},
+ 'prob'.$self->{probNum}
+ );
+
+
+}
+
+sub check_parameters {
+ my $self = shift;
+
+ # problem specific data
+ warn "The path to the current problem file template is not defined." unless $self->{fileName};
+ warn "The current studentLogin is not defined " unless $self->{studentLogin};
+ warn "The current problem set number is not defined" if $self->{setNumber} eq ""; # allow for sets equal to 0
+ warn "The current problem number is not defined" if $self->{probNum} eq "";
+ warn "The current problem set version number (psvn) is not defined" unless defined($self->{psvnNumber});
+ warn "The displayMode is not defined" unless $self->{displayMode};
+
+ # required macros
+# warn "The macro &surePathToTmpFile can't be found" unless defined(&{$self->surePathToTmpFile()} );
+# warn "The macro &convertPath can't be found" unless defined(&{$self->convertPath()});
+# warn "The macro &directoryFromPath can't be found" unless defined(&{$self->directoryFromPath()});
+# warn $self->surePathToTmpFile("foo");
+ # warn "The webwork server does not have permission to execute the gif2eps script at ${externalGif2EpsPath}." unless ( -x "${externalGif2EpsPath}" );
+ # warn "The webwork server does not have permission to execute the png2eps script at ${externalPng2EpsPath}." unless ( -x "${externalPng2EpsPath}" );
+
+ # required directory addresses (and URL address)
+ warn "htmlDirectory is not defined." unless $self->{htmlDirectory};
+ warn "htmlURL is not defined." unless $self->{htmlURL};
+ warn "tempURL is not defined." unless $self->{tempURL};
+}
+
+sub make_alias {
+ my $self = shift;
+ # input is a path to the original auxiliary file
+ my $aux_file_path = shift @_;
+ my $resource_alias = new PGresource($aux_file_path); # just call it alias? FIXME -- not in use yet.
+
+ # warn "make alias for $aux_file_path";
+ warn "Empty string used as input into the function alias" unless $aux_file_path;
+
+ my $displayMode = $self->{displayMode};
+ my $fileName = $self->{fileName}; # name of .pg file
+ my $envir = $self->{envir};
+ my $htmlDirectory = $envir->{htmlDirectory};
+ my $htmlURL = $envir->{htmlURL};
+ my $tempDirectory = $envir->{tempDirectory};
+ my $tempURL = $envir->{tempURL};
+ my $studentLogin = $envir->{studentLogin};
+ my $psvnNumber = $envir->{psvnNumber};
+ my $setNumber = $envir->{setNumber};
+ my $probNum = $envir->{probNum};
+ my $externalGif2EpsPath = $envir->{externalGif2EpsPath};
+ my $externalPng2EpsPath = $envir->{externalPng2EpsPath};
+
+ my $templateDirectory = $self->{templateDirectory};
+
+ # $adr_output is a url in HTML and Latex2HTML modes
+ # and a complete path in TEX mode.
+ my $adr_output;
+ my $ext;
+
+ # determine file type
+ # determine display mode
+ # dispatch
+
+ # determine extension, if there is one
+ # if extension exists, strip and use the value for $ext
+ # files without extensions are considered to be picture files:
+
+
+ if ($aux_file_path =~ s/\.([^\.]*)$// ) {
+ $ext = $1;
+ } else {
+ warn "This file name $aux_file_path did not have an extension.<BR> " .
+ "Every file name used as an argument to alias must have an extension.<BR> " .
+ "The permissable extensions are .gif, .png, and .html .<BR>";
+ $ext = "gif";
+ }
+
+
+ # in order to facilitate maintenance of this macro the routines for handling
+ # different file types are defined separately. This involves some redundancy
+ # in the code but it makes it easier to define special handling for a new file
+ # type, (but harder to change the behavior for all of the file types at once
+ # (sigh) ).
+
+ if ($ext eq 'html') {
+ $adr_output = $self->alias_for_html($aux_file_path)
+ } elsif ($ext eq 'gif') {
+ if ( $displayMode eq 'HTML' ||
+ $displayMode eq 'HTML_tth'||
+ $displayMode eq 'HTML_dpng'||
+ $displayMode eq 'HTML_asciimath'||
+ $displayMode eq 'HTML_LaTeXMathML'||
+ $displayMode eq 'HTML_jsMath'||
+ $displayMode eq 'HTML_img') {
+ ################################################################################
+ # .gif FILES in HTML; HTML_tth; HTML_dpng; HTML_img; and Latex2HTML modes
+ ################################################################################
+ $adr_output=$self->alias_for_gif_in_html_mode($aux_file_path);
+
+ } elsif ($displayMode eq 'TeX') {
+ ################################################################################
+ # .gif FILES in TeX mode
+ ################################################################################
+ $adr_output=$self->alias_for_gif_in_tex_mode($aux_file_path);
+
+ } else {
+ die "Error in alias: dangerousMacros.pl: unrecognizable displayMode = $displayMode";
+ }
+ } elsif ($ext eq 'png') {
+ if ( $displayMode eq 'HTML' ||
+ $displayMode eq 'HTML_tth'||
+ $displayMode eq 'HTML_dpng'||
+ $displayMode eq 'HTML_asciimath'||
+ $displayMode eq 'HTML_LaTeXMathML'||
+ $displayMode eq 'HTML_jsMath'||
+ $displayMode eq 'HTML_img' ) {
+ $adr_output = $self->alias_for_png_in_html_mode($aux_file_path);
+ } elsif ($displayMode eq 'TeX') {
+ $adr_output = $self->alias_for_png_in_tex_mode($aux_file_path);
+
+ } else {
+ warn "Error in alias: dangerousMacros.pl","unrecognizable displayMode = $displayMode","";
+ }
+ } else { # $ext is not recognized
+ ################################################################################
+ # FILES with unrecognized file extensions in any display modes
+ ################################################################################
+
+ warn "Error in the macro alias. Alias does not understand how to process files with extension $ext. (Path ot problem file is $fileName) ";
+ }
+
+ warn "The macro alias was unable to form a URL for some auxiliary file used in this problem." unless $adr_output;
+ return $adr_output;
+}
+
+
+
+sub alias_for_html {
+ my $self = shift;
+ my $aux_file_path = shift;
+ warn "aux_file for html $aux_file_path";
+ my $envir = $self->{envir}; my $fileName = $envir->{fileName};
+ my $htmlDirectory = $envir->{htmlDirectory};
+ my $htmlURL = $envir->{htmlURL};
+ my $tempDirectory = $envir->{tempDirectory};
+ my $tempURL = $envir->{tempURL};
+ my $studentLogin = $envir->{studentLogin};
+ my $psvnNumber = $envir->{psvnNumber};
+ my $setNumber = $envir->{setNumber};
+ my $probNum = $envir->{probNum};
+ my $displayMode = $envir->{displayMode};
+ my $externalGif2EpsPath = $envir->{externalGif2EpsPath};
+ my $externalPng2EpsPath = $envir->{externalPng2EpsPath};
+
+ my $templateDirectory = $self->{templateDirectory};
+
+
+ # $adr_output is a url in HTML and Latex2HTML modes
+ # and a complete path in TEX mode.
+ my $adr_output;
+ my $ext = "html";
+ ################################################################################
+ # .html FILES in HTML, HTML_tth, HTML_dpng, HTML_img, etc. and Latex2HTML mode
+ ################################################################################
+
+ # No changes are made for auxiliary files in the
+ # ${Global::htmlDirectory} subtree.
+ if ( $aux_file_path =~ m|^$tempDirectory| ) {
+ $adr_output = $aux_file_path,
+ $adr_output =~ s|$tempDirectory|$tempURL/|,
+ $adr_output .= ".$ext",
+ } elsif ($aux_file_path =~ m|^$htmlDirectory| ) {
+ $adr_output = $aux_file_path,
+ $adr_output =~ s|$htmlDirectory|$htmlURL|,
+ $adr_output .= ".$ext",
+ } else {
+ # HTML files not in the htmlDirectory are assumed under live under the
+ # templateDirectory in the same directory as the problem.
+ # Create an alias file (link) in the directory html/tmp/html which
+ # points to the original file and return the URL of this alias.
+ # Create all of the subdirectories of html/tmp/html which are needed
+ # using sure file to path.
+
+ # $fileName is obtained from environment for PGeval
+ # it gives the full path to the current problem
+ my $filePath = directoryFromPath($fileName);
+ my $htmlFileSource = convertPath("$templateDirectory${filePath}$aux_file_path.html");
+ my $link = "html/".$self->{uniqIDstub}."-$aux_file_path.$ext";
+ my $linkPath = $self->surePathToTmpFile($link);
+ $adr_output = "${tempURL}$link";
+ if (-e $htmlFileSource) {
+ if (-e $linkPath) {
+ unlink($linkPath) || warn "Unable to unlink alias file at |$linkPath|";
+ # destroy the old link.
+ }
+ symlink( $htmlFileSource, $linkPath)
+ || warn "The macro alias cannot create a link from |$linkPath| to |$htmlFileSource| <BR>" ;
+ } else {
+ warn("The macro alias cannot find an HTML file at: |$htmlFileSource|");
+ }
+ }
+ $adr_output;
+}
+
+
+sub alias_for_gif_in_html_mode {
+ my $self = shift;
+ my $aux_file_path = shift;
+# warn "entering alias_for_gif_in_html_mode $aux_file_path";
+
+ my $envir = $self->{envir}; my $fileName = $envir->{fileName};
+ my $htmlDirectory = $envir->{htmlDirectory};
+ my $htmlURL = $envir->{htmlURL};
+ my $tempDirectory = $envir->{tempDirectory};
+ my $tempURL = $envir->{tempURL};
+ my $studentLogin = $envir->{studentLogin};
+ my $psvnNumber = $envir->{psvnNumber};
+ my $setNumber = $envir->{setNumber};
+ my $probNum = $envir->{probNum};
+ my $displayMode = $envir->{displayMode};
+ my $externalGif2EpsPath = $envir->{externalGif2EpsPath};
+ my $externalPng2EpsPath = $envir->{externalPng2EpsPath};
+
+ my $templateDirectory = $self->{templateDirectory};
+
+
+ # $adr_output is a url in HTML and Latex2HTML modes
+ # and a complete path in TEX mode.
+ my $adr_output;
+ my $ext = "gif";
+
+ ################################################################################
+ # .gif FILES in HTML, HTML_tth, HTML_dpng, HTML_img, and Latex2HTML modes
+ ################################################################################
+
+ #warn "tempDirectory is $tempDirectory";
+ #warn "file Path for auxiliary file is $aux_file_path";
+
+ # No changes are made for auxiliary files in the htmlDirectory or in the tempDirectory subtree.
+ if ( $aux_file_path =~ m|^$tempDirectory| ) {
+ $adr_output = $aux_file_path;
+ $adr_output =~ s|$tempDirectory|$tempURL|;
+ $adr_output .= ".$ext";
+ #warn "adress out is $adr_output",
+ } elsif ($aux_file_path =~ m|^$htmlDirectory| ) {
+ $adr_output = $aux_file_path;
+ $adr_output =~ s|$htmlDirectory|$htmlURL|;
+ $adr_output .= ".$ext";
+ } else {
+ # files not in the htmlDirectory sub tree are assumed to live under the templateDirectory
+ # subtree in the same directory as the problem.
+
+ # For a gif file the alias macro creates an alias under the html/images directory
+ # which points to the gif file in the problem directory.
+ # All of the subdirectories of html/tmp/gif which are needed are also created.
+ my $filePath = directoryFromPath($fileName);
+
+ # $fileName is obtained from environment for PGeval
+ # it gives the full path to the current problem
+ my $gifSourceFile = convertPath("$templateDirectory${filePath}$aux_file_path.gif");
+ #my $link = "gif/$studentLogin-$psvnNumber-set$setNumber-prob$probNum-$aux_file_path.$ext";
+
+ # Make file names work in Library Browser when the images in several
+ # files have the same names.
+ my $libFix = "";
+ if ($setNumber eq "Undefined_Set") {
+ $libFix = $fileName;
+ $libFix =~ s!.*/!!, $libFix =~ s!\.pg(\..*)?$!!;
+ $libFix =~ s![^a-zA-Z0-9._-]!!g;
+ $libFix .= '-';
+ }
+
+ my $link = "gif/$setNumber-prob$probNum-$libFix$aux_file_path.$ext";
+
+ my $linkPath = $self->surePathToTmpFile($link);
+ $adr_output = "${tempURL}$link";
+ #warn "linkPath is $linkPath";
+ #warn "adr_output is $adr_output";
+ if (-e $gifSourceFile) {
+ if (-e $linkPath) {
+ unlink($linkPath) || warn "Unable to unlink old alias file at $linkPath";
+ }
+ symlink($gifSourceFile, $linkPath)
+ || warn "The macro alias cannot create a link from |$linkPath| to |$gifSourceFile| <BR>" ;
+ } else {
+ warn("The macro alias cannot find a GIF file at: |$gifSourceFile|");
+ }
+ }
+ $adr_output;
+}
+
+sub alias_for_gif_in_tex_mode {
+ my $self = shift;
+ my $aux_file_path = shift;
+
+ my $envir = $self->{envir}; my $fileName = $envir->{fileName};
+ my $htmlDirectory = $envir->{htmlDirectory};
+ my $htmlURL = $envir->{htmlURL};
+ my $tempDirectory = $envir->{tempDirectory};
+ my $tempURL = $envir->{tempURL};
+ my $studentLogin = $envir->{studentLogin};
+ my $psvnNumber = $envir->{psvnNumber};
+ my $setNumber = $envir->{setNumber};
+ my $probNum = $envir->{probNum};
+ my $displayMode = $envir->{displayMode};
+ my $externalGif2EpsPath = $envir->{externalGif2EpsPath};
+ my $externalPng2EpsPath = $envir->{externalPng2EpsPath};
+
+ my $templateDirectory = $self->{templateDirectory};
+
+
+ # $adr_output is a url in HTML and Latex2HTML modes
+ # and a complete path in TEX mode.
+ my $adr_output;
+ my $ext = "gif";
+ ################################################################################
+ # .gif FILES in TeX mode
+ ################################################################################
+
+ $setNumber =~ s/\./_/g; ## extra dots confuse latex's graphics package
+ if ($envir->{texDisposition} eq "pdf") {
+ # We're going to create PDF files with our TeX (using pdflatex); so we
+ # need images in PNG format.
+
+ my $gifFilePath;
+
+ if ($aux_file_path =~ m/^$htmlDirectory/ or $aux_file_path =~ m/^$tempDirectory/) {
+ # we've got a full pathname to a file
+ $gifFilePath = "$aux_file_path.gif";
+ } else {
+ # we assume the file is in the same directory as the problem source file
+ $gifFilePath = $templateDirectory . directoryFromPath($fileName) . "$aux_file_path.gif";
+ }
+
+ my $gifFileName = fileFromPath($gifFilePath);
+
+ $gifFileName =~ /^(.*)\.gif$/;
+# my $pngFilePath = $self->surePathToTmpFile("${tempDirectory}png/$probNum-$1.png");
+ my $pngFilePath = $self->surePathToTmpFile("${tempDirectory}png/$setNumber-$probNum-$1.png");
+ my $returnCode = system "cat $gifFilePath | ${$envir->{externalGif2PngPath}} > $pngFilePath";
+
+ if ($returnCode or not -e $pngFilePath) {
+ die "failed to convert $gifFilePath to $pngFilePath using gif->png with ${$envir->{externalGif2PngPath}}: $!\n";
+ }
+
+ $adr_output = $pngFilePath;
+ } else {
+ # Since we're not creating PDF files; we're probably just using a plain
+ # vanilla latex. Hence; we need EPS images.
+
+ ################################################################################
+ # This is statement used below is system dependent.
+ # Notice that the range of colors is restricted when converting to postscript to keep the files small
+ # "cat $gifSourceFile | /usr/math/bin/giftopnm | /usr/math/bin/pnmtops -noturn > $adr_output"
+ # "cat $gifSourceFile | /usr/math/bin/giftopnm | /usr/math/bin/pnmdepth 1 | /usr/math/bin/pnmtops -noturn > $adr_output"
+ ################################################################################
+ if ($aux_file_path =~ m|^$htmlDirectory| or $aux_file_path =~ m|^$tempDirectory|) {
+ # To serve an eps file copy an eps version of the gif file to the subdirectory of eps/
+ my $linkPath = directoryFromPath($fileName);
+
+ my $gifSourceFile = "$aux_file_path.gif";
+ my $gifFileName = fileFromPath($gifSourceFile);
+ $adr_output = surePathToTmpFile("$tempDirectory/eps/$studentLogin-$psvnNumber-$gifFileName.eps") ;
+
+ if (-e $gifSourceFile) {
+ #system("cat $gifSourceFile | /usr/math/bin/giftopnm | /usr/math/bin/pnmdepth 1 | /usr/math/bin/pnmtops -noturn>$adr_output")
+ system("cat $gifSourceFile | ${externalGif2EpsPath} > $adr_output" )
+ && die "Unable to create eps file:\n |$adr_output| from file\n |$gifSourceFile|\n in problem $probNum " .
+ "using the system dependent script\n |${externalGif2EpsPath}| \n";
+ } else {
+ die "|$gifSourceFile| cannot be found. Problem number: |$probNum|";
+ }
+ } else {
+ # To serve an eps file copy an eps version of the gif file to a subdirectory of eps/
+ my $filePath = directoryFromPath($fileName);
+ my $gifSourceFile = "${templateDirectory}${filePath}$aux_file_path.gif";
+ #print "content-type: text/plain \n\nfileName = $fileName and aux_file_path =$aux_file_path<BR>";
+ $adr_output = surePathToTmpFile("eps/$studentLogin-$psvnNumber-set$setNumber-prob$probNum-$aux_file_path.eps");
+
+ if (-e $gifSourceFile) {
+ #system("cat $gifSourceFile | /usr/math/bin/giftopnm | /usr/math/bin/pnmdepth 1 | /usr/math/bin/pnmtops -noturn>$adr_output") &&
+ #warn "Unable to create eps file: |$adr_output|\n from file\n |$gifSourceFile|\n in problem $probNum";
+ #warn "Help ${:externalGif2EpsPath}" unless -x "${main::externalGif2EpsPath}";
+ system("cat $gifSourceFile | ${externalGif2EpsPath} > $adr_output" )
+ && die "Unable to create eps file:\n |$adr_output| from file\n |$gifSourceFile|\n in problem $probNum " .
+ "using the system dependent commands \n |${externalGif2EpsPath}| \n ";
+ } else {
+ die "|$gifSourceFile| cannot be found. Problem number: |$probNum|";
+ }
+ }
+ }
+ $adr_output;
+
+}
+sub alias_for_png_in_html_mode {
+ my $self = shift;
+ my $aux_file_path = shift;
+
+ my $envir = $self->{envir}; my $fileName = $envir->{fileName};
+ my $htmlDirectory = $envir->{htmlDirectory};
+ my $htmlURL = $envir->{htmlURL};
+ my $tempDirectory = $envir->{tempDirectory};
+ my $tempURL = $envir->{tempURL};
+ my $studentLogin = $envir->{studentLogin};
+ my $psvnNumber = $envir->{psvnNumber};
+ my $setNumber = $envir->{setNumber};
+ my $probNum = $envir->{probNum};
+ my $displayMode = $envir->{displayMode};
+ my $externalGif2EpsPath = $envir->{externalGif2EpsPath};
+ my $externalPng2EpsPath = $envir->{externalPng2EpsPath};
+
+ my $templateDirectory = $self->{templateDirectory};
+
+
+ # $adr_output is a url in HTML and Latex2HTML modes
+ # and a complete path in TEX mode.
+ my $adr_output;
+ my $ext = "png";
+ ################################################################################
+ # .png FILES in HTML; HTML_tth; HTML_dpng; HTML_img; etc. and Latex2HTML modes
+ ################################################################################
+
+ #warn "tempDirectory is $tempDirectory";
+ #warn "file Path for auxiliary file is $aux_file_path";
+
+ # No changes are made for auxiliary files in the htmlDirectory or in the tempDirectory subtree.
+ if ( $aux_file_path =~ m|^$tempDirectory| ) {
+ $adr_output = $aux_file_path;
+ $adr_output =~ s|$tempDirectory|$tempURL|;
+ $adr_output .= ".$ext";
+ #warn "adress out is $adr_output";
+ } elsif ($aux_file_path =~ m|^$htmlDirectory| ) {
+ $adr_output = $aux_file_path;
+ $adr_output =~ s|$htmlDirectory|$htmlURL|;
+ $adr_output .= ".$ext";
+ } else {
+ # files not in the htmlDirectory sub tree are assumed to live under the templateDirectory
+ # subtree in the same directory as the problem.
+
+ # For a png file the alias macro creates an alias under the html/images directory
+ # which points to the png file in the problem directory.
+ # All of the subdirectories of html/tmp/gif which are needed are also created.
+ my $filePath = directoryFromPath($fileName);
+
+ # $fileName is obtained from environment for PGeval
+ # it gives the full path to the current problem
+ my $pngSourceFile = convertPath("$templateDirectory${filePath}$aux_file_path.png");
+ my $link = "gif/".$self->{uniqIDstub}."-$aux_file_path.$ext";
+ my $linkPath = surePathToTmpFile($link);
+ $adr_output = "${tempURL}$link";
+ #warn "linkPath is $linkPath";
+ #warn "adr_output is $adr_output";
+ if (-e $pngSourceFile) {
+ if (-e $linkPath) {
+ unlink($linkPath) || warn "Unable to unlink old alias file at $linkPath";
+ }
+ symlink($pngSourceFile, $linkPath)
+ || warn "The macro alias cannot create a link from |$linkPath| to |$pngSourceFile| <BR>" ;
+ } else {
+ warn("The macro alias cannot find a PNG file at: |$pngSourceFile|");
+ }
+ }
+ $adr_output;
+
+}
+
+sub alias_for_png_in_tex_mode {
+
+ my $self = shift;
+ my $aux_file_path = shift;
+
+ my $envir = $self->{envir}; my $fileName = $envir->{fileName};
+ my $htmlDirectory = $envir->{htmlDirectory};
+ my $htmlURL = $envir->{htmlURL};
+ my $tempDirectory = $envir->{tempDirectory};
+ my $tempURL = $envir->{tempURL};
+ my $studentLogin = $envir->{studentLogin};
+ my $psvnNumber = $envir->{psvnNumber};
+ my $setNumber = $envir->{setNumber};
+ my $probNum = $envir->{probNum};
+ my $displayMode = $envir->{displayMode};
+ my $externalGif2EpsPath = $envir->{externalGif2EpsPath};
+ my $externalPng2EpsPath = $envir->{externalPng2EpsPath};
+
+ my $templateDirectory = $self->{templateDirectory};
+
+
+ # $adr_output is a url in HTML and Latex2HTML modes
+ # and a complete path in TEX mode.
+ my $adr_output;
+ my $ext = "png";
+ ################################################################################
+ # .png FILES in TeX mode
+ ################################################################################
+
+ $setNumber =~ s/\./_/g; ## extra dots confuse latex's graphics package
+ if ($envir->{texDisposition} eq "pdf") {
+ # We're going to create PDF files with our TeX (using pdflatex); so we
+ # need images in PNG format. what luck! they're already in PDF format!
+
+ my $pngFilePath;
+
+ if ($aux_file_path =~ m/^$htmlDirectory/ or $aux_file_path =~ m/^$tempDirectory/) {
+ # we've got a full pathname to a file
+ $pngFilePath = "$aux_file_path.png";
+ } else {
+ # we assume the file is in the same directory as the problem source file
+ $pngFilePath = $templateDirectory . directoryFromPath($fileName) . "$aux_file_path.png";
+ }
+
+ $adr_output = $pngFilePath;
+ } else {
+ # Since we're not creating PDF files; we're probably just using a plain
+ # vanilla latex. Hence; we need EPS images.
+
+ ################################################################################
+ # This is statement used below is system dependent.
+ # Notice that the range of colors is restricted when converting to postscript to keep the files small
+ # "cat $pngSourceFile | /usr/math/bin/pngtopnm | /usr/math/bin/pnmtops -noturn > $adr_output"
+ # "cat $pngSourceFile | /usr/math/bin/pngtopnm | /usr/math/bin/pnmdepth 1 | /usr/math/bin/pnmtops -noturn > $adr_output"
+ ################################################################################
+
+ if ($aux_file_path =~ m|^$htmlDirectory| or $aux_file_path =~ m|^$tempDirectory|) {
+ # To serve an eps file copy an eps version of the png file to the subdirectory of eps/
+ my $linkPath = directoryFromPath($fileName);
+
+ my $pngSourceFile = "$aux_file_path.png";
+ my $pngFileName = fileFromPath($pngSourceFile);
+ $adr_output = surePathToTmpFile("$tempDirectory/eps/$studentLogin-$psvnNumber-$pngFileName.eps") ;
+
+ if (-e $pngSourceFile) {
+ #system("cat $pngSourceFile | /usr/math/bin/pngtopnm | /usr/math/bin/pnmdepth 1 | /usr/math/bin/pnmtops -noturn>$adr_output")
+ system("cat $pngSourceFile | ${externalPng2EpsPath} > $adr_output" )
+ && die "Unable to create eps file:\n |$adr_output| from file\n |$pngSourceFile|\n in problem $probNum " .
+ "using the system dependent commands\n |${externalPng2EpsPath}| \n";
+ } else {
+ die "|$pngSourceFile| cannot be found. Problem number: |$probNum|";
+ }
+ } else {
+ # To serve an eps file copy an eps version of the png file to a subdirectory of eps/
+ my $filePath = directoryFromPath($fileName);
+ my $pngSourceFile = "${templateDirectory}${filePath}$aux_file_path.png";
+ #print "content-type: text/plain \n\nfileName = $fileName and aux_file_path =$aux_file_path<BR>";
+ $adr_output = surePathToTmpFile("eps/$studentLogin-$psvnNumber-set$setNumber-prob$probNum-$aux_file_path.eps") ;
+ if (-e $pngSourceFile) {
+ #system("cat $pngSourceFile | /usr/math/bin/pngtopnm | /usr/math/bin/pnmdepth 1 | /usr/math/bin/pnmtops -noturn>$adr_output") &&
+ #warn "Unable to create eps file: |$adr_output|\n from file\n |$pngSourceFile|\n in problem $probNum";
+ #warn "Help ${externalPng2EpsPath}" unless -x "${externalPng2EpsPath}";
+ system("cat $pngSourceFile | ${externalPng2EpsPath} > $adr_output" )
+ && die "Unable to create eps file:\n |$adr_output| from file\n |$pngSourceFile|\n in problem $probNum " .
+ "using the system dependent commands\n |${externalPng2EpsPath}| \n ";
+ } else {
+ die "|$pngSourceFile| cannot be found. Problem number: |$probNum|";
+ }
+ }
+ }
+ $adr_output;
+
+}
+
+################################################
+
+# More resource search macros
+
+################################################
+
+#
+# Look for a macro file in the directories specified in the macros path
+#
+
+# ^variable my $macrosPath
+our ($macrosPath,
+ # ^variable my $pwd
+ $pwd,
+ # ^variable my $appletPath
+ $appletPath,
+ # ^variable my $server_root_url
+ $server_root_url,
+ # ^variable my $templateDirectory
+ $templateDirectory,
+ # ^variable my $scriptDirectory
+ $scriptDirectory,
+ # ^variable my $externalTTHPath
+ $externalTTHPath,
+ );
+
+# ^function findMacroFile
+# ^uses $macrosPath
+# ^uses $pwd
+sub findMacroFile {
+ my $self = shift;
+ my $fileName = shift;
+ my $filePath;
+ foreach my $dir (@{$macrosPath}) {
+ $filePath = "$dir/$fileName";
+ $filePath =~ s!^\.\.?/!$pwd/!;
+ return $filePath if (-r $filePath);
+ }
+ return; # no file found
+}
+
+# ^function check_url
+# ^uses %envir
+sub check_url {
+ my $self = shift;
+ my $url = shift;
+ return undef if $url =~ /;/; # make sure we can't get a second command in the url
+ #FIXME -- check for other exploits of the system call
+ #FIXME -- ALARM feature so that the response cannot be held up for too long.
+ #FIXME doesn't seem to work with relative addresses.
+ #FIXME Can we get the machine name of the server?
+
+ my $check_url_command = $self->{envir}->{externalCheckUrl};
+ my $response = system("$check_url_command $url");
+ return ($response) ? 0 : 1; # 0 indicates success, 256 is failure possibly more checks can be made
+}
+
+# ^variable our %appletCodebaseLocations
+our %appletCodebaseLocations = ();
+# ^function findAppletCodebase
+# ^uses %appletCodebaseLocations
+# ^uses $appletPath
+# ^uses $server_root_url
+# ^uses check_url
+sub findAppletCodebase {
+ my $self = shift;
+ my $fileName = shift; # probably the name of a jar file
+ return $appletCodebaseLocations{$fileName} #check cache first
+ if defined($appletCodebaseLocations{$fileName})
+ and $appletCodebaseLocations{$fileName} =~/\S/;
+
+ foreach my $appletLocation (@{$appletPath}) {
+ if ($appletLocation =~ m|^/|) {
+ $appletLocation = "$server_root_url$appletLocation";
+ }
+ return $appletLocation; # --hack workaround -- just pick the first location and use that -- no checks
+#hack to workaround conflict between lwp-request and apache2
+# comment out the check_url block
+# my $url = "$appletLocation/$fileName";
+# if ($self->check_url($url)) {
+# $appletCodebaseLocations{$fileName} = $appletLocation; #update cache
+# return $appletLocation # return codebase part of url
+# }
+ }
+ return "Error: $fileName not found at ". join(", ", @{$appletPath} ); # no file found
+}
+
+
+1;
\ No newline at end of file
--- /dev/null
+++ lib/PGanswergroup.pm
@@ -0,0 +1,150 @@
+################################################################################
+# WeBWorK Online Homework Delivery System
+# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/
+# $CVSHeader: pg/lib/PGanswergroup.pm,v 1.1 2010/05/14 11:39:02 gage Exp $
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of either: (a) the GNU General Public License as published by the
+# Free Software Foundation; either version 2, or (at your option) any later
+# version, or (b) the "Artistic License" which comes with this package.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
+# Artistic License for more details.
+################################################################################
+package PGanswergroup;
+use Exporter;
+use PGcore qw( not_null);
+use PGresponsegroup;
+
+our @ISA=qw(PGcore);
+
+#############################################
+# An object which contains an answer label and
+# an answer evaluator
+# and the links to and contents of all associated answer blanks
+# (i.e the student responses)
+#############################################
+# Notes
+# Answergroup -- input to a single answerEvaluator, may have several answer blanks
+# for example an array or a radio button group or several checkboxes
+# 1. create a answerEvaluator label name
+# 2. provide space for an answerEvaluator
+# 3. indicate that an answer blank or blanks has been published to receive the responses
+# for example store the number of response strings associated with this answerEvaluator label name
+# 4. space for the contents of the responses is provided in the PGresponse group
+# 5. provide a method for applying the evaluator to the responses
+#
+# use Tie: IxHash??? to create ordered hash? (see Perl Cookbook)
+
+sub new {
+ my $class = shift;
+ my $label = shift;
+ my $self = {
+ ans_label => $label,
+ ans_eval => undef, # usually an AnswerEvaluator, sometimes a CODE
+ response => new PGresponsegroup($label), # A PGresponse object which holds the responses
+ # which make up the answer
+ active => 1, # whether this answer group is currently active (for multistate problems)
+
+ @_,
+ };
+ bless $self, $class;
+ return $self;
+
+}
+
+sub evaluate { # applies the answer evaluator to the student response and returns an answer hash
+
+
+
+}
+
+sub complete { # test to see if answer evaluator and appropriate response blanks are all present
+
+
+
+}
+sub ans_eval {
+ my $self = shift;
+ my $ans_eval = shift;
+ $self->{ans_eval}= $ans_eval if ref($ans_eval);
+ $self->{ans_eval};
+}
+sub append_responses { #add or modify a response to the PGresponsegroup object
+ my $self = shift;
+ my @response_list = @_; # ordered list of label/ value pairs
+ $self->{response}->append_responses(@response_list);
+}
+
+sub insert_responses { # add a group of responses ( label/value pairs)
+ my $self = shift;
+ my @response_list = @_;
+ $self->{response}->clear();
+ $self->{response}->a...
[truncated message content] |
|
From: Mike G. v. a. <we...@ma...> - 2010-05-14 02:09:01
|
Log Message:
-----------
major update which adds objective methods to the basic code of PG.
HEAD should be considered more beta than usual for a few days until minor glitches
are shaken out.
new modules needed:
PGcore PGalias PGresource PGloadfiles PGanswergroup PGresponsegroup Tie::IxHash
dangerousMacros.pl is no longer used.
Modified Files:
--------------
pg/macros:
dangerousMacros.pl
Revision Data
-------------
Index: dangerousMacros.pl
===================================================================
RCS file: /webwork/cvs/system/pg/macros/dangerousMacros.pl,v
retrieving revision 1.58
retrieving revision 1.59
diff -Lmacros/dangerousMacros.pl -Lmacros/dangerousMacros.pl -u -r1.58 -r1.59
--- macros/dangerousMacros.pl
+++ macros/dangerousMacros.pl
@@ -92,1292 +92,1308 @@
=cut
-BEGIN {
- be_strict(); # an alias for use strict. This means that all global variable must contain main:: as a prefix.
-
-}
-
-# ^variable my $debugON
-my $debugON = 0;
-
-# grab read only variables from the current safe compartment
-
-# ^variable my $macrosPath
-my ($macrosPath,
- # ^variable my $pwd
- $pwd,
- # ^variable my $appletPath
- $appletPath,
- # ^variable my $server_root_url
- $server_root_url,
- # ^variable my $templateDirectory
- $templateDirectory,
- # ^variable my $scriptDirectory
- $scriptDirectory,
- # ^variable my $externalTTHPath
- $externalTTHPath,
- );
+#
+# BEGIN {
+# be_strict(); # an alias for use strict. This means that all global variable must contain main:: as a prefix.
+#
+# }
+#
+# # ^variable my $debugON
+# my $debugON = 0;
+#
+# # grab read only variables from the current safe compartment
+#
+# # ^variable my $macrosPath
+# my ($macrosPath,
+# # ^variable my $pwd
+# $pwd,
+# # ^variable my $appletPath
+# $appletPath,
+# # ^variable my $server_root_url
+# $server_root_url,
+# # ^variable my $templateDirectory
+# $templateDirectory,
+# # ^variable my $scriptDirectory
+# $scriptDirectory,
+# # ^variable my $externalTTHPath
+# $externalTTHPath,
+# );
+#
+# # ^function _dangerousMacros_init
+# # ^uses %envir
+# # ^uses $macrosPath
+# # ^uses $pwd
+# # ^uses $appletPath
+# # ^uses $server_root_url
+# # ^uses $templateDirectory
+# # ^uses $scriptDirectory
+# # ^uses $externalTTHPath
+# # ^uses $debugON
-# ^function _dangerousMacros_init
-# ^uses %envir
-# ^uses $macrosPath
-# ^uses $pwd
-# ^uses $appletPath
-# ^uses $server_root_url
-# ^uses $templateDirectory
-# ^uses $scriptDirectory
-# ^uses $externalTTHPath
-# ^uses $debugON
sub _dangerousMacros_init { #use envir instead of local variables?
- # will allow easy addition of new directories -- is this too liberal? do some pg directories need to be protected?
- $macrosPath = eval('$main::envir{pgDirectories}{macrosPath}');
- # will allow easy addition of new directories -- is this too liberal? do some pg directories need to be protected?
- $pwd = eval('$main::envir{fileName}'); $pwd =~ s!/[^/]*$!!;
- $appletPath = eval('$main::envir{pgDirectories}{appletPath}');
- $server_root_url = eval('$main::envir{server_root_url}');
-
- $templateDirectory = eval('$main::envir{templateDirectory}');
- $scriptDirectory = eval('$main::envir{scriptDirectory}');
- $externalTTHPath = eval('$main::envir{externalTTHPath}');
- $pwd = $templateDirectory.$pwd unless substr($pwd,0,1) eq '/';
- $pwd =~ s!/tmpEdit/!/!;
- warn "dangerousmacros initialized" if $debugON;
- warn eval(q! "dangerousmacros.pl externalTTHPath is ".$main::externalTTHPath;!) if $debugON;
- warn eval(q! "dangerousmacros.pl: The envir variable $main::{envir} is".join(" ",%main::envir)!) if $debugON;
-}
-
-# ^function _dangerousMacros_export
-sub _dangerousMacros_export {
- my @EXPORT= (
- '&_dangerousMacros_init',
- '&alias',
- '&compile_file',
- '&insertGraph',
- '&loadMacros',
- '&HEADER_TEXT',
- '&sourceAlias',
- '&tth',
- );
- @EXPORT;
-}
-
-
-=head2 loadMacros
-
- loadMacros(@macroFiles)
-
-loadMacros takes a list of file names and evaluates the contents of each file.
-This is used to load macros which define and augment the PG language. The macro
-files are searched for in the directories specified by the array referenced by
-$macrosPath, which by default is the current course's macros directory followed
-by WeBWorK's pg/macros directory. The latter is where the default behaviour of
-the PG language is defined. The default path is set in the global.conf file.
-
-Macro files named PG.pl, IO.pl, or dangerousMacros.pl will be loaded with no
-opcode restrictions, hence any code in those files will be able to execute
-privileged operations. This is true no matter which macro directory the file is
-in. For example, if $macrosPath contains the path to a problem library macros
-directory which contains a PG.pl file, this file will be loaded and allowed to
-engage in privileged behavior.
-
-=head3 Overloading macro files
-
-An individual course can modify the PG language, for that course only, by
-duplicating one of the macro files in the system-wide macros directory and
-placing this file in the macros directory for the course. The new file in the
-course's macros directory will now be used instead of the file in the
-system-wide macros directory.
-
-The new file in the course macros directory can by modified by adding macros or
-modifying existing macros.
-
-=head3 Modifying existing macros
-
-I<Modifying macros is for users with some experience.>
-
-Modifying existing macros might break other standard macros or problems which
-depend on the unmodified behavior of these macors so do this with great caution.
-In addition problems which use new macros defined in these files or which depend
-on the modified behavior of existing macros will not work in other courses
-unless the macros are also transferred to the new course. It helps to document
-the problems by indicating any special macros which the problems require.
-
-There is no facility for modifying or overloading a single macro. The entire
-file containing the macro must be overloaded.
-
-Modifications to files in the course macros directory affect only that course,
-they will not interfere with the normal behavior of WeBWorK in other courses.
-
-=cut
-
-# Global variables used
-# ${main::macrosPath}
-# Global macros used
-# None
-
-# Because of the need to use the directory variables it is tricky to define this
-# in translate.pl since, as currently written, the directories are not available
-# at that time. Perhaps if I rewrite translate as an object that method will work.
-
-# The only difficulty with defining loadMacros inside the Safe compartment is that
-# the error reporting does not work with syntax errors.
-# A kludge using require works around this problem
-
-
-# ^function loadMacros
-# ^uses time_it
-# ^uses $debugON
-# ^uses $externalTTHPath
-# ^uses findMacroFile
-sub loadMacros {
- my @files = @_;
- my $fileName;
- eval {main::time_it("begin load macros");};
- ###############################################################################
- # At this point the directories have been defined from %envir and we can define
- # the directories for this file
- ###############################################################################
-
- # special case inits
- foreach my $file ('PG.pl','dangerousMacros.pl','IO.pl') {
- my $macro_file_name = $file;
- $macro_file_name =~s/\.pl//; # trim off the extension
- $macro_file_name =~s/\.pg//; # sometimes the extension is .pg (e.g. CAPA files)
- my $init_subroutine_name = "_${macro_file_name}_init";
- my $init_subroutine = eval { \&{$init_subroutine_name} };
- use strict;
- my $macro_file_loaded = defined($init_subroutine);
- warn "dangerousMacros: macro init $init_subroutine_name defined |$init_subroutine| |$macro_file_loaded|" if $debugON;
- if ( defined($init_subroutine) && defined( &{$init_subroutine} ) ) {
-
- warn "dangerousMacros: initializing $macro_file_name" if $debugON;
- &$init_subroutine();
- }
- }
- unless (defined( $externalTTHPath)){
- warn "WARNING::Please make sure that the DOCUMENT() statement comes before<BR>\n" .
- " the loadMacros() statement in the problem template.<p>" .
- " The externalTTHPath variable |$externalTTHPath| was\n".
- " not defined which usually indicates the problem above.<br>\n";
-
- }
- #warn "running load macros";
- while (@files) {
- $fileName = shift @files;
- next if ($fileName =~ /^PG.pl$/) ; # the PG.pl macro package is already loaded.
-
- my $macro_file_name = $fileName;
- $macro_file_name =~s/\.pl//; # trim off the extension
- $macro_file_name =~s/\.pg//; # sometimes the extension is .pg (e.g. CAPA files)
- my $init_subroutine_name = "_${macro_file_name}_init";
- $init_subroutine_name =~ s![^a-zA-Z0-9_]!_!g; # remove dangerous chars
-
- ###############################################################################
- # For some reason the "no stict" which works on webwork-db doesn't work on
- # webwork. For this reason the constuction &{$init_subroutine_name}
- # was abandoned and replaced by eval. This is considerably more dangerous
- # since one could hide something nasty in a file name.
- # Keep an eye on this ???
- # webwork-db used perl 5.6.1 and webwork used perl 5.6.0
- ###############################################################################
-
- # compile initialization subroutine. (5.6.0 version)
-
-
-# eval( q{ \$init_subroutine = \\&main::}.$init_subroutine_name);
-# warn "dangerousMacros: failed to compile $init_subroutine_name. $@" if $@;
-
-
- ###############################################################################
- #compile initialization subroutine. (5.6.1 version) also works with 5.6.0
-
-# no strict;
- my $init_subroutine = eval { \&{'main::'.$init_subroutine_name} };
-# use strict;
-
- ###############################################################################
-
- # macros are searched for in the directories listed in the $macrosPath array reference.
-
- my $macro_file_loaded = defined($init_subroutine) && defined(&$init_subroutine);
- warn "dangerousMacros: macro init $init_subroutine_name defined |$init_subroutine| |$macro_file_loaded|" if $debugON;
- unless ($macro_file_loaded) {
- warn "loadMacros: loading macro file $fileName\n" if $debugON;
- my $filePath = findMacroFile($fileName);
- #### (check for renamed files here?) ####
- if ($filePath) {compile_file($filePath)}
- else {
- die "Can't locate macro file |$fileName| via path: |".join("|, |",@{$macrosPath})."|";
- }
- }
- ###############################################################################
- # Try again to define the initialization subroutine. (5.6.0 version)
-
-# eval( q{ \$init_subroutine = \\&main::}.$init_subroutine_name );
-# warn "dangerousMacros: failed to compile $init_subroutine_name. $@" if $@;
-# $init_subroutine = $temp::rf_init_subroutine;
- ###############################################################################
- # Try again to define the initialization subroutine. (5.6.1 version) also works with 5.6.0
-
-# no strict;
- $init_subroutine = eval { \&{'main::'.$init_subroutine_name} };
+# # will allow easy addition of new directories -- is this too liberal? do some pg directories need to be protected?
+# $macrosPath = eval('$main::envir{pgDirectories}{macrosPath}');
+# # will allow easy addition of new directories -- is this too liberal? do some pg directories need to be protected?
+# $pwd = eval('$main::envir{fileName}'); $pwd =~ s!/[^/]*$!!;
+# $appletPath = eval('$main::envir{pgDirectories}{appletPath}');
+# $server_root_url = eval('$main::envir{server_root_url}');
+#
+# $templateDirectory = eval('$main::envir{templateDirectory}');
+# $scriptDirectory = eval('$main::envir{scriptDirectory}');
+# $externalTTHPath = eval('$main::envir{externalTTHPath}');
+# $pwd = $templateDirectory.$pwd unless substr($pwd,0,1) eq '/';
+# $pwd =~ s!/tmpEdit/!/!;
+# warn "dangerousmacros initialized" if $debugON;
+# warn eval(q! "dangerousmacros.pl externalTTHPath is ".$main::externalTTHPath;!) if $debugON;
+# warn eval(q! "dangerousmacros.pl: The envir variable $main::{envir} is".join(" ",%main::envir)!) if $debugON;
+
+ }
+
+#
+# # ^function _dangerousMacros_export
+# sub _dangerousMacros_export {
+# my @EXPORT= (
+# '&_dangerousMacros_init',
+# '&alias',
+# '&compile_file',
+# '&insertGraph',
+# '&loadMacros',
+# '&HEADER_TEXT',
+# '&sourceAlias',
+# '&tth',
+# );
+# @EXPORT;
+# }
+#
+#
+# =head2 loadMacros
+#
+# loadMacros(@macroFiles)
+#
+# loadMacros takes a list of file names and evaluates the contents of each file.
+# This is used to load macros which define and augment the PG language. The macro
+# files are searched for in the directories specified by the array referenced by
+# $macrosPath, which by default is the current course's macros directory followed
+# by WeBWorK's pg/macros directory. The latter is where the default behaviour of
+# the PG language is defined. The default path is set in the global.conf file.
+#
+# Macro files named PG.pl, IO.pl, or dangerousMacros.pl will be loaded with no
+# opcode restrictions, hence any code in those files will be able to execute
+# privileged operations. This is true no matter which macro directory the file is
+# in. For example, if $macrosPath contains the path to a problem library macros
+# directory which contains a PG.pl file, this file will be loaded and allowed to
+# engage in privileged behavior.
+#
+# =head3 Overloading macro files
+#
+# An individual course can modify the PG language, for that course only, by
+# duplicating one of the macro files in the system-wide macros directory and
+# placing this file in the macros directory for the course. The new file in the
+# course's macros directory will now be used instead of the file in the
+# system-wide macros directory.
+#
+# The new file in the course macros directory can by modified by adding macros or
+# modifying existing macros.
+#
+# =head3 Modifying existing macros
+#
+# I<Modifying macros is for users with some experience.>
+#
+# Modifying existing macros might break other standard macros or problems which
+# depend on the unmodified behavior of these macors so do this with great caution.
+# In addition problems which use new macros defined in these files or which depend
+# on the modified behavior of existing macros will not work in other courses
+# unless the macros are also transferred to the new course. It helps to document
+# the problems by indicating any special macros which the problems require.
+#
+# There is no facility for modifying or overloading a single macro. The entire
+# file containing the macro must be overloaded.
+#
+# Modifications to files in the course macros directory affect only that course,
+# they will not interfere with the normal behavior of WeBWorK in other courses.
+#
+# =cut
+#
+# # Global variables used
+# # ${main::macrosPath}
+# # Global macros used
+# # None
+#
+# # Because of the need to use the directory variables it is tricky to define this
+# # in translate.pl since, as currently written, the directories are not available
+# # at that time. Perhaps if I rewrite translate as an object that method will work.
+#
+# # The only difficulty with defining loadMacros inside the Safe compartment is that
+# # the error reporting does not work with syntax errors.
+# # A kludge using require works around this problem
+#
+#
+# # ^function loadMacros
+# # ^uses time_it
+# # ^uses $debugON
+# # ^uses $externalTTHPath
+# # ^uses findMacroFile
+# sub loadMacros {
+# my @files = @_;
+# my $fileName;
+# eval {main::time_it("begin load macros");};
+# ###############################################################################
+# # At this point the directories have been defined from %envir and we can define
+# # the directories for this file
+# ###############################################################################
+#
+# # special case inits
+# foreach my $file ('PG.pl','dangerousMacros.pl','IO.pl') {
+# my $macro_file_name = $file;
+# $macro_file_name =~s/\.pl//; # trim off the extension
+# $macro_file_name =~s/\.pg//; # sometimes the extension is .pg (e.g. CAPA files)
+# my $init_subroutine_name = "_${macro_file_name}_init";
+# my $init_subroutine = eval { \&{$init_subroutine_name} };
# use strict;
- ###############################################################################
- #warn "loadMacros: defining \$temp::rf_init_subroutine ",$temp::rf_init_subroutine;
- $macro_file_loaded = defined($init_subroutine) && defined(&$init_subroutine);
- warn "dangerousMacros: macro init $init_subroutine_name defined |$init_subroutine| |$macro_file_loaded|" if $debugON;
-
- if ( defined($init_subroutine) && defined( &{$init_subroutine} ) ) {
- warn "dangerousMacros: initializing $macro_file_name" if $debugON;
- &$init_subroutine();
- }
- #warn "main:: contains <br>\n $macro_file_name ".join("<br>\n $macro_file_name ", %main::);
- }
- eval{main::time_it("end load macros");};
-}
-
-#
-# Look for a macro file in the directories specified in the macros path
-#
-
-# ^function findMacroFile
-# ^uses $macrosPath
-# ^uses $pwd
-sub findMacroFile {
- my $fileName = shift;
- my $filePath;
- foreach my $dir (@{$macrosPath}) {
- $filePath = "$dir/$fileName";
- $filePath =~ s!^\.\.?/!$pwd/!;
- return $filePath if (-r $filePath);
- }
- return; # no file found
-}
-
-# ^function check_url
-# ^uses %envir
-sub check_url {
- my $url = shift;
- return undef if $url =~ /;/; # make sure we can't get a second command in the url
- #FIXME -- check for other exploits of the system call
- #FIXME -- ALARM feature so that the response cannot be held up for too long.
- #FIXME doesn't seem to work with relative addresses.
- #FIXME Can we get the machine name of the server?
-
- my $check_url_command = $envir{externalCheckUrl};
- my $response = system("$check_url_command $url");
- return ($response) ? 0 : 1; # 0 indicates success, 256 is failure possibly more checks can be made
-}
-
-# ^variable our %appletCodebaseLocations
-our %appletCodebaseLocations = ();
-# ^function findAppletCodebase
-# ^uses %appletCodebaseLocations
-# ^uses $appletPath
-# ^uses $server_root_url
-# ^uses check_url
-sub findAppletCodebase {
- my $fileName = shift; # probably the name of a jar file
- return $appletCodebaseLocations{$fileName} #check cache first
- if defined($appletCodebaseLocations{$fileName})
- and $appletCodebaseLocations{$fileName} =~/\S/;
-
- foreach my $appletLocation (@{$appletPath}) {
- if ($appletLocation =~ m|^/|) {
- $appletLocation = "$server_root_url$appletLocation";
- }
- my $url = "$appletLocation/$fileName";
- if (check_url($url)) {
- $appletCodebaseLocations{$fileName} = $appletLocation; #update cache
- return $appletLocation # return codebase part of url
- }
- }
- return "Error: $fileName not found at ". join(", ", @{$appletPath} ); # no file found
-}
-# errors in compiling macros is not always being reported.
-# ^function compile_file
-# ^uses @__eval__
-# ^uses PG_restricted_eval
-# ^uses $__files__
-sub compile_file {
- my $filePath = shift;
- warn "loading $filePath" if $debugON;
- local(*MACROFILE);
- local($/);
- $/ = undef; # allows us to treat the file as a single line
- open(MACROFILE, "<$filePath") || die "Cannot open file: $filePath";
- my $string = 'BEGIN {push @__eval__, __FILE__};' . "\n" . <MACROFILE>;
- my ($result,$error,$fullerror) = &PG_restricted_eval($string);
- eval ('$main::__files__->{pop @main::__eval__} = $filePath');
- if ($error) { # the $fullerror report has formatting and is never empty
- # this is now handled by PG_errorMessage() in the PG translator
- #$fullerror =~ s/\(eval \d+\)/ $filePath\n/; # attempt to insert file name instead of eval number
- die "Error detected while loading $filePath:\n$fullerror";
-
- }
-
- close(MACROFILE);
-
-}
-
-# This creates on the fly graphs
-
-=head2 insertGraph
-
- # returns a path to the file containing the graph image.
- $filePath = insertGraph($graphObject);
-
-insertGraph writes a GIF or PNG image file to the gif subdirectory of the
-current course's HTML temp directory. The file name is obtained from the graph
-object. Warnings are issued if errors occur while writing to the file.
-
-Returns a string containing the full path to the temporary file containing the
-image. This is most often used in the construct
-
- TEXT(alias(insertGraph($graph)));
-
-where alias converts the directory address to a URL when serving HTML pages and
-insures that an EPS file is generated when creating TeX code for downloading.
-
-=cut
-
-# ^function insertGraph
-# ^uses $WWPlot::use_png
-# ^uses convertPath
-# ^uses surePathToTmpFile
-# ^uses PG_restricted_eval
-# ^uses $refreshCachedImages
-# ^uses $templateDirectory
-# ^uses %envir
-sub insertGraph {
- # Convert the image to GIF and print it on standard output
- my $graph = shift;
- my $extension = ($WWPlot::use_png) ? '.png' : '.gif';
- my $fileName = $graph->imageName . $extension;
- my $filePath = convertPath("gif/$fileName");
- $filePath = &surePathToTmpFile( $filePath );
- my $refreshCachedImages = PG_restricted_eval(q!$refreshCachedImages!);
- # Check to see if we already have this graph, or if we have to make it
- if( not -e $filePath # does it exist?
- or ((stat "$templateDirectory"."$main::envir{fileName}")[9] > (stat $filePath)[9]) # source has changed
- or $graph->imageName =~ /Undefined_Set/ # problems from SetMaker and its ilk should always be redone
- or $refreshCachedImages
- ) {
- #createFile($filePath, $main::tmp_file_permission, $main::numericalGroupID);
- local(*OUTPUT); # create local file handle so it won't overwrite other open files.
- open(OUTPUT, ">$filePath")||warn ("$0","Can't open $filePath<BR>","");
- chmod( 0777, $filePath);
- print OUTPUT $graph->draw|| warn("$0","Can't print graph to $filePath<BR>","");
- close(OUTPUT)||warn("$0","Can't close $filePath<BR>","");
- }
- $filePath;
-}
-
-=head2 [DEPRECATED] tth
-
- # returns an HTML version of the TeX code passed to it.
- tth($texString);
-
-This macro sends $texString to the filter program TtH, a TeX to HTML translator
-written by Ian Hutchinson. TtH is available free of change non-commerical
-use at L<http://hutchinson.belmont.ma.us/tth/>.
-
-The purpose of TtH is to translate text in the TeX or LaTeX markup language into
-HTML markup as best as possible. Some symbols, such as square root symbols are
-not translated completely. Macintosh users must use the "MacRoman" encoding
-(available in 4.0 and higher browsers) in order to view the symbols correctly.
-WeBWorK attempts to force Macintosh browsers to use this encoding when such a
-browser is detected.
-
-The contents of the file F<tthPreamble.tex> in the courses template directory
-are prepended to each string. This allows one to define TeX macros which can be
-used in every problem. Currently there is no default F<tthPreamble.tex> file, so
-if the file is not present in the course template directory no TeX macro
-definitions are prepended. TtH already understands most LaTeX commands, but will
-not in general know AMS-LaTeX commands.
-
-This macro contains code which is system dependent and may need to be modified
-to run on different systems.
-
-=cut
-
-# the contents of this file will not change during problem compilation it
-# only needs to be read once. however, the contents of the file may change,
-# and indeed the file refered to may change, between rendering passes. thus,
-# we need to keep track of the file name and the mtime as well.
-# ^variable my $tthPreambleFile
-# ^variable my $tthPreambleMtime
-# ^variable my $tthPreambleContents
-my ($tthPreambleFile, $tthPreambleMtime, $tthPreambleContents);
-
-# ^function tth
-# ^uses $templateDirectory
-# ^uses $envir{externalTTHPath}
-# ^uses $tthPreambleFile
-# ^uses $tthPreambleMtime
-# ^uses $tthPreambleContents
-sub tth {
- my $inputString = shift;
-
- my $thisFile = "${templateDirectory}tthPreamble.tex" if -r "${templateDirectory}tthPreamble.tex";
-
- if (defined $thisFile) {
- my $thisMtime = (stat $thisFile)[9];
- my $load =
- # load preamble if we haven't loaded it ever
- (not defined $tthPreambleFile or not defined $tthPreambleMtime or not defined $tthPreambleContents)
- ||
- # load if the file path has changed
- ($tthPreambleFile ne $thisFile)
- ||
- # load if the file has been modified
- ($tthPreambleMtime < $thisMtime);
-
- if ($load) {
- local(*TTHIN);
- open (TTHIN, "${templateDirectory}tthPreamble.tex") || die "Can't open file ${templateDirectory}tthPreamble.tex";
- local($/);
- $/ = undef;
- $tthPreambleContents = <TTHIN>;
- close(TTHIN);
-
- $tthPreambleContents =~ s/(.)\n/$1%\n/g; # thanks to Jim Martino
- # each line in the definition file
- # should end with a % to prevent
- # adding supurious paragraphs to output.
-
- $tthPreambleContents .="%\n"; # solves the problem if the file doesn't end with a return.
- }
- } else {
- $tthPreambleContents = "";
- }
-
- $inputString = $tthPreambleContents . $inputString;
- $inputString = "<<END_OF_TTH_INPUT_STRING;\n\n\n" . $inputString . "\nEND_OF_TTH_INPUT_STRING\necho \"\" >/dev/null"; #it's not clear why another command is needed.
-
- # $tthpath is now taken from $Global::externalTTHPath via %envir.
- my $tthpath = $envir{externalTTHPath};
- my $out;
-
- if (-x $tthpath ) {
- my $tthcmd = "$tthpath -L -f5 -u -r 2>/dev/null " . $inputString;
- if (open(TTH, "$tthcmd |")) {
- local($/);
- $/ = undef;
- $out = <TTH>;
- $/ = "\n";
- close(TTH);
- }else {
- $out = "<BR>there has been an error in executing $tthcmd<BR>";
- }
- } else {
- $out = "<BR> Can't execute the program tth at |$tthpath|<BR>";
- }
-
- $out;
-}
-
-# possible solution to the tth font problem? Works only for iCab.
-# ^function symbolConvert
-sub symbolConvert {
- my $string = shift;
- $string =~ s/\x5C/\\/g; #\ 92 \
- $string =~ s/\x7B/\{/g; #{ 123 {
- $string =~ s/\x7D/\}/g; #} 125 }
- $string =~ s/\xE7/\Á/g; #Á 231 Á
- $string =~ s/\xE6/\Ê/g; #Ê 230 Ê
- $string =~ s/\xE8/\Ë/g; #Ë 232 Ë
- $string =~ s/\xF3/\Û/g; #Û 243 Û
- $string =~ s/\xA5/\•/g; # 165 •
- $string =~ s/\xB2/\≤/g; #¾ 178 ≤
- $string =~ s/\xB3/\≥/g; # 179 ≥
- $string =~ s/\xB6/\∂/g; # 182 ∂
- $string =~ s/\xCE/\Œ/g; # 206 Œ
- $string =~ s/\xD6/\˜/g; #÷ 214 ˜
- $string =~ s/\xD9/\Ÿ/g; # 217 Ÿ
- $string =~ s/\xDA/\⁄/g; # 218 ⁄
- $string =~ s/\xF5/\ı/g; # 245 ı
- $string =~ s/\xF6/\ˆ/g; # 246 ˆ
- $string =~ s/\xF7/\Á/g; # 247 Á
- $string =~ s/\xF8/\¯/g; #¯ 248 ¯
- $string =~ s/\xF9/\˘/g; # 249 ˘
- $string =~ s/\xFA/\˙/g; # 250 ˙
- $string =~ s/\xFB/\˚;/g; # 251 ˚
- $string;
-}
-
-# ----- ----- ----- -----
-
-=head2 [DEPRECATED] math2img
-
- # returns an IMG tag pointing to an image version of the supplied TeX
- math2img($texString);
-
-This macro was used by the HTML_img display mode, which no longer exists.
-
-=cut
-
-# ^variable my $math2imgCount
-my $math2imgCount = 0;
-
-# ^function math2img
-# ^uses $math2imgCount
-# ^uses $envir{templateDirectory}
-# ^uses $envir{fileName}
-# ^uses $envir{studentLogin}
-# ^uses $envir{setNumber}
-# ^uses $envir{probNum}
-# ^uses $envir{tempURL}
-# ^uses $envir{refreshMath2img}
-# ^uses $envir{dvipngTempDir}
-# ^uses $envir{externalLaTeXPath}
-# ^uses $envir{externalDvipngPath}
-sub math2img {
- my $tex = shift;
- my $mode = shift;
-
- my $sourcePath = $envir{templateDirectory} . "/" . $envir{fileName};
- my $tempFile = "m2i/$envir{studentLogin}.$envir{setNumber}.$envir{probNum}."
- . $math2imgCount++ . ".png";
- my $tempPath = surePathToTmpFile($tempFile); #my $tempPath = "$envir{tempDirectory}$tempFile";
- my $tempURL = "$envir{tempURL}/$tempFile";
- my $forceRefresh = $envir{refreshMath2img};
- my $imageMissing = not -e $tempPath;
- my $imageStale = (stat $sourcePath)[9] > (stat $tempPath)[9] if -e $tempPath;
- if ($forceRefresh or $imageMissing or $imageStale) {
- # image file doesn't exist, or source file is newer then image file
- #warn "math2img: refreshMath2img forcing image generation for $tempFile\n" if $forceRefresh;
- #warn "math2img: $tempFile doesn't exist, so generating it\n" if $imageMissing;
- #warn "math2img: source file (", (stat $sourcePath)[9], ") is newer than image file (",
- # (stat $tempPath)[9], ") so re-generating image\n" if $imageStale;
- if (-e $tempPath) {
- unlink $tempPath or die "Failed to delete stale math2img file $tempPath: $!";
- }
- dvipng(
- $envir{dvipngTempDir}, $envir{externalLaTeXPath},
- $envir{externalDvipngPath}, $tex, $tempPath
- );
- }
-
- if (-e $tempPath) {
- return "<img align=\"middle\" src=\"$tempURL\" alt=\"$tex\">" if $mode eq "inline";
- return "<div align=\"center\"><img src=\"$tempURL\" alt=\"$tex\"></div>" if $mode eq "display";
- } else {
- return "<b>[math2img failed]</b>";
- # it might be nice to call tth here as a fallback instead:
- #return tth($tex);
- }
-};
-
-=head2 [DEPRECATED] dvipng
-
- dvipng($working_directory, $latex_path, $dvipng_path, $tex_string, $target_path)
-
-This macro was used by the HTML_img display mode, which no longer exists.
-
-=cut
-
-# copied from IO.pm for backward compatibility with WeBWorK1.8;
-# ^function dvipng
-sub dvipng($$$$$) {
- my (
- $wd, # working directory, for latex and dvipng garbage
- # (must already exist!)
- $latex, # path to latex binary
- $dvipng, # path to dvipng binary
- $tex, # tex string representing equation
- $targetPath # location of resulting image file
- ) = @_;
-
- my $dvipngBroken = 0;
-
- my $texFile = "$wd/equation.tex";
- my $dviFile = "$wd/equation.dvi";
- my $dviFile2 = "$wd/equationequation.dvi";
- my $dviCall = "equation";
- my $pngFile = "$wd/equation1.png";
-
- unless (-e $wd) {
- die "dvipng working directory $wd doesn't exist -- caller should have created it for us!\n";
- return 0;
- }
-
- # write the tex file
- local *TEX;
- open TEX, ">", $texFile or warn "Failed to create $texFile: $!";
- print TEX <<'EOF';
-% BEGIN HEADER
-\batchmode
-\documentclass[12pt]{article}
-\usepackage{amsmath,amsfonts,amssymb}
-\def\gt{>}
-\def\lt{<}
-\usepackage[active,textmath,displaymath]{preview}
-\begin{document}
-% END HEADER
-EOF
- print TEX "\\( \\displaystyle{$tex} \\)\n";
- print TEX <<'EOF';
-% BEGIN FOOTER
-\end{document}
-% END FOOTER
-EOF
- close TEX;
-
- # call latex
- system "cd $wd && $latex $texFile > /dev/null"
- and warn "Failed to call $latex with $texFile: $!";
-
- unless (-e $dviFile) {
- warn "Failed to generate DVI file $dviFile";
- return 0;
- }
-
- if ($dvipngBroken) {
- # change the name of the DVI file to get around dvipng's
- # crackheadedness. This is no longer needed with the newest
- # version of dvipng (10 something)
- system "/bin/mv", $dviFile, $dviFile2;
- }
-
- # call dvipng -- using warn instead of die passes some extra information
- # back to the user the complete warning is still printed in the apache
- # error log and a simple message (math2img failed) is returned to the
- # webpage.
- my $cmdout;
- $cmdout = system "cd $wd && $dvipng $dviCall > /dev/null"
- and warn "Failed to call$dvipng with $dviCall: $! with signal $cmdout";
-
- unless (-e $pngFile) {
- warn "Failed to create PNG file $pngFile";
- return 0;
- }
-
- $cmdout = system "/bin/mv", $pngFile, $targetPath and warn "Failed to mv: /bin/mv $pngFile $targetPath $!. Call returned $cmdout. \n";
-}
-
-
+# my $macro_file_loaded = defined($init_subroutine);
+# warn "dangerousMacros: macro init $init_subroutine_name defined |$init_subroutine| |$macro_file_loaded|" if $debugON;
+# if ( defined($init_subroutine) && defined( &{$init_subroutine} ) ) {
+#
+# warn "dangerousMacros: initializing $macro_file_name" if $debugON;
+# &$init_subroutine();
+# }
+# }
+# unless (defined( $externalTTHPath)){
+# warn "WARNING::Please make sure that the DOCUMENT() statement comes before<BR>\n" .
+# " the loadMacros() statement in the problem template.<p>" .
+# " The externalTTHPath variable |$externalTTHPath| was\n".
+# " not defined which usually indicates the problem above.<br>\n";
+#
+# }
+# #warn "running load macros";
+# while (@files) {
+# $fileName = shift @files;
+# next if ($fileName =~ /^PG.pl$/) ; # the PG.pl macro package is already loaded.
+#
+# my $macro_file_name = $fileName;
+# $macro_file_name =~s/\.pl//; # trim off the extension
+# $macro_file_name =~s/\.pg//; # sometimes the extension is .pg (e.g. CAPA files)
+# my $init_subroutine_name = "_${macro_file_name}_init";
+# $init_subroutine_name =~ s![^a-zA-Z0-9_]!_!g; # remove dangerous chars
+#
+# ###############################################################################
+# # For some reason the "no stict" which works on webwork-db doesn't work on
+# # webwork. For this reason the constuction &{$init_subroutine_name}
+# # was abandoned and replaced by eval. This is considerably more dangerous
+# # since one could hide something nasty in a file name.
+# # Keep an eye on this ???
+# # webwork-db used perl 5.6.1 and webwork used perl 5.6.0
+# ###############################################################################
+#
+# # compile initialization subroutine. (5.6.0 version)
+#
+#
+# # eval( q{ \$init_subroutine = \\&main::}.$init_subroutine_name);
+# # warn "dangerousMacros: failed to compile $init_subroutine_name. $@" if $@;
+#
+#
+# ###############################################################################
+# #compile initialization subroutine. (5.6.1 version) also works with 5.6.0
+#
+# # no strict;
+# my $init_subroutine = eval { \&{'main::'.$init_subroutine_name} };
+# # use strict;
+#
+# ###############################################################################
+#
+# # macros are searched for in the directories listed in the $macrosPath array reference.
+#
+# my $macro_file_loaded = defined($init_subroutine) && defined(&$init_subroutine);
+# warn "dangerousMacros: macro init $init_subroutine_name defined |$init_subroutine| |$macro_file_loaded|" if $debugON;
+# unless ($macro_file_loaded) {
+# warn "loadMacros: loading macro file $fileName\n" if $debugON;
+# my $filePath = findMacroFile($fileName);
+# #### (check for renamed files here?) ####
+# if ($filePath) {
+# compile_file($filePath);
+# #warn "loadMacros is compiling $filePath\n";
+# }
+# else {
+# die "Can't locate macro file |$fileName| via path: |".join("|, |",@{$macrosPath})."|";
+# }
+# }
+# ###############################################################################
+# # Try again to define the initialization subroutine. (5.6.0 version)
+#
+# # eval( q{ \$init_subroutine = \\&main::}.$init_subroutine_name );
+# # warn "dangerousMacros: failed to compile $init_subroutine_name. $@" if $@;
+# # $init_subroutine = $temp::rf_init_subroutine;
+# ###############################################################################
+# # Try again to define the initialization subroutine. (5.6.1 version) also works with 5.6.0
+#
+# # no strict;
+# $init_subroutine = eval { \&{'main::'.$init_subroutine_name} };
+# # use strict;
+# ###############################################################################
+# #warn "loadMacros: defining \$temp::rf_init_subroutine ",$temp::rf_init_subroutine;
+# $macro_file_loaded = defined($init_subroutine) && defined(&$init_subroutine);
+# warn "dangerousMacros: macro init $init_subroutine_name defined |$init_subroutine| |$macro_file_loaded|" if $debugON;
+#
+# if ( defined($init_subroutine) && defined( &{$init_subroutine} ) ) {
+# warn "dangerousMacros: initializing $macro_file_name" if $debugON;
+# &$init_subroutine();
+# }
+# #warn "main:: contains <br>\n $macro_file_name ".join("<br>\n $macro_file_name ", %main::);
+# }
+# eval{main::time_it("end load macros");};
+# }
+#
+# #
+# # Look for a macro file in the directories specified in the macros path
+# #
+#
+# # ^function findMacroFile
+# # ^uses $macrosPath
+# # ^uses $pwd
+# sub findMacroFile {
+# my $fileName = shift;
+# my $filePath;
+# foreach my $dir (@{$macrosPath}) {
+# $filePath = "$dir/$fileName";
+# $filePath =~ s!^\.\.?/!$pwd/!;
+# return $filePath if (-r $filePath);
+# }
+# return; # no file found
+# }
+#
+# # ^function check_url
+# # ^uses %envir
+# sub check_url {
+# my $url = shift;
+# return undef if $url =~ /;/; # make sure we can't get a second command in the url
+# #FIXME -- check for other exploits of the system call
+# #FIXME -- ALARM feature so that the response cannot be held up for too long.
+# #FIXME doesn't seem to work with relative addresses.
+# #FIXME Can we get the machine name of the server?
+#
+# my $check_url_command = $envir{externalCheckUrl};
+# my $response = system("$check_url_command $url");
+# return ($response) ? 0 : 1; # 0 indicates success, 256 is failure possibly more checks can be made
+# }
+#
+# # ^variable our %appletCodebaseLocations
+# our %appletCodebaseLocations = ();
+# # ^function findAppletCodebase
+# # ^uses %appletCodebaseLocations
+# # ^uses $appletPath
+# # ^uses $server_root_url
+# # ^uses check_url
+# sub findAppletCodebase {
+# my $fileName = shift; # probably the name of a jar file
+# return $appletCodebaseLocations{$fileName} #check cache first
+# if defined($appletCodebaseLocations{$fileName})
+# and $appletCodebaseLocations{$fileName} =~/\S/;
+#
+# foreach my $appletLocation (@{$appletPath}) {
+# if ($appletLocation =~ m|^/|) {
+# $appletLocation = "$server_root_url$appletLocation";
+# }
+# return $appletLocation; # --hack workaround -- just pick the first location and use that -- no checks
+# #hack to workaround conflict between lwp-request and apache2
+# # comment out the check_url block
+# # my $url = "$appletLocation/$fileName";
+# # if (check_url($url)) {
+# # $appletCodebaseLocations{$fileName} = $appletLocation; #update cache
+# # return $appletLocation # return codebase part of url
+# # }
+# }
+# return "Error: $fileName not found at ". join(", ", @{$appletPath} ); # no file found
+# }
+# # errors in compiling macros is not always being reported.
+# # ^function compile_file
+# # ^uses @__eval__
+# # ^uses PG_restricted_eval
+# # ^uses $__files__
+# sub compile_file {
+# my $filePath = shift;
+# warn "loading $filePath" if $debugON;
+# local(*MACROFILE);
+# local($/);
+# $/ = undef; # allows us to treat the file as a single line
+# open(MACROFILE, "<$filePath") || die "Cannot open file: $filePath";
+# my $string = 'BEGIN {push @__eval__, __FILE__};' . "\n" . <MACROFILE>;
+# my ($result,$error,$fullerror) = &PG_restricted_eval($string);
+# eval ('$main::__files__->{pop @main::__eval__} = $filePath');
+# if ($error) { # the $fullerror report has formatting and is never empty
+# # this is now handled by PG_errorMessage() in the PG translator
+# #$fullerror =~ s/\(eval \d+\)/ $filePath\n/; # attempt to insert file name instead of eval number
+# die "Error detected while loading $filePath:\n$fullerror";
+#
+# }
+#
+# close(MACROFILE);
+#
+# }
+#
+# # This creates on the fly graphs
+#
+# =head2 insertGraph
+#
+# # returns a path to the file containing the graph image.
+# $filePath = insertGraph($graphObject);
+#
+# insertGraph writes a GIF or PNG image file to the gif subdirectory of the
+# current course's HTML temp directory. The file name is obtained from the graph
+# object. Warnings are issued if errors occur while writing to the file.
+#
+# Returns a string containing the full path to the temporary file containing the
+# image. This is most often used in the construct
+#
+# TEXT(alias(insertGraph($graph)));
+#
+# where alias converts the directory address to a URL when serving HTML pages and
+# insures that an EPS file is generated when creating TeX code for downloading.
+#
+# =cut
+#
+# # ^function insertGraph
+# # ^uses $WWPlot::use_png
+# # ^uses convertPath
+# # ^uses surePathToTmpFile
+# # ^uses PG_restricted_eval
+# # ^uses $refreshCachedImages
+# # ^uses $templateDirectory
+# # ^uses %envir
+# sub insertGraph {
+# # Convert the image to GIF and print it on standard output
+# my $graph = shift;
+# my $extension = ($WWPlot::use_png) ? '.png' : '.gif';
+# my $fileName = $graph->imageName . $extension;
+# my $filePath = convertPath("gif/$fileName");
+# $filePath = &surePathToTmpFile( $filePath );
+# my $refreshCachedImages = PG_restricted_eval(q!$refreshCachedImages!);
+# # Check to see if we already have this graph, or if we have to make it
+# if( not -e $filePath # does it exist?
+# or ((stat "$templateDirectory"."$main::envir{fileName}")[9] > (stat $filePath)[9]) # source has changed
+# or $graph->imageName =~ /Undefined_Set/ # problems from SetMaker and its ilk should always be redone
+# or $refreshCachedImages
+# ) {
+# #createFile($filePath, $main::tmp_file_permission, $main::numericalGroupID);
+# local(*OUTPUT); # create local file handle so it won't overwrite other open files.
+# open(OUTPUT, ">$filePath")||warn ("$0","Can't open $filePath<BR>","");
+# chmod( 0777, $filePath);
+# print OUTPUT $graph->draw|| warn("$0","Can't print graph to $filePath<BR>","");
+# close(OUTPUT)||warn("$0","Can't close $filePath<BR>","");
+# }
+# $filePath;
+# }
+#
+# =head2 [DEPRECATED] tth
+#
+# # returns an HTML version of the TeX code passed to it.
+# tth($texString);
+#
+# This macro sends $texString to the filter program TtH, a TeX to HTML translator
+# written by Ian Hutchinson. TtH is available free of change non-commerical
+# use at L<http://hutchinson.belmont.ma.us/tth/>.
+#
+# The purpose of TtH is to translate text in the TeX or LaTeX markup language into
+# HTML markup as best as possible. Some symbols, such as square root symbols are
+# not translated completely. Macintosh users must use the "MacRoman" encoding
+# (available in 4.0 and higher browsers) in order to view the symbols correctly.
+# WeBWorK attempts to force Macintosh browsers to use this encoding when such a
+# browser is detected.
+#
+# The contents of the file F<tthPreamble.tex> in the courses template directory
+# are prepended to each string. This allows one to define TeX macros which can be
+# used in every problem. Currently there is no default F<tthPreamble.tex> file, so
+# if the file is not present in the course template directory no TeX macro
+# definitions are prepended. TtH already understands most LaTeX commands, but will
+# not in general know AMS-LaTeX commands.
+#
+# This macro contains code which is system dependent and may need to be modified
+# to run on different systems.
+#
+# =cut
+#
+# # the contents of this file will not change during problem compilation it
+# # only needs to be read once. however, the contents of the file may change,
+# # and indeed the file refered to may change, between rendering passes. thus,
+# # we need to keep track of the file name and the mtime as well.
+# # ^variable my $tthPreambleFile
+# # ^variable my $tthPreambleMtime
+# # ^variable my $tthPreambleContents
+# my ($tthPreambleFile, $tthPreambleMtime, $tthPreambleContents);
+#
+# # ^function tth
+# # ^uses $templateDirectory
+# # ^uses $envir{externalTTHPath}
+# # ^uses $tthPreambleFile
+# # ^uses $tthPreambleMtime
+# # ^uses $tthPreambleContents
+# sub tth {
+# my $inputString = shift;
+#
+# my $thisFile = "${templateDirectory}tthPreamble.tex" if -r "${templateDirectory}tthPreamble.tex";
+#
+# if (defined $thisFile) {
+# my $thisMtime = (stat $thisFile)[9];
+# my $load =
+# # load preamble if we haven't loaded it ever
+# (not defined $tthPreambleFile or not defined $tthPreambleMtime or not defined $tthPreambleContents)
+# ||
+# # load if the file path has changed
+# ($tthPreambleFile ne $thisFile)
+# ||
+# # load if the file has been modified
+# ($tthPreambleMtime < $thisMtime);
+#
+# if ($load) {
+# local(*TTHIN);
+# open (TTHIN, "${templateDirectory}tthPreamble.tex") || die "Can't open file ${templateDirectory}tthPreamble.tex";
+# local($/);
+# $/ = undef;
+# $tthPreambleContents = <TTHIN>;
+# close(TTHIN);
+#
+# $tthPreambleContents =~ s/(.)\n/$1%\n/g; # thanks to Jim Martino
+# # each line in the definition file
+# # should end with a % to prevent
+# # adding supurious paragraphs to output.
+#
+# $tthPreambleContents .="%\n"; # solves the problem if the file doesn't end with a return.
+# }
+# } else {
+# $tthPreambleContents = "";
+# }
+#
+# $inputString = $tthPreambleContents . $inputString;
+# $inputString = "<<END_OF_TTH_INPUT_STRING;\n\n\n" . $inputString . "\nEND_OF_TTH_INPUT_STRING\necho \"\" >/dev/null"; #it's not clear why another command is needed.
+#
+# # $tthpath is now taken from $Global::externalTTHPath via %envir.
+# my $tthpath = $envir{externalTTHPath};
+# my $out;
+#
+# if (-x $tthpath ) {
+# my $tthcmd = "$tthpath -L -f5 -u -r 2>/dev/null " . $inputString;
+# if (open(TTH, "$tthcmd |")) {
+# local($/);
+# $/ = undef;
+# $out = <TTH>;
+# $/ = "\n";
+# close(TTH);
+# }else {
+# $out = "<BR>there has been an error in executing $tthcmd<BR>";
+# }
+# } else {
+# $out = "<BR> Can't execute the program tth at |$tthpath|<BR>";
+# }
+#
+# $out;
+# }
+#
+# # possible solution to the tth font problem? Works only for iCab.
+# # ^function symbolConvert
+# sub symbolConvert {
+# my $string = shift;
+# $string =~ s/\x5C/\\/g; #\ 92 \
+# $string =~ s/\x7B/\{/g; #{ 123 {
+# $string =~ s/\x7D/\}/g; #} 125 }
+# $string =~ s/\xE7/\Á/g; #Á 231 Á
+# $string =~ s/\xE6/\Ê/g; #Ê 230 Ê
+# $string =~ s/\xE8/\Ë/g; #Ë 232 Ë
+# $string =~ s/\xF3/\Û/g; #Û 243 Û
+# $string =~ s/\xA5/\•/g; # 165 •
+# $string =~ s/\xB2/\≤/g; #¾ 178 ≤
+# $string =~ s/\xB3/\≥/g; # 179 ≥
+# $string =~ s/\xB6/\∂/g; # 182 ∂
+# $string =~ s/\xCE/\Œ/g; # 206 Œ
+# $string =~ s/\xD6/\˜/g; #÷ 214 ˜
+# $string =~ s/\xD9/\Ÿ/g; # 217 Ÿ
+# $string =~ s/\xDA/\⁄/g; # 218 ⁄
+# $string =~ s/\xF5/\ı/g; # 245 ı
+# $string =~ s/\xF6/\ˆ/g; # 246 ˆ
+# $string =~ s/\xF7/\Á/g; # 247 Á
+# $string =~ s/\xF8/\¯/g; #¯ 248 ¯
+# $string =~ s/\xF9/\˘/g; # 249 ˘
+# $string =~ s/\xFA/\˙/g; # 250 ˙
+# $string =~ s/\xFB/\˚;/g; # 251 ˚
+# $string;
+# }
+#
+# # ----- ----- ----- -----
+#
+# =head2 [DEPRECATED] math2img
+#
+# # returns an IMG tag pointing to an image version of the supplied TeX
+# math2img($texString);
+#
+# This macro was used by the HTML_img display mode, which no longer exists.
+#
+# =cut
+#
+# # ^variable my $math2imgCount
+# my $math2imgCount = 0;
+#
+# # ^function math2img
+# # ^uses $math2imgCount
+# # ^uses $envir{templateDirectory}
+# # ^uses $envir{fileName}
+# # ^uses $envir{studentLogin}
+# # ^uses $envir{setNumber}
+# # ^uses $envir{probNum}
+# # ^uses $envir{tempURL}
+# # ^uses $envir{refreshMath2img}
+# # ^uses $envir{dvipngTempDir}
+# # ^uses $envir{externalLaTeXPath}
+# # ^uses $envir{externalDvipngPath}
+# sub math2img {
+# my $tex = shift;
+# my $mode = shift;
+#
+# my $sourcePath = $envir{templateDirectory} . "/" . $envir{fileName};
+# my $tempFile = "m2i/$envir{studentLogin}.$envir{setNumber}.$envir{probNum}."
+# . $math2imgCount++ . ".png";
+# my $tempPath = surePathToTmpFile($tempFile); #my $tempPath = "$envir{tempDirectory}$tempFile";
+# my $tempURL = "$envir{tempURL}/$tempFile";
+# my $forceRefresh = $envir{refreshMath2img};
+# my $imageMissing = not -e $tempPath;
+# my $imageStale = (stat $sourcePath)[9] > (stat $tempPath)[9] if -e $tempPath;
+# if ($forceRefresh or $imageMissing or $imageStale) {
+# # image file doesn't exist, or source file is newer then image file
+# #warn "math2img: refreshMath2img forcing image generation for $tempFile\n" if $forceRefresh;
+# #warn "math2img: $tempFile doesn't exist, so generating it\n" if $imageMissing;
+# #warn "math2img: source file (", (stat $sourcePath)[9], ") is newer than image file (",
+# # (stat $tempPath)[9], ") so re-generating image\n" if $imageStale;
+# if (-e $tempPath) {
+# unlink $tempPath or die "Failed to delete stale math2img file $tempPath: $!";
+# }
+# dvipng(
+# $envir{dvipngTempDir}, $envir{externalLaTeXPath},
+# $envir{externalDvipngPath}, $tex, $tempPath
+# );
+# }
+#
+# if (-e $tempPath) {
+# return "<img align=\"middle\" src=\"$tempURL\" alt=\"$tex\">" if $mode eq "inline";
+# return "<div align=\"center\"><img src=\"$tempURL\" alt=\"$tex\"></div>" if $mode eq "display";
+# } else {
+# return "<b>[math2img failed]</b>";
+# # it might be nice to call tth here as a fallback instead:
+# #return tth($tex);
+# }
+# };
+#
+# =head2 [DEPRECATED] dvipng
+#
+# dvipng($working_directory, $latex_path, $dvipng_path, $tex_string, $target_path)
+#
+# This macro was used by the HTML_img display mode, which no longer exists.
+#
+# =cut
+#
+# # copied from IO.pm for backward compatibility with WeBWorK1.8;
+# # ^function dvipng
+# sub dvipng($$$$$) {
+# my (
+# $wd, # working directory, for latex and dvipng garbage
+# # (must already exist!)
+# $latex, # path to latex binary
+# $dvipng, # path to dvipng binary
+# $tex, # tex string representing equation
+# $targetPath # location of resulting image file
+# ) = @_;
+#
+# my $dvipngBroken = 0;
+#
+# my $texFile = "$wd/equation.tex";
+# my $dviFile = "$wd/equation.dvi";
+# my $dviFile2 = "$wd/equationequation.dvi";
+# my $dviCall = "equation";
+# my $pngFile = "$wd/equation1.png";
+#
+# unless (-e $wd) {
+# die "dvipng working directory $wd doesn't exist -- caller should have created it for us!\n";
+# return 0;
+# }
+#
+# # write the tex file
+# local *TEX;
+# open TEX, ">", $texFile or warn "Failed to create $texFile: $!";
+# print TEX <<'EOF';
+# % BEGIN HEADER
+# \batchmode
+# \documentclass[12pt]{article}
+# \usepackage{amsmath,amsfonts,amssymb}
+# \def\gt{>}
+# \def\lt{<}
+# \usepackage[active,textmath,displaymath]{preview}
+# \begin{document}
+# % END HEADER
+# EOF
+# print TEX "\\( \\displaystyle{$tex} \\)\n";
+# print TEX <<'EOF';
+# % BEGIN FOOTER
+# \end{document}
+# % END FOOTER
+# EOF
+# close TEX;
+#
+# # call latex
+# system "cd $wd && $latex $texFile > /dev/null"
+# and warn "Failed to call $latex with $texFile: $!";
+#
+# unless (-e $dviFile) {
+# warn "Failed to generate DVI file $dviFile";
+# return 0;
+# }
+#
+# if ($dvipngBroken) {
+# # change the name of the DVI file to get around dvipng's
+# # crackheadedness. This is no longer needed with the newest
+# # version of dvipng (10 something)
+# system "/bin/mv", $dviFile, $dviFile2;
+# }
+#
+# # call dvipng -- using warn instead of die passes some extra information
+# # back to the user the complete warning is still printed in the apache
+# # error log and a simple message (math2img failed) is returned to the
+# # webpage.
+# my $cmdout;
+# $cmdout = system "cd $wd && $dvipng $dviCall > /dev/null"
+# and warn "Failed to call$dvipng with $dviCall: $! with signal $cmdout";
+#
+# unless (-e $pngFile) {
+# warn "Failed to create PNG file $pngFile";
+# return 0;
+# }
+#
+# $cmdout = system "/bin/mv", $pngFile, $targetPath and warn "Failed to mv: /bin/mv $pngFile $targetPath $!. Call returned $cmdout. \n";
+# }
+#
# ----- ----- ----- -----
+#
+# =head2 alias
+#
+# # In HTML modes, returns the URL of a web-friendly version of the specified file.
+# # In TeX mode, returns the path to a TeX-friendly version of the specified file.
+# alias($pathToFile);
+#
+# alias allows you to refer to auxiliary files which are in a directory along with
+# the problem definition. In addition alias creates an EPS version of GIF or PNG
+# files when called in TeX mode.
+#
+# As a rule auxiliary files that are used by a number of problems in a course
+# should be placed in C<html/gif> or C<html> or in a subdirectory of the C<html>
+# directory, while auxiliary files which are used in only one problem should be
+# placed in the same directory as the problem in order to make the problem more
+# portable.
+#
+# =head3 Specific behavior of the alias macro
+#
+# =head4 Files in the html subdirectory
+#
+# =over
+#
+# =item When not in TeX mode
+#
+# If the file lies under the F<html> subdirectory, then the approriate URL for the
+# file is returned. Since the F<html> subdirectory is already accessible to the
+# webserver no other changes need to be made. The file path for this type of file
+# should be the complete file path. The path should start with the prefix defined
+# in $courseDirs{html_temp} in global.conf.
+#
+# =item When in TeX mode
+#
+# GIF and PNG files will be translated into EPS files and placed in the directory
+# F<tmp/eps>. The full path to this file is returned for use by TeX in producing
+# the hard copy. The conversion is done by a system dependent commands defined in
+# F<global.conf> $externalPrograms{gif2eps} (for GIF images) or
+# $externalPrograms{png2eps} (for PNG images). The URLs for the other files are
+# produced as in non-TeX mode but will of course not be usable to TeX.
+#
+# =back
+#
+# =head4 Files in the tmp subdirectory
+#
+# =over
+#
+# =item When not in TeX mode
+#
+# If the file lies under the F<tmp> subdirectory, then the approriate URL for the
+# file is created. Since the F<tmp> subdirectory is already accessible to the
+# webserver no other changes need to be made. The file path for this type of file
+# should be the complete file path. The path should start with the prefix defined
+# in $courseDirs{html_temp} in global.conf.
+#
+# =item When in TeX mode
+#
+# GIF and PNG files will be translated into EPS files and placed in the directory
+# F<tmp/eps>. The full path to this file is returned for use by TeX in producing
+# the hard copy. The conversion is done by a system dependent commands defined in
+# F<global.conf> $externalPrograms{gif2eps} (for GIF images) or
+# $externalPrograms{png2eps} (for PNG images). The URLs for the other files are
+# produced as in non-TeX mode but will of course not be usable to TeX.
+#
+# =back
+#
+# =head4 Files in the course template subdirectory
+#
+# =over
+#
+# =item When not in TeX mode
+#
+# If the file lies under the course templates subdirectory, it is assumed to lie
+# in subdirectory rooted in the directory containing the problem template file. An
+# alias is created under the F<html/tmp/gif> or F<html/tmp/html> directory and
+# linked to the original file. The file path for this type of file is a relative
+# path rooted at the directory containing the problem template file.
+#
+# =item When in TeX mode
+#
+# GIF and PNG files will be translated into EPS files and placed in the directory
+# F<tmp/eps>. The full path to this file is returned for use by TeX in producing
+# the hard copy. The conversion is done by a system dependent commands defined in
+# F<global.conf> $externalPrograms{gif2eps} (for GIF images) or
+# $externalPrograms{png2eps} (for PNG images). The URLs for the other files are
+# produced as in non-TeX mode but will of course not be usable to TeX.
+#
+# =back
+#
+# =cut
+#
+
+#
+# # Currently gif, html and types are supported.
+# #
+# # If the auxiliary file path has not extension then the extension .gif isassumed.
+# #
+# # If the auxiliary file path leads to a file in the ${Global::htmlDirectory}
+# # no changes are made to the file path.
+# #
+# # If the auxiliary file path is not complete, than it is assumed that it refers
+# # to a subdirectoy of the directory containing the problem..
+# #
+# # The output is either the correct URL for the file
+# # or (in TeX mode) the complete path to the eps version of the file
+# # and can be used as input into the image macro.
+# #
+# # surePathToTmpFile takes a path and outputs the complete path:
+# # ${main::htmlDirectory}/tmp/path
+# # It insures that all of the directories in the path have been created,
+# # but does not create the
+# # final file.
+#
+# # For postscript printing, alias generates an eps version of the gif image and places
+# # it in the directory eps. This slows down downloading postscript versions somewhat,
+# # but not excessivevly.
+# # Alias does not do any garbage collection, so files and alias may accumulate and
+# # need to be removed manually or by a reaper daemon.
+#
+# # This subroutine has commands which will not work on non-UNIX environments.
+# # system("cat $gifSourceFile | /usr/math/bin/giftopnm | /usr/math/bin/pnmdepth 1 | /usr/math/bin/pnmtops -noturn>$adr_output") &&
+#
+# # ^function alias
+# # ^uses %envir
+# # ^uses $envir{fileName}
+# # ^uses $envir{htmlDirectory}
+# # ^uses $envir{htmlURL}
+# # ^uses $envir{tempDirectory}
+# # ^uses $envir{tempURL}
+# # ^uses $envir{studentLogin}
+# # ^uses $envir{psvnNumber}
+# # ^uses $envir{setNumber}
+# # ^uses $envir{probNum}
+# # ^uses $envir{displayMode}
+# # ^uses $envir{externalGif2EpsPath}
+# # ^uses $envir{externalPng2EpsPath}
+# # ^uses &surePathToTmpFile
+# # ^uses &convertPath
+# # ^uses &directoryFromPath
+# # ^uses &fileFromPath
+# # ^uses $envir{texDisposition}
+
+# sub alias {
+# # input is a path to the original auxiliary file
+# my $envir = eval(q!\%main::envir!); # get the current root environment
+# my $fileName = $envir->{fileName};
+# my $htmlDirectory = $envir->{htmlDirectory};
+# my $htmlURL = $envir->{htmlURL};
+# my $tempDirectory = $envir->{tempDirectory};
+# my $tempURL = $envir->{tempURL};
+# my $studentLogin = $envir->{studentLogin};
+# my $psvnNumber = $envir->{psvnNumber};
+# my $setNumber = $envir->{setNumber};
+# my $probNum = $envir->{probNum};
+# my $displayMode ...
[truncated message content] |
|
From: Mike G. v. a. <we...@ma...> - 2010-05-14 02:08:58
|
Log Message:
-----------
major update which adds objective methods to the basic code of PG.
HEAD should be considered more beta than usual for a few days until minor glitches
are shaken out.
new modules needed:
PGcore PGalias PGresource PGloadfiles PGanswergroup PGresponsegroup Tie::IxHash
Modified Files:
--------------
pg/macros:
PGanswermacros.pl
PG.pl
Revision Data
-------------
Index: PG.pl
===================================================================
RCS file: /webwork/cvs/system/pg/macros/PG.pl,v
retrieving revision 1.40
retrieving revision 1.41
diff -Lmacros/PG.pl -Lmacros/PG.pl -u -r1.40 -r1.41
--- macros/PG.pl
+++ macros/PG.pl
@@ -1,6 +1,559 @@
+
+#use AnswerEvaluator;
+
+
+# provided by the translator
+# initialize PGcore and PGrandom
+
+
+ $main::VERSION ="WW2";
+
+sub _PG_init{
+ $main::VERSION ="WW2.9+";
+}
+sub not_null {PGcore::not_null(@_)};
+
+
+our $PG;
+
+sub DEBUG_MESSAGE {
+ $PG->append_debug_message(@_);
+}
+
+
+sub DOCUMENT {
+
+ # get environment
+ $rh_envir = \%envir; #KLUDGE FIXME
+ # warn "rh_envir is ",ref($rh_envir);
+ $PG = new PGcore($rh_envir, # can add key/value options to modify
+ );
+ $PG->clear_internal_debug_messages;
+
+ # initialize main:: variables
+
+ $ANSWER_PREFIX = $PG->{ANSWER_PREFIX};
+ $QUIZ_PREFIX = $PG->{QUIZ_PREFIX};
+ $showPartialCorrectAnswers = $PG->{PG_FLAGS}->{showPartialCorrectAnswers};
+ $showHint = $PG->{PG_FLAGS}->{showHint};
+ $solutionExists = $PG->{PG_FLAGS}->{solutionExists};
+ $hintExists = $PG->{PG_FLAGS}->{hintExists};
+ $pgComment = '';
+ %gifs_created = %{ $PG->{gifs_created}};
+ %external_refs = %{ $PG->{external_refs}};
+
+ @KEPT_EXTRA_ANSWERS =(); #temporary hack
+
+ my %envir = %$rh_envir;
+ $displayMode = $PG->{displayMode};
+ $PG_random_generator = $PG->{PG_random_generator};
+ # Save the file name for use in error messages
+ # Doesn't appear to be used FIXME
+# my ($callpkg,$callfile) = caller(0);
+# $envir{__files__}{$callfile} = $envir{templateDirectory}.$envir{fileName};
+
+ #no strict;
+ foreach my $var (keys %envir) {
+ PG_restricted_eval(qq!\$main::$var = \$envir{$var}!); #whew!! makes sure $var is interpolated but $main:: is evaluated at run time.
+ warn "Problem defining $var while initializing the PG problem: $@" if $@;
+ }
+ #use strict;
+ #FIXME
+ # load java script needed for displayModes
+ if ($envir{displayMode} eq 'HTML_jsMath') {
+ my $prefix = "";
+ if (!$envir{jsMath}{reportMissingFonts}) {
+ $prefix .= '<SCRIPT>noFontMessage = 1</SCRIPT>'."\n";
+ } elsif ($main::envir{jsMath}{missingFontMessage}) {
+ $prefix .= '<SCRIPT>missingFontMessage = "'.$main::envir{jsMath}{missingFontMessage}.'"</SCRIPT>'."\n";
+ }
+ $prefix .= '<SCRIPT>processDoubleClicks = '.($main::envir{jsMath}{processDoubleClicks}?'1':'0')."</SCRIPT>\n";
+ TEXT(
+ $prefix,
+ '<SCRIPT SRC="'.$envir{jsMathURL}. '"></SCRIPT>' . "\n" ,
+ '<NOSCRIPT><CENTER><FONT COLOR="#CC0000">' ,
+ "$BBOLD", 'Warning: the mathematics on this page requires JavaScript.', ,$BR,
+ 'If your browser supports it, be sure it is enabled.',
+ "$EBOLD",
+ '</FONT></CENTER><p>
+ </NOSCRIPT>'
+ );
+ TEXT('<SCRIPT>jsMath.Setup.Script("plugins/noImageFonts.js")</SCRIPT>')
+ if ($envir{jsMath}{noImageFonts});
+ } elsif ($envir{displayMode} eq 'HTML_asciimath') {
+ TEXT('<SCRIPT SRC="'.$main::envir{asciimathURL}.'"></SCRIPT>' . "\n" ,
+ '<SCRIPT>mathcolor = "black"</SCRIPT>' );
+ } elsif ($envir{displayMode} eq 'HTML_LaTeXMathML') {
+ TEXT('<SCRIPT SRC="'.$envir{LaTeXMathMLURL}.'"></SCRIPT>'."\n");
+
+ }
+
+}
+$main::displayMode = $PG->{displayMode};
+$main::PG = $PG;
+sub TEXT {
+ $PG->TEXT(@_) ;
+}
+
+sub HEADER_TEXT {
+ $PG->HEADER_TEXT(@_);
+}
+
+sub LABELED_ANS {
+ $PG->LABELED_ANS(@_); # returns pointer to the labeled answer group
+}
+
+sub NAMED_ANS {
+ $PG->LABELED_ANS(@_); # returns pointer to the labeled answer group
+}
+
+sub ANS {
+ #warn "using PGnew for ANS";
+ $PG->ANS(@_); # returns pointer to the labeled answer group
+}
+
+sub RECORD_ANS_NAME {
+ $PG->record_ans_name(@_);
+}
+
+sub inc_ans_rule_count {
+ #$PG->{unlabeled_answer_blank_count}++;
+ #my $num = $PG->{unlabeled_answer_blank_count};
+ DEBUG_MESSAGE( " using PG to inc_ans_rule_count = $num ", caller(2));
+ warn " using PG to inc_ans_rule_count = $num ", caller(2);
+ $PG->{unlabeled_answer_blank_count};
+}
+sub ans_rule_count {
+ $PG->{unlabeled_answer_blank_count};
+}
+sub NEW_ANS_NAME {
+ return "" if $PG_STOP_FLAG;
+ #my $number=shift;
+ # we have an internal count so the number not actually used.
+ my $name =$PG->record_unlabeled_ans_name();
+ $name;
+}
+sub NEW_ARRAY_NAME {
+ return "" if $PG_STOP_FLAG;
+ my $name =$PG->record_unlabeled_array_name();
+ $name;
+}
+
+# new subroutine
+sub NEW_ANS_BLANK {
+ return "" if $PG_STOP_FLAG;
+ $PG->record_unlabeled_ans_name(@_);
+}
+
+sub ANS_NUM_TO_NAME {
+ $PG->new_label(@_); # behaves as in PG.pl
+}
+
+sub store_persistent_data {
+ $PG->store_persistent_data(@_); #needs testing
+}
+sub RECORD_FORM_LABEL { # this stores form data (such as sticky answers), but does nothing more
+ # it's a bit of hack since we are storing these in the
+ # KEPT_EXTRA_ANSWERS queue even if they aren't answers per se.
+ warn "Using RECORD_FORM_LABEL -- deprecated?";
+ RECORD_EXTRA_ANSWERS(@_);
+}
+
+sub RECORD_EXTRA_ANSWERS {
+ return "" if $PG_STOP_FLAG;
+ my $label = shift; # the label of the input box or textarea
+ eval(q!push(@main::KEPT_EXTRA_ANSWERS, $label)!); #put the labels into the hash to be caught later for recording purposes
+ $label;
+
+}
+
+
+sub NEW_ANS_ARRAY_NAME { # this keeps track of the answers within an array which are entered implicitly,
+ # rather than with a specific label
+ return "" if $PG_STOP_FLAG;
+ my $number=shift;
+ $main::vecnum = -1;
+ my $row = shift;
+ my $col = shift;
+# my $array_ans_eval_label = "ArRaY"."$number"."__"."$vecnum".":";
+ my $label = $PG->{QUIZ_PREFIX}.$PG->{ARRAY_PREFIX}."$number"."__"."$vecnum".":"."$row".":"."$col"."__";
+# my $response_group = new PGresponsegroup($label,undef);
+# $PG->record_ans_name($array_ans_eval_label, $response_group);
+# What does vecnum do?
+# The name is simply so that it won't conflict when placed on the HTML page
+# my $array_label = shift;
+ $PG->record_array_name($label); # returns $array_label, $ans_label
+}
+
+sub NEW_ANS_ARRAY_NAME_EXTENSION {
+ NEW_ANS_ARRAY_ELEMENT_NAME(@_);
+}
+
+sub NEW_ANS_ARRAY_ELEMENT_NAME { # creates a new array element answer name and records it
+
+ return "" if $PG_STOP_FLAG;
+ my $number=shift;
+ my $row_num = shift;
+ my $col_num = shift;
+ if( $row_num == 0 && $col_num == 0 ){
+ $main::vecnum += 1;
+ }
+# my $ans_label = "ArRaY".sprintf("%04u", $number);
+ my $ans_label = $PG->new_array_label($number);
+ my $element_ans_label = $PG->new_array_element_label($ans_label,$row_num, $col_num,vec_num=>$vecnum);
+ my $response = new PGresponsegroup($ans_label,$element_ans_label, undef);
+ $PG->extend_ans_group($ans_label,$response);
+ $element_ans_label;
+}
+sub NEW_LABELED_ANS_ARRAY { #not in PG_original
+ my $ans_label = shift;
+ my @response_list = @_;
+ #$PG->extend_ans_group($ans_label,@response_list);
+ $PG->{PG_ANSWERS_HASH}->{$ans_label}->insert_responses(@response_list);
+ # should this return an array of labeled answer blanks???
+}
+sub EXTEND_ANS_ARRAY { #not in PG_original
+ my $ans_label = shift;
+ my @response_list = @_;
+ #$PG->extend_ans_group($ans_label,@response_list);
+ $PG->{PG_ANSWERS_HASH}->{$ans_label}->append_responses(@response_list);
+}
+sub CLEAR_RESPONSES {
+ my $ans_label = shift;
+# my $response_label = shift;
+# my $ans_value = shift;
+ if (defined ($PG->{PG_ANSWERS_HASH}->{$ans_label}) ) {
+ my $responsegroup = $PG->{PG_ANSWERS_HASH}->{$ans_label}->{response};
+ if ( ref($responsegroup) ) {
+ $responsegroup->clear;
+ } else {
+ $responsegroup = $PG->{PG_ANSWERS_HASH}->{$ans_label}->{response} = new PGresponsegroup($label);
+ }
+ }
+ '';
+}
+sub INSERT_RESPONSE {
+ my $ans_label = shift;
+ my $response_label = shift;
+ my $ans_value = shift;
+ my $selected = shift;
+ # warn "\n\nanslabel $ans_label responselabel $response_label value $ans_value";
+ if (defined ($PG->{PG_ANSWERS_HASH}->{$ans_label}) ) {
+ my $responsegroup = $PG->{PG_ANSWERS_HASH}->{$ans_label}->{response};
+ $responsegroup->append_response($response_label, $ans_value, $selected);
+ #warn "\n$responsegroup responses are now ", $responsegroup->responses;
+ }
+ '';
+}
+
+sub EXTEND_RESPONSE { # for radio buttons and checkboxes
+ my $ans_label = shift;
+ my $response_label = shift;
+ my $ans_value = shift;
+ my $selected = shift;
+ # warn "\n\nanslabel $ans_label responselabel $response_label value $ans_value";
+ if (defined ($PG->{PG_ANSWERS_HASH}->{$ans_label}) ) {
+ my $responsegroup = $PG->{PG_ANSWERS_HASH}->{$ans_label}->{response};
+ $responsegroup->extend_response($response_label, $ans_value,$selected);
+ warn "\n$responsegroup responses are now ", pretty_print($response_group);
+ }
+ '';
+}
+sub ENDDOCUMENT {
+ # check that answers match
+ # gather up PG_FLAGS elements
+
+
+ my @elements = qw(showPartialCorrectAnswers
+ recordSubmittedAnswers refreshCachedImages
+ hintExists solutionExists
+ );
+ while (@elements) {
+ my $var= shift @elements;
+ $PG->{PG_FLAGS}->{$var} = ${$var};
+ }
+ $PG->{PG_FLAGS}->{comment} = $pgComment; #KLUDGE #FIXME
+ $PG->{PG_FLAGS}->{showHintLimit} = $showHint; #KLUDGE #FIXME
+
+
+ # install problem grader
+ if (defined($PG->{PG_FLAGS}->{PROBLEM_GRADER_TO_USE}) ) {
+ # problem grader defined within problem -- no further action needed
+ } elsif ( defined( $rh_envir->{PROBLEM_GRADER_TO_USE} ) ) {
+ if (ref($rh_envir->{PROBLEM_GRADER_TO_USE}) eq 'CODE' ) { # user defined grader
+ $PG->{PG_FLAGS}->{PROBLEM_GRADER_TO_USE} = $rh_envir->{PROBLEM_GRADER_TO_USE};
+ } elsif ($rh_envir->{PROBLEM_GRADER_TO_USE} eq 'std_problem_grader' ) {
+ if (defined(&std_problem_grader) ){
+ $PG->{PG_FLAGS}->{PROBLEM_GRADER_TO_USE} = \&std_problem_grader; # defined in PGanswermacros.pl
+ } # std_problem_grader is the default in any case so don't give a warning.
+ } elsif ($rh_envir->{PROBLEM_GRADER_TO_USE} eq 'avg_problem_grader' ) {
+ if (defined(&avg_problem_grader) ){
+ $PG->{PG_FLAGS}->{PROBLEM_GRADER_TO_USE} = \&avg_problem_grader; # defined in PGanswermacros.pl
+ }
+ } else {
+ warn "Error: ". $PG->{PG_FLAGS}->{PROBLEM_GRADER_TO_USE} . "is not a known program grader.";
+ }
+ } elsif (defined(&std_problem_grader)) {
+ $PG->{PG_FLAGS}->{PROBLEM_GRADER_TO_USE} = \&std_problem_grader; # defined in PGanswermacros.pl
+ } else {
+ # PGtranslator will install its default problem grader
+ }
+
+ # add javaScripts
+ if ($rh_envir->{displayMode} eq 'HTML_jsMath') {
+ TEXT('<SCRIPT> jsMath.wwProcess() </SCRIPT>');
+ } elsif ($rh_envir->{displayMode} eq 'HTML_asciimath') {
+ TEXT('<SCRIPT> translate() </SCRIPT>');
+ my $STRING = join("", @{$PG->{HEADER_ARRAY} });
+ unless ($STRING =~ m/mathplayer/) {
+ HEADER_TEXT('<object id="mathplayer" classid="clsid:32F66A20-7614-11D4-BD11-00104BD3F987">' . "\n" .
+ '</object><?import namespace="mml" implementation="#mathplayer"?>'
+ );
+ }
+
+ }
+ TEXT( MODES(%{$rh_envir->{problemPostamble}}) );
+
+
+
+
+
+ @PG_ANSWERS=();
+
+ #warn keys %{ $PG->{PG_ANSWERS_HASH} };
+ @PG_ANSWER_ENTRY_ORDER = ();
+ my $ans_debug = 0;
+ foreach my $key (keys %{ $PG->{PG_ANSWERS_HASH} }) {
+ $answergroup = $PG->{PG_ANSWERS_HASH}->{$key};
+ #warn "$key is defined =", defined($answergroup), "PG object is $PG";
+ #################
+ # EXTRA ANSWERS KLUDGE
+ #################
+ # The first response in each answer group is placed in @PG_ANSER_ENTRY_ORDER and %PG_ANSWERS_HASH
+ # The remainder of the response keys are placed in the EXTRA ANSWERS ARRAY
+ if (defined($answergroup)) {
+ my @response_keys = $answergroup->{response}->response_labels;
+ warn pretty_print($answergroup->{response}) if $ans_debug==1;
+ my $response_key = shift @response_keys;
+ #unshift @response_keys, $response_key unless ($response_key eq $answer_group->{ans_label});
+ # don't save the first response key if it is the same as the ans_label
+ # maybe we should insure that the first response key is always the same as the answer label?
+ # even if no answer blank is printed for it? or a hidden answer blank?
+ # this is still a KLUDGE
+ # for compatibility the first response key is closer to the old method than the $ans_label
+ # this is because a response key might indicate an array but an answer label won't
+ push @PG_ANSWERS, $response_key,$answergroup->{ans_eval};
+ push @PG_ANSWER_ENTRY_ORDER, $response_key;
+ push @KEPT_EXTRA_ANSWERS, @response_keys;
+ } else {
+ #warn "$key is ", join("|",%{$PG->{PG_ANSWERS_HASH}->{$key}});
+ }
+ }
+ push @KEPT_EXTRA_ANSWERS, keys %{$PG->{PERSISTENCE_HASH}};
+ my %PG_ANSWERS_HASH = @PG_ANSWERS;
+ $PG->{PG_FLAGS}->{KEPT_EXTRA_ANSWERS} = \@KEPT_EXTRA_ANSWERS;
+ $PG->{PG_FLAGS}->{ANSWER_ENTRY_ORDER} = \@PG_ANSWER_ENTRY_ORDER;
+ warn "KEPT_EXTRA_ANSWERS", join(" ", @KEPT_EXTRA_ANSWERS), $BR if $ans_debug==1;
+ warn "PG_ANSWER_ENTRY_ORDER",join(" ",@PG_ANSWER_ENTRY_ORDER), $BR if $ans_debug==1;
+ warn "DEBUG messages", join( "$BR",@{$PG->get_debug_messages} ) if $ans_debug==1;
+ warn "INTERNAL_DEBUG messages", join( "$BR",@{$PG->get_internal_debug_messages} ) if $ans_debug==1;
+ $STRINGforOUTPUT = join("", @{$PG->{OUTPUT_ARRAY} });
+
+
+ $STRINGforHEADER_TEXT = join("", @{$PG->{HEADER_ARRAY} });
+
+ # warn pretty_print($PG->{PG_ANSWERS_HASH});
+ #warn "printing another warning";
+
+ (\$STRINGforOUTPUT, \$STRINGforHEADER_TEXT,\%PG_ANSWERS_HASH, $PG->{PG_FLAGS} , $PG );
+}
+################################################################################
+#
+# macros from dangerousMacros
+#
+################################################################################
+sub alias {
+ #warn "alias called ",@_;
+ $PG->{PG_alias}->make_alias(@_) ;
+}
+sub insertGraph {
+ $PG->insertGraph(@_);
+}
+
+sub findMacroFile {
+ $PG->{PG_alias}->findMacroFile(@_);
+}
+sub check_url {
+ $PG->{PG_alias}->check_url(@_);
+}
+sub findAppletCodebase {
+ $PG->{PG_alias}->findAppletCodebase(@_);
+}
+
+sub loadMacros {
+ $PG->{PG_loadMacros}->loadMacros(@_);
+}
+# FIXME? these were taken from the former dangerousMacros.pl file and might have issues when placed here.
+#
+# Some constants that can be used in perl experssions
+#
+
+# ^function i
+# ^uses $_parser_loaded
+# ^uses &Complex::i
+# ^uses &Value::Package
+sub i () {
+ # check if Parser.pl is loaded, otherwise use Complex package
+ if (!eval(q!$main::_parser_loaded!)) {return Complex::i}
+ return Value->Package("Formula")->new('i')->eval;
+}
+
+# ^function j
+# ^uses $_parser_loaded
+# ^uses &Value::Package
+sub j () {
+ if (!eval(q!$main::_parser_loaded!)) {return 'j'}
+ Value->Package("Formula")->new('j')->eval;
+}
+
+# ^function k
+# ^uses $_parser_loaded
+# ^uses &Value::Package
+sub k () {
+ if (!eval(q!$main::_parser_loaded!)) {return 'k'}
+ Value->Package("Formula")->new('k')->eval;
+}
+
+# ^function pi
+# ^uses &Value::Package
+sub pi () {Value->Package("Formula")->new('pi')->eval}
+
+# ^function Infinity
+# ^uses &Value::Package
+sub Infinity () {Value->Package("Infinity")->new()}
+
+
+# ^function abs
+# ^function sqrt
+# ^function exp
+# ^function log
+# ^function sin
+# ^function cos
+# ^function atan2
+#
+# Allow these functions to be overridden
+# (needed for log() to implement $useBaseTenLog)
+#
+use subs 'abs', 'sqrt', 'exp', 'log', 'sin', 'cos', 'atan2';
+sub abs($) {return CORE::abs($_[0])};
+sub sqrt($) {return CORE::sqrt($_[0])};
+sub exp($) {return CORE::exp($_[0])};
+sub log($) {return CORE::log($_[0])};
+sub sin($) {return CORE::sin($_[0])};
+sub cos($) {return CORE::cos($_[0])};
+sub atan2($$) {return CORE::atan2($_[0],$_[1])};
+
+sub Parser::defineLog {eval {sub log($) {CommonFunction->Call("log",@_)}}};
+=head2 Filter utilities
+
+These two subroutines can be used in filters to set default options. They
+help make filters perform in uniform, predictable ways, and also make it
+easy to recognize from the code which options a given filter expects.
+
+
+=head4 assign_option_aliases
+
+Use this to assign aliases for the standard options. It must come before set_default_options
+within the subroutine.
+
+ assign_option_aliases(\%options,
+ 'alias1' => 'option5'
+ 'alias2' => 'option7'
+ );
+
+
+If the subroutine is called with an option " alias1 => 23 " it will behave as if it had been
+called with the option " option5 => 23 "
+
+=cut
+
+
+# ^function assign_option_aliases
+sub assign_option_aliases {
+ my $rh_options = shift;
+ warn "The first entry to set_default_options must be a reference to the option hash" unless ref($rh_options) eq 'HASH';
+ my @option_aliases = @_;
+ while (@option_aliases) {
+ my $alias = shift @option_aliases;
+ my $option_key = shift @option_aliases;
+
+ if (defined($rh_options->{$alias} )) { # if the alias appears in the option list
+ if (not defined($rh_options->{$option_key}) ) { # and the option itself is not defined,
+ $rh_options->{$option_key} = $rh_options->{$alias}; # insert the value defined by the alias into the option value
+ # the FIRST alias for a given option takes precedence
+ # (after the option itself)
+ } else {
+ warn "option $option_key is already defined as", $rh_options->{$option_key}, "<br>\n",
+ "The attempt to override this option with the alias $alias with value ", $rh_options->{$alias},
+ " was ignored.";
+ }
+ }
+ delete($rh_options->{$alias}); # remove the alias from the initial list
+ }
+
+}
+
+=head4 set_default_options
+
+ set_default_options(\%options,
+ '_filter_name' => 'filter',
+ 'option5' => .0001,
+ 'option7' => 'ascii',
+ 'allow_unknown_options => 0,
+ }
+
+Note that the first entry is a reference to the options with which the filter was called.
+
+The option5 is set to .0001 unless the option is explicitly set when the subroutine is called.
+
+The B<'_filter_name'> option should always be set, although there is no error if it is missing.
+It is used mainly for debugging answer evaluators and allows
+you to keep track of which filter is currently processing the answer.
+
+If B<'allow_unknown_options'> is set to 0 then if the filter is called with options which do NOT appear in the
+set_default_options list an error will be signaled and a warning message will be printed out. This provides
+error checking against misspelling an option and is generally what is desired for most filters.
+
+Occasionally one wants to write a filter which accepts a long list of options, not all of which are known in advance,
+but only uses a subset of the options
+provided. In this case, setting 'allow_unkown_options' to 1 prevents the error from being signaled.
+
+=cut
+
+# ^function set_default_options
+# ^uses pretty_print
+sub set_default_options {
+ my $rh_options = shift;
+ warn "The first entry to set_default_options must be a reference to the option hash" unless ref($rh_options) eq 'HASH';
+ my %default_options = @_;
+ unless ( defined($default_options{allow_unknown_options}) and $default_options{allow_unknown_options} == 1 ) {
+ foreach my $key1 (keys %$rh_options) {
+ warn "This option |$key1| is not recognized in this subroutine<br> ", pretty_print($rh_options) unless exists($default_options{$key1});
+ }
+ }
+ foreach my $key (keys %default_options) {
+ if ( not defined($rh_options->{$key} ) and defined( $default_options{$key} ) ) {
+ $rh_options->{$key} = $default_options{$key}; #this allows tol => undef to allow the tol option, but doesn't define
+ # this key unless tol is explicitly defined.
+ }
+ }
+}
+1;
+__END__
+
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/
+# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/
# $CVSHeader$
#
# This program is free software; you can redistribute it and/or modify it under
@@ -93,195 +646,8 @@
The HEADER_TEXT(), TEXT(), and ANS() macros add to the header text string,
body text string, and answer evaluator queue, respectively.
-=cut
-
-BEGIN {
- be_strict();
-}
-
-sub _PG_init{
-
-}
-
-#package PG;
-
-# Private variables for the PG.pl file.
-
-# ^variable my $STRINGforOUTPUT
-my $STRINGforOUTPUT;
-# ^variable my $STRINGforHEADER_TEXT
-my $STRINGforHEADER_TEXT;
-# ^variable my @PG_ANSWERS
-my @PG_ANSWERS;
-# ^variable my @PG_UNLABELED_ANSWERS
-my @PG_UNLABELED_ANSWERS;
-# ^variable my %PG_ANSWERS_HASH
-my %PG_ANSWERS_HASH;
-
-# ^variable our $PG_STOP_FLAG
-our $PG_STOP_FLAG;
-
-# my variables are unreliable if two DOCUMENTS were to be called before an ENDDOCUMENT
-# there could be conflicts. As I understand the behavior of the Apache child
-# this cannot occur -- a child finishes with one request before obtaining the next
-
-################################################################################
-
-=head1 MACROS
-
-These macros may be used from PG problem files.
-
=over
-=item DOCUMENT()
-
-DOCUMENT() should be the first statement in each problem template. It can
-only be used once in each problem.
-
-DOCUMENT() initializes some empty variables and unpacks the variables in the
-%envir hash which is implicitly passed from WeBWorK to the problem. It must be
-the first statement in any problem. It also unpacks any answers submitted and
-places them in the @submittedAnswer list, saves the problem seed in
-$PG_original_problemSeed in case you need it later, and initializes the pseudo
-random number generator object in $PG_random_generator.
-
-You can reset the standard number generator using the command:
-
- $PG_random_generator->srand($new_seed_value);
-
-See also SRAND() in the L<PGbasicmacros.pl> file.
-
-=cut
-
-# ^function DOCUMENT
-# ^uses $STRINGforOUTPUT
-# ^uses $STRINGforHEADER_TEXT
-# ^uses @PG_ANSWERS
-# ^uses $PG_STOP_FLAG
-# ^uses @PG_UNLABELED_ANSWERS
-# ^uses %PG_ANSWERS_HASH
-# ^uses @PG_ANSWER_ENTRY_ORDER
-# ^uses $ANSWER_PREFIX
-# ^uses %PG_FLAGS
-# ^uses $showPartialCorrectAnswers
-# ^uses $showHints
-# ^uses $solutionExists
-# ^uses $hintExists
-# ^uses $pgComment
-# ^uses %gifs_created
-# ^uses %envir
-# ^uses $refSubmittedAnswers
-# ^uses @submittedAnswers
-# ^uses $PG_original_problemSeed
-# ^uses $problemSeed
-# ^uses $PG_random_generator
-# ^uses $ans_rule_count
-# ^uses $QUIZ_PREFIX
-# (Also creates a package scalar named after each key in %envir containing a copy of the corresponding value.)
-# ^uses &PGrandom::new
-sub DOCUMENT {
-
- $STRINGforOUTPUT ="";
- $STRINGforHEADER_TEXT ="";
- @PG_ANSWERS=();
- $PG_STOP_FLAG=0;
- @PG_UNLABELED_ANSWERS = ();
- %PG_ANSWERS_HASH = ();
- # FIXME: We are initializing these variables into both Safe::Root1 (the cached safe compartment)
- # and Safe::Root2 (the current one)
- # There is a good chance they won't be properly updated in one or the other of these compartments.
-
-
-# @main::PG_ANSWER_ENTRY_ORDER = ();
-# $main::ANSWER_PREFIX = 'AnSwEr';
-# %main::PG_FLAGS=(); #global flags
-# $main::showPartialCorrectAnswers = 0 unless defined($main::showPartialCorrectAnswers );
-# $main::showHint = 1 unless defined($main::showHint);
-# $main::solutionExists =0;
-# $main::hintExists =0;
-# %main::gifs_created = ();
- eval(q!
- # set perl to use capital E for scientific notation: e.g. 5.4E-05 instead of 5.4e-05
- # $#="%G"; #FIXME -- this causes bad warnings in perl 5.10
-
- @main::PG_ANSWER_ENTRY_ORDER = ();
- $main::ANSWER_PREFIX = 'AnSwEr';
- %main::PG_FLAGS=(); #global flags
- $main::showPartialCorrectAnswers = 0 unless defined($main::showPartialCorrectAnswers );
- $main::showHint = 1 unless defined($main::showHint);
- $main::solutionExists =0;
- $main::hintExists =0;
- $main::pgComment = '';
- %main::gifs_created = ();
-
- !);
-# warn eval(q! "PG.pl: The envir variable $main::{envir} is".join(" ",%main::envir)!);
- my $rh_envir = eval(q!\%main::envir!);
- my %envir = %$rh_envir;
-
- # Save the file name for use in error messages
- my ($callpkg,$callfile) = caller(0);
- $envir{__files__}{$callfile} = $envir{templateDirectory}.$envir{fileName};
-
- #no strict;
- foreach my $var (keys %envir) {
- eval(q!$main::!.$var.q! = $main::envir{!.$var.q!}! ); #whew!! makes sure $var is interpolated but $main:: is evaluated at run time.
- # warn eval(q! "var $var is defined ". $main::!.$var);
- warn "Problem defining ", q{\$main::}.$var, " while initializing the PG problem: $@" if $@;
- }
- #use strict;
- #FIXME these strict pragmas don't seem to be needed and they cause trouble in perl 5.6.0
-
-
-
- eval(q!
- @main::submittedAnswers = @{$main::refSubmittedAnswers} if defined($main::refSubmittedAnswers);
- $main::PG_original_problemSeed = $main::problemSeed;
- $main::PG_random_generator = new PGrandom($main::problemSeed) || die "Can't create random number generator.";
- $main::ans_rule_count = 0; # counts questions
-
- # end unpacking of environment variables.
- $main::QUIZ_PREFIX = '' unless defined($main::QUIZ_PREFIX)
-
- !);
-# @main::submittedAnswers = @{$main::refSubmittedAnswers} if defined($main::refSubmittedAnswers);
-# $main::PG_original_problemSeed = $main::problemSeed;
-# $main::PG_random_generator = new PGrandom($main::problemSeed) || die "Can't create random number generator.";
-# $main::ans_rule_count = 0; # counts questions
-
- # end unpacking of environment variables.
-# $main::QUIZ_PREFIX = '' unless defined($main::QUIZ_PREFIX)
-
- if ($main::envir{displayMode} eq 'HTML_jsMath') {
- my $prefix = "";
- if (!$main::envir{jsMath}{reportMissingFonts}) {
- $prefix .= '<SCRIPT>noFontMessage = 1</SCRIPT>'."\n";
- } elsif ($main::envir{jsMath}{missingFontMessage}) {
- $prefix .= '<SCRIPT>missingFontMessage = "'.$main::envir{jsMath}{missingFontMessage}.'"</SCRIPT>'."\n";
- }
- $prefix .= '<SCRIPT>processDoubleClicks = '.($main::envir{jsMath}{processDoubleClicks}?'1':'0')."</SCRIPT>\n";
- $STRINGforOUTPUT =
- $prefix .
- '<SCRIPT SRC="'.$main::envir{jsMathURL}.'"></SCRIPT>' . "\n" .
- '<NOSCRIPT><CENTER><FONT COLOR="#CC0000">' .
- '<B>Warning: the mathematics on this page requires JavaScript.<BR>' .
- 'If your browser supports it, be sure it is enabled.</B>'.
- '</FONT></CENTER><p></NOSCRIPT>' .
- $STRINGforOUTPUT;
- $STRINGforOUTPUT .=
- '<SCRIPT>jsMath.Setup.Script("plugins/noImageFonts.js")</SCRIPT>'
- if ($main::envir{jsMath}{noImageFonts});
- }
-
- $STRINGforOUTPUT = '<SCRIPT SRC="'.$main::envir{asciimathURL}.'"></SCRIPT>' . "\n" .
- '<SCRIPT>mathcolor = "black"</SCRIPT>' . $STRINGforOUTPUT
- if ($main::envir{displayMode} eq 'HTML_asciimath');
-
- $STRINGforOUTPUT = '<SCRIPT SRC="'.$main::envir{LaTeXMathMLURL}.'"></SCRIPT>'."\n" . $STRINGforOUTPUT
- if ($main::envir{displayMode} eq 'HTML_LaTeXMathML');
-
-}
-
=item HEADER_TEXT()
HEADER_TEXT("string1", "string2", "string3");
@@ -296,14 +662,7 @@
introduced between the existing content of the header text string and the new
content being appended.
-=cut
-# ^function HEADER_TEXT
-# ^uses $STRINGforHEADER_TEXT
-sub HEADER_TEXT {
- my @in = @_;
- $STRINGforHEADER_TEXT .= join(" ",@in);
- }
=item TEXT()
@@ -326,16 +685,7 @@
introduced between the existing content of the header text string and the new
content being appended.
-=cut
-# ^function TEXT
-# ^uses $PG_STOP_FLAG
-# ^uses $STRINGforOUTPUT
-sub TEXT {
- return "" if $PG_STOP_FLAG;
- my @in = @_;
- $STRINGforOUTPUT .= join(" ",@in);
-}
=item ANS()
@@ -352,21 +702,7 @@
evaluator generator such as the cmp() method of MathObjects or the num_cmp()
macro in L<PGanswermacros.pl>.
-=cut
-# ^function ANS
-# ^uses $PG_STOP_FLAG
-# ^uses @PG_ANSWERS
-sub ANS{
- return "" if $PG_STOP_FLAG;
- my @in = @_;
- while (@in ) {
- warn("<BR><B>Error in ANS:$in[0]</B> -- inputs must be references to
- subroutines<BR>")
- unless ref($in[0]);
- push(@PG_ANSWERS, shift @in );
- }
-}
=item LABELED_ANS()
@@ -378,35 +714,8 @@
order entered. This allows pairing of answer evaluators and answer rules that
may not have been entered in the same order.
-=cut
-
-# ^function LABELED_ANS
-# ^uses &NAMED_ANS
-sub LABELED_ANS {
- &NAMED_ANS;
-}
-
-=item NAMED_ANS()
-Old name for LABELED_ANS(). DEPRECATED.
-
-=cut
-# ^function NAMED_ANS
-# ^uses $PG_STOP_FLAG
-sub NAMED_ANS{
- return "" if $PG_STOP_FLAG;
- my @in = @_;
- while (@in ) {
- my $label = shift @in;
- $label = eval(q!$main::QUIZ_PREFIX.$label!);
- my $ans_eval = shift @in;
- TEXT("<BR><B>Error in NAMED_ANS:$in[0]</B>
- -- inputs must be references to subroutines<BR>")
- unless ref($ans_eval);
- $PG_ANSWERS_HASH{$label}= $ans_eval;
- }
-}
=item STOP_RENDERING()
@@ -415,14 +724,7 @@
Temporarily suspends accumulation of problem text and storing of answer blanks
and answer evaluators until RESUME_RENDERING() is called.
-=cut
-# ^function STOP_RENDERING
-# ^uses $PG_STOP_FLAG
-sub STOP_RENDERING {
- $PG_STOP_FLAG=1;
- "";
-}
=item RESUME_RENDERING()
@@ -431,14 +733,7 @@
Resumes accumulating problem text and storing answer blanks and answer
evaluators. Reverses the effect of STOP_RENDERING().
-=cut
-# ^function RESUME_RENDERING
-# ^uses $PG_STOP_FLAG
-sub RESUME_RENDERING {
- $PG_STOP_FLAG=0;
- "";
-}
=item ENDDOCUMENT()
@@ -449,7 +744,8 @@
be the last executable statement of every problem. It can only appear once. It
returns a list consisting of:
-=over
+
+
=item *
@@ -468,7 +764,7 @@
A reference to a hash containing various flags:
-=over
+
=item *
@@ -532,85 +828,9 @@
=back
-=back
-
-=cut
-
-# ^function ENDDOCUMENT
-# ^uses @PG_UNLABELED_ANSWERS
-# ^uses %PG_ANSWERS_HASH
-# ^uses @PG_ANSWERS
-sub ENDDOCUMENT {
- my $index=0;
- foreach my $label (@PG_UNLABELED_ANSWERS) {
- if ( defined($PG_ANSWERS[$index]) ) {
- $PG_ANSWERS_HASH{"$label"}= $PG_ANSWERS[$index];
- #warn "recording answer label = $label";
- } else {
- warn "No answer provided by instructor for answer $label";
- }
- $index++;
- }
- $STRINGforOUTPUT .="\n";
- eval q{ #make sure that "main" points to the current safe compartment by evaluating these lines.
- $main::PG_FLAGS{'showPartialCorrectAnswers'} = $main::showPartialCorrectAnswers;
- $main::PG_FLAGS{'recordSubmittedAnswers'} = $main::recordSubmittedAnswers;
- $main::PG_FLAGS{'refreshCachedImages'} = $main::refreshCachedImages;
- $main::PG_FLAGS{'comment'} = $main::pgComment;
- $main::PG_FLAGS{'hintExists'} = $main::hintExists;
- $main::PG_FLAGS{'showHintLimit'} = $main::showHint;
- $main::PG_FLAGS{'solutionExists'} = $main::solutionExists;
- $main::PG_FLAGS{ANSWER_ENTRY_ORDER} = \@main::PG_ANSWER_ENTRY_ORDER;
- $main::PG_FLAGS{KEPT_EXTRA_ANSWERS} = \@main::KEPT_EXTRA_ANSWERS;##need to keep array labels that don't call "RECORD_ANS_NAME"
- $main::PG_FLAGS{ANSWER_PREFIX} = $main::ANSWER_PREFIX;
- # install problem grader
- if (defined($main::PG_FLAGS{PROBLEM_GRADER_TO_USE}) ) {
- # problem grader defined within problem -- no further action needed
- } elsif ( defined( $main::envir{PROBLEM_GRADER_TO_USE} ) ) {
- if (ref($main::envir{PROBLEM_GRADER_TO_USE}) eq 'CODE' ) { # user defined grader
- $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} = $main::envir{PROBLEM_GRADER_TO_USE};
- } elsif ($main::envir{PROBLEM_GRADER_TO_USE} eq 'std_problem_grader' ) {
- if (defined(&std_problem_grader) ){
- $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} = \&std_problem_grader; # defined in PGanswermacros.pl
- } # std_problem_grader is the default in any case so don't give a warning.
- } elsif ($main::envir{PROBLEM_GRADER_TO_USE} eq 'avg_problem_grader' ) {
- if (defined(&avg_problem_grader) ){
- $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} = \&avg_problem_grader; # defined in PGanswermacros.pl
- }
- #else { # avg_problem_grader will be installed by PGtranslator so there is no need for a warning.
- # warn "The problem grader 'avg_problem_grader' has not been defined. Has PGanswermacros.pl been loaded?";
- #}
- } else {
- warn "Error: $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} is not a known program grader.";
- }
- } elsif (defined(&std_problem_grader)) {
- $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} = \&std_problem_grader; # defined in PGanswermacros.pl
- } else {
- # PGtranslator will install its default problem grader
- }
-
- warn "ERROR: The problem grader is not a subroutine" unless ref( $main::PG_FLAGS{PROBLEM_GRADER_TO_USE}) eq 'CODE'
- or $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} = 'std_problem_grader'
- or $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} = 'avg_problem_grader';
- # return results
- };
-
- $STRINGforOUTPUT .= '<SCRIPT> jsMath.wwProcess() </SCRIPT>'
- if ($main::envir{displayMode} eq 'HTML_jsMath');
-
- if ($main::envir{displayMode} eq 'HTML_asciimath') {
- $STRINGforOUTPUT .= '<SCRIPT> translate() </SCRIPT>';
- $STRINGforHEADER_TEXT .=
- '<object id="mathplayer" classid="clsid:32F66A20-7614-11D4-BD11-00104BD3F987">' . "\n" .
- '</object><?import namespace="mml" implementation="#mathplayer"?>'
- unless ($STRINGforHEADER_TEXT =~ m/mathplayer/);
- }
- $STRINGforOUTPUT .= MODES(%{PG_restricted_eval('$main::problemPostamble')});
-
- (\$STRINGforOUTPUT, \$STRINGforHEADER_TEXT,\%PG_ANSWERS_HASH,eval(q!\%main::PG_FLAGS!));
-}
+=cut
################################################################################
@@ -624,7 +844,7 @@
=item inc_ans_rule_count()
- NEW_ANS_NAME(inc_ans_rule_count());
+ NEW_ANS_NAME();
Increments the internal count of the number of answer blanks that have been
defined ($ans_rule_count) and returns the new count. This should only be used
@@ -632,55 +852,25 @@
=cut
-# ^function inc_ans_rule_count
-# ^uses $ans_rule_count
-sub inc_ans_rule_count {
- eval(q!++$main::ans_rule_count!); # evalute at runtime to get correct main::
-}
-
=item RECORD_ANS_NAME()
- RECORD_ANS_NAME("label");
+ RECORD_ANS_NAME("label", "VALUE");
Records the label for an answer blank. Used internally by L<PGbasicmacros.pl>
to record the order of explicitly-labelled answer blanks.
=cut
-# ^function RECORD_ANS_NAME
-# ^uses $PG_STOP_FLAG
-# ^uses @PG_ANSWER_ENTRY_ORDER
-sub RECORD_ANS_NAME {
- return "" if $PG_STOP_FLAG;
- my $label = shift;
- eval(q!push(@main::PG_ANSWER_ENTRY_ORDER, $label)!);
- $label;
-}
-
=item NEW_ANS_NAME()
- NEW_ANS_NAME($num);
+ NEW_ANS_NAME();
-Generates an answer label from the supplied answer number. The label is
+Generates an anonymous answer label from the internal count The label is
added to the list of implicity-labeled answers. Used internally by
L<PGbasicmacros.pl> to generate labels for unlabeled answer blanks.
=cut
-# ^function NEW_ANS_NAME
-# ^uses $PG_STOP_FLAG
-# ^uses $QUIZ_PREFIX
-# ^uses $ANSWER_PREFIX
-# ^uses @PG_UNLABELED_ANSWERS
-sub NEW_ANS_NAME {
- return "" if $PG_STOP_FLAG;
- my $number=shift;
- my $prefix = eval(q!$main::QUIZ_PREFIX.$main::ANSWER_PREFIX!);
- my $label = $prefix.$number;
- push(@PG_UNLABELED_ANSWERS,$label);
- $label;
-}
-
=item ANS_NUM_TO_NAME()
ANS_NUM_TO_NAME($num);
@@ -694,17 +884,6 @@
=cut
-# ^function ANS_NUM_TO_NAME
-# ^uses $QUIZ_PREFIX
-# ^uses $ANSWER_PREFIX
-sub ANS_NUM_TO_NAME {
- my $number=shift;
- my $label = eval(q!$main::QUIZ_PREFIX.$main::ANSWER_PREFIX!).$number;
- $label;
-}
-
-my $vecnum;
-
=item RECORD_FROM_LABEL()
RECORD_FORM_LABEL("label");
@@ -714,17 +893,6 @@
=cut
-# ^function RECORD_FORM_LABEL
-# ^uses $PG_STOP_FLAG
-# ^uses @KEPT_EXTRA_ANSWERS
-sub RECORD_FORM_LABEL { # this stores form data (such as sticky answers), but does nothing more
- # it's a bit of hack since we are storing these in the KEPT_EXTRA_ANSWERS queue even if they aren't answers per se.
- return "" if $PG_STOP_FLAG;
- my $label = shift; # the label of the input box or textarea
- eval(q!push(@main::KEPT_EXTRA_ANSWERS, $label)!); #put the labels into the hash to be caught later for recording purposes
- $label;
-}
-
=item NEW_ANS_ARRAY_NAME()
NEW_ANS_ARRAY_NAME($num, $row, $col);
@@ -734,23 +902,6 @@
=cut
-# ^function NEW_ANS_ARRAY_NAME
-# ^uses $PG_STOP_FLAG
-# ^uses $QUIZ_PREFIX
-# ^uses @PG_UNLABELED_ANSWERS
-sub NEW_ANS_ARRAY_NAME { # this keeps track of the answers which are entered implicitly,
- # rather than with a specific label
- return "" if $PG_STOP_FLAG;
- my $number=shift;
- $vecnum = 0;
- my $row = shift;
- my $col = shift;
-# my $label = "ArRaY"."$number"."["."$vecnum".","."$row".","."$col"."]";
- my $label = eval(q!$main::QUIZ_PREFIX."ArRaY"."$number"."__"."$vecnum".":"."$row".":"."$col"."__"!);
- push(@PG_UNLABELED_ANSWERS,$label);
- $label;
-}
-
=item NEW_ANS_ARRAY_NAME_EXTENSION()
NEW_ANS_ARRAY_NAME_EXTENSION($num, $row, $col);
@@ -760,26 +911,6 @@
=cut
-# ^function NEW_ANS_ARRAY_NAME_EXTENSION
-# ^uses $PG_STOP_FLAG
-sub NEW_ANS_ARRAY_NAME_EXTENSION { # this keeps track of the answers which are entered implicitly,
- # rather than with a specific label
- return "" if $PG_STOP_FLAG;
- my $number=shift;
- my $row = shift;
- my $col = shift;
- if( $row == 0 && $col == 0 ){
- $vecnum += 1;
- }
- #FIXME change made to conform to HTML 4.01 standards. "Name" attributes can only contain
- # alphanumeric characters, _ : and .
- # Also need to make corresponding changes in PGmorematrixmacros. grep for ArRaY.
- #my $label = "ArRaY"."$number"."["."$vecnum".","."$row".","."$col"."]";
- my $label = eval(q!$main::QUIZ_PREFIX."ArRaY"."$number"."__"."$vecnum".":"."$row".":"."$col"."__"!);
- eval(q!push(@main::KEPT_EXTRA_ANSWERS, $label)!);#put the labels into the hash to be caught later for recording purposes
- $label;
-}
-
=item get_PG_ANSWERS_HASH()
get_PG_ANSWERS_HASH();
@@ -789,37 +920,6 @@
=cut
-# ^function get_PG_ANSWERS_HASH
-# ^uses %PG_ANSWERS_HASH
-# ^uses @PG_UNLABELED_ANSWERS
-# ^uses @PG_ANSWERS
-sub get_PG_ANSWERS_HASH {
- # update the PG_ANSWWERS_HASH, then report the result.
- # This is used in writing sequential problems
- # if there is an input, use that as a key into the answer hash
- my $key = shift;
- my (%pg_answers_hash, @pg_unlabeled_answers);
- %pg_answers_hash= %PG_ANSWERS_HASH;
- #warn "order ", eval(q!@main::PG_ANSWER_ENTRY_ORDER!);
- #warn "pg answers", %PG_ANSWERS_HASH;
- #warn "unlabeled", @PG_UNLABELED_ANSWERS;
- my $index=0;
- foreach my $label (@PG_UNLABELED_ANSWERS) {
- if ( defined($PG_ANSWERS[$index]) ) {
- $pg_answers_hash{"$label"}= $PG_ANSWERS[$index];
- #warn "recording answer label = $label";
- } else {
- warn "No answer provided by instructor for answer $label";
- }
- $index++;
- }
- if ($key) {
- return $pg_answers_hash{$key};
- } else {
- return %pg_answers_hash;
- }
-}
-
=item includePGproblem($filePath)
includePGproblem($filePath);
@@ -830,33 +930,6 @@
=cut
-# ^function includePGproblem
-# ^uses %envir
-# ^uses &read_whole_problem_file
-# ^uses &includePGtext
-sub includePGproblem {
- my $filePath = shift;
- my %save_envir = %main::envir;
- my $fullfilePath = $main::envir{templateDirectory}.$filePath;
- my $r_string = read_whole_problem_file($fullfilePath);
- if (ref($r_string) eq 'SCALAR') {
- $r_string = $$r_string;
- }
-
- # The problem calling this should provide DOCUMENT and ENDDOCUMENT,
- # so we remove them from the included file.
- $r_string=~ s/^\s*(END)?DOCUMENT(\(\s*\));?//gm;
-
- # Reset the problem path so that static images can be found via
- # their relative paths.
- eval('$main::envir{probFileName} = $filePath');
- eval('$main::envir{fileName} = $filePath');
- includePGtext($r_string);
- # Reset the environment to what it is before.
- %main::envir = %save_envir;
-}
-
-
=back
=head1 SEE ALSO
@@ -865,4 +938,7 @@
=cut
-1;
+
+
+
+1;
\ No newline at end of file
Index: PGanswermacros.pl
===================================================================
RCS file: /webwork/cvs/system/pg/macros/PGanswermacros.pl,v
retrieving revision 1.72
retrieving revision 1.73
diff -Lmacros/PGanswermacros.pl -Lmacros/PGanswermacros.pl -u -r1.72 -r1.73
--- macros/PGanswermacros.pl
+++ macros/PGanswermacros.pl
@@ -1447,100 +1447,6 @@
-=head2 Filter utilities
-
-These two subroutines can be used in filters to set default options. They
-help make filters perform in uniform, predictable ways, and also make it
-easy to recognize from the code which options a given filter expects.
-
-
-=head4 assign_option_aliases
-
-Use this to assign aliases for the standard options. It must come before set_default_options
-within the subroutine.
-
- assign_option_aliases(\%options,
- 'alias1' => 'option5'
- 'alias2' => 'option7'
- );
-
-
-If the subroutine is called with an option " alias1 => 23 " it will behave as if it had been
-called with the option " option5 => 23 "
-
-=cut
-
-
-# ^function assign_option_aliases
-sub assign_option_aliases {
- my $rh_options = shift;
- warn "The first entry to set_default_options must be a reference to the option hash" unless ref($rh_options) eq 'HASH';
- my @option_aliases = @_;
- while (@option_aliases) {
- my $alias = shift @option_aliases;
- my $option_key = shift @option_aliases;
-
- if (defined($rh_options->{$alias} )) { # if the alias appears in the option list
- if (not defined($rh_options->{$option_key}) ) { # and the option itself is not defined,
- $rh_options->{$option_key} = $rh_options->{$alias}; # insert the value defined by the alias into the option value
- # the FIRST alias for a given option takes precedence
- # (after the option itself)
- } else {
- warn "option $option_key is already defined as", $rh_options->{$option_key}, "<br>\n",
- "The attempt to override this option with the alias $alias with value ", $rh_options->{$alias},
- " was ignored.";
- }
- }
- delete($rh_options->{$alias}); # remove the alias from the initial list
- }
-
-}
-
-=head4 set_default_options
-
- set_default_options(\%options,
- '_filter_name' => 'filter',
- 'option5' => .0001,
- 'option7' => 'ascii',
- 'allow_unknown_options => 0,
- }
-
-Note that the first entry is a reference to the options with which the filter was called.
-
-The option5 is set to .0001 unless the option is explicitly set when the subroutine is called.
-
-The B<'_filter_name'> option should always be set, although there is no error if it is missing.
-It is used mainly for debugging answer evaluators and allows
-you to keep track of which filter is currently processing the answer.
-
-If B<'allow_unknown_options'> is set to 0 then if the filter is called with options which do NOT appear in the
-set_default_options list an error will be signaled and a warning message will be printed out. This provides
-error checking against misspelling an option and is generally what is desired for most filters.
-
-Occasionally one wants to write a filter which accepts a long list of options, not all of which are known in advance,
-but only uses a subset of the options
-provided. In this case, setting 'allow_unkown_options' to 1 prevents the error from being signaled.
-
-=cut
-
-# ^function set_default_options
-# ^uses pretty_print
-sub set_default_options {
- my $rh_options = shift;
- warn "The first entry to set_default_options must be a reference to the option hash" unless ref($rh_options) eq 'HASH';
- my %default_options = @_;
- unless ( defined($default_options{allow_unknown_options}) and $default_options{allow_unknown_options} == 1 ) {
- foreach my $key1 (keys %$rh_options) {
- warn "This option |$key1| is not recognized in this subroutine<br> ", pretty_print($rh_options) unless exists($default_options{$key1});
- }
- }
- foreach my $key (keys %default_options) {
- if ( not defined($rh_options->{$key} ) and defined( $default_options{$key} ) ) {
- $rh_options->{$key} = $default_options{$key}; #this allows tol => undef to allow the tol option, but doesn't define
- # this key unless tol is explicitly defined.
- }
- }
-}
=head2 Problem Grader Subroutines
@@ -1875,14 +1781,20 @@
my $r_input = shift;
my $out = '';
if ( not ref($r_input) ) {
- $out = $r_input; # not a reference
- $out =~ s/</</g; # protect for HTML output
+ $out = $r_input if defined $r_input; # not a reference
+ $out =~ s/</</g ; # protect for HTML output
} elsif ("$r_input" =~/hash/i) { # this will pick up objects whose '$self' is hash and so works better than ref($r_iput).
local($^W) = 0;
+
$out .= "$r_input " ."<TABLE border = \"2\" cellpadding = \"3\" BGCOLOR = \"#FFFFFF\">";
+
+
foreach my $key (lex_sort( keys %$r_input )) {
$out .= "<tr><TD> $key</TD><TD>=></td><td> ".pretty_print($r_input->{$key}) . "</td></tr>";
}
+
+
+
$out .="</table>";
} elsif (ref($r_input) eq 'ARRAY' ) {
my @array = @$r_input;
@@ -1895,7 +1807,7 @@
$out = "$r_input";
} else {
$out = $r_input;
- $out =~ s/</</g; # protect for HTML output
+ $out =~ s/</</g ; # protect for HTML output
}
$out;
}
|
|
From: Mike G. v. a. <we...@ma...> - 2010-05-14 02:08:57
|
Log Message:
-----------
major update which adds objective methods to the basic code of PG.
HEAD should be considered more beta than usual for a few days until minor glitches
are shaken out.
new modules needed:
PGcore PGalias PGresource PGloadfiles PGanswergroup PGresponsegroup Tie::IxHash
Modified Files:
--------------
pg/macros:
PGbasicmacros.pl
Added Files:
-----------
pg/macros:
LiveGraphics3D.pl
source.pl
Revision Data
-------------
Index: PGbasicmacros.pl
===================================================================
RCS file: /webwork/cvs/system/pg/macros/PGbasicmacros.pl,v
retrieving revision 1.64
retrieving revision 1.65
diff -Lmacros/PGbasicmacros.pl -Lmacros/PGbasicmacros.pl -u -r1.64 -r1.65
--- macros/PGbasicmacros.pl
+++ macros/PGbasicmacros.pl
@@ -192,6 +192,28 @@
$r_ans_rule_count = PG_restricted_eval(q!\$ans_rule_count!);
}
+=head2 Utility Macros
+
+ not_null(item) returns 1 or 0
+
+ empty arrays, empty hashes, strings containing only whitespace are all NULL and return 0
+ all undefined quantities are null and return 0
+
+
+=cut
+
+sub not_null { # empty arrays, empty hashes and strings containing only whitespace are all NULL
+ my $item = shift;
+ return 0 unless defined($item);
+ if (ref($item)=~/ARRAY/) {
+ return scalar(@{$item}); # return the length
+ } elsif (ref($item)=~/HASH/) {
+ return scalar( keys %{$item});
+ } else { # string case return 1 if none empty
+ return ($item =~ /\S/)? 1:0;
+ }
+}
+
=head2 Answer blank macros:
These produce answer blanks of various sizes or pop up lists or radio answer buttons.
@@ -258,11 +280,11 @@
These auxiliary macros are defined in PG.pl
- NEW_ANS_NAME( number ); # produces a new answer blank name from a number by adding a prefix (AnSwEr)
+ NEW_ANS_NAME( ); # produces a new anonymous answer blank name by appending a number to the prefix (AnSwEr)
# and registers this name as an implicitly labeled answer
# Its use is paired with each answer evaluator being entered using ANS()
- ANS_NUM_TO_NAME(number); # adds the prefix (AnSwEr) to the number, but does nothing else.
+ ANS_NUM_TO_NAME(number); # prepends the prefix (AnSwEr) to the number, but does nothing else.
RECORD_ANS_NAME( name ); # records the order in which the answer blank is rendered
# This is called by all of the constructs above, but must
@@ -271,9 +293,9 @@
These are legacy macros:
- ANS_RULE( number, width ); # equivalent to NAMED_ANS_RULE( NEW_ANS_NAME(number), width)
- ANS_BOX( question_number,height, width ); # equivalent to NAMED_ANS_BOX( NEW_ANS_NAME(number), height, width)
- ANS_RADIO( question_number, value,tag ); # equivalent to NAMED_ANS_RADIO( NEW_ANS_NAME(number), value,tag)
+ ANS_RULE( number, width ); # equivalent to NAMED_ANS_RULE( NEW_ANS_NAME( ), width)
+ ANS_BOX( question_number,height, width ); # equivalent to NAMED_ANS_BOX( NEW_ANS_NAME( ), height, width)
+ ANS_RADIO( question_number, value,tag ); # equivalent to NAMED_ANS_RADIO( NEW_ANS_NAME( ), value,tag)
ANS_RADIO_OPTION( question_number, value,tag ); # equivalent to NAMED_ANS_RADIO_EXTENSION( ANS_NUM_TO_NAME(number), value,tag)
@@ -283,14 +305,16 @@
sub labeled_ans_rule { # syntactic sugar for NAMED_ANS_RULE
my($name,$col) = @_;
- $col = 20 unless defined($col);
+ $col = 20 unless not_null($col);
NAMED_ANS_RULE($name,$col);
}
sub NAMED_ANS_RULE {
my($name,$col) = @_;
+ $col = 20 unless not_null($col);
my $answer_value = '';
$answer_value = ${$inputs_ref}{$name} if defined(${$inputs_ref}{$name});
+ #FIXME -- code factoring needed
if ($answer_value =~ /\0/ ) {
my @answers = split("\0", $answer_value);
$answer_value = shift(@answers); # use up the first answer
@@ -302,15 +326,17 @@
my @answers = @{ $answer_value};
$answer_value = shift(@answers); # use up the first answer
$rh_sticky_answers->{$name}=\@answers;
- # store the rest -- beacuse this stores to a main:; variable
+ # store the rest -- because this stores to a main:; variable
# it must be evaluated at run time
$answer_value= '' unless defined($answer_value);
}
- $answer_value =~ tr/\\$@`//d; #`## make sure student answers can not be interpolated by e.g. EV3
+ $answer_value =~ tr/\\$@`//d; ## make sure student answers can not be interpolated by e.g. EV3
$answer_value =~ s/\s+/ /g; ## remove excessive whitespace from student answer
- $name = RECORD_ANS_NAME($name);
-
+ DEBUG_MESSAGE( "RECORD_ANS_NAME($name, $answer_value)");
+ $name = RECORD_ANS_NAME($name, $answer_value);
+ #INSERT_RESPONSE($name,$name,$answer_value); #FIXME -- why can't we do this inside RECORD_ANS_NAME?
+
my $tcol = $col/2 > 3 ? $col/2 : 3; ## get max
$tcol = $tcol < 40 ? $tcol : 40; ## get min
@@ -325,6 +351,7 @@
sub NAMED_HIDDEN_ANS_RULE { # this is used to hold information being passed into and out of applets
# -- preserves state -- identical to NAMED_ANS_RULE except input type "hidden"
my($name,$col) = @_;
+ $col = 20 unless not_null($col);
my $answer_value = '';
$answer_value = ${$inputs_ref}{$name} if defined(${$inputs_ref}{$name});
if ($answer_value =~ /\0/ ) {
@@ -345,8 +372,8 @@
$answer_value =~ tr/\\$@`//d; #`## make sure student answers can not be interpolated by e.g. EV3
$answer_value =~ s/\s+/ /g; ## remove excessive whitespace from student answer
- $name = RECORD_ANS_NAME($name);
-
+ $name = RECORD_ANS_NAME($name, $answer_value);
+ #INSERT_RESPONSE($name,$name,$answer_value);
my $tcol = $col/2 > 3 ? $col/2 : 3; ## get max
$tcol = $tcol < 40 ? $tcol : 40; ## get min
@@ -371,6 +398,7 @@
}
$answer_value =~ tr/\\$@`//d; #`## make sure student answers can not be interpolated by e.g. EV3
$answer_value =~ s/\s+/ /g; ## remove excessive whitespace from student answer
+ INSERT_RESPONSE($name,$name,$answer_value); #hack -- this needs more work to decide how to make it work
my $tcol = $col/2 > 3 ? $col/2 : 3; ## get max
$tcol = $tcol < 40 ? $tcol : 40; ## get min
MODES(
@@ -392,11 +420,13 @@
my($name,$row,$col) = @_;
$row = 10 unless defined($row);
$col = 80 unless defined($col);
- $name = RECORD_ANS_NAME($name);
+
my $height = .07*$row;
my $answer_value = '';
$answer_value = $inputs_ref->{$name} if defined( $inputs_ref->{$name} );
+ $name = RECORD_ANS_NAME($name, $answer_value);
# $answer_value =~ tr/\\$@`//d; #`## make sure student answers can not be interpolated by e.g. EV3
+ INSERT_RESPONSE($name,$name,$answer_value);
my $out = MODES(
TeX => qq!\\vskip $height in \\hrulefill\\quad !,
Latex2HTML => qq!\\begin{rawhtml}<TEXTAREA NAME="$name" id="$name" ROWS="$row" COLS="$col"
@@ -411,7 +441,7 @@
sub ANS_BOX { #deprecated
my($number,$row,$col) = @_;
- my $name = NEW_ANS_NAME($number);
+ my $name = NEW_ANS_NAME();
NAMED_ANS_BOX($name,$row,$col);
}
@@ -419,7 +449,7 @@
my $name = shift;
my $value = shift;
my $tag =shift;
- $name = RECORD_ANS_NAME($name);
+
my $checked = '';
if ($value =~/^\%/) {
$value =~ s/^\%//;
@@ -433,7 +463,7 @@
}
}
-
+ $name = RECORD_ANS_NAME($name, {$value=>$checked} );
MODES(
TeX => qq!\\item{$tag}\n!,
Latex2HTML => qq!\\begin{rawhtml}\n<INPUT TYPE=RADIO NAME="$name" id="$name" VALUE="$value" $checked>\\end{rawhtml}$tag!,
@@ -465,7 +495,7 @@
}
}
-
+ EXTEND_RESPONSE($name,$name,$value, $checked);
MODES(
TeX => qq!\\item{$tag}\n!,
Latex2HTML => qq!\\begin{rawhtml}\n<INPUT TYPE=RADIO NAME="$name" id="$name" VALUE="$value" $checked>\\end{rawhtml}$tag!,
@@ -493,7 +523,7 @@
my $number = shift;
my $value = shift;
my $tag =shift;
- my $name = NEW_ANS_NAME($number);
+ my $name = NEW_ANS_NAME();
NAMED_ANS_RADIO($name,$value,$tag);
}
@@ -501,8 +531,6 @@
my $number = shift;
my $value = shift;
my $tag =shift;
-
-
my $name = ANS_NUM_TO_NAME($number);
NAMED_ANS_RADIO_OPTION($name,$value,$tag);
}
@@ -559,7 +587,7 @@
my $name = shift;
my $value = shift;
my $tag =shift;
- $name = RECORD_ANS_NAME($name);
+
my $checked = '';
if ($value =~/^\%/) {
@@ -576,7 +604,7 @@
}
}
-
+ $name = RECORD_ANS_NAME($name, {$value => $checked});
MODES(
TeX => qq!\\item{$tag}\n!,
Latex2HTML => qq!\\begin{rawhtml}\n<INPUT TYPE=CHECKBOX NAME="$name" id="$name" VALUE="$value" $checked>\\end{rawhtml}$tag!,
@@ -605,7 +633,7 @@
}
}
-
+ EXTEND_RESPONSE($name,$name,$value, $checked);
MODES(
TeX => qq!\\item{$tag}\n!,
Latex2HTML => qq!\\begin{rawhtml}\n<INPUT TYPE=CHECKBOX NAME="$name" id="$name" VALUE="$value" $checked>\\end{rawhtml}$tag!,
@@ -635,7 +663,7 @@
my $number = shift;
my $value = shift;
my $tag =shift;
- my $name = NEW_ANS_NAME($number);
+ my $name = NEW_ANS_NAME();
NAMED_ANS_CHECKBOX($name,$value,$tag);
}
@@ -671,17 +699,19 @@
sub ans_rule {
my $len = shift; # gives the optional length of the answer blank
$len = 20 unless $len ;
- my $name = NEW_ANS_NAME(inc_ans_rule_count());
+ #my $name = NEW_ANS_NAME();
+ my $name = NEW_ANS_NAME(); # increment is done internally
NAMED_ANS_RULE($name ,$len);
}
sub ans_rule_extension {
my $len = shift;
$len = 20 unless $len ;
+ warn "ans_rule_extension may be misnumbering the answers";
my $name = NEW_ANS_NAME($$r_ans_rule_count); # don't update the answer name
NAMED_ANS_RULE($name ,$len);
}
sub ans_radio_buttons {
- my $name = NEW_ANS_NAME(inc_ans_rule_count());
+ my $name = NEW_ANS_NAME();
my @radio_buttons = NAMED_ANS_RADIO_BUTTONS($name, @_);
if ($displayMode eq 'TeX') {
@@ -694,7 +724,7 @@
#added 6/14/2000 by David Etlinger
sub ans_checkbox {
- my $name = NEW_ANS_NAME( inc_ans_rule_count() );
+ my $name = NEW_ANS_NAME( );
my @checkboxes = NAMED_ANS_CHECKBOX_BUTTONS( $name, @_ );
if ($displayMode eq 'TeX') {
@@ -713,7 +743,7 @@
sub tex_ans_rule {
my $len = shift;
$len = 20 unless $len ;
- my $name = NEW_ANS_NAME(inc_ans_rule_count());
+ my $name = NEW_ANS_NAME();
my $answer_rule = NAMED_ANS_RULE($name ,$len); # we don't want to create three answer rules in different modes.
my $out = MODES(
'TeX' => $answer_rule,
@@ -728,6 +758,7 @@
sub tex_ans_rule_extension {
my $len = shift;
$len = 20 unless $len ;
+ warn "tex_ans_rule_extension may be missnumbering the answer";
my $name = NEW_ANS_NAME($$r_ans_rule_count);
my $answer_rule = NAMED_ANS_RULE($name ,$len); # we don't want to create three answer rules in different modes.
my $out = MODES(
@@ -776,7 +807,7 @@
my $col =shift;
$row = 5 unless $row;
$col = 80 unless $col;
- my $name = NEW_ANS_NAME(inc_ans_rule_count());
+ my $name = NEW_ANS_NAME();
NAMED_ANS_BOX($name ,$row,$col);
}
@@ -794,7 +825,7 @@
my @list1 = @{$list[0]};
@list = map { $_ => $_ } @list1;
}
- $name = RECORD_ANS_NAME($name); # record answer name
+
my $answer_value = '';
$answer_value = ${$inputs_ref}{$name} if defined(${$inputs_ref}{$name});
my $out = "";
@@ -819,12 +850,13 @@
} elsif ( $displayMode eq "TeX") {
$out .= "\\fbox{?}";
}
-
+ $name = RECORD_ANS_NAME($name,$answer_value); # record answer name
+ $out;
}
sub pop_up_list {
my @list = @_;
- my $name = NEW_ANS_NAME(inc_ans_rule_count()); # get new answer name
+ my $name = NEW_ANS_NAME(); # get new answer name
NAMED_POP_UP_LIST($name, @list);
}
@@ -876,6 +908,7 @@
my $name = shift;
my $col = shift;
+ my %options = @_;
$col = 20 unless $col;
my $answer_value = '';
@@ -891,6 +924,10 @@
}
$answer_value =~ tr/\\$@`//d; #`## make sure student answers can not be interpolated by e.g. EV3
+ warn "ans_label $options{ans_label} $name $answer_value";
+ if (defined($options{ans_label}) ) {
+ INSERT_RESPONSE($options{ans_label}, $name, $answer_value);
+ }
MODES(
TeX => "\\mbox{\\parbox[t]{10pt}{\\hrulefill}}\\hrulefill\\quad ",
Latex2HTML => qq!\\begin{rawhtml}\n<INPUT TYPE=TEXT SIZE=$col NAME="$name" id="$name" VALUE = "">\n\\end{rawhtml}\n!,
@@ -903,19 +940,21 @@
my $n = shift;
my $col = shift;
$col = 20 unless $col;
- my $num = inc_ans_rule_count() ;
- my $name = NEW_ANS_ARRAY_NAME($num,0,0);
+ my $ans_label = NEW_ANS_NAME();
+ my $num = ans_rule_count();
my @options = @_;
my @array=();
- my $string;
my $answer_value = "";
-
- $array[0][0] = NAMED_ANS_RULE($name,$col);
-
- for( my $i = 1; $i < $n; $i+=1)
+ my @response_list = ();
+ my $name;
+ $main::vecnum = -1;
+ CLEAR_RESPONSES($ans_label);
+
+
+ for( my $i = 0; $i < $n; $i+=1)
{
$name = NEW_ANS_ARRAY_NAME_EXTENSION($num,0,$i);
- $array[0][$i] = NAMED_ANS_ARRAY_EXTENSION($name,$col);
+ $array[0][$i] = NAMED_ANS_ARRAY_EXTENSION($name,$col,ans_label=>$ans_label);
}
@@ -924,8 +963,7 @@
for( my $i = 0; $i < $n; $i+=1)
{
$name = NEW_ANS_ARRAY_NAME_EXTENSION($num,$j,$i);
- $array[$j][$i] = NAMED_ANS_ARRAY_EXTENSION($name,$col);
-
+ $array[$j][$i] = NAMED_ANS_ARRAY_EXTENSION($name,$col, ans_label=>$ans_label);
}
}
@@ -939,19 +977,18 @@
my $n = shift;
my $col = shift;
$col = 20 unless $col;
- my $num = PG_restricted_eval(q!$main::ans_rule_count!);
+ my $num = ans_rule_count(); #hack -- ans_rule_count is updated after being used
my @options = @_;
+ my @response_list = ();
my $name;
my @array=();
- my $string;
- my $answer_value = "";
-
+ my $ans_label = $main::PG->new_label($num);
for( my $j = 0; $j < $m; $j+=1 ){
for( my $i = 0; $i < $n; $i+=1)
{
$name = NEW_ANS_ARRAY_NAME_EXTENSION($num,$j,$i);
- $array[$j][$i] = NAMED_ANS_ARRAY_EXTENSION($name,$col);
+ $array[$j][$i] = NAMED_ANS_ARRAY_EXTENSION($name,$col, ans_label=>$ans_label);
}
@@ -1069,8 +1106,7 @@
sub COMMENT {
my @in = @_;
my $out = join("$BR", @in);
- my $out = '<div class=\"AuthorComment\">'.$out.'</div>';
-
+ $out = '<div class=\"AuthorComment\">'.$out.'</div>';
PG_restricted_eval(q!$main::pgComment = "!.$out.q!"!);
return('');
}
@@ -1182,18 +1218,18 @@
my %options = @_;
# is a string supplied for the current display mode? if so, return it
- return $options{$displayMode} if defined $options{$displayMode};
+ return $options{$main::displayMode} if defined $options{$main::displayMode};
# otherwise, fail over to backup modes
my @backup_modes;
- if (exists $DISPLAY_MODE_FAILOVER{$displayMode}) {
- @backup_modes = @{$DISPLAY_MODE_FAILOVER{$displayMode}};
+ if (exists $DISPLAY_MODE_FAILOVER{$main::displayMode}) {
+ @backup_modes = @{$DISPLAY_MODE_FAILOVER{$main::displayMode}};
foreach my $mode (@backup_modes) {
return $options{$mode} if defined $options{$mode};
}
}
- die "ERROR in defining MODES: neither display mode $displayMode nor",
- " any fallback modes (", join(", ", @backup_modes), ") supplied.\n";
+ die "ERROR in defining MODES: neither display mode '$main::displayMode' nor",
+ " any fallback modes (", join(", ", @backup_modes), ") supplied.";
}
# end display macros
@@ -1261,7 +1297,7 @@
#sub BR { MODES( TeX => '\\par\\noindent ', Latex2HTML => '\\begin{rawhtml}<BR>\\end{rawhtml}', HTML => '<BR>'); };
# Alternate definition of BR which is slightly more flexible and gives more white space in printed output
# which looks better but kills more trees.
-sub BR { MODES( TeX => '\\leavevmode\\\\\\relax ', Latex2HTML => '\\begin{rawhtml}<BR>\\end{rawhtml}', HTML => '<BR>'); };
+sub BR { MODES( TeX => '\\leavevmode\\\\\\relax ', Latex2HTML => '\\begin{rawhtml}<BR>\\end{rawhtml}', HTML => '<BR/>'); };
sub LQ { MODES( TeX => "\\lq\\lq{}", Latex2HTML => '"', HTML => '"' ); };
sub RQ { MODES( TeX => "\\rq\\rq{}", Latex2HTML => '"', HTML => '"' ); };
sub BM { MODES(TeX => '\\(', Latex2HTML => '\\(', HTML => ''); }; # begin math mode
@@ -1469,6 +1505,7 @@
}
sub safe_ev {
my ($out,$PG_eval_errors,$PG_full_error_report) = &old_safe_ev; # process input by old_safe_ev first
+ $out = "" unless defined($out) and $out =~/\S/;
$out =~s/\\/\\\\/g; # protect any new backslashes introduced.
($out,$PG_eval_errors,$PG_full_error_report)
}
@@ -1810,7 +1847,7 @@
sub beginproblem {
my $out = "";
- my $problemValue = $envir->{problemValue};
+ my $problemValue = $envir->{problemValue} || 0;
my $fileName = $envir->{fileName};
my $probNum = $envir->{probNum};
my $TeXFileName = protect_underbar($envir->{fileName});
@@ -1949,6 +1986,7 @@
my %typeHash = (
'interval notation' => 'IntervalNotation.html',
'units' => 'Units.html',
+ 'syntax' => 'Syntax.html',
);
my $infoRef = $typeHash{$type};
@@ -1985,7 +2023,7 @@
} else {
# we are set to include the applet
}
- my $appletHeader = qq! archive = "$archive " codebase = "$codebase" !;
+ $appletHeader = qq! archive = "$archive " codebase = "$codebase" !;
foreach my $key ('name', 'code','width','height', ) {
if ( defined($applet->{$key}) ) {
$appletHeader .= qq! $key = "!.$applet->{$key}.q!" ! ;
@@ -2058,10 +2096,10 @@
}
sub lex_sort {
- PGsort sub {$_[0] lt $_[1]}, @_;
+ PGsort( sub {$_[0] lt $_[1]}, @_);
}
sub num_sort {
- PGsort sub {$_[0] < $_[1]}, @_;
+ PGsort( sub {$_[0] < $_[1]}. @_);
}
@@ -2130,7 +2168,7 @@
$out = "Error: PGbasicmacros: endtable: Unknown displayMode: $displayMode.\n";
}
$out;
- }
+}
sub row {
|
|
From: Mike G. v. a. <we...@ma...> - 2010-05-14 02:08:56
|
Log Message:
-----------
major change to the base
major update which adds objective methods to the basic code of PG.
HEAD should be considered more beta than usual for a few days until minor glitches
are shaken out.
new modules needed:
PGcore PGalias PGresource PGloadfiles PGanswergroup PGresponsegroup Tie::IxHash
Modified Files:
--------------
pg/lib/WeBWorK/PG:
Translator.pm
Revision Data
-------------
Index: Translator.pm
===================================================================
RCS file: /webwork/cvs/system/pg/lib/WeBWorK/PG/Translator.pm,v
retrieving revision 1.25
retrieving revision 1.26
diff -Llib/WeBWorK/PG/Translator.pm -Llib/WeBWorK/PG/Translator.pm -u -r1.25 -r1.26
--- lib/WeBWorK/PG/Translator.pm
+++ lib/WeBWorK/PG/Translator.pm
@@ -12,6 +12,9 @@
use Net::SMTP;
use WeBWorK::PG::IO;
+#use PadWalker; # used for processing error messages
+#use Data::Dumper;
+
# loading GD within the Safe compartment has occasionally caused infinite recursion
# Putting these use statements here seems to avoid this problem
@@ -389,7 +392,7 @@
# All other files are loaded with restriction
#
# construct a regex that matches only these three files safely
- my @unrestricted_files = qw/PG.pl dangerousMacros.pl IO.pl/;
+ my @unrestricted_files = (); # no longer needed? FIXME w/PG.pl dangerousMacros.pl IO.pl/;
my $unrestricted_files = join("|", map { quotemeta } @unrestricted_files);
my $store_mask;
@@ -536,13 +539,15 @@
my $macro_file_name = fileFromPath($filePath);
$macro_file_name =~s/\.pl//; # trim off the extenstion
my $export_subroutine_name = "_${macro_file_name}_export";
- my $init_subroutine_name = "_${macro_file_name}_init";
+ my $init_subroutine_name = "${safe_cmpt_package_name}::_${macro_file_name}_init";
+
my $local_errors = "";
no strict;
# warn "dangerousMacros main:: contains <br>\n ".join("<br>\n ", %main::) if $debugON;
- my $init_subroutine = eval { \&{"${safe_cmpt_package_name}::$init_subroutine_name"} };
+ my $init_subroutine = eval { \&{$init_subroutine_name} };
+ warn "No init routine for $init_subroutine_name: $@" if $debugON and $@;
use strict;
- my $macro_file_loaded = defined(&$init_subroutine);
+ my $macro_file_loaded = ref($init_subroutine) =~ /CODE/;
#print STDERR "$macro_file_name has not yet been loaded\n" unless $macro_file_loaded;
unless ($macro_file_loaded) {
@@ -552,7 +557,7 @@
my $local_errors = "";
if (-r $filePath ) {
my $rdoResult = $safe_cmpt->rdo($filePath);
- #warn "There were problems compiling the file: $filePath: <BR>--$@" if $@;
+ #warn "unrestricted load: $filePath\n";
$local_errors ="\nThere were problems compiling the file:\n $filePath\n $@\n" if $@;
$self ->{errors} .= $local_errors if $local_errors;
use strict;
@@ -564,11 +569,11 @@
}
# try again to define the initization subroutine
- $init_subroutine = eval { \&{"${safe_cmpt_package_name}::$init_subroutine_name"} };
- $macro_file_loaded = defined(&$init_subroutine );
+ $init_subroutine = eval { \&{"$init_subroutine_name"} };
+ $macro_file_loaded = ref($init_subroutine) =~ /CODE/;
if ( $macro_file_loaded ) {
- # warn "unrestricted load: initializing $macro_file_name $init_subroutine" ;
+ #warn "unrestricted load: initializing $macro_file_name $init_subroutine" ;
&$init_subroutine();
}
$local_errors .= "\nUnknown error. Unable to load $filePath\n" if ($local_errors eq '' and not $macro_file_loaded);
@@ -739,7 +744,7 @@
=cut
sub PG_errorMessage {
- my $return = shift; my $frame = 2;
+ my $return = shift; my $frame = 2; # return can be 'message' or 'traceback'
my $message = join("\n",@_); $message =~ s/\.?\s+$//;
my $files = eval ('$main::__files__'); $files = {} unless $files;
my $tmpl = $files->{tmpl} || '$';
@@ -783,6 +788,36 @@
return join("\n",@trace,'');
}
+=head2 PG_undef_var_check
+
+=pod
+
+ Produces warnings of this type in order to help you guess which local variable is undefined
+ Warning: Use of uninitialized value in concatenation (.) or string at mpu.cgi line 25.
+ Possible variables are:
+ '$GLOBAL_VARIABLE' => \'global',
+ '$t' => \undef,
+ '$s' => \'regular output'
+
+
+
+
+=cut
+
+sub PG_undef_var_check {
+ if($_[0] !~ /^Use of uninitialized value/) {
+ return @_;
+ } else {
+ # If there are objects, the output can be VERY large when you increase this
+ local $Data::Dumper::Maxdepth = 2;
+ # takes all lexical variables from caller-nemaspace
+ my $possibles = Data::Dumper::Dumper({ %{PadWalker::peek_my(1)}, %{PadWalker::peek_our(1)} });
+
+ $possibles ne "\$VAR1 = {};\n" ? ($possibles =~ s/^.*?\n(.*)\n.*?\n$/$1/ms) : ($possibles = '');
+ return "Warning: " . join(', ', @_) . "Possible variables are:\n$possibles\n";
+ }
+
+}
############################################################################
=head2 Translate
@@ -910,7 +945,7 @@
my ($PG_PROBLEM_TEXT_REF, $PG_HEADER_TEXT_REF, $PG_ANSWER_HASH_REF, $PG_FLAGS_REF)
=$safe_cmpt->reval(" $evalString");
-
+ #warn "using safe compartment ", $safe_cmpt->root;
# This section could use some more error messages. In particular if a problem doesn't produce the right output, the user needs
# information about which problem was at fault.
#
@@ -1150,7 +1185,7 @@
$self->{safe}->share('$rf_fun','$temp_ans');
# clear %errorTable for each problem
- %errorTable = ();
+ %errorTable = (); # is the error table being used? perhaps by math objects?
my $rh_ans_evaluation_result;
if (ref($rf_fun) eq 'CODE' ) {
@@ -1158,7 +1193,7 @@
warn "Error in Translator.pm::process_answers: Answer $ans_name: |$temp_ans|\n $@\n" if $@;
} elsif (ref($rf_fun) =~ /AnswerEvaluator/) {
$rh_ans_evaluation_result = $self->{safe} ->reval('$rf_fun->evaluate($temp_ans, ans_label => \''.$ans_name.'\')');
- $@ = $errorTable{$@} if $@ && defined($errorTable{$@});
+ $@ = $errorTable{$@} if $@ && defined($errorTable{$@}); #Are we redefining error messages here?
warn "Error in Translator.pm::process_answers: Answer $ans_name: |$temp_ans|\n $@\n" if $@;
warn "Evaluation error: Answer $ans_name:<BR>\n",
$rh_ans_evaluation_result->error_flag(), " :: ",
@@ -1562,7 +1597,7 @@
local $SIG{__DIE__} = "DEFAULT";
no strict;
- my $out = eval ("package main; be_strict();" . $string );
+ my $out = eval ("package main; be_strict();\n" . $string );
my $errors =$@;
my $full_error_report = "PG_macro_file_eval detected error at line $line of file $file \n"
. $errors .
|
|
From: Mike G. v. a. <we...@ma...> - 2010-05-14 02:08:55
|
Log Message:
-----------
minor formatting changes
Modified Files:
--------------
pg/macros:
PGmatrixmacros.pl
Revision Data
-------------
Index: PGmatrixmacros.pl
===================================================================
RCS file: /webwork/cvs/system/pg/macros/PGmatrixmacros.pl,v
retrieving revision 1.15
retrieving revision 1.16
diff -Lmacros/PGmatrixmacros.pl -Lmacros/PGmatrixmacros.pl -u -r1.15 -r1.16
--- macros/PGmatrixmacros.pl
+++ macros/PGmatrixmacros.pl
@@ -175,16 +175,16 @@
$main::defaultDisplayMatrixStyle : "(s)";
set_default_options(\%opts,
- '_filter_name' => 'display_matrix',
- 'force_tex' => 0,
- 'left' => substr($styleParams,0,1),
- 'right' => substr($styleParams,2,1),
- 'midrule' => substr($styleParams,1,1),
- 'top_labels' => 0,
- 'box'=>[-1,-1], # pair location of boxed element
- 'allow_unknown_options'=> 1,
- 'num_format' => "%.0f",
- );
+ '_filter_name' => 'display_matrix',
+ 'force_tex' => 0,
+ 'left' => substr($styleParams,0,1),
+ 'right' => substr($styleParams,2,1),
+ 'midrule' => substr($styleParams,1,1),
+ 'top_labels' => 0,
+ 'box'=>[-1,-1], # pair location of boxed element
+ 'allow_unknown_options'=> 1,
+ 'num_format' => "%.0f",
+ );
my ($numRows, $numCols, @myRows);
@@ -616,10 +616,11 @@
}
set_default_options(\%opts,
- '_filter_name' => 'mbox',
- 'valign' => 'middle',
- 'allowbreaks' => 'no',
- 'allow_unknown_options'=> 0);
+ '_filter_name' => 'mbox',
+ 'valign' => 'middle',
+ 'allowbreaks' => 'no',
+ 'allow_unknown_options'=> 0
+ );
if(! $opts{'allowbreaks'}) { $opts{'allowbreaks'}='no';}
my $out = "";
my $j;
|
|
From: Mike G. v. a. <we...@ma...> - 2010-05-14 02:08:52
|
Log Message:
-----------
major update which adds objective methods to the basic code of PG.
HEAD should be considered more beta than usual for a few days until minor glitches
are shaken out.
new modules needed:
PGcore PGalias PGresource PGloadfiles PGanswergroup PGresponsegroup Tie::IxHash
Modified Files:
--------------
pg/lib/Value:
AnswerChecker.pm
Revision Data
-------------
Index: AnswerChecker.pm
===================================================================
RCS file: /webwork/cvs/system/pg/lib/Value/AnswerChecker.pm,v
retrieving revision 1.128
retrieving revision 1.129
diff -Llib/Value/AnswerChecker.pm -Llib/Value/AnswerChecker.pm -u -r1.128 -r1.129
--- lib/Value/AnswerChecker.pm
+++ lib/Value/AnswerChecker.pm
@@ -366,13 +366,15 @@
sub ans_matrix {
my $self = shift;
my ($extend,$name,$rows,$cols,$size,$open,$close,$sep) = @_;
- my $named_extension = pgRef('NAMED_ANS_RULE_EXTENSION');
- my $new_name = pgRef('RECORD_FORM_LABEL');
+ #my $named_extension = pgRef('NAMED_ANS_RULE_EXTENSION');
+ my $named_extension = pgRef('NAMED_ANS_ARRAY_EXTENSION');
+ my $new_name = sub {@_}; # pgRef('RECORD_EXTRA_ANSWERS');
my $HTML = ""; my $ename = $name;
if ($name eq '') {
- my $n = pgCall('inc_ans_rule_count');
+ #my $n = pgCall('inc_ans_rule_count');
$name = pgCall('NEW_ANS_NAME',$n);
- $ename = $answerPrefix.$n;
+ #$name = pgCall('NEW_ARRAY_NAME',$n);
+ $ename = "${answerPrefix}_${name}_";
}
$self->{ans_name} = $ename;
$self->{ans_rows} = $rows;
@@ -382,10 +384,15 @@
my @row = ();
foreach my $j (0..$cols-1) {
if ($i == 0 && $j == 0) {
- if ($extend) {push(@row,&$named_extension(&$new_name($name),$size))}
- else {push(@row,pgCall('NAMED_ANS_RULE',$name,$size))}
+ if ($extend) {
+ push(@row,&$named_extension(&$new_name($name),$size,ans_label=>$name));
+ #push(@row,&$named_extension(&$new_name($name),$size))
+ }else {
+ push(@row,pgCall('NAMED_ANS_RULE',$name,$size))
+ }
} else {
- push(@row,&$named_extension(&$new_name(ANS_NAME($ename,$i,$j)),$size));
+ push(@row,&$named_extension(&$new_name(ANS_NAME($ename,$i,$j)),$size,ans_label=>$name));
+ #push(@row,&$named_extension(&$new_name(ANS_NAME($ename,$i,$j)),$size,ans_label=>$name));
}
}
push(@array,[@row]);
|
|
From: Mike G. v. a. <we...@ma...> - 2010-05-14 02:08:50
|
Log Message:
-----------
insure that MatrixReal1.pm is used
Modified Files:
--------------
pg/lib:
Matrix.pm
Revision Data
-------------
Index: Matrix.pm
===================================================================
RCS file: /webwork/cvs/system/pg/lib/Matrix.pm,v
retrieving revision 1.9
retrieving revision 1.10
diff -Llib/Matrix.pm -Llib/Matrix.pm -u -r1.9 -r1.10
--- lib/Matrix.pm
+++ lib/Matrix.pm
@@ -21,6 +21,7 @@
# be_strict(); # an alias for use strict. This means that all global variable must contain main:: as a prefix.
#
# }
+use MatrixReal1;
package Matrix;
@Matrix::ISA = qw(MatrixReal1);
|
|
From: Mike G. v. a. <we...@ma...> - 2010-05-14 01:25:23
|
Log Message:
-----------
back out of mistaken commit
Modified Files:
--------------
webwork2/lib/WeBWorK/ContentGenerator:
ProblemRenderer.pm
Revision Data
-------------
Index: ProblemRenderer.pm
===================================================================
RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/ProblemRenderer.pm,v
retrieving revision 1.2
retrieving revision 1.3
diff -Llib/WeBWorK/ContentGenerator/ProblemRenderer.pm -Llib/WeBWorK/ContentGenerator/ProblemRenderer.pm -u -r1.2 -r1.3
--- lib/WeBWorK/ContentGenerator/ProblemRenderer.pm
+++ lib/WeBWorK/ContentGenerator/ProblemRenderer.pm
@@ -16,7 +16,6 @@
package WeBWorK::ContentGenerator::ProblemRenderer;
use base qw(WeBWorK::ContentGenerator);
-use MIME::Base64 qw( encode_base64 decode_base64);
=head1 NAME
@@ -28,33 +27,23 @@
use strict;
use warnings;
use WeBWorK::CGI;
-use WeBWorK::Utils qw(pretty_print_rh);
use WeBWorK::Utils::Tasks qw(renderProblems);
sub pre_header_initialize {
my ($self) = @_;
my $r = $self->r;
- my $db = new WeBWorK::DB($r->ce->{dbLayout});
- $r->db($db);
-
+
my $pg = $r->param('pg');
- $pg = decode_base64($r->param('problemSource'));
my $file = $r->param('file');
my $seed = $r->param('seed');
- $seed = $r->param('problemSeed');
my $mode = $r->param('mode');
my $hint = $r->param('hint');
my $sol = $r->param('sol');
- # pretty_print_rh($r);
- # pretty_print_rh($r->{paramcache});
- warn "answers", @{ $r->{paramcache}->{AnSwEr0001}},"answersSubmitted",@{ $r->{paramcache}->{answersSubmitted}};
- warn "problemSource" , @{ $r->{paramcache}->{problemSource} };
- warn "request object", ${$r->{r}};
- #die "view warning";
+
die "must specify either a PG problems (param 'pg') or a path to a PG file (param 'file') and not both"
unless defined $pg and length $pg xor defined $file and length $file;
- #my $problem = $self->get_problem($pg, $file);
+ my $problem = $self->get_problem($pg, $file);
my @options = (r=>$r, problem_list=>[\$pg]);
#push @options, (problem_seed=>$seed) if defined $seed;
@@ -65,15 +54,15 @@
($self->{result}) = renderProblems(@options);
}
-# sub get_problem {
-# my ($self, $pg, $file) = @_;
-#
-# if (defined $pg) {
-# return \$pg;
-# } else {
-# return $file;
-# }
-# }
+sub get_problem {
+ my ($self, $pg, $file) = @_;
+
+ if (defined $pg) {
+ return \$pg;
+ } else {
+ return $file;
+ }
+}
use Data::Dumper;
sub content {
@@ -93,92 +82,4 @@
EOF
}
-# ideas from renderProblem.pl
-
-# new version of output:
-# my $out2 = {
-# text => encode_base64( $pg->{body_text} ),
-# header_text => encode_base64( $pg->{head_text} ),
-# answers => $pg->{answers},
-# errors => $pg->{errors},
-# WARNINGS => encode_base64($pg->{warnings} ),
-# problem_result => $pg->{result},
-# problem_state => $pg->{state},
-# #PG_flag => $pg->{flags},
-#
-#
-#
-# };
-sub formatAnswerRow {
- my $rh_answer = shift;
- my $problemNumber = shift;
- my $answerString = $rh_answer->{original_student_ans}||' ';
- my $correctAnswer = $rh_answer->{correct_ans}||'';
- my $score = ($rh_answer->{score}) ? 'Correct' : 'Incorrect';
- my $row = qq{
- <tr>
- <td>
- $problemNumber
- </td>
- <td>
- $answerString
- </td>
- <td>
- $score
- </td>
- <td>
- Correct answer is $correctAnswer
- </td>
- <td>
- <i></i>
- </td>
- </tr>\n
- };
- $row;
-}
-
-# sub formatRenderedProblem {
-# my $rh_result = shift; # wrap problem in formats
-# my $problemText = decode_base64($rh_result->{text});
-# my $rh_answers = $rh_result->{answers};
-#
-# my $warnings = '';
-# if ( defined ($rh_result->{WARNINGS}) and $rh_result->{WARNINGS} ){
-# $warnings = "<div style=\"background-color:pink\">
-# <p >WARNINGS</p><p>".decode_base64($rh_result->{WARNINGS})."</p></div>";
-# }
-#
-# ;
-# # collect answers
-# my $answerTemplate = q{<hr>ANSWERS <table border="3" align="center">};
-# my $problemNumber = 1;
-# foreach my $key (sort keys %{$rh_answers}) {
-# $answerTemplate .= formatAnswerRow($rh_answers->{$key}, $problemNumber++);
-# }
-# $answerTemplate .= q{</table> <hr>};
-#
-#
-#
-# my $problemTemplate = <<ENDPROBLEMTEMPLATE;
-#
-# $answerTemplate
-# $warnings
-# <form action="http://webhost.math.rochester.edu/webworkdocs/ww/render" method="post">
-# $problemText
-# <input type="hidden" name="answersSubmitted" value="1">
-# <input type="hidden" name="problemAddress" value="probSource">
-# <input type="hidden" name="problemSource" value="$encodedSource">
-# <input type="hidden" name="problemSeed" value="1234">
-# <input type="hidden" name="pathToProblemFile" value="foobar">
-# <p><input type="submit" name="submit" value="submit answers"></p>
-# </form>
-#
-#
-# ENDPROBLEMTEMPLATE
-#
-#
-#
-# $problemTemplate;
-# }
-
1;
|
|
From: Mike G. v. a. <we...@ma...> - 2010-05-14 01:21:37
|
Log Message:
-----------
minor changes to configuration section
Modified Files:
--------------
webwork2/lib:
WebworkWebservice.pm
webwork2/lib/Apache:
WeBWorK.pm
webwork2/lib/WeBWorK/ContentGenerator:
ProblemRenderer.pm
Revision Data
-------------
Index: WebworkWebservice.pm
===================================================================
RCS file: /webwork/cvs/system/webwork2/lib/WebworkWebservice.pm,v
retrieving revision 1.5
retrieving revision 1.6
diff -Llib/WebworkWebservice.pm -Llib/WebworkWebservice.pm -u -r1.5 -r1.6
--- lib/WebworkWebservice.pm
+++ lib/WebworkWebservice.pm
@@ -17,8 +17,18 @@
# warn "Assuming webwork directory is |$webwork_directory| and |$webwork_directory2|", $webwork_directory eq $webwork_directory2;
# #WTF??? why don't these two methods give me the same directory name?
- my $webwork_directory = '/opt/webwork/webwork2';
-
+###############################################################################
+# Configuration -- set to top webwork directory (webwork2) (set in webwork.apache2-config)
+# Configuration -- set server name
+###############################################################################
+
+ my $webwork_directory = $WeBWorK::Constants::WEBWORK_DIRECTORY; #'/opt/webwork/webwork2';
+
+ $WebworkWebservice::HOST_NAME = 'localhost'; # Apache->server->server_hostname;
+ $WebworkWebservice::HOST_PORT = '80'; # Apache->server->port;
+
+###############################################################################
+
eval "use lib '$webwork_directory/lib'"; die $@ if $@;
eval "use WeBWorK::CourseEnvironment"; die $@ if $@;
my $ce = new WeBWorK::CourseEnvironment({ webwork_dir => $webwork_directory });
@@ -29,16 +39,19 @@
$WebworkWebservice::WW_DIRECTORY = $webwork_directory;
$WebworkWebservice::PG_DIRECTORY = $pg_dir;
$WebworkWebservice::SeedCE = $ce;
- $WebworkWebservice::HOST_NAME = 'localhost'; #Apache->server->server_hostname;
- $WebworkWebservice::HOST_PORT = '80'; #Apache->server->port;
+
+###############################################################################
+
$WebworkWebservice::PASSWORD = 'xmluser';
$WebworkWebservice::COURSENAME = 'daemon2_course'; # default course
+
+ warn "webwork_directory set to ", $WeBWorK::Constants::WEBWORK_DIRECTORY;
}
use strict;
-###############################################################################
+
package WebworkWebservice;
Index: ProblemRenderer.pm
===================================================================
RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/ProblemRenderer.pm,v
retrieving revision 1.1
retrieving revision 1.2
diff -Llib/WeBWorK/ContentGenerator/ProblemRenderer.pm -Llib/WeBWorK/ContentGenerator/ProblemRenderer.pm -u -r1.1 -r1.2
--- lib/WeBWorK/ContentGenerator/ProblemRenderer.pm
+++ lib/WeBWorK/ContentGenerator/ProblemRenderer.pm
@@ -16,6 +16,7 @@
package WeBWorK::ContentGenerator::ProblemRenderer;
use base qw(WeBWorK::ContentGenerator);
+use MIME::Base64 qw( encode_base64 decode_base64);
=head1 NAME
@@ -27,23 +28,33 @@
use strict;
use warnings;
use WeBWorK::CGI;
+use WeBWorK::Utils qw(pretty_print_rh);
use WeBWorK::Utils::Tasks qw(renderProblems);
sub pre_header_initialize {
my ($self) = @_;
my $r = $self->r;
-
+ my $db = new WeBWorK::DB($r->ce->{dbLayout});
+ $r->db($db);
+
my $pg = $r->param('pg');
+ $pg = decode_base64($r->param('problemSource'));
my $file = $r->param('file');
my $seed = $r->param('seed');
+ $seed = $r->param('problemSeed');
my $mode = $r->param('mode');
my $hint = $r->param('hint');
my $sol = $r->param('sol');
-
+ # pretty_print_rh($r);
+ # pretty_print_rh($r->{paramcache});
+ warn "answers", @{ $r->{paramcache}->{AnSwEr0001}},"answersSubmitted",@{ $r->{paramcache}->{answersSubmitted}};
+ warn "problemSource" , @{ $r->{paramcache}->{problemSource} };
+ warn "request object", ${$r->{r}};
+ #die "view warning";
die "must specify either a PG problems (param 'pg') or a path to a PG file (param 'file') and not both"
unless defined $pg and length $pg xor defined $file and length $file;
- my $problem = $self->get_problem($pg, $file);
+ #my $problem = $self->get_problem($pg, $file);
my @options = (r=>$r, problem_list=>[\$pg]);
#push @options, (problem_seed=>$seed) if defined $seed;
@@ -54,15 +65,15 @@
($self->{result}) = renderProblems(@options);
}
-sub get_problem {
- my ($self, $pg, $file) = @_;
-
- if (defined $pg) {
- return \$pg;
- } else {
- return $file;
- }
-}
+# sub get_problem {
+# my ($self, $pg, $file) = @_;
+#
+# if (defined $pg) {
+# return \$pg;
+# } else {
+# return $file;
+# }
+# }
use Data::Dumper;
sub content {
@@ -82,4 +93,92 @@
EOF
}
+# ideas from renderProblem.pl
+
+# new version of output:
+# my $out2 = {
+# text => encode_base64( $pg->{body_text} ),
+# header_text => encode_base64( $pg->{head_text} ),
+# answers => $pg->{answers},
+# errors => $pg->{errors},
+# WARNINGS => encode_base64($pg->{warnings} ),
+# problem_result => $pg->{result},
+# problem_state => $pg->{state},
+# #PG_flag => $pg->{flags},
+#
+#
+#
+# };
+sub formatAnswerRow {
+ my $rh_answer = shift;
+ my $problemNumber = shift;
+ my $answerString = $rh_answer->{original_student_ans}||' ';
+ my $correctAnswer = $rh_answer->{correct_ans}||'';
+ my $score = ($rh_answer->{score}) ? 'Correct' : 'Incorrect';
+ my $row = qq{
+ <tr>
+ <td>
+ $problemNumber
+ </td>
+ <td>
+ $answerString
+ </td>
+ <td>
+ $score
+ </td>
+ <td>
+ Correct answer is $correctAnswer
+ </td>
+ <td>
+ <i></i>
+ </td>
+ </tr>\n
+ };
+ $row;
+}
+
+# sub formatRenderedProblem {
+# my $rh_result = shift; # wrap problem in formats
+# my $problemText = decode_base64($rh_result->{text});
+# my $rh_answers = $rh_result->{answers};
+#
+# my $warnings = '';
+# if ( defined ($rh_result->{WARNINGS}) and $rh_result->{WARNINGS} ){
+# $warnings = "<div style=\"background-color:pink\">
+# <p >WARNINGS</p><p>".decode_base64($rh_result->{WARNINGS})."</p></div>";
+# }
+#
+# ;
+# # collect answers
+# my $answerTemplate = q{<hr>ANSWERS <table border="3" align="center">};
+# my $problemNumber = 1;
+# foreach my $key (sort keys %{$rh_answers}) {
+# $answerTemplate .= formatAnswerRow($rh_answers->{$key}, $problemNumber++);
+# }
+# $answerTemplate .= q{</table> <hr>};
+#
+#
+#
+# my $problemTemplate = <<ENDPROBLEMTEMPLATE;
+#
+# $answerTemplate
+# $warnings
+# <form action="http://webhost.math.rochester.edu/webworkdocs/ww/render" method="post">
+# $problemText
+# <input type="hidden" name="answersSubmitted" value="1">
+# <input type="hidden" name="problemAddress" value="probSource">
+# <input type="hidden" name="problemSource" value="$encodedSource">
+# <input type="hidden" name="problemSeed" value="1234">
+# <input type="hidden" name="pathToProblemFile" value="foobar">
+# <p><input type="submit" name="submit" value="submit answers"></p>
+# </form>
+#
+#
+# ENDPROBLEMTEMPLATE
+#
+#
+#
+# $problemTemplate;
+# }
+
1;
|
|
From: Mike G. v. a. <we...@ma...> - 2010-05-14 01:19:35
|
Log Message:
-----------
work around for mysqldump bug (or bad documentation)
new version will work fine as long as only one defaults file is to be read.
Modified Files:
--------------
webwork2/lib/WeBWorK/DB/Schema/NewSQL:
Std.pm
Revision Data
-------------
Index: Std.pm
===================================================================
RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/DB/Schema/NewSQL/Std.pm,v
retrieving revision 1.22
retrieving revision 1.23
diff -Llib/WeBWorK/DB/Schema/NewSQL/Std.pm -Llib/WeBWorK/DB/Schema/NewSQL/Std.pm -u -r1.22 -r1.23
--- lib/WeBWorK/DB/Schema/NewSQL/Std.pm
+++ lib/WeBWorK/DB/Schema/NewSQL/Std.pm
@@ -219,7 +219,8 @@
# 2>&1 is specified first, which apparently makes stderr go to stdout
# and stdout (not including stderr) go to the dumpfile. see bash(1).
my $dump_cmd = "2>&1 " . shell_quote($mysqldump)
- . " --defaults-extra-file=" . shell_quote($my_cnf->filename)
+# . " --defaults-extra-file=" . shell_quote($my_cnf->filename)
+ . " --defaults-file=" . shell_quote($my_cnf->filename) # work around for mysqldump bug
. " " . shell_quote($database)
. " " . shell_quote($self->sql_table_name)
. " > " . shell_quote($dumpfile_path);
@@ -242,7 +243,8 @@
my $mysql = $self->{params}{mysql_path};
my $restore_cmd = "2>&1 " . shell_quote($mysql)
- . " --defaults-extra-file=" . shell_quote($my_cnf->filename)
+# . " --defaults-extra-file=" . shell_quote($my_cnf->filename)
+ . " --defaults-file=" . shell_quote($my_cnf->filename) # work around for mysqldump bug
. " " . shell_quote($database)
. " < " . shell_quote($dumpfile_path);
my $restore_out = readpipe $restore_cmd;
|
|
From: Mike G. v. a. <we...@ma...> - 2010-05-14 01:17:44
|
Log Message:
-----------
No longer need to preload PG.pl, dangerousMacros.pl and IO.pl
Modified Files:
--------------
webwork2/lib/WeBWorK/PG:
Local.pm
Revision Data
-------------
Index: Local.pm
===================================================================
RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/PG/Local.pm,v
retrieving revision 1.28
retrieving revision 1.29
diff -Llib/WeBWorK/PG/Local.pm -Llib/WeBWorK/PG/Local.pm -u -r1.28 -r1.29
--- lib/WeBWorK/PG/Local.pm
+++ lib/WeBWorK/PG/Local.pm
@@ -266,11 +266,11 @@
# at compile time.
#
# TO ENABLE CACHEING UNCOMMENT THE FOLLOWING:
- eval{$translator->pre_load_macro_files(
- $WeBWorK::PG::Local::safeCache,
- $ce->{pg}->{directories}->{macros},
- 'PG.pl', 'dangerousMacros.pl','IO.pl','PGbasicmacros.pl','PGanswermacros.pl'
- )};
+# eval{$translator->pre_load_macro_files(
+# $WeBWorK::PG::Local::safeCache,
+# $ce->{pg}->{directories}->{macros},
+# #'PG.pl', 'dangerousMacros.pl','IO.pl','PGbasicmacros.pl','PGanswermacros.pl'
+# )};
warn "Error while preloading macro files: $@" if $@;
# STANDARD LOADING CODE: for cached script files, this merely
|
|
From: Mike G. v. a. <we...@ma...> - 2010-05-14 01:16:21
|
Log Message:
-----------
Try to catch user_id's with bad characters -- not sure this is successful yet.
Modified Files:
--------------
webwork2/lib/WeBWorK/ContentGenerator/Instructor:
UserList.pm
Revision Data
-------------
Index: UserList.pm
===================================================================
RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/Instructor/UserList.pm,v
retrieving revision 1.95
retrieving revision 1.96
diff -Llib/WeBWorK/ContentGenerator/Instructor/UserList.pm -Llib/WeBWorK/ContentGenerator/Instructor/UserList.pm -u -r1.95 -r1.96
--- lib/WeBWorK/ContentGenerator/Instructor/UserList.pm
+++ lib/WeBWorK/ContentGenerator/Instructor/UserList.pm
@@ -69,6 +69,7 @@
#use CGI qw(-nosticky );
use WeBWorK::CGI;
use WeBWorK::File::Classlist;
+use WeBWorK::DB;
use WeBWorK::Utils qw(readFile readDirectory cryptPassword);
use constant HIDE_USERS_THRESHHOLD => 200;
use constant EDIT_FORMS => [qw(cancelEdit saveEdit)];
@@ -1328,6 +1329,10 @@
my %record = %$record;
my $user_id = $record{user_id};
+ unless (WeBWorK::DB::check_user_id($user_id) ) { # try to catch lines with bad characters
+ push @skipped, $user_id;
+ next;
+ }
if ($user_id eq $user) { # don't replace yourself!!
push @skipped, $user_id;
next;
|
|
From: Mike G. v. a. <we...@ma...> - 2010-05-14 01:15:12
|
Log Message:
-----------
remove commented out cruft
Modified Files:
--------------
webwork2/lib/WeBWorK/ContentGenerator/Instructor:
ProblemSetDetail.pm
Revision Data
-------------
Index: ProblemSetDetail.pm
===================================================================
RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm,v
retrieving revision 1.79
retrieving revision 1.80
diff -Llib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm -Llib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm -u -r1.79 -r1.80
--- lib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm
+++ lib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm
@@ -1794,14 +1794,6 @@
1, # prune against path relative to $templates
);
- # this just takes too much time to search
-# my @problemFileList = listFilesRecursive(
-# $templates,
-# qr/\.pg$/i, # problem files don't say problem
-# qr/^(?:$skip|CVS)$/, # prune these directories
-# 0, # match against file name only
-# 1, # prune against path relative to $templates
-# );
# Display a useful warning message
if ($forUsers) {
@@ -2051,15 +2043,7 @@
$viewProblemLink = $self->systemLink($viewProblemPage, params => { effectiveUser => ($forOneUser ? $editForUser[0] : $userID)});
}
- ###-----
- ### The array @fields never gets used in the following, so
- ### I'm commenting it out. If there's a reason it should
- ### be here, someone else can add it back in and maybe
- ### comment why. Thanks, Gavin. -glarose 6/19/08
- ### my @fields = @{ PROBLEM_FIELDS() };
- ### push @fields, @{ USER_PROBLEM_FIELDS() } if $forOneUser;
- ###-----
-
+
my $problemFile = $r->param("problem.$problemID.source_file") || $problemRecord->source_file;
# warn of repeat problems
|
|
From: Mike G. v. a. <we...@ma...> - 2010-05-14 01:13:47
|
Log Message:
-----------
stop printing the problem path and the problem seed in hidden variables
students could use this to reverse engineer the problem using the library
website.
Modified Files:
--------------
webwork2/lib/WeBWorK/ContentGenerator:
Problem.pm
Revision Data
-------------
Index: Problem.pm
===================================================================
RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/Problem.pm,v
retrieving revision 1.220
retrieving revision 1.221
diff -Llib/WeBWorK/ContentGenerator/Problem.pm -Llib/WeBWorK/ContentGenerator/Problem.pm -u -r1.220 -r1.221
--- lib/WeBWorK/ContentGenerator/Problem.pm
+++ lib/WeBWorK/ContentGenerator/Problem.pm
@@ -1129,15 +1129,17 @@
-value => $self->{editMode},
)
) if defined($self->{editMode}) and $self->{editMode} eq 'temporaryFile';
- print( CGI::hidden(
- -name => 'sourceFilePath',
- -value => $self->{problem}->{source_file}
- )) if defined($self->{problem}->{source_file});
+
+ # this is a security risk -- students can use this to find the source code for the problem
+# print( CGI::hidden(
+# -name => 'sourceFilePath',
+# -value => $self->{problem}->{source_file}
+# )) if defined($self->{problem}->{source_file});
- print( CGI::hidden(
- -name => 'problemSeed',
- -value => $r->param("problemSeed")
- )) if defined($r->param("problemSeed"));
+# print( CGI::hidden(
+# -name => 'problemSeed',
+# -value => $r->param("problemSeed")
+# )) if defined($r->param("problemSeed"));
# end of main form
print CGI::endform();
@@ -1171,27 +1173,6 @@
CGI::endform();
}
- ## feedback form url
- #my $feedbackPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Feedback",
- # courseID => $courseName);
- #my $feedbackURL = $self->systemLink($feedbackPage, authen => 0); # no authen info for form action
- #
- ##print feedback form
- #print
- # CGI::start_form(-method=>"POST", -action=>$feedbackURL),"\n",
- # $self->hidden_authen_fields,"\n",
- # CGI::hidden("module", __PACKAGE__),"\n",
- # CGI::hidden("set", $set->set_id),"\n",
- # CGI::hidden("problem", $problem->problem_id),"\n",
- # CGI::hidden("displayMode", $self->{displayMode}),"\n",
- # CGI::hidden("showOldAnswers", $will{showOldAnswers}),"\n",
- # CGI::hidden("showCorrectAnswers", $will{showCorrectAnswers}),"\n",
- # CGI::hidden("showHints", $will{showHints}),"\n",
- # CGI::hidden("showSolutions", $will{showSolutions}),"\n",
- # CGI::p({-align=>"left"},
- # CGI::submit(-name=>"feedbackForm", -label=>"Email instructor")
- # ),
- # CGI::endform(),"\n";
print $self->feedbackMacro(
module => __PACKAGE__,
|
|
From: Mike G. v. a. <we...@ma...> - 2010-05-14 01:12:32
|
Log Message:
-----------
minor variable name changes
Modified Files:
--------------
webwork2/lib/WeBWorK/ContentGenerator:
renderViaXMLRPC.pm
Revision Data
-------------
Index: renderViaXMLRPC.pm
===================================================================
RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/renderViaXMLRPC.pm,v
retrieving revision 1.1
retrieving revision 1.2
diff -Llib/WeBWorK/ContentGenerator/renderViaXMLRPC.pm -Llib/WeBWorK/ContentGenerator/renderViaXMLRPC.pm -u -r1.1 -r1.2
--- lib/WeBWorK/ContentGenerator/renderViaXMLRPC.pm
+++ lib/WeBWorK/ContentGenerator/renderViaXMLRPC.pm
@@ -24,11 +24,10 @@
use strict;
use warnings;
+
package WeBWorK::ContentGenerator::renderViaXMLRPC;
use base qw(WeBWorK::ContentGenerator);
-
-
#use Crypt::SSLeay;
use XMLRPC::Lite;
use MIME::Base64 qw( encode_base64 decode_base64);
@@ -37,17 +36,17 @@
# configuration section -- point back to the current server to process answers
##################################################
use constant PROTOCOL => 'http';
-use constant HOSTURL => 'localhost';
+use constant HOSTNAME => 'localhost';
use constant HOSTPORT => 80;
-our $FULL_URL = PROTOCOL."://".HOSTURL; # .":".HOSTPORT;
+our $FULL_URL = PROTOCOL."://".HOSTNAME; # .":".HOSTPORT;
our $FORM_ACTION_URL = "/webwork2/html2xml"; # points back to current server
# use constant PROTOCOL => 'https'; # or 'http';
-# use constant HOSTURL => 'hosted2.webwork.rochester.edu'; # 'localhost';
+# use constant HOSTNAME => 'hosted2.webwork.rochester.edu'; # 'localhost';
# use constant HOSTPORT => 443; #( for secure https) # 80;
-# our $FULL_URL = PROTOCOL."://".HOSTURL; # .":".HOSTPORT;
-# our $FORM_ACTION_URL = "$FULL_URL/webwork2/html2xml"; # points back to current server
+# our $FULL_URL = PROTOCOL."://".HOSTNAME; # .":".HOSTPORT;
+# our $FORM_ACTION_URL = "/webwork2/html2xml"; # points back to current server
use constant TRANSPORT_METHOD => 'XMLRPC::Lite';
use constant REQUEST_CLASS => 'WebworkXMLRPC'; # WebworkXMLRPC is used for soap also!!
@@ -112,7 +111,7 @@
$command = 'listLibraries' unless $command;
my $requestResult = TRANSPORT_METHOD
- #->uri('http://'.HOSTURL.':'.HOSTPORT.'/'.REQUEST_CLASS)
+ #->uri('http://'.HOSTNAME.':'.HOSTPORT.'/'.REQUEST_CLASS)
-> proxy($FULL_URL.'/'.REQUEST_URI);
my $input = $self->setInputTable();
|
|
From: Mike G. v. a. <we...@ma...> - 2010-05-14 01:10:56
|
Log Message:
-----------
major update which adds objective methods to the basic code of PG.
HEAD should be considered more beta than usual for a few days until minor glitches
are shaken out.
new modules needed:
PGcore PGalias PGresource PGloadfiles PGanswergroup PGresponsegroup Tie::IxHash
Modified Files:
--------------
webwork2/conf:
global.conf.dist
Revision Data
-------------
Index: global.conf.dist
===================================================================
RCS file: /webwork/cvs/system/webwork2/conf/global.conf.dist,v
retrieving revision 1.221
retrieving revision 1.222
diff -Lconf/global.conf.dist -Lconf/global.conf.dist -u -r1.221 -r1.222
--- conf/global.conf.dist
+++ conf/global.conf.dist
@@ -467,7 +467,7 @@
# required permissions
# GRANT SELECT ON webwork.* TO webworkRead@localhost IDENTIFIED BY 'passwordRO';
-# GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, ALTER, DROP, LOCK TABLES ON webwork.* TO webworkWrite@localhost IDENTIFIED BY 'passwordRW';
+# GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, ALTER, DROP, INDEX, LOCK TABLES ON webwork.* TO webworkWrite@localhost IDENTIFIED BY 'passwordRW';
$database_dsn = "dbi:mysql:webwork";
$database_username = "webworkWrite";
@@ -899,10 +899,10 @@
# Locations of CAPA resources. (Only necessary if you need to use converted CAPA
# problems.)
-$pg{specialPGEnvironmentVars}{CAPA_Tools} = "/opt/webwork/CAPA/CAPA_Tools/",
-$pg{specialPGEnvironmentVars}{CAPA_MCTools} = "/opt/webwork/CAPA/CAPA_MCTools/",
-$pg{specialPGEnvironmentVars}{CAPA_GraphicsDirectory} = "$webworkDirs{htdocs}/CAPA_Graphics/",
-$pg{specialPGEnvironmentVars}{CAPA_Graphics_URL} = "$webworkURLs{htdocs}/CAPA_Graphics/",
+$pg{specialPGEnvironmentVars}{CAPA_Tools} = "$courseDirs{macros}/CAPA_Tools/",
+$pg{specialPGEnvironmentVars}{CAPA_MCTools} = "$courseDirs{macros}/CAPA_MCTools/",
+$pg{specialPGEnvironmentVars}{CAPA_GraphicsDirectory} = "$courseDirs{html}/CAPA_Graphics/",
+$pg{specialPGEnvironmentVars}{CAPA_Graphics_URL} = "$courseURLs{html}/CAPA_Graphics/",
# Size in pixels of dynamically-generated images, i.e. graphs.
$pg{specialPGEnvironmentVars}{onTheFlyImageSize} = 400,
@@ -963,6 +963,7 @@
# [qw(Chromatic)], # for Northern Arizona graph problems
# # -- follow instructions at libraries/nau_problib/lib/README to install
[qw(Applet FlashApplet JavaApplet)],
+ [qw(PGcore PGalias PGresource PGloadfiles PGanswergroup PGresponsegroup Tie::IxHash)],
];
##### Problem creation defaults
|
|
From: Mike G. v. a. <we...@ma...> - 2010-05-12 02:08:45
|
Log Message:
-----------
Clarification updates on documentation.
Rearranged the configuration lines to emphasize what has to be customized.
Modified Files:
--------------
webwork2/clients:
README
renderProblem.pl
Revision Data
-------------
Index: renderProblem.pl
===================================================================
RCS file: /webwork/cvs/system/webwork2/clients/renderProblem.pl,v
retrieving revision 1.4
retrieving revision 1.5
diff -Lclients/renderProblem.pl -Lclients/renderProblem.pl -u -r1.4 -r1.5
--- clients/renderProblem.pl
+++ clients/renderProblem.pl
@@ -27,7 +27,7 @@
The formatting allows the browser presentation to be interactive with the
daemon running the script webwork2/lib/renderViaXMLRPC.pm
-
+Rembember to configure the local output file and display command !!!!!!!!
=cut
@@ -42,6 +42,15 @@
##################################################
# configuration section for client
##################################################
+# configure the local output file and display command !!!!!!!!
+
+use constant TEMPOUTPUTFILE => '/Users/gage/Desktop/renderProblemOutput.html'; # client only
+use constant DISPLAY_COMMAND => 'open -a firefox '; # mac client only opens tempoutputfile above
+# other command lines for opening the html file gnome-open or firefox file.html
+
+# the rest can be configured later to use a different server
+
+# the rest can work!!
# use constant PROTOCOL => 'http';
# use constant HOSTNAME => 'localhost';
# use constant HOSTPORT => 80;
@@ -57,8 +66,7 @@
use constant TRANSPORT_METHOD => 'XMLRPC::Lite';
use constant REQUEST_CLASS => 'WebworkXMLRPC'; # WebworkXMLRPC is used for soap also!!
use constant REQUEST_URI => 'mod_xmlrpc';
-use constant TEMPOUTPUTFILE => '/Users/gage/Desktop/renderProblemOutput.html'; # client only
-use constant DISPLAY_COMMAND => 'open -a firefox '; # client only
+
use constant XML_PASSWORD => 'xmlwebwork';
use constant XML_COURSE => 'daemon_course';
Index: README
===================================================================
RCS file: /webwork/cvs/system/webwork2/clients/README,v
retrieving revision 1.5
retrieving revision 1.6
diff -Lclients/README -Lclients/README -u -r1.5 -r1.6
--- clients/README
+++ clients/README
@@ -12,9 +12,12 @@
The webwork_xmplrp_client and webwork_soap_client.pl have not been updated, but may still work.
to do:
- add facilities for inspecting libraries via xmlrpc and html2xml links
- figure out how to automate the configuration of these xmlrpc files
- Apache2::ServerUtil should do this?? but I can't figure out how.
+ â¢add facilities for inspecting libraries via xmlrpc and html2xml links
+ â¢figure out how to automate the configuration of these xmlrpc files
+ Apache2::ServerUtil should do this?? but I can't figure out how.
+ â¢I suspect that auxiliary html files and applets don't work yet.
+ â¢finish factoring the code into xmlrpc_clients_inc.pl
+ â¢get checkProblem working again.
I'm pretty sure that there is no security checking to insure that only appropriate
people have access to the xmlrpc webservice.
|
|
From: Mike G. v. a. <we...@ma...> - 2010-05-11 23:14:15
|
Log Message:
-----------
update clients to work with apache2 servers
update README
Modified Files:
--------------
webwork2/clients:
README
checkProblem.pl
hello_world_soap_client.pl
hello_world_xmlrpc_client.pl
renderProblem_rawoutput.pl
webwork_soap_client.pl
webwork_xmlrpc_client.pl
webwork_xmlrpc_inc.pl
Revision Data
-------------
Index: webwork_xmlrpc_client.pl
===================================================================
RCS file: /webwork/cvs/system/webwork2/clients/webwork_xmlrpc_client.pl,v
retrieving revision 1.4
retrieving revision 1.5
diff -Lclients/webwork_xmlrpc_client.pl -Lclients/webwork_xmlrpc_client.pl -u -r1.4 -r1.5
--- clients/webwork_xmlrpc_client.pl
+++ clients/webwork_xmlrpc_client.pl
@@ -18,14 +18,14 @@
use MIME::Base64 qw( encode_base64 decode_base64);
# configuration section
-use constant PROTOCOL => 'https'; # or 'http';
-use constant HOSTURL => 'webwork.rochester.edu';
-use constant HOSTPORT => '443'; # or 80
+use constant PROTOCOL => 'http'; # or 'http';
+use constant HOSTURL => 'localhost';
+use constant HOSTPORT => '80'; # or 80
use constant TRANSPORT_METHOD => 'XMLRPC::Lite';
use constant REQUEST_CLASS =>'WebworkXMLRPC'; # WebworkXMLRPC is used for soap also!!
use constant REQUEST_URI =>'mod_xmlrpc';
use constant TEMPOUTPUTFILE => '/Users/gage/Desktop/renderProblemOutput.html';
-use constant COURSE => 'daemon2_course';
+use constant COURSE => 'gage_course';
@@ -74,7 +74,7 @@
#->uri('http://'.HOSTURL.':'.HOSTPORT.'/'.REQUEST_CLASS)
-> proxy(PROTOCOL.'://'.HOSTURL.':'.HOSTPORT.'/'.REQUEST_URI);
- my $test = [3,4,5,6];
+ # my $test = [3,4,5,6];
my $input = setInputTable();
print "displayMode=",$input->{envir}->{displayMode},"\n";
local( $result);
@@ -146,7 +146,7 @@
$out = {
pw => 'geometry',
set => 'set0',
- library_name => 'rochesterLibrary',
+ library_name => 'Library',
command => 'all',
};
@@ -156,7 +156,7 @@
$out = {
pw => 'geometry',
set => 'set0',
- library_name => 'rochesterLibrary',
+ library_name => 'Library',
command => 'all',
answer_form_submitted => 1,
course => COURSE(),
Index: webwork_xmlrpc_inc.pl
===================================================================
RCS file: /webwork/cvs/system/webwork2/clients/webwork_xmlrpc_inc.pl,v
retrieving revision 1.2
retrieving revision 1.3
diff -Lclients/webwork_xmlrpc_inc.pl -Lclients/webwork_xmlrpc_inc.pl -u -r1.2 -r1.3
--- clients/webwork_xmlrpc_inc.pl
+++ clients/webwork_xmlrpc_inc.pl
@@ -18,9 +18,9 @@
use MIME::Base64 qw( encode_base64 decode_base64);
# configuration section
-use constant PROTOCOL => 'https'; # or 'http';
-use constant HOSTURL => 'webwork.rochester.edu';
-use constant HOSTPORT => 443;
+use constant PROTOCOL => 'http'; # or 'http';
+use constant HOSTURL => 'localhost';
+use constant HOSTPORT => 80;
use constant TRANSPORT_METHOD => 'XMLRPC::Lite';
use constant REQUEST_CLASS => 'WebworkXMLRPC'; # WebworkXMLRPC is used for soap also!!
use constant REQUEST_URI => 'mod_xmlrpc';
@@ -127,7 +127,7 @@
#password => 'geometry',
pw => 'geometry',
set => 'set0',
- library_name => 'rochesterLibrary',
+ library_name => 'Library',
command => 'all',
};
@@ -139,7 +139,7 @@
#password => 'geometry',
pw => 'geometry',
set => 'set0',
- library_name => 'rochesterLibrary',
+ library_name => 'Library',
command => 'all',
answer_form_submitted => 1,
course => COURSE(),
Index: renderProblem_rawoutput.pl
===================================================================
RCS file: /webwork/cvs/system/webwork2/clients/renderProblem_rawoutput.pl,v
retrieving revision 1.2
retrieving revision 1.3
diff -Lclients/renderProblem_rawoutput.pl -Lclients/renderProblem_rawoutput.pl -u -r1.2 -r1.3
--- clients/renderProblem_rawoutput.pl
+++ clients/renderProblem_rawoutput.pl
@@ -28,13 +28,7 @@
-# $pg{displayModes} = [
-# "plainText", # display raw TeX for math expressions
-# "formattedText", # format math expressions using TtH
-# "images", # display math expressions as images generated by dvipng
-# "jsMath", # render TeX math expressions on the client side using jsMath
-# "asciimath", # render TeX math expressions on the client side using ASCIIMathML
-# ];
+
use constant DISPLAYMODE => 'images';
Index: checkProblem.pl
===================================================================
RCS file: /webwork/cvs/system/webwork2/clients/checkProblem.pl,v
retrieving revision 1.2
retrieving revision 1.3
diff -Lclients/checkProblem.pl -Lclients/checkProblem.pl -u -r1.2 -r1.3
--- clients/checkProblem.pl
+++ clients/checkProblem.pl
@@ -5,8 +5,7 @@
This script will take a command and an input
file.
-It will list available libraries, list the contents of libraries
-or render the input file.
+It will render the input file.
All of this is done by contacting the webservice.
Index: hello_world_xmlrpc_client.pl
===================================================================
RCS file: /webwork/cvs/system/webwork2/clients/hello_world_xmlrpc_client.pl,v
retrieving revision 1.2
retrieving revision 1.3
diff -Lclients/hello_world_xmlrpc_client.pl -Lclients/hello_world_xmlrpc_client.pl -u -r1.2 -r1.3
--- clients/hello_world_xmlrpc_client.pl
+++ clients/hello_world_xmlrpc_client.pl
@@ -3,9 +3,9 @@
#
use XMLRPC::Lite;
my $soap = XMLRPC::Lite
- -> proxy('https://math.webwork.rochester.edu:443/mod_xmlrpc/');
+ # -> proxy('https://math.webwork.rochester.edu/mod_xmlrpc/');
#-> proxy('https://devel.webwork.rochester.edu:8002/mod_xmlrpc/');
- # -> proxy('http://localhost/mod_xmlrpc/');
+ -> proxy('http://localhost/mod_xmlrpc/');
my $result = $soap->call("WebworkXMLRPC.hi");
Index: webwork_soap_client.pl
===================================================================
RCS file: /webwork/cvs/system/webwork2/clients/webwork_soap_client.pl,v
retrieving revision 1.1
retrieving revision 1.2
diff -Lclients/webwork_soap_client.pl -Lclients/webwork_soap_client.pl -u -r1.1 -r1.2
--- clients/webwork_soap_client.pl
+++ clients/webwork_soap_client.pl
@@ -1,10 +1,10 @@
-#!/usr/local/bin/perl -w
+#!/usr/bin/perl -w
use SOAP::Lite;
# configuration section
-use constant HOSTURL => 'devel.webwork.rochester.edu';
-use constant HOSTPORT => 8002;
+use constant HOSTURL => 'localhost';
+use constant HOSTPORT => 80;
use constant TRANSPORT_METHOD => 'SOAP::Lite';
use constant REQUEST_CLASS =>'WebworkXMLRPC'; # WebworkXMLRPC is used for soap also!!
use constant REQUEST_URI =>'mod_soap';
Index: hello_world_soap_client.pl
===================================================================
RCS file: /webwork/cvs/system/webwork2/clients/hello_world_soap_client.pl,v
retrieving revision 1.2
retrieving revision 1.3
diff -Lclients/hello_world_soap_client.pl -Lclients/hello_world_soap_client.pl -u -r1.2 -r1.3
--- clients/hello_world_soap_client.pl
+++ clients/hello_world_soap_client.pl
@@ -3,10 +3,10 @@
use SOAP::Lite;
my $soap = SOAP::Lite
--> uri('https://math.webwork.rochester.edu/WebworkXMLRPC')
--> proxy('https://math.webwork.rochester.edu/mod_soap/WebworkWebservice');
-#-> uri('http://localhost/WebworkXMLRPC')
-#-> proxy('http://localhost/mod_soap/WebworkWebservice');
+#-> uri('http://math.webwork.rochester.edu/WebworkXMLRPC')
+#-> proxy('https://math.webwork.rochester.edu/mod_soap/WebworkWebservice');
+-> uri('http://localhost/WebworkXMLRPC')
+-> proxy('http://localhost/mod_soap/WebworkWebservice');
#-> uri('https://devel.webwork.rochester.edu:8002/WebworkXMLRPC')
#-> proxy('https://devel.webwork.rochester.edu:8002/mod_soap/WebworkWebservice');
Index: README
===================================================================
RCS file: /webwork/cvs/system/webwork2/clients/README,v
retrieving revision 1.4
retrieving revision 1.5
diff -Lclients/README -Lclients/README -u -r1.4 -r1.5
--- clients/README
+++ clients/README
@@ -1,3 +1,26 @@
+2010.05.11
+
+At this point renderProblem.pl is the most up to date and works with BBedit to
+to send a local file to an xmlserver (mod_xmlrpc) to be rendered. In addition the returned'
+file's question form action url is pointed to the same server with the path ending in xml2html.
+This allows one to enter answers and test whether they work.
+
+There were changes to webwork2/lib/WebworkWebservice.pm to make it work with Apache2.
+There were also changes to webwork2/lib/WeBWorK/URLpath.pm and a new
+module webwork2/lib/WeBWorK/ContentGenerator/xmlViaHTTP.pm
+
+The webwork_xmplrp_client and webwork_soap_client.pl have not been updated, but may still work.
+
+to do:
+ add facilities for inspecting libraries via xmlrpc and html2xml links
+ figure out how to automate the configuration of these xmlrpc files
+ Apache2::ServerUtil should do this?? but I can't figure out how.
+
+ I'm pretty sure that there is no security checking to insure that only appropriate
+ people have access to the xmlrpc webservice.
+##############################################################################################
+
+
These test clients will process these two commands:
./webwork_xmlrpc_client.pl renderProblem input.txt >foo.txt
|
|
From: Mike G. v. a. <we...@ma...> - 2010-05-11 16:08:09
|
Log Message:
-----------
remove =53 error
Modified Files:
--------------
webwork2/clients:
input.txt
Revision Data
-------------
Index: input.txt
===================================================================
RCS file: /webwork/cvs/system/webwork2/clients/input.txt,v
retrieving revision 1.2
retrieving revision 1.3
diff -Lclients/input.txt -Lclients/input.txt -u -r1.2 -r1.3
--- clients/input.txt
+++ clients/input.txt
@@ -25,7 +25,7 @@
This problem demonstrates how you enter numerical answers into WeBWorK. $PAR
Evaluate the expression \(3($a )($b -$c -2($d ))\):
- \{==53; ans_rule(10) \}
+ \{ ans_rule(10) \}
$BR
END_TEXT
|
|
From: Mike G. v. a. <we...@ma...> - 2010-05-11 16:07:34
|
Log Message:
-----------
updated to work with apache2 and the new html2xml
facility
Modified Files:
--------------
webwork2/clients:
renderProblem.pl
Revision Data
-------------
Index: renderProblem.pl
===================================================================
RCS file: /webwork/cvs/system/webwork2/clients/renderProblem.pl,v
retrieving revision 1.3
retrieving revision 1.4
diff -Lclients/renderProblem.pl -Lclients/renderProblem.pl -u -r1.3 -r1.4
--- clients/renderProblem.pl
+++ clients/renderProblem.pl
@@ -1,108 +1,169 @@
#!/usr/bin/perl -w
-=pod
+################################################################################
+# WeBWorK Online Homework Delivery System
+# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/
+# $CVSHeader$
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of either: (a) the GNU General Public License as published by the
+# Free Software Foundation; either version 2, or (at your option) any later
+# version, or (b) the "Artistic License" which comes with this package.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
+# Artistic License for more details.
+################################################################################
+
+=head1 NAME
+
+webwork2/clients/renderProblem.pl
This script will take a file and send it to a WeBWorK daemon webservice
to have it rendered. The result is split into the basic HTML rendering
-and evaluation of answers and then passed to Safari for printing.
+and evaluation of answers and then passed to a browser for printing.
-The formatting allows the Safari presentation to be interactive with the
-daemon running on webhost.math.rochester.edu
+The formatting allows the browser presentation to be interactive with the
+daemon running the script webwork2/lib/renderViaXMLRPC.pm
=cut
+use strict;
+use warnings;
+
+package WeBWorK::ContentGenerator::renderViaXMLRPC_client;
+use Crypt::SSLeay; # needed for https
use XMLRPC::Lite;
use MIME::Base64 qw( encode_base64 decode_base64);
-#require "webwork_xmlrpc_inc.pl";
-# configuration section
-use constant PROTOCOL => 'https'; # or 'http';
-use constant HOSTURL => 'webwork.rochester.edu';
-use constant HOSTPORT => 443;
+##################################################
+# configuration section for client
+##################################################
+# use constant PROTOCOL => 'http';
+# use constant HOSTNAME => 'localhost';
+# use constant HOSTPORT => 80;
+# our $FORM_ACTION_URL ='http://localhost/webwork2/html2xml';
+
+
+use constant PROTOCOL => 'https'; # or 'http';
+use constant HOSTNAME => 'hosted2.webwork.rochester.edu'; # 'localhost';
+use constant HOSTPORT => 443; #( for secure https) # 80;
+our $FULL_URL = PROTOCOL."://".HOSTNAME; # .":".HOSTPORT;
+our $FORM_ACTION_URL = "$FULL_URL/webwork2/html2xml"; # server parameter
+
use constant TRANSPORT_METHOD => 'XMLRPC::Lite';
use constant REQUEST_CLASS => 'WebworkXMLRPC'; # WebworkXMLRPC is used for soap also!!
use constant REQUEST_URI => 'mod_xmlrpc';
-use constant TEMPOUTPUTFILE => '/Users/gage/Desktop/renderProblemOutput.html';
-use constant DISPLAY_COMMAND => 'open -a safari ';
-use constant COURSE => 'daemon2_course';
+use constant TEMPOUTPUTFILE => '/Users/gage/Desktop/renderProblemOutput.html'; # client only
+use constant DISPLAY_COMMAND => 'open -a firefox '; # client only
+
+use constant XML_PASSWORD => 'xmlwebwork';
+use constant XML_COURSE => 'daemon_course';
+
-# $pg{displayModes} = [
-# "plainText", # display raw TeX for math expressions
-# "formattedText", # format math expressions using TtH
-# "images", # display math expressions as images generated by dvipng
-# "jsMath", # render TeX math expressions on the client side using jsMath
-# "asciimath", # render TeX math expressions on the client side using ASCIIMathML
-# ];
use constant DISPLAYMODE => 'images'; # tex and jsMath are other possibilities.
-my @COMMANDS = qw( listLibraries renderProblem ); #listLib readFile tex2pdf
+our @COMMANDS = qw( listLibraries renderProblem ); #listLib readFile tex2pdf
+
+##################################################
# end configuration section
+##################################################
+sub new {
+ my $self = {
+ output => '',
+ encodedSource => '',
+ self => '',
+ inputs_ref => { AnSwEr0001 => '',
+ AnSwEr0002 => '',
+ AnSwEr0003 => '',
+ },,
+ };
+
+ bless $self;
+}
+our $xmlrpc_client = new WeBWorK::ContentGenerator::renderViaXMLRPC_client;
+
+##################################################
+# input/output section
+##################################################
-#print STDERR "inputs are ", join (" | ", @ARGV), "\n";
our $source;
-our $encodedSource;
our $rh_result;
# filter mode main code
undef $/;
$source = <>; #slurp input
-$encodedSource = encodeSource($source);
+$xmlrpc_client->{encodedSource} = encodeSource($source);
$/ =1;
-xmlrpcCall('renderProblem');
+#xmlrpcCall('renderProblem');
+$xmlrpc_client->xmlrpcCall('renderProblem');
+
+
+local(*FH);
+open(FH, '>'.TEMPOUTPUTFILE) or die "Can't open file ".TEMPOUTPUTFILE()." for writing";
+print FH $xmlrpc_client->{output} ;
+close(FH);
+
+system(DISPLAY_COMMAND().TEMPOUTPUTFILE());
+
+##################################################
+# end input/output section
+##################################################
+
+
+our $result;
-# end filter main code
+##################################################
+# Utilities --
+# this code is identical between renderProblem.pl and renderViaXMLRPC.pm
+##################################################
sub xmlrpcCall {
- my $command = shift;
- $command = 'listLibraries' unless $command;
+ my $self = shift;
+ my $command = shift;
+ $command = 'listLibraries' unless $command;
my $requestResult = TRANSPORT_METHOD
- #->uri('http://'.HOSTURL.':'.HOSTPORT.'/'.REQUEST_CLASS)
- -> proxy(PROTOCOL.'://'.HOSTURL.':'.HOSTPORT.'/'.REQUEST_URI);
-
- my $test = [3,4,5,6];
- my $input = setInputTable();
- #print "displayMode=",$input->{envir}->{displayMode},"\n";
+ #->uri('http://'.HOSTNAME.':'.HOSTPORT.'/'.REQUEST_CLASS)
+ -> proxy($FULL_URL.'/'.REQUEST_URI);
+
+ my $input = $self->setInputTable();
local( $result);
# use eval to catch errors
eval { $result = $requestResult->call(REQUEST_CLASS.'.'.$command,$input) };
print STDERR "There were a lot of errors\n" if $@;
- print "Errors: \n $@\n End Errors\n" if $@;
-
- #print "result is|", ref($result),"|";
-
- unless (ref($result) and $result->fault) {
+ print STDERR "Errors: \n $@\n End Errors\n" if $@;
- $rh_result = $result->result();
-
- # look at output
-
- local(*FH);
- open(FH, '>'.TEMPOUTPUTFILE) or die "Can't open file ".TEMPOUTPUTFILE()." for writing";
- print FH formatRenderedProblem($rh_result);
- close(FH);
-
- system(DISPLAY_COMMAND().TEMPOUTPUTFILE());
- #print pretty_print_rh($result->result()),"\n"; #$result->result()
+
+
+ unless (ref($result) and $result->fault) {
+ my $rh_result = $result->result();
+ #print STDERR pretty_print_rh($rh_result);
+ $self->{output} = $self->formatRenderedProblem($rh_result);
+
} else {
- print 'oops ', join ', ',
+ $self->{output} = 'Error from server: ', join( ",\n ",
$result->faultcode,
- $result->faultstring;
+ $result->faultstring);
}
}
sub encodeSource {
+ my $source = shift;
encode_base64($source);
}
+
+
sub pretty_print_rh {
shift if UNIVERSAL::isa($_[0] => __PACKAGE__);
my $rh = shift;
@@ -145,30 +206,29 @@
}
sub setInputTable_for_listLib {
- $out = {
- #password => 'geometry',
- pw => 'geometry',
+ my $self = shift;
+ my $out = {
+ pw => XML_PASSWORD(),
set => 'set0',
- library_name => 'rochesterLibrary',
+ library_name => 'Library',
command => 'all',
};
$out;
}
sub setInputTable {
- $out = {
- #password => 'geometry',
- pw => 'geometry',
- set => 'set0',
- library_name => 'rochesterLibrary',
- command => 'all',
+ my $self = shift;
+ my $out = {
+ pw => XML_PASSWORD(),
+ library_name => 'Library',
+ command => 'renderProblem',
answer_form_submitted => 1,
- course => COURSE(),
+ course => XML_COURSE(),
extra_packages_to_load => [qw( AlgParserWithImplicitExpand Expr
ExprWithImplicitExpand AnswerEvaluator
AnswerEvaluatorMaker
)],
- mode => DISPLAMODE(),
+ mode => DISPLAYMODE(),
modules_to_evaluate => [ qw(
Exporter
DynaLoader
@@ -195,14 +255,14 @@
Regression
)],
- envir => environment(),
+ envir => $self->environment(),
problem_state => {
num_of_correct_ans => 2,
num_of_incorrect_ans => 4,
recorded_score => 1.0,
},
- source => $encodedSource, #base64 encoded
+ source => $self->{encodedSource}, #base64 encoded
@@ -212,17 +272,18 @@
}
sub environment {
+ my $self = shift;
my $envir = {
answerDate => '4014438528',
- CAPA_Graphics_URL=>'http://webwork-db.math.rochester.edu/capa_graphics/',
- CAPA_GraphicsDirectory =>'/ww/webwork/CAPA/CAPA_Graphics/',
- CAPA_MCTools=>'/ww/webwork/CAPA/CAPA_MCTools/',
- CAPA_Tools=>'/ww/webwork/CAPA/CAPA_Tools/',
+ CAPA_Graphics_URL=>"not defined",
+ CAPA_GraphicsDirectory =>"not defined",
+ CAPA_MCTools=>"not defined",
+ CAPA_Tools=>'not defined',
cgiDirectory=>'Not defined',
cgiURL => 'Not defined',
classDirectory=> 'Not defined',
courseName=>'Not defined',
- courseScriptsDirectory=>'/ww/webwork/system/courseScripts/',
+ courseScriptsDirectory=>'not defined',
displayMode=>DISPLAYMODE,
dueDate=> '4014438528',
externalGif2EpsPath=>'not defined',
@@ -241,14 +302,10 @@
functVarDefault=> 'x',
functZeroLevelDefault=> 0.000001,
functZeroLevelTolDefault=>0.000001,
- htmlDirectory =>'/ww/webwork/courses/gage_course/html/',
- htmlURL =>'http://webwork-db.math.rochester.edu/gage_course/',
- inputs_ref => {
- AnSwEr1 => '',
- AnSwEr2 => '',
- AnSwEr3 => '',
- },
- macroDirectory=>'/ww/webwork/courses/gage_course/templates/macros/',
+ htmlDirectory =>'not defined',
+ htmlURL =>'not defined',
+ inputs_ref => $self->{inputs_ref},
+ macroDirectory=>'not defined',
numAbsTolDefault=>0.0000001,
numFormatDefault=>'%0.13g',
numOfAttempts=> 0,
@@ -268,27 +325,29 @@
sectionName => 'Gage',
sectionNumber => 1,
sessionKey=> 'Not defined',
- setNumber =>'MAAtutorial',
+ setNumber =>'not defined',
studentLogin =>'gage',
studentName => 'Mike Gage',
- tempDirectory => '/ww/htdocs/tmp/gage_course/',
- templateDirectory=>'/ww/webwork/courses/gage_course/templates/',
- tempURL=>'http://webwork-db.math.rochester.edu/tmp/gage_course/',
- webworkDocsURL => 'http://webwork.math.rochester.edu/webwork_gage_system_html',
+ tempDirectory => 'not defined',
+ templateDirectory=>'not defined',
+ tempURL=>'not defined',
+ webworkDocsURL => 'not defined',
};
$envir;
};
sub formatAnswerRow {
+ my $self = shift;
my $rh_answer = shift;
my $problemNumber = shift;
my $answerString = $rh_answer->{original_student_ans}||' ';
- my $correctAnswer = $rh_answer->{correct_ans};
+ my $correctAnswer = $rh_answer->{correct_ans}||'';
+ my $ans_message = $rh_answer->{ans_message};
my $score = ($rh_answer->{score}) ? 'Correct' : 'Incorrect';
my $row = qq{
<tr>
<td>
- $problemNumber
+ Prob: $problemNumber
</td>
<td>
$answerString
@@ -300,7 +359,7 @@
Correct answer is $correctAnswer
</td>
<td>
- <i></i>
+ <i>$ans_message</i>
</td>
</tr>\n
};
@@ -308,10 +367,11 @@
}
sub formatRenderedProblem {
+ my $self = shift;
my $rh_result = shift; # wrap problem in formats
my $problemText = decode_base64($rh_result->{text});
my $rh_answers = $rh_result->{answers};
-
+ my $encodedSource = $self->{encodedSource}||'foobar';
my $warnings = '';
if ( defined ($rh_result->{WARNINGS}) and $rh_result->{WARNINGS} ){
$warnings = "<div style=\"background-color:pink\">
@@ -323,17 +383,21 @@
my $answerTemplate = q{<hr>ANSWERS <table border="3" align="center">};
my $problemNumber = 1;
foreach my $key (sort keys %{$rh_answers}) {
- $answerTemplate .= formatAnswerRow($rh_answers->{$key}, $problemNumber++);
+ $answerTemplate .= $self->formatAnswerRow($rh_answers->{$key}, $problemNumber++);
}
$answerTemplate .= q{</table> <hr>};
my $problemTemplate = <<ENDPROBLEMTEMPLATE;
-
+<html>
+<head>
+<title>WeBWorK Editor</title>
+</head>
+<body>
$answerTemplate
$warnings
- <form action="http://webhost.math.rochester.edu/webworkdocs/ww/render" method="post">
+ <form action="$FORM_ACTION_URL" method="post">
$problemText
<input type="hidden" name="answersSubmitted" value="1">
<input type="hidden" name="problemAddress" value="probSource">
@@ -342,7 +406,8 @@
<input type="hidden" name="pathToProblemFile" value="foobar">
<p><input type="submit" name="submit" value="submit answers"></p>
</form>
-
+</body>
+</html>
ENDPROBLEMTEMPLATE
@@ -350,3 +415,6 @@
$problemTemplate;
}
+
+
+1;
|
|
From: Mike G. v. a. <we...@ma...> - 2010-05-11 15:53:36
|
Log Message:
-----------
update to work with Apache2
Modified Files:
--------------
webwork2/lib/WebworkWebservice:
LibraryActions.pm
MathTranslators.pm
RenderProblem.pm
Revision Data
-------------
Index: RenderProblem.pm
===================================================================
RCS file: /webwork/cvs/system/webwork2/lib/WebworkWebservice/RenderProblem.pm,v
retrieving revision 1.8
retrieving revision 1.9
diff -Llib/WebworkWebservice/RenderProblem.pm -Llib/WebworkWebservice/RenderProblem.pm -u -r1.8 -r1.9
--- lib/WebworkWebservice/RenderProblem.pm
+++ lib/WebworkWebservice/RenderProblem.pm
@@ -21,7 +21,7 @@
use sigtrap;
use Carp;
use Safe;
-use Apache;
+#use Apache;
use WeBWorK::CourseEnvironment;
use WeBWorK::PG::Translator;
use WeBWorK::PG::Local;
@@ -358,10 +358,7 @@
WARNINGS => encode_base64($pg->{warnings} ),
problem_result => $pg->{result},
problem_state => $pg->{state},
- #PG_flag => $pg->{flags},
-
-
-
+ PG_flag => $pg->{flags},
};
# Filter out bad reference types
###################
@@ -374,8 +371,8 @@
open (DEBUGCODE, ">>$xmlDebugLog") || die "Can't open $xmlDebugLog";
print DEBUGCODE "\n\nStart xml encoding\n";
}
- xml_filter($out2->{answers});
+ $out2->{answers} = xml_filter($out2->{answers}); # check this -- it might not be working correctly
##################
close(DEBUGCODE) if $debugXmlCode;
###################
@@ -389,13 +386,13 @@
}
-
+# insures proper conversion to xml structure.
sub xml_filter {
my $input = shift;
my $level = shift || 0;
my $space=" ";
# Hack to filter out CODE references
- my $type = ref($input);
+ my $type = ref($input);
if (!defined($type) or !$type ) {
print DEBUGCODE $space x $level." : scalar -- not converted\n" if $debugXmlCode;
} elsif( $type =~/HASH/i or "$input"=~/HASH/i) {
@@ -410,11 +407,14 @@
} elsif( $type=~/ARRAY/i or "$input"=~/ARRAY/i) {
print DEBUGCODE " "x$level."ARRAY reference with ".@{$input}." elements will be investigated\n" if $debugXmlCode;
$level++;
+ my $tmp = [];
foreach my $item (@{$input}) {
$item = xml_filter($item,$level);
+ push @$tmp, $item;
}
+ $input = $tmp;
$level--;
- print DEBUGCODE " "x$level."ARRAY reference completed \n" if $debugXmlCode;
+ print DEBUGCODE " "x$level."ARRAY reference completed",join(" ",@$input),"\n" if $debugXmlCode;
} elsif($type =~ /CODE/i or "$input" =~/CODE/i) {
$input = "CODE reference";
print DEBUGCODE " "x$level."CODE reference, converted $input\n" if $debugXmlCode;
Index: MathTranslators.pm
===================================================================
RCS file: /webwork/cvs/system/webwork2/lib/WebworkWebservice/MathTranslators.pm,v
retrieving revision 1.2
retrieving revision 1.3
diff -Llib/WebworkWebservice/MathTranslators.pm -Llib/WebworkWebservice/MathTranslators.pm -u -r1.2 -r1.3
--- lib/WebworkWebservice/MathTranslators.pm
+++ lib/WebworkWebservice/MathTranslators.pm
@@ -17,7 +17,7 @@
use sigtrap;
use Carp;
use Safe;
-use Apache;
+#use Apache;
use WeBWorK::PG::Translator;
use WeBWorK::PG::IO;
use Benchmark;
Index: LibraryActions.pm
===================================================================
RCS file: /webwork/cvs/system/webwork2/lib/WebworkWebservice/LibraryActions.pm,v
retrieving revision 1.4
retrieving revision 1.5
diff -Llib/WebworkWebservice/LibraryActions.pm -Llib/WebworkWebservice/LibraryActions.pm -u -r1.4 -r1.5
--- lib/WebworkWebservice/LibraryActions.pm
+++ lib/WebworkWebservice/LibraryActions.pm
@@ -18,7 +18,7 @@
use sigtrap;
use Carp;
use Safe;
-use Apache;
+#use Apache;
use WeBWorK::Utils;
use WeBWorK::CourseEnvironment;
use WeBWorK::PG::Translator;
@@ -110,7 +110,7 @@
$out->{error} = "Could not find library:".$rh->{library_name}.":";
return($out);
}
- #warn "library directory path is $dirPath";
+ warn "library directory path is $dirPath";
my @outListLib;
my $wanted = sub {
my $name = $File::Find::name;
@@ -125,7 +125,7 @@
my $command = $rh->{command};
$command = 'all' unless defined($command);
$command eq 'all' && do {
- find({wanted=>$wanted,follow=>1 }, $dirPath);
+ find({wanted=>$wanted,follow_fast=>1 }, $dirPath);
@outListLib = sort @outListLib;
$out->{ra_out} = \@outListLib;
$out->{text} = join("\n", @outListLib);
|
|
From: Mike G. v. a. <we...@ma...> - 2010-05-11 15:52:58
|
Log Message:
-----------
update WebworkWebservice to handle bin/renderProblem.pl
Modified Files:
--------------
webwork2/lib:
WebworkWebservice.pm
Revision Data
-------------
Index: WebworkWebservice.pm
===================================================================
RCS file: /webwork/cvs/system/webwork2/lib/WebworkWebservice.pm,v
retrieving revision 1.4
retrieving revision 1.5
diff -Llib/WebworkWebservice.pm -Llib/WebworkWebservice.pm -u -r1.4 -r1.5
--- lib/WebworkWebservice.pm
+++ lib/WebworkWebservice.pm
@@ -4,49 +4,38 @@
BEGIN {
$main::VERSION = "2.1";
- my $webwork_directory = $ENV{WEBWORK_ROOT};
-
+ #use Apache;
+ use Cwd;
+ use WeBWorK::PG::Local;
+# warn "my first webwork $webwork_directory";
+#
+# my $webwork_directory = $ENV{WEBWORK_ROOT};
+# warn "my first webwork $webwork_directory";
+# $webwork_directory2 =Cwd::cwd();
+# chomp $webwork_directory2;
+# $webwork_directory2 =~ s|/lib/?$||; # this will usually get the right webwork home directory
+# warn "Assuming webwork directory is |$webwork_directory| and |$webwork_directory2|", $webwork_directory eq $webwork_directory2;
+# #WTF??? why don't these two methods give me the same directory name?
+
+ my $webwork_directory = '/opt/webwork/webwork2';
+
eval "use lib '$webwork_directory/lib'"; die $@ if $@;
eval "use WeBWorK::CourseEnvironment"; die $@ if $@;
my $ce = new WeBWorK::CourseEnvironment({ webwork_dir => $webwork_directory });
my $webwork_url = $ce->{webwork_url};
my $pg_dir = $ce->{pg_dir};
-# my $webwork_htdocs_url = $ce->{webwork_htdocs_url};
-# my $webwork_htdocs_dir = $ce->{webwork_htdocs_dir};
-# my $webwork_courses_url = $ce->{webwork_courses_url};
-# my $webwork_courses_dir = $ce->{webwork_courses_dir};
eval "use lib '$pg_dir/lib'"; die $@ if $@;
$WebworkWebservice::WW_DIRECTORY = $webwork_directory;
$WebworkWebservice::PG_DIRECTORY = $pg_dir;
$WebworkWebservice::SeedCE = $ce;
- $WebworkWebservice::HOST_NAME = Apache->server->server_hostname;
- $WebworkWebservice::HOST_PORT = Apache->server->port;
- $WebworkWebservice::PASSWORD = 'geometry';
+ $WebworkWebservice::HOST_NAME = 'localhost'; #Apache->server->server_hostname;
+ $WebworkWebservice::HOST_PORT = '80'; #Apache->server->port;
+ $WebworkWebservice::PASSWORD = 'xmluser';
$WebworkWebservice::COURSENAME = 'daemon2_course'; # default course
}
-use Apache;
-use WeBWorK::PG::Local;
-
-#use lib '/home/gage/webwork/webwork-modperl/lib';
-#use lib '/home/gage/webwork/pg/lib';
-
-#$Webservice::HOST_PATH = "http://$Webservice::HOST_NAME";
-#$Webservice::HOST_PATH .= ":$Webservice::HOST_PORT"
-# unless ($Webservice::HOST_PORT == 80 );
-
-# warn "webwork_directory = $WebworkWebservice::WW_DIRECTORY\n\t";
-# warn "pg_directory = $WebworkWebservice::PG_DIRECTORY\n\t";
-# warn "seedCE = $WebworkWebservice::SeedCE\n\t";
-# warn "host name = $WebworkWebservice::HOST_NAME\n\t";
-
-#FIXME port is not being set!
-# warn "host port = $Webservice::HOST_PORT\n\t";
-
-# #warn "host path = $Webservice::HOST_PATH\n\t";
-# warn " password $WebworkWebservice::PASSWORD\n\t";
use strict;
###############################################################################
@@ -57,7 +46,9 @@
shift if UNIVERSAL::isa($_[0] => __PACKAGE__);
my $rh = shift;
my $indent = shift || 0;
+
my $out = "";
+ return $out if $indent>10;
my $type = ref($rh);
if (defined($type) and $type) {
|
|
From: Mike G. v. a. <we...@ma...> - 2010-05-11 15:50:37
|
Log Message:
-----------
add support for the xml2html features
Added Files:
-----------
webwork2/lib/WeBWorK/ContentGenerator:
renderViaXMLRPC.pm
Revision Data
-------------
--- /dev/null
+++ lib/WeBWorK/ContentGenerator/renderViaXMLRPC.pm
@@ -0,0 +1,399 @@
+################################################################################
+# WeBWorK Online Homework Delivery System
+# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/
+# $CVSHeader: webwork2/lib/WeBWorK/ContentGenerator/renderViaXMLRPC.pm,v 1.1 2010/05/11 15:27:08 gage Exp $
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of either: (a) the GNU General Public License as published by the
+# Free Software Foundation; either version 2, or (at your option) any later
+# version, or (b) the "Artistic License" which comes with this package.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
+# Artistic License for more details.
+################################################################################
+
+=head1 NAME
+
+WeBWorK::ContentGenerator::ProblemRenderer - renderViaXMLRPC is an HTML
+front end for calls to the xmlrpc webservice
+
+=cut
+
+use strict;
+use warnings;
+
+package WeBWorK::ContentGenerator::renderViaXMLRPC;
+use base qw(WeBWorK::ContentGenerator);
+
+
+
+#use Crypt::SSLeay;
+use XMLRPC::Lite;
+use MIME::Base64 qw( encode_base64 decode_base64);
+
+##################################################
+# configuration section -- point back to the current server to process answers
+##################################################
+use constant PROTOCOL => 'http';
+use constant HOSTURL => 'localhost';
+use constant HOSTPORT => 80;
+our $FULL_URL = PROTOCOL."://".HOSTURL; # .":".HOSTPORT;
+our $FORM_ACTION_URL = "/webwork2/html2xml"; # points back to current server
+
+
+# use constant PROTOCOL => 'https'; # or 'http';
+# use constant HOSTURL => 'hosted2.webwork.rochester.edu'; # 'localhost';
+# use constant HOSTPORT => 443; #( for secure https) # 80;
+# our $FULL_URL = PROTOCOL."://".HOSTURL; # .":".HOSTPORT;
+# our $FORM_ACTION_URL = "$FULL_URL/webwork2/html2xml"; # points back to current server
+
+use constant TRANSPORT_METHOD => 'XMLRPC::Lite';
+use constant REQUEST_CLASS => 'WebworkXMLRPC'; # WebworkXMLRPC is used for soap also!!
+use constant REQUEST_URI => 'mod_xmlrpc';
+use constant TEMPOUTPUTFILE => '/Users/gage/Desktop/renderProblemOutput.html'; # client only
+use constant DISPLAY_COMMAND => 'open -a firefox '; # client only
+
+use constant XML_PASSWORD => 'xmlwebwork';
+use constant XML_COURSE => 'daemon_course';
+
+
+use constant DISPLAYMODE => 'images'; # tex and jsMath are other possibilities.
+
+
+our @COMMANDS = qw( listLibraries renderProblem ); #listLib readFile tex2pdf
+
+
+##################################################
+# end configuration section
+##################################################
+
+##################################################
+# input/output section
+##################################################
+
+
+sub pre_header_initialize {
+ my ($self) = @_;
+ my $r = $self->r;
+ my $db = new WeBWorK::DB($r->ce->{dbLayout});
+ $self->{encodedSource} = $r->param('problemSource');
+ my %inputs_ref;
+ foreach my $key ( keys %{ $r->{paramcache} } ) {
+ $inputs_ref{$key} = $r->param("$key");
+ }
+ $self->{inputs_ref} = \%inputs_ref;
+ print STDERR pretty_print_rh($r->{paramcache});
+ $self->xmlrpcCall('renderProblem'); # takes from {encodedSource}; result in {output}
+
+ }
+
+sub content {
+ my ($self) = @_;
+ print $self->{output};
+}
+
+##################################################
+# end input/output section
+##################################################
+
+
+our $result;
+
+##################################################
+# Utilities --
+# this code is identical between renderProblem.pl and renderViaXMLRPC.pm
+##################################################
+
+sub xmlrpcCall {
+ my $self = shift;
+ my $command = shift;
+ $command = 'listLibraries' unless $command;
+
+ my $requestResult = TRANSPORT_METHOD
+ #->uri('http://'.HOSTURL.':'.HOSTPORT.'/'.REQUEST_CLASS)
+ -> proxy($FULL_URL.'/'.REQUEST_URI);
+
+ my $input = $self->setInputTable();
+ local( $result);
+ # use eval to catch errors
+ eval { $result = $requestResult->call(REQUEST_CLASS.'.'.$command,$input) };
+ print STDERR "There were a lot of errors\n" if $@;
+ print STDERR "Errors: \n $@\n End Errors\n" if $@;
+
+
+
+
+ unless (ref($result) and $result->fault) {
+ my $rh_result = $result->result();
+ #print STDERR pretty_print_rh($rh_result);
+ $self->{output} = $self->formatRenderedProblem($rh_result);
+
+ } else {
+ $self->{output} = 'Error from server: ', join( ",\n ",
+ $result->faultcode,
+ $result->faultstring);
+ }
+}
+
+sub encodeSource {
+ my $source = shift;
+ encode_base64($source);
+}
+
+
+sub pretty_print_rh {
+ shift if UNIVERSAL::isa($_[0] => __PACKAGE__);
+ my $rh = shift;
+ my $indent = shift || 0;
+ my $out = "";
+ my $type = ref($rh);
+
+ if (defined($type) and $type) {
+ $out .= " type = $type; ";
+ } elsif (! defined($rh )) {
+ $out .= " type = UNDEFINED; ";
+ }
+ return $out." " unless defined($rh);
+
+ if ( ref($rh) =~/HASH/ or "$rh" =~/HASH/ ) {
+ $out .= "{\n";
+ $indent++;
+ foreach my $key (sort keys %{$rh}) {
+ $out .= " "x$indent."$key => " . pretty_print_rh( $rh->{$key}, $indent ) . "\n";
+ }
+ $indent--;
+ $out .= "\n"." "x$indent."}\n";
+
+ } elsif (ref($rh) =~ /ARRAY/ or "$rh" =~/ARRAY/) {
+ $out .= " ( ";
+ foreach my $elem ( @{$rh} ) {
+ $out .= pretty_print_rh($elem, $indent);
+
+ }
+ $out .= " ) \n";
+ } elsif ( ref($rh) =~ /SCALAR/ ) {
+ $out .= "scalar reference ". ${$rh};
+ } elsif ( ref($rh) =~/Base64/ ) {
+ $out .= "base64 reference " .$$rh;
+ } else {
+ $out .= $rh;
+ }
+
+ return $out." ";
+}
+
+sub setInputTable_for_listLib {
+ my $self = shift;
+ my $out = {
+ pw => XML_PASSWORD,
+ set => 'set0',
+ library_name => 'Library',
+ command => 'all',
+ };
+
+ $out;
+}
+sub setInputTable {
+ my $self = shift;
+ my $out = {
+ pw => XML_PASSWORD,
+ library_name => 'Library',
+ command => 'renderProblem',
+ answer_form_submitted => 1,
+ course => XML_COURSE(),
+ extra_packages_to_load => [qw( AlgParserWithImplicitExpand Expr
+ ExprWithImplicitExpand AnswerEvaluator
+ AnswerEvaluatorMaker
+ )],
+ mode => DISPLAYMODE(),
+ modules_to_evaluate => [ qw(
+Exporter
+DynaLoader
+GD
+WWPlot
+Fun
+Circle
+Label
+PGrandom
+Units
+Hermite
+List
+Match
+Multiple
+Select
+AlgParser
+AnswerHash
+Fraction
+VectorField
+Complex1
+Complex
+MatrixReal1 Matrix
+Distributions
+Regression
+
+ )],
+ envir => $self->environment(),
+ problem_state => {
+
+ num_of_correct_ans => 2,
+ num_of_incorrect_ans => 4,
+ recorded_score => 1.0,
+ },
+ source => $self->{encodedSource}, #base64 encoded
+
+
+
+ };
+
+ $out;
+}
+
+sub environment {
+ my $self = shift;
+ my $envir = {
+ answerDate => '4014438528',
+ CAPA_Graphics_URL=>"not defined",
+ CAPA_GraphicsDirectory =>"not defined",
+ CAPA_MCTools=>"not defined",
+ CAPA_Tools=>'not defined',
+ cgiDirectory=>'Not defined',
+ cgiURL => 'Not defined',
+ classDirectory=> 'Not defined',
+ courseName=>'Not defined',
+ courseScriptsDirectory=>'not defined',
+ displayMode=>DISPLAYMODE,
+ dueDate=> '4014438528',
+ externalGif2EpsPath=>'not defined',
+ externalPng2EpsPath=>'not defined',
+ externalTTHPath=>'/usr/local/bin/tth',
+ fileName=>'set0/prob1a.pg',
+ formattedAnswerDate=>'6/19/00',
+ formattedDueDate=>'6/19/00',
+ formattedOpenDate=>'6/19/00',
+ functAbsTolDefault=> 0.0000001,
+ functLLimitDefault=>0,
+ functMaxConstantOfIntegration=> 1000000000000.0,
+ functNumOfPoints=> 5,
+ functRelPercentTolDefault=> 0.000001,
+ functULimitDefault=>1,
+ functVarDefault=> 'x',
+ functZeroLevelDefault=> 0.000001,
+ functZeroLevelTolDefault=>0.000001,
+ htmlDirectory =>'not defined',
+ htmlURL =>'not defined',
+ inputs_ref => $self->{inputs_ref},
+ macroDirectory=>'not defined',
+ numAbsTolDefault=>0.0000001,
+ numFormatDefault=>'%0.13g',
+ numOfAttempts=> 0,
+ numRelPercentTolDefault => 0.0001,
+ numZeroLevelDefault =>0.000001,
+ numZeroLevelTolDefault =>0.000001,
+ openDate=> '3014438528',
+ PRINT_FILE_NAMES_FOR => [ 'gage'],
+ probFileName => 'set0/prob1a.pg',
+ problemSeed => 1234,
+ problemValue =>1,
+ probNum => 13,
+ psvn => 54321,
+ psvnNumber=> 54321,
+ questionNumber => 1,
+ scriptDirectory => 'Not defined',
+ sectionName => 'Gage',
+ sectionNumber => 1,
+ sessionKey=> 'Not defined',
+ setNumber =>'not defined',
+ studentLogin =>'gage',
+ studentName => 'Mike Gage',
+ tempDirectory => 'not defined',
+ templateDirectory=>'not defined',
+ tempURL=>'not defined',
+ webworkDocsURL => 'not defined',
+ };
+ $envir;
+};
+
+sub formatAnswerRow {
+ my $self = shift;
+ my $rh_answer = shift;
+ my $problemNumber = shift;
+ my $answerString = $rh_answer->{original_student_ans}||' ';
+ my $correctAnswer = $rh_answer->{correct_ans}||'';
+ my $ans_message = $rh_answer->{ans_message};
+ my $score = ($rh_answer->{score}) ? 'Correct' : 'Incorrect';
+ my $row = qq{
+ <tr>
+ <td>
+ Prob: $problemNumber
+ </td>
+ <td>
+ $answerString
+ </td>
+ <td>
+ $score
+ </td>
+ <td>
+ Correct answer is $correctAnswer
+ </td>
+ <td>
+ <i>$ans_message</i>
+ </td>
+ </tr>\n
+ };
+ $row;
+}
+
+sub formatRenderedProblem {
+ my $self = shift;
+ my $rh_result = shift; # wrap problem in formats
+ my $problemText = decode_base64($rh_result->{text});
+ my $rh_answers = $rh_result->{answers};
+ my $encodedSource = $self->{encodedSource}||'foobar';
+ my $warnings = '';
+ if ( defined ($rh_result->{WARNINGS}) and $rh_result->{WARNINGS} ){
+ $warnings = "<div style=\"background-color:pink\">
+ <p >WARNINGS</p><p>".decode_base64($rh_result->{WARNINGS})."</p></div>";
+ }
+
+ ;
+ # collect answers
+ my $answerTemplate = q{<hr>ANSWERS <table border="3" align="center">};
+ my $problemNumber = 1;
+ foreach my $key (sort keys %{$rh_answers}) {
+ $answerTemplate .= $self->formatAnswerRow($rh_answers->{$key}, $problemNumber++);
+ }
+ $answerTemplate .= q{</table> <hr>};
+
+
+
+ my $problemTemplate = <<ENDPROBLEMTEMPLATE;
+<html>
+<head>
+<title>WeBWorK Editor</title>
+</head>
+<body>
+ $answerTemplate
+ $warnings
+ <form action="$FORM_ACTION_URL" method="post">
+ $problemText
+ <input type="hidden" name="answersSubmitted" value="1">
+ <input type="hidden" name="problemAddress" value="probSource">
+ <input type="hidden" name="problemSource" value="$encodedSource">
+ <input type="hidden" name="problemSeed" value="1234">
+ <input type="hidden" name="pathToProblemFile" value="foobar">
+ <p><input type="submit" name="submit" value="submit answers"></p>
+ </form>
+</body>
+</html>
+
+ENDPROBLEMTEMPLATE
+
+
+
+ $problemTemplate;
+}
+
+
+1;
|
|
From: Mike G. v. a. <we...@ma...> - 2010-05-11 15:48:56
|
Log Message:
-----------
add xml2html feature
Modified Files:
--------------
webwork2/lib/WeBWorK:
URLPath.pm
Revision Data
-------------
Index: URLPath.pm
===================================================================
RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/URLPath.pm,v
retrieving revision 1.36
retrieving revision 1.37
diff -Llib/WeBWorK/URLPath.pm -Llib/WeBWorK/URLPath.pm -u -r1.36 -r1.37
--- lib/WeBWorK/URLPath.pm
+++ lib/WeBWorK/URLPath.pm
@@ -45,6 +45,7 @@
root /
course_admin /admin/ -> logout, options, instructor_tools
+ html2xml /html2xml/
set_list /$courseID/
equation_display /$courseID/equation/
@@ -110,7 +111,7 @@
root => {
name => 'WeBWorK',
parent => '',
- kids => [ qw/course_admin set_list/ ],
+ kids => [ qw/course_admin html2xml set_list / ],
match => qr|^/|,
capture => [ qw// ],
produce => '/',
@@ -127,7 +128,16 @@
},
################################################################################
-
+ html2xml => {
+ name => 'html2xml',
+ parent => 'root', # 'set_list',
+ kids => [ qw// ],
+ match => qr|^html2xml/|,
+ capture => [ qw// ],
+ produce => 'html2xml/',
+ display => 'WeBWorK::ContentGenerator::renderViaXMLRPC',
+ },
+
set_list => {
name => '$courseID',
parent => 'root',
|