| Commit | Line | Data |
|---|---|---|
| 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 | 12 | function 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 | 44 | function 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 |
51 | function 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 |
62 | function 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 | */ |
| 127 | function 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 |
159 | function 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 | 166 | function 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 |
206 | function 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 |
220 | function 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 | */ | |
| 234 | function 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 |
260 | function 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 | } |