| 1 |
<?php
|
| 2 |
// $Id: filefield_field.inc,v 1.33 2009/10/20 18:09:05 quicksketch Exp $
|
| 3 |
|
| 4 |
/**
|
| 5 |
* @file
|
| 6 |
* FileField CCK field hooks and callbacks.
|
| 7 |
*/
|
| 8 |
|
| 9 |
/**
|
| 10 |
* Implementation of CCK's hook_field_settings($op = 'form').
|
| 11 |
*/
|
| 12 |
function filefield_field_settings_form($field) {
|
| 13 |
drupal_add_js(drupal_get_path('module', 'filefield') .'/filefield.js');
|
| 14 |
|
| 15 |
$form = array();
|
| 16 |
|
| 17 |
$form['list_field'] = array(
|
| 18 |
'#type' => 'radios',
|
| 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'],
|
| 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.'),
|
| 23 |
'#attributes' => array('class' => 'filefield-list-field'),
|
| 24 |
);
|
| 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'],
|
| 29 |
);
|
| 30 |
$form['description_field'] = array(
|
| 31 |
'#type' => 'radios',
|
| 32 |
'#title' => t('Description field'),
|
| 33 |
'#default_value' => $field['description_field'] === '' ? 0 : (int) $field['description_field'],
|
| 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 |
);
|
| 37 |
|
| 38 |
return $form;
|
| 39 |
}
|
| 40 |
|
| 41 |
/**
|
| 42 |
* Implementation of CCK's hook_field_settings($op = 'save').
|
| 43 |
*/
|
| 44 |
function filefield_field_settings_save($field) {
|
| 45 |
return array('list_field', 'list_default', 'description_field');
|
| 46 |
}
|
| 47 |
|
| 48 |
/**
|
| 49 |
* Implementation of CCK's hook_field_settings($op = 'database_columns').
|
| 50 |
*/
|
| 51 |
function filefield_field_settings_database_columns($field) {
|
| 52 |
return array(
|
| 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),
|
| 56 |
);
|
| 57 |
}
|
| 58 |
|
| 59 |
/**
|
| 60 |
* Implementation of CCK's hook_field_settings($op = 'views_data').
|
| 61 |
*/
|
| 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 |
|
| 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'],
|
| 76 |
'handler' => 'content_handler_relationship',
|
| 77 |
'label' => t($field['widget']['label']),
|
| 78 |
'content_field_name' => $field['field_name'],
|
| 79 |
);
|
| 80 |
|
| 81 |
// Use the Views boolean handler for filtering the list value.
|
| 82 |
$data[$table_alias][$field['field_name'] .'_list']['filter']['handler'] = 'views_handler_filter_boolean_operator';
|
| 83 |
|
| 84 |
// Add our special handler for serialized data.
|
| 85 |
$data[$table_alias][$field['field_name'] .'_data']['field'] = array(
|
| 86 |
'title' => $data[$table_alias][$field['field_name'] .'_data']['title'],
|
| 87 |
'title short' => $data[$table_alias][$field['field_name'] .'_data']['title short'],
|
| 88 |
'field' => $db_info['columns']['data']['column'],
|
| 89 |
'table' => $db_info['table'],
|
| 90 |
'handler' => 'filefield_handler_field_data',
|
| 91 |
'click sortable' => FALSE,
|
| 92 |
'access callback' => 'content_access',
|
| 93 |
'access arguments' => array('view', $field),
|
| 94 |
);
|
| 95 |
|
| 96 |
// Remove handlers that are probably unnecessary.
|
| 97 |
unset($data[$table_alias][$field['field_name'] .'_data']['filter']);
|
| 98 |
unset($data[$table_alias][$field['field_name'] .'_data']['argument']);
|
| 99 |
unset($data[$table_alias][$field['field_name'] .'_list']['argument']);
|
| 100 |
|
| 101 |
return $data;
|
| 102 |
}
|
| 103 |
|
| 104 |
/**
|
| 105 |
* Implementation of CCK's hook_field($op = 'load').
|
| 106 |
*/
|
| 107 |
function filefield_field_load($node, $field, &$items, $teaser, $page) {
|
| 108 |
if (empty($items)) {
|
| 109 |
return array();
|
| 110 |
}
|
| 111 |
foreach ($items as $delta => $item) {
|
| 112 |
// Despite hook_content_is_empty(), CCK still doesn't filter out
|
| 113 |
// empty items from $op = 'load', so we need to do that ourselves.
|
| 114 |
if (empty($item['fid']) || !($file = field_file_load($item['fid']))) {
|
| 115 |
$items[$delta] = NULL;
|
| 116 |
}
|
| 117 |
else {
|
| 118 |
$item['data'] = unserialize($item['data']);
|
| 119 |
// Temporary fix to unserialize data serialized multiple times.
|
| 120 |
// See the FileField issue http://drupal.org/node/402860.
|
| 121 |
// And the CCK issue http://drupal.org/node/407446.
|
| 122 |
while (!empty($item['data']) && is_string($item['data'])) {
|
| 123 |
$item['data'] = unserialize($item['data']);
|
| 124 |
}
|
| 125 |
$items[$delta] = array_merge($item, $file);
|
| 126 |
}
|
| 127 |
}
|
| 128 |
return array($field['field_name'] => $items);
|
| 129 |
}
|
| 130 |
|
| 131 |
/**
|
| 132 |
* Implementation of CCK's hook_field($op = 'insert').
|
| 133 |
*/
|
| 134 |
function filefield_field_insert($node, $field, &$items, $teaser, $page) {
|
| 135 |
return filefield_field_update($node, $field, $items, $teaser, $page);
|
| 136 |
}
|
| 137 |
|
| 138 |
/**
|
| 139 |
* Implementation of CCK's hook_field($op = 'update').
|
| 140 |
*/
|
| 141 |
function filefield_field_update($node, $field, &$items, $teaser, $page) {
|
| 142 |
|
| 143 |
// Accumulator to gather current fid to compare with the original node
|
| 144 |
// for deleting replaced files.
|
| 145 |
$curfids = array();
|
| 146 |
foreach ($items as $delta => $item) {
|
| 147 |
$items[$delta] = field_file_save($node, $item);
|
| 148 |
// Remove items from the array if they have been deleted.
|
| 149 |
if (empty($items[$delta]) || empty($items[$delta]['fid'])) {
|
| 150 |
$items[$delta] = NULL;
|
| 151 |
}
|
| 152 |
else {
|
| 153 |
$curfids[] = $items[$delta]['fid'];
|
| 154 |
}
|
| 155 |
}
|
| 156 |
|
| 157 |
// If this is a new node there are no old items to worry about.
|
| 158 |
// On new revisions, old files are always maintained in the previous revision.
|
| 159 |
if ($node->is_new || !empty($node->revision)) {
|
| 160 |
return;
|
| 161 |
}
|
| 162 |
|
| 163 |
// Delete items from original node.
|
| 164 |
$orig = node_load($node->nid);
|
| 165 |
// If there are, figure out which ones must go.
|
| 166 |
if (!empty($orig->$field['field_name'])) {
|
| 167 |
foreach ($orig->$field['field_name'] as $oitem) {
|
| 168 |
if (isset($oitem['fid']) && !in_array($oitem['fid'], $curfids)) {
|
| 169 |
// For hook_file_references, remember that this is being deleted.
|
| 170 |
$oitem['field_name'] = $field['field_name'];
|
| 171 |
filefield_field_delete_file($oitem, $field);
|
| 172 |
}
|
| 173 |
}
|
| 174 |
}
|
| 175 |
}
|
| 176 |
|
| 177 |
/**
|
| 178 |
* Implementation of CCK's hook_field($op = 'delete_revision').
|
| 179 |
*/
|
| 180 |
function filefield_field_delete_revision($node, $field, &$items, $teaser, $page) {
|
| 181 |
foreach ($items as $delta => $item) {
|
| 182 |
// For hook_file_references, remember that this is being deleted.
|
| 183 |
$item['field_name'] = $field['field_name'];
|
| 184 |
if (filefield_field_delete_file($item, $field)) {
|
| 185 |
$items[$delta] = NULL;
|
| 186 |
}
|
| 187 |
}
|
| 188 |
}
|
| 189 |
|
| 190 |
/**
|
| 191 |
* Implementation of CCK's hook_field($op = 'delete').
|
| 192 |
*/
|
| 193 |
function filefield_field_delete($node, $field, &$items, $teaser, $page) {
|
| 194 |
foreach ($items as $delta => $item) {
|
| 195 |
// For hook_file_references(), remember that this is being deleted.
|
| 196 |
$item['field_name'] = $field['field_name'];
|
| 197 |
// Pass in the nid of the node that is being removed so all references can
|
| 198 |
// be counted in hook_file_references().
|
| 199 |
$item['delete_nid'] = $node->nid;
|
| 200 |
filefield_field_delete_file($item, $field);
|
| 201 |
}
|
| 202 |
}
|
| 203 |
|
| 204 |
/**
|
| 205 |
* Check that FileField controls a file before attempting to deleting it.
|
| 206 |
*/
|
| 207 |
function filefield_field_delete_file($file, $field) {
|
| 208 |
$file = (object) $file;
|
| 209 |
|
| 210 |
// Remove the field_name and delete_nid properties so that references can be
|
| 211 |
// counted including the files to be deleted.
|
| 212 |
$field_name = isset($file->field_name) ? $file->field_name : NULL;
|
| 213 |
$delete_nid = isset($file->delete_nid) ? $file->delete_nid : NULL;
|
| 214 |
unset($file->field_name, $file->delete_nid);
|
| 215 |
|
| 216 |
// To prevent FileField from deleting files it doesn't know about, check the
|
| 217 |
// FileField reference count. Temporary files can be deleted because they
|
| 218 |
// are not yet associated with any content at all.
|
| 219 |
if ($file->status == 0 || filefield_get_file_reference_count($file, $field) > 0) {
|
| 220 |
$file->field_name = $field_name;
|
| 221 |
$file->delete_nid = $delete_nid;
|
| 222 |
return field_file_delete($file);
|
| 223 |
}
|
| 224 |
|
| 225 |
// Even if the file is not deleted, return TRUE to indicate the FileField
|
| 226 |
// record can be removed from the FileField database tables.
|
| 227 |
return TRUE;
|
| 228 |
}
|
| 229 |
|
| 230 |
/**
|
| 231 |
* Implementation of CCK's hook_field($op = 'sanitize').
|
| 232 |
*/
|
| 233 |
function filefield_field_sanitize($node, $field, &$items, $teaser, $page) {
|
| 234 |
foreach ($items as $delta => $item) {
|
| 235 |
// Cleanup $items during node preview.
|
| 236 |
if (empty($item['fid']) || !empty($item['delete'])) {
|
| 237 |
// Check for default images at the widget level.
|
| 238 |
// TODO: Provide an API to ImageField to do this itself?
|
| 239 |
if (!empty($field['widget']['use_default_image']) && !empty($field['widget']['default_image']['filepath'])) {
|
| 240 |
$items[$delta] = $field['widget']['default_image'];
|
| 241 |
$items[$delta]['default'] = TRUE;
|
| 242 |
}
|
| 243 |
else {
|
| 244 |
$items[$delta] = NULL;
|
| 245 |
continue;
|
| 246 |
}
|
| 247 |
}
|
| 248 |
// Load the complete file if a filepath is not available.
|
| 249 |
if (!empty($item['fid']) && empty($item['filepath'])) {
|
| 250 |
$items[$delta] = array_merge($item, field_file_load($item['fid']));
|
| 251 |
}
|
| 252 |
// Add nid so formatters can create a link to the node.
|
| 253 |
$items[$delta]['nid'] = $node->nid;
|
| 254 |
|
| 255 |
// TODO: This is only necessary for Views, which doesn't call the "load"
|
| 256 |
// $op. It might be preferable to move this to Views integration somehow.
|
| 257 |
if (!empty($items['data']) && is_string($items[$delta]['data'])) {
|
| 258 |
$item['data'] = unserialize($item['data']);
|
| 259 |
}
|
| 260 |
// Temporary fix to unserialize data serialized multiple times.
|
| 261 |
// See the FileField issue http://drupal.org/node/402860.
|
| 262 |
// And the CCK issue http://drupal.org/node/407446.
|
| 263 |
while (!empty($items[$delta]['data']) && is_string($items[$delta]['data'])) {
|
| 264 |
$items[$delta]['data'] = unserialize($items[$delta]['data']);
|
| 265 |
}
|
| 266 |
|
| 267 |
// Verify the file exists on the server.
|
| 268 |
if (!empty($item['filepath']) && !file_exists($item['filepath'])) {
|
| 269 |
watchdog('filefield', 'FileField was trying to display the file %file, but it does not exist.', array('%file' => $item['filepath']), WATCHDOG_WARNING);
|
| 270 |
}
|
| 271 |
}
|
| 272 |
}
|