Index: components/date.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/webform/components/date.inc,v
retrieving revision 1.29.2.17
diff -u -r1.29.2.17 date.inc
--- components/date.inc	6 Jan 2011 18:35:00 -0000	1.29.2.17
+++ components/date.inc	7 Feb 2011 03:49:26 -0000
@@ -154,6 +154,11 @@
 function webform_expand_date($element) {
   $component = $element['#webform_component'];
 
+  // Accept a string or array value for #default_value.
+  if (isset($element['#default_value']) && is_string($element['#default_value'])) {
+    $element['#default_value'] = webform_date_array($element['#default_value'], 'date');
+  }
+
   // Set defaults according to existing #default_value (set by Form API)
   if (isset($element['#default_value']['month']) || isset($element['#default_value']['day']) || isset($element['#default_value']['year'])) {
     $default_values = array(
Index: components/file.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/webform/components/file.inc,v
retrieving revision 1.17.2.12
diff -u -r1.17.2.12 file.inc
--- components/file.inc	29 Dec 2010 21:56:49 -0000	1.17.2.12
+++ components/file.inc	7 Feb 2011 03:49:26 -0000
@@ -346,11 +346,15 @@
  */
 function theme_webform_render_file($element) {
   // Add information about the existing file, if any.
+  if (isset($element['#default_value'])) {
+    $element['_fid']['#value'] = $element['#default_value'];
+  }
   $value = $element['_fid']['#value'] ? $element['_fid']['#value'] : $element['_old']['#value'];
+
   if ($value && ($file = webform_get_file($value))) {
     $firstchild = array_shift(array_keys($element));
     $element[$firstchild]['#suffix'] = ' ' . l(t('Download !filename', array('!filename' => webform_file_name($file->filepath))), webform_file_url($file->filepath)) . (isset($element['#suffix']) ? $element['#suffix'] : '');
-    $element[$firstchild]['#description'] = ' <div class="webform-newfile-message">'. t('Uploading a new file will replace the current file.') .'</div>'. $element[$firstchild]['#description'];
+    $element[$firstchild]['#description'] = '<div class="webform-newfile-message">'. t('Uploading a new file will replace the current file.') . '</div>' . (isset($element[$firstchild]['#description']) ? $element[$firstchild]['#description'] : '');
   }
 
   // Add the required asterisk.
Index: components/time.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/webform/components/time.inc,v
retrieving revision 1.24.2.14
diff -u -r1.24.2.14 time.inc
--- components/time.inc	18 Oct 2010 07:20:54 -0000	1.24.2.14
+++ components/time.inc	7 Feb 2011 03:49:27 -0000
@@ -82,6 +82,22 @@
  * Implementation of _webform_render_component().
  */
 function _webform_render_time($component, $value = NULL, $filter = TRUE) {
+  $element = array(
+    '#title' => $filter ? _webform_filter_xss($component['name']) : $component['name'],
+    '#title_display' => $component['extra']['title_display'] ? $component['extra']['title_display'] : 'before',
+    '#required' => $component['mandatory'],
+    '#weight' => $component['weight'],
+    '#description' => $filter ? _webform_filter_descriptions($component['extra']['description']) : $component['extra']['description'],
+    '#element_validate' => array('webform_validate_time'),
+    '#hourformat' => $component['extra']['hourformat'],
+    '#after_build' => array('webform_expand_time'),
+    '#theme' => 'webform_time',
+    '#theme_wrappers' => array('webform_element_wrapper'),
+    '#pre_render' => array('webform_element_title_display'),
+    '#post_render' => array('webform_element_wrapper'),
+    '#webform_component' => $component,
+  );
+
   if (drupal_strlen($component['value']) > 0) {
     // Adjust the time based on the user or site timezone.
     // The "timezone_name" variable is provided by DateAPI in Drupal 6.
@@ -109,6 +125,10 @@
     );
   }
 
+  if (!empty($value[0])) {
+    $default_values = webform_date_array($value[0], 'time');
+  }
+
   $first_hour = 0;
   $last_hour = 23;
   if ($component['extra']['hourformat'] == '12-hour') {
@@ -125,21 +145,6 @@
   for ($i = 0; $i <= 59; $i++) $minutes[$i] = $i < 10 ? "0$i" : $i;
   $ampms = array('am' => t('am'), 'pm' => t('pm'));
 
-  $element = array(
-    '#title' => $filter ? _webform_filter_xss($component['name']) : $component['name'],
-    '#title_display' => $component['extra']['title_display'] ? $component['extra']['title_display'] : 'before',
-    '#required' => $component['mandatory'],
-    '#weight' => $component['weight'],
-    '#description' => $filter ? _webform_filter_descriptions($component['extra']['description']) : $component['extra']['description'],
-    '#element_validate' => array('webform_validate_time'),
-    '#hourformat' => $component['extra']['hourformat'],
-    '#theme' => 'webform_time',
-    '#theme_wrappers' => array('webform_element_wrapper'),
-    '#pre_render' => array('webform_element_title_display'),
-    '#post_render' => array('webform_element_wrapper'),
-    '#webform_component' => $component,
-  );
-
   $element['hour'] = array(
     '#prefix' => '',
     '#type' => 'select',
@@ -160,16 +165,27 @@
     );
   }
 
-  if (isset($value[0]) && $value[0] !== '') {
-    $value = webform_date_array($value[0], 'time');
-    if ($component['extra']['hourformat'] == '12-hour') {
-      $value = webform_time_convert($value, '12-hour');
-    }
+  // Set the overall default value.
+  if ($default_values['hour'] !== '') {
+    $element['#default_value'] = webform_date_string($default_values);
+  }
+
+  return $element;
+}
+
+/**
+ * Form API #after_build function for Webform time fields.
+ *
+ * Note that a #process function will not work on time fields, since they are
+ * not actual FAPI elements. We use this function to set the proper default
+ * values for the time fields.
+ */
+function webform_expand_time($element) {
+  if (array_key_exists('#default_value', $element)) {
+    $time_value = webform_date_array($element['#default_value'], 'time');
 
-    $element['hour']['#default_value'] = $value['hour'];
-    $element['minute']['#default_value'] = $value['minute'];
-    if (isset($value['ampm'])) {
-      $element['ampm']['#default_value'] = $value['ampm'];
+    foreach ($time_value as $key => $value) {
+      $element[$key]['#value'] = $value;
     }
   }
 
Index: includes/webform.pages.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/webform/includes/webform.pages.inc,v
retrieving revision 1.10.2.5
diff -u -r1.10.2.5 webform.pages.inc
--- includes/webform.pages.inc	6 Jan 2011 18:35:00 -0000	1.10.2.5
+++ includes/webform.pages.inc	7 Feb 2011 03:49:27 -0000
@@ -172,9 +172,15 @@
   );
   $form['advanced']['allow_draft'] = array(
     '#type' => 'checkbox',
-    '#title' => t('Allow users to save a draft'),
+    '#title' => t('Show "Save draft" button'),
     '#default_value' => $node->webform['allow_draft'],
-    '#description' => t('Allow your users to save and finish the form later.'),
+    '#description' => t('Allow your users to save and finish the form later. This option is available only for authenticated users.'),
+  );
+  $form['advanced']['auto_save'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Automatically save as draft between pages'),
+    '#default_value' => $node->webform['auto_save'],
+    '#description' => t('Automatically save partial submissions when users click the "Next" or "Previous" buttons in a multipage form.'),
   );
   $form['advanced']['submit_notice'] = array(
     '#type' => 'checkbox',
@@ -263,6 +269,9 @@
   // Set the draft option.
   $node->webform['allow_draft'] = $form_state['values']['allow_draft'];
 
+  // Set the auto-save draft option.
+  $node->webform['auto_save'] = $form_state['values']['auto_save'];
+
   // Set the submit limit to -1 if set to unlimited.
   if ($form_state['values']['enforce_limit'] == 'no') {
     $node->webform['submit_limit'] = -1;
Index: includes/webform.submissions.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/webform/includes/webform.submissions.inc,v
retrieving revision 1.18.2.14
diff -u -r1.18.2.14 webform.submissions.inc
--- includes/webform.submissions.inc	5 Jan 2011 23:46:02 -0000	1.18.2.14
+++ includes/webform.submissions.inc	7 Feb 2011 03:49:27 -0000
@@ -46,7 +46,11 @@
   // preserve any data from form pages not visited in this submission.
   if ($submission->is_draft) {
     $submitted_cids = array_keys($submission->data);
-    db_query("DELETE FROM {webform_submitted_data} WHERE sid = %d AND cid IN (" . implode(', ', $submitted_cids) . ")", $submission->sid);
+    if ($submitted_cids) {
+      $placeholders = db_placeholders($submitted_cids);
+      $arguments = array_merge(array($submission->sid), $submitted_cids);
+      db_query("DELETE FROM {webform_submitted_data} WHERE sid = %d AND cid IN (" . $placeholders . ")", $arguments);
+    }
   }
   else {
     db_query("DELETE FROM {webform_submitted_data} WHERE sid = %d", $submission->sid);
@@ -377,7 +381,7 @@
   static $counts;
 
   if (!isset($counts[$nid][$uid]) || $reset) {
-    $query = 'SELECT count(*) FROM {webform_submissions} WHERE nid = %d';
+    $query = 'SELECT count(*) FROM {webform_submissions} WHERE nid = %d AND is_draft = 0';
     $arguments = array($nid);
     if ($uid !== NULL) {
       $query .= ' AND uid = %d';
Index: webform.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/webform/webform.install,v
retrieving revision 1.40.2.23
diff -u -r1.40.2.23 webform.install
--- webform.install	6 Jan 2011 18:34:59 -0000	1.40.2.23
+++ webform.install	7 Feb 2011 03:49:24 -0000
@@ -67,6 +67,13 @@
          'not null' => TRUE,
          'default' => 0,
       ),
+      'auto_save' => array(
+         'description' => 'Boolean value for whether submissions to this form should be auto-saved between pages.',
+         'type' => 'int',
+         'size' => 'tiny',
+         'not null' => TRUE,
+         'default' => 0,
+      ),
       'submit_notice' => array(
         'description' => 'Boolean value for whether to show or hide the previous submissions notification.',
         'type' => 'int',
@@ -1277,6 +1284,15 @@
 }
 
 /**
+ * Add the ability to auto-save as draft between pages.
+ */
+function webform_update_6324() {
+  $ret = array();
+  db_add_field($ret, 'webform', 'auto_save', array('type' => 'int', 'size' => 'tiny', 'not null' => TRUE, 'default' => 0));
+  return $ret;
+}
+
+/**
  * Recursively delete all files and folders in the specified filepath, then
  * delete the containing folder.
  *
Index: webform.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/webform/webform.module,v
retrieving revision 1.196.2.74
diff -u -r1.196.2.74 webform.module
--- webform.module	6 Jan 2011 18:34:59 -0000	1.196.2.74
+++ webform.module	7 Feb 2011 03:49:26 -0000
@@ -1010,6 +1010,7 @@
     'teaser' => 0,
     'block' => 0,
     'allow_draft' => 0,
+    'auto_save' => 0,
     'submit_notice' => 1,
     'submit_text' => '',
     'submit_limit' => -1,
@@ -1222,7 +1223,7 @@
 
   // Check if this user has a draft for this webform.
   $is_draft = FALSE;
-  if ($node->webform['allow_draft'] && $user->uid != 0) {
+  if (($node->webform['allow_draft'] || $node->webform['auto_save']) && $user->uid != 0) {
     // Draft found - display form with draft data for further editing.
     if ($draft_sid = _webform_fetch_draft_sid($node->nid, $user->uid)) {
       module_load_include('inc', 'webform', 'includes/webform.submissions');
@@ -1527,6 +1528,14 @@
   global $user;
 
   module_load_include('inc', 'webform', 'includes/webform.components');
+  module_load_include('inc', 'webform', 'includes/webform.submissions');
+
+  // If in a multi-step form, a submission ID may be specified in form state.
+  // Load this submission. This allows anonymous users to use auto-save.
+  if (empty($submission) && !empty($form_state['values']['details']['sid'])) {
+    $submission = webform_get_submission($node->nid, $form_state['values']['details']['sid']);
+    $is_draft = $submission->is_draft;
+  }
 
   // Bind arguments to $form to make them available in theming and form_alter.
   $form['#node'] = $node;
@@ -1581,7 +1590,7 @@
 
     // Recursively add components to the form.
     foreach ($component_tree['children'] as $cid => $component) {
-      $component_value = isset($form_state['values']['submitted'][$component['form_key']]) ? $form_state['values']['submitted'][$component['form_key']] : NULL;
+      $component_value = isset($form_state['values']['submitted'][$cid]) ? $form_state['values']['submitted'][$cid] : NULL;
       if (_webform_client_form_rule_check($node, $component, $page_num, $form_state)) {
         _webform_client_form_add_component($node, $component, $component_value, $form['submitted'], $form, $form_state, $submission, 'form', $page_num, $filter);
       }
@@ -1621,19 +1630,21 @@
       '#suffix' => '</div>',
     );
 
+    // Add the draft button.
+    if ($node->webform['allow_draft'] && (empty($submission) || $submission->is_draft) && $user->uid != 0) {
+      $form['actions']['draft'] = array(
+        '#type' => 'submit',
+        '#value' => t('Save Draft'),
+        '#weight' => -2,
+        '#validate' => array(),
+      );
+    }
+
     if ($page_count > 1) {
       $next_page = t('Next Page >');
       $prev_page = t('< Previous Page');
 
       // Add the submit button(s).
-     if ($node->webform['allow_draft'] && (empty($submission) || $submission->is_draft) && $user->uid != 0) {
-        $form['actions']['draft'] = array(
-          '#type' => 'submit',
-          '#value' => t('Save Draft'),
-          '#weight' => -2,
-          '#validate' => array(),
-        );
-      }
       if ($page_num > 1) {
         $form['actions']['previous'] = array(
           '#type' => 'submit',
@@ -1821,7 +1832,7 @@
 
   if (isset($component['children']) && is_array($component['children'])) {
     foreach ($component['children'] as $scid => $subcomponent) {
-      $subcomponent_value = isset($component_value[$subcomponent['form_key']]) ? $component_value[$subcomponent['form_key']] : NULL;
+      $subcomponent_value = isset($form_state['values']['submitted'][$scid]) ? $form_state['values']['submitted'][$scid] : NULL;
       if (_webform_client_form_rule_check($node, $subcomponent, $page_num, $form_state, $submission)) {
         _webform_client_form_add_component($node, $subcomponent, $subcomponent_value, $parent_fieldset[$component['form_key']], $form, $form_state, $submission, $format, $page_num, $filter);
       }
@@ -1934,9 +1945,12 @@
     $form_state['storage']['page_num'] = $form_state['webform']['page_num'];
   }
 
+  // Assume the form is completed unless the page logic says otherwise.
+  $form_state['webform_completed'] = TRUE;
+
   // Check for a multi-page form that is not yet complete.
-  $submit_op = empty($node->webform['submit_text']) ? t('Submit') : t($node->webform['submit_text']);
-  $draft_op = t('Save Draft');
+  $submit_op = !empty($form['actions']['submit']['#value']) ? $form['actions']['submit']['#value'] : t('Submit');
+  $draft_op = !empty($form['actions']['draft']['#value']) ? $form['actions']['draft']['#value'] : t('Save Draft');
   if (!in_array($form_state['values']['op'], array($submit_op, $draft_op))) {
     // Checkboxes need post-processing to maintain their values.
     _webform_client_form_submit_process($node, $form_state['values']['submitted'], array('select', 'grid'));
@@ -1993,15 +2007,12 @@
       }
     }
 
-    // Rebuild the form and display the next page.
-    if ($form_state['storage']['page_num'] <= $form_state['storage']['page_count']) {
-      $form_state['rebuild'] = TRUE;
-      return;
-    }
+    // The form is done if the page number is greater than the page count.
+    $form_state['webform_completed'] = $form_state['storage']['page_num'] > $form_state['storage']['page_count'];
   }
 
+  // Merge any stored submission data for multistep forms.
   if (isset($form_state['storage']['submitted'])) {
-    // Merge any stored submission data for multistep forms.
     $original_values = is_array($form_state['values']['submitted']) ? $form_state['values']['submitted'] : array();
     unset($form_state['values']['submitted']);
 
@@ -2016,10 +2027,6 @@
     unset($original_values);
   }
 
-  // Remove the form state storage now that we're done with the pages.
-  unset($form_state['rebuild']);
-  unset($form_state['storage']);
-
   // Perform post processing by components.
   _webform_client_form_submit_process($node, $form_state['values']['submitted']);
 
@@ -2027,8 +2034,21 @@
   $form_state['values']['submitted_tree'] = $form_state['values']['submitted'];
   $form_state['values']['submitted'] = _webform_client_form_submit_flatten($node, $form_state['values']['submitted']);
 
-  // Set a flag indicating processing should continue and be saved.
-  $form_state['webform_completed'] = TRUE;
+  // Inform the submit handlers that a draft will be saved.
+  if ($form_state['values']['op'] == $draft_op || ($node->webform['auto_save'] && !$form_state['webform_completed'])) {
+    $form_state['save_draft'] = TRUE;
+  }
+
+  // Determine what we need to do on the next page.
+  if (!empty($form_state['save_draft']) || !$form_state['webform_completed']) {
+    // Rebuild the form and display the current (on drafts) or next page.
+    $form_state['rebuild'] = TRUE;
+  }
+  else {
+    // Remove the form state storage now that we're done with the pages.
+    unset($form_state['rebuild']);
+    unset($form_state['storage']);
+  }
 }
 
 /**
@@ -2039,14 +2059,14 @@
   module_load_include('inc', 'webform', 'includes/webform.components');
   global $user;
 
-  if (empty($form_state['webform_completed'])) {
+  if (empty($form_state['save_draft']) && empty($form_state['webform_completed'])) {
     return;
   }
 
   $node = $form['#node'];
 
   // Check if user is submitting as a draft.
-  $is_draft = $form_state['values']['op'] == t('Save Draft');
+  $is_draft = !empty($form_state['save_draft']);
 
   // Create a submission object.
   $submission = (object) array(
@@ -2213,8 +2233,11 @@
   $message = NULL;
   $redirect = NULL;
   $external_url = FALSE;
-  if ($is_draft) {
-    $message = t('Draft saved');
+  if (isset($form['actions']['draft']['#value']) && $form_state['values']['op'] == $form['actions']['draft']['#value']) {
+    $message = t('Submission saved. You may return to this form later and it will restore the current values.');
+  }
+  elseif ($is_draft) {
+    $redirect = NULL;
   }
   elseif (!empty($form_state['values']['details']['finished'])) {
     $message = t('Submission updated.');
@@ -2270,11 +2293,8 @@
       if (isset($node->webform['components'][$cid])) {
         // Call the component process submission function.
         $component = $node->webform['components'][$cid];
-        if ((!isset($types) || in_array($component['type'], $types))) {
-          $new_value = webform_component_invoke($component['type'], 'submit', $component, $form_values[$component['form_key']]);
-          if ($new_value !== NULL) {
-            $form_values[$component['form_key']] = $new_value;
-          }
+        if ((!isset($types) || in_array($component['type'], $types)) && webform_component_implements($component['type'], 'submit')) {
+          $form_values[$component['form_key']] = webform_component_invoke($component['type'], 'submit', $component, $form_values[$component['form_key']]);
         }
       }
     }
@@ -3081,6 +3101,20 @@
 }
 
 /**
+ * Check if a component implements a particular hook.
+ *
+ * @param $type
+ *   The component type as a string.
+ * @param $callback
+ *   The callback to check.
+ */
+function webform_component_implements($type, $callback) {
+  $function = '_webform_' . $callback . '_' . $type;
+  webform_component_include($type);
+  return function_exists($function);
+}
+
+/**
  * Disable the Drupal page cache.
  */
 function webform_disable_page_cache() {
