#846598: Code cleanup, moving devel_shutdown turn-off to top of filefield_js().
[project/filefield.git] / filefield_field.inc
CommitLineData
d2f124d5 1<?php
75fea574 2// $Id$
d2f124d5
DP
3
4/**
5 * @file
87d3e491 6 * FileField CCK field hooks and callbacks.
d2f124d5
DP
7 */
8
87d3e491
NH
9/**
10 * Implementation of CCK's hook_field_settings($op = 'form').
11 */
22e736e6 12function filefield_field_settings_form($field) {
35873ed9
NH
13 drupal_add_js(drupal_get_path('module', 'filefield') .'/filefield.js');
14
28b1c58e 15 $form = array();
35873ed9
NH
16
17 $form['list_field'] = array(
28b1c58e 18 '#type' => 'radios',
35873ed9
NH
19 '#title' => t('List field'),
20 '#options' => array(0 => t('Disabled'), 1 => t('Enabled')),
21 '#default_value' => $field['list_field'] === '' ? 0 : (int) $field['list_field'],
38d91104 22 '#description' => t('The "list" option lets a user choose if a file should be shown in a list when viewing the content after creation.'),
35873ed9 23 '#attributes' => array('class' => 'filefield-list-field'),
28b1c58e 24 );
35873ed9
NH
25 $form['list_default'] = array(
26 '#type' => 'checkbox',
27 '#title' => t('Files listed by default'),
28 '#default_value' => $field['list_default'] === '' ? 1 : (int) $field['list_default'],
28b1c58e 29 );
35873ed9 30 $form['description_field'] = array(
2635f5af 31 '#type' => 'radios',
32 '#title' => t('Description field'),
35873ed9 33 '#default_value' => $field['description_field'] === '' ? 0 : (int) $field['description_field'],
2635f5af 34 '#options' => array(0 => t('Disabled'), 1 => t('Enabled')),
35 '#description' => t('When enabled, will display a text field where users may enter a description about the uploaded file.'),
36 );
28b1c58e
DP
37
38 return $form;
22e736e6
DP
39}
40
87d3e491
NH
41/**
42 * Implementation of CCK's hook_field_settings($op = 'save').
43 */
22e736e6 44function filefield_field_settings_save($field) {
35873ed9 45 return array('list_field', 'list_default', 'description_field');
22e736e6
DP
46}
47
87d3e491
NH
48/**
49 * Implementation of CCK's hook_field_settings($op = 'database_columns').
50 */
22e736e6
DP
51function filefield_field_settings_database_columns($field) {
52 return array(
f6e58c1c
NH
53 'fid' => array('type' => 'int', 'not null' => FALSE, 'views' => TRUE),
54 'list' => array('type' => 'int', 'size' => 'tiny', 'not null' => FALSE, 'views' => TRUE),
55 'data' => array('type' => 'text', 'serialize' => TRUE, 'views' => TRUE),
22e736e6
DP
56 );
57}
58
87d3e491
NH
59/**
60 * Implementation of CCK's hook_field_settings($op = 'views_data').
61 */
22e736e6
DP
62function filefield_field_settings_views_data($field) {
63 $data = content_views_field_views_data($field);
64 $db_info = content_database_info($field);
65 $table_alias = content_views_tablename($field);
66
22e736e6
DP
67 // By defining the relationship, we already have a "Has file" filter
68 // plus all the filters that Views already provides for files.
69 // No need for having a filter by ourselves.
70 unset($data[$table_alias][$field['field_name'] .'_fid']['filter']);
71
72 // Add a relationship for related file.
73 $data[$table_alias][$field['field_name'] .'_fid']['relationship'] = array(
74 'base' => 'files',
75 'field' => $db_info['columns']['fid']['column'],
9d33a4b6 76 'handler' => 'content_handler_relationship',
77 'label' => t($field['widget']['label']),
78 'content_field_name' => $field['field_name'],
b7d5f8cd 79 'skip base' => array('files'),
22e736e6 80 );
f6e58c1c
NH
81
82 // Use the Views boolean handler for filtering the list value.
83 $data[$table_alias][$field['field_name'] .'_list']['filter']['handler'] = 'views_handler_filter_boolean_operator';
84
85 // Add our special handler for serialized data.
86 $data[$table_alias][$field['field_name'] .'_data']['field'] = array(
87 'title' => $data[$table_alias][$field['field_name'] .'_data']['title'],
88 'title short' => $data[$table_alias][$field['field_name'] .'_data']['title short'],
89 'field' => $db_info['columns']['data']['column'],
90 'table' => $db_info['table'],
91 'handler' => 'filefield_handler_field_data',
92 'click sortable' => FALSE,
93 'access callback' => 'content_access',
94 'access arguments' => array('view', $field),
95 );
96
97 // Remove handlers that are probably unnecessary.
98 unset($data[$table_alias][$field['field_name'] .'_data']['filter']);
99 unset($data[$table_alias][$field['field_name'] .'_data']['argument']);
100 unset($data[$table_alias][$field['field_name'] .'_list']['argument']);
101
b7d5f8cd
NH
102 // Set up relationship with file views.
103 $data[$table_alias]['table']['join']['files'] = array(
104 'table' => $db_info['table'],
105 'left_field' => 'fid',
106 'field' => $db_info['columns']['fid']['column'],
107 );
108 $data[$table_alias]['vid'] = array(
109 'title' => t($field['widget']['label']),
110 'help' => t('The node the uploaded file is attached to'),
111 'relationship' => array(
112 'label' => t($field['widget']['label']),
113 'base' => 'node',
114 'base field' => 'vid',
115 // This allows us to not show this relationship if the base is already
116 // node so users won't create circular relationships.
117 'skip base' => array('node', 'node_revisions'),
118 ),
119 );
120
22e736e6
DP
121 return $data;
122}
123
d2f124d5 124/**
87d3e491 125 * Implementation of CCK's hook_field($op = 'load').
d2f124d5
DP
126 */
127function filefield_field_load($node, $field, &$items, $teaser, $page) {
128 if (empty($items)) {
129 return array();
130 }
131 foreach ($items as $delta => $item) {
132 // Despite hook_content_is_empty(), CCK still doesn't filter out
133 // empty items from $op = 'load', so we need to do that ourselves.
134 if (empty($item['fid']) || !($file = field_file_load($item['fid']))) {
111b60ca 135 $items[$delta] = NULL;
d2f124d5
DP
136 }
137 else {
6f0f10d4 138 $item['data'] = unserialize($item['data']);
2c1d1ab8
NH
139 // Temporary fix to unserialize data serialized multiple times.
140 // See the FileField issue http://drupal.org/node/402860.
141 // And the CCK issue http://drupal.org/node/407446.
142 while (!empty($item['data']) && is_string($item['data'])) {
143 $item['data'] = unserialize($item['data']);
144 }
fd7bcf39
NH
145 // Merge any data added by modules in hook_file_load().
146 if (isset($file['data']) && isset($item['data'])) {
147 $file['data'] = array_merge((array) $item['data'], (array) $file['data']);
148 }
d2f124d5
DP
149 $items[$delta] = array_merge($item, $file);
150 }
151 }
fd7bcf39 152
d2f124d5
DP
153 return array($field['field_name'] => $items);
154}
155
87d3e491
NH
156/**
157 * Implementation of CCK's hook_field($op = 'insert').
158 */
d2f124d5
DP
159function filefield_field_insert($node, $field, &$items, $teaser, $page) {
160 return filefield_field_update($node, $field, $items, $teaser, $page);
161}
162
87d3e491
NH
163/**
164 * Implementation of CCK's hook_field($op = 'update').
165 */
d2f124d5 166function filefield_field_update($node, $field, &$items, $teaser, $page) {
697f5509
DP
167
168 // Accumulator to gather current fid to compare with the original node
169 // for deleting replaced files.
170 $curfids = array();
d2f124d5
DP
171 foreach ($items as $delta => $item) {
172 $items[$delta] = field_file_save($node, $item);
173 // Remove items from the array if they have been deleted.
111b60ca
NH
174 if (empty($items[$delta]) || empty($items[$delta]['fid'])) {
175 $items[$delta] = NULL;
176 }
177 else {
178 $curfids[] = $items[$delta]['fid'];
179 }
d2f124d5 180 }
afc98b48 181
87d3e491 182 // If this is a new node there are no old items to worry about.
1231621b 183 // On new revisions, old files are always maintained in the previous revision.
6a189547 184 if ($node->is_new || !empty($node->revision) || !empty($node->skip_filefield_delete)) {
afc98b48
DP
185 return;
186 }
187
1231621b 188 // Delete items from original node.
697f5509 189 $orig = node_load($node->nid);
8aa536fb 190 // If there are, figure out which ones must go.
1231621b 191 if (!empty($orig->$field['field_name'])) {
8aa536fb 192 foreach ($orig->$field['field_name'] as $oitem) {
6231ddf9 193 if (isset($oitem['fid']) && !in_array($oitem['fid'], $curfids)) {
5b3aa822
NH
194 // For hook_file_references, remember that this is being deleted.
195 $oitem['field_name'] = $field['field_name'];
6a189547 196 $oitem['delete_vid'] = $orig->vid;
b703ffee 197 filefield_field_delete_file($oitem, $field);
8aa536fb 198 }
697f5509
DP
199 }
200 }
d2f124d5
DP
201}
202
87d3e491
NH
203/**
204 * Implementation of CCK's hook_field($op = 'delete_revision').
205 */
d2f124d5
DP
206function filefield_field_delete_revision($node, $field, &$items, $teaser, $page) {
207 foreach ($items as $delta => $item) {
28b1c58e 208 // For hook_file_references, remember that this is being deleted.
d2f124d5 209 $item['field_name'] = $field['field_name'];
6a189547 210 $item['delete_vid'] = $node->vid;
b703ffee 211 if (filefield_field_delete_file($item, $field)) {
111b60ca 212 $items[$delta] = NULL;
4b58ccde 213 }
d2f124d5
DP
214 }
215}
216
87d3e491
NH
217/**
218 * Implementation of CCK's hook_field($op = 'delete').
219 */
d2f124d5
DP
220function filefield_field_delete($node, $field, &$items, $teaser, $page) {
221 foreach ($items as $delta => $item) {
f289c6e2 222 // For hook_file_references(), remember that this is being deleted.
d2f124d5 223 $item['field_name'] = $field['field_name'];
f289c6e2
NH
224 // Pass in the nid of the node that is being removed so all references can
225 // be counted in hook_file_references().
226 $item['delete_nid'] = $node->nid;
b703ffee 227 filefield_field_delete_file($item, $field);
d2f124d5
DP
228 }
229}
230
87d3e491 231/**
b703ffee
NH
232 * Check that FileField controls a file before attempting to deleting it.
233 */
234function filefield_field_delete_file($file, $field) {
235 $file = (object) $file;
236
237 // Remove the field_name and delete_nid properties so that references can be
238 // counted including the files to be deleted.
239 $field_name = isset($file->field_name) ? $file->field_name : NULL;
240 $delete_nid = isset($file->delete_nid) ? $file->delete_nid : NULL;
241 unset($file->field_name, $file->delete_nid);
242
243 // To prevent FileField from deleting files it doesn't know about, check the
244 // FileField reference count. Temporary files can be deleted because they
245 // are not yet associated with any content at all.
246 if ($file->status == 0 || filefield_get_file_reference_count($file, $field) > 0) {
247 $file->field_name = $field_name;
248 $file->delete_nid = $delete_nid;
249 return field_file_delete($file);
250 }
251
252 // Even if the file is not deleted, return TRUE to indicate the FileField
253 // record can be removed from the FileField database tables.
254 return TRUE;
255}
256
257/**
87d3e491
NH
258 * Implementation of CCK's hook_field($op = 'sanitize').
259 */
d2f124d5
DP
260function filefield_field_sanitize($node, $field, &$items, $teaser, $page) {
261 foreach ($items as $delta => $item) {
262 // Cleanup $items during node preview.
263 if (empty($item['fid']) || !empty($item['delete'])) {
d38aafdd
NH
264 // Check for default images at the widget level.
265 // TODO: Provide an API to ImageField to do this itself?
d510b348 266 if (!empty($field['widget']['use_default_image']) && !empty($field['widget']['default_image']['filepath']) && $delta == 0) {
d38aafdd 267 $items[$delta] = $field['widget']['default_image'];
9ce3cdc5 268 $items[$delta]['default'] = TRUE;
d38aafdd
NH
269 }
270 else {
271 $items[$delta] = NULL;
272 continue;
273 }
d2f124d5 274 }
93c5556a 275
d2f124d5
DP
276 // Add nid so formatters can create a link to the node.
277 $items[$delta]['nid'] = $node->nid;
9df4f68c 278
93c5556a
NH
279 // Get the 'data' column stored by CCK into an array. This is necessary
280 // for Views, which doesn't call the "load" $op and to fix an issue with
281 // CCK double-serializing data.
37011622
NH
282 // See the FileField issue http://drupal.org/node/402860.
283 // And the CCK issue http://drupal.org/node/407446.
284 while (!empty($items[$delta]['data']) && is_string($items[$delta]['data'])) {
285 $items[$delta]['data'] = unserialize($items[$delta]['data']);
286 }
287
93c5556a
NH
288 // Load the complete file if a filepath is not available.
289 if (!empty($item['fid']) && empty($item['filepath'])) {
290 $file = (array) field_file_load($item['fid']);
d00b218d 291 if (isset($file['data'])) {
317c8ec0 292 $file['data'] = array_merge($items[$delta]['data'], $file['data']);
d00b218d 293 }
317c8ec0 294 $items[$delta] = array_merge($items[$delta], $file);
93c5556a
NH
295 }
296
9df4f68c
NH
297 // Verify the file exists on the server.
298 if (!empty($item['filepath']) && !file_exists($item['filepath'])) {
b719f898 299 watchdog('filefield', 'FileField was trying to display the file %file, but it does not exist.', array('%file' => $item['filepath']), WATCHDOG_WARNING);
9df4f68c 300 }
d2f124d5
DP
301 }
302}