[system] / branches / rel-2-2-dev / webwork2 / lib / WeBWorK / ContentGenerator / Instructor / PGProblemEditor.pm Repository:
ViewVC logotype

View of /branches/rel-2-2-dev/webwork2/lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 4057 - (download) (as text) (annotate)
Tue Apr 18 23:27:32 2006 UTC (7 years, 2 months ago) by sh002i
File size: 78185 byte(s)
SECURITY: prevent reading/saving files outside of templates directory.

    1 ################################################################################
    2 # WeBWorK Online Homework Delivery System
    3 # Copyright © 2000-2006 The WeBWorK Project, http://openwebwork.sf.net/
    4 # $CVSHeader: webwork2/lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm,v 1.66.2.7 2006/01/25 23:12:01 sh002i Exp $
    5 #
    6 # This program is free software; you can redistribute it and/or modify it under
    7 # the terms of either: (a) the GNU General Public License as published by the
    8 # Free Software Foundation; either version 2, or (at your option) any later
    9 # version, or (b) the "Artistic License" which comes with this package.
   10 #
   11 # This program is distributed in the hope that it will be useful, but WITHOUT
   12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
   13 # FOR A PARTICULAR PURPOSE.  See either the GNU General Public License or the
   14 # Artistic License for more details.
   15 ################################################################################
   16 
   17 package WeBWorK::ContentGenerator::Instructor::PGProblemEditor;
   18 use base qw(WeBWorK::ContentGenerator::Instructor);
   19 
   20 
   21 
   22 =head1 NAME
   23 
   24 WeBWorK::ContentGenerator::Instructor::PGProblemEditor - Edit a pg file
   25 
   26 =cut
   27 
   28 use strict;
   29 use warnings;
   30 use CGI qw();
   31 use WeBWorK::Utils qw(readFile surePathToFile path_is_subdir);
   32 use Apache::Constants qw(:common REDIRECT);
   33 use HTML::Entities;
   34 use URI::Escape;
   35 use WeBWorK::Utils;
   36 use WeBWorK::Utils::Tasks qw(fake_set fake_problem);
   37 
   38 ###########################################################
   39 # This editor will edit problem files or set header files or files, such as course_info
   40 # whose name is defined in the global.conf database
   41 #
   42 # Only files under the template directory ( or linked to this location) can be edited.
   43 #
   44 # The course information and problems are located in the course templates directory.
   45 # Course information has the name  defined by courseFiles->{course_info}
   46 #
   47 # Only files under the template directory ( or linked to this location) can be edited.
   48 #
   49 # editMode = temporaryFile    (view the temp file defined by course_info.txt.user_name.tmp
   50 #                              instead of the file course_info.txt)
   51 #            this flag is read by Problem.pm and ProblemSet.pm, perhaps others
   52 # The TEMPFILESUFFIX is "user_name.tmp" by default.  It's definition should be moved to Instructor.pm #FIXME
   53 ###########################################################
   54 
   55 ###########################################################
   56 # The behavior of this module is essentially defined
   57 # by the values of $file_type and the submit button which is placed in $action
   58 #############################################################
   59 #  File types which can be edited
   60 #
   61 #  file_type  eq 'problem'
   62 #                 this is the most common type -- this editor can be called by an instructor when viewing any problem.
   63 #                 the information for retrieving the source file is found using the problemID in order to look
   64 #                 look up the source file path.
   65 #
   66 #  file_type  eq 'source_path_for_problem_file'
   67 #                 This is the same as the 'problem' file type except that the source for the problem is found in
   68 #                 the parameter $r->param('sourceFilePath').  This path is relative to the templates directory
   69 #
   70 #  file_type  eq 'set_header'
   71 #                 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.
   72 #
   73 #  file_type  eq 'hardcopy_header'
   74 #                  This is a special case of editing the problem.  The hardcopy_header is often listed as problem 0 in the set's list of problems.
   75 #                  But it is used instead of set_header when producing a hardcopy of the problem set in the TeX format, instead of producing HTML
   76 #                  formatted version for use on the computer screen.
   77 #
   78 #  file_type eq 'course_info
   79 #                 This allows editing of the course_info.txt file which gives general information about the course.  It is called from the
   80 #                 ProblemSets.pm module.
   81 #
   82 #  file_type eq 'options_info
   83 #                 This allows editing of the options_info.txt file which gives general information about the course.  It is called from the
   84 #                 Options.pm module.
   85 #
   86 #  file_type  eq 'blank_problem'
   87 #                 This is a special call which allows one to create and edit a new PG problem.  The "stationery" source for this problem is
   88 #                 stored in the conf/snippets directory and defined in global.conf by $webworkFiles{screenSnippets}{blankProblem}
   89 #############################################################
   90 # Requested actions  -- these and the file_type determine the state of the module
   91 #      Save                       ---- action = save
   92 #      Save as                    ---- action = save_as
   93 #      View Problem               ---- action = view
   94 #      Add this problem to:       ---- action = add_problem
   95 #      Make this set header for:  ---- action = add_problem
   96 #      Revert                     ---- action = revert
   97 #      no submit button defined   ---- action = fresh_edit
   98 ###################################################
   99 #
  100 # Determining which is the correct path to the file is a mess!!! FIXME
  101 # The path to the file to be edited is eventually put in tempFilePath
  102 #
  103 #  (tempFilePath)(editFilePath)(forcedSourceFile)
  104 #input parameter is:  sourceFilePath
  105 #################################################################
  106 # params read
  107 # user
  108 # effectiveUser
  109 # submit
  110 # file_type
  111 # problemSeed
  112 # displayMode
  113 # edit_level
  114 # make_local_copy
  115 # sourceFilePath
  116 # problemContents
  117 # save_to_new_file
  118 #
  119 
  120 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)];
  121 
  122 # permissions needed to perform a given action
  123 use constant FORM_PERMS => {
  124     view => "modify_student_data",
  125     add_problem => "modify_student_data",
  126     make_local_copy => "modify_student_data",
  127     save => "modify_student_data",
  128     save_as => "modify_student_data",
  129 #   rename  => "modify_student_data",
  130     revert => "modify_student_data",
  131 };
  132 
  133 our $BLANKPROBLEM = 'blankProblem.pg';
  134 # use constant BLANKPROBLEM => 'blankProblem.pg';  # doesn't work because this constant needs to be used inside a match.
  135 sub pre_header_initialize {
  136   my ($self)         = @_;
  137   my $r              = $self->r;
  138   my $ce             = $r->ce;
  139   my $urlpath        = $r->urlpath;
  140   my $authz          = $r->authz;
  141   my $user           = $r->param('user');
  142   $self->{courseID}   = $urlpath->arg("courseID");
  143   $self->{setID}      = $r->urlpath->arg("setID") ;  # using $r->urlpath->arg("setID")  ||'' causes trouble with set 0!!!
  144   $self->{problemID}  = $r->urlpath->arg("problemID");
  145 
  146   my $submit_button   = $r->param('submit');  # obtain submit command from form
  147   my $actionID        = $r->param('action');
  148   my $file_type       = $r->param("file_type") || '';
  149   my $setName         = $self->{setID};
  150   my $problemNumber   = $self->{problemID};
  151 
  152   # Check permissions
  153   return unless ($authz->hasPermissions($user, "access_instructor_tools"));
  154   return unless ($authz->hasPermissions($user, "modify_problem_sets"));
  155 
  156   ##############################################################################
  157   # displayMode   and problemSeed
  158   #
  159   # Determine the display mode
  160   # If $self->{problemSeed} was obtained within saveFileChanges from the problem_record
  161   # then it can be overridden by the value obtained from the form.
  162   # Insure that $self->{problemSeed} has some non-empty value
  163   # displayMode and problemSeed
  164   # will be needed for viewing the problem via redirect.
  165   # They are also two of the parameters which can be set by the editor
  166   ##############################################################################
  167 
  168   if (defined $r->param('displayMode')) {
  169     $self->{displayMode} = $r->param('displayMode');
  170   } else {
  171     $self->{displayMode} = $ce->{pg}->{options}->{displayMode};
  172   }
  173 
  174   # form version of problemSeed overrides version obtained from the the problem_record
  175   # inside saveFileChanges
  176   $self->{problemSeed} = $r->param('problemSeed') if (defined $r->param('problemSeed'));
  177   # Make sure that the problem seed has some value
  178   $self->{problemSeed} = '123456' unless defined $self->{problemSeed} and $self->{problemSeed} =~/\S/;
  179 
  180   ##############################################################################
  181     #############################################################################
  182   # Save file to permanent or temporary file, then redirect for viewing
  183   #############################################################################
  184   #
  185   #  Any file "saved as" should be assigned to "Undefined_Set" and redirectoed to be viewed again in the editor
  186   #
  187   #  Problems "saved" or 'refreshed' are to be redirected to the Problem.pm module
  188   #  Set headers which are "saved" are to be redirected to the ProblemSet.pm page
  189   #  Hardcopy headers which are "saved" are also to be redirected to the ProblemSet.pm page
  190   #  Course_info files are redirected to the ProblemSets.pm page
  191   #  Options_info files are redirected to the Options.pm page
  192   ##############################################################################
  193 
  194 
  195 
  196   ######################################
  197     # Insure that file_type is defined
  198   ######################################
  199   # We have already read in the file_type parameter from the form
  200   #
  201   # If this has not been defined we are  dealing with a set header
  202   # or regular problem
  203   if (defined($file_type) and ($file_type =~/\S/)) { #file_type is defined and is not blank
  204     # file type is already defined -- do nothing
  205     #warn "file type already defined as $file_type"  #FIXME debug
  206   } else {
  207       # if "sourceFilePath" is defined in the form, then we are getting the path directly.
  208     # if the problem number is defined and is 0
  209     # then we are dealing with some kind of
  210     # header file.  The default is 'set_header' which prints properly
  211     # to the screen.
  212     # If the problem number is not zero, we are dealing with a real problem
  213     ######################################
  214     if ( defined($r->param('sourceFilePath') and $r->param('sourceFilePath') =~/\S/) ) {
  215       $file_type ='source_path_for_problem_file';
  216       $file_type = 'set_header' if $r->param('sourceFilePath') =~ m!/headers/|Header\.pg$!; #FIXME this need to be cleaned up
  217     } elsif ( defined($problemNumber) ) {
  218        if ( $problemNumber =~/^\d+$/ and $problemNumber == 0 ) {  # if problem number is numeric and zero
  219                 $file_type = 'set_header' unless  $file_type eq 'set_header'
  220                                                or $file_type eq 'hardcopy_header';
  221              } else {
  222               $file_type = 'problem';
  223               #warn "setting file type to 'problem'\n";  #FIXME debug
  224              }
  225 
  226     }
  227   }
  228   die "The file_type variable has not been defined or is blank." unless defined($file_type) and $file_type =~/\S/;
  229   # clean up sourceFilePath, just in case
  230   # double check that sourceFilePath is relative to the templates file
  231   if ($file_type eq 'source_path_for_problem_file' ) {
  232     my $templatesDirectory = $ce->{courseDirs}->{templates};
  233     my $sourceFilePath = $r->param('sourceFilePath');
  234     $sourceFilePath =~ s/$templatesDirectory//;
  235     $sourceFilePath =~ s|^/||;  # remove intial /
  236     $self->{sourceFilePath} = $sourceFilePath;
  237   }
  238   $self->{file_type} = $file_type;
  239   # $self->addgoodmessage("file type is $file_type");  #FIXME debug
  240   # warn "file type is $file_type\n parameter is ".$self->r->param("file_type");
  241   ##########################################
  242   # File type is one of:     blank_problem course_info options_info problem set_header hardcopy_header source_path_for_problem_file
  243     ##########################################
  244     #
  245     # Determine the path to the file
  246     #
  247     ###########################################
  248       $self->getFilePaths($setName, $problemNumber, $file_type);
  249       #defines $self->{editFilePath}   # path to the permanent file to be edited
  250       #        $self->{tempFilePath}   # path to the permanent file to be edited  has .tmp suffix
  251       #        $self->{inputFilePath}  # path to the file for input, (might be a .tmp file)
  252 
  253 
  254 
  255     ##########################################
  256     # Default problem contents
  257     ##########################################
  258     $self->{r_problemContents}= undef;
  259 
  260     ##########################################
  261     #
  262     # Determine action
  263     #
  264     ###########################################
  265 
  266   if ($actionID) {
  267     unless (grep { $_ eq $actionID } @{ ACTION_FORMS() } ) {
  268       die "Action $actionID not found";
  269     }
  270     # Check permissions
  271     if (not FORM_PERMS()->{$actionID} or $authz->hasPermissions($user, FORM_PERMS()->{$actionID})) {
  272       my $actionHandler = "${actionID}_handler";
  273       my %genericParams =();
  274 #       foreach my $param (qw(selected_users)) {
  275 #         $genericParams{$param} = [ $r->param($param) ];
  276 #     }
  277       my %actionParams = $self->getActionParams($actionID);
  278       my %tableParams = (); # $self->getTableParams();
  279       $self->{action}= $actionID;
  280       $self->$actionHandler(\%genericParams, \%actionParams, \%tableParams);
  281     } else {
  282       $self->addbadmessage( "You are not authorized to perform this action.");
  283     }
  284   } else {
  285     $self->{action}='fresh_edit';
  286     my $actionHandler = "fresh_edit_handler";
  287     my %genericParams;
  288     my %actionParams = (); #$self->getActionParams($actionID);
  289     my %tableParams = (); # $self->getTableParams();
  290     my $problemContents = '';
  291     $self->{r_problemContents}=\$problemContents;
  292     $self->$actionHandler(\%genericParams, \%actionParams, \%tableParams);
  293   }
  294 
  295 
  296   ##############################################################################
  297   # displayMode   and problemSeed
  298   #
  299   # Determine the display mode
  300   # If $self->{problemSeed} was obtained within saveFileChanges from the problem_record
  301   # then it can be overridden by the value obtained from the form.
  302   # Insure that $self->{problemSeed} has some non-empty value
  303   # displayMode and problemSeed
  304   # will be needed for viewing the problem via redirect.
  305   # They are also two of the parameters which can be set by the editor
  306   ##############################################################################
  307 
  308   if (defined $r->param('displayMode')) {
  309     $self->{displayMode} = $r->param('displayMode');
  310   } else {
  311     $self->{displayMode} = $ce->{pg}->{options}->{displayMode};
  312   }
  313 
  314   # form version of problemSeed overrides version obtained from the the problem_record
  315   # inside saveFileChanges
  316   $self->{problemSeed} = $r->param('problemSeed') if (defined $r->param('problemSeed'));
  317   # Make sure that the problem seed has some value
  318   $self->{problemSeed} = '123456' unless defined $self->{problemSeed} and $self->{problemSeed} =~/\S/;
  319 
  320   ##############################################################################
  321   # Return
  322   #   If  file saving fails or
  323   #   if no redirects are required. No further processing takes place in this subroutine.
  324   #   Redirects are required only for the following submit values
  325   #        'Save'
  326   #        'Save as'
  327   #        'Refresh'
  328   #        add problem to set
  329   #        add set header to set
  330   #
  331     #########################################
  332 
  333     return if $self->{failure};
  334     # FIXME: even with an error we still open a new page because of the target specified in the form
  335 
  336 
  337   # Some cases do not need a redirect: save, refresh, save_as, add_problem_to_set, add_header_to_set,make_local_copy
  338   my $action = $self->{action};
  339     return ;
  340 
  341 }
  342 
  343 
  344 sub initialize  {
  345   my ($self) = @_;
  346   my $r = $self->r;
  347   my $authz = $r->authz;
  348   my $user = $r->param('user');
  349 
  350   # Check permissions
  351   return unless ($authz->hasPermissions($user, "access_instructor_tools"));
  352   return unless ($authz->hasPermissions($user, "modify_problem_sets"));
  353 
  354   my $tempFilePath    = $self->{tempFilePath}; # path to the file currently being worked with (might be a .tmp file)
  355   my $inputFilePath   = $self->{inputFilePath};   # path to the file for input, (might be a .tmp file)
  356 
  357   $self->addmessage($r->param('status_message') ||'');  # record status messages carried over if this is a redirect
  358   $self->addbadmessage("Changes in this file have not yet been permanently saved.") if -r $tempFilePath;
  359   if ( not( -e $inputFilePath) ) {
  360     $self->addbadmessage("The file '".$self->shortPath($inputFilePath)."' cannot be found.");
  361   } elsif (not -w $inputFilePath ) {
  362 
  363     $self->addbadmessage("The file '".$self->shortPath($inputFilePath)."' is protected! ".CGI::br().
  364     "To edit this text you must make a copy of this file using the 'make local editable copy at ...'action below.");
  365 
  366   }
  367     if ($inputFilePath =~/$BLANKPROBLEM$/) {
  368 #     $self->addbadmessage("This file '$inputFilePath' is a blank problem! ".CGI::br()."To edit this text you must
  369       $self->addbadmessage("The file '".$self->shortPath($inputFilePath)."' is a blank problem! ".CGI::br()."To edit this text you must
  370                            use 'Save as' to save it to another file.");
  371     }
  372 
  373 }
  374 
  375 sub path {
  376   my ($self, $args) = @_;
  377   my $r = $self->r;
  378   my $urlpath = $r->urlpath;
  379   my $courseName  = $urlpath->arg("courseID");
  380   my $setName = $r->urlpath->arg("setID") || '';
  381   my $problemNumber = $r->urlpath->arg("problemID") || '';
  382 
  383   # we need to build a path to the problem being edited by hand, since it is not the same as the urlpath
  384   # For this page the bread crum path leads back to the problem being edited, not to the Instructor tool.
  385   my @path = ( 'WeBWork', $r->location,
  386             "$courseName", $r->location."/$courseName",
  387             "$setName",    $r->location."/$courseName/$setName",
  388             "$problemNumber", $r->location."/$courseName/$setName/$problemNumber",
  389             "Editor", ""
  390   );
  391 
  392   #print "\n<!-- BEGIN " . __PACKAGE__ . "::path -->\n";
  393   print $self->pathMacro($args, @path);
  394   #print "<!-- END " . __PACKAGE__ . "::path -->\n";
  395 
  396   return "";
  397 }
  398 sub title {
  399   my $self = shift;
  400   my $r = $self->r;
  401   my $courseName    = $r->urlpath->arg("courseID");
  402   my $setID         = $r->urlpath->arg("setID");
  403   my $problemNumber = $r->urlpath->arg("problemID");
  404   my $file_type = $self->{'file_type'} || '';
  405   return "Set Header for  set $setID" if ($file_type eq 'set_header');
  406   return "Hardcopy Header for set $setID" if ($file_type eq 'hardcopy_header');
  407   return "Course Information for course $courseName" if ($file_type eq 'course_info');
  408   return "Options Information" if ($file_type eq 'options_info');
  409   return 'Problem ' . $r->{urlpath}->name;
  410 }
  411 
  412 sub body {
  413   my ($self) = @_;
  414   my $r = $self->r;
  415   my $db = $r->db;
  416   my $ce = $r->ce;
  417   my $authz = $r->authz;
  418   my $user = $r->param('user');
  419   my $make_local_copy = $r->param('make_local_copy');
  420 
  421   # Check permissions
  422   return CGI::div({class=>"ResultsWithError"}, "You are not authorized to access the Instructor tools.")
  423     unless $authz->hasPermissions($user, "access_instructor_tools");
  424 
  425   return CGI::div({class=>"ResultsWithError"}, "You are not authorized to modify problems.")
  426     unless $authz->hasPermissions($user, "modify_student_data");
  427 
  428 
  429 
  430 
  431   # Gathering info
  432   my $editFilePath    = $self->{editFilePath}; # path to the permanent file to be edited
  433   my $tempFilePath    = $self->{tempFilePath}; # path to the file currently being worked with (might be a .tmp file)
  434   my $inputFilePath   = $self->{inputFilePath};   # path to the file for input, (might be a .tmp file)
  435   my $setName         = $self->{setID} ;
  436   my $problemNumber   = $self->{problemID} ;
  437     $setName            = defined($setName) ? $setName : '';  # we need this instead of using the || construction
  438                                                               # to keep set 0 from being set to the
  439                                                               # empty string.
  440     $problemNumber      = defined($problemNumber) ? $problemNumber : '';
  441 
  442   #########################################################################
  443     # Construct url for reporting bugs:
  444   #########################################################################
  445 
  446 #   $editFilePath =~ m|([^/]*)Library|;    #find the path to the file
  447 #   my $libraryName = $1;                  # find the library, if any exists in the path name (first library is picked)
  448 #   $libraryName ='rochester' unless defined($libraryName) and $libraryName =~/\S/; # default library
  449   my $libraryName = '';
  450   if ($editFilePath =~ m|([^/]*)Library|)   {  #find the path to the file
  451     # find the library, if any exists in the path name (first library is picked)
  452     my $tempLibraryName = $1;
  453     $libraryName = ( defined($tempLibraryName) and $tempLibraryName =~/\S/ ) ?
  454                     $tempLibraryName : "Library";
  455     # things that start /Library/setFoo/probBar  are labeled as component "Library"
  456     # which refers to the SQL based problem library. (is nationalLibrary a better name?)
  457   } else {
  458     $libraryName = 'rochester';  # make sure there is some default component defined.
  459   }
  460 
  461     my $BUGZILLA = "http://bugs.webwork.rochester.edu/enter_bug.cgi?product=Problem%20libraries".
  462                    "&component=$libraryName&bug_file_loc=${editFilePath}_with_problemSeed=".$self->{problemSeed};
  463   #FIXME  # The construction of this URL is somewhat fragile.  A separate module could be devoted to intelligent reporting of bugs.
  464 
  465   #########################################################################
  466   # Find the text for the problem, either in the tmp file, if it exists
  467   # or in the original file in the template directory
  468   # or in the problem contents gathered in the initialization phase.
  469   #########################################################################
  470 
  471   my $problemContents = ${$self->{r_problemContents}};
  472 
  473   unless ( $problemContents =~/\S/)   { # non-empty contents
  474     if (-r $tempFilePath and not -d $tempFilePath) {
  475       die "tempFilePath is unsafe!" unless path_is_subdir($tempFilePath, $ce->{courseDirs}->{templates}, 1); # 1==path can be relative to dir
  476       eval { $problemContents = WeBWorK::Utils::readFile($tempFilePath) };
  477       $problemContents = $@ if $@;
  478       $inputFilePath = $tempFilePath;
  479     } elsif  (-r $editFilePath and not -d $editFilePath) {
  480       die "editFilePath is unsafe!" unless path_is_subdir($editFilePath, $ce->{courseDirs}->{templates}, 1); # 1==path can be relative to dir
  481       eval { $problemContents = WeBWorK::Utils::readFile($editFilePath) };
  482       $problemContents = $@ if $@;
  483       $inputFilePath = $editFilePath;
  484     } else { # file not existing is not an error
  485         #warn "No file exists";
  486       $problemContents = '';
  487     }
  488   } else {
  489     #warn "obtaining input from r_problemContents";
  490   }
  491 
  492   my $protected_file = not -w $inputFilePath;
  493 
  494   my $file_type = $self->{file_type};
  495   my %titles = (
  496     problem         =>CGI::b("set $setName/problem $problemNumber"),
  497     set_header      => "header file",
  498     hardcopy_header => "hardcopy header file",
  499     course_info     => "course information",
  500     options_info    => "options information",
  501     ''              => 'Unknown file type',
  502     source_path_for_problem_file => " unassigned problem file:  ".CGI::b("set $setName/problem $problemNumber"),
  503   );
  504   my $header = CGI::i("Editing $titles{$file_type} in file '".$self->shortPath($inputFilePath)."'");
  505   $header = ($self->isTempEditFilePath($inputFilePath)  ) ? CGI::div({class=>'temporaryFile'},$header) : $header;  # use colors if temporary file
  506 
  507   #########################################################################
  508   # Format the page
  509   #########################################################################
  510 
  511   # Define parameters for textarea
  512   # FIXME
  513   # Should the seed be set from some particular user instance??
  514   my $rows            = 20;
  515   my $columns         = 80;
  516   my $mode_list       = $ce->{pg}->{displayModes};
  517   my $displayMode     = $self->{displayMode};
  518   my $problemSeed     = $self->{problemSeed};
  519   my $uri             = $r->uri;
  520   my $edit_level      = $r->param('edit_level') || 0;
  521 
  522   my $force_field = (defined($self->{sourceFilePath}) and $self->{sourceFilePath} ne "") ?
  523     CGI::hidden(-name=>'sourceFilePath',
  524                 -default=>$self->{sourceFilePath}) : '';
  525 
  526   my @allSetNames = sort $db->listGlobalSets;
  527   for (my $j=0; $j<scalar(@allSetNames); $j++) {
  528     $allSetNames[$j] =~ s|^set||;
  529     $allSetNames[$j] =~ s|\.def||;
  530   }
  531   my $target = 'WW_View'; #"problem$edit_level"; # increasing edit_level gives you a new window with each edit.
  532 
  533   print CGI::script(<<EOF);
  534     function setTarget(inWindow) {
  535       document.getElementById("newWindow").checked = inWindow;
  536       updateTarget();
  537     }
  538     function updateTarget() {
  539       var inWindow = document.getElementById("newWindow").checked;
  540       document.getElementById("editor").target = (inWindow? "WW_View": "");
  541     }
  542     function setRadio(i,nw) {
  543       document.getElementById('action'+i).checked = true;
  544       setTarget(nw);
  545     }
  546 EOF
  547 
  548   print CGI::p($header),
  549 
  550     CGI::start_form({method=>"POST", id=>"editor", name=>"editor", action=>"$uri", enctype=>"application/x-www-form-urlencoded"}),
  551 
  552     $self->hidden_authen_fields,
  553     $force_field,
  554     CGI::hidden(-name=>'file_type',-default=>$self->{file_type}),
  555     CGI::div(" | ",
  556       CGI::a({-href=>'http://webwork.math.rochester.edu/docs/docs/pglanguage/manpages/',-target=>"manpage_window"},
  557         '&nbsp;Manpages&nbsp;',
  558       )," | ",
  559       CGI::a({-href=>'http://devel.webwork.rochester.edu/twiki/bin/view/Webwork/PGmacrosByFile',-target=>"manpage_window"},
  560         '&nbsp;macro list&nbsp;',
  561       )," | ",
  562       CGI::a({-href=>'http://devel.webwork.rochester.edu/doc/cvs/pg_HEAD/',-target=>"manpage_window"},
  563         '&nbsp;pod docs&nbsp;',
  564       )," | ",
  565       CGI::a({-href=>$BUGZILLA,-target=>"bugs_window"},
  566         '&nbsp;report problem bugs&nbsp;',
  567       )," | ",
  568     ),
  569     CGI::p(
  570       CGI::textarea(
  571         -name => 'problemContents', -default => $problemContents,
  572         -rows => $rows, -columns => $columns, -override => 1,
  573       ),
  574     );
  575 
  576 
  577 
  578 ######### print action forms
  579 
  580       print CGI::start_table({});
  581       #print CGI::Tr({}, CGI::td({-colspan=>2}, "Select an action to perform:"));
  582 
  583       my @formsToShow = @{ ACTION_FORMS() };
  584       my $default_choice = $formsToShow[0];
  585       my $i = 0;
  586       foreach my $actionID (@formsToShow) {
  587         # Check permissions
  588         #next if FORM_PERMS()->{$actionID} and not $authz->hasPermissions($user, FORM_PERMS()->{$actionID});
  589         my $actionForm = "${actionID}_form";
  590         my $newWindow = ($actionID =~ m/^(view|add_problem|save)$/)? 1: 0;
  591         my $onChange = "setRadio($i,$newWindow)";
  592         my %actionParams = $self->getActionParams($actionID);
  593         my $line_contents = $self->$actionForm($onChange, %actionParams);
  594         my $radio_params = {-type=>"radio", -name=>"action", -value=>$actionID};
  595         $radio_params->{checked}=1 if ($actionID eq $default_choice) ;
  596         $radio_params->{onclick} = "setTarget($newWindow)";
  597         $radio_params->{id} = "action$i";
  598         print CGI::Tr({-valign=>"top"},
  599           CGI::td({}, CGI::input($radio_params)),
  600           CGI::td({}, $line_contents)
  601         ) if $line_contents;
  602 
  603         $i++;
  604       }
  605       my $checkbox = CGI::input({-type=>"checkbox", -id=>"newWindow", -checked=>"checked",
  606                -onchange=>"updateTarget()"});
  607       $checkbox =~ s/\n//; # remove unwanted linebreak
  608       print CGI::Tr({}, CGI::td({-colspan=>2}, "Select above then:",
  609                         CGI::submit(-name=>'submit', -value=>"Take Action!"),
  610             CGI::script("document.write('$checkbox in another window')")));
  611       print CGI::end_table();
  612 
  613 
  614   print  CGI::end_form();
  615 
  616   print CGI::script("updateTarget()");
  617   return "";
  618 
  619 
  620 }
  621 
  622 #
  623 #  Convert long paths to [TMPL], etc.
  624 #
  625 sub shortPath {
  626   my $self = shift; my $file = shift;
  627   my $tmpl = $self->r->ce->{courseDirs}{templates};
  628   my $root = $self->r->ce->{courseDirs}{root};
  629   my $ww = $self->r->ce->{webworkDirs}{root};
  630   $file =~ s|^$tmpl|[TMPL]|; $file =~ s|^$root|[COURSE]|; $file =~ s|^$ww|[WW]|;
  631   return $file;
  632 }
  633 
  634 ################################################################################
  635 # Utilities
  636 ################################################################################
  637 
  638 # determineLocalFilePath   constructs a local file path parallel to a library file path
  639 # This is a subroutine, not a method
  640 #
  641 sub determineLocalFilePath {
  642   my $self= shift;        die "determineLocalFilePath is a method" unless ref($self);
  643   my $path = shift;
  644   my $default_screen_header_path   = $self->r->ce->{webworkFiles}->{hardcopySnippets}->{setHeader};
  645   my $default_hardcopy_header_path = $self->r->ce->{webworkFiles}->{screenSnippets}->{setHeader};
  646   my $setID = $self->{setID};
  647   $setID = int(rand(1000)) unless $setID =~/\S/;  # setID can be 0
  648   if ($path =~ /Library/) {
  649     $path =~ s|^.*?Library/||;  # truncate the url up to a segment such as ...rochesterLibrary/.......
  650   } elsif ($path eq $default_screen_header_path) {
  651     $path = "set$setID/setHeader.pg";
  652   } elsif ($path eq $default_hardcopy_header_path) {
  653     $path = "set$setID/hardcopyHeader.tex";
  654   } else { # if its not in a library we'll just save it locally
  655     $path = "new_problem_".int(rand(1000)).".pg"; #l hope there aren't any collisions.
  656   }
  657     $path;
  658 
  659 }
  660 
  661 sub determineTempEditFilePath {  # this does not create the directories in the path to the file
  662                                  # it  returns an absolute path to the file
  663   my $self = shift;  die "determineTempEditFilePath is a method" unless ref($self);
  664   my $path =shift;    # this should be an absolute path to the file
  665   my $user = $self->r->param("user");
  666   $user    = int(rand(1000)) unless defined $user;
  667   my $setID = $self->{setID} || int(rand(1000));
  668   my $courseDirectory = $self->r->ce->{courseDirs};
  669   ###############
  670   # Calculate the location of the temporary file
  671   ###############
  672   my $templatesDirectory           = $courseDirectory->{templates};
  673   my $blank_file_path              = $self->r->ce->{webworkFiles}->{screenSnippets}->{blankProblem};
  674   my $default_screen_header_path   = $self->r->ce->{webworkFiles}->{hardcopySnippets}->{setHeader};
  675   my $default_hardcopy_header_path = $self->r->ce->{webworkFiles}->{screenSnippets}->{setHeader};
  676   my $tmpEditFileDirectory = $self->getTempEditFileDirectory();
  677   $self->addbadmessage("The path to the original file should be absolute") unless $path =~m|^/|;  # debug
  678   if ($path =~/^$tmpEditFileDirectory/) {
  679     $self->addbadmessage("Error: This path is already in the temporary edit directory -- no new temporary file is created. path = $path");
  680 
  681   } else {
  682     if ($path =~ /^$templatesDirectory/ ) {
  683       $path =~ s|^$templatesDirectory||;
  684       $path =~ s|^/||;   # remove the initial slash if any
  685       $path = "$tmpEditFileDirectory/$path.$user.tmp";
  686     } elsif ($path eq $blank_file_path) {
  687       $path = "$tmpEditFileDirectory/blank.$setID.$user.tmp";  # handle the case of the blank problem
  688     } elsif ($path eq $default_screen_header_path) {
  689       $path = "$tmpEditFileDirectory/screenHeader.$setID.$user.tmp";  # handle the case of the screen header in snippets
  690     } elsif ($path eq $default_hardcopy_header_path) {
  691       $path = "$tmpEditFileDirectory/hardcopyHeader.$setID.$user.tmp";  # handle the case of the hardcopy header in snippets
  692     } else {
  693       die "determineTempEditFilePath should only be used on paths within the templates directory, not on $path";
  694     }
  695   }
  696   $path;
  697 }
  698 sub determineOriginalEditFilePath {  # determine the original path to a file corresponding to a temporary edit file
  699                                      # returns path relative to the template directory
  700   my $self = shift;
  701   my $path = shift;
  702   my $user = $self->r->param("user");
  703   $self->addbadmessage("Can't determine user of temporary edit file $path.") unless defined($user);
  704   my $templatesDirectory = $self->r->ce->{courseDirs} ->{templates};
  705   my $tmpEditFileDirectory = $self->getTempEditFileDirectory();
  706   # unless path is absolute assume that it is relative to the template directory
  707   my $newpath = $path;
  708   unless ($path =~ m|^/| ) {
  709     $newpath = "$templatesDirectory/$path";
  710   }
  711   if ($self->isTempEditFilePath($newpath) ) {
  712     $newpath =~ s|^$tmpEditFileDirectory/||; # delete temp edit directory
  713     if ($newpath =~m|blank\.[^/]*$|) {                     # handle the case of the blank problem
  714       $newpath = $self->r->ce->{webworkFiles}->{screenSnippets}->{blankProblem};
  715     } elsif (($newpath =~m|hardcopyHeader\.[^/]*$|)) {     # handle the case of the hardcopy header in snippets
  716       $newpath = $self->r->ce->{webworkFiles}->{hardcopySnippets}->{setHeader};
  717     } elsif (($newpath =~m|screenHeader\.[^/]*$|)) {       # handle the case of the screen header in snippets
  718       $newpath = $self->r->ce->{webworkFiles}->{screenSnippets}->{setHeader};
  719     } else {
  720       $newpath =~ s|\.$user\.tmp$||;           # delete suffix
  721 
  722     }
  723     #$self->addgoodmessage("Original file path is $newpath"); #FIXME debug
  724   } else {
  725     $self->addbadmessage("This path |$newpath| is not the path to a temporary edit file.");
  726     # returns original path
  727   }
  728   $newpath;
  729 }
  730 
  731 sub getTempEditFileDirectory {
  732   my $self = shift;
  733   my $courseDirectory       = $self->r->ce->{courseDirs};
  734   my $templatesDirectory    = $courseDirectory->{templates};
  735   my $tmpEditFileDirectory  = (defined ($courseDirectory->{tmpEditFileDir}) ) ? $courseDirectory->{tmpEditFileDir} : "$templatesDirectory/tmpEdit";
  736   $tmpEditFileDirectory;
  737 }
  738 sub isTempEditFilePath  {
  739   my $self = shift;
  740   my $path = shift;
  741   my $templatesDirectory = $self->r->ce->{courseDirs} ->{templates};
  742   # unless path is absolute assume that it is relative to the template directory
  743   unless ($path =~ m|^/| ) {
  744     $path = "$templatesDirectory/$path";
  745   }
  746   my $tmpEditFileDirectory = $self->getTempEditFileDirectory();
  747   ($path =~/^$tmpEditFileDirectory/) ? 1: 0;
  748 }
  749 sub getFilePaths {
  750   my ($self, $setName, $problemNumber, $file_type) = @_;
  751   my $r = $self->r;
  752   my $ce = $r->ce;
  753   my $db = $r->db;
  754   my $urlpath = $r->urlpath;
  755   my $courseName = $urlpath->arg("courseID");
  756   my $user = $r->param('user');
  757   my $effectiveUserName = $r->param('effectiveUser');
  758 
  759   $setName = '' unless defined $setName;
  760   $problemNumber = '' unless defined $problemNumber;
  761   die 'Internal error to PGProblemEditor -- file type is not defined'  unless defined $file_type;
  762   #$self->addgoodmessage("file type is $file_type");  #FIXME remove
  763   ##########################################################
  764   # Determine path to the input file to be edited.
  765   #   The permanent path of the input file  == $editFilePath
  766   #   A temporary path to the input file    == $tempFilePath
  767   ##########################################################
  768   # Relevant parameters
  769   #     $r->param("displayMode")
  770   #     $r->param('problemSeed')
  771   #     $r->param('submit')
  772   #     $r->param('make_local_copy')
  773   #     $r->param('sourceFilePath')
  774   #     $r->param('problemContents')
  775   #     $r->param('save_to_new_file')
  776   ##########################################################################
  777   # Define the following  variables
  778   #   path to regular file -- $editFilePath;
  779   #     path to file being read (temporary or permanent)
  780   #       contents of the file being read  --- $problemContents
  781   #       $self->{r_problemContents}        =   \$problemContents;
  782     ###########################################################################
  783 
  784   my $editFilePath = $ce->{courseDirs}->{templates};
  785 
  786     ##########################################################################
  787     # Determine path to regular file, place it in $editFilePath
  788     # problemSeed is defined for the file_type = 'problem' and 'source_path_to_problem'
  789     ##########################################################################
  790   CASE:
  791   {
  792     ($file_type eq 'course_info') and do {
  793       # we are editing the course_info file
  794       # value of courseFiles::course_info is relative to templates directory
  795       $editFilePath           .= '/' . $ce->{courseFiles}->{course_info};
  796       last CASE;
  797     };
  798 
  799     ($file_type eq 'options_info') and do {
  800       # we are editing the options_info file
  801       # value of courseFiles::options_info is relative to templates directory
  802       $editFilePath           .= '/' . $ce->{courseFiles}->{options_info};
  803       last CASE;
  804     };
  805 
  806     ($file_type eq 'blank_problem') and do {
  807       $editFilePath = $ce->{webworkFiles}->{screenSnippets}->{blankProblem};
  808       $self->addbadmessage("'".$self->shortPath($editFilePath)."' is blank problem template file and can not be edited directly. "
  809                            ."First use 'Save as' to make a local copy, then add the file to the current problem set, then edit the file."
  810       );
  811       last CASE;
  812     };
  813 
  814     ($file_type eq 'set_header' or $file_type eq 'hardcopy_header') and do {
  815       # first try getting the merged set for the effective user
  816       my $set_record = $db->getMergedSet($effectiveUserName, $setName); # checked
  817       # if that doesn't work (the set is not yet assigned), get the global record
  818       $set_record = $db->getGlobalSet($setName); # checked
  819       # bail if no set is found
  820       die "Cannot find a set record for set $setName" unless defined($set_record);
  821 
  822       my $header_file = "";
  823       $header_file = $set_record->{$file_type};
  824       if ($header_file && $header_file ne "") {
  825           if ( $header_file =~ m|^/| ) { # if absolute address
  826             $editFilePath  = $header_file;
  827           } else {
  828             $editFilePath .= '/' . $header_file;
  829           }
  830       } else {
  831           # if the set record doesn't specify the filename for a header
  832           # then the set uses the default from snippets
  833 
  834             $editFilePath = $ce->{webworkFiles}->{screenSnippets}->{setHeader} if $file_type eq 'set_header';
  835             $editFilePath = $ce->{webworkFiles}->{hardcopySnippets}->{setHeader} if $file_type eq 'hardcopy_header';
  836 
  837 #           $self->addbadmessage("'".$self->shortPath($editFilePath)."' is the default header file and cannot be edited directly.".CGI::br()."Any changes you make will have to be saved as another file.");
  838           #}
  839 
  840       }
  841       last CASE;
  842     }; #end 'set_header, hardcopy_header' case
  843 
  844     ($file_type eq 'problem') and do {
  845 
  846       # first try getting the merged problem for the effective user
  847       my $problem_record = $db->getMergedProblem($effectiveUserName, $setName, $problemNumber); # checked
  848 
  849       # if that doesn't work (the problem is not yet assigned), get the global record
  850       $problem_record = $db->getGlobalProblem($setName, $problemNumber) unless defined($problem_record); # checked
  851       # bail if no source path for the problem is found ;
  852         die "Cannot find a problem record for set $setName / problem $problemNumber" unless defined($problem_record);
  853       $editFilePath .= '/' . $problem_record->source_file;
  854       # define the problem seed for later use
  855       $self->{problemSeed}= $problem_record->problem_seed if  defined($problem_record) and  $problem_record->can('problem_seed') ;
  856       last CASE;
  857     };  # end 'problem' case
  858 
  859     ($file_type eq 'source_path_for_problem_file') and do {
  860       my $forcedSourceFile = $self->{sourceFilePath};
  861       # if the source file is in the temporary edit directory find the original source file
  862       # the source file is relative to the templates directory.
  863       if ($self->isTempEditFilePath($forcedSourceFile) ) {
  864           $forcedSourceFile   = $self->determineOriginalEditFilePath($forcedSourceFile);     # original file path
  865           #$self->addgoodmessage("the original path to the file is $forcedSourceFile");  #FIXME debug
  866       }
  867       # bail if no source path for the problem is found ;
  868       die "Cannot find a file path to save to" unless( defined($forcedSourceFile) and ($forcedSourceFile =~ /\S/)  );
  869       $self->{problemSeed} = 1234;
  870       $editFilePath .= '/' . $forcedSourceFile;
  871       last CASE;
  872     }; # end 'source_path_for_problem_file' case
  873   }  # end CASE: statement
  874 
  875 
  876   # if a set record or problem record contains an empty blank for a header or problem source_file
  877   # we could find ourselves trying to edit /blah/templates/.toenail.tmp or something similar
  878   # which is almost undoubtedly NOT desirable
  879 
  880   if (-d $editFilePath) {
  881     my $msg = "The file '".$self->shortPath($editFilePath)."' is a directory!";
  882     $self->{failure} = 1;
  883     $self->addbadmessage($msg);
  884   }
  885   if (-e $editFilePath and not -r $editFilePath) {   #it's ok if the file doesn't exist, perhaps we're going to create it
  886                                                     # with save as
  887     my $msg = "The file '".$self->shortPath($editFilePath)."' cannot be read!";
  888     $self->{failure} = 1;
  889     $self->addbadmessage($msg);
  890   }
  891     #################################################
  892   # The path to the permanent file is now verified and stored in $editFilePath
  893   # Whew!!!
  894   #################################################
  895 
  896   my $tempFilePath = $self->determineTempEditFilePath($editFilePath);  #"$editFilePath.$TEMPFILESUFFIX";
  897   $self->{editFilePath}   = $editFilePath;
  898   $self->{tempFilePath}   = $tempFilePath;
  899   $self->{inputFilePath}  = (-r $tempFilePath) ? $tempFilePath : $editFilePath;
  900   #warn "editfile path is $editFilePath and tempFile is $tempFilePath and inputFilePath is ". $self->{inputFilePath};
  901 }
  902 sub saveFileChanges {
  903 
  904 ################################################################################
  905 # saveFileChanges does most of the work. it is a separate method so that it can
  906 # be called from either pre_header_initialize() or initilize(), depending on
  907 # whether a redirect is needed or not.
  908 #
  909 # it actually does a lot more than save changes to the file being edited, and
  910 # sometimes less.
  911 ################################################################################
  912 
  913   my ($self, $outputFilePath, $problemContents ) = @_;
  914   my $r             = $self->r;
  915   my $ce            = $r->ce;
  916 
  917   my $action        = $self->{action}||'no action';
  918   my $editFilePath  = $self->{editFilePath};
  919   my $tempFilePath  = $self->{tempFilePath};
  920 
  921   if (defined($problemContents) and ref($problemContents) ) {
  922     $problemContents = ${$problemContents};
  923   } elsif( not defined($problemContents) or $problemContents =~/\S/ ) {
  924     $problemContents = ${$self->{r_problemContents}};
  925   }
  926   ##############################################################################
  927   # read and update the targetFile and targetFile.tmp files in the directory
  928   # if a .tmp file already exists use that, unless the revert button has been pressed.
  929   # The .tmp files are removed when the file is or when the revert occurs.
  930   ##############################################################################
  931 
  932 
  933     unless (defined($outputFilePath) and $outputFilePath =~/\S/ ) {
  934       $self->addbadmessage("You must specify an file name in order to save a new file.");
  935       return "";
  936     }
  937   my $do_not_save    = 0 ;       # flag to prevent saving of file
  938   my $editErrors = '';
  939 
  940   ##############################################################################
  941   # write changes to the approriate files
  942   # FIXME  make sure that the permissions are set correctly!!!
  943   # Make sure that the warning is being transmitted properly.
  944   ##############################################################################
  945 
  946   my $writeFileErrors;
  947   if ( defined($outputFilePath) and $outputFilePath =~/\S/  ) {   # save file
  948       # Handle the problem of line endings.
  949     # Make sure that all of the line endings are of unix type.
  950     # Convert \r\n to \n
  951     #$problemContents =~ s/\r\n/\n/g;
  952     #$problemContents =~ s/\r/\n/g;
  953 
  954     # make sure any missing directories are created
  955     WeBWorK::Utils::surePathToFile($ce->{courseDirs}->{templates},
  956                                                             $outputFilePath);
  957     die "outputFilePath is unsafe!" unless path_is_subdir($outputFilePath, $ce->{courseDirs}->{templates}, 1); # 1==path can be relative to dir
  958 
  959     eval {
  960       local *OUTPUTFILE;
  961       open OUTPUTFILE,  ">$outputFilePath"
  962           or die "Failed to open $outputFilePath";
  963       print OUTPUTFILE $problemContents;
  964       close OUTPUTFILE;
  965       # any errors are caught in the next block
  966     };
  967 
  968     $writeFileErrors = $@ if $@;
  969   }
  970 
  971   ###########################################################
  972   # Catch errors in saving files,  clean up temp files
  973   ###########################################################
  974 
  975   $self->{saveError} = $do_not_save;    # don't do redirects if the file was not saved.
  976                                       # don't unlink files or send success messages
  977 
  978   if ($writeFileErrors) {
  979       # get the current directory from the outputFilePath
  980     $outputFilePath =~ m|^(/.*?/)[^/]+$|;
  981     my $currentDirectory = $1;
  982 
  983     my $errorMessage;
  984     # check why we failed to give better error messages
  985     if ( not -w $ce->{courseDirs}->{templates} ) {
  986       $errorMessage = "Write permissions have not been enabled in the templates directory.  No changes can be made.";
  987     } elsif ( not -w $currentDirectory ) {
  988       $errorMessage = "Write permissions have not been enabled in '".$self->shortPath($currentDirectory)."'.  Changes must be saved to a different directory for viewing.";
  989     } elsif ( -e $outputFilePath and not -w $outputFilePath ) {
  990       $errorMessage = "Write permissions have not been enabled for '".$self->shortPath($outputFilePath)."'.  Changes must be saved to another file for viewing.";
  991     } else {
  992       $errorMessage = "Unable to write to '".$self->shortPath($outputFilePath)."': $writeFileErrors";
  993     }
  994 
  995     $self->{failure} = 1;
  996     $self->addbadmessage(CGI::p($errorMessage));
  997 
  998   }
  999   ###########################################################
 1000   # clean up temp files on revert, save and save_as
 1001   ###########################################################
 1002   unless( $writeFileErrors or $do_not_save) {  # everything worked!  unlink and announce success!
 1003     # unlink the temporary file if there are no errors and the save button has been pushed
 1004     if (($action eq 'save' or $action eq 'save_as') and (-w $self->{tempFilePath})  ) {
 1005 
 1006                  $self->addgoodmessage("Deleting temp file at " . $self->shortPath($self->{tempFilePath}));
 1007                  die "tempFilePath is unsafe!" unless path_is_subdir($self->{tempFilePath}, $ce->{courseDirs}->{templates}, 1); # 1==path can be relative to dir
 1008                  unlink($self->{tempFilePath}) ;
 1009     }
 1010 
 1011     if ( defined($outputFilePath) and ! $self->{failure} and not $self->isTempEditFilePath($outputFilePath) ) {
 1012                 # don't announce saving of temporary editing files
 1013       my $msg = "Saved to file '".$self->shortPath($outputFilePath)."'.";
 1014 
 1015       $self->addgoodmessage($msg);
 1016       #$self->{inputFilePath} = $outputFilePath; ## DPVC -- avoid file-not-found message
 1017     }
 1018 
 1019   }
 1020 
 1021 
 1022 }  # end saveFileChanges
 1023 
 1024 
 1025 
 1026 
 1027 
 1028 sub getActionParams {
 1029   my ($self, $actionID) = @_;
 1030   my $r = $self->{r};
 1031 
 1032   my %actionParams=();
 1033   foreach my $param ($r->param) {
 1034     next unless $param =~ m/^action\.$actionID\./;
 1035     $actionParams{$param} = [ $r->param($param) ];
 1036   }
 1037   return %actionParams;
 1038 }
 1039 
 1040 sub fixProblemContents {
 1041     #NOT a method
 1042     my $problemContents = shift;
 1043     # Handle the problem of line endings.
 1044     # Make sure that all of the line endings are of unix type.
 1045     # Convert \r\n to \n
 1046     $problemContents =~ s/\r\n/\n/g;
 1047     $problemContents =~ s/\r/\n/g;
 1048     $problemContents;
 1049 }
 1050 
 1051 sub fresh_edit_handler {
 1052   my ($self, $genericParams, $actionParams, $tableParams) = @_;
 1053   #$self->addgoodmessage("fresh_edit_handler called");
 1054 }
 1055 sub view_form {
 1056   my ($self, $onChange, %actionParams) = @_;
 1057   my $file_type     = $self->{file_type};
 1058   return "" if    $file_type eq 'hardcopy_header';  # these can't yet be edited from temporary files #FIXME
 1059   my $output_string = "View";
 1060   unless ($file_type eq 'course_info' || $file_type eq 'options_info') {
 1061     $output_string .= join(" ",
 1062       " using seed ",
 1063       CGI::textfield(-name=>'action.view.seed',-value=>$self->{problemSeed},-onfocus=>$onChange),
 1064       "and display mode ",
 1065       CGI::popup_menu(-name=>'action.view.displayMode', -values=>$self->r->ce->{pg}->{displayModes},
 1066         -default=>$self->{displayMode}, -onmousedown=>$onChange)
 1067     );
 1068   }
 1069 
 1070   return $output_string;  #FIXME  add -lables to the pop up menu
 1071 }
 1072 
 1073 sub view_handler {
 1074   my ($self, $genericParams, $actionParams, $tableParams) = @_;
 1075   my $courseName      =  $self->{courseID};
 1076   my $setName         =  $self->{setID};
 1077   my $problemNumber   =  $self->{problemID};
 1078   my $problemSeed     = ($actionParams->{'action.view.seed'}) ?
 1079                                   $actionParams->{'action.view.seed'}->[0]
 1080                                   : 1234;
 1081   my $displayMode     = ($actionParams->{'action.view.displayMode'}) ?
 1082                                   $actionParams->{'action.view.displayMode'}->[0]
 1083                                   : $self->r->ce->{pg}->{options}->{displayMode};
 1084 
 1085   my $editFilePath        = $self->{editFilePath};
 1086   my $tempFilePath        = $self->{tempFilePath};
 1087   ########################################################
 1088   # grab the problemContents from the form in order to save it to the tmp file
 1089   ########################################################
 1090   my $problemContents     = fixProblemContents($self->r->param('problemContents'));
 1091   $self->{r_problemContents}    = \$problemContents;
 1092 
 1093 
 1094   my $do_not_save = 0;
 1095   my $file_type = $self->{file_type};
 1096   $self->saveFileChanges($tempFilePath,);
 1097 
 1098   ########################################################
 1099   # construct redirect URL and redirect
 1100   ########################################################
 1101   my $edit_level = $self->r->param("edit_level") || 0;
 1102   $edit_level++;
 1103   my $viewURL;
 1104 
 1105   if ($file_type eq 'problem' or $file_type eq 'source_path_for_problem_file') { # redirect to Problem.pm
 1106     my $problemPage = $self->r->urlpath->newFromModule("WeBWorK::ContentGenerator::Problem",
 1107       courseID => $courseName, setID => $setName, problemID => $problemNumber
 1108     );
 1109 
 1110     $viewURL = $self->systemLink($problemPage,
 1111       params => {
 1112         displayMode        => $displayMode,
 1113         problemSeed        => $problemSeed,
 1114         editMode           => "temporaryFile",
 1115         edit_level         => $edit_level,
 1116         sourceFilePath     => $tempFilePath,
 1117         status_message     => uri_escape($self->{status_message})
 1118 
 1119       }
 1120     );
 1121   } elsif ($file_type eq 'set_header' ) { # redirect to ProblemSet
 1122     my $problemPage = $self->r->urlpath->newFromModule("WeBWorK::ContentGenerator::ProblemSet",
 1123       courseID => $courseName, setID => $setName,
 1124     );
 1125 
 1126     $viewURL = $self->systemLink($problemPage,
 1127       params => {
 1128         set_header         => $tempFilePath,
 1129         displayMode        => $displayMode,
 1130         problemSeed        => $problemSeed,
 1131         editMode           => "temporaryFile",
 1132         edit_level         => $edit_level,
 1133         sourceFilePath     => $tempFilePath,
 1134         status_message     => uri_escape($self->{status_message})
 1135 
 1136       }
 1137     );
 1138   } elsif ($file_type eq 'hardcopy_header') { # redirect to ProblemSet?? # it's difficult to view temporary changes for hardcopy headers
 1139     my $problemPage = $self->r->urlpath->newFromModule("WeBWorK::ContentGenerator::ProblemSet",
 1140       courseID => $courseName, setID => $setName,
 1141     );
 1142 
 1143     $viewURL = $self->systemLink($problemPage,
 1144       params => {
 1145         set_header         => $tempFilePath,
 1146         displayMode        => $displayMode,
 1147         problemSeed        => $problemSeed,
 1148         editMode           => "temporaryFile",
 1149         edit_level         => $edit_level,
 1150         sourceFilePath     => $tempFilePath,
 1151         status_message     => uri_escape($self->{status_message})
 1152 
 1153       }
 1154     );
 1155 
 1156   } elsif ($file_type eq 'course_info') {  # redirec to ProblemSets.pm
 1157     my $problemSetsPage = $self->r->urlpath->newFromModule("WeBWorK::ContentGenerator::ProblemSets",
 1158       courseID => $courseName);
 1159     $viewURL = $self->systemLink($problemSetsPage,
 1160       params => {
 1161         course_info        => $tempFilePath,
 1162         editMode           => "temporaryFile",
 1163         edit_level         => $edit_level,
 1164         sourceFilePath     => $tempFilePath,
 1165         status_message     => uri_escape($self->{status_message})
 1166       }
 1167     );
 1168   } elsif ($file_type eq 'options_info') {  # redirec to Options.pm
 1169     my $optionsPage = $self->r->urlpath->newFromModule("WeBWorK::ContentGenerator::Options",
 1170       courseID => $courseName);
 1171     $viewURL = $self->systemLink($optionsPage,
 1172       params => {
 1173         options_info       => $tempFilePath,
 1174         editMode           => "temporaryFile",
 1175         edit_level         => $edit_level,
 1176         sourceFilePath     => $tempFilePath,
 1177         status_message     => uri_escape($self->{status_message})
 1178       }
 1179     );
 1180   } else {
 1181     die "I don't know how to redirect this file type $file_type ";
 1182   }
 1183 
 1184   $self->reply_with_redirect($viewURL);
 1185 }
 1186 
 1187 sub add_problem_form {
 1188    my $self            = shift;
 1189   my ($onChange, %actionParams) = @_;
 1190   my $r               = $self->r;
 1191   my $setName         = $self->{setID} ;
 1192   my $problemNumber   = $self->{problemID} ;
 1193     $setName            = defined($setName) ? $setName : '';  # we need this instead of using the || construction
 1194                                                               # to keep set 0 from being set to the
 1195                                                               # empty string.
 1196     my $filePath        = $self->{inputFilePath};
 1197   $setName   =~ s|^set||;
 1198   my @allSetNames = sort $r->db->listGlobalSets;
 1199   for (my $j=0; $j<scalar(@allSetNames); $j++) {
 1200     $allSetNames[$j] =~ s|^set||;
 1201     $allSetNames[$j] =~ s|\.def||;
 1202   }
 1203   my $labels = {
 1204     problem         => 'problem',
 1205     set_header      => 'set header',
 1206     hardcopy_header => 'hardcopy header',
 1207   };
 1208   return "" if $self->{file_type} eq 'course_info' || $self->{file_type} eq 'options_info';
 1209   return join(" ",
 1210     "Add to set " ,
 1211     CGI::popup_menu({name=>'action.add_problem.target_set', values=>\@allSetNames, default=>$setName, onmousedown=>$onChange}),
 1212     " as ",
 1213     CGI::popup_menu({name=>'action.add_problem.file_type', values=>['problem','set_header', 'hardcopy_header'], labels=>$labels, default=>$self->{file_type}, onmousedown=>$onChange}),
 1214 
 1215   );  #FIXME  add -lables to the pop up menu
 1216   return "";
 1217 }
 1218 
 1219 sub add_problem_handler {
 1220   my ($self, $genericParams, $actionParams, $tableParams) = @_;
 1221   #$self->addgoodmessage("add_problem_handler called");
 1222   my $courseName      =  $self->{courseID};
 1223   my $setName         =  $self->{setID};
 1224   my $problemNumber   =  $self->{problemID};
 1225   my $sourceFilePath  =  $self->{editFilePath};
 1226   my $displayMode     =  $self->{displayMode};
 1227   my $problemSeed     =  $self->{problemSeed};
 1228 
 1229   my $targetSetName         =  $actionParams->{'action.add_problem.target_set'}->[0];
 1230   my $targetFileType        =  $actionParams->{'action.add_problem.file_type'}->[0];
 1231   my $templatesPath         =  $self->r->ce->{courseDirs}->{templates};
 1232   $sourceFilePath    =~ s|^$templatesPath/||;
 1233 
 1234   my $edit_level = $self->r->param("edit_level") || 0;
 1235   $edit_level++;
 1236 
 1237   my $viewURL ='';
 1238   if ($targetFileType eq 'problem') {
 1239     my $targetProblemNumber   =  1+ WeBWorK::Utils::max( $self->r->db->listGlobalProblems($targetSetName));
 1240 
 1241     #################################################
 1242     # Update problem record
 1243     #################################################
 1244     my $problemRecord  = $self->addProblemToSet(
 1245                  setName        => $targetSetName,
 1246                  sourceFile     => $sourceFilePath,
 1247                  problemID      => $targetProblemNumber, #added to end of set
 1248     );
 1249     $self->assignProblemToAllSetUsers($problemRecord);
 1250     $self->addgoodmessage("Added $sourceFilePath to ". $targetSetName. " as problem $targetProblemNumber") ;
 1251     $self->{file_type}   = 'problem'; # change file type to problem -- if it's not already that
 1252 
 1253     #################################################
 1254     # Set up redirect Problem.pm
 1255     #################################################
 1256     my $problemPage = $self->r->urlpath->newFromModule("WeBWorK::ContentGenerator::Problem",
 1257       courseID  => $courseName,
 1258       setID     => $targetSetName,
 1259       problemID => $targetProblemNumber,
 1260     );
 1261     $viewURL = $self->systemLink($problemPage,
 1262         params => {
 1263           displayMode        => $displayMode,
 1264           problemSeed        => $problemSeed,
 1265           editMode           => "savedFile",
 1266           edit_level         => $edit_level,
 1267           sourceFilePath     => $sourceFilePath,
 1268           status_message     => uri_escape($self->{status_message})
 1269 
 1270         }
 1271     );
 1272   } elsif ($targetFileType eq 'set_header')  {
 1273     #################################################
 1274     # Update set record
 1275     #################################################
 1276     my $setRecord  = $self->r->db->getGlobalSet($targetSetName);
 1277     $setRecord->set_header($sourceFilePath);
 1278     if(  $self->r->db->putGlobalSet($setRecord) ) {
 1279       $self->addgoodmessage("Added '".$self->shortPath($sourceFilePath)."' to ". $targetSetName. " as new set header ") ;
 1280     } else {
 1281       $self->addbadmessage("Unable to make '".$self->shortPath($sourceFilePath)."' the set header for $targetSetName");
 1282     }
 1283     $self->{file_type} = 'set_header'; # change file type to set_header if it not already so
 1284     #################################################
 1285     # Set up redirect
 1286     #################################################
 1287     my $problemPage = $self->r->urlpath->newFromModule("WeBWorK::ContentGenerator::ProblemSet",
 1288       courseID => $courseName, setID => $targetSetName
 1289     );
 1290     $viewURL = $self->systemLink($problemPage,
 1291         params => {
 1292           displayMode        => $displayMode,
 1293           editMode           => "savedFile",
 1294           edit_level         => $edit_level,
 1295           status_message     => uri_escape($self->{status_message})
 1296         }
 1297     );
 1298   } else {
 1299     die "Don't know what to do with target file type $targetFileType";
 1300   }
 1301 
 1302   $self->reply_with_redirect($viewURL);
 1303 }
 1304 
 1305 
 1306 sub save_form {
 1307   my ($self, $onChange, %actionParams) = @_;
 1308   my $r => $self->r;
 1309   #return "" unless defined($self->{tempFilePath}) and -e $self->{tempFilePath};
 1310   if ($self->{editFilePath} =~ /$BLANKPROBLEM$/ ) {
 1311     return "";  #Can't save blank problems without changing names
 1312   } elsif (-w $self->{editFilePath}) {
 1313 
 1314     return "Save to: ".$self->shortPath($self->{editFilePath})." and View";
 1315 
 1316   } else {
 1317     return ""; #"Can't save -- No write permission";
 1318   }
 1319 
 1320 }
 1321 
 1322 sub save_handler {
 1323   my ($self, $genericParams, $actionParams, $tableParams) = @_;
 1324   #$self->addgoodmessage("save_handler called");
 1325   my $courseName      =  $self->{courseID};
 1326   my $setName         =  $self->{setID};
 1327   my $problemNumber   =  $self->{problemID};
 1328   my $displayMode     =  $self->{displayMode};
 1329   my $problemSeed     =  $self->{problemSeed};
 1330 
 1331   #################################################
 1332   # grab the problemContents from the form in order to save it to a new permanent file
 1333   # later we will unlink (delete) the current temporary file
 1334   #################################################
 1335   my $problemContents = fixProblemContents($self->r->param('problemContents'));
 1336   $self->{r_problemContents} = \$problemContents;
 1337 
 1338   #################################################
 1339   # Construct the output file path
 1340   #################################################
 1341   my $editFilePath        = $self->{editFilePath};
 1342   my $outputFilePath      = $editFilePath;
 1343 
 1344   my $do_not_save = 0;
 1345   my $file_type = $self->{file_type};
 1346   $self->saveFileChanges($outputFilePath);
 1347   #################################################
 1348   # Set up redirect to Problem.pm
 1349   #################################################
 1350   my $viewURL;
 1351   ########################################################
 1352   # construct redirect URL and redirect
 1353   ########################################################
 1354   if ($file_type eq 'problem' || $file_type eq 'source_path_for_problem_file') { # redirect to Problem.pm
 1355     my $problemPage = $self->r->urlpath->newFromModule("WeBWorK::ContentGenerator::Problem",
 1356       courseID => $courseName, setID => $setName, problemID => $problemNumber
 1357     );
 1358 
 1359     $viewURL = $self->systemLink($problemPage,
 1360       params => {
 1361         displayMode        => $displayMode,
 1362         problemSeed        => $problemSeed,
 1363         editMode           => "savedFile",
 1364         edit_level         => 0,
 1365         sourceFilePath     => $editFilePath,
 1366         status_message     => uri_escape($self->{status_message})
 1367 
 1368       }
 1369     );
 1370   } elsif ($file_type eq 'set_header' ) { # redirect to ProblemSet
 1371     my $problemPage = $self->r->urlpath->newFromModule("WeBWorK::ContentGenerator::ProblemSet",
 1372       courseID => $courseName, setID => $setName,
 1373     );
 1374 
 1375     $viewURL = $self->systemLink($problemPage,
 1376       params => {
 1377         displayMode        => $displayMode,
 1378         problemSeed        => $problemSeed,
 1379         editMode           => "savedFile",
 1380         edit_level         => 0,
 1381         status_message     => uri_escape($self->{status_message})
 1382 
 1383       }
 1384     );
 1385   } elsif ( $file_type eq 'hardcopy_header') { # redirect to ProblemSet
 1386     my $problemPage = $self->r->urlpath->newFromModule('WeBWorK::ContentGenerator::Hardcopy',
 1387       courseID => $courseName, setID => $setName,
 1388     );
 1389 
 1390     $viewURL = $self->systemLink($problemPage,
 1391       params => {
 1392         displayMode        => $displayMode,
 1393         problemSeed        => $problemSeed,
 1394         editMode           => "savedFile",
 1395         edit_level         => 0,
 1396         status_message     => uri_escape($self->{status_message})
 1397 
 1398       }
 1399     );
 1400 
 1401   } elsif ($file_type eq 'course_info') {  # redirect to ProblemSets.pm
 1402     my $problemSetsPage = $self->r->urlpath->newFromModule("WeBWorK::ContentGenerator::ProblemSets",
 1403       courseID => $courseName);
 1404     $viewURL = $self->systemLink($problemSetsPage,
 1405       params => {
 1406         editMode           => ("savedFile"),
 1407         edit_level         => 0,
 1408         status_message     => uri_escape($self->{status_message})
 1409       }
 1410     );
 1411   } elsif ($file_type eq 'options_info') {  # redirect to Options.pm
 1412     my $optionsPage = $self->r->urlpath->newFromModule("WeBWorK::ContentGenerator::Options",
 1413       courseID => $courseName);
 1414     $viewURL = $self->systemLink($optionsPage,
 1415       params => {
 1416         editMode           => ("savedFile"),
 1417         edit_level         => 0,
 1418         status_message     => uri_escape($self->{status_message})
 1419       }
 1420     );
 1421   } elsif ($file_type eq 'source_path_for_problem_file') {  # redirect to ProblemSets.pm
 1422     my $problemPage = $self->r->urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::PGProblemEditor",
 1423     courseID => $courseName, setID => $setName, problemID => $problemNumber
 1424     );
 1425     my $viewURL = $self->systemLink($problemPage,
 1426        params=>{
 1427           displayMode        => $displayMode,
 1428         problemSeed        => $problemSeed,
 1429         editMode           => "savedFile",
 1430         edit_level         => 0,
 1431         sourceFilePath     => $outputFilePath, #The path relative to the templates directory is required.
 1432         file_type          => 'source_path_for_problem_file',
 1433         status_message     => uri_escape($self->{status_message})
 1434 
 1435        }
 1436   );
 1437 
 1438   } else {
 1439     die "I don't know how to redirect this file type $file_type ";
 1440   }
 1441 
 1442   $self->reply_with_redirect($viewURL);
 1443 }
 1444 
 1445 
 1446 sub make_local_copy_form {
 1447   my ($self, $genericParams, $actionParams, $tableParams) = @_;
 1448   my $editFilePath    = $self->{editFilePath}; # path to the permanent file to be edited
 1449   #warn "editFilePath $editFilePath inputFilePath",$self->{inputFilePath};
 1450   return "" unless -e $editFilePath;
 1451   return "" if -w $editFilePath;
 1452   return "" unless    $self->{file_type} eq 'problem'           # need problem structure to make local copy in most cases
 1453                    or $self->{file_type} eq 'set_header'      # $editFilePath eq  $self->r->ce->{webworkFiles}->{hardcopySnippets}->{setHeader}   # special case to make copy of hardcopy header
 1454                    or $self->{file_type} eq 'hardcopy_header';  #  $editFilePath eq  $self->r->ce->{webworkFiles}->{screenSnippets}->{setHeader}   ;  # special case to make copy of screen header
 1455                    #  or $self->{file_type} eq 'source_path_for_problem_file'; # need setID and problemID to make local copy -- can't be done in this case.
 1456   return join ("",
 1457     "Make local editable copy at: [TMPL]/".($self->determineLocalFilePath($editFilePath)),
 1458     CGI::hidden(-name=>'action.make_local_copy.target_file', -value=>$self->determineLocalFilePath($editFilePath) ),
 1459     CGI::hidden(-name=>'action.make_local_copy.source_file', -value=>$editFilePath ),
 1460     CGI::hidden(-name=>'action.make_local_copy.file_type',-value=>$self->{file_type}),
 1461     CGI::hidden(-name=>'action.make_local_copy.saveMode',-value=>'rename')
 1462   );
 1463 
 1464 }
 1465 
 1466 
 1467 
 1468 
 1469 
 1470 
 1471 
 1472 
 1473 
 1474 
 1475 sub make_local_copy_handler {
 1476   my ($self, $genericParams, $actionParams, $tableParams) = @_;
 1477   foreach my $key (qw(target_file file_type saveMode source_file)) {
 1478     $actionParams->{"action.save_as.$key"}->[0] = $actionParams->{"action.make_local_copy.$key"}->[0];
 1479     #warn "action.make_local_copy.$key", @{$actionParams->{"action.make_local_copy.$key"}}
 1480   }
 1481     save_as_handler($self, $genericParams, $actionParams, $tableParams);
 1482 
 1483 
 1484 }
 1485 sub save_as_form {  # calls the save_as_handler
 1486   my ($self, $onChange, %actionParams) = @_;
 1487   my $editFilePath  = $self->{editFilePath};
 1488   return "" unless -w $editFilePath;
 1489 
 1490   my $shortFilePath =  $editFilePath;
 1491   my $templatesDir  =  $self->r->ce->{courseDirs}->{templates};
 1492   my $setID         = $self->{setID};
 1493   $shortFilePath   =~ s|^$templatesDir/||;
 1494   $shortFilePath  = '' if $shortFilePath =~ m|^/|;  # if it is still an absolute path don't suggest that you save to it.
 1495   my $allowedActions = (defined($setID) && $setID =~/\S/ && $setID ne 'Undefined_Set') ? ['save_a_copy','rename' ] : ['save_a_copy'];
 1496 
 1497   return CGI::popup_menu(
 1498              -name=>'action.save_as.saveMode', -values=>$allowedActions,
 1499              -default=>'rename',-labels=>{save_a_copy=>'Create a copy of file at ', rename=>'Rename file path to'},
 1500              -onmousedown=>$onChange
 1501             ). ": [TMPL]/".
 1502           CGI::textfield(
 1503              -name=>'action.save_as.target_file', -size=>40, -value=>$shortFilePath,
 1504              -onfocus=>$onChange
 1505             ).
 1506       CGI::hidden(-name=>'action.save_as.source_file', -value=>$editFilePath ).
 1507       CGI::hidden(-name=>'action.save_as.file_type',-value=>$self->{file_type}),
 1508       ;
 1509 #               if $self->{setID} && $self->{setID} ne '' && $self->{setID} ne 'Undefined_Set';
 1510 # FIXME  -- this should eventually work for undefined sets as well.
 1511 #   return CGI::popup_menu(-name=>'action.save_as.saveMode', -values=>['save_a_copy'],
 1512 #         -default=>'save_a_copy',-labels=>{save_a_copy=>'Save as'}, -onmousedown=>$onChange
 1513 #       ). ": [TMPL]/".CGI::textfield(-name=>'action.save_as.target_file', -size=>40, -value=>$shortFilePath)
 1514 }
 1515 
 1516 sub save_as_handler {
 1517   my ($self, $genericParams, $actionParams, $tableParams) = @_;
 1518   #$self->addgoodmessage("save_as_handler called");
 1519   $self->{status_message} = ''; ## DPVC -- remove bogus old messages
 1520   my $courseName      =  $self->{courseID};
 1521   my $setName         =  $self->{setID};
 1522   my $problemNumber   =  $self->{problemID};
 1523   my $displayMode     =  $self->{displayMode};
 1524   my $problemSeed     =  $self->{problemSeed};
 1525 
 1526   my $do_not_save = 0;
 1527   my $saveMode       = $actionParams->{'action.save_as.saveMode'}->[0] || '';
 1528   my $new_file_name  = $actionParams->{'action.save_as.target_file'}->[0] || '';
 1529   my $sourceFilePath = $actionParams->{'action.save_as.source_file'}->[0] || '';
 1530   my $file_type      = $actionParams->{'action.save_as.file_type'}->[0] || '';
 1531 
 1532   $new_file_name =~ s/^\s*//;  #remove initial and final white space
 1533   $new_file_name =~ s/\s*$//;
 1534   if ( $new_file_name !~ /\S/) { # need a non-blank file name
 1535     # setting $self->{failure} stops saving and any redirects
 1536     $do_not_save = 1;
 1537     $self->addbadmessage(CGI::p("Please specify a file to save to."));
 1538   }
 1539 
 1540   #################################################
 1541   # grab the problemContents from the form in order to save it to a new permanent file
 1542   # later we will unlink (delete) the current temporary file
 1543   #################################################
 1544   my $problemContents = fixProblemContents($self->r->param('problemContents'));
 1545   $self->{r_problemContents} = \$problemContents;
 1546   warn "problem contents is empty" unless $problemContents;
 1547   #################################################
 1548   # Rescue the user in case they forgot to end the file name with .pg
 1549   #################################################
 1550 
 1551   if($file_type eq 'problem'
 1552     or $file_type eq 'blank_problem'
 1553     or $file_type eq 'set_header') {
 1554       $new_file_name =~ s/\.pg$//; # remove it if it is there
 1555       $new_file_name .= '.pg'; # put it there
 1556 
 1557   }
 1558   #################################################
 1559   # Construct the output file path
 1560   #################################################
 1561   my $outputFilePath = $self->r->ce->{courseDirs}->{templates} . '/' .
 1562                  $new_file_name;
 1563   if (defined $outputFilePath and -e $outputFilePath) {
 1564     # setting $do_not_save stops saving and any redirects
 1565     $do_not_save = 1;
 1566     $self->addbadmessage(CGI::p("File '".$self->shortPath($outputFilePath)."' exists.
 1567     File not saved. No changes have been made.
 1568     You can change the file path for this problem manually from the 'Hmwk Sets Editor' page"));
 1569   } else {
 1570     $self->{editFilePath} = $outputFilePath;
 1571     $self->{tempFilePath} = ''; # nothing needs to be unlinked.
 1572     $self->{inputFilePath} = '';
 1573   }
 1574 
 1575 
 1576   unless ($do_not_save ) {
 1577     $self->saveFileChanges($outputFilePath);
 1578 
 1579     if ($saveMode eq 'rename' and -r $outputFilePath) {
 1580     #################################################
 1581     # Modify source file in problem
 1582     #################################################
 1583       if ($file_type eq 'set_header' ) {
 1584         my $setRecord = $self->r->db->getGlobalSet($setName);
 1585         $setRecord->set_header($new_file_name);
 1586         if ($self->r->db->putGlobalSet($setRecord)) {
 1587           $self->addgoodmessage("The set header for set $setName has been renamed to '".$self->shortPath($outputFilePath)."'.") ;
 1588         } else {
 1589           $self->addbadmessage("Unable to change the set header for set $setName. Unknown error.");
 1590         }
 1591       } elsif ($file_type eq 'hardcopy_header' ) {
 1592         my $setRecord = $self->r->db->getGlobalSet($setName);
 1593         $setRecord->hardcopy_header($new_file_name);
 1594         if ($self->r->db->putGlobalSet($setRecord)) {
 1595           $self->addgoodmessage("The hardcopy header for set $setName has been renamed to '".$self->shortPath($outputFilePath)."'.") ;
 1596         } else {
 1597           $self->addbadmessage("Unable to change the hardcopy header for set $setName. Unknown error.");
 1598         }
 1599       } else {
 1600         my $problemRecord = $self->r->db->getGlobalProblem($setName,$problemNumber);
 1601         $problemRecord->source_file($new_file_name);
 1602         if  ( $self->r->db->putGlobalProblem($problemRecord)  ) {
 1603           $self->addgoodmessage("The source file for 'set $setName / problem $problemNumber' has been changed from ".
 1604           $self->shortPath($sourceFilePath)." to '".$self->shortPath($outputFilePath)."'.") ;
 1605         } else {
 1606           $self->addbadmessage("Unable to change the source file path for set $setName, problem $problemNumber. Unknown error.");
 1607         }
 1608       }
 1609     } elsif ($saveMode eq 'save_a_copy') {
 1610     #################################################
 1611     # Don't modify source file in problem -- just report
 1612     #################################################
 1613 
 1614       #$self->{status_message} = '';  ## DPVC remove old messages
 1615       $self->addgoodmessage("A new file has been created at '".$self->shortPath($outputFilePath).
 1616       "' with the contents below. No changes have been made to set $setName.");
 1617     } else {
 1618       $self->addbadmessage("Don't recognize saveMode: |$saveMode|. Unknown error.");
 1619     }
 1620 
 1621   }
 1622   my $edit_level = $self->r->param("edit_level") || 0;
 1623   $edit_level++;
 1624 
 1625   #################################################
 1626   # Set up redirect
 1627   # The redirect gives the server time to detect that the new file exists.
 1628   #################################################
 1629   my $problemPage;
 1630   my $new_file_type;
 1631   if ($saveMode eq 'save_a_copy' ) {
 1632     $problemPage = $self->r->urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::PGProblemEditor",
 1633       courseID => $courseName, setID => 'Undefined_Set', problemID => 'Undefined_Set'
 1634     );
 1635     $new_file_type = 'source_path_for_problem_file';
 1636   } elsif ($saveMode eq 'rename') {
 1637     $problemPage = $self->r->urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::PGProblemEditor",
 1638       courseID => $courseName, setID => $setName, problemID => $problemNumber
 1639     );
 1640     $new_file_type = $file_type;
 1641   } else {
 1642     $self->addbadmessage("Don't recognize saveMode: |$saveMode|. Unknown error.");
 1643     die "Don't recognize saveMode: |$saveMode|. Unknown error."
 1644   }
 1645   #warn "save mode is $saveMode";
 1646   my $viewURL = $self->systemLink($problemPage,
 1647                  params=>{
 1648                    sourceFilePath     => $outputFilePath, #The path relative to the templates directory is required.
 1649                    edit_level         => $edit_level,
 1650                    file_type          => $new_file_type,
 1651                    status_message     => uri_escape($self->{status_message})
 1652 
 1653                  }
 1654   );
 1655 
 1656   $self->reply_with_redirect($viewURL);
 1657 return "";  # no redirect needed
 1658 }
 1659 sub revert_form {
 1660   my ($self, $onChange, %actionParams) = @_;
 1661   my $editFilePath    = $self->{editFilePath};
 1662   return "Error: The original file $editFilePath cannot be read." unless -r $editFilePath;
 1663   return "" unless defined($self->{tempFilePath}) and -e $self->{tempFilePath} ;
 1664   return "Revert to ".$self->shortPath($editFilePath) ;
 1665 
 1666 }
 1667 sub revert_handler {
 1668   my ($self, $genericParams, $actionParams, $tableParams) = @_;
 1669   my $ce = $self->ce;
 1670   #$self->addgoodmessage("revert_handler called");
 1671   my $editFilePath       = $self->{editFilePath};
 1672   $self->{inputFilePath} = $editFilePath;
 1673   # unlink the temp files;
 1674   die "tempFilePath is unsafe!" unless path_is_subdir($self->{tempFilePath}, $ce->{courseDirs}->{templates}, 1); # 1==path can be relative to dir
 1675   unlink($self->{tempFilePath});
 1676   $self->addgoodmessage("Deleting temp file at " . $self->shortPath($self->{tempFilePath}));
 1677   $self->{tempFilePath}  = '';
 1678   my $problemContents    ='';
 1679   $self->{r_problemContents} = \$problemContents;
 1680   $self->addgoodmessage("Reverting to original file '".$self->shortPath($editFilePath)."'");
 1681   # no redirect is needed
 1682 }
 1683 
 1684 
 1685 # sub make_local_copy_handler {
 1686 #   my ($self, $genericParams, $actionParams, $tableParams) = @_;
 1687 #   #$self->addgoodmessage("make_local_copy_handler called");
 1688 #
 1689 #   my $courseName      =  $self->{courseID};
 1690 #   my $setName         =  $self->{setID};
 1691 #   my $problemNumber   =  $self->{problemID};
 1692 #
 1693 #   my $displayMode     =  $self->{displayMode};
 1694 #   my $problemSeed     =  $self->{problemSeed};
 1695 #   my $do_not_save     = 0;   #error flag
 1696 #
 1697 #   my $new_file_name = $actionParams->{'action.make_local_copy.target_file'}->[0] || '';
 1698 #   my $sourceFilePath = $actionParams->{'action.make_local_copy.source_file'}->[0] || '';
 1699 #   my $file_type      = $actionParams->{'action.make_local_copy.file_type'}->[0] ||'';
 1700 #
 1701 #   my $templatesPath         =  $self->r->ce->{courseDirs}->{templates};
 1702 #   $sourceFilePath    =~ s|^$templatesPath/||; # make sure path relative to templates directory
 1703 #
 1704 #   if ( $new_file_name !~ /\S/) { # need a non-blank file name
 1705 #     # setting $self->{failure} stops saving and any redirects
 1706 #     $do_not_save = 1;
 1707 #     #warn "new file name is $new_file_name";
 1708 #     $self->addbadmessage(CGI::p("Error: File to save to not specified."));
 1709 #   }
 1710 #
 1711 #   #################################################
 1712 #   # grab the problemContents from the form in order to save it to a new permanent file
 1713 #   # later we will unlink (delete) the current temporary file
 1714 #   #################################################
 1715 #
 1716 #   my $problemContents = fixProblemContents($self->r->param('problemContents'));
 1717 #   $self->{r_problemContents} = \$problemContents;
 1718 #   warn "problem contents is empty" unless $problemContents;
 1719 #   #################################################
 1720 #   # Construct the output file path
 1721 #   #################################################
 1722 #   my $outputFilePath = $self->r->ce->{courseDirs}->{templates} . '/' .
 1723 #                  $new_file_name;
 1724 #   if (defined $outputFilePath and -e $outputFilePath) {
 1725 #     # setting $do_not_save stops saving and any redirects
 1726 #     $do_not_save = 1;
 1727 #     $self->addbadmessage(CGI::p("File '".$self->shortPath($outputFilePath)."' exists.
 1728 #     File not saved. No changes have been made.
 1729 #     You can change the file path for this problem manually from the 'Hmwk Sets Editor' page"));
 1730 #   } else {
 1731 #     #$self->addgoodmessage("Saving to file '".$self->shortPath($outputFilePath)."'.");
 1732 #   }
 1733 #
 1734 #   unless ($do_not_save ) {
 1735 #     $self->saveFileChanges($outputFilePath);
 1736 #     #################################################
 1737 #     # Modify source file in problem
 1738 #     #################################################
 1739 #       if ($file_type eq 'set_header') {
 1740 #         my $setRecord = $self->r->db->getGlobalSet($setName);
 1741 #         $setRecord->set_header($new_file_name);
 1742 #         if ($self->r->db->putGlobalSet($setRecord)) {
 1743 #           $self->addgoodmessage("The set header for set $setName has been renamed to '".$self->shortPath($outputFilePath)."'.") ;
 1744 #         } else {
 1745 #           $self->addbadmessage("Unable to change the header for set $setName. Unknown error.");
 1746 #         }
 1747 #       } else {
 1748 #         my $problemRecord = $self->r->db->getGlobalProblem($setName,$problemNumber);
 1749 #         $problemRecord->source_file($new_file_name);
 1750 #         if  ( $self->r->db->putGlobalProblem($problemRecord)  ) {
 1751 #           $self->addgoodmessage("The current source file for problem $problemNumber has been renamed to '".$self->shortPath($outputFilePath)."'.") ;
 1752 #         } else {
 1753 #           $self->addbadmessage("Unable to change the source file path for set $setName, problem $problemNumber. Unknown error.");
 1754 #         }
 1755 #       }
 1756 #
 1757 #   }
 1758 #   my $edit_level = $self->r->param("edit_level") || 0;
 1759 #   $edit_level++;
 1760 #   #################################################
 1761 #   # Set up redirect
 1762 #   #################################################
 1763 #
 1764 #   my $problemPage = $self->r->urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::PGProblemEditor",
 1765 #     courseID => $courseName, setID => $setName, problemID => $problemNumber
 1766 #   );
 1767 #   my $viewURL = $self->systemLink($problemPage,
 1768 #                  params=>{
 1769 #                    sourceFilePath     => $new_file_name,
 1770 #                    edit_level         => $edit_level,
 1771 #                    file_type          => $self->{file_type},
 1772 #                    status_message     => uri_escape($self->{status_message})
 1773 #
 1774 #                  }
 1775 #   );
 1776 #   $self->reply_with_redirect($viewURL);
 1777 # }
 1778 
 1779 # sub rename_form {  # see the save_as form
 1780 # #   my ($self, $onChange, %actionParams) = @_;
 1781 # #   my $problemPath = $self->{editFilePath};
 1782 # #   my $templatesDir = $self->r->ce->{courseDirs}->{templates};
 1783 # #   #warn "problemPath $problemPath $templatesDir";
 1784 # #   $problemPath   =~ s|^$templatesDir/||;
 1785 # #   $problemPath  = '' if $problemPath =~ m|^/|;  # if it is still an absolute path don't suggest that you save to it.
 1786 # #   $self->addbadmessage("problem Path is $problemPath");
 1787 # #   return join("",
 1788 # #          "Rename problem file to : [TMPL]/".CGI::textfield(-name=>'action.rename.target_file', -size=>40, -value=>$problemPath),
 1789 # #           CGI::hidden(-name=>'action.make_local_copy.source_file', -value=>$self->{editFilePath} ),
 1790 # #   );
 1791 #
 1792 #
 1793 # }
 1794 
 1795 # sub rename_handler {
 1796 #     my ($self, $genericParams, $actionParams, $tableParams) = @_;
 1797 #     $actionParams->{'action.make_local_copy.target_file'}->[0] = $actionParams->{'action.rename.target_file'}->[0];
 1798 #   make_local_copy_handler($self, $genericParams, $actionParams, $tableParams);
 1799 # }
 1800 
 1801 
 1802 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9