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