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