You are here

filebrowser.module in Filebrowser 6

File

filebrowser.module
View source
<?php

function filebrowser_menu() {
  $items = array();

  // TODO: Query below causes error on module uninstall
  $qry = db_query('SELECT path, location FROM {filebrowser}');
  while ($o = db_fetch_object($qry)) {
    $items[$o->path] = array(
      'page callback' => 'filebrowser_page',
      'page arguments' => array(
        $o->path,
      ),
      'access arguments' => array(
        'view directory listing',
      ),
      'type' => MENU_CALLBACK,
    );
  }
  $items['admin/settings/filebrowser'] = array(
    'title' => 'Filebrowser',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'filebrowser_admin_settings',
    ),
    'access arguments' => array(
      'access administration pages',
    ),
    'type' => MENU_NORMAL_ITEM,
  );
  return $items;
}
function filebrowser_page($path) {
  global $user;

  // Grab info for the listing we're viewing
  $listing = db_fetch_object(db_query("SELECT location, can_explore FROM {filebrowser} WHERE path = '%s'", $path));
  $listing->can_explore = (bool) (int) $listing->can_explore;
  $listing->path = $path;

  // Grab full Drupal path
  $curr_dir = str_replace($listing->path, $listing->location, $_GET['q']);

  // Are we in a subdirectory?
  $is_subdir = $listing->location != $curr_dir;

  // If we shouldn't be in a subdir, redirect to root_dir
  if ($is_subdir && !$listing->can_explore) {
    drupal_set_message(t('You\'re not allowed to view subdirectories.'), 'error');
    drupal_goto($listing->path);
  }

  // Reflect current directory in title
  drupal_set_title(t('Displaying contents of directory %dir', array(
    '%dir' => $curr_dir,
  )));
  $dir = $curr_dir;
  $files = array();
  $total_size = 0;
  if (is_dir($dir) && ($dh = opendir($dir))) {
    while (($file = readdir($dh)) !== false && is_readable($dir . '/' . $file)) {
      $full_path = $dir . '/' . $file;
      if (is_file($full_path)) {
        $f_stats = stat($full_path);
        if ($f_stats !== false) {
          $total_size += $f_stats['size'];

          // Mark this file new or updated
          $mark_value = MARK_READ;
          if ($user->access < $f_stats['ctime']) {
            $mark_value = MARK_NEW;
          }
          else {
            if ($user->access < $f_stats['mtime']) {
              $mark_value = MARK_UPDATED;
            }
          }
          $files[] = array(
            l($file, $full_path) . theme('mark', $mark_value),
            format_size($f_stats['size']),
          );
        }
        else {
          $files[] = array(
            l($file, $full_path),
            t('Unknown'),
          );
        }
      }
      else {
        if (is_dir($full_path)) {
          if ($listing->can_explore) {
            $dirs[] = $file;
          }
        }
      }
    }
    closedir($dh);
  }
  if ($listing->can_explore) {
    rsort($dirs);
    foreach ($dirs as $dir) {

      // Always remove '.'
      if ($dir == '.') {
        continue;
      }

      // Only allow parent directory link in subdirectories
      if (!$is_subdir && $dir == '..') {
        continue;
      }
      $dir_row = array(
        l($dir, $_GET['q'] . '/' . $dir),
        t('Directory'),
      );
      array_unshift($files, $dir_row);
    }
  }
  $header = array(
    t('Name'),
    t('Size'),
  );
  $output = theme('filebrowser_page', $files, $header, $total_size);
  return $output;
}
function filebrowser_admin_settings() {
  $form = array();
  $form['listings'] = array(
    '#type' => 'fieldset',
    '#title' => t('Directory listings'),
  );

  // Insert listings table markup
  $qry = db_query('SELECT path, location, can_explore FROM {filebrowser}');
  $paths = array();
  while ($o = db_fetch_object($qry)) {
    $form['paths'][$o->path] = array(
      '#type' => 'value',
      '#value' => $o->path,
    );
    $form['locations'][$o->path] = array(
      '#type' => 'value',
      '#value' => $o->location,
    );
    $form['can_explore'][$o->path] = array(
      '#type' => 'value',
      '#value' => (bool) (int) $o->can_explore,
    );
    $paths[$o->path] = '';
  }

  // Only display existing listing information if we have some
  if (!empty($paths)) {
    $form['listing_checks'] = array(
      '#type' => 'checkboxes',
      '#options' => $paths,
    );
    $form['listings']['submit'] = array(
      '#type' => 'submit',
      '#value' => t('Update directory listings'),
      '#weight' => 10,
      '#submit' => array(
        'filebrowser_admin_settings_submit_update',
      ),
    );
  }
  else {
    $form['listings']['listing_status'] = array(
      '#type' => 'markup',
      '#value' => '<p>' . t('No listings exist.') . '</p>',
    );
  }
  $form['new_listing'] = array(
    '#type' => 'fieldset',
    '#title' => t('Add listing'),
  );

  // Insert options to enter a new listing
  $form['new_listing']['path'] = array(
    '#type' => 'textfield',
    '#title' => t('Drupal path'),
    '#description' => t('Path that users can view this directory listing at.'),
    '#required' => true,
  );
  $form['new_listing']['location'] = array(
    '#type' => 'textfield',
    '#title' => t('File system directory'),
    '#description' => t('File system path to a directory.'),
    '#required' => true,
  );
  $form['new_listing']['can_explore'] = array(
    '#type' => 'checkbox',
    '#title' => t('Do you want to allow exploring subdirectories?'),
  );
  $form['new_listing']['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Add listing'),
    '#validate' => array(
      'filebrowser_admin_settings_validate_add',
    ),
    '#submit' => array(
      'filebrowser_admin_settings_submit_add',
    ),
  );
  $form['#after_build'] = array(
    'filebrowser_strip_requirements',
  );
  return $form;
}
function filebrowser_strip_requirements($form, $form_state) {
  if (isset($form_state['clicked_button']) && $form_state['clicked_button']['#value'] == t('Update directory listings')) {
    $form['new_listing']['path']['#required'] = false;
    $form['new_listing']['location']['#required'] = false;
  }
  return $form;
}
function filebrowser_admin_settings_validate_add($form, &$form_state) {
  $path = $form_state['values']['path'];
  $location = $form_state['values']['location'];

  // Verify the Drupal path
  // Check that the path isn't already in use
  $path_exists = db_result(db_query('SELECT COUNT(1) FROM {menu_router} mr, {menu_links} ml WHERE mr.path = "%s" OR ml.link_path = "%s"', $path, $path));
  if ($path_exists) {
    form_set_error('path', t('You must specify an unused Drupal path'));
  }

  // Verify the file system location
  // Check that it's a directory
  if (!is_dir($location)) {
    form_set_error('location', t('You must specify a valid directory.'));
  }

  // Check that it's readable
  if (!is_readable($location)) {
    form_set_error('location', t('The directory %dir is not readable.', array(
      '%dir' => $location,
    )));
  }
}
function filebrowser_admin_settings_submit_add($form, &$form_state) {
  db_query('INSERT INTO {filebrowser} (path, location, can_explore) VALUES ("%s", "%s", %d)', $form_state['values']['path'], $form_state['values']['location'], $form_state['values']['can_explore']);

  // Make sure our entries are deleted from the menu system also
  // TODO: See if there's a lighter-weight function to update the menu
  // for just this module
  menu_rebuild();
  drupal_set_message(t('Your directory listing has been added'));
}
function filebrowser_admin_settings_submit_update($form, &$form_state) {
  foreach ($form_state['values']['listing_checks'] as $check) {
    if ($check) {
      db_query('DELETE FROM {filebrowser} WHERE path = "%s"', $check);
    }
  }

  // Make sure our entries are deleted from the menu system also
  menu_rebuild();
  drupal_set_message(t('Your directory listing has been updated'));
}
function theme_filebrowser_admin_settings($form) {
  $rows = array();
  if (isset($form['paths'])) {
    foreach (element_children($form['paths']) as $key) {
      $row = array();
      $row[] = l($form['paths'][$key]['#value'], $form['paths'][$key]['#value']);
      $row[] = $form['locations'][$key]['#value'];
      $stats = _filebrowser_dir_stats($form['locations'][$key]['#value']);
      $row[] = $stats['file_count'];
      $row[] = format_size($stats['total_size']);
      $row[] = $form['can_explore'][$key]['#value'] ? t('Yes') : t('No');
      $row[] = drupal_render($form['listing_checks'][$key]);
      $rows[] = $row;
    }
    $headers = array(
      t('Path'),
      t('Location'),
      t('Files'),
      t('Size'),
      t('Explorable'),
      t('Delete'),
    );
    $form['listings']['listings'] = array(
      '#value' => theme('table', $headers, $rows),
    );
  }
  $output = drupal_render($form);

  // Process any other fields and display them
  return $output;
}

/**
 * Implementation of hook_theme
 *
 * @return array of theming functions
 */
function filebrowser_theme() {
  return array(
    'filebrowser_page' => array(
      'arguments' => array(
        'files' => NULL,
        'header' => array(),
        'total_size' => 0,
      ),
    ),
    'filebrowser_admin_settings' => array(
      'arguments' => array(
        'form' => NULL,
      ),
    ),
  );
}

/**
 * Implementation of hook_perm()
 *
 */
function filebrowser_perm() {
  return array(
    'view directory listing',
  );
}
function _filebrowser_dir_stats($dir) {
  $file_count = 0;
  $total_size = 0;
  if (is_dir($dir) && ($dh = opendir($dir))) {
    while (($file = readdir($dh)) !== false && is_readable($dir . '/' . $file)) {

      // Exclude fake directories
      if ($file == '.' || $file == '..') {
        continue;
      }
      $full_path = $dir . '/' . $file;
      $f_size = filesize($full_path);
      $total_size += $f_size;
      ++$file_count;
    }
    closedir($dh);
  }
  return array(
    'file_count' => $file_count,
    'total_size' => $total_size,
  );
}

/**
 * Theme a filebrowser page, if files are available or not.
 * Here you have some possibility to reformat the data or the table layout.
 */
function theme_filebrowser_page(&$files, $header = array(), $total_size = 0) {
  $output = '';
  if ($files) {

    // CSS can hook on this ID to style table elements differently
    $output .= theme('table', $header, $files, array(
      'id' => 'filebrowser-table',
    ));
    $output .= '<p>' . t('Contains @fc files totaling @ds in size', array(
      '@fc' => count($files),
      '@ds' => format_size($total_size),
    )) . '</p>';
  }
  else {
    $output .= '<p>' . t('This directory is empty.') . '</p>';
  }
  return $output;
}