| 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.22 2003/12/12 02:24:30 gage Exp $ |
4 | # $CVSHeader: webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm,v 1.29 2004/05/06 21:53:41 jj 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. |
| … | |
… | |
| 27 | use strict; |
27 | use strict; |
| 28 | use warnings; |
28 | use warnings; |
| 29 | use CGI qw(); |
29 | use CGI qw(); |
| 30 | use WeBWorK::Utils qw(readFile); |
30 | use WeBWorK::Utils qw(readFile); |
| 31 | use Apache::Constants qw(:common REDIRECT); |
31 | use Apache::Constants qw(:common REDIRECT); |
|
|
32 | use HTML::Entities; |
|
|
33 | use WeBWorK::Utils::Tasks qw(fake_set fake_problem); |
| 32 | |
34 | |
|
|
35 | ########################################################### |
|
|
36 | # 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 |
|
|
38 | # |
|
|
39 | # Only files under the template directory ( or linked to this location) can be edited. |
|
|
40 | # |
|
|
41 | # The course information and problems are located in the course templates directory. |
|
|
42 | # Course information has the name defined by courseFiles->{course_info} |
|
|
43 | # |
|
|
44 | # Only files under the template directory ( or linked to this location) can be edited. |
|
|
45 | # |
|
|
46 | # editMode = temporaryFile (view the temp file defined by course_info.txt.user_name.tmp |
|
|
47 | # instead of the file course_info.txt) |
|
|
48 | # The editFileSuffix is "user_name.tmp" by default. It's definition should be moved to Instructor.pm #FIXME |
|
|
49 | ########################################################### |
| 33 | |
50 | |
| 34 | our $libraryName; |
51 | #our $libraryName; |
| 35 | our $rowheight; |
52 | #our $rowheight; |
|
|
53 | |
|
|
54 | sub pre_header_initialize { |
|
|
55 | my ($self) = @_; |
|
|
56 | my $r = $self->r; |
|
|
57 | my $ce = $r->ce; |
|
|
58 | my $urlpath = $r->urlpath; |
|
|
59 | |
|
|
60 | my $submit_button = $r->param('submit'); # obtain submit command from form |
|
|
61 | my $file_type = $r->param("file_type") || ''; |
|
|
62 | |
|
|
63 | # Save problem to permanent or temporary file, then redirect for viewing |
|
|
64 | if (defined($submit_button) and |
|
|
65 | ($submit_button eq 'Save' or $submit_button eq 'Refresh' |
|
|
66 | or ($submit_button eq 'Save as' and $file_type eq 'problem'))) { |
|
|
67 | my $setName = $r->urlpath->arg("setID"); |
|
|
68 | my $problemNumber = $r->urlpath->arg("problemID"); |
|
|
69 | |
|
|
70 | # write the necessary files |
|
|
71 | # return file path for viewing problem in $self->{currentSourceFilePath} |
|
|
72 | # obtain the appropriate seed |
|
|
73 | $self->saveFileChanges($setName, $problemNumber); |
|
|
74 | |
|
|
75 | ##### calculate redirect URL based on file type ##### |
|
|
76 | |
|
|
77 | # get some information |
|
|
78 | #my $hostname = $r->hostname(); |
|
|
79 | #my $port = $r->get_server_port(); |
|
|
80 | #my $uri = $r->uri; |
|
|
81 | my $courseName = $urlpath->arg("courseID"); |
|
|
82 | my $problemSeed = ($r->param('problemSeed')) ? $r->param('problemSeed') : ''; |
|
|
83 | my $displayMode = ($r->param('displayMode')) ? $r->param('displayMode') : ''; |
|
|
84 | |
|
|
85 | my $viewURL = ''; |
|
|
86 | |
|
|
87 | if($self->{file_type} eq 'problem') { |
|
|
88 | if($submit_button eq 'Save as') { # redirect to myself |
|
|
89 | my $sourceFile = $self->{problemPath}; |
|
|
90 | # strip off template directory prefix |
|
|
91 | my $edit_level = $r->param("edit_level") || 0; |
|
|
92 | $edit_level++; |
|
|
93 | $sourceFile =~ s|^$ce->{courseDirs}->{templates}/||; |
|
|
94 | my $problemPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::PGProblemEditor", |
|
|
95 | courseID => $courseName, setID => 'Undefined_Set', problemID => $problemNumber); |
|
|
96 | $viewURL = $self->systemLink($problemPage, |
|
|
97 | params=>{sourceFilePath => $sourceFile, edit_level=>$edit_level}); |
|
|
98 | } else { # other problems redirect to Problem.pm |
|
|
99 | my $problemPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Problem", |
|
|
100 | courseID => $courseName, setID => $setName, problemID => $problemNumber); |
|
|
101 | $viewURL = $self->systemLink($problemPage, |
|
|
102 | params => { |
|
|
103 | displayMode => $displayMode, |
|
|
104 | problemSeed => $problemSeed, |
|
|
105 | editMode => ($submit_button eq "Save" ? "savedFile" : "temporaryFile"), |
|
|
106 | sourceFilePath => $self->{currentSourceFilePath}, |
|
|
107 | submiterror => $self->{submiterror}, |
|
|
108 | } |
|
|
109 | ); |
|
|
110 | } |
|
|
111 | } |
|
|
112 | |
|
|
113 | # set headers redirect to ProblemSet.pm |
|
|
114 | $self->{file_type} eq 'set_header' and do { |
|
|
115 | my $problemSetPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::ProblemSet", |
|
|
116 | courseID => $courseName, setID => $setName); |
|
|
117 | $viewURL = $self->systemLink($problemSetPage, |
|
|
118 | params => { |
|
|
119 | displayMode => $displayMode, |
|
|
120 | problemSeed => $problemSeed, |
|
|
121 | editMode => ($submit_button eq "Save" ? "savedFile" : "temporaryFile"), |
|
|
122 | } |
|
|
123 | ); |
|
|
124 | }; |
|
|
125 | |
|
|
126 | # course info redirects to ProblemSets.pm |
|
|
127 | $self->{file_type} eq 'course_info' and do { |
|
|
128 | my $problemSetsPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::ProblemSets", |
|
|
129 | courseID => $courseName); |
|
|
130 | $viewURL = $self->systemLink($problemSetsPage, |
|
|
131 | params => { |
|
|
132 | editMode => ($submit_button eq "Save" ? "savedFile" : "temporaryFile"), |
|
|
133 | } |
|
|
134 | ); |
|
|
135 | }; |
|
|
136 | |
|
|
137 | if ($viewURL) { |
|
|
138 | $self->reply_with_redirect($viewURL); |
|
|
139 | } else { |
|
|
140 | die "Invalid file_type ", $self->{file_type}, " specified by saveFileChanges"; |
|
|
141 | } |
|
|
142 | } |
|
|
143 | } |
|
|
144 | |
|
|
145 | sub initialize { |
|
|
146 | my ($self) = @_; |
|
|
147 | my $r = $self->r; |
|
|
148 | |
|
|
149 | my $setName = $r->urlpath->arg("setID"); |
|
|
150 | my $problemNumber = $r->urlpath->arg("problemID"); |
|
|
151 | |
|
|
152 | # if we got to initialize(), then saveFileChanges was not called in pre_header_initialize(). |
|
|
153 | # therefore we call it here: |
|
|
154 | $self->saveFileChanges($setName, $problemNumber); |
|
|
155 | } |
| 36 | |
156 | |
| 37 | sub title { |
157 | sub title { |
| 38 | my $self = shift; |
158 | my $self = shift; |
| 39 | #FIXME don't need the entire path ?? |
159 | my $r = $self->r; |
| 40 | return "Instructor Tools - PG Problem Editor "; |
160 | my $problemNumber = $r->urlpath->arg("problemID"); |
|
|
161 | my $file_type = $self->{'file_type'} || ''; |
|
|
162 | return "Set Header" if($file_type eq 'set_header'); |
|
|
163 | return "Course Information" if($file_type eq 'course_info'); |
|
|
164 | return 'Problem ' . $r->{urlpath}->name; |
| 41 | } |
165 | } |
| 42 | |
166 | |
| 43 | sub header { #FIXME this should be moved up to ContentGenerator |
167 | sub body { |
| 44 | my $self = shift; |
168 | my ($self) = @_; |
| 45 | return REDIRECT if $self->{noContent}; |
|
|
| 46 | my $r = $self->{r}; |
169 | my $r = $self->r; |
| 47 | $r->content_type('text/html'); |
170 | my $db = $r->db; |
| 48 | $r->send_http_header(); |
171 | my $ce = $r->ce; |
| 49 | return OK; |
172 | |
|
|
173 | # Gathering info |
|
|
174 | my $editFilePath = $self->{problemPath}; # path to the permanent file to be edited |
|
|
175 | my $inputFilePath = $self->{inputFilePath}; # path to the file currently being worked with (might be a .tmp file) |
|
|
176 | |
|
|
177 | my $header = CGI::i("Editing problem: $inputFilePath"); |
|
|
178 | |
|
|
179 | ######################################################################### |
|
|
180 | # Find the text for the problem, either in the tmp file, if it exists |
|
|
181 | # or in the original file in the template directory |
|
|
182 | ######################################################################### |
|
|
183 | |
|
|
184 | my $problemContents = ${$self->{r_problemContents}}; |
|
|
185 | |
|
|
186 | ######################################################################### |
|
|
187 | # Format the page |
|
|
188 | ######################################################################### |
|
|
189 | |
|
|
190 | # Define parameters for textarea |
|
|
191 | # FIXME |
|
|
192 | # Should the seed be set from some particular user instance?? |
|
|
193 | # The mode list should be obtained from global.conf ultimately |
|
|
194 | my $rows = 20; |
|
|
195 | my $columns = 80; |
|
|
196 | my $mode_list = ['plainText','formattedText','images']; |
|
|
197 | my $displayMode = $self->{displayMode}; |
|
|
198 | my $problemSeed = $self->{problemSeed}; |
|
|
199 | my $uri = $r->uri; |
|
|
200 | my $edit_level = $r->param('edit_level') || 0; |
|
|
201 | |
|
|
202 | my $force_field = defined($r->param('sourceFilePath')) ? |
|
|
203 | CGI::hidden(-name=>'sourceFilePath', |
|
|
204 | -default=>$r->param('sourceFilePath')) : ''; |
|
|
205 | return CGI::p($header), |
|
|
206 | #CGI::start_form("POST",$r->uri,-target=>'_problem'), doesn't pass on the target parameter??? |
|
|
207 | # THIS IS BECAUSE TARGET IS NOT A PARAMETER OF <FORM>!!!!!!!! |
|
|
208 | qq!<form method="POST" action="$uri" enctype="application/x-www-form-urlencoded", target="problem$edit_level">!, |
|
|
209 | $self->hidden_authen_fields, |
|
|
210 | $force_field, |
|
|
211 | CGI::hidden(-name=>'file_type',-default=>$self->{file_type}), |
|
|
212 | CGI::div( |
|
|
213 | 'Seed: ', |
|
|
214 | CGI::textfield(-name=>'problemSeed',-value=>$problemSeed), |
|
|
215 | 'Mode: ', |
|
|
216 | CGI::popup_menu(-name=>'displayMode', -values=>$mode_list, -default=>$displayMode), |
|
|
217 | CGI::a({-href=>'http://webwork.math.rochester.edu/docs/docs/pglanguage/manpages/',-target=>"manpage_window"}, |
|
|
218 | 'Manpages', |
|
|
219 | ) |
|
|
220 | ), |
|
|
221 | CGI::p( |
|
|
222 | CGI::textarea( |
|
|
223 | -name => 'problemContents', -default => $problemContents, |
|
|
224 | -rows => $rows, -columns => $columns, -override => 1, |
|
|
225 | ), |
|
|
226 | ), |
|
|
227 | CGI::p( |
|
|
228 | CGI::submit(-value=>'Refresh',-name=>'submit'), |
|
|
229 | CGI::submit(-value=>'Save', -name=>'submit'), |
|
|
230 | CGI::submit(-value=>'Revert', -name=>'submit'), |
|
|
231 | CGI::submit(-value=>'Save as',-name=>'submit'), |
|
|
232 | CGI::textfield(-name=>'save_to_new_file', -value=>""), |
|
|
233 | ), |
|
|
234 | CGI::end_form(), |
| 50 | } |
235 | } |
| 51 | |
236 | |
| 52 | ########################################################### |
237 | ################################################################################ |
| 53 | # This editor will edit problem files or set header files or files, such as course_info |
238 | # Utilities |
| 54 | # whose name is defined in the global.conf database |
|
|
| 55 | # |
|
|
| 56 | # Only files under the template directory ( or linked to this location) can be edited. |
|
|
| 57 | # |
|
|
| 58 | # The course information and problems are located in the course templates directory. |
|
|
| 59 | # Course information has the name defined by courseFiles->{course_info} |
|
|
| 60 | # |
|
|
| 61 | # Only files under the template directory ( or linked to this location) can be edited. |
|
|
| 62 | # |
|
|
| 63 | # editMode = temporaryFile (view the temp file defined by course_info.txt.user_name.tmp |
|
|
| 64 | # instead of the file course_info.txt) |
|
|
| 65 | # The editFileSuffix is "user_name.tmp" by default. It's definition should be moved to Instructor.pm #FIXME |
|
|
| 66 | ########################################################### |
239 | ################################################################################ |
| 67 | |
240 | |
| 68 | sub pre_header_initialize { |
241 | # saveFileChanges does most of the work. it is a separate method so that it can |
| 69 | my $self = shift; |
242 | # be called from either pre_header_initialize() or initilize(), depending on |
|
|
243 | # whether a redirect is needed or not. |
|
|
244 | # |
|
|
245 | # it actually does a lot more than save changes to the file being edited, and |
|
|
246 | # sometimes less. |
|
|
247 | |
|
|
248 | sub saveFileChanges { |
| 70 | my ($setName, $problemNumber) = @_; |
249 | my ($self, $setName, $problemNumber) = @_; |
| 71 | my $r = $self->{r}; |
250 | my $r = $self->r; |
| 72 | my $ce = $self->{ce}; |
251 | my $ce = $r->ce; |
| 73 | my $submit_button = $r->param('submit'); # obtain submit command from form |
252 | my $db = $r->db; |
| 74 | |
253 | my $urlpath = $r->urlpath; |
| 75 | ##################################################### |
|
|
| 76 | # Save problem to permanent or temporary file |
|
|
| 77 | # Then redirect for viewing |
|
|
| 78 | ##################################################### |
|
|
| 79 | if ( defined($submit_button) and ($submit_button eq 'Save' or $submit_button eq 'Refresh') ) { |
|
|
| 80 | |
254 | |
| 81 | $self->saveFileChanges($setName,$problemNumber); # write the necessary files |
255 | my $courseName = $urlpath->arg("courseID"); |
| 82 | # return file path for viewing problem |
256 | my $user = $r->param('user'); |
| 83 | # in $self->{currentSourceFilePath} |
257 | my $effectiveUserName = $r->param('effectiveUser'); |
| 84 | # obtain the appropriate seed. |
258 | |
| 85 | #redirect to view the problem |
259 | $setName = '' unless defined $setName; |
|
|
260 | $problemNumber = '' unless defined $problemNumber; |
|
|
261 | |
|
|
262 | ##### Determine path to the file to be edited. ##### |
|
|
263 | |
|
|
264 | my $editFilePath = $ce->{courseDirs}->{templates}; |
|
|
265 | my $problem_record = undef; |
|
|
266 | |
|
|
267 | my $file_type = $r->param("file_type") || ''; |
|
|
268 | |
|
|
269 | if ($file_type eq 'course_info') { |
|
|
270 | # we are editing the course_info file |
|
|
271 | $self->{file_type} = 'course_info'; |
| 86 | |
272 | |
| 87 | my $hostname = $r->hostname(); |
273 | # value of courseFiles::course_info is relative to templates directory |
| 88 | my $port = $r->get_server_port(); |
274 | $editFilePath .= '/' . $ce->{courseFiles}->{course_info}; |
| 89 | my $uri = $r->uri; |
275 | } else { |
| 90 | my $courseName = $self->{ce}->{courseName}; |
276 | # we are editing a problem file or a set header file |
| 91 | my $problemSeed = ($r->param('problemSeed')) ? $r->param('problemSeed') : ''; |
277 | |
| 92 | my $displayMode = ($r->param('displayMode')) ? $r->param('displayMode') : ''; |
278 | # FIXME there is a discrepancy in the way that the problems are found. |
| 93 | my $viewURL = ''; |
279 | # FIXME more error checking is needed in case the problem doesn't exist. |
| 94 | if ($self->{file_type} eq 'problem') { |
280 | # (i wonder what the above comments mean... -sam) |
| 95 | # redirect to have problem read by Problem.pm |
281 | |
| 96 | $viewURL = "http://$hostname:$port"; |
282 | if (defined $problemNumber) { |
| 97 | $viewURL .= $ce->{webworkURLs}->{root}."/$courseName/$setName/$problemNumber/?"; |
283 | if ($problemNumber == 0) { |
| 98 | $viewURL .= $self->url_authen_args; |
284 | # we are editing a header file |
| 99 | $viewURL .= "&displayMode=$displayMode&problemSeed=$problemSeed"; # optional displayMode and problemSeed overrides |
285 | $self->{file_type} = 'set_header'; |
| 100 | if ($submit_button eq 'Save') { |
286 | |
| 101 | $viewURL .= "&editMode=savedFile"; |
287 | # first try getting the merged set for the effective user |
|
|
288 | my $set_record = $db->getMergedSet($effectiveUserName, $setName); # checked |
|
|
289 | |
|
|
290 | # if that doesn't work (the set is not yet assigned), get the global record |
|
|
291 | $set_record = $db->getGlobalSet($setName); # checked |
|
|
292 | |
|
|
293 | # bail if no set is found |
|
|
294 | die "Cannot find a set record for set $setName" unless defined($set_record); |
|
|
295 | |
|
|
296 | $editFilePath .= '/' . $set_record->set_header; |
| 102 | } else { |
297 | } else { |
| 103 | $viewURL .= "&editMode=temporaryFile"; |
298 | # we are editing a "real" problem |
|
|
299 | $self->{file_type} = 'problem'; |
|
|
300 | |
|
|
301 | # first try getting the merged problem for the effective user |
|
|
302 | $problem_record = $db->getMergedProblem($effectiveUserName, $setName, $problemNumber); # checked |
|
|
303 | |
|
|
304 | # if that doesn't work (the problem is not yet assigned), get the global record |
|
|
305 | $problem_record = $db->getGlobalProblem($setName, $problemNumber) unless defined($problem_record); # checked |
|
|
306 | |
|
|
307 | |
|
|
308 | if(not defined($problem_record)) { |
|
|
309 | my $forcedSourceFile = $r->param('sourceFilePath'); |
|
|
310 | # bail if no problem is found and we aren't faking it |
|
|
311 | die "Cannot find a problem record for set $setName / problem $problemNumber" unless defined($forcedSourceFile); |
|
|
312 | $problem_record = fake_problem($db); |
|
|
313 | $problem_record->problem_id($problemNumber); |
|
|
314 | $problem_record->source_file($forcedSourceFile); |
|
|
315 | } |
|
|
316 | |
|
|
317 | |
|
|
318 | $editFilePath .= '/' . $problem_record->source_file; |
| 104 | } |
319 | } |
| 105 | $viewURL .= '&sourceFilePath='. $self->{currentSourceFilePath}; # path to pg text for viewing |
|
|
| 106 | # allows Problem.pg to recognize state |
|
|
| 107 | # of problem being viewed. |
|
|
| 108 | } elsif ($self->{file_type} eq 'set_header') { |
|
|
| 109 | # redirect set headers to ProblemList page |
|
|
| 110 | $viewURL = "http://$hostname:$port"; |
|
|
| 111 | $viewURL .= $ce->{webworkURLs}->{root}."/$courseName/$setName/?"; |
|
|
| 112 | $viewURL .= $self->url_authen_args; |
|
|
| 113 | $viewURL .= "&displayMode=$displayMode&problemSeed=$problemSeed"; # optional displayMode and problemSeed overrides |
|
|
| 114 | if ($submit_button eq 'Save') { |
|
|
| 115 | $viewURL .= "&editMode=savedFile"; |
|
|
| 116 | } else { |
|
|
| 117 | $viewURL .= "&editMode=temporaryFile"; |
|
|
| 118 | } |
|
|
| 119 | } elsif ($self->{file_type} eq 'course_info' ) { |
|
|
| 120 | $viewURL = "http://$hostname:$port"; |
|
|
| 121 | $viewURL .= $ce->{webworkURLs}->{root}."/$courseName/?"; |
|
|
| 122 | $viewURL .= $self->url_authen_args; |
|
|
| 123 | if ($submit_button eq 'Save') { |
|
|
| 124 | $viewURL .= "&editMode=savedFile"; |
|
|
| 125 | } else { |
|
|
| 126 | $viewURL .= "&editMode=temporaryFile"; |
|
|
| 127 | } |
|
|
| 128 | } else { |
|
|
| 129 | warn "PGProblemEditor does not have facilities for editing files with file_type ".$self->{file_type}; |
|
|
| 130 | } |
320 | } |
| 131 | |
|
|
| 132 | $r->header_out(Location => $viewURL ); |
|
|
| 133 | $self->{noContent} = 1; # forces redirect |
|
|
| 134 | return; |
|
|
| 135 | } |
|
|
| 136 | |
|
|
| 137 | } |
|
|
| 138 | |
|
|
| 139 | sub initialize { |
|
|
| 140 | my $self = shift; |
|
|
| 141 | my ($setName, $problemNumber) = @_; |
|
|
| 142 | $self -> saveFileChanges(@_); |
|
|
| 143 | |
|
|
| 144 | |
|
|
| 145 | |
|
|
| 146 | } |
|
|
| 147 | |
|
|
| 148 | sub saveFileChanges { |
|
|
| 149 | |
|
|
| 150 | my ($self, $setName, $problemNumber) = @_; |
|
|
| 151 | my $ce = $self->{ce}; |
|
|
| 152 | my $r = $self->{r}; |
|
|
| 153 | my $path_info = $r->path_info || ""; |
|
|
| 154 | my $db = $self->{db}; |
|
|
| 155 | my $user = $r->param('user'); |
|
|
| 156 | my $effectiveUserName = $r->param('effectiveUser'); |
|
|
| 157 | my $courseName = $ce->{courseName}; |
|
|
| 158 | |
|
|
| 159 | $setName = '' unless defined $setName; |
|
|
| 160 | $problemNumber = '' unless defined $problemNumber; |
|
|
| 161 | |
|
|
| 162 | ################################################## |
|
|
| 163 | # Determine path to the file to be edited. |
|
|
| 164 | ################################################## |
|
|
| 165 | my $templateDirectory = $ce->{courseDirs}->{templates}; |
|
|
| 166 | my $editFilePath = $templateDirectory; |
|
|
| 167 | my $problem_record = undef; |
|
|
| 168 | |
|
|
| 169 | my $file_type = $r->param("file_type") || ''; |
|
|
| 170 | |
|
|
| 171 | if ($file_type eq 'course_info' ) { |
|
|
| 172 | $editFilePath .= '/'. $ce->{courseFiles}->{course_info}; |
|
|
| 173 | $self->{file_type} = 'course_info'; |
|
|
| 174 | # no problem_record is defined in this case |
|
|
| 175 | |
|
|
| 176 | } else { # we areediting a problem file or a set header file |
|
|
| 177 | |
|
|
| 178 | # FIXME there is a discrepancy in the way that the problems are found. |
|
|
| 179 | # FIXME more error checking is needed in case the problem doesn't exist. |
|
|
| 180 | if (defined($problemNumber) and $problemNumber) { |
|
|
| 181 | $problem_record = $db->getMergedProblem($effectiveUserName, $setName, $problemNumber); # checked |
|
|
| 182 | # If there is no global_user defined problem, (i.e. the sets haven't been assigned yet), |
|
|
| 183 | # look for a global version of the problem. |
|
|
| 184 | $problem_record = $db->getGlobalProblem($setName, $problemNumber) unless defined($problem_record); # checked |
|
|
| 185 | # bail if no problem is found |
|
|
| 186 | die "Cannot find a problem record for set $setName / problem $problemNumber" |
|
|
| 187 | unless defined($problem_record); |
|
|
| 188 | $editFilePath .= '/'.$problem_record->source_file; |
|
|
| 189 | $self->{file_type} = 'problem'; |
|
|
| 190 | } elsif (defined($problemNumber) and $problemNumber==0) { # we are editing a header file |
|
|
| 191 | my $set_record = $db->getMergedSet($effectiveUserName, $setName); # checked |
|
|
| 192 | die "Cannot find a set record for set $setName" unless defined($set_record); |
|
|
| 193 | $editFilePath .= '/'.$set_record->set_header; |
|
|
| 194 | $self->{file_type} = 'set_header'; |
|
|
| 195 | } |
321 | } |
| 196 | } |
|
|
| 197 | |
322 | |
| 198 | |
|
|
| 199 | |
|
|
| 200 | |
|
|
| 201 | my $editFileSuffix = $user.'.tmp'; |
323 | my $editFileSuffix = $user.'.tmp'; |
| 202 | my $submit_button = $r->param('submit'); |
324 | my $submit_button = $r->param('submit'); |
| 203 | |
|
|
| 204 | |
325 | |
| 205 | ############################################################################## |
326 | ############################################################################## |
| 206 | # Determine the display mode |
327 | # Determine the display mode |
| 207 | # try to get problem seed from the input parameter, or from the problem record |
328 | # try to get problem seed from the input parameter, or from the problem record |
| 208 | # This will be needed for viewing the problem via redirect. |
329 | # This will be needed for viewing the problem via redirect. |
| 209 | # They are also two of the parameters which can be set by the editor |
330 | # They are also two of the parameters which can be set by the editor |
| 210 | ############################################################################## |
331 | ############################################################################## |
| 211 | my $displayMode = ( defined($r->param('displayMode')) ) ? $r->param('displayMode') : $ce->{pg}->{options}->{displayMode}; |
332 | |
| 212 | |
333 | my $displayMode; |
|
|
334 | if (defined $r->param('displayMode')) { |
|
|
335 | $displayMode = $r->param('displayMode'); |
|
|
336 | } else { |
|
|
337 | $displayMode = $ce->{pg}->{options}->{displayMode}; |
|
|
338 | } |
|
|
339 | |
| 213 | my $problemSeed; |
340 | my $problemSeed; |
| 214 | if ( defined($r->param('problemSeed')) ) { |
341 | if (defined $r->param('problemSeed')) { |
| 215 | $problemSeed = $r->param('problemSeed'); |
342 | $problemSeed = $r->param('problemSeed'); |
| 216 | } elsif (defined($problem_record) and $problem_record->can('problem_seed')) { |
343 | } elsif (defined($problem_record) and $problem_record->can('problem_seed')) { |
| 217 | $problemSeed = $problem_record->problem_seed; |
344 | $problemSeed = $problem_record->problem_seed; |
| 218 | } |
345 | } |
|
|
346 | |
| 219 | # make absolutely sure that the problem seed is defined, if it hasn't been. |
347 | # make absolutely sure that the problem seed is defined, if it hasn't been. |
| 220 | $problemSeed = '123456' unless defined($problemSeed) and $problemSeed =~/\S/; |
348 | $problemSeed = '123456' unless defined $problemSeed and $problemSeed =~/\S/; |
| 221 | |
349 | |
| 222 | ############################################################################## |
350 | ############################################################################## |
| 223 | # read and update the targetFile and targetFile.tmp files in the directory |
351 | # read and update the targetFile and targetFile.tmp files in the directory |
| 224 | # if a .tmp file already exists use that, unless the revert button has been pressed. |
352 | # if a .tmp file already exists use that, unless the revert button has been pressed. |
| 225 | # These .tmp files are |
353 | # These .tmp files are |
| 226 | # removed when the file is finally saved. |
354 | # removed when the file is finally saved. |
| 227 | ############################################################################## |
355 | ############################################################################## |
| 228 | |
356 | |
| 229 | my $problemContents = ''; |
357 | my $problemContents = ''; |
| 230 | my $currentSourceFilePath = ''; |
358 | my $currentSourceFilePath = ''; |
| 231 | my $editErrors = ''; |
359 | my $editErrors = ''; |
| 232 | |
360 | |
| 233 | my $inputFilePath = (-r "$editFilePath.$editFileSuffix")?"$editFilePath.$editFileSuffix" : $editFilePath; |
361 | my $inputFilePath; |
|
|
362 | if (-r "$editFilePath.$editFileSuffix") { |
|
|
363 | $inputFilePath = "$editFilePath.$editFileSuffix"; |
|
|
364 | } else { |
|
|
365 | $inputFilePath = $editFilePath; |
|
|
366 | } |
|
|
367 | |
| 234 | $inputFilePath = $editFilePath if defined($submit_button) and $submit_button eq 'Revert'; |
368 | $inputFilePath = $editFilePath if defined($submit_button) and $submit_button eq 'Revert'; |
| 235 | |
369 | |
|
|
370 | ##### handle button clicks ##### |
|
|
371 | |
| 236 | if (not defined($submit_button) or $submit_button eq 'Revert' ) { |
372 | if (not defined $submit_button or $submit_button eq 'Revert' ) { |
| 237 | # this is a fresh editing job |
373 | # this is a fresh editing job |
| 238 | # copy the pg file to a new file with the same name with .tmp added |
374 | # copy the pg file to a new file with the same name with .tmp added |
| 239 | # store this name in the $self->currentSourceFilePath for use in body |
375 | # store this name in the $self->currentSourceFilePath for use in body |
| 240 | eval { $problemContents = WeBWorK::Utils::readFile($inputFilePath) }; |
376 | |
| 241 | # try to read file |
377 | # try to read file |
|
|
378 | if(-e $inputFilePath) { |
|
|
379 | eval { $problemContents = WeBWorK::Utils::readFile($inputFilePath) }; |
| 242 | $problemContents = $@ if $@; |
380 | $problemContents = $@ if $@; |
| 243 | $editErrors .= $problemContents; |
381 | } else { # file not existing is not an error |
|
|
382 | $problemContents = ''; |
|
|
383 | } |
|
|
384 | |
| 244 | $currentSourceFilePath = "$editFilePath.$editFileSuffix"; |
385 | $currentSourceFilePath = "$editFilePath.$editFileSuffix"; |
| 245 | $self->{currentSourceFilePath} = $currentSourceFilePath; |
386 | $self->{currentSourceFilePath} = $currentSourceFilePath; |
|
|
387 | $self->{problemPath} = $editFilePath; |
| 246 | } elsif ($submit_button eq 'Refresh' ) { |
388 | } elsif ($submit_button eq 'Refresh') { |
| 247 | # grab the problemContents from the form in order to save it to the tmp file |
389 | # grab the problemContents from the form in order to save it to the tmp file |
| 248 | # store tmp file name in the $self->currentSourceFilePath for use in body |
390 | # store tmp file name in the $self->currentSourceFilePath for use in body |
| 249 | |
|
|
| 250 | $problemContents = $r->param('problemContents'); |
391 | $problemContents = $r->param('problemContents'); |
|
|
392 | |
| 251 | $currentSourceFilePath = "$editFilePath.$editFileSuffix"; |
393 | $currentSourceFilePath = "$editFilePath.$editFileSuffix"; |
| 252 | $self->{currentSourceFilePath} = $currentSourceFilePath; |
394 | $self->{currentSourceFilePath} = $currentSourceFilePath; |
|
|
395 | $self->{problemPath} = $editFilePath; |
| 253 | } elsif ($submit_button eq 'Save') { |
396 | } elsif ($submit_button eq 'Save') { |
| 254 | # grab the problemContents from the form in order to save it to the permanent file |
397 | # grab the problemContents from the form in order to save it to the permanent file |
| 255 | # later we will unlink (delete) the temporary file |
398 | # later we will unlink (delete) the temporary file |
| 256 | # store permanent file name in the $self->currentSourceFilePath for use in body |
399 | # store permanent file name in the $self->currentSourceFilePath for use in body |
| 257 | |
|
|
| 258 | $problemContents = $r->param('problemContents'); |
400 | $problemContents = $r->param('problemContents'); |
|
|
401 | |
| 259 | $currentSourceFilePath = "$editFilePath"; |
402 | $currentSourceFilePath = "$editFilePath"; |
| 260 | $self->{currentSourceFilePath} = $currentSourceFilePath; |
403 | $self->{currentSourceFilePath} = $currentSourceFilePath; |
|
|
404 | $self->{problemPath} = $editFilePath; |
|
|
405 | } elsif ($submit_button eq 'Save as') { |
|
|
406 | # grab the problemContents from the form in order to save it to a new permanent file |
|
|
407 | # later we will unlink (delete) the current temporary file |
|
|
408 | # store new permanent file name in the $self->currentSourceFilePath for use in body |
|
|
409 | $problemContents = $r->param('problemContents'); |
|
|
410 | $currentSourceFilePath = $ce->{courseDirs}->{templates} . '/' .$r->param('save_to_new_file'); |
|
|
411 | $self->{currentSourceFilePath} = $currentSourceFilePath; |
|
|
412 | $self->{problemPath} = $currentSourceFilePath; |
| 261 | } else { |
413 | } else { |
| 262 | # give a warning |
|
|
| 263 | die "Unrecognized submit command $submit_button"; |
414 | die "Unrecognized submit command: $submit_button"; |
| 264 | } |
415 | } |
| 265 | |
416 | |
| 266 | # Handle the problem of line endings. Make sure that all of the line endings. Convert \r\n to \n |
417 | # Handle the problem of line endings. Make sure that all of the line endings. Convert \r\n to \n |
| 267 | $problemContents =~ s/\r\n/\n/g; |
418 | $problemContents =~ s/\r\n/\n/g; |
| 268 | $problemContents =~ s/\r/\n/g; |
419 | $problemContents =~ s/\r/\n/g; |
| 269 | |
420 | |
| 270 | # FIXME convert all double returns to paragraphs |
421 | # FIXME convert all double returns to paragraphs for .txt files |
|
|
422 | # instead of doing this here, it should be done n the PLACE WHERE THE FILE IS DISPLAYED!!! |
|
|
423 | #if ($self->{file_type} eq 'course_info' ) { |
| 271 | $problemContents =~ s/\n\n/\n<p>\n/g; |
424 | # $problemContents =~ s/\n\n/\n<p>\n/g; |
| 272 | ############################################################################## |
|
|
| 273 | # |
425 | #} |
|
|
426 | |
|
|
427 | ############################################################################## |
| 274 | # write changes to the approriate files |
428 | # write changes to the approriate files |
| 275 | # FIXME make sure that the permissions are set correctly!!! |
429 | # FIXME make sure that the permissions are set correctly!!! |
| 276 | # Make sure that the warning is being transmitted properly. |
430 | # Make sure that the warning is being transmitted properly. |
| 277 | ############################################################################## |
431 | ############################################################################## |
|
|
432 | |
|
|
433 | # FIXME set a local state rather continue to call on the submit button. |
|
|
434 | if (defined $submit_button and $submit_button eq 'Save as' and defined $currentSourceFilePath and -e $currentSourceFilePath) { |
|
|
435 | warn "File $currentSourceFilePath exists. File not saved."; |
|
|
436 | } else { |
| 278 | eval { |
437 | eval { |
| 279 | local *OUTPUTFILE; |
438 | local *OUTPUTFILE; |
| 280 | open OUTPUTFILE, ">", $currentSourceFilePath |
439 | open OUTPUTFILE, ">", $currentSourceFilePath |
| 281 | or die "Failed to write to $currentSourceFilePath. |
440 | or die "Failed to open $currentSourceFilePath"; |
| 282 | It is likely that the permissions in the template directory have not been set correctly.". |
|
|
| 283 | "The web server must be able to create and write to files in the directory containing the problem. |
|
|
| 284 | $!"; |
|
|
| 285 | print OUTPUTFILE $problemContents; |
441 | print OUTPUTFILE $problemContents; |
| 286 | close OUTPUTFILE; |
442 | close OUTPUTFILE; |
|
|
443 | }; # any errors are caught in the next block |
|
|
444 | |
| 287 | }; |
445 | } |
| 288 | # record an error string for later use if there was a difficulty in writing to the file |
|
|
| 289 | # FIXME is this string ever inspected? |
|
|
| 290 | |
446 | |
|
|
447 | ########################################################### |
|
|
448 | # Catch errors in saving files, clean up temp files |
|
|
449 | ########################################################### |
|
|
450 | |
| 291 | my $openTempFileErrors = $@ if $@; |
451 | my $openTempFileErrors = $@ if $@; |
| 292 | |
452 | |
| 293 | if ( $openTempFileErrors) { |
453 | if ($openTempFileErrors) { |
| 294 | |
454 | $self->{submiterror} = "Unable to write to $currentSourceFilePath: It is likely that the permissions in the template directory have not been set correctly. See log for details."; |
| 295 | $self->{openTempFileErrors} = "Unable to write to $currentSourceFilePath: $openTempFileErrors"; |
|
|
| 296 | #diagnose errors: |
455 | #diagnose errors: |
| 297 | warn "Editing errors: $openTempFileErrors\n"; |
456 | warn "Unable to write to $currentSourceFilePath: $openTempFileErrors"; |
| 298 | warn "The file $currentSourceFilePath exists. \n " if -e $currentSourceFilePath; #FIXME |
457 | warn "The file $currentSourceFilePath exists. \n " if -e $currentSourceFilePath; #FIXME |
| 299 | warn "The file $currentSourceFilePath cannot be found. \n " unless -e $currentSourceFilePath; |
458 | warn "The file $currentSourceFilePath cannot be found. \n " unless -e $currentSourceFilePath; |
| 300 | warn "The file $currentSourceFilePath does not have write permissions. \n" |
459 | warn "The file $currentSourceFilePath does not have write permissions. \n" |
| 301 | if -e $currentSourceFilePath and not -w $currentSourceFilePath; |
460 | if -e $currentSourceFilePath and not -w $currentSourceFilePath; |
| 302 | |
|
|
| 303 | |
|
|
| 304 | |
|
|
| 305 | } else { |
461 | } else { |
| 306 | # unlink the temporary file if there are no errors and the save button has been pushed |
462 | # unlink the temporary file if there are no errors and the save button has been pushed |
| 307 | |
463 | unlink("$editFilePath.$editFileSuffix") |
| 308 | $self->{openTempFileErrors} = ''; |
464 | if defined $submit_button and ($submit_button eq 'Save' or $submit_button eq 'Save as'); |
| 309 | unlink("$editFilePath.$editFileSuffix") if defined($submit_button) and $submit_button eq 'Save'; |
|
|
| 310 | }; |
465 | } |
| 311 | |
|
|
| 312 | |
466 | |
| 313 | # return values for use in the body subroutine |
467 | # return values for use in the body subroutine |
| 314 | $self->{problemPath} = $editFilePath; |
|
|
| 315 | $self->{inputFilePath} = $inputFilePath; |
468 | $self->{inputFilePath} = $inputFilePath; |
| 316 | $self->{displayMode} = $displayMode; |
469 | $self->{displayMode} = $displayMode; |
| 317 | $self->{problemSeed} = $problemSeed; |
470 | $self->{problemSeed} = $problemSeed; |
| 318 | $self->{r_problemContents} = \$problemContents; |
471 | $self->{r_problemContents} = \$problemContents; |
| 319 | $self->{editFileSuffix} = $editFileSuffix; |
472 | $self->{editFileSuffix} = $editFileSuffix; |
| 320 | |
|
|
| 321 | |
|
|
| 322 | |
|
|
| 323 | |
|
|
| 324 | } |
473 | } |
| 325 | sub saveFile { |
|
|
| 326 | my $self = shift; |
|
|
| 327 | |
|
|
| 328 | |
|
|
| 329 | |
|
|
| 330 | |
|
|
| 331 | |
|
|
| 332 | |
|
|
| 333 | } |
|
|
| 334 | sub path { |
|
|
| 335 | my $self = shift; |
|
|
| 336 | my $r = $self->{r}; |
|
|
| 337 | my $set_id = ''; |
|
|
| 338 | my $problem_id = ''; |
|
|
| 339 | unless (defined( $r->param("file_type") and $r->param("file_type") eq 'course_info' ) ){ |
|
|
| 340 | $set_id = shift; |
|
|
| 341 | $problem_id = shift; |
|
|
| 342 | } |
|
|
| 343 | #FIXME this is a bad way to pass the args, since it's position changes if the set/problem info |
|
|
| 344 | # isn't there |
|
|
| 345 | my $args = $_[-1]; |
|
|
| 346 | |
|
|
| 347 | my $ce = $self->{ce}; |
|
|
| 348 | my $root = $ce->{webworkURLs}->{root}; |
|
|
| 349 | my $courseName = $ce->{courseName}; |
|
|
| 350 | return $self->pathMacro($args, |
|
|
| 351 | "Home" => "$root", |
|
|
| 352 | $courseName => "$root/$courseName", |
|
|
| 353 | 'instructor' => "$root/$courseName/instructor", |
|
|
| 354 | 'sets' => "$root/$courseName/instructor/sets/", |
|
|
| 355 | "$set_id" => "$root/$courseName/instructor/sets/$set_id/", |
|
|
| 356 | "problems" => "$root/$courseName/instructor/sets/$set_id/problems", |
|
|
| 357 | "$problem_id" => '' |
|
|
| 358 | ); |
|
|
| 359 | } |
|
|
| 360 | sub body { |
|
|
| 361 | my $self = shift; |
|
|
| 362 | |
|
|
| 363 | # test area |
|
|
| 364 | my $r = $self->{r}; |
|
|
| 365 | my $db = $self->{db}; |
|
|
| 366 | my $ce = $self->{ce}; |
|
|
| 367 | my $user = $r->param('user'); |
|
|
| 368 | |
|
|
| 369 | |
|
|
| 370 | |
|
|
| 371 | ################ |
|
|
| 372 | # Gathering info |
|
|
| 373 | # What is needed |
|
|
| 374 | # $editFilePath -- |
|
|
| 375 | # $formURL -- given by $r->uri |
|
|
| 376 | # $tmpProblemPath |
|
|
| 377 | my $editFilePath = $self->{problemPath}; # path to the permanent file to be edited |
|
|
| 378 | my $inputFilePath = $self->{inputFilePath}; # path to the file currently being worked with (might be a .tmp file) |
|
|
| 379 | |
|
|
| 380 | |
|
|
| 381 | |
|
|
| 382 | |
|
|
| 383 | |
|
|
| 384 | my $header = CGI::i("Editing problem: $inputFilePath"); |
|
|
| 385 | |
|
|
| 386 | ######################################################################### |
|
|
| 387 | # Find the text for the problem, either in the tmp file, if it exists |
|
|
| 388 | # or in the original file in the template directory |
|
|
| 389 | ######################################################################### |
|
|
| 390 | my $problemContents = ${$self->{r_problemContents}}; |
|
|
| 391 | |
|
|
| 392 | # eval { $problemContents = WeBWorK::Utils::readFile($editFilePath) }; # try to read file |
|
|
| 393 | # $problemContents = $@ if $@; |
|
|
| 394 | |
|
|
| 395 | |
|
|
| 396 | |
|
|
| 397 | ######################################################################### |
|
|
| 398 | # Format the page |
|
|
| 399 | ######################################################################### |
|
|
| 400 | # Define parameters for textarea |
|
|
| 401 | # FIXME |
|
|
| 402 | # Should the seed be set from some particular user instance?? |
|
|
| 403 | # The mode list should be obtained from global.conf ultimately |
|
|
| 404 | my $rows = 20; |
|
|
| 405 | my $columns = 80; |
|
|
| 406 | my $mode_list = ['plainText','formattedText','images']; |
|
|
| 407 | my $displayMode = $self->{displayMode}; |
|
|
| 408 | my $problemSeed = $self->{problemSeed}; |
|
|
| 409 | my $uri = $r->uri; |
|
|
| 410 | ######################################################################## |
|
|
| 411 | # Define a link to view the problem |
|
|
| 412 | #FIXME |
|
|
| 413 | |
|
|
| 414 | ######################################################################### |
|
|
| 415 | |
|
|
| 416 | |
|
|
| 417 | warn "Errors in the problem ".CGI::br().$self->{editErrors} if $self->{editErrors}; |
|
|
| 418 | |
|
|
| 419 | |
|
|
| 420 | return CGI::p($header), |
|
|
| 421 | #CGI::start_form("POST",$r->uri,-target=>'_problem'), doesn't pass on the target parameter??? |
|
|
| 422 | qq!<form method="POST" action="$uri" enctype="application/x-www-form-urlencoded", target="_problem">!, |
|
|
| 423 | $self->hidden_authen_fields, |
|
|
| 424 | CGI::hidden(-name=>'file_type',-default=>$self->{file_type}), |
|
|
| 425 | CGI::div( |
|
|
| 426 | 'Seed: ', |
|
|
| 427 | CGI::textfield(-name=>'problemSeed',-value=>$problemSeed), |
|
|
| 428 | 'Mode: ', |
|
|
| 429 | CGI::popup_menu(-name=>'displayMode', -'values'=>$mode_list, |
|
|
| 430 | -default=>$displayMode), |
|
|
| 431 | CGI::a( |
|
|
| 432 | {-href=>'http://webwork.math.rochester.edu/docs/docs/pglanguage/manpages/',-target=>"manpage_window"}, |
|
|
| 433 | 'Manpages', |
|
|
| 434 | ) |
|
|
| 435 | ), |
|
|
| 436 | CGI::p( |
|
|
| 437 | CGI::textarea(-name => 'problemContents', -default => $problemContents, |
|
|
| 438 | -rows => $rows, -columns => $columns, -override => 1, |
|
|
| 439 | ), |
|
|
| 440 | ), |
|
|
| 441 | CGI::p( |
|
|
| 442 | CGI::submit(-value=>'Refresh',-name=>'submit'), |
|
|
| 443 | CGI::submit(-value=>'Save',-name=>'submit'), |
|
|
| 444 | CGI::submit(-value=>'Revert',-name=>'submit'), |
|
|
| 445 | ), |
|
|
| 446 | CGI::end_form(), |
|
|
| 447 | |
|
|
| 448 | |
|
|
| 449 | } |
|
|
| 450 | |
|
|
| 451 | |
|
|
| 452 | |
474 | |
| 453 | 1; |
475 | 1; |