Index: form_example.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/examples/form_example/form_example.module,v
retrieving revision 1.1.2.2
diff -u -r1.1.2.2 form_example.module
--- form_example/form_example.module	5 Sep 2010 15:33:00 -0000	1.1.2.2
+++ form_example/form_example.module	6 Oct 2010 13:20:54 -0000
@@ -118,6 +118,16 @@
     'file' => 'form_example_tutorial.inc',
     'weight' => 10,
   );
+  $items['examples/form_example/tutorial/11'] = array(
+    'title' => t('#11'),
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('form_example_tutorial_11'),
+    'access callback' => TRUE,
+    'description' => t('Tutorial 11: Form with file upload'),
+    'type' => MENU_LOCAL_TASK,
+    'file' => 'form_example_tutorial.inc',
+    'weight' => 11,
+  );
   $items['examples/form_example/element_example'] = array(
     'title' => 'Element example',
     'page callback' => 'drupal_get_form',
@@ -140,7 +150,7 @@
  * Implements hook_help() to provide a bit of help.
  */
 function form_example_help($path, $arg) {
-  switch($path) {
+  switch ($path) {
     case 'examples/form_example/tutorial':
       $help = t('This form example tutorial for Drupal 6 is the code from the <a href="http://drupal.org/node/262422">Handbook 10-step tutorial</a>');
       break;
Index: form_example.test
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/examples/form_example/form_example.test,v
retrieving revision 1.1.2.2
diff -u -r1.1.2.2 form_example.test
--- form_example/form_example.test	5 Sep 2010 15:33:00 -0000	1.1.2.2
+++ form_example/form_example.test	6 Oct 2010 13:21:21 -0000
@@ -117,6 +117,24 @@
     $this->assertText(t('The form has been submitted. name="firstname lastname", year of birth=1955'));
     $this->assertText(t('Second name: name="firstname2 lastname2", year of birth=1956'));
     $this->assertText(t('And the favorite color is green'));
+
+    // #11
+    // Get sample images.
+    $images = $this->drupalGetTestFiles('image');
+
+    foreach ($images as $image) {
+      $this->drupalPost('examples/form_example/tutorial/11', array('files[file]' => $image->filename), t('Submit'));
+      $this->assertText(t('The form has been submitted and the image has been saved, filename: @filename.', array('@filename' => $image->basename)));
+    }
+    // Try invalid files
+    $files = $this->drupalGetTestFiles('html');
+    foreach ($files as $file) {
+      $this->drupalPost('examples/form_example/tutorial/11', array('files[file]' => $file->filename), t('Submit'));
+      // Can't find a quick way to rebuild the whole validation string, so check
+      // only that file has not been submitted.
+      $this->assertText(t('The selected file @filename could not be uploaded.', array('@filename' => $file->basename)));
+    }
+
   }
 
   /**
@@ -127,12 +145,12 @@
     $edit = array(
       'form_example_textfield' => $this->randomName(),
       'form_example_checkbox' => TRUE,
-      'form_example_element_discrete[areacode]' => sprintf('%03d', rand(0,999)),
-      'form_example_element_discrete[prefix]' => sprintf('%03d', rand(0,999)),
-      'form_example_element_discrete[extension]' => sprintf('%04d', rand(0,9999)),
-      'form_example_element_combined[areacode]' => sprintf('%03d', rand(0,999)),
-      'form_example_element_combined[prefix]' => sprintf('%03d', rand(0,999)),
-      'form_example_element_combined[extension]' => sprintf('%04d', rand(0,9999)),
+      'form_example_element_discrete[areacode]' => sprintf('%03d', rand(0, 999)),
+      'form_example_element_discrete[prefix]' => sprintf('%03d', rand(0, 999)),
+      'form_example_element_discrete[extension]' => sprintf('%04d', rand(0, 9999)),
+      'form_example_element_combined[areacode]' => sprintf('%03d', rand(0, 999)),
+      'form_example_element_combined[prefix]' => sprintf('%03d', rand(0, 999)),
+      'form_example_element_combined[extension]' => sprintf('%04d', rand(0, 9999)),
     );
     $this->drupalPost('examples/form_example/element_example', $edit, t('Submit'));
     $this->assertText(t('form_example_textfield has value @value', array('@value' => $edit['form_example_textfield'])));
Index: form_example_elements.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/examples/form_example/Attic/form_example_elements.inc,v
retrieving revision 1.1.2.2
diff -u -r1.1.2.2 form_example_elements.inc
--- form_example/form_example_elements.inc	26 Sep 2010 13:16:24 -0000	1.1.2.2
+++ form_example/form_example_elements.inc	6 Oct 2010 13:21:39 -0000
@@ -75,8 +75,8 @@
     // needs its own value callback.
   );
 
-	// This discrete phonenumber element keeps its values as the separate elements
-	// area code, prefix, extension.
+  // This discrete phonenumber element keeps its values as the separate elements
+  // area code, prefix, extension.
   $types['form_example_phonenumber_discrete'] = array(
     '#input' => TRUE,
 
@@ -136,11 +136,11 @@
  * Process callback for the discrete version of phonenumber.
  */
 function form_example_phonenumber_discrete_process($element, $edit, &$form_state, $complete_form) {
-	// #tree = TRUE means that the values in $form_state['values'] will be stored
-	// hierarchically. In this case, the parts of the element will appear in
-	// $form_state['values'] as
-	// $form_state['values']['<element_name>']['areacode'],
-	// $form_state['values']['<element_name>']['prefix'],
+  // #tree = TRUE means that the values in $form_state['values'] will be stored
+  // hierarchically. In this case, the parts of the element will appear in
+  // $form_state['values'] as
+  // $form_state['values']['<element_name>']['areacode'],
+  // $form_state['values']['<element_name>']['prefix'],
   // etc. This technique is preferred when an element has member form
   // elements.
   $element['#tree'] = TRUE;
@@ -315,8 +315,8 @@
  * are placed next to each other, rather than on separate lines.
  */
 function theme_form_example_phonenumber_discrete($element) {
-	// #children represents all the sublevels elements already rendered in HTML.
-	// Here it contains the three parts of the 'phonenumber' element type ('areacode', 'prefix' and 'extension').
+  // #children represents all the sublevels elements already rendered in HTML.
+  // Here it contains the three parts of the 'phonenumber' element type ('areacode', 'prefix' and 'extension').
   return theme('form_element', $element, '<div class="container-inline">' . $element['#children'] . '</div>');
 }
 
Index: form_example_tutorial.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/examples/form_example/form_example_tutorial.inc,v
retrieving revision 1.1.2.2
diff -u -r1.1.2.2 form_example_tutorial.inc
--- form_example/form_example_tutorial.inc	26 Sep 2010 13:19:31 -0000	1.1.2.2
+++ form_example/form_example_tutorial.inc	6 Oct 2010 13:25:00 -0000
@@ -555,9 +555,9 @@
 
 function form_example_tutorial_9_clear($form, &$form_state) {
   // Ensures fields are blank after the reset button is clicked.
-  unset ($form_state['values']);
+  unset($form_state['values']);
   // Ensures the reset button removes the new_name button.
-  unset ($form_state['storage']);
+  unset($form_state['storage']);
   // Setting $form_state['rebuild'] = TRUE causes the default submit
   // function to be skipped and the form to be rebuilt.
 
@@ -742,8 +742,8 @@
 }
 
 function form_example_tutorial_10_clear($form, &$form_state) {
-  unset ($form_state['values']);
-  unset ($form_state['storage']);
+  unset($form_state['values']);
+  unset($form_state['storage']);
 
   $form_state['rebuild'] = TRUE;
 }
@@ -832,7 +832,83 @@
     // $form_state['storage'] must be unset for redirection to occur. Otherwise
     // $form_state['rebuild'] is automatically set and this form will be
     // rebuilt.
-    unset ($form_state['storage']);
+    unset($form_state['storage']);
     $form_state['redirect'] = 'node'; // Redirects the user to /node.
   }
-}
\ No newline at end of file
+}
+
+//////////////// Tutorial Example 11 //////////////////////
+
+/**
+ * Example 11: A form with a file upload field.
+ *
+ * This example allows the user to upload a file to Drupal which is stored
+ * physically and with a reference in the database. In this example, only
+ * png, gif, jpg, and jpeg files are allowed.
+ *
+ * @see form_example_tutorial_11_validate()
+ * @see form_example_tutorial_11_submit()
+ * @ingroup form_example
+ *
+ */
+function form_example_tutorial_11($form_state) {
+  // This is required to upload files.
+  // enctype="multipart/form-data" required by browsers to handle files.
+  $form = array(
+    '#attributes' => array('enctype' => "multipart/form-data"),
+  );
+
+  $form['file'] = array(
+    '#type' => 'file',
+    '#title' => t('Image'),
+    '#description' => t('Upload a file, allowed extensions: jpg, jpeg, png, gif'),
+  );
+
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Submit'),
+  );
+
+  return $form;
+}
+
+/**
+ * Validate handler for form_example_tutorial_11().
+ * Verify the valid extensions, and verify content is an image also.
+ */
+function form_example_tutorial_11_validate($form, &$form_state) {
+  $file = file_save_upload('file', array(
+    'file_validate_extensions' => array('png gif jpg jpeg'),
+    'file_validate_is_image' => array(),
+  ));
+  
+  // If the file passed validation:
+  if (isset($file->filename)) {
+    // Move the file, into the Drupal file system
+    if (file_move($file, $file->filename)) {
+      // Update the new file location in the database.
+      drupal_write_record('files', $file, 'fid');
+      // Save the file for use in the submit handler.
+      $form_state['storage']['file'] = $file;
+    }
+    else {
+      form_set_error('file', t('Failed to write the uploaded file the site\'s file folder.'));
+    }
+  }
+  else {
+    form_set_error('file', t('Invalid file, only images with the extension png, gif, jpg, jpeg are allowed'));
+  }
+}
+
+/**
+ * Submit handler for form_example_tutorial_11().
+ */
+function form_example_tutorial_11_submit($form, &$form_state) {
+  $file = $form_state['storage']['file'];
+  // We are done with the file, remove it from storage.
+  unset($form_state['storage']['file']);
+  // Make the storage of the file permanent
+  file_set_status($file, FILE_STATUS_PERMANENT);
+  // Set a response to the user.
+  drupal_set_message(t('The form has been submitted and the image has been saved, filename: @filename.', array('@filename' => $file->filename)));
+}

