Display a CCK Filefield or Imagefield Upload Widget on Your Own Custom Form

Took a fair amount of googling around to find the solution to this one. With the Node Gallery 3.x branch, we needed a way to quickly add an image to an existing gallery. We could have displayed the whole node form, but there's a lot of things on that form that we can just use the defaults for 99% of the time. We need just three fields filled in: Title, Caption, and the imagefield itself.

To use the same imagefield widget that handles all the hard work for you on the node add field on your own field, first create a handler in hook_menu such as this:

  $items['node/%node_gallery_gallery/upload'] = array(
    'title' => 'Upload New Image',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('node_gallery_upload_image_form', 1),
    'access callback' => 'node_gallery_user_access',
    'access arguments' => array('upload', 1),
    'file' => 'node_gallery.pages.inc',
    'type' => MENU_LOCAL_TASK,
  );

Then, in node_gallery.pages.inc, you create the form function that does the work:

function node_gallery_upload_image_form($form_state, $gallery) {
  $imagetype = 'node_gallery_image';
  $form_id = $imagetype . '_node_form';
 
  module_load_include('inc', 'content', 'includes/content.node_form');

  $field = content_fields('field_node_gallery_image',$imagetype);
 
  $form['title'] = array(
    '#title' => t('Title'),
    '#type' => 'textfield',
    '#required' => TRUE,
    '#weight' => -10,
  );
  $form['body'] = array(
    '#title' => t('Caption'),
    '#type' => 'textarea',
    '#weight' => -9,
  );
  $form['type'] = array(
    '#type' => 'value',
    '#value' => $imagetype,
  );
  $form['gid'] = array(
    '#type' => 'value',
    '#value' => $gallery->nid,
  );
  $form['#field_info']['field_node_gallery_image'] = $field;
  $form['#field_info']['field_node_gallery_image']['#required'] = TRUE;
  $form += (array) content_field_form($form, $form_state, $field);
 
  $form['submit'] = array('#type' => 'submit', '#weight' => 10, '#value' => 'Save');
 
  return $form;

This is pretty straightforward, up until lines 28 - 30. Those three lines setup the form array and then append the results from content_field_form() to our existing form. Still, very easy, but I wasn't able to find any documentation on how to do this. Just in case you're curious, here's the submit handler for that form.

function node_gallery_upload_image_form_submit($form, &$form_state) {
  global $user;
  $image = new stdClass;
  $image->uid = $user->uid;
  $image->name = (isset($user->name) ? $user->name : '');
  $values = $form_state['values'];
  foreach ($values as $key => $value) {
    $image->$key = $value;
  }
  node_gallery_image_save($image);
}

Nothing new there. The end result is a nice looking, concise form that allows you to quickly upload an image to a gallery. Sweet!

Comments

Extremely helpful!  I've been searching for days looking for this!

I have a quick question though.  I've created a module that is basically a front end friendly story node edit form.  I added a image field using CCK to the story content type.

Everything works great until submit and then nothing.  Here is my submit handler.

 

function story_form_submit($form, &$form_state) {
    global $user;
    module_load_include('inc', 'node', 'node.pages');
    $node = array('type' => 'story');
    $story_form_state = array();
    $story_form_state['values']['name'] = $user->name;
    $story_form_state['values']['title'] = $form_state['values']['title'];
    $story_form_state['values']['field_story_name'][0]['value'] = $form_state['values']['name'];
    $story_form_state['values']['field_story_city'][0]['value'] = $form_state['values']['city'];
    $story_form_state['values']['body'] = $form_state['values']['body'];
    $story_form_state['values']['field_story_image'][0]['value'] = $form_state['values']['field_node_gallery_image'];
    $story_form_state['values']['op'] = t('Save');
    drupal_execute('story_node_form', $story_form_state, (object)$node);
}

Thank you for any help!

From http://drupal.org/node/293663

Anyway, drupal_execute() called drupal_retrieve_form(), which called node_form(). It was in node_form() that I finally found the culprit. Apparently node_form() expects that the $form_state variable should contain a duplicate copy of the $node object, as an array. The first thing it does is overwrite the $node variable that's passed to it as an argument with the one that comes inside the $form_state array. I hadn't set such a thing on my $form_state variable, and so it was resetting it the way it thought the $node really OUGHT to be.

So the very simple solution was to add the following single line of code immediately before the call to drupal_execute():

<span style="color: rgb(0, 0, 0);"><span style="color: rgb(0, 0, 187);">&lt;?php<br />
&nbsp;&nbsp;&nbsp; $form_state</span><span style="color: rgb(0, 119, 0);">[</span><span style="color: rgb(221, 0, 0);">&#39;node&#39;</span><span style="color: rgb(0, 119, 0);">] = (array) </span><span style="color: rgb(0, 0, 187);">$node</span><span style="color: rgb(0, 119, 0);">;</span><br />
<span style="color: rgb(0, 0, 187);">?&gt;</span></span>

Thank you again for all your help.  Unfortunatly that didn't fix the problem. 

It actually seems like a validation issue.  If I remove the required setting from the field and submit the form without an image it works perfectly.

Am I referencing the submitted form elements correctly for the image field?

First, unless you named your imagefield 'node_gallery_image' (which is what I named mine in the module), then you have the wrong value in there.  I'm going to assume you called it 'story_image'.

Second, your arrays are out of alignment.

Give this a try:

    $story_form_state['values']['field_story_image'] = $form_state['values']['field_story_image'];

Hi:

I need to do something very similar...add a file upload field to my form.  I don't quite understand your code...can you please post your code for your content_fields and content_field_form functions?

My current problem is that I have a file upload field in my form, defined like so:

function mmil_upload_form ( $form_state )
{
       ....    

       $form['#attributes']['enctype'] = 'multipart/form-data';
       $form['file'] = array (
        '#type' => 'file',
        '#description' => 'We accept .zip, .gzip, .tgz, tar.gz and .bz2 files up to 2 MB.',
        '#required' => TRUE,
        '#title' => t ( 'Choose your file to upload' ) );

        $form['submit'] = array ( '#type' => 'submit', '#value' => 'Submit' );

        return ( $form );

}

But when I choose a file to upload and then then click on the submit button, I get an error saying that "Choose your file to upload field is required.", so it thinks the file is missing.  I don't understand why and what I can do to fix this problem.

Your help is appreciated.

Mona

I solved my problem...it turns out that I CANNOT use '#required' => TRUE when setting up the file field in the form!  That field will be empty for both validate and submit.  Instead I had to actually save the file in the submit function with file_save_upload() to valiidate it.  Here is my submit function excerpt:

function a_upload_form_submit ( $form, &$form_state )
{
    global $user;

    $limits = _upload_file_limits ( $user );
    $validators = array (
        'file_validate_extensions' => array ( $limits['extensions'] ),
    );
    $file = file_save_upload ( 'file', $validators, null, false );
    if ( $file == 0  )
    {
        form_set_error ( '', t ( 'Error: missing required upload file.' ) );
        return;
    }
    // save data code here

}

Hope this helps someone else.

cheers,

Mona

Thanks Justin.

 

I`m trying to create a CCK widget, based on FileField widget like that. Then I create new CCK widget for my content type. But I did not get this widget while creating a node.

 

<?php
// $Id:

/**
* Implementation of CCK's hook_widget_info().
*/
function filewidget_widget_info() {
  return array(
    
'filewidget' => array(
      
'label' => t('Filewidget'),
      
'field types' => array('filefield'),
      
'multiple values' => CONTENT_HANDLE_CORE,
      
'callbacks' => array(
        
'default value' => CONTENT_CALLBACK_DEFAULT,
      ),
    ),
  );
}

/**
* Implementation of CCK's hook_widget_settings().
*/

function filewidget_widget_settings($op$widget) {
  switch (
$op) {
    case 
'form':
      return 
filewidget_widget_settings_form($widget);
    case 
'validate':
      return 
filewidget_widget_settings_validate($widget);
    case 
'save':
      return 
filewidget_widget_settings_save($widget);
  }
}

function filewidget_widget_settings_form($widget) {
  
$form module_invoke('filefield''widget_settings''form'$widget);

  if ($form['file_extensions']['#default_value'] == 'txt') {
    
$form['file_extensions']['#default_value'] = '3gp avi flv mp4 mpg mpeg';
  }

  $form['text'] = array(
    
'#type' => 'textfield',
    
'#title' => t('Text'),
    
'#default_value' => !empty($widget['text']) ? $widget['text'] : '',
    
'#size' => 15,
    
'#maxlength' => 10,
    
'#description' => t('Lorem ipsum dolorum set amen.'),
  );

  return $form;
}

function filewidget_widget_settings_validate($widget) {
}

function filewidget_widget_settings_save($widget) {
  
$filefield_settings module_invoke('filefield''widget_settings''save'$widget);
  return 
array_merge($filefield_settings, array('text'));
}

/**
* Implementation of CCK's hook_widget().
*
* Assign default properties to item and delegate to FileField.
*/

function filewidget_widget(&$form, &$form_state$field$items$delta 0) {
  
$element filefield_widget($form$form_state$field$items$delta);
  return 
$element;
}

/**
* Element #value_callback function.
*/

function filewidget_widget_value($element$edit FALSE) {
  
$item filefield_widget_value($element$edit);

  return $item;
}

/**
* Element #process callback function.
*/

function filewidget_widget_process($element$edit, &$form_state$form) {
  
$element = array();

  return $element;
}

/**
* Implementation of CCK's hook_default_value().
*/

function filewidget_default_value(&$form, &$form_state$field$delta) {
  return 
filefield_default_value($form$form_state$field$delta);
}

?>

Hey Guys!

The post by

widget inside a fieldset.

I figured that I need to add the content_field_form to my fieldset in order to get a desired result and this action places my widget in place.

But after the upload, instead of showing the uploaded image, all the widget disapears.

It looks like the AJAX can not find a correct object to return it's data.

If You have any idea of how to deal with that, please help!..

...another thing, there's a need for placing several image uploads, each in different fieldset.

The idea is to create a page where a user can fill some kind of registration form which contains photo.

So I need an uploader photo for the user's photo along with the photos of all his family members.

Each family member is separated by fieldset.

Thanks for your post. I'm trying to do something similar, and I have a couple of questions:

1: How can I load a given node's already uploaded images into the form?

2: The submit handler is using node_gallery_image_save. Is it possible to just save regularly through imagefield? How should my handler look in that case?

Regards,

Marcus

I updated CCK today and had the same problem as well. Spent all day yesterday uploading images to nodes, and now they are all gone! The files for images are still in the file folder, but the nodes don't seem to show them. however if I go into edit mode it still shows me thumbnail that something was uploaded.

Add new comment

Subscribe to SysAdmin's Journey RSS