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