'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; } }