[system] / branches / rel-2-2-dev / webwork2 / lib / WeBWorK / ContentGenerator / Instructor / PGProblemEditor.pm Repository:
ViewVC logotype

Diff of /branches/rel-2-2-dev/webwork2/lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

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

Legend:
Removed from v.2305  
changed lines
  Added in v.3779

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9