[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

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

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

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9