You are here

auditfiles.notindatabase.inc in Audit Files 7.3

Same filename and directory in other branches
  1. 7.4 auditfiles.notindatabase.inc

Generates a report showing files on the server, but not in the database.

File

auditfiles.notindatabase.inc
View source
<?php

/**
 * @file
 * Generates a report showing files on the server, but not in the database.
 */

/**
 * The following are functions for displaying the list of files on the page.
 */

/**
 * Generates the report.
 *
 * This cannot be sorted, because a result set that is too large will time out.
 *
 * @param array $form
 *   The form definition.
 * @param array $form_state
 *   The current state of the form.
 *
 * @return array
 *   The form definition.
 */
function auditfiles_not_in_database_form(array $form, array &$form_state) {

  // Check to see if the confirmation form needs to be displayed instead of the
  // normal form.
  if (isset($form_state['storage']['confirm'])) {
    return _auditfiles_not_in_database_confirm_operation($form, $form_state);
  }

  // Get the records to display.
  // Check to see if there is saved data, and if so, use that.
  $rows = variable_get('auditfiles_not_in_database_files_to_display', array());
  if (empty($rows)) {

    // The data is not saved and the batch operation has not been run, so get
    // the data using the default options.
    $rows = _auditfiles_not_in_database_get_reports_files();

    // Save the data for persistent use.
    variable_set('auditfiles_not_in_database_files_to_display', $rows);
  }
  if (!empty($rows)) {

    // Set up the pager.
    $items_per_page = variable_get('auditfiles_report_options_items_per_page', 50);
    if (!empty($items_per_page)) {
      $current_page = pager_default_initialize(count($rows), $items_per_page);

      // Break the total data set into page sized chunks.
      $pages = array_chunk($rows, $items_per_page, TRUE);
    }
  }

  // Define the form.
  // Setup the record count and related messages.
  $maximum_records = variable_get('auditfiles_report_options_maximum_records', 250);
  if (!empty($rows)) {
    if ($maximum_records > 0) {
      $file_count_message = 'Found at least @count files on the server that are not in the database.';
    }
    else {
      $file_count_message = 'Found @count files on the server that are not in the database.';
    }
    $form_count = format_plural(count($rows), 'Found 1 file on the server that is not in the database.', $file_count_message);
  }
  else {
    $form_count = t('Found no files on the server that are not in the database.');
  }

  // Add the button to reset the record selection.
  $form['reset_records'] = array(
    '#type' => 'submit',
    '#value' => t('Reset file list'),
    '#suffix' => '<div>' . t("Use this button to reset this report's variables and load the page anew.") . '</div>',
  );

  // Add the button to batch process the list of results.
  if ($maximum_records > 0) {
    $form['batch_process'] = array(
      '#type' => 'submit',
      '#value' => t('Load all files'),
      '#suffix' => '<div>' . t('Use this button to load the number of records specified with the "Batch size" administrative configuration setting.') . '</div>',
    );
  }

  // Create the form table.
  $form['files'] = array(
    '#type' => 'tableselect',
    '#header' => _auditfiles_not_in_database_get_header(),
    '#empty' => t('No items found.'),
    '#prefix' => '<div><em>' . $form_count . '</em></div>',
  );

  // Add the data.
  if (!empty($rows) && !empty($pages)) {
    $form['files']['#options'] = $pages[$current_page];
  }
  elseif (!empty($rows)) {
    $form['files']['#options'] = $rows;
  }
  else {
    $form['files']['#options'] = array();
  }

  // Add any action buttons.
  if (!empty($rows)) {
    $form['actions'] = array(
      '#type' => 'actions',
    );
    $form['actions']['add'] = array(
      '#type' => 'submit',
      '#value' => t('Add selected items to the database'),
    );
    $form['actions']['markup'] = array(
      '#markup' => '&nbsp;' . t('or') . '&nbsp;',
    );
    $form['actions']['delete'] = array(
      '#type' => 'submit',
      '#value' => t('Delete selected items from the server'),
    );

    // Add the pager.
    $form['pager'] = array(
      '#markup' => theme('pager'),
    );
  }
  return $form;
}

/**
 * Submit handler for the auditfiles_not_in_database_form form.
 */
function auditfiles_not_in_database_form_submit($form, &$form_state) {

  // Check if an operation was performed.
  if (!empty($form_state['values']['op'])) {

    // Check which operation was performed and start the batch process.
    if ($form_state['values']['op'] == t('Load all files')) {

      // Clear the variable, so subsequent pages will load the correct data.
      variable_del('auditfiles_not_in_database_files_to_display');

      // Prepare and set the batch.
      batch_set(_auditfiles_not_in_database_batch_display_create_batch());
    }
    elseif ($form_state['values']['op'] == t('Reset file list')) {

      // Reset all the variables for this report, so subsequent pages loads will
      // load and use the correct data.
      db_delete('variable')
        ->condition('name', 'auditfiles_not_in_database_%', 'LIKE')
        ->execute();
      cache_clear_all('variables', 'cache_bootstrap');
    }
    elseif (($form_state['values']['op'] == t('Add selected items to the database') || $form_state['values']['op'] == t('Delete selected items from the server')) && !empty($form_state['values']['files'])) {
      foreach ($form_state['values']['files'] as $file_id) {
        if (!empty($file_id)) {

          // At least one file was selected, and the operation has not been
          // confirmed, so modify the data to display the confirmation form.
          $form_state['storage']['files'] = $form_state['values']['files'];
          $form_state['storage']['op'] = $form_state['values']['op'];
          $form_state['storage']['confirm'] = TRUE;
          $form_state['rebuild'] = TRUE;
          return TRUE;
        }
      }
      drupal_set_message(t('No items were selected to operate on.'));
    }
    elseif ($form_state['values']['op'] == t('Yes')) {
      if ($form_state['values']['operation'] == 'add') {

        // Prepare and set the batch.
        batch_set(_auditfiles_not_in_database_batch_add_create_batch($form_state['values']['changelist']));
      }
      elseif ($form_state['values']['operation'] == 'delete') {

        // Prepare and set the batch.
        batch_set(_auditfiles_not_in_database_batch_delete_create_batch($form_state['values']['changelist']));
      }
    }
  }
}

/**
 * The following are functions that are common for all batches in this file.
 */

/**
 * Adds vaules to a batch definition that are common to all batches in the file.
 *
 * @return array
 *   The beginning of the batch definition.
 */
function _auditfiles_not_in_database_batch_set_common_values() {
  return array(
    'error_message' => t('One or more errors were encountered processing the files.'),
    'file' => drupal_get_path('module', 'auditfiles') . '/auditfiles.notindatabase.inc',
    'finished' => '_auditfiles_not_in_database_batch_finish_batch',
    'progress_message' => t('Completed @current of @total operations.'),
  );
}

/**
 * The function that is called when the batch is completed.
 */
function _auditfiles_not_in_database_batch_finish_batch($success, $results, $operations) {
  if ($success) {
    if (!empty($results['files_to_display'])) {

      // Save the gathered data for display.
      variable_set('auditfiles_not_in_database_files_to_display', $results['files_to_display']);
    }
  }
  else {

    // An error occurred.
    // $operations contains the operations that remained unprocessed.
    $error_operation = reset($operations);
    drupal_set_message(t('An error occurred while processing @operation with arguments : @args', array(
      '@operation' => $error_operation[0],
      '@args' => print_r($error_operation[0], TRUE),
    )));
  }
}

/**
 * The following are functions for preparing the batch for displaying the files.
 */

/**
 * Prepares the definition for the page display batch.
 *
 * @return array
 *   The batch definition.
 */
function _auditfiles_not_in_database_batch_display_create_batch() {
  $batch = _auditfiles_not_in_database_batch_set_common_values();
  $batch['title'] = t('Loading file audit data');
  $batch['operations'] = _auditfiles_not_in_database_batch_display_get_operations();
  return $batch;
}

/**
 * Configures the operations for the batch process.
 *
 * @return array
 *   The operations to execute.
 */
function _auditfiles_not_in_database_batch_display_get_operations() {
  $operations = array();
  $operations[] = array(
    '_auditfiles_not_in_database_batch_display_get_files',
    array(),
  );
  $operations[] = array(
    '_auditfiles_not_in_database_batch_display_process_files',
    array(),
  );
  return $operations;
}

/**
 * Recurse directories and add files to an array.
 *
 * @param array $context
 *   Used by the Batch API to keep track of data and pass it from one operation
 *   to the next.
 */
function _auditfiles_not_in_database_batch_display_get_files(array &$context) {
  if (empty($context['sandbox'])) {
    $context['sandbox'] = array();
    $context['sandbox']['progress'] = 0;

    // Get the starter list of files from the root directory.
    $file_system_stream = variable_get('auditfiles_file_system_path', 'public');
    $context['sandbox']['drupal_path'] = variable_get('file_' . $file_system_stream . '_path', conf_path() . '/files');
    $context['sandbox']['real_path'] = drupal_realpath($file_system_stream . '://');
    $files = _auditfiles_not_in_database_get_files($context['sandbox']['real_path']);
    if (!empty($files)) {
      $context['sandbox']['exclusions'] = _auditfiles_get_exclusions();
      $context['sandbox']['files'] = _auditfiles_not_in_database_report_processing_merge_file_lists(array(), $files, $context['sandbox']['real_path'], '', $context['sandbox']['exclusions']);
    }
    $context['sandbox']['batch_size'] = variable_get('auditfiles_report_options_batch_size', 1000);
    if ($context['sandbox']['batch_size'] > 0) {
      $context['sandbox']['max'] = $context['sandbox']['batch_size'];
    }
    else {
      $context['sandbox']['max'] = count($context['sandbox']['files']);
    }
  }
  if (empty($context['results'])) {
    $context['results'] = array();
  }
  if (empty($context['results']['file_list'])) {
    $context['results']['file_list'] = array();
  }
  if (!empty($context['sandbox']['files'])) {

    // Process 20 files at a time, so the batch process has an opportunity to
    // reset itself.
    $file_list = array_slice($context['sandbox']['files'], 0, 20, TRUE);
    foreach ($file_list as $key => $context['sandbox']['file']) {
      if (is_dir($context['sandbox']['real_path'] . DIRECTORY_SEPARATOR . $context['sandbox']['file'])) {

        // Scan the directory and append the files to the files array.
        $found_files = _auditfiles_not_in_database_get_files($context['sandbox']['real_path'] . DIRECTORY_SEPARATOR . $context['sandbox']['file']);
        if (!empty($found_files)) {

          // Add the newly found files to the end of the files array.
          $context['sandbox']['files'] = _auditfiles_not_in_database_report_processing_merge_file_lists($context['sandbox']['files'], $found_files, $context['sandbox']['real_path'], $context['sandbox']['file'], $context['sandbox']['exclusions']);

          // Update the max file count.
          if (empty($context['sandbox']['batch_size'])) {
            $context['sandbox']['max'] = count($context['sandbox']['files']);
          }
        }
      }
      else {

        // Add the file to the list.
        $context['results']['file_list'][] = _auditfiles_not_in_database_fix_path_separators($context['sandbox']['file']);

        // Check to see if the array has reached its limit.
        if (count($context['results']['file_list']) >= $context['sandbox']['max']) {
          $context['sandbox']['progress'] = $context['sandbox']['max'];
        }
        else {
          $context['sandbox']['progress']++;
        }
      }
      unset($context['sandbox']['files'][$key]);

      // Update the progress information.
      $context['message'] = t('Getting the list of files. Processed file @num1 of @num2. Last file processed: !file_name.', array(
        '@num1' => $context['sandbox']['progress'],
        '@num2' => $context['sandbox']['max'],
        '!file_name' => $context['sandbox']['file'],
      ));
    }
  }
  else {
    $context['sandbox']['progress'] = $context['sandbox']['max'];
  }
  if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
    $context['finished'] = $context['sandbox']['progress'] >= $context['sandbox']['max'];
  }
}

/**
 * Retrieves file data from the database and checks if it is on the server.
 *
 * @param array $context
 *   Used by the Batch API to keep track of data and pass it from one operation
 *   to the next.
 */
function _auditfiles_not_in_database_batch_display_process_files(array &$context) {
  if (empty($context['sandbox'])) {
    $context['sandbox'] = array();
    if (empty($context['results']['file_list'])) {
      $context['sandbox']['progress'] = 1;
      $context['sandbox']['max'] = 1;
    }
    else {
      $context['sandbox']['progress'] = 0;
      $context['sandbox']['max'] = count($context['results']['file_list']);
      $context['sandbox']['date_format'] = variable_get('auditfiles_report_options_date_format', 'long');

      // Get the path of the currently used file scheme.
      $file_system_stream = variable_get('auditfiles_file_system_path', 'public');
      $context['sandbox']['real_path'] = drupal_realpath($file_system_stream . '://');
    }
  }
  if (empty($context['results']['files_to_display'])) {
    $context['results']['files_to_display'] = array();
  }
  if (!empty($context['results']['file_list'])) {
    $file_list = array_slice($context['results']['file_list'], 0, 20, TRUE);
    foreach ($file_list as $file_id => $filepathname) {

      // See if the file is in the database.
      if (!_auditfiles_not_in_database_is_file_in_database($filepathname)) {

        // The file is not in the database, so save the file information for the
        // report.
        $context['results']['files_to_display'][$filepathname] = _auditfiles_not_in_database_format_row_data($filepathname, $context['sandbox']['real_path'], $context['sandbox']['date_format']);
      }
      unset($context['results']['file_list'][$file_id]);

      // Update the progress information.
      $context['sandbox']['progress']++;
      $context['message'] = t('Processing the file list. Processed file @num1 of @num2. Last file processed: !file_name.', array(
        '@num1' => $context['sandbox']['progress'],
        '@num2' => $context['sandbox']['max'],
        '!file_name' => $file_id,
      ));
    }
  }
  if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
    $context['finished'] = $context['sandbox']['progress'] >= $context['sandbox']['max'];
  }
}

/**
 * The following are functions for the batch add & delete operations.
 */

/**
 * Presents a confimation form to verify the user wants to complete the action.
 *
 * @param array $form
 *   The form definition.
 * @param array $form_state
 *   The current state of the form.
 *
 * @return array
 *   A form array for a confirmation form.
 */
function _auditfiles_not_in_database_confirm_operation(array $form, array &$form_state) {
  $values = $form_state['values'];
  $form['changelist'] = array(
    '#prefix' => '<ul>',
    '#suffix' => '</ul>',
    '#tree' => TRUE,
  );

  // Prepare the list of items to present to the user.
  if (!empty($values['files'])) {
    foreach ($values['files'] as $filename) {
      if (!empty($filename)) {
        if ($values['op'] == t('Add selected items to the database')) {
          $message = t('will be added to the database.');
        }
        elseif ($values['op'] == t('Delete selected items from the server')) {
          $message = t('will be deleted from the server.');
        }
        $form['changelist'][$filename] = array(
          '#type' => 'hidden',
          '#value' => $filename,
          '#prefix' => '<li><strong>' . $filename . '</strong> ' . $message,
          '#suffix' => "</li>\n",
        );
      }
      else {

        // Unsetting the unprocessed files prevents confirm_submit from dealing
        // with them.
        unset($form_state['values']['files'][$filename]);
      }
    }
  }
  $form['operation'] = array(
    '#type' => 'hidden',
  );
  if ($values['op'] == t('Add selected items to the database')) {
    $form['operation']['#value'] = 'add';
    $confirm_question = t('Add these files to the database?');
    $confirm_description = '';
  }
  elseif ($values['op'] == t('Delete selected items from the server')) {
    $form['operation']['#value'] = 'delete';
    $confirm_question = t('Delete these files from the server?');
    $confirm_description = '<strong>' . t('This action cannot be undone.') . '</strong>';
  }

  // Tell the submit handler to process the confirmation.
  $form['process'] = array(
    '#type' => 'hidden',
    '#value' => 'TRUE',
  );

  // Go back to the main form, when done with this one.
  $form['destination'] = array(
    '#type' => 'hidden',
    '#value' => 'admin/reports/auditfiles/notindatabase',
  );
  return confirm_form($form, $confirm_question, 'admin/reports/auditfiles/notindatabase', $confirm_description, t('Yes'), t('No'));
}

/**
 * Creates the batch for adding files to the database.
 *
 * @param array $fileids
 *   The list of file IDs to be processed.
 *
 * @return array
 *   The definition of the batch.
 */
function _auditfiles_not_in_database_batch_add_create_batch(array $fileids) {
  $batch = _auditfiles_not_in_database_batch_set_common_values();
  $batch['title'] = t('Adding files to Drupal file management');
  $operations = array();

  // Remove all the empty values from the array.
  $file_ids = array();
  foreach ($fileids as $file_id) {
    if (!empty($file_id)) {
      $file_ids[] = $file_id;
    }
  }

  // Fill in the $operations variable.
  foreach ($file_ids as $file_id) {
    $operations[] = array(
      '_auditfiles_not_in_database_batch_add_process_batch',
      array(
        $file_id,
      ),
    );
  }
  $batch['operations'] = $operations;
  return $batch;
}

/**
 * The batch process for adding the file.
 *
 * @param string $filename
 *   The full pathname to the file to add to the database.
 * @param array $context
 *   Used by the Batch API to keep track of data and pass it from one operation
 *   to the next.
 */
function _auditfiles_not_in_database_batch_add_process_batch($filename, array &$context) {

  // Process the current file.
  _auditfiles_not_in_database_batch_add_process_file($filename);

  // The contents of 'results' are available as $results in the 'finished'
  // function.
  $context['results'][] = $filename;

  // Set a progress message.
  $context['message'] = t('Processed %filename.', array(
    '%filename' => $filename,
  ));
}

/**
 * Adds the specified file to the database.
 *
 * @param string $filepathname
 *   The full pathname to the file to add to the database.
 */
function _auditfiles_not_in_database_batch_add_process_file($filepathname) {
  global $user;
  $file = new stdClass();
  $file->uid = $user->uid;
  $file->filename = trim(basename($filepathname));
  $file->uri = file_build_uri($filepathname);

  // Get the full system file path name, so that the following two operations
  // will work.
  $real_filenamepath = drupal_realpath($file->uri);
  $file->filemime = file_get_mimetype($real_filenamepath);
  $file->filesize = filesize($real_filenamepath);
  $file->status = FILE_STATUS_PERMANENT;
  $file->timestamp = REQUEST_TIME;

  // Make sure the file is not already in the database.
  $query = 'SELECT fid FROM {file_managed} WHERE uri = :uri';
  $existing_file = db_query($query, array(
    ':uri' => $file->uri,
  ))
    ->fetchAll();
  if (empty($existing_file)) {

    // The file is not already in the database, so add it.
    $results = drupal_write_record('file_managed', $file);
    if (empty($results)) {
      drupal_set_message(t('Failed to add %file to the database.', array(
        '%file' => $filepathname,
      )));
    }
    else {
      drupal_set_message(t('Sucessfully added %file to the database.', array(
        '%file' => $filepathname,
      )));

      // Remove the file from the list of files to display.
      $rows = variable_get('auditfiles_not_in_database_files_to_display', array());
      unset($rows[$filepathname]);
      variable_set('auditfiles_not_in_database_files_to_display', $rows);
    }
  }
  else {
    drupal_set_message(t('The file %file is already in the database.', array(
      '%file' => $filepathname,
    )), 'error');
  }
}

/**
 * Creates the batch for deleting files from the server.
 *
 * @param array $file_names
 *   The list of file names to be processed.
 *
 * @return array
 *   The definition of the batch.
 */
function _auditfiles_not_in_database_batch_delete_create_batch(array $file_names) {
  $batch = _auditfiles_not_in_database_batch_set_common_values();
  $batch['title'] = t('Deleting files from the server');
  $operations = array();

  // Remove all the empty values from the array.
  $filenames = array();
  foreach ($file_names as $file_name) {
    if (!empty($file_name)) {
      $filenames[] = $file_name;
    }
  }

  // Fill in the $operations variable.
  foreach ($filenames as $filename) {
    $operations[] = array(
      '_auditfiles_not_in_database_batch_delete_process_batch',
      array(
        $filename,
      ),
    );
  }
  $batch['operations'] = $operations;
  return $batch;
}

/**
 * The batch process for deleting the file.
 *
 * @param string $filename
 *   The filename to delete.
 * @param array $context
 *   Used by the Batch API to keep track of data and pass it from one operation
 *   to the next.
 */
function _auditfiles_not_in_database_batch_delete_process_batch($filename, array &$context) {

  // Process the current file.
  _auditfiles_not_in_database_batch_delete_process_file($filename);

  // The contents of 'results' are available as $results in the 'finished'
  // function.
  $context['results'][] = check_plain($filename);

  // Set a progress message.
  $context['message'] = t('Processed %filename.', array(
    '%filename' => $filename,
  ));
}

/**
 * Deletes the specified file from the server.
 *
 * @param string $filename
 *   The full pathname of the file to delete from the server.
 */
function _auditfiles_not_in_database_batch_delete_process_file($filename) {

  // Get the static paths necessary for processing the files.
  $file_system_stream = variable_get('auditfiles_file_system_path', 'public');

  // The full file system path to the Drupal root directory.
  $real_files_path = drupal_realpath($file_system_stream . '://');

  // Delete the file without invoking any hooks.
  if (file_unmanaged_delete($real_files_path . DIRECTORY_SEPARATOR . $filename)) {
    drupal_set_message(t('Sucessfully deleted %file from the server.', array(
      '%file' => $filename,
    )));

    // Remove the deleted files from the list of files to display.
    $rows = variable_get('auditfiles_not_in_database_files_to_display', array());
    unset($rows[$filename]);
    variable_set('auditfiles_not_in_database_files_to_display', $rows);
  }
  else {
    drupal_set_message(t('Failed to delete %file from the server.', array(
      '%file' => $filename,
    )));
  }
}

/**
 * The following are functions for retrieving and processing the file data.
 */

/**
 * Recurse directories and add files to an array.
 *
 * @return array
 *   The list of file names to display.
 */
function _auditfiles_not_in_database_get_reports_files() {

  // Set up the variables for holding the file data.
  $report_files = array();
  $reported_files = array();
  _auditfiles_not_in_database_get_files_for_report('', $report_files);
  if (!empty($report_files)) {

    // Get the static paths necessary for processing the files.
    $file_system_stream = variable_get('auditfiles_file_system_path', 'public');

    // The full file system path to the Drupal root directory.
    $real_files_path = drupal_realpath($file_system_stream . '://');

    // Get the chosen date format for displaying the file dates with.
    $date_format = variable_get('auditfiles_report_options_date_format', 'long');
    foreach ($report_files as $report_file) {

      // Check to see if the file is in the database.
      if (empty($report_file['path_from_files_root'])) {
        $file_to_check = $report_file['file_name'];
      }
      else {
        $file_to_check = $report_file['path_from_files_root'] . DIRECTORY_SEPARATOR . $report_file['file_name'];
      }
      $file_in_database = _auditfiles_not_in_database_is_file_in_database($file_to_check);

      // If the file is not in the database, add it to the list for displaying.
      if (!$file_in_database) {

        // Gets the file's information (size, date, etc.) and assempbles the
        // array for the table.
        $reported_files += _auditfiles_not_in_database_format_row_data($report_file, $real_files_path, $date_format);
      }
    }
  }
  return $reported_files;
}

/**
 * Updates a list of files with files found in the specified path.
 *
 * @param string $path
 *   The filepath to read the files from.
 * @param array $report_files
 *   The list of files to update.
 */
function _auditfiles_not_in_database_get_files_for_report($path, array &$report_files) {

  // Get the static paths necessary for processing the files.
  $file_system_stream = variable_get('auditfiles_file_system_path', 'public');

  // The full file system path to the Drupal root directory.
  $real_files_path = drupal_realpath($file_system_stream . '://');

  // If record limition has been configured, only use those records within that
  // specification.
  $maximum_records = variable_get('auditfiles_report_options_maximum_records', 250);
  if ($maximum_records > 0 && count($report_files) < $maximum_records) {
    $new_files = _auditfiles_not_in_database_get_files($path);
    if (!empty($new_files)) {
      foreach ($new_files as $file) {

        // Check if the current item is a directory or a file.
        if (empty($file['path_from_files_root'])) {
          $item_path_check = $real_files_path . DIRECTORY_SEPARATOR . $file['file_name'];
        }
        else {
          $item_path_check = $real_files_path . DIRECTORY_SEPARATOR . $file['path_from_files_root'] . DIRECTORY_SEPARATOR . $file['file_name'];
        }
        if (is_dir($item_path_check)) {

          // The item is a directory, so go into it and get any files there.
          if (empty($path)) {
            $file_path = $file['file_name'];
          }
          else {
            $file_path = $path . DIRECTORY_SEPARATOR . $file['file_name'];
          }
          _auditfiles_not_in_database_get_files_for_report($file_path, $report_files);
        }
        else {

          // The item is a file, so add it to the list.
          $file['path_from_files_root'] = _auditfiles_not_in_database_fix_path_separators($file['path_from_files_root']);
          $report_files[] = $file;
        }
      }
    }
    else {
    }
  }
}

/**
 * The following are helper functions.
 */

/**
 * Retrieves a list of files in the given path.
 *
 * @param string $path
 *   The path to search for files in.
 *
 * @return array
 *   The list of files and diretories found in the given path.
 */
function _auditfiles_not_in_database_get_files($path) {

  // Get the static paths necessary for processing the files.
  $file_system_stream = variable_get('auditfiles_file_system_path', 'public');

  // The full file system path to the Drupal root directory.
  $real_files_path = drupal_realpath($file_system_stream . '://');

  // Get the list of any excluded files, extensions, and/or directories.
  $exclusions = _auditfiles_get_exclusions();

  // The variable to store the data being returned.
  $file_list = array();
  if (empty($path)) {
    $scan_path = $real_files_path;
  }
  else {
    $scan_path = $real_files_path . DIRECTORY_SEPARATOR . $path;
  }

  // Get the files in the specified directory.
  $files = scandir($scan_path);
  foreach ($files as $file) {
    if ($file != '.' && $file != '..') {

      // Check to see if this file should be included.
      $include_file = _auditfiles_not_in_database_include_file($real_files_path . DIRECTORY_SEPARATOR . $path, $file, $exclusions);
      if ($include_file) {

        // The file is to be included, so add it to the data array.
        $file_list[] = array(
          'file_name' => $file,
          'path_from_files_root' => $path,
        );
      }
    }
  }
  return $file_list;
}

/**
 * Checks to see if the file is being included.
 *
 * @param string $path
 *   The complete file system path to the file.
 * @param string $file
 *   The name of the file being checked.
 * @param string $exclusions
 *   The list of files and directories that are not to be included in the list
 *   of files to check.
 *
 * @return bool
 *   Returns TRUE, if the path or file is being included, or FALSE, if the path
 *   or file has been excluded.
 *
 * @todo Possibly add other file streams that are on the system but not the one
 *   being checked to the exclusions check.
 */
function _auditfiles_not_in_database_include_file($path, $file, $exclusions) {

  // No exclusions have been specified, so all paths and files are included.
  if (empty($exclusions)) {
    return TRUE;
  }
  elseif (!preg_match('@' . $exclusions . '@', $file) && !preg_match('@' . $exclusions . '@', $path . DIRECTORY_SEPARATOR . $file)) {
    return TRUE;
  }

  // This path and/or file are being excluded.
  return FALSE;
}

/**
 * Merges one array of files into another.
 *
 * @param array $dest_files
 *   The existing set of files to merge the new set into.
 * @param array $source_files
 *   The new set of files to merge into the other set.
 * @param string $real_path
 *   The complete file system path to the file.
 * @param string $exclusions
 *   The list of files and directories that are not to be included in the list
 *   of files to check.
 *
 * @return array
 *   The merged file arrays.
 */
function _auditfiles_not_in_database_report_processing_merge_file_lists(array $dest_files, array $source_files, $real_path, $new_files_path, $exclusions) {
  foreach ($source_files as $source_file) {
    $include = _auditfiles_not_in_database_include_file($real_path, $source_file, $exclusions);
    if ($include) {
      if (empty($new_files_path)) {
        $dest_files[] = $source_file;
      }
      else {
        $dest_files[] = $new_files_path . DIRECTORY_SEPARATOR . $source_file;
      }
    }
  }
  return $dest_files;
}

/**
 * Corrects the separators of a file system's file path.
 *
 * Changes the separators of a file path, so they are match the ones being used
 * on the operating system the site is running on.
 *
 * @param string $path
 *   The path to correct.
 *
 * @return string
 *   The corrected path.
 */
function _auditfiles_not_in_database_fix_path_separators($path) {
  $path = preg_replace('@\\/\\/@', DIRECTORY_SEPARATOR, $path);
  $path = preg_replace('@\\\\@', DIRECTORY_SEPARATOR, $path);
  return $path;
}

/**
 * Checks if the specified file is in the database.
 *
 * @param string $filepathname
 *   The path and filename, from the "files" directory, of the file to check.
 *
 * @return bool
 *   Returns TRUE if the file was found in the database, or FALSE, if not.
 */
function _auditfiles_not_in_database_is_file_in_database($filepathname) {
  $file_uri = file_build_uri($filepathname);
  $query = "SELECT fid FROM {file_managed} WHERE uri = :file_uri";
  $fid = db_query($query, array(
    ':file_uri' => $file_uri,
  ))
    ->fetchField();
  return empty($fid) ? FALSE : TRUE;
}

/**
 * Formats data about a file for adding to a table row.
 *
 * @param array $file
 *   The name and path of the file, in an associative array.
 * @param string $real_path
 *   The full system path to Drupal's "files" directory.
 * @param string $date_format
 *   The specified system format to use when preparing a date.
 *
 * @return array
 *   The file data, formatted for adding to a table row.
 */
function _auditfiles_not_in_database_format_row_data(array $file, $real_path, $date_format) {
  $filename = $file['file_name'];
  $filepath = $file['path_from_files_root'];
  if (empty($filepath)) {
    $filepathname = $filename;
  }
  else {
    $filepathname = $filepath . DIRECTORY_SEPARATOR . $filename;
  }
  $real_filepathname = $real_path . DIRECTORY_SEPARATOR . $filepathname;
  $filemime = file_get_mimetype($real_filepathname);
  $filesize = number_format(filesize($real_filepathname));
  if (!empty($date_format)) {
    $filemodtime = format_date(filemtime($real_filepathname), $date_format);
  }

  // Format the data for the table row.
  $row_data[$filepathname] = array(
    'filepathname' => empty($filepathname) ? '' : $filepathname,
    'filemime' => empty($filemime) ? '' : $filemime,
    'filesize' => !isset($filesize) ? '' : $filesize,
    'filemodtime' => empty($filemodtime) ? '' : $filemodtime,
    'filename' => empty($filename) ? '' : $filename,
  );
  return $row_data;
}

/**
 * Returns any report limiting settings.
 *
 * Returns the information that is needed to modify the report and display based
 * on any report limiting options that may have been set on the administrative
 * configuration settings page.
 *
 * @return array
 *   An associative array with these two values:
 *   - record_selection: A string representing record selection type, on which
 *     maximum_records is based.
 *   - maximum_records: An integer representing the total number of records to
 *     display on a report. (A value of 0 means there is no limit.)
 */
function _auditfiles_not_in_database_get_record_limiters() {
  $record_selection = variable_get('auditfiles_not_in_database_record_selection', 'normal');
  $maximum_records = 0;
  if ($record_selection == 'batch_sets') {
    $maximum_records = variable_get('auditfiles_report_options_batch_size', 1000);
  }
  elseif ($record_selection != 'batched') {
    $maximum_records = variable_get('auditfiles_report_options_maximum_records', 250);
    if ($maximum_records > 0) {
      $record_selection = 'limited';
      variable_set('auditfiles_not_in_database_record_selection', 'limited');
    }
  }
  return array(
    'record_selection' => $record_selection,
    'maximum_records' => $maximum_records,
  );
}

/**
 * Returns the header to use for the display table.
 *
 * @return array
 *   The header to use.
 */
function _auditfiles_not_in_database_get_header() {
  return array(
    'filepathname' => array(
      'data' => t('File pathname'),
    ),
    'filemime' => array(
      'data' => t('MIME'),
    ),
    'filesize' => array(
      'data' => t('Size (in bytes)'),
    ),
    'filemodtime' => array(
      'data' => t('Last modified'),
    ),
  );
}

/**
 * Creates an exclusion string.
 *
 * This function creates a list of file and/or directory exclusions to be used
 * with a preg_* function.
 *
 * @return string
 *   The excluions.
 */
function _auditfiles_get_exclusions() {

  // Get the static paths necessary for processing the files.
  $file_system_stream = variable_get('auditfiles_file_system_path', 'public');

  // The full file system path to the Drupal root directory.
  $real_files_path = drupal_realpath($file_system_stream . '://');
  $user_data['real_files_path'] = $real_files_path;
  $exclusions_array = array();

  // Create the list of requested file exclusions.
  $files = trim(variable_get('auditfiles_exclude_files', '.htaccess'));
  if ($files) {
    $exclude_files = explode(';', $files);
    $user_data['make_file_path'] = FALSE;
    array_walk($exclude_files, '_auditfiles_make_preg', $user_data);
    $exclusions_array = array_merge($exclusions_array, $exclude_files);
  }

  // Create the list of requested path exclusions.
  $paths = trim(variable_get('auditfiles_exclude_paths', 'color;css;ctools;js'));
  if ($paths) {
    $exclude_paths = explode(';', $paths);
    $user_data['make_file_path'] = TRUE;
    array_walk($exclude_paths, '_auditfiles_make_preg', $user_data);
    $exclusions_array = array_merge($exclusions_array, $exclude_paths);
  }

  // Exclude other file streams that may be defined and in use.
  $exclude_streams = array();
  $auditfiles_file_system_path = variable_get('auditfiles_file_system_path', 'public');
  $file_system_paths = file_get_stream_wrappers();
  foreach ($file_system_paths as $file_system_path_id => $file_system_path) {
    if ($file_system_path_id != $auditfiles_file_system_path) {
      $uri = $file_system_path_id . '://';
      if ($wrapper = file_stream_wrapper_get_instance_by_uri($uri)) {
        $exclude_streams[] = $wrapper
          ->realpath();
      }
    }
  }
  $user_data['make_file_path'] = FALSE;
  array_walk($exclude_streams, '_auditfiles_make_preg', $user_data);
  $exclusions_array = array_merge($exclusions_array, $exclude_streams);

  // Create the list of requested extension exclusions. (This is a little more
  // complicated.)
  $extensions = trim(variable_get('auditfiles_exclude_extensions', ''));
  if ($extensions) {
    $exclude_extensions = explode(';', $extensions);
    $user_data['make_file_path'] = FALSE;
    array_walk($exclude_extensions, '_auditfiles_make_preg', $user_data);
    $extensions = implode('|', $exclude_extensions);

    // Add grouping around string & end marker and append to exlusions_array.
    $extensions = '(' . $extensions . ')$';
    $exclusions_array[] = $extensions;
  }

  // Filter out any empty exclusions.
  $exclusions_array = array_filter($exclusions_array);

  // Implode exclusions array to a string.
  $exclusions = implode('|', $exclusions_array);

  // Return prepared exclusion string.
  return $exclusions;
}

/**
 * Escapes any possible regular expression characters in the specified string.
 *
 * @param string $element
 *   The string to escape.
 * @param mixed $key
 *   The key or index for the array item passed into $element.
 * @param array $user_data
 *   Data to operate on the specified file name with. Currently, this array
 *   consists of two elements:
 *   - real_files_path: A string with the path to the file, as specified in the
 *     "File system path" setting on the administrative configuration page.
 *   - make_file_path: A boolean value, indeicating whether to change elements
 *     to file paths.
 */
function _auditfiles_make_preg(&$element, $key, array $user_data) {
  if ($user_data['make_file_path']) {
    $realpath = $user_data['real_files_path'] . '/' . $element;
    if ($realpath) {
      $element = $realpath;
    }
  }
  $element = preg_quote($element);
}

Functions

Namesort descending Description
auditfiles_not_in_database_form Generates the report.
auditfiles_not_in_database_form_submit Submit handler for the auditfiles_not_in_database_form form.
_auditfiles_get_exclusions Creates an exclusion string.
_auditfiles_make_preg Escapes any possible regular expression characters in the specified string.
_auditfiles_not_in_database_batch_add_create_batch Creates the batch for adding files to the database.
_auditfiles_not_in_database_batch_add_process_batch The batch process for adding the file.
_auditfiles_not_in_database_batch_add_process_file Adds the specified file to the database.
_auditfiles_not_in_database_batch_delete_create_batch Creates the batch for deleting files from the server.
_auditfiles_not_in_database_batch_delete_process_batch The batch process for deleting the file.
_auditfiles_not_in_database_batch_delete_process_file Deletes the specified file from the server.
_auditfiles_not_in_database_batch_display_create_batch Prepares the definition for the page display batch.
_auditfiles_not_in_database_batch_display_get_files Recurse directories and add files to an array.
_auditfiles_not_in_database_batch_display_get_operations Configures the operations for the batch process.
_auditfiles_not_in_database_batch_display_process_files Retrieves file data from the database and checks if it is on the server.
_auditfiles_not_in_database_batch_finish_batch The function that is called when the batch is completed.
_auditfiles_not_in_database_batch_set_common_values Adds vaules to a batch definition that are common to all batches in the file.
_auditfiles_not_in_database_confirm_operation Presents a confimation form to verify the user wants to complete the action.
_auditfiles_not_in_database_fix_path_separators Corrects the separators of a file system's file path.
_auditfiles_not_in_database_format_row_data Formats data about a file for adding to a table row.
_auditfiles_not_in_database_get_files Retrieves a list of files in the given path.
_auditfiles_not_in_database_get_files_for_report Updates a list of files with files found in the specified path.
_auditfiles_not_in_database_get_header Returns the header to use for the display table.
_auditfiles_not_in_database_get_record_limiters Returns any report limiting settings.
_auditfiles_not_in_database_get_reports_files Recurse directories and add files to an array.
_auditfiles_not_in_database_include_file Checks to see if the file is being included.
_auditfiles_not_in_database_is_file_in_database Checks if the specified file is in the database.
_auditfiles_not_in_database_report_processing_merge_file_lists Merges one array of files into another.