<?php
-// $Id$
/**
* @file
*/
function filefield_widget_settings_form($widget) {
$form = array();
+
+ // Convert the extensions list to be a human-friendly comma-separated list.
+ $extensions = is_string($widget['file_extensions']) ? $widget['file_extensions'] : 'txt';
$form['file_extensions'] = array(
'#type' => 'textfield',
'#title' => t('Permitted upload file extensions'),
- '#default_value' => is_string($widget['file_extensions']) ? $widget['file_extensions'] : 'txt',
+ '#default_value' => $extensions,
'#size' => 64,
+ '#maxlength' => 512,
'#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.'),
+ '#element_validate' => array('_filefield_widget_settings_extensions_validate'),
+ '#pre_render' => array('_filefield_widget_settings_extensions_value'),
'#weight' => 1,
);
+ $form['progress_indicator'] = array(
+ '#type' => 'radios',
+ '#title' => t('Progress indicator'),
+ '#options' => array(
+ 'bar' => t('Bar with progress meter'),
+ 'throbber' => t('Throbber'),
+ ),
+ '#default_value' => empty($widget['progress_indicator']) ? 'bar' : $widget['progress_indicator'],
+ '#description' => t('Your server supports upload progress capabilities. The "throbber" display does not indicate progress but takes up less room on the form, you may want to use it if you\'ll only be uploading small files or if experiencing problems with the progress bar.'),
+ '#weight' => 5,
+ '#access' => filefield_progress_implementation(),
+ );
+
$form['path_settings'] = array(
'#type' => 'fieldset',
'#title' => t('Path settings'),
'#default_value' => is_string($widget['max_filesize_per_file'])
? $widget['max_filesize_per_file']
: '',
- '#description' => t('Specify the size limit that applies to each file separately. Enter a value like "512" (bytes), "80K" (kilobytes) or "50M" (megabytes) in order to restrict the allowed file size. If you leave this this empty the file sizes will be limited only by PHP\'s maximum post and file upload sizes (current limit <strong>%limit</strong>).', array('%limit' => format_size(file_upload_max_size()))),
+ '#description' => t('Specify the size limit that applies to each file separately. Enter a value like "512" (bytes), "80K" (kilobytes) or "50M" (megabytes) in order to restrict the allowed file size. If you leave this empty the file sizes will be limited only by PHP\'s maximum post and file upload sizes (current limit <strong>%limit</strong>).', array('%limit' => format_size(file_upload_max_size()))),
'#element_validate' => array('_filefield_widget_settings_max_filesize_per_file_validate'),
);
$form['max_filesize']['max_filesize_per_node'] = array(
* Implementation of CCK's hook_widget_settings($op == 'save').
*/
function filefield_widget_settings_save($widget) {
- return array('file_extensions', 'file_path', 'max_filesize_per_file', 'max_filesize_per_node');
+ return array('file_extensions', 'file_path', 'progress_indicator', 'max_filesize_per_file', 'max_filesize_per_node');
+}
+
+/**
+ * A FAPI #pre_render() function to set a cosmetic default value for extensions.
+ */
+function _filefield_widget_settings_extensions_value($element) {
+ $element['#value'] = implode(', ', array_filter(explode(' ', str_replace(',', ' ', $element['#value']))));
+ return $element;
+}
+
+/**
+ * A FAPI #element_validate callback to strip commas from extension lists.
+ */
+function _filefield_widget_settings_extensions_validate($element, &$form_state) {
+ // Remove commas and leading dots from file extensions.
+ $value = str_replace(',', ' ', $element['#value']);
+ $value = str_replace(' .', ' ', $value);
+ $value = array_filter(explode(' ', $value));
+ $value = implode(' ', $value);
+ form_set_value($element, $value, $form_state);
}
function _filefield_widget_settings_file_path_validate($element, &$form_state) {
*
* @param $field
* A CCK field array.
+ * @param $account
+ * The user account object to calculate the file path for.
* @return
* The files directory path, with any tokens replaced.
*/
-function filefield_widget_file_path($field_instance) {
- $dest = $field_instance['widget']['file_path'];
+function filefield_widget_file_path($field, $account = NULL) {
+ $account = isset($account) ? $account : $GLOBALS['user'];
+ $dest = $field['widget']['file_path'];
+ // 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')) {
- global $user;
- $dest = token_replace($dest, 'user', $user);
+ $dest = token_replace($dest, 'user', $account);
+ }
+ // Replace nasty characters in the path if possible.
+ if (module_exists('transliteration')) {
+ module_load_include('inc', 'transliteration');
+ $dest_array = array_filter(explode('/', $dest));
+ foreach ($dest_array as $key => $directory) {
+ $dest_array[$key] = transliteration_clean_filename($directory);
+ }
+ $dest = implode('/', $dest_array);
}
return file_directory_path() .'/'. $dest;
* The FAPI element whose values are being saved.
*/
function filefield_save_upload($element) {
- $upload_name = $element['#field_name'] .'_'. $element['#delta'];
- $field_instance = content_fields($element['#field_name'], $element['#type_name']);
+ $upload_name = implode('_', $element['#array_parents']);
+ $field = content_fields($element['#field_name'], $element['#type_name']);
if (empty($_FILES['files']['name'][$upload_name])) {
return 0;
}
- $dest = filefield_widget_file_path($field_instance);
+ $dest = filefield_widget_file_path($field);
if (!field_file_check_directory($dest, FILE_CREATE_DIRECTORY)) {
watchdog('filefield', 'The upload directory %directory for the file field %field (content type %type) could not be created or is not accessible. A newly uploaded file could not be saved in this directory as a consequence, and the upload was canceled.', array('%directory' => $dest, '%field' => $element['#field_name'], '%type' => $element['#type_name']));
form_set_error($upload_name, t('The file could not be uploaded.'));
$item = $element['#default_value'];
}
else {
- $item = array_merge($element['#default_value'], $edit);
+ $item = $edit;
$field = content_fields($element['#field_name'], $element['#type_name']);
// Uploads take priority over value of fid text field.
}
// Load file if the FID has changed so that it can be saved by CCK.
- $file = field_file_load($item['fid']);
+ $file = isset($item['fid']) ? field_file_load($item['fid']) : NULL;
// If the file entry doesn't exist, don't save anything.
if (empty($file)) {
$item = array();
+ $file = array();
}
// Checkboxes loose their value when empty.
// If the list field is present make sure its unchecked value is saved.
- if ($field['list_field'] && empty($edit['list'])) {
+ if (!empty($field['list_field']) && empty($edit['list'])) {
$item['list'] = 0;
}
}
// Merge file and item data so it is available to all widgets.
+ if (isset($item['data']) && isset($file['data'])) {
+ $file['data'] = array_merge($item['data'], $file['data']);
+ }
$item = array_merge($item, $file);
return $item;
* remove buttons, and the description field.
*/
function filefield_widget_process($element, $edit, &$form_state, $form) {
- // The widget is being presented, so apply the JavaScript.
- drupal_add_js(drupal_get_path('module', 'filefield') .'/filefield.js');
+ static $settings_added;
$item = $element['#value'];
$field_name = $element['#field_name'];
$delta = $element['#delta'];
$element['#theme'] = 'filefield_widget_item';
- $field = content_fields($element['#field_name'], $element['#type_name']);
+ $field = $form['#field_info'][$field_name];
+
+ // The widget is being presented, so apply the JavaScript.
+ drupal_add_js(drupal_get_path('module', 'filefield') .'/filefield.js');
+ if (!isset($settings_added[$field_name]) && isset($element['#upload_validators']['filefield_validate_extensions'])) {
+ $settings_added[$field_name] = TRUE;
+ $settings = array(
+ 'filefield' => array(
+ $field_name => $element['#upload_validators']['filefield_validate_extensions'][0],
+ ),
+ );
+ drupal_add_js($settings, 'setting');
+ }
// Title is not necessary for each individual field.
if ($field['multiple'] > 0) {
$element['filefield_upload'] = array(
'#type' => 'submit',
'#value' => t('Upload'),
- '#process' => array('form_expand_ahah'),
'#submit' => array('node_form_submit_build_node'),
'#ahah' => array( // with JavaScript
'path' => 'filefield/ahah/'. $element['#type_name'] .'/'. $element['#field_name'] .'/'. $element['#delta'],
'#post' => $element['#post'],
);
$element['filefield_remove'] = array(
- '#name' => $element['#field_name'] .'_'. $element['#delta'] .'_filefield_remove',
+ // With default CCK edit forms, $element['#parents'] is array($element['#field_name'], $element['#delta']).
+ // However, if some module (for example, flexifield) places our widget deeper in the tree, we want to
+ // use that information in constructing the button name.
+ '#name' => implode('_', $element['#parents']) .'_filefield_remove',
'#type' => 'submit',
'#value' => t('Remove'),
- '#process' => array('form_expand_ahah'),
'#submit' => array('node_form_submit_build_node'),
'#ahah' => array( // with JavaScript
'path' => 'filefield/ahah/'. $element['#type_name'] .'/'. $element['#field_name'] .'/'. $element['#delta'],
// $form_state['clicked_button'] is only available after this #process
// callback is finished.
if (_form_button_was_clicked($element['filefield_remove'])) {
+ // Delete the file if it is currently unused. Note that field_file_delete()
+ // does a reference check in addition to our basic status check.
+ if (isset($edit['fid'])) {
+ $removed_file = field_file_load($edit['fid']);
+ if ($removed_file['status'] == 0) {
+ field_file_delete($removed_file);
+ }
+ }
$item = array('fid' => 0, 'list' => $field['list_default'], 'data' => array('description' => ''));
}
$element['filefield_upload']['#access'] = empty($item['fid']);
$element['filefield_remove']['#access'] = !empty($item['fid']);
+ // Add progress bar support to the upload if possible.
+ $progress_indicator = isset($field['widget']['progress_indicator']) ? $field['widget']['progress_indicator'] : 'bar';
+ if ($progress_indicator != 'throbber' && $implementation = filefield_progress_implementation()) {
+ $upload_progress_key = md5(mt_rand());
+
+ if ($implementation == 'uploadprogress') {
+ $element['UPLOAD_IDENTIFIER'] = array(
+ '#type' => 'hidden',
+ '#value' => $upload_progress_key,
+ '#attributes' => array('class' => 'filefield-progress'),
+ );
+ }
+ elseif ($implementation == 'apc') {
+ $element['APC_UPLOAD_PROGRESS'] = array(
+ '#type' => 'hidden',
+ '#value' => $upload_progress_key,
+ '#attributes' => array('class' => 'filefield-progress'),
+ );
+ }
+
+ // Add the upload progress callback.
+ $element['filefield_upload']['#ahah']['progress']['type'] = 'bar';
+ $element['filefield_upload']['#ahah']['progress']['path'] = 'filefield/progress/' . $upload_progress_key;
+ }
+
// Set the FID.
$element['fid'] = array(
'#type' => 'hidden',
);
}
+ // Grant access to temporary files.
+ if ($item['fid'] && isset($item['status']) && $item['status'] == 0 && variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC) == FILE_DOWNLOADS_PRIVATE) {
+ $_SESSION['filefield_access'][] = $item['fid'];
+ }
+
// placeholder.. will be serialized into the data column. this is a place for widgets
// to put additional data.
$element['data'] = array(
'#access' => !empty($item['fid']),
);
- if ($field['description_field']) {
+ if (!empty($field['description_field'])) {
$element['data']['description'] = array(
'#type' => 'textfield',
'#title' => t('Description'),
'#value' => isset($item['data']['description']) ? $item['data']['description'] : '',
+ '#type' => variable_get('filefield_description_type', 'textfield'),
+ '#maxlength' => variable_get('filefield_description_length', 128),
);
}
- if ($field['list_field']) {
+ if (!empty($field['list_field'])) {
$element['list'] = array(
- '#type' => 'checkbox',
+ '#type' => empty($item['fid']) ? 'hidden' : 'checkbox',
'#title' => t('List'),
- '#value' => isset($item['list']) ? $item['list'] : $field['list_default'],
+ '#value' => isset($item['list']) && !empty($item['fid']) ? $item['list'] : $field['list_default'],
'#attributes' => array('class' => 'filefield-list'),
- '#access' => !empty($item['fid']),
);
}
else {
$desc[] = call_user_func_array($help_func, $arguments);
}
}
+
$element['upload'] = array(
- '#name' => 'files['. $element['#field_name'] .'_'. $element['#delta'] .']',
+ '#name' => 'files[' . implode('_', $element['#array_parents']) . ']',
'#type' => 'file',
'#description' => implode('<br />', $desc),
'#size' => 22,
- '#attributes' => array(
- 'accept' => implode(',', array_filter(explode(' ', $field['widget']['file_extensions']))),
- ),
'#access' => empty($item['fid']),
);
if ($file = field_file_load($element['fid']['#value'])) {
$file = (object) $file;
if ($file->status == FILE_STATUS_PERMANENT) {
- // TODO: We could use field_file_references() here to reference any file
- // but hook_file_delete() needs to be implemented first.
- $references = module_invoke('filefield', 'file_references', $file);
- if ($references['filefield'] == 0) {
- form_error($element, t('Referencing to the file used the %field field is not allowed.', array('%field' => $element['#title'])));
+ if (field_file_references($file) == 0) {
+ form_error($element, t('Referencing to the file used in the %field field is not allowed.', array('%field' => $element['#title'])));
}
}
}
* FormAPI theme function. Theme the output of an image field.
*/
function theme_filefield_widget($element) {
+ $element['#id'] .= '-upload'; // Link the label to the upload field.
return theme('form_element', $element, $element['#children']);
}
* properly supported on file fields by Drupal core, so we do this manually.
*/
function filefield_node_form_validate($form, &$form_state) {
- $type = content_types($form['type']['#value']);
- foreach ($type['fields'] as $field_name => $field) {
+ foreach ($form['#field_info'] as $field_name => $field) {
if (!(in_array($field['module'], array('imagefield', 'filefield')))) continue;
$empty = $field['module'] .'_content_is_empty';
$valid = FALSE;
}
}
- if (!$valid && $field['required']) {
+ if (!$valid && $field['required'] && filefield_edit_access($field['type_name'], $field_name, $form['#node'])) {
form_set_error($field_name, t('%title field is required.', array('%title' => $field['widget']['label'])));
}
$max_filesize = parse_size($field['widget']['max_filesize_per_node']);
if ($max_filesize && $total_filesize > $max_filesize) {
- form_set_error($field_name, t('Total filesize for %title, %tsize, exceeds field settings of %msize.',
+ form_set_error($field_name, t('Total filesize for %title, %tsize, exceeds field settings of %msize.',
array(
'%title' => $field['widget']['label'],
'%tsize' => format_size($total_filesize),
}
}
}
-
-/**
- * Additional #submit handler for the node form.
- */
-function filefield_node_form_submit($form, &$form_state) {
- // Only add additional submit handling on save.
- if ($form_state['values']['op'] == t('Save')) {
- // TODO: Immediately delete temporary files that were uploaded but never
- // saved. This isn't critical to do, since any file not saved with the node
- // will by cleaned up by system_cron() after 6 hours.
- return;
- }
-}