Index: modules/filebrowser/filebrowser.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/filebrowser/filebrowser.module,v
retrieving revision 1.7
diff -u -F -r1.7 filebrowser.module
--- modules/filebrowser/filebrowser.module 28 Jun 2006 12:29:58 -0000 1.7
+++ modules/filebrowser/filebrowser.module 28 Aug 2006 21:38:35 -0000
@@ -1,5 +1,5 @@
array(
'#type' => 'textfield',
'#title' => t('Root directory'),
- '#default_value' => variable_get('filebrowser_root', ''),
+ '#default_value' => filebrowser_root(),
'#maxlength' => '100',
'#size' => '70',
- '#description' => t('Root directory used to present the filebrowser interface. Users will not be able to go up from this folder. Only a directory name under the Drupal root is accepted. Example: "public/files".'),
+ '#description' => t('Root directory used to present the filebrowser interface. Users will not be able to go up from this folder. Only a directory name under the Drupal root is accepted. Example: "public/files". No trailing slash. For super-user access to all web files, enter "." (at your own risk) '),
),
'filebrowser_icons' => array(
'#type' => 'textfield',
@@ -85,45 +85,47 @@
*/
function filebrowser_page() {
// Build breadcrumb list for uplevel folders
- $subfolder = preg_replace("!^filebrowser/*!", "", $_GET['q']);
- $parts = explode("/", $subfolder);
- $breadcrumb = array();
- $dirname = t('root');
- if ($subfolder) {
- $dirname = array_pop($parts);
- while (count($parts)) {
- $breadcrumb[] = l($parts[count($parts) -1], filebrowser_proper_path(join("/", $parts)));
- array_pop($parts);
- }
- $breadcrumb[] = l(t('Filebrowser root'), 'filebrowser');
- }
- $breadcrumb[] = l(t('Home'), NULL);
- drupal_set_breadcrumb(array_reverse($breadcrumb));
+ $parts = func_get_args();
+ $subfolder = join('/',$parts);
+
+ // set context for later filebrowser links
+ $filebrowser_base = filebrowser_base($subfolder);
+ filebrowser_proper_path('', $filebrowser_base);
+
+ filebrowser_set_breadcrumbs($parts);
// Set page title
drupal_set_title(t('%dirname directory', array("%dirname" => $dirname)));
+ // Default list of columns to render
+ $cols = array('icon','name','size','age','info');
+
+ if($_REQUEST['edit']['cols']){$cols = $_REQUEST['edit']['cols'];}
+ filebrowser_set_context($filebrowser_base, $cols);
+
+ // LIST FETCHED HERE
// No files to list, return with an empty page
- if (!($files = filebrowser_get_list($subfolder))) {
- drupal_set_message(t('Unable to get files for this directory'));
+ if (!($files = filebrowser_get_list($subfolder, $cols))) {
+ drupal_set_message(t("Unable to get files for this directory '%subfolder'",array('%subfolder'=>$subfolder)));
return theme("filebrowser_page", '');
}
// Prepare headers for display
- $headers = array_merge(
- array(
- array('data' => t("Name"), 'field' => 1),
- array('data' => t("Size"), 'field' => 2),
- array('data' => t("Last modified"), 'field' => 3),
- ), filebrowser_get_fileinfo()
- );
+ $headers = array_map(
+ create_function(
+ '$colname',
+ '$func = "filebrowser_get_cell_".$colname; if(function_exists($func))return $func(""); '
+ ),
+ $cols
+ );
+
// Set sorting criteria eg. array(the 'field' key's associated value, asc/desc)
filebrowser_sort_table(array(tablesort_get_order($headers), tablesort_get_sort($headers)));
// Protect folders from being resorted
$folders = array();
foreach ($files as $rnum => $filei) {
- if ($filei[1]['data'] == '') {
+ if ($filei['size']['data'] == '') {
$folders[] = $filei;
unset($files[$rnum]);
}
@@ -156,7 +158,7 @@
function theme_filebrowser_page(&$files, $header = NULL, $pre = NULL) {
if ($files) {
// CSS can hook on this ID to style table elements differently
- return '
' . $pre . theme("table", $header, $files) . '
';
+ return '' . $pre . theme("table", $header, $files) . '
';
}
else {
return '';
@@ -168,18 +170,25 @@
* specified filebrowser root. File system details (size, last
* modification) is added, plus a metafile is parsed to gather
* more information, if available.
- */
-function filebrowser_get_list($subfolder = '') {
- global $base_path;
+ * @param $subfolder the folder to scan. Must be under and relative to the
+ * system files dir unless $unsafe is set
+ * @param $columns a list of column identifiers to show
+ * @param $unsafe If set, the listing will NOT prepend the file path to the
+ * subfolder. File listings from anywhere in the server become possible!
+ * @return an array of table cells
+ */
+function filebrowser_get_list($subfolder = '', $cols = array('expander','icon','name','size','age','info'), $unsafe=FALSE) {
+ $folder = ($unsafe)?$subfolder:filebrowser_safe_folder($subfolder);
+ if(is_file($folder)){$folder = dirname($folder);}
+ $inroot = ($folder == filebrowser_root() );
- $folder = filebrowser_safe_folder($subfolder);
- $inroot = ($folder == variable_get('filebrowser_root', ''));
-
+ if(!$folder){$folder='.';}
// Signal error in case of bogus directory name
- if (!(file_exists($folder) && is_dir($folder) && ($dir = opendir($folder)))) {
+ if (!(is_dir($folder) && ($dir = opendir($folder)))) {
+ drupal_set_message($folder." was not readable");
return FALSE;
}
-
+
// Collect folders and files separately and check for a metainfo file
$files = $folders = array();
$infofile = FALSE;
@@ -194,6 +203,7 @@
// Grab information file, decide whether it should be shown or not
if (in_array(strtolower($entry), array("descript.ion", "files.bbs"))) {
$infofile = $entry;
+ filebrowser_get_cell_info("$folder/$infofile"); // prefetch the info
if (!variable_get('filebrowser_hide_description_files', 0)) {
$files[] = $entry;
}
@@ -211,61 +221,135 @@
sort($files);
$files = array_merge($folders, $files);
- // Get metainformation about files, and construct table spaceholder
- // for files, which have no metainformation in that file
- if ($infofile) {
- $info = filebrowser_get_fileinfo("$folder/$infofile", $subfolder);
- $count = count(filebrowser_get_fileinfo());
- $emptyinfo = ($count ? array_fill(0, $count, '') : array());
- }
- else {
- $info = $emptyinfo = array();
- }
// Build detailed list of files
$details = array();
foreach ($files as $file) {
- $extrainfo = (isset($info[$file]) ? $info[$file] : $emptyinfo);
+ if ( $file == '.'){continue;}
+ if ($inroot && $file == '..'){continue;}
+ // no up from the top, but it's allowed the rest of the time.
+
// Some real folder or file
- if (!in_array($file, array(".", ".."))) {
- $completepath = "$folder/$file";
- if ($stat = stat($completepath)) {
- $icon = filebrowser_get_icon($completepath);
- $age = time() - $stat['mtime'];
- if (is_dir($completepath)) {
- $link = l("$icon $file", filebrowser_proper_path("$subfolder/$file"), array(), NULL, NULL, FALSE, TRUE);
- $size = '';
- }
- else {
- $link = "$icon $file";
- $size = format_size($stat['size']);
+ $completepath = ($folder && $folder != '.') ? "$folder/$file" : $file;
+ if ($stat = stat($completepath)) {
+ $row = array();
+ // populate the columns
+ foreach($cols as $colname){
+ $func = 'filebrowser_get_cell_'.$colname;
+ if(function_exists($func)){
+ $row[$colname] = $func($completepath);
}
- $details[] = array_merge(
- array(
- array('data' => $link, 'class' => 'filename', 'sv' => $file),
- array('data' => $size, 'sv' => ($size ? $stat['size'] : 0)),
- array('data' => format_interval($age), 'sv' => $age)
- ), $extrainfo);
}
- }
- // The special one-up folder not in the root folder
- elseif ($file == ".." && !$inroot) {
- $icon = filebrowser_get_icon(NULL, 'folder');
- $link = "$icon $file";
- $parts = explode("/", $subfolder);
- array_pop($parts);
- $up = t('up');
- $link = l("$icon $file <$up>", filebrowser_proper_path(join("/", $parts)), array(), NULL, NULL, FALSE, TRUE);
- $details[] = array_merge(array(
- array('data' => $link, 'class' => 'filename', 'sv' => $file),
- array('data' => '', 'sv' => 0),
- array('data' => '', 'sv' => 0)
- ), $extrainfo);
+ $details[] = $row;
}
}
return $details;
}
+
+function filebrowser_build_table($files,$folder,$cols){
+ $inroot = ($folder == filebrowser_root());
+ // Build detailed list of files
+ $details = array();
+ foreach ($files as $file) {
+ if ( $file == '.'){continue;}
+ if ($inroot && $file == '..'){continue;}
+ // the up link is now treated almost normally
+
+ // Some real folder or file
+ $completepath = "$folder/$file";
+ if ($stat = stat($completepath)) {
+ $row = array();
+ // populate the columns
+ foreach($cols as $colname){
+ $func = 'filebrowser_get_cell_'.$colname;
+ $row[$colname] = (function_exists($func)) ? $func($completepath) : '';
+ }
+ $details[] = $row;
+ }
+ }
+ return $details;
+}
+
+
+/**
+ * Callback to render just one cell of data - the name link
+ * A filebrowser_get_cell_hook()
+ */
+function filebrowser_get_cell_name($completepath){
+ if(! $completepath){return array('data' => t("Name"), 'field' => 'name'); }
+ $file = basename($completepath);
+ if (is_dir($completepath)) {
+ // for this link I need to prefix the filebrowser relink, BUT chop the filebrowser directory part
+ $relpath = filebrowser_trim_root($completepath);
+ $href = filebrowser_proper_path($relpath);
+ }
+ else {
+ $href = $completepath;
+ }
+ if ($file == ".." ) { $file = ".. up " ;}
+
+ $link = l($file, $href);
+ return array('data' => $link, 'class' => 'filename', 'sv' => $file);
+}
+
+
+/**
+ * Return the formatted size of this file.
+ * A filebrowser_get_cell_hook()
+ */
+function filebrowser_get_cell_size($completepath){
+ if(! $completepath){return array('data' => t("Size"), 'field' => 'size'); }
+ if(is_dir($completepath)){return array();}
+ $size = filesize($completepath);
+ return array('data' => $size?format_size($size):'', 'class' => 'size', 'sv' => ($size ? $size : 0));
+}
+
+/**
+ * Return the formatted age of this file.
+ * A filebrowser_get_cell_hook()
+ */
+function filebrowser_get_cell_age($completepath){
+ if(! $completepath){return array('data' => t("Age"), 'field' => 'age'); }
+ $age = time() - filemtime($completepath);
+ return array('data' => format_interval($age), 'class' => 'age', 'sv' => $age);
+}
+
+/**
+ * Return the deduced description or extended info for this file.
+ * A filebrowser_get_cell_hook()
+ */
+function filebrowser_get_cell_info($completepath){
+ if(! $completepath){return array('data' => t("Info")); }
+ static $info;
+
+ if(! isset($info)){
+ // The first time this cell is requested, look hard into the current directory
+ // cache results
+ if(!is_file($completepath)){
+ $subfolder = (is_dir($completepath))?$completepath:dirname($completepath);
+ $completepath = $subfolder.'/descript.ion';
+ if(! file_exists($completepath)){
+ $completepath = $subfolder.'/files.bbs';
+ }
+ }
+
+ if(is_file($completepath)){
+ $info = filebrowser_get_fileinfo($completepath, dirname($completepath));
+ }
+ else {
+ $info = array();
+ }
+ }
+
+ $file = basename($completepath);
+ // each info returned was an array. - not sure how to deal with that in one cell
+ $detail = (is_array($info[$file])) ? array_pop($info[$file]) : $info[$file] ;
+ return array('data' => $detail, 'class' => 'info');
+}
+
+
+
/**
* Loads file metainformation from the specified file. Also
* allows the file to specify a *callback* with which the
@@ -320,7 +404,7 @@
* a file, based on the extension of the file. A specific icon
* can also be requested with the second parameter.
*/
-function filebrowser_get_icon($fullpath = NULL, $iconname = NULL) {
+function filebrowser_get_cell_icon($fullpath = NULL, $iconname = NULL) {
if (isset($fullpath)) {
$iconname = (is_dir($fullpath) ? 'folder' : preg_replace("!^.+\\.([^\\.]+)$!", "\\1", $fullpath));
}
@@ -333,7 +417,7 @@
);
foreach ($iconfiles as $icon) {
if (file_exists($icon)) {
- return theme("image", $icon);
+ return theme("image", $icon, $iconname." file", $iconname." file");
}
}
return '';
@@ -345,7 +429,7 @@
* Also prevent from accessing version control system folders.
*/
function filebrowser_safe_folder($subfolder) {
- $folder = variable_get('filebrowser_root', '') . "/$subfolder";
+ $folder = filebrowser_root() . ($subfolder?"/$subfolder":'');
while (TRUE) {
$safer = str_replace(array("\\", "../", "/.svn", "/CVS", ".."), array("/", "", "", "", ""), $folder);
if ($safer !== $folder) {
@@ -356,26 +440,37 @@
}
}
$folder = preg_replace("!^/*([^/].+[^/])/*$!", "\\1", $folder);
+ // handle the case when superuser has set files folder to '.'
+ $folder = preg_replace("!^./!", "", $folder);
return preg_replace("!/+!", "/", $folder);
}
/**
- * Allows easy path generation even if $path has a leading slash.
- */
-function filebrowser_proper_path($path) {
- return str_replace("//", "/", "filebrowser/$path");
+ * Prepend the filebrowser location to the path.
+ * Allows easy path generation even if $path has a leading slash. Setting the
+ * second parameter allows filebrowser to run from different contexts.
+ * @param $path
+ * @param $filebrowser_base
+ * Define where filebrowser is run from. Requires trailing slash.
+ */
+function filebrowser_proper_path($path, $filebrowser_base=NULL) {
+ static $base; if(!$base){$base='filebrowser/';}
+ if($filebrowser_base){$base=$filebrowser_base;}
+ return preg_replace("|//+|", "/", $base.'/'.$path);
}
/**
* Does the custom sorting of the table contents based on the
* sort values (sv) added previously.
- **/
+ * Note this sorts by named columns, not field numbers as tablesort usually
+ * does.
+ */
function filebrowser_sort_table($a, $b = NULL) {
static $orderby = 0;
static $sort = '';
// Set sorting criteria
if (!isset($b)) {
- $orderby = ((int) $a[0]['sql']) - 1; // number of field to sort with
+ $orderby = $a[0]['sql']; // NAME of field to sort with
$sort = $a[1]; // asc/desc
}
// Sort two values with the sorting criteria
@@ -391,3 +486,69 @@
}
}
}
+
+function filebrowser_set_breadcrumbs($parts){
+ $breadcrumb = array();
+ $dirname = t('root');
+ if ($parts) {
+ $breadcrumb[] = $dirname = array_pop($parts);
+ while (count($parts)) {
+ $breadcrumb[] = l($parts[count($parts) -1], filebrowser_proper_path(join("/", $parts)));
+ array_pop($parts);
+ }
+ $breadcrumb[] = l(t('Filebrowser root'), filebrowser_proper_path(''));
+ } else {
+ $breadcrumb[] = t('Filebrowser');
+ }
+ $breadcrumb[] = l(t('Home'), NULL);
+ drupal_set_breadcrumb(array_reverse($breadcrumb));
+}
+
+/**
+ * Returns the base of the allowed area for filebrowsing.
+ * May be independant of the files directory, and different for different users.
+ */
+function filebrowser_root(){
+ return variable_get('filebrowser_root', file_directory_path());
+}
+/**
+ * Chop the filebrowser root directory (not the url base) off the start of a
+ * path - translate from a filepath to a relative path.
+ *
+ * May operate by reference.
+ */
+function filebrowser_trim_root(&$path){
+ $fbr = filebrowser_root();
+ if(substr($path,0,strlen($fbr)) == $fbr){
+ $path = substr($path,strlen($fbr)+1); // trim slash also
+ }
+ return $path;
+}
+
+/**
+ * Separate the request from the function.
+ * Custom filebrowsers can be invoked from any path on the server, and their
+ * relative subfolders are tacked onto the end of the URL.
+ *
+ * [admin/custom/filebrowser/][my/special/files]
+ *
+ * Find where to split this request.
+ * @return the base filebrowser path for the current job. Contains trailing
+ * slash.
+ */
+function filebrowser_base($subfolder){
+ $filebrowser_base = $subfolder ? substr($_GET['q'],0,strpos($_GET['q'],$subfolder)) : $_GET['q'].'/';
+ return $filebrowser_base;
+}
+
+/**
+ * Tell the system to remember what cols to display when this lookup is called
+ * again by ajax. An internal memo, so the page which is called asynchronously
+ * knows what its parent expects to see.
+ */
+function filebrowser_set_context($context, $cols){
+ $filebrowser_contexts = variable_get('filebrowser_contexts',array());
+ $filebrowser_contexts[$context] = $cols;
+ variable_set('filebrowser_contexts',$filebrowser_contexts);
+}
+