=====Development of Files action===== <= 1024; $level++) { $bytes /= 1024; } switch ($level) { case 0: $suffix = (isset($names[0])) ? $names[0] : 'Bytes'; break; case 1: $suffix = (isset($names[1])) ? $names[1] : 'KB'; break; case 2: $suffix = (isset($names[2])) ? $names[2] : 'MB'; break; case 3: $suffix = (isset($names[3])) ? $names[3] : 'GB'; break; case 4: $suffix = (isset($names[4])) ? $names[4] : 'TB'; break; default: $suffix = (isset($names[$level])) ? $names[$level] : ''; break; } if (empty($suffix)) { trigger_error('Unable to find suffix for case ' . $level); return false; } return round($bytes, $precision) . ' ' . $suffix; } } if ($download <> '') { // link to download a file if ($text == '') $text = $download; echo "href('files.xml',$this->GetPageTag(),'action=download&file='.urlencode($download))."\">".$text.""; } elseif ($this->page AND $this->HasAccess('write') AND ($this->method <> 'print.xml') AND ($this->method <> 'edit')) { // upload path if ($this->config['upload_path'] == '') $this->config['upload_path'] = 'files'; $upload_path = $this->config['upload_path'].'/'.$this->GetPageTag(); if (! is_dir($upload_path)) mkdir_r($upload_path); // upload action $uploaded = $_FILES['file']; if ($_REQUEST['action'] == 'upload' AND $uploaded['size'] > 0) copy ($uploaded['tmp_name'], $upload_path.'/'.$uploaded['name']); // uploaded files print " "; $dir = opendir($upload_path); while ($file = readdir($dir)) { if ($file != '.' && $file != '..') { $num++; $delete_link = "href('files.xml',$this->GetPageTag(),'action=delete&file='.urlencode($file))."\">x"; $download_link = "href('files.xml',$this->GetPageTag(),'action=download&file='.urlencode($file))."\">".$file.""; $size = bytesToHumanReadableUsage(filesize("$upload_path/$file")); $date = date("n/d/Y g:i a",filemtime("$upload_path/$file")); print " "; } } closedir($dir); // print n/a if no files currently exist if (!$num) print ""; else print ""; // form $result = "href()."\" method=\"post\" enctype=\"multipart/form-data\">\n"; if (!$this->config["rewrite_mode"]) $result .= "MiniHref()."\">\n"; echo $result; // echo $this->FormClose(); // close disp table print "
  Attachment Size Date Added
   {$delete_link}    $download_link   $size   $date
    
 
  $result add new attachment: ".$this->FormClose()."
"; } ?> %% ===First version=== ==actions/files.php== %%(php) '') { // link to download a file if ($text == '') $text = $download; echo "href('files.xml',$this->GetPageTag(),'action=download&file='.urlencode($download))."\">".$text.""; } elseif ($this->page AND $this->HasAccess('write') AND ($this->method <> 'print.xml') AND ($this->method <> 'edit')) { // upload path if ($this->config['upload_path'] == '') $this->config['upload_path'] = 'files'; $upload_path = $this->config['upload_path'].'/'.$this->GetPageTag(); if (! is_dir($upload_path)) mkdir_r($upload_path); // upload action $uploaded = $_FILES['file']; if ($_REQUEST['action'] == 'upload' AND $uploaded['size'] > 0) copy ($uploaded['tmp_name'], $upload_path.'/'.$uploaded['name']); // form $result = "href()."\" method=\"post\" enctype=\"multipart/form-data\">\n"; if (!$this->config["rewrite_mode"]) $result .= "MiniHref()."\">\n"; echo $result; ?> FormClose(); // uploaded files $dir = opendir($upload_path); while ($file = readdir($dir)) { if ($file != '.' && $file != '..') { $delete_link = "href('files.xml',$this->GetPageTag(),'action=delete&file='.urlencode($file))."\">x"; $download_link = "href('files.xml',$this->GetPageTag(),'action=download&file='.urlencode($file))."\">".$file.""; print "[ {$delete_link} ] "; if ($file == $uploaded['name']) print "{$download_link}\n"; else print $download_link; print '
'; } } closedir($dir); } ?> %% ==handlers/page/files.xml.php== %%(php) config['mime_types']; if ($mt_f == '') $mt_f='mime.types'; /* build an array keyed on the file ext */ if (is_readable($mt_f)) { $mime_types=array(); /* open our mime.types file for reading */ $mt_fd=fopen($mt_f,"r"); while (!feof($mt_fd)) { /* pull a line off the file */ $mt_buf=trim(fgets($mt_fd,1024)); /* discard if the line was blank or started with a comment */ if (strlen($mt_buf) > 0) if (substr($mt_buf,0,1) != "#") { /* make temp array of the mime.types line we just read */ $mt_tmp=preg_split("/[\s]+/", $mt_buf, -1, PREG_SPLIT_NO_EMPTY); $mt_num=count($mt_tmp); /* if $mt_num = 1 then we got no file extensions for the type */ if ($mt_num > 1) { for ($i=1;$i<$mt_num;$i++) { /* if we find a comment mid-line, stop processing */ if (strstr($mt_tmp[$i],"#")) { break; /* otherwise stick the type in an array keyed by extension */ } else { $mime_types[$mt_tmp[$i]]=$mt_tmp[0]; } } /* zero the temporary array */ unset($mt_tmp); } } } /* close the mime.types file we were reading */ fclose($mt_fd); } else { echo "ERROR: unreadable file " . $mt_f . "\n"; } // upload path if ($this->config['upload_path'] == '') $this->config['upload_path'] = 'files'; $upload_path = $this->config['upload_path'].'/'.$this->GetPageTag(); if (! is_dir($upload_path)) mkdir_r($upload_path); // do the action switch ($_REQUEST['action']) { case 'download': $_REQUEST['file'] = urldecode($_REQUEST['file']); if ($this->HasAccess('read')) { $path = "{$upload_path}/{$_REQUEST['file']}"; $filename = basename($path); header('MIME-Version: 1.0'); $afn = split("\.",$filename); $ext = strtolower($afn[count($afn)-1]); $mime_type = $mime_types[$ext]; if ($mime_type == '') $mime_type = 'application/octet-stream'; header("Content-Type: {$mime_type}; name=\"{$filename}\""); header('Content-Length: '. filesize($path)); header("Content-Disposition: filename=\"{$filename}\""); $fp=fopen($path,'r'); print fread($fp,filesize($path)); fclose($fp); exit(); } case 'delete': if ($this->HasAccess('write')) { @unlink("{$upload_path}/{$_REQUEST['file']}"); print $this->redirect($this->href()); } } ?> %% Nice.. but shouldn't delete at least check some permissions? ''(answer: I am checking write permission...)'' -- ArnarBirgisson Why "files.xml"? shouldn't it be "files.php"? ''(answer: .xml actions does not include header and footer)'' (files.xml didn't work, but renaming it to "files.xml.php" helped.) => Kommentar:: files-action only works if you copy files.xml and store it as files.xml.php as well as files.xml ?????????? //Jetzt geht es, aber das ist ziemlich irre, weil :: - files.xml noch einmal als :: - files.xml.php abgespeichert werden muss. ?? es funktioniert .. // KonradTadesse ---- :: FileUp :: By the way, having an upload form on a page seems to break the page preview "Store" and "Re-edit" buttons. ''(fixed hidding the form on editing, thanks for your bug information)'' -- Tero ---- Here is the same bug like in ImageAction: the use of ##$vars##. Replace line 11 to 16 in actions/files.php with this: %%(php) if ($tokens['download'] ) { // link to download a file $text = $this->stripquotes($action_params['download']); if ($tokens['text']) { $text = $this->stripquotes($action_params['text']); } echo "href('files.xml',$this->GetPageTag(),'action=download&file='.urlencode($this->stripquotes($action_params['download'])))."\">".$text.""; } %% For the function ##$this->stripquotes## read ImageAction --SilBaer ---- Fileupload on editing pages see FileUpload --SilBaer Fix: Added closing CurlyBrace in last replacement code. --MarkHissinkMuller ====Code cleanup==== **actions/files.php** %%(php) '; //this realy should be in the config $max_upload_size = 2*1048576; $allowed_extensions = 'gif|jpeg|jpg|jpe|png|doc|xls|csv|ppt|ppz|pps|pot|pdf|asc|txt|zip|gtar|gz|bz2|tar|rar|vpp|mpp|vsd|mm'; /* $date_format = 'n/d/Y g:i a'; (original) */ /* $date_format = 'd-M-Y H:i'; //01-Feb-2005 05:23 */ $date_format = 'Y-m-d H:i'; //2005-02-01 05:23 (easy javascript table sorting) // mkdir_r if (! function_exists('mkdir_r')) { function mkdir_r ($dir) { if (strlen($dir) == 0) return 0; if (is_dir($dir)) return 1; elseif (dirname($dir) == $dir) return 1; return (mkdir_r(dirname($dir)) and mkdir($dir,0755)); } } // bytesToHumanReadableUsage if (! function_exists('bytesToHumanReadableUsage')) { /** * Converts bytes to a human readable string * @param int $bytes Number of bytes * @param int $precision Number of decimal places to include in return string * @param array $names Custom usage strings * @return string formatted string rounded to $precision */ function bytesToHumanReadableUsage($bytes, $precision = 0, $names = '') { if (!is_numeric($bytes) || $bytes < 0) { $bytes = 0; } if (!is_numeric($precision) || $precision < 0) { $precision = 0; } if (!is_array($names)) { /* $names = array(' Bytes',' KB',' MB',' GB',' TB'); //original */ $names = array('B','k','M','G','T','P','E'); } $level = floor(log($bytes)/log(1024)); $suffix = ''; if ($level < count($names)) { $suffix = $names[$level]; } return round($bytes/pow(1024, $level), $precision) . $suffix; } } // error code constants if (! defined('UPLOAD_ERR_OK')){ define('UPLOAD_ERR_OK', 0); } if (! defined('UPLOAD_ERR_INI_SIZE')){ define('UPLOAD_ERR_INI_SIZE', 1); } if (! defined('UPLOAD_ERR_FORM_SIZE')){ define('UPLOAD_ERR_FORM_SIZE', 2); } if (! defined('UPLOAD_ERR_PARTIAL')){ define('UPLOAD_ERR_PARTIAL', 3); } if (! defined('UPLOAD_ERR_NO_FILE')){ define('UPLOAD_ERR_NO_FILE', 4); } if (! defined('UPLOAD_ERR_NO_TMP_DIR')){ define('UPLOAD_ERR_NO_TMP_DIR', 6); } /* input: {{files download="filename.txt" text="important textfile"}} output: important textfile */ /* input: {{files}} output:
Name Last modified Size
filename.txt 2005-02-03 17:57 189k
another file.txt 2005-02-03 17:57 34k
output if you're admin:
Name Last modified Size
[delete] filename.txt 2005-02-03 17:57 189k
[delete] another file.txt 2005-02-03 17:57 34k

add new attachment:

*/ if ($vars['download'] != '') { // link to download a file if ($vars['text'] == '') $text = $vars['download']; $output .= '' . $vars['text'] . ''; // Show files to anyone with read access, we'll check for write access if they try to delete a file. } elseif ($this->page && $this->HasAccess('read') && $this->method != 'print.xml' && $this->method != 'edit') { // upload path if ($this->config['upload_path'] == '') { $this->config['upload_path'] = 'files'; } $upload_path = $this->config['upload_path'].'/'.$this->GetPageTag(); if (! is_dir($upload_path)) { mkdir_r($upload_path); } // upload action if ($_POST['action'] == 'upload') { $status_text = ''; switch ($_FILES['file']['error']) { case UPLOAD_ERR_OK: if ($_FILES['file']['size'] > $max_upload_size) { $status_text = 'Attempted file upload was too big. ' . 'Maximum allowed size is ' . bytesToHumanReadableUsage($max_upload_size) . '.'; unlink($_FILES['file']['tmp_name']); } elseif (preg_match('/.+\.('.$allowed_extensions.')$/i', $_FILES['file']['name'])) { $strippedname = str_replace('\'', '', $_FILES['file']['name']); $strippedname = stripslashes($strippedname); $destfile = $upload_path.'/'.$strippedname; if (!file_exists($destfile)) { if (move_uploaded_file($_FILES['file']['tmp_name'], $destfile)) { $status_text = 'File was successfully uploaded.'; } else { $status_text = 'There was an error uploading your file.'; } } else { $status_text = 'There is already a file named "' . $strippedname . '".'; } } else { $status_text = 'This file\'s extension is unknown.'; unlink($_FILES['file']['tmp_name']); } break; case UPLOAD_ERR_INI_SIZE: case UPLOAD_ERR_FORM_SIZE: $status_text = 'Attempted file upload was too big. ' . 'Maximum allowed size is ' . bytesToHumanReadableUsage($max_upload_size).'.'; break; case UPLOAD_ERR_PARTIAL: $status_text = 'File upload incomplete. Please try again.'; break; case UPLOAD_ERR_NO_FILE: $status_text = 'No file uploaded.'; break; case UPLOAD_ERR_NO_TMP_DIR: $status_text = 'File uploads impossible due to misconfigured server.'; break; } if ($status_text != '') { $output .= '

' . $status_text . '

'; } } // uploaded files $output .= << Name Last modified Size HEREDOC; $dir = opendir($upload_path); $num = 0; while ($file = readdir($dir)) { /* if ( $file != '.' && $file != '..') { */ if ($file{0} != '.') { $num++; $delete_link = ''; /* if ($this->HasAccess('write')) { */ if ($this->IsAdmin()) { $delete_link = '[delete]'; } $download_link = ''.$file.''; $size = bytesToHumanReadableUsage(filesize($upload_path . '/' . $file)); $date = date($date_format, filemtime($upload_path . '/' . $file)); // easy even/odd zebra table // also possible to zebra color every three rows $row_class = 'r'.$num.' r'.($num % 2).'m2 r'.($num % 3).'m3'; $output .= << {$delete_link} {$download_link} {$date} {$size} HEREDOC; } } closedir($dir); // print n/a if no files currently exist if ($num < 1) { $output .= <<no files here HEREDOC; } $output .= << HEREDOC; /* if ($this->HasAccess('write')) { */ if ($this->IsAdmin()) { // form $input_for_rewrite_mode = ''; if (!$this->config['rewrite_mode']){ $input_for_rewrite_mode = ''; } // close disp table $href = $this->Href(); $output .= <<

{$input_for_rewrite_mode} add new attachment:

HEREDOC; } } $output .= ''; $output = $this->ReturnSafeHTML($output); echo $output; ?> %% **handlers/page/files.xml.php** %%(php) config['upload_path'] == '') { $this->config['upload_path'] = 'files'; } $upload_path = $this->config['upload_path'].'/'.$this->GetPageTag(); if (!is_dir($upload_path)) { mkdir_r($upload_path); } $allowed_extensions = 'gif|jpeg|jpg|jpe|png|doc|xls|csv|ppt|ppz|pps|pot|pdf|asc|txt|zip|gtar|gz|bz2|tar|rar|vpp|mpp|vsd|mm'; $mime_types_file = $this->config['mime_types']; $base_name = basename(urldecode($_REQUEST['file'])); $parts = explode('.', $base_name ); $extension = ''; if(count($parts) > 1){ $extension = array_pop($parts); } $first_letter = $base_name{0}; if ($first_letter != '.' && stristr('|'.$allowed_extensions.'|', '|'.$extension.'|')) { $path = $upload_path.'/'.basename(urldecode($_REQUEST['file'])); // do the action switch ($_REQUEST['action']) { case 'download': if ($this->HasAccess('read')) { $filename = basename($path); $mimetype = 'application/x-download'; $mimes = file($mime_types_file); foreach($mimes as $line){ if(preg_match('/^(\w+\/\S+)\b.*\s'.$extension.'\b/i', $line, $matches)){ $mimetype = $matches[1]; break; } } Header('Content-Length: '.filesize($path)); Header('Content-Type: '.$mimetype); Header('Content-Disposition: attachment; filename="'.$filename.'"; ' .'modification-date="'.date('r', filemtime($path)).'";'); Header('Connection: close'); @readfile($path); exit(); } break; case 'delete': // if ($this->HasAccess('write')) { if ($this->IsAdmin()) { @unlink($path); } print $this->redirect($this->Href()); break; } } ?> %% **css** %%(css) .files table{ border: 1px solid #ccc; border-collapse: collapse; } .files thead th, .files thead td { color: #fff; background: #999; } .files td, .files th { padding: 0 3px 0 3px; } .files tr.r0m2 { background: #efe; color: #000; } %% I cleaned up the code and added: ~- mime types when downloading a file ~- security check on file extension ~- more error checking ~- semanticly correct output for easier styling Hope you like it. --CryDust ---- ====yet another actions/files.php==== http://wush.net/trac/wikka/ticket/72 it is not said in the ticket, but of course my version is also fully css skinable. some other changes: ~- parameter **total** which gives a line of total number of files ~- the delete column is shown based on access rights as i state in the ticket also, the upload file limit is not working for me. see the ticket ---- ====styling the file upload form==== people might get frustrated by the browse button and the file input field. this page offers a solution: http://www.quirksmode.org/dom/inputfile.html ~''Not a good solution: it requires JavaScript, and the field itself accepts no (typed) input any more (in a normal file upload field you can type or paste a path - that doesn't work on any of PPK's examples): that beaks basic functionality. The only "frustration" is the lack of styleability of the button - well, too bad, at least it's accessible (provided there's also a label, of course), and that's more important.'' -- JavaWoman ---- =====making uploaded images useful===== to make the wiki a truly useful documentation tool, one needs pictures, uploaded pictures. at the moment pictures uploaded using FilesAction are accessible **only** for download (unless i am missing something). to let a department manage it's own pictures and be able to include them in the page, one needs to access ##$upload_path##. the current ##.htaccess## file makes this impossible. so here is what i did: **.htaccess** %% RewriteEngine on RewriteCond %{REQUEST_FILENAME} -d RewriteRule ^(.*/[^\./]*[^/])$ $1/ RewriteCond %{REQUEST_FILENAME} !robots.txt RewriteCond %{REQUEST_FILENAME} !favicon.ico RewriteCond %{REQUEST_FILENAME} !files/.*$ RewriteRule ^(.*)$ wikka.php?wakka=$1 [QSA,L] %% **in the wikka page:** ##""{{image alt="db model" title="db model" url="files/CostCenters/costcenters.png"}}""## this way i can include the images for viewing in the page. ---- ====detecting php-level disabled uploads==== If you don't control your web server, you might run into the situation where file uploads are disabled in php.ini http://wush.net/trac/wikka/ticket/825 contains this patch: %% 106a107,111 > if (! ((bool) ini_get('file_uploads'))) { > echo "File uploads are disallowed on this server."; > } > else > { 282a288 > } %%