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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 5318 - (download) (as text) (annotate)
Mon Aug 13 22:53:51 2007 UTC (5 years, 9 months ago) by sh002i
File size: 82097 byte(s)
updated copyright dates

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

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9