[system] / branches / rel-2-3-dev / webwork-modperl / lib / WeBWorK / ContentGenerator / Instructor / FileManager.pm Repository:
ViewVC logotype

Annotation of /branches/rel-2-3-dev/webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/FileManager.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3111 - (view) (download) (as text)
Original Path: trunk/webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/FileManager.pm

1 : sh002i 2896 ################################################################################
2 :     # WeBWorK Online Homework Delivery System
3 :     # Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/
4 : dpvc 3111 # $CVSHeader: webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/FileManager.pm,v 1.7 2005/01/08 16:42:10 dpvc Exp $
5 : sh002i 2896 #
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 : sh002i 2944 # FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
14 : sh002i 2896 # Artistic License for more details.
15 :     ################################################################################
16 :    
17 :     package WeBWorK::ContentGenerator::Instructor::FileManager;
18 :     use base qw(WeBWorK::ContentGenerator::Instructor);
19 :    
20 :     use WeBWorK::Utils qw(readDirectory readFile sortByName);
21 :     use WeBWorK::Upload;
22 :     use File::Path;
23 :     use File::Copy;
24 :    
25 :     =head1 NAME
26 :    
27 : sh002i 2944 WeBWorK::ContentGenerator::Instructor::FileManager.pm -- simple directory manager for WW files
28 : sh002i 2896
29 :     =cut
30 :    
31 :     use strict;
32 :     use warnings;
33 :     use CGI;
34 :    
35 : dpvc 3111 #
36 :     # The list of file extensions and the directories they usually go in.
37 :     #
38 :     my %uploadDir = (
39 :     csv => 'scoring',
40 :     lst => 'templates',
41 :     pg => 'templates/.*',
42 :     pl => 'templates/macros',
43 :     def => 'templates',
44 :     html => 'html/.*',
45 :     );
46 :    
47 : sh002i 2896 ##################################################
48 :     #
49 : sh002i 2944 # Check that the user is authorized, and then
50 :     # see if there is a download to perform.
51 : sh002i 2896 #
52 :     sub pre_header_initialize {
53 : sh002i 2944 my $self = shift;
54 :     my $r = $self->r;
55 :     my $authz = $r->authz;
56 :     my $user = $r->param('user');
57 : sh002i 2896
58 : sh002i 2898 unless ($authz->hasPermissions($user, "access_instructor_tools")) {
59 :     $self->addbadmessage("You aren't authorized to manage course files");
60 :     return;
61 :     }
62 : sh002i 2896
63 : sh002i 2898 my $action = $r->param('action');
64 :     $self->Download if ($action && $action eq 'Download');
65 :     my $file = $r->param('download');
66 :     $self->downloadFile($file) if (defined $file);
67 : sh002i 2896 }
68 :    
69 :     ##################################################
70 :     #
71 : sh002i 2944 # Download a given file
72 : sh002i 2896 #
73 :     sub downloadFile {
74 : sh002i 2898 my $self = shift;
75 :     my $file = checkName(shift);
76 :     my $pwd = checkPWD(shift || $self->r->param('pwd') || '.');
77 :     return unless $pwd;
78 :     $pwd = $self->{ce}{courseDirs}{root} . '/' . $pwd;
79 :     unless (-e "$pwd/$file") {
80 :     $self->addbadmessage("The file you are trying to download doesn't exist");
81 :     return;
82 :     }
83 :     unless (-f "$pwd/$file") {
84 :     $self->addbadmessage("You can only download regular files.");
85 :     return;
86 :     }
87 :     my $type = "application/octet-stream";
88 :     $type = "text/plain" if $file =~ m/\.(pg|pl|pm|txt|def|csv|lst)/;
89 : sh002i 2944 $type = "image/gif" if $file =~ m/\.gif/;
90 : sh002i 2898 $type = "image/jpeg" if $file =~ m/\.(jpg|jpeg)/;
91 : sh002i 2944 $type = "image/png" if $file =~ m/\.png/;
92 : sh002i 2898 $self->reply_with_file($type, "$pwd/$file", $file, 0);
93 : sh002i 2896 }
94 :    
95 :     ##################################################
96 :     #
97 : sh002i 2944 # The main body of the page
98 : sh002i 2896 #
99 :     sub body {
100 : sh002i 2944 my $self = shift;
101 :     my $r = $self->r;
102 :     my $urlpath = $r->urlpath;
103 :     my $db = $r->db;
104 :     my $ce = $r->ce;
105 :     my $authz = $r->authz;
106 :     my $courseRoot = $ce->{courseDirs}{root};
107 :     my $courseName = $urlpath->arg('courseID');
108 :     my $user = $r->param('user');
109 :     my $key = $r->param('key');
110 : sh002i 2896
111 : sh002i 2898 return CGI::em("You are not authorized to access the instructor tools")
112 :     unless $authz->hasPermissions($user, "access_instructor_tools");
113 : sh002i 2896
114 : sh002i 2898 $self->{pwd} = checkPWD($r->param('pwd') || '.');
115 :     return CGI::em("You have specified an illegal working directory!") unless defined $self->{pwd};
116 : sh002i 2896
117 : sh002i 2898 my $fileManagerPage = $urlpath->newFromModule($urlpath->module, courseID => $courseName);
118 : sh002i 2944 my $fileManagerURL = $self->systemLink($fileManagerPage, authen => 0);
119 : sh002i 2896
120 : sh002i 2898 print CGI::start_multipart_form(
121 :     -method=>"POST",
122 :     -action=>$fileManagerURL,
123 :     -id=>"FileManager",
124 :     -name=>"FileManager"
125 :     );
126 :     print $self->hidden_authen_fields;
127 : sh002i 2896
128 : sh002i 2898 $self->{courseRoot} = $courseRoot;
129 :     $self->{courseName} = $courseName;
130 : sh002i 2896
131 : sh002i 2898 my $action = $r->param('action') || $r->param('formAction') || 'Refresh';
132 : sh002i 2896
133 : sh002i 2898 for ($action) {
134 : sh002i 2944 /^Refresh/i and do {$self->Refresh; last};
135 :     /^Cancel/i and do {$self->Refresh; last};
136 :     /^\^/i and do {$self->ParentDir; last};
137 :     /^Directory/i and do {$self->Go; last};
138 :     /^Go/i and do {$self->Go; last};
139 :     /^View/i and do {$self->View; last};
140 :     /^Edit/i and do {$self->Edit; last};
141 :     /^Download/i and do {$self->Refresh; last};
142 :     /^Copy/i and do {$self->Copy; last};
143 :     /^Rename/i and do {$self->Rename; last};
144 :     /^Delete/i and do {$self->Delete; last};
145 :     /^New Folder/i and do {$self->NewFolder; last};
146 :     /^New File/i and do {$self->NewFile; last};
147 :     /^Upload/i and do {$self->Upload; last};
148 :     /^Revert/i and do {$self->Edit; last};
149 :     /^Save As/i and do {$self->SaveAs; last};
150 :     /^Save/i and do {$self->Save; last};
151 : sh002i 2898 $self->addbadmessage("Unknown action.");
152 :     $self->Refresh;
153 :     }
154 : sh002i 2896
155 : sh002i 2898 print CGI::hidden({name=>'pwd',value=>$self->{pwd}});
156 :     print CGI::hidden({name=>'formAction'});
157 :     print CGI::end_multipart_form();
158 : sh002i 2896
159 : sh002i 2898 return "";
160 : sh002i 2896 }
161 :    
162 :    
163 :     ##################################################
164 :     #
165 : sh002i 2944 # Display the directory listing and associated buttons
166 : sh002i 2896 #
167 :     sub Refresh {
168 : sh002i 2898 my $self = shift;
169 :     my $pwd = shift || $self->{pwd};
170 :     my $isTop = $pwd eq '.' || $pwd eq '';
171 : sh002i 2896
172 : sh002i 2898 my ($dirs,$dirlabels) = directoryMenu($self->{courseName},$pwd);
173 :     my ($files,$filelabels) = directoryListing($self->{courseRoot},$pwd);
174 : sh002i 2896
175 : sh002i 2898 unless ($files) {
176 :     $self->addbadmessage("The directory you specified doesn't exist");
177 :     $files = []; $filelabels = {};
178 :     }
179 : sh002i 2896
180 : sh002i 2898 #
181 : sh002i 2944 # Some JavaScript to make things easier for the user
182 : sh002i 2898 #
183 :     print CGI::script(<<EOF);
184 :     function doForm(action) {
185 :     var form = window.document.getElementById('FileManager');
186 :     form.formAction.value = action;
187 :     form.submit();
188 :     }
189 :     function disableButton(id,state) {
190 :     var element = window.document.getElementById(id);
191 :     element.disabled = state;
192 :     }
193 :     function checkFiles() {
194 :     var files = window.document.getElementById('files');
195 :     var state = files.selectedIndex < 0;
196 :     disableButton('View',state);
197 :     disableButton('Edit',state);
198 :     disableButton('Download',state);
199 :     disableButton('Rename',state);
200 :     disableButton('Copy',state);
201 :     disableButton('Delete',state);
202 :     }
203 :     function checkFile() {
204 :     var file = window.document.getElementById('file');
205 :     var state = (file.value == "");
206 :     disableButton('Upload',state);
207 :     }
208 : sh002i 2896 EOF
209 :    
210 : sh002i 2898 #
211 : sh002i 2944 # Start the table
212 : sh002i 2898 #
213 :     print CGI::start_table({border=>0,cellpadding=>0,cellspacing=>10, style=>"margin:1em 0 0 3em"});
214 : sh002i 2896
215 : sh002i 2898 #
216 : sh002i 2944 # Directory menu
217 : sh002i 2898 #
218 :     print CGI::Tr(
219 :     CGI::td({colspan=>3},
220 : sh002i 2944 CGI::input({type=>"submit", name=>"action", value => "^", ($isTop? (disabled=>1): ())}),
221 :     CGI::popup_menu(
222 :     -name => "directory",
223 :     -values => $dirs,
224 :     -labels => $dirlabels,
225 :     -style => "width:25em",
226 :     -onChange => "doForm('Go')"
227 :     ),
228 : sh002i 2898 CGI::noscript(CGI::input({type=>"submit",name=>"action",value=>"Go"}))
229 :     )
230 : sh002i 2944 );
231 : sh002i 2896
232 : sh002i 2898 #
233 : sh002i 2944 # Directory Listing
234 : sh002i 2898 #
235 :     my %button = (type=>"submit",name=>"action",style=>"width:10em");
236 :     print CGI::Tr({valign=>"middle"},
237 : sh002i 2944 CGI::td(CGI::scrolling_list(
238 :     -name => "files", id => "files",
239 : dpvc 3063 -style => "font-family:monospace; width:30em",
240 : sh002i 2944 -size => 15,
241 :     -multiple => 1,
242 :     -values => $files,
243 :     -labels => $filelabels,
244 :     -onDblClick => "doForm('View')",
245 :     -onChange => "checkFiles()"
246 :     )),
247 : sh002i 2898 CGI::td({width=>3}),
248 :     CGI::td(
249 :     CGI::start_table({border=>0,cellpadding=>0,cellspacing=>3}),
250 :     CGI::Tr([
251 :     CGI::td(CGI::input({%button,value=>"View",id=>"View"})),
252 :     CGI::td(CGI::input({%button,value=>"Edit",id=>"Edit"})),
253 :     CGI::td(CGI::input({%button,value=>"Download",id=>"Download"})),
254 :     CGI::td(CGI::input({%button,value=>"Rename",id=>"Rename"})),
255 :     CGI::td(CGI::input({%button,value=>"Copy",id=>"Copy"})),
256 :     CGI::td(CGI::input({%button,value=>"Delete",id=>"Delete"})),
257 :     CGI::td({height=>10}),
258 :     CGI::td(CGI::input({%button,value=>"New File"})),
259 :     CGI::td(CGI::input({%button,value=>"New Folder"})),
260 :     CGI::td(CGI::input({%button,value=>"Refresh"})),
261 :     ]),
262 :     CGI::end_table(),
263 :     ),
264 : sh002i 2896 );
265 :    
266 : sh002i 2898 #
267 : sh002i 2944 # Upload button
268 : sh002i 2898 #
269 :     print CGI::Tr([
270 :     CGI::td(),
271 :     CGI::td({colspan=>3},
272 : dpvc 3111 CGI::input({type=>"submit",name=>"action",style=>"width:7em",value=>"Upload:",id=>"Upload"}),
273 :     CGI::input({type=>"file",name=>"file",id=>"file",size=>40,onChange=>"checkFile()"}),
274 :     CGI::br(),
275 :     CGI::small(join(' &nbsp; ',"Format:",
276 :     CGI::radio_group('format',['Text','Binary','Automatic'],'Automatic'))),
277 :     ),
278 : sh002i 2896 ]);
279 :    
280 : sh002i 2898 #
281 : sh002i 2944 # End the table
282 :     #
283 : sh002i 2898 print CGI::end_table();
284 :     print CGI::script("checkFiles(); checkFile();");
285 : sh002i 2896 }
286 :    
287 :     ##################################################
288 :     #
289 : sh002i 2944 # Move to the parent directory
290 : sh002i 2896 #
291 :     sub ParentDir {
292 : sh002i 2898 my $self = shift;
293 :     $self->{pwd} = '.' unless ($self->{pwd} =~ s!/[^/]*$!!);
294 :     $self->Refresh;
295 : sh002i 2896 }
296 :    
297 :     ##################################################
298 :     #
299 : sh002i 2944 # Move to the parent directory
300 : sh002i 2896 #
301 :     sub Go {
302 : sh002i 2898 my $self = shift;
303 :     $self->{pwd} = $self->r->param('directory');
304 :     $self->Refresh;
305 : sh002i 2896 }
306 :    
307 :     ##################################################
308 :     #
309 : sh002i 2944 # Open a directory or view a file
310 : sh002i 2896 #
311 :     sub View {
312 : sh002i 2898 my $self = shift; my $pwd = $self->{pwd};
313 :     my $filename = $self->getFile("view"); return unless $filename;
314 :     my $name = "$pwd/$filename"; $name =~ s!^\./?!!;
315 : sh002i 2896
316 : sh002i 2898 #
317 : sh002i 2944 # Handle directories by making them the working directory
318 : sh002i 2898 #
319 :     my $file = "$self->{courseRoot}/$pwd/$filename";
320 :     if (-d $file) {
321 :     $self->{pwd} .= '/'.$filename;
322 :     $self->Refresh; return;
323 :     }
324 : sh002i 2896
325 : sh002i 2898 unless (-f $file) {
326 :     $self->addbadmessage("You can't view files of that type");
327 :     $self->Refresh; return;
328 :     }
329 : sh002i 2896
330 : sh002i 2898 #
331 : sh002i 2944 # Include a download link
332 : sh002i 2898 #
333 :     my $urlpath = $self->r->urlpath;
334 :     my $fileManagerPage = $urlpath->newFromModule($urlpath->module, courseID => $self->{courseName});
335 : sh002i 2944 my $fileManagerURL = $self->systemLink($fileManagerPage, params => {download => $filename, pwd => $pwd});
336 : sh002i 2898 print CGI::div({style=>"float:right"},
337 : sh002i 2896 CGI::a({href=>$fileManagerURL},"Download"));
338 : sh002i 2898 print CGI::p(),CGI::b($name),CGI::p();
339 :     print CGI::hr();
340 : sh002i 2896
341 : sh002i 2898 #
342 : sh002i 2944 # For files, display the file, if possible.
343 :     # If the file is an image, display it as an image.
344 : sh002i 2898 #
345 :     my $data = readFile($file);
346 :     if (isText($data)) {
347 :     print CGI::pre(showHTML($data));
348 :     } elsif ($file =~ m/\.(gif|jpg|png)/i) {
349 :     print CGI::img({src=>$fileManagerURL, border=>0});
350 :     } else {
351 :     print CGI::div({class=>"ResultsWithError"},
352 :     "The file does not appear to be a text file.");
353 :     }
354 : sh002i 2896 }
355 :    
356 :     ##################################################
357 :     #
358 : sh002i 2944 # Edit a file
359 : sh002i 2896 #
360 :     sub Edit {
361 : sh002i 2898 my $self = shift;
362 :     my $filename = $self->getFile('edit'); return unless $filename;
363 :     my $file = "$self->{courseRoot}/$self->{pwd}/$filename";
364 : sh002i 2896
365 : sh002i 2898 if (-d $file) {
366 :     $self->addbadmessage("You can't edit a directory");
367 :     $self->Refresh; return;
368 :     }
369 :     unless (-f $file) {
370 :     $self->addbadmessage("You can only edit text files");
371 :     $self->Refresh; return;
372 :     }
373 :     my $data = readFile($file);
374 :     if (!isText($data)) {
375 :     $self->addbadmessage("The file does not appear to be a text file");
376 :     $self->Refresh; return;
377 :     }
378 : sh002i 2896
379 : sh002i 2898 $self->RefreshEdit($data,$filename);
380 : sh002i 2896 }
381 :    
382 :     ##################################################
383 :     #
384 : sh002i 2944 # Save the edited file
385 : sh002i 2896 #
386 :     sub Save {
387 : sh002i 2898 my $self = shift; my $filename = shift;
388 :     my $pwd = $self->{pwd};
389 :     if ($filename) {
390 :     $pwd = substr($filename,length($self->{courseRoot})+1);
391 : dpvc 3086 $pwd =~ s!(/|^)([^/]*)$!!; $filename = $2;
392 :     $pwd = '.' if $pwd eq '';
393 : sh002i 2898 } else {
394 :     $filename = $self->getFile("save"); return unless $filename;
395 :     }
396 :     my $file = "$self->{courseRoot}/$pwd/$filename";
397 :     my $data = $self->r->param("data");
398 : sh002i 2896
399 : sh002i 2898 if (defined($data)) {
400 :     if (open(OUTFILE,">$file")) {
401 :     eval {print OUTFILE $data; close(OUTFILE)};
402 : sh002i 2944 if ($@) {$self->addbadmessage("Failed to save: $@")}
403 : dpvc 3111 else {$self->addgoodmessage("File saved")}
404 : sh002i 2944 } else {$self->addbadmessage("Can't write to file: $!")}
405 : sh002i 2898 } else {$data = ""; $self->addbadmessage("Error: no file data was submitted!")}
406 : sh002i 2896
407 : sh002i 2898 $self->{pwd} = $pwd;
408 :     $self->RefreshEdit($data,$filename);
409 : sh002i 2896 }
410 :    
411 :     ##################################################
412 :     #
413 : sh002i 2944 # Save the edited file under a new name
414 : sh002i 2896 #
415 :     sub SaveAs {
416 : sh002i 2898 my $self = shift;
417 : sh002i 2896
418 : sh002i 2898 my $newfile = $self->r->param('name');
419 :     my $original = $self->r->param('files');
420 :     $newfile = $self->verifyPath($newfile,$original);
421 :     if ($newfile) {$self->Save($newfile); return}
422 :     $self->RefreshEdit($self->r->param('data'),$original);
423 : sh002i 2896 }
424 :    
425 :     ##################################################
426 :     #
427 : sh002i 2944 # Display the Edit page
428 : sh002i 2896 #
429 :     sub RefreshEdit {
430 : sh002i 2898 my $self = shift; my $data = shift; my $file = shift;
431 :     my $pwd = shift || $self->{pwd};
432 :     my $name = "$pwd/$file"; $name =~ s!^\./?!!;
433 : sh002i 2896
434 : sh002i 2898 my %button = (type=>"submit",name=>"action",style=>"width:6em");
435 :     print CGI::p();
436 :     print CGI::start_table({border=>0,cellspacing=>0,cellpadding=>2, width=>"95%", align=>"center"});
437 :     print CGI::Tr([
438 :     CGI::td({align=>"center",style=>"background-color:#CCCCCC"},CGI::b($name)),
439 :     CGI::td(CGI::textarea(-name=>"data",-default=>$data,-override=>1,-rows=>30,-columns=>80,
440 : sh002i 2896 -style=>"width:100%")), ## can't seem to get variable height to work
441 : sh002i 2898 CGI::td({align=>"center", nowrap=>1},
442 :     CGI::input({%button,value=>"Cancel"}),"&nbsp;",
443 :     CGI::input({%button,value=>"Revert"}),"&nbsp;",
444 :     CGI::input({%button,value=>"Save As:"}),
445 :     CGI::input({type=>"text",name=>"name",size=>20,style=>"width:50%"}),"&nbsp;",
446 :     CGI::input({%button,value=>"Save"}),
447 :     ),
448 : sh002i 2896 ]);
449 : sh002i 2898 print CGI::end_table();
450 :     print CGI::hidden({name=>"files",value=>$file});
451 : sh002i 2896 }
452 :    
453 :     ##################################################
454 :     #
455 : sh002i 2944 # Copy a file
456 : sh002i 2896 #
457 :     sub Copy {
458 : sh002i 2898 my $self = shift;
459 :     my $oldfile = $self->getFile('copy'); return unless $oldfile;
460 :     my $original = $oldfile;
461 :     $oldfile = "$self->{courseRoot}/$self->{pwd}/$oldfile";
462 : sh002i 2896
463 : sh002i 2898 if (-d $oldfile) {
464 : sh002i 2944 # FIXME: need to do recursive directory copy
465 : sh002i 2898 $self->addbadmessage("Directory copies are not yet implemented");
466 :     $self->Refresh;
467 :     return;
468 :     }
469 : sh002i 2896
470 : sh002i 2898 if ($self->r->param('confirmed')) {
471 :     my $newfile = $self->r->param('name');
472 :     if ($newfile = $self->verifyPath($newfile,$original)) {
473 :     if (copy($oldfile, $newfile)) {
474 : dpvc 3111 $self->addgoodmessage("File successfully copied");
475 :     $self->Refresh; return;
476 : sh002i 2898 } else {$self->addbadmessage("Can't copy file: $!")}
477 :     }
478 :     }
479 : sh002i 2896
480 : sh002i 2898 Confirm("Copy file as:","Copy");
481 :     print CGI::hidden({name=>"files",value=>$original});
482 : sh002i 2896 }
483 :    
484 :     ##################################################
485 :     #
486 : sh002i 2944 # Rename a file
487 : sh002i 2896 #
488 :     sub Rename {
489 : sh002i 2898 my $self = shift;
490 :     my $oldfile = $self->getFile('rename'); return unless $oldfile;
491 :     my $original = $oldfile;
492 :     $oldfile = "$self->{courseRoot}/$self->{pwd}/$oldfile";
493 : sh002i 2896
494 : sh002i 2898 if ($self->r->param('confirmed')) {
495 :     my $newfile = $self->r->param('name');
496 :     if ($newfile = $self->verifyPath($newfile,$original)) {
497 :     if (rename $oldfile, $newfile) {
498 : sh002i 2896 $self->addgoodmessage("File successfully renamed");
499 :     $self->Refresh; return;
500 : sh002i 2898 } else {$self->addbadmessage("Can't rename file: $!")}
501 :     }
502 :     }
503 : sh002i 2896
504 : sh002i 2898 Confirm("Rename file as:","Rename");
505 :     print CGI::hidden({name=>"files",value=>$original});
506 : sh002i 2896 }
507 :    
508 :     ##################################################
509 :     #
510 : sh002i 2944 # Delete a file
511 : sh002i 2896 #
512 :     sub Delete {
513 : sh002i 2898 my $self = shift;
514 :     my @files = $self->r->param('files');
515 :     if (scalar(@files) == 0) {
516 :     $self->addbadmessage("You must select at least one file to delete");
517 :     $self->Refresh; return;
518 :     }
519 : sh002i 2896
520 : sh002i 2898 my $pwd = $self->{pwd};
521 :     my $dir = $self->{courseRoot}.'/'.$pwd;
522 :     if ($self->r->param('confirmed')) {
523 : sh002i 2896
524 : sh002i 2898 #
525 : sh002i 2944 # If confirmed, go ahead and delete the files
526 : sh002i 2898 #
527 :     foreach my $file (@files) {
528 :     if (defined checkPWD("$pwd/$file",1)) {
529 : dpvc 3111 if (-d "$dir/$file") {
530 :     my $removed = eval {rmtree("$dir/$file",0,1)};
531 :     if ($removed) {$self->addgoodmessage("Directory '$file' removed (items deleted: $removed)")}
532 :     else {$self->addbadmessage("Directory '$file' not removed: $!")}
533 :     } else {
534 :     if (unlink("$dir/$file")) {$self->addgoodmessage("File '$file' successfully removed")}
535 :     else {$self->addbadmessage("File '$file' not removed: $!")}
536 :     }
537 : sh002i 2898 } else {$self->addbadmessage("Illegal file '$file' specified"); last}
538 :     }
539 :     $self->Refresh;
540 : sh002i 2896
541 : sh002i 2898 } else {
542 : sh002i 2896
543 : sh002i 2898 #
544 : sh002i 2944 # Put up the confirmation dialog box
545 : sh002i 2898 #
546 :     print CGI::start_table({border=>1,cellspacing=>2,cellpadding=>20, style=>"margin: 1em 0 0 5em"});
547 :     print CGI::Tr(
548 :     CGI::td(
549 : dpvc 3111 CGI::b("Warning:")," You have requested that the following items be deleted\n",
550 :     CGI::ul(CGI::li(\@files)),
551 :     ((grep { -d "$dir/$_" } @files)?
552 :     CGI::p({style=>"width:500"},"Some of these files are directories. ",
553 :     "Only delete directories if you really know what you are doing. ",
554 :     "You can seriously damage your course if you delete the wrong thing."): ""),
555 :     CGI::p({style=>"color:red"},"There is no undo for deleting files or directories!"),
556 :     CGI::p("Really delete the items listed above?"),
557 :     CGI::div({style=>"float:left; padding-left:3ex"},
558 :     CGI::input({type=>"submit",name=>"action",value=>"Cancel"})),
559 :     CGI::div({style=>"float:right; padding-right:3ex"},
560 :     CGI::input({type=>"submit",name=>"action",value=>"Delete"})),
561 : sh002i 2898 ),
562 :     );
563 :     print CGI::end_table();
564 : sh002i 2896
565 : sh002i 2898 print CGI::hidden({name=>"confirmed",value=>1});
566 :     foreach my $file (@files) {print CGI::hidden({name=>"files",value=>$file})}
567 :     }
568 : sh002i 2896 }
569 :    
570 :     ##################################################
571 :     #
572 : sh002i 2944 # Make a new file and edit it
573 : sh002i 2896 #
574 :     sub NewFile {
575 : sh002i 2898 my $self = shift;
576 : sh002i 2896
577 : sh002i 2898 if ($self->r->param('confirmed')) {
578 :     my $name = $self->r->param('name');
579 :     if (my $file = $self->verifyName($name,"file")) {
580 :     if (open(NEWFILE,">$file")) {
581 : dpvc 3111 close(NEWFILE);
582 :     $self->RefreshEdit("",$name);
583 :     return;
584 : sh002i 2898 } else {$self->addbadmessage("Can't create file: $!")}
585 :     }
586 :     }
587 : sh002i 2896
588 : sh002i 2898 Confirm("New file name:","New File");
589 : sh002i 2896 }
590 :    
591 :     ##################################################
592 :     #
593 : sh002i 2944 # Make a new directory
594 : sh002i 2896 #
595 :     sub NewFolder {
596 : sh002i 2898 my $self = shift;
597 : sh002i 2896
598 : sh002i 2898 if ($self->r->param('confirmed')) {
599 :     my $name = $self->r->param('name');
600 :     if (my $dir = $self->verifyName($name,"directory")) {
601 :     if (mkdir $dir, 0750) {
602 : dpvc 3111 $self->{pwd} .= '/'.$name;
603 :     $self->Refresh; return;
604 : sh002i 2898 } else {$self->addbadmessage("Can't create directory: $!")}
605 :     }
606 :     }
607 : sh002i 2896
608 : sh002i 2898 Confirm("New folder name:","New Folder");
609 : sh002i 2896 }
610 :    
611 :     ##################################################
612 :     #
613 : sh002i 2944 # Download a file
614 : sh002i 2896 #
615 :     sub Download {
616 : sh002i 2898 my $self = shift;
617 :     my $filename = $self->getFile("download"); return unless $filename;
618 :     my $pwd = checkPWD($self->r->param('pwd') || '.');
619 :     return unless $pwd;
620 :     my $file = $self->{ce}{courseDirs}{root}.'/'.$pwd.'/'.$filename;
621 : sh002i 2896
622 : sh002i 2898 if (-d $file) {$self->addbadmessage("You can't download directories"); return}
623 :     unless (-f $file) {$self->addbadmessage("You can't download files of that type"); return}
624 : sh002i 2896
625 : sh002i 2898 $self->r->param('download',$filename);
626 : sh002i 2896 }
627 :    
628 :     ##################################################
629 :     #
630 : sh002i 2944 # Upload a file to the server
631 : sh002i 2896 #
632 :     sub Upload {
633 : sh002i 2898 my $self = shift;
634 :     my $dir = "$self->{courseRoot}/$self->{pwd}";
635 :     my $fileIDhash = $self->r->param('file');
636 :     unless ($fileIDhash) {
637 :     $self->addbadmessage("You have not chosen a file to upload.");
638 :     $self->Refresh;
639 :     return;
640 :     }
641 : sh002i 2896
642 : sh002i 2898 my ($id,$hash) = split(/\s+/,$fileIDhash);
643 :     my $upload = WeBWorK::Upload->retrieve($id,$hash,dir=>$self->{ce}{webworkDirs}{uploadCache});
644 : sh002i 2896
645 : sh002i 2898 my $name = uniqueName($dir,checkName($upload->filename));
646 :     if (-e "$dir/$name") {
647 :     $self->addbadmessage("A file with that name already exists");
648 :     $self->Refresh;
649 :     $upload->dispose;
650 :     return;
651 :     }
652 : dpvc 3111 $self->checkFileLocation($name,$self->{pwd});
653 : sh002i 2896
654 : dpvc 3111 my $type = $self->r->param('format') || 'automatic';
655 :     my $data;
656 :    
657 :     #
658 :     # Check if we need to convert linebreaks
659 :     #
660 :     if ($type ne 'Binary') {
661 :     my $fh = $upload->fileHandle;
662 :     my @lines = <$fh>; $data = join('',@lines);
663 :     if ($type eq 'Automatic') {$type = isText($data) ? 'Text' : 'Binary'}
664 :     }
665 :     if ($type eq 'Text') {
666 :     $data =~ s/\r\n?/\n/g;
667 :     open(UPLOAD,">$dir/$name") || $self->addbadmessage("Can't create file '$name'");
668 :     print UPLOAD $data; close(UPLOAD);
669 :     $upload->dispose();
670 :     } else {
671 :     $upload->disposeTo("$dir/$name");
672 :     }
673 :    
674 :     $self->addgoodmessage("$type file '$name' uploaded successfully");
675 : sh002i 2898 $self->Refresh;
676 : sh002i 2896 }
677 :    
678 :     ##################################################
679 :     ##################################################
680 :     #
681 : sh002i 2944 # Print a confirmation dialog box
682 : sh002i 2896 #
683 :     sub Confirm {
684 : sh002i 2898 my $message = shift;
685 :     my $button = shift;
686 : sh002i 2896
687 : sh002i 2898 print CGI::p();
688 :     print CGI::start_table({border=>1,cellspacing=>2,cellpadding=>20, style=>"margin: 1em 0 0 3em"});
689 :     print CGI::Tr(
690 :     CGI::td(
691 : dpvc 3111 $message,
692 :     CGI::input({type=>"text",name=>"name",size=>50}),
693 :     CGI::p(),
694 :     CGI::div({style=>"float:right; padding-right:3ex"},
695 :     CGI::input({type=>"submit",name=>"action",value=>$button})), # this will be the default
696 :     CGI::div({style=>"float:left; padding-left:3ex"},
697 :     CGI::input({type=>"submit",name=>"action",value=>"Cancel"})),
698 :     ),
699 :     );
700 : sh002i 2898 print CGI::end_table();
701 :     print CGI::hidden({name=>"confirmed",value=>1});
702 :     print CGI::script("window.document.FileManager.name.focus()");
703 : sh002i 2896 }
704 :    
705 :     ##################################################
706 :     #
707 : sh002i 2944 # Check that there is exactly one vailid file
708 : sh002i 2896 #
709 :     sub getFile {
710 : sh002i 2898 my $self = shift; my $action = shift;
711 :     my @files = $self->r->param("files");
712 :     if (scalar(@files) > 1) {
713 :     $self->addbadmessage("You can only $action one file at a time.");
714 :     $self->Refresh unless $action eq 'download';
715 :     return;
716 :     }
717 :     if (scalar(@files) == 0 || $files[0] eq "") {
718 :     $self->addbadmessage("You need to select a file to $action.");
719 :     $self->Refresh unless $action eq 'download';
720 :     return;
721 :     }
722 :     my $pwd = checkPWD($self->{pwd} || $self->r->param('pwd') || '.') || '.';
723 :     $self->addbadmessage("You have specified an illegal file")
724 :     unless checkPWD($pwd.'/'.$files[0],1);
725 :     return $files[0];
726 : sh002i 2896 }
727 :    
728 :     ##################################################
729 :     #
730 : sh002i 2944 # Get the entries for the directory menu
731 : sh002i 2896 #
732 :     sub directoryMenu {
733 : sh002i 2898 my $course = shift;
734 : sh002i 2944 my $dir = shift; $dir =~ s!^\.(/|$)!!;
735 : sh002i 2898 my @dirs = split('/',$dir);
736 :     my $menu = ""; my $pwd;
737 :    
738 :     my (@values,%labels);
739 :     while (scalar(@dirs)) {
740 :     $pwd = join('/',(@dirs)[0..$#dirs]);
741 :     $dir = pop(@dirs);
742 :     push(@values,$pwd); $labels{$pwd} = $dir;
743 :     }
744 :     push(@values,'.'); $labels{'.'} = $course;
745 :     return (\@values,\%labels);
746 : sh002i 2896 }
747 :    
748 :     ##################################################
749 :     #
750 : sh002i 2944 # Get the directory listing
751 : sh002i 2896 #
752 :     sub directoryListing {
753 : sh002i 2898 my $root = shift; my $pwd = shift;
754 :     my $dir = $root.'/'.$pwd;
755 :     my (@values,%labels,$size,$data);
756 : sh002i 2896
757 : gage 2904 return unless -d $dir and not -l $dir; #FIXME -- don't follow links
758 : sh002i 2898 my @names = sortByName(undef,grep(/^[^.]/,readDirectory($dir)));
759 :     foreach my $name(@names) {
760 : gage 2904 unless ( $name eq 'DATA') { #FIXME don't view the DATA directory
761 :     push(@values,$name); $labels{$name} = $name;
762 :     $labels{$name} .= '/' if (-d $dir.'/'.$name);
763 :     }
764 : sh002i 2898 }
765 :     return (\@values,\%labels);
766 : sh002i 2896 }
767 :    
768 :     ##################################################
769 :     #
770 : sh002i 2944 # Normalize the working directory and check if it is OK.
771 : sh002i 2896 #
772 :     sub checkPWD {
773 : sh002i 2898 my $pwd = shift;
774 :     my $renameError = shift;
775 : sh002i 2896
776 : sh002i 2944 $pwd =~ s!//+!/!g; # remove duplicate slashes
777 :     $pwd =~ s!(^|/)~!$1_!g; # remove ~user references
778 :     $pwd =~ s!(^|/)(\.(/|$))+!$1!g; # remove dot directories
779 :    
780 :     # remove dir/.. constructions
781 : sh002i 2898 while ($pwd =~ s!((\.[^./]+|\.\.[^/]+|[^./][^/]*)/\.\.(/|$))!!) {};
782 : sh002i 2944
783 :     $pwd =~ s!/$!!; # remove trailing /
784 :     return if ($pwd =~ m!(^|/)\.\.(/|$)!); # Error if outside the root
785 : sh002i 2896
786 : sh002i 2898 my $original = $pwd;
787 : sh002i 2944 $pwd =~ s!(^|/)\.!$1_!g; # don't enter hidden directories
788 :     $pwd =~ s!^/!!; # remove leading /
789 : dpvc 3111 $pwd =~ s![^-_./A-Z0-9~ ]!_!gi; # no illegal characters
790 : sh002i 2898 return if $renameError && $original ne $pwd;
791 : sh002i 2896
792 : sh002i 2898 $pwd = '.' if $pwd eq '';
793 :     return $pwd;
794 : sh002i 2896 }
795 :    
796 :     ##################################################
797 :     #
798 : sh002i 2944 # Check a name for bad characters, etc.
799 : sh002i 2896 #
800 :     sub checkName {
801 : sh002i 2898 my $file = shift;
802 : sh002i 2944 $file =~ s!.*[/\\]!!; # remove directory
803 :     $file =~ s/[^-_.a-zA-Z0-9 ]/_/g; # no illegal characters
804 :     $file = "newfile.txt" unless $file; # no blank names
805 :     $file =~ s/^\./_/; # no initial dot
806 : sh002i 2898 return $file;
807 : sh002i 2896 }
808 :    
809 :     ##################################################
810 :     #
811 : dpvc 3111 # Check that a file is uploaded to the correct directory
812 :     #
813 :     sub checkFileLocation {
814 :     my $self = shift;
815 :     my $extension = shift; $extension =~ s/.*\.//;
816 :     my $dir = shift;
817 :     return unless defined($uploadDir{$extension});
818 :     return if $dir =~ m/^$uploadDir{$extension}$/;
819 :     $dir = $uploadDir{$extension}; $dir =~ s!/.*!!;
820 :     $self->addbadmessage("Files with extension '.$extension' usually belong in '$dir'");
821 :     }
822 :    
823 :     ##################################################
824 :     #
825 : sh002i 2944 # Get a unique name (in case it already exists)
826 : sh002i 2896 #
827 :     sub uniqueName {
828 : sh002i 2898 my $dir = shift; my $name = shift;
829 : dpvc 3086 return $name unless (-e "$dir/$name");
830 : sh002i 2898 my $type = ""; my $n = -1;
831 :     $type = $1 if ($name =~ s/(\.[^.]*)$//);
832 :     $n = $1 if ($name =~ s/(\d+)$//);
833 :     while (-e "$dir/$name$n$type") {if ($n < 0) {$n--} else {$n++}}
834 :     return "$name$n$type";
835 : sh002i 2896 }
836 :    
837 :     ##################################################
838 :     #
839 : sh002i 2944 # Verify that a name can be added tot he current
840 :     # directory.
841 : sh002i 2896 #
842 :     sub verifyName {
843 : sh002i 2898 my $self = shift; my $name = shift; my $object = shift;
844 :     if ($name) {
845 :     unless ($name =~ m!/!) {
846 :     unless ($name =~ m!^\.!) {
847 : dpvc 3111 unless ($name =~ m![^-_.a-zA-Z0-9 ]!) {
848 :     my $file = "$self->{courseRoot}/$self->{pwd}/$name";
849 :     return $file unless (-e $file);
850 :     $self->addbadmessage("A file with that name already exists");
851 :     } else {$self->addbadmessage("Your $object name contains illegal characters")}
852 : sh002i 2898 } else {$self->addbadmessage("Your $object name may not begin with a dot")}
853 :     } else {$self->addbadmessage("Your $object name may not contain a path component")}
854 :     } else {$self->addbadmessage("You must specify a $object name")}
855 :     return
856 : sh002i 2896 }
857 :    
858 :     ##################################################
859 :     #
860 : sh002i 2944 # Verify that a file path is valid
861 : sh002i 2896 #
862 :     sub verifyPath {
863 : sh002i 2898 my $self = shift; my $path = shift; my $name = shift;
864 : sh002i 2896
865 : sh002i 2898 if ($path) {
866 :     unless ($path =~ m![^-_.a-zA-Z0-9 /]!) {
867 :     unless ($path =~ m!^/!) {
868 : dpvc 3111 $path = checkPWD($self->{pwd}.'/'.$path,1);
869 :     if ($path) {
870 :     $path = $self->{courseRoot}.'/'.$path;
871 :     $path .= '/'.$name if -d $path && $name;
872 :     return $path unless (-e $path);
873 :     $self->addbadmessage("A file with that name already exists");
874 :     } else {$self->addbadmessage("You have specified an illegal path")}
875 : sh002i 2898 } else {$self->addbadmessage("You can not specify an absolute path")}
876 :     } else {$self->addbadmessage("Your file name contains illegal characters")}
877 :     } else {$self->addbadmessage("You must specify a file name")}
878 :     return
879 : sh002i 2896 }
880 :    
881 :     ##################################################
882 :     #
883 : sh002i 2944 # Make HTML symbols printable
884 : sh002i 2896 #
885 :     sub showHTML {
886 : dpvc 3111 my $string = shift;
887 :     return '' unless defined $string;
888 :     $string =~ s/&/\&amp;/g;
889 :     $string =~ s/</\&lt;/g;
890 :     $string =~ s/>/\&gt;/g;
891 :     $string;
892 : sh002i 2896 }
893 :    
894 :     ##################################################
895 :     #
896 : sh002i 2944 # Check if a string is plain text
897 :     # (i.e., doesn't contain three non-regular
898 :     # characters in a row.)
899 : sh002i 2896 #
900 :     sub isText {
901 : sh002i 2898 my $string = shift;
902 :     return $string !~ m/[^\s\x20-\x7E]{3,}/;
903 : sh002i 2896 }
904 :    
905 :     ##################################################
906 :    
907 :     1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9