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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 4346 - (download) (as text) (annotate)
Thu Aug 3 19:56:36 2006 UTC (6 years, 9 months ago) by sh002i
File size: 78871 byte(s)
backport (dpvc): Allow viewing of the default header files (so they can
be saved to local files).

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

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9