Index: filefield_sources.module =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/filefield_sources/filefield_sources.module,v retrieving revision 1.8 diff -u -r1.8 filefield_sources.module --- filefield_sources.module 7 Aug 2010 19:28:25 -0000 1.8 +++ filefield_sources.module 9 Aug 2010 04:09:04 -0000 @@ -229,6 +229,7 @@ function filefield_sources_info() { $info = module_invoke_all('filefield_sources_info'); drupal_alter('filefield_sources_info', $info); + uasort($info, '_filefield_sources_sort'); return $info; } @@ -280,6 +281,45 @@ } /** + * Clean up the file name, munging extensions and transliterating. + * + * @param $filepath + * A string containing a file name or full path. Only the file name will + * actually be modified. + * @return + * A file path with a cleaned-up file name. + */ +function filefield_sources_clean_filename($filepath) { + global $user; + + $filename = basename($filepath); + + if (module_exists('transliteration')) { + module_load_include('inc', 'transliteration'); + + $langcode = NULL; + if (!empty($_POST['language'])) { + $languages = language_list(); + $langcode = isset($languages[$_POST['language']]) ? $_POST['language'] : NULL; + } + $filename = transliteration_clean_filename($filename, $langcode); + } + + // Because this transfer mechanism does not use file_save_upload(), we need + // to manually munge the filename to prevent dangerous extensions. + // See file_save_upload(). + $extensions = ''; + foreach ($user->roles as $rid => $name) { + $extensions .= ' '. variable_get("upload_extensions_$rid", + variable_get('upload_extensions_default', 'jpg jpeg gif png txt html doc xls pdf ppt pps odt ods odp')); + } + $filename = file_munge_filename($filename, $extensions); + + $directory = dirname($filepath); + return ($directory ? $directory . '/' : $directory) . $filename; +} + +/** * Theme the display of the sources list. */ function theme_filefield_sources_list($element, $sources) { @@ -340,9 +380,9 @@ /** * Generate help text based on the $element['#upload_validators'] property. */ -function filefield_sources_element_validation_help($element) { +function filefield_sources_element_validation_help($validators) { $desc = array(); - foreach ($element['#upload_validators'] as $callback => $arguments) { + foreach ($validators as $callback => $arguments) { $help_func = $callback .'_help'; if (function_exists($help_func)) { $desc[] = call_user_func_array($help_func, $arguments); @@ -350,3 +390,12 @@ } return empty($desc) ? '' : implode('
', $desc); } + +/** + * Custom sort function for ordering sources. + */ +function _filefield_sources_sort($a, $b) { + $a = (array)$a + array('weight' => 0, 'label' => ''); + $b = (array)$b + array('weight' => 0, 'label' => ''); + return $a['weight'] < $b['weight'] ? -1 : ($a['weight'] > $b['weight'] ? 1 : strnatcasecmp($a['label'], $b['label'])); +} Index: filefield_sources.css =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/filefield_sources/filefield_sources.css,v retrieving revision 1.1 diff -u -r1.1 filefield_sources.css --- filefield_sources.css 17 Apr 2009 00:20:12 -0000 1.1 +++ filefield_sources.css 9 Aug 2010 04:09:04 -0000 @@ -2,9 +2,14 @@ /* Generic display for all sources. */ -div.filefield-source input.form-text { +div.filefield-source input.form-text, +div.filefield-source select.form-select { display: inline; - width: 28em; + width: 20em; +} + +div.filefield-source .form-item { + white-space: normal; } div.filefield-source input.hint { Index: sources/reference.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/filefield_sources/sources/reference.inc,v retrieving revision 1.5 diff -u -r1.5 reference.inc --- sources/reference.inc 17 Jun 2010 00:10:20 -0000 1.5 +++ sources/reference.inc 9 Aug 2010 04:09:04 -0000 @@ -23,6 +23,7 @@ 'description' => t('Reuse an existing file by entering its file name.'), 'process' => 'filefield_source_reference_process', 'value' => 'filefield_source_reference_value', + 'weight' => 1, ); return $source; } @@ -70,7 +71,8 @@ $return['sources_reference'] = array( '#title' => t('Autocomplete reference options'), '#type' => 'fieldset', - '#collapsible' => FALSE, + '#collapsible' => TRUE, + '#collapsed' => TRUE, ); $return['sources_reference']['filefield_source_autocomplete'] = array( @@ -105,7 +107,7 @@ $element['filefield_reference']['autocomplete'] = array( '#type' => 'textfield', '#autocomplete_path' => 'filefield/reference/' . $element['#type_name'] . '/' . $element['#field_name'], - '#description' => filefield_sources_element_validation_help($element), + '#description' => filefield_sources_element_validation_help($element['#upload_validators']), ); $element['filefield_reference']['select'] = array( Index: sources/remote.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/filefield_sources/sources/remote.inc,v retrieving revision 1.8 diff -u -r1.8 remote.inc --- sources/remote.inc 8 Aug 2010 22:42:29 -0000 1.8 +++ sources/remote.inc 9 Aug 2010 04:09:05 -0000 @@ -144,6 +144,7 @@ $url_info = parse_url($url); $pathinfo = pathinfo($url_info['path']); $filename = rawurldecode(basename($url_info['path'])); + $filename = filefield_sources_clean_filename($filename); $filepath = file_create_filename($filename, file_directory_temp()); if (empty($pathinfo['extension'])) { Index: sources/imce.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/filefield_sources/sources/imce.inc,v retrieving revision 1.4 diff -u -r1.4 imce.inc --- sources/imce.inc 2 Mar 2010 20:01:27 -0000 1.4 +++ sources/imce.inc 9 Aug 2010 04:09:04 -0000 @@ -32,6 +32,7 @@ 'description' => t('Select a file to use from a file browser.'), 'process' => 'filefield_source_imce_process', 'value' => 'filefield_source_imce_value', + 'weight' => -1, ); return $source; } @@ -67,10 +68,10 @@ $field = content_fields($element['#field_name'], $element['#type_name']); $element['filefield_imce'] = array( - '#weight' => 100.3, + '#weight' => 100.5, '#access' => empty($element['fid']['#value']), '#theme' => 'filefield_source_imce_element', - '#description' => filefield_sources_element_validation_help($element), + '#description' => filefield_sources_element_validation_help($element['#upload_validators']), ); $filepath_id = $element['#id'] . '-imce-path'; Index: sources/attach.inc =================================================================== RCS file: sources/attach.inc diff -N sources/attach.inc --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ sources/attach.inc 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,266 @@ + t('File attach from server directory'), + 'label' => t('File attach'), + 'description' => t('Select a file from a directory on the server.'), + 'process' => 'filefield_source_attach_process', + 'value' => 'filefield_source_attach_value', + 'weight' => 3, + ); + return $source; +} + +/** + * Implementation of hook_theme(). + */ +function filefield_source_attach_theme() { + return array( + 'filefield_source_attach_element' => array( + 'arguments' => array('element' => NULL), + 'file' => 'sources/attach.inc', + ), + ); +} + +/** + * Implementation of hook_filefield_source_settings(). + */ +function filefield_source_attach_settings($op, $field) { + $return = array(); + + if ($op == 'form') { + $return['source_attach'] = array( + '#title' => t('File attach settings'), + '#type' => 'fieldset', + '#collapsible' => TRUE, + '#collapsed' => TRUE, + '#description' => t('File attach allows for selecting a file from a directory on the server, commonly used in combination with FTP.') . ' ' . t('This file source will ignore file size checking when used.') . '', + '#element_validate' => array('_filefield_source_attach_file_path_validate'), + '#weight' => 3, + ); + $return['source_attach']['filefield_source_attach_path'] = array( + '#type' => 'textfield', + '#title' => t('File attach path'), + '#default_value' => empty($field['filefield_source_attach_path']) ? 'file_attach' : $field['filefield_source_attach_path'], + '#size' => 60, + '#maxlength' => 128, + '#description' => t('The directory within the File attach location that will contain attachable files.'), + ); + $return['source_attach']['filefield_source_attach_absolute'] = array( + '#type' => 'radios', + '#title' => t('File attach location'), + '#options' => array( + 0 => t('Within the files directory'), + 1 => t('Absolute server path'), + ), + '#default_value' => isset($field['filefield_source_attach_absolute']) ? $field['filefield_source_attach_absolute'] : 0, + '#description' => t('The File attach path may be with the files directory (%file_directory) or from the root of your server. If an absolute path is used and it does not start with a "/" your path will be relative to your site directory: %realpath.', array('%file_directory' => file_directory_path(), '%realpath' => realpath('./'))), + ); + $return['source_attach']['filefield_source_attach_mode'] = array( + '#type' => 'radios', + '#title' => t('Attach method'), + '#options' => array( + 'move' => t('Move the file directly to the final location'), + 'copy' => t('Leave a copy of the file in the attach directory'), + ), + '#default_value' => isset($field['filefield_source_attach_mode']) ? $field['filefield_source_attach_mode'] : 'move', + '#description' => t(), + ); + $return['source_attach']['tokens'] = array( + '#type' => 'markup', + '#value' => theme('token_help', 'user'), + ); + } + elseif ($op == 'save') { + $return[] = 'filefield_source_attach_path'; + $return[] = 'filefield_source_attach_absolute'; + $return[] = 'filefield_source_attach_mode'; + } + + return $return; +} + +function _filefield_source_attach_file_path_validate($element, &$form_state) { + // Strip slashes from the end of the file path. + $filepath = rtrim($element['filefield_source_attach_path']['#value'], '\\/'); + form_set_value($element['filefield_source_attach_path'], $filepath, $form_state); + + $filepath = _filefield_source_attach_directory($form_state['values']); + + // Check that the directory exists and is writable. + if (!field_file_check_directory($filepath, FILE_CREATE_DIRECTORY)) { + form_error($element['filefield_source_attach_path'], t('Specified file attach path must exist or be writable.')); + } +} + +/** + * A #process callback to extend the filefield_widget element type. + */ +function filefield_source_attach_process($element, $edit, &$form_state, $form) { + $field = content_fields($element['#field_name'], $element['#type_name']); + + $element['filefield_attach'] = array( + '#theme' => 'filefield_source_attach_element', + '#weight' => 100.5, + '#access' => empty($element['fid']['#value']), + ); + + $path = _filefield_source_attach_directory($field['widget']); + $options = _filefield_source_attach_options($path); + + $description = t('This method may be used to attach files that exceed the file size limit. Files may be attached from the %directory directory on the server, usually uploaded through FTP.', array('%directory' => realpath($path))); + + // Error messages. + if ($options === FALSE || empty($field['widget']['filefield_source_attach_path'])) { + $attach_message = t('A file attach directory could not be located.'); + $attach_description = t('Please check your settings for the %field field.', array('%field' => $field['widget']['label'])); + } + elseif (!count($options)) { + $attach_message = t('There currently no files to attach.'); + $attach_description = $description; + } + + if (isset($attach_message)) { + $element['filefield_attach']['attach_message'] = array( + '#value' => $attach_message, + ); + $element['filefield_attach']['#description'] = $attach_description; + } + else { + $validators = $element['#upload_validators']; + if (isset($validators['filefield_validate_size'])) { + unset($validators['filefield_validate_size']); + } + $description .= '
' . filefield_sources_element_validation_help($validators); + $element['filefield_attach']['filename'] = array( + '#type' => 'select', + '#options' => $options, + ); + $element['filefield_attach']['#description'] = $description; + } + + $element['filefield_attach']['attach'] = array( + '#type' => 'submit', + '#value' => $attach_message ? t('Refresh') : t('Attach'), + '#submit' => array('node_form_submit_build_node'), + '#ahah' => array( + 'path' => 'filefield/ahah/'. $element['#type_name'] .'/'. $element['#field_name'] .'/'. $element['#delta'], + 'wrapper' => $element['#id'] .'-ahah-wrapper', + 'method' => 'replace', + 'effect' => 'fade', + ), + ); + + return $element; +} + +function _filefield_source_attach_options($path) { + if (!field_file_check_directory($path, FILE_CREATE_DIRECTORY)) { + drupal_set_message(t('Specified file attach path must exist or be writable.'), 'error'); + return FALSE; + } + + $options = array(); + $file_attach = file_scan_directory($path, '.*', array('.', '..', 'CVS', '.svn'), 0, TRUE, 'filename', 0, 0); + + if (count($file_attach)) { + $options = array('' => t('-- Select file --')); + foreach ($file_attach as $filename => $fileinfo) { + $filename = basename($filename); + $options[$filename] = $filename; + } + } + + natcasesort($options); + return $options; +} + +/** + * A #filefield_value_callback function. + */ +function filefield_source_attach_value($element, &$item) { + if (!empty($item['filefield_attach']['filename'])) { + $field = content_fields($element['#field_name'], $element['#type_name']); + $attach_path = _filefield_source_attach_directory($field['widget']); + $filepath = $attach_path . '/' . $item['filefield_attach']['filename']; + + // Clean up the file name extensions and transliterate. + $original_filepath = $filepath; + $new_filepath = filefield_sources_clean_filename($filepath); + rename($filepath, $new_filepath); + $filepath = $new_filepath; + + // Run all the normal validations, minus file size restrictions. + $validators = $element['#upload_validators']; + if (isset($validators['filefield_validate_size'])) { + unset($validators['filefield_validate_size']); + } + + // Save the file to the new location. + if ($file = field_file_save_file($filepath, $validators, filefield_widget_file_path($field))) { + $item = array_merge($item, $file); + + // Delete the original file if "moving" the file instead of copying. + if (empty($field['widget']['filefield_source_attach_mode']) || $field['widget']['filefield_source_attach_mode'] !== 'copy') { + @unlink($filepath); + } + } + + // Restore the original file name if the file still exists. + if (file_exists($filepath) && $filepath != $original_filepath) { + rename($filepath, $original_filepath); + } + } + + $item['filefield_attach']['filename'] = ''; +} + +/** + * Theme the output of the autocomplete field. + */ +function theme_filefield_source_attach_element($element) { + if (isset($element['attach_message'])) { + $output = $element['attach_message']['#value']; + } + else { + $select = ''; + $size = $element['#size'] ? ' size="'. $element['filename']['#size'] .'"' : ''; + _form_set_class($element['filename'], array('form-select')); + $multiple = $element['#multiple']; + $output = ''; + } + $output .= theme('submit', $element['attach']); + $element['#type'] = 'item'; + return '
' . theme('form_element', $element, $output) . '
'; +} + +function _filefield_source_attach_directory($field, $account = NULL) { + $account = isset($account) ? $account : $GLOBALS['user']; + $path = $field['filefield_source_attach_path']; + $absolute = !empty($field['filefield_source_attach_absolute']); + + // Replace user level tokens. + // Node level tokens require a lot of complexity like temporary storage + // locations when values don't exist. See the filefield_paths module. + if (module_exists('token')) { + $path = token_replace($path, 'user', $account); + } + + return $absolute ? $path : file_directory_path() . '/' . $path; +}