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