'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,
);
return $items;
}
/**
* 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',
);
return $elements;
}
/**
* Implementation of hook_theme().
*/
function filefield_theme() {
return array(
'filefield_file_edit' => array(
'arguments' => array('element' => NULL),
),
'filefield_file_upload' => array(
'arguments' => array('element' => NULL),
),
'filefield_container_item' => 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.'),
);
return $form;
case 'validate':
break;
case 'save':
return array('force_list');
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),
);
return $columns;
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;
}
return FALSE;
}
/**
* Implementation of hook_field().
*/
function filefield_field($op, $node, $field, &$items, $teaser, $page) {
$field_name = $field['field_name'];
switch ($op) {
// Called after content.module loads default data.
case 'load':
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);
case 'insert':
case 'update':
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
break;
case 'presave':
// 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'];
}
}
break;
case 'delete revision':
foreach ($items as $delta => $item) {
if (field_file_delete($item)) {
$items[$delta] = array();
}
}
$items = array_values($items); // compact deltas
break;
case 'delete':
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(
'filefield_combo' => 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' => '
',
'#suffix' => '
',
);
// 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(
'#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);
$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',
'filefield-file-edit',
);
$widget = array(
'#type' => 'filefield_file_edit',
'#default_value' => $file,
'#field' => $field,
'#prefix' => '',
'#suffix' => '
',
);
// 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' => '',
'#suffix' => '
',
);
$widget['flags'][$field['field_name'] .'_'. $delta .'_delete'] = array(
'#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,
);
return $widget;
}
/**
* The 'process' callback for 'filefield_file_edit' form elements.
* Called after defining the form and while building it, transforms the
* barebone element array into a file editing widget.
*/
function filefield_file_edit_process($element, $edit, &$form_state, $form) {
$field = $element['#field'];
$file = $element['#value'];
// We want the child elements to be included in the value array.
$element['#tree'] = TRUE;
// Slightly crufty code to share the same 'flags' element with the "Delete"
// button and still be an independent widget.
if (empty($element['flags'])) {
$element['flags'] = array();
}
if (empty($element['flags']['#type'])) {
$element['flags'] = array_merge($element['flags'], array(
'#type' => 'markup',
'#value' => '',
'#prefix' => '',
'#suffix' => '
',
));
}
// Only show the list checkbox if files are not forced to be listed.
if (!$field['force_list']) {
$element['flags']['list'] = array(
'#type' => 'checkbox',
'#title' => t('List'),
'#default_value' => $file['list'],
);
}
$widget_info = filefield_widget_for_file($field, $file);
$form_callback = $widget_info['form callback'];
$element['edit'] = $form_callback($field, $file, $delta);
return $element;
}
/**
* 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 $widget_name => $info) {
$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(
'file_generic' => array(
'priority callback' => 'filefield_generic_priority',
'form callback' => 'filefield_generic_form',
),
);
}
/**
* Priority callback for the 'filefield_generic' widget:
* Really low priority for all files, as this is the most basic fallback.
*/
function filefield_generic_priority($field, $file) {
return -1;
}
/**
* Form callback for the 'filefield_generic' widget:
* Simply display an icon and a text field for editing the file description.
*/
function filefield_generic_form($field, $file, $delta) {
$url = file_create_url($file['filepath']);
$form = array(
'#type' => 'markup',
'#value' => '',
'#prefix' => '',
'#suffix' => '
',
);
$form['icon'] = array(
'#type' => 'item',
'#value' => theme('filefield_icon', $file),
);
$form['description'] = array(
'#type' => 'textfield',
'#default_value' => (strlen($file['description'])) ? $file['description'] : $file['filename'],
'#maxlength' => 256,
'#description' => t('Size: !size, URL: !url', array(
'!size' => format_size($file['filesize']),
'!url' => l($url, $url),
)),
);
return $form;
}
/*
function _filefield_widget_prepare_form_values($node, $field, &$items) {
$field_name = $field['field_name'];
// Attach new files.
if ($file = file_check_upload($field_name .'_upload')) {
$file = (array)$file;
// 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);
}
// let modules massage act on the file.
foreach(module_implements('filefield') as $module) {
$function = $module .'_filefield';
$function('file_prepare', $node, $field, $file);
}
}
}*/
/*
function _filefield_widget_form($node, $field, &$items) {
$form[$field_name] = array(
'#type' => 'fieldset',
'#title' => t($field['widget']['label']),
'#description' => t('Changes made to the attachments are not permanent until you save this post.'),
);
return $form;
}
*/
/**
* 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);
}
function theme_filefield_icon($file) {
$dashed_mime = check_plain(strtr($file['filemime'], array('/' => '-')));
if ($icon_url = filefield_icon_url($file)) {
$icon = '';
}
return ''. $icon .'
';
}
function theme_filefield($file) {
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 ''. $icon . l($desc, $url) .'
';
}
return '';
}
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) {
$dirs[] = $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;
}
}
return TRUE;
}
/**
* 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");
return $tokens;
}
}
/**
* 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'];
return $tokens;
}
}
/**
* Determine the most appropriate icon for the given file's mimetype.
*
* @return The URL of the icon image file, or FALSE if no icon could be found.
*/
function filefield_icon_url($file) {
global $base_url;
$theme = variable_get('filefield_icon_theme', 'protocons');
if ($iconpath = _filefield_icon_path($file, $theme)) {
return $base_url .'/'. $iconpath;
}
return FALSE;
}
function _filefield_icon_path($file, $theme = 'protocons') {
// If there's an icon matching the exact mimetype, go for it.
$dashed_mime = strtr($file['filemime'], array('/' => '-'));
if ($iconpath = _filefield_create_icon_path($dashed_mime, $theme)) {
return $iconpath;
}
// For a couple of mimetypes, we can "manually" tell a generic icon.
if ($generic_name = _filefield_generic_icon_map($file)) {
if ($iconpath = _filefield_create_icon_path($generic_name, $theme)) {
return $iconpath;
}
}
// Use generic icons for each category that provides such icons.
foreach (array('audio', 'image', 'text', 'video') as $category) {
if (strpos($file['filemime'], $category .'/') === 0) {
if ($iconpath = _filefield_create_icon_path($category .'-x-generic', $theme)) {
return $iconpath;
}
}
}
// Try application-octet-stream as last fallback.
if ($iconpath = _filefield_create_icon_path('application-octet-stream', $theme)) {
return $iconpath;
}
// Sorry, no icon can be found...
return FALSE;
}
function _filefield_create_icon_path($iconname, $theme = 'protocons') {
$iconpath = drupal_get_path('module', 'filefield')
.'/icons/'. $theme .'/16x16/mimetypes/'. $iconname .'.png';
if (file_exists($iconpath)) {
return $iconpath;
}
return FALSE;
}
function _filefield_generic_icon_map($file) {
switch ($file['filemime']) {
// Word document types.
case 'application/msword':
case 'application/vnd.ms-word.document.macroEnabled.12':
case 'application/vnd.oasis.opendocument.text':
case 'application/vnd.oasis.opendocument.text-template':
case 'application/vnd.oasis.opendocument.text-master':
case 'application/vnd.oasis.opendocument.text-web':
case 'application/vnd.openxmlformats-officedocument.wordprocessingml.document':
case 'application/vnd.stardivision.writer':
case 'application/vnd.sun.xml.writer':
case 'application/vnd.sun.xml.writer.template':
case 'application/vnd.sun.xml.writer.global':
case 'application/vnd.wordperfect':
case 'application/x-abiword':
case 'application/x-applix-word':
case 'application/x-kword':
case 'application/x-kword-crypt':
return 'x-office-document';
// Spreadsheet document types.
case 'application/vnd.ms-excel':
case 'application/vnd.ms-excel.sheet.macroEnabled.12':
case 'application/vnd.oasis.opendocument.spreadsheet':
case 'application/vnd.oasis.opendocument.spreadsheet-template':
case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':
case 'application/vnd.stardivision.calc':
case 'application/vnd.sun.xml.calc':
case 'application/vnd.sun.xml.calc.template':
case 'application/vnd.lotus-1-2-3':
case 'application/x-applix-spreadsheet':
case 'application/x-gnumeric':
case 'application/x-kspread':
case 'application/x-kspread-crypt':
return 'x-office-spreadsheet';
// Presentation document types.
case 'application/vnd.ms-powerpoint':
case 'application/vnd.ms-powerpoint.presentation.macroEnabled.12':
case 'application/vnd.oasis.opendocument.presentation':
case 'application/vnd.oasis.opendocument.presentation-template':
case 'application/vnd.openxmlformats-officedocument.presentationml.presentation':
case 'application/vnd.stardivision.impress':
case 'application/vnd.sun.xml.impress':
case 'application/vnd.sun.xml.impress.template':
case 'application/x-kpresenter':
return 'x-office-presentation';
// Compressed archive types.
case 'application/zip':
case 'application/x-zip':
case 'application/stuffit':
case 'application/x-stuffit':
case 'application/x-7z-compressed':
case 'application/x-ace':
case 'application/x-arj':
case 'application/x-bzip':
case 'application/x-bzip-compressed-tar':
case 'application/x-compress':
case 'application/x-compressed-tar':
case 'application/x-cpio-compressed':
case 'application/x-deb':
case 'application/x-gzip':
case 'application/x-java-archive':
case 'application/x-lha':
case 'application/x-lhz':
case 'application/x-lzop':
case 'application/x-rar':
case 'application/x-rpm':
case 'application/x-tzo':
case 'application/x-tar':
case 'application/x-tarz':
case 'application/x-tgz':
return 'package-x-generic';
// Script file types.
case 'application/ecmascript':
case 'application/javascript':
case 'application/mathematica':
case 'application/vnd.mozilla.xul+xml':
case 'application/x-asp':
case 'application/x-awk':
case 'application/x-cgi':
case 'application/x-csh':
case 'application/x-m4':
case 'application/x-perl':
case 'application/x-php':
case 'application/x-ruby':
case 'application/x-shellscript':
case 'text/vnd.wap.wmlscript':
case 'text/x-emacs-lisp':
case 'text/x-haskell':
case 'text/x-literate-haskell':
case 'text/x-lua':
case 'text/x-makefile':
case 'text/x-matlab':
case 'text/x-python':
case 'text/x-sql':
case 'text/x-tcl':
return 'text-x-script';
// HTML aliases.
case 'application/xhtml+xml':
return 'text-html';
// Executable types.
case 'application/x-macbinary':
case 'application/x-ms-dos-executable':
case 'application/x-pef-executable':
return 'application-x-executable';
default:
return FALSE;
}
}