File field in core From: <> --- .../modules/file_reference/file_reference.info | 6 .../modules/file_reference/file_reference.module | 307 ++++++++++++++++++++ .../modules/file_reference/file_reference.test | 34 ++ 3 files changed, 347 insertions(+), 0 deletions(-) diff --git modules/field/modules/file_reference/file_reference.info modules/field/modules/file_reference/file_reference.info new file mode 100644 index 0000000..c06f42f --- /dev/null +++ modules/field/modules/file_reference/file_reference.info @@ -0,0 +1,6 @@ +; $Id$ +name = File +description = Defines file field types. +package = Core - fields +core = 7.x +files[]=file_reference.module diff --git modules/field/modules/file_reference/file_reference.module modules/field/modules/file_reference/file_reference.module new file mode 100644 index 0000000..982923f --- /dev/null +++ modules/field/modules/file_reference/file_reference.module @@ -0,0 +1,307 @@ + array( + 'arguments' => array('element' => NULL), + ), + 'field_formatter_file_reference_default' => array( + 'arguments' => array('element' => NULL), + ), + ); +} + +/** + * Implementation of hook_field_info(). + */ +function file_reference_field_info() { + return array( + 'file_reference' => array( + 'label' => t('File'), + 'description' => t('This field stores files in the database.'), + 'settings' => array(), + 'instance_settings' => array('file_reference_processing' => 0), + 'default_widget' => 'file_reference_textfield', + 'default_formatter' => 'file_reference_default', + ), + ); +} + +/** + * Implementation of hook_field_schema(). + */ +function file_reference_field_columns($field) { + $columns = array( + 'fid' => array( + 'type' => 'int', + 'not null' => FALSE + ), + ); + return $columns; +} + + +/** + * Implementation of hook_field_load(). + * + * @param $obj_type + * The type of $object. + * @param $object + * The object for the operation. + * @param $field + * The field structure for the operation. + * @param $instance + * The instance structure for $field on $object's bundle. + * @param $items + * $object->{$field['field_name']}, or an empty array if unset. + */ +function file_reference_field_load($obj_type, $object, $field, $instance, &$items) { + $fids = array(); + foreach ($items as $item) { + if (!empty($items['fid'])) { + $fids[] = $item['fid']; + } + } + $files = file_load_multiple($fids); + foreach ($items as &$item) { + if (!empty($items['fid']) && isset($files[$items['fid']])) { + $items['file'] = $files[$items['fid']]; + } + else { + $item['file'] = NULL; + } + } +} + +/** + * Implementation of hook_field_validate(). + */ +function file_reference_field_validate($obj_type, $object, $field, $instance, $items, $form) { + if (is_array($items)) { + foreach ($items as $delta => $item) { + $error_element = isset($item['_error_element']) ? $item['_error_element'] : ''; + if (is_array($item) && isset($item['_error_element'])) unset($item['_error_element']); + if (!empty($item['fid'])) { + #TODO: file validation with file_reference_validate()? + } + } + } +} + +function file_reference_field_sanitize($obj_type, $object, $field, $instance, &$items) { + // TODO: Figure out if I need to do anything here. +} + +/** + * Implementation of hook_field_is_empty(). + */ +function file_reference_field_is_empty($item, $field) { + if (empty($item['fid']) && (string)$item['fid'] !== '0') { + return TRUE; + } + return FALSE; +} + +/** + * Implementation of hook_field_formatter_info(). + */ +function file_reference_field_formatter_info() { + return array( + 'file_reference_default' => array( + 'label' => t('Default'), + 'field types' => array('file_reference'), + 'behaviors' => array( + 'multiple values' => FIELD_BEHAVIOR_DEFAULT, + ), + ), + ); +} + +/** + * Theme function for 'default' file field formatter. + */ +function theme_field_formatter_file_reference_default($element) { + dsm($element); + return var_export($element['#item']['file'],1); +} + +/** + * Implementation of hook_file_delete(). + */ +function file_file_delete($file) { + // Delete all information associated with the file. +# TODO: Need to figure out how to query all our tables to search for the file id. +// db_delete('upload')->condition('fid', $file->fid)->execute(); +} + +/** + * Implementation of hook_field_widget_info(). + * + * Here we indicate that the field module will handle + * the default value and multiple values for these widgets. + * + * Callbacks can be omitted if default handing is used. + * They're included here just so this module can be used + * as an example for custom modules that might do things + * differently. + */ +function file_reference_field_widget_info() { + return array( + 'file_reference_textfield' => array( + 'label' => t('File reference field'), + 'field types' => array('file_reference'), + 'settings' => array('size' => 60), + 'behaviors' => array( + 'multiple values' => FIELD_BEHAVIOR_DEFAULT, + 'default value' => FIELD_BEHAVIOR_DEFAULT, + ), + ), + ); +} + +/** + * Implementation of FAPI hook_elements(). + * + * Any FAPI callbacks needed for individual widgets can be declared here, + * and the element will be passed to those callbacks for processing. + * + * Drupal will automatically theme the element using a theme with + * the same name as the hook_elements key. + * + * Autocomplete_path is not used by file_reference_field_widget but other + * widgets can use it (see nodereference and userreference). + */ +function file_reference_elements() { + return array( + 'file_reference_textfield' => array( + '#input' => TRUE, + '#columns' => array('value'), + '#delta' => 0, + '#process' => array('file_reference_textfield_process'), + '#autocomplete_path' => FALSE, + ), + ); +} + +/** + * Implementation of hook_field_widget(). + * + * Attach a single form element to the form. It will be built out and + * validated in the callback(s) listed in hook_elements. We build it + * out in the callbacks rather than here in hook_field_widget so it can be + * plugged into any module that can provide it with valid + * $field information. + * + * Field module will set the weight, field name and delta values + * for each form element. + * + * If there are multiple values for this field, the field module will + * call this function as many times as needed. + * + * @param $form + * the entire form array, $form['#node'] holds node information + * @param $form_state + * the form_state, $form_state['values'][$field['field_name']] + * holds the field's form values. + * @param $field + * The field structure. + * @param $instance + * the field instance array + * @param $items + * array of default values for this field + * @param $delta + * the order of this item in the array of subelements (0, 1, 2, etc) + * + * @return + * the form item for a single element for this field + */ +function file_reference_field_widget(&$form, &$form_state, $field, $instance, $items, $delta = 0) { + $element = array( + '#type' => $instance['widget']['type'], + '#default_value' => isset($items[$delta]) ? $items[$delta] : '', + ); + return $element; +} + +/** + * Process an individual element. + * + * Build the form element. When creating a form using FAPI #process, + * note that $element['#value'] is already set. + * + * The $field and $instance arrays are in $form['#fields'][$element['#field_name']]. + * + * TODO: For widgets to be actual FAPI 'elements', reusable outside of a + * 'field' context, they shoudn't rely on $field and $instance. The bits of + * information needed to adjust the behavior of the 'element' should be + * extracted in hook_field_widget() above. + */ +function file_reference_textfield_process($element, $edit, $form_state, $form) { + $field = $form['#fields'][$element['#field_name']]['field']; + $instance = $form['#fields'][$element['#field_name']]['instance']; + $field_key = $element['#columns'][0]; + $delta = $element['#delta']; + + $element[$field_key] = array( + '#type' => 'textfield', + '#default_value' => isset($element['#value'][$field_key]) ? $element['#value'][$field_key] : NULL, + '#autocomplete_path' => $element['#autocomplete_path'], + '#size' => $instance['widget']['settings']['size'], + '#attributes' => array('class' => 'text'), + // The following values were set by the field module and need + // to be passed down to the nested element. + '#title' => $element['#title'], + '#description' => $element['#description'], + '#required' => $element['#required'], + '#field_name' => $element['#field_name'], + '#bundle' => $element['#bundle'], + '#delta' => $element['#delta'], + '#columns' => $element['#columns'], + ); + + $element[$field_key]['#maxlength'] = !empty($field['settings']['max_length']) ? $field['settings']['max_length'] : NULL; + + if (!empty($instance['settings']['file_reference_processing'])) { + $filter_key = $element['#columns'][1]; + $format = isset($element['#value'][$filter_key]) ? $element['#value'][$filter_key] : FILTER_FORMAT_DEFAULT; + $parents = array_merge($element['#parents'] , array($filter_key)); + $element[$filter_key] = filter_form($format, 1, $parents); + } + + // Used so that hook_field('validate') knows where to flag an error. + // TODO: rework that. See http://groups.drupal.org/node/18019. + $element['_error_element'] = array( + '#type' => 'value', + '#value' => implode('][', array_merge($element['#parents'], array($field_key))), + ); + + return $element; +} + +/** + * FAPI theme for an individual text elements. + * + * The textfield or textarea is already rendered by the + * textfield or textarea themes and the html output + * lives in $element['#children']. Override this theme to + * make custom changes to the output. + * + * $element['#field_name'] contains the field name + * $element['#delta] is the position of this element in the group + */ +function theme_file_reference_textfield($element) { + return $element['#children']; +} + +function theme_file_reference_textarea($element) { + return $element['#children']; +} diff --git modules/field/modules/file_reference/file_reference.test modules/field/modules/file_reference/file_reference.test new file mode 100644 index 0000000..0e1b922 --- /dev/null +++ modules/field/modules/file_reference/file_reference.test @@ -0,0 +1,34 @@ + t('File Field'), + 'description' => t("Test the creation of image fields."), + 'group' => t('Field') + ); + } + + function setUp() { + parent::setUp('field', 'file_reference', 'field_test'); + } + + // Test widgets. + + /** + * Test filefield widget. + */ + function testFilefieldWidget() { + // Create a field. + $field = $this->drupalCreateField('file_reference'); + $this->instance = $this->drupalCreateFieldInstance($field['field_name'], 'file_reference_textfield', 'file_reference_default', FIELD_TEST_BUNDLE); + } + + // Test formatters. + /** + * + */ +}