[system] / trunk / webwork2 / lib / WeBWorK / ContentGenerator / Instructor / PGProblemEditor.pm Repository:
ViewVC logotype

Annotation of /trunk/webwork2/lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3876 - (view) (download) (as text)

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

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9