Skip to content

Instantly share code, notes, and snippets.

@DvdGiessen
Created July 22, 2018 13:53
Show Gist options
  • Save DvdGiessen/17e04db10619824f181688999dc87532 to your computer and use it in GitHub Desktop.
Save DvdGiessen/17e04db10619824f181688999dc87532 to your computer and use it in GitHub Desktop.
Simple PHP file browser
<?php
// By Black / DvdGiessen
// http://www.dvdgiessen.nl/
// Layout based on Mozilla Firefox's file:// display
// Wheter to rewrite directory URL's to the form ?dir=PATH
// Disable if you have mod_rewrite/htaccess configured to rewrite directories to this script
$rewrite_directory_urls = FALSE;
// Skip hidden files?
$list_hiddenfiles = FALSE;
// Sort directories as one group.
$sort_directories_together = TRUE;
// Maximum size of files served through this script, in bytes.
// Set to 0 to disable file serving, set to -1 to disable the size limit.
$max_file_size = 5 * 1024 * 1024; // 5 MiB
// Set a base directory for the file listings. Will force serving files through the script.
// Use NULL to use the current directory (default).
$base_dir = NULL;
// Translations of all texts
$translations = array(
'indexof' => 'Index van',
'up' => 'Naar map op hoger niveau gaan',
'showhidden' => 'Verborgen objecten tonen',
'name' => 'Naam',
'size' => 'Grootte',
'lastchanged' => 'Laatst gewijzigd',
'file' => 'Bestand',
'errors' => array(
'unknown' => 'Er is een onbekende fout opgetreden.',
'notfound' => 'De gevraagde locatie kon niet worden gevonden.',
'forbidden' => 'Toegang geweigerd.',
),
);
// That's all there is to configure!
// From this point below the script will figure everything out by itself.
// Basic PHP settings
error_reporting(0);
date_default_timezone_set('UTC');
// List of available icons
$icons = array(
'disk' => 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAjFJREFUeNqsU8uOElEQPffR3XQ3ONASdBJCSBxHos5+3Bg3rvkCv8PElS78gPkO/ATjQoUdO2ftrJiRh6aneTb9sOpC4weMN6lcuFV16pxDIfI8x12OYIDhcPiu2Wx+/HF5CW1Z6Jyegt/TNEWSJIjjGFEUIQxDrFYrWFSzXC4/dLvd95pRKpXKy+pRFZ7nwaWo1+sGnQG2260BKJfLKJVKGI1GEEJw7ateryd0v993W63WEwjgxfn5obGYzgCbzcaEbdsIggDj8Riu6z6iUk9SYZMSx8W0LMsM/SKK75xnJlIq80anQXdbEp0OhcPJ0eiaJnGRMEyyPDsAKKUM9clkYoDo3SZJzzSdp0VSKYmfV1co+z580kw5KDIM8RbRfEnUf1HzxtQyMAGcaGruTKczMzEIaqhKifV6jd+zGQQB5llunF/M52BizC2K5sYPYvZcu653tjOM9O93wnYc08gmkgg4VAxixfqFUJT36AYBZGd6PJkFCZnnlBxMp38gqIgLpZB0y4Nph18lyWh5FFbrOSxbl3V4G+VB7T4ajYYxTyuLtO+CvWGgJE1Mc7JNsJEhvgw/QV4fo/24nbEsX2u1d5sVyn8sJO0ZAQiIYnFh+xrfLz/j29cBS/O14zg3i8XigW3ZkErDtmKoeM+AJGRMnXeEPGKf0nCD1ydvkDzU9Jbc6OpR7WIw6L8lQ+4pQ1/lPF0RGM9Ns91Wmptk0GfB4EJkt77vXYj/8m+8y/krwABHbz2H9V68DQAAAABJRU5ErkJggg==',
'up' => 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmlJREFUeNpsU0toU0EUPfPysx/tTxuDH9SCWhUDooIbd7oRUUTMouqi2iIoCO6lceHWhegy4EJFinWjrlQUpVm0IIoFpVDEIthm0dpikpf3ZuZ6Z94nrXhhMjM3c8895977BBHB2PznK8WPtDgyWH5q77cPH8PpdXuhpQT4ifR9u5sfJb1bmw6VivahATDrxcRZ2njfoaMv+2j7mLDn93MPiNRMvGbL18L9IpF8h9/TN+EYkMffSiOXJ5+hkD+PdqcLpICWHOHc2CC+LEyA/K+cKQMnlQHJX8wqYG3MAJy88Wa4OLDvEqAEOpJd0LxHIMdHBziowSwVlF8D6QaicK01krw/JynwcKoEwZczewroTvZirlKJs5CqQ5CG8pb57FnJUA0LYCXMX5fibd+p8LWDDemcPZbzQyjvH+Ki1TlIciElA7ghwLKV4kRZstt2sANWRjYTAGzuP2hXZFpJ/GsxgGJ0ox1aoFWsDXyyxqCs26+ydmagFN/rRjymJ1898bzGzmQE0HCZpmk5A0RFIv8Pn0WYPsiu6t/Rsj6PauVTwffTSzGAGZhUG2F06hEc9ibS7OPMNp6ErYFlKavo7MkhmTqCxZ/jwzGA9Hx82H2BZSw1NTN9Gx8ycHkajU/7M+jInsDC7DiaEmo1bNl1AMr9ASFgqVu9MCTIzoGUimXVAnnaN0PdBBDCCYbEtMk6wkpQwIG0sn0PQIUF4GsTwLSIFKNqF6DVrQq+IWVrQDxAYQC/1SsYOI4pOxKZrfifiUSbDUisif7XlpGIPufXd/uvdvZm760M0no1FZcnrzUdjw7au3vu/BVgAFLXeuTxhTXVAAAAAElFTkSuQmCC',
'directory' => 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAd5JREFUeNqMU79rFUEQ/vbuodFEEkzAImBpkUabFP4ldpaJhZXYm/RiZWsv/hkWFglBUyTIgyAIIfgIRjHv3r39MePM7N3LcbxAFvZ2b2bn22/mm3XMjF+HL3YW7q28YSIw8mBKoBihhhgCsoORot9d3/ywg3YowMXwNde/PzGnk2vn6PitrT+/PGeNaecg4+qNY3D43vy16A5wDDd4Aqg/ngmrjl/GoN0U5V1QquHQG3q+TPDVhVwyBffcmQGJmSVfyZk7R3SngI4JKfwDJ2+05zIg8gbiereTZRHhJ5KCMOwDFLjhoBTn2g0ghagfKeIYJDPFyibJVBtTREwq60SpYvh5++PpwatHsxSm9QRLSQpEVSd7/TYJUb49TX7gztpjjEffnoVw66+Ytovs14Yp7HaKmUXeX9rKUoMoLNW3srqI5fWn8JejrVkK0QcrkFLOgS39yoKUQe292WJ1guUHG8K2o8K00oO1BTvXoW4yasclUTgZYJY9aFNfAThX5CZRmczAV52oAPoupHhWRIUUAOoyUIlYVaAa/VbLbyiZUiyFbjQFNwiZQSGl4IDy9sO5Wrty0QLKhdZPxmgGcDo8ejn+c/6eiK9poz15Kw7Dr/vN/z6W7q++091/AQYA5mZ8GYJ9K0AAAAAASUVORK5CYII=',
'unknown' => 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAvVBMVEUAAAD////c3Nzh5OH09PS5ubm/v7+5ubl5eXmGhoaQkJCdnZ2srKytra2xsbGzs7O1tbW4uLi6urq8vLy9vb2/v7/AwMDFxcXKysrg39/m5eXy8PDz8fHz8vHz8vL08vL08/P18/P19PP19PT29PT29fT29fX29vb39fX39vX39vb49vb49/b49/f4+Pf5+Pf5+Pj5+fj6+fj6+fn6+vn7+vn7+vr7+/r8+/r8+/v8/Pv9/Pv9/Pz9/fz+/v3xv/NuAAAACHRSTlMAAF9pefn8/Qk6euYAAACbSURBVBiVTchXEoIwAAVAYqcT7BWxgyCIKBIl9z+WeZQZ93MlQiaNXkeSiCQiZ0XBOeOzfruMcVYNn567LcQorcem1EYMk/T1LmcxL8OO6hFFETSI7g8x3yYsv5wMYyHMk4d5ZuxTmAjjIOYWY5iB0N390QtCTK4jNAdzDeMkzTSEusFcfIyKUFZrx91hokRByEsx22pkxOAf+QGB3h9M82xjJwAAAABJRU5ErkJggg==',
);
// Return the requested icon upon request
if(isset($_GET['icon'])) {
$data = isset($icons[$_GET['icon']]) ? $icons[$_GET['icon']] : $icons['unknown'];
$data = base64_decode($data);
header('Cache-Control: max-age=' . 30 * 24 * 60 * 60 . ', public');
header('Content-Type: image/png');
header('Content-Length: ' . strlen($data));
die($data);
}
// Regex for normalizing directory separators in URL's
$url_directoryseparator_regex = '#[\\\\/]#';
// Determine base URL and directory
$is_ssl = isset($_SERVER['HTTPS']) && (strtolower($_SERVER['HTTPS']) == 'on' || $_SERVER['HTTPS'] == '1');
$base = ($is_ssl ? 'https://' : 'http://') . $_SERVER['SERVER_NAME'];
if(isset($_SERVER['SERVER_PORT']) && ((!$is_ssl && (int) $_SERVER['SERVER_PORT'] != 80) || ($is_ssl && (int) $_SERVER['SERVER_PORT'] != 443))) {
$base .= ':' . (int) $_SERVER['SERVER_PORT'];
}
if(is_null($base_dir)) {
$realbase = @realpath(dirname(__FILE__));
$base_dir = '/';
if(isset($_SERVER['DOCUMENT_ROOT'])) {
$base .= ($base_dir = substr($realbase, strlen(@realpath($_SERVER['DOCUMENT_ROOT']))));
}
$base_dir_autodetected = TRUE;
} else {
$realbase = @realpath($base_dir);
if(isset($_SERVER['DOCUMENT_ROOT'])) {
$base .= preg_replace($url_directoryseparator_regex, '/', ($base_dir = substr(@realpath(dirname(__FILE__)), strlen(@realpath($_SERVER['DOCUMENT_ROOT'])))));
}
$base_dir_autodetected = FALSE;
}
// Helper function for detection of file sizes
function filesize64($file) {
if(!file_exists($file) || !is_readable($file)) return FALSE;
static $iswin;
if(!isset($iswin)) {
$iswin = (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN');
}
static $exec_works;
if(!isset($exec_works)) {
$exec_works = (function_exists('exec') && !ini_get('safe_mode') && @exec('echo EXEC') == 'EXEC');
}
// Try a shell command
if($exec_works) {
$cmd = ($iswin) ? ('for %F in ("' . $file . '") do @echo %~zF') : ('stat -c%s "' . $file . '"');
@exec($cmd, $output);
if(is_array($output) && ctype_digit($size = trim(implode("\n", $output)))) {
return $size;
}
}
// Try the Windows COM interface
if($iswin) {
$fsobj = NULL;
if(class_exists('DOTNET')) {
try {
$fsobj = new DOTNET('mscorlib', 'Scripting.FileSystemObject');
} catch (Exception $e) {}
} elseif(class_exists('COM')) {
try {
$fsobj = new COM('Scripting.FileSystemObject');
} catch (Exception $e) {}
}
if($fsobj) {
try {
$f = $fsobj->GetFile(@realpath($file));
$size = $f->Size;
if(ctype_digit($size)) {
return $size;
}
} catch(Exception $e) {
$size = NULL;
}
}
}
// if all else fails
return filesize($file);
}
// Requested directory, relative to the base URL
$dir = isset($_GET['dir']) ? $_GET['dir'] : substr('/' . trim(preg_replace('/\?.*$/', '', $_SERVER['REQUEST_URI']), '\\/'), strlen($base_dir));
$dir = '/' . preg_replace($url_directoryseparator_regex, '/', trim(urldecode($dir), '\\/'));
$dirnamedir = preg_replace($url_directoryseparator_regex, '/', @dirname($dir));
$realdir = @realpath($realbase . DIRECTORY_SEPARATOR . preg_replace($url_directoryseparator_regex, DIRECTORY_SEPARATOR, $dir));
if(!$realdir || !@file_exists($realdir) || strpos($dir, '..') !== FALSE) {
// Indicate failure, redirect to parent directory
header($_SERVER['SERVER_PROTOCOL'] . ' 404 Not Found');
header('Location: ' . $base . $dirnamedir . '?error=notfound');
exit;
} elseif(!@is_dir($realdir)) {
// Appearantly, we are requested to serve a file!
if(($list_hiddenfiles || substr(basename($realdir), 0, 1) != '.') && @is_file($realdir) && @is_readable($realdir) && filesize64($realdir) >= 0 && ($max_file_size < 0 || filesize64($realdir) <= $max_file_size)) {
// Try to disable any time limits we may have
@set_time_limit(0);
// Determine the MIME type
$mimetype = 'application/octet-stream';
if(function_exists('finfo_open')) {
$finfo = @finfo_open(FILEINFO_MIME_TYPE);
$mimetype = @finfo_file($finfo, $realdir);
}
// Remove all headers
header_remove();
// Check for partial requests
$file_size = filesize64($realdir);
$range = FALSE;
if(isset($_SERVER['HTTP_RANGE'])) {
$range = $_SERVER['HTTP_RANGE'];
} elseif(function_exists('apache_request_headers')) {
$headers = @apache_request_headers();
if(isset($headers['Range'])) {
$range = $headers['Range'];
} else {
foreach($headers as $header => $value) {
if(strtolower($header) == 'range') {
$range = $value;
break;
}
}
}
}
if($range && preg_match('/bytes=(\d*)-(\d*)/', $range, $matches)) {
if($matches[1] == '' && $matches[2] != '') {
$partial_start = $file_size - intval($matches[2]);
$partial_end = $file_size;
} elseif($matches[1] != '' && $matches[2] == '') {
$partial_start = intval($matches[1]);
$partial_end = $file_size;
} elseif($matches[1] != '' && $matches[2] != '') {
$partial_start = intval($matches[1]);
$partial_end = intval($matches[2]);
} else {
header($_SERVER['SERVER_PROTOCOL'] . ' 400 Invalid Request');
exit;
}
}
if($partial_end > $file_size || (!$partial_start && (!$partial_end || $partial_end == ($file_size)))) {
$partial_start = 0;
$partial_end = $file_size;
header($_SERVER['SERVER_PROTOCOL'] . ' 200 OK');
header('Content-Length: ' . $file_size);
} else {
header('HTTP/1.1 206 Partial Content');
header('Content-Range: bytes ' . $partial_start . '-' . $partial_end . '/' . $file_size);
}
// Notice: if the browser doesn't display the file size, try disabling Apache's mod_deflate using the following .htaccess directive:
// SetEnvIfNoCase Request_URI ^/index\.php no-gzip=1 dont-vary=1
// Set headers
header('Content-Type: ' . $mimetype);
header('Content-Disposition: inline; filename="' . @basename($realdir) . '"');
header('Accept-Ranges: bytes');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', filemtime($realdir)) . ' GMT');
header('Cache-Control: must-revalidate');
header('Pragma: public');
// Send the file
if(!$file_handle = @fopen($realdir, 'r')) {
header($_SERVER['SERVER_PROTOCOL'] . ' 500 Internal Server Error');
header('Location: ' . $base . $dirnamedir . '?error=unknown');
exit;
}
@fseek($file_handle, $partial_start);
$length = $partial_end - $partial_start;
while($length) {
// Read in chunks of 8192 bytes to reduce memory usage
$read = ($length > 8192) ? 8192 : $length;
$length -= $read;
echo @fread($file_handle, $read);
}
@fclose($file_handle);
// Close connection
exit;
} else {
// Indicate failure, redirect to parent directory
header($_SERVER['SERVER_PROTOCOL'] . ' 403 Forbidden');
header('Location: ' . $base . $dirnamedir . '?error=forbidden');
exit;
}
}
if(!defined('PHP_INT_MIN')) define('PHP_INT_MIN', -1 * (PHP_INT_MAX - 1));
// Scan dir
if(!($dir_contents = @scandir($realdir))) {
// Indicate failure, redirect to parent directory
header($_SERVER['SERVER_PROTOCOL'] . ' 500 Internal Server Error');
header('Location: ' . $base . $dirnamedir . '?error=unknown');
exit;
}
usort($dir_contents, function($a, $b) use($sort_directories_together, $realdir) {
$aCmp = strtolower($a);
$bCmp = strtolower($b);
if($sort_directories_together) {
$aCmp = (@is_dir($realdir . DIRECTORY_SEPARATOR . $a) ? '0' : '1') . $aCmp;
$bCmp = (@is_dir($realdir . DIRECTORY_SEPARATOR . $b) ? '0' : '1') . $bCmp;
}
return strcoll($aCmp, $bCmp);
});
// Show hidden cookie?
$showHidden = FALSE;
if(isset($_COOKIE['showHidden']) && $_COOKIE['showHidden'] == 'true') $showHidden = TRUE;
// Set content type
header('Content-Type: text/html; charset=UTF-8');
?><!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title><?php echo htmlspecialchars($translations['indexof'] . ' ' . $base . $dir); ?></title>
<link type="image/png" rel="icon" href="<?php echo $base; ?>/?icon=disk" />
<style type="text/css">
/* <![CDATA[ */
html {
background-color: #F0F0F0;
color: #000;
font-family: 'Segoe UI', Tahoma, Arial, sans-serif;
font-size: 12px;
padding-left: 32px;
padding-right: 32px;
}
body {
border: 1px solid #A0A0A0;
border-radius: 10px;
padding: 48px;
min-width: 360px;
max-width: 780px;
margin: 64px auto;
background-color: #FFFFFF;
}
#errorMessage {
display: block;
position: absolute;
top: 0;
left: 0;
width: 100%;
min-height: 40px;
background-color: rgba(255, 60, 60, 1);
color: white;
border-bottom: 1px solid red;
text-align: center;
line-height: 40px;
font-weight: bold;
-webkit-transition: all 0.5s ease;
-moz-transition: all 0.5s ease;
-ms-transition: all 0.5s ease;
-o-transition: all 0.5s ease;
transition: all 0.5s ease;
}
h1 {
font-size: 19.2px;
margin: 0 0 11.5px;
border-bottom: 1px solid #E3E3E3;
font-weight: normal;
}
th {
text-align: left;
white-space: nowrap;
}
td {
vertical-align: middle;
}
/* Name */
th:first-child {
padding-right: 32px;
}
/* Size */
th:first-child + th {
padding-right: 16px;
}
td:first-child + td {
text-align: end;
padding-right: 16px;
white-space: nowrap;
}
/* Date */
th:last-child {
padding-left: 16px;
}
td:first-child + td + td {
padding-left: 16px;
padding-right: 8px;
white-space: nowrap;
}
/* Time */
td:last-child {
padding-left: 8px;
white-space: nowrap;
}
img {
border: 0;
}
a {
text-decoration: none;
}
a.dir,
a.file {
margin-left: 20px;
}
a.dir::before,
.file > img {
margin-right: 4px;
margin-left: -20px;
max-width: 16px;
max-height: 16px;
vertical-align: middle;
}
a:link { color: blue; }
a:visited { color: blue; }
a:hover { color: blue; }
a:active { color: blue; }
a:hover {
text-decoration: underline;
}
p {
font-size: 110%;
}
#UI_goUp {
margin-top: 0;
float: left;
}
#UI_showHidden {
margin-top: 0;
float: right;
}
table {
clear: both;
width: 90%;
margin: 0 auto;
}
thead {
font-size: 130%;
}
/* last modified */
th:last-child {
text-align: center;
}
th:hover > a {
text-decoration: underline;
}
tbody > tr:hover {
outline: 1px solid ThreeDLightShadow;
-moz-outline-radius: .3em;
}
/* let 'Size' and 'Last Modified' take only as much space as they need and 'Name' all the rest */
td:not(:first-child) {
width: 0;
}
.up {
padding: 0 .5em;
margin-left: 20px;
}
.up::before {
margin-right: 4px;
margin-left: -20px;
vertical-align: middle;
content: url(<?php echo htmlspecialchars($base); ?>/?icon=up);
}
.dir::before {
content: url(<?php echo htmlspecialchars($base); ?>/?icon=directory);
}
@keyframes spin {
to { transform: rotate(1turn); }
}
.progress {
position: relative;
display: inline-block;
width: 5em;
height: 5em;
margin: 0 .5em;
font-size: 12px;
text-indent: 999em;
overflow: hidden;
animation: spin 1s infinite steps(8);
}
.small.progress {
font-size: 6px;
}
.large.progress {
font-size: 24px;
}
.progress:before,
.progress:after,
.progress > div:before,
.progress > div:after {
content: '';
position: absolute;
top: 0;
left: 2.25em; /* (container width - part width)/2 */
width: .5em;
height: 1.5em;
border-radius: .2em;
background: #eee;
box-shadow: 0 3.5em #eee; /* container height - part height */
transform-origin: 50% 2.5em; /* container height / 2 */
}
.progress:before {
background: #555;
}
.progress:after {
transform: rotate(-45deg);
background: #777;
}
.progress > div:before {
transform: rotate(-90deg);
background: #999;
}
.progress > div:after {
transform: rotate(-135deg);
background: #bbb;
}
/* ]]> */
</style>
</head>
<body>
<?php if(isset($_GET['error'])) echo '<span id="errorMessage">', htmlspecialchars(isset($translations['errors'][$_GET['error']]) ? $translations['errors'][$_GET['error']] : $translations['errors']['unknown']), '</span>'; ?>
<h1><?php echo $translations['indexof'], ' ', $base, $dir, substr($dir, -1) != '/' ? '/' : ''; ?></h1>
<?php if($realdir != $realbase) {
if($rewrite_directory_urls) {
$url = $base . '?dir=' . urlencode($dirnamedir);
} else {
$url = $base . $dirnamedir;
}
if(substr($url, -1) != '/') $url .= '/';
?>
<p><a class="up" href="<?php echo htmlspecialchars($url); ?>"><?php echo htmlspecialchars($translations['up']); ?></a></p>
<?php } if($list_hiddenfiles) { ?>
<p><input type="checkbox" id="showHidden" <?php if($showHidden) echo 'checked="checked" '; ?>/><label for="showHidden"><?php echo htmlspecialchars($translations['showhidden']); ?></label></p>
<?php } ?>
<table id="fileTable">
<thead>
<tr>
<th data-sorttype="string"><?php echo htmlspecialchars($translations['name']); ?></th>
<th data-sorttype="integer"><?php echo htmlspecialchars($translations['size']); ?></th>
<th data-sorttype="integer" colspan="2"><?php echo htmlspecialchars($translations['lastchanged']); ?></th>
</tr>
</thead>
<tbody id="fileList">
<?php
// Suffixes for file sizes. Defined outside the loop for performance and clean code
$file_size_suffixes = array(' B', ' KiB', ' MiB', ' GiB', ' TiB');
$dir_count = array();
foreach($dir_contents as $file) if(@is_dir(@realpath($realdir . DIRECTORY_SEPARATOR . $file))) {
$dir_count[$file] = @count(@scandir(@realpath($realdir . DIRECTORY_SEPARATOR . $file), SCANDIR_SORT_NONE));
}
// Loop over dir contents
foreach($dir_contents as $file) {
// Skip special entries
if($file == '.' || $file == '..') continue;
// Skip hidden files if these are disabled
if(!$list_hiddenfiles && $file[0] == '.') continue;
// Determine real file (if it cannot be determined, skip it)
if(!($realfile = @realpath($realdir . DIRECTORY_SEPARATOR . $file))) continue;
// Special case: skip this file itself
if($realfile === @realpath(__FILE__)) continue;
// Output table row start
echo '<tr', ($file[0] == '.' ? (' class="hiddenItem"' . ($showHidden ? '' : ' style="display: none;"')) : ''), '><td data-sortdata="', ($sort_directories_together && @is_dir($realfile) ? '0' : '1'), htmlspecialchars($file), '">';
// Output link
if($rewrite_directory_urls && (!$base_dir_autodetected || @is_dir($realfile))) {
$url = $base . '?dir=' . urlencode(($dir == '/' ? '/' : ($dir . '/')) . $file);
} else {
$url = $base . ($dir == '/' ? '/' : ($dir . '/')) . $file;
}
if(is_dir($realfile)) $url .= '/';
echo '<a href="', htmlspecialchars($url), '" class="', (@is_dir($realfile) ? 'dir' : 'file'), '">';
// Output icon
if(!@is_dir($realfile)) {
$file_extension = preg_match('/.*(\.[^\.]+)$/', $file, $matches) ? $matches[1] : 'unknown';
echo '<img src="', htmlspecialchars($base), '/?icon=', (isset($icons[$file_extension]) ? htmlspecialchars($file_extension) : 'unknown'), '" data-extension="', htmlspecialchars($file_extension), '" width="16" height="16" alt="', htmlspecialchars($translations['file']), ':" />';
}
// Output name
echo htmlspecialchars($file), '</a></td>';
// Output size
$file_size = 0;
if(@is_dir($realfile)) {
$file_size = -1 * (max($dir_count) + 1) + @count(@scandir($realfile, SCANDIR_SORT_NONE));
} else {
$file_size = filesize64($realfile);
if($file_size < 0) $file_size = PHP_INT_MAX;
}
$file_size_base = $file_size == 0 ? 0 : log($file_size) / log(1024);
echo '<td data-sortdata="', $file_size, '">';
if($file_size >= 0) switch($file_size) {
case 0:
echo '0 B';
break;
case PHP_INT_MAX:
echo '> ';
default:
echo round(pow(1024, $file_size_base - floor($file_size_base)), floor($file_size_base) > 2 ? 1 : 0) . $file_size_suffixes[floor($file_size_base)];
}
echo '</td>';
// Output modification time
$file_mtime = (int) @filemtime($realfile);
echo '<td data-sortdata="', $file_mtime, '">', date('Y-m-d', $file_mtime), '</td><td>', date('H:i:s', $file_mtime), '</td>';
// End of row
echo '</tr>';
}
?>
</tbody>
</table>
<script type="application/javascript">
/* <![CDATA[ */
// Older versions of Firefox (<43) support type-based file icons using a moz-icon:// resource URL
if(navigator.userAgent.toLowerCase().indexOf('firefox') > -1) {
var i, elems = document.getElementsByTagName('img');
for(i in elems) if(elems[i] instanceof HTMLImageElement) {
var el = elems[i];
var img = new Image();
img.addEventListener('load', function(ev) {
el.setAttribute('src', img.src);
});
img.src = 'moz-icon://' + el.getAttribute('data-extension') + '?size=16';
}
}
<?php if(isset($_GET['error'])) { ?>
// Remove the error message after a short timeout
if(typeof window.history != 'undefined' && typeof history.replaceState != 'undefined') {
setTimeout(function() {
document.getElementById('errorMessage').style.opacity = 0;
history.replaceState(null, null, location.href.replace(/[\?&]error(=\w+)?/gi, ''));
}, 4000);
}
<?php } ?>
// Various functionality directly tied to the file table
var table = document.getElementById('fileTable');
if(table.rows.length > 1) {
<?php if($list_hiddenfiles) { ?>
// Showing/hiding hidden files
var hiddenItems = document.getElementsByClassName('hiddenItem');
document.getElementById('showHidden').addEventListener('change', function(e) {
for(var i = 0; i < hiddenItems.length; i++) hiddenItems.item(i).style.display = this.checked ? 'table-row' : 'none';
document.cookie = 'showHidden=' + this.checked;
});
<?php } ?>
// Sorting by clicking a table header
var headerOffset = 0;
var currentSort = 0;
var reverseSort = true;
var headers = table.tHead.rows.item(0).children;
var rows = Array.prototype.slice.call(table.tBodies.item(0).rows);
var getSortFunction = function(currentHeaderOffset) {
return function(e) {
// Determine sorting order
if(currentSort == currentHeaderOffset) {
reverseSort = !reverseSort;
} else {
reverseSort = true;
}
// Sort by currentHeaderOffset
rows.sort(function(a, b) {
var aSortData = a.children.item(currentHeaderOffset).getAttribute('data-sortdata');
var bSortData = b.children.item(currentHeaderOffset).getAttribute('data-sortdata');
switch(headers.item(currentHeaderOffset).getAttribute('data-sorttype')) {
case 'string':
return bSortData.toLowerCase().localeCompare(aSortData.toLowerCase());
case 'integer':
return parseInt(bSortData) - parseInt(aSortData);
default:
return 0;
}
});
currentSort = currentHeaderOffset;
// Reorder the DOM elements
for(var j = reverseSort ? rows.length - 1 : 0; reverseSort && j >= 0 || !reverseSort && j < rows.length; j += reverseSort ? -1 : 1) {
rows[j].parentNode.appendChild(rows[j]);
}
};
}
for(var i = 0; i < headers.length; i++) {
headers.item(i).addEventListener('click', getSortFunction(headerOffset));
headers.item(i).style.cursor = 'pointer';
headerOffset += (headers[i].hasAttribute('colspan') ? parseInt(headers[i].getAttribute('colspan')) : 1);
}
}
/* ]]> */
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment