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

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9