Skip to content
filefield.module 37.6 KiB
Newer Older
 * Uses content.module to store the fid, and Drupal's {files} table
 * to store the actual file data.
include_once(drupal_get_path('module', 'filefield') .'/field_file.inc');
/**
 * Implementation of hook_menu().
 */
function filefield_menu() {
  $items['filefield/js/upload/%/%/%'] = array(
    'page callback' => 'filefield_js',
    'page arguments' => array(3, 4, 5, 'filefield_file_upload_js'),
    'access arguments' => array('view content'),
    'type' => MENU_CALLBACK,
  );
  $items['filefield/js/delete/%/%/%'] = array(
    'page callback' => 'filefield_js',
    'page arguments' => array(3, 4, 5, 'filefield_file_edit_delete_js'),
    'access arguments' => array('view content'),
    'type' => MENU_CALLBACK,
  );
 * Implementation of hook_perm().
function filefield_perm() {
  return array('view filefield uploads');
}
/**
 * Implementation of hook_elements().
 */
function filefield_elements() {
  $elements = array();
  $elements['filefield_file_upload'] = array(
    '#input' => TRUE,
    '#process' => array('filefield_file_upload_process'),
    '#value_callback' => 'filefield_file_upload_value',
    '#replaced_file' => NULL,
  );
  $elements['filefield_file_edit'] = array(
    '#input' => TRUE,
    '#process' => array('filefield_file_edit_process'),
    '#value_callback' => 'filefield_file_edit_value',
  );
  $elements['filefield_generic_edit'] = array(
    '#input' => TRUE,
    '#process' => array('filefield_generic_edit_process'),
  );
/**
 * Implementation of hook_theme().
 */
function filefield_theme() {
  return array(
    'filefield_container_item' => array(
      'arguments' => array('element' => NULL),
    ),
    'filefield_file_upload' => array(
      'arguments' => array('element' => NULL),
    ),
    'filefield_file_edit' => array(
      'arguments' => array('element' => NULL),
    ),
    'filefield_generic_edit' => array(
      'arguments' => array('element' => NULL),
    ),
    'filefield_formatter_default' => array(
      'arguments' => array('element' => NULL),
    ),
    'filefield' => array(
      'arguments' => array('file' => NULL),
    ),
    'filefield_icon' => array(
      'arguments' => array('file' => NULL),
    ),
  );
/**
 * Implementation of hook_field_info().
 */
function filefield_field_info() {
  return array(
    'file' => array(
      'label' => 'File',
      'description' => t('Store an arbitrary file.'),
    ),
  );
}

/**
 * Implementation of hook_field_settings().
 */
function filefield_field_settings($op, $field) {
  switch ($op) {
    case 'form':
      $form = array();
      $form['force_list'] = array(
        '#type' => 'checkbox',
        '#title' => t('Always list files'),
        '#default_value' => isset($field['force_list']) ? $field['force_list'] : 0,
        '#description' => t('If enabled, the "List" checkbox will be hidden and files are always shown. Otherwise, the user can choose for each file whether it should be listed or not.'),
      );
    case 'validate':

    case 'database columns':
      $columns = array(
        'fid' => array('type' => 'int', 'not null' => FALSE),
        'description' => array('type' => 'varchar', 'length' => 255, 'not null' => FALSE, 'sortable' => TRUE),
        'list' => array('type' => 'int', 'size' => 'tiny', 'not null' => FALSE),

    case 'views data':
      $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' => 'views_handler_relationship',
      );
      return $data;
 * Implementation of hook_content_is_empty().
 *
 * The result of this determines whether content.module will save
 * the value of the field.
function filefield_content_is_empty($item, $field) {
  if (empty($item['fid'])) {
    return TRUE;
function filefield_field($op, $node, $field, &$items, $teaser, $page) {
  $field_name = $field['field_name'];
    // Called after content.module loads default data.
      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']))) {
          unset($items[$delta]);
        }
        else {
          $items[$delta] = array_merge($item, $file);
      $items = array_values($items); // compact deltas
      return array($field_name => $items);
      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])) {
          unset($items[$delta]);
        }
      $items = array_values($items); // compact deltas
      // Extract previous (permanent) files from the items array that have been
      // deleted or replaced, so that insert/update can remove them properly.
      foreach ($items as $delta => $item) {
        if (!empty($item['replaced_file'])) {
          $items[] = $item['replaced_file'];
        }
      }
    case 'delete revision':
      foreach ($items as $delta => $item) {
        if (field_file_delete($item)) {
          $items[$delta] = array();
        }
      }
      $items = array_values($items); // compact deltas
      break;

      foreach ($items as $delta => $item) {
        field_file_delete($item);
      }
      break;

    case 'sanitize':
      foreach ($items as $delta => $item) {
        // Cleanup $items during node preview.
        if (empty($item['fid']) || !empty($item['delete'])) {
          unset($items[$delta]);
        }
        else {
          // 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;
        }
      }
      break;
  }
}

/**
 * Implementation of hook_widget_info().
 */
function filefield_widget_info() {
  return array(
      'label' => 'File',
      'field types' => array('file'),
      'multiple values' => CONTENT_HANDLE_CORE,
      'callbacks' => array('default value' => CONTENT_CALLBACK_CUSTOM),
/**
 * Implementation of hook_widget_settings().
 */
function filefield_widget_settings($op, $widget) {
  switch ($op) {
    case 'form':
      $form = array();
      $form['file_extensions'] = array (
        '#type' => 'textfield',
        '#title' => t('Permitted upload file extensions'),
        '#default_value' => is_string($widget['file_extensions']) ? $widget['file_extensions'] : 'txt',
        '#size' => 64,
        '#description' => t('Extensions a user can upload to this field. Separate extensions with a space and do not include the leading dot. Leaving this blank will allow users to upload a file with any extension.'),
      );
      $form['file_path'] = array(
        '#type' => 'textfield',
        '#title' => t('File path'),
        '#default_value' => is_string($widget['file_path']) ? $widget['file_path'] : '',
        '#description' => t('Optional subdirectory within the "%dir" directory where files will be stored. Do not include trailing slash.', array('%dir' => variable_get('file_directory_path', 'files'))),
        '#element_validate' => array('_filefield_widget_settings_file_path_validate'),
      );
      if (module_exists('token')) {
        $form['file_path']['#suffix'] = theme('token_help', 'user');
      }
      // Let extension modules add their settings to the form.
      foreach (module_implements('filefield_widget_settings') as $module) {
        $function = $module .'_filefield_widget_settings';
        $function('form_alter', $widget, $form);
      }
      return $form;

    case 'validate':
      module_invoke_all('filefield_widget_settings', $op, $widget, NULL);
      break;

    case 'save':
      $core_settings = array('file_extensions', 'file_path');
      $additional_settings = module_invoke_all(
        'filefield_widget_settings', $op, $widget, NULL
      );
      return array_merge($core_settings, $additional_settings);
  }
}

function _filefield_widget_settings_file_path_validate($element, &$form_state) {
  // Strip slashes from the beginning and end of $widget['file_path']
  $form_state['values']['file_path'] = trim($form_state['values']['file_path'], '\\/');
}

/**
 * Implementation of hook_widget().
 */
function filefield_widget(&$form, &$form_state, $field, $items, $delta = 0) {
  drupal_add_css(drupal_get_path('module', 'filefield') .'/filefield.css');

  if (!$file = field_file_load($items[$delta]['fid'])) {
    return filefield_file_upload_form($form, $form_state, $field, $delta, $items[$delta]);
  $file = array_merge($items[$delta], $file);
  return filefield_file_edit_form($form, $form_state, $field, $delta, $file);
/**
 * Render either an upload or edit container item so that the children elements
 * always appear inside a nice table, whatever $field['multiple'] might be.
 */
function theme_filefield_container_item($element) {
  $field = $element['#field'];
  $children = !empty($element['#children']) ? $element['#children'] : '';

  // CCK renders a nice table for multiple-value fields, that's just fine as is.
  if ($field['multiple']) {
    return theme('form_element', $element, $children);
  }
  // If the field is single-value, we still want to have a table, for the looks.
  $header = array();
  $rows = array(array($children));
  $attributes = array('class' => 'filefield-file-container-table');
  $table = theme('table', $header, $rows, $attributes);
  return theme('form_element', $element, $table);
}

/**
 * The filefield widget for not (yet) existing files.
 */
function filefield_file_upload_form(&$form, &$form_state, $field, $delta, $item = NULL) {
  $form['#attributes']['enctype'] = 'multipart/form-data';

  //drupal_add_js('misc/progress.js');
  //drupal_add_js('misc/upload.js');
  //drupal_add_js(drupal_get_path('module', 'filefield') .'/filefield.js');

  $id = 'filefield-'. $field_name_css .'-'. $delta .'-form';

  $replaced_file = (isset($item) && isset($item['replaced_file']))
                    ? $item['replaced_file'] : NULL;

  $widget = array(
    '#type'   => 'filefield_file_upload',
    '#field' => $field,
    '#delta' => $delta,
    '#replaced_file' => $replaced_file,
    '#prefix' => '<div id="'. $id .'" class="filefield-file-form"><div class="filefield-file-upload">',
    '#suffix' => '</div></div>',
  );
  // Buttons inside custom form elements are not registered by the Forms API,
  // so we make the "Upload" button a regular child element and not a part
  // of the filefield_file_upload widget.
  $widget[$field['field_name'] .'_'. $delta .'_upload'] = array(
Jakob Petsovits's avatar
Jakob Petsovits committed
    '#name' => $field['field_name'] .'_'. $delta .'_upload',
    '#type' => 'submit',
    '#value' => t('Upload'),
    '#submit' => array('filefield_file_upload_submit'), // without JavaScript
    '#ahah' => array( // with JavaScript
      'path' => 'filefield/js/upload/'. $field['field_name'] .'/'. $field['type_name'] .'/'. $delta,
      'wrapper' => $id,
      'method' => 'replace',
      'effect' => 'fade',
    ),
    '#weight' => 10,
    '#field' => $field,
    '#delta' => $delta,
  );
  return $widget;
}

/**
 * The 'process' callback for 'filefield_file_upload' form elements.
 * Called after defining the form and while building it, transforms the
 * barebone element array into a file selection widget.
 */
function filefield_file_upload_process($element, $edit, &$form_state, $form) {
  $field = $element['#field'];
  $field_name = $field['field_name'];

  // Construct the upload description out of user supplied text,
  // maximum upload file size, and (optionally) allowed extensions.
  $upload_description = t('Maximum file size: !size.', array(
    '!size' => format_size(file_upload_max_size()),
  ));
  if (!empty($field['widget']['file_extensions'])) {
    $upload_description .= ' ' . t('Allowed extensions: %ext.', array(
      '%ext' => $field['widget']['file_extensions'],
  $element[$field_name .'_'. $element['#delta']] = array(
    '#type' => 'file',
    '#title' => t('Attach new file'),
    '#description' => $upload_description,
    '#attributes' => array(
      'class' => 'filefield filefield-'. $field_name,
      'accept' => str_replace(' ', '|', trim($field['widget']['file_extensions']))
    ),
    '#weight' => -1,
    // Emulate how FAPI normalizes the _FILES array since this won't go through form_builder
    '#name' => 'files['. $field_name .'_'. $element['#delta'] .']',
  return $element;
}

/**
 * Theme function for the file upload container element.
 */
function theme_filefield_file_upload($element) {
  return theme('filefield_container_item', $element);
}

/**
 * Value callback for 'filefield_upload' form elements.
 * Uploads and validates a file if one has been specified,
 * and returns the fid of that file as result value.
 */
function filefield_file_upload_value($element, $edit = FALSE) {
  return empty($element['#value'])
    ? array('fid' => 0, 'replaced_file' => $element['#replaced_file'])
    : $element['#value'];
}

/**
 * Submit callback for the "Upload" button next to each file upload field.
 */
function filefield_file_upload_submit($form, &$form_state) {
  $field = $form_state['clicked_button']['#field'];
  $delta = $form_state['clicked_button']['#delta'];
  filefield_file_upload($form_state, $field, $delta);
  // Rebuild the form with the new uploaded-file state (hopefully).
  node_form_submit_build_node($form, $form_state);
}

/**
 * Form callback for the "Upload" button with JavaScript enabled,
 * invoked by filefield_js().
 */
function filefield_file_upload_js(&$form, &$form_state, $field, $delta) {
  // Upload the file retrieve the replacement form element, an edit form.
  $file = filefield_file_upload($form_state, $field, $delta);
  if (empty($file['fid'])) {
    return filefield_file_upload_form($form, $form_state, $field, $delta, $file);
  }
  return filefield_file_edit_form($form, $form_state, $field, $delta, $file);
}

function filefield_file_upload(&$form_state, $field, $delta) {
  $field_name = $field['field_name'];
  $file = &$form_state['values'][$field_name][$delta];
  $replaced_file = $file['replaced_file'];

  if (module_exists('token')) {
    global $user;
    $widget_file_path = token_replace($field['widget']['file_path'], 'user', $user);
  }
  else {
    $widget_file_path = $field['widget']['file_path'];
  }

  $validators = array(
    'file_validate_extensions' => array($field['widget']['file_extensions']),
  $upload_name = $field_name .'_'. $delta;
  $complete_file_path = file_directory_path() .'/'. $widget_file_path;

  if (!filefield_check_directory($widget_file_path, $upload_name)) {
    // @todo: watchdog.
    $file = array('fid' => 0, 'replaced_file' => $replaced_file);
    return $file;
  }
  if (!$file = file_save_upload($upload_name, $validators, $complete_file_path)) {
    // @todo: watchdog.
    $file = array('fid' => 0, 'replaced_file' => $replaced_file);
    return $file;
  }

  $file_default_properties = array(
    'list' => 1,
    'description' => $file->filename,
  );
  $file = array_merge($file_default_properties, (array) $file);
Jakob Petsovits's avatar
Jakob Petsovits committed
  $file['replaced_file'] = $replaced_file;
  return $file;
/**
 * The filefield widget for previously uploaded files.
 */
function filefield_file_edit_form(&$form, &$form_state, $field, $delta, $file) {
  $field_name_css = str_replace('_', '-', $field['field_name']);
  $id = 'filefield-'. $field_name_css .'-'. $delta .'-form';

  $classes = array(
    'filefield-'. $field_name_css .'-form',
    'filefield-file-form',
  );
    '#field' => $field,
    '#prefix' => '<div id="'. $id .'" class="'. implode(' ', $classes) .'"><div class="filefield-file-edit">',
    '#suffix' => '</div></div>',
  // Buttons inside custom form elements are not registered by the Forms API,
  // so we make the "Delete" button a regular child element and not a part
  // of the filefield_file_upload widget.
  $widget['flags'] = array(
    '#type' => 'markup',
    '#value' => '',
    '#prefix' => '<div class="filefield-file-edit-flags">',
  $widget['flags'][$field['field_name'] .'_'. $delta .'_delete'] = array(
Jakob Petsovits's avatar
Jakob Petsovits committed
    '#name' => $field['field_name'] .'_'. $delta .'_delete',
    '#type' => 'submit',
    '#value' => t('Delete'),
    '#submit' => array('filefield_file_edit_delete_submit'), // without JavaScript
    '#ahah' => array( // with JavaScript
      'path' => 'filefield/js/delete/'. $field['field_name'] .'/'. $field['type_name'] .'/'. $delta,
      'wrapper' => $id,
      'method' => 'replace',
      'effect' => 'fade',
    ),
    '#field' => $field,
    '#delta' => $delta,
    '#file' => $file,
  );
  // Only show the list checkbox if files are not forced to be listed.
  if (!$field['force_list']) {
      '#type' => 'checkbox',
      '#title' => t('List'),
      '#default_value' => $file['list'],
    );
  }

  $edit_widget_info = filefield_widget_for_file($field, $file);
  $widget['edit'] = array(
    '#type' => $edit_widget_info['form element'],
    '#field' => $field,
    '#delta' => $delta,
    '#default_value' => $file,
    '#prefix' => '<div class="filefield-file-edit-widget">',
    '#suffix' => '</div>',
  );
 * Theme function for the file edit container element.
function theme_filefield_file_edit($element) {
  return theme('filefield_container_item', $element);
}

/**
 * Custom value callback for file edit widgets, so that we don't need to rely
 * on a tree structure but can assemble the file to our likings.
 */
function filefield_file_edit_value($element, $edit = FALSE) {
  $file = $element['#default_value'];

  if (!is_array($edit)) {
    return $file;
  }

  $file_fixed_properties = array(
    'list'      => isset($edit['flags']['list'])
                    ? $edit['flags']['list']
                    : $file['list'],
    'delete'    => 0,
    'fid'       => $file['fid'],
    'uid'       => $file['uid'],
    'status'    => $file['status'],
    'filename'  => $file['filename'],
    'filepath'  => $file['filepath'],
    'filemime'  => $file['filemime'],
    'filesize'  => $file['filesize'],
    'timestamp' => $file['timestamp'],
  );

  if (is_array($edit['edit'])) {
    $file = array_merge($file, $edit['edit']);
  }
  $file = array_merge($file, $file_fixed_properties);

  return $file;
}

/**
 * Submit callback for the "Delete" button next to each file item.
 */
function filefield_file_edit_delete_submit($form, &$form_state) {
  $field = $form_state['clicked_button']['#field'];
  $delta = $form_state['clicked_button']['#delta'];
  filefield_file_edit_delete($form_state, $field, $delta);

  // Rebuild the form with the new deleted-file state.
  node_form_submit_build_node($form, $form_state);
}

/**
 * Form callback for the "Delete" button with JavaScript enabled,
 * invoked by filefield_js().
 */
function filefield_file_edit_delete_js(&$form, &$form_state, $field, $delta) {
  // Mark the file as deleted and retrieve the replacement form element,
  // an upload form.
  $file = filefield_file_edit_delete($form_state, $field, $delta);
  return filefield_file_upload_form($form, $form_state, $field, $delta, $file);
}

/**
 * Update the form state so that the file for the given field and delta
 * is marked as deleted.
 */
function filefield_file_edit_delete(&$form_state, $field, $delta) {
  $field_name = $field['field_name'];
  $file = &$form_state['values'][$field_name][$delta];

  if (isset($file['status']) && $file['status'] == FILE_STATUS_PERMANENT) {
    $file['delete'] = 1;
    $file = array(
      'fid' => 0,
      'replaced_file' => $file,
    );
  }
  else { // temporary file, get rid of it before it's even saved
    $empty_file = array(
      'fid' => 0,
      'replaced_file' => $file['replaced_file'], // remember permanent files from before
    );
    field_file_delete($file);
    $file = $empty_file;
  }
  return $file;
}

/**
 * Shared AHAH callback for uploads and deletions. It just differs in a few
 * unimportant details (what happens to the file, and which form is used as
 * a replacement) so these details are taken care of by a form callback.
 */
function filefield_js($field_name, $type_name, $delta, $form_callback) {
  $field = content_fields($field_name, $type_name);

  if (empty($field) || empty($_POST['form_build_id'])) {
    // Invalid request.
    print drupal_to_js(array('data' => ''));
    exit;
  }

  // Build the new form.
  $form_state = array('submitted' => FALSE);
  $form_build_id = $_POST['form_build_id'];
  $form = form_get_cache($form_build_id, $form_state);

  if (!$form) {
    // Invalid form_build_id.
    print drupal_to_js(array('data' => ''));
    exit;
  }
  // form_get_cache() doesn't yield the original $form_state,
  // but form_builder() does. Needed for retrieving the file array.
  $built_form = $form;
  $built_form_state = $form_state;
  $built_form = form_builder($_POST['form_id'], $built_form, $built_form_state);

  $form_element = $form_callback($built_form, $built_form_state, $field, $delta);

  // Add the new element at the right place in the form.
  if (module_exists('fieldgroup') && ($group_name = _fieldgroup_field_get_group($type_name, $field_name))) {
    $form[$group_name][$field_name][$delta] = $form_element;
  }
  else {
    $form[$field_name][$delta] = $form_element;
  }

  // Write the (unbuilt, updated) form back to the form cache.
  form_set_cache($form_build_id, $form, $form_state);

  // Render the form for output.
  $form += array(
    '#post' => $_POST,
    '#programmed' => FALSE,
  );
  drupal_alter('form', $form, array(), 'filefield_js');
  $form_state = array('submitted' => FALSE);
  $form = form_builder('filefield_js', $form, $form_state);
  $field_form = empty($group_name) ? $form[$field_name] : $form[$group_name][$field_name];
  $output = theme('status_messages') . drupal_render($field_form[$delta]);

  // For some reason, file uploads don't like drupal_json() with its manual
  // setting of the text/javascript HTTP header. So use this one instead.
  print drupal_to_js(array('status' => TRUE, 'data' => $output));
  exit;
/**
 * Determine which widget will be used for displaying the edit form
 * for the given file.
 */
function filefield_widget_for_file($field, $file) {
  $file_widget_info = module_invoke_all('file_widget_info');
  drupal_alter('file_widget_info', $file_widget_info);

  $compatible_widget_info = array();
  foreach ($file_widget_info as $element_name => $info) {
    $info['form element'] = $element_name;
    $priority_callback = $info['priority callback'];
    $priority = $priority_callback($field, $file);

    if (!is_numeric($priority)) {
      continue; // this widget is not interested in our file
    }
    $compatible_widget_info[$priority] = $info;
  }
  // Return the element with highest priority.
  krsort($compatible_widget_info);
  return reset($compatible_widget_info);
}

/**
 * Implementation of hook_file_widget_info().
 */
function filefield_file_widget_info() {
  return array(
    'filefield_generic_edit' => array(
      'priority callback' => 'filefield_generic_edit_priority',
/**
 * Priority callback for the 'filefield_generic' widget:
 * Really low priority for all files, as this is the most basic fallback.
 */
function filefield_generic_edit_priority($field, $file) {
 * The 'process' callback for 'filefield_generic_edit' form elements.
 * Called after defining the form and while building it, transforms the
 * barebone element array into an icon and and a text field for editing
 * the file description.
function filefield_generic_edit_process($element, $edit, &$form_state, $form) {
  $field = $element['#field'];
  $delta = $element['#delta'];
  $file = $element['#value'];
  $url = file_create_url($file['filepath']);

  $prefix = isset($element['#prefix']) ? $element['#prefix'] : '';
  $suffix = isset($element['#suffix']) ? $element['#suffix'] : '';
  $element['#prefix'] = $prefix .'<div class="filefield-generic-edit">';
  $element['#suffix'] = '</div>'. $suffix;

  $element['icon'] = array(
    '#type' => 'markup',
    '#value' => theme('filefield_icon', $file),
  );
    '#default_value' => (strlen($file['description']))
                        ? $file['description']
                        : $file['filename'],
    '#description' => t('Size: !size, URL: !url', array(
      '!size' => format_size($file['filesize']),
      '!url' => l($url, $url),
    )),
    '#required' => TRUE,
    '#prefix' => '<div class="filefield-generic-edit-description">',
    '#suffix' => '</div>',
  return $element;
}

/**
 * Theme function for the 'filefield_generic_edit' form element.
 */
function theme_filefield_generic_edit($element) {
  return theme('form_element', $element, $element['#children']);
function _filefield_widget_prepare_form_values($node, $field, &$items) {
  $field_name = $field['field_name'];
  if ($file = file_check_upload($field_name .'_upload')) {
Darrel O'Pry's avatar
Darrel O'Pry committed
    // let extended validation from other module happen so we get all error messages.
    // if you implement hook_filefield_file() return FALSE to stop the upload.
    if (empty($errors)) {
      $errors = module_invoke_all('filefield', 'file_validate', $node, $field, $file);
    }

Darrel O'Pry's avatar
Darrel O'Pry committed
    // let modules massage act on the file.
    foreach(module_implements('filefield') as $module) {
      $function =  $module .'_filefield';
      $function('file_prepare', $node, $field, $file);
    }
Darrel O'Pry's avatar
Darrel O'Pry committed
  }
function _filefield_widget_form($node, $field, &$items) {
    '#type' => 'fieldset',
    '#title' => t($field['widget']['label']),
    '#description' => t('Changes made to the attachments are not permanent until you save this post.'),
  );
 * Implementation of hook_field_formatter_info().
 */
function filefield_field_formatter_info() {
  return array(
    'default' => array(
      'label' => t('Default'),
      'field types' => array('file'),
      'multiple values' => CONTENT_HANDLE_CORE,
    ),
/**
 * Theme function for the 'default' filefield field formatter.
 */
function theme_filefield_formatter_default($element) {
  $file = $element['#item'];
  if (!$file['fid']) {
    return '';
  $field = content_fields($element['#field_name'], $element['#type_name']);
  if($field['force_list']) {
    $file['list'] = 1; // always show the files if that option is enabled
  drupal_add_css(drupal_get_path('module', 'filefield') .'/filefield.css');
  return theme('filefield', $file);
Darrel O'Pry's avatar
Darrel O'Pry committed
function theme_filefield_icon($file) {
  $dashed_mime = check_plain(strtr($file['filemime'], array('/' => '-')));
  if ($icon_url = filefield_icon_url($file)) {
    $icon = '<img class="field-icon-'. $dashed_mime .'" src="'. $icon_url .'" />';
  }
  return '<div class="filefield-icon field-icon-'. $dashed_mime .'">'. $icon .'</div>';
  if (user_access('view filefield uploads') && is_file($file['filepath']) && $file['list']) {
    $path = ($file['fid'] == 'upload')
            ? file_create_filename($file['filename'], file_create_path($field['widget']['file_path']))
            : $file['filepath'];

    $icon = theme('filefield_icon', $file);
    $url = file_create_url($path);
    $desc = $file['description'];
    return '<div class="filefield-item">'. $icon . l($desc, $url) .'</div>';
function filefield_file_download($file) {
  $file = file_create_path($file);
  $result = db_query("SELECT * FROM {files} WHERE filepath = '%s'", $file);
  if (!$file = db_fetch_object($result)) {
    // We don't really care about this file.
    return;
  }
  // @todo: check the node for this file to be referenced in a field
  // to determine if it is managed by filefield. and do the access denied part here.
  if (!user_access('view filefield uploads')) {
    // sorry you do not have the proper permissions to view filefield uploads.
    return -1;
  }

  /* @todo: D6 port - files don't have any direct connection with nodes anymore
  $node = node_load($file->nid);
  if (!node_access('view', $node)) {
    // You don't have permission to view the node
    // this file is attached to.
    return -1;

  // Well I guess you can see this file.
  $name = mime_header_encode($file->filename);
  $type = mime_header_encode($file->filemime);
  // Serve images and text inline for the browser to display rather than download.
  $disposition = ereg('^(text/|image/)', $file->filemime) ? 'inline' : 'attachment';
  return array(
    'Content-Type: '. $type .'; name='. $name,
    'Content-Length: '. $file->filesize,
    'Content-Disposition: '. $disposition .'; filename='. $name,
    'Cache-Control: private',
  );
}

/**
 * Create the file directory relative to the 'files' dir recursively for every
 * directory in the path.
 * @param $directory
 *   The directory path under files to check, such as 'photo/path/here'
 * @param $form_item
 *   An optional string containing the name of a form item that any errors
 *   will be attached to. (See field_file_check_directory() for more details.)
 */
function filefield_check_directory($directory, $form_item = NULL) {
  $directory = field_file_strip_path($directory);

  foreach (explode('/', $directory) as $dir) {
    $path = file_create_path(implode($dirs, '/'));
    if (!field_file_check_directory($path, FILE_CREATE_DIRECTORY, $form_item)) {
      watchdog('filefield', t('FileField failed to create directory (%d) at (%p).', array('%d' => $directory, '%p' => $path)), WATCHDOG_ERROR);
      return FALSE;
    }
 * Implementation of hook_token_list():
 * Provide a user readable list of filefield tokens.
function filefield_token_list($type = 'all') {
  if ($type == 'field' || $type == 'all') {
    $tokens = array();
    $tokens['file']['fid']                = t("File ID");
    $tokens['file']['description']        = t("File description");
    $tokens['file']['filename']           = t("File name");
    $tokens['file']['filepath']           = t("File path");
    $tokens['file']['filemime']           = t("File MIME type");
    $tokens['file']['filesize']           = t("File size (in bytes)");
    $tokens['file']['filesize_formatted'] = t("File size (pretty printed)");
    $tokens['file']['view']               = t("Fully formatted HTML file tag");
/**
 * Implementation of hook_token_values():
 * Provide the concrete token values for a given file item.
 */
function filefield_token_values($type, $object = NULL) {
  if ($type == 'field') {
    $item = $object[0];
    $tokens['fid']                = $item['fid'];
    $tokens['description']        = $item['description'];
    $tokens['filename']           = $item['filename'];
    $tokens['filepath']           = $item['filepath'];
    $tokens['filemime']           = $item['filemime'];
    $tokens['filesize']           = $item['filesize'];
    $tokens['filesize_formatted'] = format_size($item['filesize']);
    $tokens['view']               = $item['view'];