Development of Files action
This is the development page for the {{files}} action.
- For info about the version currently included in Wikka, take a look at Mod015fFilesAction.
- For a documentation of how to use the current version, look at FilesActionInfo.
handlers/page/files.xml.php didn't work for me, so i use FilesActionHillar
- Hillar, can you explain why it didn't work for you, and how you solved that? --JavaWoman
Actual version
The code below offers a more visually improved layout for this handler and also displays the file size and the date of file upload.
- HeavyK (k m r @ h e a v y k . o r g), 3/27/04
actions/files.php
<?php
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));
}
}
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 = 2, $names = '')
{
if (!is_numeric($bytes) || $bytes < 0) {
return false;
}
for ($level = 0; $bytes >= 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 "<a href=\"".$this->href('files.xml',$this->GetPageTag(),'action=download&file='.urlencode($download))."\">".$text."</a>";
} 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 "
<table cellspacing=0 cellpadding=0>
<tr>
<td>
</td>
<td bgcolor=gray valign=bottom align=center>
<font color=white size=-2>
Attachment
</font>
</td>
<td bgcolor=gray valign=bottom align=center>
<font color=white size=-2>
Size
</font>
</td>
<td bgcolor=gray valign=bottom align=center>
<font color=white size=-2>
Date Added
</font>
</td>
</tr>
";
$dir = opendir($upload_path);
while ($file = readdir($dir)) {
if ($file != '.' && $file != '..') {
$num++;
$delete_link = "<a href=\"".$this->href('files.xml',$this->GetPageTag(),'action=delete&file='.urlencode($file))."\">x</a>";
$download_link = "<a href=\"".$this->href('files.xml',$this->GetPageTag(),'action=download&file='.urlencode($file))."\">".$file."</a>";
$size = bytesToHumanReadableUsage(filesize("$upload_path/$file"));
$date = date("n/d/Y g:i a",filemtime("$upload_path/$file"));
print "
<tr>
<td valign=top align=center>
{$delete_link}
</td>
<td valign=top>
$download_link
</td>
<td valign=top>
<font size=-1 color=gray>
$size
</font>
</td>
<td valign=top>
<font size=-1 color=gray>
$date
</font>
</td>
</tr>
";
}
}
closedir($dir);
// print n/a if no files currently exist
if (!$num) print "<tr><td> </td><td colspan=3 align=center><font color=gray size=-1><i> </i></font></td></tr>";
else print "<tr><td> </td></tr>";
// form
$result = "<form action=\"".$this->href()."\" method=\"post\" enctype=\"multipart/form-data\">\n";
if (!$this->config["rewrite_mode"]) $result .= "<input type=\"hidden\" name=\"wakka\" value=\"".$this->MiniHref()."\">\n";
echo $result;
//<input type="hidden" name="action" value="upload"><input type="file" name="file"><input type="submit" value="+">
echo $this->FormClose();
// close disp table
print "
<tr>
<td>
</td>
<td colspan=4 valign=top align=right nowrap>
<i>
$result
<input type=\"hidden\" name=\"action\" value=\"upload\">
<font color=gray size=-2>
add new attachment:
<input type=\"file\" name=\"file\" style=\"padding: 0px; margin: 0px; font-size: 8px; height: 15px\">
<input type=\"Submit\" value=\"+\" style=\"padding: 0px; margin: 0px; font-size: 8px; height: 15px\">
".$this->FormClose()."
</font>
</i>
</td>
</tr>
</table>
";
}
?>
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));
}
}
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 = 2, $names = '')
{
if (!is_numeric($bytes) || $bytes < 0) {
return false;
}
for ($level = 0; $bytes >= 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 "<a href=\"".$this->href('files.xml',$this->GetPageTag(),'action=download&file='.urlencode($download))."\">".$text."</a>";
} 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 "
<table cellspacing=0 cellpadding=0>
<tr>
<td>
</td>
<td bgcolor=gray valign=bottom align=center>
<font color=white size=-2>
Attachment
</font>
</td>
<td bgcolor=gray valign=bottom align=center>
<font color=white size=-2>
Size
</font>
</td>
<td bgcolor=gray valign=bottom align=center>
<font color=white size=-2>
Date Added
</font>
</td>
</tr>
";
$dir = opendir($upload_path);
while ($file = readdir($dir)) {
if ($file != '.' && $file != '..') {
$num++;
$delete_link = "<a href=\"".$this->href('files.xml',$this->GetPageTag(),'action=delete&file='.urlencode($file))."\">x</a>";
$download_link = "<a href=\"".$this->href('files.xml',$this->GetPageTag(),'action=download&file='.urlencode($file))."\">".$file."</a>";
$size = bytesToHumanReadableUsage(filesize("$upload_path/$file"));
$date = date("n/d/Y g:i a",filemtime("$upload_path/$file"));
print "
<tr>
<td valign=top align=center>
{$delete_link}
</td>
<td valign=top>
$download_link
</td>
<td valign=top>
<font size=-1 color=gray>
$size
</font>
</td>
<td valign=top>
<font size=-1 color=gray>
$date
</font>
</td>
</tr>
";
}
}
closedir($dir);
// print n/a if no files currently exist
if (!$num) print "<tr><td> </td><td colspan=3 align=center><font color=gray size=-1><i> </i></font></td></tr>";
else print "<tr><td> </td></tr>";
// form
$result = "<form action=\"".$this->href()."\" method=\"post\" enctype=\"multipart/form-data\">\n";
if (!$this->config["rewrite_mode"]) $result .= "<input type=\"hidden\" name=\"wakka\" value=\"".$this->MiniHref()."\">\n";
echo $result;
//<input type="hidden" name="action" value="upload"><input type="file" name="file"><input type="submit" value="+">
echo $this->FormClose();
// close disp table
print "
<tr>
<td>
</td>
<td colspan=4 valign=top align=right nowrap>
<i>
$result
<input type=\"hidden\" name=\"action\" value=\"upload\">
<font color=gray size=-2>
add new attachment:
<input type=\"file\" name=\"file\" style=\"padding: 0px; margin: 0px; font-size: 8px; height: 15px\">
<input type=\"Submit\" value=\"+\" style=\"padding: 0px; margin: 0px; font-size: 8px; height: 15px\">
".$this->FormClose()."
</font>
</i>
</td>
</tr>
</table>
";
}
?>
First version
actions/files.php
<?php
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));
}
}
if ($download <> '') {
// link to download a file
if ($text == '') $text = $download;
echo "<a href=\"".$this->href('files.xml',$this->GetPageTag(),'action=download&file='.urlencode($download))."\">".$text."</a>";
} 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 = "<form action=\"".$this->href()."\" method=\"post\" enctype=\"multipart/form-data\">\n";
if (!$this->config["rewrite_mode"]) $result .= "<input type=\"hidden\" name=\"wakka\" value=\"".$this->MiniHref()."\">\n";
echo $result;
?>
<input type="hidden" name="action" value="upload"><input type="file" name="file"><input type="submit" value="+">
<?php
echo $this->FormClose();
// uploaded files
$dir = opendir($upload_path);
while ($file = readdir($dir)) {
if ($file != '.' && $file != '..') {
$delete_link = "<a href=\"".$this->href('files.xml',$this->GetPageTag(),'action=delete&file='.urlencode($file))."\">x</a>";
$download_link = "<a href=\"".$this->href('files.xml',$this->GetPageTag(),'action=download&file='.urlencode($file))."\">".$file."</a>";
print "[ {$delete_link} ] ";
if ($file == $uploaded['name'])
print "<em>{$download_link}</em>\n";
else
print $download_link;
print '<br>';
}
}
closedir($dir);
}
?>
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));
}
}
if ($download <> '') {
// link to download a file
if ($text == '') $text = $download;
echo "<a href=\"".$this->href('files.xml',$this->GetPageTag(),'action=download&file='.urlencode($download))."\">".$text."</a>";
} 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 = "<form action=\"".$this->href()."\" method=\"post\" enctype=\"multipart/form-data\">\n";
if (!$this->config["rewrite_mode"]) $result .= "<input type=\"hidden\" name=\"wakka\" value=\"".$this->MiniHref()."\">\n";
echo $result;
?>
<input type="hidden" name="action" value="upload"><input type="file" name="file"><input type="submit" value="+">
<?php
echo $this->FormClose();
// uploaded files
$dir = opendir($upload_path);
while ($file = readdir($dir)) {
if ($file != '.' && $file != '..') {
$delete_link = "<a href=\"".$this->href('files.xml',$this->GetPageTag(),'action=delete&file='.urlencode($file))."\">x</a>";
$download_link = "<a href=\"".$this->href('files.xml',$this->GetPageTag(),'action=download&file='.urlencode($file))."\">".$file."</a>";
print "[ {$delete_link} ] ";
if ($file == $uploaded['name'])
print "<em>{$download_link}</em>\n";
else
print $download_link;
print '<br>';
}
}
closedir($dir);
}
?>
handlers/page/files.xml.php
<?php
/* mime stuff take from Paul Southworth */
$mt_f = $this->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());
}
}
?>
/* mime stuff take from Paul Southworth */
$mt_f = $this->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:
if ($tokens['download'] ) {
// link to download a file
$text = $this->stripquotes($action_params['download']);
if ($tokens['text'])
{
$text = $this->stripquotes($action_params['text']);
}
echo "<a href=\"".$this->href('files.xml',$this->GetPageTag(),'action=download&file='.urlencode($this->stripquotes($action_params['download'])))."\">".$text."</a>";
}
// link to download a file
$text = $this->stripquotes($action_params['download']);
if ($tokens['text'])
{
$text = $this->stripquotes($action_params['text']);
}
echo "<a href=\"".$this->href('files.xml',$this->GetPageTag(),'action=download&file='.urlencode($this->stripquotes($action_params['download'])))."\">".$text."</a>";
}
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
$output = '';
$output .= '<div class="files">';
//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:
<a href="http://example.com/wiki/ExamplePage/files.xml?action=download&file=filename.txt">important textfile</a>
*/
/*
input:
{{files}}
output:
<div class="files"><table>
<thead>
<tr>
<td></td>
<th>Name</th>
<th>Last modified</th>
<th>Size</th>
</tr>
</thead>
<tbody>
<tr class="r1 r1m2 r1m3">
<td></td>
<td><a href="http://example.com/wiki/ExamplePage/files.xml?action=download&file=filename.txt">filename.txt</a></td>
<td>2005-02-03 17:57</td>
<td align="right">189k</td>
</tr>
<tr class="r2 r0m2 r2m3">
<td></td>
<td><a href="http://example.com/wiki/ExamplePage/files.xml?action=download&file=another%20file.txt">another file.txt</a></td>
<td>2005-02-03 17:57</td>
<td align="right">34k</td>
</tr>
</tbody>
</table></div>
output if you're admin:
<div class="files"><table>
<thead>
<tr>
<td></td>
<th>Name</th>
<th>Last modified</th>
<th>Size</th>
</tr>
</thead>
<tbody>
<tr class="r1 r1m2 r1m3">
<td>[<a href="http://example.com/wiki/ExamplePage/files.xml?action=delete&file=filename.txt">delete</a>]</td>
<td><a href="http://example.com/wiki/ExamplePage/files.xml?action=download&file=filename.txt">filename.txt</a></td>
<td>2005-02-03 17:57</td>
<td align="right">189k</td>
</tr>
<tr class="r2 r0m2 r2m3">
<td>[<a href="http://example.com/wiki/ExamplePage/files.xml?action=delete&file=another%20file.txt">delete</a>]</td>
<td><a href="http://example.com/wiki/ExamplePage/files.xml?action=download&file=another%20file.txt">another file.txt</a></td>
<td>2005-02-03 17:57</td>
<td align="right">34k</td>
</tr>
</tbody>
</table>
<form action="http://example.com/wiki/HomePage" method="post" enctype="multipart/form-data">
<p>
<input type="hidden" name="action" value="upload" />
<input type="hidden" name="MAX_FILE_SIZE" value="2097152" />
add new attachment:
<input type="file" name="file" />
<input type="submit" value="Upload" />
</p>
</form></div>
*/
if ($vars['download'] != '') {
// link to download a file
if ($vars['text'] == '') $text = $vars['download'];
$output .= '<a href="'
. $this->Href(
'files.xml',
$this->GetPageTag(),
'action=download&file='.
rawurlencode($vars['download'])
)
. '">'
. $vars['text']
. '</a>';
// 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 .= '<p class="status">' . $status_text . '</p>';
}
}
// uploaded files
$output .= <<<HEREDOC
<table>
<thead>
<tr>
<td></td> <!-- For the delete link. Only needed when user is admin or has write rights. -->
<th>Name</th>
<th>Last modified</th>
<th>Size</th>
</tr>
</thead>
<tbody>
HEREDOC;
$dir = opendir($upload_path);
$num = 0;
while ($file = readdir($dir)) {
/* if ( $file != '.' && $file != '..') { */
if ($file{0} != '.') {
$num++;
$delete_link = '<!-- delete -->';
/* if ($this->HasAccess('write')) { */
if ($this->IsAdmin()) {
$delete_link = '[<a href="'
. $this->Href('files.xml',$this->GetPageTag(),'action=delete&file='.rawurlencode($file))
. '">delete</a>]';
}
$download_link = '<a href="'
. $this->Href('files.xml',$this->GetPageTag(),'action=download&file='.rawurlencode($file))
. '">'.$file.'</a>';
$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 .= <<<HEREDOC
<tr class="{$row_class}">
<td>{$delete_link}</td>
<td>{$download_link}</td>
<td>{$date}</td>
<td align="right">{$size}</td>
</tr>
HEREDOC;
}
}
closedir($dir);
// print n/a if no files currently exist
if ($num < 1) {
$output .= <<<HEREDOC
<tr><td colspan="4">no files here</td></tr>
HEREDOC;
}
$output .= <<<HEREDOC
</tbody>
</table>
HEREDOC;
/* if ($this->HasAccess('write')) { */
if ($this->IsAdmin()) {
// form
$input_for_rewrite_mode = '<!-- rewrite mode disabled -->';
if (!$this->config['rewrite_mode']){
$input_for_rewrite_mode = '<input type="hidden" name="wakka" value="'.$this->MiniHref().'" />';
}
// close disp table
$href = $this->Href();
$output .= <<<HEREDOC
<form action="{$href}" method="post" enctype="multipart/form-data">
<p>
{$input_for_rewrite_mode}
<input type="hidden" name="action" value="upload" />
<input type="hidden" name="MAX_FILE_SIZE" value="{$max_upload_size}" />
add new attachment:
<input type="file" name="file" />
<input type="submit" value="Upload" />
</p>
</form>
HEREDOC;
}
}
$output .= '</div>';
$output = $this->ReturnSafeHTML($output);
echo $output;
?>
$output = '';
$output .= '<div class="files">';
//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:
<a href="http://example.com/wiki/ExamplePage/files.xml?action=download&file=filename.txt">important textfile</a>
*/
/*
input:
{{files}}
output:
<div class="files"><table>
<thead>
<tr>
<td></td>
<th>Name</th>
<th>Last modified</th>
<th>Size</th>
</tr>
</thead>
<tbody>
<tr class="r1 r1m2 r1m3">
<td></td>
<td><a href="http://example.com/wiki/ExamplePage/files.xml?action=download&file=filename.txt">filename.txt</a></td>
<td>2005-02-03 17:57</td>
<td align="right">189k</td>
</tr>
<tr class="r2 r0m2 r2m3">
<td></td>
<td><a href="http://example.com/wiki/ExamplePage/files.xml?action=download&file=another%20file.txt">another file.txt</a></td>
<td>2005-02-03 17:57</td>
<td align="right">34k</td>
</tr>
</tbody>
</table></div>
output if you're admin:
<div class="files"><table>
<thead>
<tr>
<td></td>
<th>Name</th>
<th>Last modified</th>
<th>Size</th>
</tr>
</thead>
<tbody>
<tr class="r1 r1m2 r1m3">
<td>[<a href="http://example.com/wiki/ExamplePage/files.xml?action=delete&file=filename.txt">delete</a>]</td>
<td><a href="http://example.com/wiki/ExamplePage/files.xml?action=download&file=filename.txt">filename.txt</a></td>
<td>2005-02-03 17:57</td>
<td align="right">189k</td>
</tr>
<tr class="r2 r0m2 r2m3">
<td>[<a href="http://example.com/wiki/ExamplePage/files.xml?action=delete&file=another%20file.txt">delete</a>]</td>
<td><a href="http://example.com/wiki/ExamplePage/files.xml?action=download&file=another%20file.txt">another file.txt</a></td>
<td>2005-02-03 17:57</td>
<td align="right">34k</td>
</tr>
</tbody>
</table>
<form action="http://example.com/wiki/HomePage" method="post" enctype="multipart/form-data">
<p>
<input type="hidden" name="action" value="upload" />
<input type="hidden" name="MAX_FILE_SIZE" value="2097152" />
add new attachment:
<input type="file" name="file" />
<input type="submit" value="Upload" />
</p>
</form></div>
*/
if ($vars['download'] != '') {
// link to download a file
if ($vars['text'] == '') $text = $vars['download'];
$output .= '<a href="'
. $this->Href(
'files.xml',
$this->GetPageTag(),
'action=download&file='.
rawurlencode($vars['download'])
)
. '">'
. $vars['text']
. '</a>';
// 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 .= '<p class="status">' . $status_text . '</p>';
}
}
// uploaded files
$output .= <<<HEREDOC
<table>
<thead>
<tr>
<td></td> <!-- For the delete link. Only needed when user is admin or has write rights. -->
<th>Name</th>
<th>Last modified</th>
<th>Size</th>
</tr>
</thead>
<tbody>
HEREDOC;
$dir = opendir($upload_path);
$num = 0;
while ($file = readdir($dir)) {
/* if ( $file != '.' && $file != '..') { */
if ($file{0} != '.') {
$num++;
$delete_link = '<!-- delete -->';
/* if ($this->HasAccess('write')) { */
if ($this->IsAdmin()) {
$delete_link = '[<a href="'
. $this->Href('files.xml',$this->GetPageTag(),'action=delete&file='.rawurlencode($file))
. '">delete</a>]';
}
$download_link = '<a href="'
. $this->Href('files.xml',$this->GetPageTag(),'action=download&file='.rawurlencode($file))
. '">'.$file.'</a>';
$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 .= <<<HEREDOC
<tr class="{$row_class}">
<td>{$delete_link}</td>
<td>{$download_link}</td>
<td>{$date}</td>
<td align="right">{$size}</td>
</tr>
HEREDOC;
}
}
closedir($dir);
// print n/a if no files currently exist
if ($num < 1) {
$output .= <<<HEREDOC
<tr><td colspan="4">no files here</td></tr>
HEREDOC;
}
$output .= <<<HEREDOC
</tbody>
</table>
HEREDOC;
/* if ($this->HasAccess('write')) { */
if ($this->IsAdmin()) {
// form
$input_for_rewrite_mode = '<!-- rewrite mode disabled -->';
if (!$this->config['rewrite_mode']){
$input_for_rewrite_mode = '<input type="hidden" name="wakka" value="'.$this->MiniHref().'" />';
}
// close disp table
$href = $this->Href();
$output .= <<<HEREDOC
<form action="{$href}" method="post" enctype="multipart/form-data">
<p>
{$input_for_rewrite_mode}
<input type="hidden" name="action" value="upload" />
<input type="hidden" name="MAX_FILE_SIZE" value="{$max_upload_size}" />
add new attachment:
<input type="file" name="file" />
<input type="submit" value="Upload" />
</p>
</form>
HEREDOC;
}
}
$output .= '</div>';
$output = $this->ReturnSafeHTML($output);
echo $output;
?>
handlers/page/files.xml.php
<?php
// 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);
}
$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;
}
}
?>
// 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);
}
$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
.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;
}
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
<IfModule mod_rewrite.c> 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] </IfModule>
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.inihttp://wush.net/trac/wikka/ticket/825 contains this patch:
106a107,111 > if (! ((bool) ini_get('file_uploads'))) { > echo "<b>File uploads are disallowed on this server.</b>"; > } > else > { 282a288 > }