Issue #1125368 by tacituseu: hook_file_delete() called without valid
[project/filefield.git] / filefield_widget.inc
CommitLineData
9412a7cc 1<?php
a679aaae 2
9412a7cc
JP
3/**
4 * @file
90a92972 5 * This file contains CCK widget related functionality.
9412a7cc
JP
6 *
7 * Uses content.module to store the fid and field specific metadata,
90a92972 8 * and Drupal's files table to store the actual file data.
9412a7cc
JP
9 */
10
22e736e6 11/**
87d3e491 12 * Implementation of CCK's hook_widget_settings($op == 'form').
22e736e6 13 */
22e736e6
DP
14function filefield_widget_settings_form($widget) {
15 $form = array();
11e9b45d
NH
16
17 // Convert the extensions list to be a human-friendly comma-separated list.
18 $extensions = is_string($widget['file_extensions']) ? $widget['file_extensions'] : 'txt';
22e736e6
DP
19 $form['file_extensions'] = array(
20 '#type' => 'textfield',
21 '#title' => t('Permitted upload file extensions'),
11e9b45d 22 '#default_value' => $extensions,
22e736e6 23 '#size' => 64,
4217ecf5 24 '#maxlength' => 512,
22e736e6 25 '#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.'),
11e9b45d 26 '#element_validate' => array('_filefield_widget_settings_extensions_validate'),
3fdeaa24 27 '#pre_render' => array('_filefield_widget_settings_extensions_value'),
22e736e6
DP
28 '#weight' => 1,
29 );
87d3e491 30
70785e17
NH
31 $form['progress_indicator'] = array(
32 '#type' => 'radios',
33 '#title' => t('Progress indicator'),
34 '#options' => array(
35 'bar' => t('Bar with progress meter'),
36 'throbber' => t('Throbber'),
37 ),
38 '#default_value' => empty($widget['progress_indicator']) ? 'bar' : $widget['progress_indicator'],
39 '#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.'),
40 '#weight' => 5,
41 '#access' => filefield_progress_implementation(),
42 );
43
22e736e6
DP
44 $form['path_settings'] = array(
45 '#type' => 'fieldset',
46 '#title' => t('Path settings'),
e76d77f0
NH
47 '#collapsible' => TRUE,
48 '#collapsed' => TRUE,
22e736e6
DP
49 '#weight' => 6,
50 );
22e736e6
DP
51 $form['path_settings']['file_path'] = array(
52 '#type' => 'textfield',
53 '#title' => t('File path'),
54 '#default_value' => is_string($widget['file_path']) ? $widget['file_path'] : '',
af2b0845 55 '#description' => t('Optional subdirectory within the "%directory" directory where files will be stored. Do not include preceding or trailing slashes.', array('%directory' => variable_get('file_directory_path', 'files') . '/')),
22e736e6
DP
56 '#element_validate' => array('_filefield_widget_settings_file_path_validate'),
57 '#suffix' => theme('token_help', 'user'),
58 );
59
60 $form['max_filesize'] = array(
61 '#type' => 'fieldset',
62 '#title' => t('File size restrictions'),
63 '#description' => t('Limits for the size of files that a user can upload. Note that these settings only apply to newly uploaded files, whereas existing files are not affected.'),
64 '#weight' => 6,
65 '#collapsible' => TRUE,
66 '#collapsed' => TRUE,
67 );
68 $form['max_filesize']['max_filesize_per_file'] = array(
69 '#type' => 'textfield',
70 '#title' => t('Maximum upload size per file'),
71 '#default_value' => is_string($widget['max_filesize_per_file'])
72 ? $widget['max_filesize_per_file']
73 : '',
7982f68b 74 '#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()))),
22e736e6
DP
75 '#element_validate' => array('_filefield_widget_settings_max_filesize_per_file_validate'),
76 );
77 $form['max_filesize']['max_filesize_per_node'] = array(
78 '#type' => 'textfield',
79 '#title' => t('Maximum upload size per node'),
80 '#default_value' => is_string($widget['max_filesize_per_node'])
81 ? $widget['max_filesize_per_node']
82 : '',
83 '#description' => t('Specify the total size limit for all files in field on a given node. Enter a value like "512" (bytes), "80K" (kilobytes) or "50M" (megabytes) in order to restrict the total size of a node. Leave this empty if there should be no size restriction.'),
84 '#element_validate' => array('_filefield_widget_settings_max_filesize_per_node_validate'),
85 );
87d3e491 86
22e736e6
DP
87 return $form;
88}
89
87d3e491
NH
90/**
91 * Implementation of CCK's hook_widget_settings($op == 'save').
92 */
22e736e6 93function filefield_widget_settings_save($widget) {
70785e17 94 return array('file_extensions', 'file_path', 'progress_indicator', 'max_filesize_per_file', 'max_filesize_per_node');
22e736e6
DP
95}
96
3fdeaa24
NH
97/**
98 * A FAPI #pre_render() function to set a cosmetic default value for extensions.
99 */
100function _filefield_widget_settings_extensions_value($element) {
2d3b6a20 101 $element['#value'] = implode(', ', array_filter(explode(' ', str_replace(',', ' ', $element['#value']))));
3fdeaa24
NH
102 return $element;
103}
104
105/**
106 * A FAPI #element_validate callback to strip commas from extension lists.
107 */
11e9b45d 108function _filefield_widget_settings_extensions_validate($element, &$form_state) {
2d3b6a20 109 // Remove commas and leading dots from file extensions.
11e9b45d 110 $value = str_replace(',', ' ', $element['#value']);
2d3b6a20 111 $value = str_replace(' .', ' ', $value);
11e9b45d
NH
112 $value = array_filter(explode(' ', $value));
113 $value = implode(' ', $value);
114 form_set_value($element, $value, $form_state);
115}
116
22e736e6
DP
117function _filefield_widget_settings_file_path_validate($element, &$form_state) {
118 // Strip slashes from the beginning and end of $widget['file_path']
119 $form_state['values']['file_path'] = trim($form_state['values']['file_path'], '\\/');
5322fc46
NH
120
121 // Do not allow the file path to be the same as the file_directory_path().
122 // This causes all sorts of problems with things like file_create_url().
123 if (strpos($form_state['values']['file_path'], file_directory_path()) === 0) {
124 form_error($element, t('The file path (@file_path) cannot start with the system files directory (@files_directory), as this may cause conflicts when building file URLs.', array('@file_path' => $form_state['values']['file_path'], '@files_directory' => file_directory_path())));
125 }
22e736e6
DP
126}
127
128function _filefield_widget_settings_max_filesize_per_file_validate($element, &$form_state) {
129 if (empty($form_state['values']['max_filesize_per_file'])) {
130 return; // Empty means no size restrictions, so don't throw an error.
131 }
87d3e491
NH
132 elseif (!is_numeric(parse_size($form_state['values']['max_filesize_per_file']))) {
133 form_error($element, t('The "@field" option must contain a valid value. You can either leave the text field empty or enter a string like "512" (bytes), "80K" (kilobytes) or "50M" (megabytes).', array('@field' => t('Maximum upload size per file'))));
22e736e6
DP
134 }
135}
136
137function _filefield_widget_settings_max_filesize_per_node_validate($element, &$form_state) {
138 if (empty($form_state['values']['max_filesize_per_node'])) {
139 return; // Empty means no size restrictions, so don't throw an error.
140 }
87d3e491
NH
141 elseif (!is_numeric(parse_size($form_state['values']['max_filesize_per_node']))) {
142 form_error($element, t('The "@field" option must contain a valid value. You can either leave the text field empty or enter a string like "512" (bytes), "80K" (kilobytes) or "50M" (megabytes).', array('@field' => t('Maximum upload size per node'))));
22e736e6
DP
143 }
144}
145
bf602673 146/**
147 * Determine the widget's files directory
148 *
87d3e491
NH
149 * @param $field
150 * A CCK field array.
25f2014e
NH
151 * @param $account
152 * The user account object to calculate the file path for.
87d3e491
NH
153 * @return
154 * The files directory path, with any tokens replaced.
bf602673 155 */
25f2014e
NH
156function filefield_widget_file_path($field, $account = NULL) {
157 $account = isset($account) ? $account : $GLOBALS['user'];
158 $dest = $field['widget']['file_path'];
14907c69
NH
159 // Replace user level tokens.
160 // Node level tokens require a lot of complexity like temporary storage
161 // locations when values don't exist. See the filefield_paths module.
bf602673 162 if (module_exists('token')) {
25f2014e 163 $dest = token_replace($dest, 'user', $account);
bf602673 164 }
14907c69
NH
165 // Replace nasty characters in the path if possible.
166 if (module_exists('transliteration')) {
f21581e9 167 module_load_include('inc', 'transliteration');
14907c69
NH
168 $dest_array = array_filter(explode('/', $dest));
169 foreach ($dest_array as $key => $directory) {
170 $dest_array[$key] = transliteration_clean_filename($directory);
171 }
172 $dest = implode('/', $dest_array);
173 }
bf602673 174
175 return file_directory_path() .'/'. $dest;
176}
177
87d3e491
NH
178/**
179 * Given a FAPI element, save any files that may have been uploaded into it.
180 *
181 * This function should only be called during validate, submit, or
182 * value_callback functions.
183 *
184 * @param $element
185 * The FAPI element whose values are being saved.
186 */
8ffd8e3d 187function filefield_save_upload($element) {
4251bfe6 188 $upload_name = implode('_', $element['#array_parents']);
25f2014e 189 $field = content_fields($element['#field_name'], $element['#type_name']);
8ffd8e3d
DP
190
191 if (empty($_FILES['files']['name'][$upload_name])) {
192 return 0;
193 }
194
25f2014e 195 $dest = filefield_widget_file_path($field);
8ffd8e3d 196 if (!field_file_check_directory($dest, FILE_CREATE_DIRECTORY)) {
32984cd2 197 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']));
8ffd8e3d
DP
198 form_set_error($upload_name, t('The file could not be uploaded.'));
199 return 0;
200 }
201
202 if (!$file = field_file_save_upload($upload_name, $element['#upload_validators'], $dest)) {
32984cd2 203 watchdog('filefield', 'The file upload failed. %upload', array('%upload' => $upload_name));
29194778 204 form_set_error($upload_name, t('The file in the @field field was unable to be uploaded.', array('@field' => $element['#title'])));
8ffd8e3d
DP
205 return 0;
206 }
207 return $file['fid'];
208}
209
22e736e6 210/**
87d3e491 211 * The #value_callback for the filefield_widget type element.
22e736e6 212 */
d2f124d5 213function filefield_widget_value($element, $edit = FALSE) {
7e784adc 214 if (!$edit) {
692a0c14 215 $file = field_file_load($element['#default_value']['fid']);
d2f124d5
DP
216 $item = $element['#default_value'];
217 }
218 else {
93c5556a 219 $item = $edit;
692a0c14
NH
220 $field = content_fields($element['#field_name'], $element['#type_name']);
221
222 // Uploads take priority over value of fid text field.
8ffd8e3d 223 if ($fid = filefield_save_upload($element)) {
9811a70a 224 $item['fid'] = $fid;
7e784adc 225 }
01c15c1d
NH
226 // Check for #filefield_value_callback values.
227 // Because FAPI does not allow multiple #value_callback values like it does
228 // for #element_validate and #process, this fills the missing functionality
229 // to allow FileField to be extended purely through FAPI.
230 elseif (isset($element['#filefield_value_callback'])) {
231 foreach ($element['#filefield_value_callback'] as $callback) {
232 $callback($element, $item);
233 }
234 }
5322fc46 235
692a0c14 236 // Load file if the FID has changed so that it can be saved by CCK.
10ddcbdb 237 $file = isset($item['fid']) ? field_file_load($item['fid']) : NULL;
692a0c14 238
3ecf9d5c
NH
239 // If the file entry doesn't exist, don't save anything.
240 if (empty($file)) {
241 $item = array();
242 }
243
692a0c14
NH
244 // Checkboxes loose their value when empty.
245 // If the list field is present make sure its unchecked value is saved.
4a03547e 246 if (!empty($field['list_field']) && empty($edit['list'])) {
692a0c14
NH
247 $item['list'] = 0;
248 }
d2f124d5 249 }
692a0c14 250 // Merge file and item data so it is available to all widgets.
93c5556a
NH
251 if (isset($item['data']) && isset($file['data'])) {
252 $file['data'] = array_merge($item['data'], $file['data']);
253 }
6e7782b4 254 $item = array_merge($item, $file);
1bffec9e 255
6e7782b4 256 return $item;
d2f124d5
DP
257}
258
87d3e491
NH
259/**
260 * An element #process callback for the filefield_widget field type.
261 *
262 * Expands the filefield_widget type to include the upload field, upload and
263 * remove buttons, and the description field.
264 */
d2f124d5 265function filefield_widget_process($element, $edit, &$form_state, $form) {
e381a9b0 266 static $settings_added;
7e784adc 267
8ffd8e3d
DP
268 $item = $element['#value'];
269 $field_name = $element['#field_name'];
270 $delta = $element['#delta'];
3897ed27 271 $element['#theme'] = 'filefield_widget_item';
8ffd8e3d 272
6877d63b 273 $field = $form['#field_info'][$field_name];
ad6c5ff0 274
e381a9b0
NH
275 // The widget is being presented, so apply the JavaScript.
276 drupal_add_js(drupal_get_path('module', 'filefield') .'/filefield.js');
277 if (!isset($settings_added[$field_name]) && isset($element['#upload_validators']['filefield_validate_extensions'])) {
278 $settings_added[$field_name] = TRUE;
279 $settings = array(
280 'filefield' => array(
281 $field_name => $element['#upload_validators']['filefield_validate_extensions'][0],
282 ),
283 );
284 drupal_add_js($settings, 'setting');
285 }
286
3897ed27
NH
287 // Title is not necessary for each individual field.
288 if ($field['multiple'] > 0) {
289 unset($element['#title']);
290 }
291
4b58ccde
NH
292 // Set up the buttons first since we need to check if they were clicked.
293 $element['filefield_upload'] = array(
294 '#type' => 'submit',
295 '#value' => t('Upload'),
4b58ccde
NH
296 '#submit' => array('node_form_submit_build_node'),
297 '#ahah' => array( // with JavaScript
298 'path' => 'filefield/ahah/'. $element['#type_name'] .'/'. $element['#field_name'] .'/'. $element['#delta'],
299 'wrapper' => $element['#id'] .'-ahah-wrapper',
300 'method' => 'replace',
301 'effect' => 'fade',
302 ),
303 '#field_name' => $element['#field_name'],
304 '#delta' => $element['#delta'],
305 '#type_name' => $element['#type_name'],
306 '#upload_validators' => $element['#upload_validators'],
307 '#weight' => 100,
308 '#post' => $element['#post'],
309 );
310 $element['filefield_remove'] = array(
cfc7028a
NH
311 // With default CCK edit forms, $element['#parents'] is array($element['#field_name'], $element['#delta']).
312 // However, if some module (for example, flexifield) places our widget deeper in the tree, we want to
313 // use that information in constructing the button name.
314 '#name' => implode('_', $element['#parents']) .'_filefield_remove',
4b58ccde
NH
315 '#type' => 'submit',
316 '#value' => t('Remove'),
4b58ccde
NH
317 '#submit' => array('node_form_submit_build_node'),
318 '#ahah' => array( // with JavaScript
319 'path' => 'filefield/ahah/'. $element['#type_name'] .'/'. $element['#field_name'] .'/'. $element['#delta'],
320 'wrapper' => $element['#id'] .'-ahah-wrapper',
321 'method' => 'replace',
322 'effect' => 'fade',
323 ),
324 '#field_name' => $element['#field_name'],
325 '#delta' => $element['#delta'],
326 '#weight' => 101,
327 '#post' => $element['#post'],
328 );
329
330 // Because the output of this field changes depending on the button clicked,
331 // we need to ask FAPI immediately if the remove button was clicked.
332 // It's not good that we call this private function, but
333 // $form_state['clicked_button'] is only available after this #process
334 // callback is finished.
335 if (_form_button_was_clicked($element['filefield_remove'])) {
319a4dc5
NH
336 // Delete the file if it is currently unused. Note that field_file_delete()
337 // does a reference check in addition to our basic status check.
338 if (isset($edit['fid'])) {
339 $removed_file = field_file_load($edit['fid']);
340 if ($removed_file['status'] == 0) {
341 field_file_delete($removed_file);
342 }
343 }
3a35dc81 344 $item = array('fid' => 0, 'list' => $field['list_default'], 'data' => array('description' => ''));
8ffd8e3d 345 }
7e784adc 346
4b58ccde
NH
347 // Set access on the buttons.
348 $element['filefield_upload']['#access'] = empty($item['fid']);
349 $element['filefield_remove']['#access'] = !empty($item['fid']);
350
e3623331 351 // Add progress bar support to the upload if possible.
70785e17
NH
352 $progress_indicator = isset($field['widget']['progress_indicator']) ? $field['widget']['progress_indicator'] : 'bar';
353 if ($progress_indicator != 'throbber' && $implementation = filefield_progress_implementation()) {
e3623331
NH
354 $upload_progress_key = md5(mt_rand());
355
356 if ($implementation == 'uploadprogress') {
357 $element['UPLOAD_IDENTIFIER'] = array(
358 '#type' => 'hidden',
359 '#value' => $upload_progress_key,
360 '#attributes' => array('class' => 'filefield-progress'),
361 );
362 }
363 elseif ($implementation == 'apc') {
364 $element['APC_UPLOAD_PROGRESS'] = array(
365 '#type' => 'hidden',
366 '#value' => $upload_progress_key,
367 '#attributes' => array('class' => 'filefield-progress'),
368 );
369 }
370
371 // Add the upload progress callback.
372 $element['filefield_upload']['#ahah']['progress']['type'] = 'bar';
373 $element['filefield_upload']['#ahah']['progress']['path'] = 'filefield/progress/' . $upload_progress_key;
374 }
375
123172a6
NH
376 // Set the FID.
377 $element['fid'] = array(
378 '#type' => 'hidden',
379 '#value' => $item['fid'],
380 );
d2f124d5 381
6e7782b4 382 if ($item['fid'] != 0) {
e49d0f07
NH
383 $element['preview'] = array(
384 '#type' => 'markup',
385 '#value' => theme('filefield_widget_preview', $item),
386 );
d2f124d5 387 }
28b1c58e 388
0b168c78
NH
389 // Grant access to temporary files.
390 if ($item['fid'] && isset($item['status']) && $item['status'] == 0 && variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC) == FILE_DOWNLOADS_PRIVATE) {
391 $_SESSION['filefield_access'][] = $item['fid'];
392 }
393
28b1c58e
DP
394 // placeholder.. will be serialized into the data column. this is a place for widgets
395 // to put additional data.
a679aaae
NH
396 $element['data'] = array(
397 '#tree' => 'true',
398 '#access' => !empty($item['fid']),
399 );
2635f5af 400
4a03547e 401 if (!empty($field['description_field'])) {
2635f5af 402 $element['data']['description'] = array(
403 '#type' => 'textfield',
404 '#title' => t('Description'),
405 '#value' => isset($item['data']['description']) ? $item['data']['description'] : '',
994c57b0 406 '#type' => variable_get('filefield_description_type', 'textfield'),
f56c699b 407 '#maxlength' => variable_get('filefield_description_length', 128),
2635f5af 408 );
409 }
28b1c58e 410
4a03547e 411 if (!empty($field['list_field'])) {
28b1c58e 412 $element['list'] = array(
f24fc10c 413 '#type' => empty($item['fid']) ? 'hidden' : 'checkbox',
28b1c58e 414 '#title' => t('List'),
22920f33 415 '#value' => isset($item['list']) && !empty($item['fid']) ? $item['list'] : $field['list_default'],
28b1c58e
DP
416 '#attributes' => array('class' => 'filefield-list'),
417 );
418 }
35873ed9
NH
419 else {
420 $element['list'] = array(
421 '#type' => 'hidden',
422 '#value' => '1',
423 );
424 }
d2f124d5 425
8ffd8e3d
DP
426 foreach ($element['#upload_validators'] as $callback => $arguments) {
427 $help_func = $callback .'_help';
ad6bb31f
DP
428 if (function_exists($help_func)) {
429 $desc[] = call_user_func_array($help_func, $arguments);
430 }
8ffd8e3d 431 }
4251bfe6 432
8ffd8e3d 433 $element['upload'] = array(
4251bfe6 434 '#name' => 'files[' . implode('_', $element['#array_parents']) . ']',
8ffd8e3d 435 '#type' => 'file',
8ffd8e3d 436 '#description' => implode('<br />', $desc),
3897ed27 437 '#size' => 22,
3897ed27 438 '#access' => empty($item['fid']),
8ffd8e3d
DP
439 );
440
f613b75e 441 $element['#attributes']['id'] = $element['#id'] .'-ahah-wrapper';
f613b75e 442 $element['#prefix'] = '<div '. drupal_attributes($element['#attributes']) .'>';
aa3a8ca0 443 $element['#suffix'] = '</div>';
d2f124d5 444
d2f124d5
DP
445 return $element;
446}
447
87d3e491
NH
448/**
449 * An #element_validate callback for the filefield_widget field.
450 */
123172a6
NH
451function filefield_widget_validate(&$element, &$form_state) {
452 // If referencing an existing file, only allow if there are existing
453 // references. This prevents unmanaged files (outside of FileField) from
454 // being deleted if this node were to be deleted.
455 if (!empty($element['fid']['#value'])) {
456 $field = content_fields($element['#field_name'], $element['#type_name']);
457 if ($file = field_file_load($element['fid']['#value'])) {
458 $file = (object) $file;
ab6a2015 459 if ($file->status == FILE_STATUS_PERMANENT) {
6a189547 460 if (field_file_references($file) == 0) {
72b7664c 461 form_error($element, t('Referencing to the file used in the %field field is not allowed.', array('%field' => $element['#title'])));
ab6a2015 462 }
123172a6
NH
463 }
464 }
465 else {
466 form_error($element, t('The file referenced by the %field field does not exist.', array('%field' => $element['#title'])));
467 }
468 }
d2f124d5
DP
469}
470
d2f124d5
DP
471/**
472 * FormAPI theme function. Theme the output of an image field.
473 */
bad2b17f 474function theme_filefield_widget($element) {
fe4cb62e 475 $element['#id'] .= '-upload'; // Link the label to the upload field.
c6e8be79 476 return theme('form_element', $element, $element['#children']);
d2f124d5
DP
477}
478
22e736e6 479function theme_filefield_widget_preview($item) {
96f6f7a9
NH
480 // Remove the current description so that we get the filename as the link.
481 if (isset($item['data']['description'])) {
482 unset($item['data']['description']);
483 }
484
839679c0
NH
485 return '<div class="filefield-file-info">'.
486 '<div class="filename">'. theme('filefield_file', $item) .'</div>'.
487 '<div class="filesize">'. format_size($item['filesize']) .'</div>'.
488 '<div class="filemime">'. $item['filemime'] .'</div>'.
489 '</div>';
22e736e6
DP
490}
491
492function theme_filefield_widget_item($element) {
3897ed27 493 // Put the upload button directly after the upload field.
ad98c19d 494 $element['upload']['#field_suffix'] = drupal_render($element['filefield_upload']);
3897ed27
NH
495 $element['upload']['#theme'] = 'filefield_widget_file';
496
497 $output = '';
498 $output .= '<div class="filefield-element clear-block">';
499
500 if ($element['fid']['#value'] != 0) {
839679c0 501 $output .= '<div class="widget-preview">';
3897ed27
NH
502 $output .= drupal_render($element['preview']);
503 $output .= '</div>';
504 }
505
839679c0 506 $output .= '<div class="widget-edit">';
3897ed27
NH
507 $output .= drupal_render($element);
508 $output .= '</div>';
509 $output .= '</div>';
510
511 return $output;
512}
513
514/**
ab322a18
NH
515 * Custom theme function for FileField upload elements.
516 *
517 * This function allows us to put the "Upload" button immediately after the
518 * file upload field by respecting the #field_suffix property.
3897ed27
NH
519 */
520function theme_filefield_widget_file($element) {
521 $output = '';
522
523 $output .= '<div class="filefield-upload clear-block">';
524
525 if (isset($element['#field_prefix'])) {
526 $output .= $element['#field_prefix'];
527 }
528
529 _form_set_class($element, array('form-file'));
530 $output .= '<input type="file" name="'. $element['#name'] .'"'. ($element['#attributes'] ? ' '. drupal_attributes($element['#attributes']) : '') .' id="'. $element['#id'] .'" size="'. $element['#size'] ."\" />\n";
531
532 if (isset($element['#field_suffix'])) {
533 $output .= $element['#field_suffix'];
534 }
535
536 $output .= '</div>';
537
538 return theme('form_element', $element, $output);
bad2b17f 539}
d2f124d5 540
2be7e4b0 541/**
87d3e491
NH
542 * Additional #validate handler for the node form.
543 *
544 * This function checks the #required properties on file fields and calculates
545 * node upload totals for all file fields. The #required property is not
546 * properly supported on file fields by Drupal core, so we do this manually.
2be7e4b0 547 */
2be7e4b0 548function filefield_node_form_validate($form, &$form_state) {
176b5844 549 foreach ($form['#field_info'] as $field_name => $field) {
b6644f47 550 if (!(in_array($field['module'], array('imagefield', 'filefield')))) continue;
2be7e4b0 551 $empty = $field['module'] .'_content_is_empty';
e76d77f0 552 $valid = FALSE;
f92ea317 553 $total_filesize = 0;
9b86f834 554 if (!empty($form_state['values'][$field_name])) {
e76d77f0 555 foreach ($form_state['values'][$field_name] as $delta => $item) {
9b86f834
NH
556 if ($empty($item, $field)) continue;
557 $valid = TRUE;
558 $total_filesize += (int)$item['filesize'];
559 }
2be7e4b0 560 }
9412a7cc 561
4ba3fc3f 562 if (!$valid && $field['required'] && filefield_edit_access($field['type_name'], $field_name, $form['#node'])) {
f92ea317
DP
563 form_set_error($field_name, t('%title field is required.', array('%title' => $field['widget']['label'])));
564 }
565 $max_filesize = parse_size($field['widget']['max_filesize_per_node']);
5af49125 566 if ($max_filesize && $total_filesize > $max_filesize) {
7982f68b 567 form_set_error($field_name, t('Total filesize for %title, %tsize, exceeds field settings of %msize.',
f92ea317 568 array(
7e784adc 569 '%title' => $field['widget']['label'],
570 '%tsize' => format_size($total_filesize),
f92ea317
DP
571 '%msize' => format_size($max_filesize)
572 )
573 ));
574 }
2be7e4b0
DP
575 }
576}