/[drupal]/contributions/modules/filebrowser/filebrowser.module
ViewVC logotype

Contents of /contributions/modules/filebrowser/filebrowser.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.27 - (show annotations) (download) (as text)
Thu Oct 22 07:20:52 2009 UTC (5 weeks, 1 day ago) by ulhume
Branch: MAIN
CVS Tags: HEAD
Changes since 1.26: +802 -456 lines
File MIME type: text/x-php
- copmpanion module api
- Image thumbnailing first support
- Package peroperty changed
1 <?php
2 require_once dirname(__FILE__)."/filebrowser.security.inc";
3
4 /* This file is part of "filebrowser".
5 * Copyright 2009, arNuméral
6 * Author : Yoran Brault
7 * eMail : yoran.brault@bad_arnumeral.fr (remove bad_ before sending an email)
8 * Site : http://www.arnumeral.fr/node/5
9 *
10 * Original credit for susurrus (http://drupal.org/user/118433).
11 *
12 * "filebrowser" is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License as
14 * published by the Free Software Foundation; either version 2.1 of
15 * the License, or (at your option) any later version.
16 *
17 * "filebrowser" is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public
23 * License along with "Broken Anchor for Node comments Module"; if not, write to the Free
24 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
25 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
26 */
27
28 /**
29 * Column indentifiers definition.
30 */
31 define(FILEBROWSER_DATA_NAME_ICON,'icon');
32 define(FILEBROWSER_DATA_NAME_DISPLAY_NAME,'display_name');
33 define(FILEBROWSER_DATA_NAME_SIZE,'size');
34 define(FILEBROWSER_DATA_NAME_CREATED,'created');
35 define(FILEBROWSER_DATA_NAME_MODIFIED,'modified');
36 define(FILEBROWSER_DATA_NAME_TYPE,'type');
37 define(FILEBROWSER_DATA_NAME_DESCRIPTION,'description');
38
39 /**
40 * Use logger debug output if module is here and enabled.
41 */
42 function filebrowser_debug() {
43 static $is_debug;
44 if(!isset($is_debug)) {
45 $is_debug=module_exists("logger") && logger_is_debug();
46 }
47 return $is_debug;
48 }
49
50
51 /**
52 * Compare two sizes (used as usort callback).
53 */
54 function _filebrowser_sorter_size($a, $b) {
55 if ($a['size'] == $b['size']) {
56 return 0;
57 }
58 return ($a['size'] > $b['size']) ? -1 : 1;
59 }
60
61 /**
62 * Compare two creation dates (used as usort callback).
63 */
64 function _filebrowser_sorter_created($a, $b) {
65 if ($a['created'] == $b['created']) {
66 return 0;
67 }
68 return ($a['created'] > $b['created']) ? -1 : 1;
69 }
70
71 /**
72 * Compare two modification dates (used as usort callback).
73 */
74 function _filebrowser_sorter_modified($a, $b) {
75 if ($a['modified'] == $b['modified']) {
76 return 0;
77 }
78 return ($a['modified'] > $b['modified']) ? -1 : 1;
79 }
80
81 /**
82 * Compare two display names (used as usort callback).
83 */
84 function _filebrowser_sorter_display_name($a, $b) {
85 return -strcasecmp($a['display-name'], $b['display-name']);
86 }
87
88 /**
89 * Compare file types (used as usort callback).
90 */
91 function _filebrowser_sorter_type($a, $b) {
92 return -strcasecmp($a['mime-type'], $b['mime-type']);
93 }
94
95 /**
96 * Compare two creation descriptions (used as usort callback).
97 */
98 function _filebrowser_sorter_description($a, $b) {
99 return -strcasecmp($a['description'], $b['description']);
100 }
101
102 /** Check the end of a string
103 * @param $str source string
104 * @param $sub element to search
105 * @return return true is a string ends with another string.
106 */
107 function _filebrowser_ends_with($str, $sub) {
108 return (substr($str, strlen($str) - strlen($sub)) == $sub);
109 }
110
111 /** Helper to get the full path of a dir_listing node
112 * @param $node source node
113 * @return the full path
114 */
115 function _filebrowser_current_full_path($node) {
116 return _filebrowser_get_node_file_path($node) . $node->relative_path;
117 }
118
119
120 /**
121 * Helper function to match a pattern on the path
122 * @param path path to process
123 * @param patterns to search (seperated by cr)
124 * @return true if at least one pattern is found
125 */
126 function filebrowser_match_path($path, $patterns) {
127 static $regexps;
128
129 if (!isset ($regexps[$patterns])) {
130 $regexps[$patterns] = '/^(' . preg_replace(array (
131 '/(\r\n?|\n)/',
132 '/\\\\\*/'
133 ), array (
134 '|',
135 '.*'
136 ), preg_quote($patterns, '/')) . ')$/';
137 }
138 $result = preg_match($regexps[$patterns], $path) == 1;
139 return $result;
140 }
141
142
143 /**
144 * Callback for filebrowser_download_archive/%node menu.
145 */
146 function _filebrowser_download_archive_callback($node) {
147 if (!_filebrowser_can_download_archive($node)) {
148 drupal_access_denied();
149 exit ();
150 }
151
152 if (!function_exists("zip_open")) {
153 return t("No ZIP support found in PHP installation, please contact your administrator");
154 }
155 $zip = new ZipArchive();
156 $match['name'] = $node->download_file_name;
157 $match['mime-type'] = 'application/zip';
158 $match['path'] = file_directory_temp() . '/' . $match['name'];
159 if (file_exists($match['path'])) {
160 unlink($match['path']);
161 }
162 if ($zip->open($match['path'], ZIPARCHIVE :: CREATE) === TRUE) {
163 foreach ($node->file_listing as $file_name => $file_data) {
164 if ($file_data['kind'] == 0) {
165 $zip->addFile($file_data['path'], $file_name);
166 }
167 }
168 $zip->close();
169 } else {
170 return t("Unable to create temporary zip file '@file'", array (
171 file => $match['name']
172 ));
173 }
174 $match['size'] = filesize($match['path']);
175 _file_browser_send_file($match);
176 exit ();
177 }
178
179
180 /**
181 * Callback for filebrowser_download/%node menu.
182 */
183 function _filebrowser_download_callback($node) {
184 if (!_filebrowser_can_download_file($node)) {
185 drupal_access_denied();
186 exit ();
187 }
188
189 $match = array ();
190 foreach ($node->file_listing as $file_name => $file_data) {
191 if ($node->download_file_name == $file_data['name']) {
192 $match = $file_data;
193 break;
194 }
195 }
196 if (empty ($match)) {
197 drupal_not_found();
198 exit ();
199 }
200 _file_browser_send_file($match);
201 exit ();
202 }
203
204 /** Helper function to send a file.
205 * @param $file source file to send.
206 * @return true if sending is ok or false if an error occured.
207 */
208 function _file_browser_send_file (&$file) {
209 header('Content-Description: File Transfer');
210 header("Cache-Control: public, must-revalidate, max-age=0"); // HTTP/1.1
211 header("Pragma: public");
212 header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past
213 header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
214 header("Content-Type: " . $file['mime-type']);
215 header('Content-Disposition: attachment; filename="' . $file['name'] . '";');
216 header("Content-Transfer-Encoding: binary");
217 header("Content-Length: " . $file['size']);
218 $block_size = 4096;
219 $buffer = '';
220 $handle = fopen($file['path'], 'rb');
221 if ($handle === false) {
222 return false;
223 }
224 while (!feof($handle)) {
225 $buffer = fgets($handle, $block_size);
226 echo $buffer;
227 }
228 fclose($handle);
229 return true;
230 }
231
232 /**
233 * Helper function to get node folder with correct token replacements. Never
234 * use $node->folder_path directly !!!
235 * @param $node
236 * @return folder path
237 */
238 function _filebrowser_get_node_file_path(& $node) {
239 $result = $node->folder_path;
240 if (module_exists("token")) {
241 $result = token_replace($result, $type = 'global', $object = NULL, $leading = '[', $trailing = ']');
242 }
243 return $result;
244 }
245
246 /**
247 * Returns the appropriate HTML code for an icon representing
248 * a file, based on the extension of the file. A specific icon
249 * can also be requested with the second parameter.
250 */
251 function _filebrowser_get_default_thumbnail($file) {
252 $main_type = dirname($file['mime-type']);
253 $mime_type = str_replace("/", "-", $file['mime-type']);
254 $module_path = drupal_get_path("module", "filebrowser") . "/images/mime-types/";
255 //FIXME ça ne marche pas, la fonction path_to_theme ne renvoie pas le bon chemin...
256 $theme_path = path_to_theme() . "/filebrowser/";
257 $icons = array (
258 $theme_path . $mime_type . ".png",
259 $theme_path . $main_type . ".png",
260 $module_path . $mime_type . ".png",
261 $module_path . $main_type . ".png"
262 );
263 foreach ($icons as $icon) {
264 if (file_exists($icon)) {
265 return theme('image', $icon);
266 }
267 }
268 return theme('image', $module_path . "unknown.png");
269 }
270
271 function _filebrowser_prepare_record(& $node) {
272 // Fix trailing slashes
273 $node->folder_path = rtrim($node->folder_path, '/');
274
275 // Convert array
276 if (!is_object($node->folder_rights)) {
277 $node->folder_rights=(object)$node->folder_rights;
278 }
279 $data->folder_rights=$node->folder_rights;
280
281 if (!is_object($node->folder_presentation)) {
282 $node->folder_presentation=(object)$node->folder_presentation;
283 }
284 $data->folder_presentation=$node->folder_presentation;
285
286 if (!is_object($node->folder_uploads)) {
287 $node->folder_uploads=(object)$node->folder_uploads;
288 }
289 $data->folder_uploads=$node->folder_uploads;
290
291 foreach (module_implements("filebrowser_handler_info") as $module) {
292 if ($node->$module && !is_object($node->$module)) {
293 $node->$module=(object)$node->$module;
294 }
295 }
296
297 // If no column is visible, make at least defaults ICON+DISPLAY_NAME visible
298 $node->folder_presentation->visible_columns=_filebrowser_filter_checkboxes_result($node->folder_presentation->visible_columns);
299
300 if (!$node->folder_presentation->visible_columns || count($node->folder_presentation->visible_columns)==0) {
301 $node->folder_presentation->visible_columns=array (FILEBROWSER_DATA_NAME_ICON=>1, FILEBROWSER_DATA_NAME_DISPLAY_NAME=>1);
302 }
303
304 // Create serialized properties
305 $data=(Object)array();
306 $data->folder_rights=$node->folder_rights;
307 $data->folder_presentation=$node->folder_presentation;
308 $data->folder_uploads=$node->folder_uploads;
309 foreach (module_implements("filebrowser_handler_info") as $module) {
310 if ($node->$module) {
311 $data->$module = $node->$module;
312 }
313 }
314 $node->properties = serialize($data);
315 }
316
317
318 function &_filebrowser_columns() {
319 static $columns;
320 if (!$columns) {
321 $columns= array(
322 FILEBROWSER_DATA_NAME_ICON=>t('Icon'),
323 FILEBROWSER_DATA_NAME_DISPLAY_NAME=>t('Display name'),
324 FILEBROWSER_DATA_NAME_SIZE=>t('Size'),
325 FILEBROWSER_DATA_NAME_CREATED=>t('Creation Time'),
326 FILEBROWSER_DATA_NAME_MODIFIED=>t('Modification Time'),
327 FILEBROWSER_DATA_NAME_TYPE=>t('Mime type'),
328 FILEBROWSER_DATA_NAME_DESCRIPTION=>t('Description')
329 );
330 }
331 return $columns;
332 }
333
334 function _filebrowser_options_to_query($options, $values=array()) {
335 $options=array_merge($options, $values);
336 foreach ($options as $key=>$value) {
337 if (!empty($query)) {
338 $query.="&";
339 }
340 $query.="$key=$value";
341 }
342 return $query;
343 }
344 function _filebrowser_query_to_options() {
345 static $options=array('path','sort','order','view');
346 $result=array();
347 foreach ($options as $option) {
348 if ($_GET[$option]) {
349 $result[$option]=$_GET[$option];
350 }
351 }
352 return $result;
353 }
354
355
356 /**
357 * Load data from current path.
358 * @param source node
359 */
360 function _filebrowser_load_files(& $node) {
361 global $user, $base_url;
362
363 $url_parts = explode('/', $_GET['path']);
364 $options=_filebrowser_query_to_options();
365 unset($options['path']);
366 if (count($url_parts) == 0) {
367 // This is 'root' folder
368 $relative_path = "/";
369 } else {
370 // FIXME normalement tout élément de chemin supplémentaire implique
371 // un dégagement maintenant que ?path= est utilisé pour le sous-dossier
372 if ($url_parts['0'] == 'edit') {
373 return;
374 }
375
376 $relative_path = implode('/', $url_parts);
377 // FIXME pas terrible...
378 if (arg(0) == 'filebrowser_download' || arg(0) == 'filebrowser_download_archive') {
379 $node->download_file_name = basename($relative_path);
380 $relative_path = dirname($relative_path);
381 if ($relative_path == '.') {
382 $relative_path = "/";
383 }
384 }
385 if ($relative_path == '') {
386 $relative_path = '/';
387 }
388 if ($relative_path != '/') {
389 $relative_path = '/' . trim($relative_path, '/') . '/';
390 }
391 }
392 $node->relative_path = $relative_path;
393
394 static $cache = array ();
395 if (isset ($cache[$relative_path])) {
396 return $node->file_listing = $cache[$relative_path];
397 }
398
399 // retreive paths
400 $full_path = _filebrowser_get_node_file_path($node) . $relative_path;
401 $is_subdir = $relative_path != '/';
402
403 // If we shouldn't be in a subdirectory, redirect to root_dir.
404 if ($is_subdir && !_filebrowser_can_explore_subfolders($node)) {
405 drupal_set_message(t('You\'re not allowed to browse sub folders.'));
406 return;
407 }
408
409 // More advanced check to make sure no parent directories match our files.
410 // blacklist
411 if (!empty ($relative_path)) {
412 $dirs = explode('/', $relative_path);
413 foreach ($dirs as $dir) {
414 if (!empty ($dir)) {
415 if (_filebrowser_is_forbidden($node, $dir)) {
416 drupal_set_message(t('You\'re not allowed to view %dir.', array (
417 '%dir' => $full_path
418 )));
419 return;
420 }
421 }
422 }
423 }
424
425 // Load meta-data
426 $file_metadata = array ();
427 // Check for meta files if we need info.
428 $metadata_file = $full_path . 'descript.ion';
429 if (!file_exists($metadata_file)) {
430 $metadata_file = $full_path . 'file_name.bbs';
431 if (file_exists($metadata_file)) {
432 $file_metadata = "";
433 }
434 }
435 if ($file_metadata != "") {
436 $file_metadata = filebrowser_get_fileinfo($metadata_file);
437 }
438
439 // Iterate over files
440 $files = array ();
441 $files_count = 0;
442 $total_size = 0;
443 $folder_count=0;
444 $has_metadata = false;
445 if (is_dir($full_path) && $dh = opendir($full_path)) {
446 while (($file_name = readdir($dh)) !== false) {
447 if (!is_readable($full_path . $file_name)) {
448 continue;
449 }
450 if (is_dir($full_path . $file_name) && !_filebrowser_can_explore_subfolders($node)) {
451 continue;
452 }
453 $full_file_path = $full_path . $file_name;
454
455 // Handle files that should not be shown.
456 if ($file_name == '.' || $file_name == '..') {
457 if ($file_name == '..' && !$is_subdir) {
458 continue;
459 }
460
461 } else {
462 if (is_file($full_file_path) && !_filebrowser_is_filtered($node, $file_name)) {
463 continue;
464 }
465 if (_filebrowser_is_forbidden($node, $file_name)) {
466 continue;
467 }
468 }
469
470 // File record building
471 $files[$file_name] = array (
472 'name' => $file_name,
473 'display-name' => $file_name,
474 // FIXME on a besoin d'un réglage sur le noeud pour l'encodage
475 // Mais il faut aussi gérer le problème de l'encodage du chemin dans l'URL...
476 // 'display-name' => mb_convert_encoding($file_name, "UTF-8", "ISO-8859-1"),
477 'path' => $full_file_path,
478 'relative_path' => $relative_path . $file_name,
479 'status' => MARK_READ,
480 'created' => 0,
481 'modified' => 0,
482 'size' => 0,
483 'kind' => is_file($full_file_path) ? 0 : 1,
484 'mime-type' => !is_file($full_file_path) ? "folder" : file_get_mimetype($full_file_path),
485 'description' => $file_metadata[$file_name]
486 );
487 $has_metadata |= !empty ($file_metadata[$file_name]);
488
489 // set file properties from stat()
490 if (($f_stats = stat($full_file_path)) !== FALSE) {
491 if (is_file($full_file_path)) {
492 $files[$file_name]['size'] = $f_stats['size'];
493 $total_size += $files[$file_name]['size'];
494
495 }
496 $files[$file_name]['created'] = $f_stats['ctime'];
497 $files[$file_name]['modified'] = $f_stats['mtime'];
498 if ($user->uid) {
499 if ($user->access < $files[$file_name]['created']) {
500 $files[$file_name]['status'] = MARK_NEW;
501 } else
502 if ($user->access < $files[$file_name]['modified']) {
503 $files[$file_name]['status'] = MARK_UPDATED;
504 }
505 }
506 }
507 if ($file_name == '.') {
508 $files[$file_name]['path'] = $full_path;
509 $files[$file_name]['name'] = basename($full_path);
510 }
511
512 if ($file_name == '..') {
513 $files[$file_name]['mime-type'] .= "/parent";
514 $files[$file_name]['kind'] = 2;
515 }
516
517 $file_path=array();
518 if ($file_name == '..') {
519 $parent_folder = dirname($relative_path);
520 if ($parent_folder != "/") {
521 $file_path['path']=$parent_folder;
522 }
523 } else {
524 $file_path['path']= $relative_path . $file_name;
525 }
526
527 if (is_file($full_file_path)) {
528 $files[$file_name]['url'] = $node->folder_rights->private_downloads ? url('filebrowser_download/' .
529 $node->nid, array (
530 'query' => _filebrowser_options_to_query($options, $file_path)
531 )) : $relative_path . $file_name;
532 $files_count++;
533 } else {
534 $base = 'node/' . $node->nid;
535 $files[$file_name]['url'] = url($base, array (
536 'query' => _filebrowser_options_to_query($options, $file_path)
537 ));
538 if ($file_name!='.') {
539 $folder_count++;
540 }
541 }
542 }
543
544 // Set global folder properties
545 $files['.']['size'] = $total_size;
546 $files['.']['files_count'] = $files_count;
547 $files['.']['folders_count'] = $folder_count;
548 $files['.']['has-metadata'] = $has_metadata;
549 closedir($dh);
550 }
551
552 $cache[$relative_path] = & $files;
553 $node->file_listing = $files;
554 }
555
556 /**
557 * Implementation of hook_form().
558 */
559 function filebrowser_form(& $node, & $param) {
560 // _debug_dump($node);
561 $type = node_get_types('type', $node);
562 $form['folder_description'] = array (
563 '#type' => 'fieldset',
564 '#title' => t('Folder Description'),
565 '#collapsible' => TRUE,
566 '#collapsed' => FALSE,
567 '#weight' => -10
568 );
569
570 $form['folder_description']['title'] = array (
571 '#type' => 'textfield',
572 '#title' => check_plain($type->title_label),
573 '#default_value' => !empty ($node->title) ? $node->title : '',
574 '#required' => TRUE
575 );
576
577 $form['folder_description']['folder_path'] = array (
578 '#type' => 'textfield',
579 '#title' => t('The system file path to the directory'),
580 '#description' => t('This can be an absolute path or should be relative to the Drupal root directory.'),
581 '#default_value' => isset ($node->folder_path) ? $node->folder_path : '',
582 '#required' => TRUE
583 );
584
585 if (module_exists('token')) {
586 $form['folder_description']['token_help'] = array (
587 '#title' => t('Replacement patterns'),
588 '#type' => 'fieldset',
589 '#collapsible' => TRUE,
590 '#collapsed' => TRUE
591 );
592
593 $form['folder_description']['token_help']['help'] = array (
594 '#value' => theme('token_help', 'node')
595 );
596 }
597
598 $form['folder_description']['body_filter']['body'] = array (
599 '#type' => 'textarea',
600 '#title' => t('Description'),
601 '#description' => t("This will overide metainformations from .bbs or .ion files."),
602 '#default_value' => $node->body,
603 '#rows' => 5
604 );
605 $form['folder_description']['body_filter']['format'] = filter_form($node->format);
606
607 $form['folder_rights'] = array (
608 '#type' => 'fieldset',
609 '#title' => t('Folder rights'),
610 '#tree' => TRUE,
611 '#collapsible' => TRUE,
612 '#collapsed' => TRUE,
613 '#weight' => -9
614 );
615
616 $form['folder_rights']['explore_subdirs'] = array (
617 '#type' => 'checkbox',
618 '#title' => t('Allow subdirectory listings.'),
619 '#default_value' => isset ($node->folder_rights->explore_subdirs) ? $node->folder_rights->explore_subdirs : ''
620 );
621
622 $form['folder_rights']['private_downloads'] = array (
623 '#type' => 'checkbox',
624 '#title' => t("Use private downloads"),
625 '#description' => t("Use private downloads if your files are outside Drupal root or if you want Drupal to check permissions."),
626 '#default_value' => isset ($node->folder_rights->private_downloads) ? $node->folder_rights->private_downloads : ''
627 );
628
629 $form['folder_rights']['download_archive'] = array (
630 '#type' => 'checkbox',
631 '#title' => t("Download all files as an archive"),
632 '#description' => t("Check this if you allow users to download all folder files as an archive."),
633 '#default_value' => isset ($node->folder_rights->download_archive) ? $node->folder_rights->download_archive: false
634 );
635
636 $form['folder_rights']['forbidden_files'] = array (
637 '#type' => 'textarea',
638 '#title' => t('Forbidden files'),
639 '#description' => t('List of patterns of forbidden files, you can use wildcards (ex. .*).'),
640 '#default_value' => isset ($node->folder_rights->forbidden_files) ? $node->folder_rights->forbidden_files : ".*\r\ndescript.ion\r\nfile.bbs\r\nCVS"
641 );
642 $form['folder_rights']['filtered_files'] = array (
643 '#type' => 'textarea',
644 '#title' => t('Filteredfiles'),
645 '#description' => t('List of patterns to filter, one per line, you can use wildcards (ex. *.pdf).'),
646 '#default_value' => isset ($node->folder_rights->filtered_files) ? $node->folder_rights->filtered_files : ''
647 );
648
649 $form['folder_uploads'] = array (
650 '#type' => 'fieldset',
651 '#tree' => TRUE,
652 '#title' => t('Folder Upload'),
653 '#collapsible' => TRUE,
654 '#collapsed' => TRUE,
655 '#weight' => -8
656 );
657 $form['folder_uploads']['enabled'] = array (
658 '#type' => 'checkbox',
659 '#title' => t('Allow uploads'),
660 '#description' => t('Allow users to upload files.'),
661 '#default_value' => isset ($node->folder_uploads->enabled) ? $node->folder_uploads->enabled : false
662 );
663 $form['folder_uploads']['allow_overwrite'] = array (
664 '#type' => 'checkbox',
665 '#title' => t('Allow overwrites'),
666 '#description' => t('Allow files to be overwrotes.'),
667 '#default_value' => isset ($node->folder_uploads->allow_overwrite) ? $node->folder_uploads->allow_overwrite : false
668 );
669 $form['folder_uploads']['accepted_uploaded_files'] = array (
670 '#type' => 'textarea',
671 '#title' => t('Accepted files for uploading'),
672 '#description' => t('List of file patterns accepted for upload. Empty means anything.'),
673 '#default_value' => isset ($node->folder_uploads->accepted_uploaded_files) ? $node->folder_uploads->accepted_uploaded_files : ''
674 );
675
676 $form['folder_presentation'] = array (
677 '#type' => 'fieldset',
678 '#title' => t('Folder presentation'),
679 '#tree' => TRUE,
680 '#collapsible' => TRUE,
681 '#collapsed' => TRUE,
682 '#weight' => -7
683 );
684
685 $form['folder_presentation']['hide_extension'] = array (
686 '#type' => 'checkbox',
687 '#title' => t('Hide file extensions'),
688 '#default_value' => isset ($node->folder_presentation->hide_extension) ? $node->folder_presentation->hide_extension : ''
689 );
690
691 $form['folder_presentation']['visible_columns'] = array (
692 '#type' => 'checkboxes',
693 '#title' => t("Visible columns"),
694 '#default_value' => _filebrowser_properties_to_checkboxes($node->folder_presentation->visible_columns),
695 '#options' => _filebrowser_columns()
696 );
697
698 $form['folder_presentation']['default_view'] = array (
699 '#type' => 'select',
700 '#title' => t("Default view"),
701 '#default_value' => $node->folder_presentation->default_view,
702 '#options' => array("List View", "Icons View")
703 );
704
705 foreach (module_implements("filebrowser_handler_info") as $module) {
706 $info = module_invoke($module, "filebrowser_handler_info");
707 $form["$module"] = array (
708 '#type' => 'fieldset',
709 '#tree' => TRUE,
710 '#title' => $info['description'],
711 '#collapsible' => TRUE,
712 '#collapsed' => TRUE,
713 '#weight' => -6
714 );
715 $default_setting=$node->$module;
716 if (!$default_setting) {
717 $default_setting=(object)array();
718 }
719 $settings = module_invoke($module, "filebrowser_handler_settings", $default_setting);
720 $form["$module"]['enabled'] = array (
721 '#type' => 'checkbox',
722 '#title' => t('Enabled'),
723 '#description' => t('Enable this file handler.'),
724 '#default_value' => isset ($default_setting->enabled) ? $default_setting->enabled : false
725 );
726 foreach ($settings as $key => $setting) {
727 $form["$module"][$key] = $setting;
728 }
729 }
730 return $form;
731 }
732
733 /**
734 * Implementation of hook_node_info().
735 */
736 function filebrowser_node_info() {
737 return array (
738 'dir_listing' => array (
739 'name' => t('Directory listing'),
740 'module' => 'filebrowser',
741 'description' => t("A listing of files similar to how Apache lists files in a directory."),
742 'has_body' => FALSE
743 )
744 );
745 }
746
747 /**
748 * Implementation of hook_load().
749 */
750 function filebrowser_load($node) {
751 $data = db_fetch_object(db_query('SELECT * FROM {node_dir_listing} WHERE nid = %d', $node->nid));
752
753 $additions = (object) unserialize($data->properties);
754 $additions->folder_path = $data->folder_path;
755 $additions->nid = $data->nid;
756 _filebrowser_prepare_record($additions);
757 _filebrowser_load_files($additions);
758 return $additions;
759 }
760
761
762 /**
763 * Implementation of hook_insert().
764 */
765 function filebrowser_insert($node) {
766 _filebrowser_prepare_record($node);
767 drupal_write_record("node_dir_listing", $node);
768 }
769
770
771 /**
772 * Implementation of hook_update().
773 */
774 function filebrowser_update($node) {
775 _filebrowser_prepare_record($node);
776 drupal_write_record("node_dir_listing", $node, 'nid');
777 }
778
779 /**
780 * Implementation of hook_delete().
781 */
782 function filebrowser_delete($node) {
783 db_query('DELETE FROM {node_dir_listing} WHERE nid = %d', $node->nid);
784 }
785
786 function _filebrowser_filter_checkboxes_result($array) {
787 $result=array();
788 if ($array) {
789 foreach($array as $key=>$value) {
790 if (!empty($value)) {
791 $result[$key]=true;
792 }
793 }
794 }
795 return $result;
796 }
797
798 function _filebrowser_properties_to_checkboxes(&$properties) {
799 $result=array();
800 if ($properties) {
801 foreach($properties as $key=>$value) {
802 if ($value) {
803 $result[$key]=$key;
804 }
805 }
806 }
807 return $result;
808 }
809
810 /**
811 * Implementation of hook_validate().
812 */
813 function filebrowser_validate(& $node) {
814 if (!$node->folder_rights['private_downloads'] && $node->folder_path[0] == '/') {
815 form_set_error('file_path', t('If you specify an absolute path, you should choose "private downloads".'));
816 }
817
818 // Verify the file system location & check that it's a directory.
819 $path = _filebrowser_get_node_file_path($node);
820 if (!is_dir($path)) {
821 form_set_error('file_path', t('The directory %dir is not a valid directory.', array (
822 '%dir' => $path
823 )));
824 }
825
826 // Check that it's readable.
827 if (!is_readable($path)) {
828 form_set_error('file_path', t('The directory %dir is not readable.', array (
829 '%dir' => $path
830 )));
831 }
832 }
833
834 /**
835 * Implementation of hook_theme().
836 */
837 function filebrowser_theme() {
838 return array (
839 'dir_listing_content' => array (
840 'arguments' => array ('node'=> $node, 'view' => NULL, 'content' => NULL, 'description'=>NULL, 'statistics'=>NULL, 'toolbar'=>NULL, 'upload_form'=> NULL),
841 'template' => 'dir_listing_content'),
842 'dir_listing_view_list' => array (
843 'arguments' => array ('node' => NULL)),
844 'dir_listing_icon' => array (
845 'arguments' => array ('file' => NULL, 'thumbnail'=>NULL, 'title'=>null, 'description'=>NULL, 'href'=>NULL, 'new'=>NULL),
846 'template'=>dir_listing_icon),
847 'dir_listing_view_icons' => array (
848 'arguments' => array ('node' => NULL))
849 );
850 }
851
852 /**
853 * Implementation of hook_link().
854 */
855 function filebrowser_link($type, $node = NULL, $teaser = FALSE) {
856 if ($node->type == 'dir_listing' && _filebrowser_can_download_archive($node) && !$teaser && function_exists('zip_open')) {
857 $links = array ();
858 foreach ($node->file_listing as $file) {
859 if ($file['kind'] == 0) {
860 $links['file_browser_download_archive'] = array (
861 'href' => 'filebrowser_download_archive/' . $node->nid,
862 'query' => "path=" . $node->relative_path . ($node->relative_path == '/' ? $node->title : basename($node->relative_path)) . ".zip",
863 'title' => t("Download files as an Archive")
864 );
865 break;
866 }
867 }
868 return $links;
869 }
870 }
871
872 /**
873 * Implementation of hook_menu().
874 */
875 function filebrowser_menu() {
876 $items = array ();
877 $items['filebrowser_download/%node'] = array (
878 'page callback' => '_filebrowser_download_callback',
879 'page arguments' => array (1),
880 'type' => MENU_CALLBACK,
881 'access arguments' => array (FILEBROWSER_DOWNLOAD)
882 );
883 $items['node/%node/thumbnails/create'] = array (
884 'page callback' => '_filebrowser_create_thumbnails_callback',
885 'page arguments' => array (1),
886 'type' => MENU_CALLBACK,
887 'access arguments' => array (FILEBROWSER_DOWNLOAD)
888 );
889
890 $items['admin/settings/filebrowser'] = array (
891 'title' => 'File Browser',
892 'description' => 'Configure File Browser.',
893 'page callback' => 'drupal_get_form',
894 'page arguments' => array ('filebrowser_admin_settings'),
895 'access arguments' => array ('administer site configuration'),
896 'file' => 'filebrowser.admin.inc',
897 'type' => MENU_LOCAL_TASK
898 );
899
900 $items['filebrowser_download_archive/%node'] = array (
901 'page callback' => '_filebrowser_download_archive_callback',
902 'page arguments' => array (1),
903 'type' => MENU_CALLBACK,
904 'access arguments' => array (FILEBROWSER_DOWNLOAD_ARCHIVE)
905 );
906
907 return $items;
908 }
909
910 /**
911 * Implementation of hook_init().
912 */
913 function filebrowser_init() {
914 drupal_add_css(drupal_get_path("module", "filebrowser") . "/styles/content.css");
915 drupal_add_css(drupal_get_path("module", "filebrowser") . "/styles/icons.css");
916 drupal_add_css(drupal_get_path("module", "filebrowser") . "/styles/list.css");
917 drupal_add_js(drupal_get_path("module", "filebrowser") . "/scripts/icons.js");
918 drupal_add_js(drupal_get_path("module", "filebrowser") . "/scripts/list.js");
919 }
920
921
922 /**
923 * Implementation of hook_init().
924 */
925 function filebrowser_view($node, $teaser = FALSE, $page = FALSE) {
926 global $user, $base_url;
927 $node = node_prepare($node, $teaser);
928
929 if (!$teaser) {
930
931 // Full node content view
932 // ========================================================
933 $options=_filebrowser_query_to_options();
934
935 // Keep track of the current location and update breadcrumbs to reflect that.
936 $breadcrumbs = array (
937 l(t('Home'), NULL),
938 );
939 $breadcrumb_path="";
940 $path_elements=explode('/', $node->file_listing['.']['relative_path']);
941 array_pop($path_elements);
942 for ($i=0; $i<count($path_elements); $i++) {
943 $child_dir=$path_elements[$i];
944 $breadcrumb_path .= "/".$child_dir;
945 if (empty($child_dir)) {
946 $label=$node->title;
947 } else {
948 $label=$child_dir;
949 }
950 if ($i<(count($path_elements)-1)) {
951 $breadcrumbs[] = l($label, 'node/'.$node->nid, array('query'=>_filebrowser_options_to_query($options, array('path'=>$breadcrumb_path))));
952 } else {
953 $breadcrumbs[]=$label;
954 }
955 }
956 drupal_set_breadcrumb($breadcrumbs);
957
958 // Insert file listing content part
959 $current_view=$options['view']?$options['view']:($node->folder_presentation->default_view==0?"list":"icons");
960 $buttons=theme('links', array(
961 'list-view'=>array(
962 'href'=>'node/'.$node->nid,
963 'title'=>t('List view'),
964 'query'=>_filebrowser_options_to_query($options, array('view'=>'list')),
965 'attributes'=>array(
966 'class'=>$current_view=='list'?'active':'',
967 'title'=>'Switch to list view')),
968 'icons-view'=>array(
969 'href'=>'node/'.$node->nid,
970 'title'=>t('Icons view'),
971 'query'=>_filebrowser_options_to_query($options, array('view'=>'icons')),
972 'attributes'=>array(
973 'class'=>$current_view=='icons'?'active':'',
974 'title'=>'Switch to icon view')),
975 ), array('class' => 'dir-listing-toolbar'));
976
977 $content=
978 count($node->file_listing) ==0?
979 t('This directory is empty.'):
980 theme("dir_listing_view_$current_view", $node);
981 if ($node->folder_uploads->enabled && user_access(FILEBROWSER_UPLOAD)) {
982 $upload_form = drupal_get_form('filebrowser_upload_form', $node);
983 }
984
985 $statistics=array();
986 $statistics['empty']=t('This folder is empty');
987 if ($node->file_listing['.']['folders_count']>0) {
988 $statistics['folders'] = format_plural($node->file_listing['.']['folders_count'], '1 dossier', '@count dossiers');
989 unset($statistics['empty']);
990 }
991 if ($node->file_listing['.']['files_count']>0) {
992 $statistics['files'] = format_plural($node->file_listing['.']['files_count'], '1 file', '@count files');
993 $statistics['size'] = format_size($node->file_listing['.']['size']);
994 unset($statistics['empty']);
995 }
996
997 $node->content['filebrowser'] = array (
998 '#value' => theme('dir_listing_content',
999 $node,
1000 $current_view,
1001 $content,
1002 $node->content['body']['#value'],
1003 $statistics,
1004 $buttons,
1005 $upload_form),
1006 '#weight' => 1
1007 );
1008 unset($node->content['body']);
1009 }
1010 return $node;
1011 }
1012
1013 //function filebrowser_preprocess_node(&$variables) {
1014 // $node=$variables['node'];
1015 // if ($node->type=='dir_listing') {
1016 // var_dump($variables['template_files']);
1017 // }
1018 //}
1019
1020 /**
1021 * Implementation of hook_form() for uploads.
1022 */
1023 function filebrowser_upload_form($form_state, $node) {
1024 $form = array ();
1025 $form['filebrowser_uploads'] = array (
1026 '#type' => 'fieldset',
1027 '#title' => t('File Upload'),
1028 '#collapsible' => TRUE,
1029 '#collapsed' => TRUE,
1030 '#description' => t('Uploaded file will be saved to the current directory.'),
1031 '#prefix' => '<div class="attachments">',
1032 '#suffix' => '</div>',
1033 '#weight' => 30
1034 );
1035 $form['#node'] = $node;
1036 $form['#attributes'] = array (
1037 'enctype' => "multipart/form-data"
1038 );
1039 $form['#submit'][] = 'filebrowser_upload_form_submit';
1040 $form['#validate'][] = 'filebrowser_upload_form_validate';
1041 $form['filebrowser_uploads']['filebrowser_upload'] = array (
1042 '#type' => 'file',
1043 '#title' => t('Upload file'),
1044 '#size' => 40
1045 );
1046 $form['filebrowser_uploads']['filebrowser_file_name'] = array (
1047 '#type' => 'textfield',
1048 '#title' => t('New name'),
1049 '#size' => 40
1050 );
1051 $form['filebrowser_uploads']['filebrowser_description'] = array (
1052 '#type' => 'textarea',
1053 '#title' => t('Description'),
1054 '#size' => 255
1055 );
1056 $form['submitted'] = array (
1057 '#tree' => TRUE
1058 );
1059 $form['filebrowser_uploads']['filebrowser_button'] = array (
1060 '#type' => 'submit',
1061 '#value' => t('Upload'),
1062 '#name' => 'attach'
1063 );
1064 return $form;
1065
1066 }
1067
1068 /**
1069 * Implementation of hook_validate() for uploads.
1070 */
1071 function filebrowser_upload_form_validate($form, $form_state) {
1072 $file_name = $_FILES['files']['name']['filebrowser_upload'];
1073 if (!empty ($form_state['values']['filebrowser_file_name'])) {
1074 $file_name = $form_state['values']['filebrowser_file_name'];
1075 }
1076 $node = $form {
1077 '#node' };
1078 if (!$node->folder_uploads->allow_overwrite && file_exists(_filebrowser_current_full_path($node) . $file_name)) {
1079 form_error($form['filebrowser_uploads']['filebrowser_upload'], t("This file already exists."));
1080 }
1081 if (!empty ($node->folder_uploads->accepted_uploaded_files) && !filebrowser_match_path($file_name, $node->folder_uploads->accepted_uploaded_files)) {
1082 form_error($form['filebrowser_uploads']['filebrowser_upload'], t("Sorry, you can't upload this kind of file."));
1083 }
1084 }
1085
1086 /**
1087 * Implementation of hook_submit() for uploads.
1088 */
1089 function filebrowser_upload_form_submit($form, & $form_state) {
1090 $file_name = $_FILES['files']['name']['filebrowser_upload'];
1091 if (!empty ($form_state['values']['filebrowser_file_name'])) {
1092 $file_name = $form_state['values']['filebrowser_file_name'];
1093 }
1094 $tmp_filename = $_FILES['files']['tmp_name']['filebrowser_upload'];
1095 $node = $form {
1096 '#node' };
1097
1098 copy($tmp_filename, _filebrowser_current_full_path($node) . $file_name);
1099 $description_file = _filebrowser_current_full_path($node) . "descript.ion";
1100 if (!empty ($form['filebrowser_uploads']['filebrowser_description'])) {
1101 if (file_exists($description_file)) {
1102 $description = file_get_contents($description_file);
1103 }
1104 $description .= "\r\n$file_name " . $form_state['values']['filebrowser_description'];
1105 file_put_contents($description_file, $description);
1106 }
1107 }
1108
1109 /**
1110 * Loads file metainformation from the specified file. Also
1111 * allows the file to specify a *callback* with which the
1112 * descriptions are parsed, so more metainformation can be
1113 * presented on the output.
1114 */
1115 function filebrowser_get_fileinfo($info_file_path) {
1116 static $metacols = array ();
1117
1118 // Build meta information list.
1119 $metainfo = array ();
1120 if (is_readable($info_file_path) && ($file = file($info_file_path))) {
1121 foreach ($file as $line) {
1122 // Skip empty and commented lines.
1123 if (trim($line) == ''