#319339 by drewish, Don't keep rescanning files if they don't have a filefield_meta...
[project/filefield.git] / filefield_widget.inc
CommitLineData
9412a7cc
JP
1<?php
2// $Id$
3/**
4 * @file
5 * FileField: Defines a CCK file field type.
6 *
7 * Uses content.module to store the fid and field specific metadata,
8 * and Drupal's {files} table to store the actual file data.
9 *
10 * This file contains CCK widget related functionality.
11 */
12
22e736e6 13/**
7e784adc 14 * @file
15 *
22e736e6 16 * FileField Widget Settings Hooks.
6f0f10d4
DP
17 * @todo: move description property to filefield_widget widget callbacks
18 * (filefield_widget_widget_{$op}).
22e736e6
DP
19 */
20
21function filefield_widget_settings_form($widget) {
22 $form = array();
23 $form['file_extensions'] = array(
24 '#type' => 'textfield',
25 '#title' => t('Permitted upload file extensions'),
26 '#default_value' => is_string($widget['file_extensions']) ? $widget['file_extensions'] : 'txt',
27 '#size' => 64,
28 '#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.'),
29 '#weight' => 1,
30 );
31 $form['path_settings'] = array(
32 '#type' => 'fieldset',
33 '#title' => t('Path settings'),
34 '#collapsible' => true,
35 '#collapsed' => true,
36 '#weight' => 6,
37 );
38
39 $form['path_settings']['file_path'] = array(
40 '#type' => 'textfield',
41 '#title' => t('File path'),
42 '#default_value' => is_string($widget['file_path']) ? $widget['file_path'] : '',
43 '#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'))),
44 '#element_validate' => array('_filefield_widget_settings_file_path_validate'),
45 '#suffix' => theme('token_help', 'user'),
46 );
47
48 $form['max_filesize'] = array(
49 '#type' => 'fieldset',
50 '#title' => t('File size restrictions'),
51 '#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.'),
52 '#weight' => 6,
53 '#collapsible' => TRUE,
54 '#collapsed' => TRUE,
55 );
7e784adc 56 // upload validator. @todo: consider replacing with global
d6caa409 57 // && node validate.
22e736e6
DP
58 $form['max_filesize']['max_filesize_per_file'] = array(
59 '#type' => 'textfield',
60 '#title' => t('Maximum upload size per file'),
61 '#default_value' => is_string($widget['max_filesize_per_file'])
62 ? $widget['max_filesize_per_file']
63 : '',
64 '#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.'),
65 '#element_validate' => array('_filefield_widget_settings_max_filesize_per_file_validate'),
66 );
d6caa409
DP
67
68 // node validate.
22e736e6
DP
69 $form['max_filesize']['max_filesize_per_node'] = array(
70 '#type' => 'textfield',
71 '#title' => t('Maximum upload size per node'),
72 '#default_value' => is_string($widget['max_filesize_per_node'])
73 ? $widget['max_filesize_per_node']
74 : '',
75 '#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.'),
76 '#element_validate' => array('_filefield_widget_settings_max_filesize_per_node_validate'),
77 );
78 return $form;
79}
80
81function filefield_widget_settings_save($widget) {
82 return array(
83 'file_extensions', 'file_path', 'max_filesize_per_file',
84 'max_filesize_per_node', 'file_widgets'
85 );
86}
87
88function _filefield_widget_settings_file_path_validate($element, &$form_state) {
89 // Strip slashes from the beginning and end of $widget['file_path']
90 $form_state['values']['file_path'] = trim($form_state['values']['file_path'], '\\/');
91}
92
93function _filefield_widget_settings_max_filesize_per_file_validate($element, &$form_state) {
94 if (empty($form_state['values']['max_filesize_per_file'])) {
95 return; // Empty means no size restrictions, so don't throw an error.
96 }
97 else if (!is_numeric(parse_size($form_state['values']['max_filesize_per_file']))) {
98 form_error($element, t('The "Maximum file size for each file" 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).'));
99 }
100}
101
102function _filefield_widget_settings_max_filesize_per_node_validate($element, &$form_state) {
103 if (empty($form_state['values']['max_filesize_per_node'])) {
104 return; // Empty means no size restrictions, so don't throw an error.
105 }
106 else if (!is_numeric(parse_size($form_state['values']['max_filesize_per_node']))) {
107 form_error($element, t('The "Maximum file size per node" 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).'));
108 }
109}
110
bf602673 111/**
112 * Determine the widget's files directory
113 *
114 * @param $field CCK field
115 * @return files directory path.
116 */
117function filefield_widget_file_path($field_instance) {
118 $dest = $field_instance['widget']['file_path'];
119 if (module_exists('token')) {
120 global $user;
121 $dest = token_replace($dest, 'user', $user);
122 }
123
124 return file_directory_path() .'/'. $dest;
125}
126
8ffd8e3d
DP
127function filefield_save_upload($element) {
128 $upload_name = $element['#field_name'] .'_'. $element['#delta'];
129 $field_instance = content_fields($element['#field_name'], $element['#type_name']);
130
131 if (empty($_FILES['files']['name'][$upload_name])) {
132 return 0;
133 }
134
bf602673 135 $dest = filefield_widget_file_path($field_instance);
8ffd8e3d
DP
136 if (!field_file_check_directory($dest, FILE_CREATE_DIRECTORY)) {
137 watchdog('imagefield', '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']));
138 form_set_error($upload_name, t('The file could not be uploaded.'));
139 return 0;
140 }
141
142 if (!$file = field_file_save_upload($upload_name, $element['#upload_validators'], $dest)) {
143 watchdog('imagefield', 'The file upload failed. %upload', array('%upload' => $upload_name));
144 form_set_error($upload_name, t('The Image upload failed.'));
145 return 0;
146 }
147 return $file['fid'];
148}
149
22e736e6
DP
150/**
151 * FileField widget element callbacks.
152 */
d2f124d5 153function filefield_widget_value($element, $edit = FALSE) {
7e784adc 154 if (!$edit) {
d2f124d5
DP
155 $file = field_file_load($element['#default_value']['fid']);
156 $item = $element['#default_value'];
157 }
158 else {
8ffd8e3d
DP
159 // uploads take priority over value of fid text field.
160 if ($fid = filefield_save_upload($element)) {
161 $edit['fid'] = $fid;
7e784adc 162 }
8ffd8e3d
DP
163
164 // load file.
d2f124d5
DP
165 $file = field_file_load($edit['fid']);
166 $item = array(
167 'fid' => $edit['fid'],
86e371d9 168 'list' => !empty($edit['list']),
3d3b3ed1 169 'data' => empty($edit['data']) ? array() : $edit['data'],
d2f124d5
DP
170 );
171 }
8ffd8e3d 172 // merge file and item data so it is available to all widgets.
6e7782b4 173 $item = array_merge($item, $file);
1bffec9e 174
6e7782b4
DP
175 // if this widget is another type and leaning on filefield to do the dirty work....
176 // pass it back home.
177 $function = $element['#type'] .'_widget_value';
178 if (function_exists($function)) {
179 $item = array_merge($item, $function($element, $edit));
180 }
181 return $item;
d2f124d5
DP
182}
183
184function filefield_widget_process($element, $edit, &$form_state, $form) {
7e784adc 185
8ffd8e3d
DP
186 $item = $element['#value'];
187 $field_name = $element['#field_name'];
188 $delta = $element['#delta'];
189
ad6c5ff0
DP
190 $field = content_fields($element['#field_name'], $element['#type_name']);
191
8ffd8e3d
DP
192 // check remove buttons...
193 $remove_name = $element['#field_name'] .'_'. $element['#delta'] .'_remove_btn';
ad6c5ff0
DP
194 if (isset($form_state['clicked_button']) && $form_state['clicked_button']['#name'] == $remove_name) {
195 $item = _filefield_default_value($field);
8ffd8e3d 196 }
7e784adc 197
198 // figute out our fid...
8ffd8e3d 199 $element['fid'] = array('#type' => 'hidden', '#value' => $item['fid']);
d2f124d5 200
6e7782b4 201 if ($item['fid'] != 0) {
7e784adc 202 $element['preview'] = array('#type' => 'markup', '#value' => theme($element['#type'] .'_preview', $item));
d2f124d5 203 }
28b1c58e
DP
204
205 // placeholder.. will be serialized into the data column. this is a place for widgets
206 // to put additional data.
207 $element['data'] = array('#tree' => 'true');
2635f5af 208
209 if ($field['show_description']) {
210 $element['data']['description'] = array(
211 '#type' => 'textfield',
212 '#title' => t('Description'),
213 '#value' => isset($item['data']['description']) ? $item['data']['description'] : '',
214 );
215 }
28b1c58e
DP
216
217 if ($field['force_list_default']) {
218 $element['list'] = array(
219 '#type' => 'hidden',
220 '#value' => $field['list_default'],
221 );
222 }
223 else {
224 $element['list'] = array(
225 '#type' => 'checkbox',
226 '#title' => t('List'),
8ffd8e3d 227 '#value' => isset($item['list']) ? $item['list'] : $field['list_default'],
28b1c58e
DP
228 '#attributes' => array('class' => 'filefield-list'),
229 );
230 }
d2f124d5 231
d2f124d5 232
8ffd8e3d
DP
233
234 foreach ($element['#upload_validators'] as $callback => $arguments) {
235 $help_func = $callback .'_help';
236 $desc[] = call_user_func_array($help_func, $arguments);
237 }
238 $element['upload'] = array(
239 '#name' => 'files['. $element['#field_name'] .'_'. $element['#delta'] .']',
240 '#type' => 'file',
241 '#title' => t('New Upload'),
242 '#description' => implode('<br />', $desc),
243 '#attributes' => array(
f976317f 244 'accept' => implode(',', array_filter(explode(' ', $field['widget']['file_extensions']))),
8ffd8e3d
DP
245 )
246 );
247
248
249 if ($item['fid'] != 0) {
250 $element['upload']['#title'] = t('Replace');
251 }
aa3a8ca0
DP
252
253 $element['#prefix'] = '<div id="'. $element['#id'] .'-ahah-wrapper" class="filefield-ahah-wrapper">';
254 $element['#suffix'] = '</div>';
8ffd8e3d 255 $element['upload_btn'] = array(
7e784adc 256 '#type' => 'submit',
8ffd8e3d 257 '#value' => t('Upload'),
aa3a8ca0 258 '#process' => array('form_expand_ahah'),
8ffd8e3d 259 '#submit' => array('node_form_submit_build_node'),
7e784adc 260 '#ahah' => array( // with JavaScript
261 'path' => 'filefield/ahah/'. $element['#type_name'] .'/'. $element['#field_name'] .'/'. $element['#delta'],
262 'wrapper' => $element['#id'] .'-ahah-wrapper',
263 'method' => 'replace',
264 'effect' => 'fade',
8ffd8e3d
DP
265 ),
266 '#field_name' => $element['#field_name'],
267 '#delta' => $element['#delta'],
268 '#type_name' => $element['#type_name'],
269 '#upload_validators' => $element['#upload_validators'],
270 );
6e7782b4 271 if ($item['fid'] != 0) {
d2f124d5 272 $element['remove_btn'] = array(
6e7782b4 273 '#name' => $element['#field_name'] .'_'. $element['#delta'] .'_remove_btn',
7e784adc 274 '#type' => 'submit',
d2f124d5 275 '#value' => t('Remove'),
8ffd8e3d 276 '#process' => array('filefield_widget_process_remove_btn', 'form_expand_ahah'),
ad6c5ff0 277 '#submit' => array('node_form_submit_build_node'),
7e784adc 278 '#ahah' => array( // with JavaScript
279 'path' => 'filefield/ahah/'. $element['#type_name'] .'/'. $element['#field_name'] .'/'. $element['#delta'],
fe9499a1 280 'wrapper' => $element['#id'] .'-ahah-wrapper',
7e784adc 281 'method' => 'replace',
282 'effect' => 'fade',
6c352642 283 ),
6e7782b4
DP
284 '#field_name' => $element['#field_name'],
285 '#delta' => $element['#delta'],
d2f124d5 286 );
d2f124d5
DP
287 }
288
d2f124d5 289
d2f124d5
DP
290 return $element;
291}
292
ac83a9cf 293function filefield_widget_validate($element, &$form_state) {
1bffec9e 294
ecbb2c26 295}
1bffec9e 296
d2f124d5 297function _filefield_widget_validate($element, &$form_state) {
d2f124d5
DP
298}
299
300
d2f124d5
DP
301/**
302 * FormAPI theme function. Theme the output of an image field.
303 */
bad2b17f 304function theme_filefield_widget($element) {
c6e8be79 305 return theme('form_element', $element, $element['#children']);
d2f124d5
DP
306}
307
22e736e6 308function theme_filefield_widget_preview($item) {
c4c7e990
DP
309 return '<div class="filefield-preview clear-block">'.
310 '<div class="filename">'. theme('filefield_file', $item) .'</div>'.
311 '<div class="filesize">'. format_size($item['filesize']) .'</div>'.
312 '<div class="filemime">'. $item['filemime'] .'</div>'.
313 '</div>';
22e736e6
DP
314}
315
316function theme_filefield_widget_item($element) {
bad2b17f 317 return '<div class="filefield-row clear-block">'.
c4c7e990
DP
318 '<div class="filefield-filename clear-block">'. drupal_render($element['preview']) . '</div>'.
319 '<div class="fielfield-edit clear-block">'.
bad2b17f
DP
320 '<div class="filefield-list">'. drupal_render($element['list']) . '</div>' .
321 '<div class="filefield-description">'. drupal_render($element['description']) . '</div>' .
322 '<div class="filefield-stuff">'. drupal_render($element) .'</div>'.
323 '</div>'.
324 '</div>';
325}
d2f124d5 326
2be7e4b0
DP
327/**
328 * #require validation for filetype fields.
329 */
330
331function filefield_node_form_validate($form, &$form_state) {
2be7e4b0
DP
332 $type = content_types($form['type']['#value']);
333 foreach ($type['fields'] as $field_name => $field) {
c4c7243a 334 if (!(in_array($field['module'], array('imagefield', 'filefield'))) || !$field['required']) continue;
2be7e4b0
DP
335 $empty = $field['module'] .'_content_is_empty';
336 $valid = false;
f92ea317 337 $total_filesize = 0;
2be7e4b0
DP
338 foreach($form_state['values'][$field_name] as $delta => $item) {
339 if ($empty($item, $field)) continue;
3d3b3ed1 340 $valid = true;
f92ea317 341 $total_filesize += (int)$item['filesize'];
2be7e4b0 342 }
9412a7cc 343
f92ea317
DP
344 if (!$valid) {
345 form_set_error($field_name, t('%title field is required.', array('%title' => $field['widget']['label'])));
346 }
347 $max_filesize = parse_size($field['widget']['max_filesize_per_node']);
5af49125 348 if ($max_filesize && $total_filesize > $max_filesize) {
7e784adc 349 form_set_error($field_name, t('Total filesize for %title, %tsize, exceeds field settings of %msize.',
f92ea317 350 array(
7e784adc 351 '%title' => $field['widget']['label'],
352 '%tsize' => format_size($total_filesize),
f92ea317
DP
353 '%msize' => format_size($max_filesize)
354 )
355 ));
356 }
2be7e4b0
DP
357 }
358}
9412a7cc 359
0ed57194 360function filefield_node_form_submit($form, &$form_state) {
ad6c5ff0
DP
361 // we ignore all but the save button here.
362 if ($form_state['values']['op'] != t('Save')) {
363 return;
364 }
365 // @todo: try to delete removed files.
366}