[system] / trunk / webwork2 / lib / WeBWorK / ContentGenerator / Instructor / FileManager.pm Repository:
ViewVC logotype

Annotation of /trunk/webwork2/lib/WeBWorK/ContentGenerator/Instructor/FileManager.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3447 - (view) (download) (as text)

1 : sh002i 2896 ################################################################################
2 :     # WeBWorK Online Homework Delivery System
3 :     # Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/
4 : dpvc 3447 # $CVSHeader: webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/FileManager.pm,v 1.11 2005/07/05 17:54: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 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 : dpvc 3447 print CGI::end_table();
318 :     print CGI::hidden({name=>'pwd',value=>$self->{pwd}});
319 :     print CGI::hidden({name=>'formAction'});
320 :     print CGI::end_multipart_form();
321 :    
322 :     my $fileManagerPage = $self->r->urlpath->newFromModule($self->r->urlpath->module, courseID => $self->{courseName});
323 :     my $fileManagerURL = $self->systemLink($fileManagerPage, authen => 0);
324 :    
325 :     print CGI::start_multipart_form(
326 :     -method=>"POST",
327 :     -action=>$fileManagerURL,
328 :     -id=>"FileManager",
329 :     -name=>"FileManager",
330 :     -style=>"margin:0",
331 :     );
332 :     print $self->hidden_authen_fields;
333 :    
334 :     print CGI::start_table({border=>0,cellpadding=>0,cellspacing=>3, style=>"margin:0 0 0 3em"});
335 :    
336 : sh002i 2898 #
337 : dpvc 3330 # Upload button and checkboxes
338 : sh002i 2898 #
339 :     print CGI::Tr([
340 :     CGI::td(),
341 :     CGI::td({colspan=>3},
342 : dpvc 3111 CGI::input({type=>"submit",name=>"action",style=>"width:7em",value=>"Upload:",id=>"Upload"}),
343 :     CGI::input({type=>"file",name=>"file",id=>"file",size=>40,onChange=>"checkFile()"}),
344 :     CGI::br(),
345 :     CGI::small(join(' &nbsp; ',"Format:",
346 : dpvc 3330 CGI::radio_group('format',['Text','Binary','Automatic'],
347 :     $self->getFlag('format','Automatic')))),
348 : dpvc 3111 ),
349 : sh002i 2896 ]);
350 : dpvc 3330 print CGI::Tr([
351 :     CGI::td(),
352 :     CGI::td({colspan=>3},
353 :     CGI::small(CGI::checkbox('overwrite',$self->getFlag('overwrite'),1,
354 :     'Overwrite existing files silently')),
355 :     CGI::br(),
356 :     CGI::small(CGI::checkbox('unpack',$self->getFlag('unpack'),1,
357 :     'Unpack archives automatically')),
358 :     CGI::small(CGI::checkbox('autodelete',$self->getFlag('autodelete'),1,
359 :     'then delete them')),
360 :     ),
361 :     ]);
362 : sh002i 2896
363 : sh002i 2898 #
364 : sh002i 2944 # End the table
365 :     #
366 : sh002i 2898 print CGI::end_table();
367 :     print CGI::script("checkFiles(); checkFile();");
368 : sh002i 2896 }
369 :    
370 :     ##################################################
371 :     #
372 : sh002i 2944 # Move to the parent directory
373 : sh002i 2896 #
374 :     sub ParentDir {
375 : sh002i 2898 my $self = shift;
376 :     $self->{pwd} = '.' unless ($self->{pwd} =~ s!/[^/]*$!!);
377 :     $self->Refresh;
378 : sh002i 2896 }
379 :    
380 :     ##################################################
381 :     #
382 : sh002i 2944 # Move to the parent directory
383 : sh002i 2896 #
384 :     sub Go {
385 : sh002i 2898 my $self = shift;
386 :     $self->{pwd} = $self->r->param('directory');
387 :     $self->Refresh;
388 : sh002i 2896 }
389 :    
390 :     ##################################################
391 :     #
392 : sh002i 2944 # Open a directory or view a file
393 : sh002i 2896 #
394 :     sub View {
395 : sh002i 2898 my $self = shift; my $pwd = $self->{pwd};
396 :     my $filename = $self->getFile("view"); return unless $filename;
397 :     my $name = "$pwd/$filename"; $name =~ s!^\./?!!;
398 : dpvc 3330 my $file = "$self->{courseRoot}/$pwd/$filename";
399 : sh002i 2896
400 : sh002i 2898 #
401 : dpvc 3330 # Don't follow symbolic links
402 :     #
403 :     if ($self->isSymLink($file)) {
404 :     $self->addbadmessage("That symbolic link takes you outside your course directory");
405 :     $self->Refresh; return;
406 :     }
407 :    
408 :     #
409 : sh002i 2944 # Handle directories by making them the working directory
410 : sh002i 2898 #
411 :     if (-d $file) {
412 :     $self->{pwd} .= '/'.$filename;
413 :     $self->Refresh; return;
414 :     }
415 : sh002i 2896
416 : sh002i 2898 unless (-f $file) {
417 :     $self->addbadmessage("You can't view files of that type");
418 :     $self->Refresh; return;
419 :     }
420 : sh002i 2896
421 : sh002i 2898 #
422 : sh002i 2944 # Include a download link
423 : sh002i 2898 #
424 :     my $urlpath = $self->r->urlpath;
425 :     my $fileManagerPage = $urlpath->newFromModule($urlpath->module, courseID => $self->{courseName});
426 : sh002i 2944 my $fileManagerURL = $self->systemLink($fileManagerPage, params => {download => $filename, pwd => $pwd});
427 : sh002i 2898 print CGI::div({style=>"float:right"},
428 : sh002i 2896 CGI::a({href=>$fileManagerURL},"Download"));
429 : sh002i 2898 print CGI::p(),CGI::b($name),CGI::p();
430 :     print CGI::hr();
431 : sh002i 2896
432 : sh002i 2898 #
433 : sh002i 2944 # For files, display the file, if possible.
434 :     # If the file is an image, display it as an image.
435 : sh002i 2898 #
436 :     my $data = readFile($file);
437 :     if (isText($data)) {
438 :     print CGI::pre(showHTML($data));
439 :     } elsif ($file =~ m/\.(gif|jpg|png)/i) {
440 :     print CGI::img({src=>$fileManagerURL, border=>0});
441 :     } else {
442 :     print CGI::div({class=>"ResultsWithError"},
443 :     "The file does not appear to be a text file.");
444 :     }
445 : sh002i 2896 }
446 :    
447 :     ##################################################
448 :     #
449 : sh002i 2944 # Edit a file
450 : sh002i 2896 #
451 :     sub Edit {
452 : sh002i 2898 my $self = shift;
453 :     my $filename = $self->getFile('edit'); return unless $filename;
454 :     my $file = "$self->{courseRoot}/$self->{pwd}/$filename";
455 : sh002i 2896
456 : sh002i 2898 if (-d $file) {
457 :     $self->addbadmessage("You can't edit a directory");
458 :     $self->Refresh; return;
459 :     }
460 :     unless (-f $file) {
461 :     $self->addbadmessage("You can only edit text files");
462 :     $self->Refresh; return;
463 :     }
464 :     my $data = readFile($file);
465 :     if (!isText($data)) {
466 :     $self->addbadmessage("The file does not appear to be a text file");
467 :     $self->Refresh; return;
468 :     }
469 : sh002i 2896
470 : sh002i 2898 $self->RefreshEdit($data,$filename);
471 : sh002i 2896 }
472 :    
473 :     ##################################################
474 :     #
475 : sh002i 2944 # Save the edited file
476 : sh002i 2896 #
477 :     sub Save {
478 : sh002i 2898 my $self = shift; my $filename = shift;
479 :     my $pwd = $self->{pwd};
480 :     if ($filename) {
481 :     $pwd = substr($filename,length($self->{courseRoot})+1);
482 : dpvc 3086 $pwd =~ s!(/|^)([^/]*)$!!; $filename = $2;
483 :     $pwd = '.' if $pwd eq '';
484 : sh002i 2898 } else {
485 :     $filename = $self->getFile("save"); return unless $filename;
486 :     }
487 :     my $file = "$self->{courseRoot}/$pwd/$filename";
488 :     my $data = $self->r->param("data");
489 : sh002i 2896
490 : sh002i 2898 if (defined($data)) {
491 :     if (open(OUTFILE,">$file")) {
492 :     eval {print OUTFILE $data; close(OUTFILE)};
493 : sh002i 2944 if ($@) {$self->addbadmessage("Failed to save: $@")}
494 : dpvc 3111 else {$self->addgoodmessage("File saved")}
495 : sh002i 2944 } else {$self->addbadmessage("Can't write to file: $!")}
496 : sh002i 2898 } else {$data = ""; $self->addbadmessage("Error: no file data was submitted!")}
497 : sh002i 2896
498 : sh002i 2898 $self->{pwd} = $pwd;
499 :     $self->RefreshEdit($data,$filename);
500 : sh002i 2896 }
501 :    
502 :     ##################################################
503 :     #
504 : sh002i 2944 # Save the edited file under a new name
505 : sh002i 2896 #
506 :     sub SaveAs {
507 : sh002i 2898 my $self = shift;
508 : sh002i 2896
509 : sh002i 2898 my $newfile = $self->r->param('name');
510 :     my $original = $self->r->param('files');
511 :     $newfile = $self->verifyPath($newfile,$original);
512 :     if ($newfile) {$self->Save($newfile); return}
513 :     $self->RefreshEdit($self->r->param('data'),$original);
514 : sh002i 2896 }
515 :    
516 :     ##################################################
517 :     #
518 : sh002i 2944 # Display the Edit page
519 : sh002i 2896 #
520 :     sub RefreshEdit {
521 : sh002i 2898 my $self = shift; my $data = shift; my $file = shift;
522 :     my $pwd = shift || $self->{pwd};
523 :     my $name = "$pwd/$file"; $name =~ s!^\./?!!;
524 : sh002i 2896
525 : sh002i 2898 my %button = (type=>"submit",name=>"action",style=>"width:6em");
526 :     print CGI::p();
527 :     print CGI::start_table({border=>0,cellspacing=>0,cellpadding=>2, width=>"95%", align=>"center"});
528 :     print CGI::Tr([
529 :     CGI::td({align=>"center",style=>"background-color:#CCCCCC"},CGI::b($name)),
530 :     CGI::td(CGI::textarea(-name=>"data",-default=>$data,-override=>1,-rows=>30,-columns=>80,
531 : sh002i 2896 -style=>"width:100%")), ## can't seem to get variable height to work
532 : sh002i 2898 CGI::td({align=>"center", nowrap=>1},
533 :     CGI::input({%button,value=>"Cancel"}),"&nbsp;",
534 :     CGI::input({%button,value=>"Revert"}),"&nbsp;",
535 :     CGI::input({%button,value=>"Save As:"}),
536 :     CGI::input({type=>"text",name=>"name",size=>20,style=>"width:50%"}),"&nbsp;",
537 :     CGI::input({%button,value=>"Save"}),
538 :     ),
539 : sh002i 2896 ]);
540 : sh002i 2898 print CGI::end_table();
541 : dpvc 3354 print CGI::hidden({name=>"files", value=>$file});
542 :     $self->HiddenFlags;
543 : sh002i 2896 }
544 :    
545 :     ##################################################
546 :     #
547 : sh002i 2944 # Copy a file
548 : sh002i 2896 #
549 :     sub Copy {
550 : sh002i 2898 my $self = shift;
551 : dpvc 3330 my $dir = "$self->{courseRoot}/$self->{pwd}";
552 :     my $original = $self->getFile('copy'); return unless $original;
553 :     my $oldfile = "$dir/$original";
554 : sh002i 2896
555 : sh002i 2898 if (-d $oldfile) {
556 : sh002i 2944 # FIXME: need to do recursive directory copy
557 : sh002i 2898 $self->addbadmessage("Directory copies are not yet implemented");
558 :     $self->Refresh;
559 :     return;
560 :     }
561 : sh002i 2896
562 : sh002i 2898 if ($self->r->param('confirmed')) {
563 :     my $newfile = $self->r->param('name');
564 :     if ($newfile = $self->verifyPath($newfile,$original)) {
565 :     if (copy($oldfile, $newfile)) {
566 : dpvc 3111 $self->addgoodmessage("File successfully copied");
567 :     $self->Refresh; return;
568 : sh002i 2898 } else {$self->addbadmessage("Can't copy file: $!")}
569 :     }
570 :     }
571 : sh002i 2896
572 : dpvc 3330 $self->Confirm("Copy file as:",$original,"Copy");
573 : sh002i 2898 print CGI::hidden({name=>"files",value=>$original});
574 : sh002i 2896 }
575 :    
576 :     ##################################################
577 :     #
578 : sh002i 2944 # Rename a file
579 : sh002i 2896 #
580 :     sub Rename {
581 : sh002i 2898 my $self = shift;
582 : dpvc 3330 my $dir = "$self->{courseRoot}/$self->{pwd}";
583 :     my $original = $self->getFile('rename'); return unless $original;
584 :     my $oldfile = "$dir/$original";
585 : sh002i 2896
586 : sh002i 2898 if ($self->r->param('confirmed')) {
587 :     my $newfile = $self->r->param('name');
588 :     if ($newfile = $self->verifyPath($newfile,$original)) {
589 :     if (rename $oldfile, $newfile) {
590 : dpvc 3330 $self->addgoodmessage("File successfully renamed");
591 :     $self->Refresh; return;
592 : sh002i 2898 } else {$self->addbadmessage("Can't rename file: $!")}
593 :     }
594 :     }
595 : sh002i 2896
596 : dpvc 3330 $self->Confirm("Rename file as:",uniqueName($dir,$original),"Rename");
597 : sh002i 2898 print CGI::hidden({name=>"files",value=>$original});
598 : sh002i 2896 }
599 :    
600 :     ##################################################
601 :     #
602 : sh002i 2944 # Delete a file
603 : sh002i 2896 #
604 :     sub Delete {
605 : sh002i 2898 my $self = shift;
606 :     my @files = $self->r->param('files');
607 :     if (scalar(@files) == 0) {
608 :     $self->addbadmessage("You must select at least one file to delete");
609 :     $self->Refresh; return;
610 :     }
611 : sh002i 2896
612 : sh002i 2898 my $pwd = $self->{pwd};
613 :     my $dir = $self->{courseRoot}.'/'.$pwd;
614 :     if ($self->r->param('confirmed')) {
615 : sh002i 2896
616 : sh002i 2898 #
617 : sh002i 2944 # If confirmed, go ahead and delete the files
618 : sh002i 2898 #
619 :     foreach my $file (@files) {
620 : dpvc 3330 if (defined $self->checkPWD("$pwd/$file",1)) {
621 : dpvc 3111 if (-d "$dir/$file") {
622 :     my $removed = eval {rmtree("$dir/$file",0,1)};
623 :     if ($removed) {$self->addgoodmessage("Directory '$file' removed (items deleted: $removed)")}
624 :     else {$self->addbadmessage("Directory '$file' not removed: $!")}
625 :     } else {
626 :     if (unlink("$dir/$file")) {$self->addgoodmessage("File '$file' successfully removed")}
627 :     else {$self->addbadmessage("File '$file' not removed: $!")}
628 :     }
629 : sh002i 2898 } else {$self->addbadmessage("Illegal file '$file' specified"); last}
630 :     }
631 :     $self->Refresh;
632 : sh002i 2896
633 : sh002i 2898 } else {
634 : sh002i 2896
635 : sh002i 2898 #
636 : sh002i 2944 # Put up the confirmation dialog box
637 : sh002i 2898 #
638 :     print CGI::start_table({border=>1,cellspacing=>2,cellpadding=>20, style=>"margin: 1em 0 0 5em"});
639 :     print CGI::Tr(
640 :     CGI::td(
641 : dpvc 3111 CGI::b("Warning:")," You have requested that the following items be deleted\n",
642 :     CGI::ul(CGI::li(\@files)),
643 :     ((grep { -d "$dir/$_" } @files)?
644 :     CGI::p({style=>"width:500"},"Some of these files are directories. ",
645 :     "Only delete directories if you really know what you are doing. ",
646 :     "You can seriously damage your course if you delete the wrong thing."): ""),
647 :     CGI::p({style=>"color:red"},"There is no undo for deleting files or directories!"),
648 :     CGI::p("Really delete the items listed above?"),
649 :     CGI::div({style=>"float:left; padding-left:3ex"},
650 :     CGI::input({type=>"submit",name=>"action",value=>"Cancel"})),
651 :     CGI::div({style=>"float:right; padding-right:3ex"},
652 :     CGI::input({type=>"submit",name=>"action",value=>"Delete"})),
653 : sh002i 2898 ),
654 :     );
655 :     print CGI::end_table();
656 : sh002i 2896
657 : sh002i 2898 print CGI::hidden({name=>"confirmed",value=>1});
658 :     foreach my $file (@files) {print CGI::hidden({name=>"files",value=>$file})}
659 : dpvc 3330 $self->HiddenFlags;
660 : sh002i 2898 }
661 : sh002i 2896 }
662 :    
663 :     ##################################################
664 :     #
665 : dpvc 3330 # Make a gzipped tar archive
666 :     #
667 :     sub GZIP {
668 :     my $self = shift;
669 :     my @files = $self->r->param('files');
670 :     if (scalar(@files) == 0) {
671 :     $self->addbadmessage("You must select at least one file to GZIP");
672 :     $self->Refresh; return;
673 :     }
674 :    
675 :     my $dir = $self->{courseRoot}.'/'.$self->{pwd};
676 :     my $archive = uniqueName($dir,(scalar(@files) == 1)?
677 :     $files[0].".tgz": $self->{courseName}.".tgz");
678 : dpvc 3331 my $tar = "cd '$dir' && $self->{ce}{externalPrograms}{tar} -cvzf $archive ";
679 :     $tar .= join(" ",@files);
680 : dpvc 3330 my $files = `$tar`; chomp($files);
681 :     if ($? == 0) {
682 :     my @files = split(/\n/,$files);
683 :     my $n = scalar(@files); my $s = ($n == 1? "": "s");
684 :     $self->addgoodmessage("Archive '$archive' created successfully ($n file$s)");
685 :     } else {
686 :     $self->addbadmessage("Can't create archive '$archive': comand returned ".systemError($?));
687 :     }
688 :     $self->Refresh;
689 :     }
690 :    
691 :     ##################################################
692 :     #
693 :     # Unpack a gzipped tar archive
694 :     #
695 :     sub UNGZIP {
696 :     my $self = shift;
697 :     my $archive = $self->getFile("UNGZIP"); return unless $archive;
698 :     if ($archive !~ m/\.tgz$/) {
699 :     $self->addbadmessage("You can only unpack files ending in '.tgz'");
700 :     } else {
701 :     $self->ungzip($archive);
702 :     }
703 :     $self->Refresh;
704 :     }
705 :    
706 :     sub ungzip {
707 :     my $self = shift;
708 :     my $archive = shift;
709 :     my $dir = $self->{courseRoot}.'/'.$self->{pwd};
710 :     my $tar = "cd '$dir' && $self->{ce}{externalPrograms}{tar} -vxzf $archive";
711 :     my $files = `$tar`; chomp($files);
712 :     if ($? == 0) {
713 :     my @files = split(/\n/,$files);
714 :     my $n = scalar(@files); my $s = ($n == 1? "": "s");
715 :     $self->addgoodmessage("$n file$s unpacked successfully");
716 :     return 1;
717 :     } else {
718 :     $self->addbadmessage("Can't unpack '$archive': command returned ".systemError($?));
719 :     return 0;
720 :     }
721 :     }
722 :    
723 :     ##################################################
724 :     #
725 : sh002i 2944 # Make a new file and edit it
726 : sh002i 2896 #
727 :     sub NewFile {
728 : sh002i 2898 my $self = shift;
729 : sh002i 2896
730 : sh002i 2898 if ($self->r->param('confirmed')) {
731 :     my $name = $self->r->param('name');
732 :     if (my $file = $self->verifyName($name,"file")) {
733 :     if (open(NEWFILE,">$file")) {
734 : dpvc 3111 close(NEWFILE);
735 :     $self->RefreshEdit("",$name);
736 :     return;
737 : sh002i 2898 } else {$self->addbadmessage("Can't create file: $!")}
738 :     }
739 :     }
740 : sh002i 2896
741 : dpvc 3330 $self->Confirm("New file name:","","New File");
742 : sh002i 2896 }
743 :    
744 :     ##################################################
745 :     #
746 : sh002i 2944 # Make a new directory
747 : sh002i 2896 #
748 :     sub NewFolder {
749 : sh002i 2898 my $self = shift;
750 : sh002i 2896
751 : sh002i 2898 if ($self->r->param('confirmed')) {
752 :     my $name = $self->r->param('name');
753 :     if (my $dir = $self->verifyName($name,"directory")) {
754 :     if (mkdir $dir, 0750) {
755 : dpvc 3111 $self->{pwd} .= '/'.$name;
756 :     $self->Refresh; return;
757 : sh002i 2898 } else {$self->addbadmessage("Can't create directory: $!")}
758 :     }
759 :     }
760 : sh002i 2896
761 : dpvc 3330 $self->Confirm("New folder name:","","New Folder");
762 : sh002i 2896 }
763 :    
764 :     ##################################################
765 :     #
766 : sh002i 2944 # Download a file
767 : sh002i 2896 #
768 :     sub Download {
769 : sh002i 2898 my $self = shift;
770 : dpvc 3330 my $pwd = $self->checkPWD($self->r->param('pwd') || '.');
771 :     return unless $pwd;
772 : sh002i 2898 my $filename = $self->getFile("download"); return unless $filename;
773 :     my $file = $self->{ce}{courseDirs}{root}.'/'.$pwd.'/'.$filename;
774 : sh002i 2896
775 : sh002i 2898 if (-d $file) {$self->addbadmessage("You can't download directories"); return}
776 :     unless (-f $file) {$self->addbadmessage("You can't download files of that type"); return}
777 : sh002i 2896
778 : sh002i 2898 $self->r->param('download',$filename);
779 : sh002i 2896 }
780 :    
781 :     ##################################################
782 :     #
783 : sh002i 2944 # Upload a file to the server
784 : sh002i 2896 #
785 :     sub Upload {
786 : sh002i 2898 my $self = shift;
787 :     my $dir = "$self->{courseRoot}/$self->{pwd}";
788 :     my $fileIDhash = $self->r->param('file');
789 :     unless ($fileIDhash) {
790 :     $self->addbadmessage("You have not chosen a file to upload.");
791 :     $self->Refresh;
792 :     return;
793 :     }
794 : sh002i 2896
795 : sh002i 2898 my ($id,$hash) = split(/\s+/,$fileIDhash);
796 :     my $upload = WeBWorK::Upload->retrieve($id,$hash,dir=>$self->{ce}{webworkDirs}{uploadCache});
797 : sh002i 2896
798 : dpvc 3330 my $name = checkName($upload->filename);
799 :     my $action = $self->r->param("formAction") || "Cancel";
800 :     if ($self->r->param("confirmed")) {
801 :     if ($action eq "Cancel") {
802 :     $upload->dispose;
803 :     $self->Refresh;
804 :     return;
805 :     }
806 :     $name = checkName($self->r->param('name')) if ($action eq "Rename");
807 :     }
808 :    
809 : sh002i 2898 if (-e "$dir/$name") {
810 : dpvc 3330 unless ($self->r->param('overwrite') || $action eq "Overwrite") {
811 :     $self->Confirm("File ".CGI::b($name)." already exists. Overwrite it, or rename it as:".
812 :     CGI::p(),uniqueName($dir,$name),"Rename","Overwrite");
813 :     print CGI::hidden({name=>"action",value=>"Upload"});
814 :     print CGI::hidden({name=>"file",value=>$fileIDhash});
815 :     return;
816 :     }
817 : sh002i 2898 }
818 : dpvc 3111 $self->checkFileLocation($name,$self->{pwd});
819 : sh002i 2896
820 : dpvc 3330 my $file = "$dir/$name";
821 :     my $type = $self->getFlag('format','Automatic');
822 : dpvc 3111 my $data;
823 :    
824 :     #
825 :     # Check if we need to convert linebreaks
826 :     #
827 :     if ($type ne 'Binary') {
828 :     my $fh = $upload->fileHandle;
829 :     my @lines = <$fh>; $data = join('',@lines);
830 :     if ($type eq 'Automatic') {$type = isText($data) ? 'Text' : 'Binary'}
831 :     }
832 :     if ($type eq 'Text') {
833 : dpvc 3330 $upload->dispose;
834 : dpvc 3111 $data =~ s/\r\n?/\n/g;
835 : dpvc 3330 open(UPLOAD,">$file") || $self->addbadmessage("Can't create file '$name'");
836 : dpvc 3111 print UPLOAD $data; close(UPLOAD);
837 :     } else {
838 : dpvc 3330 $upload->disposeTo($file);
839 : dpvc 3111 }
840 :    
841 : dpvc 3330 if (-e $file) {
842 :     $self->addgoodmessage("$type file '$name' uploaded successfully");
843 :     if ($name =~ m/\.tgz$/ && $self->getFlag('unpack')) {
844 :     if ($self->ungzip($name) && $self->getFlag('autodelete')) {
845 :     if (unlink($file)) {$self->addgoodmessage("Archive '$name' deleted")}
846 :     else {$self->addbadmessage("Can't delete archive '$name': $!")}
847 :     }
848 :     }
849 :     }
850 :    
851 : sh002i 2898 $self->Refresh;
852 : sh002i 2896 }
853 :    
854 :     ##################################################
855 :     ##################################################
856 :     #
857 : sh002i 2944 # Print a confirmation dialog box
858 : sh002i 2896 #
859 :     sub Confirm {
860 : dpvc 3330 my $self = shift;
861 :     my $message = shift; my $value = shift;
862 :     my $button = shift; my $button2 = shift;
863 : sh002i 2896
864 : sh002i 2898 print CGI::p();
865 :     print CGI::start_table({border=>1,cellspacing=>2,cellpadding=>20, style=>"margin: 1em 0 0 3em"});
866 :     print CGI::Tr(
867 : dpvc 3330 CGI::td({align=>"CENTER"},
868 : dpvc 3111 $message,
869 : dpvc 3330 CGI::input({type=>"text",name=>"name",size=>50,value=>$value}),
870 :     CGI::p(), CGI::center(
871 :     CGI::div({style=>"float:right; padding-right:3ex"},
872 :     CGI::input({type=>"submit",name=>"formAction",value=>$button})), # this will be the default
873 :     CGI::div({style=>"float:left; padding-left:3ex"},
874 :     CGI::input({type=>"submit",name=>"formAction",value=>"Cancel"})),
875 :     ($button2 ? CGI::input({type=>"submit",name=>"formAction",value=>$button2}): ()),
876 :     ),
877 :     ),
878 :     );
879 : sh002i 2898 print CGI::end_table();
880 : dpvc 3330 print CGI::hidden({name=>"confirmed", value=>1});
881 :     $self->HiddenFlags;
882 : sh002i 2898 print CGI::script("window.document.FileManager.name.focus()");
883 : sh002i 2896 }
884 :    
885 :     ##################################################
886 : dpvc 3330 ##################################################
887 : sh002i 2896 #
888 : dpvc 3330 # Check that there is exactly one valid file
889 : sh002i 2896 #
890 :     sub getFile {
891 : sh002i 2898 my $self = shift; my $action = shift;
892 :     my @files = $self->r->param("files");
893 :     if (scalar(@files) > 1) {
894 :     $self->addbadmessage("You can only $action one file at a time.");
895 :     $self->Refresh unless $action eq 'download';
896 :     return;
897 :     }
898 :     if (scalar(@files) == 0 || $files[0] eq "") {
899 :     $self->addbadmessage("You need to select a file to $action.");
900 :     $self->Refresh unless $action eq 'download';
901 :     return;
902 :     }
903 : dpvc 3330 my $pwd = $self->checkPWD($self->{pwd} || $self->r->param('pwd') || '.') || '.';
904 :     if ($self->isSymLink($pwd.'/'.$files[0])) {
905 :     $self->addbadmessage("That symbolic link takes you outside your course directory");
906 :     $self->Refresh unless $action eq 'download';
907 :     return;
908 :     }
909 :     unless ($self->checkPWD($pwd.'/'.$files[0],1)) {
910 :     $self->addbadmessage("You have specified an illegal file");
911 :     $self->Refresh unless $action eq 'download';
912 :     return;
913 :     }
914 : sh002i 2898 return $files[0];
915 : sh002i 2896 }
916 :    
917 :     ##################################################
918 :     #
919 : sh002i 2944 # Get the entries for the directory menu
920 : sh002i 2896 #
921 :     sub directoryMenu {
922 : sh002i 2898 my $course = shift;
923 : sh002i 2944 my $dir = shift; $dir =~ s!^\.(/|$)!!;
924 : sh002i 2898 my @dirs = split('/',$dir);
925 :     my $menu = ""; my $pwd;
926 :    
927 :     my (@values,%labels);
928 :     while (scalar(@dirs)) {
929 :     $pwd = join('/',(@dirs)[0..$#dirs]);
930 :     $dir = pop(@dirs);
931 :     push(@values,$pwd); $labels{$pwd} = $dir;
932 :     }
933 :     push(@values,'.'); $labels{'.'} = $course;
934 :     return (\@values,\%labels);
935 : sh002i 2896 }
936 :    
937 :     ##################################################
938 :     #
939 : sh002i 2944 # Get the directory listing
940 : sh002i 2896 #
941 :     sub directoryListing {
942 : dpvc 3330 my $root = shift; my $pwd = shift; my $showdates = shift;
943 : sh002i 2898 my $dir = $root.'/'.$pwd;
944 :     my (@values,%labels,$size,$data);
945 : sh002i 2896
946 : dpvc 3330 return unless -d $dir;
947 :     my $len = 24;
948 : sh002i 2898 my @names = sortByName(undef,grep(/^[^.]/,readDirectory($dir)));
949 : dpvc 3330 foreach my $name (@names) {
950 :     unless ($name eq 'DATA') { #FIXME don't view the DATA directory
951 :     my $file = "$dir/$name";
952 : gage 2904 push(@values,$name); $labels{$name} = $name;
953 : dpvc 3330 $labels{$name} .= '@' if (-l $file);
954 :     $labels{$name} .= '/' if (-d $file && !-l $file);
955 :     $len = length($labels{$name}) if length($labels{$name}) > $len;
956 : gage 2904 }
957 : sh002i 2898 }
958 : dpvc 3330 if ($showdates) {
959 :     $len += 3;
960 :     foreach my $name (@values) {
961 :     my $file = "$dir/$name";
962 :     my ($size,$date) = (lstat($file))[7,9];
963 :     $labels{$name} = sprintf("%-${len}s%-16s%10s",$labels{$name},
964 :     ((-d $file)? ("",""):
965 :     (getDate($date),getSize($size))));
966 :     }
967 :     }
968 : sh002i 2898 return (\@values,\%labels);
969 : sh002i 2896 }
970 :    
971 : dpvc 3330 sub getDate {
972 :     my ($sec,$min,$hour,$day,$month,$year) = localtime(shift);
973 :     sprintf("%02d-%02d-%04d %02d:%02d",$month+1,$day,$year+1900,$hour,$min);
974 :     }
975 :    
976 :     sub getSize {
977 :     my $size = shift;
978 :     return $size." B " if $size < 1024;
979 :     return sprintf("%.1f KB",$size/1024) if $size < 1024*100;
980 :     return sprintf("%d KB",int($size/1024)) if $size < 1024*1024;
981 :     return sprintf("%.1f MB",$size/1024/1024) if $size < 1024*1024*100;
982 :     return sprintf("%d MB",$size/1024/1024);
983 :     }
984 :    
985 : sh002i 2896 ##################################################
986 :     #
987 : dpvc 3330 # Check if a file is a symbolic link that we
988 :     # are not allowed to follow.
989 :     #
990 :     sub isSymLink {
991 :     my $self = shift; my $file = shift;
992 :     return 0 unless -l $file;
993 :    
994 :     my $courseRoot = $self->{ce}{courseDirs}{root};
995 :     $courseRoot = readlink($courseRoot) if -l $courseRoot;
996 :     my $pwd = $self->{pwd} || $self->r->param('pwd') || '.';
997 :     my $link = File::Spec->rel2abs(readlink($file),"$courseRoot/$pwd");
998 :     #
999 :     # Remove /./ and dir/../ constructs
1000 :     #
1001 :     $link =~ s!(^|/)(\.(/|$))+!$1!g;
1002 :     while ($link =~ s!((\.[^./]+|\.\.[^/]+|[^./][^/]*)/\.\.(/|$))!!) {};
1003 :    
1004 :     #
1005 :     # Link is OK if it is in the course directory
1006 :     #
1007 :     return 0 if substr($link,0,length($courseRoot)) eq $courseRoot;
1008 :    
1009 :     #
1010 :     # Look through the list of valid paths to see if this link is OK
1011 :     #
1012 :     my $valid = $self->{ce}{webworkDirs}{valid_symlinks};
1013 :     if (defined $valid && $valid) {
1014 :     foreach my $path (@{$valid}) {
1015 :     return 0 if substr($link,0,length($path)) eq $path;
1016 :     }
1017 :     }
1018 :    
1019 :     return 1;
1020 :     }
1021 :    
1022 :     ##################################################
1023 :     #
1024 : sh002i 2944 # Normalize the working directory and check if it is OK.
1025 : sh002i 2896 #
1026 :     sub checkPWD {
1027 : dpvc 3330 my $self = shift;
1028 : sh002i 2898 my $pwd = shift;
1029 :     my $renameError = shift;
1030 : sh002i 2896
1031 : dpvc 3330 $pwd =~ s!//+!/!g; # remove duplicate slashes
1032 :     $pwd =~ s!(^|/)~!$1_!g; # remove ~user references
1033 :     $pwd =~ s!(^|/)(\.(/|$))+!$1!g; # remove dot directories
1034 : sh002i 2944
1035 :     # remove dir/.. constructions
1036 : sh002i 2898 while ($pwd =~ s!((\.[^./]+|\.\.[^/]+|[^./][^/]*)/\.\.(/|$))!!) {};
1037 : sh002i 2944
1038 :     $pwd =~ s!/$!!; # remove trailing /
1039 :     return if ($pwd =~ m!(^|/)\.\.(/|$)!); # Error if outside the root
1040 : sh002i 2896
1041 : dpvc 3330 # check for bad symbolic links
1042 :     my @dirs = split('/',$pwd);
1043 :     pop(@dirs) if $renameError; # don't check file iteself in this case
1044 :     my @path = ($self->{ce}{courseDirs}{root});
1045 :     foreach my $dir (@dirs) {
1046 :     push @path,$dir;
1047 :     return if ($self->isSymLink(join('/',@path)));
1048 :     }
1049 :    
1050 : sh002i 2898 my $original = $pwd;
1051 : sh002i 2944 $pwd =~ s!(^|/)\.!$1_!g; # don't enter hidden directories
1052 :     $pwd =~ s!^/!!; # remove leading /
1053 : dpvc 3111 $pwd =~ s![^-_./A-Z0-9~ ]!_!gi; # no illegal characters
1054 : sh002i 2898 return if $renameError && $original ne $pwd;
1055 : sh002i 2896
1056 : sh002i 2898 $pwd = '.' if $pwd eq '';
1057 :     return $pwd;
1058 : sh002i 2896 }
1059 :    
1060 :     ##################################################
1061 :     #
1062 : dpvc 3111 # Check that a file is uploaded to the correct directory
1063 :     #
1064 :     sub checkFileLocation {
1065 :     my $self = shift;
1066 :     my $extension = shift; $extension =~ s/.*\.//;
1067 :     my $dir = shift;
1068 :     return unless defined($uploadDir{$extension});
1069 :     return if $dir =~ m/^$uploadDir{$extension}$/;
1070 : dpvc 3330 $dir = $uploadDir{$extension}; $dir =~ s!/\.\*!!;
1071 : dpvc 3111 $self->addbadmessage("Files with extension '.$extension' usually belong in '$dir'");
1072 :     }
1073 :    
1074 :     ##################################################
1075 :     #
1076 : dpvc 3330 # Check a name for bad characters, etc.
1077 :     #
1078 :     sub checkName {
1079 :     my $file = shift;
1080 :     $file =~ s!.*[/\\]!!; # remove directory
1081 :     $file =~ s/[^-_.a-zA-Z0-9 ]/_/g; # no illegal characters
1082 :     $file =~ s/^\./_/; # no initial dot
1083 :     $file = "newfile.txt" unless $file; # no blank names
1084 :     return $file;
1085 :     }
1086 :    
1087 :     ##################################################
1088 :     #
1089 : sh002i 2944 # Get a unique name (in case it already exists)
1090 : sh002i 2896 #
1091 :     sub uniqueName {
1092 : sh002i 2898 my $dir = shift; my $name = shift;
1093 : dpvc 3086 return $name unless (-e "$dir/$name");
1094 : dpvc 3330 my $type = ""; my $n = 1;
1095 : sh002i 2898 $type = $1 if ($name =~ s/(\.[^.]*)$//);
1096 : dpvc 3330 $n = $1 if ($name =~ s/_(\d+)$/_/);
1097 :     while (-e "$dir/${name}_$n$type") {$n++}
1098 :     return "${name}_$n$type";
1099 : sh002i 2896 }
1100 :    
1101 :     ##################################################
1102 :     #
1103 : sh002i 2944 # Verify that a name can be added tot he current
1104 :     # directory.
1105 : sh002i 2896 #
1106 :     sub verifyName {
1107 : sh002i 2898 my $self = shift; my $name = shift; my $object = shift;
1108 :     if ($name) {
1109 :     unless ($name =~ m!/!) {
1110 :     unless ($name =~ m!^\.!) {
1111 : dpvc 3111 unless ($name =~ m![^-_.a-zA-Z0-9 ]!) {
1112 :     my $file = "$self->{courseRoot}/$self->{pwd}/$name";
1113 :     return $file unless (-e $file);
1114 :     $self->addbadmessage("A file with that name already exists");
1115 :     } else {$self->addbadmessage("Your $object name contains illegal characters")}
1116 : sh002i 2898 } else {$self->addbadmessage("Your $object name may not begin with a dot")}
1117 :     } else {$self->addbadmessage("Your $object name may not contain a path component")}
1118 :     } else {$self->addbadmessage("You must specify a $object name")}
1119 :     return
1120 : sh002i 2896 }
1121 :    
1122 :     ##################################################
1123 :     #
1124 : sh002i 2944 # Verify that a file path is valid
1125 : sh002i 2896 #
1126 :     sub verifyPath {
1127 : sh002i 2898 my $self = shift; my $path = shift; my $name = shift;
1128 : sh002i 2896
1129 : sh002i 2898 if ($path) {
1130 :     unless ($path =~ m![^-_.a-zA-Z0-9 /]!) {
1131 :     unless ($path =~ m!^/!) {
1132 : dpvc 3330 $path = $self->checkPWD($self->{pwd}.'/'.$path,1);
1133 : dpvc 3111 if ($path) {
1134 :     $path = $self->{courseRoot}.'/'.$path;
1135 :     $path .= '/'.$name if -d $path && $name;
1136 :     return $path unless (-e $path);
1137 :     $self->addbadmessage("A file with that name already exists");
1138 :     } else {$self->addbadmessage("You have specified an illegal path")}
1139 : sh002i 2898 } else {$self->addbadmessage("You can not specify an absolute path")}
1140 :     } else {$self->addbadmessage("Your file name contains illegal characters")}
1141 :     } else {$self->addbadmessage("You must specify a file name")}
1142 :     return
1143 : sh002i 2896 }
1144 :    
1145 :     ##################################################
1146 :     #
1147 : dpvc 3330 # Get the value of a parameter flag
1148 :     #
1149 :     sub getFlag {
1150 :     my $self = shift; my $flag = shift;
1151 :     my $default = shift; $default = 0 unless defined $default;
1152 :     my $value = $self->r->param($flag);
1153 :     $value = $default unless defined $value;
1154 :     return $value;
1155 :     }
1156 :    
1157 :     ##################################################
1158 :     #
1159 : sh002i 2944 # Make HTML symbols printable
1160 : sh002i 2896 #
1161 :     sub showHTML {
1162 : dpvc 3111 my $string = shift;
1163 :     return '' unless defined $string;
1164 :     $string =~ s/&/\&amp;/g;
1165 :     $string =~ s/</\&lt;/g;
1166 :     $string =~ s/>/\&gt;/g;
1167 :     $string;
1168 : sh002i 2896 }
1169 :    
1170 :     ##################################################
1171 :     #
1172 : sh002i 2944 # Check if a string is plain text
1173 :     # (i.e., doesn't contain three non-regular
1174 :     # characters in a row.)
1175 : sh002i 2896 #
1176 :     sub isText {
1177 : sh002i 2898 my $string = shift;
1178 :     return $string !~ m/[^\s\x20-\x7E]{3,}/;
1179 : sh002i 2896 }
1180 :    
1181 :     ##################################################
1182 : dpvc 3330 #
1183 :     # Convert spaces to &nbsp;, but only REAL spaces
1184 :     #
1185 :     sub sp2nbsp {
1186 :     my $s = shift;
1187 :     $s =~ s/ /\&nbsp;/g;
1188 :     return $s;
1189 :     }
1190 : sh002i 2896
1191 : dpvc 3330 ##################################################
1192 :     #
1193 :     # Hack to convert multiple spaces in the file
1194 :     # selection box into &nbsp; so that the columns
1195 :     # will allign properly in fixed-width fonts.
1196 :     # We have to do it agter the fact, since CGI::
1197 :     # is being "helpful" by turning & in the labels
1198 :     # into &amp; for us. So we have to convert
1199 :     # after the <SELECT> is created (ugh).
1200 :     #
1201 :     sub fixSpaces {
1202 :     my $s = shift;
1203 :     $s =~ s!(<option[^>]*>)(.*?)(</option>)!$1.sp2nbsp($2).$3!gei;
1204 :     return $s;
1205 :     }
1206 :    
1207 :     ##################################################
1208 :     #
1209 :     # Interpret command return errors
1210 :     #
1211 :     sub systemError {
1212 :     my $status = shift;
1213 :     return "error: $!" if $status == 0xFF00;
1214 :     return "exit status ".($status >> 8) if ($status & 0xFF) == 0;
1215 :     return "signal ".($status &= ~0x80);
1216 :     }
1217 :    
1218 :     ##################################################
1219 :    
1220 : sh002i 2896 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9