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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3451 - (view) (download) (as text)
Original Path: trunk/webwork2/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 3451 # $CVSHeader: webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/FileManager.pm,v 1.12 2005/08/01 19:23:58 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 : dpvc 3330 use File::Spec;
25 : sh002i 2896
26 :     =head1 NAME
27 :    
28 : sh002i 2944 WeBWorK::ContentGenerator::Instructor::FileManager.pm -- simple directory manager for WW files
29 : sh002i 2896
30 :     =cut
31 :    
32 :     use strict;
33 :     use warnings;
34 :     use CGI;
35 :    
36 : dpvc 3111 #
37 :     # The list of file extensions and the directories they usually go in.
38 :     #
39 :     my %uploadDir = (
40 :     csv => 'scoring',
41 :     lst => 'templates',
42 :     pg => 'templates/.*',
43 :     pl => 'templates/macros',
44 :     def => 'templates',
45 :     html => 'html/.*',
46 :     );
47 :    
48 : sh002i 2896 ##################################################
49 :     #
50 : sh002i 2944 # Check that the user is authorized, and then
51 :     # see if there is a download to perform.
52 : sh002i 2896 #
53 :     sub pre_header_initialize {
54 : sh002i 2944 my $self = shift;
55 :     my $r = $self->r;
56 :     my $authz = $r->authz;
57 :     my $user = $r->param('user');
58 : sh002i 2896
59 : sh002i 2898 unless ($authz->hasPermissions($user, "access_instructor_tools")) {
60 :     $self->addbadmessage("You aren't authorized to manage course files");
61 :     return;
62 :     }
63 : sh002i 2896
64 : sh002i 2898 my $action = $r->param('action');
65 :     $self->Download if ($action && $action eq 'Download');
66 :     my $file = $r->param('download');
67 :     $self->downloadFile($file) if (defined $file);
68 : sh002i 2896 }
69 :    
70 :     ##################################################
71 :     #
72 : sh002i 2944 # Download a given file
73 : sh002i 2896 #
74 :     sub downloadFile {
75 : sh002i 2898 my $self = shift;
76 :     my $file = checkName(shift);
77 : dpvc 3330 my $pwd = $self->checkPWD(shift || $self->r->param('pwd') || '.');
78 : sh002i 2898 return unless $pwd;
79 :     $pwd = $self->{ce}{courseDirs}{root} . '/' . $pwd;
80 :     unless (-e "$pwd/$file") {
81 :     $self->addbadmessage("The file you are trying to download doesn't exist");
82 :     return;
83 :     }
84 :     unless (-f "$pwd/$file") {
85 :     $self->addbadmessage("You can only download regular files.");
86 :     return;
87 :     }
88 :     my $type = "application/octet-stream";
89 :     $type = "text/plain" if $file =~ m/\.(pg|pl|pm|txt|def|csv|lst)/;
90 : sh002i 2944 $type = "image/gif" if $file =~ m/\.gif/;
91 : sh002i 2898 $type = "image/jpeg" if $file =~ m/\.(jpg|jpeg)/;
92 : sh002i 2944 $type = "image/png" if $file =~ m/\.png/;
93 : sh002i 2898 $self->reply_with_file($type, "$pwd/$file", $file, 0);
94 : sh002i 2896 }
95 :    
96 :     ##################################################
97 :     #
98 : sh002i 2944 # The main body of the page
99 : sh002i 2896 #
100 :     sub body {
101 : sh002i 2944 my $self = shift;
102 :     my $r = $self->r;
103 :     my $urlpath = $r->urlpath;
104 :     my $db = $r->db;
105 :     my $ce = $r->ce;
106 :     my $authz = $r->authz;
107 :     my $courseRoot = $ce->{courseDirs}{root};
108 :     my $courseName = $urlpath->arg('courseID');
109 :     my $user = $r->param('user');
110 :     my $key = $r->param('key');
111 : sh002i 2896
112 : sh002i 2898 return CGI::em("You are not authorized to access the instructor tools")
113 :     unless $authz->hasPermissions($user, "access_instructor_tools");
114 : sh002i 2896
115 : dpvc 3330 $self->{pwd} = $self->checkPWD($r->param('pwd') || '.');
116 : sh002i 2898 return CGI::em("You have specified an illegal working directory!") unless defined $self->{pwd};
117 : sh002i 2896
118 : sh002i 2898 my $fileManagerPage = $urlpath->newFromModule($urlpath->module, courseID => $courseName);
119 : sh002i 2944 my $fileManagerURL = $self->systemLink($fileManagerPage, authen => 0);
120 : sh002i 2896
121 : sh002i 2898 print CGI::start_multipart_form(
122 :     -method=>"POST",
123 :     -action=>$fileManagerURL,
124 :     -id=>"FileManager",
125 : dpvc 3447 -name=>"FileManager",
126 :     -style=>"margin:0",
127 : sh002i 2898 );
128 :     print $self->hidden_authen_fields;
129 : sh002i 2896
130 : sh002i 2898 $self->{courseRoot} = $courseRoot;
131 :     $self->{courseName} = $courseName;
132 : sh002i 2896
133 : dpvc 3330 my $action = $r->param('action') || $r->param('formAction') || 'Init';
134 : sh002i 2896
135 : sh002i 2898 for ($action) {
136 : sh002i 2944 /^Refresh/i and do {$self->Refresh; last};
137 :     /^Cancel/i and do {$self->Refresh; last};
138 :     /^\^/i and do {$self->ParentDir; last};
139 :     /^Directory/i and do {$self->Go; last};
140 :     /^Go/i and do {$self->Go; last};
141 :     /^View/i and do {$self->View; last};
142 :     /^Edit/i and do {$self->Edit; last};
143 :     /^Download/i and do {$self->Refresh; last};
144 :     /^Copy/i and do {$self->Copy; last};
145 :     /^Rename/i and do {$self->Rename; last};
146 :     /^Delete/i and do {$self->Delete; last};
147 : dpvc 3330 /^GZIP/i and do {$self->GZIP; last};
148 :     /^UNGZIP/i and do {$self->UNGZIP; last};
149 : sh002i 2944 /^New Folder/i and do {$self->NewFolder; last};
150 :     /^New File/i and do {$self->NewFile; last};
151 :     /^Upload/i and do {$self->Upload; last};
152 :     /^Revert/i and do {$self->Edit; last};
153 :     /^Save As/i and do {$self->SaveAs; last};
154 :     /^Save/i and do {$self->Save; last};
155 : dpvc 3330 /^Init/i and do {$self->Init; last};
156 : sh002i 2898 $self->addbadmessage("Unknown action.");
157 :     $self->Refresh;
158 :     }
159 : sh002i 2896
160 : sh002i 2898 print CGI::hidden({name=>'pwd',value=>$self->{pwd}});
161 :     print CGI::hidden({name=>'formAction'});
162 :     print CGI::end_multipart_form();
163 : sh002i 2896
164 : sh002i 2898 return "";
165 : sh002i 2896 }
166 :    
167 :    
168 :     ##################################################
169 :     #
170 : dpvc 3330 # First time through
171 :     #
172 :     sub Init {
173 :     my $self = shift;
174 :     $self->r->param('unpack',1);
175 :     $self->r->param('autodelete',1);
176 :     $self->r->param('format','Automatic');
177 :     $self->Refresh;
178 :     }
179 :    
180 :     sub HiddenFlags {
181 :     my $self = shift;
182 :     print CGI::hidden({name=>"dates", value=>$self->getFlag('dates')});
183 :     print CGI::hidden({name=>"overwrite", value=>$self->getFlag('overwrite')});
184 :     print CGI::hidden({name=>"unpack", value=>$self->getFlag('unpack')});
185 :     print CGI::hidden({name=>"autodelete",value=>$self->getFlag('autodelete')});
186 :     print CGI::hidden({name=>"format", value=>$self->getFlag('format','Automatic')});
187 :     }
188 :    
189 :     ##################################################
190 :     #
191 : sh002i 2944 # Display the directory listing and associated buttons
192 : sh002i 2896 #
193 :     sub Refresh {
194 : dpvc 3330 my $self = shift;
195 : sh002i 2898 my $pwd = shift || $self->{pwd};
196 :     my $isTop = $pwd eq '.' || $pwd eq '';
197 : sh002i 2896
198 : sh002i 2898 my ($dirs,$dirlabels) = directoryMenu($self->{courseName},$pwd);
199 : dpvc 3330 my ($files,$filelabels) = directoryListing($self->{courseRoot},$pwd,$self->getFlag('dates'));
200 : sh002i 2896
201 : sh002i 2898 unless ($files) {
202 :     $self->addbadmessage("The directory you specified doesn't exist");
203 :     $files = []; $filelabels = {};
204 :     }
205 : sh002i 2896
206 : sh002i 2898 #
207 : sh002i 2944 # Some JavaScript to make things easier for the user
208 : sh002i 2898 #
209 :     print CGI::script(<<EOF);
210 :     function doForm(action) {
211 :     var form = window.document.getElementById('FileManager');
212 :     form.formAction.value = action;
213 :     form.submit();
214 :     }
215 :     function disableButton(id,state) {
216 :     var element = window.document.getElementById(id);
217 :     element.disabled = state;
218 :     }
219 :     function checkFiles() {
220 :     var files = window.document.getElementById('files');
221 :     var state = files.selectedIndex < 0;
222 :     disableButton('View',state);
223 :     disableButton('Edit',state);
224 :     disableButton('Download',state);
225 :     disableButton('Rename',state);
226 :     disableButton('Copy',state);
227 :     disableButton('Delete',state);
228 : dpvc 3330 disableButton('GZIP',state);
229 :     checkGZIP(files,state);
230 : sh002i 2898 }
231 :     function checkFile() {
232 :     var file = window.document.getElementById('file');
233 : dpvc 3447 if (navigator.vendor && navigator.vendorSub && navigator.vendor == "Netscape") {
234 :     if (navigator.vendorSub.match(/(\\d+)\.(\\d+)/)) {
235 :     if (RegExp.\$1 < 7 || (RegExp.\$1 == 7 && RegExp.\$2 < 2)) return;
236 :     }
237 :     }
238 : sh002i 2898 var state = (file.value == "");
239 :     disableButton('Upload',state);
240 :     }
241 : dpvc 3330 function checkGZIP(files,disabled) {
242 :     var gzip = document.getElementById('GZIP');
243 :     gzip.value = 'GZIP';
244 :     if (disabled) return;
245 :     if (!files.childNodes[files.selectedIndex].value.match(/\\.tgz\$/)) return;
246 :     for (var i = files.selectedIndex+1; i < files.length; i++)
247 :     {if (files.childNodes[i].selected) return}
248 :     gzip.value = 'UNGZIP';
249 :     }
250 : sh002i 2896 EOF
251 :    
252 : sh002i 2898 #
253 : sh002i 2944 # Start the table
254 : sh002i 2898 #
255 : dpvc 3330 print CGI::start_table({border=>0,cellpadding=>0,cellspacing=>3, style=>"margin:1em 0 0 3em"});
256 : sh002i 2896
257 : sh002i 2898 #
258 : dpvc 3330 # Directory menu and date/size checkbox
259 : sh002i 2898 #
260 :     print CGI::Tr(
261 : dpvc 3330 CGI::td({colspan=>2},
262 : sh002i 2944 CGI::input({type=>"submit", name=>"action", value => "^", ($isTop? (disabled=>1): ())}),
263 :     CGI::popup_menu(
264 :     -name => "directory",
265 :     -values => $dirs,
266 :     -labels => $dirlabels,
267 :     -style => "width:25em",
268 :     -onChange => "doForm('Go')"
269 :     ),
270 : sh002i 2898 CGI::noscript(CGI::input({type=>"submit",name=>"action",value=>"Go"}))
271 : dpvc 3330 ),
272 :     CGI::td(CGI::small(CGI::checkbox(
273 :     -name => 'dates',
274 :     -checked => $self->getFlag('dates'),
275 :     -value => 1,
276 :     -label => 'Show Date & Size',
277 :     -onClick => 'doForm("Refresh")',
278 :     ))),
279 : sh002i 2944 );
280 : sh002i 2896
281 : sh002i 2898 #
282 : dpvc 3330 # Directory Listing and column of buttons
283 : sh002i 2898 #
284 :     my %button = (type=>"submit",name=>"action",style=>"width:10em");
285 : dpvc 3330 my $width = ($self->getFlag('dates') && scalar(@{$files}) > 0) ? "": " width:30em";
286 : sh002i 2898 print CGI::Tr({valign=>"middle"},
287 : dpvc 3330 fixSpaces(CGI::td(CGI::scrolling_list(
288 : sh002i 2944 -name => "files", id => "files",
289 : dpvc 3330 -style => "font-family:monospace; $width",
290 :     -size => 17,
291 : sh002i 2944 -multiple => 1,
292 :     -values => $files,
293 :     -labels => $filelabels,
294 :     -onDblClick => "doForm('View')",
295 :     -onChange => "checkFiles()"
296 : dpvc 3330 ))),
297 :     CGI::td({width=>15}),
298 : sh002i 2898 CGI::td(
299 :     CGI::start_table({border=>0,cellpadding=>0,cellspacing=>3}),
300 :     CGI::Tr([
301 :     CGI::td(CGI::input({%button,value=>"View",id=>"View"})),
302 :     CGI::td(CGI::input({%button,value=>"Edit",id=>"Edit"})),
303 :     CGI::td(CGI::input({%button,value=>"Download",id=>"Download"})),
304 :     CGI::td(CGI::input({%button,value=>"Rename",id=>"Rename"})),
305 :     CGI::td(CGI::input({%button,value=>"Copy",id=>"Copy"})),
306 :     CGI::td(CGI::input({%button,value=>"Delete",id=>"Delete"})),
307 : dpvc 3330 CGI::td(CGI::input({%button,value=>"GZIP",id=>"GZIP"})),
308 : sh002i 2898 CGI::td({height=>10}),
309 :     CGI::td(CGI::input({%button,value=>"New File"})),
310 :     CGI::td(CGI::input({%button,value=>"New Folder"})),
311 :     CGI::td(CGI::input({%button,value=>"Refresh"})),
312 :     ]),
313 :     CGI::end_table(),
314 :     ),
315 : sh002i 2896 );
316 :    
317 : sh002i 2898 #
318 : dpvc 3330 # Upload button and checkboxes
319 : sh002i 2898 #
320 :     print CGI::Tr([
321 :     CGI::td(),
322 :     CGI::td({colspan=>3},
323 : dpvc 3111 CGI::input({type=>"submit",name=>"action",style=>"width:7em",value=>"Upload:",id=>"Upload"}),
324 :     CGI::input({type=>"file",name=>"file",id=>"file",size=>40,onChange=>"checkFile()"}),
325 :     CGI::br(),
326 :     CGI::small(join(' &nbsp; ',"Format:",
327 : dpvc 3330 CGI::radio_group('format',['Text','Binary','Automatic'],
328 :     $self->getFlag('format','Automatic')))),
329 : dpvc 3111 ),
330 : sh002i 2896 ]);
331 : dpvc 3330 print CGI::Tr([
332 :     CGI::td(),
333 :     CGI::td({colspan=>3},
334 :     CGI::small(CGI::checkbox('overwrite',$self->getFlag('overwrite'),1,
335 :     'Overwrite existing files silently')),
336 :     CGI::br(),
337 :     CGI::small(CGI::checkbox('unpack',$self->getFlag('unpack'),1,
338 :     'Unpack archives automatically')),
339 :     CGI::small(CGI::checkbox('autodelete',$self->getFlag('autodelete'),1,
340 :     'then delete them')),
341 :     ),
342 :     ]);
343 : sh002i 2896
344 : sh002i 2898 #
345 : sh002i 2944 # End the table
346 :     #
347 : sh002i 2898 print CGI::end_table();
348 :     print CGI::script("checkFiles(); checkFile();");
349 : sh002i 2896 }
350 :    
351 :     ##################################################
352 :     #
353 : sh002i 2944 # Move to the parent directory
354 : sh002i 2896 #
355 :     sub ParentDir {
356 : sh002i 2898 my $self = shift;
357 :     $self->{pwd} = '.' unless ($self->{pwd} =~ s!/[^/]*$!!);
358 :     $self->Refresh;
359 : sh002i 2896 }
360 :    
361 :     ##################################################
362 :     #
363 : sh002i 2944 # Move to the parent directory
364 : sh002i 2896 #
365 :     sub Go {
366 : sh002i 2898 my $self = shift;
367 :     $self->{pwd} = $self->r->param('directory');
368 :     $self->Refresh;
369 : sh002i 2896 }
370 :    
371 :     ##################################################
372 :     #
373 : sh002i 2944 # Open a directory or view a file
374 : sh002i 2896 #
375 :     sub View {
376 : sh002i 2898 my $self = shift; my $pwd = $self->{pwd};
377 :     my $filename = $self->getFile("view"); return unless $filename;
378 :     my $name = "$pwd/$filename"; $name =~ s!^\./?!!;
379 : dpvc 3330 my $file = "$self->{courseRoot}/$pwd/$filename";
380 : sh002i 2896
381 : sh002i 2898 #
382 : dpvc 3330 # Don't follow symbolic links
383 :     #
384 :     if ($self->isSymLink($file)) {
385 :     $self->addbadmessage("That symbolic link takes you outside your course directory");
386 :     $self->Refresh; return;
387 :     }
388 :    
389 :     #
390 : sh002i 2944 # Handle directories by making them the working directory
391 : sh002i 2898 #
392 :     if (-d $file) {
393 :     $self->{pwd} .= '/'.$filename;
394 :     $self->Refresh; return;
395 :     }
396 : sh002i 2896
397 : sh002i 2898 unless (-f $file) {
398 :     $self->addbadmessage("You can't view files of that type");
399 :     $self->Refresh; return;
400 :     }
401 : sh002i 2896
402 : sh002i 2898 #
403 : sh002i 2944 # Include a download link
404 : sh002i 2898 #
405 :     my $urlpath = $self->r->urlpath;
406 :     my $fileManagerPage = $urlpath->newFromModule($urlpath->module, courseID => $self->{courseName});
407 : sh002i 2944 my $fileManagerURL = $self->systemLink($fileManagerPage, params => {download => $filename, pwd => $pwd});
408 : sh002i 2898 print CGI::div({style=>"float:right"},
409 : sh002i 2896 CGI::a({href=>$fileManagerURL},"Download"));
410 : sh002i 2898 print CGI::p(),CGI::b($name),CGI::p();
411 :     print CGI::hr();
412 : sh002i 2896
413 : sh002i 2898 #
414 : sh002i 2944 # For files, display the file, if possible.
415 :     # If the file is an image, display it as an image.
416 : sh002i 2898 #
417 :     my $data = readFile($file);
418 :     if (isText($data)) {
419 :     print CGI::pre(showHTML($data));
420 :     } elsif ($file =~ m/\.(gif|jpg|png)/i) {
421 :     print CGI::img({src=>$fileManagerURL, border=>0});
422 :     } else {
423 :     print CGI::div({class=>"ResultsWithError"},
424 :     "The file does not appear to be a text file.");
425 :     }
426 : sh002i 2896 }
427 :    
428 :     ##################################################
429 :     #
430 : sh002i 2944 # Edit a file
431 : sh002i 2896 #
432 :     sub Edit {
433 : sh002i 2898 my $self = shift;
434 :     my $filename = $self->getFile('edit'); return unless $filename;
435 :     my $file = "$self->{courseRoot}/$self->{pwd}/$filename";
436 : sh002i 2896
437 : sh002i 2898 if (-d $file) {
438 :     $self->addbadmessage("You can't edit a directory");
439 :     $self->Refresh; return;
440 :     }
441 :     unless (-f $file) {
442 :     $self->addbadmessage("You can only edit text files");
443 :     $self->Refresh; return;
444 :     }
445 :     my $data = readFile($file);
446 :     if (!isText($data)) {
447 :     $self->addbadmessage("The file does not appear to be a text file");
448 :     $self->Refresh; return;
449 :     }
450 : sh002i 2896
451 : sh002i 2898 $self->RefreshEdit($data,$filename);
452 : sh002i 2896 }
453 :    
454 :     ##################################################
455 :     #
456 : sh002i 2944 # Save the edited file
457 : sh002i 2896 #
458 :     sub Save {
459 : sh002i 2898 my $self = shift; my $filename = shift;
460 :     my $pwd = $self->{pwd};
461 :     if ($filename) {
462 :     $pwd = substr($filename,length($self->{courseRoot})+1);
463 : dpvc 3086 $pwd =~ s!(/|^)([^/]*)$!!; $filename = $2;
464 :     $pwd = '.' if $pwd eq '';
465 : sh002i 2898 } else {
466 :     $filename = $self->getFile("save"); return unless $filename;
467 :     }
468 :     my $file = "$self->{courseRoot}/$pwd/$filename";
469 :     my $data = $self->r->param("data");
470 : sh002i 2896
471 : sh002i 2898 if (defined($data)) {
472 :     if (open(OUTFILE,">$file")) {
473 :     eval {print OUTFILE $data; close(OUTFILE)};
474 : sh002i 2944 if ($@) {$self->addbadmessage("Failed to save: $@")}
475 : dpvc 3111 else {$self->addgoodmessage("File saved")}
476 : sh002i 2944 } else {$self->addbadmessage("Can't write to file: $!")}
477 : sh002i 2898 } else {$data = ""; $self->addbadmessage("Error: no file data was submitted!")}
478 : sh002i 2896
479 : sh002i 2898 $self->{pwd} = $pwd;
480 :     $self->RefreshEdit($data,$filename);
481 : sh002i 2896 }
482 :    
483 :     ##################################################
484 :     #
485 : sh002i 2944 # Save the edited file under a new name
486 : sh002i 2896 #
487 :     sub SaveAs {
488 : sh002i 2898 my $self = shift;
489 : sh002i 2896
490 : sh002i 2898 my $newfile = $self->r->param('name');
491 :     my $original = $self->r->param('files');
492 :     $newfile = $self->verifyPath($newfile,$original);
493 :     if ($newfile) {$self->Save($newfile); return}
494 :     $self->RefreshEdit($self->r->param('data'),$original);
495 : sh002i 2896 }
496 :    
497 :     ##################################################
498 :     #
499 : sh002i 2944 # Display the Edit page
500 : sh002i 2896 #
501 :     sub RefreshEdit {
502 : sh002i 2898 my $self = shift; my $data = shift; my $file = shift;
503 :     my $pwd = shift || $self->{pwd};
504 :     my $name = "$pwd/$file"; $name =~ s!^\./?!!;
505 : sh002i 2896
506 : sh002i 2898 my %button = (type=>"submit",name=>"action",style=>"width:6em");
507 :     print CGI::p();
508 :     print CGI::start_table({border=>0,cellspacing=>0,cellpadding=>2, width=>"95%", align=>"center"});
509 :     print CGI::Tr([
510 :     CGI::td({align=>"center",style=>"background-color:#CCCCCC"},CGI::b($name)),
511 :     CGI::td(CGI::textarea(-name=>"data",-default=>$data,-override=>1,-rows=>30,-columns=>80,
512 : sh002i 2896 -style=>"width:100%")), ## can't seem to get variable height to work
513 : sh002i 2898 CGI::td({align=>"center", nowrap=>1},
514 :     CGI::input({%button,value=>"Cancel"}),"&nbsp;",
515 :     CGI::input({%button,value=>"Revert"}),"&nbsp;",
516 :     CGI::input({%button,value=>"Save As:"}),
517 :     CGI::input({type=>"text",name=>"name",size=>20,style=>"width:50%"}),"&nbsp;",
518 :     CGI::input({%button,value=>"Save"}),
519 :     ),
520 : sh002i 2896 ]);
521 : sh002i 2898 print CGI::end_table();
522 : dpvc 3354 print CGI::hidden({name=>"files", value=>$file});
523 :     $self->HiddenFlags;
524 : sh002i 2896 }
525 :    
526 :     ##################################################
527 :     #
528 : sh002i 2944 # Copy a file
529 : sh002i 2896 #
530 :     sub Copy {
531 : sh002i 2898 my $self = shift;
532 : dpvc 3330 my $dir = "$self->{courseRoot}/$self->{pwd}";
533 :     my $original = $self->getFile('copy'); return unless $original;
534 :     my $oldfile = "$dir/$original";
535 : sh002i 2896
536 : sh002i 2898 if (-d $oldfile) {
537 : sh002i 2944 # FIXME: need to do recursive directory copy
538 : sh002i 2898 $self->addbadmessage("Directory copies are not yet implemented");
539 :     $self->Refresh;
540 :     return;
541 :     }
542 : sh002i 2896
543 : sh002i 2898 if ($self->r->param('confirmed')) {
544 :     my $newfile = $self->r->param('name');
545 :     if ($newfile = $self->verifyPath($newfile,$original)) {
546 :     if (copy($oldfile, $newfile)) {
547 : dpvc 3111 $self->addgoodmessage("File successfully copied");
548 :     $self->Refresh; return;
549 : sh002i 2898 } else {$self->addbadmessage("Can't copy file: $!")}
550 :     }
551 :     }
552 : sh002i 2896
553 : dpvc 3330 $self->Confirm("Copy file as:",$original,"Copy");
554 : sh002i 2898 print CGI::hidden({name=>"files",value=>$original});
555 : sh002i 2896 }
556 :    
557 :     ##################################################
558 :     #
559 : sh002i 2944 # Rename a file
560 : sh002i 2896 #
561 :     sub Rename {
562 : sh002i 2898 my $self = shift;
563 : dpvc 3330 my $dir = "$self->{courseRoot}/$self->{pwd}";
564 :     my $original = $self->getFile('rename'); return unless $original;
565 :     my $oldfile = "$dir/$original";
566 : sh002i 2896
567 : sh002i 2898 if ($self->r->param('confirmed')) {
568 :     my $newfile = $self->r->param('name');
569 :     if ($newfile = $self->verifyPath($newfile,$original)) {
570 :     if (rename $oldfile, $newfile) {
571 : dpvc 3330 $self->addgoodmessage("File successfully renamed");
572 :     $self->Refresh; return;
573 : sh002i 2898 } else {$self->addbadmessage("Can't rename file: $!")}
574 :     }
575 :     }
576 : sh002i 2896
577 : dpvc 3330 $self->Confirm("Rename file as:",uniqueName($dir,$original),"Rename");
578 : sh002i 2898 print CGI::hidden({name=>"files",value=>$original});
579 : sh002i 2896 }
580 :    
581 :     ##################################################
582 :     #
583 : sh002i 2944 # Delete a file
584 : sh002i 2896 #
585 :     sub Delete {
586 : sh002i 2898 my $self = shift;
587 :     my @files = $self->r->param('files');
588 :     if (scalar(@files) == 0) {
589 :     $self->addbadmessage("You must select at least one file to delete");
590 :     $self->Refresh; return;
591 :     }
592 : sh002i 2896
593 : sh002i 2898 my $pwd = $self->{pwd};
594 :     my $dir = $self->{courseRoot}.'/'.$pwd;
595 :     if ($self->r->param('confirmed')) {
596 : sh002i 2896
597 : sh002i 2898 #
598 : sh002i 2944 # If confirmed, go ahead and delete the files
599 : sh002i 2898 #
600 :     foreach my $file (@files) {
601 : dpvc 3330 if (defined $self->checkPWD("$pwd/$file",1)) {
602 : dpvc 3111 if (-d "$dir/$file") {
603 :     my $removed = eval {rmtree("$dir/$file",0,1)};
604 :     if ($removed) {$self->addgoodmessage("Directory '$file' removed (items deleted: $removed)")}
605 :     else {$self->addbadmessage("Directory '$file' not removed: $!")}
606 :     } else {
607 :     if (unlink("$dir/$file")) {$self->addgoodmessage("File '$file' successfully removed")}
608 :     else {$self->addbadmessage("File '$file' not removed: $!")}
609 :     }
610 : sh002i 2898 } else {$self->addbadmessage("Illegal file '$file' specified"); last}
611 :     }
612 :     $self->Refresh;
613 : sh002i 2896
614 : sh002i 2898 } else {
615 : sh002i 2896
616 : sh002i 2898 #
617 : sh002i 2944 # Put up the confirmation dialog box
618 : sh002i 2898 #
619 :     print CGI::start_table({border=>1,cellspacing=>2,cellpadding=>20, style=>"margin: 1em 0 0 5em"});
620 :     print CGI::Tr(
621 :     CGI::td(
622 : dpvc 3111 CGI::b("Warning:")," You have requested that the following items be deleted\n",
623 :     CGI::ul(CGI::li(\@files)),
624 :     ((grep { -d "$dir/$_" } @files)?
625 :     CGI::p({style=>"width:500"},"Some of these files are directories. ",
626 :     "Only delete directories if you really know what you are doing. ",
627 :     "You can seriously damage your course if you delete the wrong thing."): ""),
628 :     CGI::p({style=>"color:red"},"There is no undo for deleting files or directories!"),
629 :     CGI::p("Really delete the items listed above?"),
630 :     CGI::div({style=>"float:left; padding-left:3ex"},
631 :     CGI::input({type=>"submit",name=>"action",value=>"Cancel"})),
632 :     CGI::div({style=>"float:right; padding-right:3ex"},
633 :     CGI::input({type=>"submit",name=>"action",value=>"Delete"})),
634 : sh002i 2898 ),
635 :     );
636 :     print CGI::end_table();
637 : sh002i 2896
638 : sh002i 2898 print CGI::hidden({name=>"confirmed",value=>1});
639 :     foreach my $file (@files) {print CGI::hidden({name=>"files",value=>$file})}
640 : dpvc 3330 $self->HiddenFlags;
641 : sh002i 2898 }
642 : sh002i 2896 }
643 :    
644 :     ##################################################
645 :     #
646 : dpvc 3330 # Make a gzipped tar archive
647 :     #
648 :     sub GZIP {
649 :     my $self = shift;
650 :     my @files = $self->r->param('files');
651 :     if (scalar(@files) == 0) {
652 :     $self->addbadmessage("You must select at least one file to GZIP");
653 :     $self->Refresh; return;
654 :     }
655 :    
656 :     my $dir = $self->{courseRoot}.'/'.$self->{pwd};
657 :     my $archive = uniqueName($dir,(scalar(@files) == 1)?
658 :     $files[0].".tgz": $self->{courseName}.".tgz");
659 : dpvc 3331 my $tar = "cd '$dir' && $self->{ce}{externalPrograms}{tar} -cvzf $archive ";
660 :     $tar .= join(" ",@files);
661 : dpvc 3330 my $files = `$tar`; chomp($files);
662 :     if ($? == 0) {
663 :     my @files = split(/\n/,$files);
664 :     my $n = scalar(@files); my $s = ($n == 1? "": "s");
665 :     $self->addgoodmessage("Archive '$archive' created successfully ($n file$s)");
666 :     } else {
667 :     $self->addbadmessage("Can't create archive '$archive': comand returned ".systemError($?));
668 :     }
669 :     $self->Refresh;
670 :     }
671 :    
672 :     ##################################################
673 :     #
674 :     # Unpack a gzipped tar archive
675 :     #
676 :     sub UNGZIP {
677 :     my $self = shift;
678 :     my $archive = $self->getFile("UNGZIP"); return unless $archive;
679 :     if ($archive !~ m/\.tgz$/) {
680 :     $self->addbadmessage("You can only unpack files ending in '.tgz'");
681 :     } else {
682 :     $self->ungzip($archive);
683 :     }
684 :     $self->Refresh;
685 :     }
686 :    
687 :     sub ungzip {
688 :     my $self = shift;
689 :     my $archive = shift;
690 :     my $dir = $self->{courseRoot}.'/'.$self->{pwd};
691 :     my $tar = "cd '$dir' && $self->{ce}{externalPrograms}{tar} -vxzf $archive";
692 :     my $files = `$tar`; chomp($files);
693 :     if ($? == 0) {
694 :     my @files = split(/\n/,$files);
695 :     my $n = scalar(@files); my $s = ($n == 1? "": "s");
696 :     $self->addgoodmessage("$n file$s unpacked successfully");
697 :     return 1;
698 :     } else {
699 :     $self->addbadmessage("Can't unpack '$archive': command returned ".systemError($?));
700 :     return 0;
701 :     }
702 :     }
703 :    
704 :     ##################################################
705 :     #
706 : sh002i 2944 # Make a new file and edit it
707 : sh002i 2896 #
708 :     sub NewFile {
709 : sh002i 2898 my $self = shift;
710 : sh002i 2896
711 : sh002i 2898 if ($self->r->param('confirmed')) {
712 :     my $name = $self->r->param('name');
713 :     if (my $file = $self->verifyName($name,"file")) {
714 :     if (open(NEWFILE,">$file")) {
715 : dpvc 3111 close(NEWFILE);
716 :     $self->RefreshEdit("",$name);
717 :     return;
718 : sh002i 2898 } else {$self->addbadmessage("Can't create file: $!")}
719 :     }
720 :     }
721 : sh002i 2896
722 : dpvc 3330 $self->Confirm("New file name:","","New File");
723 : sh002i 2896 }
724 :    
725 :     ##################################################
726 :     #
727 : sh002i 2944 # Make a new directory
728 : sh002i 2896 #
729 :     sub NewFolder {
730 : sh002i 2898 my $self = shift;
731 : sh002i 2896
732 : sh002i 2898 if ($self->r->param('confirmed')) {
733 :     my $name = $self->r->param('name');
734 :     if (my $dir = $self->verifyName($name,"directory")) {
735 :     if (mkdir $dir, 0750) {
736 : dpvc 3111 $self->{pwd} .= '/'.$name;
737 :     $self->Refresh; return;
738 : sh002i 2898 } else {$self->addbadmessage("Can't create directory: $!")}
739 :     }
740 :     }
741 : sh002i 2896
742 : dpvc 3330 $self->Confirm("New folder name:","","New Folder");
743 : sh002i 2896 }
744 :    
745 :     ##################################################
746 :     #
747 : sh002i 2944 # Download a file
748 : sh002i 2896 #
749 :     sub Download {
750 : sh002i 2898 my $self = shift;
751 : dpvc 3330 my $pwd = $self->checkPWD($self->r->param('pwd') || '.');
752 :     return unless $pwd;
753 : sh002i 2898 my $filename = $self->getFile("download"); return unless $filename;
754 :     my $file = $self->{ce}{courseDirs}{root}.'/'.$pwd.'/'.$filename;
755 : sh002i 2896
756 : sh002i 2898 if (-d $file) {$self->addbadmessage("You can't download directories"); return}
757 :     unless (-f $file) {$self->addbadmessage("You can't download files of that type"); return}
758 : sh002i 2896
759 : sh002i 2898 $self->r->param('download',$filename);
760 : sh002i 2896 }
761 :    
762 :     ##################################################
763 :     #
764 : sh002i 2944 # Upload a file to the server
765 : sh002i 2896 #
766 :     sub Upload {
767 : sh002i 2898 my $self = shift;
768 :     my $dir = "$self->{courseRoot}/$self->{pwd}";
769 :     my $fileIDhash = $self->r->param('file');
770 :     unless ($fileIDhash) {
771 :     $self->addbadmessage("You have not chosen a file to upload.");
772 :     $self->Refresh;
773 :     return;
774 :     }
775 : sh002i 2896
776 : sh002i 2898 my ($id,$hash) = split(/\s+/,$fileIDhash);
777 :     my $upload = WeBWorK::Upload->retrieve($id,$hash,dir=>$self->{ce}{webworkDirs}{uploadCache});
778 : sh002i 2896
779 : dpvc 3330 my $name = checkName($upload->filename);
780 :     my $action = $self->r->param("formAction") || "Cancel";
781 :     if ($self->r->param("confirmed")) {
782 :     if ($action eq "Cancel") {
783 :     $upload->dispose;
784 :     $self->Refresh;
785 :     return;
786 :     }
787 :     $name = checkName($self->r->param('name')) if ($action eq "Rename");
788 :     }
789 :    
790 : sh002i 2898 if (-e "$dir/$name") {
791 : dpvc 3330 unless ($self->r->param('overwrite') || $action eq "Overwrite") {
792 :     $self->Confirm("File ".CGI::b($name)." already exists. Overwrite it, or rename it as:".
793 :     CGI::p(),uniqueName($dir,$name),"Rename","Overwrite");
794 :     print CGI::hidden({name=>"action",value=>"Upload"});
795 :     print CGI::hidden({name=>"file",value=>$fileIDhash});
796 :     return;
797 :     }
798 : sh002i 2898 }
799 : dpvc 3111 $self->checkFileLocation($name,$self->{pwd});
800 : sh002i 2896
801 : dpvc 3330 my $file = "$dir/$name";
802 :     my $type = $self->getFlag('format','Automatic');
803 : dpvc 3111 my $data;
804 :    
805 :     #
806 :     # Check if we need to convert linebreaks
807 :     #
808 :     if ($type ne 'Binary') {
809 :     my $fh = $upload->fileHandle;
810 :     my @lines = <$fh>; $data = join('',@lines);
811 :     if ($type eq 'Automatic') {$type = isText($data) ? 'Text' : 'Binary'}
812 :     }
813 :     if ($type eq 'Text') {
814 : dpvc 3330 $upload->dispose;
815 : dpvc 3111 $data =~ s/\r\n?/\n/g;
816 : dpvc 3330 open(UPLOAD,">$file") || $self->addbadmessage("Can't create file '$name'");
817 : dpvc 3111 print UPLOAD $data; close(UPLOAD);
818 :     } else {
819 : dpvc 3330 $upload->disposeTo($file);
820 : dpvc 3111 }
821 :    
822 : dpvc 3330 if (-e $file) {
823 :     $self->addgoodmessage("$type file '$name' uploaded successfully");
824 :     if ($name =~ m/\.tgz$/ && $self->getFlag('unpack')) {
825 :     if ($self->ungzip($name) && $self->getFlag('autodelete')) {
826 :     if (unlink($file)) {$self->addgoodmessage("Archive '$name' deleted")}
827 :     else {$self->addbadmessage("Can't delete archive '$name': $!")}
828 :     }
829 :     }
830 :     }
831 :    
832 : sh002i 2898 $self->Refresh;
833 : sh002i 2896 }
834 :    
835 :     ##################################################
836 :     ##################################################
837 :     #
838 : sh002i 2944 # Print a confirmation dialog box
839 : sh002i 2896 #
840 :     sub Confirm {
841 : dpvc 3330 my $self = shift;
842 :     my $message = shift; my $value = shift;
843 :     my $button = shift; my $button2 = shift;
844 : sh002i 2896
845 : sh002i 2898 print CGI::p();
846 :     print CGI::start_table({border=>1,cellspacing=>2,cellpadding=>20, style=>"margin: 1em 0 0 3em"});
847 :     print CGI::Tr(
848 : dpvc 3330 CGI::td({align=>"CENTER"},
849 : dpvc 3111 $message,
850 : dpvc 3330 CGI::input({type=>"text",name=>"name",size=>50,value=>$value}),
851 :     CGI::p(), CGI::center(
852 :     CGI::div({style=>"float:right; padding-right:3ex"},
853 :     CGI::input({type=>"submit",name=>"formAction",value=>$button})), # this will be the default
854 :     CGI::div({style=>"float:left; padding-left:3ex"},
855 :     CGI::input({type=>"submit",name=>"formAction",value=>"Cancel"})),
856 :     ($button2 ? CGI::input({type=>"submit",name=>"formAction",value=>$button2}): ()),
857 :     ),
858 :     ),
859 :     );
860 : sh002i 2898 print CGI::end_table();
861 : dpvc 3330 print CGI::hidden({name=>"confirmed", value=>1});
862 :     $self->HiddenFlags;
863 : sh002i 2898 print CGI::script("window.document.FileManager.name.focus()");
864 : sh002i 2896 }
865 :    
866 :     ##################################################
867 : dpvc 3330 ##################################################
868 : sh002i 2896 #
869 : dpvc 3330 # Check that there is exactly one valid file
870 : sh002i 2896 #
871 :     sub getFile {
872 : sh002i 2898 my $self = shift; my $action = shift;
873 :     my @files = $self->r->param("files");
874 :     if (scalar(@files) > 1) {
875 :     $self->addbadmessage("You can only $action one file at a time.");
876 :     $self->Refresh unless $action eq 'download';
877 :     return;
878 :     }
879 :     if (scalar(@files) == 0 || $files[0] eq "") {
880 :     $self->addbadmessage("You need to select a file to $action.");
881 :     $self->Refresh unless $action eq 'download';
882 :     return;
883 :     }
884 : dpvc 3330 my $pwd = $self->checkPWD($self->{pwd} || $self->r->param('pwd') || '.') || '.';
885 :     if ($self->isSymLink($pwd.'/'.$files[0])) {
886 :     $self->addbadmessage("That symbolic link takes you outside your course directory");
887 :     $self->Refresh unless $action eq 'download';
888 :     return;
889 :     }
890 :     unless ($self->checkPWD($pwd.'/'.$files[0],1)) {
891 :     $self->addbadmessage("You have specified an illegal file");
892 :     $self->Refresh unless $action eq 'download';
893 :     return;
894 :     }
895 : sh002i 2898 return $files[0];
896 : sh002i 2896 }
897 :    
898 :     ##################################################
899 :     #
900 : sh002i 2944 # Get the entries for the directory menu
901 : sh002i 2896 #
902 :     sub directoryMenu {
903 : sh002i 2898 my $course = shift;
904 : sh002i 2944 my $dir = shift; $dir =~ s!^\.(/|$)!!;
905 : sh002i 2898 my @dirs = split('/',$dir);
906 :     my $menu = ""; my $pwd;
907 :    
908 :     my (@values,%labels);
909 :     while (scalar(@dirs)) {
910 :     $pwd = join('/',(@dirs)[0..$#dirs]);
911 :     $dir = pop(@dirs);
912 :     push(@values,$pwd); $labels{$pwd} = $dir;
913 :     }
914 :     push(@values,'.'); $labels{'.'} = $course;
915 :     return (\@values,\%labels);
916 : sh002i 2896 }
917 :    
918 :     ##################################################
919 :     #
920 : sh002i 2944 # Get the directory listing
921 : sh002i 2896 #
922 :     sub directoryListing {
923 : dpvc 3330 my $root = shift; my $pwd = shift; my $showdates = shift;
924 : sh002i 2898 my $dir = $root.'/'.$pwd;
925 :     my (@values,%labels,$size,$data);
926 : sh002i 2896
927 : dpvc 3330 return unless -d $dir;
928 :     my $len = 24;
929 : sh002i 2898 my @names = sortByName(undef,grep(/^[^.]/,readDirectory($dir)));
930 : dpvc 3330 foreach my $name (@names) {
931 :     unless ($name eq 'DATA') { #FIXME don't view the DATA directory
932 :     my $file = "$dir/$name";
933 : gage 2904 push(@values,$name); $labels{$name} = $name;
934 : dpvc 3330 $labels{$name} .= '@' if (-l $file);
935 :     $labels{$name} .= '/' if (-d $file && !-l $file);
936 :     $len = length($labels{$name}) if length($labels{$name}) > $len;
937 : gage 2904 }
938 : sh002i 2898 }
939 : dpvc 3330 if ($showdates) {
940 :     $len += 3;
941 :     foreach my $name (@values) {
942 :     my $file = "$dir/$name";
943 :     my ($size,$date) = (lstat($file))[7,9];
944 :     $labels{$name} = sprintf("%-${len}s%-16s%10s",$labels{$name},
945 :     ((-d $file)? ("",""):
946 :     (getDate($date),getSize($size))));
947 :     }
948 :     }
949 : sh002i 2898 return (\@values,\%labels);
950 : sh002i 2896 }
951 :    
952 : dpvc 3330 sub getDate {
953 :     my ($sec,$min,$hour,$day,$month,$year) = localtime(shift);
954 :     sprintf("%02d-%02d-%04d %02d:%02d",$month+1,$day,$year+1900,$hour,$min);
955 :     }
956 :    
957 :     sub getSize {
958 :     my $size = shift;
959 :     return $size." B " if $size < 1024;
960 :     return sprintf("%.1f KB",$size/1024) if $size < 1024*100;
961 :     return sprintf("%d KB",int($size/1024)) if $size < 1024*1024;
962 :     return sprintf("%.1f MB",$size/1024/1024) if $size < 1024*1024*100;
963 :     return sprintf("%d MB",$size/1024/1024);
964 :     }
965 :    
966 : sh002i 2896 ##################################################
967 :     #
968 : dpvc 3330 # Check if a file is a symbolic link that we
969 :     # are not allowed to follow.
970 :     #
971 :     sub isSymLink {
972 :     my $self = shift; my $file = shift;
973 :     return 0 unless -l $file;
974 :    
975 :     my $courseRoot = $self->{ce}{courseDirs}{root};
976 :     $courseRoot = readlink($courseRoot) if -l $courseRoot;
977 :     my $pwd = $self->{pwd} || $self->r->param('pwd') || '.';
978 :     my $link = File::Spec->rel2abs(readlink($file),"$courseRoot/$pwd");
979 :     #
980 :     # Remove /./ and dir/../ constructs
981 :     #
982 :     $link =~ s!(^|/)(\.(/|$))+!$1!g;
983 :     while ($link =~ s!((\.[^./]+|\.\.[^/]+|[^./][^/]*)/\.\.(/|$))!!) {};
984 :    
985 :     #
986 :     # Link is OK if it is in the course directory
987 :     #
988 :     return 0 if substr($link,0,length($courseRoot)) eq $courseRoot;
989 :    
990 :     #
991 :     # Look through the list of valid paths to see if this link is OK
992 :     #
993 :     my $valid = $self->{ce}{webworkDirs}{valid_symlinks};
994 :     if (defined $valid && $valid) {
995 :     foreach my $path (@{$valid}) {
996 :     return 0 if substr($link,0,length($path)) eq $path;
997 :     }
998 :     }
999 :    
1000 :     return 1;
1001 :     }
1002 :    
1003 :     ##################################################
1004 :     #
1005 : sh002i 2944 # Normalize the working directory and check if it is OK.
1006 : sh002i 2896 #
1007 :     sub checkPWD {
1008 : dpvc 3330 my $self = shift;
1009 : sh002i 2898 my $pwd = shift;
1010 :     my $renameError = shift;
1011 : sh002i 2896
1012 : dpvc 3330 $pwd =~ s!//+!/!g; # remove duplicate slashes
1013 :     $pwd =~ s!(^|/)~!$1_!g; # remove ~user references
1014 :     $pwd =~ s!(^|/)(\.(/|$))+!$1!g; # remove dot directories
1015 : sh002i 2944
1016 :     # remove dir/.. constructions
1017 : sh002i 2898 while ($pwd =~ s!((\.[^./]+|\.\.[^/]+|[^./][^/]*)/\.\.(/|$))!!) {};
1018 : sh002i 2944
1019 :     $pwd =~ s!/$!!; # remove trailing /
1020 :     return if ($pwd =~ m!(^|/)\.\.(/|$)!); # Error if outside the root
1021 : sh002i 2896
1022 : dpvc 3330 # check for bad symbolic links
1023 :     my @dirs = split('/',$pwd);
1024 :     pop(@dirs) if $renameError; # don't check file iteself in this case
1025 :     my @path = ($self->{ce}{courseDirs}{root});
1026 :     foreach my $dir (@dirs) {
1027 :     push @path,$dir;
1028 :     return if ($self->isSymLink(join('/',@path)));
1029 :     }
1030 :    
1031 : sh002i 2898 my $original = $pwd;
1032 : sh002i 2944 $pwd =~ s!(^|/)\.!$1_!g; # don't enter hidden directories
1033 :     $pwd =~ s!^/!!; # remove leading /
1034 : dpvc 3111 $pwd =~ s![^-_./A-Z0-9~ ]!_!gi; # no illegal characters
1035 : sh002i 2898 return if $renameError && $original ne $pwd;
1036 : sh002i 2896
1037 : sh002i 2898 $pwd = '.' if $pwd eq '';
1038 :     return $pwd;
1039 : sh002i 2896 }
1040 :    
1041 :     ##################################################
1042 :     #
1043 : dpvc 3111 # Check that a file is uploaded to the correct directory
1044 :     #
1045 :     sub checkFileLocation {
1046 :     my $self = shift;
1047 :     my $extension = shift; $extension =~ s/.*\.//;
1048 :     my $dir = shift;
1049 :     return unless defined($uploadDir{$extension});
1050 :     return if $dir =~ m/^$uploadDir{$extension}$/;
1051 : dpvc 3330 $dir = $uploadDir{$extension}; $dir =~ s!/\.\*!!;
1052 : dpvc 3111 $self->addbadmessage("Files with extension '.$extension' usually belong in '$dir'");
1053 :     }
1054 :    
1055 :     ##################################################
1056 :     #
1057 : dpvc 3330 # Check a name for bad characters, etc.
1058 :     #
1059 :     sub checkName {
1060 :     my $file = shift;
1061 :     $file =~ s!.*[/\\]!!; # remove directory
1062 :     $file =~ s/[^-_.a-zA-Z0-9 ]/_/g; # no illegal characters
1063 :     $file =~ s/^\./_/; # no initial dot
1064 :     $file = "newfile.txt" unless $file; # no blank names
1065 :     return $file;
1066 :     }
1067 :    
1068 :     ##################################################
1069 :     #
1070 : sh002i 2944 # Get a unique name (in case it already exists)
1071 : sh002i 2896 #
1072 :     sub uniqueName {
1073 : sh002i 2898 my $dir = shift; my $name = shift;
1074 : dpvc 3086 return $name unless (-e "$dir/$name");
1075 : dpvc 3330 my $type = ""; my $n = 1;
1076 : sh002i 2898 $type = $1 if ($name =~ s/(\.[^.]*)$//);
1077 : dpvc 3330 $n = $1 if ($name =~ s/_(\d+)$/_/);
1078 :     while (-e "$dir/${name}_$n$type") {$n++}
1079 :     return "${name}_$n$type";
1080 : sh002i 2896 }
1081 :    
1082 :     ##################################################
1083 :     #
1084 : sh002i 2944 # Verify that a name can be added tot he current
1085 :     # directory.
1086 : sh002i 2896 #
1087 :     sub verifyName {
1088 : sh002i 2898 my $self = shift; my $name = shift; my $object = shift;
1089 :     if ($name) {
1090 :     unless ($name =~ m!/!) {
1091 :     unless ($name =~ m!^\.!) {
1092 : dpvc 3111 unless ($name =~ m![^-_.a-zA-Z0-9 ]!) {
1093 :     my $file = "$self->{courseRoot}/$self->{pwd}/$name";
1094 :     return $file unless (-e $file);
1095 :     $self->addbadmessage("A file with that name already exists");
1096 :     } else {$self->addbadmessage("Your $object name contains illegal characters")}
1097 : sh002i 2898 } else {$self->addbadmessage("Your $object name may not begin with a dot")}
1098 :     } else {$self->addbadmessage("Your $object name may not contain a path component")}
1099 :     } else {$self->addbadmessage("You must specify a $object name")}
1100 :     return
1101 : sh002i 2896 }
1102 :    
1103 :     ##################################################
1104 :     #
1105 : sh002i 2944 # Verify that a file path is valid
1106 : sh002i 2896 #
1107 :     sub verifyPath {
1108 : sh002i 2898 my $self = shift; my $path = shift; my $name = shift;
1109 : sh002i 2896
1110 : sh002i 2898 if ($path) {
1111 :     unless ($path =~ m![^-_.a-zA-Z0-9 /]!) {
1112 :     unless ($path =~ m!^/!) {
1113 : dpvc 3330 $path = $self->checkPWD($self->{pwd}.'/'.$path,1);
1114 : dpvc 3111 if ($path) {
1115 :     $path = $self->{courseRoot}.'/'.$path;
1116 :     $path .= '/'.$name if -d $path && $name;
1117 :     return $path unless (-e $path);
1118 :     $self->addbadmessage("A file with that name already exists");
1119 :     } else {$self->addbadmessage("You have specified an illegal path")}
1120 : sh002i 2898 } else {$self->addbadmessage("You can not specify an absolute path")}
1121 :     } else {$self->addbadmessage("Your file name contains illegal characters")}
1122 :     } else {$self->addbadmessage("You must specify a file name")}
1123 :     return
1124 : sh002i 2896 }
1125 :    
1126 :     ##################################################
1127 :     #
1128 : dpvc 3330 # Get the value of a parameter flag
1129 :     #
1130 :     sub getFlag {
1131 :     my $self = shift; my $flag = shift;
1132 :     my $default = shift; $default = 0 unless defined $default;
1133 :     my $value = $self->r->param($flag);
1134 :     $value = $default unless defined $value;
1135 :     return $value;
1136 :     }
1137 :    
1138 :     ##################################################
1139 :     #
1140 : sh002i 2944 # Make HTML symbols printable
1141 : sh002i 2896 #
1142 :     sub showHTML {
1143 : dpvc 3111 my $string = shift;
1144 :     return '' unless defined $string;
1145 :     $string =~ s/&/\&amp;/g;
1146 :     $string =~ s/</\&lt;/g;
1147 :     $string =~ s/>/\&gt;/g;
1148 :     $string;
1149 : sh002i 2896 }
1150 :    
1151 :     ##################################################
1152 :     #
1153 : sh002i 2944 # Check if a string is plain text
1154 :     # (i.e., doesn't contain three non-regular
1155 :     # characters in a row.)
1156 : sh002i 2896 #
1157 :     sub isText {
1158 : sh002i 2898 my $string = shift;
1159 :     return $string !~ m/[^\s\x20-\x7E]{3,}/;
1160 : sh002i 2896 }
1161 :    
1162 :     ##################################################
1163 : dpvc 3330 #
1164 :     # Convert spaces to &nbsp;, but only REAL spaces
1165 :     #
1166 :     sub sp2nbsp {
1167 :     my $s = shift;
1168 :     $s =~ s/ /\&nbsp;/g;
1169 :     return $s;
1170 :     }
1171 : sh002i 2896
1172 : dpvc 3330 ##################################################
1173 :     #
1174 :     # Hack to convert multiple spaces in the file
1175 :     # selection box into &nbsp; so that the columns
1176 :     # will allign properly in fixed-width fonts.
1177 :     # We have to do it agter the fact, since CGI::
1178 :     # is being "helpful" by turning & in the labels
1179 :     # into &amp; for us. So we have to convert
1180 :     # after the <SELECT> is created (ugh).
1181 :     #
1182 :     sub fixSpaces {
1183 :     my $s = shift;
1184 :     $s =~ s!(<option[^>]*>)(.*?)(</option>)!$1.sp2nbsp($2).$3!gei;
1185 :     return $s;
1186 :     }
1187 :    
1188 :     ##################################################
1189 :     #
1190 :     # Interpret command return errors
1191 :     #
1192 :     sub systemError {
1193 :     my $status = shift;
1194 :     return "error: $!" if $status == 0xFF00;
1195 :     return "exit status ".($status >> 8) if ($status & 0xFF) == 0;
1196 :     return "signal ".($status &= ~0x80);
1197 :     }
1198 :    
1199 :     ##################################################
1200 :    
1201 : sh002i 2896 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9