losing filefield_*_settings.incs... just too many files...
[project/filefield.git] / filefield_widget.inc
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
13 /**
14 * @file
15 *
16 * FileField Widget Settings Hooks.
17 */
18
19 function filefield_widget_settings_form($widget) {
20 $form = array();
21 $form['file_extensions'] = array(
22 '#type' => 'textfield',
23 '#title' => t('Permitted upload file extensions'),
24 '#default_value' => is_string($widget['file_extensions']) ? $widget['file_extensions'] : 'txt',
25 '#size' => 64,
26 '#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.'),
27 '#weight' => 1,
28 );
29 $form['path_settings'] = array(
30 '#type' => 'fieldset',
31 '#title' => t('Path settings'),
32 '#collapsible' => true,
33 '#collapsed' => true,
34 '#weight' => 6,
35 );
36
37 $form['path_settings']['file_path'] = array(
38 '#type' => 'textfield',
39 '#title' => t('File path'),
40 '#default_value' => is_string($widget['file_path']) ? $widget['file_path'] : '',
41 '#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'))),
42 '#element_validate' => array('_filefield_widget_settings_file_path_validate'),
43 '#suffix' => theme('token_help', 'user'),
44 );
45
46 $form['max_filesize'] = array(
47 '#type' => 'fieldset',
48 '#title' => t('File size restrictions'),
49 '#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.'),
50 '#weight' => 6,
51 '#collapsible' => TRUE,
52 '#collapsed' => TRUE,
53 );
54 $form['max_filesize']['max_filesize_per_file'] = array(
55 '#type' => 'textfield',
56 '#title' => t('Maximum upload size per file'),
57 '#default_value' => is_string($widget['max_filesize_per_file'])
58 ? $widget['max_filesize_per_file']
59 : '',
60 '#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.'),
61 '#element_validate' => array('_filefield_widget_settings_max_filesize_per_file_validate'),
62 );
63 $form['max_filesize']['max_filesize_per_node'] = array(
64 '#type' => 'textfield',
65 '#title' => t('Maximum upload size per node'),
66 '#default_value' => is_string($widget['max_filesize_per_node'])
67 ? $widget['max_filesize_per_node']
68 : '',
69 '#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.'),
70 '#element_validate' => array('_filefield_widget_settings_max_filesize_per_node_validate'),
71 );
72 return $form;
73 }
74
75 function filefield_widget_settings_save($widget) {
76 return array(
77 'file_extensions', 'file_path', 'max_filesize_per_file',
78 'max_filesize_per_node', 'file_widgets'
79 );
80 }
81
82 function _filefield_widget_settings_file_path_validate($element, &$form_state) {
83 // Strip slashes from the beginning and end of $widget['file_path']
84 $form_state['values']['file_path'] = trim($form_state['values']['file_path'], '\\/');
85 }
86
87 function _filefield_widget_settings_max_filesize_per_file_validate($element, &$form_state) {
88 if (empty($form_state['values']['max_filesize_per_file'])) {
89 return; // Empty means no size restrictions, so don't throw an error.
90 }
91 else if (!is_numeric(parse_size($form_state['values']['max_filesize_per_file']))) {
92 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).'));
93 }
94 }
95
96 function _filefield_widget_settings_max_filesize_per_node_validate($element, &$form_state) {
97 if (empty($form_state['values']['max_filesize_per_node'])) {
98 return; // Empty means no size restrictions, so don't throw an error.
99 }
100 else if (!is_numeric(parse_size($form_state['values']['max_filesize_per_node']))) {
101 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).'));
102 }
103 }
104
105
106 /**
107 * FileField widget element callbacks.
108 */
109
110 function filefield_widget_value($element, $edit = FALSE) {
111 //dsm($element['#default_value']);
112 if (!$edit) {
113 $file = field_file_load($element['#default_value']['fid']);
114 $item = $element['#default_value'];
115 }
116 else {
117 $file = field_file_load($edit['fid']);
118 $item = array(
119 'fid' => $edit['fid'],
120 'description' => $edit['description'],
121 'list' => empty($edit['list']),
122 'data' => '',
123 );
124 }
125 $item = array_merge($item, $file);
126 // if this widget is another type and leaning on filefield to do the dirty work....
127 // pass it back home.
128 $function = $element['#type'] .'_widget_value';
129 if (function_exists($function)) {
130 $item = array_merge($item, $function($element, $edit));
131 }
132 return $item;
133 }
134
135 function filefield_widget_process($element, $edit, &$form_state, $form) {
136 $element['#theme'] = $element['#type'] .'_item';
137 $item = $element['#value'];
138 $field = content_fields($element['#field_name'], $element['#type_name']);
139
140 $element['fid'] = array('#type' => 'hidden', '#value' => $item['fid']);
141
142 if ($item['fid'] != 0) {
143 $element['preview'] = array('#type' => 'markup', '#value' => theme($element['#type'] .'_preview', $item));
144 }
145 $element['description'] = array(
146 '#type' => 'textfield',
147 '#title' => t('Description'),
148 '#default_value' => $item['description'],
149 );
150
151 $element['list'] = array(
152 '#type' => 'checkbox',
153 '#title' => t('List'),
154 '#default_value' => $item['list'],
155 '#attributes' => array('class' => 'filefield-list'),
156 );
157
158 $element['data'] = array(
159 '#type' => 'hidden',
160 '#value' => '',
161 );
162
163 if ($item['fid'] != 0) {
164 $element['remove_btn'] = array(
165 '#name' => $element['#field_name'] .'_'. $element['#delta'] .'_remove_btn',
166 '#type' => 'submit',
167 '#value' => t('Remove'),
168 '#submit' => array('filefield_widget_submit_remove_btn'),
169 '#field_name' => $element['#field_name'],
170 '#delta' => $element['#delta'],
171 );
172 }
173
174 if ($item['fid'] == 0) {
175 $element['description']['#type'] = 'hidden';
176
177 $element['upload'] = array(
178 '#name' => 'files['. $element['#field_name'] .'_'. $element['#delta'] .']',
179 '#type' => 'file',
180 '#title' => t('New Upload'),
181 );
182
183 $element['upload_btn'] = array(
184 '#name' => $element['#field_name'] .'_'. $element['#delta'] .'_upload_btn',
185 '#type' => 'submit',
186 '#value' => t('Upload'),
187 '#submit' => array('filefield_widget_submit_upload_btn'),
188 '#field_name' => $element['#field_name'],
189 '#delta' => $element['#delta'],
190 );
191 }
192
193 // if this widget is another type and leaning on filefield to do the dirty work....
194 // pass it back home.
195 $function = $element['#type'] .'_widget_process';
196 if (function_exists($function)) {
197 $element = array_merge($element, $function($element, $edit, $form_state, $form));
198 }
199
200 return $element;
201 }
202
203 function filefield_widget_submit_remove_btn($form, &$form_state) {
204 //dsm ('imagefield_widget_submit_remove_btn');
205 //dsm($form);
206 //dsm($form_state);
207 $field_name = $form_state['clicked_button']['#field_name'];
208 $delta = $form_state['clicked_button']['#delta'];
209 $form_state['values'][$field_name][$delta] = array(
210 'fid' => 0,
211 'alt' => '',
212 'filepath' => '',
213 'filename' => '',
214 'title' => '',
215 'data' => '',
216 );
217 $form_state['rebuild'] = true;
218 }
219
220 function filefield_widget_submit_upload_btn($form, &$form_state) {
221 //dsm('imagefield_widget_submit_upload_btn');
222 //dsm($form);
223 //dsm($form_state);
224 $field_name = $form_state['clicked_button']['#field_name'];
225 $delta = $form_state['clicked_button']['#delta'];
226
227 $field = content_fields($field_name);
228
229 $widget_file_path = $field['widget']['file_path'];
230 if (module_exists('token')) {
231 global $user;
232 $widget_file_path = token_replace($widget_file_path, 'user', $user);
233 }
234
235 $complete_file_path = file_directory_path() .'/'. $widget_file_path;
236 $upload_name = $field_name .'_'. $delta;
237 if (!field_file_check_directory($complete_file_path, FILE_CREATE_DIRECTORY)) {
238 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' => $widget_file_path, '%field' => $field_name, '%type' => $field['type_name']));
239 form_set_error($upload_name, t('The file could not be uploaded.'));
240 return $file;
241 }
242
243 $validators = _filefield_filefield_validators($field, $field['widget'], $form_state['values'][$field_name]);
244
245 if (!$file = field_file_save_upload($upload_name, $validators, $complete_file_path)) {
246 watchdog('imagefield', 'The file upload failed. %upload', array('%upload' => $upload_name));
247 form_set_error($upload_name, t('The Image upload failed.'));
248 return;
249 }
250 //dsm($file);
251 $form_state['values'][$field_name][$delta]['fid'] = $file['fid'];
252 $form_state['rebuild'] = true;
253 }
254
255 function _filefield_widget_validate($element, &$form_state) {
256 //dsm('imagefield_validate');
257 //dsm($element);
258 }
259
260
261 /**
262 * FormAPI theme function. Theme the output of an image field.
263 */
264 function theme_filefield_widget($element) {
265 return $element['#children'];
266 }
267
268 function theme_filefield_widget_preview($item) {
269 return '<!-- filefield widget preview -->';
270 }
271
272 function theme_filefield_widget_item($element) {
273 return '<div class="filefield-row clear-block">'.
274 '<div class="filefield-info clear-block">'.
275 '<div class="filefield-filename clear-block">'. drupal_render($element['preview']) . '</div>'.
276 //'<div class="filefield-filesize">filesize: 32Kb</div>'.
277 //'<div class="filefield-filemime">mimetype: text/patch</div>'.
278 '</div>' .
279 '<div class="fielfield-edit clear-block"'.
280 '<div class="filefield-list">'. drupal_render($element['list']) . '</div>' .
281 '<div class="filefield-description">'. drupal_render($element['description']) . '</div>' .
282 '<div class="filefield-stuff">'. drupal_render($element) .'</div>'.
283 '</div>'.
284 '</div>';
285 }
286
287
288
289
290 /**
291 * Implementation of hook_filefield_validators():
292 * Upload restrictions for file size, file extension and supported file widgets.
293 * Implemented as private function instead of as a real hook, because we want
294 * to make an exception so that these requirements appear first in any list.
295 */
296 function _filefield_filefield_validators($field, $widget, $existing_files) {
297 $validators = array();
298
299 // Thanks to the php.ini restrictions, there is always a maximum file size.
300 // Therefore we can rely on at least one restriction always being in force.
301 $max_filesize = _filefield_maximum_filesize($field, $widget, $existing_files);
302 $filesize_message = ($max_filesize > 0)
303 ? t('Maximum file size: !size.', array('!size' => format_size($max_filesize)))
304 : t('The allowed maximum file size total has been exceeded.');
305
306 $validators['file_validate_size'] = array(
307 'validator arguments' => array($max_filesize),
308 'requirement message' => $filesize_message,
309 'upload possible' => ($max_filesize > 0),
310 );
311
312 if (!empty($widget['file_extensions'])) {
313 $validators['file_validate_extensions'] = array(
314 'validator arguments' => array($widget['file_extensions']),
315 'requirement message' => t('Allowed extensions: %ext.', array(
316 '%ext' => $widget['file_extensions'],
317 )),
318 );
319 }
320 return $validators;
321 }
322
323
324
325 /**
326 * Get the maximum file size that is allowed for a new upload.
327 *
328 * @return
329 * -1 for "no more files allowed", or any positive value as the number
330 * of bytes that may still be uploaded. A result of 0 ("unlimited") will
331 * never happen because of PHP's upload limits.)
332 */
333 function _filefield_maximum_filesize($field, $widget, $existing_files) {
334 // Calculate the maximum file size - the least of all returned values.
335 $max_filesize = FALSE;
336 $restrictions = module_invoke_all(
337 'filefield_filesize_restrictions', $field, $widget, $existing_files
338 );
339 foreach ($restrictions as $value) {
340 if ($max_filesize === FALSE || $value < $max_filesize) {
341 $max_filesize = $value;
342 }
343 }
344
345 // Return -1 if any restriction value was not a positive number.
346 if ($max_filesize === FALSE || $max_filesize <= 0) {
347 return -1;
348 }
349 return $max_filesize;
350 }
351
352 /**
353 * Implementation of hook_filefield_filesize_restrictions():
354 * Specify how large a newly uploaded file may be, in bytes.
355 * (The smallest size of all hook implementations will be applied in the end).
356 */
357 function filefield_filefield_filesize_restrictions($field, $widget, $existing_files) {
358 $filesize_restrictions = array(file_upload_max_size());
359
360 // Maximum file size for each file separately.
361 if (!empty($widget['max_filesize_per_file'])) {
362 $filesize_restrictions[] = parse_size($widget['max_filesize_per_file']);
363 }
364
365 // Maximum file size for all files in the node (for this field).
366 if (!empty($widget['max_filesize_per_node'])) {
367 $allowed_total_size = parse_size($widget['max_filesize_per_node']);
368 $total_size = 0;
369
370 foreach ($existing_files as $delta => $file) {
371 if (!empty($file->filesize)) {
372 $total_size += $file->filesize;
373 }
374 }
375 if (!empty($total_size)) {
376 $filesize_restrictions[] = $allowed_total_size - $total_size;
377 }
378 }
379 return $filesize_restrictions;
380 }
381
382
383
384