Skip to content
filefield_field.inc 9.86 KiB
Newer Older
Nate Lampton's avatar
Nate Lampton committed
// $Id$
 * FileField CCK field hooks and callbacks.
/**
 * Implementation of CCK's hook_field_settings($op = 'form').
 */
function filefield_field_settings_form($field) {
  drupal_add_js(drupal_get_path('module', 'filefield') .'/filefield.js');

    '#title' => t('List field'),
    '#options' => array(0 => t('Disabled'), 1 => t('Enabled')),
    '#default_value' => $field['list_field'] === '' ? 0 : (int) $field['list_field'],
    '#description' => t('The "list" option lets a user choose if a file should shown in a list when viewing the content after creation.'),
    '#attributes' => array('class' => 'filefield-list-field'),
  $form['list_default'] = array(
    '#type' => 'checkbox',
    '#title' => t('Files listed by default'),
    '#default_value' => $field['list_default'] === '' ? 1 : (int) $field['list_default'],
  $form['description_field'] = array(
    '#type' => 'radios',
    '#title' => t('Description field'),
    '#default_value' => $field['description_field'] === '' ? 0 : (int) $field['description_field'],
    '#options' => array(0 => t('Disabled'), 1 => t('Enabled')),
    '#description' => t('When enabled, will display a text field where users may enter a description about the uploaded file.'),
  );
/**
 * Implementation of CCK's hook_field_settings($op = 'save').
 */
function filefield_field_settings_save($field) {
  return array('list_field', 'list_default', 'description_field');
/**
 * Implementation of CCK's hook_field_settings($op = 'database_columns').
 */
function filefield_field_settings_database_columns($field) {
  return array(
    'fid' => array('type' => 'int', 'not null' => FALSE, 'views' => TRUE),
    'list' => array('type' => 'int', 'size' => 'tiny', 'not null' => FALSE, 'views' => TRUE),
    'data' => array('type' => 'text', 'serialize' => TRUE, 'views' => TRUE),
/**
 * Implementation of CCK's hook_field_settings($op = 'views_data').
 */
function filefield_field_settings_views_data($field) {
  $data = content_views_field_views_data($field);
  $db_info = content_database_info($field);
  $table_alias = content_views_tablename($field);

  // By defining the relationship, we already have a "Has file" filter
  // plus all the filters that Views already provides for files.
  // No need for having a filter by ourselves.
  unset($data[$table_alias][$field['field_name'] .'_fid']['filter']);

  // Add a relationship for related file.
  $data[$table_alias][$field['field_name'] .'_fid']['relationship'] = array(
    'base' => 'files',
    'field' => $db_info['columns']['fid']['column'],
    'handler' => 'content_handler_relationship',
    'label' => t($field['widget']['label']),
    'content_field_name' => $field['field_name'],

  // Use the Views boolean handler for filtering the list value.
  $data[$table_alias][$field['field_name'] .'_list']['filter']['handler'] = 'views_handler_filter_boolean_operator';

  // Add our special handler for serialized data.
  $data[$table_alias][$field['field_name'] .'_data']['field'] = array(
    'title' => $data[$table_alias][$field['field_name'] .'_data']['title'],
    'title short' => $data[$table_alias][$field['field_name'] .'_data']['title short'],
    'field' => $db_info['columns']['data']['column'],
    'table' => $db_info['table'],
    'handler' => 'filefield_handler_field_data',
    'click sortable' => FALSE,
    'access callback' => 'content_access',
    'access arguments' => array('view', $field),
  );

  // Remove handlers that are probably unnecessary.
  unset($data[$table_alias][$field['field_name'] .'_data']['filter']);
  unset($data[$table_alias][$field['field_name'] .'_data']['argument']);
  unset($data[$table_alias][$field['field_name'] .'_list']['argument']);

 * Implementation of CCK's hook_field($op = 'load').
 */
function filefield_field_load($node, $field, &$items, $teaser, $page) {
  if (empty($items)) {
    return array();
  }
  foreach ($items as $delta => $item) {
    // Despite hook_content_is_empty(), CCK still doesn't filter out
    // empty items from $op = 'load', so we need to do that ourselves.
    if (empty($item['fid']) || !($file = field_file_load($item['fid']))) {
      // Temporary fix to unserialize data serialized multiple times.
      // See the FileField issue http://drupal.org/node/402860.
      // And the CCK issue http://drupal.org/node/407446.
      while (!empty($item['data']) && is_string($item['data'])) {
        $item['data'] = unserialize($item['data']);
      }
      $items[$delta] = array_merge($item, $file);
    }
  }
  return array($field['field_name'] => $items);
}

/**
 * Implementation of CCK's hook_field($op = 'insert').
 */
function filefield_field_insert($node, $field, &$items, $teaser, $page) {
  return filefield_field_update($node, $field, $items, $teaser, $page);
}

/**
 * Implementation of CCK's hook_field($op = 'update').
 */
function filefield_field_update($node, $field, &$items, $teaser, $page) {

  // Accumulator to gather current fid to compare with the original node
  // for deleting replaced files.
  $curfids = array();
  foreach ($items as $delta => $item) {
    $items[$delta] = field_file_save($node, $item);
    // Remove items from the array if they have been deleted.
    if (empty($items[$delta]) || empty($items[$delta]['fid'])) {
      $items[$delta] = NULL;
    }
    else {
      $curfids[] = $items[$delta]['fid'];
    }
  // If this is a new node there are no old items to worry about.
  // Delete items from original node if no new revision was created.
  $orig = node_load($node->nid); 
  // If there are, figure out which ones must go.
  if (empty($node->revision) && !empty($orig->$field['field_name'])) {
    foreach ($orig->$field['field_name'] as $oitem) {
      if (isset($oitem['fid']) && !in_array($oitem['fid'], $curfids)) {
        // For hook_file_references, remember that this is being deleted.
        $oitem['field_name'] = $field['field_name'];
/**
 * Implementation of CCK's hook_field($op = 'delete_revision').
 */
function filefield_field_delete_revision($node, $field, &$items, $teaser, $page) {
  foreach ($items as $delta => $item) {
    // For hook_file_references, remember that this is being deleted.
    $item['field_name'] = $field['field_name'];
    if (filefield_field_delete_file($item, $field)) {
/**
 * Implementation of CCK's hook_field($op = 'delete').
 */
function filefield_field_delete($node, $field, &$items, $teaser, $page) {
  foreach ($items as $delta => $item) {
    // For hook_file_references(), remember that this is being deleted.
    $item['field_name'] = $field['field_name'];
    // Pass in the nid of the node that is being removed so all references can
    // be counted in hook_file_references().
    $item['delete_nid'] = $node->nid;
/**
 * Check that FileField controls a file before attempting to deleting it.
 */
function filefield_field_delete_file($file, $field) {
  $file = (object) $file;

  // Remove the field_name and delete_nid properties so that references can be
  // counted including the files to be deleted.
  $field_name = isset($file->field_name) ? $file->field_name : NULL;
  $delete_nid = isset($file->delete_nid) ? $file->delete_nid : NULL;
  unset($file->field_name, $file->delete_nid);

  // To prevent FileField from deleting files it doesn't know about, check the
  // FileField reference count. Temporary files can be deleted because they
  // are not yet associated with any content at all.
  if ($file->status == 0 || filefield_get_file_reference_count($file, $field) > 0) {
    $file->field_name = $field_name;
    $file->delete_nid = $delete_nid;
    return field_file_delete($file);
  }

  // Even if the file is not deleted, return TRUE to indicate the FileField
  // record can be removed from the FileField database tables.
  return TRUE;
}

/**
 * Implementation of CCK's hook_field($op = 'sanitize').
 */
function filefield_field_sanitize($node, $field, &$items, $teaser, $page) {
  foreach ($items as $delta => $item) {
    // Cleanup $items during node preview.
    if (empty($item['fid']) || !empty($item['delete'])) {
      // Check for default images at the widget level.
      // TODO: Provide an API to ImageField to do this itself?
      if (!empty($field['widget']['use_default_image']) && !empty($field['widget']['default_image']['filepath'])) {
        $items[$delta] = $field['widget']['default_image'];
      }
      else {
        $items[$delta] = NULL;
        continue;
      }
    }
    // Load the complete file if a filepath is not available.
    if (!empty($item['fid']) && empty($item['filepath'])) {
      $items[$delta] = array_merge($item, field_file_load($item['fid']));
    }
    // Add nid so formatters can create a link to the node.
    $items[$delta]['nid'] = $node->nid;
    // TODO: This is only necessary for Views, which doesn't call the "load"
    // $op. It might be preferable to move this to Views integration somehow.
    if (!empty($items['data']) && is_string($items[$delta]['data'])) {
      $item['data'] = unserialize($item['data']);
    }
    // Temporary fix to unserialize data serialized multiple times.
    // See the FileField issue http://drupal.org/node/402860.
    // And the CCK issue http://drupal.org/node/407446.
    while (!empty($items[$delta]['data']) && is_string($items[$delta]['data'])) {
      $items[$delta]['data'] = unserialize($items[$delta]['data']);
    }

    // Verify the file exists on the server.
    if (!empty($item['filepath']) && !file_exists($item['filepath'])) {
      watchdog('filefield', 'FileField was trying to display the file %file, but it does not exist.', array('%file' => $item['filepath']));
    }