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: Sam H. v. a. <we...@ma...> - 2005-10-26 16:47:43
|
Log Message: ----------- cleaned up error reporting for status methods. if arguments are not defined and non-empty, a warning is given (using "carp") and an undefined value is returned. Modified Files: -------------- webwork2/lib/WeBWorK: CourseEnvironment.pm webwork2/lib/WeBWorK/ContentGenerator: Login.pm Revision Data ------------- Index: CourseEnvironment.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/CourseEnvironment.pm,v retrieving revision 1.32 retrieving revision 1.33 diff -Llib/WeBWorK/CourseEnvironment.pm -Llib/WeBWorK/CourseEnvironment.pm -u -r1.32 -r1.33 --- lib/WeBWorK/CourseEnvironment.pm +++ lib/WeBWorK/CourseEnvironment.pm @@ -253,8 +253,10 @@ sub status_abbrev_to_name { my ($ce, $status_abbrev) = @_; - carp "status_abbrev_to_name: status_abbrev (first argument) must be defined and non-empty" - if not defined $status_abbrev or $status_abbrev eq ""; + if (not defined $status_abbrev or $status_abbrev eq "") { + carp "status_abbrev_to_name: status_abbrev (first argument) must be defined and non-empty"; + return; + } return $ce->{_status_abbrev_to_name}{$status_abbrev}; } @@ -268,8 +270,10 @@ sub status_name_to_abbrevs { my ($ce, $status_name) = @_; - carp "status_name_to_abbrevs: status_name (first argument) must be defined and non-empty" - if not defined $status_name or $status_name eq ""; + if (not defined $status_name or $status_name eq "") { + carp "status_name_to_abbrevs: status_name (first argument) must be defined and non-empty"; + return; + } return unless exists $ce->{statuses}{$status_name}; return @{$ce->{statuses}{$status_name}{abbrevs}}; @@ -283,10 +287,14 @@ sub status_has_behavior { my ($ce, $status_name, $behavior) = @_; - carp "status_has_behavior: status_name (first argument) must be defined and non-empty" - if not defined $status_name or $status_name eq ""; - carp "status_has_behavior: behavior (second argument) must be defined and non-empty" - if not defined $behavior or $behavior eq ""; + if (not defined $status_name or $status_name eq "") { + carp "status_has_behavior: status_name (first argument) must be defined and non-empty"; + return; + } + if (not defined $behavior or $behavior eq "") { + carp "status_has_behavior: behavior (second argument) must be defined and non-empty"; + return; + } if (exists $ce->{statuses}{$status_name}) { if (exists $ce->{statuses}{$status_name}{behaviors}) { @@ -309,16 +317,20 @@ sub status_abbrev_has_behavior { my ($ce, $status_abbrev, $behavior) = @_; - carp "status_abbrev_has_behavior: status_abbrev (first argument) must be defined and non-empty" - if not defined $status_abbrev or $status_abbrev eq ""; - carp "status_abbrev_has_behavior: behavior (second argument) must be defined and non-empty" - if not defined $behavior or $behavior eq ""; + if (not defined $status_abbrev or $status_abbrev eq "") { + carp "status_abbrev_has_behavior: status_abbrev (first argument) must be defined and non-empty"; + return; + } + if (not defined $behavior or $behavior eq "") { + carp "status_abbrev_has_behavior: behavior (second argument) must be defined and non-empty"; + return; + } my $status_name = $ce->status_abbrev_to_name($status_abbrev); if (defined $status_name) { return $ce->status_has_behavior($status_name, $behavior); } else { - carp "status abbreviation '$status_abbrev' not found in \%statuses -- assuming no behaviors.\n"; + warn "status abbreviation '$status_abbrev' not found in \%statuses -- assuming no behaviors.\n"; } } Index: Login.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/Login.pm,v retrieving revision 1.34 retrieving revision 1.35 diff -Llib/WeBWorK/ContentGenerator/Login.pm -Llib/WeBWorK/ContentGenerator/Login.pm -u -r1.34 -r1.35 --- lib/WeBWorK/ContentGenerator/Login.pm +++ lib/WeBWorK/ContentGenerator/Login.pm @@ -177,7 +177,6 @@ my @GuestUsers = $db->getUsers(@guestUserIDs); my @allowedGuestUsers; foreach my $GuestUser (@GuestUsers) { - warn "GuestUser=".$GuestUser->toString."\n"; next unless defined $GuestUser->status; next unless $GuestUser->status ne ""; push @allowedGuestUsers, $GuestUser |
From: Sam H. v. a. <we...@ma...> - 2005-10-26 16:41:42
|
Log Message: ----------- possible fix for problem when status_abbrev_has_behavior is used with an abbrev that doesn't map to any status. Modified Files: -------------- webwork2/lib/WeBWorK: CourseEnvironment.pm Revision Data ------------- Index: CourseEnvironment.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/CourseEnvironment.pm,v retrieving revision 1.31 retrieving revision 1.32 diff -Llib/WeBWorK/CourseEnvironment.pm -Llib/WeBWorK/CourseEnvironment.pm -u -r1.31 -r1.32 --- lib/WeBWorK/CourseEnvironment.pm +++ lib/WeBWorK/CourseEnvironment.pm @@ -314,7 +314,12 @@ carp "status_abbrev_has_behavior: behavior (second argument) must be defined and non-empty" if not defined $behavior or $behavior eq ""; - return $ce->status_has_behavior($ce->status_abbrev_to_name($status_abbrev), $behavior); + my $status_name = $ce->status_abbrev_to_name($status_abbrev); + if (defined $status_name) { + return $ce->status_has_behavior($status_name, $behavior); + } else { + carp "status abbreviation '$status_abbrev' not found in \%statuses -- assuming no behaviors.\n"; + } } =back |
From: Sam H. v. a. <we...@ma...> - 2005-10-26 16:32:41
|
Log Message: ----------- more debugging Modified Files: -------------- webwork2/lib/WeBWorK/ContentGenerator: Login.pm Revision Data ------------- Index: Login.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/Login.pm,v retrieving revision 1.33 retrieving revision 1.34 diff -Llib/WeBWorK/ContentGenerator/Login.pm -Llib/WeBWorK/ContentGenerator/Login.pm -u -r1.33 -r1.34 --- lib/WeBWorK/ContentGenerator/Login.pm +++ lib/WeBWorK/ContentGenerator/Login.pm @@ -177,6 +177,7 @@ my @GuestUsers = $db->getUsers(@guestUserIDs); my @allowedGuestUsers; foreach my $GuestUser (@GuestUsers) { + warn "GuestUser=".$GuestUser->toString."\n"; next unless defined $GuestUser->status; next unless $GuestUser->status ne ""; push @allowedGuestUsers, $GuestUser |
From: Sam H. v. a. <we...@ma...> - 2005-10-26 16:25:46
|
Log Message: ----------- typos...whoops... Modified Files: -------------- webwork2/lib/WeBWorK/ContentGenerator: Login.pm Revision Data ------------- Index: Login.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/Login.pm,v retrieving revision 1.32 retrieving revision 1.33 diff -Llib/WeBWorK/ContentGenerator/Login.pm -Llib/WeBWorK/ContentGenerator/Login.pm -u -r1.32 -r1.33 --- lib/WeBWorK/ContentGenerator/Login.pm +++ lib/WeBWorK/ContentGenerator/Login.pm @@ -178,9 +178,9 @@ my @allowedGuestUsers; foreach my $GuestUser (@GuestUsers) { next unless defined $GuestUser->status; - next unless $GusetUser->status ne ""; + next unless $GuestUser->status ne ""; push @allowedGuestUsers, $GuestUser - if $ce->status_abbrev_has_behavior($_->status, "allow_course_access"); + if $ce->status_abbrev_has_behavior($GuestUser->status, "allow_course_access"); } # form for guest login |
From: Sam H. v. a. <we...@ma...> - 2005-10-26 16:25:06
|
Log Message: ----------- made code for filtering guest users a little more clear. Modified Files: -------------- webwork2/lib/WeBWorK/ContentGenerator: Login.pm Revision Data ------------- Index: Login.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/Login.pm,v retrieving revision 1.31 retrieving revision 1.32 diff -Llib/WeBWorK/ContentGenerator/Login.pm -Llib/WeBWorK/ContentGenerator/Login.pm -u -r1.31 -r1.32 --- lib/WeBWorK/ContentGenerator/Login.pm +++ lib/WeBWorK/ContentGenerator/Login.pm @@ -175,9 +175,13 @@ # figure out if there are any valid practice users my @guestUserIDs = grep m/^$practiceUserPrefix/, $db->listUsers; my @GuestUsers = $db->getUsers(@guestUserIDs); - my @allowedGuestUsers = grep { defined $_->status and $_->status ne "" - and $ce->status_abbrev_has_behavior($_->status, "allow_course_access") - } @GuestUsers; + my @allowedGuestUsers; + foreach my $GuestUser (@GuestUsers) { + next unless defined $GuestUser->status; + next unless $GusetUser->status ne ""; + push @allowedGuestUsers, $GuestUser + if $ce->status_abbrev_has_behavior($_->status, "allow_course_access"); + } # form for guest login if (@allowedGuestUsers) { |
From: Sam H. v. a. <we...@ma...> - 2005-10-26 16:23:01
|
Log Message: ----------- changed erorrs to warnings for the time being. Modified Files: -------------- webwork2/lib/WeBWorK: CourseEnvironment.pm Revision Data ------------- Index: CourseEnvironment.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/CourseEnvironment.pm,v retrieving revision 1.30 retrieving revision 1.31 diff -Llib/WeBWorK/CourseEnvironment.pm -Llib/WeBWorK/CourseEnvironment.pm -u -r1.30 -r1.31 --- lib/WeBWorK/CourseEnvironment.pm +++ lib/WeBWorK/CourseEnvironment.pm @@ -52,7 +52,7 @@ use strict; use warnings; -use Carp qw/croak/; +use Carp; use Safe; use WeBWorK::Utils qw(readFile); use WeBWorK::Debug; @@ -253,7 +253,7 @@ sub status_abbrev_to_name { my ($ce, $status_abbrev) = @_; - croak "status_abbrev_to_name: status_abbrev (first argument) must be defined and non-empty" + carp "status_abbrev_to_name: status_abbrev (first argument) must be defined and non-empty" if not defined $status_abbrev or $status_abbrev eq ""; return $ce->{_status_abbrev_to_name}{$status_abbrev}; @@ -268,7 +268,7 @@ sub status_name_to_abbrevs { my ($ce, $status_name) = @_; - croak "status_name_to_abbrevs: status_name (first argument) must be defined and non-empty" + carp "status_name_to_abbrevs: status_name (first argument) must be defined and non-empty" if not defined $status_name or $status_name eq ""; return unless exists $ce->{statuses}{$status_name}; @@ -283,9 +283,9 @@ sub status_has_behavior { my ($ce, $status_name, $behavior) = @_; - croak "status_has_behavior: status_name (first argument) must be defined and non-empty" + carp "status_has_behavior: status_name (first argument) must be defined and non-empty" if not defined $status_name or $status_name eq ""; - croak "status_has_behavior: behavior (second argument) must be defined and non-empty" + carp "status_has_behavior: behavior (second argument) must be defined and non-empty" if not defined $behavior or $behavior eq ""; if (exists $ce->{statuses}{$status_name}) { @@ -309,9 +309,9 @@ sub status_abbrev_has_behavior { my ($ce, $status_abbrev, $behavior) = @_; - croak "status_abbrev_has_behavior: status_abbrev (first argument) must be defined and non-empty" + carp "status_abbrev_has_behavior: status_abbrev (first argument) must be defined and non-empty" if not defined $status_abbrev or $status_abbrev eq ""; - croak "status_abbrev_has_behavior: behavior (second argument) must be defined and non-empty" + carp "status_abbrev_has_behavior: behavior (second argument) must be defined and non-empty" if not defined $behavior or $behavior eq ""; return $ce->status_has_behavior($ce->status_abbrev_to_name($status_abbrev), $behavior); |
From: Sam H. v. a. <we...@ma...> - 2005-10-26 16:19:44
|
Log Message: ----------- only check status behaviors for guest users that have a defined status. apparently, status can still sometimes be empty. Modified Files: -------------- webwork2/lib/WeBWorK/ContentGenerator: Login.pm Revision Data ------------- Index: Login.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/Login.pm,v retrieving revision 1.30 retrieving revision 1.31 diff -Llib/WeBWorK/ContentGenerator/Login.pm -Llib/WeBWorK/ContentGenerator/Login.pm -u -r1.30 -r1.31 --- lib/WeBWorK/ContentGenerator/Login.pm +++ lib/WeBWorK/ContentGenerator/Login.pm @@ -175,7 +175,9 @@ # figure out if there are any valid practice users my @guestUserIDs = grep m/^$practiceUserPrefix/, $db->listUsers; my @GuestUsers = $db->getUsers(@guestUserIDs); - my @allowedGuestUsers = grep { $ce->status_abbrev_has_behavior($_->status, "allow_course_access") } @GuestUsers; + my @allowedGuestUsers = grep { defined $_->status and $_->status ne "" + and $ce->status_abbrev_has_behavior($_->status, "allow_course_access") + } @GuestUsers; # form for guest login if (@allowedGuestUsers) { |
From: Sam H. v. a. <we...@ma...> - 2005-10-26 15:52:25
|
Log Message: ----------- added error checking to ensure that arguments to status lookup methods are defined. Modified Files: -------------- webwork2/lib/WeBWorK: CourseEnvironment.pm Revision Data ------------- Index: CourseEnvironment.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/CourseEnvironment.pm,v retrieving revision 1.29 retrieving revision 1.30 diff -Llib/WeBWorK/CourseEnvironment.pm -Llib/WeBWorK/CourseEnvironment.pm -u -r1.29 -r1.30 --- lib/WeBWorK/CourseEnvironment.pm +++ lib/WeBWorK/CourseEnvironment.pm @@ -253,6 +253,9 @@ sub status_abbrev_to_name { my ($ce, $status_abbrev) = @_; + croak "status_abbrev_to_name: status_abbrev (first argument) must be defined and non-empty" + if not defined $status_abbrev or $status_abbrev eq ""; + return $ce->{_status_abbrev_to_name}{$status_abbrev}; } @@ -265,6 +268,9 @@ sub status_name_to_abbrevs { my ($ce, $status_name) = @_; + croak "status_name_to_abbrevs: status_name (first argument) must be defined and non-empty" + if not defined $status_name or $status_name eq ""; + return unless exists $ce->{statuses}{$status_name}; return @{$ce->{statuses}{$status_name}{abbrevs}}; } @@ -277,6 +283,11 @@ sub status_has_behavior { my ($ce, $status_name, $behavior) = @_; + croak "status_has_behavior: status_name (first argument) must be defined and non-empty" + if not defined $status_name or $status_name eq ""; + croak "status_has_behavior: behavior (second argument) must be defined and non-empty" + if not defined $behavior or $behavior eq ""; + if (exists $ce->{statuses}{$status_name}) { if (exists $ce->{statuses}{$status_name}{behaviors}) { my $num_matches = grep { $_ eq $behavior } @{$ce->{statuses}{$status_name}{behaviors}}; @@ -298,6 +309,11 @@ sub status_abbrev_has_behavior { my ($ce, $status_abbrev, $behavior) = @_; + croak "status_abbrev_has_behavior: status_abbrev (first argument) must be defined and non-empty" + if not defined $status_abbrev or $status_abbrev eq ""; + croak "status_abbrev_has_behavior: behavior (second argument) must be defined and non-empty" + if not defined $behavior or $behavior eq ""; + return $ce->status_has_behavior($ce->status_abbrev_to_name($status_abbrev), $behavior); } |
From: Mike G. v. a. <we...@ma...> - 2005-10-25 20:35:30
|
Log Message: ----------- Fixed bug #870 so that setHeaders can be viewed and edited. Modified Files: -------------- webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor: PGProblemEditor.pm Revision Data ------------- Index: PGProblemEditor.pm =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm,v retrieving revision 1.60 retrieving revision 1.61 diff -Llib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm -Llib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm -u -r1.60 -r1.61 --- lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm +++ lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm @@ -543,9 +543,17 @@ # This is a subroutine, not a method # sub determineLocalFilePath { + my $self= shift; die "determineLocalFilePath is a method" unless ref($self); my $path = shift; +# my $default_screen_header_path = $self->r->ce->{webworkFiles}->{hardcopySnippets}->{setHeader}; +# my $default_hardcopy_header_path = $self->r->ce->{webworkFiles}->{screenSnippets}->{setHeader}; + my $setID = $self->{setID} || int(rand(1000)); if ($path =~ /Library/) { $path =~ s|^.*?Library/||; # truncate the url up to a segment such as ...rochesterLibrary/....... +# } elsif ($path eq $default_screen_header_path) { +# $path = "set$setID/setHeader.pg"; +# } elsif ($path eq $default_hardcopy_header_path) { +# $path = "set$setID/hardcopyHeader.tex"; } else { # if its not in a library we'll just save it locally $path = "new_problem_".int(rand(1000)).".pg"; #l hope there aren't any collisions. } @@ -560,13 +568,13 @@ $user = int(rand(1000)) unless defined $user; my $setID = $self->{setID} || int(rand(1000)); my $courseDirectory = $self->r->ce->{courseDirs}; - #FIXME -- we can put all of the temp files in a special directory - # so that even library files can viewed. ############### # Calculate the location of the temporary file ############### my $templatesDirectory = $courseDirectory->{templates}; - my $blank_file_path = $self->r->ce->{webworkFiles}->{screenSnippets}->{blankProblem}; + my $blank_file_path = $self->r->ce->{webworkFiles}->{screenSnippets}->{blankProblem}; + my $default_screen_header_path = $self->r->ce->{webworkFiles}->{hardcopySnippets}->{setHeader}; + my $default_hardcopy_header_path = $self->r->ce->{webworkFiles}->{screenSnippets}->{setHeader}; my $tmpEditFileDirectory = (defined ($courseDirectory->{tmpEditFileDir}) ) ? $courseDirectory->{tmpEditFileDir} : "$templatesDirectory/tmpEdit"; if ($path =~ /^$templatesDirectory/ ) { $path =~ s|^$templatesDirectory||; @@ -574,6 +582,10 @@ $path = "$tmpEditFileDirectory/$path.$user.tmp"; } elsif ($path eq $blank_file_path) { $path = "$tmpEditFileDirectory/blank.$setID.$user.tmp"; # handle the case of the blank problem + } elsif ($path eq $default_screen_header_path) { + $path = "$tmpEditFileDirectory/screenHeader.$setID.$user.tmp"; # handle the case of the screen header in snippets + } elsif ($path eq $default_hardcopy_header_path) { + $path = "$tmpEditFileDirectory/hardcopyHeader.$setID.$user.tmp"; # handle the case of the hardcopy header in snippets } else { die "determineTempFilePath should only be used on paths within the templates directory, not on $path"; } @@ -1069,9 +1081,9 @@ ################################################# # Update set record ################################################# - my $setRecord = $self->db->getGlobalSet($targetSetName); + my $setRecord = $self->r->db->getGlobalSet($targetSetName); $setRecord->set_header($sourceFilePath); - if( $self->db->putGlobalSet($setRecord) ) { + if( $self->r->db->putGlobalSet($setRecord) ) { $self->addgoodmessage("Added $sourceFilePath to ". $targetSetName. " as new set header ") ; } else { $self->addbadmessage("Unable to make $sourceFilePath the set header for $targetSetName"); @@ -1330,12 +1342,12 @@ my $editFilePath = $self->{editFilePath}; # path to the permanent file to be edited return "" unless -e $editFilePath; return "" if -w $editFilePath; - return "" unless $self->{file_type} eq 'problem' - or $self->{file_type} eq 'set_header' ; + return "" unless $self->{file_type} eq 'problem'; + # or $self->{file_type} eq 'set_header' ; # need problem structure to make local copy -- not available for header # or $self->{file_type} eq 'source_path_for_problem_file'; # need setID and problemID to make local copy return join ("", - "Make local copy at: [TMPL]/".determineLocalFilePath($editFilePath), - CGI::hidden(-name=>'action.make_local_copy.target_file', -value=>determineLocalFilePath($editFilePath) ), + "Make local copy at: [TMPL]/".($self->determineLocalFilePath($editFilePath)), + CGI::hidden(-name=>'action.make_local_copy.target_file', -value=>$self->determineLocalFilePath($editFilePath) ), CGI::hidden(-name=>'action.make_local_copy.source_file', -value=>$editFilePath ), ); |
From: dpvc v. a. <we...@ma...> - 2005-10-22 04:31:06
|
Log Message: ----------- Updated to jsMath v2.3b. See the jsMath website for the change log for this version. Modified Files: -------------- webwork-modperl/htdocs/jsMath: jsMath-controls.html jsMath-fallback-mac.js jsMath-fallback-pc.js jsMath-fallback-symbols.js jsMath-fallback-unix.js jsMath.js webwork-modperl/htdocs/jsMath/plugins: tex2math.js Revision Data ------------- Index: jsMath-fallback-symbols.js =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/htdocs/jsMath/jsMath-fallback-symbols.js,v retrieving revision 1.1 retrieving revision 1.2 diff -Lhtdocs/jsMath/jsMath-fallback-symbols.js -Lhtdocs/jsMath/jsMath-fallback-symbols.js -u -r1.1 -r1.2 --- htdocs/jsMath/jsMath-fallback-symbols.js +++ htdocs/jsMath/jsMath-fallback-symbols.js @@ -33,7 +33,7 @@ * characters precomputed */ TeX: function (C,font,style,size) { - c = jsMath.TeX[font][C]; + c = jsMath.TeX[font][C]; if (!c.tclass) {c.tclass = font} if (c.img != null) {return this.TeX_orig(C,font,style,size)} if (c.h != null && c.a == null) {c.a = c.h-1.1*jsMath.TeX.x_height} var box = this.Text(c.c,c.tclass,style,size,c.a,c.d); Index: jsMath-controls.html =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/htdocs/jsMath/jsMath-controls.html,v retrieving revision 1.2 retrieving revision 1.3 diff -Lhtdocs/jsMath/jsMath-controls.html -Lhtdocs/jsMath/jsMath-controls.html -u -r1.2 -r1.3 --- htdocs/jsMath/jsMath-controls.html +++ htdocs/jsMath/jsMath-controls.html @@ -197,13 +197,14 @@ </TD></TR> <TR><TD ALIGN="CENTER" COLSPAN="2"> <INPUT TYPE="BUTTON" ID="jsMath.resolution" VALUE="Hi-Res Fonts for Printing" - STYLE="width:16em" onClick="jsMath.Controls.PrintResolution()"> + STYLE="width:17em" onClick="jsMath.Controls.PrintResolution()"> </TD></TR> <TR><TD HEIGHT="5"></TD></TR> -<TR VALIGN="TOP"><TD ALIGN="LEFT"> -<INPUT TYPE="BUTTON" VALUE="Options" ID="jsMath.opts" STYLE="width:7em" onClick="jsMath.Controls.Options()"> +<TR><TD ALIGN="LEFT"> +<INPUT TYPE="BUTTON" VALUE="Options" ID="jsMath.opts" STYLE="width:8em" onClick="jsMath.Controls.Options()"> + </TD><TD ALIGN="RIGHT"> -<INPUT TYPE="BUTTON" VALUE="Done" ID="jsMath.done" STYLE="width:7em" onClick="jsMath.Controls.Close()"> +<INPUT TYPE="BUTTON" VALUE="Done" ID="jsMath.done" STYLE="width:8em" onClick="jsMath.Controls.Close()"> </TD></TR> </TABLE></TD></TR> <TR><TD HEIGHT="10"></TD></TR> Index: jsMath-fallback-unix.js =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/htdocs/jsMath/jsMath-fallback-unix.js,v retrieving revision 1.2 retrieving revision 1.3 diff -Lhtdocs/jsMath/jsMath-fallback-unix.js -Lhtdocs/jsMath/jsMath-fallback-unix.js -u -r1.2 -r1.3 --- htdocs/jsMath/jsMath-fallback-unix.js +++ htdocs/jsMath/jsMath-fallback-unix.js @@ -874,7 +874,7 @@ /* * We need to replace the jsMath.Box.TeX function in order to use the - * different font metrics in thie tables above, and to handle the + * different font metrics in the tables above, and to handle the * scaling better. */ @@ -883,7 +883,7 @@ TeX_orig = jsMath.Box.TeX, TeX: function (C,font,style,size) { - c = jsMath.TeX[font][C]; + c = jsMath.TeX[font][C]; if (!c.tclass) {c.tclass = font} if (c.img != null) {return this.TeX_orig(C,font,style,size)} if (c.h != null && c.a == null) {c.a = c.h-1.1*jsMath.TeX.x_height} var box = this.Text(c.c,c.tclass,style,size,c.a,c.d); Index: jsMath.js =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/htdocs/jsMath/jsMath.js,v retrieving revision 1.17 retrieving revision 1.18 diff -Lhtdocs/jsMath/jsMath.js -Lhtdocs/jsMath/jsMath.js -u -r1.17 -r1.18 --- htdocs/jsMath/jsMath.js +++ htdocs/jsMath/jsMath.js @@ -66,7 +66,7 @@ var jsMath = { - version: "2.1d", // change this if you edit the file + version: "2.3b", // change this if you edit the file // // Name of image files @@ -210,7 +210,7 @@ * Get the em size and if it has changed, reinitialize the sizes */ ReInit: function () { - var em = this.BBoxFor('<DIV STYLE="width: 10em; height: 1em"></DIV>').w/10; + var em = this.BBoxFor('<SPAN STYLE="width: 10em; height: 1em"></SPAN>').w/10; if (em != this.em) {this.Init(em)} }, @@ -288,7 +288,8 @@ * Source a jsMath JavaScript file */ Script: function (file) { - document.write('<SCRIPT SRC="'+jsMath.root+file+'"></SCRIPT>'); + if (!file.match('^([a-zA-Z]+:/)?/')) {file = jsMath.root + file} + document.write('<SCRIPT SRC="'+file+'"></SCRIPT>'); }, /* @@ -298,7 +299,6 @@ jsMath.hidden = this.TopHTML("Hidden",{'class':"normal"},{ position:"absolute", top:0, left:0, border:0, padding:0, margin:0 }); - jsMath.hiddenDIV = jsMath.hidden; return; }, @@ -764,16 +764,22 @@ '<B>No TeX fonts found</B> -- using image fonts instead.<BR>\n' + 'These may be slow and might not print well.<BR>\n' + 'Use the jsMath control panel to get additional information.', + + extra_message: + 'Extra TeX fonts not found: <B><SPAN ID="jsMath.ExtraFonts"></SPAN></B><BR>' + + 'Using image fonts instead. This may be slow and might not print well.<BR>\n' + + 'Use the jsMath control panel to get additional information.', /* * Look to see if a font is found. HACK! - * Check the character in the '|' position, and see if it is - * wider than the usual '|'. + * Check the character in a given position, and see if it is + * wider than the usual one in that position. */ Test1: function (name,n,factor) { if (n == null) {n = 124}; if (factor == null) {factor = 2} var wh1 = jsMath.BBoxFor('<SPAN STYLE="font-family: '+name+', serif">'+jsMath.TeX[name][n].c+'</SPAN>'); var wh2 = jsMath.BBoxFor('<SPAN STYLE="font-family: serif">'+jsMath.TeX[name][n].c+'</SPAN>'); + //alert([wh1.w,wh2.w,wh1.h,factor*wh2.w]); return (wh1.w > factor*wh2.w && wh1.h != 0); }, @@ -781,6 +787,7 @@ if (n == null) {n = 124}; if (factor == null) {factor = 2} var wh1 = jsMath.BBoxFor('<SPAN STYLE="font-family: '+name+', serif">'+jsMath.TeX[name][n].c+'</SPAN>'); var wh2 = jsMath.BBoxFor('<SPAN STYLE="font-family: serif">'+jsMath.TeX[name][n].c+'</SPAN>'); + //alert([wh2.w,wh1.w,wh1.h,factor*wh1.w]); return (wh2.w > factor*wh1.w && wh1.h != 0); }, @@ -843,6 +850,7 @@ * of your page. */ Message: function (message) { + if(jsMath.Element("Warning")) return; var div = jsMath.Setup.TopHTML("Warning",{'class':'jsM_Warning'},{}); div.innerHTML = '<CENTER><TABLE><TR><TD>' @@ -859,7 +867,62 @@ HideMessage: function () { var message = jsMath.Element("Warning"); if (message) {message.style.display = "none"} - } + }, + + /* + * Register an extra font so jsMath knows about it + */ + Register: function (data) { + if (typeof(data) == 'string') {data = {name: data}} + var fontname = data.name; var name = fontname.replace(/10$/,''); + var fontfam = jsMath.TeX.fam.length; + if (!data.style) {data.style = "font-family: "+fontname+", serif"} + if (!data.styles) {data.styles = {}} + if (!data.macros) {data.macros = {}} + /* + * Register font family + */ + jsMath.TeX.fam[fontfam] = fontname; + data.macros[name] = ['HandleFont',fontfam]; + jsMath.Add(jsMath.Parser.prototype.macros,data.macros); + /* + * Set up styles + */ + data.styles['.'+fontname] = data.style; + jsMath.Setup.Styles(data.styles); + jsMath.Setup.TeXfont(fontname); + /* + * Check for font and give message if missing + */ + var hasTeXfont = !jsMath.nofonts && + data.test(fontname,data.testChar,data.testFactor); + if (hasTeXfont && jsMath.Controls.cookie.font == 'tex') { + if (data.tex) {data.tex(fontname,fontfam)} + return; + } + if (!hasTeXfont && jsMath.Controls.cookie.warn && + jsMath.Controls.cookie.font == 'tex' && !jsMath.nofonts) { + if (!jsMath.Element("Warning")) this.Message(this.extra_message); + var extra = jsMath.Element("ExtraFonts"); + if (extra) { + if (extra.innerHTML != "") {extra.innerHTML += ','} + extra.innerHTML += " " + fontname; + } + } + if (jsMath.Controls.cookie.font == 'unicode') { + if (data.fallback) {data.fallback(fontname,fontfam)} + return; + } + // Image fonts + var font = {}; font[fontname] = ['all']; + jsMath.Img.SetFont(font); + jsMath.Img.LoadFont(fontname); + }, + + /* + * Load a font + */ + Load: function (name) {jsMath.Setup.Script("fonts/"+name+"/def.js")} }; @@ -1930,9 +1993,6 @@ // image fonts are loaded loaded: 0, - // temporarily force scaling of images (when jsMath.hidden can't be changed) - forceScale: 0, - // add characters to be drawn using images SetFont: function (change) { for (var font in change) { @@ -2225,7 +2285,7 @@ TeX: function (C,font,style,size) { var c = jsMath.TeX[font][C]; if (c.d == null) {c.d = 0}; if (c.h == null) {c.h = 0} - if (c.img != null) this.TeXIMG(font,C,jsMath.Typeset.StyleSize(style,size)); + if (c.img != null && c.c != '') this.TeXIMG(font,C,jsMath.Typeset.StyleSize(style,size)); var scale = jsMath.Typeset.TeX(style,size).scale; var h = c.h + jsMath.TeX[font].dh var box = new jsMath.Box('text',c.c,c.w*scale,h*scale,c.d*scale); @@ -2259,7 +2319,7 @@ var c = jsMath.TeX[font][C]; if (c.img.size != null && c.img.size == size && c.img.best != null && c.img.best == jsMath.Img.best) return; - var mustScale = (jsMath.Img.scale != 1 || jsMath.Img.forceScale); + var mustScale = (jsMath.Img.scale != 1); var id = jsMath.Img.best + size - 4; if (id < 0) {id = 0; mustScale = 1} else if (id >= jsMath.Img.fonts.length) {id = jsMath.Img.fonts.length-1; mustScale = 1} @@ -5445,22 +5505,11 @@ /* * Move hidden to the location of the math element to be * processed and reinitialize sizes for that location. - * For MSIE (which can't put a DIV inside a SPAN), - * default to the standard hidden DIV, but check to - * see if we need to scale images or not. */ ResetHidden: function (element) { - jsMath.Img.forceScale = 0; - try { - element.innerHTML = '<DIV CLASS="normal" STYLE="position:absolute; top:0; left: 0;"></DIV>'; - element.className=''; - jsMath.hidden = element.firstChild; - } catch (err) { - element.innerHTML = '<SPAN CLASS="normal">M</SPAN>'; - var w = element.offsetWidth; - jsMath.hidden = jsMath.hiddenDIV; - jsMath.Img.forceScale = (Math.abs(w-jsMath.BBoxFor("M").w) > w/10); - } + element.innerHTML = '<SPAN CLASS="normal" STYLE="position:absolute; top:0;left:0;"></SPAN>'; + element.className=''; + jsMath.hidden = element.firstChild; jsMath.ReInit(); }, @@ -5558,7 +5607,7 @@ GetMathElements: function (obj) { var element = []; if (!obj) {obj = document} - if (typeof(obj) == 'string') {obj = document.getElementById(ibj)} + if (typeof(obj) == 'string') {obj = document.getElementById(obj)} if (!obj.getElementsByTagName) return var math = obj.getElementsByTagName('DIV'); for (var k = 0; k < math.length; k++) { Index: jsMath-fallback-mac.js =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/htdocs/jsMath/jsMath-fallback-mac.js,v retrieving revision 1.4 retrieving revision 1.5 diff -Lhtdocs/jsMath/jsMath-fallback-mac.js -Lhtdocs/jsMath/jsMath-fallback-mac.js -u -r1.4 -r1.5 --- htdocs/jsMath/jsMath-fallback-mac.js +++ htdocs/jsMath/jsMath-fallback-mac.js @@ -874,15 +874,15 @@ /* * We need to replace the jsMath.Box.TeX function in order to use the - * different font metrics in thie tables above, and to handle the + * different font metrics in the tables above, and to handle the * scaling better. */ jsMath.Add(jsMath.Box,{ TeX: function (C,font,style,size) { - c = jsMath.TeX[font][C]; + c = jsMath.TeX[font][C]; if (!c.tclass) {c.tclass = font} if (c.h != null && c.a == null) {c.a = c.h-1.1*jsMath.TeX.x_height} - if (c.img != null) this.TeXIMG(font,C,jsMath.Typeset.StyleSize(style,size)); + if (c.img != null && c.c != '') this.TeXIMG(font,C,jsMath.Typeset.StyleSize(style,size)); var box = this.Text(c.c,c.tclass,style,size,c.a,c.d); var scale = jsMath.Typeset.TeX(style,size).scale; if (c.bh != null) { @@ -941,7 +941,7 @@ '.vertical': "font-family: Copperplate", '.vertical1': "font-family: Copperplate; font-size: 85%; margin: .15em;", '.vertical2': "font-family: Copperplate; font-size: 85%; margin: .17em;", - '.greek': "font-family: Symbol", + '.greek': "font-family: serif", '.bigop1': "font-family: 'Hiragino Mincho Pro'; font-size: 133%; position: relative; top: .85em; margin:-.05em", '.bigop1a': "font-family: Baskerville; font-size: 100%; position: relative; top: .775em;", '.bigop1b': "font-family: 'Hiragino Mincho Pro'; font-size: 160%; position: relative; top: .7em; margin:-.1em", Index: jsMath-fallback-pc.js =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/htdocs/jsMath/jsMath-fallback-pc.js,v retrieving revision 1.4 retrieving revision 1.5 diff -Lhtdocs/jsMath/jsMath-fallback-pc.js -Lhtdocs/jsMath/jsMath-fallback-pc.js -u -r1.4 -r1.5 --- htdocs/jsMath/jsMath-fallback-pc.js +++ htdocs/jsMath/jsMath-fallback-pc.js @@ -880,9 +880,9 @@ jsMath.Add(jsMath.Box,{ TeX: function (C,font,style,size) { - c = jsMath.TeX[font][C]; + c = jsMath.TeX[font][C]; if (!c.tclass) {c.tclass = font} if (c.h != null && c.a == null) {c.a = c.h-1.1*jsMath.TeX.x_height} - if (c.img != null) this.TeXIMG(font,C,jsMath.Typeset.StyleSize(style,size)); + if (c.img != null && c.c != '') this.TeXIMG(font,C,jsMath.Typeset.StyleSize(style,size)); var box = this.Text(c.c,c.tclass,style,size,c.a,c.d); var scale = jsMath.Typeset.TeX(style,size).scale; if (c.bh != null) { Index: tex2math.js =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/htdocs/jsMath/plugins/tex2math.js,v retrieving revision 1.1 retrieving revision 1.2 diff -Lhtdocs/jsMath/plugins/tex2math.js -Lhtdocs/jsMath/plugins/tex2math.js -u -r1.1 -r1.2 --- htdocs/jsMath/plugins/tex2math.js +++ htdocs/jsMath/plugins/tex2math.js @@ -28,12 +28,14 @@ jsMath.Add(jsMath,{ ConvertTeX: function (element) {jsMath.tex2math.ConvertMath("tex",element)}, + ConvertTeX2: function (element) {jsMath.tex2math.ConvertMath("tex2",element)}, ConvertLaTeX: function (element) {jsMath.tex2math.ConvertMath("latex",element)}, tex2math: { pattern: { - tex: /((^|[^\\])(\\[^\[\(])*)(\\\((([^\\]|\\[^\)])*)\\\)|\\\[(([^\\]|\\[^\]])*)\\\]|(\$\$?)(([^$\\]|\\.)*)\9)/, + tex: /((^|[^\\])(\\[^\[\(])*)(\\\((([^\\]|\\[^\)])*)\\\)|\\\[(([^\\]|\\[^\]])*)\\\]|\$\$((\\.|\$[^$\\]|[^$\\])*)\$\$|\$(([^$\\]|\\.)*)\$)/, + tex2: /((^|[^\\])(\\[^\[\(])*)(\\\((([^\\]|\\[^\)])*)\\\)|\\\[(([^\\]|\\[^\]])*)\\\]|\$\$((\\.|\$[^$\\]|[^$\\])*)\$\$)/, latex: /((^|[^\\])(\\[^\[\(])*)(\\\((([^\\]|\\[^\)])*)\\\)|\\\[(([^\\]|\\[^\]])*)\\\])/ }, @@ -42,14 +44,13 @@ if (recurse) return; element = document.body; } - if (typeof(element) == 'string') - {element = document.getElementById(element)} + if (typeof(element) == 'string') {element = document.getElementById(element)} var pattern = jsMath.tex2math.pattern[method]; while (element) { if (element.nodeName == '#text') { if (!element.parentNode.tagName || - !element.parentNode.tagName.match(/^(SCRIPT|STYLE|TEXTAREA)$/i)) { + !element.parentNode.tagName.match(/^(SCRIPT|NOSCRIPT|STYLE|TEXTAREA)$/i)) { element = jsMath.tex2math.TeX2math(pattern,element); } } else { @@ -67,9 +68,15 @@ if (element.nodeValue.search(/\\\$/) >= 0) {element.nodeValue = element.nodeValue.replace(/\\\$/g,'')} math.parentNode.removeChild(math); - if (text = (result[5] || result[7] || result[10])) { + if (text = (result[5] || result[7] || result[9] || result[11])) { tag = jsMath.tex2math.createMathTag(result[4].substr(0,2),text); - rest.parentNode.insertBefore(tag,rest); + if (rest.parentNode) { + rest.parentNode.insertBefore(tag,rest); + } else if (element.nextSibling) { + element.parentNode.insertBefore(tag,element.nextSibling); + } else { + element.parentNode.appendChild(tag); + } } element = rest; } |
From: Mike G. v. a. <we...@ma...> - 2005-10-20 19:41:40
|
Log Message: ----------- Fixed an error where files of type 'source_path_for_problem_file' were not redirected in the save_handler Modified Files: -------------- webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor: PGProblemEditor.pm Revision Data ------------- Index: PGProblemEditor.pm =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm,v retrieving revision 1.59 retrieving revision 1.60 diff -Llib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm -Llib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm -u -r1.59 -r1.60 --- lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm +++ lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm @@ -1185,6 +1185,22 @@ status_message => uri_escape($self->{status_message}) } ); + } elsif ($file_type eq 'source_path_for_problem_file') { # redirect to ProblemSets.pm + my $problemPage = $self->r->urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::PGProblemEditor", + courseID => $courseName, setID => $setName, problemID => $problemNumber + ); + my $viewURL = $self->systemLink($problemPage, + params=>{ + displayMode => $displayMode, + problemSeed => $problemSeed, + editMode => "savedFile", + edit_level => 0, + sourceFilePath => $outputFilePath, #The path relative to the templates directory is required. + file_type => 'source_path_for_problem_file', + status_message => uri_escape($self->{status_message}) + + } + ); } else { die "I don't know how to redirect this file type $file_type "; @@ -1314,7 +1330,9 @@ my $editFilePath = $self->{editFilePath}; # path to the permanent file to be edited return "" unless -e $editFilePath; return "" if -w $editFilePath; - return "" unless $self->{file_type} eq 'problem' or $self->{file_type} eq 'set_header' ; + return "" unless $self->{file_type} eq 'problem' + or $self->{file_type} eq 'set_header' ; + # or $self->{file_type} eq 'source_path_for_problem_file'; # need setID and problemID to make local copy return join ("", "Make local copy at: [TMPL]/".determineLocalFilePath($editFilePath), CGI::hidden(-name=>'action.make_local_copy.target_file', -value=>determineLocalFilePath($editFilePath) ), |
From: dpvc v. a. <we...@ma...> - 2005-10-20 00:58:13
|
Log Message: ----------- A new parser object that handles popup menus. Mainly this is so that you can use them with the MultiPart object, which requires all the objects to be Value objects. See the comments in the file for documentation and examples. Added Files: ----------- pg/macros: parserPopUp.pl Revision Data ------------- --- /dev/null +++ macros/parserPopUp.pl @@ -0,0 +1,70 @@ +loadMacros('Parser.pl','contextString.pl'); + +Context("Numeric"); + +sub _parserPopUp_init {}; # don't reload this file + +#################################################################### +# +# This file implements a pop-up menu object that is compatible +# with Value objects, and in particular, with the MultiPart object. +# +# To create a PopUp object, use +# +# $popup = PopUp([choices,...],correct); +# +# where "choices" are the strings for the items in the popup menu, +# and "correct" is the choice that is the correct answer for the +# popup. +# +# To insert the popup menu into the problem text, use +# +# BEGIN_TEXT +# \{$popup->menu\} +# END_TEXT +# +# and then +# +# ANS($popup->cmp); +# +# to get the answer checker for the popup. +# +# You can use the PopUp menu object in MultiPart objects. This is +# the reason for the pop-up menu's ans_rule method (since that is what +# MultiPart calls to get answer rules). +# + +sub PopUp {parserPopUp->new(@_)} + +# +# The package that implements pop-up menus +# +package parserPopUp; +our @ISA = qw(Value::String); + +sub new { + my $self = shift; my $class = ref($self) || $self; + my $choices = shift; my $value = shift; + Value::Error("A PopUp's first argument should be a list of menu items") + unless ref($choices) eq 'ARRAY'; + Value::Error("A PopUp's second argument should be the correct menu choice") + unless defined($value) && $value ne ""; + my $oldContext = main::Context(); + my $context = $main::context{String}->copy; + main::Context($context); + $context->strings->add(map {$_=>{}} @{$choices}); + my $self = bless Value::String->new($value), $class; + $self->{isValue} = 1; $self->{choices} = $choices; + $self->{context} = $context; + main::Context($oldContext); + return $self; +} + +sub menu { + my $self = shift; + main::pop_up_list($self->{choices}); +} + +sub ans_rule {shift->menu(@_)} + +1; |
From: Mike G. v. a. <we...@ma...> - 2005-10-19 01:50:12
|
Log Message: ----------- Corrected misspelling Modified Files: -------------- webwork-modperl/lib/WeBWorK/ContentGenerator: Hardcopy.pm Revision Data ------------- Index: Hardcopy.pm =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/ContentGenerator/Hardcopy.pm,v retrieving revision 1.68 retrieving revision 1.69 diff -Llib/WeBWorK/ContentGenerator/Hardcopy.pm -Llib/WeBWorK/ContentGenerator/Hardcopy.pm -u -r1.68 -r1.69 --- lib/WeBWorK/ContentGenerator/Hardcopy.pm +++ lib/WeBWorK/ContentGenerator/Hardcopy.pm @@ -853,7 +853,7 @@ $self->add_errors(CGI::a({href=>$edit_url}, "[edit]") ."Errors encountered while processing $problem_desc. " ."This $problem_name has been omitted from the hardcopy. " - ."Error text:".CGI::br().CGI::pre(CGI::escpaeHTML($pg->{errors})) + ."Error text:".CGI::br().CGI::pre(CGI::escapeHTML($pg->{errors})) ); return; } |
From: Sam H. v. a. <we...@ma...> - 2005-10-17 17:37:19
|
Log Message: ----------- rearrange addition, deletion, change-saving, and "mark correct" code. Order is now: - save changes to existing problems - mark specified problems correct - delete specified problems - add blank problem This ensures consistencey if multiple operations are performed at once, and also prevents the values set in the new problem from being overwritten with empty values when changes to existing problems are saved. 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.23 retrieving revision 1.24 diff -Llib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm -Llib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm -u -r1.23 -r1.24 --- lib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm +++ lib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm @@ -675,28 +675,7 @@ } $db->putGlobalSet($setRecord); } - ##################################################################### - # Add blank problem if needed - ##################################################################### - if (defined($r->param("add_blank_problem") ) and $r->param("add_blank_problem") == 1) { - my $targetProblemNumber = 1+ WeBWorK::Utils::max( $self->r->db->listGlobalProblems($setID)); - my $blank_file_path = $ce->{webworkFiles}->{screenSnippets}->{blankProblem}; - my $new_file_path = $ce->{courseDirs}->{templates}."/set$setID/blank.pg"; - ################################################# - # Update problem record - ################################################# - my $problemRecord = $self->addProblemToSet( - setName => $setID, - sourceFile => $blank_file_path, - problemID => $targetProblemNumber, #added to end of set - ); - $self->assignProblemToAllSetUsers($problemRecord); - $self->addgoodmessage("Added $blank_file_path to ". $setID. " as problem $targetProblemNumber") ; - #warn "A new blank problem has been added at number $targetProblemNumber with source $blank_file_path and record is $problemRecord" ; - #FIXME -- for reasons I don't understand the sourceFile reference is not accepted. - # furthermore, while the new problem appears in the listing for problem set details, it doesn't appear in the "hmwk set editor" (ProblemSetEditor.pm) - # this snippet was copied from PGProblemSetEditor.pm line 1038 where it appears to work. What's up?? - } + ##################################################################### # Save problem information ##################################################################### @@ -805,16 +784,6 @@ } } - # Delete all problems marked for deletion - foreach my $problemID ($r->param('deleteProblem')) { - $db->deleteGlobalProblem($setID, $problemID); - } - - # Sets the specified header to "" so that the default file will get used. - foreach my $header ($r->param('defaultHeader')) { - $setRecord->$header(""); - } - # Mark the specified problems as correct for all users foreach my $problemID ($r->param('markCorrect')) { my @userProblemIDs = map { [$_, $setID, $problemID] } ($forUsers ? @editForUser : $db->listProblemUsers($setID, $problemID)); @@ -827,6 +796,39 @@ } } } + + # Delete all problems marked for deletion + foreach my $problemID ($r->param('deleteProblem')) { + $db->deleteGlobalProblem($setID, $problemID); + } + + ##################################################################### + # Add blank problem if needed + ##################################################################### + if (defined($r->param("add_blank_problem") ) and $r->param("add_blank_problem") == 1) { + my $targetProblemNumber = 1+ WeBWorK::Utils::max( $self->r->db->listGlobalProblems($setID)); + my $blank_file_path = $ce->{webworkFiles}->{screenSnippets}->{blankProblem}; + my $new_file_path = $ce->{courseDirs}->{templates}."/set$setID/blank.pg"; + ################################################# + # Update problem record + ################################################# + my $problemRecord = $self->addProblemToSet( + setName => $setID, + sourceFile => $blank_file_path, + problemID => $targetProblemNumber, #added to end of set + ); + #$self->assignProblemToAllSetUsers($problemRecord); + $self->addgoodmessage("Added $blank_file_path to ". $setID. " as problem $targetProblemNumber") ; + #warn "A new blank problem has been added at number $targetProblemNumber with source $blank_file_path and record is $problemRecord" ; + #FIXME -- for reasons I don't understand the sourceFile reference is not accepted. + # furthermore, while the new problem appears in the listing for problem set details, it doesn't appear in the "hmwk set editor" (ProblemSetEditor.pm) + # this snippet was copied from PGProblemSetEditor.pm line 1038 where it appears to work. What's up?? + } + + # Sets the specified header to "" so that the default file will get used. + foreach my $header ($r->param('defaultHeader')) { + $setRecord->$header(""); + } } # Leftover code from when there were up/down buttons |
From: Mike G. v. a. <we...@ma...> - 2005-10-17 14:01:48
|
Log Message: ----------- Changed wording on link. Modified Files: -------------- webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor: ProblemSetDetail.pm Revision Data ------------- Index: ProblemSetDetail.pm =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm,v retrieving revision 1.22 retrieving revision 1.23 diff -Llib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm -Llib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm -u -r1.22 -r1.23 --- lib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm +++ lib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm @@ -1338,7 +1338,7 @@ my $editNewProblemPage = $urlpath->new(type => 'instructor_problem_editor_withset_withproblem', args => { courseID => $courseID, setID => $setID, problemID =>'new_problem' }); my $editNewProblemLink = $self->systemLink($editNewProblemPage, params => { make_local_copy => 1, file_type => 'blank_problem' }); - print CGI::p( CGI::a({href=>$editNewProblemLink},'Create'). 'a new blank problem'); + print CGI::p( CGI::a({href=>$editNewProblemLink},'Edit'). 'a new blank problem'); print CGI::end_form(); |
From: Mike G. v. a. <we...@ma...> - 2005-10-17 13:58:57
|
Log Message: ----------- Improved handling of case where a blank_problem is asked for in PGProblemEditor.pm Also improved the random names for new local problems Added ability to create a new blank problem in ProblemSetDetail.pm (without going to the PGProblemEditor.pm) however something is not yet working when this problem record is saved. Sam -- could you look at lines near 679 to see what the problem is? There is a FIXME note there. I suspect that the addProblemToSet routine is failing somehow, but there is no error message. Many of the record fields are not filled out -- including the sourceFile path and the number of attempts. The same snippet of code works fine in PGProblemEditor.pm --Mike Modified Files: -------------- webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor: PGProblemEditor.pm ProblemSetDetail.pm Revision Data ------------- Index: PGProblemEditor.pm =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm,v retrieving revision 1.58 retrieving revision 1.59 diff -Llib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm -Llib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm -u -r1.58 -r1.59 --- lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm +++ lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm @@ -547,7 +547,7 @@ if ($path =~ /Library/) { $path =~ s|^.*?Library/||; # truncate the url up to a segment such as ...rochesterLibrary/....... } else { # if its not in a library we'll just save it locally - $path = "new_problem_".rand(40).".pg"; #l hope there aren't any collisions. + $path = "new_problem_".int(rand(1000)).".pg"; #l hope there aren't any collisions. } $path; @@ -557,7 +557,8 @@ my $self = shift; die "determineTempFilePath is a method" unless ref($self); my $path =shift; my $user = $self->r->param("user"); - $user = rand(1000) unless defined $user; + $user = int(rand(1000)) unless defined $user; + my $setID = $self->{setID} || int(rand(1000)); my $courseDirectory = $self->r->ce->{courseDirs}; #FIXME -- we can put all of the temp files in a special directory # so that even library files can viewed. @@ -565,16 +566,17 @@ # Calculate the location of the temporary file ############### my $templatesDirectory = $courseDirectory->{templates}; + my $blank_file_path = $self->r->ce->{webworkFiles}->{screenSnippets}->{blankProblem}; + my $tmpEditFileDirectory = (defined ($courseDirectory->{tmpEditFileDir}) ) ? $courseDirectory->{tmpEditFileDir} : "$templatesDirectory/tmpEdit"; if ($path =~ /^$templatesDirectory/ ) { $path =~ s|^$templatesDirectory||; $path =~ s|^/||; # remove the initial slash if any + $path = "$tmpEditFileDirectory/$path.$user.tmp"; + } elsif ($path eq $blank_file_path) { + $path = "$tmpEditFileDirectory/blank.$setID.$user.tmp"; # handle the case of the blank problem } else { die "determineTempFilePath should only be used on paths within the templates directory, not on $path"; } - my $tmpEditFileDirectory = (defined ($courseDirectory->{tmpEditFileDir}) ) ? $courseDirectory->{tmpEditFileDir} : "$templatesDirectory/tmpEdit"; - $path = "$tmpEditFileDirectory/$path.$user.tmp"; - #$path .= ".$user.tmp"; - #WeBWorK::Utils::surePathToFile($templatesDirectory, $path); $path; } sub isTempFilePath { @@ -1038,7 +1040,7 @@ my $problemRecord = $self->addProblemToSet( setName => $targetSetName, sourceFile => $sourceFilePath, - problemID => $targetProblemNumber, + problemID => $targetProblemNumber, #added to end of set ); $self->assignProblemToAllSetUsers($problemRecord); $self->addgoodmessage("Added $sourceFilePath to ". $targetSetName. " as problem $targetProblemNumber") ; Index: ProblemSetDetail.pm =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm,v retrieving revision 1.21 retrieving revision 1.22 diff -Llib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm -Llib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm -u -r1.21 -r1.22 --- lib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm +++ lib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm @@ -675,8 +675,28 @@ } $db->putGlobalSet($setRecord); } - - + ##################################################################### + # Add blank problem if needed + ##################################################################### + if (defined($r->param("add_blank_problem") ) and $r->param("add_blank_problem") == 1) { + my $targetProblemNumber = 1+ WeBWorK::Utils::max( $self->r->db->listGlobalProblems($setID)); + my $blank_file_path = $ce->{webworkFiles}->{screenSnippets}->{blankProblem}; + my $new_file_path = $ce->{courseDirs}->{templates}."/set$setID/blank.pg"; + ################################################# + # Update problem record + ################################################# + my $problemRecord = $self->addProblemToSet( + setName => $setID, + sourceFile => $blank_file_path, + problemID => $targetProblemNumber, #added to end of set + ); + $self->assignProblemToAllSetUsers($problemRecord); + $self->addgoodmessage("Added $blank_file_path to ". $setID. " as problem $targetProblemNumber") ; + #warn "A new blank problem has been added at number $targetProblemNumber with source $blank_file_path and record is $problemRecord" ; + #FIXME -- for reasons I don't understand the sourceFile reference is not accepted. + # furthermore, while the new problem appears in the listing for problem set details, it doesn't appear in the "hmwk set editor" (ProblemSetEditor.pm) + # this snippet was copied from PGProblemSetEditor.pm line 1038 where it appears to work. What's up?? + } ##################################################################### # Save problem information ##################################################################### @@ -1293,8 +1313,12 @@ print CGI::checkbox({ label=> "Force problems to be numbered consecutively from one", name=>"force_renumber", value=>"1"}), + CGI::br(), + CGI::checkbox({ + label=> "Add blank problem to set", + name=>"add_blank_problem", value=>"1"}), - CGI::br(); + CGI::br(); print CGI::input({type=>"submit", name=>"submit_changes", value=>"Save Changes"}); print CGI::input({type=>"submit", name=>"handle_numbers", value=>"Reorder problems only"}) . "(Any unsaved changes will be lost.)"; print CGI::p(<<HERE); |
From: Mike G. v. a. <we...@ma...> - 2005-10-17 03:43:28
|
Log Message: ----------- Refactored much of the action in PGProblemEditor.pm This allows a user inteface which mimics the interface for the Hmwk sets editor and for the Classlist editor. It also allows all of the temporary edit files to be placed in one location. This allows one to "temporarily" edit a library file to try to see what is wrong with it. It is also now much easier to make a local copy of a library file for your current problem set so that you can tweak it. It has been fairly well tested by one person (me) but there may still be bugs. -- Mike Modified Files: -------------- webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor: PGProblemEditor.pm Revision Data ------------- Index: PGProblemEditor.pm =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm,v retrieving revision 1.57 retrieving revision 1.58 diff -Llib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm -Llib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm -u -r1.57 -r1.58 --- lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm +++ lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm @@ -18,6 +18,7 @@ use base qw(WeBWorK::ContentGenerator::Instructor); + =head1 NAME WeBWorK::ContentGenerator::Instructor::PGProblemEditor - Edit a pg file @@ -62,9 +63,9 @@ # the information for retrieving the source file is found using the problemID in order to look # look up the source file path. # -# file_type eq 'problem_with_source' +# file_type eq 'source_path_for_problem_file' # This is the same as the 'problem' file type except that the source for the problem is found in -# the parameter $r->param('sourceFilePath'). +# the parameter $r->param('sourceFilePath'). This path is relative to the templates directory # # file_type eq 'set_header' # This is a special case of editing the problem. The set header is often listed as problem 0 in the set's list of problems. @@ -82,12 +83,12 @@ # This is a special call which allows one to create and edit a new PG problem. The "stationery" source for this problem is # stored in the conf/snippets directory and defined in global.conf by $webworkFiles{screenSnippets}{blankProblem} ############################################################# -# submit button actions -- these and the file_type determine the state of the module +# Requested actions -- these and the file_type determine the state of the module # Save ---- action = save # Save as ---- action = save_as -# View Problem ---- action = refresh -# Add this problem to: ---- action = add_problem_to_set -# Make this set header for: ---- action = add_set_header_to_set +# View Problem ---- action = view +# Add this problem to: ---- action = add_problem +# Make this set header for: ---- action = add_problem # Revert ---- action = revert # no submit button defined ---- action = fresh_edit ################################################### @@ -95,9 +96,8 @@ # Determining which is the correct path to the file is a mess!!! FIXME # The path to the file to be edited is eventually put in tempFilePath # -# $problemPath is also used as is editFilePath. let's try to regularize these. -#(sourceFile) (problemPath)(tempFilePath)(editFilePath)(forcedSourceFile)(problemPath) -#input parameter can be: sourceFilePath +# (tempFilePath)(editFilePath)(forcedSourceFile) +#input parameter is: sourceFilePath ################################################################# # params read # user @@ -113,9 +113,18 @@ # save_to_new_file # -#our $libraryName; -#our $rowheight; -our $TEMPFILESUFFIX; +use constant ACTION_FORMS => [qw(view add_problem make_local_copy save save_as revert)]; #[qw(view save save_as revert add_problem add_header make_local_copy)]; + +# permissions needed to perform a given action +use constant FORM_PERMS => { + view => "modify_student_data", + add_problem => "modify_student_data", + make_local_copy => "modify_student_data", + save => "modify_student_data", + save_as => "modify_student_data", + revert => "modify_student_data", +}; + sub pre_header_initialize { my ($self) = @_; @@ -124,17 +133,45 @@ my $urlpath = $r->urlpath; my $authz = $r->authz; my $user = $r->param('user'); - $TEMPFILESUFFIX = $user.'.tmp'; + $self->{courseID} = $urlpath->arg("courseID"); + $self->{setID} = $r->urlpath->arg("setID") ; # using $r->urlpath->arg("setID") ||'' causes trouble with set 0!!! + $self->{problemID} = $r->urlpath->arg("problemID"); my $submit_button = $r->param('submit'); # obtain submit command from form + my $actionID = $r->param('action'); my $file_type = $r->param("file_type") || ''; - my $setName = $r->urlpath->arg("setID") ; # using $r->urlpath->arg("setID") ||'' causes trouble with set 0!!! - my $problemNumber = $r->urlpath->arg("problemID"); + my $setName = $self->{setID}; + my $problemNumber = $self->{problemID}; # Check permissions return unless ($authz->hasPermissions($user, "access_instructor_tools")); return unless ($authz->hasPermissions($user, "modify_problem_sets")); + ############################################################################## + # displayMode and problemSeed + # + # Determine the display mode + # If $self->{problemSeed} was obtained within saveFileChanges from the problem_record + # then it can be overridden by the value obtained from the form. + # Insure that $self->{problemSeed} has some non-empty value + # displayMode and problemSeed + # will be needed for viewing the problem via redirect. + # They are also two of the parameters which can be set by the editor + ############################################################################## + + if (defined $r->param('displayMode')) { + $self->{displayMode} = $r->param('displayMode'); + } else { + $self->{displayMode} = $ce->{pg}->{options}->{displayMode}; + } + + # form version of problemSeed overrides version obtained from the the problem_record + # inside saveFileChanges + $self->{problemSeed} = $r->param('problemSeed') if (defined $r->param('problemSeed')); + # Make sure that the problem seed has some value + $self->{problemSeed} = '123456' unless defined $self->{problemSeed} and $self->{problemSeed} =~/\S/; + + ############################################################################## ############################################################################# # Save file to permanent or temporary file, then redirect for viewing ############################################################################# @@ -159,7 +196,7 @@ if (defined($file_type) and ($file_type =~/\S/)) { #file_type is defined and is not blank # file type is already defined -- do nothing } else { - # if "sourcFilePath" is defined in the form, then we are getting the path directly. + # if "sourceFilePath" is defined in the form, then we are getting the path directly. # if the problem number is defined and is 0 # then we are dealing with some kind of # header file. The default is 'set_header' which prints properly @@ -179,85 +216,71 @@ } } die "The file_type variable has not been defined or is blank." unless defined($file_type) and $file_type =~/\S/; + # clean up sourceFilePath, just in case + # double check that sourceFilePath is relative to the templates file + if ($file_type eq 'source_path_for_problem_file' ) { + my $templatesDirectory = $ce->{courseDirs}->{templates}; + my $sourceFilePath = $r->param('sourceFilePath'); + $sourceFilePath =~ s/$templatesDirectory//; + $sourceFilePath =~ s|^/||; # remove intial / + $self->{sourceFilePath} = $sourceFilePath; + } $self->{file_type} = $file_type; ########################################## - # File type is one of: blank_problem course_info problem set_header hardcopy_header problem_with_source + # File type is one of: blank_problem course_info problem set_header hardcopy_header source_path_for_problem_file ########################################## # # Determine the path to the file # ########################################### - $self->getFilePaths($setName, $problemNumber, $file_type,$TEMPFILESUFFIX); - # result stored in $self->{editFilePath}, and $self->{tempFilePath} - ########################################## - # - # Determine action - # - ########################################### - # Submit button is one of: "add this problem to" , "add this set header to ", "Refresh" "Revert" "Save" "Save As" - $submit_button = $r->param('submit'); - SUBMIT_CASE: { - (! defined($submit_button) ) and do { # fresh problem to edit - $self->{action} = 'fresh_edit'; - last SUBMIT_CASE; - }; + $self->getFilePaths($setName, $problemNumber, $file_type); + #defines $self->{editFilePath} # path to the permanent file to be edited + # $self->{tempFilePath} # path to the permanent file to be edited has .tmp suffix + # $self->{inputFilePath} # path to the file for input, (might be a .tmp file) - ($submit_button eq 'Add this problem to: ') and do { - $self->{action} = 'add_problem_to_set'; - last SUBMIT_CASE; - }; - ($submit_button eq 'Make this the set header for: ') and do { - $self->{action} = 'add_set_header_to_set'; - last SUBMIT_CASE; - }; - ($submit_button eq 'View problem') and do { - $self->{action} ='refresh'; - last SUBMIT_CASE; - }; - - ($submit_button eq 'Revert') and do { - $self->{action} = 'revert'; - last SUBMIT_CASE; - }; - - ($submit_button eq 'Save') and do { - $self->{action} = 'save'; - last SUBMIT_CASE; - }; - - ($submit_button eq 'Save as') and do { - $self->{action} = 'save_as'; - last SUBMIT_CASE; - }; - - ($submit_button eq 'Add problem to: ') and do { - $self->{action} = 'add_problem_to_set'; - last SUBMIT_CASE; - }; - ($submit_button eq 'Make local copy at: ') and do { - $self->{action} = 'make_local_copy'; - last SUBMIT_CASE; - }; - # else - die "Unrecognized submit command: |$submit_button|"; - - } # END SUBMIT_CASE + ########################################## + # Default problem contents + ########################################## + $self->{r_problemContents}= undef; - + ########################################## + # + # Determine action + # ########################################### - # Save file - ###################################### - - # The subroutine below writes the necessary files and obtains the appropriate seed. - # and returns - # $self->{problemPath} --- file path for viewing problem in $self->{problemPath} - # $self->{failure} - - $self->saveFileChanges($setName, $problemNumber, $file_type,$TEMPFILESUFFIX); + if ($actionID) { + unless (grep { $_ eq $actionID } @{ ACTION_FORMS() } ) { + die "Action $actionID not found"; + } + # Check permissions + if (not FORM_PERMS()->{$actionID} or $authz->hasPermissions($user, FORM_PERMS()->{$actionID})) { + my $actionHandler = "${actionID}_handler"; + my %genericParams =(); +# foreach my $param (qw(selected_users)) { +# $genericParams{$param} = [ $r->param($param) ]; +# } + my %actionParams = $self->getActionParams($actionID); + my %tableParams = (); # $self->getTableParams(); + $self->{action}= $actionID; + $self->$actionHandler(\%genericParams, \%actionParams, \%tableParams); + } else { + $self->addbadmessage( "You are not authorized to perform this action."); + } + } else { + $self->{action}='fresh_edit'; + my $actionHandler = "fresh_edit_handler"; + my %genericParams; + my %actionParams = (); #$self->getActionParams($actionID); + my %tableParams = (); # $self->getTableParams(); + my $problemContents = ''; + $self->{r_problemContents}=\$problemContents; + $self->$actionHandler(\%genericParams, \%actionParams, \%tableParams); + } + ############################################################################## # displayMode and problemSeed @@ -302,200 +325,8 @@ # Some cases do not need a redirect: save, refresh, save_as, add_problem_to_set, add_header_to_set,make_local_copy my $action = $self->{action}; + return ; - return unless $action eq 'save' - or $action eq 'refresh' - or $action eq 'save_as' - or $action eq 'add_problem_to_set' - or $action eq 'make_local_copy' - or $action eq 'add_set_header_to_set'; - - - ###################################### - # calculate redirect URL based on file type - ###################################### - my $courseName = $urlpath->arg("courseID"); - my $problemSeed = ($r->param('problemSeed')) ? $r->param('problemSeed') : ''; - my $displayMode = ($r->param('displayMode')) ? $r->param('displayMode') : ''; - - my $viewURL = ''; - - ###################################### - # problem file_type - # redirect to Problem.pm with setID = "Undefined_Set if "Save As" option is chosen - # redirect to Problem.pm with setID = current $setID if "Save" or "Revert" or "Refresh is chosen" - ###################################### - REDIRECT_CASES: { - ($file_type eq 'problem' or $file_type eq 'source_path_for_problem_file' or $file_type eq 'blank_problem') and do { - my $sourceFilePath = $self->{problemPath}; - # strip off template directory prefix - $sourceFilePath =~ s|^$ce->{courseDirs}->{templates}/||; - if ($action eq 'save_as') { # redirect to myself - my $edit_level = $r->param("edit_level") || 0; - $edit_level++; - - my $problemPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::PGProblemEditor", - courseID => $courseName, setID => 'Undefined_Set', problemID => 'Undefined_Problem' - ); - $viewURL = $self->systemLink($problemPage, - params=>{ - sourceFilePath => $sourceFilePath, - edit_level => $edit_level, - file_type => 'source_path_for_problem_file', - status_message => uri_escape($self->{status_message}) - - } - ); - - - } elsif ( $action eq 'add_problem_to_set') { - - my $targetSetName = $r->param('target_set'); - my $problemPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Problem", - courseID => $courseName, setID => $targetSetName, - problemID => WeBWorK::Utils::max( $r->db->listGlobalProblems($targetSetName)) - ); - $viewURL = $self->systemLink($problemPage, - params => { - displayMode => $displayMode, - problemSeed => $problemSeed, - editMode => "savedFile", - sourceFilePath => $sourceFilePath, - status_message => uri_escape($self->{status_message}) - - } - ); - } elsif ($action eq 'make_local_copy') { # redirect to myself - my $edit_level = $r->param("edit_level") || 0; - $edit_level++; - - my $problemPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::PGProblemEditor", - courseID => $courseName, setID => $setName, problemID => $problemNumber - ); - $viewURL = $self->systemLink($problemPage, - params=>{ - sourceFilePath => $sourceFilePath, - edit_level => $edit_level, - file_type => 'source_path_for_problem_file', - status_message => uri_escape($self->{status_message}) - - } - ); - - - } elsif ( $action eq 'add_set_header_to_set') { - my $targetSetName = $r->param('target_set'); - my $problemPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::ProblemSet", - courseID => $courseName, setID => $targetSetName - ); - $viewURL = $self->systemLink($problemPage, - params => { - displayMode => $displayMode, - editMode => "savedFile", - status_message => uri_escape($self->{status_message}) - } - ); - } else { # saved problems and refreshed problems redirect to Problem.pm - my $problemPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Problem", - courseID => $courseName, setID => $setName, problemID => $problemNumber - ); - $viewURL = $self->systemLink($problemPage, - params => { - displayMode => $displayMode, - problemSeed => $problemSeed, - editMode => ($action eq "save" ? "savedFile" : "temporaryFile"), - sourceFilePath => $sourceFilePath, - status_message => uri_escape($self->{status_message}) - - } - ); - } - last REDIRECT_CASES; - }; - ###################################### - # blank_problem file_type - # redirect to Problem.pm - ###################################### - - $file_type eq 'blank_problem' and do { - return; # no redirect is needed - }; - - ###################################### - # set headers file_type - # redirect to ProblemSet.pm - ###################################### - - ($file_type eq 'set_header' or $file_type eq 'hardcopy_header' ) and do { - if ($action eq 'save_as') { # redirect to myself - my $sourceFilePath = $self->{problemPath}; - # strip off template directory prefix - $sourceFilePath =~ s|^$ce->{courseDirs}->{templates}/||; - - my $edit_level = $r->param("edit_level") || 0; - $edit_level++; - - my $problemPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::PGProblemEditor", - courseID => $courseName, setID => 'Undefined_Set', problemID => 'Undefined_Problem' - ); - $viewURL = $self->systemLink($problemPage, - params=>{ - sourceFilePath => $sourceFilePath, - edit_level => $edit_level, - file_type => 'source_path_for_problem_file', - status_message => uri_escape($self->{status_message}) - } - ); - } elsif ( $action eq 'add_set_header_to_set') { - my $targetSetName = $r->param('target_set'); - my $problemPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::ProblemSet", - courseID => $courseName, setID => $targetSetName - ); - $viewURL = $self->systemLink($problemPage, - params => { - displayMode => $displayMode, - editMode => "savedFile", - status_message => uri_escape($self->{status_message}) - } - ); - } else { - my $problemSetPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::ProblemSet", - courseID => $courseName, setID => $setName); - $viewURL = $self->systemLink($problemSetPage, - params => { - displayMode => $displayMode, - problemSeed => $problemSeed, - editMode => ($action eq "save" ? "savedFile" : "temporaryFile"), - status_message => uri_escape($self->{status_message}) - } - ); - } - last REDIRECT_CASES; - }; - ###################################### - # course_info file type - # redirect to ProblemSets.pm - ###################################### - $file_type eq 'course_info' and do { - my $problemSetsPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::ProblemSets", - courseID => $courseName); - $viewURL = $self->systemLink($problemSetsPage, - params => { - editMode => ($action eq "save" ? "savedFile" : "temporaryFile"), - status_message => uri_escape($self->{status_message}) - } - ); - last REDIRECT_CASES; - }; - # else if no redirect needed -- there must be an error. - die "The file_type $file_type does not have a defined redirect procedure."; - } # End REDIRECT_CASES - - if ($viewURL) { - $self->reply_with_redirect($viewURL); - } else { - die "Invalid file_type $file_type specified by saveFileChanges"; - } } @@ -511,13 +342,16 @@ my $tempFilePath = $self->{tempFilePath}; # path to the file currently being worked with (might be a .tmp file) my $inputFilePath = $self->{inputFilePath}; # path to the file for input, (might be a .tmp file) - my $protected_file = (not -w $inputFilePath ) and -e $inputFilePath; #FIXME -- let's try to insure that the input file always exists, even for revert. $self->addmessage($r->param('status_message') ||''); # record status messages carried over if this is a redirect $self->addbadmessage("Changes in this file have not yet been permanently saved.") if -r $tempFilePath; - - $self->addbadmessage("This file '$inputFilePath' is protected! ".CGI::br()."To edit this text you must either 'Make a local copy' of this problem, or - use 'Save As' to save it to another file.") if $protected_file; + if ( not( -e $inputFilePath) ) { + $self->addbadmessage("This file: $inputFilePath, cannot be found."); + } elsif (not -w $inputFilePath ) { + $self->addbadmessage("This file '$inputFilePath' is protected! ".CGI::br()."To edit this text you must either 'Make a local copy' of this problem, or + use 'Save As' to save it to another file."); + } + } @@ -576,14 +410,13 @@ my $editFilePath = $self->{editFilePath}; # path to the permanent file to be edited my $tempFilePath = $self->{tempFilePath}; # path to the file currently being worked with (might be a .tmp file) my $inputFilePath = $self->{inputFilePath}; # path to the file for input, (might be a .tmp file) - my $setName = $r->urlpath->arg("setID") ; - my $problemNumber = $r->urlpath->arg("problemID") ; + my $setName = $self->{setID} ; + my $problemNumber = $self->{problemID} ; $setName = defined($setName) ? $setName : ''; # we need this instead of using the || construction # to keep set 0 from being set to the # empty string. $problemNumber = defined($problemNumber) ? $problemNumber : ''; - ######################################################################### # Find the text for the problem, either in the tmp file, if it exists # or in the original file in the template directory @@ -591,7 +424,7 @@ ######################################################################### my $problemContents = ${$self->{r_problemContents}}; - + unless ( $problemContents =~/\S/) { # non-empty contents if (-r $tempFilePath and not -d $tempFilePath) { eval { $problemContents = WeBWorK::Utils::readFile($tempFilePath) }; @@ -611,7 +444,7 @@ my $protected_file = not -w $inputFilePath; my $header = CGI::i("Editing problem".CGI::b("set $setName/ problem $problemNumber</emphasis>").CGI::br()." in file $inputFilePath"); - $header = ($inputFilePath =~ /$TEMPFILESUFFIX/) ? CGI::div({class=>'temporaryFile'},$header) : $header; # use colors if temporary file + $header = ($self->isTempFilePath($inputFilePath) ) ? CGI::div({class=>'temporaryFile'},$header) : $header; # use colors if temporary file ######################################################################### # Format the page @@ -629,49 +462,24 @@ my $edit_level = $r->param('edit_level') || 0; my $file_type = $self->{file_type}; - my $force_field = defined($r->param('sourceFilePath')) ? + my $force_field = (defined($self->{sourceFilePath}) and $self->{sourceFilePath} ne "") ? CGI::hidden(-name=>'sourceFilePath', - -default=>$r->param('sourceFilePath')) : ''; + -default=>$self->{sourceFilePath}) : ''; my @allSetNames = sort $db->listGlobalSets; for (my $j=0; $j<scalar(@allSetNames); $j++) { $allSetNames[$j] =~ s|^set||; $allSetNames[$j] =~ s|\.def||; } - my $target = "problem$edit_level"; - # Prepare Preview button - my $view_problem_form = CGI::start_form({method=>"POST", name=>"editor", action=>"$uri", target=>$target, enctype=>"application/x-www-form-urlencoded"}). - $self->hidden_authen_fields. - $force_field. - CGI::hidden(-name=>'file_type',-default=>$self->{file_type}). - CGI::hidden(-name=>'problemSeed',-default=>$problemSeed). - CGI::hidden(-name=>'displayMode',-default=>$displayMode). - CGI::hidden(-name=>'problemContents',-default=>$problemContents). - CGI::submit(-value=>'View problem',-name=>'submit'). - CGI::end_form(); - # Prepare add to set buttons - my $add_files_to_set_buttons = ''; - if ($file_type eq 'problem' or $file_type eq 'source_path_for_problem_file' or $file_type eq 'blank_problem' ) { - $add_files_to_set_buttons .= CGI::submit(-value=>'Add problem to: ',-name=>'submit' ) ; - } - if ($file_type eq 'set_header' # set header or the problem number is not a regular positive number - or ( $file_type =~ /problem/ and ($problemNumber =~ /\D|^0$|^$/ )) ){ - $add_files_to_set_buttons .=CGI::submit(-value=>'Make this the set header for: ',-name=>'submit' ); - } - # Add pop-up menu for the target set if either of these buttons has been revealed. - $add_files_to_set_buttons .= CGI::popup_menu(-name=>'target_set',-values=>\@allSetNames, -default=>$setName) if $add_files_to_set_buttons; - + my $target = "problem$edit_level"; # increasing edit_level gives you a new window with each edit. + - return CGI::p($header), + print CGI::p($header), CGI::start_form({method=>"POST", name=>"editor", action=>"$uri", target=>$target, enctype=>"application/x-www-form-urlencoded"}), $self->hidden_authen_fields, $force_field, CGI::hidden(-name=>'file_type',-default=>$self->{file_type}), CGI::div( - 'Seed: ', - CGI::textfield(-name=>'problemSeed',-value=>$problemSeed), - 'Mode: ', - CGI::popup_menu(-name=>'displayMode', -values=>$mode_list, -default=>$displayMode), CGI::a({-href=>'http://webwork.math.rochester.edu/docs/docs/pglanguage/manpages/',-target=>"manpage_window"}, ' Manpages ', ), @@ -687,24 +495,43 @@ -name => 'problemContents', -default => $problemContents, -rows => $rows, -columns => $columns, -override => 1, ), - ), - CGI::p( - $add_files_to_set_buttons, - CGI::br(), - CGI::submit(-value=>'View problem',-name=>'submit'), - $protected_file ? CGI::submit(-value=>'Save',-name=>'submit', -disabled=>1) : CGI::submit(-value=>'Save',-name=>'submit'), - CGI::submit(-value=>'Revert', -name=>'submit'), - CGI::submit(-value=>'Save as',-name=>'submit'), - CGI::textfield(-name=>'save_to_new_file', -size=>40, -value=>""), - - ), - # allow one to make a local copy if the viewed file can't be edited. #FIXME the method for determining the localfilePath needs work - (-w $editFilePath) ? "" : CGI::p(CGI::hr(), - CGI::submit(-value=>'Make local copy at: ',-name=>'submit'), "[TMPL]/".determineLocalFilePath($editFilePath), - CGI::hidden(-name=>'local_copy_file_path', -value=>determineLocalFilePath($editFilePath) ) - ), - CGI::end_form(); + ); + + + +######### print action forms + print CGI::start_table({}); + #print CGI::Tr({}, CGI::td({-colspan=>2}, "Select an action to perform:")); + + my @formsToShow = @{ ACTION_FORMS() }; + my $default_choice = $formsToShow[0]; + my $i = 0; + foreach my $actionID (@formsToShow) { + # Check permissions + #next if FORM_PERMS()->{$actionID} and not $authz->hasPermissions($user, FORM_PERMS()->{$actionID}); + my $actionForm = "${actionID}_form"; + my $onChange = "document.userlist.action[$i].checked=true"; + my %actionParams = $self->getActionParams($actionID); + my $line_contents = $self->$actionForm($onChange, %actionParams); + my $radio_params = {-type=>"radio", -name=>"action", -value=>$actionID}; + $radio_params->{checked}=1 if ($actionID eq $default_choice) ; + print CGI::Tr({-valign=>"top"}, + CGI::td({}, CGI::input($radio_params)), + CGI::td({}, $line_contents) + ) if $line_contents; + + $i++; + } + print CGI::Tr({}, CGI::td({-align=>"right"}, "Select above then:"), + CGI::td({-align=>"left"}, CGI::submit(-name=>'submit', -value=>"Take Action!")), + ); + print CGI::end_table(); + + + print CGI::end_form(); + return ""; + } @@ -720,13 +547,46 @@ if ($path =~ /Library/) { $path =~ s|^.*?Library/||; # truncate the url up to a segment such as ...rochesterLibrary/....... } else { # if its not in a library we'll just save it locally - $path = "new_problem_".rand(40); #l hope there aren't any collisions. + $path = "new_problem_".rand(40).".pg"; #l hope there aren't any collisions. } $path; } + +sub determineTempFilePath { # this does not create the path to the file + my $self = shift; die "determineTempFilePath is a method" unless ref($self); + my $path =shift; + my $user = $self->r->param("user"); + $user = rand(1000) unless defined $user; + my $courseDirectory = $self->r->ce->{courseDirs}; + #FIXME -- we can put all of the temp files in a special directory + # so that even library files can viewed. + ############### + # Calculate the location of the temporary file + ############### + my $templatesDirectory = $courseDirectory->{templates}; + if ($path =~ /^$templatesDirectory/ ) { + $path =~ s|^$templatesDirectory||; + $path =~ s|^/||; # remove the initial slash if any + } else { + die "determineTempFilePath should only be used on paths within the templates directory, not on $path"; + } + my $tmpEditFileDirectory = (defined ($courseDirectory->{tmpEditFileDir}) ) ? $courseDirectory->{tmpEditFileDir} : "$templatesDirectory/tmpEdit"; + $path = "$tmpEditFileDirectory/$path.$user.tmp"; + #$path .= ".$user.tmp"; + #WeBWorK::Utils::surePathToFile($templatesDirectory, $path); + $path; +} +sub isTempFilePath { + my $self = shift; + my $path = shift; + my $courseDirectory = $self->r->ce->{courseDirs}; + my $templatesDirectory = $courseDirectory->{templates}; + my $tmpEditFileDirectory = (defined ($courseDirectory->{tmpEditFileDir}) ) ? $courseDirectory->{tmpEditFileDir} : "$templatesDirectory/tmpEdit"; + ($path =~/^$tmpEditFileDirectory/) ? 1: 0; +} sub getFilePaths { - my ($self, $setName, $problemNumber, $file_type, $TEMPFILESUFFIX) = @_; + my ($self, $setName, $problemNumber, $file_type) = @_; my $r = $self->r; my $ce = $r->ce; my $db = $r->db; @@ -741,11 +601,8 @@ ########################################################## # Determine path to the input file to be edited. - # set EditFilePath to this value - # - # There are potentially four files in play - # The permanent path of the input file == $editFilePath == $self->{problemPath} - # A temporary path to the input file == $tempFilePath== "$editFilePath.$TEMPFILESUFFIX"== $self->{problemPath} + # The permanent path of the input file == $editFilePath + # A temporary path to the input file == $tempFilePath ########################################################## # Relevant parameters # $r->param("displayMode") @@ -757,12 +614,10 @@ # $r->param('save_to_new_file') ########################################################################## # Define the following variables - # path to regular file -- $self->{problemPath} = $editFilePath; - # path to file being read (temporary or permanent) - # --- $self->{problemPath} = $problemPath; + # path to regular file -- $editFilePath; + # path to file being read (temporary or permanent) # contents of the file being read --- $problemContents - # $self->{r_problemContents} = \$problemContents; - # $self->{TEMPFILESUFFIX} = $TEMPFILESUFFIX; + # $self->{r_problemContents} = \$problemContents; ########################################################################### my $editFilePath = $ce->{courseDirs}->{templates}; @@ -782,7 +637,7 @@ ($file_type eq 'blank_problem') and do { $editFilePath = $ce->{webworkFiles}->{screenSnippets}->{blankProblem}; - $self->addbadmessage("$editFilePath is blank problem template file and should not be edited directly. " + $self->addbadmessage("$editFilePath is blank problem template file and can not be edited directly. " ."First use 'Save as' to make a local copy, then add the file to the current problem set, then edit the file." ); last CASE; @@ -833,7 +688,7 @@ }; # end 'problem' case ($file_type eq 'source_path_for_problem_file') and do { - my $forcedSourceFile = $r->param('sourceFilePath'); + my $forcedSourceFile = $self->{sourceFilePath}; # bail if no source path for the problem is found ; die "Cannot find a file path to save to" unless( defined($forcedSourceFile) and ($forcedSourceFile =~ /\S/) ); $self->{problemSeed} = 1234; @@ -863,279 +718,77 @@ # Whew!!! ################################################# - my $tempFilePath = "$editFilePath.$TEMPFILESUFFIX"; + my $tempFilePath = $self->determineTempFilePath($editFilePath); #"$editFilePath.$TEMPFILESUFFIX"; $self->{editFilePath} = $editFilePath; $self->{tempFilePath} = $tempFilePath; - $self->{inputFilePath} = (-r "$editFilePath.$TEMPFILESUFFIX") ? $tempFilePath : $editFilePath; - + $self->{inputFilePath} = (-r $tempFilePath) ? $tempFilePath : $editFilePath; + #warn "editfile path is $editFilePath and tempFile is $tempFilePath and inputFilePath is ". $self->{inputFilePath}; } +sub new_saveFileChanges { ################################################################################ -# saveFileChanges does most of the work. it is a separate method so that it can +# new_saveFileChanges does most of the work. it is a separate method so that it can # be called from either pre_header_initialize() or initilize(), depending on # whether a redirect is needed or not. # # it actually does a lot more than save changes to the file being edited, and # sometimes less. ################################################################################ -sub saveFileChanges { - my ($self, $setName, $problemNumber, $file_type, $TEMPFILESUFFIX) = @_; - my $r = $self->r; - my $ce = $r->ce; - my $db = $r->db; - my $urlpath = $r->urlpath; - - my $courseName = $urlpath->arg("courseID"); - my $user = $r->param('user'); - my $effectiveUserName = $r->param('effectiveUser'); - $setName = '' unless defined $setName; - $problemNumber = '' unless defined $problemNumber; - $file_type = '' unless defined $file_type; + my ($self, $outputFilePath, $problemContents ) = @_; + my $r = $self->r; + my $ce = $r->ce; - my $action = $self->{action}; + my $action = $self->{action}||'no action'; my $editFilePath = $self->{editFilePath}; my $tempFilePath = $self->{tempFilePath}; + + if (defined($problemContents) and ref($problemContents) ) { + $problemContents = ${$problemContents}; + } elsif( not defined($problemContents) or $problemContents =~/\S/ ) { + $problemContents = ${$self->{r_problemContents}}; + } ############################################################################## # read and update the targetFile and targetFile.tmp files in the directory # if a .tmp file already exists use that, unless the revert button has been pressed. - # These .tmp files are - # removed when the file is finally saved. - # Place the path of the file to be read in $problemPath. + # The .tmp files are removed when the file is or when the revert occurs. ############################################################################## - my $problemContents = ''; - my $outputFilePath = undef; # this is actually the output file for this subroutine - # it is then read in as source in the body of this module - my $do_not_save = 0; # flag to prevent saving of file + unless (defined($outputFilePath) and $outputFilePath =~/\S/ ) { + $self->addbadmessage("You must specify an file name in order to save a new file."); + return ""; + } + my $do_not_save = 0 ; # flag to prevent saving of file my $editErrors = ''; - ########################################################################## - # For each of the actions define the following variables: - # - # path to permanent file -- $self->{problemPath} = $editFilePath; - # path to file being read (temporary or permanent) - # --- $self->{problemPath} = $problemPath; - # contents of the file being read --- $problemContents - # $self->{r_problemContents} = \$problemContents; - # - ################################# - # handle button clicks ##### - # Read contents of file - ################################# - ACTION_CASES: { - ($action eq 'fresh_edit') and do { - # this is a fresh editing job - # the original file will be read in the body - last ACTION_CASES; - }; - - ($action eq 'revert') and do { - # this is also fresh editing job - $outputFilePath = undef; - $self->addgoodmessage("Reverting to original file $editFilePath"); - $self->{problemPath} = $editFilePath; - $self->{inputFilePath}=$editFilePath; - last ACTION_CASES; - }; - - ($action eq 'refresh') and do { - # grab the problemContents from the form in order to save it to the tmp file - # store tmp file name in the $self->problemPath for use in body - - $problemContents = $r->param('problemContents'); - $outputFilePath = "$editFilePath.$TEMPFILESUFFIX"; - $self->{problemPath} = $outputFilePath; - last ACTION_CASES; - }; - - ($action eq 'save') and do { - # grab the problemContents from the form in order to save it to the permanent file - # later we will unlink (delete) the temporary file - # store permanent file name in the $self->problemPath for use in body - $problemContents = $r->param('problemContents'); - $outputFilePath = "$editFilePath"; - $self->{problemPath} = $outputFilePath; - #$self->addgoodmessage("Saving to file $outputFilePath"); - last ACTION_CASES; - }; - - ($action eq 'save_as') and do { - my $new_file_name =$r->param('save_to_new_file') || ''; - ################################################# - #bail unless this new file name has been defined - ################################################# - if ( $new_file_name !~ /\S/) { # need a non-blank file name - # setting $self->{failure} stops saving and any redirects - $do_not_save = 1; - warn "new file name is $new_file_name"; - $self->addbadmessage(CGI::p("Please specify a file to save to.")); - last ACTION_CASES; #stop processing - } - ################################################# - # grab the problemContents from the form in order to save it to a new permanent file - # later we will unlink (delete) the current temporary file - # store new permanent file name in the $self->problemPath for use in body - ################################################# - $problemContents = $r->param('problemContents'); - - ################################################# - # Rescue the user in case they forgot to end the file name with .pg - ################################################# - if($self->{file_type} eq 'problem' - or $self->{file_type} eq 'blank_problem' - or $self->{file_type} eq 'set_header') { - $new_file_name =~ s/\.pg$//; # remove it if it is there - $new_file_name .= '.pg'; # put it there - - } - - ################################################# - # check to prevent overwrites: - ################################################# - $outputFilePath = $ce->{courseDirs}->{templates} . '/' . - $new_file_name; - - if (defined $outputFilePath and -e $outputFilePath) { - # setting $do_not_save stops saving and any redirects - $do_not_save = 1; - $self->addbadmessage(CGI::p("File $outputFilePath exists. File not saved.")); - } else { - #$self->addgoodmessage("Saving to file $outputFilePath."); - } - $self->{problemPath} = $outputFilePath; - last ACTION_CASES; - }; - ($action eq 'make_local_copy') and do { - my $new_file_name =$r->param('local_copy_file_path') || ''; - ################################################# - #bail unless this new file name has been defined - ################################################# - if ( $new_file_name !~ /\S/) { # need a non-blank file name - # setting $self->{failure} stops saving and any redirects - $do_not_save = 1; - warn "new file name is $new_file_name"; - $self->addbadmessage(CGI::p("Please specify a file to save to.")); - last ACTION_CASES; #stop processing - } - ################################################# - # grab the problemContents from the form in order to save it to a new permanent file - # later we will unlink (delete) the current temporary file - # store new permanent file name in the $self->problemPath for use in body - ################################################# - $problemContents = $r->param('problemContents'); - - ################################################# - # Rescue the user in case they forgot to end the file name with .pg - ################################################# - if($self->{file_type} eq 'problem' - or $self->{file_type} eq 'blank_problem' - or $self->{file_type} eq 'set_header') { - $new_file_name =~ s/\.pg$//; # remove it if it is there - $new_file_name .= '.pg'; # put it there - - } - - ################################################# - # check to prevent overwrites: - ################################################# - $outputFilePath = $ce->{courseDirs}->{templates} . '/' . - $new_file_name; - - if (defined $outputFilePath and -e $outputFilePath) { - # setting $do_not_save stops saving and any redirects - $do_not_save = 1; - $self->addbadmessage(CGI::p("There is already a file at [TMPL]/$new_file_name. File not saved.")); - } else { - $self->addgoodmessage("A local copy of $editFilePath is being made...") ; - } - $self->{problemPath} = $outputFilePath; - ################################################# - # if new file has been successfully saved change the file path name for the problem - ################################################# - unless ($do_not_save) { - my $problemRecord = $db->getGlobalProblem($setName,$problemNumber); - $problemRecord->source_file($new_file_name); - if ( $db->putGlobalProblem($problemRecord) ) { - $self->addgoodmessage("A local, editable, copy of $new_file_name has been made for problem $problemNumber.") ; - $self->{problemPath} = $outputFilePath; # define the file path for redirect - } else { - $self->addbadmessage("Unable to change the source file path for set $setName, problem $problemNumber. Unknown error."); - } - } - - - - last ACTION_CASES; - }; - ($action eq 'add_problem_to_set') and do { - my $sourceFile = $editFilePath; - my $targetSetName = $r->param('target_set'); - my $freeProblemID = WeBWorK::Utils::max($db->listGlobalProblems($targetSetName)) + 1; - $sourceFile =~ s|^$ce->{courseDirs}->{templates}/||; - my $problemRecord = $self->addProblemToSet( - setName => $targetSetName, - sourceFile => $sourceFile, - problemID => $freeProblemID - ); - $self->assignProblemToAllSetUsers($problemRecord); - $self->addgoodmessage("Added $sourceFile to ". $targetSetName. " as problem $freeProblemID") ; - $outputFilePath = undef; # don't save any files - $self->{problemPath} = $editFilePath; - - }; - ($action eq 'add_set_header_to_set') and do { - my $sourceFile = $editFilePath; - my $targetSetName = $r->param('target_set'); - $sourceFile =~ s|^$ce->{courseDirs}->{templates}/||; - my $setRecord = $db->getGlobalSet($targetSetName); - $setRecord->set_header($sourceFile); - if( $db->putGlobalSet($setRecord) ) { - $self->addgoodmessage("Added $sourceFile to ". $targetSetName. " as new set header ") ; - } else { - $do_not_save = 1 ; - $self->addbadmessage("Unable to make $sourceFile the set header for $targetSetName"); - } - # change file type to set_header if it not already so - $self->{file_type} = 'set_header'; - $outputFilePath = undef; # don't save any files - $self->{problemPath} = $editFilePath; - - }; - last ACTION_CASES; - - die "Unrecognized action command: $action"; - }; # end ACTION_CASES - - - ############################################################################## # write changes to the approriate files # FIXME make sure that the permissions are set correctly!!! # Make sure that the warning is being transmitted properly. ############################################################################## - + my $writeFileErrors; - if ( defined($outputFilePath) and $outputFilePath =~/\S/ and ! $do_not_save ) { # save file + if ( defined($outputFilePath) and $outputFilePath =~/\S/ ) { # save file # Handle the problem of line endings. # Make sure that all of the line endings are of unix type. # Convert \r\n to \n - $problemContents =~ s/\r\n/\n/g; - $problemContents =~ s/\r/\n/g; + #$problemContents =~ s/\r\n/\n/g; + #$problemContents =~ s/\r/\n/g; # make sure any missing directories are created - $outputFilePath = WeBWorK::Utils::surePathToFile($ce->{courseDirs}->{templates}, + WeBWorK::Utils::surePathToFile($ce->{courseDirs}->{templates}, $outputFilePath); eval { local *OUTPUTFILE; - open OUTPUTFILE, ">", $outputFilePath + open OUTPUTFILE, ">$outputFilePath" or die "Failed to open $outputFilePath"; print OUTPUTFILE $problemContents; - close OUTPUTFILE; - }; # any errors are caught in the next block - + close OUTPUTFILE; + # any errors are caught in the next block + }; + $writeFileErrors = $@ if $@; } @@ -1143,7 +796,7 @@ # Catch errors in saving files, clean up temp files ########################################################### - $self->{failure} = $do_not_save; # don't do redirects if the file was not saved. + $self->{saveError} = $do_not_save; # don't do redirects if the file was not saved. # don't unlink files or send success messages if ($writeFileErrors) { @@ -1167,27 +820,592 @@ $self->addbadmessage(CGI::p($errorMessage)); } + ########################################################### + # clean up temp files on revert, save and save_as + ########################################################### unless( $writeFileErrors or $do_not_save) { # everything worked! unlink and announce success! # unlink the temporary file if there are no errors and the save button has been pushed if ($action eq 'save' or $action eq 'save_as' or $action eq 'revert') { unlink($self->{tempFilePath}) ; } if ( defined($outputFilePath) and ! $self->{failure} ) { - my $msg = "Saved to file: $outputFilePath"; + my $msg = "Saved to file: |$outputFilePath|"; $self->addgoodmessage($msg); } } + + +} # end new_saveFileChanges + + + + + +sub getActionParams { + my ($self, $actionID) = @_; + my $r = $self->{r}; + + my %actionParams=(); + foreach my $param ($r->param) { + next unless $param =~ m/^action\.$actionID\./; + $actionParams{$param} = [ $r->param($param) ]; + } + return %actionParams; +} + +sub fixProblemContents { + #NOT a method + my $problemContents = shift; + # Handle the problem of line endings. + # Make sure that all of the line endings are of unix type. + # Convert \r\n to \n + $problemContents =~ s/\r\n/\n/g; + $problemContents =~ s/\r/\n/g; + $problemContents; +} + +sub fresh_edit_handler { + my ($self, $genericParams, $actionParams, $tableParams) = @_; + $self->addgoodmessage("fresh_edit_handler called"); +} +sub view_form { + my ($self, $onChange, %actionParams) = @_; + my $output_string = "View"; + unless ($self->{file_type} eq 'course_info') { + $output_string .= join(" ", + " problem using seed ", + CGI::textfield(-name=>'action.view.seed',-value=>$self->{problemSeed}), + "and display mode ", + CGI::popup_menu(-name=>'action.view.displayMode', -values=>$self->r->ce->{pg}->{displayModes}, + -default=>$self->{displayMode} + ), ".", + ); + } + + return $output_string; #FIXME add -lables to the pop up menu +} + +sub view_handler { + my ($self, $genericParams, $actionParams, $tableParams) = @_; + my $courseName = $self->{courseID}; + my $setName = $self->{setID}; + my $problemNumber = $self->{problemID}; + my $problemSeed = ($actionParams->{'action.view.seed'}) ? + $actionParams->{'action.view.seed'}->[0] + : 1234; + my $displayMode = ($actionParams->{'action.view.displayMode'}) ? + $actionParams->{'action.view.displayMode'}->[0] + : $self->r->ce->{pg}->{options}->{displayMode}; + + my $editFilePath = $self->{editFilePath}; + my $tempFilePath = $self->{tempFilePath}; + ######################################################## + # grab the problemContents from the form in order to save it to the tmp file + ######################################################## + my $problemContents = fixProblemContents($self->r->param('problemContents')); + $self->{r_problemContents} = \$problemContents; + + + my $do_not_save = 0; + my $file_type = $self->{file_type}; + $self->new_saveFileChanges($tempFilePath,); + + ######################################################## + # construct redirect URL and redirect + ######################################################## + my $edit_level = $self->r->param("edit_level") || 0; + $edit_level++; + my $viewURL; + + if ($file_type eq 'problem' ) { # redirect to Problem.pm + my $problemPage = $self->r->urlpath->newFromModule("WeBWorK::ContentGenerator::Problem", + courseID => $courseName, setID => $setName, problemID => $problemNumber + ); - # return values for use in the body subroutine - # The path to the current permanent file being edited: - # $self->{problemPath} = $editFilePath; - # The path to the current temporary file (if any). If no temporary file this the same - # as the permanent file path: - # $self->{outputFilePath} = $outputFilePath; - # + $viewURL = $self->systemLink($problemPage, + params => { + displayMode => $displayMode, + problemSeed => $problemSeed, + editMode => "temporaryFile", + edit_level => $edit_level, + sourceFilePath => $tempFilePath, + status_message => uri_escape($self->{status_message}) - $self->{r_problemContents} = \$problemContents; -} # end saveFileChanges + } + ); + } elsif ($file_type eq 'set_header' or $file_type eq 'hardcopy_header') { # redirect to ProblemSet + my $problemPage = $self->r->urlpath->newFromModule("WeBWorK::ContentGenerator::ProblemSet", + courseID => $courseName, setID => $setName, + ); + + $viewURL = $self->systemLink($problemPage, + params => { + displayMode => $displayMode, + problemSeed => $problemSeed, + editMode => "temporaryFile", + edit_level => $edit_level, + status_message => uri_escape($self->{status_message}) + + } + ); + + + } elsif ($file_type eq 'course_info') { # redirecto to ProblemSets.pm + my $problemSetsPage = $self->r->urlpath->newFromModule("WeBWorK::ContentGenerator::ProblemSets", + courseID => $courseName); + $viewURL = $self->systemLink($problemSetsPage, + params => { + editMode => ("temporaryFile"), + edit_level => $edit_level, + status_message => uri_escape($self->{status_message}) + } + ); + } elsif ($file_type eq 'source_path_for_problem_file') { # redirect to Problem.pm + my $problemPage = $self->r->urlpath->newFromModule("WeBWorK::ContentGenerator::Problem", + courseID => $courseName, setID => $setName, problemID => $problemNumber + ); + + $viewURL = $self->systemLink($problemPage, + params => { + displayMode => $displayMode, + problemSeed => $problemSeed, + editMode => "temporaryFile", + edit_level => $edit_level, + sourceFilePath => $tempFilePath, + status_message => uri_escape($self->{status_message}) + + } + ); + } else { + die "I don't know how to redirect this file type $file_type "; + } + + $self->reply_with_redirect($viewURL); +} + +sub add_problem_form { + my $self = shift; + my ($onChange, %actionParams) = @_; + my $r = $self->r; + my $setName = $self->{setID} ; + my $problemNumber = $self->{problemID} ; + $setName = defined($setName) ? $setName : ''; # we need this instead of using the || construction + # to keep set 0 from being set to the + # empty string. + $setName =~ s|^set||; + my @allSetNames = sort $r->db->listGlobalSets; + for (my $j=0; $j<scalar(@allSetNames); $j++) { + $allSetNames[$j] =~ s|^set||; + $allSetNames[$j] =~ s|\.def||; + } + return "" if $self->{file_type} eq 'course_info'; + return join(" ", + "Add problem to set " , + CGI::popup_menu(-name=>'action.add_problem.target_set', -values=>\@allSetNames, -default=>$setName), + " as ", + CGI::popup_menu(-name=>'action.add_problem.file_type', -values=>['problem','set_header'], -default=>'problem'), + + ); #FIXME add -lables to the pop up menu + return ""; +} + +sub add_problem_handler { + my ($self, $genericParams, $actionParams, $tableParams) = @_; +# $self->addgoodmessage("add_problem_handler called"); + my $courseName = $self->{courseID}; + my $setName = $self->{setID}; + my $problemNumber = $self->{problemID}; + my $sourceFilePath = $self->{editFilePath}; + my $displayMode = $self->{displayMode}; + my $problemSeed = $self->{problemSeed}; + + my $targetSetName = $actionParams->{'action.add_problem.target_set'}->[0]; + my $targetFileType = $actionParams->{'action.add_problem.file_type'}->[0]; + my $templatesPath = $self->r->ce->{courseDirs}->{templates}; + $sourceFilePath =~ s|^$templatesPath/||; + + my $edit_level = $self->r->param("edit_level") || 0; + $edit_level++; + + my $viewURL =''; + if ($targetFileType eq 'problem') { + my $targetProblemNumber = 1+ WeBWorK::Utils::max( $self->r->db->listGlobalProblems($targetSetName)); + + ################################################# + # Update problem record + ################################################# + my $problemRecord = $self->addProblemToSet( + setName => $targetSetName, + sourceFile => $sourceFilePath, + problemID => $targetProblemNumber, + ); + $self->assignProblemToAllSetUsers($problemRecord); + $self->addgoodmessage("Added $sourceFilePath to ". $targetSetName. " as problem $targetProblemNumber") ; + $self->{file_type} = 'problem'; # change file type to problem -- if it's not already that + + ################################################# + # Set up redirect Problem.pm + ################################################# + my $problemPage = $self->r->urlpath->newFromModule("WeBWorK::ContentGenerator::Problem", + courseID => $courseName, + setID => $targetSetName, + problemID => $targetProblemNumber, + ); + $viewURL = $self->systemLink($problemPage, + params => { + displayMode => $displayMode, + problemSeed => $problemSeed, + editMode => "savedFile", + edit_level => $edit_level, + sourceFilePath => $sourceFilePath, + status_message => uri_escape($self->{status_message}) + + } + ); + } elsif ($targetFileType eq 'set_header') { + ################################################# + # Update set record + ################################################# + my $setRecord = $self->db->getGlobalSet($targetSetName); + $setRecord->set_header($sourceFilePath); + if( $self->db->putGlobalSet($setRecord) ) { + $self->addgoodmessage("Added $sourceFilePath to ". $targetSetName. " as new set header ") ; + } else { + $self->addbadmessage("Unable to make $sourceFilePath the set header for $targetSetName"); + } + $self->{file_type} = 'set_header'; # change file type to set_header if it not already so + ################################################# + # Set up redirect + ################################################# + my $problemPage = $self->r->urlpath->newFromModule("WeBWorK::ContentGenerator::ProblemSet", + courseID => $courseName, setID => $targetSetName + ); + $viewURL = $self->systemLink($problemPage, + params => { + displayMode => $displayMode, + editMode => "savedFile", + edit_level => $edit_level, + status_message => uri_escape($self->{status_message}) + } + ); + } else { + die "Don't know what to do with target file type $targetFileType"; + } + + $self->reply_with_redirect($viewURL); +} + + +sub save_form { + my ($self, $onChange, %actionParams) = @_; + my $r => $self->r; + if (-w $self->{editFilePath}) { + return "Save"; + } else { + return ""; #"Can't save -- No write permission"; + } + +} + +sub save_handler { + my ($self, $genericParams, $actionParams, $tableParams) = @_; + $self->addgoodmessage("save_handler called"); + my $courseName = $self->{courseID}; + my $setName = $self->{setID}; + my $problemNumber = $self->{problemID}; + my $displayMode = $self->{displayMode}; + my $problemSeed = $self->{problemSeed}; + + ################################################# + # grab the problemContents from the form in order to save it to a new permanent file + # later we will unlink (delete) the current temporary file + ################################################# + my $problemContents = fixProblemContents($self->r->param('problemContents')); + $self->{r_problemContents} = \$problemContents; + + ################################################# + # Construct the output file path + ################################################# + my $editFilePath = $self->{editFilePath}; + my $outputFilePath = $editFilePath; + + my $do_not_save = 0; + my $file_type = $self->{file_type}; + $self->new_saveFileChanges($outputFilePath); + ################################################# + # Set up redirect to Problem.pm + ################################################# + my $viewURL; + ######################################################## + # construct redirect URL and redirect + ######################################################## + if ($file_type eq 'problem' ) { # redirect to Problem.pm + my $problemPage = $self->r->urlpath->newFromModule("WeBWorK::ContentGenerator::Problem", + courseID => $courseName, setID => $setName, problemID => $problemNumber + ); + + $viewURL = $self->systemLink($problemPage, + params => { + displayMode => $displayMode, + problemSeed => $problemSeed, + editMode => "savedFile", + edit_level => 0, + sourceFilePath => $editFilePath, + status_message => uri_escape($self->{status_message}) + + } + ); + } elsif ($file_type eq 'set_header' or $file_type eq 'hardcopy_header') { # redirect to ProblemSet + my $problemPage = $self->r->urlpath->newFromModule("WeBWorK::ContentGenerator::ProblemSet", + courseID => $courseName, setID => $setName, + ); + + $viewURL = $self->systemLink($p... [truncated message content] |
From: Mike G. v. a. <we...@ma...> - 2005-10-17 03:38:26
|
Log Message: ----------- Added support for a directory for temporary edit files default: templates/tmpEdit Modified Files: -------------- webwork-modperl/conf: global.conf.dist Revision Data ------------- Index: global.conf.dist =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/conf/global.conf.dist,v retrieving revision 1.151 retrieving revision 1.152 diff -Lconf/global.conf.dist -Lconf/global.conf.dist -u -r1.151 -r1.152 --- conf/global.conf.dist +++ conf/global.conf.dist @@ -269,6 +269,9 @@ # Location of mail-merge templates. $courseDirs{email} = "$courseDirs{templates}/email"; +# Location of temporary editing files. +$courseDirs{tmpEditFileDir} = "$courseDirs{templates}/tmpEdit"; + ################################################################################ # System-wide files ################################################################################ |
From: dpvc v. a. <we...@ma...> - 2005-10-16 03:38:01
|
Log Message: ----------- In the past, when Value objects were inserted into strings, they would automatically include parentheses so that if you had $f equal to 1+x and $g equal to 1-x, then Formula("$f/$g") would mean (1+x)/(1-x) rather than 1+(x/1)-x, which is what would happen as a straing string substitution. The problem is that this would also happen for real numbers, vectors, and everything else, even when it wasn't necessary. So if $x=Real(3), then "Let x = $x" would be "Let x = (3)". I have changed the behavior of the string concatenation for Value objects so that parentheses are only added in a few cases: for Formulas, Complex numbers, and Unions. This makes the other Value objects work more like regular variables in strings, but might cause some problems with strings that are used as formulas. For example, if $a = Real(-3), then "x + 2 $a" will become "x + 2 -3", or "x-1" rather than the expected "x - 6". (The old approach would have made it "x + 2 (-3)" which would have worked properly). For the most part, it is easier to use something like "x + 2*$a" or even "x" + 2*$a in this case, so the extra trouble of having to avoid parentheses when you really meant to substitute the value into a string didn't seem worth it. Modified Files: -------------- pg/lib: Value.pm pg/lib/Value: Complex.pm Formula.pm Infinity.pm Interval.pm List.pm Matrix.pm Point.pm Real.pm Set.pm String.pm Union.pm Vector.pm Revision Data ------------- Index: Value.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value.pm,v retrieving revision 1.49 retrieving revision 1.50 diff -Llib/Value.pm -Llib/Value.pm -u -r1.49 -r1.50 --- lib/Value.pm +++ lib/Value.pm @@ -90,7 +90,7 @@ '*' => 'mult', '/' => 'div', '**' => 'power', - '.' => '_dot', # see _dot below + '.' => '_dot', # see _dot below 'x' => 'cross', '<=>' => 'compare', 'cmp' => 'compare_string', @@ -453,9 +453,9 @@ # sub _dot { my ($l,$r,$flag) = @_; - return Value::_dot($r,$l,!$flag) if ($l->promotePrecedence($r)); + return $r->_dot($l,!$flag) if ($l->promotePrecedence($r)); return $l->dot($r,$flag) if (Value::isValue($r)); - $l = $l->stringify; $l = '('.$l.')' unless $$Value::context->flag('StringifyAsTeX'); + if ($$Value::context->flag('StringifyAsTeX')) {$l = $l->TeX} else {$l = $l->pdot} return ($flag)? ($r.$l): ($l.$r); } # @@ -464,12 +464,18 @@ sub dot { my ($l,$r,$flag) = @_; my $tex = $$Value::context->flag('StringifyAsTeX'); - $l = $l->stringify; $l = '('.$l.')' if $tex; - if (ref($r)) {$r = $r->stringify; $r = '('.$l.')' if $tex} + if ($tex) {$l = $l->TeX} else {$l = $l->pdot} + if (ref($r)) {if ($tex) {$r = $r->TeX} else {$r = $r->pdot}} return ($flag)? ($r.$l): ($l.$r); } # +# Some classes override this to add parens +# +sub pdot {shift->stringify} + + +# # Compare the values of the objects # (list classes should replace this) # @@ -584,7 +590,7 @@ $message = [$message,@_] if scalar(@_) > 0; $$context->setError($message,''); $message = $$context->{error}{message}; - die $message . traceback() if $$context->{debug}; + die $message . traceback() if $$context->flags('showTraceback'); die $message . getCaller(); } Index: Real.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/Real.pm,v retrieving revision 1.19 retrieving revision 1.20 diff -Llib/Value/Real.pm -Llib/Value/Real.pm -u -r1.19 -r1.20 --- lib/Value/Real.pm +++ lib/Value/Real.pm @@ -16,7 +16,7 @@ '*' => sub {shift->mult(@_)}, '/' => sub {shift->div(@_)}, '**' => sub {shift->power(@_)}, - '.' => \&Value::_dot, + '.' => sub {shift->_dot(@_)}, 'x' => sub {shift->cross(@_)}, '<=>' => sub {shift->compare(@_)}, 'cmp' => sub {shift->compare_string(@_)}, @@ -224,4 +224,3 @@ ########################################################################### 1; - Index: Complex.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/Complex.pm,v retrieving revision 1.17 retrieving revision 1.18 diff -Llib/Value/Complex.pm -Llib/Value/Complex.pm -u -r1.17 -r1.18 --- lib/Value/Complex.pm +++ lib/Value/Complex.pm @@ -13,7 +13,7 @@ '*' => sub {shift->mult(@_)}, '/' => sub {shift->div(@_)}, '**' => sub {shift->power(@_)}, - '.' => \&Value::_dot, + '.' => sub {shift->_dot(@_)}, 'x' => sub {shift->cross(@_)}, '<=>' => sub {shift->compare(@_)}, 'cmp' => sub {shift->compare_string(@_)}, @@ -336,6 +336,13 @@ ################################################## +sub pdot { + my $self = shift; + my $z = $self->stringify; + return $z if $z !~ /[-+]/; + return "($z)"; +} + sub string {my $self = shift; Value::Complex::format(@{$self->data},'string',@_)} sub TeX {my $self = shift; Value::Complex::format(@{$self->data},'TeX',@_)} Index: List.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/List.pm,v retrieving revision 1.18 retrieving revision 1.19 diff -Llib/Value/List.pm -Llib/Value/List.pm -u -r1.18 -r1.19 --- lib/Value/List.pm +++ lib/Value/List.pm @@ -11,7 +11,7 @@ use overload '+' => sub {shift->add(@_)}, - '.' => \&Value::_dot, + '.' => sub {shift->_dot(@_)}, 'x' => sub {shift->cross(@_)}, '<=>' => sub {shift->compare(@_)}, 'cmp' => sub {shift->compare_string(@_)}, Index: String.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/String.pm,v retrieving revision 1.8 retrieving revision 1.9 diff -Llib/Value/String.pm -Llib/Value/String.pm -u -r1.8 -r1.9 --- lib/Value/String.pm +++ lib/Value/String.pm @@ -8,7 +8,7 @@ @ISA = qw(Value); use overload - '.' => \&Value::_dot, + '.' => sub {shift->_dot(@_)}, '<=>' => sub {shift->compare(@_)}, 'cmp' => sub {shift->compare(@_)}, 'nomethod' => sub {shift->nomethod(@_)}, Index: Union.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/Union.pm,v retrieving revision 1.23 retrieving revision 1.24 diff -Llib/Value/Union.pm -Llib/Value/Union.pm -u -r1.23 -r1.24 --- lib/Value/Union.pm +++ lib/Value/Union.pm @@ -10,7 +10,7 @@ use overload '+' => sub {shift->add(@_)}, '-' => sub {shift->sub(@_)}, - '.' => \&Value::_dot, + '.' => sub {shift->_dot(@_)}, 'x' => sub {shift->cross(@_)}, '<=>' => sub {shift->compare(@_)}, 'cmp' => sub {shift->compare_string(@_)}, @@ -298,6 +298,8 @@ # Generate the various output formats # +sub pdot {'('.(shift->stringify).')'} + sub stringify { my $self = shift; return $self->TeX if $$Value::context->flag('StringifyAsTeX'); Index: Infinity.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/Infinity.pm,v retrieving revision 1.8 retrieving revision 1.9 diff -Llib/Value/Infinity.pm -Llib/Value/Infinity.pm -u -r1.8 -r1.9 --- lib/Value/Infinity.pm +++ lib/Value/Infinity.pm @@ -8,7 +8,7 @@ @ISA = qw(Value); use overload - '.' => \&Value::_dot, + '.' => sub {shift->_dot(@_)}, '<=>' => sub {shift->compare(@_)}, 'cmp' => sub {shift->compare_string(@_)}, 'neg' => sub {shift->neg(@_)}, Index: Vector.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/Vector.pm,v retrieving revision 1.19 retrieving revision 1.20 diff -Llib/Value/Vector.pm -Llib/Value/Vector.pm -u -r1.19 -r1.20 --- lib/Value/Vector.pm +++ lib/Value/Vector.pm @@ -15,7 +15,7 @@ '*' => sub {shift->mult(@_)}, '/' => sub {shift->div(@_)}, '**' => sub {shift->power(@_)}, - '.' => \&Value::_dot, + '.' => sub {shift->_dot(@_)}, 'x' => sub {shift->cross(@_)}, '<=>' => sub {shift->compare(@_)}, 'cmp' => sub {shift->compare_string(@_)}, Index: Set.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/Set.pm,v retrieving revision 1.9 retrieving revision 1.10 diff -Llib/Value/Set.pm -Llib/Value/Set.pm -u -r1.9 -r1.10 --- lib/Value/Set.pm +++ lib/Value/Set.pm @@ -10,7 +10,7 @@ use overload '+' => sub {shift->add(@_)}, '-' => sub {shift->sub(@_)}, - '.' => \&Value::_dot, + '.' => sub {shift->_dot(@_)}, 'x' => sub {shift->cross(@_)}, '<=>' => sub {shift->compare(@_)}, 'cmp' => sub {shift->compare_string(@_)}, Index: Formula.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/Formula.pm,v retrieving revision 1.38 retrieving revision 1.39 diff -Llib/Value/Formula.pm -Llib/Value/Formula.pm -u -r1.38 -r1.39 --- lib/Value/Formula.pm +++ lib/Value/Formula.pm @@ -17,7 +17,7 @@ '*' => sub {shift->mult(@_)}, '/' => sub {shift->div(@_)}, '**' => sub {shift->power(@_)}, - '.' => sub {shift->dot(@_)}, + '.' => sub {shift->_dot(@_)}, 'x' => sub {shift->cross(@_)}, '<=>' => sub {shift->compare(@_)}, 'cmp' => sub {shift->compare_string(@_)}, @@ -129,14 +129,16 @@ # # Make dot work for vector operands # -sub dot { +sub _dot { my ($l,$r,$flag) = @_; - if ($l->promotePrecedence($r)) {return $r->dot($l,!$flag)} + if ($l->promotePrecedence($r)) {return $r->_dot($l,!$flag)} return bop('.',@_) if $l->type eq 'Vector' && Value::isValue($r) && $r->type eq 'Vector'; - Value::_dot(@_); + $l->SUPER::_dot($r,$flag); } +sub pdot {'('.(shift->stringify).')'} + # # Call the Parser::Function call function # @@ -155,7 +157,6 @@ $formula->{context} = $self->{context}; $formula->{variables} = $self->{variables}; $formula->{tree} = $formula->{context}{parser}{UOP}->new($formula,'u-',$self->{tree}->copy($formula)); -# return $formula->eval if $formula->isConstant; return $formula; } Index: Interval.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/Interval.pm,v retrieving revision 1.27 retrieving revision 1.28 diff -Llib/Value/Interval.pm -Llib/Value/Interval.pm -u -r1.27 -r1.28 --- lib/Value/Interval.pm +++ lib/Value/Interval.pm @@ -12,7 +12,7 @@ use overload '+' => sub {shift->add(@_)}, '-' => sub {shift->sub(@_)}, - '.' => \&Value::_dot, + '.' => sub {shift->_dot(@_)}, 'x' => sub {shift->cross(@_)}, '<=>' => sub {shift->compare(@_)}, 'cmp' => sub {shift->compare_string(@_)}, Index: Matrix.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/Matrix.pm,v retrieving revision 1.20 retrieving revision 1.21 diff -Llib/Value/Matrix.pm -Llib/Value/Matrix.pm -u -r1.20 -r1.21 --- lib/Value/Matrix.pm +++ lib/Value/Matrix.pm @@ -17,7 +17,7 @@ '*' => sub {shift->mult(@_)}, '/' => sub {shift->div(@_)}, '**' => sub {shift->power(@_)}, - '.' => \&Value::_dot, + '.' => sub {shift->_dot(@_)}, 'x' => sub {shift->cross(@_)}, '<=>' => sub {shift->compare(@_)}, 'cmp' => sub {shift->compare_string(@_)}, Index: Point.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/Point.pm,v retrieving revision 1.17 retrieving revision 1.18 diff -Llib/Value/Point.pm -Llib/Value/Point.pm -u -r1.17 -r1.18 --- lib/Value/Point.pm +++ lib/Value/Point.pm @@ -15,7 +15,7 @@ '*' => sub {shift->mult(@_)}, '/' => sub {shift->div(@_)}, '**' => sub {shift->power(@_)}, - '.' => \&Value::_dot, + '.' => sub {shift->_dot(@_)}, 'x' => sub {shift->cross(@_)}, '<=>' => sub {shift->compare(@_)}, 'cmp' => sub {shift->compate_string(@_)}, |
From: dpvc v. a. <we...@ma...> - 2005-10-16 03:28:21
|
Log Message: ----------- Added a new experimental diagonstic function for the function answer checker. When enabled, it will produce graphs of the correct answer, the student answer, and the absolute and relative errors, and will list the data points used in the comparison, plus the numerical values of the results and errors. To enable the diagnostic, use ANS(fun_cmp($f,diagnostics=>1)); Note that only single-variable functions can be graphed at the moment, so if you are using a multi-variable check, you need to disable the graphing. To do this use ANS(fun_cmp($f,vars=>['x','y'],diagnostics=>[showGraphs=>0])); The diagnostic mode is only available for the Parser-based versions of the function checker, and (of course) with the native Parser objects as well: ANS(Formula($f)->cmp(diagnostics=>1)); There are now Context settings to control the diagnostics, which can be set through Context()->diagnostics->set(). For example Context()->diagnostics->set(formulas=>{showGraphs=>0}); would turn off graphs for all functions comparisons. Some of the other values you can set are: formulas => { showTestPoints => 1, # show the test points and function values showRelativeErrors => 1, # show the relative errors for the student answer showAbsoluteErrors => 1, # show the absolute errors for the student answer showGraphs => 1, # show the various graphs graphRelativeErrors => 1, # show the relative error graph graphAbsoluteErrors => 1, # show the absolute error graph clipRelativeError => 5, # don't show relative errors above 5 clipAbsoluteError => 5, # don't show absolute errors above 5 plotTestPoints => 1, # include dots at the test points combineGraphs => 1, # show correct and student graphs in one image }, graphs => { divisions => 75, # the number of data points to plot limits => [-2,2], # the lower and upper limit of the plot # (taken from the function limits if not provided) size => 250, # pixel size of the image (could be [width,height]) grid => [10,10], # number of grid lines in each direction axes => [0,0], # where to put axes relative to origin } Any of these can be set in the Context(), or in the answer checker itself. If you set diagnostics to an array reference, the entries in the array refer to element of the formulas hash. If you set diagonstics to a hash reference, then you can set values in either the formulas or graphs hashes, as in: ANS(Formula($f)->cmp(diagnostics=>{ formulas => {showAbsoluteErrors=>0}, graphs => {size=>300, divisions=>100}, })); If you want all function checkers to show diagnostics, use Context()->diagonstics->set(formulas=>{show=>1}); The image file names are modified to include the current time so that the names will be unique. This avoids problems with the browser cache showing a old image when a new one has been generated. But this also means that the temporary image directory will fill up fast, so you may need to empty it if you use the diagnostic images frequently. This is just a first attempt at a diagnostic feature. I think it will help when you are not sure if the tolerances are set properly, or if you think a student answer should be markes correct but isn't, as it will point out which point(s) are not being accepted. Modified Files: -------------- pg/lib/Value: AnswerChecker.pm pg/macros: PGanswermacros.pl Revision Data ------------- Index: AnswerChecker.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/AnswerChecker.pm,v retrieving revision 1.70 retrieving revision 1.71 diff -Llib/Value/AnswerChecker.pm -Llib/Value/AnswerChecker.pm -u -r1.70 -r1.71 --- lib/Value/AnswerChecker.pm +++ lib/Value/AnswerChecker.pm @@ -72,10 +72,12 @@ $ans->{debug} = $ans->{rh_ans}{debug}; $ans->install_evaluator(sub {$ans = shift; $ans->{correct_value}->cmp_parse($ans)}); $ans->install_pre_filter('erase') if $self->{ans_name}; # don't do blank check if answer_array + $self->cmp_diagnostics($ans); return $ans; } sub correct_ans {protectHTML(shift->string)} +sub cmp_diagnostics {} # # Parse the student answer and compute its value, @@ -130,6 +132,7 @@ if ($self->cmp_collect($ans)) { $self->cmp_equal($ans); $self->cmp_postprocess($ans) if !$ans->{error_message}; + $self->cmp_diagnostics($ans); } } else { $self->cmp_collect($ans); @@ -1314,6 +1317,8 @@ # # Handle removal of outermost parens in a list. +# Evaluate answer, if the eval option is used. +# Handle the UpToConstant option. # sub cmp { my $self = shift; @@ -1382,6 +1387,231 @@ } # +# Diagnostics for Formulas +# +sub cmp_diagnostics { + my $self = shift; my $ans = shift; + my $isEvaluator = (ref($ans) =~ /Evaluator/)? 1: 0; + my $hash = $isEvaluator? $ans->rh_ans : $ans; + my $diagnostics = $self->{context}->diagnostics->merge("formulas",$self,$hash); + my $formulas = $diagnostics->{formulas}; + return unless $formulas->{show}; + + my $output = ""; + if ($isEvaluator) { + # + # The tests to be performed with the answer checker is created + # + $self->getPG('loadMacros("PGgraphmacros.pl")'); + my ($inputs) = $self->getPG('$inputs_ref'); + my $process = $inputs->{checkAnswers} || $inputs->{previewAnswers} || $inputs->{submitAnswers}; + if ($formulas->{checkNumericStability} && !$process) { + ### still needs to be written + } + } else { + # + # The checks to be performed when an answer is submitted + # + my $student = $ans->{student_formula}; + my $points = [map {$_->[0]} @{$self->{test_points}}]; + + # + # The graphs of the functions and errors + # + if ($formulas->{showGraphs}) { + my @G = (); + if ($formulas->{combineGraphs}) { + push(@G,$self->cmp_graph($diagnostics,[$student,$self], + title=>'Student Answer (red)<BR>Correct Answer (green)<BR>', + points=>$points,showDomain=>1)); + } else { + push(@G,$self->cmp_graph($diagnostics,$self,title=>'Correct Answer')); + push(@G,$self->cmp_graph($diagnostics,$student,title=>'Student Answer')); + } + my $cutoff = Value::Formula->new($self->getFlag('tolerance')); + if ($formulas->{graphAbsoluteErrors}) { + push(@G,$self->cmp_graph($diagnostics,[abs($self-$student),$cutoff], + clip=>$formulas->{clipAbsoluteError}, + title=>'Absolute Error',points=>$points)); + } + if ($formulas->{graphRelativeErrors}) { + push(@G,$self->cmp_graph($diagnostics,[abs(($self-$student)/$self),$cutoff], + clip=>$formulas->{clipRelativeError}, + title=>'Relative Error',points=>$points)); + } + $output .= '<TABLE BORDER="0" CELLSPACING="0" CELLPADDING="0">' + . '<TR VALIGN="TOP">'.join('<TD WIDTH="20"></TD>',@G).'</TR></TABLE>'; + } + + # + # The test points and values + # + my @rows = (); my $colsep = '</TD><TD WIDTH="20"></TD><TD ALIGN="RIGHT">'; + my @P = (map {(scalar(@{$_}) == 1)? $_->[0]: Value::Point->make(@{$_})} @{$self->{test_points}}); + my @i = sort {$P[$a] <=> $P[$b]} (0..$#P); + if ($formulas->{showTestPoints}) { + $student->createPointValues($self->{test_points},0,1,1) unless $student->{test_values}; + my @p = ("Input:",(map {$P[$i[$_]]} (0..$#P))); + push(@rows,'<TR><TD ALIGN="RIGHT">'.join($colsep,@p).'</TD></TR>'); + push(@rows,'<TR><TD ALIGN="RIGHT">'.join($colsep,("<HR>")x scalar(@p)).'</TD></TR>'); + push(@rows,'<TR><TD ALIGN="RIGHT">' + .join($colsep,"Correct Answer:", map {$self->{test_values}[$i[$_]]} (0..$#P)) + .'</TD></TR>'); + my $test = $student->{test_values}; + push(@rows,'<TR><TD ALIGN="RIGHT">' + .join($colsep,"Student Answer:", map {Value::isNumber($test->[$i[$_]])? $test->[$i[$_]]: "undefined"} (0..$#P)) + .'</TD></TR>'); + } + # + # The absolute errors (colored by whether they are ok or too big) + # + if ($formulas->{showAbsoluteErrors}) { + my @p = ("Absolute Error:"); + my $tolerance = $self->getFlag('tolerance'); + my $tolType = $self->getFlag('tolType'); my $error; + foreach my $j (0..$#P) { + if (Value::isNumber($student->{test_values}[$i[$j]])) { + $error = abs($self->{test_values}[$i[$j]]-$student->{test_values}[$i[$j]]); + $error = '<SPAN STYLE="color:#'.($error<$tolerance ? '00AA00': 'AA0000').'">'.$error.'</SPAN>' + if $tolType eq 'absolute'; + } else {$error = "---"} + push(@p,$error); + } + push(@rows,'<TR><TD ALIGN="RIGHT">'.join($colsep,@p).'</TD></TR>'); + } + # + # The relative errors (colored by whether they are OK ro too big) + # + if ($formulas->{showRelativeErrors}) { + my @p = ("Relative Error:"); + my $tolerance = $self->getFlag('tolerance'); + my $tolType = $self->getFlag('tolType'); my $error; + foreach my $j (0..$#P) { + if (Value::isNumber($student->{test_values}[$i[$j]])) { + $error = abs(($self->{test_values}[$i[$j]]-$student->{test_values}[$i[$j]])/ + ($self->{test_values}[$i[$j]]||1E-10)); + $error = '<SPAN STYLE="color:#'.($error<$tolerance ? '00AA00': 'AA0000').'">'.$error.'</SPAN>' + if $tolType eq 'relative'; + } else {$error = "---"} + push(@p,$error); + } + push(@rows,'<TR><TD ALIGN="RIGHT">'.join($colsep,@p).'</TD></TR>'); + } + # + # Put the data into a table + # + if (scalar(@rows)) { + $output .= '<p><HR><p><TABLE BORDER="0" CELLSPACING="0" CELLPADDING="0">' + . join('<TR><TD HEIGHT="3"></TD>',@rows) + . '</TABLE>'; + } + } + # + # Put all the diagnostic output into a frame + # + return unless $output; + $output + = '<TABLE BORDER="1" CELLSPACING="2" CELLPADDING="20" BGCOLOR="#F0F0F0">' + . '<TR><TD ALIGN="LEFT"><B>Diagnostics for '.$self->string .':</B>' + . '<P><CENTER>' . $output . '</CENTER></TD></TR></TABLE><P>'; + warn $output; +} + +# +# Draw a graph from a given Formula object +# +sub cmp_graph { + my $self = shift; my $diagnostics = shift; + my $F1 = shift; my $F2; ($F1,$F2) = @{$F1} if (ref($F1) eq 'ARRAY'); + # + # Get the various options + # + my %options = (title=>'',points=>[],@_); + my $graphs = $diagnostics->{graphs}; + my $limits = $graphs->{limits}; $limits = $self->getFlag('limits',[-2,2]) unless $limits; + $limits = $limits->[0] if ref($limits) eq 'ARRAY' && ref($limits->[0]) eq 'ARRAY'; + my $size = $graphs->{size}; $size = [$size,$size] unless ref($size) eq 'ARRAY'; + my $steps = $graphs->{divisions}; + my $points = $options{points}; my $clip = $options{clip}; + my ($my,$My) = (0,0); my ($mx,$Mx) = @{$limits}; + my $dx = ($Mx-$mx)/$steps; my $f; my $y; + + # + # Find the max and min values of the function + # + foreach $f ($F1,$F2) { + next unless defined($f); + unless (scalar(keys(%{$f->{variables}})) < 2) { + warn "Only formulas with one variable can be graphed"; + return ""; + } + if ($f->isConstant) { + $y = $f->eval; + $my = $y if $y < $my; $My = $y if $y > $My; + } else { + my $F = $f->perlFunction; + foreach my $i (0..$steps-1) { + $y = eval {&{$F}($mx+$i*$dx)}; next unless defined($y) && Value::isNumber($y); + $my = $y if $y < $my; $My = $y if $y > $My; + } + } + } + $My = 1 if abs($My - $my) < 1E-5; + $my *= 1.1; $My *= 1.1; + if ($clip) { + $my = -$clip if $my < -$clip; + $My = $clip if $My > $clip; + } + $my = -$My/10 if $my > -$My/10; $My = -$my/10 if $My < -$my/10; + my $a = Value::Real->new(($My-$my)/($Mx-$mx)); + + # + # Create the graph itself, with suitable title + # + my $grf = $self->getPG('$_grf_ = {n => 0}'); + $grf->{Goptions} = [ + $mx,$my,$Mx,$My, + axes => $graphs->{axes}, + grid => $graphs->{grid}, + size => $size, + ]; + $grf->{G} = $self->getPG('init_graph(@{$_grf_->{Goptions}})'); + $grf->{G}->imageName($grf->{G}->imageName.'-'.time()); # avoid browser cache + $self->cmp_graph_function($grf,$F2,"green",$steps,$points) if defined($F2); + $self->cmp_graph_function($grf,$F1,"red",$steps,$points); + my $image = $self->getPG('alias(insertGraph($_grf_->{G}))'); + $image = '<IMG SRC="'.$image.'" WIDTH="'.$size->[0].'" HEIGHT="'.$size->[1].'" BORDER="0" STYLE="margin-bottom:5px">'; + my $title = $options{title}; $title .= '<DIV STYLE="margin-top:5px"></DIV>' if $title; + $title .= "<SMALL>Domain: [$mx,$Mx]</SMALL><BR>" if $options{showDomain}; + $title .= "<SMALL>Range: [$my,$My]<BR>Aspect ratio: $a:1</SMALL>"; + return '<TD ALIGN="CENTER" VALIGN="TOP" NOWRAP>'.$image.'<BR>'.$title.'</TD>'; +} + +# +# Add a function to a graph object, and plot the points +# that are used to test the function +# +sub cmp_graph_function { + my $self = shift; my $grf = shift; my $F = shift; + my $color = shift; my $steps = shift; my $points = shift; + $grf->{n}++; my $Fn = "F".$grf->{n}; $grf->{$Fn} = $F; my $f; + if ($F->isConstant) { + my $y = $F->eval; + $f = $self->getPG('new Fun(sub {'.$y.'},$_grf_->{G})'); + } else { + my $X = (keys %{$F->{variables}})[0]; + $f = $self->getPG('new Fun(sub {Parser::Evaluate($_grf_->{'.$Fn.'},'.$X.'=>shift)},$_grf_->{G})'); + foreach my $x (@{$points}) { + my $y = Parser::Evaluate($F,($X)=>$x); next unless defined($y) && Value::isNumber($y); + $grf->{x} = $x; $grf->{y} = $y; + my $C = $self->getPG('new Circle($_grf_->{x},$_grf_->{y},4,"'.$color.'","'.$color.'")'); + $grf->{G}->stamps($C); + } + } + $f->color($color); $f->weight(2); $f->steps($steps); +} + +# # If an answer array was used, get the data from the # Matrix, Vector or Point, and format the array of # data using the original parameter Index: PGanswermacros.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/PGanswermacros.pl,v retrieving revision 1.39 retrieving revision 1.40 diff -Lmacros/PGanswermacros.pl -Lmacros/PGanswermacros.pl -u -r1.39 -r1.40 --- macros/PGanswermacros.pl +++ macros/PGanswermacros.pl @@ -1138,7 +1138,6 @@ if ref($rh_ans->{student_value}); return $rh_ans; }); - $cmp->{debug} = $num_params{debug}; &$Context($oldContext); return $cmp; @@ -1502,6 +1501,7 @@ 'zeroLevel' => $functZeroLevelDefault, 'zeroLevelTol' => $functZeroLevelTolDefault, 'debug' => 0, + 'diagnostics' => undef, ); # allow var => 'x' as an abbreviation for var => ['x'] @@ -1552,6 +1552,7 @@ 'zeroLevelTol' => $out_options{'zeroLevelTol'}, 'params' => $out_options{'params'}, 'debug' => $out_options{'debug'}, + 'diagnostics' => $out_options{'diagnostics'} , ), ); } @@ -1674,6 +1675,7 @@ 'zeroLevel' => $functZeroLevelDefault, 'zeroLevelTol' => $functZeroLevelTolDefault, 'debug' => 0, + 'diagnostics' => undef, ); my $var_ref = $options{'vars'}; @@ -1697,6 +1699,7 @@ 'scale_norm' => 1, 'params' => $ra_params, 'debug' => $options{debug} , + 'diagnostics' => $options{diagnostics} , ); } @@ -1934,7 +1937,10 @@ # End of cleanup of calling parameters ######################################################## - my %options = (debug => $func_params{'debug'}); + my %options = ( + debug => $func_params{'debug'}, + diagnostics => $func_params{'diagnostics'}, + ); # # Initialize the context for the formula @@ -1975,7 +1981,6 @@ $f->{limits} = $func_params{'limits'}; $f->{test_points} = $func_params{'test_points'}; my $cmp = $f->cmp(%options); - $cmp->{debug} = 1 if $func_params{'debug'}; &$Context($oldContext); # |
From: dpvc v. a. <we...@ma...> - 2005-10-16 02:45:23
|
Log Message: ----------- Modified context data objects to provide a copy method so that the various types of data can copy themselves (and can provide a more comprehensive copy if necessary). Modified Files: -------------- pg/lib/Parser: Context.pm pg/lib/Value: Context.pm pg/lib/Value/Context: Data.pm Added Files: ----------- pg/lib/Value/Context: Diagnostics.pm Revision Data ------------- Index: Context.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Parser/Context.pm,v retrieving revision 1.14 retrieving revision 1.15 diff -Llib/Parser/Context.pm -Llib/Parser/Context.pm -u -r1.14 -r1.15 --- lib/Parser/Context.pm +++ lib/Parser/Context.pm @@ -21,10 +21,9 @@ $context->{parser} = {%{$Parser::class}}; push(@{$context->{data}{values}},'parser'); $context->{_initialized} = 0; - foreach my $list ('functions','variables','constants','operators','strings','parens') { - push(@{$context->{data}{hashes}},$list); - $context->{$list} = {}; - } + push(@{$context->{data}{objects}},( + 'functions','variables','constants','operators','strings','parens', + )); push(@{$context->{data}{values}},'reduction'); my %data = ( functions => {}, @@ -45,8 +44,8 @@ $context->{_strings} = new Parser::Context::Strings($context,%{$data{strings}}); $context->{_parens} = new Parser::Context::Parens($context,%{$data{parens}}); $context->{_reduction} = new Parser::Context::Reduction($context,%{$data{reduction}}); - $context->lists->set(%{$data{lists}}) if defined($data{lists}); - $context->flags->set(%{$data{flags}}) if defined($data{flags}); + $context->lists->set(%{$data{lists}}); + $context->flags->set(%{$data{flags}}); $context->{_initialized} = 1; $context->update; return $context; Index: Context.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/Context.pm,v retrieving revision 1.9 retrieving revision 1.10 diff -Llib/Value/Context.pm -Llib/Value/Context.pm -u -r1.9 -r1.10 --- lib/Value/Context.pm +++ lib/Value/Context.pm @@ -13,7 +13,6 @@ sub new { my $self = shift; my $class = ref($self) || $self; my $context = bless { - flags => {}, pattern => { number => '(?:\d+(?:\.\d*)?|\.\d+)(?:E[-+]?\d+)?', signedNumber => '[-+]?(?:\d+(?:\.\d*)?|\.\d+)(?:E[-+]?\d+)?', @@ -29,17 +28,19 @@ msg => {}, # for localization }, data => { - hashes => ['lists'], + hashes => [], arrays => ['data'], - values => ['flags','pattern','format'], + values => ['flags','pattern','format','value'], + objects => ['diagnostics','lists'], }, value => { Formula => "Value::Formula" }, }, $class; - my %data = (lists=>{},flags=>{},@_); + my %data = (lists=>{},flags=>{},diagnostics=>{},@_); $context->{_lists} = new Value::Context::Lists($context,%{$data{lists}}); $context->{_flags} = new Value::Context::Flags($context,%{$data{flags}}); + $context->{_diagnostics} = new Value::Context::Diagnostics($context,%{$data{diagnostics}}); $context->{_initialized} = 1; $context->update; return $context; @@ -53,9 +54,10 @@ # # Access to the data lists # -sub lists {(shift)->{_lists}} -sub flags {(shift)->{_flags}} -sub flag {(shift)->{_flags}->get(shift)} +sub lists {(shift)->{_lists}} +sub flags {(shift)->{_flags}} +sub flag {(shift)->{_flags}->get(shift)} +sub diagnostics {(shift)->{_diagnostics}} # # Make a copy of a Context object @@ -64,12 +66,15 @@ my $self = shift; my $context = $self->new(); $context->{_initialized} = 0; + foreach my $data (@{$context->{data}{objects}}) { + $context->{$data} = $self->{"_$data"}->copy; + $context->{"_$data"}->update; + } foreach my $data (@{$context->{data}{hashes}}) { $context->{$data} = {}; foreach my $x (keys %{$self->{$data}}) { $context->{$data}{$x} = {%{$self->{$data}{$x}}}; } - $context->{"_$data"}->update; } foreach my $data (@{$context->{data}{arrays}}) { $context->{$data} = {}; Index: Data.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/Context/Data.pm,v retrieving revision 1.8 retrieving revision 1.9 diff -Llib/Value/Context/Data.pm -Llib/Value/Context/Data.pm -u -r1.8 -r1.9 --- lib/Value/Context/Data.pm +++ lib/Value/Context/Data.pm @@ -29,6 +29,25 @@ sub uncreate {shift; shift} # +# Copy the hash data +# +sub copy { + my $self = shift; + my $data = $self->{context}->{$self->{dataName}}; + my $copy = {}; + foreach my $name (keys %{$data}) { + if (ref($data->{$name}) eq 'ARRAY') { + $copy->{$name} = [@{$data->{$name}}]; + } elsif (ref($data->{$name}) eq 'HASH') { + $copy->{$name} = {%{$data->{$name}}}; + } else { + $copy->{$name} = $data->{$name}; + } + } + return $copy; +} + +# # Sort names so that they can be joined for regexp matching # sub byName { @@ -213,6 +232,7 @@ use Value::Context::Flags; use Value::Context::Lists; +use Value::Context::Diagnostics; ######################################################################### --- /dev/null +++ lib/Value/Context/Diagnostics.pm @@ -0,0 +1,67 @@ +######################################################################### +# +# Implement the list of Value::Diagnostics types +# +package Value::Context::Diagnostics; +use strict; +use vars qw (@ISA); +@ISA = qw(Value::Context::Data); + +sub new { + my $self = shift; my $parent = shift; + $self->SUPER::new($parent, + formulas => { + show => 0, + showTestPoints => 1, + showAbsoluteErrors => 1, + showRelativeErrors => 1, + showGraphs => 1, + graphRelativeErrors => 1, + graphAbsoluteErrors => 1, + clipRelativeError => 5, + clipAbsoluteError => 5, + plotTestPoints => 1, + combineGraphs => 1, + checkNumericStability => 1, + }, + graphs => { + divisions => 75, + limits => undef, + size => 250, + grid => [10,10], + axes => [0,0], + }, + @_, + ); +} + +sub init { + my $self = shift; + $self->{dataName} = 'diagnostics'; + $self->{name} = 'diagnostics'; + $self->{Name} = 'Diagnostics'; + $self->{namePattern} = '[-\w_.]+'; +} + +sub update {} # no pattern needed + +sub merge { + my $self = shift; my $type = shift; + my $merge = {%{$self->{context}{$self->{dataName}}}}; + foreach my $object (@_) { + my $data = $object->{$self->{dataName}}; next unless $data; + $data = {$type=>{@{$data}}} if ref($data) eq 'ARRAY'; + $data = {$type=>{show=>$data}} unless ref($data) eq 'HASH'; + $merge->{$type}{show} = 1 if scalar(keys(%{$data})); + foreach my $x (keys %{$data}) { + if (ref($merge->{$x}) ne 'HASH') {$merge->{$x} = $data->{$x}} + else {$merge->{$x} = {%{$merge->{$x}},%{$data->{$x}}}} + } + } + return $merge; +} + + +######################################################################### + +1; |
From: dpvc v. a. <we...@ma...> - 2005-10-14 23:34:42
|
Log Message: ----------- Let Parser object's cmp(debug=>1) option set the debug feature of the answer evaluator. 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.69 retrieving revision 1.70 diff -Llib/Value/AnswerChecker.pm -Llib/Value/AnswerChecker.pm -u -r1.69 -r1.70 --- lib/Value/AnswerChecker.pm +++ lib/Value/AnswerChecker.pm @@ -69,6 +69,7 @@ %{$self->{context}{cmpDefaults}{$self->class} || {}}, # context-specified defaults @_ ); + $ans->{debug} = $ans->{rh_ans}{debug}; $ans->install_evaluator(sub {$ans = shift; $ans->{correct_value}->cmp_parse($ans)}); $ans->install_pre_filter('erase') if $self->{ans_name}; # don't do blank check if answer_array return $ans; |
From: Sam H. v. a. <we...@ma...> - 2005-10-14 21:20:44
|
Log Message: ----------- added "height: 10em; overflow: auto;" for div.Siblings fixing bug #581. Modified Files: -------------- webwork2/htdocs/css: ur.css Revision Data ------------- Index: ur.css =================================================================== RCS file: /webwork/cvs/system/webwork2/htdocs/css/ur.css,v retrieving revision 1.9 retrieving revision 1.10 diff -Lhtdocs/css/ur.css -Lhtdocs/css/ur.css -u -r1.9 -r1.10 --- htdocs/css/ur.css +++ htdocs/css/ur.css @@ -15,7 +15,7 @@ div.Logo { } div.Links { font-size: small; } -div.Siblings { font-size: small; } +div.Siblings { font-size: small; height: 10em; overflow: auto; } div.Options { font-size: small; } /* top table cell, contains login message and path */ |
From: Sam H. v. a. <we...@ma...> - 2005-10-14 19:10:50
|
Log Message: ----------- show first pdflatex error (bug #850), HTML escaping. - generate_hardcopy_pdf() now reports the first error encountered in hardcopy.log in the list of errors. - variables substituted into errors strings are now displayed inside <code>..</code> tags, and their values are escpaed with CGI/pm's escapeHTML() function. Modified Files: -------------- webwork2/lib/WeBWorK/ContentGenerator: Hardcopy.pm Revision Data ------------- Index: Hardcopy.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/Hardcopy.pm,v retrieving revision 1.67 retrieving revision 1.68 diff -Llib/WeBWorK/ContentGenerator/Hardcopy.pm -Llib/WeBWorK/ContentGenerator/Hardcopy.pm -u -r1.67 -r1.68 --- lib/WeBWorK/ContentGenerator/Hardcopy.pm +++ lib/WeBWorK/ContentGenerator/Hardcopy.pm @@ -412,21 +412,22 @@ my $eUserID = $r->param("effectiveUser"); # we want to make the temp directory web-accessible, for error reporting - #my $temp_dir_path = eval { makeTempDirectory($ce->{webworkDirs}{tmp}, "webwork-hardcopy") }; my $temp_dir_parent_path = $ce->{courseDirs}{html_temp} . "/hardcopy"; # makeTempDirectory will ensure that .../hardcopy exists my $temp_dir_path = eval { makeTempDirectory($temp_dir_parent_path, "work") }; if ($@) { - $self->add_errors($@); + $self->add_errors(CGI::escapeHTML($@)); return; } # do some error checking unless (-e $temp_dir_path) { - $self->add_errors("Temporary directory '$temp_dir_path' does not exist, but creation didn't fail. This shouldn't happen."); + $self->add_errors("Temporary directory '".CGI::code(CGI::escapeHTML($temp_dir_path)) + ."' does not exist, but creation didn't fail. This shouldn't happen."); return; } unless (-w $temp_dir_path) { - $self->add_errors("Temporary directory '$temp_dir_path' is not writeable."); + $self->add_errors("Temporary directory '".CGI::code(CGI::escapeHTML($temp_dir_path)) + ."' is not writeable."); $self->delete_temp_dir($temp_dir_path); return; } @@ -437,7 +438,8 @@ # write TeX my $open_result = open my $FH, ">", $tex_file_path; unless ($open_result) { - $self->add_errors("Failed to open file '$tex_file_path' for writing: $!"); + $self->add_errors("Failed to open file '".CGI::code(CGI::escapeHTML($tex_file_path)) + ."' for writing: ".CGI::code(CGI::escapeHTML($!))); $self->delete_temp_dir($temp_dir_path); return; } @@ -453,7 +455,8 @@ # if no hardcopy.tex file was generated, fail now unless (-e "$temp_dir_path/hardcopy.tex") { - $self->add_errors("'hardcopy.tex' not written to temporary directory '$temp_dir_path'. Can't continue."); + $self->add_errors("'".CGI::code("hardcopy.tex")."' not written to temporary directory '" + .CGI::code(CGI::escapeHTML($temp_dir_path))."'. Can't continue."); $self->delete_temp_dir($temp_dir_path); return; } @@ -487,7 +490,9 @@ # make sure final file exists unless (-e $final_file_path) { - $self->add_errors("Final hardcopy file '$final_file_path' not found after calling '$format_subr': $!"); + $self->add_errors("Final hardcopy file '".CGI::code(CGI::escapeHTML($final_file_path)) + ."' not found after calling '".CGI::code(CGI::escapeHTML($format_subr))."': " + .CGI::code(CGI::escapeHTML($!))); return $final_file_url, %temp_file_map; } @@ -497,8 +502,10 @@ my $mv_cmd = "2>&1 /bin/mv " . shell_quote($final_file_path, $final_file_final_path); my $mv_out = readpipe $mv_cmd; if ($?) { - $self->add_errors("Failed to move hardcopy file '$final_file_name' from '$temp_dir_path' to '$temp_dir_parent_path':" - .CGI::br().CGI::pre($mv_out)); + $self->add_errors("Failed to move hardcopy file '".CGI::code(CGI::escapeHTML($final_file_name)) + ."' from '".CGI::code(CGI::escapeHTML($temp_dir_path))."' to '" + .CGI::code(CGI::escapeHTML($temp_dir_parent_path))."':".CGI::br() + .CGI::pre(CGI::escapeHTML($mv_out))); $final_file_url = "$temp_dir_url/$final_file_name"; } else { $final_file_url = "$temp_dir_parent_url/$final_file_name"; @@ -521,7 +528,7 @@ my $rm_cmd = "2>&1 /bin/rm -rf " . shell_quote($temp_dir_path); my $rm_out = readpipe $rm_cmd; if ($?) { - $self->add_errors("Failed to remove temporary directory '$temp_dir_path':" + $self->add_errors("Failed to remove temporary directory '".CGI::code(CGI::escapeHTML($temp_dir_path))."':" .CGI::br().CGI::pre($rm_out)); return 0; } else { @@ -549,7 +556,10 @@ my $mv_cmd = "2>&1 /bin/mv " . shell_quote("$temp_dir_path/$src_name", "$temp_dir_path/$dest_name"); my $mv_out = readpipe $mv_cmd; if ($?) { - $self->add_errors("Failed to rename '$src_name' to '$dest_name' in directory '$temp_dir_path':".CGI::br().CGI::pre($mv_out)); + $self->add_errors("Failed to rename '".CGI::code(CGI::escapeHTML($src_name))."' to '" + .CGI::code(CGI::escapeHTML($dest_name))."' in directory '" + .CGI::code(CGI::escapeHTML($temp_dir_path))."':".CGI::br() + .CGI::pre(CGI::escapeHTML($mv_out))); $final_file_name = $src_name; } else { $final_file_name = $dest_name; @@ -567,7 +577,35 @@ . $self->r->ce->{externalPrograms}{pdflatex} . " >pdflatex.stdout 2>pdflatex.stderr hardcopy"; if (system $pdflatex_cmd) { - $self->add_errors("Failed to convert TeX to PDF with command '$pdflatex_cmd'."); + $self->add_errors("Failed to convert TeX to PDF with command '" + .CGI::code(CGI::escapeHTML($pdflatex_cmd))."'."); + + # read hardcopy.log and report first error + my $hardcopy_log = "$temp_dir_path/hardcopy.log"; + if (-e $hardcopy_log) { + if (open my $LOG, "<", $hardcopy_log) { + my $line; + while ($line = <$LOG>) { + last if $line =~ /^!\s+/; + } + my $first_error = $line; + while ($line = <$LOG>) { + last if $line =~ /^!\s+/; + $first_error .= $line; + } + close $LOG; + if (defined $first_error) { + $self->add_errors("First error in TeX log is:".CGI::br(). + CGI::pre(CGI::escapeHTML($first_error))); + } else { + $self->add_errors("No errors encoundered in TeX log."); + } + } else { + $self->add_errors("Could not read TeX log: ".CGI::code(CGI::escapeHTML($!))); + } + } else { + $self->add_errors("No TeX log was found."); + } } my $final_file_name; @@ -578,7 +616,10 @@ my $mv_cmd = "2>&1 /bin/mv " . shell_quote("$temp_dir_path/$src_name", "$temp_dir_path/$dest_name"); my $mv_out = readpipe $mv_cmd; if ($?) { - $self->add_errors("Failed to rename '$src_name' to '$dest_name' in directory '$temp_dir_path':".CGI::br().CGI::pre($mv_out)); + $self->add_errors("Failed to rename '".CGI::code(CGI::escapeHTML($src_name))."' to '" + .CGI::code(CGI::escapeHTML($dest_name))."' in directory '" + .CGI::code(CGI::escapeHTML($temp_dir_path))."':".CGI::br() + .CGI::pre(CGI::escapeHTML($mv_out))); $final_file_name = $src_name; } else { $final_file_name = $dest_name; @@ -626,7 +667,7 @@ # get user record my $TargetUser = $db->getUser($targetUserID); # checked unless ($TargetUser) { - $self->add_errors("Can't generate hardcopy for user '$targetUserID' -- no such user exists.\n"); + $self->add_errors("Can't generate hardcopy for user '".CGI::code(CGI::escapeHTML($targetUserID))."' -- no such user exists.\n"); return; } @@ -651,17 +692,23 @@ # get set record my $MergedSet = $db->getMergedSet($TargetUser->user_id, $setID); # checked unless ($MergedSet) { - $self->add_errors("Can't generate hardcopy for set '$setID' for user '".$TargetUser->user_id."' -- set is not assigned to that user."); + $self->add_errors("Can't generate hardcopy for set ''".CGI::code(CGI::escapeHTML($setID)) + ."' for user '".CGI::code(CGI::escapeHTML($TargetUser->user_id)) + ."' -- set is not assigned to that user."); return; } # see if the *real* user is allowed to access this problem set if ($MergedSet->open_date > time and not $authz->hasPermissions($userID, "view_unopened_sets")) { - $self->add_errors("Can't generate hardcopy for set '$setID' for user '".$TargetUser->user_id."' -- set is not yet open."); + $self->add_errors("Can't generate hardcopy for set '".CGI::code(CGI::escapeHTML($setID)) + ."' for user '".CGI::code(CGI::escapeHTML($TargetUser->user_id)) + ."' -- set is not yet open."); return; } if (not $MergedSet->published and not $authz->hasPermissions($userID, "view_unpublished_sets")) { - $self->addbadmessage("Can't generate hardcopy for set '$setID' for user '".$TargetUser->user_id."' -- set has not been published."); + $self->addbadmessage("Can't generate hardcopy for set '".CGI::code(CGI::escapeHTML($setID)) + ."' for user '".CGI::code(CGI::escapeHTML($TargetUser->user_id)) + ."' -- set has not been published."); return; } @@ -706,7 +753,11 @@ # handle nonexistent problem unless ($MergedProblem) { - $self->add_errors("Can't generate hardcopy for problem '$problemID' in set '".$MergedSet->set_id."' for user '".$MergedSet->user_id."' -- problem does not exist in that set or is not assigned to that user."); + $self->add_errors("Can't generate hardcopy for problem '" + .CGI::code(CGI::escapeHTML($problemID))."' in set '" + .CGI::code(CGI::escapeHTML($MergedSet->set_id)) + ."' for user '".CGI::code(CGI::escapeHTML($MergedSet->user_id)) + ."' -- problem does not exist in that set or is not assigned to that user."); return; } } elsif ($pgFile) { @@ -793,7 +844,7 @@ if ($pg->{warnings} ne "") { $self->add_errors(CGI::a({href=>$edit_url}, "[edit]") ."Warnings encountered while processing $problem_desc. " - ."Error text:".CGI::br().CGI::pre($pg->{warnings}) + ."Error text:".CGI::br().CGI::pre(CGI::escapeHTML($pg->{warnings})) ); } @@ -802,7 +853,7 @@ $self->add_errors(CGI::a({href=>$edit_url}, "[edit]") ."Errors encountered while processing $problem_desc. " ."This $problem_name has been omitted from the hardcopy. " - ."Error text:".CGI::br().CGI::pre($pg->{errors}) + ."Error text:".CGI::br().CGI::pre(CGI::escpaeHTML($pg->{errors})) ); return; } @@ -835,7 +886,8 @@ my $tex = eval { readFile($file) }; if ($@) { - $self->add_errors("Failed to include TeX file '$file': $@"); + $self->add_errors("Failed to include TeX file '".CGI::code(CGI::escapeHTML($file))."': " + .CGI::escapeHTML($@)); } else { print $FH $tex; } |
From: Sam H. v. a. <we...@ma...> - 2005-10-11 22:45:23
|
Log Message: ----------- eliminate undefined value usage in unformatDateTime, fixing bug #826. also factored out error reporting into unformatDateTime_error and improved formatting slightly. Modified Files: -------------- webwork2/lib/WeBWorK: Utils.pm Revision Data ------------- Index: Utils.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/Utils.pm,v retrieving revision 1.70 retrieving revision 1.71 diff -Llib/WeBWorK/Utils.pm -Llib/WeBWorK/Utils.pm -u -r1.70 -r1.71 --- lib/WeBWorK/Utils.pm +++ lib/WeBWorK/Utils.pm @@ -322,23 +322,29 @@ # time zone. # # Error handling has also been improved. Exceptions are now thrown for errors, -# and more information is given abou the nature of errors. +# and more information is given about the nature of errors. # sub unformatDateAndTime { my ($string) = @_; - my $orgString =$string; + my $orgString = $string; + $string =~ s|^\s+||; $string =~ s|\s+$||; $string =~ s|at| at |i; ## OK if forget to enter spaces or use wrong case $string =~ s|AM| AM|i; ## OK if forget to enter spaces or use wrong case $string =~ s|PM| PM|i; ## OK if forget to enter spaces or use wrong case $string =~ s|,| at |; ## start translating old form of date/time to new form - if ($string =~ m|^\s*[\/\d]+\s+[:\d]+| ) { # case where the at is missing: MM/DD/YYYY at HH:MM AMPM ZONE - die "Incorrect date/time format \"$orgString\". The \"at\" appears to be missing. - Correct format is MM/DD/YYYY at HH:MM AMPM ZONE (e.g. \"03/29/2004 at 06:00am EST\")"; - } - - my($date,$at, $time,$AMPM,$TZ) = split(/\s+/,$string); + + # case where the at is missing: MM/DD/YYYY at HH:MM AMPM ZONE + unformatDateAndTime_error($orgString, "The 'at' appears to be missing.") + if $string =~ m|^\s*[\/\d]+\s+[:\d]+|; + + my ($date, $at, $time, $AMPM, $TZ) = split /\s+/, $string; + + unformatDateAndTime_error($orgString, "The date and/or time appear to be missing.", $date, $time, $AMPM, $TZ) + unless defined $date and defined $at and defined $time; + + # deal with military time unless ($time =~ /:/) { { ##bare block for 'case" structure $time =~ /(\d\d)(\d\d)/; @@ -355,57 +361,56 @@ } ##end of bare block for 'case" structure } - - my ($mday, $mon, $year, $wday, $yday,$sec, $pm, $min, $hour); + + # default value for $AMPM + $AMPM = "AM" unless defined $AMPM; + + my ($mday, $mon, $year, $wday, $yday, $sec, $pm, $min, $hour); $sec=0; $time =~ /^([0-9]+)\s*\:\s*([0-9]*)/; $min=$2; $hour = $1; - if ($hour < 1 or $hour > 12) { - die "Incorrect date/time format \"$orgString\". Hour must be in the range [1,12]. - Correct format is MM/DD/YYYY at HH:MM AMPM ZONE (e.g. \"03/29/2004 at 06:00am EST\") - date = $date - time = $time - ampm = $AMPM - zone = $TZ\n"; - } - if ($min < 0 or $min > 59) { - die "Incorrect date/time format \"$orgString\". Minute must be in the range [0-59]. - Correct format is MM/DD/YYYY at HH:MM AMPM ZONE - date = $date - time = $time - ampm = $AMPM - zone = $TZ\n"; - } + unformatDateAndTime_error($orgString, "Hour must be in the range [1,12].", $date, $time, $AMPM, $TZ) + if $hour < 1 or $hour > 12; + unformatDateAndTime_error($orgString, "Minute must be in the range [0-59].", $date, $time, $AMPM, $TZ) + if $min < 0 or $min > 59; $pm = 0; $pm = 12 if ($AMPM =~/PM/ and $hour < 12); $hour += $pm; $hour = 0 if ($AMPM =~/AM/ and $hour == 12); - $date =~ m!([0-9]+)\s*/\s*([0-9]+)/\s*([0-9]+)! ; + $date =~ m|([0-9]+)\s*/\s*([0-9]+)/\s*([0-9]+)|; $mday =$2; $mon=($1-1); - if ($mday < 1 or $mday > 31) { - die "Incorrect date/time format \"$orgString\". Day must be in the range [1,31]. - Correct format is MM/DD/YY at HH:MM AMPM ZONE - date = $date - time = $time - ampm = $AMPM - zone = $TZ\n"; - } - if ($mon < 0 or $mon > 11) { - die "Incorrect date/time format \"$orgString\". Month must be in the range [1,12]. - Correct format is MM/DD/YY at HH:MM AMPM ZONE - date = $date - time = $time - ampm = $AMPM - zone = $TZ\n"; - } + unformatDateAndTime_error($orgString, "Day must be in the range [1,31].", $date, $time, $AMPM, $TZ) + if $mday < 1 or $mday > 31; + unformatDateAndTime_error($orgString, "Month must be in the range [1,12].", $date, $time, $AMPM, $TZ) + if $mon < 0 or $mon > 11; $year=$3; $wday=""; $yday=""; return ($sec, $min, $hour, $mday, $mon, $year, $TZ); } +sub unformatDateAndTime_error { + + if (@_ > 2) { + my ($orgString, $error, $date, $time, $AMPM, $TZ) = @_; + $date = "(undefined)" unless defined $date; + $time = "(undefined)" unless defined $time; + $AMPM = "(undefined)" unless defined $AMPM; + $TZ = "(undefined)" unless defined $TZ; + die "Incorrect date/time format \"$orgString\": $error\n", + "Correct format is MM/DD/YY at HH:MM AMPM ZONE\n", + "\tdate = $date\n", + "\ttime = $time\n", + "\tampm = $AMPM\n", + "\tzone = $TZ\n"; + } else { + my ($orgString, $error) = @_; + die "Incorrect date/time format \"$orgString\": $error\n", + "Correct format is MM/DD/YY at HH:MM AMPM ZONE\n"; + } +} sub parseDateTime($;$) { my ($string, $display_tz) = @_; |