From: Mike G. v. a. <we...@ma...> - 2005-02-15 05:28:43
|
Log Message: ----------- Added changes to allow create new blank problem to work. These include significant cleanup of PGproblemEditor.pm Tags: ---- rel-2-1-patches Modified Files: -------------- webwork2/lib/WeBWorK/ContentGenerator/Instructor: PGProblemEditor.pm ProblemSetDetail.pm Revision Data ------------- Index: ProblemSetDetail.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm,v retrieving revision 1.13.2.3 retrieving revision 1.13.2.4 diff -Llib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm -Llib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm -u -r1.13.2.3 -r1.13.2.4 --- lib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm +++ lib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm @@ -1199,8 +1199,6 @@ ])); } - my $editNewProblemPage = $urlpath->new(type => 'instructor_problem_editor_withset_withproblem', args => { courseID => $courseID, setID => $setID, problemID =>'new_problem' }); - my $editNewProblemLink = $self->systemLink($editNewProblemPage, params => { make_local_copy => 1, file_type => 'blank_problem' }); # print final lines print CGI::end_table(); @@ -1211,7 +1209,6 @@ CGI::br(); print CGI::input({type=>"submit", name=>"submit_changes", value=>"Save Changes"}); print CGI::input({type=>"submit", name=>"handle_numbers", value=>"Reorder problems only"}) . "(Any unsaved changes will be lost.)"; - print CGI::p( CGI::a({href=>$editNewProblemLink},'Create'). 'a new blank problem'); print CGI::p(<<HERE); Any time problem numbers are intentionally changed, the problems will always be renumbered consecutively, starting from one. When deleting @@ -1225,6 +1222,11 @@ } else { print CGI::p(CGI::b("This set doesn't contain any problems yet.")); } + # always allow one to add a new problem. + my $editNewProblemPage = $urlpath->new(type => 'instructor_problem_editor_withset_withproblem', args => { courseID => $courseID, setID => $setID, problemID =>'new_problem' }); + my $editNewProblemLink = $self->systemLink($editNewProblemPage, params => { make_local_copy => 1, file_type => 'blank_problem' }); + + print CGI::p( CGI::a({href=>$editNewProblemLink},'Create'). 'a new blank problem'); print CGI::end_form(); Index: PGProblemEditor.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm,v retrieving revision 1.48 retrieving revision 1.48.2.1 diff -Llib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm -Llib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm -u -r1.48 -r1.48.2.1 --- lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm +++ lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm @@ -30,6 +30,7 @@ use WeBWorK::Utils qw(readFile surePathToFile); use Apache::Constants qw(:common REDIRECT); use HTML::Entities; +use URI::Escape; use WeBWorK::Utils::Tasks qw(fake_set fake_problem); ########################################################### @@ -45,140 +46,450 @@ # # editMode = temporaryFile (view the temp file defined by course_info.txt.user_name.tmp # instead of the file course_info.txt) -# The editFileSuffix is "user_name.tmp" by default. It's definition should be moved to Instructor.pm #FIXME +# this flag is read by Problem.pm and ProblemSet.pm, perhaps others +# The TEMPFILESUFFIX is "user_name.tmp" by default. It's definition should be moved to Instructor.pm #FIXME ########################################################### +########################################################### +# The behavior of this module is essentially defined +# by the values of $file_type and the submit button which is placed in $action +############################################################# +# File types which can be edited +# +# file_type eq 'problem' +# this is the most common type -- this editor can be called by an instructor when viewing any problem. +# the information for retrieving the source file is found using the problemID in order to look +# look up the source file path. +# +# file_type eq 'problem_with_source' +# This is the same as the 'problem' file type except that the source for the problem is found in +# the parameter $r->param('sourceFilePath'). +# +# file_type eq 'set_header' +# This is a special case of editing the problem. The set header is often listed as problem 0 in the set's list of problems. +# +# file_type eq 'hardcopy_header' +# 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. +# But it is used instead of set_header when producing a hardcopy of the problem set in the TeX format, instead of producing HTML +# formatted version for use on the computer screen. +# +# filte_type eq 'course_info +# This allows editing of the course_info.txt file which gives general information about the course. It is called from the +# ProblemSets.pm module. +# +# file_type eq 'blank_problem' +# This is a special call which allows one to create and edit a new PG problem. The "stationery" source for this problem is +# stored in the conf/snippets directory and defined in global.conf by $webworkFiles{screenSnippets}{blankProblem} +############################################################# +# submit button actions -- these and the file_type determine the state of the module +# Save ---- action = save +# Save as ---- action = save_as +# View Problem ---- action = refresh +# Add this problem to: ---- action = add_problem_to_set +# Make this set header for: ---- action = add_set_header_to_set +# Revert ---- action = revert +# no submit button defined ---- action = fresh_edit +################################################### +# +# Determining which is the correct path to the file is a mess!!! FIXME +# The path to the file to be edited is eventually put in tempFilePath +# +# $problemPath is also used as is editFilePath. let's try to regularize these. +#(sourceFile) (problemPath)(tempFilePath)(editFilePath)(forcedSourceFile)(problemPath) +#input parameter can be: sourceFilePath +################################################################# +# params read +# user +# effectiveUser +# submit +# file_type +# problemSeed +# displayMode +# edit_level +# make_local_copy +# sourceFilePath +# problemContents +# save_to_new_file +# + #our $libraryName; #our $rowheight; +our $TEMPFILESUFFIX; sub pre_header_initialize { - my ($self) = @_; - my $r = $self->r; - my $ce = $r->ce; - my $urlpath = $r->urlpath; - my $authz = $r->authz; - my $user = $r->param('user'); - - my $submit_button = $r->param('submit'); # obtain submit command from form - my $file_type = $r->param("file_type") || ''; - + my ($self) = @_; + my $r = $self->r; + my $ce = $r->ce; + my $urlpath = $r->urlpath; + my $authz = $r->authz; + my $user = $r->param('user'); + $TEMPFILESUFFIX = $user.'.tmp'; + + my $submit_button = $r->param('submit'); # obtain submit command from form + my $file_type = $r->param("file_type") || ''; + my $setName = $r->urlpath->arg("setID") ; # using $r->urlpath->arg("setID") ||'' causes trouble with set 0!!! + my $problemNumber = $r->urlpath->arg("problemID"); + # Check permissions return unless ($authz->hasPermissions($user, "access_instructor_tools")); return unless ($authz->hasPermissions($user, "modify_problem_sets")); + + ############################################################################# + # Save file to permanent or temporary file, then redirect for viewing + ############################################################################# + # + # Any file "saved as" should be assigned to "Undefined_Set" and redirectoed to be viewed again in the editor + # + # Problems "saved" or 'refreshed' are to be redirected to the Problem.pm module + # Set headers which are "saved" are to be redirected to the ProblemSet.pm page + # Hardcopy headers which are "saved" are aso to be redirected to the ProblemSet.pm page + # Course_info files are redirected to the ProblemSets.pm page + ############################################################################## + - # Save problem to permanent or temporary file, then redirect for viewing - if (defined($submit_button) and - ($submit_button eq 'Save' or $submit_button eq 'Refresh' - or ($submit_button eq 'Save as' and $file_type eq 'problem'))) { - my $setName = $r->urlpath->arg("setID"); - my $problemNumber = $r->urlpath->arg("problemID"); - - # write the necessary files - # return file path for viewing problem in $self->{currentSourceFilePath} - # obtain the appropriate seed - $self->saveFileChanges($setName, $problemNumber); - - ##### calculate redirect URL based on file type ##### - - # get some information - #my $hostname = $r->hostname(); - #my $port = $r->get_server_port(); - #my $uri = $r->uri; - my $courseName = $urlpath->arg("courseID"); - my $problemSeed = ($r->param('problemSeed')) ? $r->param('problemSeed') : ''; - my $displayMode = ($r->param('displayMode')) ? $r->param('displayMode') : ''; - - my $viewURL = ''; - - if($self->{file_type} eq 'problem') { - if($submit_button eq 'Save as') { # redirect to myself - my $sourceFile = $self->{problemPath}; - # strip off template directory prefix + + ###################################### + # Insure that file_type is defined + ###################################### + # We have already read in the file_type parameter from the form + # + # If this has not been defined we are dealing with a set header + # or regular problem + if (defined($file_type) and ($file_type =~/\S/)) { #file_type is defined and is not blank + # file type is already defined -- do nothing + } else { + # if "sourcFilePath" is defined in the form, then we are getting the path directly. + # if the problem number is defined and is 0 + # then we are dealing with some kind of + # header file. The default is 'set_header' which prints properly + # to the screen. + # If the problem number is not zero, we are dealing with a real problem + ###################################### + if ( defined($r->param('sourceFilePath') and $r->param('sourceFilePath') =~/\S/) ) { + $file_type ='source_path_for_problem_file'; + } elsif ( defined($problemNumber) ) { + if ( $problemNumber =~/^\d+$/ and $problemNumber == 0 ) { # if problem number is numeric and zero + $file_type = 'set_header' unless $file_type eq 'set_header' + or $file_type eq 'hardcopy_header'; + } else { + $file_type = 'problem'; + } + + } + } + die "The file_type variable has not been defined or is blank." unless defined($file_type) and $file_type =~/\S/; + $self->{file_type} = $file_type; + + ########################################## + # File type is one of: blank_problem course_info problem set_header hardcopy_header problem_with_source + ########################################## + # + # Determine the path to the file + # + ########################################### + $self->getFilePaths($setName, $problemNumber, $file_type,$TEMPFILESUFFIX); + # result stored in $self->{editFilePath}, and $self->{tempFilePath} + ########################################## + # + # Determine action + # + ########################################### + # Submit button is one of: "add this problem to" , "add this set header to ", "Refresh" "Revert" "Save" "Save As" + $submit_button = $r->param('submit'); + SUBMIT_CASE: { + (! defined($submit_button) ) and do { # fresh problem to edit + $self->{action} = 'fresh_edit'; + last SUBMIT_CASE; + }; + + ($submit_button eq 'Add this problem to: ') and do { + $self->{action} = 'add_problem_to_set'; + last SUBMIT_CASE; + }; + + ($submit_button eq 'Make this the set header for: ') and do { + $self->{action} = 'add_set_header_to_set'; + last SUBMIT_CASE; + }; + + ($submit_button eq 'View problem') and do { + $self->{action} ='refresh'; + last SUBMIT_CASE; + }; + + ($submit_button eq 'Revert') and do { + $self->{action} = 'revert'; + last SUBMIT_CASE; + }; + + ($submit_button eq 'Save') and do { + $self->{action} = 'save'; + last SUBMIT_CASE; + }; + + ($submit_button eq 'Save as') and do { + $self->{action} = 'save_as'; + last SUBMIT_CASE; + }; + + ($submit_button eq 'Add problem to: ') and do { + $self->{action} = 'add_problem_to_set'; + last SUBMIT_CASE; + }; + # else + die "Unrecognized submit command: |$submit_button|"; + + } # END SUBMIT_CASE + + + ########################################### + # Save file + ###################################### + + # The subroutine below writes the necessary files and obtains the appropriate seed. + # and returns + # $self->{problemPath} --- file path for viewing problem in $self->{problemPath} + # $self->{failure} + + + $self->saveFileChanges($setName, $problemNumber, $file_type,$TEMPFILESUFFIX); + + ############################################################################## + # displayMode and problemSeed + # + # Determine the display mode + # If $self->{problemSeed} was obtained within saveFileChanges from the problem_record + # then it can be overridden by the value obtained from the form. + # Insure that $self->{problemSeed} has some non-empty value + # displayMode and problemSeed + # will be needed for viewing the problem via redirect. + # They are also two of the parameters which can be set by the editor + ############################################################################## + + if (defined $r->param('displayMode')) { + $self->{displayMode} = $r->param('displayMode'); + } else { + $self->{displayMode} = $ce->{pg}->{options}->{displayMode}; + } + + # form version of problemSeed overrides version obtained from the the problem_record + # inside saveFileChanges + $self->{problemSeed} = $r->param('problemSeed') if (defined $r->param('problemSeed')); + # Make sure that the problem seed has some value + $self->{problemSeed} = '123456' unless defined $self->{problemSeed} and $self->{problemSeed} =~/\S/; + + ############################################################################## + # Return + # If file saving fails or + # if no redirects are required. No further processing takes place in this subroutine. + # Redirects are required only for the following submit values + # 'Save' + # 'Save as' + # 'Refresh' + # add problem to set + # add set header to set + # + ######################################### + + return if $self->{failure}; + # FIXME: even with an error we still open a new page because of the target specified in the form + + + # Some cases do not need a redirect: revert, fresh_edit + my $action = $self->{action}; + + return unless $action eq 'save' + or $action eq 'refresh' + or $action eq 'save_as' + or $action eq 'add_problem_to_set' + or $action eq 'add_set_header_to_set'; + + + ###################################### + # calculate redirect URL based on file type + ###################################### + my $courseName = $urlpath->arg("courseID"); + my $problemSeed = ($r->param('problemSeed')) ? $r->param('problemSeed') : ''; + my $displayMode = ($r->param('displayMode')) ? $r->param('displayMode') : ''; + + my $viewURL = ''; + + ###################################### + # problem file_type + # redirect to Problem.pm with setID = "Undefined_Set if "Save As" option is chosen + # redirect to Problem.pm with setID = current $setID if "Save" or "Revert" or "Refresh is chosen" + ###################################### + REDIRECT_CASES: { + ($file_type eq 'problem' or $file_type eq 'source_path_for_problem_file' or $file_type eq 'blank_problem') and do { + my $sourceFilePath = $self->{problemPath}; + # strip off template directory prefix + $sourceFilePath =~ s|^$ce->{courseDirs}->{templates}/||; + if ($action eq 'save_as') { # redirect to myself my $edit_level = $r->param("edit_level") || 0; $edit_level++; - $sourceFile =~ s|^$ce->{courseDirs}->{templates}/||; + my $problemPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::PGProblemEditor", - courseID => $courseName, setID => 'Undefined_Set', problemID => $problemNumber); - $viewURL = $self->systemLink($problemPage, params=>{sourceFilePath => $sourceFile, edit_level=>$edit_level}); - } else { # other problems redirect to Problem.pm + courseID => $courseName, setID => 'Undefined_Set', problemID => 'Undefined_Problem' + ); + $viewURL = $self->systemLink($problemPage, + params=>{ + sourceFilePath => $sourceFilePath, + edit_level => $edit_level, + file_type => 'source_path_for_problem_file', + status_message => uri_escape($self->{status_message}) + + } + ); + + + } elsif ( $action eq 'add_problem_to_set') { + + my $targetSetName = $r->param('target_set'); + my $problemPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Problem", + courseID => $courseName, setID => $targetSetName, problemID => scalar($r->db->listGlobalProblems($targetSetName)) + ); + $viewURL = $self->systemLink($problemPage, + params => { + displayMode => $displayMode, + problemSeed => $problemSeed, + editMode => "savedFile", + sourceFilePath => $sourceFilePath, + status_message => uri_escape($self->{status_message}) + + } + ); + } elsif ( $action eq 'add_set_header_to_set') { + my $targetSetName = $r->param('target_set'); + my $problemPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::ProblemSet", + courseID => $courseName, setID => $targetSetName + ); + $viewURL = $self->systemLink($problemPage, + params => { + displayMode => $displayMode, + editMode => "savedFile", + status_message => uri_escape($self->{status_message}) + } + ); + } else { # saved problems and refreshed problems redirect to Problem.pm my $problemPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Problem", - courseID => $courseName, setID => $setName, problemID => $problemNumber); - $self->{currentSourceFilePath} =~ s|^$ce->{courseDirs}->{templates}/||; + courseID => $courseName, setID => $setName, problemID => $problemNumber + ); $viewURL = $self->systemLink($problemPage, params => { displayMode => $displayMode, problemSeed => $problemSeed, - editMode => ($submit_button eq "Save" ? "savedFile" : "temporaryFile"), - sourceFilePath => $self->{currentSourceFilePath}, - success => $self->{sucess}, - failure => $self->{failure}, + editMode => ($action eq "save" ? "savedFile" : "temporaryFile"), + sourceFilePath => $sourceFilePath, + status_message => uri_escape($self->{status_message}) + } ); } - } + last REDIRECT_CASES; + }; + ###################################### + # blank_problem file_type + # redirect to Problem.pm + ###################################### - # set headers redirect to ProblemSet.pm - $self->{file_type} eq 'set_header' and do { - my $problemSetPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::ProblemSet", - courseID => $courseName, setID => $setName); - $viewURL = $self->systemLink($problemSetPage, - params => { - displayMode => $displayMode, - problemSeed => $problemSeed, - editMode => ($submit_button eq "Save" ? "savedFile" : "temporaryFile"), - } - ); + $file_type eq 'blank_problem' and do { + return; # no redirect is needed }; - # course info redirects to ProblemSets.pm - $self->{file_type} eq 'course_info' and do { + ###################################### + # set headers file_type + # redirect to ProblemSet.pm + ###################################### + + ($file_type eq 'set_header' or $file_type eq 'hardcopy_header' ) and do { + if ($action eq 'save_as') { # redirect to myself + my $sourceFilePath = $self->{problemPath}; + # strip off template directory prefix + $sourceFilePath =~ s|^$ce->{courseDirs}->{templates}/||; + + my $edit_level = $r->param("edit_level") || 0; + $edit_level++; + + my $problemPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::PGProblemEditor", + courseID => $courseName, setID => 'Undefined_Set', problemID => 'Undefined_Problem' + ); + $viewURL = $self->systemLink($problemPage, + params=>{ + sourceFilePath => $sourceFilePath, + edit_level => $edit_level, + file_type => 'source_path_for_problem_file', + status_message => uri_escape($self->{status_message}) + } + ); + } elsif ( $action eq 'add_set_header_to_set') { + my $targetSetName = $r->param('target_set'); + my $problemPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::ProblemSet", + courseID => $courseName, setID => $targetSetName + ); + $viewURL = $self->systemLink($problemPage, + params => { + displayMode => $displayMode, + editMode => "savedFile", + status_message => uri_escape($self->{status_message}) + } + ); + } else { + my $problemSetPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::ProblemSet", + courseID => $courseName, setID => $setName); + $viewURL = $self->systemLink($problemSetPage, + params => { + displayMode => $displayMode, + problemSeed => $problemSeed, + editMode => ($action eq "save" ? "savedFile" : "temporaryFile"), + status_message => uri_escape($self->{status_message}) + } + ); + } + last REDIRECT_CASES; + }; + ###################################### + # course_info file type + # redirect to ProblemSets.pm + ###################################### + $file_type eq 'course_info' and do { my $problemSetsPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::ProblemSets", courseID => $courseName); $viewURL = $self->systemLink($problemSetsPage, params => { - editMode => ($submit_button eq "Save" ? "savedFile" : "temporaryFile"), + editMode => ($action eq "save" ? "savedFile" : "temporaryFile"), + status_message => uri_escape($self->{status_message}) } ); + last REDIRECT_CASES; }; - - # don't redirect on bad save attempts - # FIXME: even with an error we still open a new page because of the target specified in the form - return if $self->{failure}; - - if ($viewURL) { - $self->reply_with_redirect($viewURL); - } else { - die "Invalid file_type ", $self->{file_type}, " specified by saveFileChanges"; - } + # else if no redirect needed -- there must be an error. + die "The file_type $file_type does not have a defined redirect procedure."; + } # End REDIRECT_CASES + + if ($viewURL) { + $self->reply_with_redirect($viewURL); + } else { + die "Invalid file_type $file_type specified by saveFileChanges"; } } + sub initialize { my ($self) = @_; my $r = $self->r; my $authz = $r->authz; my $user = $r->param('user'); - my $setName = $r->urlpath->arg("setID"); - my $problemNumber = $r->urlpath->arg("problemID"); - # Check permissions return unless ($authz->hasPermissions($user, "access_instructor_tools")); return unless ($authz->hasPermissions($user, "modify_problem_sets")); - - # if we got to initialize(), then saveFileChanges was not called in pre_header_initialize(). - # therefore we call it here unless there has been an error already: - $self->saveFileChanges($setName, $problemNumber) unless $self->{failure}; - # this seems like a good place to deal with the add to set - my $submit_button = $r->param('submit') || ''; - if($submit_button eq 'Add this problem to: ') { - my $ce = $r->ce; - my $sourcePath = $self->{problemPath}; - $sourcePath =~ s|^$ce->{courseDirs}->{templates}/||; - $self->addProblemToSet(setName => $r->param('target_set'), - sourceFile => $sourcePath); - $self->addgoodmessage("Added $sourcePath to ". $r->param('target_set') ); - } + + my $tempFilePath = $self->{tempFilePath}; # path to the file currently being worked with (might be a .tmp file) + my $inputFilePath = $self->{inputFilePath}; # path to the file for input, (might be a .tmp file) + my $protected_file = (not -w $inputFilePath ) and -e $inputFilePath; #FIXME -- let's try to insure that the input file always exists, even for revert. + $self->addbadmessage("Changes in this file have not yet been permanently saved.") if -r $tempFilePath; + $self->addbadmessage("This file |$inputFilePath| is protected! To edit this text you must first use 'Save As' to save it to another file.") if $protected_file; + } sub path { @@ -197,11 +508,6 @@ "$problemNumber", $r->location."/$courseName/$setName/$problemNumber", "Editor", "" ); -# do { -# unshift @path, $urlpath->name, $r->location . $urlpath->path; -# } while ($urlpath = $urlpath->parent); -# -# $path[$#path] = ""; # we don't want the last path element to be a link #print "\n<!-- BEGIN " . __PACKAGE__ . "::path -->\n"; print $self->pathMacro($args, @path); @@ -228,28 +534,56 @@ my $authz = $r->authz; my $user = $r->param('user'); my $make_local_copy = $r->param('make_local_copy'); - + # Check permissions return CGI::div({class=>"ResultsWithError"}, "You are not authorized to access the Instructor tools.") - unless $authz->hasPermissions($r->param("user"), "access_instructor_tools"); + unless $authz->hasPermissions($user, "access_instructor_tools"); return CGI::div({class=>"ResultsWithError"}, "You are not authorized to modify problems.") - unless $authz->hasPermissions($r->param("user"), "modify_student_data"); + unless $authz->hasPermissions($user, "modify_student_data"); # Gathering info - my $editFilePath = $self->{problemPath}; # path to the permanent file to be edited - my $inputFilePath = $self->{inputFilePath}; # path to the file currently being worked with (might be a .tmp file) - - my $header = CGI::i("Editing problem: $inputFilePath"); - + my $editFilePath = $self->{editFilePath}; # path to the permanent file to be edited + my $tempFilePath = $self->{tempFilePath}; # path to the file currently being worked with (might be a .tmp file) + my $inputFilePath = $self->{inputFilePath}; # path to the file for input, (might be a .tmp file) + my $setName = $r->urlpath->arg("setID") ; + my $problemNumber = $r->urlpath->arg("problemID") ; + $setName = defined($setName) ? $setName : ''; # we need this instead of using the || construction + # to keep set 0 from being set to the + # empty string. + $problemNumber = defined($problemNumber) ? $problemNumber : ''; + + ######################################################################### # Find the text for the problem, either in the tmp file, if it exists # or in the original file in the template directory + # or in the problem contents gathered in the initialization phase. ######################################################################### my $problemContents = ${$self->{r_problemContents}}; + unless ( $problemContents =~/\S/) { # non-empty contents + if (-r $tempFilePath and not -d $tempFilePath) { + eval { $problemContents = WeBWorK::Utils::readFile($tempFilePath) }; + $problemContents = $@ if $@; + $inputFilePath = $tempFilePath; + } elsif (-r $editFilePath and not -d $editFilePath) { + eval { $problemContents = WeBWorK::Utils::readFile($editFilePath) }; + $problemContents = $@ if $@; + $inputFilePath = $editFilePath; + } else { # file not existing is not an error + #warn "No file exists"; + $problemContents = ''; + } + } else { + #warn "obtaining input from r_problemContents"; + } + + my $protected_file = not -w $inputFilePath; + my $header = CGI::i("Editing problem".CGI::b("set $setName/ problem $problemNumber</emphasis>").CGI::br()." in file $inputFilePath"); + $header = ($inputFilePath =~ /$TEMPFILESUFFIX/) ? CGI::div({class=>'temporaryFile'},$header) : $header; # use colors if temporary file + ######################################################################### # Format the page ######################################################################### @@ -257,13 +591,14 @@ # Define parameters for textarea # FIXME # Should the seed be set from some particular user instance?? - my $rows = 20; - my $columns = 80; - my $mode_list = $ce->{pg}->{displayModes}; - my $displayMode = $self->{displayMode}; - my $problemSeed = $self->{problemSeed}; - my $uri = $r->uri; - my $edit_level = $r->param('edit_level') || 0; + my $rows = 20; + my $columns = 80; + my $mode_list = $ce->{pg}->{displayModes}; + my $displayMode = $self->{displayMode}; + my $problemSeed = $self->{problemSeed}; + my $uri = $r->uri; + my $edit_level = $r->param('edit_level') || 0; + my $file_type = $self->{file_type}; my $force_field = defined($r->param('sourceFilePath')) ? CGI::hidden(-name=>'sourceFilePath', @@ -274,25 +609,30 @@ $allSetNames[$j] =~ s|^set||; $allSetNames[$j] =~ s|\.def||; } - # next, the content of our "add to stuff", which only appears if we are a problem - my $add_to_stuff = ''; - if($self->{file_type} eq 'problem') { - # second form which does not open a new window - $add_to_stuff = CGI::start_form(-method=>"POST", -action=>"$uri"). + my $target = "problem$edit_level"; + # Prepare Preview button + my $view_problem_form = CGI::start_form({method=>"POST", name=>"editor", action=>"$uri", target=>$target, enctype=>"application/x-www-form-urlencoded"}). $self->hidden_authen_fields. $force_field. - CGI::hidden(-name=>'file_type',-default=>$self->{file_type}). - CGI::hidden(-name=>'problemSeed',-default=>$problemSeed). - CGI::hidden(-name=>'displayMode',-default=>$displayMode). - CGI::hidden(-name=>'problemContents',-default=>$problemContents). - CGI::p( - CGI::submit(-value=>'Add this problem to: ',-name=>'submit'), - CGI::popup_menu(-name=>'target_set',-values=>\@allSetNames) - ). + CGI::hidden(-name=>'file_type',-default=>$self->{file_type}). + CGI::hidden(-name=>'problemSeed',-default=>$problemSeed). + CGI::hidden(-name=>'displayMode',-default=>$displayMode). + CGI::hidden(-name=>'problemContents',-default=>$problemContents). + CGI::submit(-value=>'View problem',-name=>'submit'). CGI::end_form(); + # Prepare add to set buttons + my $add_files_to_set_buttons = ''; + if ($file_type eq 'problem' or $file_type eq 'source_path_for_problem_file' ) { + $add_files_to_set_buttons .= CGI::submit(-value=>'Add problem to: ',-name=>'submit' ) ; + } + if ($file_type eq 'set_header' # set header or the problem number is not a regular positive number + or ( $file_type =~ /problem/ and ($problemNumber =~ /\D|^0$|^$/ )) ){ + $add_files_to_set_buttons .=CGI::submit(-value=>'Make this the set header for: ',-name=>'submit' ); } - - my $target = "problem$edit_level"; + # Add pop-up menu for the target set if either of these buttons has been revealed. + $add_files_to_set_buttons .= CGI::popup_menu(-name=>'target_set',-values=>\@allSetNames) if $add_files_to_set_buttons; + + return CGI::p($header), CGI::start_form({method=>"POST", name=>"editor", action=>"$uri", target=>$target, enctype=>"application/x-www-form-urlencoded"}), $self->hidden_authen_fields, @@ -314,14 +654,18 @@ ), ), CGI::p( - CGI::submit(-value=>'Refresh',-name=>'submit'), - $make_local_copy ? CGI::submit(-value=>'Save',-name=>'submit', -disabled=>1) : CGI::submit(-value=>'Save',-name=>'submit'), + $add_files_to_set_buttons, + CGI::br(), + CGI::submit(-value=>'View problem',-name=>'submit'), + $protected_file ? CGI::submit(-value=>'Save',-name=>'submit', -disabled=>1) : CGI::submit(-value=>'Save',-name=>'submit'), CGI::submit(-value=>'Revert', -name=>'submit'), CGI::submit(-value=>'Save as',-name=>'submit'), CGI::textfield(-name=>'save_to_new_file', -size=>40, -value=>""), + ), - CGI::end_form(), - $add_to_stuff; + CGI::end_form(); + + } ################################################################################ @@ -334,228 +678,316 @@ # # it actually does a lot more than save changes to the file being edited, and # sometimes less. - -sub saveFileChanges { - my ($self, $setName, $problemNumber) = @_; +sub getFilePaths { + my ($self, $setName, $problemNumber, $file_type, $TEMPFILESUFFIX) = @_; my $r = $self->r; my $ce = $r->ce; my $db = $r->db; my $urlpath = $r->urlpath; - my $courseName = $urlpath->arg("courseID"); my $user = $r->param('user'); my $effectiveUserName = $r->param('effectiveUser'); - + $setName = '' unless defined $setName; $problemNumber = '' unless defined $problemNumber; - - ##### Determine path to the file to be edited. ##### - + die 'Internal error to PGProblemEditor -- file type is not defined' unless defined $file_type; + + ########################################################## + # Determine path to the input file to be edited. + # set EditFilePath to this value + # + # There are potentially four files in play + # The permanent path of the input file == $editFilePath == $self->{problemPath} + # A temporary path to the input file == $tempFilePath== "$editFilePath.$TEMPFILESUFFIX"== $self->{problemPath} + ########################################################## + # Relevant parameters + # $r->param("displayMode") + # $r->param('problemSeed') + # $r->param('submit') + # $r->param('make_local_copy') + # $r->param('sourceFilePath') + # $r->param('problemContents') + # $r->param('save_to_new_file') + ########################################################################## + # Define the following variables + # path to regular file -- $self->{problemPath} = $editFilePath; + # path to file being read (temporary or permanent) + # --- $self->{problemPath} = $problemPath; + # contents of the file being read --- $problemContents + # $self->{r_problemContents} = \$problemContents; + # $self->{TEMPFILESUFFIX} = $TEMPFILESUFFIX; + ########################################################################### + my $editFilePath = $ce->{courseDirs}->{templates}; - my $problem_record = undef; - my $file_type = $r->param("file_type") || ''; - - if ($file_type eq 'course_info') { - # we are editing the course_info file - $self->{file_type} = 'course_info'; + ########################################################################## + # Determine path to regular file, place it in $editFilePath + # problemSeed is defined for the file_type = 'problem' and 'source_path_to_problem' + ########################################################################## + CASE: + { + ($file_type eq 'course_info') and do { + # we are editing the course_info file + # value of courseFiles::course_info is relative to templates directory + $editFilePath .= '/' . $ce->{courseFiles}->{course_info}; + last CASE; + }; - # value of courseFiles::course_info is relative to templates directory - $editFilePath .= '/' . $ce->{courseFiles}->{course_info}; - } else { - # we are editing a problem file or a set header file + ($file_type eq 'blank_problem') and do { + $editFilePath = $ce->{webworkFiles}->{screenSnippets}->{blankProblem}; + $self->addbadmessage("$editFilePath is blank problem template file and cannot be edited directly."); + $self->addbadmessage("Any changes you make will have to be saved as another file."); + last CASE; + }; - # FIXME there is a discrepancy in the way that the problems are found. - # FIXME more error checking is needed in case the problem doesn't exist. - # (i wonder what the above comments mean... -sam) - - if (defined $problemNumber) { - if ($problemNumber == 0) { - # we are editing a header file - if ($file_type eq 'set_header' or $file_type eq 'hardcopy_header') { - $self->{file_type} = $file_type - } else { - $self->{file_type} = 'set_header'; - } - - # first try getting the merged set for the effective user - my $set_record = $db->getMergedSet($effectiveUserName, $setName); # checked - - # if that doesn't work (the set is not yet assigned), get the global record - $set_record = $db->getGlobalSet($setName); # checked - - # bail if no set is found - die "Cannot find a set record for set $setName" unless defined($set_record); - - my $header_file = ""; - $header_file = $set_record->{$self->{file_type}}; - if ($header_file && $header_file ne "") { + ($file_type eq 'set_header' or $file_type eq 'hardcopy_header') and do { + # first try getting the merged set for the effective user + my $set_record = $db->getMergedSet($effectiveUserName, $setName); # checked + # if that doesn't work (the set is not yet assigned), get the global record + $set_record = $db->getGlobalSet($setName); # checked + # bail if no set is found + die "Cannot find a set record for set $setName" unless defined($set_record); + + my $header_file = ""; + $header_file = $set_record->{$file_type}; + if ($header_file && $header_file ne "") { $editFilePath .= '/' . $header_file; - } else { + } else { # if the set record doesn't specify the filename - # then the set uses the deafult from snippets + # then the set uses the default from snippets # so we'll load that file, but change where it will be saved # to and grey out the "Save" button - if ($r->param('make_local_copy')) { - $editFilePath = $ce->{webworkFiles}->{screenSnippets}->{setHeader} if $self->{file_type} eq 'set_header'; - $editFilePath = $ce->{webworkFiles}->{hardcopySnippets}->{setHeader} if $self->{file_type} eq 'hardcopy_header'; + # FIXME why does the make_local_copy variable need to be checked? + # Isn't it automatic that a local copy has to be made? + #if ($r->param('make_local_copy')) { + $editFilePath = $ce->{webworkFiles}->{screenSnippets}->{setHeader} if $file_type eq 'set_header'; + $editFilePath = $ce->{webworkFiles}->{hardcopySnippets}->{setHeader} if $file_type eq 'hardcopy_header'; $self->addbadmessage("$editFilePath is the default header file and cannot be edited directly."); $self->addbadmessage("Any changes you make will have to be saved as another file."); - } - } - - - } else { - # we are editing a "real" problem - $self->{file_type} = 'problem'; - - # first try getting the merged problem for the effective user - $problem_record = $db->getMergedProblem($effectiveUserName, $setName, $problemNumber); # checked - - # if that doesn't work (the problem is not yet assigned), get the global record - $problem_record = $db->getGlobalProblem($setName, $problemNumber) unless defined($problem_record); # checked - - - if(not defined($problem_record)) { - my $forcedSourceFile = $r->param('sourceFilePath'); - # bail if no problem is found and we aren't faking it - die "Cannot find a problem record for set $setName / problem $problemNumber" unless defined($forcedSourceFile); - $problem_record = fake_problem($db); - $problem_record->problem_id($problemNumber); - $problem_record->source_file($forcedSourceFile); - } - - - $editFilePath .= '/' . $problem_record->source_file; + #} } - } - } + last CASE; + }; #end 'set_header, hardcopy_header' case + + ($file_type eq 'problem') and do { + + # first try getting the merged problem for the effective user + my $problem_record = $db->getMergedProblem($effectiveUserName, $setName, $problemNumber); # checked + + # if that doesn't work (the problem is not yet assigned), get the global record + $problem_record = $db->getGlobalProblem($setName, $problemNumber) unless defined($problem_record); # checked + # bail if no source path for the problem is found ; + die "Cannot find a problem record for set $setName / problem $problemNumber" unless defined($problem_record); + $editFilePath .= '/' . $problem_record->source_file; + # define the problem seed for later use + $self->{problemSeed}= $problem_record->problem_seed if defined($problem_record) and $problem_record->can('problem_seed') ; + last CASE; + }; # end 'problem' case + + ($file_type eq 'source_path_for_problem_file') and do { + my $forcedSourceFile = $r->param('sourceFilePath'); + # bail if no source path for the problem is found ; + die "Cannot find a file path to save to" unless( defined($forcedSourceFile) and ($forcedSourceFile =~ /\S/) ); + $self->{problemSeed} = 1234; + $editFilePath .= '/' . $forcedSourceFile; + last CASE; + }; # end 'source_path_for_problem_file' case + } # end CASE: statement + # if a set record or problem record contains an empty blank for a header or problem source_file # we could find ourselves trying to edit /blah/templates/.toenail.tmp or something similar # which is almost undoubtedly NOT desirable if (-d $editFilePath) { - $self->{failure} = "The file $editFilePath is a directory!"; - $self->addbadmessage("The file $editFilePath is a directory!"); - } - - - my $editFileSuffix = $user.'.tmp'; - my $submit_button = $r->param('submit'); - - ############################################################################## - # Determine the display mode - # try to get problem seed from the input parameter, or from the problem record - # This will be needed for viewing the problem via redirect. - # They are also two of the parameters which can be set by the editor - ############################################################################## - - my $displayMode; - if (defined $r->param('displayMode')) { - $displayMode = $r->param('displayMode'); - } else { - $displayMode = $ce->{pg}->{options}->{displayMode}; + my $msg = "The file $editFilePath is a directory!"; + $self->{failure} = 1; + $self->addbadmessage($msg); } - - my $problemSeed; - if (defined $r->param('problemSeed')) { - $problemSeed = $r->param('problemSeed'); - } elsif (defined($problem_record) and $problem_record->can('problem_seed')) { - $problemSeed = $problem_record->problem_seed; + if (-e $editFilePath and not -r $editFilePath) { #it's ok if the file doesn't exist, perhaps we're going to create it + # with save as + my $msg = "The file $editFilePath cannot be read!"; + $self->{failure} = 1; + $self->addbadmessage($msg); } + ################################################# + # The path to the permanent file is now verified and stored in $editFilePath + # Whew!!! + ################################################# + + my $tempFilePath = "$editFilePath.$TEMPFILESUFFIX"; + $self->{editFilePath} = $editFilePath; + $self->{tempFilePath} = $tempFilePath; + $self->{inputFilePath} = (-r "$editFilePath.$TEMPFILESUFFIX") ? $tempFilePath : $editFilePath; + +} +sub saveFileChanges { + my ($self, $setName, $problemNumber, $file_type, $TEMPFILESUFFIX) = @_; + my $r = $self->r; + my $ce = $r->ce; + my $db = $r->db; + my $urlpath = $r->urlpath; - # make absolutely sure that the problem seed is defined, if it hasn't been. - $problemSeed = '123456' unless defined $problemSeed and $problemSeed =~/\S/; - + my $courseName = $urlpath->arg("courseID"); + my $user = $r->param('user'); + my $effectiveUserName = $r->param('effectiveUser'); + + $setName = '' unless defined $setName; + $problemNumber = '' unless defined $problemNumber; + $file_type = '' unless defined $file_type; + + my $action = $self->{action}; + my $editFilePath = $self->{editFilePath}; + my $tempFilePath = $self->{tempFilePath}; ############################################################################## # read and update the targetFile and targetFile.tmp files in the directory # if a .tmp file already exists use that, unless the revert button has been pressed. # These .tmp files are # removed when the file is finally saved. + # Place the path of the file to be read in $problemPath. ############################################################################## + my $problemContents = ''; - my $currentSourceFilePath = ''; + my $outputFilePath = undef; # this is actually the output file for this subroutine + # it is then read in as source in the body of this module + my $do_not_save = 0; # flag to prevent saving of file my $editErrors = ''; - - my $inputFilePath; - if (-r "$editFilePath.$editFileSuffix") { - $inputFilePath = "$editFilePath.$editFileSuffix"; - } else { - $inputFilePath = $editFilePath; - } - - $inputFilePath = $editFilePath if defined($submit_button) and $submit_button eq 'Revert'; - - ##### handle button clicks ##### - - if (not defined $submit_button or $submit_button eq 'Revert' ) { - # this is a fresh editing job - # copy the pg file to a new file with the same name with .tmp added - # store this name in the $self->currentSourceFilePath for use in body - - # try to read file - if(-e $inputFilePath and not -d $inputFilePath) { - eval { $problemContents = WeBWorK::Utils::readFile($inputFilePath) }; - $problemContents = $@ if $@; - } else { # file not existing is not an error - $problemContents = ''; - } + + ########################################################################## + # For each of the actions define the following variables: + # + # path to permanent file -- $self->{problemPath} = $editFilePath; + # path to file being read (temporary or permanent) + # --- $self->{problemPath} = $problemPath; + # contents of the file being read --- $problemContents + # $self->{r_problemContents} = \$problemContents; + # + ################################# + # handle button clicks ##### + # Read contents of file + ################################# + ACTION_CASES: { + ($action eq 'fresh_edit') and do { + # this is a fresh editing job + # the original file will be read in the body + last ACTION_CASES; + }; + + ($action eq 'revert') and do { + # this is also fresh editing job + $outputFilePath = undef; + $self->addgoodmessage("Reverting to original file $editFilePath"); + $self->{problemPath} = $editFilePath; + last ACTION_CASES; + }; - $currentSourceFilePath = "$editFilePath.$editFileSuffix"; - $self->{currentSourceFilePath} = $currentSourceFilePath; - $self->{problemPath} = $editFilePath; - } elsif ($submit_button eq 'Refresh') { - # grab the problemContents from the form in order to save it to the tmp file - # store tmp file name in the $self->currentSourceFilePath for use in body - $problemContents = $r->param('problemContents'); - - $currentSourceFilePath = "$editFilePath.$editFileSuffix"; - $self->{currentSourceFilePath} = $currentSourceFilePath; - $self->{problemPath} = $editFilePath; - } elsif ($submit_button eq 'Save') { - # grab the problemContents from the form in order to save it to the permanent file - # later we will unlink (delete) the temporary file - # store permanent file name in the $self->currentSourceFilePath for use in body - $problemContents = $r->param('problemContents'); - - $currentSourceFilePath = "$editFilePath"; - $self->{currentSourceFilePath} = $currentSourceFilePath; - $self->{problemPath} = $editFilePath; - } elsif ($submit_button eq 'Save as') { - # grab the problemContents from the form in order to save it to a new permanent file - # later we will unlink (delete) the current temporary file - # store new permanent file name in the $self->currentSourceFilePath for use in body - $problemContents = $r->param('problemContents'); - # Save the user in case they forgot to end the file with .pg - if($self->{file_type} eq 'problem') { - my $save_to_new_file = $r->param('save_to_new_file'); - $save_to_new_file =~ s/\.pg$//; # remove it if it is there - $save_to_new_file .= '.pg'; # put it there - $r->param('save_to_new_file', $save_to_new_file); - } - $currentSourceFilePath = $ce->{courseDirs}->{templates} . '/' . $r->param('save_to_new_file'); - $self->{currentSourceFilePath} = $currentSourceFilePath; - $self->{problemPath} = $currentSourceFilePath; - } elsif ($submit_button eq 'Add this problem to: ') { - $problemContents = $r->param('problemContents'); - $currentSourceFilePath = "$editFilePath.$editFileSuffix"; - $self->{currentSourceFilePath} = $currentSourceFilePath; - $self->{problemPath} = $editFilePath; - } else { - die "Unrecognized submit command: $submit_button"; - } + ($action eq 'refresh') and do { + # grab the problemContents from the form in order to save it to the tmp file + # store tmp file name in the $self->problemPath for use in body + + $problemContents = $r->param('problemContents'); + $outputFilePath = "$editFilePath.$TEMPFILESUFFIX"; + $self->{problemPath} = $outputFilePath; + last ACTION_CASES; + }; - # Handle the problem of line endings. Make sure that all of the line endings. Convert \r\n to \n - $problemContents =~ s/\r\n/\n/g; - $problemContents =~ s/\r/\n/g; - - # FIXME convert all double returns to paragraphs for .txt files - # instead of doing this here, it should be done n the PLACE WHERE THE FILE IS DISPLAYED!!! - #if ($self->{file_type} eq 'course_info' ) { - # $problemContents =~ s/\n\n/\n<p>\n/g; - #} + ($action eq 'save') and do { + # grab the problemContents from the form in order to save it to the permanent file + # later we will unlink (delete) the temporary file + # store permanent file name in the $self->problemPath for use in body + $problemContents = $r->param('problemContents'); + $outputFilePath = "$editFilePath"; + $self->{problemPath} = $outputFilePath; + #$self->addgoodmessage("Saving to file $outputFilePath"); + last ACTION_CASES; + }; + + ($action eq 'save_as') and do { + my $new_file_name =$r->param('save_to_new_file') || ''; + ################################################# + #bail unless this new file name has been defined + ################################################# + if ( $new_file_name !~ /\S/) { # need a non-blank file name + # setting $self->{failure} stops saving and any redirects + $do_not_save = 1; + warn "new file name is $new_file_name"; + $self->addbadmessage(CGI::p("Please specify a file to save to.")); + last ACTION_CASES; #stop processing + } + ################################################# + # grab the problemContents from the form in order to save it to a new permanent file + # later we will unlink (delete) the current temporary file + # store new permanent file name in the $self->problemPath for use in body + ################################################# + $problemContents = $r->param('problemContents'); + + ################################################# + # Rescue the user in case they forgot to end the file name with .pg + ################################################# + if($self->{file_type} eq 'problem' + or $self->{file_type} eq 'blank_problem' + or $self->{file_type} eq 'set_header') { + $new_file_name =~ s/\.pg$//; # remove it if it is there + $new_file_name .= '.pg'; # put it there + + } + + ################################################# + # check to prevent overwrites: + ################################################# + $outputFilePath = $ce->{courseDirs}->{templates} . '/' . + $new_file_name; + + if (defined $outputFilePath and -e $outputFilePath) { + # setting $do_not_save stops saving and any redirects + $do_not_save = 1; + $self->addbadmessage(CGI::p("File $outputFilePath exists. File not saved.")); + } else { + #$self->addgoodmessage("Saving to file $outputFilePath."); + } + $self->{problemPath} = $outputFilePath; + last ACTION_CASES; + }; + ($action eq 'add_problem_to_set') and do { + my $sourceFile = $editFilePath; + my $targetSetName = $r->param('target_set'); + my $freeProblemID = WeBWorK::Utils::max($db->listGlobalProblems($setName)) + 1; + $sourceFile =~ s|^$ce->{courseDirs}->{templates}/||; + my $problemRecord = $self->addProblemToSet( + setName => $targetSetName, + sourceFile => $sourceFile, + problemID => $freeProblemID + ); + $self->assignProblemToAllSetUsers($problemRecord); + $self->addgoodmessage("Added $sourceFile to ". $targetSetName. " as problem $freeProblemID") ; + $outputFilePath = undef; # don't save any files + $self->{problemPath} = $editFilePath; + + }; + ($action eq 'add_set_header_to_set') and do { + my $sourceFile = $editFilePath; + my $targetSetName = $r->param('target_set'); + $sourceFile =~ s|^$ce->{courseDirs}->{templates}/||; + my $setRecord = $db->getGlobalSet($targetSetName); + $setRecord->set_header($sourceFile); + if( $db->putGlobalSet($setRecord) ) { + $self->addgoodmessage("Added $sourceFile to ". $targetSetName. " as new set header ") ; + } else { + $do_not_save = 1 ; + $self->addbadmessage("Unable to make $sourceFile the set header for $targetSetName"); + } + # change file type to set_header if it not already so + $self->{file_type} = 'set_header'; + $outputFilePath = undef; # don't save any files + $self->{problemPath} = $editFilePath; + + }; + last ACTION_CASES; + + die "Unrecognized action command: $action"; + }; # end ACTION_CASES + ############################################################################## @@ -564,36 +996,39 @@ # Make sure that the warning is being transmitted properly. ############################################################################## - # FIXME set a local state rather continue to call on the submit button. - if (defined $submit_button and $submit_button eq 'Save as' and $r->param('save_to_new_file') !~ /\w/) { - # setting $self->{failure} stops any future redirects - $self->{failure} = "Please specify a file to save to."; - $self->addbadmessage(CGI::p("Please specify a file to save to.")); - } elsif (defined $submit_button and $submit_button eq 'Save as' and defined $currentSourceFilePath and -e $currentSourceFilePath) { - # setting $self->{failure} stops any future redirects - $self->{failure} = "File $currentSourceFilePath exists. File not saved."; - $self->addbadmessage(CGI::p("File $currentSourceFilePath exists. File not saved.")); - } else { + my $writeFileErrors; + if ( defined($outputFilePath) and $outputFilePath =~/\S/ and ! $do_not_save ) { # save file + # Handle the problem of line endings. + # Make sure that all of the line endings are of unix type. + # Convert \r\n to \n + $problemContents =~ s/\r\n/\n/g; + $problemContents =~ s/\r/\n/g; + # make sure any missing directories are created - $currentSourceFilePath = WeBWorK::Utils::surePathToFile($ce->{courseDirs}->{templates},$currentSourceFilePath); + $outputFilePath = WeBWorK::Utils::surePathToFile($ce->{courseDirs}->{templates}, + $outputFilePath); + eval { local *OUTPUTFILE; - open OUTPUTFILE, ">", $currentSourceFilePath - or die "Failed to open $currentSourceFilePath"; + open OUTPUTFILE, ">", $outputFilePath + or die "Failed to open $outputFilePath"; print OUTPUTFILE $problemContents; close OUTPUTFILE; }; # any errors are caught in the next block - } + + $writeFileErrors = $@ if $@; + } ########################################################### # Catch errors in saving files, clean up temp files ########################################################### - - my $openTempFileErrors = $@ if $@; - - if ($openTempFileErrors) { - $currentSourceFilePath =~ m|^(/.*?/)[^/]+$|; + $self->{failure} = $do_not_save; # don't do redirects if the file was not saved. + # don't unlink files or send success messages + + if ($writeFileErrors) { + # get the current directory from the outputFilePath + $outputFilePath =~ m|^(/.*?/)[^/]+$|; my $currentDirectory = $1; my $errorMessage; @@ -602,28 +1037,37 @@ $errorMessage = "Write permissions have not been enabled in the templates directory. No changes can be made."; } elsif ( not -w $currentDirectory ) { $errorMessage = "Write permissions have not been enabled in $currentDirectory. Changes must be saved to a different directory for viewing."; - } elsif ( -e $currentSourceFilePath and not -w $currentSourceFilePath ) { - $errorMessage = "Write permissions have not been enabled for $currentSourceFilePath. Changes must be saved to another file for viewing."; + } elsif ( -e $outputFilePath and not -w $outputFilePath ) { + $errorMessage = "Write permissions have not been enabled for $outputFilePath. Changes must be saved to another file for viewing."; } else { - $errorMessage = "Unable to write to $currentSourceFilePath: $openTempFileErrors"; + $errorMessage = "Unable to write to $outputFilePath: $writeFileErrors"; } - $self->{failure} = $errorMessage; + $self->{failure} = 1; $self->addbadmessage(CGI::p($errorMessage)); - } else { - $self->{success} = "Problem saved to: $currentSourceFilePath"; + } + unless( $writeFileErrors or $do_not_save) { # everything worked! unlink and announce success! # unlink the temporary file if there are no errors and the save button has been pushed - unlink("$editFilePath.$editFileSuffix") - if defined $submit_button and ($submit_button eq 'Save' or $submit_button eq 'Save as'); + if ($action eq 'save' or $action eq 'save_as' or $action eq 'revert') { + unlink($self->{tempFilePath}) ; + } + if ( defined($outputFilePath) and ! $self->{failure} ) { + my $msg = "Saved to file: $outputFilePath"; + $self->addgoodmessage($msg); + } + } # return values for use in the body subroutine - $self->{inputFilePath} = $inputFilePath; - $self->{displayMode} = $displayMode; - $self->{problemSeed} = $problemSeed; + # The path to the current permanent file being edited: + # $self->{problemPath} = $editFilePath; + # The path to the current temporary file (if any). If no temporary file this the same + # as the permanent file path: + # $self->{outputFilePath} = $outputFilePath; + # + $self->{r_problemContents} = \$problemContents; - $self->{editFileSuffix} = $editFileSuffix; -} +} # end saveFileChanges 1; |